@armadra/agent 0.5.1 → 0.6.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.
- package/CHANGELOG.md +177 -304
- package/CHANGELOG.zh-CN.md +442 -0
- package/README.md +408 -364
- package/README.zh-CN.md +705 -0
- package/dist/agent/session-plan.js +2 -2
- package/dist/agent/session-state.d.ts +3 -0
- package/dist/agent/session-state.js +20 -0
- package/dist/agent/session-subagent.d.ts +15 -1
- package/dist/agent/session-subagent.js +26 -3
- package/dist/agent/session-telemetry.d.ts +16 -2
- package/dist/agent/session-telemetry.js +29 -7
- package/dist/agent/session-tools.js +5 -2
- package/dist/agent/session-trace-writer.d.ts +27 -0
- package/dist/agent/session-trace-writer.js +253 -0
- package/dist/agent/session.d.ts +2 -0
- package/dist/agent/session.js +12 -0
- package/dist/agent/subagent-direct.d.ts +20 -0
- package/dist/agent/subagent-direct.js +76 -0
- package/dist/agent/subagent-registry.d.ts +8 -1
- package/dist/agent/subagent-registry.js +28 -1
- package/dist/agent/system-prompt.d.ts +3 -1
- package/dist/agent/system-prompt.js +3 -0
- package/dist/agent/tool-runner.d.ts +2 -1
- package/dist/agent/tool-runner.js +5 -4
- package/dist/agent/types-w6.d.ts +39 -0
- package/dist/agent/types-w6.js +4 -0
- package/dist/agent/types.d.ts +5 -2
- package/dist/agents/external.js +1 -1
- package/dist/agents/task-record.d.ts +74 -1
- package/dist/agents/task-record.js +100 -0
- package/dist/ai/apis/cache-params.js +2 -1
- package/dist/ai/apis/chatgpt-backend.d.ts +52 -0
- package/dist/ai/apis/chatgpt-backend.js +224 -0
- package/dist/ai/apis/chatgpt-rate-limits.d.ts +26 -0
- package/dist/ai/apis/chatgpt-rate-limits.js +91 -0
- package/dist/ai/apis/openai-responses-request.d.ts +2 -1
- package/dist/ai/apis/openai-responses-request.js +8 -2
- package/dist/ai/apis/openai-responses.d.ts +2 -0
- package/dist/ai/apis/openai-responses.js +51 -16
- package/dist/ai/overflow.d.ts +1 -1
- package/dist/ai/overflow.js +10 -7
- package/dist/ai/providers/auth.d.ts +15 -2
- package/dist/ai/providers/auth.js +69 -2
- package/dist/ai/providers/builtin.js +27 -0
- package/dist/ai/providers/catalog-data.js +1 -0
- package/dist/ai/providers/enrich.js +2 -7
- package/dist/ai/providers/models-dev-cache.js +15 -19
- package/dist/ai/providers/models-dev-snapshot.js +8 -7
- package/dist/ai/providers/models-dev.js +5 -15
- package/dist/ai/providers/suggest.js +2 -16
- package/dist/ai/types.d.ts +27 -1
- package/dist/auth/chatgpt/backend-client.d.ts +26 -0
- package/dist/auth/chatgpt/backend-client.js +74 -0
- package/dist/auth/chatgpt/claims.d.ts +17 -0
- package/dist/auth/chatgpt/claims.js +46 -0
- package/dist/auth/chatgpt/cli.d.ts +33 -0
- package/dist/auth/chatgpt/cli.js +252 -0
- package/dist/auth/chatgpt/doctor.d.ts +17 -0
- package/dist/auth/chatgpt/doctor.js +36 -0
- package/dist/auth/chatgpt/host-id.d.ts +7 -0
- package/dist/auth/chatgpt/host-id.js +26 -0
- package/dist/auth/chatgpt/login.d.ts +30 -0
- package/dist/auth/chatgpt/login.js +130 -0
- package/dist/auth/chatgpt/presets.d.ts +66 -0
- package/dist/auth/chatgpt/presets.js +120 -0
- package/dist/auth/chatgpt/quota-text.d.ts +13 -0
- package/dist/auth/chatgpt/quota-text.js +36 -0
- package/dist/auth/oauth/browser.d.ts +12 -0
- package/dist/auth/oauth/browser.js +27 -0
- package/dist/auth/oauth/callback-server.d.ts +47 -0
- package/dist/auth/oauth/callback-server.js +147 -0
- package/dist/auth/oauth/flows.d.ts +62 -0
- package/dist/auth/oauth/flows.js +113 -0
- package/dist/auth/oauth/jwt.d.ts +24 -0
- package/dist/auth/oauth/jwt.js +81 -0
- package/dist/auth/oauth/live.d.ts +21 -0
- package/dist/auth/oauth/live.js +25 -0
- package/dist/auth/oauth/oidc.d.ts +26 -0
- package/dist/auth/oauth/oidc.js +72 -0
- package/dist/auth/oauth/pkce.d.ts +15 -0
- package/dist/auth/oauth/pkce.js +20 -0
- package/dist/auth/oauth/refresh.d.ts +35 -0
- package/dist/auth/oauth/refresh.js +129 -0
- package/dist/auth/oauth/token-client.d.ts +33 -0
- package/dist/auth/oauth/token-client.js +121 -0
- package/dist/auth/oauth/token-store.d.ts +23 -0
- package/dist/auth/oauth/token-store.js +125 -0
- package/dist/auth/testing/fake-oauth.d.ts +73 -0
- package/dist/auth/testing/fake-oauth.js +271 -0
- package/dist/auth/testing/refresh-child.d.ts +5 -0
- package/dist/auth/testing/refresh-child.js +16 -0
- package/dist/bundle/ama.cjs +25928 -11144
- package/dist/checkpoints/backend.js +6 -5
- package/dist/checkpoints/restore.js +3 -2
- package/dist/checkpoints/settings.js +2 -1
- package/dist/checkpoints/shadow-git.js +7 -6
- package/dist/checkpoints/shadow-restore.js +3 -2
- package/dist/checkpoints/tracker.js +4 -3
- package/dist/cli/args.d.ts +9 -2
- package/dist/cli/args.js +74 -27
- package/dist/cli/bootstrap.js +53 -31
- package/dist/cli/choice-prompt.js +8 -6
- package/dist/cli/codemode-notice.js +2 -2
- package/dist/cli/compose-agents.js +2 -1
- package/dist/cli/compose-extensions.js +2 -0
- package/dist/cli/compose-memory.d.ts +53 -0
- package/dist/cli/compose-memory.js +127 -0
- package/dist/cli/compose-providers.d.ts +5 -0
- package/dist/cli/compose-providers.js +32 -2
- package/dist/cli/compose-session.d.ts +5 -0
- package/dist/cli/compose-session.js +27 -13
- package/dist/cli/compose-store.d.ts +2 -0
- package/dist/cli/compose-store.js +9 -5
- package/dist/cli/compose.d.ts +2 -0
- package/dist/cli/compose.js +13 -5
- package/dist/cli/default-model.d.ts +0 -2
- package/dist/cli/default-model.js +5 -8
- package/dist/cli/deps.d.ts +5 -1
- package/dist/cli/exit-codes.d.ts +1 -1
- package/dist/cli/exit-codes.js +7 -16
- package/dist/cli/from-prompt.js +3 -2
- package/dist/cli/help-text.d.ts +2 -1
- package/dist/cli/help-text.js +5 -99
- package/dist/cli/main.d.ts +5 -0
- package/dist/cli/main.js +21 -2
- package/dist/cli/proxy.js +14 -9
- package/dist/cli/runtime.d.ts +5 -0
- package/dist/cli/startup-screen.js +20 -27
- package/dist/cli/startup-steps.js +6 -8
- package/dist/cli/subcommands/auth.d.ts +6 -3
- package/dist/cli/subcommands/auth.js +52 -22
- package/dist/cli/subcommands/config-set.d.ts +21 -0
- package/dist/cli/subcommands/config-set.js +137 -0
- package/dist/cli/subcommands/config.d.ts +2 -1
- package/dist/cli/subcommands/config.js +51 -37
- package/dist/cli/subcommands/doctor.d.ts +2 -1
- package/dist/cli/subcommands/doctor.js +97 -58
- package/dist/cli/subcommands/init.d.ts +1 -1
- package/dist/cli/subcommands/init.js +8 -7
- package/dist/cli/subcommands/memory.d.ts +18 -0
- package/dist/cli/subcommands/memory.js +189 -0
- package/dist/cli/subcommands/model-meta.js +11 -9
- package/dist/cli/subcommands/models-cache-probe.js +23 -20
- package/dist/cli/subcommands/models-discover.d.ts +3 -0
- package/dist/cli/subcommands/models-discover.js +44 -21
- package/dist/cli/subcommands/models.d.ts +1 -1
- package/dist/cli/subcommands/models.js +29 -22
- package/dist/cli/subcommands/probe-runner.js +8 -7
- package/dist/cli/subcommands/providers-list.js +32 -21
- package/dist/cli/subcommands/providers-plan.js +24 -25
- package/dist/cli/subcommands/providers-probe.js +5 -6
- package/dist/cli/subcommands/providers.d.ts +1 -1
- package/dist/cli/subcommands/providers.js +45 -50
- package/dist/cli/subcommands/sessions-export.d.ts +1 -1
- package/dist/cli/subcommands/sessions-export.js +10 -9
- package/dist/cli/subcommands/sessions-search.d.ts +1 -1
- package/dist/cli/subcommands/sessions-search.js +11 -10
- package/dist/cli/subcommands/sessions-trace.d.ts +30 -0
- package/dist/cli/subcommands/sessions-trace.js +156 -0
- package/dist/cli/subcommands/sessions.d.ts +4 -3
- package/dist/cli/subcommands/sessions.js +40 -37
- package/dist/cli/subcommands/stats.d.ts +1 -1
- package/dist/cli/subcommands/stats.js +49 -30
- package/dist/cli/system-prompt-arg.js +3 -2
- package/dist/codemode/capability.js +3 -2
- package/dist/compaction/post-compact.d.ts +1 -0
- package/dist/compaction/post-compact.js +15 -1
- package/dist/compaction/prune-tier.d.ts +1 -1
- package/dist/compaction/prune-tier.js +3 -3
- package/dist/compaction/serialize.js +2 -1
- package/dist/config/auth-file.d.ts +12 -2
- package/dist/config/auth-file.js +32 -10
- package/dist/config/checker.js +12 -11
- package/dist/config/context-files.js +3 -2
- package/dist/config/edit.d.ts +106 -0
- package/dist/config/edit.js +350 -0
- package/dist/config/init.d.ts +4 -3
- package/dist/config/init.js +10 -18
- package/dist/config/json-schema.d.ts +2 -1
- package/dist/config/json-schema.js +101 -68
- package/dist/config/key-docs.d.ts +18 -7
- package/dist/config/key-docs.js +43 -128
- package/dist/config/load.js +7 -6
- package/dist/config/merge.d.ts +4 -1
- package/dist/config/merge.js +42 -22
- package/dist/config/paths.js +6 -5
- package/dist/config/profile.d.ts +5 -1
- package/dist/config/profile.js +7 -2
- package/dist/config/schema-w5.js +3 -2
- package/dist/config/schema-w6.d.ts +19 -0
- package/dist/config/schema-w6.js +115 -0
- package/dist/config/schema.js +34 -20
- package/dist/config/settings-registry.d.ts +78 -0
- package/dist/config/settings-registry.js +184 -0
- package/dist/config/types-w6.d.ts +109 -0
- package/dist/config/types-w6.js +19 -0
- package/dist/config/types.d.ts +11 -8
- package/dist/config/types.js +1 -0
- package/dist/drivers/acp/client.js +1 -1
- package/dist/drivers/acp/driver.js +13 -8
- package/dist/drivers/agents.js +4 -3
- package/dist/drivers/base.js +3 -2
- package/dist/drivers/host-runners.js +2 -1
- package/dist/drivers/native/claude-stream.js +12 -11
- package/dist/drivers/native/codex-app-server.js +17 -12
- package/dist/drivers/native/oneshot.js +8 -7
- package/dist/drivers/pool.js +1 -1
- package/dist/drivers/runner.d.ts +3 -1
- package/dist/drivers/runner.js +68 -20
- package/dist/drivers/turn.js +1 -1
- package/dist/hooks/config.js +3 -2
- package/dist/hooks/protocol.js +18 -12
- package/dist/host/api-impl.js +11 -10
- package/dist/host/loader.js +9 -8
- package/dist/host/types.d.ts +3 -0
- package/dist/i18n/catalog.d.ts +2169 -0
- package/dist/i18n/catalog.js +72 -0
- package/dist/i18n/format.d.ts +21 -0
- package/dist/i18n/format.js +58 -0
- package/dist/i18n/index.d.ts +58 -0
- package/dist/i18n/index.js +72 -0
- package/dist/i18n/messages/agents.d.ts +84 -0
- package/dist/i18n/messages/agents.js +85 -0
- package/dist/i18n/messages/approval.d.ts +99 -0
- package/dist/i18n/messages/approval.js +100 -0
- package/dist/i18n/messages/auth.d.ts +187 -0
- package/dist/i18n/messages/auth.js +189 -0
- package/dist/i18n/messages/cli-args.d.ts +46 -0
- package/dist/i18n/messages/cli-args.js +46 -0
- package/dist/i18n/messages/cli-help.d.ts +12 -0
- package/dist/i18n/messages/cli-help.js +244 -0
- package/dist/i18n/messages/cli.d.ts +336 -0
- package/dist/i18n/messages/cli.js +311 -0
- package/dist/i18n/messages/config-keys.d.ts +289 -0
- package/dist/i18n/messages/config-keys.js +292 -0
- package/dist/i18n/messages/config.d.ts +502 -0
- package/dist/i18n/messages/config.js +239 -0
- package/dist/i18n/messages/drivers.d.ts +123 -0
- package/dist/i18n/messages/drivers.js +124 -0
- package/dist/i18n/messages/errors.d.ts +83 -0
- package/dist/i18n/messages/errors.js +171 -0
- package/dist/i18n/messages/interactive-line.d.ts +61 -0
- package/dist/i18n/messages/interactive-line.js +69 -0
- package/dist/i18n/messages/interactive-startup.d.ts +103 -0
- package/dist/i18n/messages/interactive-startup.js +104 -0
- package/dist/i18n/messages/interactive-view.d.ts +140 -0
- package/dist/i18n/messages/interactive-view.js +141 -0
- package/dist/i18n/messages/interactive.d.ts +493 -0
- package/dist/i18n/messages/interactive.js +248 -0
- package/dist/i18n/messages/memory.d.ts +123 -0
- package/dist/i18n/messages/memory.js +128 -0
- package/dist/i18n/messages/panels.d.ts +190 -0
- package/dist/i18n/messages/panels.js +189 -0
- package/dist/i18n/messages/permissions.d.ts +126 -0
- package/dist/i18n/messages/permissions.js +151 -0
- package/dist/i18n/messages/plan.d.ts +123 -0
- package/dist/i18n/messages/plan.js +124 -0
- package/dist/i18n/messages/print.d.ts +86 -0
- package/dist/i18n/messages/print.js +91 -0
- package/dist/i18n/messages/report.d.ts +391 -0
- package/dist/i18n/messages/report.js +490 -0
- package/dist/i18n/messages/rewind.d.ts +198 -0
- package/dist/i18n/messages/rewind.js +217 -0
- package/dist/i18n/messages/session.d.ts +230 -0
- package/dist/i18n/messages/session.js +258 -0
- package/dist/i18n/messages/settings.d.ts +348 -0
- package/dist/i18n/messages/settings.js +336 -0
- package/dist/i18n/messages/subcommands-config.d.ts +111 -0
- package/dist/i18n/messages/subcommands-config.js +127 -0
- package/dist/i18n/messages/subcommands.d.ts +548 -0
- package/dist/i18n/messages/subcommands.js +474 -0
- package/dist/i18n/messages/trace.d.ts +243 -0
- package/dist/i18n/messages/trace.js +238 -0
- package/dist/i18n/types.d.ts +15 -0
- package/dist/i18n/types.js +7 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +2 -0
- package/dist/memory/edit.d.ts +24 -0
- package/dist/memory/edit.js +63 -0
- package/dist/memory/frontmatter.d.ts +37 -0
- package/dist/memory/frontmatter.js +79 -0
- package/dist/memory/index.d.ts +28 -0
- package/dist/memory/index.js +126 -0
- package/dist/memory/lock.d.ts +17 -0
- package/dist/memory/lock.js +62 -0
- package/dist/memory/paths.d.ts +57 -0
- package/dist/memory/paths.js +175 -0
- package/dist/memory/report.d.ts +36 -0
- package/dist/memory/report.js +86 -0
- package/dist/memory/runtime.d.ts +42 -0
- package/dist/memory/runtime.js +36 -0
- package/dist/memory/secrets.d.ts +8 -0
- package/dist/memory/secrets.js +27 -0
- package/dist/memory/section.d.ts +28 -0
- package/dist/memory/section.js +59 -0
- package/dist/memory/store.d.ts +70 -0
- package/dist/memory/store.js +284 -0
- package/dist/memory/tool.d.ts +31 -0
- package/dist/memory/tool.js +94 -0
- package/dist/modes/acp/acp-events.js +2 -1
- package/dist/modes/acp/acp-server.js +13 -12
- package/dist/modes/commands-core.d.ts +11 -0
- package/dist/modes/commands-core.js +93 -62
- package/dist/modes/image-input.js +20 -6
- package/dist/modes/interactive/agent-bar.d.ts +95 -0
- package/dist/modes/interactive/agent-bar.js +248 -0
- package/dist/modes/interactive/agent-panels.js +27 -26
- package/dist/modes/interactive/agent-transcript.d.ts +47 -0
- package/dist/modes/interactive/agent-transcript.js +191 -0
- package/dist/modes/interactive/agent-ui.d.ts +36 -1
- package/dist/modes/interactive/agent-ui.js +180 -14
- package/dist/modes/interactive/agent-view.d.ts +72 -0
- package/dist/modes/interactive/agent-view.js +281 -0
- package/dist/modes/interactive/approval-dialog.js +46 -34
- package/dist/modes/interactive/clipboard-paste.d.ts +2 -2
- package/dist/modes/interactive/clipboard-paste.js +9 -4
- package/dist/modes/interactive/commands.d.ts +19 -1
- package/dist/modes/interactive/commands.js +84 -40
- package/dist/modes/interactive/completion.js +2 -1
- package/dist/modes/interactive/config-panel.d.ts +70 -0
- package/dist/modes/interactive/config-panel.js +319 -0
- package/dist/modes/interactive/config-ui.d.ts +96 -0
- package/dist/modes/interactive/config-ui.js +397 -0
- package/dist/modes/interactive/confirm-dialog.js +6 -7
- package/dist/modes/interactive/event-notices.js +11 -9
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.js +19 -22
- package/dist/modes/interactive/key-dispatch.d.ts +12 -0
- package/dist/modes/interactive/key-dispatch.js +24 -9
- package/dist/modes/interactive/line/line-editor.js +2 -1
- package/dist/modes/interactive/line/line-mode.js +18 -7
- package/dist/modes/interactive/line/line-render.js +25 -24
- package/dist/modes/interactive/memory-panel.d.ts +64 -0
- package/dist/modes/interactive/memory-panel.js +221 -0
- package/dist/modes/interactive/message-view.d.ts +4 -2
- package/dist/modes/interactive/message-view.js +42 -33
- package/dist/modes/interactive/panels.js +39 -31
- package/dist/modes/interactive/pickers.d.ts +2 -1
- package/dist/modes/interactive/pickers.js +10 -6
- package/dist/modes/interactive/plan-command.d.ts +1 -1
- package/dist/modes/interactive/plan-command.js +24 -27
- package/dist/modes/interactive/plan-dialog.js +18 -17
- package/dist/modes/interactive/plan-flow.js +6 -5
- package/dist/modes/interactive/rewind-command.d.ts +1 -1
- package/dist/modes/interactive/rewind-command.js +18 -16
- package/dist/modes/interactive/rewind-flow.d.ts +0 -1
- package/dist/modes/interactive/rewind-flow.js +13 -14
- package/dist/modes/interactive/rewind-list.js +7 -6
- package/dist/modes/interactive/rewind-panel.js +40 -40
- package/dist/modes/interactive/rewind-text.d.ts +5 -3
- package/dist/modes/interactive/rewind-text.js +48 -38
- package/dist/modes/interactive/run-indicator.js +15 -12
- package/dist/modes/interactive/session-events.js +3 -2
- package/dist/modes/interactive/startup-header.js +22 -23
- package/dist/modes/interactive/startup-ui.js +38 -30
- package/dist/modes/interactive/status-area.d.ts +2 -0
- package/dist/modes/interactive/status-area.js +8 -2
- package/dist/modes/interactive/status-bar.js +4 -3
- package/dist/modes/interactive/subagent-view.js +9 -7
- package/dist/modes/interactive/tasks-report.d.ts +6 -0
- package/dist/modes/interactive/tasks-report.js +28 -31
- package/dist/modes/interactive/terminal-setup.d.ts +9 -0
- package/dist/modes/interactive/terminal-setup.js +23 -0
- package/dist/modes/interactive/tool-summary.js +19 -17
- package/dist/modes/interactive/tool-view.js +7 -4
- package/dist/modes/interactive/trace-view.d.ts +89 -0
- package/dist/modes/interactive/trace-view.js +332 -0
- package/dist/modes/print/print-mode.js +14 -15
- package/dist/modes/rpc/commands.js +7 -3
- package/dist/modes/rpc/rpc-mode.js +9 -3
- package/dist/modes/session-report.d.ts +2 -0
- package/dist/modes/session-report.js +101 -105
- package/dist/modes/startup-ui-text.js +11 -6
- package/dist/permissions/bypass.d.ts +8 -10
- package/dist/permissions/bypass.js +19 -10
- package/dist/permissions/memory-class.d.ts +23 -0
- package/dist/permissions/memory-class.js +54 -0
- package/dist/permissions/modes.d.ts +3 -3
- package/dist/permissions/modes.js +21 -16
- package/dist/permissions/pipeline.d.ts +1 -1
- package/dist/permissions/pipeline.js +9 -2
- package/dist/permissions/preview.js +49 -42
- package/dist/permissions/rules.js +5 -2
- package/dist/permissions/types.d.ts +4 -0
- package/dist/plan/store.js +2 -1
- package/dist/rpc.d.ts +39 -1
- package/dist/rpc.js +3 -0
- package/dist/sandbox/bash.js +11 -8
- package/dist/sandbox/detect.js +14 -11
- package/dist/sdk.d.ts +17 -1
- package/dist/sdk.js +29 -7
- package/dist/session/export.js +31 -30
- package/dist/session/manager.d.ts +1 -1
- package/dist/session/manager.js +5 -3
- package/dist/session/reuse.js +5 -4
- package/dist/session/scan.js +3 -2
- package/dist/session/stats-aggregate.d.ts +3 -1
- package/dist/session/stats-aggregate.js +5 -1
- package/dist/session/stats-index.js +2 -1
- package/dist/session/stats-scan.d.ts +4 -1
- package/dist/session/stats-scan.js +6 -2
- package/dist/session/store.d.ts +11 -0
- package/dist/session/store.js +48 -1
- package/dist/session/types.d.ts +2 -0
- package/dist/skills/builtin.js +2 -1
- package/dist/tools/image-file.d.ts +26 -1
- package/dist/tools/image-file.js +55 -13
- package/dist/tools/presets.d.ts +14 -1
- package/dist/tools/presets.js +3 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/truncate.d.ts +1 -1
- package/dist/tools/truncate.js +2 -2
- package/dist/tools/types.d.ts +17 -1
- package/dist/trace/build-index.d.ts +91 -0
- package/dist/trace/build-index.js +127 -0
- package/dist/trace/build-nodes.d.ts +24 -0
- package/dist/trace/build-nodes.js +279 -0
- package/dist/trace/build-util.d.ts +43 -0
- package/dist/trace/build-util.js +120 -0
- package/dist/trace/build.d.ts +82 -0
- package/dist/trace/build.js +382 -0
- package/dist/trace/detail.d.ts +35 -0
- package/dist/trace/detail.js +205 -0
- package/dist/trace/flatten.d.ts +69 -0
- package/dist/trace/flatten.js +173 -0
- package/dist/trace/format.d.ts +47 -0
- package/dist/trace/format.js +329 -0
- package/dist/trace/html-template.d.ts +19 -0
- package/dist/trace/html-template.js +150 -0
- package/dist/trace/html.d.ts +104 -0
- package/dist/trace/html.js +292 -0
- package/dist/trace/preview.d.ts +29 -0
- package/dist/trace/preview.js +59 -0
- package/dist/trace/query-session.d.ts +14 -0
- package/dist/trace/query-session.js +38 -0
- package/dist/trace/query.d.ts +55 -0
- package/dist/trace/query.js +188 -0
- package/dist/trace/session.d.ts +40 -0
- package/dist/trace/session.js +168 -0
- package/dist/trace/types.d.ts +243 -0
- package/dist/trace/types.js +13 -0
- package/dist/tui/components/editor-paste.d.ts +2 -0
- package/dist/tui/components/editor-paste.js +6 -3
- package/dist/tui/components/editor.js +2 -2
- package/dist/tui/components/loader.d.ts +3 -0
- package/dist/tui/components/loader.js +14 -0
- package/dist/tui/components/settings-list.d.ts +61 -0
- package/dist/tui/components/settings-list.js +135 -0
- package/dist/tui/keybindings.d.ts +5 -0
- package/dist/tui/keybindings.js +17 -5
- package/dist/tui.d.ts +1 -0
- package/dist/tui.js +2 -0
- package/docs/en/host-api.md +167 -0
- package/docs/en/permissions.md +214 -0
- package/docs/en/providers.md +625 -0
- package/docs/en/rpc.md +361 -0
- package/docs/en/sessions.md +219 -0
- package/docs/en/tui.md +527 -0
- package/docs/host-api.md +18 -17
- package/docs/memory.md +172 -0
- package/docs/permissions.md +2 -2
- package/docs/providers.md +67 -1
- package/docs/rpc.md +31 -3
- package/docs/session-format.md +8 -2
- package/docs/sessions.md +29 -1
- package/docs/tui.md +132 -3
- package/package.json +8 -3
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama auth login | logout | status chatgpt`(docs/wave6-plan.md §4.2、D16)。[W6-O]
|
|
3
|
+
*
|
|
4
|
+
* - login:flavor 取 `--flavor` > 用户级 `auth.chatgpt.flavor` > siwc;`--paste` / `--device`(只 codex)/ 缺省
|
|
5
|
+
* 浏览器;codex 首次在 TTY 下一次性确认「非官方、仅个人使用」(非 TTY 需 `--yes`),`acknowledgedAt` 入条目;
|
|
6
|
+
* 成功后在刷新锁下写 auth.json(0600)。
|
|
7
|
+
* - logout:SIWC 先撤销 refresh token(失败照样删本地,提示到 ChatGPT 设置里断开);codex 只删本地。
|
|
8
|
+
* - status:flavor、计划、掩码邮箱、access token 剩余、needsLogin;codex 另查 `wham/usage` 显示配额。
|
|
9
|
+
*
|
|
10
|
+
* 输出里从不出现 token、code、账户 id;错误按码渲染(i18n),不回显响应原文。
|
|
11
|
+
*/
|
|
12
|
+
import { type ChatGptFlavor } from "../../config/types-w6.js";
|
|
13
|
+
import type { CliIo } from "../../cli/deps.js";
|
|
14
|
+
import { type BrowserOpener } from "../oauth/browser.js";
|
|
15
|
+
import type { FetchLike } from "../oauth/token-client.js";
|
|
16
|
+
export interface AuthCliDeps {
|
|
17
|
+
fetch?: FetchLike;
|
|
18
|
+
openBrowser?: BrowserOpener;
|
|
19
|
+
now?: () => number;
|
|
20
|
+
/** `--paste` 读回调 URL;缺省 `io.readStdin`(TTY 下一行不回显)。 */
|
|
21
|
+
readLine?: () => Promise<string>;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthCliArgs {
|
|
24
|
+
positionals: string[];
|
|
25
|
+
values: Map<string, string>;
|
|
26
|
+
flags: Set<string>;
|
|
27
|
+
path: string;
|
|
28
|
+
}
|
|
29
|
+
/** AmaError → 本地化的一行(不含响应原文)。 */
|
|
30
|
+
export declare function loginErrorText(error: unknown, flavor: ChatGptFlavor): string;
|
|
31
|
+
export declare function runLogin(args: AuthCliArgs, io: CliIo, deps?: AuthCliDeps): Promise<number>;
|
|
32
|
+
export declare function runLogout(args: AuthCliArgs, io: CliIo, deps?: AuthCliDeps): Promise<number>;
|
|
33
|
+
export declare function runStatus(args: AuthCliArgs, io: CliIo, deps?: AuthCliDeps): Promise<number>;
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama auth login | logout | status chatgpt`(docs/wave6-plan.md §4.2、D16)。[W6-O]
|
|
3
|
+
*
|
|
4
|
+
* - login:flavor 取 `--flavor` > 用户级 `auth.chatgpt.flavor` > siwc;`--paste` / `--device`(只 codex)/ 缺省
|
|
5
|
+
* 浏览器;codex 首次在 TTY 下一次性确认「非官方、仅个人使用」(非 TTY 需 `--yes`),`acknowledgedAt` 入条目;
|
|
6
|
+
* 成功后在刷新锁下写 auth.json(0600)。
|
|
7
|
+
* - logout:SIWC 先撤销 refresh token(失败照样删本地,提示到 ChatGPT 设置里断开);codex 只删本地。
|
|
8
|
+
* - status:flavor、计划、掩码邮箱、access token 剩余、needsLogin;codex 另查 `wham/usage` 显示配额。
|
|
9
|
+
*
|
|
10
|
+
* 输出里从不出现 token、code、账户 id;错误按码渲染(i18n),不回显响应原文。
|
|
11
|
+
*/
|
|
12
|
+
import { isAmaError } from "../../errors.js";
|
|
13
|
+
import { describeAuthFile, readAuthFile } from "../../config/auth-file.js";
|
|
14
|
+
import { loadConfigFile } from "../../config/load.js";
|
|
15
|
+
import { CONFIG_FILE, resolveConfigDir, resolveDataDir } from "../../config/paths.js";
|
|
16
|
+
import { CHATGPT_FLAVORS, isOAuthEntry, } from "../../config/types-w6.js";
|
|
17
|
+
import { formatDuration, msg } from "../../i18n/index.js";
|
|
18
|
+
import { UsageError } from "../../cli/args.js";
|
|
19
|
+
import { ExitCode } from "../../cli/exit-codes.js";
|
|
20
|
+
import { openBrowser } from "../oauth/browser.js";
|
|
21
|
+
import { readOAuthEntry, withRefreshLock, writeOAuthEntry } from "../oauth/token-store.js";
|
|
22
|
+
import { fetchCodexUsage } from "./backend-client.js";
|
|
23
|
+
import { maskEmail } from "./claims.js";
|
|
24
|
+
import { loginChatGpt, revokeChatGpt } from "./login.js";
|
|
25
|
+
import { CHATGPT_PROVIDER_ID, chatgptBaseUrl } from "./presets.js";
|
|
26
|
+
import { quotaParts } from "./quota-text.js";
|
|
27
|
+
function userAuthConfig(io) {
|
|
28
|
+
try {
|
|
29
|
+
const path = `${resolveConfigDir({ env: io.env })}/${CONFIG_FILE}`;
|
|
30
|
+
return loadConfigFile("config", path)?.value.auth?.chatgpt;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function providerOf(args, action) {
|
|
37
|
+
const provider = args.positionals[1];
|
|
38
|
+
if (provider === undefined)
|
|
39
|
+
throw new UsageError(msg().auth.needProvider(action));
|
|
40
|
+
if (args.positionals.length > 2)
|
|
41
|
+
throw new UsageError(msg().auth.extraArgs(args.positionals.slice(2).join(" ")));
|
|
42
|
+
if (provider !== CHATGPT_PROVIDER_ID)
|
|
43
|
+
throw new UsageError(msg().auth.login.onlyChatgpt(provider));
|
|
44
|
+
return provider;
|
|
45
|
+
}
|
|
46
|
+
/** AmaError → 本地化的一行(不含响应原文)。 */
|
|
47
|
+
export function loginErrorText(error, flavor) {
|
|
48
|
+
const m = msg().auth.errors;
|
|
49
|
+
if (!isAmaError(error))
|
|
50
|
+
return error?.message ?? String(error);
|
|
51
|
+
const detail = (error.detail ?? {});
|
|
52
|
+
switch (error.code) {
|
|
53
|
+
case "oauth_ports_busy":
|
|
54
|
+
return m.portsBusy((detail.ports ?? []).join(" / "), flavor === "codex");
|
|
55
|
+
case "oauth_timeout":
|
|
56
|
+
return m.timeout;
|
|
57
|
+
case "oauth_denied":
|
|
58
|
+
return m.denied(detail.error ?? "error");
|
|
59
|
+
case "oauth_state_mismatch":
|
|
60
|
+
return m.stateMismatch;
|
|
61
|
+
case "oauth_no_code":
|
|
62
|
+
return m.noCode;
|
|
63
|
+
case "oauth_invalid_token":
|
|
64
|
+
return m.invalidToken(detail.reason ?? "invalid");
|
|
65
|
+
case "oauth_scope_missing":
|
|
66
|
+
return m.scopeMissing(detail.scope ?? "chatgpt.tokens.use.direct");
|
|
67
|
+
case "oauth_no_client_id":
|
|
68
|
+
return m.noClientId;
|
|
69
|
+
case "oauth_no_account":
|
|
70
|
+
return m.noAccount;
|
|
71
|
+
case "oauth_device_unavailable":
|
|
72
|
+
return m.deviceUnavailable;
|
|
73
|
+
case "oauth_device_unsupported":
|
|
74
|
+
return msg().auth.login.deviceNeedsCodex;
|
|
75
|
+
case "oauth_http":
|
|
76
|
+
case "oauth_bad_response":
|
|
77
|
+
return m.http(error.message);
|
|
78
|
+
case "oauth_network":
|
|
79
|
+
return m.network(error.message);
|
|
80
|
+
case "auth_lock_timeout":
|
|
81
|
+
return m.lockTimeout;
|
|
82
|
+
case "aborted":
|
|
83
|
+
return m.aborted;
|
|
84
|
+
default:
|
|
85
|
+
return error.message;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function parseFlavor(raw, config) {
|
|
89
|
+
const value = raw ?? config?.flavor ?? "siwc";
|
|
90
|
+
if (!CHATGPT_FLAVORS.includes(value))
|
|
91
|
+
throw new UsageError(msg().auth.login.badFlavor(value));
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
function parsePort(raw) {
|
|
95
|
+
if (raw === undefined)
|
|
96
|
+
return undefined;
|
|
97
|
+
const port = Number(raw);
|
|
98
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535)
|
|
99
|
+
throw new UsageError(msg().auth.login.badPort(raw));
|
|
100
|
+
return port;
|
|
101
|
+
}
|
|
102
|
+
/** 确认输入两种语言都认(y / yes / 是 / 好),与界面语言无关。 */
|
|
103
|
+
function isYes(answer) {
|
|
104
|
+
return /^\s*(y|yes|\u662f|\u597d)\s*$/i.test(answer);
|
|
105
|
+
}
|
|
106
|
+
export async function runLogin(args, io, deps = {}) {
|
|
107
|
+
const m = msg().auth.login;
|
|
108
|
+
const provider = providerOf(args, "login");
|
|
109
|
+
const config = userAuthConfig(io);
|
|
110
|
+
const flavor = parseFlavor(args.values.get("flavor"), config);
|
|
111
|
+
const port = parsePort(args.values.get("port"));
|
|
112
|
+
if (args.flags.has("paste") && args.flags.has("device"))
|
|
113
|
+
throw new UsageError(m.pasteDevice);
|
|
114
|
+
const method = args.flags.has("device")
|
|
115
|
+
? "device"
|
|
116
|
+
: args.flags.has("paste")
|
|
117
|
+
? "paste"
|
|
118
|
+
: "browser";
|
|
119
|
+
if (method === "device" && flavor !== "codex")
|
|
120
|
+
throw new UsageError(m.deviceNeedsCodex);
|
|
121
|
+
const existing = readOAuthEntry(args.path, provider);
|
|
122
|
+
let acknowledgedAt;
|
|
123
|
+
if (flavor === "codex") {
|
|
124
|
+
acknowledgedAt = existing?.flavor === "codex" ? existing.acknowledgedAt : undefined;
|
|
125
|
+
if (acknowledgedAt === undefined) {
|
|
126
|
+
io.stderr(`${m.codexNotice}\n`);
|
|
127
|
+
if (!args.flags.has("yes")) {
|
|
128
|
+
if (!io.stdinIsTTY) {
|
|
129
|
+
io.stderr(`ama: ${m.codexNeedsYes}\n`);
|
|
130
|
+
return ExitCode.Usage;
|
|
131
|
+
}
|
|
132
|
+
io.stderr(m.codexConfirm);
|
|
133
|
+
const answer = await io.readStdin();
|
|
134
|
+
io.stderr("\n");
|
|
135
|
+
if (!isYes(answer)) {
|
|
136
|
+
io.stderr(`ama: ${m.cancelled}\n`);
|
|
137
|
+
return ExitCode.RuntimeError;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
acknowledgedAt = new Date((deps.now ?? Date.now)()).toISOString();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const onStep = (step) => {
|
|
144
|
+
if (step.kind === "open_url")
|
|
145
|
+
io.stderr(`${step.opened ? m.openedBrowser(step.url) : m.openUrl(step.url)}\n`);
|
|
146
|
+
else if (step.kind === "paste_prompt")
|
|
147
|
+
io.stderr(m.pastePrompt(step.url));
|
|
148
|
+
else
|
|
149
|
+
io.stderr(`${m.deviceCode(step.url, step.userCode)}\n`);
|
|
150
|
+
};
|
|
151
|
+
let entry;
|
|
152
|
+
try {
|
|
153
|
+
entry = await loginChatGpt({
|
|
154
|
+
flavor,
|
|
155
|
+
method,
|
|
156
|
+
noBrowser: args.flags.has("no-browser"),
|
|
157
|
+
dataDir: resolveDataDir({ env: io.env }),
|
|
158
|
+
env: io.env,
|
|
159
|
+
config,
|
|
160
|
+
entry: existing,
|
|
161
|
+
port,
|
|
162
|
+
acknowledgedAt,
|
|
163
|
+
deps: {
|
|
164
|
+
fetch: deps.fetch ?? fetch,
|
|
165
|
+
openBrowser: deps.openBrowser ?? openBrowser,
|
|
166
|
+
onStep,
|
|
167
|
+
readLine: async () => {
|
|
168
|
+
const line = await (deps.readLine ?? (() => io.readStdin()))();
|
|
169
|
+
if (io.stdinIsTTY)
|
|
170
|
+
io.stderr("\n");
|
|
171
|
+
return line;
|
|
172
|
+
},
|
|
173
|
+
pages: { success: m.pageSuccess, failure: m.pageFailure },
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
await withRefreshLock(args.path, async () => writeOAuthEntry(args.path, provider, entry));
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
io.stderr(`ama: ${loginErrorText(error, flavor)}\n`);
|
|
180
|
+
return ExitCode.RuntimeError;
|
|
181
|
+
}
|
|
182
|
+
io.stdout(`${m.loggedIn(flavor, entry.planType, maskEmail(entry.email), args.path)}\n`);
|
|
183
|
+
if (entry.planType === "free")
|
|
184
|
+
io.stdout(`${m.freePlanHint(entry.planType)}\n`);
|
|
185
|
+
if (flavor === "siwc")
|
|
186
|
+
io.stdout(`${m.siwcLimitHint}\n`);
|
|
187
|
+
io.stdout(`${m.modelHint}\n`);
|
|
188
|
+
return ExitCode.Ok;
|
|
189
|
+
}
|
|
190
|
+
export async function runLogout(args, io, deps = {}) {
|
|
191
|
+
const m = msg().auth.logout;
|
|
192
|
+
const provider = providerOf(args, "logout");
|
|
193
|
+
const entry = readOAuthEntry(args.path, provider);
|
|
194
|
+
if (entry === undefined) {
|
|
195
|
+
io.stderr(`${m.notLoggedIn(provider, args.path)}\n`);
|
|
196
|
+
return ExitCode.RuntimeError;
|
|
197
|
+
}
|
|
198
|
+
const revoked = entry.flavor === "siwc"
|
|
199
|
+
? await revokeChatGpt(deps.fetch ?? fetch, entry, { env: io.env, config: userAuthConfig(io) })
|
|
200
|
+
: undefined;
|
|
201
|
+
try {
|
|
202
|
+
await withRefreshLock(args.path, async () => writeOAuthEntry(args.path, provider, undefined));
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
io.stderr(`ama: ${loginErrorText(error, entry.flavor)}\n`);
|
|
206
|
+
return ExitCode.RuntimeError;
|
|
207
|
+
}
|
|
208
|
+
const text = revoked === undefined
|
|
209
|
+
? m.local(provider, args.path)
|
|
210
|
+
: revoked
|
|
211
|
+
? m.revoked(provider, args.path)
|
|
212
|
+
: m.notRevoked(provider, args.path);
|
|
213
|
+
io.stdout(`${text}\n`);
|
|
214
|
+
return ExitCode.Ok;
|
|
215
|
+
}
|
|
216
|
+
export async function runStatus(args, io, deps = {}) {
|
|
217
|
+
const m = msg().auth.status;
|
|
218
|
+
if (args.positionals.length > 2)
|
|
219
|
+
throw new UsageError(msg().auth.extraArgs(args.positionals.slice(2).join(" ")));
|
|
220
|
+
const only = args.positionals[1];
|
|
221
|
+
const now = (deps.now ?? Date.now)();
|
|
222
|
+
const { file } = readAuthFile(args.path);
|
|
223
|
+
const entries = Object.entries(file.providers).filter((pair) => isOAuthEntry(pair[1]) && (only === undefined || pair[0] === only));
|
|
224
|
+
if (entries.length === 0) {
|
|
225
|
+
io.stdout(`${m.none(args.path)}\n`);
|
|
226
|
+
return ExitCode.Ok;
|
|
227
|
+
}
|
|
228
|
+
const summaries = new Map(describeAuthFile(file, now).map((s) => [s.provider, s]));
|
|
229
|
+
const config = userAuthConfig(io);
|
|
230
|
+
for (const [provider, entry] of entries) {
|
|
231
|
+
const summary = summaries.get(provider);
|
|
232
|
+
io.stdout(`${m.header(provider, msg().auth.oauthSummary(entry.flavor, entry.planType, entry.needsLogin === true), maskEmail(entry.email))}\n`);
|
|
233
|
+
const left = summary?.expiresIn ?? entry.expiresAt - now;
|
|
234
|
+
if (entry.needsLogin === true)
|
|
235
|
+
io.stdout(`${m.needsLogin(provider)}\n`);
|
|
236
|
+
else
|
|
237
|
+
io.stdout(`${left > 0 ? m.expiresIn(formatDuration(left)) : m.expired}\n`);
|
|
238
|
+
if (entry.flavor === "siwc")
|
|
239
|
+
io.stdout(`${m.quotaSiwc}\n`);
|
|
240
|
+
else if (entry.needsLogin !== true && left > 0) {
|
|
241
|
+
const quota = await fetchCodexUsage(deps.fetch ?? fetch, chatgptBaseUrl("codex", io.env), {
|
|
242
|
+
flavor: "codex",
|
|
243
|
+
accessToken: entry.accessToken,
|
|
244
|
+
accountId: entry.accountId,
|
|
245
|
+
originator: config?.originator,
|
|
246
|
+
});
|
|
247
|
+
const parts = quota === undefined ? undefined : quotaParts(quota, now);
|
|
248
|
+
io.stdout(`${parts === undefined ? m.quotaUnavailable : m.quota(parts)}\n`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return ExitCode.Ok;
|
|
252
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama doctor` 的 OAuth 行([W6-O]):只读文件、不刷新 token;codex flavor 且 token 未过期时查一次
|
|
3
|
+
* `wham/usage`(3 s 超时,失败静默)显示配额,SIWC 只给查看位置。
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatGptAuthConfig, OAuthAuthEntry } from "../../config/types-w6.js";
|
|
6
|
+
import type { FetchLike } from "../oauth/token-client.js";
|
|
7
|
+
export interface OAuthDoctorLines {
|
|
8
|
+
line: string;
|
|
9
|
+
quota?: string;
|
|
10
|
+
problem?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function oauthDoctorLines(provider: string, entry: OAuthAuthEntry, options?: {
|
|
13
|
+
now?: number;
|
|
14
|
+
fetch?: FetchLike;
|
|
15
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
16
|
+
config?: ChatGptAuthConfig | undefined;
|
|
17
|
+
}): Promise<OAuthDoctorLines>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama doctor` 的 OAuth 行([W6-O]):只读文件、不刷新 token;codex flavor 且 token 未过期时查一次
|
|
3
|
+
* `wham/usage`(3 s 超时,失败静默)显示配额,SIWC 只给查看位置。
|
|
4
|
+
*/
|
|
5
|
+
import { formatDuration, msg } from "../../i18n/index.js";
|
|
6
|
+
import { fetchCodexUsage } from "./backend-client.js";
|
|
7
|
+
import { chatgptBaseUrl } from "./presets.js";
|
|
8
|
+
import { quotaParts } from "./quota-text.js";
|
|
9
|
+
export async function oauthDoctorLines(provider, entry, options = {}) {
|
|
10
|
+
const m = msg().auth;
|
|
11
|
+
const now = options.now ?? Date.now();
|
|
12
|
+
const left = entry.expiresAt - now;
|
|
13
|
+
const state = entry.needsLogin === true
|
|
14
|
+
? m.doctor.needsLogin
|
|
15
|
+
: left > 0
|
|
16
|
+
? m.doctor.valid(formatDuration(left))
|
|
17
|
+
: m.doctor.expired;
|
|
18
|
+
const out = { line: m.doctor.oauth(entry.flavor, entry.planType, state) };
|
|
19
|
+
if (entry.needsLogin === true) {
|
|
20
|
+
out.problem = m.doctor.relogin(provider);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
if (entry.flavor === "siwc")
|
|
24
|
+
out.quota = m.status.quotaSiwc.trim();
|
|
25
|
+
else if (left > 0) {
|
|
26
|
+
const quota = await fetchCodexUsage(options.fetch ?? fetch, chatgptBaseUrl("codex", options.env), {
|
|
27
|
+
flavor: "codex",
|
|
28
|
+
accessToken: entry.accessToken,
|
|
29
|
+
accountId: entry.accountId,
|
|
30
|
+
originator: options.config?.originator,
|
|
31
|
+
}, 3_000);
|
|
32
|
+
const parts = quota === undefined ? undefined : quotaParts(quota, now);
|
|
33
|
+
out.quota = (parts === undefined ? m.status.quotaUnavailable : m.status.quota(parts)).trim();
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SIWC 的 `ext_agent_host_id`(docs/wave6-plan.md D16):每个安装稳定不变,存
|
|
3
|
+
* `<dataDir>/chatgpt-host.json`(0600,按安装、不随 dotfiles 同步),形如 `urn:uuid:…`。[W6-O]
|
|
4
|
+
*/
|
|
5
|
+
export declare const HOST_ID_FILE = "chatgpt-host.json";
|
|
6
|
+
export declare function hostIdPath(dataDir: string): string;
|
|
7
|
+
export declare function readOrCreateHostId(dataDir: string): string;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SIWC 的 `ext_agent_host_id`(docs/wave6-plan.md D16):每个安装稳定不变,存
|
|
3
|
+
* `<dataDir>/chatgpt-host.json`(0600,按安装、不随 dotfiles 同步),形如 `urn:uuid:…`。[W6-O]
|
|
4
|
+
*/
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
export const HOST_ID_FILE = "chatgpt-host.json";
|
|
9
|
+
export function hostIdPath(dataDir) {
|
|
10
|
+
return join(dataDir, HOST_ID_FILE);
|
|
11
|
+
}
|
|
12
|
+
export function readOrCreateHostId(dataDir) {
|
|
13
|
+
const path = hostIdPath(dataDir);
|
|
14
|
+
try {
|
|
15
|
+
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
16
|
+
if (typeof value.hostId === "string" && /^urn:uuid:[0-9a-f-]{36}$/i.test(value.hostId))
|
|
17
|
+
return value.hostId;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// 不存在或损坏:重建
|
|
21
|
+
}
|
|
22
|
+
const hostId = `urn:uuid:${randomUUID()}`;
|
|
23
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
24
|
+
writeFileSync(path, `${JSON.stringify({ version: 1, hostId }, null, 2)}\n`, { mode: 0o600 });
|
|
25
|
+
return hostId;
|
|
26
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGPT 登录 / 登出编排(docs/wave6-plan.md §4.1–§4.3、D13–D16)。[W6-O]
|
|
3
|
+
*
|
|
4
|
+
* 登录:预设 → 授权码(browser / paste / device)→ 换 token(表单编码;SIWC 带 `resource`,client id 用
|
|
5
|
+
* 回调带回的签发 id)→ 校验(SIWC:JWKS 验签 iss / aud / nonce / exp + 授予 scope 含
|
|
6
|
+
* `chatgpt.tokens.use.direct`;codex:只解码取账户)→ `OAuthAuthEntry`。写文件由调用方经 token-store 做。
|
|
7
|
+
*
|
|
8
|
+
* 登出:SIWC 调 discovery 里的 `revocation_endpoint`(`token=<refresh>`、`token_type_hint=refresh_token`、
|
|
9
|
+
* `client_id`),失败不阻止删除本地条目;codex 只删本地。
|
|
10
|
+
*/
|
|
11
|
+
import type { ChatGptFlavor, OAuthAuthEntry } from "../../config/types-w6.js";
|
|
12
|
+
import { type AuthorizationResult, type FlowDeps } from "../oauth/flows.js";
|
|
13
|
+
import { type FetchLike } from "../oauth/token-client.js";
|
|
14
|
+
import { type ChatGptPreset, type PresetOverrides } from "./presets.js";
|
|
15
|
+
export type LoginMethod = "browser" | "paste" | "device";
|
|
16
|
+
export interface ChatGptLoginOptions extends PresetOverrides {
|
|
17
|
+
flavor: ChatGptFlavor;
|
|
18
|
+
method: LoginMethod;
|
|
19
|
+
noBrowser?: boolean;
|
|
20
|
+
/** SIWC 的 `ext_agent_host_id` 存放目录(`<dataDir>`)。 */
|
|
21
|
+
dataDir: string;
|
|
22
|
+
/** codex 的一次性确认时刻(ISO);调用方负责问。 */
|
|
23
|
+
acknowledgedAt?: string | undefined;
|
|
24
|
+
deps: FlowDeps;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_ACCESS_TTL_S = 3600;
|
|
27
|
+
export declare function exchangeCode(fetchFn: FetchLike, preset: ChatGptPreset, auth: AuthorizationResult, signal?: AbortSignal): Promise<OAuthAuthEntry>;
|
|
28
|
+
export declare function loginChatGpt(options: ChatGptLoginOptions): Promise<OAuthAuthEntry>;
|
|
29
|
+
/** SIWC 登出:撤销 refresh token。返回是否确认撤销(失败 / 不支持为 false,不抛)。 */
|
|
30
|
+
export declare function revokeChatGpt(fetchFn: FetchLike, entry: OAuthAuthEntry, overrides?: PresetOverrides): Promise<boolean>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGPT 登录 / 登出编排(docs/wave6-plan.md §4.1–§4.3、D13–D16)。[W6-O]
|
|
3
|
+
*
|
|
4
|
+
* 登录:预设 → 授权码(browser / paste / device)→ 换 token(表单编码;SIWC 带 `resource`,client id 用
|
|
5
|
+
* 回调带回的签发 id)→ 校验(SIWC:JWKS 验签 iss / aud / nonce / exp + 授予 scope 含
|
|
6
|
+
* `chatgpt.tokens.use.direct`;codex:只解码取账户)→ `OAuthAuthEntry`。写文件由调用方经 token-store 做。
|
|
7
|
+
*
|
|
8
|
+
* 登出:SIWC 调 discovery 里的 `revocation_endpoint`(`token=<refresh>`、`token_type_hint=refresh_token`、
|
|
9
|
+
* `client_id`),失败不阻止删除本地条目;codex 只删本地。
|
|
10
|
+
*/
|
|
11
|
+
import { AmaError } from "../../errors.js";
|
|
12
|
+
import { loginBrowser, loginDevice, loginPaste, } from "../oauth/flows.js";
|
|
13
|
+
import { jwtClaims } from "../oauth/jwt.js";
|
|
14
|
+
import { fetchDiscovery, validateIdToken } from "../oauth/oidc.js";
|
|
15
|
+
import { parseTokenResponse, postOAuth } from "../oauth/token-client.js";
|
|
16
|
+
import { chatgptAccount, hasScope } from "./claims.js";
|
|
17
|
+
import { readOrCreateHostId } from "./host-id.js";
|
|
18
|
+
import { SIWC_REGISTRATION_CLIENT_ID, authorizeUrl, resolveChatGptPreset, } from "./presets.js";
|
|
19
|
+
export const DEFAULT_ACCESS_TTL_S = 3600;
|
|
20
|
+
function flowPreset(preset, hostId) {
|
|
21
|
+
return {
|
|
22
|
+
issuer: preset.issuer,
|
|
23
|
+
clientId: preset.clientId,
|
|
24
|
+
redirectPorts: preset.redirectPorts,
|
|
25
|
+
buildAuthorizeUrl: (input) => authorizeUrl(preset, { ...input, hostId }),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function issuedClientId(preset, auth, tokenClientId, idToken) {
|
|
29
|
+
if (!preset.registering)
|
|
30
|
+
return preset.clientId;
|
|
31
|
+
const fromAud = () => {
|
|
32
|
+
const aud = jwtClaims(idToken)?.["aud"];
|
|
33
|
+
const list = Array.isArray(aud) ? aud : [aud];
|
|
34
|
+
return list.find((a) => typeof a === "string" && a !== SIWC_REGISTRATION_CLIENT_ID);
|
|
35
|
+
};
|
|
36
|
+
const issued = auth.clientId ?? tokenClientId ?? fromAud();
|
|
37
|
+
if (issued === undefined || issued === SIWC_REGISTRATION_CLIENT_ID)
|
|
38
|
+
throw new AmaError("oauth_no_client_id", "dynamic registration returned no client id");
|
|
39
|
+
return issued;
|
|
40
|
+
}
|
|
41
|
+
export async function exchangeCode(fetchFn, preset, auth, signal) {
|
|
42
|
+
// 新注册:交换要用签发的 id(回调带回);回调没带时先用注册入口换,再从 id_token 的 aud 取
|
|
43
|
+
const exchangeClient = preset.registering ? (auth.clientId ?? preset.clientId) : preset.clientId;
|
|
44
|
+
const params = {
|
|
45
|
+
grant_type: "authorization_code",
|
|
46
|
+
code: auth.code,
|
|
47
|
+
redirect_uri: auth.redirectUri,
|
|
48
|
+
client_id: exchangeClient,
|
|
49
|
+
code_verifier: auth.verifier,
|
|
50
|
+
};
|
|
51
|
+
if (preset.resource !== undefined)
|
|
52
|
+
params["resource"] = preset.resource;
|
|
53
|
+
const token = parseTokenResponse(await postOAuth(fetchFn, preset.tokenUrl, params, {
|
|
54
|
+
encoding: "form",
|
|
55
|
+
what: "token exchange",
|
|
56
|
+
signal,
|
|
57
|
+
}), "token exchange");
|
|
58
|
+
if (token.refreshToken === undefined)
|
|
59
|
+
throw new AmaError("oauth_bad_response", "token exchange: no refresh_token");
|
|
60
|
+
const clientId = issuedClientId(preset, auth, token.clientId, token.idToken);
|
|
61
|
+
let claims = jwtClaims(token.idToken);
|
|
62
|
+
if (preset.verifyIdToken) {
|
|
63
|
+
if (token.idToken === undefined)
|
|
64
|
+
throw new AmaError("oauth_invalid_token", "invalid id_token: missing");
|
|
65
|
+
claims = await validateIdToken(fetchFn, token.idToken, {
|
|
66
|
+
issuer: preset.issuer,
|
|
67
|
+
audience: clientId,
|
|
68
|
+
nonce: auth.nonce,
|
|
69
|
+
});
|
|
70
|
+
const scope = auth.scope ?? token.scope;
|
|
71
|
+
if (preset.requiredScope !== undefined && !hasScope(scope, preset.requiredScope))
|
|
72
|
+
throw new AmaError("oauth_scope_missing", `granted scope lacks ${preset.requiredScope}`, {
|
|
73
|
+
detail: { scope: preset.requiredScope },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const account = chatgptAccount(claims, preset.flavor);
|
|
77
|
+
if (preset.flavor === "codex" && account.accountId === undefined)
|
|
78
|
+
throw new AmaError("oauth_no_account", "id_token has no ChatGPT account id");
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
const entry = {
|
|
81
|
+
type: "oauth",
|
|
82
|
+
flavor: preset.flavor,
|
|
83
|
+
clientId,
|
|
84
|
+
issuer: preset.issuer,
|
|
85
|
+
...account,
|
|
86
|
+
accessToken: token.accessToken,
|
|
87
|
+
refreshToken: token.refreshToken,
|
|
88
|
+
...(token.idToken !== undefined ? { idToken: token.idToken } : {}),
|
|
89
|
+
expiresAt: now + (token.expiresIn ?? DEFAULT_ACCESS_TTL_S) * 1000,
|
|
90
|
+
lastRefresh: new Date(now).toISOString(),
|
|
91
|
+
};
|
|
92
|
+
return entry;
|
|
93
|
+
}
|
|
94
|
+
export async function loginChatGpt(options) {
|
|
95
|
+
const preset = resolveChatGptPreset(options.flavor, options);
|
|
96
|
+
if (options.method === "device" && !preset.device)
|
|
97
|
+
throw new AmaError("oauth_device_unsupported", "device code login needs --flavor codex");
|
|
98
|
+
const hostId = preset.flavor === "siwc" ? readOrCreateHostId(options.dataDir) : undefined;
|
|
99
|
+
const flow = flowPreset(preset, hostId);
|
|
100
|
+
const auth = options.method === "device"
|
|
101
|
+
? await loginDevice(flow, options.deps)
|
|
102
|
+
: options.method === "paste"
|
|
103
|
+
? await loginPaste(flow, options.deps)
|
|
104
|
+
: await loginBrowser(flow, options.deps, { noBrowser: options.noBrowser === true });
|
|
105
|
+
const entry = await exchangeCode(options.deps.fetch, preset, auth, options.deps.signal);
|
|
106
|
+
if (options.acknowledgedAt !== undefined)
|
|
107
|
+
entry.acknowledgedAt = options.acknowledgedAt;
|
|
108
|
+
return entry;
|
|
109
|
+
}
|
|
110
|
+
/** SIWC 登出:撤销 refresh token。返回是否确认撤销(失败 / 不支持为 false,不抛)。 */
|
|
111
|
+
export async function revokeChatGpt(fetchFn, entry, overrides = {}) {
|
|
112
|
+
const preset = resolveChatGptPreset(entry.flavor, { ...overrides, entry });
|
|
113
|
+
if (!preset.revoke)
|
|
114
|
+
return false;
|
|
115
|
+
const issuer = entry.issuer ?? preset.issuer;
|
|
116
|
+
try {
|
|
117
|
+
const discovery = await fetchDiscovery(fetchFn, issuer);
|
|
118
|
+
if (discovery.revocationEndpoint === undefined)
|
|
119
|
+
return false;
|
|
120
|
+
await postOAuth(fetchFn, discovery.revocationEndpoint, {
|
|
121
|
+
token: entry.refreshToken,
|
|
122
|
+
token_type_hint: "refresh_token",
|
|
123
|
+
client_id: entry.clientId ?? preset.clientId,
|
|
124
|
+
}, { encoding: "form", what: "token revocation" });
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChatGPT 登录预设表(docs/wave6-plan.md §4.1、D13–D16;R6 §1.1、§1.2、§4.10)。[W6-O]
|
|
3
|
+
*
|
|
4
|
+
* 两条路径共用 PKCE / 回调 / 存储 / 锁 / 配额框架,差异全部在这张表里:
|
|
5
|
+
* - `siwc`(缺省,官方动态注册):`/api/accounts/*`,首次 `client_id=dynamic_agent_client`,回调带回签发的
|
|
6
|
+
* id 后存进条目;表单编码、带 `resource`;id_token 必须 JWKS 验签;推理走 `api.openai.com/v1`;
|
|
7
|
+
* - `codex`(显式开启的备用):Codex CLI 的公开客户端,`/oauth/*`,刷新 JSON 编码;id_token 只解码;
|
|
8
|
+
* 推理走 `chatgpt.com/backend-api/codex`,另发 `ChatGPT-Account-ID` 与 `originator`。
|
|
9
|
+
*
|
|
10
|
+
* 覆盖:`AMA_CHATGPT_CLIENT_ID` / `AMA_CHATGPT_ISSUER`(测试用)/ `AMA_CHATGPT_BASE_URL` > 配置
|
|
11
|
+
* `auth.chatgpt.*` > 条目里签发的 client id > 缺省。
|
|
12
|
+
*/
|
|
13
|
+
import type { ChatGptAuthConfig, ChatGptFlavor, OAuthAuthEntry } from "../../config/types-w6.js";
|
|
14
|
+
export declare const CHATGPT_PROVIDER_ID = "chatgpt";
|
|
15
|
+
export declare const DEFAULT_ISSUER = "https://auth.openai.com";
|
|
16
|
+
export declare const SIWC_REGISTRATION_CLIENT_ID = "dynamic_agent_client";
|
|
17
|
+
export declare const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
18
|
+
export declare const SIWC_RESOURCE = "https://api.openai.com/v1";
|
|
19
|
+
export declare const SIWC_REQUIRED_SCOPE = "chatgpt.tokens.use.direct";
|
|
20
|
+
export declare const AGENT_NAME_HINT = "ama";
|
|
21
|
+
export declare const DEFAULT_ORIGINATOR = "codex_cli_rs";
|
|
22
|
+
export declare const CHATGPT_BASE_URLS: Readonly<Record<ChatGptFlavor, string>>;
|
|
23
|
+
export interface AuthorizeInput {
|
|
24
|
+
redirectUri: string;
|
|
25
|
+
state: string;
|
|
26
|
+
challenge: string;
|
|
27
|
+
nonce: string;
|
|
28
|
+
/** SIWC:`<dataDir>/chatgpt-host.json` 的安装 id。 */
|
|
29
|
+
hostId?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface ChatGptPreset {
|
|
32
|
+
flavor: ChatGptFlavor;
|
|
33
|
+
issuer: string;
|
|
34
|
+
clientId: string;
|
|
35
|
+
/** SIWC 还没有签发 id(用注册入口登录)。 */
|
|
36
|
+
registering: boolean;
|
|
37
|
+
authorizeUrl: string;
|
|
38
|
+
tokenUrl: string;
|
|
39
|
+
scopes: readonly string[];
|
|
40
|
+
redirectPorts: readonly number[];
|
|
41
|
+
/** SIWC 的 `resource`(交换与刷新都带)。 */
|
|
42
|
+
resource?: string;
|
|
43
|
+
refreshEncoding: "form" | "json";
|
|
44
|
+
/** 推理端点(渠道 baseUrl)。 */
|
|
45
|
+
baseUrl: string;
|
|
46
|
+
/** SIWC:id_token 必须 JWKS 验签,且授予 scope 含 `chatgpt.tokens.use.direct`。 */
|
|
47
|
+
verifyIdToken: boolean;
|
|
48
|
+
requiredScope?: string;
|
|
49
|
+
/** 设备码(只有 codex)。 */
|
|
50
|
+
device: boolean;
|
|
51
|
+
/** 登出调 revocation_endpoint(只有 SIWC)。 */
|
|
52
|
+
revoke: boolean;
|
|
53
|
+
originator?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface PresetOverrides {
|
|
56
|
+
config?: ChatGptAuthConfig | undefined;
|
|
57
|
+
env?: Readonly<Record<string, string | undefined>> | undefined;
|
|
58
|
+
/** 已有条目:SIWC 复用签发的 client id。 */
|
|
59
|
+
entry?: OAuthAuthEntry | undefined;
|
|
60
|
+
/** `--port N`。 */
|
|
61
|
+
port?: number | undefined;
|
|
62
|
+
}
|
|
63
|
+
export declare function chatgptBaseUrl(flavor: ChatGptFlavor, env?: Readonly<Record<string, string | undefined>>): string;
|
|
64
|
+
export declare function resolveChatGptPreset(flavor: ChatGptFlavor, overrides?: PresetOverrides): ChatGptPreset;
|
|
65
|
+
/** 授权 URL(参数顺序固定,便于测试)。 */
|
|
66
|
+
export declare function authorizeUrl(preset: ChatGptPreset, input: AuthorizeInput): string;
|