argsbarg 6.1.1 → 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 +72 -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 +76 -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 +440 -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/core/json-leaf.test.ts +156 -0
- package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
- package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +76 -16
- package/src/{parse.test.ts → core/parse.test.ts} +97 -109
- package/src/{parse.ts → core/parse.ts} +173 -25
- package/src/{schema.ts → core/schema.ts} +25 -13
- package/src/{types.ts → core/types.ts} +238 -35
- package/src/{validate.ts → core/validate.ts} +51 -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 +54 -18
- 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 +38 -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 +18 -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} +160 -50
- 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
package/src/mcp/tools.ts
CHANGED
|
@@ -3,11 +3,8 @@ This module maps CliProgram leaf nodes to MCP tool definitions and converts
|
|
|
3
3
|
flat JSON tool arguments into argv for Cli.invoke.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { visibleOptions } from "../hidden.ts";
|
|
9
|
-
import { collectOptionDefs } from "../parse.ts";
|
|
10
|
-
import { cliSchemaJson } from "../schema.ts";
|
|
6
|
+
import { collectOptionDefs } from "~/core/parse.ts";
|
|
7
|
+
import { cliSchemaJson } from "~/core/schema.ts";
|
|
11
8
|
import {
|
|
12
9
|
type CliLeaf,
|
|
13
10
|
type CliNode,
|
|
@@ -17,8 +14,12 @@ import {
|
|
|
17
14
|
type CliProgram,
|
|
18
15
|
CliValueFormat,
|
|
19
16
|
isCliLeaf,
|
|
17
|
+
isJsonLeaf,
|
|
20
18
|
leafOutputSchema,
|
|
21
|
-
} from "
|
|
19
|
+
} from "~/core/types.ts";
|
|
20
|
+
import { docsMcpResources } from "~/docs/mcp-resources.ts";
|
|
21
|
+
import { cliResolveNotes } from "~/help.ts";
|
|
22
|
+
import { isMcpHidden, visibleOptions } from "~/runtime/exposure.ts";
|
|
22
23
|
|
|
23
24
|
const DURATION_PATTERN = "^\\d+[hdms]?$";
|
|
24
25
|
|
|
@@ -27,7 +28,7 @@ export function defaultMcpSchemaUri(mcpId: string): string {
|
|
|
27
28
|
return `${mcpId}://schema`;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
export { defaultDocsTopicResourceUri, resolveDocsTopicResourceUri } from "
|
|
31
|
+
export { defaultDocsTopicResourceUri, resolveDocsTopicResourceUri } from "~/docs/mcp-resources.ts";
|
|
31
32
|
|
|
32
33
|
/** Sanitizes a command key segment for MCP tool names and server identity. */
|
|
33
34
|
export function sanitizeToolSegment(key: string): string {
|
|
@@ -43,8 +44,6 @@ export function mcpServerId(root: CliProgram): string {
|
|
|
43
44
|
export interface McpToolDef {
|
|
44
45
|
/** MCP tool name (underscore-separated, sanitized segments). */
|
|
45
46
|
name: string;
|
|
46
|
-
/** HTTP API tool name (hyphen-separated path; preserves command key spelling). */
|
|
47
|
-
apiName: string;
|
|
48
47
|
/** Tool description from the leaf command. */
|
|
49
48
|
description: string;
|
|
50
49
|
/** Command path segments from the program root. */
|
|
@@ -71,14 +70,6 @@ export function mcpToolName(root: CliProgram, path: string[]): string {
|
|
|
71
70
|
return path.map(sanitizeToolSegment).join("_");
|
|
72
71
|
}
|
|
73
72
|
|
|
74
|
-
/** Builds the HTTP API tool name for a leaf at the given path (hyphen-joined, unsanitized). */
|
|
75
|
-
export function apiToolName(root: CliProgram, path: string[]): string {
|
|
76
|
-
if (path.length === 0) {
|
|
77
|
-
return root.key;
|
|
78
|
-
}
|
|
79
|
-
return path.join("-");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
73
|
/** JSON Schema property for one option. */
|
|
83
74
|
function optionProperty(opt: CliOption): Record<string, unknown> {
|
|
84
75
|
const base: Record<string, unknown> = { description: opt.description };
|
|
@@ -121,7 +112,7 @@ function optionProperty(opt: CliOption): Record<string, unknown> {
|
|
|
121
112
|
}
|
|
122
113
|
}
|
|
123
114
|
|
|
124
|
-
function formatMcpOptionValue(opt: CliOption, val: unknown): string | { error: string } {
|
|
115
|
+
export function formatMcpOptionValue(opt: CliOption, val: unknown): string | { error: string } {
|
|
125
116
|
if (opt.format === CliValueFormat.CommaList) {
|
|
126
117
|
if (Array.isArray(val)) {
|
|
127
118
|
const items = val.map(String).filter(Boolean);
|
|
@@ -150,6 +141,10 @@ function positionalProperty(p: CliPositional): Record<string, unknown> {
|
|
|
150
141
|
|
|
151
142
|
/** Builds inputSchema for a leaf command. */
|
|
152
143
|
function buildInputSchema(root: CliProgram, path: string[], leaf: CliLeaf): Record<string, unknown> {
|
|
144
|
+
if (isJsonLeaf(leaf) && leaf.inputSchema !== undefined) {
|
|
145
|
+
return leaf.inputSchema;
|
|
146
|
+
}
|
|
147
|
+
|
|
153
148
|
const properties: Record<string, unknown> = {};
|
|
154
149
|
const required: string[] = [];
|
|
155
150
|
|
|
@@ -233,13 +228,12 @@ export function collectMcpTools(root: CliProgram): McpToolDef[] {
|
|
|
233
228
|
if (cmd.key === "completion" || cmd.key === "configure" || cmd.key === "mcp" || cmd.key === "version") {
|
|
234
229
|
return;
|
|
235
230
|
}
|
|
236
|
-
if (cmd
|
|
231
|
+
if (isMcpHidden(cmd)) {
|
|
237
232
|
return;
|
|
238
233
|
}
|
|
239
234
|
const outputSchema = leafOutputSchema(cmd);
|
|
240
235
|
out.push({
|
|
241
236
|
name: mcpToolName(root, path),
|
|
242
|
-
apiName: apiToolName(root, path),
|
|
243
237
|
description: resolveToolDescription(root, path, cmd),
|
|
244
238
|
path,
|
|
245
239
|
leaf: cmd,
|
|
@@ -286,6 +280,10 @@ export function mcpToolCallToArgv(
|
|
|
286
280
|
tool: McpToolDef,
|
|
287
281
|
args: Record<string, unknown>,
|
|
288
282
|
): string[] | { error: string } {
|
|
283
|
+
if (isJsonLeaf(tool.leaf)) {
|
|
284
|
+
return [...tool.path];
|
|
285
|
+
}
|
|
286
|
+
|
|
289
287
|
const argv = [...tool.path];
|
|
290
288
|
|
|
291
289
|
for (const opt of collectOptionDefs(root, tool.path)) {
|
|
@@ -3,12 +3,12 @@ Internal capability resolver — decides which platform builtins are active for
|
|
|
3
3
|
Not exported from the public package barrel.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { configCommandsEnabled } from "
|
|
7
|
-
import type { CliProgram } from "
|
|
6
|
+
import { configCommandsEnabled } from "~/config/entry.ts";
|
|
7
|
+
import type { CliProgram } from "~/core/types.ts";
|
|
8
8
|
|
|
9
9
|
/** Platform builtins derived from program config and runtime. */
|
|
10
10
|
export interface CliCapabilities {
|
|
11
|
-
|
|
11
|
+
http: boolean;
|
|
12
12
|
completion: boolean;
|
|
13
13
|
mcp: boolean;
|
|
14
14
|
configure: boolean;
|
|
@@ -20,11 +20,11 @@ export interface CliCapabilities {
|
|
|
20
20
|
export function resolveCapabilities(program: CliProgram): CliCapabilities {
|
|
21
21
|
const configure = program.configure?.enabled !== false;
|
|
22
22
|
return {
|
|
23
|
-
|
|
23
|
+
http: program.httpServer?.enabled === true,
|
|
24
24
|
completion: program.completion?.enabled !== false,
|
|
25
25
|
mcp: program.mcpServer?.enabled === true,
|
|
26
26
|
configure,
|
|
27
|
-
docs: program.docs?.enabled
|
|
27
|
+
docs: program.docs?.enabled !== false,
|
|
28
28
|
configCommands: configCommandsEnabled(program),
|
|
29
29
|
};
|
|
30
30
|
}
|
|
@@ -44,8 +44,8 @@ export function reservedCommandNames(caps: CliCapabilities): string[] {
|
|
|
44
44
|
if (caps.mcp) {
|
|
45
45
|
names.push("mcp");
|
|
46
46
|
}
|
|
47
|
-
if (caps.
|
|
48
|
-
names.push("
|
|
47
|
+
if (caps.http) {
|
|
48
|
+
names.push("http");
|
|
49
49
|
}
|
|
50
50
|
return names;
|
|
51
51
|
}
|
|
@@ -65,14 +65,14 @@ export function skipsRequiredAppConfigExit(path: string[], caps: CliCapabilities
|
|
|
65
65
|
return false;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export type CapabilityFeature = "
|
|
68
|
+
export type CapabilityFeature = "http" | "mcp" | "configure" | "docs" | "completion";
|
|
69
69
|
|
|
70
70
|
/** Stderr message when a disabled built-in is invoked from the CLI. */
|
|
71
71
|
export function capabilityDeniedMessage(feature: CapabilityFeature): string {
|
|
72
72
|
switch (feature) {
|
|
73
73
|
case "completion":
|
|
74
74
|
return "Shell completion is not available for this app.\n";
|
|
75
|
-
case "
|
|
75
|
+
case "http":
|
|
76
76
|
return "HTTP API is not available for this app.\n";
|
|
77
77
|
case "mcp":
|
|
78
78
|
return "MCP is not available for this app.\n";
|
|
@@ -97,8 +97,8 @@ export function assertBuiltinAllowed(argv: string[], caps: CliCapabilities): voi
|
|
|
97
97
|
process.stderr.write(capabilityDeniedMessage("mcp"));
|
|
98
98
|
process.exit(1);
|
|
99
99
|
}
|
|
100
|
-
if (first === "
|
|
101
|
-
process.stderr.write(capabilityDeniedMessage("
|
|
100
|
+
if (first === "http" && !caps.http) {
|
|
101
|
+
process.stderr.write(capabilityDeniedMessage("http"));
|
|
102
102
|
process.exit(1);
|
|
103
103
|
}
|
|
104
104
|
if (first === "configure" && !caps.configure) {
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
Handler error helper with contextual help.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { cliPresentationRoot } from "
|
|
6
|
-
import type { CliContext } from "
|
|
7
|
-
import { cliHelpRender } from "
|
|
5
|
+
import { cliPresentationRoot } from "~/builtins/presentation.ts";
|
|
6
|
+
import type { CliContext } from "~/core/context.ts";
|
|
7
|
+
import { cliHelpRender } from "~/help.ts";
|
|
8
8
|
|
|
9
9
|
export function cliErrWithHelp(ctx: CliContext, msg: string): never {
|
|
10
|
-
if (ctx.invocation === "
|
|
10
|
+
if (ctx.invocation === "http" || ctx.invocation === "mcp") {
|
|
11
11
|
throw new Error(msg);
|
|
12
12
|
}
|
|
13
13
|
const color = process.stderr.isTTY;
|
|
@@ -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
|
|
|
@@ -127,7 +147,7 @@ export class Cli {
|
|
|
127
147
|
|
|
128
148
|
let preloadedJson: Record<string, unknown> = {};
|
|
129
149
|
try {
|
|
130
|
-
preloadedJson = await preloadPipableJson(this.program, pr.path, pr.opts, "cli");
|
|
150
|
+
preloadedJson = await preloadPipableJson(this.program, pr.path, pr.opts, "cli", pr.args);
|
|
131
151
|
} catch (err) {
|
|
132
152
|
if (err instanceof LeafInputError) {
|
|
133
153
|
this.exitLeafInputError(err, pr.path);
|
|
@@ -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
|
+
}
|