argsbarg 6.1.2 → 6.1.3
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 +65 -1
- package/README.md +17 -19
- package/bin/argsbarg +10 -0
- package/docs/README.md +4 -3
- package/docs/ai-skills.md +4 -2
- package/docs/bundled-docs.md +50 -25
- package/docs/cli-program.md +52 -10
- package/docs/config-schema.md +10 -11
- package/docs/configure.md +2 -0
- package/docs/decisions.md +40 -0
- package/docs/developing.md +43 -5
- package/docs/http-server.md +171 -0
- package/docs/json-schema-subset.md +51 -0
- package/docs/mcp.md +4 -2
- package/docs/output-schema.md +55 -62
- package/examples/formats.ts +6 -6
- package/examples/full-example/Formula/full-example.rb +35 -0
- package/examples/full-example/README.md +20 -21
- package/examples/full-example/docs/README.md +1 -1
- package/examples/full-example/docs/cli-schema.json +1790 -98
- package/examples/full-example/docs/cli.md +1990 -0
- package/examples/full-example/docs/http.md +28 -29
- package/examples/full-example/docs/mcp.md +8 -22
- package/examples/full-example/docs/openapi.json +783 -50
- package/examples/full-example/docs/skill.md +10 -10
- package/examples/full-example/justfile +11 -1
- package/examples/full-example/src/commands/render-json/__generated__/RenderJsonInputSchema.json +15 -0
- package/examples/full-example/src/commands/render-json/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/render-json/command.test.ts +46 -0
- package/examples/full-example/src/commands/render-json/command.ts +30 -0
- package/examples/full-example/src/commands/render-json/types.ts +9 -0
- package/examples/full-example/src/commands/status/__generated__/StatusJsonOutputSchema.json +15 -0
- package/examples/full-example/src/commands/status/__generated__/index.ts +2 -2
- package/examples/full-example/src/commands/status/command.ts +5 -13
- package/examples/full-example/src/commands/status/types.ts +1 -14
- package/examples/full-example/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +15 -0
- package/examples/full-example/src/commands/workspaces/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/workspaces/command.test.ts +58 -0
- package/examples/full-example/src/commands/workspaces/command.ts +94 -0
- package/examples/full-example/src/commands/workspaces/types.ts +6 -0
- package/examples/full-example/src/db/index.test.ts +86 -0
- package/examples/full-example/src/db/index.ts +101 -0
- package/examples/full-example/src/db/migrate.test.ts +35 -0
- package/examples/full-example/src/db/migrate.ts +69 -0
- package/examples/full-example/src/db/migrations/001_workspaces.sql +6 -0
- package/examples/full-example/src/db/tables/workspaces.ts +66 -0
- package/examples/full-example/src/program.ts +11 -36
- package/examples/full-example/src/types/argsbarg.d.ts +11 -0
- package/examples/full-example/src/types/md.d.ts +4 -0
- package/examples/full-example/tsconfig.json +5 -2
- package/examples/mcp-test.ts +1 -2
- package/examples/minimal.ts +1 -7
- package/examples/nested.ts +1 -2
- package/examples/option-required.ts +1 -1
- package/examples/servers.ts +4 -5
- package/index.d.ts +431 -136
- package/package.json +19 -2
- package/src/builtins/builtins.test.ts +7 -7
- package/src/builtins/completion-bash.ts +1 -1
- package/src/builtins/completion-fish.ts +1 -1
- package/src/builtins/completion-group.ts +4 -4
- package/src/builtins/completion-simulate-shared.ts +9 -0
- package/src/builtins/completion-zsh.ts +1 -1
- package/src/builtins/config.test.ts +3 -3
- package/src/builtins/config.ts +9 -9
- package/src/builtins/configure-copy.ts +2 -2
- package/src/builtins/configure.ts +4 -4
- package/src/builtins/dispatch.ts +19 -18
- package/src/builtins/export.ts +7 -5
- package/src/builtins/http.ts +68 -0
- package/src/builtins/mcp.ts +28 -4
- package/src/builtins/presentation.ts +6 -6
- package/src/builtins/registry.ts +6 -6
- package/src/builtins/scopes.ts +2 -2
- package/src/builtins/version.ts +1 -1
- package/src/cli-tool/full-example-capabilities.test.ts +10 -15
- package/src/cli-tool/main.ts +1 -1
- package/src/cli-tool/program.ts +3 -2
- package/src/cli-tool/prompt.ts +1 -1
- package/src/cli-tool/run-schemagen.ts +1 -3
- package/src/cli-tool/schemagen/cleanup.ts +6 -7
- package/src/cli-tool/schemagen/discover-schema-roots.ts +66 -120
- package/src/cli-tool/schemagen/index.ts +2 -2
- package/src/cli-tool/schemagen/names.ts +8 -13
- package/src/cli-tool/schemagen/run.ts +21 -28
- package/src/cli-tool/schemagen/schemagen.test.ts +136 -46
- package/src/config/bindings.test.ts +1 -1
- package/src/config/bindings.ts +1 -1
- package/src/config/bootstrap.test.ts +1 -1
- package/src/config/bootstrap.ts +36 -4
- package/src/config/context.test.ts +1 -1
- package/src/config/context.ts +1 -1
- package/src/config/entry.ts +1 -1
- package/src/config/file.test.ts +1 -1
- package/src/config/file.ts +3 -3
- package/src/config/manifest.ts +1 -1
- package/src/config/resolve.test.ts +1 -1
- package/src/config/resolve.ts +1 -1
- package/src/config/schema.ts +1 -1
- package/src/config/validate.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.test.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.ts +1 -1
- package/src/{install → configure/artifacts}/gh-release-update.ts +1 -1
- package/src/{install → configure/artifacts}/install-validate.test.ts +3 -3
- package/src/{install → configure/artifacts}/mcp-config.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.test.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.ts +1 -1
- package/src/{install → configure/artifacts}/paths.ts +5 -5
- package/src/configure/artifacts/plan.ts +24 -0
- package/src/{install → configure/artifacts}/status.test.ts +1 -1
- package/src/{install → configure/artifacts}/status.ts +2 -2
- package/src/{install → configure/artifacts}/target-base.ts +1 -1
- package/src/{install → configure/artifacts}/target-detect.ts +1 -1
- package/src/{install → configure/artifacts}/target-effective.ts +3 -9
- package/src/{install → configure/artifacts}/target-mcp-cli.ts +1 -1
- package/src/{install → configure/artifacts}/target-mcp-json.ts +1 -1
- package/src/{install → configure/artifacts}/target-plan-build.ts +2 -2
- package/src/{install → configure/artifacts}/target-registry.ts +2 -2
- package/src/{install → configure/artifacts}/target-scope.ts +3 -3
- package/src/{install → configure/artifacts}/target-skill.ts +1 -1
- package/src/{install → configure/artifacts}/target-types.ts +2 -2
- package/src/{install → configure/artifacts}/targets/app.ts +5 -5
- package/src/{install → configure/artifacts}/targets/chatgpt-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-code-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-desktop-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/configure.ts +5 -5
- package/src/{install → configure/artifacts}/targets/cursor-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/cursor-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/index.ts +1 -1
- package/src/{install → configure/artifacts}/targets/openclaw-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/openclaw-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets/opencode-mcp.ts +5 -5
- package/src/{install → configure/artifacts}/targets/opencode-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets.test.ts +1 -1
- package/src/{install → configure/artifacts}/uninstall.ts +1 -1
- package/src/configure/configure.test.ts +11 -11
- package/src/configure/index.ts +14 -14
- package/src/configure/prompt.ts +2 -2
- package/src/{context.ts → core/context.ts} +26 -20
- package/src/{json-leaf.test.ts → core/json-leaf.test.ts} +4 -4
- package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
- package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +16 -12
- package/src/{parse.test.ts → core/parse.test.ts} +97 -109
- package/src/{parse.ts → core/parse.ts} +129 -31
- package/src/{schema.ts → core/schema.ts} +25 -13
- package/src/{types.ts → core/types.ts} +225 -35
- package/src/{validate.ts → core/validate.ts} +39 -29
- package/src/docs/builtin.ts +8 -19
- package/src/docs/{api-guide.test.ts → cli-guide.test.ts} +21 -21
- package/src/docs/{api-guide.ts → cli-guide.ts} +45 -16
- package/src/docs/docs.test.ts +76 -41
- package/src/docs/http-guide.ts +37 -34
- package/src/docs/mcp-guide.ts +12 -14
- package/src/docs/mcp-resources.test.ts +2 -3
- package/src/docs/mcp-resources.ts +6 -11
- package/src/docs/resolve.ts +22 -30
- package/src/docs/save.ts +3 -3
- package/src/exports/cli.ts +47 -0
- package/src/exports/headless.ts +13 -0
- package/src/exports/http.ts +6 -0
- package/src/exports/mcp.ts +6 -0
- package/src/{headless.test.ts → headless/routing.test.ts} +3 -3
- package/src/{headless.ts → headless/routing.ts} +3 -3
- package/src/headless/tool-call.ts +114 -46
- package/src/help.test.ts +152 -0
- package/src/help.ts +3 -3
- package/src/hooks/builtin.ts +20 -0
- package/src/hooks/run.ts +142 -0
- package/src/http/openapi.ts +182 -0
- package/src/http/readiness.ts +78 -0
- package/src/{api → http}/result.ts +16 -5
- package/src/http/routes.ts +329 -0
- package/src/http/server.ts +225 -0
- package/src/index.ts +36 -25
- package/src/log/ecs.test.ts +43 -0
- package/src/log/ecs.ts +59 -0
- package/src/log/emitter.ts +166 -0
- package/src/mcp/bundle.ts +2 -2
- package/src/mcp/claude.test.ts +1 -1
- package/src/mcp/claude.ts +4 -4
- package/src/{hidden-mcpb.test.ts → mcp/hidden-mcpb.test.ts} +10 -9
- package/src/mcp/result.ts +2 -2
- package/src/mcp/server.ts +54 -6
- package/src/mcp/tools.ts +9 -20
- package/src/{capabilities.ts → runtime/capabilities.ts} +11 -11
- package/src/{cli-errors.ts → runtime/cli-errors.ts} +4 -4
- package/src/{cli.ts → runtime/cli.ts} +159 -49
- package/src/runtime/exposure.ts +102 -0
- package/src/{invoke.test.ts → runtime/invoke.test.ts} +31 -7
- package/src/server/context.ts +25 -0
- package/src/server/overrides.ts +112 -0
- package/src/skill/generate.ts +8 -8
- package/src/skill/hint.ts +1 -1
- package/src/skill/install.ts +2 -2
- package/src/skill/naming.ts +1 -1
- package/src/{test-fixtures.ts → test/fixtures.ts} +3 -2
- package/src/{config.integration.test.ts → test/integration/config.test.ts} +8 -8
- package/src/{api.integration.test.ts → test/integration/http.test.ts} +170 -67
- package/src/{mcp.integration.test.ts → test/integration/mcp.test.ts} +11 -57
- package/docs/api-server.md +0 -141
- package/examples/full-example/docs/api.md +0 -511
- package/examples/full-example/src/commands/status/__generated__/outputSchema.json +0 -28
- package/examples/full-example/src/config/__generated__/configSchema.json +0 -40
- package/examples/full-example/src/config/__generated__/index.ts +0 -5
- package/examples/full-example/src/config/types.ts +0 -24
- package/src/api/openapi.ts +0 -117
- package/src/api/server.ts +0 -120
- package/src/builtins/api.ts +0 -38
- package/src/hidden.ts +0 -30
- package/src/install/plan.ts +0 -53
- /package/src/{install → configure/artifacts}/detect-installed.ts +0 -0
- /package/src/{install → configure/artifacts}/gh-release-update.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize-uninstall.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize.ts +0 -0
- /package/src/{install → configure/artifacts}/opts.ts +0 -0
- /package/src/{install → configure/artifacts}/shell.ts +0 -0
- /package/src/{formats.test.ts → core/formats.test.ts} +0 -0
- /package/src/{formats.ts → core/formats.ts} +0 -0
- /package/src/{respond.ts → core/respond.ts} +0 -0
- /package/src/{types.test.ts → core/types.test.ts} +0 -0
- /package/src/{api → http}/schema-deref.test.ts +0 -0
- /package/src/{api → http}/schema-deref.ts +0 -0
|
@@ -2,30 +2,46 @@
|
|
|
2
2
|
Runtime entry point: validate program, cache derived state, run / invoke / MCP serve.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
import { format } from "node:util";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { builtinInterceptRoot, dispatchBuiltin } from "~/builtins/dispatch.ts";
|
|
8
|
+
import { cliParseRoot, cliPresentationRoot } from "~/builtins/presentation.ts";
|
|
9
|
+
import { bootstrapAppConfig, type EnsureAppConfigOpts, ensureAppConfig } from "~/config/bootstrap.ts";
|
|
10
|
+
import { type AnyAppConfigSnapshot, createAppConfigSnapshot } from "~/config/context.ts";
|
|
11
|
+
import { readAppConfigFileRaw, resolveAppConfigPath } from "~/config/file.ts";
|
|
12
|
+
import { effectiveJsonSchema } from "~/config/schema.ts";
|
|
13
|
+
import { CliContext } from "~/core/context.ts";
|
|
14
|
+
import { LeafInputError, preloadPipableJson } from "~/core/leaf-inputs.ts";
|
|
15
|
+
import { ParseKind, type ParseResult, parse, postParseValidate } from "~/core/parse.ts";
|
|
16
|
+
import { type CliSchemaRootExport, cliSchemaExport } from "~/core/schema.ts";
|
|
17
|
+
import type {
|
|
18
|
+
CliHandler,
|
|
19
|
+
CliInvocation,
|
|
20
|
+
CliLeaf,
|
|
21
|
+
CliLocals,
|
|
22
|
+
CliNode,
|
|
23
|
+
CliProgram,
|
|
24
|
+
CliRespondOptions,
|
|
25
|
+
CliRouter,
|
|
26
|
+
InvokeFailureKind,
|
|
27
|
+
} from "~/core/types.ts";
|
|
28
|
+
import { isCliLeaf, isCliRouter } from "~/core/types.ts";
|
|
29
|
+
import { cliValidateProgram } from "~/core/validate.ts";
|
|
30
|
+
import { cliHelpRender } from "~/help.ts";
|
|
31
|
+
import { isBuiltinInvokePath } from "~/hooks/builtin.ts";
|
|
32
|
+
import { buildInvokeHookContext, classifyFailureKind, runErrorPipeline, runHook } from "~/hooks/run.ts";
|
|
33
|
+
import { httpServeHttp } from "~/http/server.ts";
|
|
34
|
+
import { LogEmitter } from "~/log/emitter.ts";
|
|
35
|
+
import { bootstrapMcpEnv } from "~/mcp/env.ts";
|
|
36
|
+
import { mcpServeStdioLoop } from "~/mcp/server.ts";
|
|
37
|
+
import { createServerRuntime, type ServerHandleContext } from "~/server/context.ts";
|
|
38
|
+
import { resolveHttpServeConfig, resolveMcpServeConfig, type ServeOverrides } from "~/server/overrides.ts";
|
|
9
39
|
import {
|
|
10
40
|
assertBuiltinAllowed,
|
|
11
41
|
type CliCapabilities,
|
|
12
42
|
resolveCapabilities,
|
|
13
43
|
skipsRequiredAppConfigExit,
|
|
14
44
|
} from "./capabilities.ts";
|
|
15
|
-
import { bootstrapAppConfig, type EnsureAppConfigOpts, ensureAppConfig } from "./config/bootstrap.ts";
|
|
16
|
-
import { type AnyAppConfigSnapshot, createAppConfigSnapshot } from "./config/context.ts";
|
|
17
|
-
import { readAppConfigFileRaw, resolveAppConfigPath } from "./config/file.ts";
|
|
18
|
-
import { effectiveJsonSchema } from "./config/schema.ts";
|
|
19
|
-
import { CliContext } from "./context.ts";
|
|
20
|
-
import { cliHelpRender } from "./help.ts";
|
|
21
|
-
import { LeafInputError, preloadPipableJson } from "./leaf-inputs.ts";
|
|
22
|
-
import { bootstrapMcpEnv } from "./mcp/env.ts";
|
|
23
|
-
import { mcpServeStdioLoop } from "./mcp/server.ts";
|
|
24
|
-
import { ParseKind, type ParseResult, parse, postParseValidate } from "./parse.ts";
|
|
25
|
-
import { type CliSchemaExport, cliSchemaExport } from "./schema.ts";
|
|
26
|
-
import type { CliHandler, CliInvocation, CliLeaf, CliNode, CliProgram, CliRespondOptions, CliRouter } from "./types.ts";
|
|
27
|
-
import { isCliLeaf, isCliRouter } from "./types.ts";
|
|
28
|
-
import { cliValidateProgram } from "./validate.ts";
|
|
29
45
|
|
|
30
46
|
/** Outcome of a non-exiting CLI invocation. */
|
|
31
47
|
export type CliInvokeKind = "ok" | "help" | "error";
|
|
@@ -37,6 +53,8 @@ export interface CliInvokeResult {
|
|
|
37
53
|
stdout: string;
|
|
38
54
|
stderr: string;
|
|
39
55
|
errorMsg?: string;
|
|
56
|
+
/** Classified failure for HTTP/MCP status mapping. */
|
|
57
|
+
failureKind?: InvokeFailureKind;
|
|
40
58
|
/** Headless response payload when invocation is `api` or `mcp` and the handler succeeded. */
|
|
41
59
|
response?: CliRespondOptions;
|
|
42
60
|
}
|
|
@@ -66,6 +84,8 @@ export class Cli {
|
|
|
66
84
|
private readonly parseRootMerged: CliRouter;
|
|
67
85
|
private readonly presentationRoot: CliRouter;
|
|
68
86
|
private _appConfig?: AnyAppConfigSnapshot;
|
|
87
|
+
/** Active HTTP/MCP server handle (set during serve). */
|
|
88
|
+
server?: ServerHandleContext;
|
|
69
89
|
|
|
70
90
|
constructor(program: CliProgram) {
|
|
71
91
|
cliValidateProgram(program);
|
|
@@ -86,7 +106,7 @@ export class Cli {
|
|
|
86
106
|
return this._appConfig;
|
|
87
107
|
}
|
|
88
108
|
|
|
89
|
-
exportCommandSchema():
|
|
109
|
+
exportCommandSchema(): CliSchemaRootExport {
|
|
90
110
|
return cliSchemaExport(this.program);
|
|
91
111
|
}
|
|
92
112
|
|
|
@@ -148,6 +168,8 @@ export class Cli {
|
|
|
148
168
|
snapshot,
|
|
149
169
|
undefined,
|
|
150
170
|
preloadedJson,
|
|
171
|
+
pr.pathParams,
|
|
172
|
+
{ requestId: randomUUID() } as CliLocals,
|
|
151
173
|
);
|
|
152
174
|
try {
|
|
153
175
|
this.ensureValidatedLeafInputs(ctx, leaf);
|
|
@@ -169,7 +191,13 @@ export class Cli {
|
|
|
169
191
|
|
|
170
192
|
async invoke(
|
|
171
193
|
argv: string[],
|
|
172
|
-
opts?: {
|
|
194
|
+
opts?: {
|
|
195
|
+
invocation?: CliInvocation;
|
|
196
|
+
toolArgs?: Record<string, unknown>;
|
|
197
|
+
requestId?: string;
|
|
198
|
+
http?: { request: Request; clientIp: string; requestId: string };
|
|
199
|
+
mcp?: { rpcMethod: string; toolName?: string; requestId: string };
|
|
200
|
+
},
|
|
173
201
|
): Promise<CliInvokeResult> {
|
|
174
202
|
const invocation = opts?.invocation ?? "mcp";
|
|
175
203
|
const prep = this.prepareDispatch(argv, { presentationFallback: true });
|
|
@@ -181,6 +209,7 @@ export class Cli {
|
|
|
181
209
|
stdout: "",
|
|
182
210
|
stderr: "",
|
|
183
211
|
errorMsg: "Help is not available via tool calls.",
|
|
212
|
+
failureKind: "help",
|
|
184
213
|
};
|
|
185
214
|
}
|
|
186
215
|
return {
|
|
@@ -189,6 +218,7 @@ export class Cli {
|
|
|
189
218
|
stdout: "",
|
|
190
219
|
stderr: prep.error.errorMsg,
|
|
191
220
|
errorMsg: prep.error.errorMsg,
|
|
221
|
+
failureKind: "validation",
|
|
192
222
|
};
|
|
193
223
|
}
|
|
194
224
|
|
|
@@ -198,6 +228,8 @@ export class Cli {
|
|
|
198
228
|
exitOnMissing: false,
|
|
199
229
|
});
|
|
200
230
|
|
|
231
|
+
const runtime = this.server?.runtime;
|
|
232
|
+
const requestId = opts?.requestId ?? opts?.http?.requestId ?? opts?.mcp?.requestId ?? randomUUID();
|
|
201
233
|
const ctx = new CliContext(
|
|
202
234
|
this.program.key,
|
|
203
235
|
pr.path,
|
|
@@ -207,8 +239,30 @@ export class Cli {
|
|
|
207
239
|
invocation,
|
|
208
240
|
snapshot,
|
|
209
241
|
opts?.toolArgs,
|
|
242
|
+
{},
|
|
243
|
+
pr.pathParams,
|
|
244
|
+
{ requestId } as CliLocals,
|
|
245
|
+
runtime,
|
|
210
246
|
);
|
|
211
247
|
|
|
248
|
+
const skipHooks = isBuiltinInvokePath(pr.path);
|
|
249
|
+
const hooks = this.program.hooks;
|
|
250
|
+
const obscureUnexpected =
|
|
251
|
+
invocation === "http"
|
|
252
|
+
? (this.server?.http?.obscureUnexpected ?? this.program.httpServer?.errors?.obscureUnexpected ?? false)
|
|
253
|
+
: invocation === "mcp"
|
|
254
|
+
? (this.server?.mcp?.obscureUnexpected ?? this.program.mcpServer?.errors?.obscureUnexpected ?? false)
|
|
255
|
+
: false;
|
|
256
|
+
const emitter = this.server?.emitter;
|
|
257
|
+
|
|
258
|
+
const hookCtx = () =>
|
|
259
|
+
buildInvokeHookContext(ctx, {
|
|
260
|
+
path: pr.path,
|
|
261
|
+
runtime,
|
|
262
|
+
http: opts?.http,
|
|
263
|
+
mcp: opts?.mcp,
|
|
264
|
+
});
|
|
265
|
+
|
|
212
266
|
let stdout = "";
|
|
213
267
|
let stderr = "";
|
|
214
268
|
const origExit = process.exit;
|
|
@@ -252,6 +306,33 @@ export class Cli {
|
|
|
252
306
|
stderr += `${format(...args)}\n`;
|
|
253
307
|
};
|
|
254
308
|
|
|
309
|
+
const finishError = async (
|
|
310
|
+
err: unknown,
|
|
311
|
+
kindOpts: Parameters<typeof classifyFailureKind>[1],
|
|
312
|
+
): Promise<CliInvokeResult> => {
|
|
313
|
+
const failureKind = classifyFailureKind(err, kindOpts);
|
|
314
|
+
if (!skipHooks) {
|
|
315
|
+
const piped = await runErrorPipeline(hookCtx(), err, failureKind, hooks, emitter, obscureUnexpected);
|
|
316
|
+
return {
|
|
317
|
+
kind: "error",
|
|
318
|
+
exitCode: piped.clientError.exitCode ?? 1,
|
|
319
|
+
stdout,
|
|
320
|
+
stderr: `${piped.errorMsg}\n`,
|
|
321
|
+
errorMsg: piped.errorMsg,
|
|
322
|
+
failureKind: piped.failureKind,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
326
|
+
return {
|
|
327
|
+
kind: "error",
|
|
328
|
+
exitCode: 1,
|
|
329
|
+
stdout,
|
|
330
|
+
stderr: `${message}\n`,
|
|
331
|
+
errorMsg: message,
|
|
332
|
+
failureKind,
|
|
333
|
+
};
|
|
334
|
+
};
|
|
335
|
+
|
|
255
336
|
try {
|
|
256
337
|
if (pr.kind === ParseKind.Ok) {
|
|
257
338
|
await dispatchBuiltin(this.program, pr, {
|
|
@@ -260,6 +341,10 @@ export class Cli {
|
|
|
260
341
|
});
|
|
261
342
|
}
|
|
262
343
|
|
|
344
|
+
if (!skipHooks) {
|
|
345
|
+
await runHook(() => hooks?.beforeInvoke?.(hookCtx()), "beforeInvoke");
|
|
346
|
+
}
|
|
347
|
+
|
|
263
348
|
this.ensureValidatedLeafInputs(ctx, leaf);
|
|
264
349
|
const handlerResult = await Promise.resolve(leaf.handler(ctx));
|
|
265
350
|
if (handlerResult !== undefined && ctx.getResponse() === undefined) {
|
|
@@ -267,53 +352,44 @@ export class Cli {
|
|
|
267
352
|
}
|
|
268
353
|
|
|
269
354
|
const response = ctx.getResponse();
|
|
270
|
-
|
|
355
|
+
const okResult: CliInvokeResult = {
|
|
271
356
|
kind: "ok",
|
|
272
357
|
exitCode: 0,
|
|
273
358
|
stdout,
|
|
274
359
|
stderr,
|
|
275
360
|
...(response ? { response } : {}),
|
|
276
361
|
};
|
|
362
|
+
|
|
363
|
+
if (!skipHooks) {
|
|
364
|
+
await runHook(() => hooks?.afterInvoke?.({ ...hookCtx(), result: okResult }), "afterInvoke");
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return okResult;
|
|
277
368
|
} catch (err) {
|
|
278
369
|
if (err instanceof CliInvokeExit) {
|
|
279
370
|
if (err.code === 0) {
|
|
280
371
|
const response = ctx.getResponse();
|
|
281
|
-
|
|
372
|
+
const okResult: CliInvokeResult = {
|
|
282
373
|
kind: "ok",
|
|
283
374
|
exitCode: 0,
|
|
284
375
|
stdout,
|
|
285
376
|
stderr,
|
|
286
377
|
...(response ? { response } : {}),
|
|
287
378
|
};
|
|
379
|
+
if (!skipHooks) {
|
|
380
|
+
await runHook(() => hooks?.afterInvoke?.({ ...hookCtx(), result: okResult }), "afterInvoke");
|
|
381
|
+
}
|
|
382
|
+
return okResult;
|
|
288
383
|
}
|
|
289
|
-
|
|
290
|
-
return { kind: "error", exitCode: err.code, stdout, stderr, errorMsg: msg };
|
|
384
|
+
return finishError(err, {});
|
|
291
385
|
}
|
|
292
386
|
if (err instanceof LeafInputError) {
|
|
293
|
-
return {
|
|
294
|
-
kind: "error",
|
|
295
|
-
exitCode: 1,
|
|
296
|
-
stdout,
|
|
297
|
-
stderr: `${err.message}\n`,
|
|
298
|
-
errorMsg: err.message,
|
|
299
|
-
};
|
|
387
|
+
return finishError(err, { parseError: true });
|
|
300
388
|
}
|
|
301
389
|
if (err instanceof Error) {
|
|
302
|
-
return {
|
|
303
|
-
kind: "error",
|
|
304
|
-
exitCode: 1,
|
|
305
|
-
stdout,
|
|
306
|
-
stderr: `${err.message}\n`,
|
|
307
|
-
errorMsg: err.message,
|
|
308
|
-
};
|
|
390
|
+
return finishError(err, {});
|
|
309
391
|
}
|
|
310
|
-
return {
|
|
311
|
-
kind: "error",
|
|
312
|
-
exitCode: 1,
|
|
313
|
-
stdout,
|
|
314
|
-
stderr: "Unknown error\n",
|
|
315
|
-
errorMsg: "Unknown error",
|
|
316
|
-
};
|
|
392
|
+
return finishError(err, {});
|
|
317
393
|
} finally {
|
|
318
394
|
process.exit = origExit;
|
|
319
395
|
process.stdout.write = origStdoutWrite;
|
|
@@ -325,12 +401,28 @@ export class Cli {
|
|
|
325
401
|
}
|
|
326
402
|
}
|
|
327
403
|
|
|
328
|
-
async serveMcp(): Promise<never> {
|
|
404
|
+
async serveMcp(overrides: ServeOverrides = {}): Promise<never> {
|
|
329
405
|
try {
|
|
330
406
|
if (this.program.mcpServer) {
|
|
331
407
|
bootstrapMcpEnv(this.program.mcpServer);
|
|
332
408
|
}
|
|
333
|
-
|
|
409
|
+
const resolved = resolveMcpServeConfig(this.program, overrides);
|
|
410
|
+
const runtime = createServerRuntime(this.program, "mcp");
|
|
411
|
+
const emitter = new LogEmitter({ program: this.program, resolved: resolved.log });
|
|
412
|
+
this.server = {
|
|
413
|
+
runtime,
|
|
414
|
+
emitter,
|
|
415
|
+
mcp: resolved,
|
|
416
|
+
mcpHooks: this.program.mcpServer?.hooks,
|
|
417
|
+
};
|
|
418
|
+
bootstrapAppConfig(this.program, { validateFile: "soft", runtime, emitter });
|
|
419
|
+
const shutdown = () => {
|
|
420
|
+
emitter.emit({ level: "info", message: "server stopping", action: "server.stop" });
|
|
421
|
+
process.exit(0);
|
|
422
|
+
};
|
|
423
|
+
process.once("SIGINT", shutdown);
|
|
424
|
+
process.once("SIGTERM", shutdown);
|
|
425
|
+
emitter.emitLifecycle(`${this.program.key} ${this.program.version} — MCP ready (stdio)`, "mcp.server.ready");
|
|
334
426
|
await mcpServeStdioLoop(this);
|
|
335
427
|
process.exit(0);
|
|
336
428
|
} catch (err) {
|
|
@@ -343,10 +435,25 @@ export class Cli {
|
|
|
343
435
|
}
|
|
344
436
|
}
|
|
345
437
|
|
|
346
|
-
async
|
|
438
|
+
async serveHttp(overrides: ServeOverrides = {}): Promise<never> {
|
|
347
439
|
try {
|
|
348
|
-
|
|
349
|
-
|
|
440
|
+
const resolved = resolveHttpServeConfig(this.program, overrides);
|
|
441
|
+
const runtime = createServerRuntime(this.program, "http");
|
|
442
|
+
const emitter = new LogEmitter({ program: this.program, resolved: resolved.log });
|
|
443
|
+
this.server = {
|
|
444
|
+
runtime,
|
|
445
|
+
emitter,
|
|
446
|
+
http: resolved,
|
|
447
|
+
httpHooks: this.program.httpServer?.hooks,
|
|
448
|
+
};
|
|
449
|
+
bootstrapAppConfig(this.program, { validateFile: "soft", runtime, emitter });
|
|
450
|
+
const shutdown = () => {
|
|
451
|
+
emitter.emit({ level: "info", message: "server stopping", action: "server.stop" });
|
|
452
|
+
process.exit(0);
|
|
453
|
+
};
|
|
454
|
+
process.once("SIGINT", shutdown);
|
|
455
|
+
process.once("SIGTERM", shutdown);
|
|
456
|
+
await httpServeHttp(this, resolved);
|
|
350
457
|
process.exit(0);
|
|
351
458
|
} catch (err) {
|
|
352
459
|
if (err instanceof Error) {
|
|
@@ -416,6 +523,7 @@ export class Cli {
|
|
|
416
523
|
path: pr.path,
|
|
417
524
|
args: pr.args,
|
|
418
525
|
opts: pr.opts,
|
|
526
|
+
pathParams: pr.pathParams,
|
|
419
527
|
helpExplicit: false,
|
|
420
528
|
helpPath: [],
|
|
421
529
|
errorMsg: msg,
|
|
@@ -432,6 +540,7 @@ export class Cli {
|
|
|
432
540
|
path: pr.path,
|
|
433
541
|
args: pr.args,
|
|
434
542
|
opts: pr.opts,
|
|
543
|
+
pathParams: pr.pathParams,
|
|
435
544
|
helpExplicit: false,
|
|
436
545
|
helpPath: [],
|
|
437
546
|
errorMsg: msg,
|
|
@@ -450,6 +559,7 @@ export class Cli {
|
|
|
450
559
|
path: pr.path,
|
|
451
560
|
args: pr.args,
|
|
452
561
|
opts: pr.opts,
|
|
562
|
+
pathParams: pr.pathParams,
|
|
453
563
|
helpExplicit: false,
|
|
454
564
|
helpPath: [],
|
|
455
565
|
errorMsg: msg,
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Per-surface exposure helpers (cli, http, mcpTool).
|
|
3
|
+
Parsing uses the full tree; presentation/schema/MCP/HTTP discovery use these filters.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CliLeaf, CliNode, CliNodeBase, CliOption } from "~/core/types.ts";
|
|
7
|
+
import { isCliRouter } from "~/core/types.ts";
|
|
8
|
+
|
|
9
|
+
/** True when the node is omitted from CLI help, schema, and completions (still invocable). */
|
|
10
|
+
export function isCliHidden(node: CliNodeBase): boolean {
|
|
11
|
+
return node.cli?.hidden === true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** True when the option is omitted from CLI help, schema, and completions. */
|
|
15
|
+
export function isOptionCliHidden(opt: CliOption): boolean {
|
|
16
|
+
return opt.cli?.hidden === true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** True when the node is omitted from cli-schema export. */
|
|
20
|
+
export function isCliSchemaHidden(node: CliNodeBase): boolean {
|
|
21
|
+
if (node.cli?.schema?.enabled === false) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (node.cli?.schema?.hidden === true) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return isCliHidden(node);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** True when the node is omitted from shell completions. */
|
|
31
|
+
export function isCliCompletionsHidden(node: CliNodeBase): boolean {
|
|
32
|
+
if (node.cli?.completions?.enabled === false) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (node.cli?.completions?.hidden === true) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return isCliHidden(node);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True when the leaf is omitted from MCP tools/list. */
|
|
42
|
+
export function isMcpHidden(leaf: CliLeaf): boolean {
|
|
43
|
+
if (leaf.mcpTool?.enabled === false) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return leaf.mcpTool?.hidden === true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** True when the node is not callable via CLI (`cli.enabled: false`, cascades from parent). */
|
|
50
|
+
export function isCliCallable(node: CliNodeBase, parentEnabled = true): boolean {
|
|
51
|
+
if (!parentEnabled) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
if (node.cli?.enabled === false) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** True when the leaf is omitted from HTTP route table / OpenAPI. */
|
|
61
|
+
export function isHttpHidden(node: CliNodeBase): boolean {
|
|
62
|
+
return node.http?.hidden === true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** True when the leaf is not exposed on HTTP (disabled or hidden). */
|
|
66
|
+
export function isHttpDisabled(node: CliNodeBase): boolean {
|
|
67
|
+
return node.http?.enabled === false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Options visible in help, schema, completions, and MCP tool inputSchema. */
|
|
71
|
+
export function visibleOptions(options: CliOption[] | undefined): CliOption[] {
|
|
72
|
+
return (options ?? []).filter((o) => !isOptionCliHidden(o));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Strips CLI-hidden commands and options from one node for presentation export. */
|
|
76
|
+
export function presentationNode(node: CliNode): CliNode | null {
|
|
77
|
+
if (isCliHidden(node)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const options = visibleOptions(node.options);
|
|
81
|
+
if (isCliRouter(node)) {
|
|
82
|
+
const commands = node.commands.map((ch) => presentationNode(ch)).filter((ch): ch is CliNode => ch !== null);
|
|
83
|
+
return { ...node, options, commands };
|
|
84
|
+
}
|
|
85
|
+
return { ...node, options };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Subcommands visible in help listings. */
|
|
89
|
+
export function visibleSubcommands(cmds: CliNode[]): CliNode[] {
|
|
90
|
+
return cmds.filter((c) => !isCliHidden(c));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Default HTTP response metadata from a leaf `http` block. */
|
|
94
|
+
export function leafHttpResponseDefaults(leaf: CliLeaf): {
|
|
95
|
+
contentType?: string;
|
|
96
|
+
contentDisposition?: string;
|
|
97
|
+
} {
|
|
98
|
+
return {
|
|
99
|
+
contentType: leaf.http?.successContentType,
|
|
100
|
+
contentDisposition: leaf.http?.contentDisposition,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -5,16 +5,16 @@ Domain-specific regression tests (split from index.test.ts).
|
|
|
5
5
|
import { expect, test } from "bun:test";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { $ } from "bun";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
8
|
+
import { ParseKind, parse, postParseValidate } from "~/core/parse.ts";
|
|
9
|
+
import type { CliLeaf } from "~/core/types.ts";
|
|
10
|
+
import { isCliRouter } from "~/core/types.ts";
|
|
11
|
+
import { cliValidateProgram } from "~/core/validate.ts";
|
|
12
|
+
import { Cli, type CliContext, CliOptionKind } from "~/index";
|
|
13
|
+
import { testProgram, varargsReadFixture } from "~/test/fixtures.ts";
|
|
14
14
|
|
|
15
15
|
/** Tests that ctx.invocation is cli via Cli.run. */
|
|
16
16
|
test("ctx.invocation is cli via Cli.run", async () => {
|
|
17
|
-
const indexPath = join(import.meta.dir, "index.ts");
|
|
17
|
+
const indexPath = join(import.meta.dir, "../index.ts");
|
|
18
18
|
const { stdout } = await $`bun -e ${`
|
|
19
19
|
import { Cli, CliProgram } from ${JSON.stringify(indexPath)};
|
|
20
20
|
const program = {
|
|
@@ -45,6 +45,30 @@ test("ctx.invocation is mcp via Cli.invoke", async () => {
|
|
|
45
45
|
expect(seen).toBe("mcp");
|
|
46
46
|
});
|
|
47
47
|
|
|
48
|
+
/** Tests that ctx.locals.requestId is seeded before handler on invoke. */
|
|
49
|
+
test("Cli.invoke seeds ctx.locals.requestId", async () => {
|
|
50
|
+
let requestId = "";
|
|
51
|
+
const root = testProgram({
|
|
52
|
+
key: "app",
|
|
53
|
+
description: "",
|
|
54
|
+
hooks: {
|
|
55
|
+
beforeInvoke: (ctx: CliContext) => {
|
|
56
|
+
requestId = String(ctx.locals.requestId ?? "");
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
handler: () => {},
|
|
60
|
+
});
|
|
61
|
+
cliValidateProgram(root);
|
|
62
|
+
const wireId = "00000000-0000-4000-8000-000000000001";
|
|
63
|
+
const result = await new Cli(root).invoke([], {
|
|
64
|
+
invocation: "http",
|
|
65
|
+
requestId: wireId,
|
|
66
|
+
http: { request: new Request("http://localhost/"), clientIp: "127.0.0.1", requestId: wireId },
|
|
67
|
+
});
|
|
68
|
+
expect(result.kind).toBe("ok");
|
|
69
|
+
expect(requestId).toBe(wireId);
|
|
70
|
+
});
|
|
71
|
+
|
|
48
72
|
/** Cli.invoke rejects invalid Enum value. */
|
|
49
73
|
test("Cli.invoke rejects invalid Enum value", async () => {
|
|
50
74
|
const root = testProgram({
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Per-server mutable handle context passed through HTTP/MCP dispatch.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { CliHttpWireHooks, CliMcpWireHooks, ServerRuntime, ServerState } from "~/core/types.ts";
|
|
6
|
+
import type { LogEmitter } from "~/log/emitter.ts";
|
|
7
|
+
import type { ResolvedHttpServeConfig, ResolvedMcpServeConfig } from "./overrides.ts";
|
|
8
|
+
|
|
9
|
+
/** Shared server state for one HTTP or MCP serve session. */
|
|
10
|
+
export interface ServerHandleContext {
|
|
11
|
+
runtime: ServerRuntime;
|
|
12
|
+
emitter: LogEmitter;
|
|
13
|
+
http?: ResolvedHttpServeConfig;
|
|
14
|
+
mcp?: ResolvedMcpServeConfig;
|
|
15
|
+
httpHooks?: CliHttpWireHooks;
|
|
16
|
+
mcpHooks?: CliMcpWireHooks;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Creates a fresh {@link ServerRuntime} for HTTP or MCP. */
|
|
20
|
+
export function createServerRuntime(
|
|
21
|
+
program: import("~/core/types.ts").CliProgram,
|
|
22
|
+
surface: "http" | "mcp",
|
|
23
|
+
): ServerRuntime {
|
|
24
|
+
return { state: {} as ServerState, program, surface };
|
|
25
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
CLI flag and programmatic overrides merged into HTTP/MCP server runtime config.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { resolveAppConfigDir } from "~/config/file.ts";
|
|
7
|
+
import type { CliProgram } from "~/core/types.ts";
|
|
8
|
+
import type { ResolvedLogConfig } from "~/log/emitter.ts";
|
|
9
|
+
|
|
10
|
+
/** Overrides from `myapp http` / `serveHttp()` flags and embedders. */
|
|
11
|
+
export interface ServeOverrides {
|
|
12
|
+
host?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
trustProxy?: boolean;
|
|
15
|
+
obscureErrors?: boolean;
|
|
16
|
+
logFormat?: "json" | "text";
|
|
17
|
+
logFile?: string;
|
|
18
|
+
noAccessLog?: boolean;
|
|
19
|
+
dev?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Resolved HTTP listen and error options after merging schema + overrides. */
|
|
23
|
+
export interface ResolvedHttpServeConfig {
|
|
24
|
+
hostname: string;
|
|
25
|
+
port: number;
|
|
26
|
+
trustProxy: boolean;
|
|
27
|
+
obscureUnexpected: boolean;
|
|
28
|
+
log: ResolvedLogConfig;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Resolved MCP serve options after merging schema + overrides. */
|
|
32
|
+
export interface ResolvedMcpServeConfig {
|
|
33
|
+
obscureUnexpected: boolean;
|
|
34
|
+
log: ResolvedLogConfig;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveLogFile(program: CliProgram, logFile: string | undefined): string | undefined {
|
|
38
|
+
if (!logFile) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
if (logFile.startsWith("/") || logFile.startsWith("~")) {
|
|
42
|
+
return logFile;
|
|
43
|
+
}
|
|
44
|
+
return join(resolveAppConfigDir(program), logFile);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Builds resolved HTTP server config (CLI flags > program schema). */
|
|
48
|
+
export function resolveHttpServeConfig(program: CliProgram, overrides: ServeOverrides = {}): ResolvedHttpServeConfig {
|
|
49
|
+
const http = program.httpServer;
|
|
50
|
+
const obscureUnexpected = overrides.obscureErrors ?? http?.errors?.obscureUnexpected ?? false;
|
|
51
|
+
return {
|
|
52
|
+
hostname: overrides.host ?? http?.host ?? "127.0.0.1",
|
|
53
|
+
port: overrides.port ?? http?.port ?? 3000,
|
|
54
|
+
trustProxy: overrides.trustProxy ?? http?.trustProxy ?? false,
|
|
55
|
+
obscureUnexpected,
|
|
56
|
+
log: {
|
|
57
|
+
format: overrides.logFormat ?? program.log?.format ?? "json",
|
|
58
|
+
file: resolveLogFile(program, overrides.logFile ?? program.log?.file),
|
|
59
|
+
access: overrides.noAccessLog ? false : (program.log?.access ?? true),
|
|
60
|
+
errors: program.log?.errors ?? true,
|
|
61
|
+
dev: overrides.dev ?? false,
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Builds resolved MCP server config (CLI flags > program schema). */
|
|
67
|
+
export function resolveMcpServeConfig(program: CliProgram, overrides: ServeOverrides = {}): ResolvedMcpServeConfig {
|
|
68
|
+
const mcp = program.mcpServer;
|
|
69
|
+
return {
|
|
70
|
+
obscureUnexpected: overrides.obscureErrors ?? mcp?.errors?.obscureUnexpected ?? false,
|
|
71
|
+
log: {
|
|
72
|
+
format: overrides.logFormat ?? program.log?.format ?? "json",
|
|
73
|
+
file: resolveLogFile(program, overrides.logFile ?? program.log?.file),
|
|
74
|
+
access: program.log?.access ?? true,
|
|
75
|
+
errors: program.log?.errors ?? true,
|
|
76
|
+
dev: overrides.dev ?? false,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Parses `http` / `mcp serve` CLI opts into {@link ServeOverrides}. */
|
|
82
|
+
export function serveOverridesFromOpts(opts: Record<string, string>, surface: "http" | "mcp"): ServeOverrides {
|
|
83
|
+
const out: ServeOverrides = {};
|
|
84
|
+
if (opts.host) {
|
|
85
|
+
out.host = opts.host;
|
|
86
|
+
}
|
|
87
|
+
if (opts.port) {
|
|
88
|
+
const port = Number(opts.port);
|
|
89
|
+
if (!Number.isNaN(port)) {
|
|
90
|
+
out.port = port;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (opts["trust-proxy"]) {
|
|
94
|
+
out.trustProxy = true;
|
|
95
|
+
}
|
|
96
|
+
if (opts["obscure-errors"]) {
|
|
97
|
+
out.obscureErrors = true;
|
|
98
|
+
}
|
|
99
|
+
if (opts["log-format"] === "json" || opts["log-format"] === "text") {
|
|
100
|
+
out.logFormat = opts["log-format"];
|
|
101
|
+
}
|
|
102
|
+
if (opts["log-file"]) {
|
|
103
|
+
out.logFile = opts["log-file"];
|
|
104
|
+
}
|
|
105
|
+
if (opts["no-access-log"] && surface === "http") {
|
|
106
|
+
out.noAccessLog = true;
|
|
107
|
+
}
|
|
108
|
+
if (opts.dev) {
|
|
109
|
+
out.dev = true;
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|