@rubytech/create-maxy 1.0.477 → 1.0.478
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +23 -0
- package/payload/platform/plugins/anthropic/PLUGIN.md +3 -2
- package/payload/platform/plugins/anthropic/references/console-api.md +186 -0
- package/payload/platform/plugins/anthropic/references/setup-guide.md +3 -3
- package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +89 -20
- package/payload/platform/scripts/seed-neo4j.sh +92 -0
- package/payload/platform/templates/account.json +2 -1
- package/payload/server/public/assets/ChatInput-DNdZ2WD7.css +1 -0
- package/payload/server/public/assets/{admin-DpmnCxNk.js → admin-DLtY5oYr.js} +6 -6
- package/payload/server/public/assets/{public-BBxDqQvQ.js → public-B27jukY6.js} +1 -1
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +162 -73
- package/payload/server/public/assets/ChatInput-sDYraTun.css +0 -1
- /package/payload/server/public/assets/{ChatInput-DZ0j0Gdp.js → ChatInput-BWyEqP0k.js} +0 -0
package/package.json
CHANGED
|
@@ -561,3 +561,26 @@ FOR (ag:AccessGrant) ON (ag.magicToken);
|
|
|
561
561
|
// Status filtering — admin listing of active/expired/revoked grants.
|
|
562
562
|
CREATE INDEX access_grant_status IF NOT EXISTS
|
|
563
563
|
FOR (ag:AccessGrant) ON (ag.status);
|
|
564
|
+
|
|
565
|
+
// ----------------------------------------------------------
|
|
566
|
+
// AdminUser node — device-level admin identity
|
|
567
|
+
// Platform-native. Represents a human who administers one or
|
|
568
|
+
// more accounts via the admin agent. Exists at the device level
|
|
569
|
+
// (not account-scoped). Linked to accounts via ADMIN_OF.
|
|
570
|
+
//
|
|
571
|
+
// Properties:
|
|
572
|
+
// userId — UUID, matches entry in platform/config/users.json
|
|
573
|
+
// name — display name (e.g. "Adam")
|
|
574
|
+
// createdAt — ISO 8601 timestamp
|
|
575
|
+
//
|
|
576
|
+
// Relationships:
|
|
577
|
+
// (AdminUser)-[:ADMIN_OF {role, grantedAt}]->(LocalBusiness)
|
|
578
|
+
// role: "owner" | "admin" — label only, no enforcement
|
|
579
|
+
// grantedAt: ISO 8601 timestamp
|
|
580
|
+
//
|
|
581
|
+
// No vector index — AdminUser is always queried deterministically
|
|
582
|
+
// by userId. Semantic search over admin users is not a use case.
|
|
583
|
+
// ----------------------------------------------------------
|
|
584
|
+
|
|
585
|
+
CREATE CONSTRAINT admin_user_id_unique IF NOT EXISTS
|
|
586
|
+
FOR (au:AdminUser) REQUIRE au.userId IS UNIQUE;
|
|
@@ -5,7 +5,7 @@ description: Guide users through obtaining an Anthropic API key to power the pub
|
|
|
5
5
|
|
|
6
6
|
# Anthropic / Claude Setup
|
|
7
7
|
|
|
8
|
-
The public agent runs on the Anthropic API. This plugin handles obtaining, storing, and verifying an API key from
|
|
8
|
+
The public agent runs on the Anthropic API. This plugin handles obtaining, storing, and verifying an API key from the Anthropic Console (platform.claude.com).
|
|
9
9
|
|
|
10
10
|
## When to activate
|
|
11
11
|
|
|
@@ -24,12 +24,13 @@ The public agent runs on the Anthropic API. This plugin handles obtaining, stori
|
|
|
24
24
|
|
|
25
25
|
| Task | When to use | Skill |
|
|
26
26
|
|------|-------------|-------|
|
|
27
|
-
| Obtain API key
|
|
27
|
+
| Obtain API key | `api-key-verify` returns `missing` or user wants to rotate key | `skills/get-api-key/SKILL.md` |
|
|
28
28
|
|
|
29
29
|
## References
|
|
30
30
|
|
|
31
31
|
| Topic | When to use | Reference |
|
|
32
32
|
|-------|-------------|-----------|
|
|
33
33
|
| Troubleshooting & FAQ | API key rejected, billing questions, manual key entry | `references/setup-guide.md` |
|
|
34
|
+
| Console API surface | Understanding the endpoints used by the get-api-key skill | `references/console-api.md` |
|
|
34
35
|
|
|
35
36
|
For API key acquisition (including during onboarding), use the `skills/get-api-key/SKILL.md` skill. For troubleshooting and common questions, load the reference.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Anthropic Console — Internal API Reference
|
|
2
|
+
|
|
3
|
+
Captured 2026-04-07 from `platform.claude.com` (formerly `console.anthropic.com`).
|
|
4
|
+
|
|
5
|
+
These are undocumented internal endpoints used by the Console's React frontend. They are not part of Anthropic's public API and may change without notice.
|
|
6
|
+
|
|
7
|
+
## Authentication
|
|
8
|
+
|
|
9
|
+
Cookie-based via `sessionKey` cookie:
|
|
10
|
+
- Domain: `.platform.claude.com`
|
|
11
|
+
- Flags: `httpOnly`, `secure`, `SameSite=Lax`
|
|
12
|
+
- Expiry: 7 days from sign-in
|
|
13
|
+
|
|
14
|
+
The cookie is set during the OAuth sign-in flow (Google or email verification). Because it is `httpOnly`, it cannot be read by JavaScript — but `fetch()` with `credentials: 'include'` sends it automatically.
|
|
15
|
+
|
|
16
|
+
Common request headers (set by the Console frontend, not required for API auth):
|
|
17
|
+
- `content-type: application/json`
|
|
18
|
+
- `anthropic-client-platform: web_console`
|
|
19
|
+
|
|
20
|
+
## Org Discovery
|
|
21
|
+
|
|
22
|
+
The Console is org-scoped. All billing and key management endpoints require an org UUID. A user may have multiple orgs (e.g., a consumer chat org and an API org).
|
|
23
|
+
|
|
24
|
+
### GET /api/organizations
|
|
25
|
+
|
|
26
|
+
Returns all orgs the user belongs to.
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
[
|
|
30
|
+
{
|
|
31
|
+
"id": 55730262,
|
|
32
|
+
"uuid": "1882f41b-...",
|
|
33
|
+
"name": "Neo's Individual Org",
|
|
34
|
+
"billing_type": "prepaid",
|
|
35
|
+
"capabilities": ["api", "api_individual"],
|
|
36
|
+
"created_at": "2025-06-28T06:40:26.071676Z"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Selecting the API org:** Filter for the org whose `capabilities` array contains `"api"`. Consumer orgs have `capabilities: ["claude_max", "chat"]` and no API key management.
|
|
42
|
+
|
|
43
|
+
### GET /api/bootstrap
|
|
44
|
+
|
|
45
|
+
Returns current user account info and org memberships. Useful for getting the user's name and email.
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"account": {
|
|
50
|
+
"tagged_id": "user_01YW5...",
|
|
51
|
+
"uuid": "ff1a722d-...",
|
|
52
|
+
"email_address": "user@example.com",
|
|
53
|
+
"full_name": "Neo Aeon",
|
|
54
|
+
"memberships": [
|
|
55
|
+
{ "organization": { "uuid": "...", "name": "..." }, "role": "admin" }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Billing Endpoints
|
|
62
|
+
|
|
63
|
+
All GET, org-scoped. Replace `{orgId}` with the org UUID.
|
|
64
|
+
|
|
65
|
+
### GET /api/organizations/{orgId}/prepaid/credits
|
|
66
|
+
|
|
67
|
+
Credit balance.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"amount": 504,
|
|
72
|
+
"currency": "USD",
|
|
73
|
+
"auto_reload_settings": {
|
|
74
|
+
"enabled": true,
|
|
75
|
+
"threshold_in_minor_units": 500,
|
|
76
|
+
"reload_to_in_minor_units": 1500
|
|
77
|
+
},
|
|
78
|
+
"pending_invoice_amount_cents": null
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`amount` is in **cents** (minor currency units). 504 = $5.04.
|
|
83
|
+
|
|
84
|
+
### GET /api/organizations/{orgId}/current_spend
|
|
85
|
+
|
|
86
|
+
Current billing period spend.
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"amount": 39,
|
|
91
|
+
"resets_at": "2026-05-01T00:00:00Z"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`amount` is in **cents**. 39 = $0.39.
|
|
96
|
+
|
|
97
|
+
### GET /api/organizations/{orgId}/prepaid/auto_recharge
|
|
98
|
+
|
|
99
|
+
Auto-recharge configuration.
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"status": "active",
|
|
104
|
+
"stripe_error_code": null,
|
|
105
|
+
"target_amount": 1500,
|
|
106
|
+
"threshold_amount": 500
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Amounts in **cents**.
|
|
111
|
+
|
|
112
|
+
### GET /api/organizations/{orgId}/payment_method
|
|
113
|
+
|
|
114
|
+
Payment method on file.
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"brand": null,
|
|
119
|
+
"country": null,
|
|
120
|
+
"last4": null,
|
|
121
|
+
"type": "link"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## API Key Endpoints
|
|
126
|
+
|
|
127
|
+
### GET /api/console/organizations/{orgId}/api_keys
|
|
128
|
+
|
|
129
|
+
List all API keys for the org.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
[
|
|
133
|
+
{
|
|
134
|
+
"id": "apikey_01XY5wjo397BKKLjHNgvHATx",
|
|
135
|
+
"type": "api_key",
|
|
136
|
+
"name": "maxy",
|
|
137
|
+
"workspace_id": null,
|
|
138
|
+
"created_at": "2026-03-22T19:08:25.868735Z",
|
|
139
|
+
"created_by": { "id": "user_01YW5...", "type": "user" },
|
|
140
|
+
"partial_key_hint": "sk-ant-api03-kNh...HAAA",
|
|
141
|
+
"status": "active",
|
|
142
|
+
"expires_at": null
|
|
143
|
+
}
|
|
144
|
+
]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`status` values: `"active"`, `"inactive"` (disabled), `"archived"` (deleted).
|
|
148
|
+
|
|
149
|
+
### POST /api/console/organizations/{orgId}/workspaces/default/api_keys
|
|
150
|
+
|
|
151
|
+
Create a new API key. The `raw_key` is only returned once at creation time.
|
|
152
|
+
|
|
153
|
+
**Request:**
|
|
154
|
+
```json
|
|
155
|
+
{ "name": "Maxy" }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Response (200):**
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"id": "apikey_01Hkz...",
|
|
162
|
+
"type": "api_key",
|
|
163
|
+
"name": "Maxy",
|
|
164
|
+
"workspace_id": null,
|
|
165
|
+
"created_at": "2026-04-07T11:05:39.949811Z",
|
|
166
|
+
"created_by": { "id": "user_01YW5...", "type": "user" },
|
|
167
|
+
"partial_key_hint": "sk-ant-api03-cPs...1AAA",
|
|
168
|
+
"status": "active",
|
|
169
|
+
"expires_at": null,
|
|
170
|
+
"raw_key": "sk-ant-api03-cPsiih...ewev1AAA"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### POST /api/console/organizations/{orgId}/workspaces/default/api_keys/{keyId}
|
|
175
|
+
|
|
176
|
+
Update key status.
|
|
177
|
+
|
|
178
|
+
**Disable:**
|
|
179
|
+
```json
|
|
180
|
+
{ "status": "inactive" }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
**Archive (delete):**
|
|
184
|
+
```json
|
|
185
|
+
{ "status": "archived" }
|
|
186
|
+
```
|
|
@@ -18,10 +18,10 @@ If the automated browser flow is unavailable or the user already has a key:
|
|
|
18
18
|
|
|
19
19
|
| Problem | Solution |
|
|
20
20
|
|---------|----------|
|
|
21
|
-
| API key rejected | Verify it starts with `sk-ant-`. Check that billing is set up on
|
|
21
|
+
| API key rejected | Verify it starts with `sk-ant-`. Check that billing is set up on platform.claude.com — keys don't work without an active billing method. |
|
|
22
22
|
| Key created on zero-balance account | Keys created before credits are loaded are permanently rejected. Create a new key after adding credits. |
|
|
23
23
|
| "Which provider should I pick?" | Anthropic / Claude is the right choice. It powers all of the public agent's conversations and reasoning. |
|
|
24
|
-
| Browser automation failed | Tell the user to get a key from
|
|
24
|
+
| Browser automation failed | Tell the user to get a key from platform.claude.com/settings/keys in their own browser, then paste it in the chat. |
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
@@ -32,4 +32,4 @@ If the automated browser flow is unavailable or the user already has a key:
|
|
|
32
32
|
| "How much does the API cost?" | Anthropic charges per API call. For small business use, typical costs are a few dollars per month. See anthropic.com/pricing for current rates. |
|
|
33
33
|
| "Do I need to restart after adding a key?" | No — the API key is applied immediately. |
|
|
34
34
|
| "Can I rotate my key?" | Yes — run the `get-api-key` skill again or paste a new key. The old key is overwritten. |
|
|
35
|
-
| "What if my key stops working?" | Check that billing is still active on
|
|
35
|
+
| "What if my key stops working?" | Check that billing is still active on platform.claude.com. If the account ran out of credits, add more and the existing key should resume working (unless it was created on a zero-balance account). |
|
|
@@ -1,51 +1,115 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: get-api-key
|
|
3
|
-
description: Obtain an Anthropic API key by
|
|
3
|
+
description: Obtain an Anthropic API key by calling the Console's internal API endpoints via browser_evaluate, falling back to screenshot-based browser automation if direct API calls fail.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Get Anthropic API Key
|
|
7
7
|
|
|
8
|
-
Obtain an API key from
|
|
8
|
+
Obtain an API key from the Anthropic Console (platform.claude.com) on behalf of the user. The browser-specialist calls Console API endpoints directly via `browser_evaluate` + `fetch()` — the browser's session cookies handle auth automatically. This is orders of magnitude faster than navigating pages and interpreting screenshots.
|
|
9
9
|
|
|
10
10
|
**Critical ordering rule:** Credits must be confirmed on the account before creating an API key. Keys created on a zero-balance account are permanently rejected by the Anthropic API — even after credits are added later.
|
|
11
11
|
|
|
12
|
+
**Auth model:** The Console uses an `httpOnly` session cookie (`sessionKey`) set during sign-in. Because it's `httpOnly`, JavaScript can't read it — but `fetch()` with `credentials: 'include'` sends it automatically. All API calls in the briefs below rely on this.
|
|
13
|
+
|
|
12
14
|
## Steps
|
|
13
15
|
|
|
14
16
|
1. **Show the browser.** Call `render-component` with:
|
|
15
17
|
- `name: "browser-viewer"`
|
|
16
18
|
- `data: { title: "Anthropic Console" }`
|
|
17
19
|
|
|
18
|
-
2. **Dispatch browser-specialist for billing assessment.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
|
|
19
|
-
|
|
20
|
-
> Navigate to https://
|
|
20
|
+
2. **Dispatch browser-specialist for sign-in check and billing assessment.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
|
|
21
|
+
|
|
22
|
+
> Navigate to https://platform.claude.com/settings/billing. Wait for the page to load.
|
|
23
|
+
>
|
|
24
|
+
> Then use `browser_evaluate` to run this JavaScript:
|
|
25
|
+
> ```js
|
|
26
|
+
> (async () => {
|
|
27
|
+
> try {
|
|
28
|
+
> const orgsRes = await fetch('/api/organizations', { credentials: 'include', headers: { 'content-type': 'application/json' } });
|
|
29
|
+
> if (orgsRes.status === 401 || orgsRes.status === 403) return { status: 'SIGN_IN_REQUIRED' };
|
|
30
|
+
> const orgs = await orgsRes.json();
|
|
31
|
+
> const apiOrg = orgs.find(o => o.capabilities && o.capabilities.includes('api'));
|
|
32
|
+
> if (!apiOrg) return { status: 'NO_API_ORG', orgs: orgs.map(o => ({ name: o.name, capabilities: o.capabilities })) };
|
|
33
|
+
> const creditsRes = await fetch(`/api/organizations/${apiOrg.uuid}/prepaid/credits`, { credentials: 'include', headers: { 'content-type': 'application/json' } });
|
|
34
|
+
> if (!creditsRes.ok) return { status: 'BILLING_CHECK_FAILED', httpStatus: creditsRes.status };
|
|
35
|
+
> const credits = await creditsRes.json();
|
|
36
|
+
> return { status: 'OK', orgId: apiOrg.uuid, orgName: apiOrg.name, creditsCents: credits.amount, currency: credits.currency };
|
|
37
|
+
> } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
|
|
38
|
+
> })()
|
|
39
|
+
> ```
|
|
40
|
+
>
|
|
41
|
+
> Report the result object exactly as returned. Do not interpret or summarize — pass the JSON back verbatim.
|
|
21
42
|
|
|
22
43
|
3. **Handle the billing result.**
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
44
|
+
- `status: "OK"` with `creditsCents > 0`: Credits available. Note the `orgId` — it's needed for key creation. Proceed to step 6.
|
|
45
|
+
- `status: "OK"` with `creditsCents === 0`: No credits. Proceed to step 5.
|
|
46
|
+
- `status: "SIGN_IN_REQUIRED"`: Proceed to step 4.
|
|
47
|
+
- `status: "NO_API_ORG"`: Tell the user: "Your Anthropic account doesn't have an API organization. Visit platform.claude.com and set up API access, then try again."
|
|
48
|
+
- `status: "BILLING_CHECK_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based billing check (step 2F).
|
|
27
49
|
|
|
28
50
|
4. **Sign-in required.** Tell the user: "Sign in to your Anthropic account in the browser. You can use Google sign-in or enter your email — Anthropic will send a verification email. Open the email in the same browser (open a new tab) to complete sign-in. If you don't have an account, create one — you will need to add credits before a key can be created." Wait for the user to confirm they have signed in. Then return to step 2 to re-check billing.
|
|
29
51
|
|
|
30
|
-
5. **No credits — guide the user.** Tell the user: "Your Anthropic account needs credits loaded before I can create an API key. Add credits or enable auto-top-up on the billing page. Let me know when you're done." Wait for confirmation, then
|
|
31
|
-
|
|
32
|
-
6. **Dispatch browser-specialist for key creation.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
|
|
33
|
-
|
|
34
|
-
>
|
|
52
|
+
5. **No credits — guide the user.** Tell the user: "Your Anthropic account needs credits loaded before I can create an API key. Add credits or enable auto-top-up on the billing page. Let me know when you're done." Wait for confirmation, then re-dispatch the billing check (step 2). If credits are now present, proceed to step 6. If still no credits, repeat this step (maximum 3 attempts). After 3 attempts with no credits detected, tell the user: "Credits still not detected. You can add them at platform.claude.com/settings/billing and then ask me to create the API key again."
|
|
53
|
+
|
|
54
|
+
6. **Dispatch browser-specialist for key creation.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Include the `orgId` from step 3. Brief:
|
|
55
|
+
|
|
56
|
+
> Use `browser_evaluate` to run this JavaScript (replace ORG_ID with the orgId from the billing check):
|
|
57
|
+
> ```js
|
|
58
|
+
> (async () => {
|
|
59
|
+
> try {
|
|
60
|
+
> const res = await fetch('/api/console/organizations/ORG_ID/workspaces/default/api_keys', {
|
|
61
|
+
> method: 'POST',
|
|
62
|
+
> credentials: 'include',
|
|
63
|
+
> headers: { 'content-type': 'application/json' },
|
|
64
|
+
> body: JSON.stringify({ name: 'Maxy' })
|
|
65
|
+
> });
|
|
66
|
+
> if (res.status === 401 || res.status === 403) return { status: 'SIGN_IN_REQUIRED' };
|
|
67
|
+
> if (!res.ok) return { status: 'CREATE_FAILED', httpStatus: res.status, body: await res.text() };
|
|
68
|
+
> const key = await res.json();
|
|
69
|
+
> if (!key.raw_key) return { status: 'NO_RAW_KEY', keyId: key.id };
|
|
70
|
+
> return { status: 'KEY_CREATED', rawKey: key.raw_key, keyName: key.name, keyId: key.id };
|
|
71
|
+
> } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
|
|
72
|
+
> })()
|
|
73
|
+
> ```
|
|
74
|
+
>
|
|
75
|
+
> If the result has `status: "KEY_CREATED"`, call `api-key-store` with the `rawKey` value. Report the full result object back.
|
|
76
|
+
>
|
|
77
|
+
> If the result has any other status, report it back verbatim — do not attempt to navigate the Console UI.
|
|
35
78
|
|
|
36
79
|
7. **Handle the key creation result.**
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
80
|
+
- `status: "KEY_CREATED"` and specialist confirms key was stored: proceed to step 9 (verify).
|
|
81
|
+
- `status: "SIGN_IN_REQUIRED"`: this is unexpected since sign-in was handled during the billing check. Tell the user to sign in, wait for confirmation, then retry step 6 once. If it fails again, proceed to step 8 (fallback).
|
|
82
|
+
- `status: "CREATE_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based key creation (step 6F).
|
|
83
|
+
- `status: "NO_RAW_KEY"`: The key was created but the response didn't include the full key value. Proceed to step 8 (manual fallback).
|
|
40
84
|
|
|
41
85
|
8. **Fallback.** Tell the user: "I could not extract the key automatically. Please copy it from the browser and paste it here in the chat." When the user pastes a key starting with `sk-ant-`, call `api-key-store` with it. Then proceed to step 9.
|
|
42
86
|
|
|
43
87
|
9. **Verify the key works.** Call the `api-key-verify` MCP tool. Handle each status:
|
|
44
88
|
- `valid`: Confirm to the user — "API key stored and verified. Your public agent is ready."
|
|
45
89
|
- `billing`: Credits were confirmed before key creation, so this is unexpected. Tell the user: "The key was stored but credits may not have propagated yet. Please wait a moment." Retry `api-key-verify` once after a brief pause. If still `billing`, tell the user: "Credits are not being detected yet. The key is stored — the public agent will start working once Anthropic recognises the credits on your account."
|
|
46
|
-
- `auth_error`: The key was rejected. Tell the user: "The stored key was rejected by Anthropic. It may have been copied incorrectly. Let's try again." Return to step 6 (maximum 2 retries — if it fails 3 times total, tell the user to visit
|
|
90
|
+
- `auth_error`: The key was rejected. Tell the user: "The stored key was rejected by Anthropic. It may have been copied incorrectly. Let's try again." Return to step 6 (maximum 2 retries — if it fails 3 times total, tell the user to visit platform.claude.com/settings/keys in their own browser and paste the key manually).
|
|
47
91
|
- `error`: Could not verify (network or Anthropic issue). Tell the user: "I couldn't verify the key right now — this is likely a temporary issue. The key is stored and should work once the connection is restored."
|
|
48
92
|
|
|
93
|
+
## Fallback Steps (screenshot-based)
|
|
94
|
+
|
|
95
|
+
These steps are used when `browser_evaluate` + `fetch()` fails. They replicate the original browser automation approach.
|
|
96
|
+
|
|
97
|
+
### Step 2F: Screenshot-based billing check
|
|
98
|
+
|
|
99
|
+
Dispatch browser-specialist with brief:
|
|
100
|
+
|
|
101
|
+
> Navigate to https://platform.claude.com/settings/billing. Take a screenshot to assess the page. Report what you see: Is there a credit balance shown? Does it say "Buy credits to get started"? Is a sign-in page shown instead? Report the billing status and any dollar amount visible. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
|
|
102
|
+
|
|
103
|
+
Handle the result the same as step 3, mapping the specialist's text observations to the structured statuses.
|
|
104
|
+
|
|
105
|
+
### Step 6F: Screenshot-based key creation
|
|
106
|
+
|
|
107
|
+
Dispatch browser-specialist with brief:
|
|
108
|
+
|
|
109
|
+
> Navigate to https://platform.claude.com/settings/keys. Take a screenshot to verify you are on the API Keys page. Create a new API key named "Maxy": find and click the "Create Key" button, fill the key name with "Maxy", submit the form. After creation, look for a string starting with sk-ant- on the page — Anthropic shows the key only once. If found, call api-key-store with the key. Report what happened. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
|
|
110
|
+
|
|
111
|
+
Handle the result the same as step 7.
|
|
112
|
+
|
|
49
113
|
## Important
|
|
50
114
|
|
|
51
115
|
- **`render-component` is required before any browser-specialist dispatch.** The browser-viewer component is what makes the VNC browser visible to the user. Without it, automation runs invisibly. Always call `render-component` with `name: "browser-viewer"` and confirm it renders before dispatching a browser-specialist subagent.
|
|
@@ -53,5 +117,10 @@ Obtain an API key from console.anthropic.com on behalf of the user. Browser auto
|
|
|
53
117
|
- The browser is visible to the user via VNC. They can see everything the specialist does.
|
|
54
118
|
- The user must sign in themselves — the specialist does not fill credentials.
|
|
55
119
|
- For email verification: tell the user to open the verification email in a new tab in the same VNC browser (not on their own device). This keeps the sign-in session in the same browser context.
|
|
56
|
-
- Anthropic shows the API key only once after creation.
|
|
57
|
-
- If the browser-specialist is not available (not installed, Agent tool fails), skip the browser-viewer component entirely and tell the user to get a key from
|
|
120
|
+
- Anthropic shows the API key only once after creation. With the direct API approach, the `raw_key` is in the JSON response — but if the specialist fails to call `api-key-store` before its context ends, the key is lost and a new one must be created.
|
|
121
|
+
- If the browser-specialist is not available (not installed, Agent tool fails), skip the browser-viewer component entirely and tell the user to get a key from platform.claude.com in their own browser, then paste it in the chat.
|
|
122
|
+
- The Console URL `console.anthropic.com` redirects to `platform.claude.com`. All briefs use `platform.claude.com` to avoid redirect latency.
|
|
123
|
+
|
|
124
|
+
## API Reference
|
|
125
|
+
|
|
126
|
+
For the complete captured API surface (endpoint details, response schemas, auth mechanism), see `references/console-api.md`.
|
|
@@ -222,6 +222,75 @@ fi
|
|
|
222
222
|
|
|
223
223
|
echo " Account $ACCOUNT_ID at $ACCOUNT_DIR"
|
|
224
224
|
|
|
225
|
+
# ------------------------------------------------------------------
|
|
226
|
+
# 1b. Create first admin user (migration from .admin-pin)
|
|
227
|
+
# ------------------------------------------------------------------
|
|
228
|
+
|
|
229
|
+
CONFIG_DIR="$PROJECT_DIR/config"
|
|
230
|
+
USERS_FILE="$CONFIG_DIR/users.json"
|
|
231
|
+
|
|
232
|
+
# Resolve the brand-specific config directory for .admin-pin location.
|
|
233
|
+
# Mirrors the logic in platform/ui/app/lib/paths.ts.
|
|
234
|
+
_CONFIG_DIR_NAME=".maxy"
|
|
235
|
+
if [ -f "$CONFIG_DIR/brand.json" ]; then
|
|
236
|
+
_BRAND_CFG_DIR=$(python3 -c "import json; print(json.load(open('$CONFIG_DIR/brand.json')).get('configDir',''))" 2>/dev/null || true)
|
|
237
|
+
[ -n "$_BRAND_CFG_DIR" ] && _CONFIG_DIR_NAME="$_BRAND_CFG_DIR"
|
|
238
|
+
fi
|
|
239
|
+
ADMIN_PIN_FILE="$HOME/$_CONFIG_DIR_NAME/.admin-pin"
|
|
240
|
+
|
|
241
|
+
# Only create users.json if it doesn't exist AND .admin-pin exists (migration case).
|
|
242
|
+
# Fresh installs: no .admin-pin yet (set during onboarding). The session route falls
|
|
243
|
+
# back to .admin-pin when users.json is absent — no migration needed until next seed.
|
|
244
|
+
if [ ! -f "$USERS_FILE" ] && [ -f "$ADMIN_PIN_FILE" ]; then
|
|
245
|
+
USER_ID="$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')"
|
|
246
|
+
PIN_HASH="$(cat "$ADMIN_PIN_FILE")"
|
|
247
|
+
|
|
248
|
+
# Create users.json with the first user entry.
|
|
249
|
+
# PINs are stored as SHA-256 hashes — same format as .admin-pin.
|
|
250
|
+
cat > "$USERS_FILE" << USERS_EOF
|
|
251
|
+
[{"userId":"$USER_ID","name":"Owner","pin":"$PIN_HASH"}]
|
|
252
|
+
USERS_EOF
|
|
253
|
+
echo " [seed] created initial user: userId=$USER_ID"
|
|
254
|
+
|
|
255
|
+
# Add the user to account.json's admins array.
|
|
256
|
+
# The template includes "admins": [] — populate it with the owner entry.
|
|
257
|
+
if [ -f "$ACCOUNT_DIR/account.json" ]; then
|
|
258
|
+
python3 -c "
|
|
259
|
+
import json, sys
|
|
260
|
+
with open('$ACCOUNT_DIR/account.json', 'r') as f:
|
|
261
|
+
config = json.load(f)
|
|
262
|
+
config.setdefault('admins', [])
|
|
263
|
+
if not any(a.get('userId') == '$USER_ID' for a in config['admins']):
|
|
264
|
+
config['admins'].append({'userId': '$USER_ID', 'role': 'owner'})
|
|
265
|
+
with open('$ACCOUNT_DIR/account.json', 'w') as f:
|
|
266
|
+
json.dump(config, f, indent=2)
|
|
267
|
+
f.write('\n')
|
|
268
|
+
"
|
|
269
|
+
echo " Added userId=$USER_ID as owner to account.json admins"
|
|
270
|
+
fi
|
|
271
|
+
elif [ -f "$USERS_FILE" ]; then
|
|
272
|
+
# Re-run: users.json exists. Read the first user's ID for Neo4j seeding.
|
|
273
|
+
USER_ID=$(python3 -c "import json; print(json.load(open('$USERS_FILE'))[0]['userId'])" 2>/dev/null || true)
|
|
274
|
+
[ -n "$USER_ID" ] && echo " Existing users.json found, userId=$USER_ID"
|
|
275
|
+
|
|
276
|
+
# Backfill admins in account.json if missing (existing accounts pre-Task 248).
|
|
277
|
+
if [ -f "$ACCOUNT_DIR/account.json" ] && ! grep -q '"admins"' "$ACCOUNT_DIR/account.json"; then
|
|
278
|
+
python3 -c "
|
|
279
|
+
import json
|
|
280
|
+
with open('$ACCOUNT_DIR/account.json', 'r') as f:
|
|
281
|
+
config = json.load(f)
|
|
282
|
+
config['admins'] = [{'userId': '$USER_ID', 'role': 'owner'}]
|
|
283
|
+
with open('$ACCOUNT_DIR/account.json', 'w') as f:
|
|
284
|
+
json.dump(config, f, indent=2)
|
|
285
|
+
f.write('\n')
|
|
286
|
+
"
|
|
287
|
+
echo " Backfilled admins array in account.json"
|
|
288
|
+
fi
|
|
289
|
+
else
|
|
290
|
+
echo " No .admin-pin found — skipping users.json creation (fresh install)"
|
|
291
|
+
USER_ID=""
|
|
292
|
+
fi
|
|
293
|
+
|
|
225
294
|
# ------------------------------------------------------------------
|
|
226
295
|
# 2. Apply Neo4j schema (constraints + indexes only)
|
|
227
296
|
# ------------------------------------------------------------------
|
|
@@ -236,4 +305,27 @@ echo "==> Applying schema (constraints, indexes, vector indexes)..."
|
|
|
236
305
|
"$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
|
|
237
306
|
-f "$NEO4J_DIR/schema.cypher"
|
|
238
307
|
|
|
308
|
+
# ------------------------------------------------------------------
|
|
309
|
+
# 3. Create AdminUser node + ADMIN_OF relationship
|
|
310
|
+
# ------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
if [ -n "${USER_ID:-}" ]; then
|
|
313
|
+
# Escape single quotes for Cypher string interpolation (e.g. O'Brien → O''Brien).
|
|
314
|
+
USER_NAME=$(python3 -c "import json; n=json.load(open('$USERS_FILE'))[0]['name']; print(n.replace(\"'\",\"''\"))" 2>/dev/null || echo "Owner")
|
|
315
|
+
CREATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
316
|
+
|
|
317
|
+
echo "==> Creating AdminUser node for userId=$USER_ID"
|
|
318
|
+
"$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" << CYPHER_EOF
|
|
319
|
+
MERGE (au:AdminUser {userId: '$USER_ID'})
|
|
320
|
+
ON CREATE SET au.name = '$USER_NAME', au.createdAt = '$CREATED_AT'
|
|
321
|
+
ON MATCH SET au.name = '$USER_NAME'
|
|
322
|
+
WITH au
|
|
323
|
+
MATCH (b:LocalBusiness {accountId: '$ACCOUNT_ID'})
|
|
324
|
+
MERGE (au)-[r:ADMIN_OF]->(b)
|
|
325
|
+
ON CREATE SET r.role = 'owner', r.grantedAt = '$CREATED_AT'
|
|
326
|
+
RETURN au.userId, b.accountId;
|
|
327
|
+
CYPHER_EOF
|
|
328
|
+
echo " AdminUser node created/updated"
|
|
329
|
+
fi
|
|
330
|
+
|
|
239
331
|
echo " Done."
|