@cortexkit/opencode-anthropic-auth 1.21.0 → 1.23.0

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.
Files changed (68) hide show
  1. package/README.md +57 -12
  2. package/dist/billing-lineage.d.ts +51 -0
  3. package/dist/claustrum-enrollment-registry.d.ts +4 -0
  4. package/dist/cli.d.ts +2 -1
  5. package/dist/cli.js +89 -6
  6. package/dist/custody-dimensions.d.ts +36 -0
  7. package/dist/custody-enrollment.d.ts +14 -0
  8. package/dist/custody-live.d.ts +111 -0
  9. package/dist/custody-manifest-watcher.d.ts +16 -0
  10. package/dist/custody-mode.d.ts +186 -0
  11. package/dist/effort-history.d.ts +62 -0
  12. package/dist/fable-fallback.d.ts +6 -7
  13. package/dist/{index-w9d41yy9.js → index-0jfsxyn9.js} +1 -1
  14. package/dist/index-1fq7h6yd.js +430 -0
  15. package/dist/{index-y0khnbt6.js → index-1gr8v4fn.js} +1 -1
  16. package/dist/index-797pmamn.js +2 -0
  17. package/dist/{index-1db17bc2.js → index-8y2t4mwz.js} +2 -2
  18. package/dist/index-a5d6v3ba.js +5 -0
  19. package/dist/index-n96q8pks.js +2 -0
  20. package/dist/index-xxhyhp98.js +2 -0
  21. package/dist/index-xzbhzz0a.js +2 -0
  22. package/dist/index-z7gfdyv3.js +1 -0
  23. package/dist/index.js +28 -21
  24. package/dist/local-login.d.ts +64 -0
  25. package/dist/{logger-ftk4bg3p.js → logger-kk9vyem9.js} +1 -1
  26. package/dist/prime-manager-registry.d.ts +5 -1
  27. package/dist/rpc/notifications.d.ts +3 -3
  28. package/dist/rpc/port-file.js +1 -1
  29. package/dist/rpc/protocol.d.ts +23 -1
  30. package/dist/rpc/protocol.js +1 -1
  31. package/dist/rpc/rpc-client.d.ts +1 -1
  32. package/dist/rpc/rpc-client.js +1 -1
  33. package/dist/rpc/rpc-dir.js +1 -1
  34. package/dist/rpc/server-registry.d.ts +10 -0
  35. package/dist/setup/activation.d.ts +7 -0
  36. package/dist/setup/claustrum.d.ts +26 -0
  37. package/dist/setup/command-runner.d.ts +2 -0
  38. package/dist/setup/command.d.ts +8 -0
  39. package/dist/setup/detect.d.ts +5 -0
  40. package/dist/setup/jsonc.d.ts +9 -0
  41. package/dist/setup/opencode-config.d.ts +18 -0
  42. package/dist/setup/paths.d.ts +15 -0
  43. package/dist/setup/pi.d.ts +8 -0
  44. package/dist/setup/process-fence.d.ts +14 -0
  45. package/dist/setup/types.d.ts +37 -0
  46. package/dist/sidebar-state.d.ts +5 -2
  47. package/dist/sidebar-state.js +1 -1
  48. package/dist/transform.d.ts +17 -1
  49. package/dist/tui/command-dialogs.d.ts +28 -9
  50. package/dist/tui-preferences.js +2 -2
  51. package/package.json +10 -5
  52. package/src/rpc/notifications.ts +28 -24
  53. package/src/rpc/protocol.ts +34 -1
  54. package/src/rpc/rpc-client.ts +1 -1
  55. package/src/rpc/rpc-server.ts +18 -5
  56. package/src/rpc/server-registry.ts +61 -0
  57. package/src/sidebar-state.ts +42 -3
  58. package/src/tui/command-dialogs.tsx +301 -97
  59. package/src/tui-compiled/rpc/protocol.ts +34 -1
  60. package/src/tui-compiled/rpc/rpc-client.ts +1 -1
  61. package/src/tui-compiled/sidebar-state.ts +42 -3
  62. package/src/tui-compiled/tui/command-dialogs.tsx +240 -70
  63. package/src/tui-compiled/tui.tsx +8 -3
  64. package/src/tui.tsx +18 -3
  65. package/dist/index-4nz423bt.js +0 -4
  66. package/dist/index-4x20896x.js +0 -2
  67. package/dist/index-wezdyxst.js +0 -2
  68. package/dist/index-yskwj5e6.js +0 -428
package/README.md CHANGED
@@ -32,27 +32,39 @@ This repo is a Bun workspace monorepo with two user-facing integrations and one
32
32
  - **Cache keepalive**: use `/claude-cachekeep always` or `/claude-cachekeep HH-HH` to pre-warm hybrid cache anchors for active sessions before the 1-hour TTL expires.
33
33
  - **Quota window priming**: opt in with `/claude-prime on` to start each 5-hour quota window about one minute after it resets instead of waiting for the next normal prompt.
34
34
  - **Lane start (OpenCode only)**: use `/claude-start` to fire one synthetic, one-token turn through the current session's normal model, agent, variant, quota, routing, cache, and request pipeline.
