Extract authenticated coulomb.social design language via designlang

Session probe lands on /vw_pages (Research). Use main designlang extract
with --cookie-file — pack ignores auth. 39 artifacts under
docs/design-extract; tolerate designlang late summary crash when cores exist.
This commit is contained in:
tegwick 2026-08-09 01:31:00 +02:00
parent 319dd00465
commit 6fe263444a
71 changed files with 4939 additions and 797 deletions

View file

@ -142,39 +142,33 @@ async function main() {
const lsKeys = (state.origins || []).flatMap((o) =>
(o.localStorage || []).map((e) => e.name),
);
// Bubble always sets coulomb_* session cookies for anonymous visitors.
// A real login usually adds more localStorage keys and/or more cookies.
const onlyDefaultBubbleCookies =
cookieCount <= 3 &&
(state.cookies || []).every((c) =>
String(c.name || "").startsWith("coulomb_"),
);
const thinLocalStorage =
lsKeys.length === 0 ||
(lsKeys.length === 1 && lsKeys[0] === "algoliasearch-client-js");
if (onlyDefaultBubbleCookies && thinLocalStorage) {
// Bubble often keeps the same coulomb_* cookie *names* for guests and
// members — names alone are not a login signal. Probe the live page:
// authenticated app usually leaves the bare landing path.
let probeUrl = page.url();
let probeTitle = await page.title().catch(() => "");
const path = (() => {
try {
return new URL(probeUrl).pathname;
} catch {
return "";
}
})();
const looksLikeLanding = path === "/" || path === "";
if (looksLikeLanding && !fromPasswordFiles) {
console.error("");
console.error(
"WARNING: This looks like an anonymous Bubble session, not a logged-in user.",
"WARNING: Still on the site root after capture — login may not have completed.",
);
console.error(` url: ${probeUrl}`);
console.error(` title: ${probeTitle}`);
console.error(
` cookies=${cookieCount} (only default coulomb_* names)`,
"Log in fully, open an app page (e.g. Research / Pages), then press Enter.",
);
console.error(` localStorage keys: ${lsKeys.join(", ") || "(none)"}`);
console.error("");
console.error("Do not use this for authenticated design extract.");
console.error("Re-run, fully log in, wait until the app UI is loaded,");
console.error("then press Enter. If login uses a popup, complete it first.");
process.exitCode = 2;
// Still write so you can inspect, but mark filename
const weakPath = STATE_PATH.replace(/\.json$/, ".anonymous.json");
writeFileSync(weakPath, JSON.stringify(state, null, 2) + "\n", {
mode: 0o600,
});
chmodSync(weakPath, 0o600);
console.error(`Wrote weak session for inspection only: ${weakPath}`);
return;
// Continue to save anyway — Bubble guest+member share cookie names; user
// may still have a valid session that only reveals itself after navigation.
}
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2) + "\n", {
@ -186,6 +180,7 @@ async function main() {
console.log(
`Saved storageState (${cookieCount} cookies, ${originCount} origins, localStorage keys: ${lsKeys.length}).`,
);
console.log(` page at save: ${probeUrl}${probeTitle}`);
console.log(`File: ${STATE_PATH}`);
console.log("Next: ./scripts/run-design-extract.sh");
} finally {

21
scripts/probe-auth-session.mjs Executable file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env node
/** Print final URL after loading coulomb.social with storageState (no secrets). */
import { chromium } from "playwright";
import { homedir } from "os";
import { join } from "path";
const statePath =
process.env.COULOMB_SOCIAL_STORAGE_STATE ||
join(homedir(), ".config/coulomb-social/auth/storage-state.json");
const url = process.env.COULOMB_SOCIAL_URL || "https://coulomb.social";
const browser = await chromium.launch({ headless: true });
try {
const context = await browser.newContext({ storageState: statePath });
const page = await context.newPage();
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45000 });
await page.waitForTimeout(3000);
console.log(page.url());
} finally {
await browser.close();
}

View file

@ -1,6 +1,10 @@
#!/usr/bin/env bash
# Run designlang against coulomb.social using a captured Playwright storageState.
# Does not print secrets. Requires: ./scripts/capture-auth-state.mjs first.
# Does not print secrets. Requires: ./scripts/capture-auth-state.sh first.
#
# NOTE: designlang's `pack` subcommand does NOT pass --cookie-file into the
# crawler (it calls extractDesignLanguage(url) with no auth options). Use the
# main extract command, which does apply cookies from storageState.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@ -8,6 +12,7 @@ AUTH_DIR="${COULOMB_SOCIAL_AUTH_DIR:-$HOME/.config/coulomb-social/auth}"
STATE_PATH="${COULOMB_SOCIAL_STORAGE_STATE:-$AUTH_DIR/storage-state.json}"
OUT_DIR="${COULOMB_SOCIAL_EXTRACT_OUT:-$ROOT/docs/design-extract}"
URL="${COULOMB_SOCIAL_URL:-https://coulomb.social}"
WAIT_MS="${COULOMB_SOCIAL_EXTRACT_WAIT:-5000}"
if [[ ! -f "$STATE_PATH" ]]; then
echo "ERROR: No auth session at $STATE_PATH" >&2
@ -22,21 +27,78 @@ if [[ "$mode" != "600" && "$mode" != "0600" ]]; then
echo "warning: $STATE_PATH mode is $mode (prefer 600)" >&2
fi
echo "==> probing session (no secrets printed)…"
PROBE_URL="$(
cd "$ROOT/scripts" && \
COULOMB_SOCIAL_STORAGE_STATE="$STATE_PATH" \
COULOMB_SOCIAL_URL="$URL" \
node ./probe-auth-session.mjs
)" || PROBE_URL=""
if [[ -n "$PROBE_URL" ]]; then
echo " session lands on: $PROBE_URL"
if [[ "$PROBE_URL" == "https://coulomb.social/" || "$PROBE_URL" == "https://coulomb.social" ]]; then
echo "warning: session still on site root — may be guest; re-run capture-auth-state.sh if extract looks empty" >&2
fi
fi
mkdir -p "$OUT_DIR"
echo "==> designlang extract"
echo "==> designlang extract (main command — cookies applied)"
echo " url: $URL"
echo " auth: $STATE_PATH (session file present; not printed)"
echo " out: $OUT_DIR"
echo " wait: ${WAIT_MS}ms"
echo " extra: $*"
# Global flags (auth, name) must precede the subcommand; pack only accepts -o/--with-clone/--open.
# --cookie-file accepts Playwright storageState JSON.
# Extra args after -- are appended to pack (e.g. --with-clone).
mkdir -p "$(dirname "$OUT_DIR")"
exec npx --yes designlang \
# Main command supports --cookie-file. Prefer a rich capture of the live app UI.
set +e
npx --yes designlang "$URL" \
--cookie-file "$STATE_PATH" \
--name coulomb-social \
pack "$URL" \
-o "$OUT_DIR" \
-n coulomb-social \
--wait "$WAIT_MS" \
--depth "${COULOMB_SOCIAL_EXTRACT_DEPTH:-2}" \
--interactions \
--verbose \
"$@"
dl_ec=$?
set -e
# designlang can crash late while printing summary (Buffer.byteLength on undefined
# content for optional emitters) after already writing the useful artifacts.
if [[ $dl_ec -ne 0 ]]; then
if [[ -f "$OUT_DIR/coulomb-social-design-language.md" && -f "$OUT_DIR/coulomb-social-design-tokens.json" ]]; then
echo "warning: designlang exited $dl_ec after writing core artifacts (known late summary bug); treating as success" >&2
else
echo "ERROR: designlang failed (exit $dl_ec) and core artifacts are missing" >&2
exit "$dl_ec"
fi
fi
# Drop root-level pack leftovers from earlier mistaken `pack` runs (no cookies).
if [[ -d "$ROOT/coulomb-social-design-system" ]]; then
echo "==> removing unauthenticated pack leftover: coulomb-social-design-system/"
rm -rf "$ROOT/coulomb-social-design-system"
fi
# Provenance (no secrets)
cat > "$OUT_DIR/PROVENANCE.md" <<EOF
# Design extract provenance
| Field | Value |
|-------|--------|
| Source | $URL |
| Tool | designlang (main extract, not pack) |
| Captured | $(date -Iseconds) |
| Auth | Playwright storageState via --cookie-file |
| Session probe | ${PROBE_URL:-unknown} |
| designlang exit | $dl_ec |
## Note
Use \`./scripts/run-design-extract.sh\`do **not** use \`designlang pack\` for
authenticated crawls; pack ignores cookies.
EOF
echo "==> done. Inspect: $OUT_DIR"
ls -la "$OUT_DIR" | head -50