@armadra/agent 0.3.0 → 0.5.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 +257 -0
- package/README.md +263 -95
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/dist/acp.d.ts +18 -0
- package/dist/acp.js +19 -0
- package/dist/agent/limits.d.ts +38 -0
- package/dist/agent/limits.js +127 -0
- package/dist/agent/loop-guard.d.ts +38 -0
- package/dist/agent/loop-guard.js +95 -0
- package/dist/agent/loop.d.ts +4 -0
- package/dist/agent/loop.js +7 -0
- package/dist/agent/prompt-rules.d.ts +12 -0
- package/dist/agent/prompt-rules.js +18 -0
- package/dist/agent/reminders.d.ts +59 -0
- package/dist/agent/reminders.js +327 -0
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.d.ts +5 -0
- package/dist/agent/session-cache.js +18 -2
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +105 -0
- package/dist/agent/session-compaction.d.ts +35 -6
- package/dist/agent/session-compaction.js +157 -31
- package/dist/agent/session-core.d.ts +40 -1
- package/dist/agent/session-extensions.d.ts +61 -0
- package/dist/agent/session-extensions.js +129 -0
- package/dist/agent/session-images.d.ts +23 -0
- package/dist/agent/session-images.js +97 -0
- package/dist/agent/session-plan.d.ts +21 -0
- package/dist/agent/session-plan.js +524 -0
- package/dist/agent/session-rewind.d.ts +81 -0
- package/dist/agent/session-rewind.js +354 -0
- package/dist/agent/session-run.d.ts +28 -1
- package/dist/agent/session-run.js +157 -6
- package/dist/agent/session-settings.d.ts +49 -0
- package/dist/agent/session-settings.js +95 -0
- package/dist/agent/session-subagent.d.ts +36 -24
- package/dist/agent/session-subagent.js +259 -129
- package/dist/agent/session-sync.d.ts +10 -3
- package/dist/agent/session-sync.js +37 -8
- package/dist/agent/session-telemetry.d.ts +39 -0
- package/dist/agent/session-telemetry.js +231 -0
- package/dist/agent/session-tools.js +63 -3
- package/dist/agent/session.d.ts +33 -29
- package/dist/agent/session.js +91 -126
- package/dist/agent/subagent-registry.d.ts +91 -0
- package/dist/agent/subagent-registry.js +494 -0
- package/dist/agent/system-prompt.d.ts +6 -3
- package/dist/agent/system-prompt.js +7 -2
- package/dist/agent/tool-runner.d.ts +8 -1
- package/dist/agent/tool-runner.js +72 -6
- package/dist/agent/types-w5.d.ts +192 -0
- package/dist/agent/types-w5.js +10 -0
- package/dist/agent/types.d.ts +55 -4
- package/dist/agent/types.js +2 -0
- package/dist/agent/worktree.d.ts +36 -0
- package/dist/agent/worktree.js +86 -0
- package/dist/agents/builtin.d.ts +14 -0
- package/dist/agents/builtin.js +39 -0
- package/dist/agents/catalog.d.ts +43 -0
- package/dist/agents/catalog.js +82 -0
- package/dist/agents/discover.d.ts +35 -0
- package/dist/agents/discover.js +83 -0
- package/dist/agents/external.d.ts +82 -0
- package/dist/agents/external.js +326 -0
- package/dist/agents/parse.d.ts +18 -0
- package/dist/agents/parse.js +116 -0
- package/dist/agents/result.d.ts +32 -0
- package/dist/agents/result.js +67 -0
- package/dist/agents/task-control.d.ts +17 -0
- package/dist/agents/task-control.js +17 -0
- package/dist/agents/task-record.d.ts +78 -0
- package/dist/agents/task-record.js +141 -0
- package/dist/agents/types.d.ts +46 -0
- package/dist/agents/types.js +8 -0
- package/dist/ai/apis/anthropic-compat.d.ts +36 -0
- package/dist/ai/apis/anthropic-compat.js +92 -0
- package/dist/ai/apis/anthropic-messages.js +3 -2
- package/dist/ai/apis/anthropic-request.d.ts +7 -6
- package/dist/ai/apis/anthropic-request.js +10 -28
- package/dist/ai/apis/cache-params.d.ts +11 -2
- package/dist/ai/apis/cache-params.js +25 -6
- package/dist/ai/apis/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-compat.d.ts +4 -1
- package/dist/ai/apis/openai-compat.js +8 -1
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +3 -2
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/image-limits.d.ts +45 -0
- package/dist/ai/image-limits.js +68 -0
- package/dist/ai/providers/builtin.d.ts +18 -6
- package/dist/ai/providers/builtin.js +135 -6
- package/dist/ai/providers/catalog-data.js +17 -13
- package/dist/ai/providers/catalog.d.ts +60 -7
- package/dist/ai/providers/catalog.js +197 -38
- package/dist/ai/providers/channels.d.ts +33 -3
- package/dist/ai/providers/channels.js +108 -7
- package/dist/ai/providers/enrich.d.ts +6 -3
- package/dist/ai/providers/enrich.js +19 -4
- package/dist/ai/providers/models-dev-cache.d.ts +28 -24
- package/dist/ai/providers/models-dev-cache.js +92 -78
- package/dist/ai/providers/models-dev-data.d.ts +7 -0
- package/dist/ai/providers/models-dev-data.js +32 -0
- package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
- package/dist/ai/providers/models-dev-snapshot.js +137 -0
- package/dist/ai/providers/models-dev.d.ts +51 -10
- package/dist/ai/providers/models-dev.js +132 -39
- package/dist/ai/providers/registry.d.ts +28 -11
- package/dist/ai/providers/registry.js +112 -65
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +37 -4
- package/dist/ai/types.js +5 -0
- package/dist/bundle/ama.cjs +41979 -21147
- package/dist/checkpoints/backend.d.ts +61 -0
- package/dist/checkpoints/backend.js +135 -0
- package/dist/checkpoints/blobs.d.ts +60 -0
- package/dist/checkpoints/blobs.js +203 -0
- package/dist/checkpoints/gc.d.ts +47 -0
- package/dist/checkpoints/gc.js +144 -0
- package/dist/checkpoints/git-head.d.ts +15 -0
- package/dist/checkpoints/git-head.js +89 -0
- package/dist/checkpoints/index.d.ts +17 -0
- package/dist/checkpoints/index.js +17 -0
- package/dist/checkpoints/replay.d.ts +48 -0
- package/dist/checkpoints/replay.js +154 -0
- package/dist/checkpoints/restore.d.ts +86 -0
- package/dist/checkpoints/restore.js +263 -0
- package/dist/checkpoints/settings.d.ts +14 -0
- package/dist/checkpoints/settings.js +23 -0
- package/dist/checkpoints/shadow-git.d.ts +108 -0
- package/dist/checkpoints/shadow-git.js +491 -0
- package/dist/checkpoints/shadow-restore.d.ts +25 -0
- package/dist/checkpoints/shadow-restore.js +128 -0
- package/dist/checkpoints/tracker.d.ts +64 -0
- package/dist/checkpoints/tracker.js +192 -0
- package/dist/checkpoints/types.d.ts +99 -0
- package/dist/checkpoints/types.js +12 -0
- package/dist/cli/args.d.ts +26 -7
- package/dist/cli/args.js +75 -82
- package/dist/cli/bootstrap.js +40 -4
- package/dist/cli/codemode-notice.d.ts +21 -0
- package/dist/cli/codemode-notice.js +56 -0
- package/dist/cli/compose-agents.d.ts +27 -0
- package/dist/cli/compose-agents.js +111 -0
- package/dist/cli/compose-extensions.d.ts +29 -0
- package/dist/cli/compose-extensions.js +44 -0
- package/dist/cli/compose-session.d.ts +8 -0
- package/dist/cli/compose-session.js +76 -2
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +17 -4
- package/dist/cli/compose.js +60 -14
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +42 -0
- package/dist/cli/exit-codes.d.ts +12 -0
- package/dist/cli/exit-codes.js +15 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/help-text.d.ts +4 -0
- package/dist/cli/help-text.js +102 -0
- package/dist/cli/main.d.ts +9 -2
- package/dist/cli/main.js +77 -3
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +51 -2
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +21 -13
- package/dist/cli/subcommands/config.d.ts +22 -3
- package/dist/cli/subcommands/config.js +119 -21
- package/dist/cli/subcommands/context.js +4 -1
- package/dist/cli/subcommands/doctor.js +33 -1
- package/dist/cli/subcommands/init.js +2 -1
- package/dist/cli/subcommands/models-discover.d.ts +15 -9
- package/dist/cli/subcommands/models-discover.js +58 -52
- package/dist/cli/subcommands/models.d.ts +2 -0
- package/dist/cli/subcommands/models.js +33 -14
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +3 -2
- package/dist/cli/subcommands/providers.js +53 -51
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +6 -3
- package/dist/cli/subcommands/sessions.js +58 -3
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- package/dist/codemode/capability.d.ts +24 -7
- package/dist/codemode/capability.js +38 -10
- package/dist/codemode/host-side.d.ts +14 -0
- package/dist/codemode/host-side.js +24 -1
- package/dist/codemode/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +76 -31
- package/dist/compaction/breaker.d.ts +25 -9
- package/dist/compaction/breaker.js +47 -17
- package/dist/compaction/estimate.d.ts +11 -3
- package/dist/compaction/estimate.js +64 -22
- package/dist/compaction/image-budget.d.ts +45 -0
- package/dist/compaction/image-budget.js +115 -0
- package/dist/compaction/post-compact.d.ts +38 -0
- package/dist/compaction/post-compact.js +152 -0
- package/dist/compaction/protect.d.ts +27 -0
- package/dist/compaction/protect.js +56 -0
- package/dist/compaction/prune-tier.d.ts +58 -12
- package/dist/compaction/prune-tier.js +120 -54
- package/dist/compaction/summarize-tier.d.ts +23 -4
- package/dist/compaction/summarize-tier.js +100 -29
- package/dist/config/checker.d.ts +28 -0
- package/dist/config/checker.js +97 -0
- package/dist/config/init.d.ts +6 -2
- package/dist/config/init.js +12 -5
- package/dist/config/json-schema.d.ts +1 -0
- package/dist/config/json-schema.js +98 -4
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +174 -0
- package/dist/config/merge.d.ts +12 -9
- package/dist/config/merge.js +102 -13
- package/dist/config/profile.d.ts +2 -0
- package/dist/config/profile.js +2 -0
- package/dist/config/schema-w5.d.ts +18 -0
- package/dist/config/schema-w5.js +109 -0
- package/dist/config/schema.d.ts +5 -9
- package/dist/config/schema.js +91 -118
- package/dist/config/types-w5.d.ts +118 -0
- package/dist/config/types-w5.js +20 -0
- package/dist/config/types.d.ts +84 -7
- package/dist/config/types.js +29 -1
- package/dist/drivers/acp/client.d.ts +57 -0
- package/dist/drivers/acp/client.js +160 -0
- package/dist/drivers/acp/driver.d.ts +31 -0
- package/dist/drivers/acp/driver.js +274 -0
- package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
- package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
- package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
- package/dist/drivers/acp/testing/fake-agent.js +211 -0
- package/dist/drivers/acp/types.d.ts +285 -0
- package/dist/drivers/acp/types.js +36 -0
- package/dist/drivers/agents.d.ts +58 -0
- package/dist/drivers/agents.js +142 -0
- package/dist/drivers/base.d.ts +35 -0
- package/dist/drivers/base.js +59 -0
- package/dist/drivers/catalog.d.ts +46 -0
- package/dist/drivers/catalog.js +167 -0
- package/dist/drivers/env.d.ts +20 -0
- package/dist/drivers/env.js +46 -0
- package/dist/drivers/host-runners.d.ts +18 -0
- package/dist/drivers/host-runners.js +45 -0
- package/dist/drivers/jsonrpc.d.ts +58 -0
- package/dist/drivers/jsonrpc.js +180 -0
- package/dist/drivers/native/claude-normalize.d.ts +46 -0
- package/dist/drivers/native/claude-normalize.js +106 -0
- package/dist/drivers/native/claude-stream.d.ts +29 -0
- package/dist/drivers/native/claude-stream.js +456 -0
- package/dist/drivers/native/codex-app-server.d.ts +26 -0
- package/dist/drivers/native/codex-app-server.js +407 -0
- package/dist/drivers/native/codex-normalize.d.ts +38 -0
- package/dist/drivers/native/codex-normalize.js +124 -0
- package/dist/drivers/native/oneshot.d.ts +28 -0
- package/dist/drivers/native/oneshot.js +312 -0
- package/dist/drivers/permissions.d.ts +40 -0
- package/dist/drivers/permissions.js +92 -0
- package/dist/drivers/pids.d.ts +32 -0
- package/dist/drivers/pids.js +93 -0
- package/dist/drivers/pool.d.ts +28 -0
- package/dist/drivers/pool.js +96 -0
- package/dist/drivers/probe.d.ts +47 -0
- package/dist/drivers/probe.js +172 -0
- package/dist/drivers/process.d.ts +48 -0
- package/dist/drivers/process.js +100 -0
- package/dist/drivers/runner.d.ts +61 -0
- package/dist/drivers/runner.js +388 -0
- package/dist/drivers/store.d.ts +49 -0
- package/dist/drivers/store.js +91 -0
- package/dist/drivers/turn.d.ts +41 -0
- package/dist/drivers/turn.js +99 -0
- package/dist/drivers/types.d.ts +122 -0
- package/dist/drivers/types.js +11 -0
- package/dist/git/info.d.ts +75 -0
- package/dist/git/info.js +191 -0
- package/dist/hooks/protocol.js +6 -1
- package/dist/hooks/types.d.ts +10 -1
- package/dist/hooks/types.js +2 -0
- package/dist/host/api-impl.d.ts +7 -0
- package/dist/host/api-impl.js +15 -0
- package/dist/host/types.d.ts +24 -2
- package/dist/host/types.js +4 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/modes/acp/acp-events.d.ts +46 -0
- package/dist/modes/acp/acp-events.js +233 -0
- package/dist/modes/acp/acp-mode.d.ts +17 -0
- package/dist/modes/acp/acp-mode.js +47 -0
- package/dist/modes/acp/acp-server.d.ts +79 -0
- package/dist/modes/acp/acp-server.js +315 -0
- package/dist/modes/commands-core.d.ts +7 -1
- package/dist/modes/commands-core.js +61 -5
- package/dist/modes/image-input.d.ts +12 -4
- package/dist/modes/image-input.js +22 -5
- package/dist/modes/interactive/agent-panels.d.ts +14 -0
- package/dist/modes/interactive/agent-panels.js +103 -0
- package/dist/modes/interactive/agent-ui.d.ts +73 -0
- package/dist/modes/interactive/agent-ui.js +234 -0
- package/dist/modes/interactive/approval-dialog.d.ts +40 -7
- package/dist/modes/interactive/approval-dialog.js +192 -35
- package/dist/modes/interactive/approval-merge.d.ts +41 -0
- package/dist/modes/interactive/approval-merge.js +89 -0
- package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
- package/dist/modes/interactive/clipboard-paste.js +28 -0
- package/dist/modes/interactive/commands.d.ts +20 -3
- package/dist/modes/interactive/commands.js +75 -16
- package/dist/modes/interactive/double-esc.d.ts +20 -0
- package/dist/modes/interactive/double-esc.js +34 -0
- package/dist/modes/interactive/event-notices.d.ts +32 -0
- package/dist/modes/interactive/event-notices.js +79 -0
- package/dist/modes/interactive/external-editor.d.ts +16 -0
- package/dist/modes/interactive/external-editor.js +39 -0
- package/dist/modes/interactive/interactive-mode.d.ts +16 -2
- package/dist/modes/interactive/interactive-mode.js +184 -202
- package/dist/modes/interactive/key-dispatch.d.ts +18 -2
- package/dist/modes/interactive/key-dispatch.js +53 -6
- package/dist/modes/interactive/line/line-mode.d.ts +3 -1
- package/dist/modes/interactive/line/line-mode.js +28 -9
- package/dist/modes/interactive/line/line-render.d.ts +6 -1
- package/dist/modes/interactive/line/line-render.js +66 -8
- package/dist/modes/interactive/message-view.d.ts +49 -10
- package/dist/modes/interactive/message-view.js +262 -46
- package/dist/modes/interactive/panels.d.ts +35 -0
- package/dist/modes/interactive/panels.js +157 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- package/dist/modes/interactive/plan-command.d.ts +40 -0
- package/dist/modes/interactive/plan-command.js +129 -0
- package/dist/modes/interactive/plan-dialog.d.ts +98 -0
- package/dist/modes/interactive/plan-dialog.js +290 -0
- package/dist/modes/interactive/plan-flow.d.ts +34 -0
- package/dist/modes/interactive/plan-flow.js +98 -0
- package/dist/modes/interactive/rewind-command.d.ts +31 -0
- package/dist/modes/interactive/rewind-command.js +106 -0
- package/dist/modes/interactive/rewind-flow.d.ts +37 -0
- package/dist/modes/interactive/rewind-flow.js +151 -0
- package/dist/modes/interactive/rewind-list.d.ts +36 -0
- package/dist/modes/interactive/rewind-list.js +123 -0
- package/dist/modes/interactive/rewind-panel.d.ts +77 -0
- package/dist/modes/interactive/rewind-panel.js +309 -0
- package/dist/modes/interactive/rewind-text.d.ts +40 -0
- package/dist/modes/interactive/rewind-text.js +139 -0
- package/dist/modes/interactive/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -0
- package/dist/modes/interactive/session-events.d.ts +24 -0
- package/dist/modes/interactive/session-events.js +96 -0
- package/dist/modes/interactive/startup-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -0
- package/dist/modes/interactive/status-area.d.ts +73 -0
- package/dist/modes/interactive/status-area.js +190 -0
- package/dist/modes/interactive/status-bar.d.ts +107 -16
- package/dist/modes/interactive/status-bar.js +306 -81
- package/dist/modes/interactive/status-line.d.ts +33 -0
- package/dist/modes/interactive/status-line.js +113 -0
- package/dist/modes/interactive/subagent-view.d.ts +56 -0
- package/dist/modes/interactive/subagent-view.js +154 -0
- package/dist/modes/interactive/tasks-report.d.ts +27 -0
- package/dist/modes/interactive/tasks-report.js +114 -0
- package/dist/modes/interactive/tool-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +58 -15
- package/dist/modes/interactive/tool-view.js +233 -145
- package/dist/modes/print/print-mode.d.ts +40 -4
- package/dist/modes/print/print-mode.js +161 -8
- package/dist/modes/rpc/commands.d.ts +9 -0
- package/dist/modes/rpc/commands.js +49 -0
- package/dist/modes/rpc/rpc-mode.js +7 -1
- package/dist/modes/session-report.d.ts +11 -0
- package/dist/modes/session-report.js +48 -1
- package/dist/permissions/auto-safe.d.ts +65 -0
- package/dist/permissions/auto-safe.js +547 -0
- package/dist/permissions/classifier.d.ts +68 -0
- package/dist/permissions/classifier.js +186 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +72 -6
- package/dist/permissions/pipeline.js +306 -11
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/readonly-bash.d.ts +30 -0
- package/dist/permissions/readonly-bash.js +118 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +92 -3
- package/dist/permissions/types.js +3 -0
- package/dist/plan/compose.d.ts +9 -0
- package/dist/plan/compose.js +19 -0
- package/dist/plan/controller.d.ts +71 -0
- package/dist/plan/controller.js +27 -0
- package/dist/plan/done-markers.d.ts +17 -0
- package/dist/plan/done-markers.js +51 -0
- package/dist/plan/extract.d.ts +29 -0
- package/dist/plan/extract.js +160 -0
- package/dist/plan/prompts.d.ts +36 -0
- package/dist/plan/prompts.js +89 -0
- package/dist/plan/store.d.ts +37 -0
- package/dist/plan/store.js +90 -0
- package/dist/rpc.d.ts +58 -2
- package/dist/rpc.js +7 -0
- package/dist/sandbox/bash.d.ts +74 -0
- package/dist/sandbox/bash.js +126 -0
- package/dist/sandbox/detect.d.ts +48 -0
- package/dist/sandbox/detect.js +149 -0
- package/dist/sandbox/index.d.ts +5 -0
- package/dist/sandbox/index.js +5 -0
- package/dist/sandbox/profile.d.ts +33 -0
- package/dist/sandbox/profile.js +76 -0
- package/dist/sandbox/wrap.d.ts +29 -0
- package/dist/sandbox/wrap.js +117 -0
- package/dist/sdk.d.ts +42 -5
- package/dist/sdk.js +34 -4
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- package/dist/session/types.d.ts +3 -1
- package/dist/session/types.js +1 -0
- package/dist/skills/builtin.d.ts +31 -0
- package/dist/skills/builtin.js +109 -0
- package/dist/skills/discover.d.ts +2 -1
- package/dist/skills/index-prompt.js +5 -8
- package/dist/tools/background-jobs.d.ts +71 -0
- package/dist/tools/background-jobs.js +198 -0
- package/dist/tools/bash.d.ts +53 -1
- package/dist/tools/bash.js +158 -7
- package/dist/tools/clipboard-image.d.ts +44 -0
- package/dist/tools/clipboard-image.js +148 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.js +18 -3
- package/dist/tools/image-file.d.ts +38 -3
- package/dist/tools/image-file.js +81 -10
- package/dist/tools/image-resize.d.ts +58 -0
- package/dist/tools/image-resize.js +97 -0
- package/dist/tools/presets.d.ts +40 -8
- package/dist/tools/presets.js +65 -19
- package/dist/tools/read.d.ts +5 -2
- package/dist/tools/read.js +13 -10
- package/dist/tools/registry.d.ts +7 -1
- package/dist/tools/registry.js +27 -4
- package/dist/tools/task-ctl.d.ts +22 -0
- package/dist/tools/task-ctl.js +116 -0
- package/dist/tools/task.d.ts +22 -10
- package/dist/tools/task.js +66 -26
- package/dist/tools/todo.d.ts +22 -2
- package/dist/tools/todo.js +58 -14
- package/dist/tools/truncate.d.ts +15 -0
- package/dist/tools/truncate.js +25 -0
- package/dist/tools/types.d.ts +113 -0
- package/dist/tools/types.js +5 -0
- package/dist/tools/write.js +3 -0
- package/dist/tui/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +72 -0
- package/dist/tui/glyphs.js +118 -0
- package/dist/tui/keybindings.d.ts +5 -0
- package/dist/tui/keybindings.js +5 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/acp.md +78 -0
- package/docs/agents.md +250 -0
- package/docs/codemode.md +28 -10
- package/docs/hooks.md +36 -32
- package/docs/permissions.md +212 -0
- package/docs/plan.md +107 -0
- package/docs/providers.md +261 -62
- package/docs/rewind-plan.md +181 -0
- package/docs/rpc.md +127 -40
- package/docs/sandbox.md +215 -0
- package/docs/session-format.md +43 -22
- package/docs/sessions.md +174 -0
- package/docs/tui-design.md +751 -0
- package/docs/tui.md +260 -64
- package/package.json +15 -2
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 探测的执行层(`ama providers add|refresh --probe`、`ama models discover --probe` 共用)。
|
|
3
|
+
*
|
|
4
|
+
* - **首事件即判定**:HTTP 成功、流里出现第一个内容事件(text / thinking / toolcall)或 done 即判可用,
|
|
5
|
+
* 立刻中止请求;只见到 `start`(推理模型可能要想很久才出第一个 delta)时再等 `settleMs`,期间没有
|
|
6
|
+
* error 事件也判可用——防止中转先回 200、紧接着在流里报错被误判。HTTP 错误、流 error 事件、超时判不可用。
|
|
7
|
+
* - **有界并发**:同时在途 ≤ `concurrency`;结果按任务下标交回,调用方自己按顺序输出。
|
|
8
|
+
* - **限流**:429 时并发减半(至少 1),等 `retryDelayMs` 后重试该请求一次;重试仍 429 → 停止;
|
|
9
|
+
* 401 / 403 → 立即停止。停止后不再发新请求,并中止在途的请求(它们的结果标记为 aborted)。
|
|
10
|
+
* - **恢复(加法增、乘法减)**:降并发之后,每连续 `recoverAfter`(缺省 4)次没被限流的请求,并发 +1,
|
|
11
|
+
* 直到回到初始并发;中途再遇 429 重新减半、计数清零。
|
|
12
|
+
*
|
|
13
|
+
* `ama models check` 不走这里:它只发一次,要看到完整回复与耗时。
|
|
14
|
+
*/
|
|
15
|
+
import { UsageError } from "../args.js";
|
|
16
|
+
export const PROBE_TIMEOUT_MS = 15_000;
|
|
17
|
+
export const DEFAULT_PROBE_CONCURRENCY = 6;
|
|
18
|
+
export const MAX_PROBE_CONCURRENCY = 16;
|
|
19
|
+
export const PROBE_SETTLE_MS = 1_000;
|
|
20
|
+
export const RATE_LIMIT_RETRY_MS = 2_000;
|
|
21
|
+
/** 降并发后,连续多少次没被限流才 +1。 */
|
|
22
|
+
export const RECOVER_AFTER = 4;
|
|
23
|
+
/** 鉴权失败或限流:继续只会浪费请求。 */
|
|
24
|
+
export const FATAL_STATUS = /^(?:HTTP )?(?:401|403|429)\b/;
|
|
25
|
+
const RATE_LIMITED = /^(?:HTTP )?429\b/;
|
|
26
|
+
const sleep = (ms, signal) => new Promise((resolve) => {
|
|
27
|
+
const timer = setTimeout(done, ms);
|
|
28
|
+
function done() {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
signal?.removeEventListener("abort", done);
|
|
31
|
+
resolve();
|
|
32
|
+
}
|
|
33
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
34
|
+
});
|
|
35
|
+
/** 发一次最小请求,见到首个有效事件即判定并中止。 */
|
|
36
|
+
export async function probeOnce(registry, model, apiKey, tuning = {}, signal) {
|
|
37
|
+
const impl = registry.getApi(model.api);
|
|
38
|
+
if (impl === undefined)
|
|
39
|
+
return { error: `协议 ${model.api} 尚未实现` };
|
|
40
|
+
if (signal?.aborted)
|
|
41
|
+
return { aborted: true };
|
|
42
|
+
const timeoutMs = tuning.timeoutMs ?? PROBE_TIMEOUT_MS;
|
|
43
|
+
const settleMs = tuning.settleMs ?? PROBE_SETTLE_MS;
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
let timedOut = false;
|
|
46
|
+
const timer = setTimeout(() => {
|
|
47
|
+
timedOut = true;
|
|
48
|
+
controller.abort();
|
|
49
|
+
}, timeoutMs);
|
|
50
|
+
const onAbort = () => controller.abort();
|
|
51
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
52
|
+
const verdict = (outcome) => {
|
|
53
|
+
if (signal?.aborted)
|
|
54
|
+
return { aborted: true };
|
|
55
|
+
if (timedOut)
|
|
56
|
+
return { error: `超时(${Math.round(timeoutMs / 1000)} s 内没有响应)` };
|
|
57
|
+
return outcome;
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
const stream = impl.stream(model, { messages: [{ role: "user", content: "Reply with: ok", timestamp: Date.now() }] }, {
|
|
61
|
+
signal: controller.signal,
|
|
62
|
+
...(apiKey !== undefined ? { apiKey } : {}),
|
|
63
|
+
maxTokens: 16,
|
|
64
|
+
cacheRetention: "none",
|
|
65
|
+
purpose: "probe",
|
|
66
|
+
});
|
|
67
|
+
const events = stream[Symbol.asyncIterator]();
|
|
68
|
+
let settled;
|
|
69
|
+
while (true) {
|
|
70
|
+
const next = events.next();
|
|
71
|
+
const step = settled !== undefined ? await Promise.race([next, settled]) : await next;
|
|
72
|
+
if (step === "settled")
|
|
73
|
+
return verdict({});
|
|
74
|
+
if (step.done)
|
|
75
|
+
break;
|
|
76
|
+
const event = step.value;
|
|
77
|
+
if (event.type === "error")
|
|
78
|
+
return verdict({ error: event.message.errorMessage ?? event.reason });
|
|
79
|
+
if (event.type === "start") {
|
|
80
|
+
settled ??= sleep(settleMs, controller.signal).then(() => "settled");
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
return verdict({});
|
|
84
|
+
}
|
|
85
|
+
// 流没有逐个产出事件(实现只给 result):按最终消息判定
|
|
86
|
+
const message = await stream.result();
|
|
87
|
+
if (message.stopReason === "error" || message.stopReason === "aborted")
|
|
88
|
+
return verdict({ error: message.errorMessage ?? message.stopReason });
|
|
89
|
+
return verdict({});
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return verdict({ error: error instanceof Error ? error.message : String(error) });
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
signal?.removeEventListener("abort", onAbort);
|
|
97
|
+
// 判定后立刻断开:不等推理模型把 16 个 token 想完
|
|
98
|
+
controller.abort();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 有界并发的探测调度器:`run` 按下标调度任务,任务里用 `probe` 发请求(带 429 重试与停止判定)。
|
|
103
|
+
*/
|
|
104
|
+
export class ProbeScheduler {
|
|
105
|
+
registry;
|
|
106
|
+
apiKey;
|
|
107
|
+
options;
|
|
108
|
+
limit;
|
|
109
|
+
initial;
|
|
110
|
+
/** 上次降并发以来连续没被限流的请求数。 */
|
|
111
|
+
streak = 0;
|
|
112
|
+
controller = new AbortController();
|
|
113
|
+
stopReason;
|
|
114
|
+
active = 0;
|
|
115
|
+
/** 观测用:同时在途请求数的峰值。 */
|
|
116
|
+
peak = 0;
|
|
117
|
+
constructor(registry, apiKey, options = {}) {
|
|
118
|
+
this.registry = registry;
|
|
119
|
+
this.apiKey = apiKey;
|
|
120
|
+
this.options = options;
|
|
121
|
+
this.limit = options.concurrency ?? DEFAULT_PROBE_CONCURRENCY;
|
|
122
|
+
this.initial = this.limit;
|
|
123
|
+
}
|
|
124
|
+
get stopped() {
|
|
125
|
+
return this.stopReason;
|
|
126
|
+
}
|
|
127
|
+
get concurrency() {
|
|
128
|
+
return this.limit;
|
|
129
|
+
}
|
|
130
|
+
stop(reason) {
|
|
131
|
+
if (this.stopReason !== undefined)
|
|
132
|
+
return;
|
|
133
|
+
this.stopReason = reason;
|
|
134
|
+
this.controller.abort();
|
|
135
|
+
}
|
|
136
|
+
/** 一次探测;429 降并发、退避后重试一次,仍 429 或 401 / 403 → 停止。 */
|
|
137
|
+
async probe(model) {
|
|
138
|
+
if (this.stopReason !== undefined)
|
|
139
|
+
return { aborted: true };
|
|
140
|
+
let outcome = await probeOnce(this.registry, model, this.apiKey, this.options, this.signal);
|
|
141
|
+
if (outcome.error !== undefined && RATE_LIMITED.test(outcome.error)) {
|
|
142
|
+
this.limit = Math.max(1, Math.floor(this.limit / 2));
|
|
143
|
+
this.streak = 0;
|
|
144
|
+
this.options.onThrottle?.(this.limit);
|
|
145
|
+
await sleep(this.options.retryDelayMs ?? RATE_LIMIT_RETRY_MS, this.signal);
|
|
146
|
+
if (this.stopReason !== undefined)
|
|
147
|
+
return { aborted: true };
|
|
148
|
+
outcome = await probeOnce(this.registry, model, this.apiKey, this.options, this.signal);
|
|
149
|
+
if (outcome.error !== undefined && RATE_LIMITED.test(outcome.error)) {
|
|
150
|
+
this.stop(`连续 429 限流:${outcome.error}`);
|
|
151
|
+
return outcome;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (outcome.error !== undefined && FATAL_STATUS.test(outcome.error))
|
|
155
|
+
this.stop(outcome.error);
|
|
156
|
+
else if (outcome.aborted !== true)
|
|
157
|
+
this.recover();
|
|
158
|
+
return outcome;
|
|
159
|
+
}
|
|
160
|
+
/** 一次没被限流的请求:攒够 recoverAfter 次就并发 +1(不超过初始值)。 */
|
|
161
|
+
recover() {
|
|
162
|
+
if (this.limit >= this.initial)
|
|
163
|
+
return;
|
|
164
|
+
this.streak++;
|
|
165
|
+
if (this.streak < (this.options.recoverAfter ?? RECOVER_AFTER))
|
|
166
|
+
return;
|
|
167
|
+
this.streak = 0;
|
|
168
|
+
this.limit++;
|
|
169
|
+
this.options.onRecover?.(this.limit);
|
|
170
|
+
}
|
|
171
|
+
/** 并发执行 `count` 个任务;`onDone` 按完成顺序回调。停止后未开始的任务不执行(结果为 undefined)。 */
|
|
172
|
+
async run(count, task, onDone) {
|
|
173
|
+
const results = new Array(count).fill(undefined);
|
|
174
|
+
let next = 0;
|
|
175
|
+
await new Promise((resolve, reject) => {
|
|
176
|
+
const pump = () => {
|
|
177
|
+
while (this.stopReason === undefined && next < count && this.active < this.limit) {
|
|
178
|
+
const index = next++;
|
|
179
|
+
this.active++;
|
|
180
|
+
this.peak = Math.max(this.peak, this.active);
|
|
181
|
+
task(index).then((result) => {
|
|
182
|
+
this.active--;
|
|
183
|
+
results[index] = result;
|
|
184
|
+
onDone?.(index, result);
|
|
185
|
+
pump();
|
|
186
|
+
}, (error) => {
|
|
187
|
+
this.active--;
|
|
188
|
+
this.stop(error instanceof Error ? error.message : String(error));
|
|
189
|
+
reject(error);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
if (this.active === 0 && (next >= count || this.stopReason !== undefined))
|
|
193
|
+
resolve();
|
|
194
|
+
};
|
|
195
|
+
pump();
|
|
196
|
+
});
|
|
197
|
+
return results;
|
|
198
|
+
}
|
|
199
|
+
get signal() {
|
|
200
|
+
return this.controller.signal;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** `--concurrency`(1–16)与 `--probe-timeout`(毫秒,1000–300000)。 */
|
|
204
|
+
export function parseProbeTuning(values) {
|
|
205
|
+
const int = (name, fallback, min, max) => {
|
|
206
|
+
const raw = values.get(name);
|
|
207
|
+
if (raw === undefined)
|
|
208
|
+
return fallback;
|
|
209
|
+
const n = Number(raw);
|
|
210
|
+
if (!Number.isInteger(n) || n < min || n > max)
|
|
211
|
+
throw new UsageError(`--${name} 需要 ${min}–${max} 的整数:${raw}`);
|
|
212
|
+
return n;
|
|
213
|
+
};
|
|
214
|
+
return {
|
|
215
|
+
concurrency: int("concurrency", DEFAULT_PROBE_CONCURRENCY, 1, MAX_PROBE_CONCURRENCY),
|
|
216
|
+
timeoutMs: int("probe-timeout", PROBE_TIMEOUT_MS, 1_000, 300_000),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/** 预估文案:`并发 6,单次最长 15 s,预计不超过 150 s`。 */
|
|
220
|
+
export function describeProbePlan(requests, concurrency, timeoutMs) {
|
|
221
|
+
const worst = Math.ceil(requests / concurrency) * Math.ceil(timeoutMs / 1000);
|
|
222
|
+
return `并发 ${concurrency},单次最长 ${Math.round(timeoutMs / 1000)} s,预计不超过 ${worst} s`;
|
|
223
|
+
}
|
|
224
|
+
/** 进度:TTY 下单行刷新「探测 18/60」;非 TTY 只在结束时打印一行汇总。 */
|
|
225
|
+
export class ProbeProgress {
|
|
226
|
+
write;
|
|
227
|
+
tty;
|
|
228
|
+
total;
|
|
229
|
+
done = 0;
|
|
230
|
+
shown = false;
|
|
231
|
+
started = Date.now();
|
|
232
|
+
constructor(write, tty, total) {
|
|
233
|
+
this.write = write;
|
|
234
|
+
this.tty = tty;
|
|
235
|
+
this.total = total;
|
|
236
|
+
}
|
|
237
|
+
/** 输出一行结果(TTY 下先擦掉进度行,再重画)。 */
|
|
238
|
+
line(text) {
|
|
239
|
+
this.clear();
|
|
240
|
+
this.write(text);
|
|
241
|
+
this.draw();
|
|
242
|
+
}
|
|
243
|
+
tick() {
|
|
244
|
+
this.done++;
|
|
245
|
+
this.draw();
|
|
246
|
+
}
|
|
247
|
+
finish() {
|
|
248
|
+
this.clear();
|
|
249
|
+
const seconds = ((Date.now() - this.started) / 1000).toFixed(1);
|
|
250
|
+
this.write(`探测完成 ${this.done}/${this.total},用时 ${seconds} s\n`);
|
|
251
|
+
}
|
|
252
|
+
draw() {
|
|
253
|
+
if (!this.tty)
|
|
254
|
+
return;
|
|
255
|
+
this.write(`\r\x1b[2K探测 ${this.done}/${this.total}`);
|
|
256
|
+
this.shown = true;
|
|
257
|
+
}
|
|
258
|
+
clear() {
|
|
259
|
+
if (!this.tty || !this.shown)
|
|
260
|
+
return;
|
|
261
|
+
this.write("\r\x1b[2K");
|
|
262
|
+
this.shown = false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama providers add|refresh --probe` 的逐渠道探测:任务是「模型 × 渠道」,有界并发执行(见
|
|
3
|
+
* probe-runner.ts);每个模型的所有渠道探完后按模型顺序打印一行,不按完成顺序。
|
|
4
|
+
*/
|
|
5
|
+
import type { ProviderRegistryApi } from "../../ai/types.js";
|
|
6
|
+
import type { CliIo } from "../deps.js";
|
|
7
|
+
import type { CandidateChannel } from "./providers-plan.js";
|
|
8
|
+
export interface ChannelProbeResult {
|
|
9
|
+
/** 探测成功的渠道(按渠道尝试顺序)。 */
|
|
10
|
+
ok: string[];
|
|
11
|
+
/** 最后一个失败渠道的原因(首行,≤ 120 字符)。 */
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ChannelProbeInput {
|
|
15
|
+
io: Pick<CliIo, "stdout" | "stderr" | "stdoutIsTTY">;
|
|
16
|
+
providerId: string;
|
|
17
|
+
ids: readonly string[];
|
|
18
|
+
/** 每个模型要试的渠道名(顺序即输出顺序)。 */
|
|
19
|
+
channelsFor: (id: string) => readonly string[];
|
|
20
|
+
candidates: readonly CandidateChannel[];
|
|
21
|
+
registry: ProviderRegistryApi;
|
|
22
|
+
apiKey: string | undefined;
|
|
23
|
+
concurrency: number;
|
|
24
|
+
timeoutMs: number;
|
|
25
|
+
/** 测试用:429 重试前的退避。 */
|
|
26
|
+
retryDelayMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 探测全部「模型 × 渠道」。返回有结论的模型(全部渠道探完,或因停止没探完但已有成功渠道)与停止原因。
|
|
30
|
+
*/
|
|
31
|
+
export declare function probeChannels(input: ChannelProbeInput): Promise<{
|
|
32
|
+
results: Map<string, ChannelProbeResult>;
|
|
33
|
+
stopped?: string;
|
|
34
|
+
}>;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama providers add|refresh --probe` 的逐渠道探测:任务是「模型 × 渠道」,有界并发执行(见
|
|
3
|
+
* probe-runner.ts);每个模型的所有渠道探完后按模型顺序打印一行,不按完成顺序。
|
|
4
|
+
*/
|
|
5
|
+
import { withCustomDefaults } from "../../ai/providers/catalog.js";
|
|
6
|
+
import { materializeModel } from "../../ai/providers/registry.js";
|
|
7
|
+
import { ProbeProgress, ProbeScheduler } from "./probe-runner.js";
|
|
8
|
+
/** 渠道的探测用供应商数据(key 由调用方传入)。 */
|
|
9
|
+
function probeProvider(id, channel) {
|
|
10
|
+
return {
|
|
11
|
+
id,
|
|
12
|
+
name: id,
|
|
13
|
+
api: channel.api,
|
|
14
|
+
baseUrl: channel.baseUrl,
|
|
15
|
+
envKeys: [],
|
|
16
|
+
models: [],
|
|
17
|
+
requiresApiKey: true,
|
|
18
|
+
builtin: false,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const firstLine = (error) => error.split("\n")[0]?.slice(0, 120) ?? error;
|
|
22
|
+
/**
|
|
23
|
+
* 探测全部「模型 × 渠道」。返回有结论的模型(全部渠道探完,或因停止没探完但已有成功渠道)与停止原因。
|
|
24
|
+
*/
|
|
25
|
+
export async function probeChannels(input) {
|
|
26
|
+
const { io } = input;
|
|
27
|
+
const tasks = input.ids.flatMap((id) => input.channelsFor(id).map((name) => ({ id, name })));
|
|
28
|
+
const pending = new Map(input.ids.map((id) => [id, input.channelsFor(id).length]));
|
|
29
|
+
const outcomes = new Map();
|
|
30
|
+
const results = new Map();
|
|
31
|
+
const progress = new ProbeProgress(io.stdout, io.stdoutIsTTY, tasks.length);
|
|
32
|
+
const scheduler = new ProbeScheduler(input.registry, input.apiKey, {
|
|
33
|
+
concurrency: input.concurrency,
|
|
34
|
+
timeoutMs: input.timeoutMs,
|
|
35
|
+
retryDelayMs: input.retryDelayMs,
|
|
36
|
+
onThrottle: (n) => progress.line(` 遇到 429 限流,并发降到 ${n},稍后重试一次\n`),
|
|
37
|
+
onRecover: (n) => progress.line(` 一段时间没再限流,并发回升到 ${n}\n`),
|
|
38
|
+
});
|
|
39
|
+
let printed = 0;
|
|
40
|
+
/** 按模型顺序输出:前面的模型都探完才输出后面的。 */
|
|
41
|
+
const flush = (final) => {
|
|
42
|
+
while (printed < input.ids.length) {
|
|
43
|
+
const id = input.ids[printed];
|
|
44
|
+
if (!final && (pending.get(id) ?? 0) > 0)
|
|
45
|
+
return;
|
|
46
|
+
printed++;
|
|
47
|
+
const result = { ok: [] };
|
|
48
|
+
const failed = [];
|
|
49
|
+
let complete = true;
|
|
50
|
+
for (const name of input.channelsFor(id)) {
|
|
51
|
+
const outcome = outcomes.get(`${id}\n${name}`);
|
|
52
|
+
if (outcome === undefined || outcome.aborted)
|
|
53
|
+
complete = false;
|
|
54
|
+
else if (outcome.error === undefined)
|
|
55
|
+
result.ok.push(name);
|
|
56
|
+
else {
|
|
57
|
+
result.error = firstLine(outcome.error);
|
|
58
|
+
failed.push(`${name}:${result.error}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!complete && result.ok.length === 0)
|
|
62
|
+
continue;
|
|
63
|
+
results.set(id, result);
|
|
64
|
+
progress.line(` ${id} ${result.ok.length > 0 ? result.ok.join(", ") : "全部失败"}` +
|
|
65
|
+
`${failed.length > 0 ? `(失败 ${failed.join(";")})` : ""}` +
|
|
66
|
+
`${complete ? "" : "(探测提前停止,部分渠道未探)"}\n`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
await scheduler.run(tasks.length, async (index) => {
|
|
70
|
+
const { id, name } = tasks[index];
|
|
71
|
+
const channel = input.candidates.find((c) => c.name === name);
|
|
72
|
+
const provider = probeProvider(input.providerId, channel);
|
|
73
|
+
const model = materializeModel(withCustomDefaults({ id }, input.providerId, channel.api), provider);
|
|
74
|
+
return scheduler.probe(model);
|
|
75
|
+
}, (index, outcome) => {
|
|
76
|
+
const { id, name } = tasks[index];
|
|
77
|
+
outcomes.set(`${id}\n${name}`, outcome);
|
|
78
|
+
pending.set(id, (pending.get(id) ?? 1) - 1);
|
|
79
|
+
if (!outcome.aborted)
|
|
80
|
+
progress.tick();
|
|
81
|
+
flush(false);
|
|
82
|
+
});
|
|
83
|
+
flush(true);
|
|
84
|
+
progress.finish();
|
|
85
|
+
const stopped = scheduler.stopped;
|
|
86
|
+
return { results, ...(stopped !== undefined ? { stopped } : {}) };
|
|
87
|
+
}
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
* - refresh:重拉 `/models` 与 models.dev,只追加新模型;已下架的只提示。
|
|
8
8
|
* - list / channels:供应商 → 渠道 → 模型数与 key 来源(从不显示 key)。remove:删配置与 auth.json 条目。
|
|
9
9
|
*
|
|
10
|
-
* 计费:`--probe`
|
|
10
|
+
* 计费:`--probe` 每模型每渠道一次最小请求(并发执行,见到首个流事件即判可用并断开,见
|
|
11
|
+
* probe-runner.ts);执行前打印预估,TTY 问 y/N,非 TTY 必须 `--yes`。
|
|
11
12
|
*/
|
|
12
13
|
import type { AmaConfig } from "../../config/types.js";
|
|
13
14
|
import type { CliIo, RuntimeDeps } from "../deps.js";
|
|
14
15
|
import { type UserLevel } from "./context.js";
|
|
15
16
|
export declare const DEFAULT_MAX_REQUESTS = 60;
|
|
16
17
|
export declare const DEFAULT_LIMIT = 30;
|
|
17
|
-
export declare const PROVIDERS_USAGE = "\u7528\u6CD5\uFF1Aama providers add <id> --base-url <url> [--channel <\u540D\u5B57>=<\u534F\u8BAE>@<\u5730\u5740> \u2026]\n [--api openai-completions|openai-responses|anthropic-messages|auto]\n [--key-env <VAR>] [--probe] [--limit N] [--probe-models a,b,\u2026]\n [--max-requests N] [--prefer chat,responses,messages]
|
|
18
|
+
export declare const PROVIDERS_USAGE = "\u7528\u6CD5\uFF1Aama providers add <id> --base-url <url> [--channel <\u540D\u5B57>=<\u534F\u8BAE>@<\u5730\u5740> \u2026]\n [--api openai-completions|openai-responses|anthropic-messages|auto]\n [--key-env <VAR>] [--probe] [--limit N] [--probe-models a,b,\u2026]\n [--max-requests N] [--concurrency N] [--probe-timeout ms]\n [--prefer chat,responses,messages] [--include-no-tools] [--yes]\n ama providers list\n ama providers channels <id>\n ama providers remove <id>\n ama providers refresh <id> [--probe] [--limit N] [--probe-models a,b,\u2026] [--max-requests N]\n [--concurrency N] [--probe-timeout ms] [--yes]\n";
|
|
18
19
|
export interface Ctx {
|
|
19
20
|
io: CliIo;
|
|
20
21
|
deps: Pick<RuntimeDeps, "providers">;
|
|
@@ -7,36 +7,39 @@
|
|
|
7
7
|
* - refresh:重拉 `/models` 与 models.dev,只追加新模型;已下架的只提示。
|
|
8
8
|
* - list / channels:供应商 → 渠道 → 模型数与 key 来源(从不显示 key)。remove:删配置与 auth.json 条目。
|
|
9
9
|
*
|
|
10
|
-
* 计费:`--probe`
|
|
10
|
+
* 计费:`--probe` 每模型每渠道一次最小请求(并发执行,见到首个流事件即判可用并断开,见
|
|
11
|
+
* probe-runner.ts);执行前打印预估,TTY 问 y/N,非 TTY 必须 `--yes`。
|
|
11
12
|
*/
|
|
12
13
|
import { existsSync } from "node:fs";
|
|
13
14
|
import { createInterface } from "node:readline/promises";
|
|
14
|
-
import {
|
|
15
|
-
import { describeRefresh, refreshModelsDev } from "../../ai/providers/models-dev-cache.js";
|
|
15
|
+
import { describeModelsDev, loadModelsDevIndex } from "../../ai/providers/models-dev-cache.js";
|
|
16
16
|
import { modelsDevFields } from "../../ai/providers/models-dev.js";
|
|
17
|
-
import { materializeModel } from "../../ai/providers/registry.js";
|
|
18
17
|
import { PROVIDER_ID_PATTERN, setAuthKey } from "../../config/auth-file.js";
|
|
19
18
|
import { loadConfigFile } from "../../config/load.js";
|
|
20
19
|
import { CONFIG_FILE_VERSION } from "../../config/types.js";
|
|
21
20
|
import { writeConfigFile } from "../../config/write.js";
|
|
22
21
|
import { parseSubArgs, UsageError } from "../args.js";
|
|
22
|
+
import { pickByPrice } from "../default-model.js";
|
|
23
23
|
import { ExitCode } from "../exit-codes.js";
|
|
24
24
|
import { extractKey } from "./auth.js";
|
|
25
25
|
import { listChannels, listProviders, removeProvider } from "./providers-list.js";
|
|
26
26
|
import { buildRegistry, loadUserLevel } from "./context.js";
|
|
27
|
-
import {
|
|
27
|
+
import { DISCOVER_TIMEOUT_MS } from "./models-discover.js";
|
|
28
|
+
import { describeProbePlan, parseProbeTuning } from "./probe-runner.js";
|
|
29
|
+
import { probeChannels } from "./providers-probe.js";
|
|
28
30
|
import { DEFAULT_PREFER, capRequests, defaultChannels, hintedChannels, listingTarget, mergeProvider, orderChannels, parseChannelSpec, parseModelList, probeSelection, renderTable, } from "./providers-plan.js";
|
|
29
31
|
export const DEFAULT_MAX_REQUESTS = 60;
|
|
30
32
|
export const DEFAULT_LIMIT = 30;
|
|
31
33
|
export const PROVIDERS_USAGE = `用法:ama providers add <id> --base-url <url> [--channel <名字>=<协议>@<地址> …]
|
|
32
34
|
[--api openai-completions|openai-responses|anthropic-messages|auto]
|
|
33
35
|
[--key-env <VAR>] [--probe] [--limit N] [--probe-models a,b,…]
|
|
34
|
-
[--max-requests N] [--
|
|
35
|
-
[--include-no-tools] [--yes]
|
|
36
|
+
[--max-requests N] [--concurrency N] [--probe-timeout ms]
|
|
37
|
+
[--prefer chat,responses,messages] [--include-no-tools] [--yes]
|
|
36
38
|
ama providers list
|
|
37
39
|
ama providers channels <id>
|
|
38
40
|
ama providers remove <id>
|
|
39
|
-
ama providers refresh <id> [--probe] [--limit N] [--probe-models a,b,…] [--max-requests N]
|
|
41
|
+
ama providers refresh <id> [--probe] [--limit N] [--probe-models a,b,…] [--max-requests N]
|
|
42
|
+
[--concurrency N] [--probe-timeout ms] [--yes]
|
|
40
43
|
`;
|
|
41
44
|
const VALUE_OPTIONS = [
|
|
42
45
|
"base-url",
|
|
@@ -45,6 +48,8 @@ const VALUE_OPTIONS = [
|
|
|
45
48
|
"limit",
|
|
46
49
|
"probe-models",
|
|
47
50
|
"max-requests",
|
|
51
|
+
"concurrency",
|
|
52
|
+
"probe-timeout",
|
|
48
53
|
"prefer",
|
|
49
54
|
"name",
|
|
50
55
|
"profile",
|
|
@@ -119,19 +124,6 @@ function channelsOfConfig(config) {
|
|
|
119
124
|
const api = config.api ?? "openai-completions";
|
|
120
125
|
return [{ name: "default", api, baseUrl: config.baseUrl }];
|
|
121
126
|
}
|
|
122
|
-
/** 渠道的探测用供应商数据(key 由调用方传入)。 */
|
|
123
|
-
function probeProvider(id, channel) {
|
|
124
|
-
return {
|
|
125
|
-
id,
|
|
126
|
-
name: id,
|
|
127
|
-
api: channel.api,
|
|
128
|
-
baseUrl: channel.baseUrl,
|
|
129
|
-
envKeys: [],
|
|
130
|
-
models: [],
|
|
131
|
-
requiresApiKey: true,
|
|
132
|
-
builtin: false,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
127
|
/** 逐模型定渠道(含探测);返回各模型的计划与是否因鉴权 / 限流提前停止。 */
|
|
136
128
|
async function plan(ctx, input) {
|
|
137
129
|
const { io } = ctx;
|
|
@@ -151,36 +143,28 @@ async function plan(ctx, input) {
|
|
|
151
143
|
const selected = probeSelection(fresh.map((m) => m.id).filter(probeable), intValue(ctx, "limit", DEFAULT_LIMIT), only);
|
|
152
144
|
const max = intValue(ctx, "max-requests", DEFAULT_MAX_REQUESTS);
|
|
153
145
|
const capped = capRequests(selected, tryChannels, max);
|
|
146
|
+
const tuning = parseProbeTuning(ctx.values);
|
|
154
147
|
io.stdout(`\n探测:${capped.ids.length} 个模型、${capped.requests} 次最小请求(每模型每渠道 1 次,上限 ${max})` +
|
|
155
|
-
`${capped.dropped > 0 ? `;另有 ${capped.dropped} 个超出上限未探测` : ""}\n`
|
|
148
|
+
`${capped.dropped > 0 ? `;另有 ${capped.dropped} 个超出上限未探测` : ""}\n` +
|
|
149
|
+
`${describeProbePlan(capped.requests, tuning.concurrency, tuning.timeoutMs)}\n`);
|
|
156
150
|
const ok = await approved(ctx, `将发 ${capped.requests} 次计费请求并写入 ${ctx.level.userConfigPath}`);
|
|
157
151
|
if (ok === "usage")
|
|
158
152
|
return { plans: [], cancelled: ExitCode.Usage };
|
|
159
153
|
if (!ok)
|
|
160
154
|
return { plans: [], cancelled: ExitCode.Ok };
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
const probed = await probeChannels({
|
|
156
|
+
io,
|
|
157
|
+
providerId: input.id,
|
|
158
|
+
ids: capped.ids,
|
|
159
|
+
channelsFor: tryChannels,
|
|
160
|
+
candidates: input.candidates,
|
|
161
|
+
registry: input.registry,
|
|
162
|
+
apiKey: input.apiKey,
|
|
163
|
+
...tuning,
|
|
164
|
+
});
|
|
165
|
+
for (const [id, result] of probed.results)
|
|
164
166
|
results.set(id, result);
|
|
165
|
-
|
|
166
|
-
const channel = input.candidates.find((c) => c.name === name);
|
|
167
|
-
const provider = probeProvider(input.id, channel);
|
|
168
|
-
const model = materializeModel(withCustomDefaults({ id }, input.id, channel.api), provider);
|
|
169
|
-
const error = await attempt(input.registry, model, input.apiKey);
|
|
170
|
-
if (error === undefined)
|
|
171
|
-
result.ok.push(name);
|
|
172
|
-
else {
|
|
173
|
-
result.error = error.split("\n")[0]?.slice(0, 120) ?? error;
|
|
174
|
-
failed.push(`${name}:${result.error}`);
|
|
175
|
-
if (FATAL_STATUS.test(error)) {
|
|
176
|
-
stopped = error;
|
|
177
|
-
break outer;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
io.stdout(` ${id} ${result.ok.length > 0 ? result.ok.join(", ") : "全部失败"}` +
|
|
182
|
-
`${failed.length > 0 ? `(失败 ${failed.join(";")})` : ""}\n`);
|
|
183
|
-
}
|
|
167
|
+
stopped = probed.stopped;
|
|
184
168
|
}
|
|
185
169
|
const includeNoTools = ctx.flags.has("include-no-tools");
|
|
186
170
|
const plans = input.listed.map((listed) => {
|
|
@@ -276,17 +260,16 @@ async function add(ctx, id, refresh) {
|
|
|
276
260
|
return ExitCode.RuntimeError;
|
|
277
261
|
}
|
|
278
262
|
io.stdout(`${id}:发现 ${listed.length} 个模型(${target.url})\n`);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
io.stderr(`ama: 警告:${md.warning}\n`);
|
|
263
|
+
// models.dev 只读本地(快照 ⊕ `ama models refresh` 的覆盖),不联网。
|
|
264
|
+
const mdIndex = loadModelsDevIndex(level.dataDir);
|
|
265
|
+
io.stdout(`${describeModelsDev(level.dataDir)}\n`);
|
|
283
266
|
io.stdout(`候选渠道:${candidates.map((c) => `${c.name}(${c.api} ${c.baseUrl})`).join(" · ")}\n`);
|
|
284
267
|
const existingIds = new Set((existing?.models ?? []).map((m) => m.id));
|
|
285
268
|
const result = await plan(ctx, {
|
|
286
269
|
id,
|
|
287
270
|
candidates,
|
|
288
271
|
listed,
|
|
289
|
-
index:
|
|
272
|
+
index: mdIndex,
|
|
290
273
|
existingIds,
|
|
291
274
|
apiKey: key.apiKey,
|
|
292
275
|
registry,
|
|
@@ -314,9 +297,25 @@ async function add(ctx, id, refresh) {
|
|
|
314
297
|
prefer: list(ctx.values.get("prefer")) ?? [...DEFAULT_PREFER],
|
|
315
298
|
});
|
|
316
299
|
const keyNote = key.store !== undefined ? `;key → ${level.authFile}(0600)` : "";
|
|
300
|
+
// 还没有缺省模型时按价格规则挑一个写进 defaultModel(default-model.ts 的 pickByPrice);
|
|
301
|
+
// 探测过时只在探测通过的模型里挑
|
|
302
|
+
const verified = writable.filter((p) => p.status === "ok");
|
|
303
|
+
const defaultPick = level.merged.config.defaultModel === undefined && config.defaultModel === undefined
|
|
304
|
+
? pickByPrice((verified.length > 0 ? verified : writable).map((p) => ({
|
|
305
|
+
id: p.id,
|
|
306
|
+
contextWindow: p.fields.contextWindow,
|
|
307
|
+
inputCost: p.fields.cost?.input,
|
|
308
|
+
toolCall: p.fields.toolCall,
|
|
309
|
+
})))
|
|
310
|
+
: undefined;
|
|
311
|
+
const defaultNote = defaultPick !== undefined ? `;defaultModel → ${id}/${defaultPick.id}` : "";
|
|
317
312
|
const summary = `\n将写入 ${level.userConfigPath}:${id} 新增渠道 ${merged.addedChannels.join(", ") || "无"},` +
|
|
318
|
-
`新增模型 ${merged.addedModels.length} 个${keyNote}`;
|
|
313
|
+
`新增模型 ${merged.addedModels.length} 个${keyNote}${defaultNote}`;
|
|
319
314
|
io.stdout(`${summary}\n`);
|
|
315
|
+
if (defaultPick !== undefined)
|
|
316
|
+
io.stdout(` 选 ${defaultPick.id}:${defaultPick.reason}\n`);
|
|
317
|
+
else if (level.merged.config.defaultModel === undefined && writable.length > 0)
|
|
318
|
+
io.stdout(" 未设置 defaultModel:没有同时支持工具调用、上下文 ≥ 64k 且有价格的模型;用 ama config edit 设置\n");
|
|
320
319
|
if (merged.addedModels.length === 0 &&
|
|
321
320
|
merged.addedChannels.length === 0 &&
|
|
322
321
|
key.store === undefined) {
|
|
@@ -334,12 +333,15 @@ async function add(ctx, id, refresh) {
|
|
|
334
333
|
}
|
|
335
334
|
const providers = (config.providers ??= {});
|
|
336
335
|
providers[id] = merged.config;
|
|
336
|
+
if (defaultPick !== undefined)
|
|
337
|
+
config.defaultModel = `${id}/${defaultPick.id}`;
|
|
337
338
|
const backup = existsSync(level.userConfigPath);
|
|
338
339
|
writeConfigFile(level.userConfigPath, config, { backup: true });
|
|
339
340
|
if (key.store !== undefined)
|
|
340
341
|
setAuthKey(level.authFile, id, key.store);
|
|
341
342
|
io.stdout(`已写入 ${level.userConfigPath}${backup ? "(原文件备份为 config.json.bak)" : ""}\n`);
|
|
342
|
-
const sample =
|
|
343
|
+
const sample = defaultPick?.id ??
|
|
344
|
+
result.plans.find((p) => p.status === "ok" && merged.addedModels.includes(p.id))?.id ??
|
|
343
345
|
merged.addedModels[0];
|
|
344
346
|
if (sample !== undefined)
|
|
345
347
|
io.stdout(`试试:ama -p "hi" --model ${id}/${sample}\n`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama sessions export <id>`:导出会话(只读,不加锁;正在运行的会话也能导)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - `--format md|json|jsonl`(缺省 md);`--branch leaf|all`(缺省 leaf = 当前分支);
|
|
5
|
+
* - `--output <文件>`:写文件(0600,已存在则覆盖),否则写 stdout;
|
|
6
|
+
* - 导出内容先脱敏(session/redact.ts),格式说明见 docs/sessions.md「导出」。
|
|
7
|
+
*/
|
|
8
|
+
import type { CliIo } from "../deps.js";
|
|
9
|
+
export declare const SESSIONS_EXPORT_USAGE = "\u7528\u6CD5\uFF1Aama sessions export <id> [--format md|json|jsonl] [--output <\u6587\u4EF6>] [--branch leaf|all]\n [--session-dir <\u76EE\u5F55>]\n";
|
|
10
|
+
export declare function runSessionsExport(argv: readonly string[], io: CliIo): Promise<number>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama sessions export <id>`:导出会话(只读,不加锁;正在运行的会话也能导)。[W4-D]
|
|
3
|
+
*
|
|
4
|
+
* - `--format md|json|jsonl`(缺省 md);`--branch leaf|all`(缺省 leaf = 当前分支);
|
|
5
|
+
* - `--output <文件>`:写文件(0600,已存在则覆盖),否则写 stdout;
|
|
6
|
+
* - 导出内容先脱敏(session/redact.ts),格式说明见 docs/sessions.md「导出」。
|
|
7
|
+
*/
|
|
8
|
+
import { writeFileSync } from "node:fs";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
import { expandHome, resolveDataDir } from "../../config/paths.js";
|
|
11
|
+
import { renderExport } from "../../session/export.js";
|
|
12
|
+
import { findSessionFileReadOnly, readSessionReadOnly } from "../../session/scan.js";
|
|
13
|
+
import { parseSubArgs, UsageError } from "../args.js";
|
|
14
|
+
import { ExitCode } from "../exit-codes.js";
|
|
15
|
+
export const SESSIONS_EXPORT_USAGE = `用法:ama sessions export <id> [--format md|json|jsonl] [--output <文件>] [--branch leaf|all]
|
|
16
|
+
[--session-dir <目录>]
|
|
17
|
+
`;
|
|
18
|
+
const FORMATS = ["md", "json", "jsonl"];
|
|
19
|
+
const BRANCHES = ["leaf", "all"];
|
|
20
|
+
export async function runSessionsExport(argv, io) {
|
|
21
|
+
const { positionals, values, flags } = parseSubArgs(argv, [
|
|
22
|
+
"format",
|
|
23
|
+
"output",
|
|
24
|
+
"branch",
|
|
25
|
+
"session-dir",
|
|
26
|
+
]);
|
|
27
|
+
if (flags.has("help")) {
|
|
28
|
+
io.stdout(SESSIONS_EXPORT_USAGE);
|
|
29
|
+
return ExitCode.Ok;
|
|
30
|
+
}
|
|
31
|
+
const [id, extra] = positionals;
|
|
32
|
+
if (id === undefined)
|
|
33
|
+
throw new UsageError("ama sessions export 需要 <id>");
|
|
34
|
+
if (extra !== undefined)
|
|
35
|
+
throw new UsageError(`多余的参数:${extra}`);
|
|
36
|
+
const format = (values.get("format") ?? "md");
|
|
37
|
+
if (!FORMATS.includes(format)) {
|
|
38
|
+
throw new UsageError(`--format 的取值应为 ${FORMATS.join(" | ")}(收到 ${format})`);
|
|
39
|
+
}
|
|
40
|
+
const branch = (values.get("branch") ?? "leaf");
|
|
41
|
+
if (!BRANCHES.includes(branch)) {
|
|
42
|
+
throw new UsageError(`--branch 的取值应为 ${BRANCHES.join(" | ")}(收到 ${branch})`);
|
|
43
|
+
}
|
|
44
|
+
const dirFlag = values.get("session-dir");
|
|
45
|
+
const root = dirFlag !== undefined
|
|
46
|
+
? resolve(io.cwd, expandHome(dirFlag, { env: io.env }))
|
|
47
|
+
: join(resolveDataDir({ env: io.env }), "sessions");
|
|
48
|
+
const session = readSessionReadOnly(findSessionFileReadOnly(root, id, io.cwd));
|
|
49
|
+
const text = renderExport(session, format, branch);
|
|
50
|
+
const output = values.get("output");
|
|
51
|
+
if (output === undefined) {
|
|
52
|
+
io.stdout(text);
|
|
53
|
+
return ExitCode.Ok;
|
|
54
|
+
}
|
|
55
|
+
const target = resolve(io.cwd, expandHome(output, { env: io.env }));
|
|
56
|
+
writeFileSync(target, text, { mode: 0o600 });
|
|
57
|
+
io.stderr(`已导出到 ${target}\n`);
|
|
58
|
+
return ExitCode.Ok;
|
|
59
|
+
}
|