35
- - **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models.
36
- - **Adaptive reasoning visibility**: request summarized adaptive thinking for Claude Fable 5, Mythos 5, and Opus 5. OpenCode receives native `low`, `medium`, `high`, `xhigh`, and `max` Opus 5 effort variants rather than legacy manual-thinking budgets.
37
- - **Fable/Opus 5 safety fallback**: eligible OAuth requests try Anthropic's server-side safety fallback first. The plugin preserves Anthropic's fallback conversation boundary across OpenCode history and automatically starts its deterministic 10-response Opus 4.8 recovery if the response still ends in refusal. The TUI sidebar and OpenCode Desktop report the active target model and restoration. Set `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy` to bypass the server policy and use client-side recovery exclusively.
35
+ - **Fast mode toggle**: use `/claude-fast on|off` to request Anthropic fast mode for supported Opus models (`claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, and `claude-opus-5-5`).
36
+ - **Adaptive reasoning visibility**: request summarized adaptive thinking for Claude Fable 5/5.1, Mythos 5/5.1, Opus 5, and Opus 5.5. OpenCode exposes native `low`, `medium`, `high`, `xhigh`, and `max` effort variants for Fable 5.1, Opus 5, and Opus 5.5. On Opus 5.5, thinking is always on (adaptive summarized) per the model specification. OAuth Fable 5.1 sessions can change effort between turns without rewriting the cached prefix.
37
+ - **Fable/Opus 5 and 5.5 safety fallback**: eligible OAuth requests try Anthropic's server-side safety fallback first. The plugin preserves Anthropic's fallback conversation boundary across OpenCode history and automatically starts its deterministic 10-response Opus 4.8 recovery if the response still ends in refusal. The TUI sidebar and OpenCode Desktop report the active target model and restoration. Set `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy` to bypass the server policy and use client-side recovery exclusively.
38
38
  - **Live quota visibility**: use `/claude-quota` to see main and fallback quota state, reset times, and refresh errors.
39
39
  - **Host-local quota feed**: optionally publish sanitized response-header quota observations so another local CortexKit process can reuse fresh account state without polling Anthropic's rate-limited usage endpoint.
40
40
  - **Killswitch**: per-account hard-block thresholds that stop requests before hitting Anthropic's rate limits, with synthetic 429 retry-after when all accounts are exhausted.
41
41
  - **User-owned Cloudflare relay**: optionally provision your own Worker relay to reduce repeated client upload bytes for large OpenCode or Pi requests.
42
- - **Claude-compatible request hardening**: final-body billing signing, safer token refresh persistence, replay-safe fallback retries, and subagent cache isolation.
42
+ - **Claude-compatible request hardening**: Claude Code 2.1.258 identity and final-body billing signing, user-turn prompt and request lineage across tool loops, live-session-stable billing suffixes, Fable 5.1 thinking-prefix recovery, safer token refresh persistence, replay-safe fallback retries, and subagent cache isolation.
43
43
 
44
44
  ## What these integrations do
45
45
 
46
46
  - Let OpenCode and Pi use Claude Pro/Max OAuth credentials instead of an Anthropic API key.
47
47
  - In OpenCode, intercept the final Anthropic request and rewrite it into the Claude-compatible shape expected by Anthropic OAuth access.
48
48
  - In Pi, replace Pi's built-in Anthropic provider with a CortexKit provider override that uses the same Claude-compatible request path.
49
- - Add Claude billing headers with stable `cc_version` and body-derived `cch` signing.
49
+ - Add Claude billing headers with stable `cc_version`, user-turn `cc_prompt_id`/`cc_prev_req` lineage, and body-derived `cch` signing.
50
50
  - Support fallback Claude accounts stored in a local per-agent sidecar file.
51
51
  - Keep fallback OAuth tokens fresh in the background.
52
52
  - Apply quota thresholds before routing to main or fallback accounts.
53
53
  - Add `/claude-cache`, `/claude-cachekeep`, `/claude-prime`, `/claude-start`, `/claude-fast`, `/claude-quota`, and `/claude-dump` commands.
54
54
  - Optionally relay large requests through a Cloudflare Worker owned by the user.
55
55
 
56
+ ## Setup wizard (recommended)
57
+
58
+ Run the interactive setup wizard:
59
+
60
+ ```bash
61
+ bunx @cortexkit/opencode-anthropic-auth setup
62
+ ```
63
+
64
+ The wizard detects OpenCode, Pi, and Claustrum, configures plugin entries, and optionally activates Claustrum custody.
65
+
66
+ ---
67
+
56
68
  ## Install
57
69
 
58
70
  ### OpenCode
@@ -184,6 +196,9 @@ Example:
184
196
  "quotaHeaderFeed": {
185
197
  "enabled": false
186
198
  },
199
+ "claustrum": {
200
+ "accounts": {}
201
+ },
187
202
  "killswitch": {
188
203
  "enabled": false,
189
204
  "main": { "five_hour": 5, "seven_day": 10, "scoped": 0 },
@@ -205,6 +220,9 @@ Example:
205
220
  "claudeFast": {
206
221
  "enabled": false
207
222
  },
223
+ "thinkingBinding": {
224
+ "prefixMismatchBehavior": "account-default"
225
+ },
208
226
  "costZeroing": {
209
227
  "enabled": true
210
228
  },
@@ -221,8 +239,12 @@ Example:
221
239
 
222
240
  The `routing` block controls `/claude-routing`, `claudeCache` controls `/claude-cache`, `cacheKeep` controls `/claude-cachekeep`, and `claudeFast` controls `/claude-fast`. OpenCode zeroes Anthropic OAuth model costs by default because OAuth usage is quota-based; set `costZeroing.enabled` to `false` only if you want OpenCode to display the provider's model pricing instead. Set `quota.showToasts` to `true` to opt into OpenCode quota toast notifications after quota refreshes. The `main` field identifies OpenCode's primary auth entry; Pi keeps primary OAuth credentials in Pi's own credential store, but uses the same sidecar shape for CortexKit settings and fallback account labels.
223
241
 
242
+ `thinkingBinding.prefixMismatchBehavior` controls replayed signed/redacted thinking on OAuth Fable 5.1 requests. `account-default` (the default) sends no explicit prefix-mismatch override. Use `error` to make prefix changes fail deterministically during compaction testing, or `drop_block` to ask Anthropic to discard mismatched thinking blocks. The beta header is added only when an explicit behavior is configured and replayable thinking is present.
243
+
224
244
  `quotaHeaderFeed.enabled` is an OpenCode-only, restart-required opt-in. It publishes only allowlisted quota-window values, an opaque account reference, an observation timestamp, and the configured OAuth-account count; it never publishes tokens, raw headers, request bodies, model IDs, or refresh errors. Per-process lease files use owner-only permissions under `$TMPDIR/opencode-anthropic-auth/quota-header-feed`, expire after three minutes, and can be redirected with `OPENCODE_ANTHROPIC_AUTH_QUOTA_FEED_DIR`.
225
245
 
246
+ `claustrum.accounts` remains loadable only so configurations can safely downgrade to older releases; it does not gate custody per account. Capability handles are bearer credentials and must not be copied into the public config file.
247
+
226
248
  Runtime data is stored separately in `anthropic-auth-state.json`: fallback OAuth tokens, API-route keys, token refresh backoff, quota snapshots, and quota API backoff. Sticky session assignments use `anthropic-auth-routing-state.json` and store only SHA-256 hashes of session IDs. Background refresh and quota checks write only runtime state, so editing `anthropic-auth.json` does not get overwritten by another running plugin instance.
227
249
 
228
250
  ## OpenCode lane-start setting
@@ -234,7 +256,7 @@ Fallback accounts are separate Claude OAuth accounts or Anthropic-compatible API
234
256
 
235
257
  Use `/claude-routing fallback-first` to prefer usable fallback accounts before the main account, `/claude-routing main-first` to restore the default, or `/claude-routing sticky-balanced` to allocate each new session according to spendable 5-hour, 7-day, and matching model-scoped OAuth quota headroom. The command persists `routing.mode` and takes effect on the next request without restarting. `/claude-routing reset` clears only the current session's assignment so its next request is allocated again.
236
258
 
237
- `sticky-balanced` normalizes quota headroom by time until reset and tracks weighted initial-prompt deficit until the next quota refresh. Assignments are persisted by hashed session ID in `anthropic-auth-routing-state.json`, shared safely across OpenCode plugin instances and restarts. Active hybrid CacheKeep sessions seed their current route when the mode is first enabled.
259
+ `sticky-balanced` normalizes quota headroom by time until reset and tracks weighted initial-prompt deficit until the next quota refresh. Assignments are persisted by hashed session ID in `anthropic-auth-routing-state.json`, shared safely across OpenCode plugin instances and restarts. Active hybrid CacheKeep sessions seed their current route when the mode is first enabled. Changing the user-selected model discards the old assignment and its model-specific CacheKeep preference before quota-based reselection; transparent safety-recovery model changes retain the original account.
238
260
 
239
261
  Affinity survives network/relay failures, 5xx responses, unconfirmed 429s, quota-probe failures, and relative quota changes. Confirmed 7-day or matching model-scoped exhaustion migrates a session. Confirmed 5-hour exhaustion migrates only when more than 15 minutes remain; otherwise the plugin retains the route and returns `Retry-After`. Fable content-filter recovery keeps Fable, Opus 4.8, and Fable prewarms on the same sticky account. Direct Opus sessions first prefer an otherwise usable account whose Fable quota is exhausted.
240
262
 
@@ -269,9 +291,21 @@ Fallback retries are only attempted when the request body is safely replayable.
269
291
 
270
292
  ### Token refresh
271
293
 
272
- Fallback OAuth tokens refresh in the background so idle accounts do not expire before they are needed. Refresh token rotation is persisted immediately. The plugin also re-reads the latest sidecar account before refreshing, which avoids using stale refresh-token snapshots when multiple background paths run close together.
294
+ Fallback OAuth tokens refresh in the background so idle accounts do not expire before they are needed. Refresh token rotation is persisted immediately. The plugin also re-reads the latest sidecar account before refreshing, which avoids using stale refresh-token snapshots when multiple background paths run close together. Persisted refresh backoffs are bound to the refresh token that produced them, so replacing a rejected credential immediately clears its stale latch.
295
+
296
+ If Anthropic reports `invalid_grant`, that account must be logged in again. `/claude-account reset-backoff` manually clears the main account's refresh backoff and its matching quota backoff.
297
+
298
+ ### Claustrum manifest service
299
+
300
+ In global Claustrum mode, `/claude-account claustrum` serves OAuth routes that have a binding present in the Claustrum handle manifest; `/claude-account local` returns authority to local OAuth refresh. The vault serves credentials, and the plugin discovers only its own provider block.
273
301
 
274
- If Anthropic reports `invalid_grant`, that fallback account must be logged in again.
302
+ The request path reads only a resident in-memory credential. Startup warming and periodic reconciliation perform vault I/O and keep idle credentials refreshed. In Claustrum mode, a cold or unavailable vault must return a typed provider-unavailable response; it does not fall back to the sidecar credential path. That cold-route behavior lands with the dedicated cold-route task. Vault-served 401 reports carry the exact record version and response provenance, including relay-stream 401s, so a sidecar-served failure cannot invalidate a healthy vault credential. `/claude-account` and the account modal show manifest-binding presence, current vault service, and vault reauthentication state without exposing capability handles.
303
+
304
+ After a new Anthropic credential is explicitly bound to `anthropic-auth`, a running plugin watches the manifest and automatically creates its secret-free routing row; the same reconciliation runs during startup. Enrollment requires an exact live `credential.get` match for both the manifest credential ID and provider account UUID, persists only a custody tombstone plus identity metadata, and immediately refreshes the new route's quota so sticky-balanced routing does not wait for the next background interval. Existing disabled rows remain disabled. To stop routing without changing the manifest, disable the account; removing a manifest-bound row is refused until its binding is removed.
305
+
306
+ Claustrum mode also starts the future scoped-discovery enrollment ceremony under the host identity `anthropic-auth-opencode`. The plugin writes the request secret before proposing, shares one locked ceremony across every OpenCode project process, and displays the pending request ID and exact approval command in `/claude-account`. Approval is persisted atomically as an owner-only token at `$XDG_STATE_HOME/cortexkit/anthropic-auth/opencode-enrollment.json` (default `~/.local/state/cortexkit/anthropic-auth/opencode-enrollment.json`). Grant that identity only `category:anthropic-native`; never grant the broader `llm-provider` category. This release does not spend the enrollment token yet: model requests continue through the existing capability-handle manifest until the scoped-discovery cutover lands. `/claude-account enrollment-reset` retries only a denied or locally blocked ceremony and refuses to discard pending or approved authority.
307
+
308
+ If Claustrum has replaced the main host credential with its provider-bound tombstone, the plugin rejects refresh locally without contacting Anthropic or persisting a permanent `invalid_grant` state. API-key routes are unaffected.
275
309
 
276
310
  ## Quota-aware routing
277
311
 
@@ -302,7 +336,7 @@ Reset times are rendered as relative durations, such as `resets in 10m` or `rese
302
336
 
303
337
  ## Safety fallback
304
338
 
305
- Eligible Fable 5 and Opus 5 OAuth requests try Anthropic's server-side safety fallback first. The plugin sends `fallbacks: "default"` with Anthropic's server-side fallback beta, preserves fallback conversation boundaries in OpenCode history, and reports model handoffs and restoration in the TUI sidebar or OpenCode Desktop. Follow-up requests may remain on Anthropic's selected fallback model for approximately one hour.
339
+ Eligible Fable 5/5.1 and Opus 5 OAuth requests try Anthropic's server-side safety fallback first. The plugin sends `fallbacks: "default"` with Anthropic's server-side fallback beta, preserves fallback conversation boundaries in OpenCode history, and reports model handoffs and restoration in the TUI sidebar or OpenCode Desktop. Follow-up requests may remain on Anthropic's selected fallback model for approximately one hour.
306
340
 
307
341
  If an eligible OAuth response still ends in refusal, CortexKit automatically starts its deterministic 10-successful-response Opus 4.8 recovery, prewarms the selected source model without the server-fallback opt-in, and then restores that model. If the refused response already completed a tool call, the plugin preserves that call and continues with its existing tool result instead of replaying the tool.
308
342
 
@@ -314,7 +348,7 @@ To bypass Anthropic's server policy and use deterministic client recovery from t
314
348
  OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE=legacy opencode
315
349
  ```
316
350
 
317
- Legacy mode routes a refused Fable 5 or Opus 5 session through Opus 4.8 for 10 successful responses, prewarms the selected source model's cache, and then restores that model without first opting into Anthropic's server policy. Unset the variable and restart OpenCode to return to the default server-first policy with the same client recovery as a backstop.
351
+ Legacy mode routes a refused Fable 5/5.1 or Opus 5 session through Opus 4.8 for 10 successful responses, prewarms the selected source model's cache, and then restores that model without first opting into Anthropic's server policy. Unset the variable and restart OpenCode to return to the default server-first policy with the same client recovery as a backstop.
318
352
 
319
353
  ## Killswitch
320
354
 
@@ -520,7 +554,7 @@ Both OpenCode and Pi packages can persistently request Anthropic fast mode for s
520
554
  /claude-fast off
521
555
  ```
522
556
 
523
- When enabled, supported requests add `speed: "fast"` to the Anthropic JSON body and include the `fast-mode-2026-02-01` beta header. Unsupported models are left at standard speed. Anthropic currently documents fast mode for `claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`; Claude Fable 5 and Mythos 5 are not fast-mode models.
557
+ When enabled, supported requests add `speed: "fast"` to the Anthropic JSON body and include the `fast-mode-2026-02-01` beta header. Unsupported models are left at standard speed. Anthropic currently documents fast mode for `claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`; Claude Fable and Mythos 5/5.1 are not fast-mode models.
524
558
 
525
559
  Fast and standard speeds do not share prompt-cache prefixes, so switching this setting can cause cache misses.
526
560
 
@@ -648,14 +682,25 @@ Dump state is persisted in the active sidecar config as `dump.enabled` (`~/.conf
648
682
  | Variable | Description |
649
683
  | --- | --- |
650
684
  | `ANTHROPIC_BASE_URL` | Override the Anthropic API endpoint. Must be HTTP(S). |
685
+ | `ANTHROPIC_CUSTOM_HEADERS` | Add proxy-specific headers on API-key and proxy routes. Authentication, protocol, framing, and internal correlation headers cannot be overridden. Ignored for OAuth requests. |
686
+ | `ANTHROPIC_MODEL` | Default proxy alias for any `claude-*` model. Ignored for OAuth requests. |
687
+ | `ANTHROPIC_DEFAULT_SONNET_MODEL` | Proxy alias for `claude-sonnet-*` models. |
688
+ | `ANTHROPIC_DEFAULT_OPUS_MODEL` | Proxy alias for `claude-opus-*` models. |
689
+ | `ANTHROPIC_DEFAULT_HAIKU_MODEL` | Proxy alias for `claude-haiku-*` models. |
690
+ | `ANTHROPIC_DEFAULT_FABLE_MODEL` | Proxy alias for `claude-fable-*` and `claude-mythos-*` models. |
651
691
  | `ANTHROPIC_INSECURE` | Set to `1` or `true` to skip TLS verification when `ANTHROPIC_BASE_URL` is set. |
652
692
  | `OPENCODE_ANTHROPIC_AUTH_FILE` | Override the OpenCode sidecar config path. |
693
+ | `OPENCODE_ANTHROPIC_AUTH_CLAUSTRUM_ENROLLMENT_FILE` | Override the owner-only OpenCode Claustrum enrollment-token path. |
653
694
  | `OPENCODE_ANTHROPIC_AUTH_FALLBACK_MODE` | Set to `legacy` to bypass Anthropic's server policy and use deterministic 10-response client recovery exclusively. The default tries server-side safety fallback first and uses client recovery as a backstop. |
654
695
  | `PI_ANTHROPIC_AUTH_FILE` | Override the Pi sidecar config path. |
655
696
  | `PI_AGENT_DIR` | Override Pi's agent directory when deriving the default sidecar path. |
656
697
  | `CLOUDFLARE_API_TOKEN` | Cloudflare token used by `bunx @cortexkit/opencode-anthropic-auth relay setup`. Not stored. |
657
698
  | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account ID used by relay setup. |
658
699
 
700
+ `ANTHROPIC_CUSTOM_HEADERS` and the model-alias variables apply only to API-key and proxy routes. OAuth requests keep the Claude Code header and model identity unchanged. Custom headers accept either a JSON object or comma/newline-separated `name: value` entries. A configuration containing an invalid or protected header is ignored as a whole with one redacted warning; it cannot replace route authentication, Anthropic protocol headers, body framing, or plugin-internal correlation headers.
701
+
702
+ An `ANTHROPIC_BASE_URL` path is preserved. For example, `https://proxy.example/anthropic` sends requests to `/anthropic/v1/messages`, while a versioned base such as `https://proxy.example/anthropic/v2` sends to `/anthropic/v2/messages`. Existing version segments are not duplicated, and unrelated paths ending in `/messages` are not rewritten.
703
+
659
704
  ## Request rewriting
660
705
 
661
706
  For Claude Pro/Max OAuth requests, the plugin works at the final Anthropic wire-request layer:
@@ -666,7 +711,7 @@ For Claude Pro/Max OAuth requests, the plugin works at the final Anthropic wire-
666
711
  4. Prepends Claude Code identity and billing-header blocks.
667
712
  5. Rewrites cache controls according to `/claude-cache` mode.
668
713
  6. Renames MCP tool names into Claude-compatible PascalCase form.
669
- 7. Opts eligible Fable 5 and Opus 5 OAuth requests into Anthropic's server-side safety fallback, restores stored fallback boundaries, and activates replay-safe client recovery if the response still refuses.
714
+ 7. Opts eligible Fable 5/5.1 and Opus 5 OAuth requests into Anthropic's server-side safety fallback, restores stored fallback boundaries, and activates replay-safe client recovery if the response still refuses.
670
715
  8. Computes final-body `cch` over the fully serialized request body.
671
716
 
672
717
  The sanitizer is anchor-based: it removes paragraphs containing known OpenCode documentation or source anchors, performs a small set of inline replacements, and preserves the rest of the prompt including user/project instructions, tool policy, environment context, and file paths.
@@ -0,0 +1,51 @@
1
+ export declare const BILLING_LINEAGE_REQUEST_HEADER = "x-cortexkit-billing-lineage";
2
+ interface MessageLike {
3
+ info?: {
4
+ id?: unknown;
5
+ sessionID?: unknown;
6
+ sessionId?: unknown;
7
+ role?: unknown;
8
+ agent?: unknown;
9
+ synthetic?: unknown;
10
+ ignored?: unknown;
11
+ };
12
+ parts?: Array<{
13
+ type?: unknown;
14
+ text?: unknown;
15
+ synthetic?: unknown;
16
+ ignored?: unknown;
17
+ metadata?: Record<string, unknown>;
18
+ }>;
19
+ }
20
+ export interface BillingLineageContext {
21
+ sessionId: string;
22
+ messageId: string;
23
+ promptId: string;
24
+ previousRequestId?: string;
25
+ requestToken: string;
26
+ generation: number;
27
+ sequence: number;
28
+ }
29
+ export interface BillingLineageTrackerOptions {
30
+ createPromptId?: () => string;
31
+ createRequestToken?: () => string;
32
+ maxPrompts?: number;
33
+ maxRequests?: number;
34
+ maxSessions?: number;
35
+ }
36
+ export declare function extractAnthropicRequestId(headers: Headers): string | undefined;
37
+ export declare class BillingLineageTracker {
38
+ #private;
39
+ constructor(options?: BillingLineageTrackerOptions);
40
+ observeMessages(messages: MessageLike[]): void;
41
+ markHeaders(input: {
42
+ sessionId: string;
43
+ messageId: string;
44
+ agent?: string;
45
+ headers: Record<string, string>;
46
+ }): boolean;
47
+ resolveHeader(value: string | null | undefined): BillingLineageContext | undefined;
48
+ commit(context: BillingLineageContext | undefined, requestId: string | undefined): boolean;
49
+ clearSession(sessionId: string): void;
50
+ }
51
+ export {};
@@ -0,0 +1,4 @@
1
+ import { adoptClaustrumEnrollment as adoptSharedEnrollment } from '@cortexkit/anthropic-auth-core';
2
+ export type { ClaustrumEnrollmentAdoption } from '@cortexkit/anthropic-auth-core';
3
+ export declare function getOpenCodeClaustrumEnrollmentPaths(env?: NodeJS.ProcessEnv, cwd?: string): import("@cortexkit/anthropic-auth-core").ClaustrumEnrollmentPaths;
4
+ export declare function adoptClaustrumEnrollment(options: Omit<Parameters<typeof adoptSharedEnrollment>[0], 'proposedName'>): import("@cortexkit/anthropic-auth-core").ClaustrumEnrollmentAdoption;
package/dist/cli.d.ts CHANGED
@@ -40,4 +40,5 @@ export interface ApiAddDeps {
40
40
  prompt?: (message: string) => Promise<string>;
41
41
  }
42
42
  export declare function addApiRoute(labelArg?: string, deps?: ApiAddDeps): Promise<void>;
43
- export {};
43
+ import { runSetupCommand } from './setup/command.ts';
44
+ export { runSetupCommand };
package/dist/cli.js CHANGED
@@ -1,14 +1,97 @@
1
1
  #!/usr/bin/env node
2
- import{G as z,H as N,I as P,K as L,Ka as U,M as E,P as K,R as S,mc as T,oc as q}from"./index-yskwj5e6.js";import"./index-4nz423bt.js";import{Zc as w}from"./index-4x20896x.js";import{stdin as x,stdout as v}from"node:process";import{createInterface as y}from"node:readline/promises";function R(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function _(){console.log(`Usage:
2
+ import{a as NF,d as PF,g as IF}from"./index-797pmamn.js";import{$d as e1,Aa as LF,Ba as UF,G as YF,H as KF,Ia as kF,Ka as DF,Ld as w0,Na as e,Nd as o1,Oa as HF,Pd as l0,Rd as s1,Sa as q0,U as MF,Vd as t1,Wa as VF,Wf as wF,Ya as n1,_a as bF,_f as hF,dc as F1}from"./index-1fq7h6yd.js";import"./index-a5d6v3ba.js";import{nh as _3}from"./index-xxhyhp98.js";import{th as u0,uh as xF,vh as z0}from"./index-n96q8pks.js";var A0=xF((Df,L1)=>{var C0={to(F,f){if(!f)return`\x1B[${F+1}G`;return`\x1B[${f+1};${F+1}H`},move(F,f){let $="";if(F<0)$+=`\x1B[${-F}D`;else if(F>0)$+=`\x1B[${F}C`;if(f<0)$+=`\x1B[${-f}A`;else if(f>0)$+=`\x1B[${f}B`;return $},up:(F=1)=>`\x1B[${F}A`,down:(F=1)=>`\x1B[${F}B`,forward:(F=1)=>`\x1B[${F}C`,backward:(F=1)=>`\x1B[${F}D`,nextLine:(F=1)=>"\x1B[E".repeat(F),prevLine:(F=1)=>"\x1B[F".repeat(F),left:"\x1B[G",hide:"\x1B[?25l",show:"\x1B[?25h",save:"\x1B7",restore:"\x1B8"},yF={up:(F=1)=>"\x1B[S".repeat(F),down:(F=1)=>"\x1B[T".repeat(F)},aF={screen:"\x1B[2J",up:(F=1)=>"\x1B[1J".repeat(F),down:(F=1)=>"\x1B[J".repeat(F),line:"\x1B[2K",lineEnd:"\x1B[K",lineStart:"\x1B[1K",lines(F){let f="";for(let $=0;$<F;$++)f+=this.line+($<F-1?C0.up():"");if(F)f+=C0.left;return f}};L1.exports={cursor:C0,scroll:yF,erase:aF,beep:"\x07"}});import{stdin as s3,stdout as t3}from"node:process";import{createInterface as e3}from"node:readline/promises";import{styleText as m0}from"node:util";import{stdout as p0,stdin as H1}from"node:process";import*as a from"node:readline";import TF from"node:readline";var f1=(()=>{let F=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;return(f)=>{let $=0;F.lastIndex=0;while(F.test(f))$+=1;return f.length-$}})(),$1=(F)=>{return F===12288||F>=65281&&F<=65376||F>=65504&&F<=65510},J1=(F)=>{return F===8987||F===9001||F>=12272&&F<=12287||F>=12289&&F<=12350||F>=12441&&F<=12543||F>=12549&&F<=12591||F>=12593&&F<=12686||F>=12688&&F<=12771||F>=12783&&F<=12830||F>=12832&&F<=12871||F>=12880&&F<=19903||F>=65040&&F<=65049||F>=65072&&F<=65106||F>=65108&&F<=65126||F>=65128&&F<=65131||F>=127488&&F<=127490||F>=127504&&F<=127547||F>=127552&&F<=127560||F>=131072&&F<=196605||F>=196608&&F<=262141};var OF=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,BF=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,j1=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,gF=/\t{1,1000}/y,W1=/[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu,CF=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,AF=/\p{M}+/gu,RF={limit:1/0,ellipsis:""},Z1=(F,f={},$={})=>{let J=f.limit??1/0,j=f.ellipsis??"",W=f?.ellipsisWidth??(j?Z1(j,RF,$).width:0),Q=0,Z=$.controlWidth??0,z=$.tabWidth??8,Y=$.emojiWidth??2,X=2,G=$.regularWidth??1,K=$.wideWidth??2,L=[[CF,G],[OF,0],[BF,Z],[gF,z],[W1,Y],[j1,K]],M=0,k=0,h=F.length,E=0,B=!1,O=h,U=Math.max(0,J-W),D=0,v=0,P=0,H=0;F:while(!0){if(v>D||k>=h&&k>M){let V=F.slice(D,v)||F.slice(M,k);E=0;for(let m of V.replaceAll(AF,"")){let C=m.codePointAt(0)||0;if($1(C))H=2;else if(J1(C))H=K;else H=G;if(P+H>U)O=Math.min(O,Math.max(D,M)+E);if(P+H>J){B=!0;break F}E+=m.length,P+=H}D=v=0}if(k>=h)break F;for(let V=0,m=L.length;V<m;V++){let[C,G0]=L[V];if(C.lastIndex=k,C.test(F)){if(E=C===j1?f1(F.slice(k,C.lastIndex)):C===W1?1:C.lastIndex-k,H=E*G0,P+H>U)O=Math.min(O,k+Math.floor((U-P)/G0));if(P+H>J){B=!0;break F}P+=H,D=M,v=k,k=M=C.lastIndex;continue F}}k+=1}return{width:B?U:P,index:B?O:h,truncated:B,ellipsed:B&&J>=W}},Q1=Z1;var mF={limit:1/0,ellipsis:"",ellipsisWidth:0},SF=(F,f={})=>{return Q1(F,mF,f).width},R=SF;var K0="\x1B",X1="›",uF=39,B0="\x07",K1="[",pF="]",M1="m",g0=`${pF}8;;`,z1=new RegExp(`(?:\\${K1}(?<code>\\d+)m|\\${g0}(?<uri>.*)${B0})`,"y"),Y1=(F)=>{if(F>=30&&F<=37)return 39;if(F>=90&&F<=97)return 39;if(F>=40&&F<=47)return 49;if(F>=100&&F<=107)return 49;if(F===1||F===2)return 22;if(F===3)return 23;if(F===4)return 24;if(F===7)return 27;if(F===8)return 28;if(F===9)return 29;if(F===0)return 0;return},q1=(F)=>`${K0}${K1}${F}${M1}`,G1=(F)=>`${K0}${g0}${F}${B0}`,O0=(F,f,$)=>{let J=f[Symbol.iterator](),j=!1,W=!1,Q=F.at(-1),Z=Q===void 0?0:R(Q),z=J.next(),Y=J.next(),X=0;while(!z.done){let G=z.value,K=R(G);if(Z+K<=$)F[F.length-1]+=G;else F.push(G),Z=0;if(G===K0||G===X1)j=!0,W=f.startsWith(g0,X+1);if(j){if(W){if(G===B0)j=!1,W=!1}else if(G===M1)j=!1}else if(Z+=K,Z===$&&!Y.done)F.push(""),Z=0;z=Y,Y=J.next(),X+=G.length}if(Q=F.at(-1),!Z&&Q!==void 0&&Q.length&&F.length>1)F[F.length-2]+=F.pop()},dF=(F)=>{let f=F.split(" "),$=f.length;while($){if(R(f[$-1]))break;$--}if($===f.length)return F;return f.slice(0,$).join(" ")+f.slice($).join("")},cF=(F,f,$={})=>{if($.trim!==!1&&F.trim()==="")return"";let J="",j,W,Q=F.split(" "),Z=[""],z=0;for(let G=0;G<Q.length;G++){let K=Q[G];if($.trim!==!1){let M=Z.at(-1)??"",k=M.trimStart();if(M.length!==k.length)Z[Z.length-1]=k,z=R(k)}if(G!==0){if(z>=f&&($.wordWrap===!1||$.trim===!1))Z.push(""),z=0;if(z||$.trim===!1)Z[Z.length-1]+=" ",z++}let L=R(K);if($.hard&&L>f){let M=f-z,k=1+Math.floor((L-M-1)/f);if(Math.floor((L-1)/f)<k)Z.push("");O0(Z,K,f),z=R(Z.at(-1)??"");continue}if(z+L>f&&z&&L){if($.wordWrap===!1&&z<f){O0(Z,K,f),z=R(Z.at(-1)??"");continue}Z.push(""),z=0}if(z+L>f&&$.wordWrap===!1){O0(Z,K,f),z=R(Z.at(-1)??"");continue}Z[Z.length-1]+=K,z+=L}if($.trim!==!1)Z=Z.map((G)=>dF(G));let Y=Z.join(`
3
+ `),X=!1;for(let G=0;G<Y.length;G++){let K=Y[G];if(J+=K,!X){if(X=K>="\uD800"&&K<="\uDBFF",X)continue}else X=!1;if(K===K0||K===X1){z1.lastIndex=G+1;let M=z1.exec(Y)?.groups;if(M?.code!==void 0){let k=Number.parseFloat(M.code);j=k===uF?void 0:k}else if(M?.uri!==void 0)W=M.uri.length===0?void 0:M.uri}if(Y[G+1]===`
4
+ `){if(W)J+=G1("");let L=j?Y1(j):void 0;if(j&&L)J+=q1(L)}else if(K===`
5
+ `){if(j&&Y1(j))J+=q1(j);if(W)J+=G1(W)}}return J},_F=/\r?\n/;function c(F,f,$){return String(F).normalize().split(_F).map((J)=>cF(J,f,$)).join(`
6
+ `)}var A=u0(A0(),1);import{ReadStream as U1}from"node:tty";function o(F,f,$){if(!$.some((Q)=>!Q.disabled))return F;let J=F+f,j=Math.max($.length-1,0),W=J<0?j:J>j?0:J;return $[W]?.disabled?o(W,f<0?-1:1,$):W}function M0(F,f,$,J){let j=J.split(`
7
+ `),W=0,Q=F;for(let z of j){if(Q<=z.length)break;Q-=z.length+1,W++}for(W=Math.max(0,Math.min(j.length-1,W+$)),Q=Math.min(Q,j[W].length)+f;Q<0&&W>0;)W--,Q+=j[W].length+1;for(;Q>j[W].length&&W<j.length-1;)Q-=j[W].length+1,W++;Q=Math.max(0,Math.min(j[W].length,Q));let Z=0;for(let z=0;z<W;z++)Z+=j[z].length+1;return Z+Q}var iF=["up","down","left","right","space","enter","cancel"],lF=["January","February","March","April","May","June","July","August","September","October","November","December"],g={actions:new Set(iF),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"},withGuide:!0,accessible:void 0,date:{monthNames:[...lF],messages:{required:"Please enter a valid date",invalidMonth:"There are only 12 months in a year",invalidDay:(F,f)=>`There are only ${F} days in ${f}`,afterMin:(F)=>`Date must be on or after ${F.toISOString().slice(0,10)}`,beforeMax:(F)=>`Date must be on or before ${F.toISOString().slice(0,10)}`}}};function rF(F){if(F!==void 0)return F;if(g.accessible!==void 0)return g.accessible;let f=process.env.ACCESSIBLE;return f!==void 0&&f!==""&&f!=="0"&&f!=="false"}function d0(F,f){if(typeof F=="string")return g.aliases.get(F)===f;for(let $ of F)if($!==void 0&&d0($,f))return!0;return!1}function nF(F,f){if(F===f)return;let $=F.split(`
8
+ `),J=f.split(`
9
+ `),j=Math.max($.length,J.length),W=[];for(let Q=0;Q<j;Q++)$[Q]!==J[Q]&&W.push(Q);return{lines:W,numLinesBefore:$.length,numLinesAfter:J.length,numLines:j}}var oF=globalThis.process.platform.startsWith("win"),k0=Symbol("clack:cancel");function s(F){return F===k0}function L0(F,f){let $=F;$.isTTY&&$.setRawMode(f)}function V1({input:F=H1,output:f=p0,overwrite:$=!0,hideCursor:J=!0}={}){let j=a.createInterface({input:F,output:f,prompt:"",tabSize:1});a.emitKeypressEvents(F,j),F instanceof U1&&F.isTTY&&F.setRawMode(!0);let W=(Q,{name:Z,sequence:z})=>{let Y=String(Q);if(d0([Y,Z,z],"cancel")){J&&f.write(A.cursor.show),process.exit(0);return}if(!$)return;a.moveCursor(f,Z==="return"?0:-1,Z==="return"?-1:0,()=>{a.clearLine(f,1,()=>{F.once("keypress",W)})})};return J&&f.write(A.cursor.hide),F.once("keypress",W),()=>{F.off("keypress",W),J&&f.write(A.cursor.show),F instanceof U1&&F.isTTY&&!oF&&F.setRawMode(!1),j.terminal=!1,j.close()}}var f0=(F)=>("columns"in F)&&typeof F.columns=="number"?F.columns:80,c0=(F)=>("rows"in F)&&typeof F.rows=="number"?F.rows:20;function $0(F,f,$,J=$,j=$,W){let Q=f0(F??p0);return c(f,Q-$.length,{hard:!0,trim:!1}).split(`
10
+ `).map((Z,z,Y)=>{let X=W?W(Z,z):Z;return z===0?`${J}${X}`:z===Y.length-1?`${j}${X}`:`${$}${X}`}).join(`
11
+ `)}function b1(F,f){if("~standard"in F){let $=F["~standard"].validate(f);return $ instanceof Promise?$.then((J)=>J.issues?.at(0)?.message):$.issues?.at(0)?.message}return F(f)}class l{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";get accessible(){return rF(this.opts.accessible)}constructor(F,f=!0){let{input:$=H1,output:J=p0,render:j,signal:W,...Q}=F;this.opts=Q,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=j.bind(this),this._track=f,this._abortSignal=W,this.input=$,this.output=J}unsubscribe(){this._subscribers.clear()}setSubscriber(F,f){let $=this._subscribers.get(F)??[];$.push(f),this._subscribers.set(F,$)}on(F,f){this.setSubscriber(F,{cb:f})}once(F,f){this.setSubscriber(F,{cb:f,once:!0})}emit(F,...f){let $=this._subscribers.get(F)??[],J=[];for(let j of $)j.cb(...f),j.once&&J.push(()=>$.splice($.indexOf(j),1));for(let j of J)j()}prompt(){return new Promise((F)=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),F(k0);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=TF.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0&&this._setUserInput(this.opts.initialUserInput,!0),this.input.on("keypress",this.onKeypress),L0(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(A.cursor.show),this.output.off("resize",this.render),L0(this.input,!1),F(this.value)}),this.once("cancel",()=>{this.output.write(A.cursor.show),this.output.off("resize",this.render),L0(this.input,!1),F(k0)})})}_isActionKey(F,f){return F==="\t"}_shouldSubmit(F,f){return!0}_setValue(F){this.value=F,this.emit("value",this.value)}_setUserInput(F,f){this.userInput=F??"",this.emit("userInput",this.userInput),f&&this._track&&this.rl&&(this.rl.write(this.userInput),this._cursor=this.rl.cursor)}_clearUserInput(){this.rl?.write(null,{ctrl:!0,name:"u"}),this._setUserInput("")}async onKeypress(F,f){if(this.state!=="validating"){if(this._track&&f.name!=="return"&&(f.name&&this._isActionKey(F,f)&&this.rl?.write(null,{ctrl:!0,name:"h"}),this._cursor=this.rl?.cursor??0,this._setUserInput(this.rl?.line)),this.state==="error"&&(this.state="active"),f?.name&&(!this._track&&g.aliases.has(f.name)&&this.emit("cursor",g.aliases.get(f.name)),g.actions.has(f.name)&&this.emit("cursor",f.name)),F&&(F.toLowerCase()==="y"||F.toLowerCase()==="n")&&this.emit("confirm",F.toLowerCase()==="y"),this.emit("key",F,f),f?.name==="return"&&this._shouldSubmit(F,f)){if(this.opts.validate){let $=b1(this.opts.validate,this.value),J;$ instanceof Promise?(this.state="validating",this.render(),J=await $):J=$,J&&(this.error=J instanceof Error?J.message:J,this.state="error",this.rl?.write(this.userInput))}this.state!=="error"&&(this.state="submit")}d0([F,f?.name,f?.sequence],"cancel")&&(this.state="cancel"),(this.state==="submit"||this.state==="cancel")&&this.emit("finalize"),this.render(),(this.state==="submit"||this.state==="cancel")&&this.close()}}close(){this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
12
+ `),L0(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){let F=c(this._prevFrame,process.stdout.columns,{hard:!0,trim:!1}).split(`
13
+ `).length-1;this.output.write(A.cursor.move(-999,F*-1))}render(){let F=c(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(F!==this._prevFrame){if(this.state==="initial")this.output.write(A.cursor.hide);else{let f=nF(this._prevFrame,F),$=c0(this.output);if(this.restoreCursor(),f){let J=Math.max(0,f.numLinesAfter-$),j=Math.max(0,f.numLinesBefore-$),W=f.lines.find((Q)=>Q>=J);if(W===void 0){this._prevFrame=F;return}if(f.lines.length===1){this.output.write(A.cursor.move(0,W-j)),this.output.write(A.erase.lines(1));let Q=F.split(`
14
+ `);this.output.write(Q[W]),this._prevFrame=F,this.output.write(A.cursor.move(0,Q.length-W-1));return}else if(f.lines.length>1){if(J<j)W=J;else{let Z=W-j;Z>0&&this.output.write(A.cursor.move(0,Z))}this.output.write(A.erase.down());let Q=F.split(`
15
+ `).slice(W);this.output.write(Q.join(`
16
+ `)),this._prevFrame=F;return}}this.output.write(A.erase.down())}this.output.write(F),this.state==="initial"&&(this.state="active"),this._prevFrame=F}}}function sF(F,f){if(F===void 0||f.length===0)return 0;let $=f.findIndex((J)=>J.value===F);return $!==-1?$:0}function tF(F,f){return(f.label??String(f.value)).toLowerCase().includes(F.toLowerCase())}function eF(F,f){if(f)return F?f:f[0]}var F3=class extends l{filteredOptions;multiple;isNavigating=!1;selectedValues=[];focusedValue;#f=0;#j="";#F;#Z;#Q;#$;get cursor(){return this.#f}get userInputWithCursor(){if(!this.userInput)return m0(["inverse","hidden"],"_");if(this._cursor>=this.userInput.length)return`${this.userInput}█`;let f=this.userInput.slice(0,this.cursor),$=this.userInput.slice(this.cursor,this.cursor+1),J=this.userInput.slice(this.cursor+1);return`${f}${m0("inverse",$)}${J}`}get options(){return typeof this.#Z=="function"?this.#Z():this.#Z}constructor(f){super(f),this.#Z=f.options,this.#Q=f.placeholder,this.#$=f.completeOnTab===!0;let $=this.options;this.filteredOptions=[...$],this.multiple=f.multiple===!0,this.#F=typeof f.options=="function"?f.filter:f.filter??tF;let J;if(f.initialValue&&Array.isArray(f.initialValue)?this.multiple?J=f.initialValue:J=f.initialValue.slice(0,1):!this.multiple&&this.options.length>0&&(J=[this.options[0]?.value]),J)for(let j of J){let W=$.findIndex((Q)=>Q.value===j);W!==-1&&(this.toggleSelected(j),this.#f=W)}this.focusedValue=this.options[this.#f]?.value,this.on("key",(j,W)=>this.#J(j,W)),this.on("userInput",(j)=>this.#W(j))}_isActionKey(f,$){return f==="\t"||this.multiple&&this.isNavigating&&$.name==="space"&&f!==void 0&&f!==""}#J(f,$){let J=$.name==="up",j=$.name==="down",W=$.name==="return",Q=this.userInput===""||this.userInput==="\t",Z=this.#Q,z=this.options,Y=Z!==void 0&&Z!==""&&z.some((X)=>!X.disabled&&(this.#F?this.#F(Z,X):!0));if($.name==="tab"&&Q&&Y){this.userInput==="\t"&&this._clearUserInput(),this._setUserInput(Z,!0),this.isNavigating=!1;return}if($.name==="tab"&&this.#$&&!this.multiple&&this.focusedValue!==void 0){let X=String(this.focusedValue);this._clearUserInput(),this._setUserInput(X,!0),this.isNavigating=!1;return}J||j?(this.#f=o(this.#f,J?-1:1,this.filteredOptions),this.focusedValue=this.filteredOptions[this.#f]?.value,this.multiple||(this.selectedValues=[this.focusedValue]),this.isNavigating=!0):W?this.value=eF(this.multiple,this.selectedValues):this.multiple?this.focusedValue!==void 0&&($.name==="tab"||this.isNavigating&&$.name==="space")?this.toggleSelected(this.focusedValue):this.isNavigating=!1:(this.focusedValue&&(this.selectedValues=[this.focusedValue]),this.isNavigating=!1)}deselectAll(){this.selectedValues=[]}toggleSelected(f){this.filteredOptions.length!==0&&(this.multiple?this.selectedValues.includes(f)?this.selectedValues=this.selectedValues.filter(($)=>$!==f):this.selectedValues=[...this.selectedValues,f]:this.selectedValues=[f])}#W(f){if(f!==this.#j){this.#j=f;let $=this.options;f&&this.#F?this.filteredOptions=$.filter((W)=>this.#F?.(f,W)):this.filteredOptions=[...$];let J=sF(this.focusedValue,this.filteredOptions);this.#f=o(J,0,this.filteredOptions);let j=this.filteredOptions[this.#f];j&&!j.disabled?this.focusedValue=j.value:this.focusedValue=void 0,this.multiple||(this.focusedValue!==void 0?this.toggleSelected(this.focusedValue):this.deselectAll())}}};class _0 extends l{get cursor(){return this.value?0:1}get _value(){return this.cursor===0}constructor(F){super(F,!1),this.value=!!F.initialValue,this.on("userInput",()=>{this.value=this._value}),this.on("confirm",(f)=>{this.output.write(A.cursor.move(0,-1)),this.value=f,this.state="submit",this.close()}),this.on("cursor",()=>{this.value=!this.value})}}var f3={Y:{type:"year",len:4},M:{type:"month",len:2},D:{type:"day",len:2}};function k1(F){return[...F].map((f)=>f3[f])}function $3(F){let f=new Intl.DateTimeFormat(F,{year:"numeric",month:"2-digit",day:"2-digit"}).formatToParts(new Date(2000,0,15)),$=[],J="/";for(let j of f)j.type==="literal"?J=j.value.trim()||j.value:(j.type==="year"||j.type==="month"||j.type==="day")&&$.push({type:j.type,len:j.type==="year"?4:2});return{segments:$,separator:J}}function R0(F){return Number.parseInt((F||"0").replace(/_/g,"0"),10)||0}function U0(F){return{year:R0(F.year),month:R0(F.month),day:R0(F.day)}}function S0(F,f){return new Date(F||2001,f||1,0).getDate()}function w1(F){let{year:f,month:$,day:J}=U0(F);if(!f||f<0||f>9999||!$||$<1||$>12||!J||J<1)return;let j=new Date(Date.UTC(f,$-1,J));if(!(j.getUTCFullYear()!==f||j.getUTCMonth()!==$-1||j.getUTCDate()!==J))return{year:f,month:$,day:J}}function D1(F){let f=w1(F);return f?new Date(Date.UTC(f.year,f.month-1,f.day)):void 0}function J3(F,f,$,J){let j=$?{year:$.getUTCFullYear(),month:$.getUTCMonth()+1,day:$.getUTCDate()}:null,W=J?{year:J.getUTCFullYear(),month:J.getUTCMonth()+1,day:J.getUTCDate()}:null;return F==="year"?{min:j?.year??1,max:W?.year??9999}:F==="month"?{min:j&&f.year===j.year?j.month:1,max:W&&f.year===W.year?W.month:12}:{min:j&&f.year===j.year&&f.month===j.month?j.day:1,max:W&&f.year===W.year&&f.month===W.month?W.day:S0(f.year,f.month)}}class h1 extends l{#f;#j;#F;#Z;#Q;#$={segmentIndex:0,positionInSegment:0};#J=!0;#W=null;inlineError="";get segmentCursor(){return{...this.#$}}get segmentValues(){return{...this.#F}}get segments(){return this.#f}get separator(){return this.#j}get formattedValue(){return this.#G(this.#F)}#G(F){return this.#f.map((f)=>F[f.type]).join(this.#j)}#z(){this._setUserInput(this.#G(this.#F)),this._setValue(D1(this.#F)??void 0)}constructor(F){let f=F.format?{segments:k1(F.format),separator:F.separator??"/"}:$3(F.locale),$=F.separator??f.separator,J=F.format?k1(F.format):f.segments,j=F.initialValue??F.defaultValue,W=j?{year:String(j.getUTCFullYear()).padStart(4,"0"),month:String(j.getUTCMonth()+1).padStart(2,"0"),day:String(j.getUTCDate()).padStart(2,"0")}:{year:"____",month:"__",day:"__"},Q=J.map((Z)=>W[Z.type]).join($);super({...F,initialUserInput:Q},!1),this.#f=J,this.#j=$,this.#F=W,this.#Z=F.minDate,this.#Q=F.maxDate,this.#z(),this.on("cursor",(Z)=>this.#M(Z)),this.on("key",(Z,z)=>this.#L(Z,z)),this.on("finalize",()=>this.#U(F))}#Y(){let F=Math.max(0,Math.min(this.#$.segmentIndex,this.#f.length-1)),f=this.#f[F];if(f)return this.#$.positionInSegment=Math.max(0,Math.min(this.#$.positionInSegment,f.len-1)),{segment:f,index:F}}#q(F){this.inlineError="",this.#W=null;let f=this.#Y();f&&(this.#$.segmentIndex=Math.max(0,Math.min(this.#f.length-1,f.index+F)),this.#$.positionInSegment=0,this.#J=!0)}#X(F){let f=this.#Y();if(!f)return;let{segment:$}=f,J=this.#F[$.type],j=!J||J.replace(/_/g,"")==="",W=Number.parseInt((J||"0").replace(/_/g,"0"),10)||0,Q=J3($.type,U0(this.#F),this.#Z,this.#Q),Z;j?Z=F===1?Q.min:Q.max:Z=Math.max(Math.min(Q.max,W+F),Q.min),this.#F={...this.#F,[$.type]:Z.toString().padStart($.len,"0")},this.#J=!0,this.#W=null,this.#z()}#M(F){if(F)switch(F){case"right":return this.#q(1);case"left":return this.#q(-1);case"up":return this.#X(1);case"down":return this.#X(-1)}}#L(F,f){if(f?.name==="backspace"||f?.sequence===""||f?.sequence==="\b"||F===""||F==="\b"){this.inlineError="";let $=this.#Y();if(!$)return;if(!this.#F[$.segment.type].replace(/_/g,"")){this.#q(-1);return}this.#F[$.segment.type]="_".repeat($.segment.len),this.#J=!0,this.#$.positionInSegment=0,this.#z();return}if(f?.name==="tab"){this.inlineError="";let $=this.#Y();if(!$)return;let J=f.shift?-1:1,j=$.index+J;j>=0&&j<this.#f.length&&(this.#$.segmentIndex=j,this.#$.positionInSegment=0,this.#J=!0);return}if(F&&/^[0-9]$/.test(F)){let $=this.#Y();if(!$)return;let{segment:J}=$,j=!this.#F[J.type].replace(/_/g,"");if(this.#J&&this.#W!==null&&!j){let K=this.#W+F,L={...this.#F,[J.type]:K},M=this.#K(L,J);if(M){this.inlineError=M,this.#W=null,this.#J=!1;return}this.inlineError="",this.#F[J.type]=K,this.#W=null,this.#J=!1,this.#z(),$.index<this.#f.length-1&&(this.#$.segmentIndex=$.index+1,this.#$.positionInSegment=0,this.#J=!0);return}this.#J&&!j&&(this.#F[J.type]="_".repeat(J.len),this.#$.positionInSegment=0),this.#J=!1,this.#W=null;let W=this.#F[J.type],Q=W.indexOf("_"),Z=Q>=0?Q:Math.min(this.#$.positionInSegment,J.len-1);if(Z<0||Z>=J.len)return;let z=W.slice(0,Z)+F+W.slice(Z+1),Y=!1;if(Z===0&&W==="__"&&(J.type==="month"||J.type==="day")){let K=Number.parseInt(F,10);z=`0${F}`,Y=K<=(J.type==="month"?1:2)}if(J.type==="year"&&(z=(W.replace(/_/g,"")+F).padStart(J.len,"_")),!z.includes("_")){let K={...this.#F,[J.type]:z},L=this.#K(K,J);if(L){this.inlineError=L;return}}this.inlineError="",this.#F[J.type]=z;let X=z.includes("_")?void 0:w1(this.#F);if(X){let{year:K,month:L}=X,M=S0(K,L);this.#F={year:String(Math.max(0,Math.min(9999,K))).padStart(4,"0"),month:String(Math.max(1,Math.min(12,L))).padStart(2,"0"),day:String(Math.max(1,Math.min(M,X.day))).padStart(2,"0")}}this.#z();let G=z.indexOf("_");Y?(this.#J=!0,this.#W=F):G>=0?this.#$.positionInSegment=G:Q>=0&&$.index<this.#f.length-1?(this.#$.segmentIndex=$.index+1,this.#$.positionInSegment=0,this.#J=!0):this.#$.positionInSegment=Math.min(Z+1,J.len-1)}}#K(F,f){let{month:$,day:J}=U0(F);if(f.type==="month"&&($<0||$>12))return g.date.messages.invalidMonth;if(f.type==="day"&&(J<0||J>31))return g.date.messages.invalidDay(31,"any month")}#U(F){let{year:f,month:$,day:J}=U0(this.#F);if(f&&$&&J){let j=S0(f,$);this.#F={...this.#F,day:String(Math.min(J,j)).padStart(2,"0")}}this.value=D1(this.#F)??F.defaultValue??void 0}}var j3=class extends l{options;cursor=0;#f;getGroupItems(f){return this.options.filter(($)=>$.group===f)}isGroupSelected(f){let $=this.getGroupItems(f),J=this.value;return J===void 0?!1:$.every((j)=>J.includes(j.value))}toggleValue(){let f=this.options[this.cursor];if(f!==void 0)if(this.value===void 0&&(this.value=[]),f.group===!0){let $=f.value,J=this.getGroupItems($);this.isGroupSelected($)?this.value=this.value.filter((j)=>J.findIndex((W)=>W.value===j)===-1):this.value=[...this.value,...J.map((j)=>j.value)],this.value=Array.from(new Set(this.value))}else{let $=this.value.includes(f.value);this.value=$?this.value.filter((J)=>J!==f.value):[...this.value,f.value]}}constructor(f){super(f,!1);let{options:$}=f;this.#f=f.selectableGroups!==!1,this.options=Object.entries($).flatMap(([J,j])=>[{value:J,group:!0,label:J},...j.map((W)=>({...W,group:J}))]),this.value=[...f.initialValues??[]],this.cursor=Math.max(this.options.findIndex(({value:J})=>J===f.cursorAt),this.#f?0:1),this.on("cursor",(J)=>{switch(J){case"left":case"up":{this.cursor=this.cursor===0?this.options.length-1:this.cursor-1;let j=this.options[this.cursor]?.group===!0;!this.#f&&j&&(this.cursor=this.cursor===0?this.options.length-1:this.cursor-1);break}case"down":case"right":{this.cursor=this.cursor===this.options.length-1?0:this.cursor+1;let j=this.options[this.cursor]?.group===!0;!this.#f&&j&&(this.cursor=this.cursor===this.options.length-1?0:this.cursor+1);break}case"space":this.toggleValue();break}})}},W3=new Set(["up","down","left","right"]);class N1 extends l{#f=!1;#j;focused="editor";get userInputWithCursor(){if(this.state==="submit")return this.userInput;let F=this.userInput;if(this.cursor>=F.length)return`${F}█`;let f=F.slice(0,this.cursor),$=F.slice(this.cursor,this.cursor+1),J=F.slice(this.cursor+1);return $===`
17
+ `?`${f}█
18
+ ${J}`:`${f}${m0("inverse",$)}${J}`}get cursor(){return this._cursor}#F(F){if(this.userInput.length===0){this._setUserInput(F);return}this._setUserInput(this.userInput.slice(0,this.cursor)+F+this.userInput.slice(this.cursor))}#Z(F){let f=this.value??"";switch(F){case"up":this._cursor=M0(this._cursor,0,-1,f);return;case"down":this._cursor=M0(this._cursor,0,1,f);return;case"left":this._cursor=M0(this._cursor,-1,0,f);return;case"right":this._cursor=M0(this._cursor,1,0,f);return}}_shouldSubmit(F,f){if(this.#j)return this.focused==="submit"?!0:(this.#F(`
19
+ `),this._cursor++,!1);let $=this.#f;return this.#f=!0,$&&this.cursor===this.userInput.length?(this.userInput[this.cursor-1]===`
20
+ `&&(this._setUserInput(this.userInput.slice(0,this.cursor-1)+this.userInput.slice(this.cursor)),this._cursor--),!0):(this.#F(`
21
+ `),this._cursor++,!1)}constructor(F){let f=F.initialUserInput??F.initialValue;super({...F,initialUserInput:f},!1),f!==void 0&&(this._cursor=f.length),this.#j=F.showSubmit??!1,this.on("key",($,J)=>{if(J?.name&&W3.has(J.name)){this.#f=!1,this.#Z(J.name);return}if($==="\t"&&this.#j){this.focused=this.focused==="editor"?"submit":"editor";return}if(J?.name!=="return"){if(this.#f=!1,J?.name==="backspace"&&this.cursor>0){this._setUserInput(this.userInput.slice(0,this.cursor-1)+this.userInput.slice(this.cursor)),this._cursor--;return}if(J?.name==="delete"&&this.cursor<this.userInput.length){this._setUserInput(this.userInput.slice(0,this.cursor)+this.userInput.slice(this.cursor+1));return}$&&(this.#j&&this.focused==="submit"&&(this.focused="editor"),this.#F($??""),this._cursor++)}}),this.on("userInput",($)=>{this._setValue($)}),this.on("finalize",()=>{this.value||(this.value=F.defaultValue),this.value===void 0&&(this.value="")})}}class x0 extends l{options;cursor=0;get _value(){return this.options[this.cursor]?.value}get _enabledOptions(){return this.options.filter((F)=>F.disabled!==!0)}toggleAll(){let F=this._enabledOptions,f=this.value!==void 0&&this.value.length===F.length;this.value=f?[]:F.map(($)=>$.value)}toggleInvert(){let F=this.value;if(!F)return;let f=this._enabledOptions.filter(($)=>!F.includes($.value));this.value=f.map(($)=>$.value)}toggleValue(){this.value===void 0&&(this.value=[]);let F=this.value.includes(this._value);this.value=F?this.value.filter((f)=>f!==this._value):[...this.value,this._value]}constructor(F){super(F,!1),this.options=F.options,this.value=[...F.initialValues??[]];let f=Math.max(this.options.findIndex(({value:$})=>$===F.cursorAt),0);this.cursor=this.options[f]?.disabled?o(f,1,this.options):f,this.on("key",($,J)=>{J.name==="a"&&this.toggleAll(),J.name==="i"&&this.toggleInvert()}),this.on("cursor",($)=>{switch($){case"left":case"up":this.cursor=o(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=o(this.cursor,1,this.options);break;case"space":this.toggleValue();break}})}}import{styleText as q,stripVTControlCharacters as Cf}from"node:util";import p from"node:process";var j0=u0(A0(),1);function Z3(){if(p.platform!=="win32")return p.env.TERM!=="linux";return Boolean(p.env.CI)||Boolean(p.env.WT_SESSION)||Boolean(p.env.TERMINUS_SUBLIME)||p.env.ConEmuTask==="{cmd::Cmder}"||p.env.TERM_PROGRAM==="Terminus-Sublime"||p.env.TERM_PROGRAM==="vscode"||p.env.TERM==="xterm-256color"||p.env.TERM==="alacritty"||p.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var y0=Z3(),Q3=()=>process.env.CI==="true";var b=(F,f)=>y0?F:f,P1=b("◆","*"),A1=b("■","x"),R1=b("▲","x"),D0=b("◇","o"),z3=b("┌","T"),w=b("│","|"),W0=b("└","—"),Sf=b("┐","T"),uf=b("┘","—"),I1=b("●",">"),E1=b("○"," "),Y3=b("◻","[•]"),v1=b("◼","[+]"),O1=b("◻","[ ]"),pf=b("▪","•"),B1=b("─","-"),q3=b("╮","+"),G3=b("├","+"),X3=b("╯","+"),K3=b("╰","+"),df=b("╭","+"),M3=b("●","•"),L3=b("◆","*"),U3=b("▲","!"),k3=b("■","x"),m1=(F)=>{switch(F){case"initial":case"active":return q("cyan",P1);case"cancel":return q("red",A1);case"error":return q("yellow",R1);case"submit":return q("green",D0);case"validating":return q("dim",P1)}},D3=(F)=>{switch(F){case"initial":case"active":return q("cyan",w);case"cancel":return q("red",w);case"error":return q("yellow",w);case"submit":return q("green",w)}};function H3(F,f){let $=[`${f?`${q("cyan",w)} `:""}${F.join(" • ")}`];return f&&$.push(q("cyan",W0)),$}var g1=(F,f,$,J,j,W=!1)=>{let Q=f,Z=0;if(W)for(let z=J-1;z>=$;z--){let Y=F[z];if(Y&&(Q-=Y.length),Z++,Q<=j)break}else for(let z=$;z<J;z++){let Y=F[z];if(Y&&(Q-=Y.length),Z++,Q<=j)break}return{lineCount:Q,removals:Z}},C1=({cursor:F,options:f,style:$,output:J=process.stdout,maxItems:j=Number.POSITIVE_INFINITY,columnPadding:W=0,rowPadding:Q=4})=>{let Z=f0(J)-W,z=c0(J),Y=q("dim","..."),X=Math.max(z-Q,0),G=Math.max(Math.min(j,X),5),K=0;F>=G-3&&(K=Math.max(Math.min(F-G+3,f.length-G),0));let L=G<f.length&&K>0,M=G<f.length&&K+G<f.length,k=Math.min(K+G,f.length),h=[],E=0;L&&E++,M&&E++;let B=K+(L?1:0),O=k-(M?1:0);for(let D=B;D<O;D++){let v=f[D],P=v?$(v,D===F):"",H=c(P,Z,{hard:!0,trim:!1}).split(`
22
+ `);h.push(H),E+=H.length}if(E>X){let D=0,v=0,P=E,H=F-B,V=X,m=()=>g1(h,P,0,H,V),C=()=>g1(h,P,H+1,h.length,V,!0);L?({lineCount:P,removals:D}=m(),P>V&&(M||(V-=1),{lineCount:P,removals:v}=C())):(M||(V-=1),{lineCount:P,removals:v}=C(),P>V&&(V-=1,{lineCount:P,removals:D}=m())),D>0&&(L=!0,h.splice(0,D)),v>0&&(M=!0,h.splice(h.length-v,v))}let U=[];L&&U.push(Y);for(let D of h)for(let v of D)U.push(v);return M&&U.push(Y),U};var a0=(F)=>{let f=F.active??"Yes",$=F.inactive??"No";return new _0({active:f,inactive:$,signal:F.signal,input:F.input,output:F.output,initialValue:F.initialValue??!0,render(){let J=F.withGuide??g.withGuide,j=`${m1(this.state)} `,W=J?`${q("gray",w)} `:"",Q=$0(F.output,F.message,W,j),Z=`${J?`${q("gray",w)}
23
+ `:""}${Q}
24
+ `,z=this.value?f:$;switch(this.state){case"submit":{let Y=J?`${q("gray",w)} `:"";return`${Z}${Y}${q("dim",z)}`}case"cancel":{let Y=J?`${q("gray",w)} `:"";return`${Z}${Y}${q(["strikethrough","dim"],z)}${J?`
25
+ ${q("gray",w)}`:""}`}default:{let Y=J?`${q("cyan",w)} `:"",X=J?q("cyan",W0):"";return`${Z}${Y}${this.value?`${q("green",I1)} ${f}`:`${q("dim",E1)} ${q("dim",f)}`}${F.vertical?J?`
26
+ ${q("cyan",w)} `:`
27
+ `:` ${q("dim","/")} `}${this.value?`${q("dim",E1)} ${q("dim",$)}`:`${q("green",I1)} ${$}`}
28
+ ${X}
29
+ `}}}}).prompt()};var V3=[`${q("dim","↑/↓")} to navigate`,`${q("dim","Space:")} select`,`${q("dim","Enter:")} confirm`],J0=(F,f)=>F.split(`
30
+ `).map(($)=>f($)).join(`
31
+ `),S1=(F)=>{let f=(j,W)=>{let Q=j.label??String(j.value);return W==="disabled"?`${q("gray",O1)} ${J0(Q,(Z)=>q(["strikethrough","gray"],Z))}${j.hint?` ${q("dim",`(${j.hint??"disabled"})`)}`:""}`:W==="active"?`${q("cyan",Y3)} ${Q}${j.hint?` ${q("dim",`(${j.hint})`)}`:""}`:W==="selected"?`${q("green",v1)} ${J0(Q,(Z)=>q("dim",Z))}${j.hint?` ${q("dim",`(${j.hint})`)}`:""}`:W==="cancelled"?`${J0(Q,(Z)=>q(["strikethrough","dim"],Z))}`:W==="active-selected"?`${q("green",v1)} ${Q}${j.hint?` ${q("dim",`(${j.hint})`)}`:""}`:W==="submitted"?`${J0(Q,(Z)=>q("dim",Z))}`:`${q("dim",O1)} ${J0(Q,(Z)=>q("dim",Z))}`},$=F.required??!0,J=F.showInstructions??!0;return new x0({options:F.options,signal:F.signal,input:F.input,output:F.output,initialValues:F.initialValues,required:$,cursorAt:F.cursorAt,validate(j){if($&&(j===void 0||j.length===0))return`Please select at least one option.
32
+ ${q("reset",q("dim",`Press ${q(["gray","bgWhite","inverse"]," space ")} to select, ${q("gray",q("bgWhite",q("inverse"," enter ")))} to submit`))}`},render(){let j=F.withGuide??g.withGuide,W=$0(F.output,F.message,j?`${D3(this.state)} `:"",`${m1(this.state)} `),Q=`${j?`${q("gray",w)}
33
+ `:""}${W}
34
+ `,Z=this.value??[],z=(Y,X)=>{if(Y.disabled)return f(Y,"disabled");let G=Z.includes(Y.value);return X&&G?f(Y,"active-selected"):G?f(Y,"selected"):f(Y,X?"active":"inactive")};switch(this.state){case"submit":{let Y=this.options.filter(({value:G})=>Z.includes(G)).map((G)=>f(G,"submitted")).join(q("dim",", "))||q("dim","none"),X=$0(F.output,Y,j?`${q("gray",w)} `:"");return`${Q}${X}`}case"cancel":{let Y=this.options.filter(({value:G})=>Z.includes(G)).map((G)=>f(G,"cancelled")).join(q("dim",", "));if(Y.trim()==="")return`${Q}${q("gray",w)}`;let X=$0(F.output,Y,j?`${q("gray",w)} `:"");return`${Q}${X}${j?`
35
+ ${q("gray",w)}`:""}`}case"error":{let Y=j?`${q("yellow",w)} `:"",X=this.error.split(`
36
+ `).map((L,M)=>M===0?`${j?`${q("yellow",W0)} `:""}${q("yellow",L)}`:` ${L}`).join(`
37
+ `),G=Q.split(`
38
+ `).length,K=X.split(`
39
+ `).length+1;return`${Q}${Y}${C1({output:F.output,options:this.options,cursor:this.cursor,maxItems:F.maxItems,columnPadding:Y.length,rowPadding:G+K,style:z}).join(`
40
+ ${Y}`)}
41
+ ${X}
42
+ `}default:{let Y=j?`${q("cyan",w)} `:"",X=Q.split(`
43
+ `).length,G=J?H3(V3,j):j?[q("cyan",W0)]:[],K=G.join(`
44
+ `),L=G.length+1;return`${Q}${Y}${C1({output:F.output,options:this.options,cursor:this.cursor,maxItems:F.maxItems,columnPadding:Y.length,rowPadding:X+L,style:z}).join(`
45
+ ${Y}`)}
46
+ ${K}
47
+ `}}}}).prompt()};var N={message:(F=[],{symbol:f=q("gray",w),secondarySymbol:$=q("gray",w),output:J=process.stdout,spacing:j=1,withGuide:W}={})=>{let Q=[],Z=W??g.withGuide,z=Z?$:"",Y=Z?`${f} `:"",X=Z?`${$} `:"";for(let K=0;K<j;K++)Q.push(z);let G=Array.isArray(F)?F:F.split(`
48
+ `);if(G.length>0){let[K,...L]=G;K.length>0?Q.push(`${Y}${K}`):Q.push(Z?f:"");for(let M of L)M.length>0?Q.push(`${X}${M}`):Q.push(Z?$:"")}J.write(`${Q.join(`
49
+ `)}
50
+ `)},info:(F,f)=>{N.message(F,{...f,symbol:q("blue",M3)})},success:(F,f)=>{N.message(F,{...f,symbol:q("green",L3)})},step:(F,f)=>{N.message(F,{...f,symbol:q("green",D0)})},warn:(F,f)=>{N.message(F,{...f,symbol:q("yellow",U3)})},warning:(F,f)=>{N.warn(F,f)},error:(F,f)=>{N.message(F,{...f,symbol:q("red",k3)})}};var u1=(F="",f)=>{let $=f?.output??process.stdout,J=f?.withGuide??g.withGuide?`${q("gray",z3)} `:"";$.write(`${J}${F}
51
+ `)},y=(F="",f)=>{let $=f?.output??process.stdout,J=f?.withGuide??g.withGuide?`${q("gray",w)}
52
+ ${q("gray",W0)} `:"";$.write(`${J}${F}
53
+
54
+ `)};var b3=(F)=>F,w3=(F,f,$)=>{let J={hard:!0,trim:!1},j=c(F,f,J).split(`
55
+ `),W=j.reduce((z,Y)=>Math.max(R(Y),z),0),Q=j.map($).reduce((z,Y)=>Math.max(R(Y),z),0),Z=f-(Q-W);return c(F,Z,J)},p1=(F="",f="",$)=>{let J=$?.output??p.stdout,j=$?.withGuide??g.withGuide,W=$?.format??b3,Q=["",...w3(F,f0(J)-6,W).split(`
56
+ `).map(W),""],Z=R(f),z=Math.max(Q.reduce((K,L)=>{let M=R(L);return M>K?M:K},0),Z)+2,Y=Q.map((K)=>`${q("gray",w)} ${K}${" ".repeat(z-R(K))}${q("gray",w)}`).join(`
57
+ `),X=j?`${q("gray",w)}
58
+ `:"",G=j?G3:K3;J.write(`${X}${q("green",D0)} ${q("reset",f)} ${q("gray",B1.repeat(Math.max(z-Z-1,1))+q3)}
59
+ ${Y}
60
+ ${q("gray",G+B1.repeat(z+2)+X3)}
61
+ `)};var h3=(F)=>q("magenta",F),d1=({indicator:F="dots",onCancel:f,output:$=process.stdout,cancelMessage:J,errorMessage:j,frames:W=y0?["◒","◐","◓","◑"]:["•","o","O","0"],delay:Q=y0?80:120,signal:Z,...z}={})=>{let Y=Q3(),X,G,K=!1,L=!1,M="",k,h=performance.now(),E=f0($),B=z?.styleFrame??h3,O=(I)=>{let u=I>1?j??g.messages.error:J??g.messages.cancel;L=I===1,K&&(F0(u,I),L&&typeof f=="function"&&f())},U=()=>O(2),D=()=>O(1),v=()=>{process.on("uncaughtExceptionMonitor",U),process.on("unhandledRejection",U),process.on("SIGINT",D),process.on("SIGTERM",D),process.on("exit",O),Z&&Z.addEventListener("abort",D)},P=()=>{process.removeListener("uncaughtExceptionMonitor",U),process.removeListener("unhandledRejection",U),process.removeListener("SIGINT",D),process.removeListener("SIGTERM",D),process.removeListener("exit",O),Z&&Z.removeEventListener("abort",D)},H=()=>{if(k===void 0)return;Y&&$.write(`
62
+ `);let I=c(k,E,{hard:!0,trim:!1}).split(`
63
+ `);I.length>1&&$.write(j0.cursor.up(I.length-1)),$.write(j0.cursor.to(0)),$.write(j0.erase.down())},V=(I)=>I.replace(/\.+$/,""),m=(I)=>{let u=(performance.now()-I)/1000,_=Math.floor(u/60),x=Math.floor(u%60);return _>0?`[${_}m ${x}s]`:`[${x}s]`},C=z.withGuide??g.withGuide,G0=(I="")=>{K=!0,X=V1({output:$}),M=V(I),h=performance.now(),C&&$.write(`${q("gray",w)}
64
+ `);let u=0,_=0;v(),G=setInterval(()=>{if(Y&&M===k)return;H(),k=M;let x=B(W[u]),X0;if(Y)X0=`${x} ${M}...`;else if(F==="timer")X0=`${x} ${M} ${m(h)}`;else{let vF=".".repeat(Math.floor(_)).slice(0,3);X0=`${x} ${M}${vF}`}let EF=c(X0,E,{hard:!0,trim:!1});$.write(EF),u=u+1<W.length?u+1:0,_=_<4?_+0.125:0},Q)},F0=(I="",u=0,_=!1)=>{if(!K)return;K=!1,clearInterval(G),H();let x=u===0?q("green",D0):u===1?q("red",A1):q("red",R1);M=I??M,_||(F==="timer"?$.write(`${x} ${M} ${m(h)}
65
+ `):$.write(`${x} ${M}
66
+ `)),P(),X()};return{start:G0,stop:(I="")=>F0(I,0),message:(I="")=>{M=V(I??M)},cancel:(I="")=>F0(I,1),error:(I="")=>F0(I,2),clear:()=>F0("",0,!0),get isCancelled(){return L}}},cf={light:b("─","-"),heavy:b("━","="),block:b("█","#")};var _f=[`${q("dim","↑/↓")} to navigate`,`${q("dim","Enter:")} confirm`];var xf=`${q("gray",w)} `;import{existsSync as c1}from"node:fs";import{mkdir as O3,readFile as _1,rename as B3,unlink as g3,writeFile as C3}from"node:fs/promises";import{dirname as A3,join as R3}from"node:path";import{execFile as N3}from"node:child_process";import{promisify as P3}from"node:util";var I3=P3(N3),S={async run(F,f,$={}){try{let J=$.env?{...process.env,...$.env}:process.env,{stdout:j,stderr:W}=await I3(F,f,{env:J,timeout:$.timeoutMs??15000,maxBuffer:10485760});return{exitCode:0,stdout:typeof j==="string"?j:String(j),stderr:typeof W==="string"?W:String(W)}}catch(J){let j=J;return{exitCode:typeof j?.code==="number"?j.code:1,stdout:typeof j?.stdout==="string"?j.stdout:j?.stdout!=null?String(j.stdout):"",stderr:typeof j?.stderr==="string"?j.stderr:j?.stderr!=null?String(j.stderr):String(j?.message??J)}}}};var E3=/(?:^|\/)(opencode|litecode|opencode2|pi)(?:$|\s)/i;class H0 extends Error{activeHosts;constructor(F){let f=F.map(($)=>` - PID ${$.pid}: ${$.command.slice(0,80)}`).join(`
67
+ `);super(`OpenCode or Pi processes are currently running:
68
+ ${f}
69
+
70
+ Please quit all OpenCode, LiteCode, and Pi processes before running setup.`);this.activeHosts=F;this.name="ProcessFenceViolationError"}}function v3(F=S){return{async listRunningHosts(f){let $=process.platform==="win32",J=$?"tasklist":"ps",j=$?["/fo","csv","/nh"]:["-eo","pid,command"],W=await F.run(J,j,{env:f});if(W.exitCode!==0)throw Error(`Process inspection failed (${W.stderr.trim()||`exit ${W.exitCode}`})`);let Q=W.stdout.split(/\r?\n/),Z=[],z=process.pid;for(let Y of Q){let X=Y.trim();if(!X)continue;let G,K;if($){let L=X.split(",").map((M)=>M.replace(/^"|"$/g,""));if(L.length<2)continue;K=L[0]??"",G=Number(L[1])}else{let L=/^(\d+)\s+(.+)$/.exec(X);if(!L)continue;G=Number(L[1]),K=L[2]??""}if(!Number.isInteger(G)||G===z)continue;if(E3.test(K))Z.push({pid:G,command:K})}return Z}}}var Z0=v3(S);async function Q0(F=Z0){let f=await F.listRunningHosts();if(f.length>0)throw new H0(f)}var m3={type:"oauth",access:"",refresh:"claustrum-tombstone:v1:anthropic",expires:0};async function x1(F){let f=F.fence??Z0;await Q0(f);let $={},J=null;if(c1(F.authPath)){J=await _1(F.authPath,"utf8");try{if($=JSON.parse(J),typeof $!=="object"||$===null||Array.isArray($))$={}}catch{$={}}}$.anthropic=m3;let j=`${JSON.stringify($,null,2)}
71
+ `,W=A3(F.authPath);await O3(W,{recursive:!0,mode:448});let Q=R3(W,`.auth.${process.pid}.${Date.now()}.tmp`);await C3(Q,j,{mode:384});try{if(await Q0(f),J!==null&&c1(F.authPath)){if(await _1(F.authPath,"utf8")!==J)throw Error("OpenCode auth.json was modified concurrently during setup")}if(F.beforeRename)await F.beforeRename();await B3(Q,F.authPath)}catch(Z){throw await g3(Q).catch(()=>{}),Z}}import{join as c3}from"node:path";import{existsSync as S3,readFileSync as u3}from"node:fs";import{homedir as T0}from"node:os";import{dirname as y1,join as T,resolve as V0}from"node:path";import{fileURLToPath as p3}from"node:url";var i="@cortexkit/opencode-anthropic-auth",t="@cortexkit/pi-anthropic-auth";function r(F=process.env){let f=F.OPENCODE_CONFIG_DIR?.trim();if(f)return V0(f);let $=F.XDG_CONFIG_HOME?.trim();if($)return T(V0($),"opencode");return T(T0(),".config","opencode")}function d3(F=process.env){let f=F.XDG_DATA_HOME?.trim();if(f)return T(V0(f),"opencode");return T(T0(),".local","share","opencode")}function b0(F=process.env){return T(d3(F),"auth.json")}function n(F=process.env){let f=F.PI_CODING_AGENT_DIR?.trim()||F.PI_AGENT_DIR?.trim();if(f)return V0(f);return T(T0(),".pi","agent")}function i0(F=process.env){return T(n(F),"auth.json")}function a1(F){return`${i}@${F}`}function T1(F){return`${i}@${F}`}function i1(F){return`npm:${t}@${F}`}async function l1(){let F=y1(p3(import.meta.url));for(let f=0;f<5;f++){let $=T(F,"package.json");if(S3($))try{let j=JSON.parse(u3($,"utf8"));if(typeof j.version==="string"&&j.version.trim())return j.version.trim()}catch{}let J=y1(F);if(J===F)break;F=J}return"1.22.0"}var r1=300000;async function r0(F,f={}){let $=f.runner??S,J=f.ckBinary??"ck",j=f.env??process.env,W=`anthropic-auth-${F}`,Q=f.paths??s1(F,j),Z=await o1(Q,W),z=f.enrollmentClient,Y=!1,X=async()=>{if(!z)z=await w0(),Y=!0;return z};try{if(Z.state==="idle")Z=await new l0({client:await X(),paths:Q,proposedName:W}).reconcile();if(Z.state==="pending"&&Z.requestId){let k=await $.run(J,["auth","enroll","approve","--request-id",Z.requestId,"--name",W],{env:j});if(k.exitCode!==0)return{ok:!1,message:`Failed to approve enrollment via ck: ${k.stderr.trim()||`exit ${k.exitCode}`}`,discoveredAccounts:[]};Z=await new l0({client:await X(),paths:Q,proposedName:W}).reconcile()}if(Z.state!=="approved")return{ok:!1,message:`Enrollment did not complete (state: ${Z.state})`,discoveredAccounts:[]};let G=await $.run(J,["auth","grant","--principal",`enrolled:${W}`,"--selector-kind","category","--selector","anthropic-native","--operation","read"],{env:j});if(G.exitCode!==0)return{ok:!1,message:`Failed to grant anthropic-native access: ${G.stderr.trim()||`exit ${G.exitCode}`}`,discoveredAccounts:[]};let{readClaustrumEnrollmentToken:K}=await import("./index-z7gfdyv3.js"),L=await K(Q.tokenPath),M=f.scopedClient??await e1();try{let h=(await M.listScoped(L.token)).rows.filter((H)=>H.refreshAdapter==="anthropic"&&H.categories?.includes("anthropic-native")&&H.operations?.includes("read")&&H.state==="active");if(h.length===0)return{ok:!1,message:"No active Anthropic accounts found in Claustrum vault",discoveredAccounts:[]};let E=Date.now(),B=[];for(let H of h){let V;try{V=await M.getScoped({credentialId:H.id,enrollmentToken:L.token,minTtlMs:r1})}catch(m){let C=m instanceof Error?m.message:String(m);return{ok:!1,message:`Scoped retrieval failed for ${H.id}: ${C}`,discoveredAccounts:[]}}if(!V||typeof V.credentialId!=="string"||V.credentialId!==H.id||typeof V.accountId!=="string"||V.accountId!==H.accountId||typeof V.expiresAtMs!=="number"||!Number.isFinite(V.expiresAtMs)||V.expiresAtMs<E+r1||typeof V.material!=="string"||!V.material.trim())return{ok:!1,message:`Invalid or incomplete credential served for ${H.id}`,discoveredAccounts:[]};B.push({credentialId:H.id,accountId:V.accountId})}let O=F==="opencode"?r(j):n(j),U=c3(O,"anthropic-auth.json");await n1("claustrum",U);let{ClaustrumScopedCustody:D}=await import("./index-z7gfdyv3.js"),v=new D({client:M,readToken:async()=>L});if(!await t1({path:U,custody:v}))return{ok:!1,message:`Failed to commit scoped roster for ${F}`,discoveredAccounts:[]};return{ok:!0,message:`Configured Claustrum custody with ${B.length} Anthropic account(s)`,discoveredAccounts:B}}finally{if(!f.scopedClient)M.close?.()}}finally{if(Y)z?.close?.()}}import{existsSync as t0}from"node:fs";var d=u0(_3(),1);import{existsSync as x3,readFileSync as y3}from"node:fs";function Y0(F){if(!x3(F))return{value:null,text:"",exists:!1};let f=y3(F,"utf8"),$=[],J=d.parse(f,$,{allowTrailingComma:!0});return{value:$.length===0?J:null,text:f,exists:!0}}function n0(F,f,$,J){let W=d.parse(F,[],{allowTrailingComma:!0})??{};for(let G of f)if(typeof W==="object"&&W!==null)W=W[G];else{W=void 0;break}let Q=Array.isArray(W)?W:[],Z=Q.findIndex((G)=>typeof G==="string"&&J(G));if(Z!==-1&&Q[Z]===$)return{text:F,changed:!1};let z;if(Z!==-1)z=d.modify(F,[...f,Z],$,{formattingOptions:{insertSpaces:!0,tabSize:2}});else if(!Array.isArray(W))z=d.modify(F,f,[$],{formattingOptions:{insertSpaces:!0,tabSize:2}});else z=d.modify(F,[...f,Q.length],$,{formattingOptions:{insertSpaces:!0,tabSize:2}});let Y=d.applyEdits(F,z);return{text:d.applyEdits(Y,d.format(Y,void 0,{insertSpaces:!0,tabSize:2})),changed:!0}}import{existsSync as h0}from"node:fs";import{mkdir as FF,writeFile as N0}from"node:fs/promises";import{join as P0}from"node:path";function fF(F){return F===i||F.startsWith(`${i}@`)}function o0(F){let f=P0(F,"opencode.jsonc");if(h0(f))return{path:f,exists:!0};let $=P0(F,"opencode.json");if(h0($))return{path:$,exists:!0};return{path:f,exists:!1}}function a3(F){let f=P0(F,"tui.jsonc");if(h0(f))return{path:f,exists:!0};let $=P0(F,"tui.json");if(h0($))return{path:$,exists:!0};return{path:f,exists:!1}}async function $F(F,f=process.env){let $=r(f);await FF($,{recursive:!0});let{path:J,exists:j}=o0($),W=a1(F);if(!j){let Y=`{
72
+ "$schema": "https://opencode.ai/config.json",
73
+ "plugin": [
74
+ "${W}"
75
+ ]
76
+ }
77
+ `;return await N0(J,Y,"utf8"),{configPath:J,changed:!0,created:!0}}let{text:Q}=Y0(J),{text:Z,changed:z}=n0(Q,["plugin"],W,fF);if(z)await N0(J,Z,"utf8");return{configPath:J,changed:z,created:!1}}async function JF(F,f=process.env){let $=r(f);await FF($,{recursive:!0});let{path:J,exists:j}=a3($),W=T1(F);if(!j){let Y=`{
78
+ "plugin": [
79
+ "${W}"
80
+ ]
81
+ }
82
+ `;return await N0(J,Y,"utf8"),{configPath:J,changed:!0,created:!0}}let{text:Q}=Y0(J),{text:Z,changed:z}=n0(Q,["plugin"],W,fF);if(z)await N0(J,Z,"utf8");return{configPath:J,changed:z,created:!1}}import{existsSync as s0}from"node:fs";import{readFile as jF,writeFile as T3}from"node:fs/promises";import{join as i3}from"node:path";function WF(F=process.env){let f=i3(n(F),"settings.json");if(!s0(f))return!1;try{let J=JSON.parse(l3(f))?.packages;if(!Array.isArray(J))return!1;return J.some((j)=>{let W=typeof j==="string"?j:typeof j==="object"&&j!==null?j.source:"";if(typeof W!=="string")return!1;return W===t||W.startsWith(`${t}@`)||W===`npm:${t}`||W.startsWith(`npm:${t}@`)})}catch{return!1}}function l3(F){return z0("node:fs").readFileSync(F,"utf8")}async function ZF(F=process.env){let f=i0(F);if(!s0(f))return!1;try{let $=await jF(f,"utf8"),J=JSON.parse($);return Boolean(J?.anthropic&&typeof J.anthropic==="object")}catch{return!1}}async function QF(F=process.env){let f=i0(F);if(!s0(f))return!1;try{let $=await jF(f,"utf8"),J=JSON.parse($);if(J?.anthropic)return delete J.anthropic,await T3(f,`${JSON.stringify(J,null,2)}
83
+ `,{mode:384}),!0;return!1}catch{return!1}}async function zF(F,f=S,$=process.env){let J=i1(F),j=await f.run("pi",["install",J],{env:$});if(j.exitCode!==0)return{ok:!1,message:`Failed to install Pi package: ${j.stderr.trim()||`exit ${j.exitCode}`}`};return{ok:!0,message:`Installed ${J}`}}async function r3(F=process.env,f=S){let $=await f.run("opencode",["--version"],{env:F}),J=$.exitCode===0,j=J?$.stdout.trim()||null:null,W=r(F),{path:Q,exists:Z}=o0(W),z=!1;if(Z){let{value:X}=Y0(Q);if(Array.isArray(X?.plugin))z=X.plugin.some((G)=>typeof G==="string"&&(G===i||G.startsWith(`${i}@`)))}return{kind:"opencode",installed:J||Z||t0(b0(F)),version:j,configPath:Q,pluginInstalled:z}}async function n3(F=process.env,f=S){let $=await f.run("pi",["--version"],{env:F}),J=$.exitCode===0,j=null;if(J){let Y=$.stdout.trim()||$.stderr.trim();j=Y.split(/\r?\n/).find((G)=>/^\d+\.\d+\.\d+/.test(G.trim()))?.trim()??Y??null}let W=n(F),Q=J||t0(W),Z=WF(F),z=await ZF(F);return{kind:"pi",installed:Q,version:j,configPath:W,pluginInstalled:Z,hasLocalAuth:z}}async function o3(F=process.env,f=S){let $=await f.run("ck",["--version"],{env:F}),J=$.exitCode===0,j=J?$.stdout.trim()||null:null,W=null,Q=!1;try{if(W=YF(),W&&t0(W)){let Z=await w0({connectionFile:W,handshakeTimeoutMs:2000});Q=!0,Z.close()}}catch{Q=!1}return{ckInstalled:J,ckVersion:j,daemonRunning:Q,connectionPath:W}}async function qF(F=process.env,f=S){let[$,J,j]=await Promise.all([r3(F,f),n3(F,f),o3(F,f)]);return{opencode:$,pi:J,claustrum:j}}async function GF(F=[],f={}){let $=f.env??process.env,J=f.runner??S,j=f.fence??Z0,W=F.includes("--yes")||F.includes("-y"),Q=F.includes("--dry-run"),Z=F.includes("--no-claustrum"),z=F.includes("--claustrum");u1("CortexKit Anthropic Auth Setup");let Y=d1();Y.start("Detecting installed environments…");let X=await qF($,J);Y.stop("Environment detection complete.");let G=X.opencode.installed?`detected (${X.opencode.version??"version unknown"}, ${X.opencode.pluginInstalled?"plugin already configured":"plugin not configured"})`:"not found",K=X.pi.installed?`detected (${X.pi.version??"version unknown"}, ${X.pi.pluginInstalled?"extension already installed":"extension not installed"})`:"not found",L=X.claustrum.daemonRunning?`daemon running (${X.claustrum.ckVersion??"ck ready"})`:X.claustrum.ckInstalled?"ck installed, daemon not running":"not installed";if(N.info(`OpenCode: ${G}`),N.info(`Pi: ${K}`),N.info(`Claustrum: ${L}`),!X.opencode.installed&&!X.pi.installed)return N.error("Neither OpenCode nor Pi was detected on this system."),y("Install OpenCode (https://opencode.ai) or Pi (https://github.com/badlogic/pi-mono) first."),1;let M=[];if(W){if(X.opencode.installed)M.push("opencode");if(X.pi.installed)M.push("pi")}else{let U=[];if(X.opencode.installed)U.push({value:"opencode",label:"OpenCode",hint:X.opencode.pluginInstalled?"already configured":"configure plugin + TUI sidebar"});if(X.pi.installed)U.push({value:"pi",label:"Pi",hint:X.pi.pluginInstalled?"already installed":"install provider extension"});let D=await S1({message:"Select integrations to configure:",options:U,initialValues:U.map((v)=>v.value),required:!0});if(s(D))return y("Setup cancelled."),0;M=D}if(M.length===0)return y("No integrations selected."),0;let k=!1;if(X.claustrum.daemonRunning&&X.claustrum.ckInstalled&&!Z)if(z)k=!0;else if(W)k=!0;else{let U=await a0({message:"Claustrum daemon is running. Use Claustrum custody to manage Anthropic accounts securely in the vault?",initialValue:!0});if(s(U))return y("Setup cancelled."),0;k=U}let E=!1;if(M.includes("pi")&&k&&X.pi.hasLocalAuth)if(W)E=!0;else{N.warn("Pi has a stored local Anthropic OAuth token. Claustrum custody replaces local credentials.");let U=await a0({message:"Remove local Pi Anthropic OAuth credential so Claustrum custody can serve requests?",initialValue:!0});if(s(U)||!U)return N.error("Claustrum custody cannot be enabled for Pi while a local OAuth token takes precedence."),y("Setup aborted."),1;E=!0}try{await Q0(j)}catch(U){if(U instanceof H0)return N.error(U.message),y("Setup aborted — quit active hosts and rerun setup."),1;throw U}if(Q){if(N.info("[dry-run] Planned actions:"),N.info(` - Target integrations: ${M.join(", ")}`),N.info(` - Claustrum custody: ${k?"enabled":"disabled"}`),E)N.info(" - Pi local OAuth token will be removed");return y("Dry run complete. No changes were made."),0}let B=await l1();if(M.includes("opencode")){Y.start("Configuring OpenCode plugin and TUI sidebar…");try{if(await $F(B,$),await JF(B,$),Y.stop("OpenCode configuration updated."),k){Y.start("Enrolling OpenCode into Claustrum custody…");let U=await r0("opencode",{runner:J,env:$});if(!U.ok)return Y.stop("Claustrum enrollment failed."),N.error(U.message),1;Y.stop(`OpenCode enrolled into Claustrum: ${U.message}`),Y.start("Installing Anthropic activation tombstone…"),await x1({authPath:b0($),fence:j}),Y.stop("Anthropic activation tombstone installed.")}}catch(U){return Y.stop("OpenCode setup failed."),N.error(U instanceof Error?U.message:String(U)),1}}if(M.includes("pi")){Y.start("Installing Pi Anthropic extension…");try{let U=await zF(B,J,$);if(!U.ok)return Y.stop("Pi extension installation failed."),N.error(U.message),1;if(Y.stop("Pi Anthropic extension installed."),k){if(E)Y.start("Removing local Pi Anthropic OAuth credential…"),await QF($),Y.stop("Local Pi Anthropic OAuth credential removed.");Y.start("Enrolling Pi into Claustrum custody…");let D=await r0("pi",{runner:J,env:$});if(!D.ok)return Y.stop("Pi Claustrum enrollment failed."),N.error(D.message),1;Y.stop(`Pi enrolled into Claustrum: ${D.message}`)}}catch(U){return Y.stop("Pi setup failed."),N.error(U instanceof Error?U.message:String(U)),1}}let O=[];if(M.includes("opencode"))O.push("• Start OpenCode normally (opencode).");if(M.includes("pi"))O.push("• Start Pi normally (pi).");if(k)O.push("• Manage accounts in Claustrum with: ck auth login --provider anthropic"),O.push(" Newly added accounts will be discovered automatically without restarting.");return p1(O.join(`
84
+ `),"Next Steps"),y("Setup complete!"),0}function Ff(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function XF(){console.log(`Usage:
85
+ opencode-anthropic-auth setup [--yes] [--dry-run]
3
86
  opencode-anthropic-auth login [label]
4
87
  opencode-anthropic-auth api add [label]
5
88
  opencode-anthropic-auth list
6
89
  opencode-anthropic-auth relay setup
7
90
 
8
91
  Fallback accounts are stored in:
9
- ${E()}`)}function H(j,G){let F=j?.trim();if(!F)throw Error(`${G} is required`);return F}async function V(j){let F=await(j.fetchImpl??fetch)(`https://api.cloudflare.com/client/v4${j.path}`,{method:j.method,headers:{authorization:`Bearer ${j.token}`,...j.body instanceof FormData?{}:{"content-type":"application/json"},...j.headers},body:j.body}),J=await F.text(),M;try{M=JSON.parse(J)}catch{throw Error(`Cloudflare API returned ${F.status}: ${J}`)}if(!F.ok||M.success===!1){let Q=M.errors?.map((X)=>X.message).filter(Boolean).join("; ");throw Error(Q||`Cloudflare API returned ${F.status}`)}return M.result}async function A(j,G,F,J){return V({token:j,method:"POST",path:`/accounts/${G}/storage/kv/namespaces`,body:JSON.stringify({title:F}),fetchImpl:J})}async function g(j){let G={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:j.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:j.relayToken}]},F=new FormData;return F.set("metadata",JSON.stringify(G)),F.set("worker.js",new Blob([q],{type:"application/javascript+module"}),"worker.js"),V({token:j.token,method:"PUT",path:`/accounts/${j.accountId}/workers/scripts/${j.scriptName}`,body:F,fetchImpl:j.fetchImpl})}async function h(j,G,F,J){await V({token:j,method:"POST",path:`/accounts/${G}/workers/scripts/${F}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1}),fetchImpl:J})}async function k(j,G,F){return V({token:j,method:"GET",path:`/accounts/${G}/workers/subdomain`,fetchImpl:F}).catch(()=>null)}async function f(j={}){let G=j.fetchImpl??fetch,F=j.prompt??C,J=H(process.env.CLOUDFLARE_API_TOKEN?.trim()||await F("Cloudflare API token: "),"Cloudflare API token"),M=H(process.env.CLOUDFLARE_ACCOUNT_ID||await F("Cloudflare account ID: "),"Cloudflare account ID"),Q=await F("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",X=`${Q}-state`,Z=T();console.log("Creating Cloudflare KV namespace...");let Y=await A(J,M,X,G);console.log("Uploading relay Worker..."),await g({token:J,accountId:M,scriptName:Q,kvNamespaceId:Y.id,relayToken:Z,fetchImpl:G}),await h(J,M,Q,G).catch((D)=>{console.warn(`Could not enable workers.dev automatically: ${D instanceof Error?D.message:String(D)}`)});let $=await k(J,M,G),W=($?.subdomain?`https://${Q}.${$.subdomain}.workers.dev`:"")||H(await C("Relay Worker URL: "),"Relay Worker URL"),O=await K()??R();O.relay={enabled:!0,url:W,token:Z,fallbackToDirect:!0,transport:"http"},await S(O),console.log(`Relay enabled at ${W}`),console.log(`Config saved to ${E()}.`)}var B=null;async function C(j){return B??=y({input:x,output:v}),(await B.question(j)).trim()}function I(){B?.close(),B=null}async function b(j,G={}){let F=G.prompt??C,J=G.authorize??z,M=G.exchange??N,Q=j?.trim()||await F("Fallback account label (optional): "),X=await J("max");console.log(`
92
+ ${e()}`)}function e0(F,f){let $=F?.trim();if(!$)throw Error(`${f} is required`);return $}async function v0(F){let $=await(F.fetchImpl??fetch)(`https://api.cloudflare.com/client/v4${F.path}`,{method:F.method,headers:{authorization:`Bearer ${F.token}`,...F.body instanceof FormData?{}:{"content-type":"application/json"},...F.headers},body:F.body}),J=await $.text(),j;try{j=JSON.parse(J)}catch{throw Error(`Cloudflare API returned ${$.status}: ${J}`)}if(!$.ok||j.success===!1){let W=j.errors?.map((Q)=>Q.message).filter(Boolean).join("; ");throw Error(W||`Cloudflare API returned ${$.status}`)}return j.result}async function ff(F,f,$,J){return v0({token:F,method:"POST",path:`/accounts/${f}/storage/kv/namespaces`,body:JSON.stringify({title:$}),fetchImpl:J})}async function $f(F){let f={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:F.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:F.relayToken}]},$=new FormData;return $.set("metadata",JSON.stringify(f)),$.set("worker.js",new Blob([hF],{type:"application/javascript+module"}),"worker.js"),v0({token:F.token,method:"PUT",path:`/accounts/${F.accountId}/workers/scripts/${F.scriptName}`,body:$,fetchImpl:F.fetchImpl})}async function Jf(F,f,$,J){await v0({token:F,method:"POST",path:`/accounts/${f}/workers/scripts/${$}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1}),fetchImpl:J})}async function jf(F,f,$){return v0({token:F,method:"GET",path:`/accounts/${f}/workers/subdomain`,fetchImpl:$}).catch(()=>null)}async function Wf(F={}){let f=F.fetchImpl??fetch,$=F.prompt??E0,J=e0(process.env.CLOUDFLARE_API_TOKEN?.trim()||await $("Cloudflare API token: "),"Cloudflare API token"),j=e0(process.env.CLOUDFLARE_ACCOUNT_ID||await $("Cloudflare account ID: "),"Cloudflare account ID"),W=await $("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",Q=`${W}-state`,Z=wF();console.log("Creating Cloudflare KV namespace...");let z=await ff(J,j,Q,f);console.log("Uploading relay Worker..."),await $f({token:J,accountId:j,scriptName:W,kvNamespaceId:z.id,relayToken:Z,fetchImpl:f}),await Jf(J,j,W,f).catch((L)=>{console.warn(`Could not enable workers.dev automatically: ${L instanceof Error?L.message:String(L)}`)});let Y=await jf(J,j,f),G=(Y?.subdomain?`https://${W}.${Y.subdomain}.workers.dev`:"")||e0(await E0("Relay Worker URL: "),"Relay Worker URL"),K=await q0()??Ff();K.relay={enabled:!0,url:G,token:Z,fallbackToDirect:!0,transport:"http"},await bF(K),console.log(`Relay enabled at ${G}`),console.log(`Config saved to ${e()}.`)}var I0=null;async function E0(F){return I0??=e3({input:s3,output:t3}),(await I0.question(F)).trim()}function Zf(){I0?.close(),I0=null}async function Qf(F,f={}){let $=f.prompt??E0,J=f.authorize??LF,j=f.exchange??UF;if(VF(await q0())==="claustrum")throw Error("Exit Claustrum mode first: /claude-account local");let W=F?.trim()||await $("Fallback account label (optional): "),Q=await J("max");console.log(`
10
93
  Open this URL in your browser and complete Claude sign-in:
11
- `),console.log(`${X.url}
12
- `);let Z=await F("Paste the full callback URL or authorization code here: "),Y=await M(Z,X.verifier,X.redirectUri,X.state);if(Y.type==="failed")throw Error("Authentication failed");let $=Date.now();await U({id:Q||crypto.randomUUID(),label:Q||void 0,type:"oauth",authLineageId:crypto.randomUUID(),access:Y.access,refresh:Y.refresh,expires:Y.expires,enabled:!0,addedAt:$,lastUsed:$,lastRefreshedAt:$}),console.log(`
13
- Saved fallback account${Q?` "${Q}"`:""}.`)}async function d(j,G={}){let F=G.prompt??C,J=j?.trim()||await F("API fallback label (optional): "),M=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await F("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!L(M))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let Q=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await F("API key: ");if(!Q.trim())throw Error("API key is required");let Z=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await F("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",Y=Date.now();await U({id:J||crypto.randomUUID(),label:J||void 0,type:"api",apiKey:Q.trim(),baseURL:M,authHeader:Z,enabled:!0,addedAt:Y,lastUsed:Y}),console.log(`
14
- Saved API fallback route${J?` "${J}"`:""} (${M}).`)}async function m(){let j=await K();if(!j?.accounts.length){console.log(`No fallback accounts found at ${E()}.`);return}for(let[G,F]of j.accounts.entries()){let J=F.label||F.id,M=F.enabled===!1?"disabled":"enabled";if(!P(F)){console.log(`${G+1}. ${J} (${M}) — API route ${F.baseURL}`);continue}let Q=F.quota?.five_hour?.remainingPercent,X=F.quota?.seven_day?.remainingPercent,Z=Q===void 0&&X===void 0?"quota unknown":`5h ${Q??"?"}%, 1w ${X??"?"}% remaining`;console.log(`${G+1}. ${J} (${M}) — ${Z}`)}}async function u(){let[j,G,F]=process.argv.slice(2);if(!j||j==="help"||j==="--help"||j==="-h"){_();return}if(j==="login"){await b(G);return}if(j==="api"&&G==="add"){await d(F);return}if(j==="list"){await m();return}if(j==="relay"&&G==="setup"){await f();return}_(),process.exitCode=1}if(w.main==w.module)try{await u()}catch(j){console.error(j instanceof Error?j.message:String(j)),process.exitCode=1}finally{I()}export{f as relaySetup,b as login,d as addApiRoute};
94
+ `),console.log(`${Q.url}
95
+ `);let Z=await $("Paste the full callback URL or authorization code here: "),z=await j(Z,Q.verifier,Q.redirectUri,Q.state);if(z.type==="failed")throw Error("Authentication failed");let Y=Date.now(),X={id:W||crypto.randomUUID(),label:W||void 0,type:"oauth",authLineageId:crypto.randomUUID(),access:z.access,refresh:z.refresh,expires:z.expires,enabled:!0,addedAt:Y,lastUsed:Y,lastRefreshedAt:Y},G=MF(X.label??X.id);await F1(X),await IF({accountId:X.id,credentialId:G,authFingerprint:PF(z.access,z.refresh),completedAt:Y},{accountStoragePath:e(),manifestPath:KF((await q0())?.claustrum,process.env),divergence:{statePath:HF(e()),lastVaultServedRecordVersion:NF({accountId:X.id,warn:(K)=>console.warn(`Fallback login had no served vault record for ${K}`)})}}),console.log(`
96
+ Saved fallback account${W?` "${W}"`:""}.`)}async function zf(F,f={}){let $=f.prompt??E0,J=F?.trim()||await $("API fallback label (optional): "),j=process.env.OPENCODE_ANTHROPIC_AUTH_API_BASE_URL?.trim()||(await $("Anthropic-compatible base URL [https://api.kie.ai/claude]: ")).trim()||"https://api.kie.ai/claude";if(!DF(j))throw Error("API fallback base URL must be an http(s) URL without embedded credentials");let W=process.env.OPENCODE_ANTHROPIC_AUTH_API_KEY?.trim()||await $("API key: ");if(!W.trim())throw Error("API key is required");let Z=(process.env.OPENCODE_ANTHROPIC_AUTH_API_AUTH_HEADER?.trim()||await $("Auth header [authorization-bearer|x-api-key] (default authorization-bearer): ")).trim().toLowerCase()==="x-api-key"?"x-api-key":"authorization-bearer",z=Date.now();await F1({id:J||crypto.randomUUID(),label:J||void 0,type:"api",apiKey:W.trim(),baseURL:j,authHeader:Z,enabled:!0,addedAt:z,lastUsed:z}),console.log(`
97
+ Saved API fallback route${J?` "${J}"`:""} (${j}).`)}async function Yf(){let F=await q0();if(!F?.accounts.length){console.log(`No fallback accounts found at ${e()}.`);return}for(let[f,$]of F.accounts.entries()){let J=$.label||$.id,j=$.enabled===!1?"disabled":"enabled";if(!kF($)){console.log(`${f+1}. ${J} (${j}) — API route ${$.baseURL}`);continue}let W=$.quota?.five_hour?.remainingPercent,Q=$.quota?.seven_day?.remainingPercent,Z=W===void 0&&Q===void 0?"quota unknown":`5h ${W??"?"}%, 1w ${Q??"?"}% remaining`;console.log(`${f+1}. ${J} (${j}) — ${Z}`)}}async function qf(){let[F,f,$]=process.argv.slice(2);if(!F||F==="help"||F==="--help"||F==="-h"){XF();return}if(F==="setup"){let J=await GF(process.argv.slice(3));if(J!==0)process.exitCode=J;return}if(F==="login"){await Qf(f);return}if(F==="api"&&f==="add"){await zf($);return}if(F==="list"){await Yf();return}if(F==="relay"&&f==="setup"){await Wf();return}XF(),process.exitCode=1}if(z0.main==z0.module)try{await qf()}catch(F){console.error(F instanceof Error?F.message:String(F)),process.exitCode=1}finally{Zf()}export{GF as runSetupCommand,Wf as relaySetup,Qf as login,zf as addApiRoute};
@@ -0,0 +1,36 @@
1
+ import { type AccountStorage, type ClaustrumCredential, type CustodyHandleManifest, type CustodyHandleResolution, type CustodyStatusState, type OAuthAccount } from '@cortexkit/anthropic-auth-core';
2
+ type CustodyDimensionsDeps = {
3
+ getCache: () => {
4
+ peek: (handle: string) => ClaustrumCredential | undefined;
5
+ } | null | undefined;
6
+ now: () => number;
7
+ resolveAccountCustodyHandle: (account: OAuthAccount, storage: AccountStorage) => CustodyHandleResolution;
8
+ usableAccessToken: (credential: ClaustrumCredential | undefined, now: number) => string | undefined;
9
+ hasIdentityMismatch: (account: OAuthAccount, credential: ClaustrumCredential | undefined) => boolean;
10
+ isBlocked: (accountId: string) => boolean;
11
+ isReauth: (accountId: string) => boolean;
12
+ getManifest: () => CustodyHandleManifest | undefined;
13
+ };
14
+ export declare function fallbackCustodyDimensions(storage: AccountStorage | null, deps: Pick<CustodyDimensionsDeps, 'getCache' | 'now' | 'resolveAccountCustodyHandle' | 'usableAccessToken'> & {
15
+ construction?: boolean;
16
+ }): {
17
+ fallbacks: "R" | "T";
18
+ evidence: "V" | "unknown";
19
+ } | {
20
+ fallbacks: 'M';
21
+ evidence: "N" | "unknown";
22
+ } | {
23
+ fallbacks: "R" | "T";
24
+ evidence: "N" | "V";
25
+ };
26
+ export declare function mainCustodyDimension(auth: {
27
+ type: string;
28
+ access?: string;
29
+ refresh?: string;
30
+ }): "R" | "T" | "X";
31
+ export declare function isFallbackAccountVaultServed(accountId: string, storage: AccountStorage | null, deps: Pick<CustodyDimensionsDeps, 'getCache' | 'now' | 'resolveAccountCustodyHandle' | 'usableAccessToken' | 'hasIdentityMismatch' | 'isBlocked'>): boolean;
32
+ export declare function custodyStateFor(account: {
33
+ id: string;
34
+ role: 'main' | 'fallback';
35
+ }, storage: AccountStorage | null, deps: CustodyDimensionsDeps, vaultServed?: boolean): CustodyStatusState;
36
+ export {};
@@ -0,0 +1,14 @@
1
+ import { type AccountStorage, type CustodyHandleAccount, type CustodyHandleManifest, type OAuthAccount } from '@cortexkit/anthropic-auth-core';
2
+ /**
3
+ * Returns valid manifest bindings that are not yet represented by an OAuth
4
+ * routing row. Existing disabled rows are intentionally retained: binding a
5
+ * credential must not silently override a user's routing preference.
6
+ */
7
+ export declare function pendingClaustrumEnrollments(storage: AccountStorage, manifest: CustodyHandleManifest | undefined): CustodyHandleAccount[];
8
+ /** Build the secret-free routing row for a provider-verified manifest binding. */
9
+ export declare function materializeClaustrumEnrollment(input: {
10
+ storage: AccountStorage;
11
+ binding: CustodyHandleAccount;
12
+ providerAccountUuid: string;
13
+ now?: number;
14
+ }): OAuthAccount;