@armadra/agent 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/CHANGELOG.md +203 -304
  2. package/CHANGELOG.zh-CN.md +462 -0
  3. package/README.md +408 -364
  4. package/README.zh-CN.md +705 -0
  5. package/dist/agent/session-plan.js +2 -2
  6. package/dist/agent/session-state.d.ts +3 -0
  7. package/dist/agent/session-state.js +20 -0
  8. package/dist/agent/session-subagent.d.ts +15 -1
  9. package/dist/agent/session-subagent.js +26 -3
  10. package/dist/agent/session-telemetry.d.ts +16 -2
  11. package/dist/agent/session-telemetry.js +29 -7
  12. package/dist/agent/session-tools.js +5 -2
  13. package/dist/agent/session-trace-writer.d.ts +27 -0
  14. package/dist/agent/session-trace-writer.js +253 -0
  15. package/dist/agent/session.d.ts +2 -0
  16. package/dist/agent/session.js +12 -0
  17. package/dist/agent/subagent-direct.d.ts +20 -0
  18. package/dist/agent/subagent-direct.js +76 -0
  19. package/dist/agent/subagent-registry.d.ts +8 -1
  20. package/dist/agent/subagent-registry.js +28 -1
  21. package/dist/agent/system-prompt.d.ts +3 -1
  22. package/dist/agent/system-prompt.js +3 -0
  23. package/dist/agent/tool-runner.d.ts +2 -1
  24. package/dist/agent/tool-runner.js +5 -4
  25. package/dist/agent/types-w6.d.ts +39 -0
  26. package/dist/agent/types-w6.js +4 -0
  27. package/dist/agent/types.d.ts +5 -2
  28. package/dist/agents/external.js +1 -1
  29. package/dist/agents/task-record.d.ts +74 -1
  30. package/dist/agents/task-record.js +100 -0
  31. package/dist/ai/apis/cache-params.js +2 -1
  32. package/dist/ai/apis/chatgpt-backend.d.ts +52 -0
  33. package/dist/ai/apis/chatgpt-backend.js +224 -0
  34. package/dist/ai/apis/chatgpt-rate-limits.d.ts +26 -0
  35. package/dist/ai/apis/chatgpt-rate-limits.js +91 -0
  36. package/dist/ai/apis/openai-responses-request.d.ts +2 -1
  37. package/dist/ai/apis/openai-responses-request.js +8 -2
  38. package/dist/ai/apis/openai-responses.d.ts +2 -0
  39. package/dist/ai/apis/openai-responses.js +51 -16
  40. package/dist/ai/overflow.d.ts +1 -1
  41. package/dist/ai/overflow.js +10 -7
  42. package/dist/ai/providers/auth.d.ts +15 -2
  43. package/dist/ai/providers/auth.js +69 -2
  44. package/dist/ai/providers/builtin.js +27 -0
  45. package/dist/ai/providers/catalog-data.js +1 -0
  46. package/dist/ai/providers/discovered-cache.d.ts +48 -0
  47. package/dist/ai/providers/discovered-cache.js +102 -0
  48. package/dist/ai/providers/enrich.js +2 -7
  49. package/dist/ai/providers/model-visibility.d.ts +33 -0
  50. package/dist/ai/providers/model-visibility.js +70 -0
  51. package/dist/ai/providers/models-dev-cache.js +15 -19
  52. package/dist/ai/providers/models-dev-snapshot.js +8 -7
  53. package/dist/ai/providers/models-dev.js +5 -15
  54. package/dist/ai/providers/registry.d.ts +2 -0
  55. package/dist/ai/providers/registry.js +6 -1
  56. package/dist/ai/providers/suggest.js +2 -16
  57. package/dist/ai/types.d.ts +27 -1
  58. package/dist/auth/chatgpt/backend-client.d.ts +26 -0
  59. package/dist/auth/chatgpt/backend-client.js +74 -0
  60. package/dist/auth/chatgpt/claims.d.ts +17 -0
  61. package/dist/auth/chatgpt/claims.js +46 -0
  62. package/dist/auth/chatgpt/cli.d.ts +35 -0
  63. package/dist/auth/chatgpt/cli.js +279 -0
  64. package/dist/auth/chatgpt/doctor.d.ts +17 -0
  65. package/dist/auth/chatgpt/doctor.js +36 -0
  66. package/dist/auth/chatgpt/host-id.d.ts +7 -0
  67. package/dist/auth/chatgpt/host-id.js +26 -0
  68. package/dist/auth/chatgpt/login.d.ts +30 -0
  69. package/dist/auth/chatgpt/login.js +130 -0
  70. package/dist/auth/chatgpt/presets.d.ts +66 -0
  71. package/dist/auth/chatgpt/presets.js +120 -0
  72. package/dist/auth/chatgpt/quota-text.d.ts +13 -0
  73. package/dist/auth/chatgpt/quota-text.js +36 -0
  74. package/dist/auth/oauth/browser.d.ts +12 -0
  75. package/dist/auth/oauth/browser.js +27 -0
  76. package/dist/auth/oauth/callback-server.d.ts +47 -0
  77. package/dist/auth/oauth/callback-server.js +147 -0
  78. package/dist/auth/oauth/flows.d.ts +62 -0
  79. package/dist/auth/oauth/flows.js +113 -0
  80. package/dist/auth/oauth/jwt.d.ts +24 -0
  81. package/dist/auth/oauth/jwt.js +81 -0
  82. package/dist/auth/oauth/live.d.ts +21 -0
  83. package/dist/auth/oauth/live.js +25 -0
  84. package/dist/auth/oauth/oidc.d.ts +26 -0
  85. package/dist/auth/oauth/oidc.js +72 -0
  86. package/dist/auth/oauth/pkce.d.ts +15 -0
  87. package/dist/auth/oauth/pkce.js +20 -0
  88. package/dist/auth/oauth/refresh.d.ts +35 -0
  89. package/dist/auth/oauth/refresh.js +129 -0
  90. package/dist/auth/oauth/token-client.d.ts +33 -0
  91. package/dist/auth/oauth/token-client.js +121 -0
  92. package/dist/auth/oauth/token-store.d.ts +23 -0
  93. package/dist/auth/oauth/token-store.js +125 -0
  94. package/dist/auth/testing/fake-oauth.d.ts +73 -0
  95. package/dist/auth/testing/fake-oauth.js +271 -0
  96. package/dist/auth/testing/refresh-child.d.ts +5 -0
  97. package/dist/auth/testing/refresh-child.js +16 -0
  98. package/dist/bundle/ama.cjs +26532 -11045
  99. package/dist/checkpoints/backend.js +6 -5
  100. package/dist/checkpoints/restore.js +3 -2
  101. package/dist/checkpoints/settings.js +2 -1
  102. package/dist/checkpoints/shadow-git.js +7 -6
  103. package/dist/checkpoints/shadow-restore.js +3 -2
  104. package/dist/checkpoints/tracker.js +4 -3
  105. package/dist/cli/args.d.ts +9 -2
  106. package/dist/cli/args.js +74 -27
  107. package/dist/cli/bootstrap.js +53 -31
  108. package/dist/cli/choice-prompt.js +8 -6
  109. package/dist/cli/codemode-notice.js +2 -2
  110. package/dist/cli/compose-agents.js +2 -1
  111. package/dist/cli/compose-extensions.js +2 -0
  112. package/dist/cli/compose-memory.d.ts +53 -0
  113. package/dist/cli/compose-memory.js +127 -0
  114. package/dist/cli/compose-providers.d.ts +6 -0
  115. package/dist/cli/compose-providers.js +38 -2
  116. package/dist/cli/compose-session.d.ts +5 -0
  117. package/dist/cli/compose-session.js +27 -13
  118. package/dist/cli/compose-store.d.ts +2 -0
  119. package/dist/cli/compose-store.js +9 -5
  120. package/dist/cli/compose.d.ts +2 -0
  121. package/dist/cli/compose.js +13 -5
  122. package/dist/cli/default-model.d.ts +0 -2
  123. package/dist/cli/default-model.js +5 -8
  124. package/dist/cli/deps.d.ts +5 -1
  125. package/dist/cli/exit-codes.d.ts +1 -1
  126. package/dist/cli/exit-codes.js +7 -16
  127. package/dist/cli/from-prompt.js +3 -2
  128. package/dist/cli/help-text.d.ts +2 -1
  129. package/dist/cli/help-text.js +5 -99
  130. package/dist/cli/main.d.ts +5 -0
  131. package/dist/cli/main.js +21 -2
  132. package/dist/cli/proxy.js +14 -9
  133. package/dist/cli/runtime.d.ts +5 -0
  134. package/dist/cli/startup-screen.js +20 -27
  135. package/dist/cli/startup-steps.js +6 -8
  136. package/dist/cli/subcommands/auth.d.ts +6 -3
  137. package/dist/cli/subcommands/auth.js +52 -22
  138. package/dist/cli/subcommands/config-set.d.ts +21 -0
  139. package/dist/cli/subcommands/config-set.js +145 -0
  140. package/dist/cli/subcommands/config.d.ts +2 -1
  141. package/dist/cli/subcommands/config.js +51 -37
  142. package/dist/cli/subcommands/doctor.d.ts +2 -1
  143. package/dist/cli/subcommands/doctor.js +97 -58
  144. package/dist/cli/subcommands/init.d.ts +1 -1
  145. package/dist/cli/subcommands/init.js +8 -7
  146. package/dist/cli/subcommands/memory.d.ts +18 -0
  147. package/dist/cli/subcommands/memory.js +189 -0
  148. package/dist/cli/subcommands/model-meta.js +11 -9
  149. package/dist/cli/subcommands/models-cache-probe.js +23 -20
  150. package/dist/cli/subcommands/models-discover.d.ts +4 -0
  151. package/dist/cli/subcommands/models-discover.js +60 -21
  152. package/dist/cli/subcommands/models-enable.d.ts +13 -0
  153. package/dist/cli/subcommands/models-enable.js +145 -0
  154. package/dist/cli/subcommands/models.d.ts +3 -1
  155. package/dist/cli/subcommands/models.js +39 -24
  156. package/dist/cli/subcommands/probe-runner.js +8 -7
  157. package/dist/cli/subcommands/providers-list.js +32 -21
  158. package/dist/cli/subcommands/providers-plan.js +24 -25
  159. package/dist/cli/subcommands/providers-probe.js +5 -6
  160. package/dist/cli/subcommands/providers.d.ts +1 -1
  161. package/dist/cli/subcommands/providers.js +45 -50
  162. package/dist/cli/subcommands/sessions-export.d.ts +1 -1
  163. package/dist/cli/subcommands/sessions-export.js +10 -9
  164. package/dist/cli/subcommands/sessions-search.d.ts +1 -1
  165. package/dist/cli/subcommands/sessions-search.js +11 -10
  166. package/dist/cli/subcommands/sessions-trace.d.ts +30 -0
  167. package/dist/cli/subcommands/sessions-trace.js +156 -0
  168. package/dist/cli/subcommands/sessions.d.ts +4 -3
  169. package/dist/cli/subcommands/sessions.js +40 -37
  170. package/dist/cli/subcommands/stats.d.ts +1 -1
  171. package/dist/cli/subcommands/stats.js +49 -30
  172. package/dist/cli/system-prompt-arg.js +3 -2
  173. package/dist/codemode/capability.js +3 -2
  174. package/dist/compaction/post-compact.d.ts +1 -0
  175. package/dist/compaction/post-compact.js +15 -1
  176. package/dist/compaction/prune-tier.d.ts +1 -1
  177. package/dist/compaction/prune-tier.js +3 -3
  178. package/dist/compaction/serialize.js +2 -1
  179. package/dist/config/auth-file.d.ts +12 -2
  180. package/dist/config/auth-file.js +32 -10
  181. package/dist/config/checker.js +12 -11
  182. package/dist/config/context-files.js +3 -2
  183. package/dist/config/edit.d.ts +106 -0
  184. package/dist/config/edit.js +350 -0
  185. package/dist/config/init.d.ts +4 -3
  186. package/dist/config/init.js +10 -18
  187. package/dist/config/json-schema.d.ts +2 -1
  188. package/dist/config/json-schema.js +102 -69
  189. package/dist/config/key-docs.d.ts +18 -7
  190. package/dist/config/key-docs.js +43 -128
  191. package/dist/config/load.js +7 -6
  192. package/dist/config/merge.d.ts +4 -1
  193. package/dist/config/merge.js +42 -22
  194. package/dist/config/paths.js +6 -5
  195. package/dist/config/profile.d.ts +5 -1
  196. package/dist/config/profile.js +7 -2
  197. package/dist/config/schema-w5.js +12 -4
  198. package/dist/config/schema-w6.d.ts +19 -0
  199. package/dist/config/schema-w6.js +115 -0
  200. package/dist/config/schema.js +34 -20
  201. package/dist/config/settings-registry.d.ts +81 -0
  202. package/dist/config/settings-registry.js +185 -0
  203. package/dist/config/types-w5.d.ts +7 -0
  204. package/dist/config/types-w5.js +2 -0
  205. package/dist/config/types-w6.d.ts +109 -0
  206. package/dist/config/types-w6.js +19 -0
  207. package/dist/config/types.d.ts +11 -8
  208. package/dist/config/types.js +1 -0
  209. package/dist/drivers/acp/client.js +1 -1
  210. package/dist/drivers/acp/driver.js +13 -8
  211. package/dist/drivers/agents.js +4 -3
  212. package/dist/drivers/base.js +3 -2
  213. package/dist/drivers/host-runners.js +2 -1
  214. package/dist/drivers/native/claude-stream.js +12 -11
  215. package/dist/drivers/native/codex-app-server.js +17 -12
  216. package/dist/drivers/native/oneshot.js +8 -7
  217. package/dist/drivers/pool.js +1 -1
  218. package/dist/drivers/runner.d.ts +3 -1
  219. package/dist/drivers/runner.js +68 -20
  220. package/dist/drivers/turn.js +1 -1
  221. package/dist/hooks/config.js +3 -2
  222. package/dist/hooks/protocol.js +18 -12
  223. package/dist/host/api-impl.js +11 -10
  224. package/dist/host/loader.js +9 -8
  225. package/dist/host/types.d.ts +3 -0
  226. package/dist/i18n/catalog.d.ts +2209 -0
  227. package/dist/i18n/catalog.js +72 -0
  228. package/dist/i18n/format.d.ts +21 -0
  229. package/dist/i18n/format.js +58 -0
  230. package/dist/i18n/index.d.ts +58 -0
  231. package/dist/i18n/index.js +72 -0
  232. package/dist/i18n/messages/agents.d.ts +84 -0
  233. package/dist/i18n/messages/agents.js +85 -0
  234. package/dist/i18n/messages/approval.d.ts +99 -0
  235. package/dist/i18n/messages/approval.js +100 -0
  236. package/dist/i18n/messages/auth.d.ts +189 -0
  237. package/dist/i18n/messages/auth.js +201 -0
  238. package/dist/i18n/messages/cli-args.d.ts +46 -0
  239. package/dist/i18n/messages/cli-args.js +46 -0
  240. package/dist/i18n/messages/cli-help.d.ts +12 -0
  241. package/dist/i18n/messages/cli-help.js +248 -0
  242. package/dist/i18n/messages/cli.d.ts +336 -0
  243. package/dist/i18n/messages/cli.js +311 -0
  244. package/dist/i18n/messages/config-keys.d.ts +293 -0
  245. package/dist/i18n/messages/config-keys.js +296 -0
  246. package/dist/i18n/messages/config.d.ts +508 -0
  247. package/dist/i18n/messages/config.js +241 -0
  248. package/dist/i18n/messages/drivers.d.ts +123 -0
  249. package/dist/i18n/messages/drivers.js +124 -0
  250. package/dist/i18n/messages/errors.d.ts +83 -0
  251. package/dist/i18n/messages/errors.js +171 -0
  252. package/dist/i18n/messages/interactive-line.d.ts +61 -0
  253. package/dist/i18n/messages/interactive-line.js +69 -0
  254. package/dist/i18n/messages/interactive-startup.d.ts +99 -0
  255. package/dist/i18n/messages/interactive-startup.js +100 -0
  256. package/dist/i18n/messages/interactive-view.d.ts +140 -0
  257. package/dist/i18n/messages/interactive-view.js +141 -0
  258. package/dist/i18n/messages/interactive.d.ts +489 -0
  259. package/dist/i18n/messages/interactive.js +248 -0
  260. package/dist/i18n/messages/memory.d.ts +123 -0
  261. package/dist/i18n/messages/memory.js +128 -0
  262. package/dist/i18n/messages/panels.d.ts +233 -0
  263. package/dist/i18n/messages/panels.js +232 -0
  264. package/dist/i18n/messages/permissions.d.ts +126 -0
  265. package/dist/i18n/messages/permissions.js +151 -0
  266. package/dist/i18n/messages/plan.d.ts +123 -0
  267. package/dist/i18n/messages/plan.js +124 -0
  268. package/dist/i18n/messages/print.d.ts +86 -0
  269. package/dist/i18n/messages/print.js +91 -0
  270. package/dist/i18n/messages/report.d.ts +391 -0
  271. package/dist/i18n/messages/report.js +490 -0
  272. package/dist/i18n/messages/rewind.d.ts +198 -0
  273. package/dist/i18n/messages/rewind.js +217 -0
  274. package/dist/i18n/messages/session.d.ts +230 -0
  275. package/dist/i18n/messages/session.js +258 -0
  276. package/dist/i18n/messages/settings.d.ts +355 -0
  277. package/dist/i18n/messages/settings.js +343 -0
  278. package/dist/i18n/messages/subcommands-config.d.ts +113 -0
  279. package/dist/i18n/messages/subcommands-config.js +129 -0
  280. package/dist/i18n/messages/subcommands-models.d.ts +34 -0
  281. package/dist/i18n/messages/subcommands-models.js +34 -0
  282. package/dist/i18n/messages/subcommands.d.ts +576 -0
  283. package/dist/i18n/messages/subcommands.js +477 -0
  284. package/dist/i18n/messages/trace.d.ts +243 -0
  285. package/dist/i18n/messages/trace.js +238 -0
  286. package/dist/i18n/types.d.ts +15 -0
  287. package/dist/i18n/types.js +7 -0
  288. package/dist/index.d.ts +6 -0
  289. package/dist/index.js +2 -0
  290. package/dist/memory/edit.d.ts +24 -0
  291. package/dist/memory/edit.js +63 -0
  292. package/dist/memory/frontmatter.d.ts +37 -0
  293. package/dist/memory/frontmatter.js +79 -0
  294. package/dist/memory/index.d.ts +28 -0
  295. package/dist/memory/index.js +126 -0
  296. package/dist/memory/lock.d.ts +17 -0
  297. package/dist/memory/lock.js +62 -0
  298. package/dist/memory/paths.d.ts +57 -0
  299. package/dist/memory/paths.js +175 -0
  300. package/dist/memory/report.d.ts +36 -0
  301. package/dist/memory/report.js +86 -0
  302. package/dist/memory/runtime.d.ts +42 -0
  303. package/dist/memory/runtime.js +36 -0
  304. package/dist/memory/secrets.d.ts +8 -0
  305. package/dist/memory/secrets.js +27 -0
  306. package/dist/memory/section.d.ts +28 -0
  307. package/dist/memory/section.js +59 -0
  308. package/dist/memory/store.d.ts +70 -0
  309. package/dist/memory/store.js +284 -0
  310. package/dist/memory/tool.d.ts +31 -0
  311. package/dist/memory/tool.js +94 -0
  312. package/dist/modes/acp/acp-events.js +2 -1
  313. package/dist/modes/acp/acp-server.js +13 -12
  314. package/dist/modes/commands-core.d.ts +11 -0
  315. package/dist/modes/commands-core.js +93 -62
  316. package/dist/modes/image-input.js +20 -6
  317. package/dist/modes/interactive/agent-bar.d.ts +95 -0
  318. package/dist/modes/interactive/agent-bar.js +248 -0
  319. package/dist/modes/interactive/agent-panels.js +27 -26
  320. package/dist/modes/interactive/agent-transcript.d.ts +47 -0
  321. package/dist/modes/interactive/agent-transcript.js +191 -0
  322. package/dist/modes/interactive/agent-ui.d.ts +36 -1
  323. package/dist/modes/interactive/agent-ui.js +180 -14
  324. package/dist/modes/interactive/agent-view.d.ts +72 -0
  325. package/dist/modes/interactive/agent-view.js +281 -0
  326. package/dist/modes/interactive/approval-dialog.js +46 -34
  327. package/dist/modes/interactive/clipboard-paste.d.ts +2 -2
  328. package/dist/modes/interactive/clipboard-paste.js +9 -4
  329. package/dist/modes/interactive/commands.d.ts +22 -1
  330. package/dist/modes/interactive/commands.js +100 -49
  331. package/dist/modes/interactive/completion.js +2 -1
  332. package/dist/modes/interactive/config-panel.d.ts +70 -0
  333. package/dist/modes/interactive/config-panel.js +319 -0
  334. package/dist/modes/interactive/config-ui.d.ts +96 -0
  335. package/dist/modes/interactive/config-ui.js +397 -0
  336. package/dist/modes/interactive/confirm-dialog.js +6 -7
  337. package/dist/modes/interactive/event-notices.js +11 -9
  338. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  339. package/dist/modes/interactive/interactive-mode.js +21 -22
  340. package/dist/modes/interactive/key-dispatch.d.ts +12 -0
  341. package/dist/modes/interactive/key-dispatch.js +24 -9
  342. package/dist/modes/interactive/line/line-editor.js +2 -1
  343. package/dist/modes/interactive/line/line-mode.js +18 -7
  344. package/dist/modes/interactive/line/line-render.js +25 -24
  345. package/dist/modes/interactive/memory-panel.d.ts +64 -0
  346. package/dist/modes/interactive/memory-panel.js +221 -0
  347. package/dist/modes/interactive/message-view.d.ts +4 -2
  348. package/dist/modes/interactive/message-view.js +42 -33
  349. package/dist/modes/interactive/model-items.d.ts +45 -0
  350. package/dist/modes/interactive/model-items.js +134 -0
  351. package/dist/modes/interactive/model-picker.d.ts +57 -0
  352. package/dist/modes/interactive/model-picker.js +191 -0
  353. package/dist/modes/interactive/panels.js +39 -31
  354. package/dist/modes/interactive/pickers.d.ts +2 -1
  355. package/dist/modes/interactive/pickers.js +10 -6
  356. package/dist/modes/interactive/plan-command.d.ts +1 -1
  357. package/dist/modes/interactive/plan-command.js +24 -27
  358. package/dist/modes/interactive/plan-dialog.js +18 -17
  359. package/dist/modes/interactive/plan-flow.js +6 -5
  360. package/dist/modes/interactive/rewind-command.d.ts +1 -1
  361. package/dist/modes/interactive/rewind-command.js +18 -16
  362. package/dist/modes/interactive/rewind-flow.d.ts +0 -1
  363. package/dist/modes/interactive/rewind-flow.js +13 -14
  364. package/dist/modes/interactive/rewind-list.js +7 -6
  365. package/dist/modes/interactive/rewind-panel.js +40 -40
  366. package/dist/modes/interactive/rewind-text.d.ts +5 -3
  367. package/dist/modes/interactive/rewind-text.js +48 -38
  368. package/dist/modes/interactive/run-indicator.js +15 -12
  369. package/dist/modes/interactive/session-events.js +3 -2
  370. package/dist/modes/interactive/startup-header.js +22 -23
  371. package/dist/modes/interactive/startup-ui.d.ts +3 -9
  372. package/dist/modes/interactive/startup-ui.js +44 -88
  373. package/dist/modes/interactive/status-area.d.ts +2 -0
  374. package/dist/modes/interactive/status-area.js +8 -2
  375. package/dist/modes/interactive/status-bar.js +4 -3
  376. package/dist/modes/interactive/subagent-view.js +9 -7
  377. package/dist/modes/interactive/tasks-report.d.ts +6 -0
  378. package/dist/modes/interactive/tasks-report.js +28 -31
  379. package/dist/modes/interactive/terminal-setup.d.ts +9 -0
  380. package/dist/modes/interactive/terminal-setup.js +23 -0
  381. package/dist/modes/interactive/tool-summary.js +19 -17
  382. package/dist/modes/interactive/tool-view.js +7 -4
  383. package/dist/modes/interactive/trace-view.d.ts +89 -0
  384. package/dist/modes/interactive/trace-view.js +332 -0
  385. package/dist/modes/print/print-mode.js +14 -15
  386. package/dist/modes/rpc/commands.js +7 -3
  387. package/dist/modes/rpc/rpc-mode.js +9 -3
  388. package/dist/modes/session-report.d.ts +2 -0
  389. package/dist/modes/session-report.js +101 -105
  390. package/dist/modes/startup-ui-text.js +11 -6
  391. package/dist/permissions/bypass.d.ts +8 -10
  392. package/dist/permissions/bypass.js +19 -10
  393. package/dist/permissions/memory-class.d.ts +23 -0
  394. package/dist/permissions/memory-class.js +54 -0
  395. package/dist/permissions/modes.d.ts +3 -3
  396. package/dist/permissions/modes.js +21 -16
  397. package/dist/permissions/pipeline.d.ts +1 -1
  398. package/dist/permissions/pipeline.js +9 -2
  399. package/dist/permissions/preview.js +49 -42
  400. package/dist/permissions/rules.js +5 -2
  401. package/dist/permissions/types.d.ts +4 -0
  402. package/dist/plan/store.js +2 -1
  403. package/dist/rpc.d.ts +39 -1
  404. package/dist/rpc.js +3 -0
  405. package/dist/sandbox/bash.js +11 -8
  406. package/dist/sandbox/detect.js +14 -11
  407. package/dist/sdk.d.ts +17 -1
  408. package/dist/sdk.js +29 -7
  409. package/dist/session/export.js +31 -30
  410. package/dist/session/manager.d.ts +1 -1
  411. package/dist/session/manager.js +5 -3
  412. package/dist/session/reuse.js +5 -4
  413. package/dist/session/scan.js +3 -2
  414. package/dist/session/stats-aggregate.d.ts +3 -1
  415. package/dist/session/stats-aggregate.js +5 -1
  416. package/dist/session/stats-index.js +2 -1
  417. package/dist/session/stats-scan.d.ts +4 -1
  418. package/dist/session/stats-scan.js +6 -2
  419. package/dist/session/store.d.ts +11 -0
  420. package/dist/session/store.js +48 -1
  421. package/dist/session/types.d.ts +2 -0
  422. package/dist/skills/builtin.js +2 -1
  423. package/dist/tools/image-file.d.ts +26 -1
  424. package/dist/tools/image-file.js +55 -13
  425. package/dist/tools/presets.d.ts +14 -1
  426. package/dist/tools/presets.js +3 -3
  427. package/dist/tools/registry.js +1 -1
  428. package/dist/tools/truncate.d.ts +1 -1
  429. package/dist/tools/truncate.js +2 -2
  430. package/dist/tools/types.d.ts +17 -1
  431. package/dist/trace/build-index.d.ts +91 -0
  432. package/dist/trace/build-index.js +127 -0
  433. package/dist/trace/build-nodes.d.ts +24 -0
  434. package/dist/trace/build-nodes.js +279 -0
  435. package/dist/trace/build-util.d.ts +43 -0
  436. package/dist/trace/build-util.js +120 -0
  437. package/dist/trace/build.d.ts +82 -0
  438. package/dist/trace/build.js +382 -0
  439. package/dist/trace/detail.d.ts +35 -0
  440. package/dist/trace/detail.js +205 -0
  441. package/dist/trace/flatten.d.ts +69 -0
  442. package/dist/trace/flatten.js +173 -0
  443. package/dist/trace/format.d.ts +47 -0
  444. package/dist/trace/format.js +329 -0
  445. package/dist/trace/html-template.d.ts +19 -0
  446. package/dist/trace/html-template.js +150 -0
  447. package/dist/trace/html.d.ts +104 -0
  448. package/dist/trace/html.js +292 -0
  449. package/dist/trace/preview.d.ts +29 -0
  450. package/dist/trace/preview.js +59 -0
  451. package/dist/trace/query-session.d.ts +14 -0
  452. package/dist/trace/query-session.js +38 -0
  453. package/dist/trace/query.d.ts +55 -0
  454. package/dist/trace/query.js +188 -0
  455. package/dist/trace/session.d.ts +40 -0
  456. package/dist/trace/session.js +168 -0
  457. package/dist/trace/types.d.ts +243 -0
  458. package/dist/trace/types.js +13 -0
  459. package/dist/tui/components/editor-paste.d.ts +2 -0
  460. package/dist/tui/components/editor-paste.js +6 -3
  461. package/dist/tui/components/editor.js +2 -2
  462. package/dist/tui/components/loader.d.ts +3 -0
  463. package/dist/tui/components/loader.js +14 -0
  464. package/dist/tui/components/select-list.d.ts +2 -0
  465. package/dist/tui/components/select-list.js +7 -0
  466. package/dist/tui/components/settings-list.d.ts +61 -0
  467. package/dist/tui/components/settings-list.js +135 -0
  468. package/dist/tui/keybindings.d.ts +5 -0
  469. package/dist/tui/keybindings.js +17 -5
  470. package/dist/tui.d.ts +1 -0
  471. package/dist/tui.js +2 -0
  472. package/docs/en/host-api.md +167 -0
  473. package/docs/en/permissions.md +214 -0
  474. package/docs/en/providers.md +627 -0
  475. package/docs/en/rpc.md +361 -0
  476. package/docs/en/sessions.md +219 -0
  477. package/docs/en/tui.md +534 -0
  478. package/docs/host-api.md +18 -17
  479. package/docs/memory.md +172 -0
  480. package/docs/permissions.md +2 -2
  481. package/docs/providers.md +73 -1
  482. package/docs/rpc.md +31 -3
  483. package/docs/session-format.md +8 -2
  484. package/docs/sessions.md +29 -1
  485. package/docs/tui.md +140 -4
  486. package/package.json +8 -3
