scoutline 0.1.0 → 0.2.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/README.md +170 -5
- package/bin/scoutline.js +36 -15
- package/dist/capabilities/diagnostics.d.ts +97 -0
- package/dist/capabilities/diagnostics.d.ts.map +1 -0
- package/dist/capabilities/diagnostics.js +88 -0
- package/dist/capabilities/diagnostics.js.map +1 -0
- package/dist/capabilities/quota.d.ts +99 -0
- package/dist/capabilities/quota.d.ts.map +1 -0
- package/dist/capabilities/quota.js +129 -0
- package/dist/capabilities/quota.js.map +1 -0
- package/dist/capabilities/search.d.ts +110 -0
- package/dist/capabilities/search.d.ts.map +1 -0
- package/dist/capabilities/search.js +21 -0
- package/dist/capabilities/search.js.map +1 -0
- package/dist/capabilities/vision.d.ts +156 -0
- package/dist/capabilities/vision.d.ts.map +1 -0
- package/dist/capabilities/vision.js +111 -0
- package/dist/capabilities/vision.js.map +1 -0
- package/dist/command-invocation.d.ts +58 -0
- package/dist/command-invocation.d.ts.map +1 -0
- package/dist/command-invocation.js +95 -0
- package/dist/command-invocation.js.map +1 -0
- package/dist/commands/code.d.ts +11 -4
- package/dist/commands/code.d.ts.map +1 -1
- package/dist/commands/code.js +16 -42
- package/dist/commands/code.js.map +1 -1
- package/dist/commands/doctor.d.ts +58 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +154 -52
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/quota.d.ts +45 -3
- package/dist/commands/quota.d.ts.map +1 -1
- package/dist/commands/quota.js +116 -60
- package/dist/commands/quota.js.map +1 -1
- package/dist/commands/read.d.ts +15 -1
- package/dist/commands/read.d.ts.map +1 -1
- package/dist/commands/read.js +53 -54
- package/dist/commands/read.js.map +1 -1
- package/dist/commands/repo.d.ts +8 -3
- package/dist/commands/repo.d.ts.map +1 -1
- package/dist/commands/repo.js +40 -78
- package/dist/commands/repo.js.map +1 -1
- package/dist/commands/search.d.ts +27 -2
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +95 -103
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/tools.d.ts +9 -3
- package/dist/commands/tools.d.ts.map +1 -1
- package/dist/commands/tools.js +29 -59
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/vision.d.ts +35 -9
- package/dist/commands/vision.d.ts.map +1 -1
- package/dist/commands/vision.js +184 -131
- package/dist/commands/vision.js.map +1 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +389 -207
- package/dist/index.js.map +1 -1
- package/dist/lib/cache.d.ts +61 -0
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +74 -11
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/code-mode.d.ts +13 -0
- package/dist/lib/code-mode.d.ts.map +1 -1
- package/dist/lib/code-mode.js +26 -6
- package/dist/lib/code-mode.js.map +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +15 -18
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/errors.d.ts +65 -10
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +135 -19
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/execution.d.ts +76 -0
- package/dist/lib/execution.d.ts.map +1 -0
- package/dist/lib/execution.js +177 -0
- package/dist/lib/execution.js.map +1 -0
- package/dist/lib/index.d.ts +10 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +10 -2
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/mcp-client.d.ts +46 -11
- package/dist/lib/mcp-client.d.ts.map +1 -1
- package/dist/lib/mcp-client.js +92 -27
- package/dist/lib/mcp-client.js.map +1 -1
- package/dist/lib/mcp-config.d.ts +17 -0
- package/dist/lib/mcp-config.d.ts.map +1 -1
- package/dist/lib/mcp-config.js +29 -0
- package/dist/lib/mcp-config.js.map +1 -1
- package/dist/lib/monitor-client.d.ts +17 -3
- package/dist/lib/monitor-client.d.ts.map +1 -1
- package/dist/lib/monitor-client.js +23 -69
- package/dist/lib/monitor-client.js.map +1 -1
- package/dist/lib/output.d.ts +53 -7
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +126 -39
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/redact.d.ts +62 -3
- package/dist/lib/redact.d.ts.map +1 -1
- package/dist/lib/redact.js +165 -31
- package/dist/lib/redact.js.map +1 -1
- package/dist/lib/tty.d.ts +7 -20
- package/dist/lib/tty.d.ts.map +1 -1
- package/dist/lib/tty.js +51 -30
- package/dist/lib/tty.js.map +1 -1
- package/dist/node-command-invocation-adapter.d.ts +28 -0
- package/dist/node-command-invocation-adapter.d.ts.map +1 -0
- package/dist/node-command-invocation-adapter.js +88 -0
- package/dist/node-command-invocation-adapter.js.map +1 -0
- package/dist/providers/minimax/adapter.d.ts +47 -0
- package/dist/providers/minimax/adapter.d.ts.map +1 -0
- package/dist/providers/minimax/adapter.js +443 -0
- package/dist/providers/minimax/adapter.js.map +1 -0
- package/dist/providers/minimax/config.d.ts +32 -0
- package/dist/providers/minimax/config.d.ts.map +1 -0
- package/dist/providers/minimax/config.js +84 -0
- package/dist/providers/minimax/config.js.map +1 -0
- package/dist/providers/minimax/media.d.ts +34 -0
- package/dist/providers/minimax/media.d.ts.map +1 -0
- package/dist/providers/minimax/media.js +92 -0
- package/dist/providers/minimax/media.js.map +1 -0
- package/dist/providers/minimax/quota-client.d.ts +37 -0
- package/dist/providers/minimax/quota-client.d.ts.map +1 -0
- package/dist/providers/minimax/quota-client.js +78 -0
- package/dist/providers/minimax/quota-client.js.map +1 -0
- package/dist/providers/minimax/quota.d.ts +48 -0
- package/dist/providers/minimax/quota.d.ts.map +1 -0
- package/dist/providers/minimax/quota.js +120 -0
- package/dist/providers/minimax/quota.js.map +1 -0
- package/dist/providers/minimax/sdk-client.d.ts +29 -0
- package/dist/providers/minimax/sdk-client.d.ts.map +1 -0
- package/dist/providers/minimax/sdk-client.js +50 -0
- package/dist/providers/minimax/sdk-client.js.map +1 -0
- package/dist/providers/minimax/vision-attestations.d.ts +29 -0
- package/dist/providers/minimax/vision-attestations.d.ts.map +1 -0
- package/dist/providers/minimax/vision-attestations.js +54 -0
- package/dist/providers/minimax/vision-attestations.js.map +1 -0
- package/dist/providers/minimax/vision-conformance.d.ts +184 -0
- package/dist/providers/minimax/vision-conformance.d.ts.map +1 -0
- package/dist/providers/minimax/vision-conformance.js +369 -0
- package/dist/providers/minimax/vision-conformance.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/chart.d.ts +35 -0
- package/dist/providers/minimax/vision-mappings/chart.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/chart.js +55 -0
- package/dist/providers/minimax/vision-mappings/chart.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/common.d.ts +60 -0
- package/dist/providers/minimax/vision-mappings/common.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/common.js +75 -0
- package/dist/providers/minimax/vision-mappings/common.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/diagnose-error.d.ts +35 -0
- package/dist/providers/minimax/vision-mappings/diagnose-error.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/diagnose-error.js +55 -0
- package/dist/providers/minimax/vision-mappings/diagnose-error.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/diagram.d.ts +35 -0
- package/dist/providers/minimax/vision-mappings/diagram.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/diagram.js +55 -0
- package/dist/providers/minimax/vision-mappings/diagram.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/extract-text.d.ts +35 -0
- package/dist/providers/minimax/vision-mappings/extract-text.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/extract-text.js +55 -0
- package/dist/providers/minimax/vision-mappings/extract-text.js.map +1 -0
- package/dist/providers/minimax/vision-mappings/ui-artifact.d.ts +35 -0
- package/dist/providers/minimax/vision-mappings/ui-artifact.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings/ui-artifact.js +69 -0
- package/dist/providers/minimax/vision-mappings/ui-artifact.js.map +1 -0
- package/dist/providers/minimax/vision-mappings.generated.d.ts +29 -0
- package/dist/providers/minimax/vision-mappings.generated.d.ts.map +1 -0
- package/dist/providers/minimax/vision-mappings.generated.js +29 -0
- package/dist/providers/minimax/vision-mappings.generated.js.map +1 -0
- package/dist/providers/minimax/vision-revisions.d.ts +22 -0
- package/dist/providers/minimax/vision-revisions.d.ts.map +1 -0
- package/dist/providers/minimax/vision-revisions.js +27 -0
- package/dist/providers/minimax/vision-revisions.js.map +1 -0
- package/dist/providers/registry.d.ts +36 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +45 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/selection.d.ts +44 -0
- package/dist/providers/selection.d.ts.map +1 -0
- package/dist/providers/selection.js +85 -0
- package/dist/providers/selection.js.map +1 -0
- package/dist/providers/types.d.ts +247 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +127 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/providers/zai/adapter.d.ts +35 -0
- package/dist/providers/zai/adapter.d.ts.map +1 -0
- package/dist/providers/zai/adapter.js +619 -0
- package/dist/providers/zai/adapter.js.map +1 -0
- package/dist/providers/zai/credentials.d.ts +38 -0
- package/dist/providers/zai/credentials.d.ts.map +1 -0
- package/dist/providers/zai/credentials.js +60 -0
- package/dist/providers/zai/credentials.js.map +1 -0
- package/dist/providers/zai/media.d.ts +38 -0
- package/dist/providers/zai/media.d.ts.map +1 -0
- package/dist/providers/zai/media.js +107 -0
- package/dist/providers/zai/media.js.map +1 -0
- package/dist/providers/zai/monitor-client.d.ts +53 -0
- package/dist/providers/zai/monitor-client.d.ts.map +1 -0
- package/dist/providers/zai/monitor-client.js +117 -0
- package/dist/providers/zai/monitor-client.js.map +1 -0
- package/dist/providers/zai/quota.d.ts +47 -0
- package/dist/providers/zai/quota.d.ts.map +1 -0
- package/dist/providers/zai/quota.js +127 -0
- package/dist/providers/zai/quota.js.map +1 -0
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -6,34 +6,50 @@ import { search, SEARCH_HELP } from "./commands/search.js";
|
|
|
6
6
|
import { read, READ_HELP } from "./commands/read.js";
|
|
7
7
|
import { repoSearch, repoTree, repoRead, REPO_HELP } from "./commands/repo.js";
|
|
8
8
|
import { listTools, showTool, callTool, TOOLS_HELP, CALL_HELP } from "./commands/tools.js";
|
|
9
|
-
import { doctor, DOCTOR_HELP } from "./commands/doctor.js";
|
|
10
|
-
import { quota, QUOTA_HELP } from "./commands/quota.js";
|
|
9
|
+
import { doctor, buildDiagnosticsReport, DOCTOR_HELP } from "./commands/doctor.js";
|
|
10
|
+
import { quota, buildQuotaDashboard, QUOTA_HELP } from "./commands/quota.js";
|
|
11
11
|
import { isExtractMode } from "./lib/extract.js";
|
|
12
12
|
import { runCodeFile, evalCode, printInterfaces, printPromptTemplate, CODE_HELP, } from "./commands/code.js";
|
|
13
|
-
import {
|
|
13
|
+
import { isOutputMode, OUTPUT_MODES } from "./lib/output.js";
|
|
14
|
+
import { formatErrorOutput } from "./lib/output.js";
|
|
15
|
+
import { ConfigurationError, ValidationError, UnsupportedCapabilityError, getErrorExitCode, } from "./lib/errors.js";
|
|
16
|
+
import { invokeCommand } from "./command-invocation.js";
|
|
17
|
+
import { defaultResponseCache } from "./lib/cache.js";
|
|
18
|
+
import { configuredSecrets } from "./lib/redact.js";
|
|
19
|
+
import { resolveProviderId } from "./providers/selection.js";
|
|
20
|
+
import { BUILT_IN_PROVIDER_DESCRIPTORS, getProviderDescriptor } from "./providers/registry.js";
|
|
21
|
+
import { visionOperationToCapability } from "./capabilities/vision.js";
|
|
14
22
|
import { createRequire } from "node:module";
|
|
15
23
|
const require = createRequire(import.meta.url);
|
|
16
24
|
const { version: VERSION } = require("../package.json");
|
|
17
25
|
const MAIN_HELP = `
|
|
18
|
-
scoutline v${VERSION} -
|
|
26
|
+
scoutline v${VERSION} - Multimodal source investigation CLI
|
|
19
27
|
|
|
20
28
|
Usage: scoutline <command> [args] [options]
|
|
21
29
|
|
|
22
30
|
Commands:
|
|
23
|
-
vision Image and video analysis (
|
|
24
|
-
search Real-time web search
|
|
25
|
-
read Fetch and parse web pages
|
|
26
|
-
repo GitHub repository exploration
|
|
27
|
-
quota
|
|
28
|
-
tools List available MCP tools
|
|
29
|
-
tool Show a tool schema
|
|
30
|
-
call Call a tool directly
|
|
31
|
-
doctor
|
|
32
|
-
code Execute TypeScript tool chains (Code Mode)
|
|
31
|
+
vision Image and video analysis (Z.AI; MiniMax for interpret-image)
|
|
32
|
+
search Real-time web search (shared: Z.AI + MiniMax)
|
|
33
|
+
read Fetch and parse web pages (Z.AI only)
|
|
34
|
+
repo GitHub repository exploration (Z.AI only)
|
|
35
|
+
quota Provider-aware plan usage (calls remaining, reset time)
|
|
36
|
+
tools List available MCP tools (Z.AI)
|
|
37
|
+
tool Show a tool schema (Z.AI)
|
|
38
|
+
call Call a tool directly (Z.AI)
|
|
39
|
+
doctor Provider-aware environment + connectivity checks
|
|
40
|
+
code Execute TypeScript tool chains (Code Mode, Z.AI)
|
|
41
|
+
|
|
42
|
+
Provider selection (precedence: --provider, then SCOUTLINE_PROVIDER, then zai):
|
|
43
|
+
--provider <zai|minimax> Select the active Provider for shared capabilities
|
|
44
|
+
SCOUTLINE_PROVIDER=<id> Fallback when --provider is not passed
|
|
45
|
+
|
|
46
|
+
Shared capabilities accept --provider. Z.AI-only commands (read, repo,
|
|
47
|
+
tools, tool, call, code) carry the flag but ignore it. Quota and doctor
|
|
48
|
+
report per-Provider; --provider picks the effective Provider for metadata.
|
|
33
49
|
|
|
34
50
|
Global Options:
|
|
35
|
-
--output-format <data|json|pretty|compact|markdown|refs> Output mode (default: data)
|
|
36
|
-
-O <mode>
|
|
51
|
+
--output-format <data|json|pretty|compact|markdown|refs|tty> Output mode (default: data)
|
|
52
|
+
-O <mode> Alias for --output-format
|
|
37
53
|
|
|
38
54
|
Help:
|
|
39
55
|
scoutline --help
|
|
@@ -93,6 +109,7 @@ function extractGlobalOptions(args) {
|
|
|
93
109
|
let outputFormat;
|
|
94
110
|
let forcePretty = false;
|
|
95
111
|
let forceRaw = false;
|
|
112
|
+
let provider;
|
|
96
113
|
for (let i = 0; i < args.length; i += 1) {
|
|
97
114
|
const arg = args[i];
|
|
98
115
|
if (arg === "--output-format" || arg === "-O") {
|
|
@@ -108,90 +125,208 @@ function extractGlobalOptions(args) {
|
|
|
108
125
|
forceRaw = true;
|
|
109
126
|
continue;
|
|
110
127
|
}
|
|
128
|
+
if (arg === "--provider") {
|
|
129
|
+
// Global option: accepted before OR after the command token. It is
|
|
130
|
+
// removed from the rest stream so command-local positional parsing
|
|
131
|
+
// never observes it. Only shared Search resolves/validates it; the
|
|
132
|
+
// Z.AI-only command families carry it but never consult it.
|
|
133
|
+
provider = args[i + 1];
|
|
134
|
+
i += 1;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
111
137
|
rest.push(arg);
|
|
112
138
|
}
|
|
113
|
-
return { outputFormat, forcePretty, forceRaw, rest };
|
|
139
|
+
return { outputFormat, forcePretty, forceRaw, provider, rest };
|
|
114
140
|
}
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
141
|
+
function resolveOutputMode(explicit, forcePretty, forceRaw, adapter) {
|
|
142
|
+
if (explicit !== undefined) {
|
|
143
|
+
if (!isOutputMode(explicit)) {
|
|
144
|
+
throw new ValidationError(`Invalid output format: ${explicit}`, `Use one of: ${OUTPUT_MODES.join(", ")}`);
|
|
145
|
+
}
|
|
146
|
+
return explicit;
|
|
147
|
+
}
|
|
148
|
+
if (forcePretty)
|
|
149
|
+
return "tty";
|
|
150
|
+
if (forceRaw)
|
|
151
|
+
return "data";
|
|
152
|
+
const envMode = adapter.environmentOutputMode;
|
|
153
|
+
if (typeof envMode === "string" && isOutputMode(envMode)) {
|
|
154
|
+
return envMode;
|
|
155
|
+
}
|
|
156
|
+
if (adapter.stdoutIsTTY)
|
|
157
|
+
return "tty";
|
|
158
|
+
return "data";
|
|
124
159
|
}
|
|
125
|
-
async function handleVision(args) {
|
|
160
|
+
async function handleVision(args, outputMode, deps) {
|
|
126
161
|
const { flags, positional } = parseArgs(args);
|
|
127
162
|
if (flags.help || flags.h || positional.length === 0) {
|
|
128
|
-
|
|
129
|
-
return;
|
|
163
|
+
deps.invocation.writeStdout(vision.VISION_HELP);
|
|
164
|
+
return 0;
|
|
130
165
|
}
|
|
131
166
|
const command = positional[0];
|
|
132
167
|
const source = positional[1];
|
|
133
168
|
const prompt = positional[2];
|
|
169
|
+
// Map the subcommand to its Vision operation. Unknown subcommands are
|
|
170
|
+
// a parse-time VALIDATION_ERROR and are rejected before any Provider
|
|
171
|
+
// resolution, support check, or media access.
|
|
172
|
+
const operation = visionOperationForCommand(command);
|
|
173
|
+
// Resolve the effective Provider for Vision (DESIGN.md §6). Invalid
|
|
174
|
+
// explicit/env input fails here with VALIDATION_ERROR before any Vision
|
|
175
|
+
// support check or media access. Selection never consults credentials
|
|
176
|
+
// (FR-003); the configured check below is the caller's responsibility.
|
|
177
|
+
const providerId = resolveProviderId(deps.provider, deps.env);
|
|
178
|
+
const descriptor = getProviderDescriptor(providerId, deps.providerDescriptors);
|
|
179
|
+
// Gate the operation on descriptor metadata BEFORE Adapter construction.
|
|
180
|
+
// An unsupported operation (e.g. MiniMax for any specialized op, diff, or
|
|
181
|
+
// video) fails with UNSUPPORTED_CAPABILITY before credentials, media,
|
|
182
|
+
// transport, cache, or any Z.AI fallback (FR-023, FR-024). No command
|
|
183
|
+
// branches on a Provider ID: the support check alone decides availability.
|
|
184
|
+
const capabilityId = visionOperationToCapability(operation);
|
|
185
|
+
if (!descriptor.capabilities().has(capabilityId)) {
|
|
186
|
+
throw new UnsupportedCapabilityError(providerId, capabilityId);
|
|
187
|
+
}
|
|
188
|
+
// FR-003: selection returns the default zai even when unconfigured. The
|
|
189
|
+
// dispatch layer surfaces a missing credential as ConfigurationError
|
|
190
|
+
// (exit 3), AFTER the capability support check (FR-023) but before any
|
|
191
|
+
// Adapter construction or media access (Fixup A — B5).
|
|
192
|
+
if (!descriptor.isConfigured(deps.env)) {
|
|
193
|
+
throw new ConfigurationError(`Provider "${providerId}" is not configured. Set the required API key.`);
|
|
194
|
+
}
|
|
195
|
+
const adapter = descriptor.create({ env: deps.env });
|
|
196
|
+
const visionCapability = adapter.vision;
|
|
197
|
+
if (!visionCapability || !visionCapability.supports(operation)) {
|
|
198
|
+
throw new UnsupportedCapabilityError(providerId, capabilityId);
|
|
199
|
+
}
|
|
200
|
+
// Vision bypasses the response cache (FR-022). The shared execution
|
|
201
|
+
// primitives (sleep/random) are the same ones Search consumes; they
|
|
202
|
+
// drive retry backoff deterministically under test.
|
|
203
|
+
const visionDeps = {
|
|
204
|
+
capability: visionCapability,
|
|
205
|
+
sleep: deps.searchSleep,
|
|
206
|
+
random: deps.searchRandom,
|
|
207
|
+
};
|
|
134
208
|
switch (command) {
|
|
135
209
|
case "analyze":
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
await vision.analyze(source, prompt);
|
|
140
|
-
break;
|
|
141
|
-
case "ui-to-code":
|
|
142
|
-
if (!source) {
|
|
143
|
-
outputError("Missing image source", "INVALID_ARGS", "Usage: scoutline vision ui-to-code <image> [prompt]");
|
|
144
|
-
}
|
|
210
|
+
return invokeCommand(deps.invocation, (context) => vision.analyze(source, prompt, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
211
|
+
case "ui-to-code": {
|
|
145
212
|
const outputType = flags.output || "code";
|
|
146
|
-
|
|
147
|
-
|
|
213
|
+
return invokeCommand(deps.invocation, (context) => vision.uiToCode(source, prompt, outputType, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
214
|
+
}
|
|
148
215
|
case "extract-text":
|
|
149
|
-
|
|
150
|
-
outputError("Missing image source", "INVALID_ARGS", "Usage: scoutline vision extract-text <image> [prompt] [--language <lang>]");
|
|
151
|
-
}
|
|
152
|
-
await vision.extractText(source, prompt, flags.language);
|
|
153
|
-
break;
|
|
216
|
+
return invokeCommand(deps.invocation, (context) => vision.extractText(source, prompt, flags.language, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
154
217
|
case "diagnose-error":
|
|
155
|
-
|
|
156
|
-
outputError("Missing image source", "INVALID_ARGS", "Usage: scoutline vision diagnose-error <image> [prompt] [--context <ctx>]");
|
|
157
|
-
}
|
|
158
|
-
await vision.diagnoseError(source, prompt, flags.context);
|
|
159
|
-
break;
|
|
218
|
+
return invokeCommand(deps.invocation, (context) => vision.diagnoseError(source, prompt, flags.context, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
160
219
|
case "diagram":
|
|
161
|
-
|
|
162
|
-
outputError("Missing image source", "INVALID_ARGS", "Usage: scoutline vision diagram <image> [prompt] [--type <type>]");
|
|
163
|
-
}
|
|
164
|
-
await vision.diagram(source, prompt, flags.type);
|
|
165
|
-
break;
|
|
220
|
+
return invokeCommand(deps.invocation, (context) => vision.diagram(source, prompt, flags.type, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
166
221
|
case "chart":
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
await vision.chart(source, prompt, flags.focus);
|
|
171
|
-
break;
|
|
172
|
-
case "diff":
|
|
222
|
+
return invokeCommand(deps.invocation, (context) => vision.chart(source, prompt, flags.focus, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
223
|
+
case "diff": {
|
|
173
224
|
const actual = positional[2];
|
|
174
225
|
const diffPrompt = positional[3];
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
await vision.diff(source, actual, diffPrompt);
|
|
179
|
-
break;
|
|
226
|
+
return invokeCommand(deps.invocation, (context) => vision.diff(source, actual, diffPrompt, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
227
|
+
}
|
|
180
228
|
case "video":
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
|
|
229
|
+
return invokeCommand(deps.invocation, (context) => vision.video(source, prompt, visionDeps, context), outputMode, deps.now, deps.secrets);
|
|
230
|
+
default:
|
|
231
|
+
throw new ValidationError(`Unknown vision command: ${command}`, 'Run "scoutline vision --help" for available commands');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Map a Vision subcommand to its discriminated operation id. Used by
|
|
236
|
+
* `handleVision` to gate the operation against descriptor metadata before
|
|
237
|
+
* Adapter construction. Unknown subcommands throw `ValidationError`.
|
|
238
|
+
*/
|
|
239
|
+
function visionOperationForCommand(command) {
|
|
240
|
+
switch (command) {
|
|
241
|
+
case "analyze":
|
|
242
|
+
return "interpret-image";
|
|
243
|
+
case "ui-to-code":
|
|
244
|
+
return "ui-artifact";
|
|
245
|
+
case "extract-text":
|
|
246
|
+
return "extract-text";
|
|
247
|
+
case "diagnose-error":
|
|
248
|
+
return "diagnose-error";
|
|
249
|
+
case "diagram":
|
|
250
|
+
return "diagram";
|
|
251
|
+
case "chart":
|
|
252
|
+
return "chart";
|
|
253
|
+
case "diff":
|
|
254
|
+
return "diff";
|
|
255
|
+
case "video":
|
|
256
|
+
return "video";
|
|
186
257
|
default:
|
|
187
|
-
|
|
258
|
+
throw new ValidationError(`Unknown vision command: ${command}`, 'Run "scoutline vision --help" for available commands');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Parse and validate the `--count` flag value (Fixup C — B11, Fixup D). Per
|
|
263
|
+
* DESIGN.md §7, count must be a safe integer >= 0. Invalid values (NaN,
|
|
264
|
+
* negative, non-integer, Infinity, values above Number.MAX_SAFE_INTEGER)
|
|
265
|
+
* throw `ValidationError` BEFORE any Provider resolution or invocation.
|
|
266
|
+
*
|
|
267
|
+
* Fixup D hardens two gaps:
|
|
268
|
+
* - `--count` without a value parses to `true`; that is a user error,
|
|
269
|
+
* not an absent flag, and now throws VALIDATION_ERROR instead of being
|
|
270
|
+
* silently treated as absent.
|
|
271
|
+
* - Uses `Number.isSafeInteger` instead of `Number.isFinite` +
|
|
272
|
+
* `Number.isInteger` so values above 2^53-1 are rejected rather than
|
|
273
|
+
* silently rounded.
|
|
274
|
+
*
|
|
275
|
+
* Exported for testing so the validation can be exercised without going
|
|
276
|
+
* through the CLI parser (which does not deliver negative numbers as flag
|
|
277
|
+
* values today).
|
|
278
|
+
*/
|
|
279
|
+
export function parseAndValidateCount(raw) {
|
|
280
|
+
if (raw === undefined || raw === "")
|
|
281
|
+
return undefined;
|
|
282
|
+
if (raw === true) {
|
|
283
|
+
throw new ValidationError("Count requires a numeric value.", "Use a non-negative integer (e.g. --count 5).");
|
|
284
|
+
}
|
|
285
|
+
const str = typeof raw === "string" ? raw : String(raw);
|
|
286
|
+
if (!/^\d+$/.test(str)) {
|
|
287
|
+
throw new ValidationError(`Invalid --count value "${str}": must be a non-negative integer`, "Use a non-negative integer (e.g. --count 5).");
|
|
188
288
|
}
|
|
289
|
+
const parsed = Number(str);
|
|
290
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
|
291
|
+
throw new ValidationError(`Invalid --count value "${str}": must be a non-negative safe integer`, "Use a non-negative integer (e.g. --count 5).");
|
|
292
|
+
}
|
|
293
|
+
return parsed;
|
|
189
294
|
}
|
|
190
|
-
async function handleSearch(args) {
|
|
295
|
+
async function handleSearch(args, outputMode, deps) {
|
|
191
296
|
const { flags, positional } = parseArgs(args);
|
|
192
297
|
if (flags.help || flags.h || positional.length === 0) {
|
|
193
|
-
|
|
194
|
-
return;
|
|
298
|
+
deps.invocation.writeStdout(SEARCH_HELP);
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
// Fixup D — B11: validate --count BEFORE Provider resolution and the
|
|
302
|
+
// configured/credential check. A syntax error in a CLI argument must
|
|
303
|
+
// not depend on whether credentials are present: `search q --count nope`
|
|
304
|
+
// with NO credentials must surface VALIDATION_ERROR (exit 1), not
|
|
305
|
+
// CONFIGURATION_ERROR (exit 3). Order: parse global options -> validate
|
|
306
|
+
// count -> resolve provider -> check configured -> dispatch.
|
|
307
|
+
const count = parseAndValidateCount(flags.count);
|
|
308
|
+
// Resolve the Provider ONLY inside shared Search (DESIGN.md §6). Other
|
|
309
|
+
// command families carry the parsed flag but never resolve or validate
|
|
310
|
+
// it. An invalid explicit/env value throws VALIDATION_ERROR here, before
|
|
311
|
+
// any Adapter construction or invocation. Selection never consults
|
|
312
|
+
// credentials (FR-003); the configured check below is the caller's
|
|
313
|
+
// responsibility (Fixup A — B5).
|
|
314
|
+
const providerId = resolveProviderId(deps.provider, deps.env);
|
|
315
|
+
const descriptor = getProviderDescriptor(providerId, deps.providerDescriptors);
|
|
316
|
+
if (!descriptor.capabilities().has("search")) {
|
|
317
|
+
throw new UnsupportedCapabilityError(providerId, "search");
|
|
318
|
+
}
|
|
319
|
+
// FR-003: selection returns the default zai even when unconfigured. The
|
|
320
|
+
// dispatch layer surfaces a missing credential as ConfigurationError
|
|
321
|
+
// (exit 3), AFTER the capability support check (FR-023) but before any
|
|
322
|
+
// Adapter construction (Fixup A — B5).
|
|
323
|
+
if (!descriptor.isConfigured(deps.env)) {
|
|
324
|
+
throw new ConfigurationError(`Provider "${providerId}" is not configured. Set the required API key.`);
|
|
325
|
+
}
|
|
326
|
+
const adapter = descriptor.create({ env: deps.env });
|
|
327
|
+
const capability = adapter.search;
|
|
328
|
+
if (!capability) {
|
|
329
|
+
throw new UnsupportedCapabilityError(providerId, "search");
|
|
195
330
|
}
|
|
196
331
|
const query = positional.join(" ");
|
|
197
332
|
const fieldsRaw = flags.fields;
|
|
@@ -201,26 +336,33 @@ async function handleSearch(args) {
|
|
|
201
336
|
.map((f) => f.trim())
|
|
202
337
|
.filter(Boolean)
|
|
203
338
|
: undefined;
|
|
204
|
-
|
|
205
|
-
count
|
|
339
|
+
return invokeCommand(deps.invocation, (context) => search(query, {
|
|
340
|
+
count,
|
|
206
341
|
domain: flags.domain,
|
|
207
342
|
recency: flags.recency,
|
|
208
343
|
contentSize: flags["content-size"],
|
|
209
344
|
location: flags.location,
|
|
210
|
-
maxSummary: flags["max-summary"]
|
|
345
|
+
maxSummary: flags["max-summary"]
|
|
346
|
+
? parseInt(flags["max-summary"], 10)
|
|
347
|
+
: undefined,
|
|
211
348
|
fields: fields && fields.length > 0 ? fields : undefined,
|
|
212
349
|
noCache: flags["no-cache"] === true,
|
|
213
350
|
merge: flags.merge === true,
|
|
214
|
-
}
|
|
351
|
+
}, {
|
|
352
|
+
capability,
|
|
353
|
+
cache: deps.searchCache,
|
|
354
|
+
sleep: deps.searchSleep,
|
|
355
|
+
random: deps.searchRandom,
|
|
356
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
215
357
|
}
|
|
216
|
-
async function handleRead(args) {
|
|
358
|
+
async function handleRead(args, outputMode, deps) {
|
|
217
359
|
const { flags, positional } = parseArgs(args);
|
|
218
360
|
if (flags.help || flags.h || positional.length === 0) {
|
|
219
|
-
|
|
220
|
-
return;
|
|
361
|
+
deps.invocation.writeStdout(READ_HELP);
|
|
362
|
+
return 0;
|
|
221
363
|
}
|
|
222
364
|
const url = positional[0];
|
|
223
|
-
|
|
365
|
+
return invokeCommand(deps.invocation, (context) => read(url, {
|
|
224
366
|
format: flags.format,
|
|
225
367
|
noImages: flags["no-images"] === true,
|
|
226
368
|
noCache: flags["no-cache"] === true,
|
|
@@ -231,112 +373,142 @@ async function handleRead(args) {
|
|
|
231
373
|
withImagesSummary: flags["with-images-summary"] === true,
|
|
232
374
|
maxChars: flags["max-chars"] ? parseInt(flags["max-chars"], 10) : undefined,
|
|
233
375
|
fullEnvelope: flags["full-envelope"] === true,
|
|
234
|
-
extract: isExtractMode(flags.extract)
|
|
235
|
-
|
|
376
|
+
extract: isExtractMode(flags.extract)
|
|
377
|
+
? flags.extract
|
|
378
|
+
: undefined,
|
|
379
|
+
}, outputMode, context), outputMode, deps.now, deps.secrets);
|
|
236
380
|
}
|
|
237
|
-
async function handleRepo(args) {
|
|
381
|
+
async function handleRepo(args, outputMode, deps) {
|
|
238
382
|
const { flags, positional } = parseArgs(args);
|
|
239
383
|
if (flags.help || flags.h || positional.length === 0) {
|
|
240
|
-
|
|
241
|
-
return;
|
|
384
|
+
deps.invocation.writeStdout(REPO_HELP);
|
|
385
|
+
return 0;
|
|
242
386
|
}
|
|
243
387
|
const command = positional[0];
|
|
244
388
|
const repo = positional[1];
|
|
245
389
|
switch (command) {
|
|
246
|
-
case "search":
|
|
390
|
+
case "search": {
|
|
247
391
|
const query = positional.slice(2).join(" ");
|
|
248
392
|
if (!repo || !query) {
|
|
249
|
-
|
|
393
|
+
throw new ValidationError("Missing repo or query", "Usage: scoutline repo search <owner/repo> <query>");
|
|
250
394
|
}
|
|
251
|
-
|
|
395
|
+
return invokeCommand(deps.invocation, (context) => repoSearch(repo, query, {
|
|
252
396
|
language: flags.language,
|
|
253
397
|
maxChars: flags["max-chars"] ? parseInt(flags["max-chars"], 10) : undefined,
|
|
254
398
|
noCache: flags["no-cache"] === true,
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
case "tree":
|
|
399
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
400
|
+
}
|
|
401
|
+
case "tree": {
|
|
258
402
|
if (!repo) {
|
|
259
|
-
|
|
403
|
+
throw new ValidationError("Missing repo", "Usage: scoutline repo tree <owner/repo>");
|
|
260
404
|
}
|
|
261
|
-
|
|
405
|
+
return invokeCommand(deps.invocation, (context) => repoTree(repo, {
|
|
262
406
|
path: flags.path,
|
|
263
407
|
depth: flags.depth ? parseInt(flags.depth, 10) : undefined,
|
|
264
408
|
noCache: flags["no-cache"] === true,
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
case "read":
|
|
409
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
410
|
+
}
|
|
411
|
+
case "read": {
|
|
268
412
|
const path = positional[2];
|
|
269
413
|
if (!repo || !path) {
|
|
270
|
-
|
|
414
|
+
throw new ValidationError("Missing repo or path", "Usage: scoutline repo read <owner/repo> <path>");
|
|
271
415
|
}
|
|
272
|
-
|
|
416
|
+
return invokeCommand(deps.invocation, (context) => repoRead(repo, path, {
|
|
273
417
|
maxChars: flags["max-chars"] ? parseInt(flags["max-chars"], 10) : undefined,
|
|
274
418
|
noCache: flags["no-cache"] === true,
|
|
275
|
-
});
|
|
276
|
-
|
|
419
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
420
|
+
}
|
|
277
421
|
default:
|
|
278
|
-
|
|
422
|
+
throw new ValidationError(`Unknown repo command: ${command}`, 'Run "scoutline repo --help" for available commands');
|
|
279
423
|
}
|
|
280
424
|
}
|
|
281
|
-
async function handleTools(args) {
|
|
425
|
+
async function handleTools(args, outputMode, deps) {
|
|
282
426
|
const { flags } = parseArgs(args);
|
|
283
427
|
if (flags.help || flags.h) {
|
|
284
|
-
|
|
285
|
-
return;
|
|
428
|
+
deps.invocation.writeStdout(TOOLS_HELP);
|
|
429
|
+
return 0;
|
|
286
430
|
}
|
|
287
|
-
|
|
431
|
+
return invokeCommand(deps.invocation, (context) => listTools({
|
|
288
432
|
filter: flags.filter,
|
|
289
433
|
full: flags.full === true,
|
|
290
434
|
typescript: flags.typescript === true || flags.ts === true,
|
|
291
435
|
enableVision: flags.vision !== false,
|
|
292
|
-
});
|
|
436
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
293
437
|
}
|
|
294
|
-
async function handleTool(args) {
|
|
438
|
+
async function handleTool(args, outputMode, deps) {
|
|
295
439
|
const { flags, positional } = parseArgs(args);
|
|
296
440
|
if (flags.help || flags.h || positional.length === 0) {
|
|
297
|
-
|
|
298
|
-
return;
|
|
441
|
+
deps.invocation.writeStdout(TOOLS_HELP);
|
|
442
|
+
return 0;
|
|
299
443
|
}
|
|
300
|
-
|
|
444
|
+
return invokeCommand(deps.invocation, (context) => showTool(positional[0], { enableVision: flags.vision !== false }, context), outputMode, deps.now, deps.secrets);
|
|
301
445
|
}
|
|
302
|
-
async function handleCall(args) {
|
|
446
|
+
async function handleCall(args, outputMode, deps) {
|
|
303
447
|
const { flags, positional } = parseArgs(args);
|
|
304
448
|
if (flags.help || flags.h || positional.length === 0) {
|
|
305
|
-
|
|
306
|
-
return;
|
|
449
|
+
deps.invocation.writeStdout(CALL_HELP);
|
|
450
|
+
return 0;
|
|
307
451
|
}
|
|
308
|
-
|
|
452
|
+
return invokeCommand(deps.invocation, (context) => callTool(positional[0], {
|
|
309
453
|
json: flags.json,
|
|
310
454
|
file: flags.file,
|
|
311
455
|
stdin: flags.stdin === true,
|
|
312
456
|
dryRun: flags["dry-run"] === true,
|
|
313
457
|
enableVision: flags.vision !== false,
|
|
314
|
-
});
|
|
458
|
+
}, context), outputMode, deps.now, deps.secrets);
|
|
315
459
|
}
|
|
316
|
-
async function handleDoctor(args) {
|
|
460
|
+
async function handleDoctor(args, outputMode, deps) {
|
|
317
461
|
const { flags } = parseArgs(args);
|
|
318
462
|
if (flags.help || flags.h) {
|
|
319
|
-
|
|
320
|
-
return;
|
|
463
|
+
deps.invocation.writeStdout(DOCTOR_HELP);
|
|
464
|
+
return 0;
|
|
321
465
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
466
|
+
const noTools = flags["no-tools"] === true;
|
|
467
|
+
// Resolve the effective Provider ID for report metadata, mirroring
|
|
468
|
+
// Search/Vision/quota. Descriptors are intentionally NOT passed here so
|
|
469
|
+
// the report always lists every built-in Provider even when the
|
|
470
|
+
// effective Provider is unconfigured.
|
|
471
|
+
const effectiveProvider = resolveProviderId(deps.provider, deps.env);
|
|
472
|
+
return invokeCommand(deps.invocation, () => doctor({
|
|
473
|
+
buildReport: () => buildDiagnosticsReport({
|
|
474
|
+
noTools,
|
|
475
|
+
effectiveProvider,
|
|
476
|
+
descriptors: deps.providerDescriptors,
|
|
477
|
+
env: deps.env,
|
|
478
|
+
sleep: deps.searchSleep,
|
|
479
|
+
random: deps.searchRandom,
|
|
480
|
+
}),
|
|
481
|
+
}), outputMode, deps.now, deps.secrets);
|
|
326
482
|
}
|
|
327
|
-
async function handleQuota(args) {
|
|
483
|
+
async function handleQuota(args, outputMode, deps) {
|
|
328
484
|
const { flags } = parseArgs(args);
|
|
329
485
|
if (flags.help || flags.h) {
|
|
330
|
-
|
|
331
|
-
return;
|
|
486
|
+
deps.invocation.writeStdout(QUOTA_HELP);
|
|
487
|
+
return 0;
|
|
332
488
|
}
|
|
333
|
-
|
|
489
|
+
const allProviders = flags["all-providers"] === true;
|
|
490
|
+
// Resolve the effective Provider ID for dashboard metadata. Config
|
|
491
|
+
// validation is owned by the dashboard builder (ConfigurationError,
|
|
492
|
+
// exit 3) so an unconfigured default is reported as configuration, not
|
|
493
|
+
// a registry error. Descriptors are intentionally NOT passed here so
|
|
494
|
+
// all-provider mode is not blocked by an unconfigured effective.
|
|
495
|
+
const effectiveProvider = resolveProviderId(deps.provider, deps.env);
|
|
496
|
+
return invokeCommand(deps.invocation, () => quota({
|
|
497
|
+
buildDashboard: () => buildQuotaDashboard({
|
|
498
|
+
allProviders,
|
|
499
|
+
effectiveProvider,
|
|
500
|
+
descriptors: deps.providerDescriptors,
|
|
501
|
+
env: deps.env,
|
|
502
|
+
sleep: deps.searchSleep,
|
|
503
|
+
random: deps.searchRandom,
|
|
504
|
+
}),
|
|
505
|
+
}), outputMode, deps.now, deps.secrets);
|
|
334
506
|
}
|
|
335
|
-
async function handleCode(args) {
|
|
507
|
+
async function handleCode(args, outputMode, deps) {
|
|
336
508
|
const { flags, positional } = parseArgs(args);
|
|
337
509
|
if (flags.help || flags.h || positional.length === 0) {
|
|
338
|
-
|
|
339
|
-
return;
|
|
510
|
+
deps.invocation.writeStdout(CODE_HELP);
|
|
511
|
+
return 0;
|
|
340
512
|
}
|
|
341
513
|
const command = positional[0];
|
|
342
514
|
const timeout = flags.timeout ? parseInt(flags.timeout, 10) : undefined;
|
|
@@ -345,99 +517,109 @@ async function handleCode(args) {
|
|
|
345
517
|
case "run": {
|
|
346
518
|
const filePath = positional[1];
|
|
347
519
|
if (!filePath) {
|
|
348
|
-
|
|
520
|
+
throw new ValidationError("Missing code file", "Usage: scoutline code run <file>");
|
|
349
521
|
}
|
|
350
|
-
|
|
351
|
-
break;
|
|
522
|
+
return invokeCommand(deps.invocation, (context) => runCodeFile(filePath, { timeout, includeLogs }, context), outputMode, deps.now, deps.secrets);
|
|
352
523
|
}
|
|
353
524
|
case "eval": {
|
|
354
525
|
const code = positional.slice(1).join(" ");
|
|
355
526
|
if (!code) {
|
|
356
|
-
|
|
527
|
+
throw new ValidationError("Missing code string", "Usage: scoutline code eval <code>");
|
|
357
528
|
}
|
|
358
|
-
|
|
359
|
-
break;
|
|
529
|
+
return invokeCommand(deps.invocation, (context) => evalCode(code, { timeout, includeLogs }, context), outputMode, deps.now, deps.secrets);
|
|
360
530
|
}
|
|
361
531
|
case "interfaces":
|
|
362
|
-
|
|
363
|
-
break;
|
|
532
|
+
return invokeCommand(deps.invocation, (context) => printInterfaces(context), outputMode, deps.now, deps.secrets);
|
|
364
533
|
case "prompt":
|
|
365
|
-
printPromptTemplate();
|
|
366
|
-
break;
|
|
534
|
+
return invokeCommand(deps.invocation, async (context) => printPromptTemplate(context), outputMode, deps.now, deps.secrets);
|
|
367
535
|
default:
|
|
368
|
-
|
|
536
|
+
throw new ValidationError(`Unknown code command: ${command}`, 'Run "scoutline code --help" for available commands');
|
|
369
537
|
}
|
|
370
538
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
const {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
539
|
+
const realSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
540
|
+
export async function main(args, dependencies) {
|
|
541
|
+
const { invocation, env, now } = dependencies;
|
|
542
|
+
const providerDescriptors = dependencies.providerDescriptors ?? BUILT_IN_PROVIDER_DESCRIPTORS;
|
|
543
|
+
const searchCache = dependencies.searchCache ?? defaultResponseCache;
|
|
544
|
+
const searchSleep = dependencies.searchSleep ?? realSleep;
|
|
545
|
+
const searchRandom = dependencies.searchRandom ?? Math.random;
|
|
546
|
+
// Resolve configured Provider credentials from the INJECTED env (B3) so
|
|
547
|
+
// redaction follows the same environment the handlers see — a secret
|
|
548
|
+
// that exists only in MainDependencies.env is still redacted from output.
|
|
549
|
+
const secrets = configuredSecrets(env);
|
|
550
|
+
const { outputFormat, forcePretty, forceRaw, provider, rest } = extractGlobalOptions([...args]);
|
|
551
|
+
// Fixup C — B10: resolve the output mode BEFORE the dispatch try/catch.
|
|
552
|
+
// An invalid explicit mode still surfaces as a typed ValidationError,
|
|
553
|
+
// but the surface formatter uses the user's REQUESTED mode (or the
|
|
554
|
+
// best deterministic fallback) so the envelope matches what the user
|
|
555
|
+
// asked for. Pre-invocation validation errors (provider resolution,
|
|
556
|
+
// missing credentials, count parsing, etc.) MUST honour the requested
|
|
557
|
+
// output mode the same way handler errors do.
|
|
558
|
+
let outputMode;
|
|
559
|
+
try {
|
|
560
|
+
outputMode = resolveOutputMode(outputFormat, forcePretty ?? false, forceRaw ?? false, invocation);
|
|
561
|
+
}
|
|
562
|
+
catch (error) {
|
|
563
|
+
// The explicit mode is invalid — fall back to a deterministic
|
|
564
|
+
// compact form so we can still surface a structured error envelope.
|
|
565
|
+
invocation.writeStderr(formatErrorOutput(error, "data", secrets));
|
|
566
|
+
return getErrorExitCode(error);
|
|
567
|
+
}
|
|
388
568
|
if (rest.length === 0 || rest[0] === "--help" || rest[0] === "-h") {
|
|
389
|
-
|
|
390
|
-
return;
|
|
569
|
+
invocation.writeStdout(MAIN_HELP);
|
|
570
|
+
return 0;
|
|
391
571
|
}
|
|
392
572
|
if (rest[0] === "--version" || rest[0] === "-v") {
|
|
393
|
-
|
|
394
|
-
return;
|
|
573
|
+
invocation.writeStdout(VERSION);
|
|
574
|
+
return 0;
|
|
395
575
|
}
|
|
396
576
|
const command = rest[0];
|
|
397
577
|
const commandArgs = rest.slice(1);
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
578
|
+
const handlerDeps = {
|
|
579
|
+
invocation,
|
|
580
|
+
env,
|
|
581
|
+
secrets,
|
|
582
|
+
now,
|
|
583
|
+
provider,
|
|
584
|
+
providerDescriptors,
|
|
585
|
+
searchCache,
|
|
586
|
+
searchSleep,
|
|
587
|
+
searchRandom,
|
|
588
|
+
};
|
|
589
|
+
try {
|
|
590
|
+
switch (command) {
|
|
591
|
+
case "vision":
|
|
592
|
+
return await handleVision(commandArgs, outputMode, handlerDeps);
|
|
593
|
+
case "search":
|
|
594
|
+
return await handleSearch(commandArgs, outputMode, handlerDeps);
|
|
595
|
+
case "read":
|
|
596
|
+
return await handleRead(commandArgs, outputMode, handlerDeps);
|
|
597
|
+
case "repo":
|
|
598
|
+
return await handleRepo(commandArgs, outputMode, handlerDeps);
|
|
599
|
+
case "tools":
|
|
600
|
+
return await handleTools(commandArgs, outputMode, handlerDeps);
|
|
601
|
+
case "tool":
|
|
602
|
+
return await handleTool(commandArgs, outputMode, handlerDeps);
|
|
603
|
+
case "call":
|
|
604
|
+
return await handleCall(commandArgs, outputMode, handlerDeps);
|
|
605
|
+
case "doctor":
|
|
606
|
+
return await handleDoctor(commandArgs, outputMode, handlerDeps);
|
|
607
|
+
case "quota":
|
|
608
|
+
return await handleQuota(commandArgs, outputMode, handlerDeps);
|
|
609
|
+
case "code":
|
|
610
|
+
return await handleCode(commandArgs, outputMode, handlerDeps);
|
|
611
|
+
default:
|
|
612
|
+
invocation.writeStderr(formatErrorOutput(new ValidationError(`Unknown command: ${command}`, 'Run "scoutline --help" for available commands'), outputMode, secrets));
|
|
613
|
+
return 1;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
// Fixup C — B10: pre-invocation validation errors (provider
|
|
618
|
+
// resolution, missing credential, count parsing, etc.) MUST be
|
|
619
|
+
// formatted in the resolved output mode — they used to be hardcoded
|
|
620
|
+
// to "data" regardless of what the user asked for.
|
|
621
|
+
invocation.writeStderr(formatErrorOutput(error, outputMode, secrets));
|
|
622
|
+
return getErrorExitCode(error);
|
|
431
623
|
}
|
|
432
624
|
}
|
|
433
|
-
main(process.argv)
|
|
434
|
-
.then(() => process.exit(0))
|
|
435
|
-
.catch((error) => {
|
|
436
|
-
console.error(JSON.stringify({
|
|
437
|
-
success: false,
|
|
438
|
-
error: error.message,
|
|
439
|
-
code: "FATAL_ERROR",
|
|
440
|
-
}, null, 2));
|
|
441
|
-
process.exit(1);
|
|
442
|
-
});
|
|
443
625
|
//# sourceMappingURL=index.js.map
|