myshell-tools 2.4.0 → 2.7.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 (60) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +28 -12
  3. package/dist/cli.js +38 -3
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/cost.js +4 -1
  6. package/dist/commands/cost.js.map +1 -1
  7. package/dist/commands/doctor.js +2 -2
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/login.d.ts +51 -5
  10. package/dist/commands/login.js +207 -14
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/core/assess.js +2 -62
  13. package/dist/core/assess.js.map +1 -1
  14. package/dist/core/budget.d.ts +26 -0
  15. package/dist/core/budget.js +37 -0
  16. package/dist/core/budget.js.map +1 -0
  17. package/dist/core/history.d.ts +35 -0
  18. package/dist/core/history.js +116 -0
  19. package/dist/core/history.js.map +1 -0
  20. package/dist/core/json-envelope.d.ts +49 -0
  21. package/dist/core/json-envelope.js +117 -0
  22. package/dist/core/json-envelope.js.map +1 -0
  23. package/dist/core/orchestrate.js +107 -8
  24. package/dist/core/orchestrate.js.map +1 -1
  25. package/dist/core/policy.js +17 -9
  26. package/dist/core/policy.js.map +1 -1
  27. package/dist/core/prompt.d.ts +9 -4
  28. package/dist/core/prompt.js +14 -5
  29. package/dist/core/prompt.js.map +1 -1
  30. package/dist/core/review.js +2 -49
  31. package/dist/core/review.js.map +1 -1
  32. package/dist/core/route.d.ts +13 -5
  33. package/dist/core/route.js +20 -6
  34. package/dist/core/route.js.map +1 -1
  35. package/dist/core/types.d.ts +37 -0
  36. package/dist/infra/credentials.d.ts +58 -0
  37. package/dist/infra/credentials.js +172 -0
  38. package/dist/infra/credentials.js.map +1 -0
  39. package/dist/infra/pricing.d.ts +17 -4
  40. package/dist/infra/pricing.js +73 -3
  41. package/dist/infra/pricing.js.map +1 -1
  42. package/dist/interface/menu.d.ts +26 -0
  43. package/dist/interface/menu.js +131 -26
  44. package/dist/interface/menu.js.map +1 -1
  45. package/dist/providers/detect.d.ts +17 -5
  46. package/dist/providers/detect.js +56 -4
  47. package/dist/providers/detect.js.map +1 -1
  48. package/dist/providers/install.js +1 -0
  49. package/dist/providers/install.js.map +1 -1
  50. package/dist/providers/opencode-parse.d.ts +49 -0
  51. package/dist/providers/opencode-parse.js +181 -0
  52. package/dist/providers/opencode-parse.js.map +1 -0
  53. package/dist/providers/opencode.d.ts +43 -0
  54. package/dist/providers/opencode.js +121 -0
  55. package/dist/providers/opencode.js.map +1 -0
  56. package/dist/providers/port.d.ts +1 -1
  57. package/dist/providers/registry.d.ts +2 -2
  58. package/dist/providers/registry.js +6 -2
  59. package/dist/providers/registry.js.map +1 -1
  60. package/package.json +2 -2
@@ -16,13 +16,21 @@ import type { ProviderId } from '../providers/port.js';
16
16
  * Algorithm:
17
17
  * 1. Walk `policy.providerOrderByTier[tier]` in order.
18
18
  * 2. For the first provider that is present in `available`, resolve the
19
- * cheapest model for that provider+tier via `getCheapestForTier`.
19
+ * cheapest model for that provider+tier via `getCheapestForTier`, further
20
+ * restricted to `availableModels[provider]` when that set is non-empty.
20
21
  * 3. If none of the policy-preferred providers are available but `available`
21
22
  * is non-empty, fall back to the globally cheapest model for that tier.
22
23
  * 4. If `available` is empty, throw — there is nothing to route to.
23
24
  *
