@promptcellar/pc 0.3.3 → 0.4.1
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/README.md +3 -3
- package/bin/pc.js +3 -1
- package/docs/plans/2026-01-30-wa-auth-integration-design.md +118 -0
- package/docs/plans/2026-01-30-wa-auth-integration-plan.md +776 -0
- package/hooks/codex-capture.js +16 -3
- package/hooks/gemini-capture.js +37 -24
- package/hooks/prompt-capture.js +67 -23
- package/package.json +4 -4
- package/src/commands/login.js +125 -41
- package/src/commands/logout.js +6 -0
- package/src/commands/push.js +1 -29
- package/src/commands/save.js +21 -2
- package/src/commands/setup.js +44 -44
- package/src/commands/status.js +11 -0
- package/src/lib/api.js +0 -1
- package/src/lib/config.js +28 -1
- package/src/lib/crypto.js +39 -0
- package/src/lib/device-transfer.js +43 -0
- package/src/lib/keychain.js +80 -0
- package/src/lib/websocket.js +2 -6
- package/tests/device-login.test.js +121 -0
- package/tests/device-transfer.test.js +69 -0
- package/tests/keychain.test.js +40 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npm install -g @promptcellar/pc
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
# Login
|
|
14
|
+
# Login via browser authorization
|
|
15
15
|
pc login
|
|
16
16
|
|
|
17
17
|
# Set up auto-capture for your AI CLI tools
|
|
@@ -26,7 +26,7 @@ pc status
|
|
|
26
26
|
### Authentication
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
pc login # Login
|
|
29
|
+
pc login # Login via browser authorization
|
|
30
30
|
pc logout # Remove stored credentials
|
|
31
31
|
pc status # Show current status and connection info
|
|
32
32
|
```
|
|
@@ -41,7 +41,7 @@ pc unsetup # Remove auto-capture hooks
|
|
|
41
41
|
Supported tools (auto-detected during setup):
|
|
42
42
|
- **Claude Code** - via Stop hooks
|
|
43
43
|
- **Codex CLI** - via notify hook
|
|
44
|
-
- **Gemini CLI** - via
|
|
44
|
+
- **Gemini CLI** - via BeforeAgent hook
|
|
45
45
|
|
|
46
46
|
Coming soon:
|
|
47
47
|
- Cursor
|
package/bin/pc.js
CHANGED
|
@@ -13,12 +13,14 @@ import { update } from '../src/commands/update.js';
|
|
|
13
13
|
program
|
|
14
14
|
.name('pc')
|
|
15
15
|
.description('PromptCellar CLI - sync prompts between your terminal and the cloud')
|
|
16
|
-
.version('0.
|
|
16
|
+
.version('0.4.0');
|
|
17
17
|
|
|
18
18
|
program
|
|
19
19
|
.command('login')
|
|
20
20
|
.description('Authenticate with PromptCellar via browser')
|
|
21
21
|
.option('-u, --url <url>', 'API URL (for self-hosted instances)')
|
|
22
|
+
.option('-a, --account-url <url>', 'Account URL (for self-hosted instances)')
|
|
23
|
+
.option('-c, --client-id <id>', 'OIDC client ID (default: prompts-prod)')
|
|
22
24
|
.action(login);
|
|
23
25
|
|
|
24
26
|
program
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# wa-auth Integration Design
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Migrate pc-cli authentication from prompt-registry's removed device auth endpoints to wa-auth, and add client-side encryption so captured prompts match prompt-registry's zero-knowledge architecture.
|
|
6
|
+
|
|
7
|
+
## Login Flow
|
|
8
|
+
|
|
9
|
+
Four stages:
|
|
10
|
+
|
|
11
|
+
1. **Device code request** -- CLI calls `POST /device/code` on wa-auth with `{ client_id: "prompts-prod" }`. Gets back `device_code`, `user_code`, and `verification_url`.
|
|
12
|
+
|
|
13
|
+
2. **User approval** -- User opens the verification URL in browser, authenticates with their passkey, approves the device. CLI polls `POST /device/poll` with `{ device_code }`.
|
|
14
|
+
|
|
15
|
+
3. **JWT received** -- On approval, wa-auth returns `{ status: "approved", access_token: "<JWT>", user_id: "..." }`. The JWT has `aud: "prompts-prod"`, `sub: "<user_id>"`, standard exp/iss claims.
|
|
16
|
+
|
|
17
|
+
4. **API key exchange** -- CLI calls `POST /api/v1/auth/device-token` on prompt-registry with `Authorization: Bearer <JWT>` and `{ device_name: "Linux - myhostname" }`. Prompt-registry validates the JWT, auto-revokes any previous key with the same device name, creates a new `pk_*` API key, and returns:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"api_key": "pk_...",
|
|
22
|
+
"email": "user@example.com",
|
|
23
|
+
"vault": {
|
|
24
|
+
"available": true,
|
|
25
|
+
"encrypted_key": "base64url(...)"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If `vault.available` is false, the CLI warns that capture requires vault setup at the web UI.
|
|
31
|
+
|
|
32
|
+
## Changes to wa-auth
|
|
33
|
+
|
|
34
|
+
Two modifications to the device auth flow:
|
|
35
|
+
|
|
36
|
+
1. **`POST /device/code` accepts `client_id`** -- Validates that `client_id` exists in `OIDC_CLIENTS`. Stores `client_id` on the `DeviceAuthRequest` record (new column). Rejects unknown client IDs.
|
|
37
|
+
|
|
38
|
+
2. **`POST /device/poll` returns a JWT on approval** -- When status is `approved`, generates a JWT with `sub: user_id`, `aud: client_id` (from the stored device request), `iss: account.weldedanvil.com`, and standard `iat`/`exp`. Returns it as `access_token` alongside `status` and `user_id`.
|
|
39
|
+
|
|
40
|
+
The `DeviceAuthRequest` model gets one new field:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
client_id: String(64) -- stored from the initial code request
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
No other wa-auth changes needed.
|
|
47
|
+
|
|
48
|
+
## Changes to prompt-registry
|
|
49
|
+
|
|
50
|
+
One new endpoint:
|
|
51
|
+
|
|
52
|
+
**`POST /api/v1/auth/device-token`** -- Exchanges a wa-auth JWT for a `pk_*` API key.
|
|
53
|
+
|
|
54
|
+
- Reads JWT from `Authorization: Bearer <jwt>` header
|
|
55
|
+
- Validates with `verify_account_token()` (existing function)
|
|
56
|
+
- Calls `create_or_get_user(sub, email)` to resolve the user
|
|
57
|
+
- Reads `device_name` from the request body
|
|
58
|
+
- Deletes any existing `UserApiKey` with the same `user_id` and `name` (auto-revoke)
|
|
59
|
+
- Creates a new `UserApiKey` with `generate_api_key()`, stores the hash
|
|
60
|
+
- Returns the plaintext key, user email, and vault status
|
|
61
|
+
|
|
62
|
+
Vault status is determined by checking if a vault record exists for the user. If it does, returns `encrypted_metadata`. If not, `vault.available` is false.
|
|
63
|
+
|
|
64
|
+
No changes to existing endpoints or middleware.
|
|
65
|
+
|
|
66
|
+
## Changes to pc-cli
|
|
67
|
+
|
|
68
|
+
### `src/commands/login.js` -- Rewritten login flow
|
|
69
|
+
|
|
70
|
+
- Calls wa-auth `POST /device/code` with `client_id`
|
|
71
|
+
- Polls wa-auth `POST /device/poll`
|
|
72
|
+
- On approval, exchanges JWT at prompt-registry `POST /api/v1/auth/device-token` with device name
|
|
73
|
+
- Stores API key, vault key, and account URL in config
|
|
74
|
+
- Warns if vault not available
|
|
75
|
+
|
|
76
|
+
### `src/lib/config.js` -- New config fields
|
|
77
|
+
|
|
78
|
+
- `accountUrl` -- wa-auth base URL (default `https://account.weldedanvil.com`)
|
|
79
|
+
- `vaultKey` -- encrypted vault metadata from login (empty string default)
|
|
80
|
+
- `vaultAvailable` -- boolean, whether capture is enabled
|
|
81
|
+
|
|
82
|
+
### `src/lib/crypto.js` -- New file
|
|
83
|
+
|
|
84
|
+
- Derives AES-256-GCM key from vault metadata
|
|
85
|
+
- `encryptPrompt(plaintext, key)` returns `{ encrypted_content, content_iv }`
|
|
86
|
+
- Uses Node.js built-in `crypto` module (no new dependencies)
|
|
87
|
+
|
|
88
|
+
### Hook scripts -- Updated capture payload
|
|
89
|
+
|
|
90
|
+
`hooks/prompt-capture.js`, `codex-capture.js`, `gemini-capture.js`:
|
|
91
|
+
|
|
92
|
+
- Check `vaultAvailable` before attempting capture
|
|
93
|
+
- Encrypt prompt content before sending
|
|
94
|
+
- Send `{ encrypted_content, content_iv, context_hash }` instead of plaintext
|
|
95
|
+
|
|
96
|
+
### `src/lib/api.js` -- No changes
|
|
97
|
+
|
|
98
|
+
Already sends `Authorization: Bearer <key>` which works with `pk_*` keys.
|
|
99
|
+
|
|
100
|
+
## Error Handling
|
|
101
|
+
|
|
102
|
+
| Scenario | Behavior |
|
|
103
|
+
|----------|----------|
|
|
104
|
+
| Expired JWT during key exchange | 401 from prompt-registry, CLI says re-run `pc login` |
|
|
105
|
+
| Vault not set up | Login succeeds with warning. Hooks silently skip capture. `pc save` shows actionable error. |
|
|
106
|
+
| Vault set up after login | Re-run `pc login` to pick up vault key |
|
|
107
|
+
| API key revoked from web UI | 401 on API calls, existing "Not logged in" error |
|
|
108
|
+
| wa-auth down during login | Connection error, no partial state saved |
|
|
109
|
+
| prompt-registry down during exchange | JWT expires, re-run `pc login` |
|
|
110
|
+
|
|
111
|
+
## Out of Scope
|
|
112
|
+
|
|
113
|
+
- Search index encryption from CLI (web UI concern)
|
|
114
|
+
- `pc push` decryption (separate effort)
|
|
115
|
+
- Vault creation from CLI (stays in web UI)
|
|
116
|
+
- Token refresh (API keys don't expire)
|
|
117
|
+
- Migration of existing plaintext captures
|
|
118
|
+
- Changes to `pc setup` (hook registration unchanged)
|