@zhihaohong52/sonata 0.4.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 (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +437 -0
  3. package/dist/adapters/claude.js +83 -0
  4. package/dist/adapters/claude.js.map +1 -0
  5. package/dist/adapters/codex.js +382 -0
  6. package/dist/adapters/codex.js.map +1 -0
  7. package/dist/adapters/index.js +21 -0
  8. package/dist/adapters/index.js.map +1 -0
  9. package/dist/adapters/opencode.js +104 -0
  10. package/dist/adapters/opencode.js.map +1 -0
  11. package/dist/adapters/pi.js +152 -0
  12. package/dist/adapters/pi.js.map +1 -0
  13. package/dist/adapters/reasonix.js +358 -0
  14. package/dist/adapters/reasonix.js.map +1 -0
  15. package/dist/adapters/types.js +2 -0
  16. package/dist/adapters/types.js.map +1 -0
  17. package/dist/agent-markers.js +3 -0
  18. package/dist/agent-markers.js.map +1 -0
  19. package/dist/aipricing.js +109 -0
  20. package/dist/aipricing.js.map +1 -0
  21. package/dist/catalog.js +274 -0
  22. package/dist/catalog.js.map +1 -0
  23. package/dist/cli.js +766 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/commands/approve.js +23 -0
  26. package/dist/commands/approve.js.map +1 -0
  27. package/dist/commands/auth.js +41 -0
  28. package/dist/commands/auth.js.map +1 -0
  29. package/dist/commands/catalog.js +172 -0
  30. package/dist/commands/catalog.js.map +1 -0
  31. package/dist/commands/code.js +113 -0
  32. package/dist/commands/code.js.map +1 -0
  33. package/dist/commands/dispatch.js +148 -0
  34. package/dist/commands/dispatch.js.map +1 -0
  35. package/dist/commands/doctor.js +545 -0
  36. package/dist/commands/doctor.js.map +1 -0
  37. package/dist/commands/gc.js +25 -0
  38. package/dist/commands/gc.js.map +1 -0
  39. package/dist/commands/init-log.js +83 -0
  40. package/dist/commands/init-log.js.map +1 -0
  41. package/dist/commands/init.js +157 -0
  42. package/dist/commands/init.js.map +1 -0
  43. package/dist/commands/log.js +24 -0
  44. package/dist/commands/log.js.map +1 -0
  45. package/dist/commands/route.js +660 -0
  46. package/dist/commands/route.js.map +1 -0
  47. package/dist/commands/run.js +199 -0
  48. package/dist/commands/run.js.map +1 -0
  49. package/dist/commands/runs.js +37 -0
  50. package/dist/commands/runs.js.map +1 -0
  51. package/dist/commands/serve.js +818 -0
  52. package/dist/commands/serve.js.map +1 -0
  53. package/dist/commands/status.js +14 -0
  54. package/dist/commands/status.js.map +1 -0
  55. package/dist/commands/sync.js +258 -0
  56. package/dist/commands/sync.js.map +1 -0
  57. package/dist/commands/tail.js +229 -0
  58. package/dist/commands/tail.js.map +1 -0
  59. package/dist/commands/usage.js +85 -0
  60. package/dist/commands/usage.js.map +1 -0
  61. package/dist/commands/verify.js +69 -0
  62. package/dist/commands/verify.js.map +1 -0
  63. package/dist/commands/wait.js +38 -0
  64. package/dist/commands/wait.js.map +1 -0
  65. package/dist/config.js +588 -0
  66. package/dist/config.js.map +1 -0
  67. package/dist/detect.js +445 -0
  68. package/dist/detect.js.map +1 -0
  69. package/dist/filelock.js +83 -0
  70. package/dist/filelock.js.map +1 -0
  71. package/dist/init/apply.js +96 -0
  72. package/dist/init/apply.js.map +1 -0
  73. package/dist/init/candidates.js +115 -0
  74. package/dist/init/candidates.js.map +1 -0
  75. package/dist/init/discover.js +216 -0
  76. package/dist/init/discover.js.map +1 -0
  77. package/dist/init/helpers.js +429 -0
  78. package/dist/init/helpers.js.map +1 -0
  79. package/dist/init/interactive-state.js +128 -0
  80. package/dist/init/interactive-state.js.map +1 -0
  81. package/dist/init/plan.js +225 -0
  82. package/dist/init/plan.js.map +1 -0
  83. package/dist/init/scripted-state.js +130 -0
  84. package/dist/init/scripted-state.js.map +1 -0
  85. package/dist/init/toml.js +83 -0
  86. package/dist/init/toml.js.map +1 -0
  87. package/dist/init/validate.js +149 -0
  88. package/dist/init/validate.js.map +1 -0
  89. package/dist/ledger.js +123 -0
  90. package/dist/ledger.js.map +1 -0
  91. package/dist/mcp/protocol.js +64 -0
  92. package/dist/mcp/protocol.js.map +1 -0
  93. package/dist/mcp/server.js +96 -0
  94. package/dist/mcp/server.js.map +1 -0
  95. package/dist/mcp/tools.js +210 -0
  96. package/dist/mcp/tools.js.map +1 -0
  97. package/dist/mode.js +64 -0
  98. package/dist/mode.js.map +1 -0
  99. package/dist/native/claude-shim.js +228 -0
  100. package/dist/native/claude-shim.js.map +1 -0
  101. package/dist/native/codex-auth.js +177 -0
  102. package/dist/native/codex-auth.js.map +1 -0
  103. package/dist/native/copilot-auth.js +105 -0
  104. package/dist/native/copilot-auth.js.map +1 -0
  105. package/dist/native/credentials.js +83 -0
  106. package/dist/native/credentials.js.map +1 -0
  107. package/dist/native/hosts.js +60 -0
  108. package/dist/native/hosts.js.map +1 -0
  109. package/dist/native/litellm.js +103 -0
  110. package/dist/native/litellm.js.map +1 -0
  111. package/dist/native/models.js +96 -0
  112. package/dist/native/models.js.map +1 -0
  113. package/dist/native/oauth-login.js +112 -0
  114. package/dist/native/oauth-login.js.map +1 -0
  115. package/dist/native/router.js +469 -0
  116. package/dist/native/router.js.map +1 -0
  117. package/dist/native/tls.js +73 -0
  118. package/dist/native/tls.js.map +1 -0
  119. package/dist/native/usage.js +119 -0
  120. package/dist/native/usage.js.map +1 -0
  121. package/dist/normalize.js +98 -0
  122. package/dist/normalize.js.map +1 -0
  123. package/dist/pricing.js +86 -0
  124. package/dist/pricing.js.map +1 -0
  125. package/dist/roles.js +49 -0
  126. package/dist/roles.js.map +1 -0
  127. package/dist/sessions.js +62 -0
  128. package/dist/sessions.js.map +1 -0
  129. package/dist/settings.js +131 -0
  130. package/dist/settings.js.map +1 -0
  131. package/dist/store.js +70 -0
  132. package/dist/store.js.map +1 -0
  133. package/dist/tmux.js +89 -0
  134. package/dist/tmux.js.map +1 -0
  135. package/dist/tui-ink/app-state.js +340 -0
  136. package/dist/tui-ink/app-state.js.map +1 -0
  137. package/dist/tui-ink/app.js +156 -0
  138. package/dist/tui-ink/app.js.map +1 -0
  139. package/dist/tui-ink/components/byok-step.js +102 -0
  140. package/dist/tui-ink/components/byok-step.js.map +1 -0
  141. package/dist/tui-ink/components/login-screen.js +80 -0
  142. package/dist/tui-ink/components/login-screen.js.map +1 -0
  143. package/dist/tui-ink/components/models-step.js +92 -0
  144. package/dist/tui-ink/components/models-step.js.map +1 -0
  145. package/dist/tui-ink/components/multi-select-state.js +56 -0
  146. package/dist/tui-ink/components/multi-select-state.js.map +1 -0
  147. package/dist/tui-ink/components/multi-select.js +69 -0
  148. package/dist/tui-ink/components/multi-select.js.map +1 -0
  149. package/dist/tui-ink/components/providers-step.js +285 -0
  150. package/dist/tui-ink/components/providers-step.js.map +1 -0
  151. package/dist/tui-ink/components/ranked-select-state.js +41 -0
  152. package/dist/tui-ink/components/ranked-select-state.js.map +1 -0
  153. package/dist/tui-ink/components/ranked-select.js +61 -0
  154. package/dist/tui-ink/components/ranked-select.js.map +1 -0
  155. package/dist/tui-ink/components/search-select.js +47 -0
  156. package/dist/tui-ink/components/search-select.js.map +1 -0
  157. package/dist/tui-ink/components/text-input-state.js +42 -0
  158. package/dist/tui-ink/components/text-input-state.js.map +1 -0
  159. package/dist/tui-ink/components/text-input.js +48 -0
  160. package/dist/tui-ink/components/text-input.js.map +1 -0
  161. package/dist/tui-ink/run.js +38 -0
  162. package/dist/tui-ink/run.js.map +1 -0
  163. package/dist/tui-ink/test-compile.js +4 -0
  164. package/dist/tui-ink/test-compile.js.map +1 -0
  165. package/dist/tui-ink/types.js +2 -0
  166. package/dist/tui-ink/types.js.map +1 -0
  167. package/dist/tui.js +330 -0
  168. package/dist/tui.js.map +1 -0
  169. package/dist/types.js +2 -0
  170. package/dist/types.js.map +1 -0
  171. package/dist/watchdog.js +85 -0
  172. package/dist/watchdog.js.map +1 -0
  173. package/hooks/capture-mode.mjs +56 -0
  174. package/hooks/ensure-serve.mjs +140 -0
  175. package/hooks/hooks.json +12 -0
  176. package/hooks/route-session.mjs +47 -0
  177. package/hooks/route-subagent.mjs +54 -0
  178. package/package.json +64 -0
  179. package/roles/code.md +13 -0
  180. package/roles/explore.md +9 -0
  181. package/roles/plan.md +10 -0
  182. package/roles/review.md +11 -0
  183. package/skills/loop/SKILL.md +43 -0
@@ -0,0 +1,228 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { closeSync, copyFileSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync, unlinkSync, writeFileSync, writeSync, } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ /**
5
+ * Remote Control (control-from-phone via claude.ai/code) is gated inside Claude
6
+ * Code itself by `isFirstPartyAnthropicBaseUrl`: the literal ANTHROPIC_BASE_URL
7
+ * string must host-equal api.anthropic.com, and no credential combination lifts
8
+ * that — ANTHROPIC_UNIX_SOCKET trips the auth gate ("requires claude.ai
9
+ * subscription auth") and CLAUDE_CODE_OAUTH_TOKEN trips a stronger one
10
+ * ("setup-token tokens are inference-only"). Verified against 2.1.241.
11
+ *
12
+ * Sonata's router forwards claude-* requests to the real API unmodified, so the
13
+ * only thing standing between a routed session and Remote Control is that
14
+ * client-side string comparison. This module builds a private clone of the
15
+ * installed claude binary with the host check short-circuited to true, and
16
+ * launches that clone from `sonata code`.
17
+ *
18
+ * The clone is a copy (cp -c, APFS-friendly) of the versioned binary under
19
+ * ~/.local/share/claude/versions/, so the user's own `claude` — and every
20
+ * session outside sonata — keeps running the stock binary. The patch is
21
+ * same-length, applied to the clone only, and re-signed ad hoc because macOS
22
+ * arm64 refuses to exec binaries whose signature no longer matches their
23
+ * content.
24
+ */
25
+ const HOST_CHECK = 'return["api.anthropic.com"].includes(';
26
+ const PATCH_MARKER = 'sonata-rc-host-check';
27
+ /**
28
+ * Rewrites `return["api.anthropic.com"].includes(<ident>)` into a constant
29
+ * `true` of exactly the same byte length, so every offset after the patch site
30
+ * stays put. Returns null when the shape is not found — a future Claude Code
31
+ * that restructures the check must fail loudly here rather than produce a
32
+ * silently unpatched clone.
33
+ */
34
+ export function patchHostCheck(binary) {
35
+ const anchor = Buffer.from(HOST_CHECK, 'utf8');
36
+ const offset = binary.indexOf(anchor);
37
+ if (offset < 0)
38
+ return null;
39
+ // Sanity: this lives at the end of `let t=new URL(e).host;<here>` — refuse to
40
+ // patch an unrelated occurrence of the anchor string.
41
+ if (binary.subarray(Math.max(0, offset - 6), offset).toString('latin1') !== '.host;') {
42
+ return null;
43
+ }
44
+ // Consume the identifier and the closing paren: HOST_CHECK ends mid-statement.
45
+ let end = offset + anchor.length;
46
+ while (end < binary.length && binary[end] !== 0x29 /* ) */)
47
+ end += 1;
48
+ if (end >= binary.length)
49
+ return null;
50
+ end += 1;
51
+ const length = end - offset;
52
+ const replacement = truePaddedTo(length);
53
+ if (!replacement)
54
+ return null;
55
+ const patched = Buffer.from(binary);
56
+ replacement.copy(patched, offset);
57
+ return { patched, offset, length };
58
+ }
59
+ /** `return!0` padded out to `length` bytes with a comment carrying the marker. */
60
+ function truePaddedTo(length) {
61
+ const head = Buffer.from(`return!0/*${PATCH_MARKER}`, 'utf8');
62
+ if (head.length + 2 > length)
63
+ return null;
64
+ return Buffer.concat([head, Buffer.alloc(length - head.length - 2, 0x78 /* x */), Buffer.from('*/', 'utf8')]);
65
+ }
66
+ /**
67
+ * Finds the real claude executable behind the many shims a machine may carry
68
+ * (cmux wrappers, npm launchers). Only the native install — versioned
69
+ * self-contained binaries under ~/.local/share/claude/versions/ — can be
70
+ * cloned-and-patched; anything else returns null and sonata code falls back to
71
+ * plain `claude` with Remote Control off.
72
+ */
73
+ export function resolveNativeClaude(home, env = process.env) {
74
+ const override = env.SONATA_CLAUDE_BIN;
75
+ if (override && existsSync(override))
76
+ return { path: realpathSync(override) };
77
+ const binLink = join(home, '.local', 'bin', 'claude');
78
+ if (existsSync(binLink)) {
79
+ try {
80
+ const real = realpathSync(binLink);
81
+ if (real.includes(`${sep()}versions${sep()}`))
82
+ return { path: real };
83
+ }
84
+ catch {
85
+ // Unreadable link: fall through to the versions directory.
86
+ }
87
+ }
88
+ const versionsDir = join(home, '.local', 'share', 'claude', 'versions');
89
+ if (!existsSync(versionsDir))
90
+ return null;
91
+ const versions = readdirSync(versionsDir)
92
+ .filter((name) => /^\d+\.\d+\.\d+$/.test(name))
93
+ .sort(compareSemver);
94
+ const newest = versions.at(-1);
95
+ return newest ? { path: join(versionsDir, newest) } : null;
96
+ }
97
+ function sep() {
98
+ return process.platform === 'win32' ? '\\' : '/';
99
+ }
100
+ function compareSemver(a, b) {
101
+ const pa = a.split('.').map(Number);
102
+ const pb = b.split('.').map(Number);
103
+ for (let i = 0; i < 3; i += 1) {
104
+ if ((pa[i] ?? 0) !== (pb[i] ?? 0))
105
+ return (pa[i] ?? 0) - (pb[i] ?? 0);
106
+ }
107
+ return 0;
108
+ }
109
+ export function shimDir(home) {
110
+ return join(home, '.config', 'sonata', 'claude-shim');
111
+ }
112
+ function defaultSign(path) {
113
+ if (process.platform !== 'darwin')
114
+ return;
115
+ const result = spawnSync('codesign', ['--force', '--sign', '-', path], { stdio: 'ignore' });
116
+ if (result.status !== 0) {
117
+ throw new Error(`codesign failed for ${path} — macOS will refuse to exec an unsigned modified binary`);
118
+ }
119
+ }
120
+ /**
121
+ * Returns the path to a patched clone of the native claude binary, building it
122
+ * when the source version changed. Null means "cannot provide" — caller falls
123
+ * back to the stock binary and Remote Control stays off, exactly as before this
124
+ * module existed.
125
+ */
126
+ export function ensureRemoteControlCapableClaude(deps) {
127
+ const resolved = resolveNativeClaude(deps.home, deps.env);
128
+ if (!resolved)
129
+ return null;
130
+ const source = resolved.path;
131
+ const version = source.split(sep()).at(-1) ?? 'claude';
132
+ const clonePath = join(shimDir(deps.home), version);
133
+ const metaPath = `${clonePath}.json`;
134
+ const stat = statSync(source);
135
+ if (existsSync(clonePath) && existsSync(metaPath)) {
136
+ try {
137
+ const meta = JSON.parse(readFileSync(metaPath, 'utf8'));
138
+ if (meta.sourceSize === stat.size &&
139
+ meta.sourceMtimeMs === stat.mtimeMs &&
140
+ statSync(clonePath).size === stat.size &&
141
+ probeMarker(clonePath, meta)) {
142
+ return clonePath;
143
+ }
144
+ }
145
+ catch {
146
+ // Corrupt meta or clone: rebuild below.
147
+ }
148
+ }
149
+ mkdirSync(shimDir(deps.home), { recursive: true });
150
+ // cp -c uses APFS cloning where available; a plain copy is the fallback
151
+ // elsewhere. Either way the clone diverges from the stock binary only at the
152
+ // patched statement.
153
+ const cloned = spawnSync('cp', ['-c', source, clonePath], { stdio: 'ignore' });
154
+ if (cloned.status !== 0 || !existsSync(clonePath)) {
155
+ try {
156
+ if (existsSync(clonePath))
157
+ unlinkSync(clonePath);
158
+ copyFileSync(source, clonePath);
159
+ }
160
+ catch (error) {
161
+ deps.log?.(`sonata: could not clone ${source}: ${error.message}`);
162
+ return null;
163
+ }
164
+ }
165
+ const binary = readFileSync(source);
166
+ const result = patchHostCheck(binary);
167
+ if (!result) {
168
+ // Leave no unpatched clone lying around to be mistaken for a good one.
169
+ try {
170
+ if (existsSync(clonePath))
171
+ unlinkSync(clonePath);
172
+ if (existsSync(metaPath))
173
+ unlinkSync(metaPath);
174
+ }
175
+ catch {
176
+ // Best-effort cleanup.
177
+ }
178
+ deps.log?.(`sonata: ${version} no longer contains the known Remote Control gate; leaving it intact`);
179
+ return null;
180
+ }
181
+ const fd = openSync(clonePath, 'r+');
182
+ try {
183
+ const statement = result.patched.subarray(result.offset, result.offset + result.length);
184
+ writeSync(fd, statement, 0, statement.length, result.offset);
185
+ }
186
+ finally {
187
+ closeQuietly(fd);
188
+ }
189
+ const sign = deps.sign ?? defaultSign;
190
+ try {
191
+ sign(clonePath);
192
+ }
193
+ catch (error) {
194
+ deps.log?.(`sonata: ${error.message}`);
195
+ return null;
196
+ }
197
+ const meta = {
198
+ sourceSize: stat.size,
199
+ sourceMtimeMs: stat.mtimeMs,
200
+ offset: result.offset,
201
+ length: result.length,
202
+ };
203
+ writeFileSync(metaPath, JSON.stringify(meta));
204
+ return clonePath;
205
+ }
206
+ function probeMarker(path, meta) {
207
+ const fd = openSync(path, 'r');
208
+ try {
209
+ const buffer = Buffer.alloc(meta.length);
210
+ const read = readSync(fd, buffer, 0, buffer.length, meta.offset);
211
+ return read === buffer.length && buffer.toString('latin1').startsWith('return!0/*');
212
+ }
213
+ catch {
214
+ return false;
215
+ }
216
+ finally {
217
+ closeQuietly(fd);
218
+ }
219
+ }
220
+ function closeQuietly(fd) {
221
+ try {
222
+ closeSync(fd);
223
+ }
224
+ catch {
225
+ // Already closed or invalid: nothing to salvage.
226
+ }
227
+ }
228
+ //# sourceMappingURL=claude-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-shim.js","sourceRoot":"","sources":["../../src/native/claude-shim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Bridges a `codex login` credential into the shape LiteLLM's `chatgpt`
3
+ * provider reads.
4
+ *
5
+ * Codex stores its ChatGPT OAuth tokens nested under `tokens`; LiteLLM expects
6
+ * them flat, alongside an `expires_at` it would otherwise have to derive. The
7
+ * two formats are otherwise the same credential, so this is a re-shaping, not a
8
+ * copy of a secret into a new trust domain — the file is written 0600 into
9
+ * serve's own temp directory and removed when serve stops.
10
+ *
11
+ * Nothing here logs a token. Callers that report health use `codexAuthReport`,
12
+ * which carries no secret material.
13
+ */
14
+ import { readFileSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ export function codexAuthPath(home) {
17
+ return join(home, '.codex', 'auth.json');
18
+ }
19
+ export function opencodeAuthPath(home) {
20
+ return join(home, '.local', 'share', 'opencode', 'auth.json');
21
+ }
22
+ /**
23
+ * The OAuth app both codex and opencode use for a ChatGPT login.
24
+ *
25
+ * Checked before treating an opencode `openai` entry as a ChatGPT credential:
26
+ * that key could in principle hold some other OpenAI OAuth grant, and handing a
27
+ * non-subscription token to the chatgpt provider would fail in the confusing
28
+ * way this whole module exists to avoid.
29
+ */
30
+ export const CHATGPT_CLIENT_ID = 'app_EMoamEEZ73f0CkXaXp7hrann';
31
+ /** The `exp` claim of a JWT, without verifying the signature. */
32
+ export function jwtExpiry(token) {
33
+ const payload = token.split('.')[1];
34
+ if (payload === undefined)
35
+ return undefined;
36
+ try {
37
+ const padded = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), '=');
38
+ const claims = JSON.parse(Buffer.from(padded, 'base64url').toString('utf8'));
39
+ if (claims === null || typeof claims !== 'object')
40
+ return undefined;
41
+ const exp = claims.exp;
42
+ return typeof exp === 'number' ? exp : undefined;
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ }
48
+ function str(value) {
49
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
50
+ }
51
+ /**
52
+ * The flattened credential, or null when codex is not logged in with a
53
+ * subscription. A refresh token alone is enough for LiteLLM to mint a new
54
+ * access token, so an expired access token is not fatal here.
55
+ */
56
+ export function readCodexOAuth(home) {
57
+ let raw;
58
+ try {
59
+ raw = JSON.parse(readFileSync(codexAuthPath(home), 'utf8'));
60
+ }
61
+ catch {
62
+ return null;
63
+ }
64
+ if (raw === null || typeof raw !== 'object')
65
+ return null;
66
+ const tokens = raw.tokens;
67
+ if (tokens === null || typeof tokens !== 'object')
68
+ return null;
69
+ const t = tokens;
70
+ const accessToken = str(t.access_token);
71
+ if (accessToken === undefined)
72
+ return null;
73
+ return {
74
+ access_token: accessToken,
75
+ refresh_token: str(t.refresh_token),
76
+ id_token: str(t.id_token),
77
+ expires_at: jwtExpiry(accessToken),
78
+ account_id: str(t.account_id),
79
+ };
80
+ }
81
+ /** The `client_id` claim of a JWT, used to identify which OAuth app issued it. */
82
+ function jwtClientId(token) {
83
+ const payload = token.split('.')[1];
84
+ if (payload === undefined)
85
+ return undefined;
86
+ try {
87
+ const padded = payload.padEnd(payload.length + ((4 - (payload.length % 4)) % 4), '=');
88
+ const claims = JSON.parse(Buffer.from(padded, 'base64url').toString('utf8'));
89
+ if (claims === null || typeof claims !== 'object')
90
+ return undefined;
91
+ const id = claims.client_id;
92
+ return typeof id === 'string' ? id : undefined;
93
+ }
94
+ catch {
95
+ return undefined;
96
+ }
97
+ }
98
+ /**
99
+ * The ChatGPT credential opencode stores under `openai`.
100
+ *
101
+ * opencode writes `{type: "oauth", access, refresh, expires, accountId}` — the
102
+ * same subscription credential codex holds, under different field names. It is
103
+ * NOT an API key: `opencodeKeys` is right to skip it, because handing it to a
104
+ * gateway as a bearer produces a 429 from the metered API after the token has
105
+ * already authenticated.
106
+ */
107
+ export function readOpencodeChatGptOAuth(home) {
108
+ let raw;
109
+ try {
110
+ raw = JSON.parse(readFileSync(opencodeAuthPath(home), 'utf8'));
111
+ }
112
+ catch {
113
+ return null;
114
+ }
115
+ if (raw === null || typeof raw !== 'object')
116
+ return null;
117
+ const entry = raw.openai;
118
+ if (entry === null || typeof entry !== 'object')
119
+ return null;
120
+ const e = entry;
121
+ if (e.type !== 'oauth')
122
+ return null;
123
+ const accessToken = str(e.access);
124
+ if (accessToken === undefined)
125
+ return null;
126
+ if (jwtClientId(accessToken) !== CHATGPT_CLIENT_ID)
127
+ return null;
128
+ // opencode records `expires` in milliseconds; the JWT is authoritative.
129
+ const expires = typeof e.expires === 'number' ? Math.floor(e.expires / 1000) : undefined;
130
+ return {
131
+ access_token: accessToken,
132
+ refresh_token: str(e.refresh),
133
+ expires_at: jwtExpiry(accessToken) ?? expires,
134
+ account_id: str(e.accountId),
135
+ };
136
+ }
137
+ /**
138
+ * A ChatGPT credential from whichever harness has one.
139
+ *
140
+ * codex is preferred only because it is the harness the gateway is usually
141
+ * named after; either file yields the same subscription.
142
+ */
143
+ export function readChatGptOAuth(home, source) {
144
+ if (source === 'codex')
145
+ return readCodexOAuth(home);
146
+ if (source === 'opencode')
147
+ return readOpencodeChatGptOAuth(home);
148
+ return readCodexOAuth(home) ?? readOpencodeChatGptOAuth(home);
149
+ }
150
+ /** Health information about the codex credential, carrying no secret material. */
151
+ export function codexAuthReport(home, now = Date.now()) {
152
+ const fromCodex = readCodexOAuth(home);
153
+ const record = fromCodex ?? readOpencodeChatGptOAuth(home);
154
+ if (record === null) {
155
+ return {
156
+ present: false,
157
+ problem: 'no ChatGPT login found in codex or opencode — run `codex login`',
158
+ };
159
+ }
160
+ const source = fromCodex === null ? 'opencode' : 'codex';
161
+ let mode;
162
+ if (source === 'codex') {
163
+ try {
164
+ const raw = JSON.parse(readFileSync(codexAuthPath(home), 'utf8'));
165
+ mode = str(raw?.auth_mode);
166
+ }
167
+ catch { /* the record parsed, so this is only the label */ }
168
+ }
169
+ const expired = record.expires_at !== undefined && now / 1000 >= record.expires_at;
170
+ // LiteLLM refreshes on demand, so an expired access token with a refresh
171
+ // token is healthy; without one it needs a fresh login.
172
+ const problem = expired && record.refresh_token === undefined
173
+ ? `token expired and no refresh token — run \`${source === 'codex' ? 'codex login' : 'opencode auth login'}\``
174
+ : undefined;
175
+ return { present: true, source, mode, expiresAt: record.expires_at, expired, problem };
176
+ }
177
+ //# sourceMappingURL=codex-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-auth.js","sourceRoot":"","sources":["../../src/native/codex-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Bridges opencode's GitHub Copilot login into LiteLLM's `github_copilot`
3
+ * provider.
4
+ *
5
+ * opencode stores `{type: "oauth", access: "gho_…", refresh, expires}` under
6
+ * `github-copilot`. That `gho_` value is a GitHub OAuth token, **not** a
7
+ * Copilot API key: it has to be exchanged at
8
+ * `api.github.com/copilot_internal/v2/token` for a short-lived key. LiteLLM's
9
+ * provider performs that exchange and caches the result, reading the GitHub
10
+ * token from a plain-text `access-token` file in `GITHUB_COPILOT_TOKEN_DIR`.
11
+ *
12
+ * So `opencodeKeys` is right to skip this entry — handing a `gho_` token to a
13
+ * gateway as a bearer would fail. It just must not be invisible, which is what
14
+ * this module fixes.
15
+ *
16
+ * Nothing here logs a token.
17
+ */
18
+ import { readFileSync } from 'node:fs';
19
+ import { opencodeAuthPath } from './codex-auth.js';
20
+ function str(value) {
21
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
22
+ }
23
+ function copilotEntry(home) {
24
+ let raw;
25
+ try {
26
+ raw = JSON.parse(readFileSync(opencodeAuthPath(home), 'utf8'));
27
+ }
28
+ catch {
29
+ return null;
30
+ }
31
+ if (raw === null || typeof raw !== 'object')
32
+ return null;
33
+ const entry = raw['github-copilot'];
34
+ if (entry === null || typeof entry !== 'object')
35
+ return null;
36
+ const e = entry;
37
+ return e.type === 'oauth' ? e : null;
38
+ }
39
+ /**
40
+ * The GitHub OAuth token opencode holds for Copilot, or null.
41
+ *
42
+ * This is the value LiteLLM writes into its `access-token` file; it is not
43
+ * usable directly against any model endpoint.
44
+ */
45
+ export function readCopilotToken(home) {
46
+ const entry = copilotEntry(home);
47
+ if (entry === null)
48
+ return null;
49
+ return str(entry.access) ?? null;
50
+ }
51
+ /** The scope GitHub requires before it will mint a Copilot key. */
52
+ export const COPILOT_SCOPE = 'copilot';
53
+ /**
54
+ * Whether the stored GitHub token may be exchanged for a Copilot key.
55
+ *
56
+ * It usually may not. opencode requests only `read:user`, so GitHub answers the
57
+ * exchange with 403 and LiteLLM silently drops the deployment — the request
58
+ * then fails as "no healthy deployments", naming neither the token nor the
59
+ * scope. Checking first is what keeps a credential that cannot work from being
60
+ * offered as if it could.
61
+ *
62
+ * Scopes are not in the token (it is opaque), so this asks GitHub, which
63
+ * returns them in `x-oauth-scopes`. **Fails closed**: any error, timeout or
64
+ * offline machine yields false, because "unknown" must not be treated as usable.
65
+ */
66
+ export async function copilotTokenCanExchange(token, opts = {}) {
67
+ const doFetch = opts.fetch ?? fetch;
68
+ try {
69
+ const response = await doFetch('https://api.github.com/user', {
70
+ headers: { Authorization: `Bearer ${token}` },
71
+ signal: AbortSignal.timeout(opts.timeoutMs ?? 5000),
72
+ });
73
+ if (!response.ok)
74
+ return false;
75
+ const scopes = (response.headers.get('x-oauth-scopes') ?? '')
76
+ .split(',')
77
+ .map((scope) => scope.trim());
78
+ return scopes.includes(COPILOT_SCOPE);
79
+ }
80
+ catch {
81
+ return false;
82
+ }
83
+ }
84
+ /** Health information, carrying no secret material. */
85
+ export function copilotAuthReport(home, now = Date.now()) {
86
+ const entry = copilotEntry(home);
87
+ if (entry === null || str(entry.access) === undefined) {
88
+ return {
89
+ present: false,
90
+ problem: 'no Copilot login in opencode — run `opencode auth login` and choose github-copilot',
91
+ };
92
+ }
93
+ // opencode records `expires` in milliseconds, and writes 0 for the Copilot
94
+ // entry to mean "does not expire" — reading that literally reported a live
95
+ // token as expired since 1970.
96
+ const expiresAt = typeof entry.expires === 'number' && entry.expires > 0
97
+ ? Math.floor(entry.expires / 1000)
98
+ : undefined;
99
+ const expired = expiresAt !== undefined && now / 1000 >= expiresAt;
100
+ // LiteLLM re-exchanges the GitHub token for a Copilot key on demand, and the
101
+ // GitHub token itself is long-lived; an expired *Copilot* key is not a problem
102
+ // the user has to act on. Only a missing token is.
103
+ return { present: true, expiresAt, expired };
104
+ }
105
+ //# sourceMappingURL=copilot-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copilot-auth.js","sourceRoot":"","sources":["../../src/native/copilot-auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { chmodSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ function readJson(path) {
4
+ try {
5
+ const value = JSON.parse(readFileSync(path, 'utf8'));
6
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
7
+ ? value
8
+ : {};
9
+ }
10
+ catch {
11
+ return {};
12
+ }
13
+ }
14
+ function usableKey(value) {
15
+ return typeof value === 'string' && value.trim() !== '' ? value : undefined;
16
+ }
17
+ function sonataKeys(home) {
18
+ return Object.fromEntries(Object.entries(readJson(sonataKeyStorePath(home))).flatMap(([gateway, key]) => {
19
+ const usable = usableKey(key);
20
+ return usable === undefined ? [] : [[gateway, usable]];
21
+ }));
22
+ }
23
+ function opencodeKeys(home) {
24
+ return Object.fromEntries(Object.entries(readJson(join(home, '.local/share/opencode/auth.json'))).flatMap(([gateway, value]) => {
25
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
26
+ return [];
27
+ const credential = value;
28
+ const key = usableKey(credential.key) ?? usableKey(credential.apiKey);
29
+ return key === undefined ? [] : [[gateway, key]];
30
+ }));
31
+ }
32
+ const SOURCES = [
33
+ { name: 'sonata', read: sonataKeys },
34
+ { name: 'opencode', read: opencodeKeys },
35
+ ];
36
+ export function sonataKeyStorePath(home) {
37
+ return join(home, '.config/sonata/keys.json');
38
+ }
39
+ export function resolveKeyFromSource(gateway, home, source) {
40
+ const reader = SOURCES.find((candidate) => candidate.name === source);
41
+ return reader?.read(home)[gateway];
42
+ }
43
+ export function resolveKeys(gateways, home) {
44
+ const sources = SOURCES.map((source) => ({ name: source.name, keys: source.read(home) }));
45
+ const resolved = [];
46
+ for (const gateway of new Set(gateways)) {
47
+ for (const source of sources) {
48
+ const key = source.keys[gateway];
49
+ if (key !== undefined) {
50
+ resolved.push({ gateway, source: source.name, key });
51
+ break;
52
+ }
53
+ }
54
+ }
55
+ return resolved;
56
+ }
57
+ export function keyReport(gateways, home) {
58
+ const sources = new Map(resolveKeys(gateways, home).map(({ gateway, source }) => [gateway, source]));
59
+ return gateways.map((gateway) => ({ gateway, source: sources.get(gateway) ?? null }));
60
+ }
61
+ export function writeSonataKey(home, gateway, key) {
62
+ const path = sonataKeyStorePath(home);
63
+ mkdirSync(join(home, '.config/sonata'), { recursive: true });
64
+ writeFileSync(path, `${JSON.stringify({ ...sonataKeys(home), [gateway]: key }, null, 2)}\n`, { mode: 0o600 });
65
+ chmodSync(path, 0o600);
66
+ }
67
+ export function removeSonataKey(home, gateway) {
68
+ const path = sonataKeyStorePath(home);
69
+ const keys = sonataKeys(home);
70
+ delete keys[gateway];
71
+ if (Object.keys(keys).length === 0) {
72
+ try {
73
+ unlinkSync(path);
74
+ }
75
+ catch {
76
+ // A missing store has the same result as an empty one.
77
+ }
78
+ return;
79
+ }
80
+ writeFileSync(path, `${JSON.stringify(keys, null, 2)}\n`, { mode: 0o600 });
81
+ chmodSync(path, 0o600);
82
+ }
83
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/native/credentials.ts"],"names":[],"mappings":"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"}