@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 CHANGED
@@ -11,7 +11,7 @@ npm install -g @promptcellar/pc
11
11
  ## Quick Start
12
12
 
13
13
  ```bash
14
- # Login with your API key
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 with your API key
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 AfterAgent hook
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.3.1');
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)