@armadra/agent 0.0.0-stage → 0.3.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 +37 -0
- package/LICENSE +21 -0
- package/README.md +470 -2
- package/dist/agent/agent.d.ts +56 -0
- package/dist/agent/agent.js +129 -0
- package/dist/agent/loop.d.ts +51 -0
- package/dist/agent/loop.js +158 -0
- package/dist/agent/queue.d.ts +25 -0
- package/dist/agent/queue.js +55 -0
- package/dist/agent/retry.d.ts +22 -0
- package/dist/agent/retry.js +92 -0
- package/dist/agent/schema.d.ts +27 -0
- package/dist/agent/schema.js +186 -0
- package/dist/agent/session-cache.d.ts +100 -0
- package/dist/agent/session-cache.js +514 -0
- package/dist/agent/session-compaction.d.ts +49 -0
- package/dist/agent/session-compaction.js +251 -0
- package/dist/agent/session-core.d.ts +103 -0
- package/dist/agent/session-core.js +7 -0
- package/dist/agent/session-run.d.ts +55 -0
- package/dist/agent/session-run.js +246 -0
- package/dist/agent/session-state.d.ts +39 -0
- package/dist/agent/session-state.js +119 -0
- package/dist/agent/session-subagent.d.ts +54 -0
- package/dist/agent/session-subagent.js +157 -0
- package/dist/agent/session-sync.d.ts +20 -0
- package/dist/agent/session-sync.js +111 -0
- package/dist/agent/session-tools.d.ts +24 -0
- package/dist/agent/session-tools.js +277 -0
- package/dist/agent/session.d.ts +117 -0
- package/dist/agent/session.js +485 -0
- package/dist/agent/system-prompt.d.ts +49 -0
- package/dist/agent/system-prompt.js +121 -0
- package/dist/agent/tool-runner.d.ts +64 -0
- package/dist/agent/tool-runner.js +309 -0
- package/dist/agent/transform.d.ts +26 -0
- package/dist/agent/transform.js +171 -0
- package/dist/agent/types.d.ts +378 -0
- package/dist/agent/types.js +21 -0
- package/dist/ai/apis/anthropic-messages.d.ts +26 -0
- package/dist/ai/apis/anthropic-messages.js +214 -0
- package/dist/ai/apis/anthropic-request.d.ts +42 -0
- package/dist/ai/apis/anthropic-request.js +282 -0
- package/dist/ai/apis/api.d.ts +28 -0
- package/dist/ai/apis/api.js +102 -0
- package/dist/ai/apis/cache-params.d.ts +48 -0
- package/dist/ai/apis/cache-params.js +163 -0
- package/dist/ai/apis/google-generative-ai.d.ts +32 -0
- package/dist/ai/apis/google-generative-ai.js +249 -0
- package/dist/ai/apis/google-request.d.ts +40 -0
- package/dist/ai/apis/google-request.js +255 -0
- package/dist/ai/apis/openai-compat.d.ts +35 -0
- package/dist/ai/apis/openai-compat.js +142 -0
- package/dist/ai/apis/openai-completions.d.ts +28 -0
- package/dist/ai/apis/openai-completions.js +246 -0
- package/dist/ai/apis/openai-request.d.ts +35 -0
- package/dist/ai/apis/openai-request.js +303 -0
- package/dist/ai/apis/openai-responses-request.d.ts +52 -0
- package/dist/ai/apis/openai-responses-request.js +266 -0
- package/dist/ai/apis/openai-responses.d.ts +28 -0
- package/dist/ai/apis/openai-responses.js +304 -0
- package/dist/ai/apis/shared.d.ts +56 -0
- package/dist/ai/apis/shared.js +179 -0
- package/dist/ai/cache/economics.d.ts +24 -0
- package/dist/ai/cache/economics.js +69 -0
- package/dist/ai/cache/fingerprint.d.ts +17 -0
- package/dist/ai/cache/fingerprint.js +39 -0
- package/dist/ai/cache/miss.d.ts +49 -0
- package/dist/ai/cache/miss.js +96 -0
- package/dist/ai/cache/reporting.d.ts +39 -0
- package/dist/ai/cache/reporting.js +103 -0
- package/dist/ai/cache/types.d.ts +94 -0
- package/dist/ai/cache/types.js +19 -0
- package/dist/ai/cache/warmer.d.ts +94 -0
- package/dist/ai/cache/warmer.js +224 -0
- package/dist/ai/context.d.ts +35 -0
- package/dist/ai/context.js +114 -0
- package/dist/ai/cost.d.ts +21 -0
- package/dist/ai/cost.js +60 -0
- package/dist/ai/event-stream.d.ts +33 -0
- package/dist/ai/event-stream.js +101 -0
- package/dist/ai/fake/fake-provider.d.ts +68 -0
- package/dist/ai/fake/fake-provider.js +278 -0
- package/dist/ai/fake/fake-script.d.ts +69 -0
- package/dist/ai/fake/fake-script.js +142 -0
- package/dist/ai/http.d.ts +54 -0
- package/dist/ai/http.js +212 -0
- package/dist/ai/json-partial.d.ts +17 -0
- package/dist/ai/json-partial.js +243 -0
- package/dist/ai/overflow.d.ts +22 -0
- package/dist/ai/overflow.js +61 -0
- package/dist/ai/providers/auth.d.ts +71 -0
- package/dist/ai/providers/auth.js +202 -0
- package/dist/ai/providers/builtin.d.ts +23 -0
- package/dist/ai/providers/builtin.js +148 -0
- package/dist/ai/providers/catalog-data.d.ts +5 -0
- package/dist/ai/providers/catalog-data.js +19 -0
- package/dist/ai/providers/catalog.d.ts +28 -0
- package/dist/ai/providers/catalog.js +166 -0
- package/dist/ai/providers/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +114 -0
- package/dist/ai/providers/registry.js +458 -0
- package/dist/ai/sse.d.ts +32 -0
- package/dist/ai/sse.js +140 -0
- package/dist/ai/thinking.d.ts +42 -0
- package/dist/ai/thinking.js +101 -0
- package/dist/ai/types.d.ts +448 -0
- package/dist/ai/types.js +19 -0
- package/dist/bundle/ama-sandbox.cjs +324 -0
- package/dist/bundle/ama.cjs +27837 -0
- package/dist/bundle.d.ts +9 -0
- package/dist/bundle.js +15 -0
- package/dist/cli/args.d.ts +85 -0
- package/dist/cli/args.js +408 -0
- package/dist/cli/bootstrap.d.ts +18 -0
- package/dist/cli/bootstrap.js +399 -0
- package/dist/cli/compose-providers.d.ts +37 -0
- package/dist/cli/compose-providers.js +107 -0
- package/dist/cli/compose-session.d.ts +72 -0
- package/dist/cli/compose-session.js +370 -0
- package/dist/cli/compose-store.d.ts +24 -0
- package/dist/cli/compose-store.js +125 -0
- package/dist/cli/compose.d.ts +108 -0
- package/dist/cli/compose.js +221 -0
- package/dist/cli/default-model.d.ts +16 -0
- package/dist/cli/default-model.js +24 -0
- package/dist/cli/deps.d.ts +207 -0
- package/dist/cli/deps.js +12 -0
- package/dist/cli/exit-codes.d.ts +29 -0
- package/dist/cli/exit-codes.js +40 -0
- package/dist/cli/main.d.ts +27 -0
- package/dist/cli/main.js +177 -0
- package/dist/cli/runtime.d.ts +90 -0
- package/dist/cli/runtime.js +8 -0
- package/dist/cli/startup-screen.d.ts +15 -0
- package/dist/cli/startup-screen.js +56 -0
- package/dist/cli/startup-steps.d.ts +35 -0
- package/dist/cli/startup-steps.js +202 -0
- package/dist/cli/subcommands/auth.d.ts +12 -0
- package/dist/cli/subcommands/auth.js +91 -0
- package/dist/cli/subcommands/config.d.ts +53 -0
- package/dist/cli/subcommands/config.js +277 -0
- package/dist/cli/subcommands/context.d.ts +21 -0
- package/dist/cli/subcommands/context.js +42 -0
- package/dist/cli/subcommands/doctor.d.ts +11 -0
- package/dist/cli/subcommands/doctor.js +246 -0
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +22 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -0
- package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
- package/dist/cli/subcommands/models-cache-probe.js +190 -0
- package/dist/cli/subcommands/models-discover.d.ts +46 -0
- package/dist/cli/subcommands/models-discover.js +232 -0
- package/dist/cli/subcommands/models.d.ts +40 -0
- package/dist/cli/subcommands/models.js +152 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -0
- package/dist/cli/subcommands/providers.d.ts +27 -0
- package/dist/cli/subcommands/providers.js +433 -0
- package/dist/cli/subcommands/sessions.d.ts +11 -0
- package/dist/cli/subcommands/sessions.js +100 -0
- package/dist/codemode/capability.d.ts +37 -0
- package/dist/codemode/capability.js +55 -0
- package/dist/codemode/declarations.d.ts +44 -0
- package/dist/codemode/declarations.js +111 -0
- package/dist/codemode/host-side.d.ts +59 -0
- package/dist/codemode/host-side.js +253 -0
- package/dist/codemode/modes.d.ts +23 -0
- package/dist/codemode/modes.js +40 -0
- package/dist/codemode/protocol.d.ts +91 -0
- package/dist/codemode/protocol.js +114 -0
- package/dist/codemode/sandbox-entry.d.ts +25 -0
- package/dist/codemode/sandbox-entry.js +323 -0
- package/dist/codemode/store.d.ts +26 -0
- package/dist/codemode/store.js +53 -0
- package/dist/codemode/tool.d.ts +105 -0
- package/dist/codemode/tool.js +283 -0
- package/dist/compaction/branch-summary.d.ts +30 -0
- package/dist/compaction/branch-summary.js +79 -0
- package/dist/compaction/breaker.d.ts +32 -0
- package/dist/compaction/breaker.js +65 -0
- package/dist/compaction/cut-point.d.ts +23 -0
- package/dist/compaction/cut-point.js +70 -0
- package/dist/compaction/estimate.d.ts +30 -0
- package/dist/compaction/estimate.js +114 -0
- package/dist/compaction/prune-tier.d.ts +31 -0
- package/dist/compaction/prune-tier.js +83 -0
- package/dist/compaction/serialize.d.ts +20 -0
- package/dist/compaction/serialize.js +115 -0
- package/dist/compaction/summarize-tier.d.ts +111 -0
- package/dist/compaction/summarize-tier.js +305 -0
- package/dist/config/auth-file.d.ts +39 -0
- package/dist/config/auth-file.js +92 -0
- package/dist/config/context-files.d.ts +30 -0
- package/dist/config/context-files.js +77 -0
- package/dist/config/init.d.ts +30 -0
- package/dist/config/init.js +92 -0
- package/dist/config/json-schema.d.ts +14 -0
- package/dist/config/json-schema.js +182 -0
- package/dist/config/load.d.ts +45 -0
- package/dist/config/load.js +97 -0
- package/dist/config/merge.d.ts +73 -0
- package/dist/config/merge.js +248 -0
- package/dist/config/paths.d.ts +38 -0
- package/dist/config/paths.js +108 -0
- package/dist/config/profile.d.ts +27 -0
- package/dist/config/profile.js +65 -0
- package/dist/config/schema.d.ts +34 -0
- package/dist/config/schema.js +476 -0
- package/dist/config/trust.d.ts +42 -0
- package/dist/config/trust.js +122 -0
- package/dist/config/types.d.ts +190 -0
- package/dist/config/types.js +29 -0
- package/dist/config/write.d.ts +10 -0
- package/dist/config/write.js +25 -0
- package/dist/errors.d.ts +28 -0
- package/dist/errors.js +30 -0
- package/dist/hooks/config.d.ts +49 -0
- package/dist/hooks/config.js +102 -0
- package/dist/hooks/dispatcher.d.ts +41 -0
- package/dist/hooks/dispatcher.js +104 -0
- package/dist/hooks/matcher.d.ts +22 -0
- package/dist/hooks/matcher.js +108 -0
- package/dist/hooks/protocol.d.ts +33 -0
- package/dist/hooks/protocol.js +240 -0
- package/dist/hooks/runner.d.ts +30 -0
- package/dist/hooks/runner.js +143 -0
- package/dist/hooks/types.d.ts +147 -0
- package/dist/hooks/types.js +20 -0
- package/dist/host/api-impl.d.ts +75 -0
- package/dist/host/api-impl.js +173 -0
- package/dist/host/loader.d.ts +31 -0
- package/dist/host/loader.js +147 -0
- package/dist/host/types.d.ts +169 -0
- package/dist/host/types.js +14 -0
- package/dist/host.d.ts +5 -0
- package/dist/host.js +4 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +28 -0
- package/dist/modes/commands-core.d.ts +44 -0
- package/dist/modes/commands-core.js +171 -0
- package/dist/modes/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -0
- package/dist/modes/interactive/approval-dialog.d.ts +48 -0
- package/dist/modes/interactive/approval-dialog.js +202 -0
- package/dist/modes/interactive/commands.d.ts +41 -0
- package/dist/modes/interactive/commands.js +201 -0
- package/dist/modes/interactive/completion.d.ts +53 -0
- package/dist/modes/interactive/completion.js +157 -0
- package/dist/modes/interactive/interactive-mode.d.ts +46 -0
- package/dist/modes/interactive/interactive-mode.js +491 -0
- package/dist/modes/interactive/key-dispatch.d.ts +29 -0
- package/dist/modes/interactive/key-dispatch.js +107 -0
- package/dist/modes/interactive/line/line-editor.d.ts +50 -0
- package/dist/modes/interactive/line/line-editor.js +232 -0
- package/dist/modes/interactive/line/line-mode.d.ts +20 -0
- package/dist/modes/interactive/line/line-mode.js +218 -0
- package/dist/modes/interactive/line/line-render.d.ts +35 -0
- package/dist/modes/interactive/line/line-render.js +171 -0
- package/dist/modes/interactive/line/paste-state.d.ts +26 -0
- package/dist/modes/interactive/line/paste-state.js +72 -0
- package/dist/modes/interactive/message-view.d.ts +77 -0
- package/dist/modes/interactive/message-view.js +269 -0
- package/dist/modes/interactive/pickers.d.ts +42 -0
- package/dist/modes/interactive/pickers.js +115 -0
- package/dist/modes/interactive/startup-ui.d.ts +33 -0
- package/dist/modes/interactive/startup-ui.js +268 -0
- package/dist/modes/interactive/status-bar.d.ts +48 -0
- package/dist/modes/interactive/status-bar.js +139 -0
- package/dist/modes/interactive/tool-view.d.ts +95 -0
- package/dist/modes/interactive/tool-view.js +344 -0
- package/dist/modes/print/json-event.d.ts +15 -0
- package/dist/modes/print/json-event.js +39 -0
- package/dist/modes/print/print-mode.d.ts +17 -0
- package/dist/modes/print/print-mode.js +107 -0
- package/dist/modes/rpc/commands.d.ts +40 -0
- package/dist/modes/rpc/commands.js +263 -0
- package/dist/modes/rpc/jsonl.d.ts +16 -0
- package/dist/modes/rpc/jsonl.js +55 -0
- package/dist/modes/rpc/rpc-mode.d.ts +22 -0
- package/dist/modes/rpc/rpc-mode.js +153 -0
- package/dist/modes/session-report.d.ts +55 -0
- package/dist/modes/session-report.js +278 -0
- package/dist/modes/shared.d.ts +13 -0
- package/dist/modes/shared.js +52 -0
- package/dist/modes/startup-ui-text.d.ts +16 -0
- package/dist/modes/startup-ui-text.js +103 -0
- package/dist/permissions/broker.d.ts +47 -0
- package/dist/permissions/broker.js +107 -0
- package/dist/permissions/dangerous.d.ts +47 -0
- package/dist/permissions/dangerous.js +370 -0
- package/dist/permissions/pipeline.d.ts +51 -0
- package/dist/permissions/pipeline.js +181 -0
- package/dist/permissions/preview.d.ts +43 -0
- package/dist/permissions/preview.js +463 -0
- package/dist/permissions/rules.d.ts +58 -0
- package/dist/permissions/rules.js +249 -0
- package/dist/permissions/types.d.ts +100 -0
- package/dist/permissions/types.js +18 -0
- package/dist/rpc.d.ts +173 -0
- package/dist/rpc.js +13 -0
- package/dist/sdk.d.ts +98 -0
- package/dist/sdk.js +195 -0
- package/dist/session/manager.d.ts +70 -0
- package/dist/session/manager.js +278 -0
- package/dist/session/migrate.d.ts +19 -0
- package/dist/session/migrate.js +53 -0
- package/dist/session/projection.d.ts +50 -0
- package/dist/session/projection.js +184 -0
- package/dist/session/store.d.ts +47 -0
- package/dist/session/store.js +210 -0
- package/dist/session/tree.d.ts +21 -0
- package/dist/session/tree.js +88 -0
- package/dist/session/types.d.ts +201 -0
- package/dist/session/types.js +18 -0
- package/dist/skills/discover.d.ts +60 -0
- package/dist/skills/discover.js +160 -0
- package/dist/skills/expand.d.ts +35 -0
- package/dist/skills/expand.js +46 -0
- package/dist/skills/frontmatter.d.ts +27 -0
- package/dist/skills/frontmatter.js +157 -0
- package/dist/skills/index-prompt.d.ts +13 -0
- package/dist/skills/index-prompt.js +42 -0
- package/dist/skills/templates.d.ts +52 -0
- package/dist/skills/templates.js +177 -0
- package/dist/tools/bash.d.ts +49 -0
- package/dist/tools/bash.js +222 -0
- package/dist/tools/edit-fuzzy.d.ts +36 -0
- package/dist/tools/edit-fuzzy.js +129 -0
- package/dist/tools/edit.d.ts +38 -0
- package/dist/tools/edit.js +241 -0
- package/dist/tools/file-mutex.d.ts +8 -0
- package/dist/tools/file-mutex.js +29 -0
- package/dist/tools/glob.d.ts +44 -0
- package/dist/tools/glob.js +217 -0
- package/dist/tools/grep.d.ts +39 -0
- package/dist/tools/grep.js +179 -0
- package/dist/tools/ignore.d.ts +40 -0
- package/dist/tools/ignore.js +174 -0
- package/dist/tools/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -0
- package/dist/tools/ls.d.ts +21 -0
- package/dist/tools/ls.js +96 -0
- package/dist/tools/output-accumulator.d.ts +48 -0
- package/dist/tools/output-accumulator.js +106 -0
- package/dist/tools/paths.d.ts +20 -0
- package/dist/tools/paths.js +58 -0
- package/dist/tools/presets.d.ts +71 -0
- package/dist/tools/presets.js +128 -0
- package/dist/tools/process-tree.d.ts +35 -0
- package/dist/tools/process-tree.js +125 -0
- package/dist/tools/read.d.ts +24 -0
- package/dist/tools/read.js +132 -0
- package/dist/tools/registry.d.ts +43 -0
- package/dist/tools/registry.js +106 -0
- package/dist/tools/shell.d.ts +38 -0
- package/dist/tools/shell.js +91 -0
- package/dist/tools/task.d.ts +29 -0
- package/dist/tools/task.js +107 -0
- package/dist/tools/todo.d.ts +28 -0
- package/dist/tools/todo.js +107 -0
- package/dist/tools/truncate.d.ts +44 -0
- package/dist/tools/truncate.js +175 -0
- package/dist/tools/types.d.ts +135 -0
- package/dist/tools/types.js +19 -0
- package/dist/tools/write.d.ts +20 -0
- package/dist/tools/write.js +84 -0
- package/dist/tui/ansi.d.ts +29 -0
- package/dist/tui/ansi.js +420 -0
- package/dist/tui/component.d.ts +38 -0
- package/dist/tui/component.js +13 -0
- package/dist/tui/components/box.d.ts +26 -0
- package/dist/tui/components/box.js +65 -0
- package/dist/tui/components/container.d.ts +14 -0
- package/dist/tui/components/container.js +39 -0
- package/dist/tui/components/editor-buffer.d.ts +67 -0
- package/dist/tui/components/editor-buffer.js +300 -0
- package/dist/tui/components/editor-paste.d.ts +29 -0
- package/dist/tui/components/editor-paste.js +64 -0
- package/dist/tui/components/editor.d.ts +114 -0
- package/dist/tui/components/editor.js +530 -0
- package/dist/tui/components/key-value.d.ts +29 -0
- package/dist/tui/components/key-value.js +48 -0
- package/dist/tui/components/loader.d.ts +35 -0
- package/dist/tui/components/loader.js +71 -0
- package/dist/tui/components/markdown.d.ts +90 -0
- package/dist/tui/components/markdown.js +462 -0
- package/dist/tui/components/meter.d.ts +29 -0
- package/dist/tui/components/meter.js +47 -0
- package/dist/tui/components/overlay.d.ts +24 -0
- package/dist/tui/components/overlay.js +52 -0
- package/dist/tui/components/select-list.d.ts +57 -0
- package/dist/tui/components/select-list.js +179 -0
- package/dist/tui/components/spacer.d.ts +11 -0
- package/dist/tui/components/spacer.js +16 -0
- package/dist/tui/components/text.d.ts +30 -0
- package/dist/tui/components/text.js +65 -0
- package/dist/tui/keybindings.d.ts +66 -0
- package/dist/tui/keybindings.js +120 -0
- package/dist/tui/keys.d.ts +38 -0
- package/dist/tui/keys.js +275 -0
- package/dist/tui/stdin-buffer.d.ts +41 -0
- package/dist/tui/stdin-buffer.js +206 -0
- package/dist/tui/terminal.d.ts +79 -0
- package/dist/tui/terminal.js +153 -0
- package/dist/tui/theme.d.ts +31 -0
- package/dist/tui/theme.js +199 -0
- package/dist/tui/tui.d.ts +103 -0
- package/dist/tui/tui.js +363 -0
- package/dist/tui/vt-screen.d.ts +46 -0
- package/dist/tui/vt-screen.js +251 -0
- package/dist/tui.d.ts +30 -0
- package/dist/tui.js +31 -0
- package/dist/version.d.ts +8 -0
- package/dist/version.js +24 -0
- package/docs/codemode.md +102 -0
- package/docs/hooks.md +196 -0
- package/docs/host-api.md +161 -0
- package/docs/providers.md +531 -0
- package/docs/rpc.md +253 -0
- package/docs/session-format.md +119 -0
- package/docs/tui.md +170 -0
- package/package.json +101 -4
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama doctor`(设计 §1.2、§6.1、R4、R8)。[B5]
|
|
3
|
+
*
|
|
4
|
+
* 输出:目录、配置层级(每层文件存在 / 有效 / 警告)、信任状态与需信任的资源、AGENTS.md、
|
|
5
|
+
* key 来源(只给来源不给值)、将执行的 Hook 命令(含因未信任被跳过的项目级)、终端能力。
|
|
6
|
+
* 只读:不询问信任、不写任何文件;配置错误照常列出并以退出码 3 结束。
|
|
7
|
+
* [W3-B12] baseUrl 来自 `OPENAI_BASE_URL` / `ANTHROPIC_BASE_URL` 时在 key 行后标出变量与地址。
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync } from "node:fs";
|
|
10
|
+
import { baseUrlEnvOf } from "../../ai/providers/registry.js";
|
|
11
|
+
import { classifyKeyValue, isModeTooOpen, fileMode, readAuthFile } from "../../config/auth-file.js";
|
|
12
|
+
import { findContextFiles } from "../../config/context-files.js";
|
|
13
|
+
import { loadConfigFile, probeConfigFile } from "../../config/load.js";
|
|
14
|
+
import { mergeProjectAndCli } from "../../config/merge.js";
|
|
15
|
+
import { CONFIG_FILE, HOOKS_FILE, projectFile, userFile } from "../../config/paths.js";
|
|
16
|
+
import { findTrustEntry, readTrustFile, trustGatedResources } from "../../config/trust.js";
|
|
17
|
+
import { loadHookConfigs } from "../../hooks/config.js";
|
|
18
|
+
import { AMA_VERSION } from "../../version.js";
|
|
19
|
+
import { parseSubArgs } from "../args.js";
|
|
20
|
+
import { ExitCode } from "../exit-codes.js";
|
|
21
|
+
import { describeModel } from "./config.js";
|
|
22
|
+
import { buildRegistry, loadUserLevel } from "./context.js";
|
|
23
|
+
export const DOCTOR_USAGE = `用法:ama doctor [--profile <文件>] [--auth-file <文件>] [--trust | --no-trust]
|
|
24
|
+
`;
|
|
25
|
+
class Report {
|
|
26
|
+
lines = [];
|
|
27
|
+
problems = 0;
|
|
28
|
+
section(title) {
|
|
29
|
+
if (this.lines.length > 0)
|
|
30
|
+
this.lines.push("");
|
|
31
|
+
this.lines.push(title);
|
|
32
|
+
}
|
|
33
|
+
item(text) {
|
|
34
|
+
this.lines.push(` ${text}`);
|
|
35
|
+
}
|
|
36
|
+
problem(text) {
|
|
37
|
+
this.problems++;
|
|
38
|
+
this.lines.push(` ✗ ${text}`);
|
|
39
|
+
}
|
|
40
|
+
text() {
|
|
41
|
+
return `${this.lines.join("\n")}\n`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function probeLine(report, label, kind, path) {
|
|
45
|
+
const probe = probeConfigFile(kind, path);
|
|
46
|
+
if (!probe.exists) {
|
|
47
|
+
report.item(`${label}:${path}(不存在)`);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!probe.ok) {
|
|
51
|
+
report.problem(`${label}:${probe.messages.join("; ")}`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
report.item(`${label}:${path}`);
|
|
55
|
+
for (const message of probe.messages)
|
|
56
|
+
report.item(` 警告:${message}`);
|
|
57
|
+
}
|
|
58
|
+
function trustSection(report, io, level, flag) {
|
|
59
|
+
report.section("信任");
|
|
60
|
+
let trusted = false;
|
|
61
|
+
let from;
|
|
62
|
+
if (flag !== undefined) {
|
|
63
|
+
trusted = flag;
|
|
64
|
+
from = "命令行";
|
|
65
|
+
}
|
|
66
|
+
else if (level.profile?.trustProject === true) {
|
|
67
|
+
trusted = true;
|
|
68
|
+
from = "profile.trustProject";
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
try {
|
|
72
|
+
const entry = findTrustEntry(readTrustFile(level.configDir).entries, io.cwd);
|
|
73
|
+
trusted = entry?.trusted ?? false;
|
|
74
|
+
from = entry === undefined ? "缺省:无记录,交互模式会询问" : `trust.json ${entry.path}`;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
report.problem(error.message);
|
|
78
|
+
from = "trust.json 无效";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
report.item(`${io.cwd}:${trusted ? "已信任" : "未信任"}(${from})`);
|
|
82
|
+
const gated = trustGatedResources(io.cwd);
|
|
83
|
+
if (gated.length > 0) {
|
|
84
|
+
report.item(`需要信任的资源${trusted ? "" : "(当前不加载)"}:`);
|
|
85
|
+
for (const path of gated)
|
|
86
|
+
report.item(` ${path}`);
|
|
87
|
+
}
|
|
88
|
+
return trusted;
|
|
89
|
+
}
|
|
90
|
+
async function keySection(report, io, level, deps) {
|
|
91
|
+
report.section("Key 来源(只显示来源,不显示值)");
|
|
92
|
+
try {
|
|
93
|
+
const auth = readAuthFile(level.authFile);
|
|
94
|
+
const mode = fileMode(level.authFile);
|
|
95
|
+
if (!auth.exists)
|
|
96
|
+
report.item(`auth.json:${level.authFile}(不存在)`);
|
|
97
|
+
else if (isModeTooOpen(mode))
|
|
98
|
+
report.problem(`auth.json 权限 ${mode?.toString(8)},应为 600:${level.authFile}`);
|
|
99
|
+
else
|
|
100
|
+
report.item(`auth.json:${level.authFile}`);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
report.problem(error.message);
|
|
104
|
+
}
|
|
105
|
+
if (deps !== undefined) {
|
|
106
|
+
try {
|
|
107
|
+
const registry = await buildRegistry(level, io, deps);
|
|
108
|
+
for (const provider of registry.list()) {
|
|
109
|
+
const key = await registry.resolveApiKey(provider.id);
|
|
110
|
+
const text = key.apiKey !== undefined
|
|
111
|
+
? `${key.source}${key.origin !== undefined ? `(${key.origin})` : ""}`
|
|
112
|
+
: provider.requiresApiKey
|
|
113
|
+
? "无"
|
|
114
|
+
: "无需";
|
|
115
|
+
report.item(`${provider.id.padEnd(20)} ${text}`);
|
|
116
|
+
const env = baseUrlEnvOf(registry, provider.id);
|
|
117
|
+
if (env !== undefined)
|
|
118
|
+
report.item(`${"".padEnd(20)} baseUrl 来自环境变量 ${env}:${provider.baseUrl}(compat 按保守缺省)`);
|
|
119
|
+
}
|
|
120
|
+
const model = await describeModel(level.merged.config, registry);
|
|
121
|
+
report.item(`将使用的模型:${model.ref ?? "(无)"}(${model.reason})`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
report.problem(`供应商注册表:${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 未装配注册表:按文件与环境变量名列出能看到的来源。
|
|
129
|
+
try {
|
|
130
|
+
for (const [id, entry] of Object.entries(readAuthFile(level.authFile).file.providers)) {
|
|
131
|
+
report.item(`${id.padEnd(20)} auth-file(${classifyKeyValue(entry.apiKey)})`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// 上面已报告
|
|
136
|
+
}
|
|
137
|
+
for (const [id, provider] of Object.entries(level.merged.config.providers ?? {})) {
|
|
138
|
+
if (provider.apiKey !== undefined) {
|
|
139
|
+
report.item(`${id.padEnd(20)} config(${classifyKeyValue(provider.apiKey)})`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const envNames = Object.keys(io.env)
|
|
143
|
+
.filter((n) => /_API_KEY$/.test(n) || n.startsWith("AMA_API_KEY_"))
|
|
144
|
+
.filter((n) => (io.env[n] ?? "") !== "")
|
|
145
|
+
.sort();
|
|
146
|
+
report.item(`环境变量:${envNames.length > 0 ? envNames.join(", ") : "(无)"}`);
|
|
147
|
+
}
|
|
148
|
+
function hookSection(report, io, level, trusted) {
|
|
149
|
+
report.section("Hook");
|
|
150
|
+
try {
|
|
151
|
+
const hooks = loadHookConfigs({
|
|
152
|
+
configDir: level.configDir,
|
|
153
|
+
cwd: io.cwd,
|
|
154
|
+
profileHooksFile: level.profile?.hooksFile,
|
|
155
|
+
trusted,
|
|
156
|
+
defaultTimeoutMs: level.merged.config.hooks?.timeoutMs,
|
|
157
|
+
});
|
|
158
|
+
if (hooks.hooks.length === 0)
|
|
159
|
+
report.item("(无)");
|
|
160
|
+
for (const hook of hooks.hooks) {
|
|
161
|
+
const matcher = hook.matcher !== undefined ? ` [${hook.matcher}]` : "";
|
|
162
|
+
report.item(`${hook.source.padEnd(7)} ${hook.event}${matcher} → ${hook.command}(${hook.timeoutMs} ms)`);
|
|
163
|
+
}
|
|
164
|
+
if (hooks.skippedProject !== undefined) {
|
|
165
|
+
report.item(`未信任,跳过:${hooks.skippedProject}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
report.problem(error.message);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function terminalSection(report, io) {
|
|
173
|
+
report.section("终端");
|
|
174
|
+
const env = io.env;
|
|
175
|
+
report.item(`stdin TTY:${io.stdinIsTTY ? "是" : "否"} · stdout TTY:${io.stdoutIsTTY ? "是" : "否"}`);
|
|
176
|
+
report.item(`TERM=${env["TERM"] ?? "(未设置)"} · COLORTERM=${env["COLORTERM"] ?? "(未设置)"}`);
|
|
177
|
+
if (env["NO_COLOR"] !== undefined)
|
|
178
|
+
report.item("NO_COLOR 已设置:不输出颜色");
|
|
179
|
+
if (env["TMUX"] !== undefined)
|
|
180
|
+
report.item("在 tmux 中运行");
|
|
181
|
+
const columns = process.stdout.columns;
|
|
182
|
+
if (io.stdoutIsTTY && columns !== undefined)
|
|
183
|
+
report.item(`尺寸:${columns}×${process.stdout.rows}`);
|
|
184
|
+
const tui = io.stdinIsTTY && io.stdoutIsTTY && env["TERM"] !== "dumb";
|
|
185
|
+
report.item(`缺省界面:${tui ? "终端界面" : "行式(--no-tui 等价)"}`);
|
|
186
|
+
}
|
|
187
|
+
export async function runDoctor(argv, io, deps) {
|
|
188
|
+
const { values, flags } = parseSubArgs(argv, ["profile", "auth-file"], ["trust", "no-trust"]);
|
|
189
|
+
if (flags.has("help")) {
|
|
190
|
+
io.stdout(DOCTOR_USAGE);
|
|
191
|
+
return ExitCode.Ok;
|
|
192
|
+
}
|
|
193
|
+
const report = new Report();
|
|
194
|
+
report.section(`ama ${AMA_VERSION} · Node ${process.version} · ${process.platform}-${process.arch}`);
|
|
195
|
+
let level;
|
|
196
|
+
try {
|
|
197
|
+
level = loadUserLevel(io, {
|
|
198
|
+
profile: values.get("profile"),
|
|
199
|
+
authFile: values.get("auth-file"),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
report.problem(error.message);
|
|
204
|
+
io.stdout(report.text());
|
|
205
|
+
return ExitCode.Config;
|
|
206
|
+
}
|
|
207
|
+
report.section("目录");
|
|
208
|
+
report.item(`配置目录:${level.configDir}`);
|
|
209
|
+
report.item(`数据目录:${level.dataDir}${existsSync(level.dataDir) ? "" : "(尚未创建)"}`);
|
|
210
|
+
report.section("配置层级(缺省 ← 用户级 ← profile ← 项目级(只能收紧)← 命令行)");
|
|
211
|
+
report.item("缺省:内置");
|
|
212
|
+
probeLine(report, "用户级", "config", level.userConfigPath);
|
|
213
|
+
if (level.profile !== undefined) {
|
|
214
|
+
report.item(`profile:${level.profile.path}`);
|
|
215
|
+
if (level.profile.configFile !== undefined)
|
|
216
|
+
probeLine(report, "profile.config", "config", level.profile.configFile);
|
|
217
|
+
}
|
|
218
|
+
const projectPath = projectFile(io.cwd, CONFIG_FILE);
|
|
219
|
+
probeLine(report, "项目级", "config", projectPath);
|
|
220
|
+
try {
|
|
221
|
+
const project = loadConfigFile("config", projectPath);
|
|
222
|
+
const merged = mergeProjectAndCli(level.merged, project?.value, undefined);
|
|
223
|
+
for (const warning of merged.warnings)
|
|
224
|
+
report.item(` 收紧:${warning}`);
|
|
225
|
+
report.item(`有效权限模式:${merged.config.permission?.mode ?? "default"}`);
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// probeLine 已报告
|
|
229
|
+
}
|
|
230
|
+
for (const warning of level.warnings)
|
|
231
|
+
report.item(`警告:${warning}`);
|
|
232
|
+
const flag = flags.has("trust") ? true : flags.has("no-trust") ? false : undefined;
|
|
233
|
+
const trusted = trustSection(report, io, level, flag);
|
|
234
|
+
report.section("上下文文件(AGENTS.md,外层在前)");
|
|
235
|
+
const context = findContextFiles({ cwd: io.cwd, configDir: level.configDir });
|
|
236
|
+
if (context.files.length === 0)
|
|
237
|
+
report.item("(无)");
|
|
238
|
+
for (const file of context.files)
|
|
239
|
+
report.item(`${file.scope === "user" ? "用户级" : "项目"} ${file.path}`);
|
|
240
|
+
await keySection(report, io, level, deps);
|
|
241
|
+
hookSection(report, io, level, trusted);
|
|
242
|
+
report.item(`用户级 hooks.json:${userFile(level, HOOKS_FILE)}`);
|
|
243
|
+
terminalSection(report, io);
|
|
244
|
+
io.stdout(report.text());
|
|
245
|
+
return report.problems > 0 ? ExitCode.Config : ExitCode.Ok;
|
|
246
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama init [--force]`(docs/providers.md「配置目录」):建配置目录与缺省文件,逐个报告状态。
|
|
3
|
+
*/
|
|
4
|
+
import type { CliIo } from "../deps.js";
|
|
5
|
+
export declare const INIT_USAGE = "\u7528\u6CD5\uFF1Aama init [--force] \u5EFA\u914D\u7F6E\u76EE\u5F55\uFF080700\uFF09\u4E0E config.json\u3001config.schema.json\n \u5DF2\u6709\u7684 config.json \u4E0D\u8986\u76D6\uFF08--force \u5148\u5907\u4EFD\u4E3A .bak \u518D\u91CD\u5199\uFF09\uFF1B\u4E0D\u521B\u5EFA auth.json\n";
|
|
6
|
+
export declare function runInit(argv: readonly string[], io: CliIo): number;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama init [--force]`(docs/providers.md「配置目录」):建配置目录与缺省文件,逐个报告状态。
|
|
3
|
+
*/
|
|
4
|
+
import { describeInit, initConfigDir } from "../../config/init.js";
|
|
5
|
+
import { resolveConfigDir } from "../../config/paths.js";
|
|
6
|
+
import { parseSubArgs, UsageError } from "../args.js";
|
|
7
|
+
import { ExitCode } from "../exit-codes.js";
|
|
8
|
+
export const INIT_USAGE = `用法:ama init [--force] 建配置目录(0700)与 config.json、config.schema.json
|
|
9
|
+
已有的 config.json 不覆盖(--force 先备份为 .bak 再重写);不创建 auth.json
|
|
10
|
+
`;
|
|
11
|
+
export function runInit(argv, io) {
|
|
12
|
+
const { positionals, flags } = parseSubArgs(argv, [], ["force"]);
|
|
13
|
+
if (flags.has("help")) {
|
|
14
|
+
io.stdout(INIT_USAGE);
|
|
15
|
+
return ExitCode.Ok;
|
|
16
|
+
}
|
|
17
|
+
if (positionals.length > 0)
|
|
18
|
+
throw new UsageError(`多余的参数:${positionals.join(" ")}`);
|
|
19
|
+
const result = initConfigDir(resolveConfigDir({ env: io.env }), { force: flags.has("force") });
|
|
20
|
+
io.stdout(describeInit(result));
|
|
21
|
+
return ExitCode.Ok;
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型元数据的一行描述(`ama models list`、`ama config show`、`ama providers`):上下文、输出、思考、
|
|
3
|
+
* 图片、渠道,以及每个字段来自哪里(config / 目录 / models.dev / 缺省)与 models.dev 匹配。
|
|
4
|
+
*/
|
|
5
|
+
import { type ModelMetadata } from "../../ai/providers/enrich.js";
|
|
6
|
+
import type { Model, ProviderRegistryApi } from "../../ai/types.js";
|
|
7
|
+
export declare function compactTokens(n: number | undefined): string;
|
|
8
|
+
/** 注册表若是 ProviderRegistry(或同形状),取模型元数据来源;否则 undefined。 */
|
|
9
|
+
export declare function metadataOf(registry: ProviderRegistryApi, providerId: string, modelId: string): ModelMetadata | undefined;
|
|
10
|
+
/** `ctx 262k · out 64k · 思考 · 图片 · 渠道 chat,messages`。 */
|
|
11
|
+
export declare function modelFlags(model: Model): string;
|
|
12
|
+
/**
|
|
13
|
+
* 来源说明:全部来自内置目录时返回 undefined(不刷屏);否则
|
|
14
|
+
* `来源 ctx models.dev · out config · 图片 models.dev · …;models.dev 原厂 moonshotai/kimi-k2.5`。
|
|
15
|
+
*/
|
|
16
|
+
export declare function sourcesLine(metadata: ModelMetadata | undefined): string | undefined;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 模型元数据的一行描述(`ama models list`、`ama config show`、`ama providers`):上下文、输出、思考、
|
|
3
|
+
* 图片、渠道,以及每个字段来自哪里(config / 目录 / models.dev / 缺省)与 models.dev 匹配。
|
|
4
|
+
*/
|
|
5
|
+
import { sourceText } from "../../ai/providers/enrich.js";
|
|
6
|
+
import { matchLabel } from "../../ai/providers/models-dev.js";
|
|
7
|
+
export function compactTokens(n) {
|
|
8
|
+
if (n === undefined)
|
|
9
|
+
return "?";
|
|
10
|
+
if (n >= 1_000_000)
|
|
11
|
+
return `${Math.round(n / 100_000) / 10}M`;
|
|
12
|
+
if (n >= 1000)
|
|
13
|
+
return `${Math.round(n / 1000)}k`;
|
|
14
|
+
return String(n);
|
|
15
|
+
}
|
|
16
|
+
/** 注册表若是 ProviderRegistry(或同形状),取模型元数据来源;否则 undefined。 */
|
|
17
|
+
export function metadataOf(registry, providerId, modelId) {
|
|
18
|
+
const r = registry;
|
|
19
|
+
return typeof r.modelMetadata === "function" ? r.modelMetadata(providerId, modelId) : undefined;
|
|
20
|
+
}
|
|
21
|
+
/** `ctx 262k · out 64k · 思考 · 图片 · 渠道 chat,messages`。 */
|
|
22
|
+
export function modelFlags(model) {
|
|
23
|
+
return [
|
|
24
|
+
`ctx ${compactTokens(model.contextWindow)}`,
|
|
25
|
+
`out ${compactTokens(model.maxTokens)}`,
|
|
26
|
+
model.reasoning ? "思考" : undefined,
|
|
27
|
+
model.input.includes("image") ? "图片" : undefined,
|
|
28
|
+
model.channels !== undefined ? `渠道 ${model.channels.join(",")}` : undefined,
|
|
29
|
+
]
|
|
30
|
+
.filter((x) => x !== undefined)
|
|
31
|
+
.join(" · ");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 来源说明:全部来自内置目录时返回 undefined(不刷屏);否则
|
|
35
|
+
* `来源 ctx models.dev · out config · 图片 models.dev · …;models.dev 原厂 moonshotai/kimi-k2.5`。
|
|
36
|
+
*/
|
|
37
|
+
export function sourcesLine(metadata) {
|
|
38
|
+
if (metadata === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
const values = Object.values(metadata.sources);
|
|
41
|
+
if (values.every((s) => s === "catalog" || s === "default") && !metadata.looked)
|
|
42
|
+
return undefined;
|
|
43
|
+
const label = {
|
|
44
|
+
contextWindow: "ctx",
|
|
45
|
+
maxTokens: "out",
|
|
46
|
+
input: "图片",
|
|
47
|
+
reasoning: "思考",
|
|
48
|
+
cost: "价格",
|
|
49
|
+
};
|
|
50
|
+
const parts = Object.keys(label).map((field) => `${label[field]} ${sourceText(metadata.sources[field])}`);
|
|
51
|
+
const match = metadata.looked ? `;models.dev ${matchLabel(metadata.match)}` : "";
|
|
52
|
+
const tools = metadata.toolCall === false ? ";不支持工具调用" : "";
|
|
53
|
+
return `来源 ${parts.join(" · ")}${match}${tools}`;
|
|
54
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama models cache-probe <provider/model>`(第三波 §1.11):这个端点报不报缓存。[W3-C2]
|
|
3
|
+
*
|
|
4
|
+
* 固定前缀(system 一段确定性的编号句子,按字符 / 4 估到 `--tokens`,缺省 2048)+ user
|
|
5
|
+
* `Reply with: ok`,`maxTokens` 16、`cacheRetention: "short"`、`purpose: "probe"`,相隔 `--gap-ms`
|
|
6
|
+
* 发两次。判定:第二次 cacheRead ≥ 第一次前缀的 50% → `reported`;两次读写都是 0 → `silent`
|
|
7
|
+
* (字段缺失或恒 0);其余(读到一点、只有写入)→ `inconclusive`(粒度或 TTL 问题)。
|
|
8
|
+
*
|
|
9
|
+
* 计费动作:执行前打印预估(两次 × `--tokens` × 目录输入价,无价 `$?`);非 TTY 必须 `--yes`
|
|
10
|
+
* (否则退出 2),TTY 下问一次 y/N。`--json` 时预估写 stderr,stdout 只有一个结果对象。
|
|
11
|
+
* 协议层不保留原始响应,「usage 字段」列的是该协议解析时读取的字段名。
|
|
12
|
+
*/
|
|
13
|
+
import type { Api, Model, Usage } from "../../ai/types.js";
|
|
14
|
+
import type { ModelsAction } from "./models.js";
|
|
15
|
+
export declare const PROBE_DEFAULT_TOKENS = 2048;
|
|
16
|
+
export declare const PROBE_DEFAULT_GAP_MS = 3000;
|
|
17
|
+
export declare const PROBE_REQUEST_TIMEOUT_MS = 60000;
|
|
18
|
+
export type ProbeVerdict = "reported" | "silent" | "inconclusive";
|
|
19
|
+
export interface ProbeSample {
|
|
20
|
+
input: number;
|
|
21
|
+
cacheRead: number;
|
|
22
|
+
cacheWrite: number;
|
|
23
|
+
/** 原始响应里出现过缓存字段(含 0);协议没给出时 undefined。 */
|
|
24
|
+
cacheReported: boolean | undefined;
|
|
25
|
+
promptTokens: number;
|
|
26
|
+
}
|
|
27
|
+
/** 各协议解析缓存用量时读取的字段(`parseOpenAIUsage` 等)。 */
|
|
28
|
+
export declare const USAGE_FIELDS: Readonly<Partial<Record<Api, readonly string[]>>>;
|
|
29
|
+
/** 确定性前缀:编号句子,字符数 ≈ tokens × 4。 */
|
|
30
|
+
export declare function probePrefix(tokens: number): string;
|
|
31
|
+
export declare function sampleOf(usage: Usage): ProbeSample;
|
|
32
|
+
export declare function judgeProbe(first: ProbeSample, second: ProbeSample): ProbeVerdict;
|
|
33
|
+
export declare function probeAdvice(verdict: ProbeVerdict, model: Model, samples?: readonly ProbeSample[]): string | undefined;
|
|
34
|
+
export declare const CACHE_PROBE_ACTION: ModelsAction;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama models cache-probe <provider/model>`(第三波 §1.11):这个端点报不报缓存。[W3-C2]
|
|
3
|
+
*
|
|
4
|
+
* 固定前缀(system 一段确定性的编号句子,按字符 / 4 估到 `--tokens`,缺省 2048)+ user
|
|
5
|
+
* `Reply with: ok`,`maxTokens` 16、`cacheRetention: "short"`、`purpose: "probe"`,相隔 `--gap-ms`
|
|
6
|
+
* 发两次。判定:第二次 cacheRead ≥ 第一次前缀的 50% → `reported`;两次读写都是 0 → `silent`
|
|
7
|
+
* (字段缺失或恒 0);其余(读到一点、只有写入)→ `inconclusive`(粒度或 TTL 问题)。
|
|
8
|
+
*
|
|
9
|
+
* 计费动作:执行前打印预估(两次 × `--tokens` × 目录输入价,无价 `$?`);非 TTY 必须 `--yes`
|
|
10
|
+
* (否则退出 2),TTY 下问一次 y/N。`--json` 时预估写 stderr,stdout 只有一个结果对象。
|
|
11
|
+
* 协议层不保留原始响应,「usage 字段」列的是该协议解析时读取的字段名。
|
|
12
|
+
*/
|
|
13
|
+
import { createInterface } from "node:readline/promises";
|
|
14
|
+
import { priceTokens } from "../../ai/cost.js";
|
|
15
|
+
import { UsageError } from "../args.js";
|
|
16
|
+
import { ExitCode } from "../exit-codes.js";
|
|
17
|
+
export const PROBE_DEFAULT_TOKENS = 2048;
|
|
18
|
+
export const PROBE_DEFAULT_GAP_MS = 3000;
|
|
19
|
+
export const PROBE_REQUEST_TIMEOUT_MS = 60_000;
|
|
20
|
+
/** 各协议解析缓存用量时读取的字段(`parseOpenAIUsage` 等)。 */
|
|
21
|
+
export const USAGE_FIELDS = {
|
|
22
|
+
"openai-completions": [
|
|
23
|
+
"prompt_tokens_details.cached_tokens",
|
|
24
|
+
"prompt_cache_hit_tokens",
|
|
25
|
+
"cached_tokens",
|
|
26
|
+
"prompt_tokens_details.cache_write_tokens",
|
|
27
|
+
],
|
|
28
|
+
"openai-responses": ["input_tokens_details.cached_tokens"],
|
|
29
|
+
"anthropic-messages": ["cache_read_input_tokens", "cache_creation_input_tokens"],
|
|
30
|
+
"google-generative-ai": ["usageMetadata.cachedContentTokenCount"],
|
|
31
|
+
};
|
|
32
|
+
/** 确定性前缀:编号句子,字符数 ≈ tokens × 4。 */
|
|
33
|
+
export function probePrefix(tokens) {
|
|
34
|
+
const lines = [];
|
|
35
|
+
let chars = 0;
|
|
36
|
+
for (let i = 1; chars < tokens * 4; i++) {
|
|
37
|
+
const line = `Reference line ${i}: cache probe filler text that stays byte-identical between requests.`;
|
|
38
|
+
lines.push(line);
|
|
39
|
+
chars += line.length + 1;
|
|
40
|
+
}
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
export function sampleOf(usage) {
|
|
44
|
+
return {
|
|
45
|
+
input: usage.input,
|
|
46
|
+
cacheRead: usage.cacheRead,
|
|
47
|
+
cacheWrite: usage.cacheWrite,
|
|
48
|
+
cacheReported: usage.cacheReported,
|
|
49
|
+
promptTokens: usage.input + usage.cacheRead + usage.cacheWrite,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function judgeProbe(first, second) {
|
|
53
|
+
if (first.promptTokens > 0 && second.cacheRead >= first.promptTokens * 0.5)
|
|
54
|
+
return "reported";
|
|
55
|
+
const any = first.cacheRead + first.cacheWrite + second.cacheRead + second.cacheWrite;
|
|
56
|
+
return any === 0 ? "silent" : "inconclusive";
|
|
57
|
+
}
|
|
58
|
+
export function probeAdvice(verdict, model, samples = []) {
|
|
59
|
+
if (verdict === "silent") {
|
|
60
|
+
// 实测:同一中转的 Kimi 间隔 3 s 两次都是 0、间隔更久才命中——字段存在时提示写入延迟的可能
|
|
61
|
+
const present = samples.some((s) => s.cacheReported === true)
|
|
62
|
+
? "(响应里有缓存字段但恒为 0;也可能是缓存写入有延迟,可加大 --gap-ms 再测一次)"
|
|
63
|
+
: "";
|
|
64
|
+
return `可在 config 里设 providers.${model.provider}.compat.cacheReporting: "silent",状态栏将显示未报告${present}`;
|
|
65
|
+
}
|
|
66
|
+
if (verdict === "inconclusive")
|
|
67
|
+
return "第二次只读到少量缓存或只有写入:可能是缓存粒度或 TTL 问题,可加大 --tokens 或缩短 --gap-ms 重试";
|
|
68
|
+
if (model.promptCache === undefined)
|
|
69
|
+
return `可自填 promptCache.short 以启用保温(providers.${model.provider}.modelOverrides: [{ "id": "${model.id}", "promptCache": { "short": 300 } }])`;
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
function intOption(ctx, name, fallback, min) {
|
|
73
|
+
const raw = ctx.values.get(name);
|
|
74
|
+
if (raw === undefined)
|
|
75
|
+
return fallback;
|
|
76
|
+
const value = Number(raw);
|
|
77
|
+
if (!Number.isInteger(value) || value < min)
|
|
78
|
+
throw new UsageError(`--${name} 应为不小于 ${min} 的整数`);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
async function confirm() {
|
|
82
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
83
|
+
try {
|
|
84
|
+
return /^y(es)?$/i.test((await rl.question("继续?[y/N] ")).trim());
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
rl.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function sampleLine(index, s) {
|
|
91
|
+
const field = s.cacheReported === undefined ? "?" : s.cacheReported ? "有" : "无";
|
|
92
|
+
return `#${index} input ${s.input} · cacheRead ${s.cacheRead} · cacheWrite ${s.cacheWrite} · 缓存字段 ${field}`;
|
|
93
|
+
}
|
|
94
|
+
async function run(ctx) {
|
|
95
|
+
const { io, registry } = ctx;
|
|
96
|
+
const ref = ctx.args[0] ?? "";
|
|
97
|
+
const tokens = intOption(ctx, "tokens", PROBE_DEFAULT_TOKENS, 1);
|
|
98
|
+
const gapMs = intOption(ctx, "gap-ms", PROBE_DEFAULT_GAP_MS, 0);
|
|
99
|
+
const json = ctx.flags.has("json");
|
|
100
|
+
const found = registry.findModel(ref);
|
|
101
|
+
if (!found.ok) {
|
|
102
|
+
io.stderr(`ama: 模型不存在:${ref}\n`);
|
|
103
|
+
return ExitCode.NoModel;
|
|
104
|
+
}
|
|
105
|
+
const { model, provider } = found;
|
|
106
|
+
const key = await registry.resolveApiKey(provider.id, model.channel);
|
|
107
|
+
if (key.apiKey === undefined && provider.requiresApiKey) {
|
|
108
|
+
io.stderr(`ama: ${provider.id} 没有 API key(ama auth set ${provider.id})\n`);
|
|
109
|
+
return ExitCode.NoModel;
|
|
110
|
+
}
|
|
111
|
+
const api = registry.getApi(model.api);
|
|
112
|
+
if (api === undefined) {
|
|
113
|
+
io.stderr(`ama: 协议 ${model.api} 尚未实现\n`);
|
|
114
|
+
return ExitCode.RuntimeError;
|
|
115
|
+
}
|
|
116
|
+
const estimate = priceTokens(model, { input: tokens * 2, output: 32 });
|
|
117
|
+
const name = `${provider.id}/${model.id}`;
|
|
118
|
+
const head = `cache-probe ${name}(${model.api})· 前缀约 ${tokens} token · 间隔 ${gapMs} ms\n预估花费:${estimate === undefined ? "$?" : `$${estimate.toFixed(4)}`}(两次 × ${tokens} token × 目录价)\n`;
|
|
119
|
+
(json ? io.stderr : io.stdout)(head);
|
|
120
|
+
if (!ctx.flags.has("yes")) {
|
|
121
|
+
if (!io.stdinIsTTY) {
|
|
122
|
+
io.stderr("ama: cache-probe 会发两次计费请求,非交互环境需加 --yes\n");
|
|
123
|
+
return ExitCode.Usage;
|
|
124
|
+
}
|
|
125
|
+
if (!(await confirm())) {
|
|
126
|
+
io.stderr("ama: 已取消\n");
|
|
127
|
+
return ExitCode.Ok;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const messages = [
|
|
131
|
+
{ role: "system", sections: { probe: probePrefix(tokens) }, timestamp: 0 },
|
|
132
|
+
{ role: "user", content: "Reply with: ok", timestamp: 0 },
|
|
133
|
+
];
|
|
134
|
+
const samples = [];
|
|
135
|
+
for (let i = 0; i < 2; i++) {
|
|
136
|
+
if (i > 0 && gapMs > 0)
|
|
137
|
+
await new Promise((r) => setTimeout(r, gapMs));
|
|
138
|
+
const message = await api
|
|
139
|
+
.stream(model, { messages }, {
|
|
140
|
+
signal: AbortSignal.timeout(PROBE_REQUEST_TIMEOUT_MS),
|
|
141
|
+
...(key.apiKey !== undefined ? { apiKey: key.apiKey } : {}),
|
|
142
|
+
maxTokens: 16,
|
|
143
|
+
cacheRetention: "short",
|
|
144
|
+
purpose: "probe",
|
|
145
|
+
})
|
|
146
|
+
.result();
|
|
147
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
148
|
+
io.stderr(`ama: 第 ${i + 1} 次请求失败:${message.errorMessage ?? message.stopReason}\n`);
|
|
149
|
+
return ExitCode.RuntimeError;
|
|
150
|
+
}
|
|
151
|
+
samples.push(sampleOf(message.usage));
|
|
152
|
+
}
|
|
153
|
+
const [first, second] = samples;
|
|
154
|
+
const verdict = judgeProbe(first, second);
|
|
155
|
+
const advice = probeAdvice(verdict, model, samples);
|
|
156
|
+
const fields = USAGE_FIELDS[model.api] ?? [];
|
|
157
|
+
if (json) {
|
|
158
|
+
const result = {
|
|
159
|
+
model: name,
|
|
160
|
+
api: model.api,
|
|
161
|
+
tokens,
|
|
162
|
+
gapMs,
|
|
163
|
+
estimatedCostUsd: estimate ?? null,
|
|
164
|
+
requests: samples,
|
|
165
|
+
usageFields: fields,
|
|
166
|
+
verdict,
|
|
167
|
+
...(advice !== undefined ? { advice } : {}),
|
|
168
|
+
};
|
|
169
|
+
io.stdout(`${JSON.stringify(result)}\n`);
|
|
170
|
+
return ExitCode.Ok;
|
|
171
|
+
}
|
|
172
|
+
const share = first.promptTokens > 0
|
|
173
|
+
? `(第二次读到前缀的 ${Math.round((second.cacheRead / first.promptTokens) * 100)}%)`
|
|
174
|
+
: "";
|
|
175
|
+
io.stdout([
|
|
176
|
+
sampleLine(1, first),
|
|
177
|
+
sampleLine(2, second),
|
|
178
|
+
...(fields.length > 0 ? [`usage 字段(${model.api} 读取):${fields.join(" / ")}`] : []),
|
|
179
|
+
`判定:${verdict}${share}`,
|
|
180
|
+
...(advice !== undefined ? [`建议:${advice}`] : []),
|
|
181
|
+
].join("\n") + "\n");
|
|
182
|
+
return ExitCode.Ok;
|
|
183
|
+
}
|
|
184
|
+
export const CACHE_PROBE_ACTION = {
|
|
185
|
+
usage: "ama models cache-probe <provider/id> [--tokens 2048] [--gap-ms 3000] [--json] [--yes]",
|
|
186
|
+
required: "<provider/id>",
|
|
187
|
+
valueOptions: ["tokens", "gap-ms"],
|
|
188
|
+
flagOptions: ["json", "yes"],
|
|
189
|
+
run,
|
|
190
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ama models discover <provider>`(第三波 §2.3):问中转站有哪些模型。[W3-B12]
|
|
3
|
+
*
|
|
4
|
+
* GET `{baseUrl}/models`(anthropic-messages 且 baseUrl 不以 `/v1` 结尾时为 `{baseUrl}/v1/models`),
|
|
5
|
+
* 带供应商的鉴权头,打印 id 列表并标出已配置的条目。
|
|
6
|
+
*
|
|
7
|
+
* `--probe`:同一中转下不同模型支持的协议不同,对每个 id 依次试供应商协议 → completions →
|
|
8
|
+
* responses → messages(去重),各发一次最小请求(`ama models check` 同款,maxTokens 16),
|
|
9
|
+
* 记第一个成功的协议;每模型最多 3 次请求,`--limit`(缺省 30)限制探测的模型数,执行前打印
|
|
10
|
+
* 预估;401 / 403 / 429 立即停止(key 无效或被限流,继续只会浪费请求)。
|
|
11
|
+
*
|
|
12
|
+
* `--write`:新条目合并进用户级 config.json 的 `providers.<id>.models`——已有同 id 不覆盖;只写
|
|
13
|
+
* `id` 与探到的 `api`(与供应商协议相同时省略);带 `--probe` 时只写探测成功的模型。不猜
|
|
14
|
+
* `contextWindow`(自动压缩随之关闭,输出 warning)。写前备份为 `config.json.bak`。
|
|
15
|
+
*
|
|
16
|
+
* models.dev:列表之后按需刷新缓存(24 小时内不重拉),每个模型标出上下文、输出、图片、工具调用与
|
|
17
|
+
* 匹配结果;`--write` 跳过 models.dev 标明不支持工具调用的模型。元数据不写进配置——运行时从缓存补。
|
|
18
|
+
*/
|
|
19
|
+
import type { Api, Model, ProviderData, ProviderRegistryApi } from "../../ai/types.js";
|
|
20
|
+
import type { ModelsAction } from "./models.js";
|
|
21
|
+
export declare const DISCOVER_TIMEOUT_MS = 15000;
|
|
22
|
+
export declare const PROBE_TIMEOUT_MS = 30000;
|
|
23
|
+
export declare const DEFAULT_PROBE_LIMIT = 30;
|
|
24
|
+
/** 中转常见的三种协议,按此顺序探测(供应商协议排最前)。 */
|
|
25
|
+
export declare const PROBE_APIS: readonly Api[];
|
|
26
|
+
/** 模型列表的 URL:OpenAI 系是 `{baseUrl}/models`;Anthropic 官方形状的 baseUrl 不带 `/v1`。 */
|
|
27
|
+
export declare function modelsUrl(provider: ProviderData): string;
|
|
28
|
+
export declare function discoverModels(provider: ProviderData, apiKey: string | undefined, options?: {
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}): Promise<Model[]>;
|
|
31
|
+
export declare function probeOrder(provider: ProviderData): Api[];
|
|
32
|
+
export interface ProbeOptions {
|
|
33
|
+
timeoutMs?: number;
|
|
34
|
+
/** 每次请求后回调。 */
|
|
35
|
+
onAttempt?: (id: string, api: Api, error: string | undefined) => void;
|
|
36
|
+
/** 每个模型探完回调(进度输出)。 */
|
|
37
|
+
onResult?: (id: string, api: Api | undefined) => void;
|
|
38
|
+
/** 因鉴权失败或限流提前停止时回调。 */
|
|
39
|
+
onStop?: (reason: string) => void;
|
|
40
|
+
}
|
|
41
|
+
export declare const FATAL_STATUS: RegExp;
|
|
42
|
+
/** 返回已探测模型 → 第一个成功的协议(都失败为 undefined);超出 `limit` 或提前停止的不在表里。 */
|
|
43
|
+
export declare function probeModelApis(registry: ProviderRegistryApi, provider: ProviderData, ids: string[], limit: number, options?: ProbeOptions): Promise<Map<string, Api | undefined>>;
|
|
44
|
+
/** 一次最小请求;成功返回 undefined,失败返回错误文本(`ama providers add --probe` 也用)。 */
|
|
45
|
+
export declare function attempt(registry: ProviderRegistryApi, model: Model, apiKey: string | undefined, timeoutMs?: number): Promise<string | undefined>;
|
|
46
|
+
export declare const DISCOVER_ACTION: ModelsAction;
|