-
Notifications
You must be signed in to change notification settings - Fork 1.6k
domain-skills: add claude-ai internal chat API #497
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
johncheng-max
wants to merge
2
commits into
browser-use:main
Choose a base branch
from
johncheng-max:domain-skill-claude-ai
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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-<org-id>/projects` | Org-scoped project list (default landing) | | ||
| | `/app/projects/<project-id>` | Project dashboard (Connect button lives here) | | ||
| | `/app/projects/<project-id>/branches` | Branches table: name, parent, compute hours, primary compute endpoint, storage | | ||
| | `/app/projects/<project-id>?branchId=<branch-id>&database=<db>` | 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-<random>-<region>.<region>.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/<id>/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 `<div>` (NOT a `<textarea>`) where the host is split across multiple inline elements styled with `word-break`. Reading `el.textContent` returns the visible string — which has invisible inter-element whitespace baked in (e.g. `aws .neon.tech` with 4 spaces). You will get DNS `ENOTFOUND` if you pass that string to a postgres client. | ||
|
|
||
| **Don't read textContent.** Either: | ||
|
|
||
| 1. **Walk text nodes and strip all whitespace.** Postgres URLs cannot legally contain whitespace anywhere, so a global `/\s+/g` strip is safe (special chars in passwords are %-encoded per RFC 3986): | ||
|
|
||
| ```python | ||
| url_data = js(""" | ||
| (() => { | ||
| function walkText(root, out) { | ||
| if (root.nodeType === 3) { out.push(root.nodeValue); return; } | ||
| for (const c of root.childNodes) walkText(c, out); | ||
| } | ||
| const candidates = [...document.querySelectorAll('*')].filter(el => { | ||
| const t = el.textContent || ''; | ||
| return /postgresql:\\/\\//.test(t) && /neon\\.tech/.test(t) && el.children.length < 50; | ||
| }); | ||
| candidates.sort((a, b) => (a.textContent || '').length - (b.textContent || '').length); | ||
| if (!candidates.length) return null; | ||
| const parts = []; | ||
| walkText(candidates[0], parts); | ||
| return parts.join(''); | ||
| })() | ||
| """) | ||
| import re | ||
| url = re.sub(r"\s+", "", url_data) # strip the layout whitespace | ||
| ``` | ||
|
|
||
| 2. **Or click "Copy snippet" and read the clipboard** (more robust if the markup changes). | ||
|
|
||
| Sanity-check before use: the URL should start with `postgresql://`, contain a host matching `ep-[a-z0-9-]+\.[a-z0-9-]+\.aws\.neon\.tech`, contain `?sslmode=require`, and contain no `*` characters (asterisks mean Show password didn't get clicked). | ||
|
|
||
| ### Pooled vs direct host | ||
|
|
||
| When "Connection pooling" is on (default), the host has a `-pooler` suffix: `ep-<random>-pooler.<region>.aws.neon.tech`. Use the pooler for normal app traffic; use the un-pooled host for DDL/migrations only if the migration runner needs session-level features (most don't, including drizzle-kit). | ||
|
|
||
| ## Quirks and traps | ||
|
|
||
| - **Compute auto-suspends after idle (5 min on free plan)**, then needs ~1–2s to cold-start on the next connection. First connection from a fresh session may time out (`ETIMEDOUT`); retry once. Long-running clients (workers, pg-boss) keep it warm. | ||
| - **The "production" branch on a Neon project is just a default name**, not a guarantee that production data lives there. People often develop on a `test` branch and never cut over — so always confirm which branch holds the live data (look at CU-hrs and recency, not the name). | ||
| - **Each branch fork copies parent data + schema + drizzle migrations table at fork time**. If you ran `bun run db:migrate` against a branch that's never been migrated, drizzle will run all migrations from `0000` forward, not just the new one. Pre-check with `SELECT count(*) FROM drizzle.__drizzle_migrations` against the target branch URL before applying. | ||
| - **Org-id changes per Neon account** (`/app/org-<id>/projects`) — never hardcode it; navigate to the projects list and click through. | ||
| - **Project dashboard "Connect" button is branch-scoped** via the left-nav BRANCH picker, not via the Compute dropdown inside the dialog. The Compute dropdown only switches between primary/replica; it doesn't switch branches. | ||
| - **Branch row clicks (`<tr>`) don't navigate** — there is no anchor on the branch name. The kebab (3-dot) menu at the row's right edge has the only branch-level actions: rename, set-default, set-protected, delete. There is no "set as primary compute" because each branch already owns its own compute. | ||
|
|
||
| ## What's not in this skill | ||
|
|
||
| - Programmatic API key + Neon REST API (`api.neon.tech/api/v2/...`) — preferable to the dashboard for repeatable ops, but out of scope here. | ||
| - Branch creation/deletion via UI (one-shot ops; just use the kebab menu). | ||
| - Billing surface. | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: The prose includes a raw pixel coordinate (
x=124, y=310for a 1442×1508 viewport) to describe where the branch-picker button appears. This is viewport-dependent and will be wrong at different window sizes, zoom levels, or after layout changes. The code snippet just below already shows the correct approach — find the button by selector + text matching. Drop the coordinate from the description; the selector-based code is the durable reference.Prompt for AI agents