24
- * @param tier - The orchestration tier to route.
25
- * @param available - Provider IDs that are currently reachable.
26
- * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
25
+ * The `availableModels` parameter is additive/opt-in:
26
+ * - When absent or undefined behaviour is IDENTICAL to today (no change).
27
+ * - When a provider's entry is present and non-empty prefer a model in that
28
+ * list; if none match the pricing table, fall back to cheapest-for-tier
29
+ * (graceful degradation, never throws, never returns nothing).
30
+ *
31
+ * @param tier - The orchestration tier to route.
32
+ * @param available - Provider IDs that are currently reachable.
33
+ * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
34
+ * @param availableModels - Optional per-provider advertised model sets from detection.
27
35
  */
28
- export declare function route(tier: Tier, available: ProviderId[], policy: Policy): RouteDecision;
36
+ export declare function route(tier: Tier, available: ProviderId[], policy: Policy, availableModels?: Partial<Record<ProviderId, readonly string[]>>): RouteDecision;
@@ -15,16 +15,24 @@ import { getCheapestForTier } from '../infra/pricing.js';
15
15
  * Algorithm:
16
16
  * 1. Walk `policy.providerOrderByTier[tier]` in order.
17
17
  * 2. For the first provider that is present in `available`, resolve the
18
- * cheapest model for that provider+tier via `getCheapestForTier`.
18
+ * cheapest model for that provider+tier via `getCheapestForTier`, further
19
+ * restricted to `availableModels[provider]` when that set is non-empty.
19
20
  * 3. If none of the policy-preferred providers are available but `available`
20
21
  * is non-empty, fall back to the globally cheapest model for that tier.
21
22
  * 4. If `available` is empty, throw — there is nothing to route to.
22
23
  *
23
- * @param tier - The orchestration tier to route.
24
- * @param available - Provider IDs that are currently reachable.
25
- * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
24
+ * The `availableModels` parameter is additive/opt-in:
25
+ * - When absent or undefined behaviour is IDENTICAL to today (no change).
26
+ * - When a provider's entry is present and non-empty prefer a model in that
27
+ * list; if none match the pricing table, fall back to cheapest-for-tier
28
+ * (graceful degradation, never throws, never returns nothing).
29
+ *
30
+ * @param tier - The orchestration tier to route.
31
+ * @param available - Provider IDs that are currently reachable.
32
+ * @param policy - Active routing policy (from `DEFAULT_POLICY` or overrides).
33
+ * @param availableModels - Optional per-provider advertised model sets from detection.
26
34
  */
