diff --git a/domain-skills/claude-ai/api.md b/domain-skills/claude-ai/api.md new file mode 100644 index 00000000..f0b32f71 --- /dev/null +++ b/domain-skills/claude-ai/api.md @@ -0,0 +1,53 @@ +# claude.ai — internal chat API + +Read recent conversations and full transcripts from a logged-in claude.ai +session. Verified live 2026-07-07. + +## The trap: plain HTTP gets Cloudflare-challenged + +`http_get()` (cookieless urllib, generic UA) against any `claude.ai/api/*` +endpoint returns a Cloudflare managed challenge, never JSON. Don't fight it +with headers — run `fetch()` **inside an authenticated claude.ai tab** +instead: same-origin, real session cookies, real browser fingerprint. GET +endpoints need no CSRF header. + +```python +tabs = [t for t in list_tabs(include_chrome=False) if "claude.ai" in t["url"]] +if tabs: + switch_tab(tabs[0]["targetId"]) +else: + new_tab("https://claude.ai/recents") + wait_for_load(20) + +org_uuid = js("fetch('/api/organizations').then(r => r.json()).then(o => o[0].uuid)") +``` + +`js()` awaits promises, so a full fetch chain returns the resolved value. +If the session is logged out, the promise rejects and `js()` returns +`None` — check for it. + +A 2026-07-02 attempt saw challenges even on plain CDP-tab *navigation* to +claude.ai; by 2026-07-07 navigation was clean. Treat navigation challenges +as transient; the in-page fetch pattern works either way once a tab is open. + +## Endpoints (all relative to the tab's origin) + +- `GET /api/organizations` — array; `[0].uuid` is the org for personal + accounts. +- `GET /api/organizations/{org_uuid}/chat_conversations?limit=30` — array + of `{uuid, name, updated_at, ...}`, newest first. `updated_at` is ISO + 8601 with `Z` suffix. +- `GET /api/organizations/{org_uuid}/chat_conversations/{uuid}` — one + conversation; `chat_messages` is the transcript array: + `{uuid, text, sender, index, created_at, updated_at, truncated, + attachments, files, parent_message_uuid}` with `sender` ∈ + `human | assistant`. `text` is usually populated; when empty, look for + `content` blocks (`[{type, text, ...}]`) — same shape as the claude.ai + data-export format. + +## Notes + +- Large transcripts (150+ messages) return fine through + `js()`/`returnByValue` — no pagination needed at conversation level. +- Keep payload prints single-line (`JSON.stringify`) if a wrapper script + parses marker lines from stdout; JSON escapes embedded newlines. diff --git a/domain-skills/neon/console.md b/domain-skills/neon/console.md new file mode 100644 index 00000000..397c4f65 --- /dev/null +++ b/domain-skills/neon/console.md @@ -0,0 +1,123 @@ +--- +name: neon-console +description: Neon Postgres console (console.neon.tech) — project/branch navigation, finding compute endpoint hostnames, extracting connection strings without picking up the UI's display-whitespace. +--- + +# Neon Console — console.neon.tech + +Serverless Postgres dashboard. Auth via SSO; treat as already-logged-in. The UI is a SPA (Next.js). + +## Routes + +| Route | What | +|---|---| +| `/app/org-/projects` | Org-scoped project list (default landing) | +| `/app/projects/` | Project dashboard (Connect button lives here) | +| `/app/projects//branches` | Branches table: name, parent, compute hours, primary compute endpoint, storage | +| `/app/projects/?branchId=&database=` | Project dashboard scoped to a branch — what the left-nav BRANCH picker switches between | + +## Branches table — what the columns actually mean + +Free plan typically shows two branches (`production` + `test`). **Each branch has its own primary compute endpoint** with its own hostname (`ep--..aws.neon.tech`) — they are NOT shared. Earlier docs/folklore claiming "free plan shares one compute across branches" is wrong. + +- `Compute` column = CU-hrs consumed in current period. +- `Primary compute` column shows autoscale range (e.g. `.25 ↔ 2 CU`) and an **Idle/Active** pill. The `.25 ↔ 2 CU` text *looks* like a link but it's actually a clickable cell that opens the **Edit primary compute** drawer — that drawer shows the endpoint name (`ep-<...>`) in an editable input. + +### Getting an endpoint hostname for a branch + +```python +# On /app/projects//branches +rect = js(""" + (() => { + const rows = [...document.querySelectorAll('tr')].filter(tr => /production/i.test(tr.textContent || '') && /Default/.test(tr.textContent || '')); + if (!rows.length) return null; + const tr = rows[0]; + // Find the .25 ↔ 2 CU compute cell + const all = [...tr.querySelectorAll('*')]; + const cu = all.find(el => /CU/.test((el.textContent || '').trim()) && /↔/.test(el.textContent || '') && el.children.length <= 1); + if (!cu) return null; + const r = cu.getBoundingClientRect(); + return {x: r.x + r.width/2, y: r.y + r.height/2}; + })() +""") +click(rect["x"], rect["y"]) +wait(1) +endpoint = js("document.querySelector('input[value^=\"ep-\"]')?.value") +press_key("Escape") # close drawer +``` + +The compute cell wraps to two visual lines on rows with multi-digit CU-hrs; clicking the centre of the cell sometimes lands between lines and opens a tooltip instead of the drawer. If `endpoint` is `None`, retry by clicking the row's cell at `r.x + 30, r.y + r.height/2` (left-justified, single-line target). + +## Branch picker (left nav, "BRANCH" combobox) + +Below the PROJECT nav. Currently selected branch's name is shown in a button at roughly `x=124, y=310` for a default 1442×1508 viewport. Clicking opens a dropdown listing all branches with a checkmark on the current one. + +```python +# Switch the dashboard to the production-branch context +js(""" +(() => { + const btn = [...document.querySelectorAll('button')].find(b => /^test$/i.test((b.textContent || '').trim()) && b.getBoundingClientRect().x < 200); + btn?.click(); +})() +""") +wait(1) +js("""[...document.querySelectorAll('div')].find(el => (el.textContent || '').trim() === 'production' && el.getBoundingClientRect().x < 250)?.click()""") +wait(1) # URL updates to ?branchId=... +``` + +The branch picker scopes the **Connect** dialog (and most dashboard widgets) to that branch — so to get a branch's connection string, switch the picker first, then click Connect. + +## Getting a connection string — the whitespace trap + +Project dashboard → **Connect** button (top-right) opens "Connect to your database". Branch + Compute dropdowns let you pick role/database/pooled-vs-direct. **Show password** is a button that toggles password visibility. + +The connection string is rendered inside a `
` (NOT a `