@@ -0,0 +1,121 @@
1
+ /**
2
+ * OAuth 端点的 HTTP 调用(token 交换 / 刷新 / 撤销 / discovery)。[W6-O]
3
+ *
4
+ * 日志红线(docs/wave6-plan.md §4.3):错误只带 HTTP 状态与 `error` 码(截断到 64 字符),从不带响应
5
+ * 原文、请求参数、code 或任何 token。
6
+ */
7
+ import { AmaError } from "../../errors.js";
8
+ export const OAUTH_TIMEOUT_MS = 30_000;
9
+ /** OAuth 端点返回非 2xx:`status` 与 `error` 码(不含描述原文)。 */
10
+ export class OAuthHttpError extends AmaError {
11
+ status;
12
+ error;
13
+ constructor(status, error, what) {
14
+ super("oauth_http", `${what} failed: HTTP ${status}${error ? ` ${error}` : ""}`, {
15
+ detail: { status, error },
16
+ });
17
+ this.status = status;
18
+ this.error = error;
19
+ }
20
+ }
21
+ /** 响应体里的错误码:`error`(字符串或 `{code|type}`)、`code`、`error_code`。 */
22
+ export function oauthErrorCode(body) {
23
+ try {
24
+ const value = JSON.parse(body);
25
+ const error = value["error"];
26
+ const nested = typeof error === "object" && error !== null
27
+ ? (error["code"] ?? error["type"])
28
+ : error;
29
+ const code = nested ?? value["code"] ?? value["error_code"];
30
+ return typeof code === "string" && code !== "" ? code.slice(0, 64) : undefined;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ function signalOf(signal) {
37
+ const timeout = AbortSignal.timeout(OAUTH_TIMEOUT_MS);
38
+ return signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
39
+ }
40
+ export async function postOAuth(fetchFn, url, params, options) {
41
+ let response;
42
+ try {
43
+ response = await fetchFn(url, {
44
+ method: "POST",
45
+ headers: options.encoding === "form"
46
+ ? { "content-type": "application/x-www-form-urlencoded", accept: "application/json" }
47
+ : { "content-type": "application/json", accept: "application/json" },
48
+ body: options.encoding === "form"
49
+ ? new URLSearchParams(params).toString()
50
+ : JSON.stringify(params),
51
+ signal: signalOf(options.signal),
52
+ });
53
+ }
54
+ catch (error) {
55
+ throw new AmaError("oauth_network", `${options.what} failed: ${error.name}`, {
56
+ cause: undefined,
57
+ });
58
+ }
59
+ const text = await response.text().catch(() => "");
60
+ if (!response.ok)
61
+ throw new OAuthHttpError(response.status, oauthErrorCode(text), options.what);
62
+ if (text.trim() === "")
63
+ return {};
64
+ try {
65
+ const value = JSON.parse(text);
66
+ if (typeof value === "object" && value !== null)
67
+ return value;
68
+ }
69
+ catch {
70
+ // 落到下面
71
+ }
72
+ throw new AmaError("oauth_bad_response", `${options.what}: response is not JSON`);
73
+ }
74
+ export function parseTokenResponse(value, what) {
75
+ const access = value["access_token"];
76
+ if (typeof access !== "string" || access === "")
77
+ throw new AmaError("oauth_bad_response", `${what}: no access_token`);
78
+ const out = { accessToken: access };
79
+ const str = (key) => typeof value[key] === "string" && value[key] !== "" ? value[key] : undefined;
80
+ const refresh = str("refresh_token");
81
+ if (refresh !== undefined)
82
+ out.refreshToken = refresh;
83
+ const id = str("id_token");
84
+ if (id !== undefined)
85
+ out.idToken = id;
86
+ const scope = str("scope");
87
+ if (scope !== undefined)
88
+ out.scope = scope;
89
+ const clientId = str("client_id");
90
+ if (clientId !== undefined)
91
+ out.clientId = clientId;
92
+ const expires = value["expires_in"];
93
+ const seconds = typeof expires === "string" ? Number(expires) : expires;
94
+ if (typeof seconds === "number" && Number.isFinite(seconds) && seconds > 0)
95
+ out.expiresIn = seconds;
96
+ return out;
97
+ }
98
+ export async function getJson(fetchFn, url, what, signal) {
99
+ let response;
100
+ try {
101
+ response = await fetchFn(url, {
102
+ headers: { accept: "application/json" },
103
+ signal: signalOf(signal),
104
+ });
105
+ }
106
+ catch (error) {
107
+ throw new AmaError("oauth_network", `${what} failed: ${error.name}`);
108
+ }
109
+ const text = await response.text().catch(() => "");
110
+ if (!response.ok)
111
+ throw new OAuthHttpError(response.status, oauthErrorCode(text), what);
112
+ try {
113
+ const value = JSON.parse(text);
114
+ if (typeof value === "object" && value !== null)
115
+ return value;
116
+ }
117
+ catch {
118
+ // 落到下面
119
+ }
120
+ throw new AmaError("oauth_bad_response", `${what}: response is not JSON`);
121
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * OAuth 条目的存取与跨进程刷新锁(docs/wave6-plan.md §4.3、D15)。[W6-O]
3
+ *
4
+ * - 读:每次都从磁盘读(绕过 `ApiKeyResolver.fileCache`,长会话才能看到别的进程刷新后的 token);文件不存在 /
5
+ * 坏掉 / 不是 oauth 条目 → undefined;
6
+ * - 写:读-改-写 `auth.json` 整个文件,经 `writeAuthFile`(同目录临时文件 0600 → rename → chmod),其它条目原样保留;
7
+ * - 锁:`<auth.json>.lock`,`open(O_CREAT|O_EXCL)`(flag `wx`)写 `{ pid, at }`;等待 ≤ 15 s;锁文件超过 60 s 且
8
+ * pid 已死视为陈旧,删除后重试。取锁后由调用方**重读文件**再决定要不要刷新。
9
+ */
10
+ import { type OAuthAuthEntry } from "../../config/types-w6.js";
11
+ export declare const LOCK_WAIT_MS = 15000;
12
+ export declare const LOCK_STALE_MS = 60000;
13
+ export declare function readOAuthEntry(path: string, provider: string): OAuthAuthEntry | undefined;
14
+ /** 写入(`undefined` 删除)该供应商的条目,其余条目不动。 */
15
+ export declare function writeOAuthEntry(path: string, provider: string, entry: OAuthAuthEntry | undefined): void;
16
+ export declare function lockPath(authFile: string): string;
17
+ /** 锁文件陈旧:超过 60 s 且写锁的进程已不在。 */
18
+ export declare function isStaleLock(path: string, now?: number): boolean;
19
+ export interface LockOptions {
20
+ waitMs?: number;
21
+ }
22
+ /** 在 `auth.json.lock` 下执行 `fn`;等不到锁抛 `auth_lock_timeout`。 */
23
+ export declare function withRefreshLock<T>(authFile: string, fn: () => Promise<T>, options?: LockOptions): Promise<T>;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * OAuth 条目的存取与跨进程刷新锁(docs/wave6-plan.md §4.3、D15)。[W6-O]
3
+ *
4
+ * - 读:每次都从磁盘读(绕过 `ApiKeyResolver.fileCache`,长会话才能看到别的进程刷新后的 token);文件不存在 /
5
+ * 坏掉 / 不是 oauth 条目 → undefined;
6
+ * - 写:读-改-写 `auth.json` 整个文件,经 `writeAuthFile`(同目录临时文件 0600 → rename → chmod),其它条目原样保留;
7
+ * - 锁:`<auth.json>.lock`,`open(O_CREAT|O_EXCL)`(flag `wx`)写 `{ pid, at }`;等待 ≤ 15 s;锁文件超过 60 s 且
8
+ * pid 已死视为陈旧,删除后重试。取锁后由调用方**重读文件**再决定要不要刷新。
9
+ */
10
+ import { closeSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeSync, } from "node:fs";
11
+ import { dirname } from "node:path";
12
+ import { writeAuthFile } from "../../config/auth-file.js";
13
+ import { CONFIG_FILE_VERSION } from "../../config/types.js";
14
+ import { isOAuthEntry } from "../../config/types-w6.js";
15
+ import { AmaError } from "../../errors.js";
16
+ export const LOCK_WAIT_MS = 15_000;
17
+ export const LOCK_STALE_MS = 60_000;
18
+ const LOCK_POLL_MS = 50;
19
+ function readRaw(path) {
20
+ try {
21
+ const value = JSON.parse(readFileSync(path, "utf8"));
22
+ return typeof value === "object" && value !== null && typeof value.providers === "object"
23
+ ? value
24
+ : undefined;
25
+ }
26
+ catch {
27
+ return undefined;
28
+ }
29
+ }
30
+ export function readOAuthEntry(path, provider) {
31
+ const entry = readRaw(path)?.providers?.[provider];
32
+ return isOAuthEntry(entry) ? entry : undefined;
33
+ }
34
+ /** 写入(`undefined` 删除)该供应商的条目,其余条目不动。 */
35
+ export function writeOAuthEntry(path, provider, entry) {
36
+ const file = readRaw(path) ?? { version: CONFIG_FILE_VERSION, providers: {} };
37
+ const providers = { ...file.providers };
38
+ if (entry === undefined)
39
+ delete providers[provider];
40
+ else
41
+ providers[provider] = entry;
42
+ writeAuthFile(path, { ...file, version: file.version ?? CONFIG_FILE_VERSION, providers });
43
+ }
44
+ export function lockPath(authFile) {
45
+ return `${authFile}.lock`;
46
+ }
47
+ function pidAlive(pid) {
48
+ try {
49
+ process.kill(pid, 0);
50
+ return true;
51
+ }
52
+ catch (error) {
53
+ return error.code === "EPERM";
54
+ }
55
+ }
56
+ /** 锁文件陈旧:超过 60 s 且写锁的进程已不在。 */
57
+ export function isStaleLock(path, now = Date.now()) {
58
+ let age;
59
+ let pid;
60
+ try {
61
+ age = now - statSync(path).mtimeMs;
62
+ const value = JSON.parse(readFileSync(path, "utf8"));
63
+ pid = typeof value.pid === "number" ? value.pid : undefined;
64
+ }
65
+ catch {
66
+ // 读不到内容(正在写或已删):只按时间判断
67
+ try {
68
+ age = now - statSync(path).mtimeMs;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ return age > LOCK_STALE_MS && (pid === undefined || !pidAlive(pid));
75
+ }
76
+ function tryAcquire(path) {
77
+ try {
78
+ const fd = openSync(path, "wx", 0o600);
79
+ try {
80
+ writeSync(fd, JSON.stringify({ pid: process.pid, at: new Date().toISOString() }));
81
+ }
82
+ finally {
83
+ closeSync(fd);
84
+ }
85
+ return true;
86
+ }
87
+ catch (error) {
88
+ if (error.code === "EEXIST")
89
+ return false;
90
+ throw error;
91
+ }
92
+ }
93
+ /** 在 `auth.json.lock` 下执行 `fn`;等不到锁抛 `auth_lock_timeout`。 */
94
+ export async function withRefreshLock(authFile, fn, options = {}) {
95
+ const path = lockPath(authFile);
96
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
97
+ const deadline = Date.now() + (options.waitMs ?? LOCK_WAIT_MS);
98
+ for (;;) {
99
+ if (tryAcquire(path))
100
+ break;
101
+ if (isStaleLock(path)) {
102
+ try {
103
+ unlinkSync(path);
104
+ }
105
+ catch {
106
+ // 别人先删了
107
+ }
108
+ continue;
109
+ }
110
+ if (Date.now() >= deadline)
111
+ throw new AmaError("auth_lock_timeout", `timed out waiting for ${path}`);
112
+ await new Promise((r) => setTimeout(r, LOCK_POLL_MS + Math.floor(Math.random() * LOCK_POLL_MS)));
113
+ }
114
+ try {
115
+ return await fn();
116
+ }
117
+ finally {
118
+ try {
119
+ unlinkSync(path);
120
+ }
121
+ catch {
122
+ // 已被当成陈旧锁删掉
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * 测试用 fake OAuth / ChatGPT 后端(docs/wave6-plan.md §4.6)。`node:http` 随机端口;不进 bundle。
3
+ *
4
+ * OAuth:authorize(记参数、302 回 redirect_uri;SIWC 注册时带签发的 client_id 与 scope)、token(校验 PKCE;
5
+ * 授权码 / 刷新;刷新轮换,旧 refresh token 再用 → `refresh_token_reused`)、deviceauth、discovery + JWKS
6
+ * (现场生成 RSA 密钥对)、revoke。后端:`/v1/responses`、`/codex/responses`、`/v1/models`、`/codex/models`、
7
+ * `/wham/usage`,按脚本回放。
8
+ */
9
+ export interface RecordedRequest {
10
+ method: string;
11
+ path: string;
12
+ headers: Record<string, string | string[] | undefined>;
13
+ body: string;
14
+ query: URLSearchParams;
15
+ }
16
+ export interface ScriptedResponse {
17
+ status?: number;
18
+ headers?: Record<string, string>;
19
+ /** SSE 事件(JSON 对象)或原始文本。 */
20
+ events?: unknown[];
21
+ body?: unknown;
22
+ }
23
+ export interface FakeOAuthOptions {
24
+ /** SIWC 注册时签发的 id。 */
25
+ issuedClientId?: string;
26
+ /** 回调与 token 响应里的 scope;缺省含 chatgpt.tokens.use.direct。 */
27
+ scope?: string;
28
+ accessTtlS?: number;
29
+ /** 改 id_token 声明(坏 nonce 等)。 */
30
+ idTokenClaims?: (claims: Record<string, unknown>) => Record<string, unknown>;
31
+ /** 用另一把钥签 id_token(验签失败)。 */
32
+ wrongKey?: boolean;
33
+ /** 刷新响应延迟(并发测试)。 */
34
+ refreshDelayMs?: number;
35
+ /** 刷新直接失败的错误码。 */
36
+ refreshError?: string;
37
+ /** 设备码轮询先返回几次 403。 */
38
+ devicePending?: number;
39
+ marker?: string;
40
+ }
41
+ export declare class FakeOAuthServer {
42
+ readonly options: FakeOAuthOptions;
43
+ readonly requests: RecordedRequest[];
44
+ readonly responses: ScriptedResponse[];
45
+ readonly revoked: string[];
46
+ refreshCalls: number;
47
+ exchangeCalls: number;
48
+ issuer: string;
49
+ private server;
50
+ private readonly key;
51
+ private readonly otherKey;
52
+ private readonly codes;
53
+ private current;
54
+ private readonly used;
55
+ private seq;
56
+ private devicePolls;
57
+ constructor(options?: FakeOAuthOptions);
58
+ start(): Promise<string>;
59
+ stop(): Promise<void>;
60
+ get marker(): string;
61
+ /** 预置一个可刷新的 refresh token(不经登录)。 */
62
+ seedRefreshToken(token: string, clientId: string): void;
63
+ private next;
64
+ idToken(claims: Record<string, unknown>): string;
65
+ private tokens;
66
+ private get scope();
67
+ private handle;
68
+ private backend;
69
+ }
70
+ /** 一段最小的成功 SSE(一个文本块)。 */
71
+ export declare function completedEvents(text: string, extra?: unknown[]): unknown[];
72
+ /** 跟随 302 打到本地回调(代替浏览器)。 */
73
+ export declare function followAuthorize(url: string): Promise<void>;
@@ -0,0 +1,271 @@
1
+ /**
2
+ * 测试用 fake OAuth / ChatGPT 后端(docs/wave6-plan.md §4.6)。`node:http` 随机端口;不进 bundle。
3
+ *
4
+ * OAuth:authorize(记参数、302 回 redirect_uri;SIWC 注册时带签发的 client_id 与 scope)、token(校验 PKCE;
5
+ * 授权码 / 刷新;刷新轮换,旧 refresh token 再用 → `refresh_token_reused`)、deviceauth、discovery + JWKS
6
+ * (现场生成 RSA 密钥对)、revoke。后端:`/v1/responses`、`/codex/responses`、`/v1/models`、`/codex/models`、
7
+ * `/wham/usage`,按脚本回放。
8
+ */
9
+ import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
10
+ import { createServer } from "node:http";
11
+ function b64url(value) {
12
+ return Buffer.from(JSON.stringify(value)).toString("base64url");
13
+ }
14
+ export class FakeOAuthServer {
15
+ options;
16
+ requests = [];
17
+ responses = [];
18
+ revoked = [];
19
+ refreshCalls = 0;
20
+ exchangeCalls = 0;
21
+ issuer = "";
22
+ server;
23
+ key;
24
+ otherKey;
25
+ codes = new Map();
26
+ current = new Map(); // refresh token → client id
27
+ used = new Set();
28
+ seq = 0;
29
+ devicePolls = 0;
30
+ constructor(options = {}) {
31
+ this.options = options;
32
+ this.key = generateKeyPairSync("rsa", { modulusLength: 2048 });
33
+ this.otherKey = generateKeyPairSync("rsa", { modulusLength: 2048 }).privateKey;
34
+ }
35
+ async start() {
36
+ this.server = createServer((req, res) => void this.handle(req, res));
37
+ await new Promise((resolve) => this.server?.listen(0, "127.0.0.1", resolve));
38
+ const address = this.server.address();
39
+ const port = typeof address === "object" && address !== null ? address.port : 0;
40
+ this.issuer = `http://127.0.0.1:${port}`;
41
+ return this.issuer;
42
+ }
43
+ async stop() {
44
+ this.server?.closeAllConnections();
45
+ await new Promise((resolve) => this.server?.close(resolve));
46
+ }
47
+ get marker() {
48
+ return this.options.marker ?? "MARK";
49
+ }
50
+ /** 预置一个可刷新的 refresh token(不经登录)。 */
51
+ seedRefreshToken(token, clientId) {
52
+ this.current.set(token, clientId);
53
+ }
54
+ next(prefix) {
55
+ this.seq++;
56
+ return `${prefix}-${this.seq}-${this.marker}-${randomBytes(4).toString("hex")}`;
57
+ }
58
+ idToken(claims) {
59
+ const header = { alg: "RS256", kid: "k1", typ: "JWT" };
60
+ const final = this.options.idTokenClaims ? this.options.idTokenClaims(claims) : claims;
61
+ const input = `${b64url(header)}.${b64url(final)}`;
62
+ const key = this.options.wrongKey ? this.otherKey : this.key.privateKey;
63
+ return `${input}.${sign("sha256", Buffer.from(input), key).toString("base64url")}`;
64
+ }
65
+ tokens(clientId, nonce) {
66
+ const refresh = this.next("rt");
67
+ this.current.set(refresh, clientId);
68
+ const now = Math.floor(Date.now() / 1000);
69
+ return {
70
+ access_token: this.next("at"),
71
+ refresh_token: refresh,
72
+ id_token: this.idToken({
73
+ iss: this.issuer,
74
+ aud: clientId,
75
+ sub: "user-sub-1",
76
+ nonce,
77
+ exp: now + 3600,
78
+ iat: now,
79
+ email: "alice@example.com",
80
+ "https://api.openai.com/auth": { chatgpt_account_id: "acct-1", chatgpt_plan_type: "plus" },
81
+ }),
82
+ token_type: "Bearer",
83
+ expires_in: this.options.accessTtlS ?? 3600,
84
+ scope: this.scope,
85
+ };
86
+ }
87
+ get scope() {
88
+ return (this.options.scope ??
89
+ "openid profile email offline_access resource.invoke chatgpt.tokens.use.direct");
90
+ }
91
+ async handle(req, res) {
92
+ const chunks = [];
93
+ for await (const chunk of req)
94
+ chunks.push(chunk);
95
+ const body = Buffer.concat(chunks).toString("utf8");
96
+ const url = new URL(req.url ?? "/", this.issuer);
97
+ const record = {
98
+ method: req.method ?? "GET",
99
+ path: url.pathname,
100
+ headers: req.headers,
101
+ body,
102
+ query: url.searchParams,
103
+ };
104
+ this.requests.push(record);
105
+ const json = (status, value, headers = {}) => {
106
+ res.writeHead(status, { "content-type": "application/json", ...headers });
107
+ res.end(JSON.stringify(value));
108
+ };
109
+ const params = () => String(req.headers["content-type"]).includes("json")
110
+ ? JSON.parse(body || "{}")
111
+ : Object.fromEntries(new URLSearchParams(body));
112
+ switch (url.pathname) {
113
+ case "/.well-known/openid-configuration":
114
+ return json(200, {
115
+ issuer: this.issuer,
116
+ jwks_uri: `${this.issuer}/jwks`,
117
+ revocation_endpoint: `${this.issuer}/revoke`,
118
+ });
119
+ case "/jwks":
120
+ return json(200, {
121
+ keys: [
122
+ {
123
+ ...this.key.publicKey.export({ format: "jwk" }),
124
+ kid: "k1",
125
+ use: "sig",
126
+ alg: "RS256",
127
+ },
128
+ ],
129
+ });
130
+ case "/api/accounts/authorize":
131
+ case "/oauth/authorize": {
132
+ const q = url.searchParams;
133
+ const code = this.next("code");
134
+ const registering = q.get("client_id") === "dynamic_agent_client";
135
+ const clientId = registering
136
+ ? (this.options.issuedClientId ?? "oaiapp_issued")
137
+ : (q.get("client_id") ?? "");
138
+ this.codes.set(code, {
139
+ challenge: q.get("code_challenge") ?? "",
140
+ nonce: q.get("nonce") ?? "",
141
+ clientId,
142
+ redirectUri: q.get("redirect_uri") ?? "",
143
+ });
144
+ const back = new URL(q.get("redirect_uri") ?? "");
145
+ back.searchParams.set("code", code);
146
+ back.searchParams.set("state", q.get("state") ?? "");
147
+ if (url.pathname.startsWith("/api/")) {
148
+ if (registering)
149
+ back.searchParams.set("client_id", clientId);
150
+ back.searchParams.set("scope", this.scope);
151
+ }
152
+ res.writeHead(302, { location: back.toString() }).end();
153
+ return;
154
+ }
155
+ case "/api/accounts/oauth/token":
156
+ case "/oauth/token": {
157
+ const p = params();
158
+ if (p["grant_type"] === "authorization_code") {
159
+ this.exchangeCalls++;
160
+ const saved = this.codes.get(p["code"] ?? "");
161
+ if (!saved)
162
+ return json(400, { error: "invalid_grant" });
163
+ const challenge = createHash("sha256")
164
+ .update(p["code_verifier"] ?? "")
165
+ .digest("base64url");
166
+ if (challenge !== saved.challenge)
167
+ return json(400, { error: "invalid_grant" });
168
+ if (p["redirect_uri"] !== saved.redirectUri)
169
+ return json(400, { error: "invalid_grant" });
170
+ if (p["client_id"] !== saved.clientId)
171
+ return json(400, { error: "invalid_client" });
172
+ this.codes.delete(p["code"] ?? "");
173
+ return json(200, this.tokens(saved.clientId, saved.nonce));
174
+ }
175
+ if (p["grant_type"] === "refresh_token") {
176
+ this.refreshCalls++;
177
+ if (this.options.refreshDelayMs)
178
+ await new Promise((r) => setTimeout(r, this.options.refreshDelayMs));
179
+ if (this.options.refreshError)
180
+ return json(400, { error: this.options.refreshError });
181
+ const token = p["refresh_token"] ?? "";
182
+ if (this.used.has(token))
183
+ return json(400, { error: "refresh_token_reused" });
184
+ const clientId = this.current.get(token);
185
+ if (clientId === undefined)
186
+ return json(400, { error: "invalid_grant" });
187
+ this.used.add(token);
188
+ this.current.delete(token);
189
+ const out = this.tokens(clientId, "");
190
+ delete out["id_token"];
191
+ return json(200, out);
192
+ }
193
+ return json(400, { error: "unsupported_grant_type" });
194
+ }
195
+ case "/api/accounts/deviceauth/usercode":
196
+ return json(200, { device_auth_id: "dev-1", user_code: "ABCD-1234", interval: "1" });
197
+ case "/api/accounts/deviceauth/token": {
198
+ this.devicePolls++;
199
+ if (this.devicePolls <= (this.options.devicePending ?? 1))
200
+ return json(403, { error: "authorization_pending" });
201
+ const code = this.next("code");
202
+ const verifier = "device-verifier-0123456789abcdef0123456789abcdef";
203
+ this.codes.set(code, {
204
+ challenge: createHash("sha256").update(verifier).digest("base64url"),
205
+ nonce: "",
206
+ clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
207
+ redirectUri: `${this.issuer}/deviceauth/callback`,
208
+ });
209
+ return json(200, {
210
+ authorization_code: code,
211
+ code_verifier: verifier,
212
+ code_challenge: "x",
213
+ });
214
+ }
215
+ case "/revoke":
216
+ this.revoked.push(params()["token"] ?? "");
217
+ res.writeHead(200).end();
218
+ return;
219
+ default:
220
+ return this.backend(res);
221
+ }
222
+ }
223
+ backend(res) {
224
+ const scripted = this.responses.shift() ?? { events: completedEvents("ok") };
225
+ const status = scripted.status ?? 200;
226
+ if (scripted.events === undefined || status !== 200) {
227
+ res.writeHead(status, { "content-type": "application/json", ...scripted.headers });
228
+ res.end(JSON.stringify(scripted.body ?? {}));
229
+ return;
230
+ }
231
+ res.writeHead(200, { "content-type": "text/event-stream", ...scripted.headers });
232
+ for (const event of scripted.events) {
233
+ const data = typeof event === "string" ? event : JSON.stringify(event);
234
+ res.write(`data: ${data}\n\n`);
235
+ }
236
+ res.end();
237
+ }
238
+ }
239
+ /** 一段最小的成功 SSE(一个文本块)。 */
240
+ export function completedEvents(text, extra = []) {
241
+ return [
242
+ { type: "response.created", response: { id: "resp_1" } },
243
+ ...extra,
244
+ {
245
+ type: "response.output_item.added",
246
+ output_index: 0,
247
+ item: { type: "message", id: "msg_1" },
248
+ },
249
+ { type: "response.output_text.delta", output_index: 0, delta: text },
250
+ {
251
+ type: "response.output_item.done",
252
+ output_index: 0,
253
+ item: { type: "message", id: "msg_1", content: [{ type: "output_text", text }] },
254
+ },
255
+ {
256
+ type: "response.completed",
257
+ response: {
258
+ id: "resp_1",
259
+ status: "completed",
260
+ usage: { input_tokens: 10, output_tokens: 2, input_tokens_details: { cached_tokens: 4 } },
261
+ },
262
+ },
263
+ ];
264
+ }
265
+ /** 跟随 302 打到本地回调(代替浏览器)。 */
266
+ export async function followAuthorize(url) {
267
+ const res = await fetch(url, { redirect: "manual" });
268
+ const location = res.headers.get("location");
269
+ if (location)
270
+ await fetch(location);
271
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 并发刷新测试的子进程入口(esbuild 打包后由 `node` 运行):对同一 auth.json 取一次新鲜条目,
3
+ * 只把 access token 的 SHA-256 打到 stdout(token 原文不进测试输出)。
4
+ */
5
+ export {};
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 并发刷新测试的子进程入口(esbuild 打包后由 `node` 运行):对同一 auth.json 取一次新鲜条目,
3
+ * 只把 access token 的 SHA-256 打到 stdout(token 原文不进测试输出)。
4
+ */
5
+ import { createHash } from "node:crypto";
6
+ import { freshOAuthEntry } from "../oauth/refresh.js";
7
+ const [authFile, provider] = process.argv.slice(2);
8
+ freshOAuthEntry(authFile ?? "", provider ?? "chatgpt").then((entry) => {
9
+ const hash = createHash("sha256")
10
+ .update(entry?.accessToken ?? "")
11
+ .digest("hex");
12
+ process.stdout.write(`${hash}\n`);
13
+ }, (error) => {
14
+ process.stdout.write(`error ${error.code ?? "unknown"}\n`);
15
+ process.exitCode = 1;
16
+ });