27
- export function route(tier, available, policy) {
35
+ export function route(tier, available, policy, availableModels) {
28
36
  if (available.length === 0) {
29
37
  throw new Error(`route: no providers available for tier "${tier}" — start at least one provider`);
30
38
  }
@@ -32,7 +40,13 @@ export function route(tier, available, policy) {
32
40
  // Walk the preferred order and pick the first available provider.
33
41
  for (const preferred of preferredOrder) {
34
42
  if (available.includes(preferred)) {
35
- const pricing = getCheapestForTier(tier, [preferred]);
43
+ // When advertised models are supplied for this provider, pass them as
44
+ // the allowed-model filter so we prefer a model the CLI actually has.
45
+ const providerAllowed = availableModels?.[preferred];
46
+ const allowedSet = providerAllowed !== undefined && providerAllowed.length > 0
47
+ ? providerAllowed
48
+ : undefined;
49
+ const pricing = getCheapestForTier(tier, [preferred], allowedSet);
36
50
  return {
37
51
  tier,
38
52
  provider: preferred,
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/core/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,KAAK,CACnB,IAAU,EACV,SAAuB,EACvB,MAAc;IAEd,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,iCAAiC,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAExD,kEAAkE;IAClE,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;QACvC,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;YACtD,OAAO;gBACL,IAAI;gBACJ,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,QAAQ,CAAC,QAAsB;QACzC,KAAK,EAAE,QAAQ,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/core/route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,KAAK,CACnB,IAAU,EACV,SAAuB,EACvB,MAAc,EACd,eAAgE;IAEhE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,2CAA2C,IAAI,iCAAiC,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAExD,kEAAkE;IAClE,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;QACvC,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAClC,sEAAsE;YACtE,sEAAsE;YACtE,MAAM,eAAe,GAAG,eAAe,EAAE,CAAC,SAAS,CAAC,CAAC;YACrD,MAAM,UAAU,GACd,eAAe,KAAK,SAAS,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC;gBACzD,CAAC,CAAC,eAAe;gBACjB,CAAC,CAAC,SAAS,CAAC;YAChB,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;YAClE,OAAO;gBACL,IAAI;gBACJ,QAAQ,EAAE,SAAS;gBACnB,KAAK,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACrD,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,QAAQ,CAAC,QAAsB;QACzC,KAAK,EAAE,QAAQ,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -85,6 +85,25 @@ export interface Policy {
85
85
  readonly escalateBelowConfidence: Record<Risk, number>;
86
86
  /** Ordered provider preference per tier; route() honours availability. */
87
87
  readonly providerOrderByTier: Record<Tier, readonly ProviderId[]>;
88
+ /**
89
+ * Controls when cross-vendor review runs automatically.
90
+ *
91
+ * - `'auto'` : review when risk is high/critical OR the model sets needsReview
92
+ * (current default behaviour).
93
+ * - `'critical-only'` : review only when risk is `critical` (or needsReview AND critical).
94
+ * - `'off'` : never trigger an automatic cross-vendor review.
95
+ *
96
+ * Omitting the field is equivalent to `'auto'` (backward-compatible).
97
+ */
98
+ readonly reviewPolicy?: 'auto' | 'critical-only' | 'off';
99
+ /**
100
+ * Per-task cost budget cap in USD. When `totalCostUsd` reaches or exceeds
101
+ * this value, orchestrate() stops spending (no new escalation, no new review)
102
+ * and accepts the best result produced so far.
103
+ *
104
+ * `null` or `undefined` (the default) means no cap is applied.
105
+ */
106
+ readonly maxCostUsd?: number | null;
88
107
  }
89
108
  export interface OrchestrateDeps {
90
109
  /** Available providers, keyed by id. Absent key = provider unavailable. */
@@ -96,6 +115,24 @@ export interface OrchestrateDeps {
96
115
  readonly cwd: string;
97
116
  readonly sandbox: SandboxLevel;
98
117
  readonly timeoutMs: number;
118
+ /**
119
+ * Prior conversation history for context continuity. When provided, the most
120
+ * recent turns are compacted and injected into the first provider prompt so
121
+ * stateless one-shot providers (claude -p / codex exec) have multi-turn
122
+ * awareness. Leave undefined for fresh (one-shot) sessions.
123
+ */
124
+ readonly history?: readonly SessionEntry[];
125
+ /**
126
+ * Advertised model lists from provider detection, keyed by provider id.
127
+ * When supplied, route() restricts candidates to models that the provider CLI
128
+ * actually advertises, preventing the CLI from routing to a model it cannot run.
129
+ *
130
+ * Absence (undefined) or an empty list for a provider → fall back to the
131
+ * standard cheapest-for-tier pricing-table behaviour (backward-compatible).
132
+ *
133
+ * Only include providers that are installed; exactOptionalPropertyTypes is ON.
134
+ */
135
+ readonly availableModels?: Partial<Record<ProviderId, readonly string[]>>;
99
136
  }
100
137
  /**
101
138
  * High-level events emitted by orchestrate(). The interface/render layer
@@ -0,0 +1,58 @@
1
+ /**
2
+ * src/infra/credentials.ts — Persisted credential store for myshell-tools.
3
+ *
4
+ * Stores a small JSON file at <homeDir>/.myshell-tools/credentials.json with
5
+ * shape `{ claudeOauthToken?: string }` so the Claude OAuth token captured
6
+ * during `myshell-tools login claude --code` is available across restarts.
7
+ *
8
+ * On startup, `applyStoredCredentials` injects the token into `process.env` so
9
+ * that both the provider detection (`claude auth status`) and the spawned
10
+ * `claude -p …` child process see it via the inherited environment.
11
+ *
12
+ * Security: the file is written with mode 0o600 (owner-read-only) on POSIX
13
+ * systems. The chmod is best-effort — a failure is silently ignored so the
14
+ * function never throws on Windows or unusual filesystems.
15
+ */
16
+ export interface Credentials {
17
+ claudeOauthToken?: string;
18
+ }
19
+ /**
20
+ * Load stored credentials. Never throws — missing or corrupt files return `{}`.
21
+ */
22
+ export declare function loadCredentials(homeDir?: string): Promise<Credentials>;
23
+ /**
24
+ * Persist the Claude OAuth token atomically to
25
+ * `~/.myshell-tools/credentials.json` with restrictive permissions (0o600).
26
+ */
27
+ export declare function saveClaudeToken(token: string, homeDir?: string): Promise<void>;
28
+ /**
29
+ * Remove the stored Claude OAuth token. Writes the file back without the
30
+ * token key so any future credential fields are preserved.
31
+ * Never throws.
32
+ */
33
+ export declare function clearClaudeToken(homeDir?: string): Promise<void>;
34
+ /**
35
+ * Inject a previously-saved Claude OAuth token into `env` if:
36
+ * 1. A token is stored in `~/.myshell-tools/credentials.json`, AND
37
+ * 2. `env.CLAUDE_CODE_OAUTH_TOKEN` is not already set (user's explicit env wins).
38
+ *
39
+ * Called once at the very top of `main()` so every code path — detection,
40
+ * spawned `claude -p …`, and the menu — sees the token without the user
41
+ * needing to export it manually.
42
+ *
43
+ * Never throws.
44
+ */
45
+ export declare function applyStoredCredentials(env: NodeJS.ProcessEnv, homeDir?: string): Promise<void>;
46
+ /**
47
+ * Extract the first Claude long-lived OAuth token from `text`.
48
+ *
49
+ * Token format: `sk-ant-oat` followed by optional digits/lowercase-letters,
50
+ * then a dash, then one or more Base64url characters (`[A-Za-z0-9_-]+`).
51
+ *
52
+ * Returns `null` when no token is found. Never throws.
53
+ *
54
+ * @example
55
+ * extractClaudeToken('Token: sk-ant-oat01-abc-XYZ123') // → 'sk-ant-oat01-abc-XYZ123'
56
+ * extractClaudeToken('no token here') // → null
57
+ */
58
+ export declare function extractClaudeToken(text: string): string | null;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * src/infra/credentials.ts — Persisted credential store for myshell-tools.
3
+ *
4
+ * Stores a small JSON file at <homeDir>/.myshell-tools/credentials.json with
5
+ * shape `{ claudeOauthToken?: string }` so the Claude OAuth token captured
6
+ * during `myshell-tools login claude --code` is available across restarts.
7
+ *
8
+ * On startup, `applyStoredCredentials` injects the token into `process.env` so
9
+ * that both the provider detection (`claude auth status`) and the spawned
10
+ * `claude -p …` child process see it via the inherited environment.
11
+ *
12
+ * Security: the file is written with mode 0o600 (owner-read-only) on POSIX
13
+ * systems. The chmod is best-effort — a failure is silently ignored so the
14
+ * function never throws on Windows or unusual filesystems.
15
+ */
16
+ import { mkdir, readFile, chmod } from 'node:fs/promises';
17
+ import { homedir } from 'node:os';
18
+ import { join } from 'node:path';
19
+ import { atomicWrite } from './atomic.js';
20
+ // ---------------------------------------------------------------------------
21
+ // Path helpers (pure)
22
+ // ---------------------------------------------------------------------------
23
+ function getCredentialsDir(home) {
24
+ return join(home, '.myshell-tools');
25
+ }
26
+ function getCredentialsPath(home) {
27
+ return join(getCredentialsDir(home), 'credentials.json');
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Internal parse helper
31
+ // ---------------------------------------------------------------------------
32
+ /**
33
+ * Parse credentials from raw JSON text. Returns `{}` on any error so callers
34
+ * never need to handle thrown exceptions from the load path.
35
+ */
36
+ function parseCredentials(raw) {
37
+ try {
38
+ const parsed = JSON.parse(raw);
39
+ if (typeof parsed !== 'object' || parsed === null) {
40
+ return {};
41
+ }
42
+ const obj = parsed;
43
+ const result = {};
44
+ if (typeof obj['claudeOauthToken'] === 'string' && obj['claudeOauthToken'].length > 0) {
45
+ result.claudeOauthToken = obj['claudeOauthToken'];
46
+ }
47
+ return result;
48
+ }
49
+ catch {
50
+ return {};
51
+ }
52
+ }
53
+ // ---------------------------------------------------------------------------
54
+ // Public API
55
+ // ---------------------------------------------------------------------------
56
+ /**
57
+ * Load stored credentials. Never throws — missing or corrupt files return `{}`.
58
+ */
59
+ export async function loadCredentials(homeDir) {
60
+ const home = homeDir ?? homedir();
61
+ try {
62
+ const raw = await readFile(getCredentialsPath(home), 'utf8');
63
+ return parseCredentials(raw);
64
+ }
65
+ catch {
66
+ return {};
67
+ }
68
+ }
69
+ /**
70
+ * Persist the Claude OAuth token atomically to
71
+ * `~/.myshell-tools/credentials.json` with restrictive permissions (0o600).
72
+ */
73
+ export async function saveClaudeToken(token, homeDir) {
74
+ const home = homeDir ?? homedir();
75
+ const dir = getCredentialsDir(home);
76
+ const path = getCredentialsPath(home);
77
+ await mkdir(dir, { recursive: true });
78
+ // Load existing credentials so we only replace the token key, preserving others.
79
+ const existing = await loadCredentials(homeDir);
80
+ const updated = { ...existing, claudeOauthToken: token };
81
+ // atomicWrite uses rename — ensures no partial writes on crash.
82
+ await atomicWrite(path, JSON.stringify(updated, null, 2));
83
+ // Best-effort: restrict to owner-read-only. Silently ignored on Windows or
84
+ // unusual filesystems where chmod is unavailable or unsupported.
85
+ try {
86
+ await chmod(path, 0o600);
87
+ }
88
+ catch {
89
+ // Cross-platform best-effort only — never throws
90
+ }
91
+ }
92
+ /**
93
+ * Remove the stored Claude OAuth token. Writes the file back without the
94
+ * token key so any future credential fields are preserved.
95
+ * Never throws.
96
+ */
97
+ export async function clearClaudeToken(homeDir) {
98
+ try {
99
+ const home = homeDir ?? homedir();
100
+ const dir = getCredentialsDir(home);
101
+ const path = getCredentialsPath(home);
102
+ await mkdir(dir, { recursive: true });
103
+ // Load the raw file to preserve unknown future keys.
104
+ let rawObj = {};
105
+ try {
106
+ const raw = await readFile(path, 'utf8');
107
+ const parsed = JSON.parse(raw);
108
+ if (typeof parsed === 'object' && parsed !== null) {
109
+ rawObj = parsed;
110
+ }
111
+ }
112
+ catch {
113
+ // Missing or corrupt file — start from empty
114
+ }
115
+ delete rawObj['claudeOauthToken'];
116
+ await atomicWrite(path, JSON.stringify(rawObj, null, 2));
117
+ }
118
+ catch {
119
+ // Never throws — clear is best-effort
120
+ }
121
+ }
122
+ /**
123
+ * Inject a previously-saved Claude OAuth token into `env` if:
124
+ * 1. A token is stored in `~/.myshell-tools/credentials.json`, AND
125
+ * 2. `env.CLAUDE_CODE_OAUTH_TOKEN` is not already set (user's explicit env wins).
126
+ *
127
+ * Called once at the very top of `main()` so every code path — detection,
128
+ * spawned `claude -p …`, and the menu — sees the token without the user
129
+ * needing to export it manually.
130
+ *
131
+ * Never throws.
132
+ */
133
+ export async function applyStoredCredentials(env, homeDir) {
134
+ try {
135
+ // Never overwrite an explicitly-set env var — user's env wins.
136
+ if (env['CLAUDE_CODE_OAUTH_TOKEN'] !== undefined) {
137
+ return;
138
+ }
139
+ const creds = await loadCredentials(homeDir);
140
+ if (creds.claudeOauthToken !== undefined) {
141
+ env['CLAUDE_CODE_OAUTH_TOKEN'] = creds.claudeOauthToken;
142
+ }
143
+ }
144
+ catch {
145
+ // Never throws — startup injection is best-effort
146
+ }
147
+ }
148
+ // ---------------------------------------------------------------------------
149
+ // Pure token extraction helper
150
+ // ---------------------------------------------------------------------------
151
+ /**
152
+ * Extract the first Claude long-lived OAuth token from `text`.
153
+ *
154
+ * Token format: `sk-ant-oat` followed by optional digits/lowercase-letters,
155
+ * then a dash, then one or more Base64url characters (`[A-Za-z0-9_-]+`).
156
+ *
157
+ * Returns `null` when no token is found. Never throws.
158
+ *
159
+ * @example
160
+ * extractClaudeToken('Token: sk-ant-oat01-abc-XYZ123') // → 'sk-ant-oat01-abc-XYZ123'
161
+ * extractClaudeToken('no token here') // → null
162
+ */
163
+ export function extractClaudeToken(text) {
164
+ try {
165
+ const match = text.match(/sk-ant-oat[0-9a-z]*-[A-Za-z0-9_-]+/);
166
+ return match !== null && match[0] !== undefined ? match[0] : null;
167
+ }
168
+ catch {
169
+ return null;
170
+ }
171
+ }
172
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/infra/credentials.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@
10
10
  * Captured : 2026-05-29
11
11
  */
12
12
  export interface ModelPricing {
13
- readonly provider: 'claude' | 'codex';
13
+ readonly provider: 'claude' | 'codex' | 'opencode';
14
14
  readonly model: string;
15
15
  readonly aliases: readonly string[];
16
16
  readonly tier: 'worker' | 'ic' | 'manager';
@@ -35,11 +35,24 @@ export declare function getModelPricing(provider: string, model: string): ModelP
35
35
  export declare function calculateCost(inputTokens: number, outputTokens: number, pricing: ModelPricing): number;
36
36
  /**
37
37
  * Return the cheapest model (lowest inputPer1M) for a given tier,
38
- * optionally restricted to the supplied provider IDs.
38
+ * optionally restricted to the supplied provider IDs and/or an allowed-model set.
39
39
  *
40
- * Throws if no matching model exists.
40
+ * When `allowedModels` is supplied and non-empty for the relevant provider(s),
41
+ * only models whose `model` id or any alias appears in `allowedModels` are
42
+ * considered. The match is case-insensitive (mirrors getModelPricing behaviour).
43
+ * If no candidates survive the allowed-model filter, the filter is ignored and
44
+ * the full provider-scoped set is used (graceful degradation — never throws due
45
+ * to a missing advertised model).
46
+ *
47
+ * Throws if no matching model exists (no tier entries at all, or no entries for
48
+ * the given providers).
49
+ *
50
+ * @param tier - Orchestration tier to select for.
51
+ * @param availableProviders - Restrict to these provider IDs when supplied.
52
+ * @param allowedModels - Further restrict to models advertised by the CLI.
53
+ * The set contains model IDs and/or aliases (any case).
41
54
  */
42
- export declare function getCheapestForTier(tier: 'worker' | 'ic' | 'manager', availableProviders?: string[]): ModelPricing;
55
+ export declare function getCheapestForTier(tier: 'worker' | 'ic' | 'manager', availableProviders?: string[], allowedModels?: readonly string[]): ModelPricing;
43
56
  /**
44
57
  * Returns true when the pricing table is older than maxAgeDays (default 90).
45
58
  * Useful for emitting a staleness warning at runtime.
@@ -17,6 +17,7 @@ export const PRICING_TABLE = {
17
17
  sourceUrls: [
18
18
  'https://www.anthropic.com/pricing',
19
19
  'https://platform.openai.com/docs/pricing',
20
+ 'https://opencode.ai/docs',
20
21
  ],
21
22
  models: [
22
23
  // ---- Anthropic / Claude ------------------------------------------------
@@ -93,6 +94,48 @@ export const PRICING_TABLE = {
93
94
  outputPer1M: 14,
94
95
  contextWindow: 128_000,
95
96
  },
97
+ // ---- opencode ----------------------------------------------------------
98
+ // opencode ships free models whose real cost is reported at runtime via the
99
+ // step_finish `cost` field in JSONL output (see opencode.ts). The zero-cost
100
+ // entries below exist solely for model SELECTION by getCheapestForTier/route
101
+ // when opencode is the only available provider. They must NOT displace
102
+ // claude/codex in the pricing sort when those providers are also available,
103
+ // because opencode's cost=0 entries would always win. The route() function
104
+ // respects providerOrderByTier (opencode last) before falling back to the
105
+ // pricing table, so this zero-cost sentinel is safe.
106
+ {
107
+ provider: 'opencode',
108
+ model: 'opencode/mimo-v2.5-free',
109
+ aliases: ['mimo-v2.5-free', 'opencode-worker'],
110
+ tier: 'worker',
111
+ // Real cost is reported at runtime by opencode's step_finish event.
112
+ // Zero here is a placeholder for model selection only — not for billing.
113
+ inputPer1M: 0,
114
+ outputPer1M: 0,
115
+ contextWindow: 32_000,
116
+ },
117
+ {
118
+ provider: 'opencode',
119
+ model: 'opencode/deepseek-v4-flash-free',
120
+ aliases: ['deepseek-v4-flash-free', 'opencode-free'],
121
+ tier: 'ic',
122
+ // Real cost is reported at runtime by opencode's step_finish event.
123
+ // Zero here is a placeholder for model selection only — not for billing.
124
+ inputPer1M: 0,
125
+ outputPer1M: 0,
126
+ contextWindow: 128_000,
127
+ },
128
+ {
129
+ provider: 'opencode',
130
+ model: 'opencode/big-pickle',
131
+ aliases: ['big-pickle', 'opencode-manager'],
132
+ tier: 'manager',
133
+ // Real cost is reported at runtime by opencode's step_finish event.
134
+ // Zero here is a placeholder for model selection only — not for billing.
135
+ inputPer1M: 0,
136
+ outputPer1M: 0,
137
+ contextWindow: 128_000,
138
+ },
96
139
  ],
97
140
  };
98
141
  // ---------------------------------------------------------------------------
@@ -118,11 +161,24 @@ export function calculateCost(inputTokens, outputTokens, pricing) {
118
161
  }
119
162
  /**
120
163
  * Return the cheapest model (lowest inputPer1M) for a given tier,
121
- * optionally restricted to the supplied provider IDs.
164
+ * optionally restricted to the supplied provider IDs and/or an allowed-model set.
122
165
  *
123
- * Throws if no matching model exists.
166
+ * When `allowedModels` is supplied and non-empty for the relevant provider(s),
167
+ * only models whose `model` id or any alias appears in `allowedModels` are
168
+ * considered. The match is case-insensitive (mirrors getModelPricing behaviour).
169
+ * If no candidates survive the allowed-model filter, the filter is ignored and
170
+ * the full provider-scoped set is used (graceful degradation — never throws due
171
+ * to a missing advertised model).
172
+ *
173
+ * Throws if no matching model exists (no tier entries at all, or no entries for
174
+ * the given providers).
175
+ *
176
+ * @param tier - Orchestration tier to select for.
177
+ * @param availableProviders - Restrict to these provider IDs when supplied.
178
+ * @param allowedModels - Further restrict to models advertised by the CLI.
179
+ * The set contains model IDs and/or aliases (any case).
124
180
  */
125
- export function getCheapestForTier(tier, availableProviders) {
181
+ export function getCheapestForTier(tier, availableProviders, allowedModels) {
126
182
  let candidates = PRICING_TABLE.models.filter((m) => m.tier === tier);
127
183
  if (availableProviders && availableProviders.length > 0) {
128
184
  candidates = candidates.filter((m) => availableProviders.includes(m.provider));
@@ -131,6 +187,20 @@ export function getCheapestForTier(tier, availableProviders) {
131
187
  throw new Error(`No models available for tier "${tier}"` +
132
188
  (availableProviders ? ` with providers [${availableProviders.join(', ')}]` : ''));
133
189
  }
190
+ // When an allowed-model set is provided and non-empty, further restrict
191
+ // candidates to those whose model id or any alias appears in the set.
192
+ // Case-insensitive to match getModelPricing behaviour.
193
+ if (allowedModels !== undefined && allowedModels.length > 0) {
194
+ const allowed = new Set(allowedModels.map((a) => a.toLowerCase()));
195
+ const filtered = candidates.filter((m) => allowed.has(m.model.toLowerCase()) ||
196
+ m.aliases.some((a) => allowed.has(a.toLowerCase())));
197
+ // Graceful degradation: if the filter eliminates all candidates (e.g. the
198
+ // provider advertised a model not yet in our pricing table), fall back to the
199
+ // full provider-scoped set — never throw, never return nothing.
200
+ if (filtered.length > 0) {
201
+ candidates = filtered;
202
+ }
203
+ }
134
204
  // Primary sort: inputPer1M ascending; secondary: outputPer1M ascending
135
205
  return candidates.reduce((cheapest, m) => m.inputPer1M < cheapest.inputPer1M ||
136
206
  (m.inputPer1M === cheapest.inputPer1M && m.outputPer1M < cheapest.outputPer1M)
@@ -1 +1 @@
1
- {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/infra/pricing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/infra/pricing.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ import type { SpendSummary } from '../infra/insights.js';
22
22
  import type { EnvironmentStatus } from '../providers/detect.js';
23
23
  import type { Provider, ProviderId, SandboxLevel } from '../providers/port.js';
24
24
  import type { OutputSink } from './render.js';
25
+ import type { LoginMethod } from '../commands/login.js';
25
26
  export interface MenuContext {
26
27
  readonly version: string;
27
28
  readonly clock: Clock;
@@ -41,6 +42,31 @@ export interface MenuContext {
41
42
  * Returns the next trimmed line of input, or `null` on EOF/close.
42
43
  */
43
44
  readonly readLine?: () => Promise<string | null>;
45
+ /**
46
+ * Optional injected installProvider for testing. When provided, `startMenu`
47
+ * uses this instead of the real `installProvider` from providers/install.ts,
48
+ * preventing real `npm install -g …` subprocesses from spawning during tests.
49
+ */
50
+ readonly installProvider?: (id: ProviderId, out: OutputSink) => Promise<boolean>;
51
+ /**
52
+ * Optional injected login function for testing. When provided, `startMenu`
53
+ * uses this instead of the real `runLogin` from commands/login.ts, preventing
54
+ * real `claude`/`codex login` subprocesses from spawning during tests.
55
+ *
56
+ * The third argument mirrors the `opts` parameter of `runLogin` so the menu
57
+ * can pass the shared `readLine` function for the token-paste prompt.
58
+ */
59
+ readonly login?: (out: OutputSink, providerArg?: string, opts?: {
60
+ method?: LoginMethod;
61
+ readLine?: () => Promise<string | null>;
62
+ }) => Promise<number>;
63
+ /**
64
+ * Optional injected detectEnvironment for testing. When provided, `startMenu`
65
+ * uses this instead of the real `detectEnvironment` from providers/detect.ts,
66
+ * preventing real `claude`/`codex`/`opencode --version` subprocesses from
67
+ * spawning during tests (e.g. after first-run onboarding or [j]/[k]/[o] login).
68
+ */
69
+ readonly detectEnvironment?: () => Promise<EnvironmentStatus>;
44
70
  }
45
71
  /**
46
72
  * Return the shell alias hint the user can add to their shell profile to make