@hydranium/cli 1.0.0-next.10
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/LICENSE +21 -0
- package/README.md +240 -0
- package/heap-analysis/analyze-heap.mjs +798 -0
- package/heap-analysis/heap/core/aggregate.mjs +152 -0
- package/heap-analysis/heap/core/bucket-label.mjs +23 -0
- package/heap-analysis/heap/core/cutpoints.mjs +51 -0
- package/heap-analysis/heap/core/diff.mjs +56 -0
- package/heap-analysis/heap/core/format.mjs +47 -0
- package/heap-analysis/heap/core/holders.mjs +80 -0
- package/heap-analysis/heap/core/load.mjs +46 -0
- package/heap-analysis/heap/core/report.mjs +68 -0
- package/heap-analysis/heap/core/strings.mjs +80 -0
- package/heap-analysis/heap/langium/concepts.mjs +230 -0
- package/heap-analysis/heap/langium/glsp.classifier.mjs +80 -0
- package/heap-analysis/heap/langium/langium.classifier.mjs +203 -0
- package/heap-analysis/heap/renderer/renderer.mjs +122 -0
- package/lib/cli-version.d.ts +21 -0
- package/lib/cli-version.d.ts.map +1 -0
- package/lib/cli-version.js +30 -0
- package/lib/cli-version.js.map +1 -0
- package/lib/cli.d.ts +11 -0
- package/lib/cli.d.ts.map +1 -0
- package/lib/cli.js +135 -0
- package/lib/cli.js.map +1 -0
- package/lib/commands/analyze-heap-args.d.ts +46 -0
- package/lib/commands/analyze-heap-args.d.ts.map +1 -0
- package/lib/commands/analyze-heap-args.js +174 -0
- package/lib/commands/analyze-heap-args.js.map +1 -0
- package/lib/commands/analyze-heap.d.ts +37 -0
- package/lib/commands/analyze-heap.d.ts.map +1 -0
- package/lib/commands/analyze-heap.js +63 -0
- package/lib/commands/analyze-heap.js.map +1 -0
- package/lib/commands/ast-ground-truth-args.d.ts +23 -0
- package/lib/commands/ast-ground-truth-args.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-args.js +47 -0
- package/lib/commands/ast-ground-truth-args.js.map +1 -0
- package/lib/commands/ast-ground-truth-driver.d.ts +10 -0
- package/lib/commands/ast-ground-truth-driver.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth-driver.js +72 -0
- package/lib/commands/ast-ground-truth-driver.js.map +1 -0
- package/lib/commands/ast-ground-truth.d.ts +31 -0
- package/lib/commands/ast-ground-truth.d.ts.map +1 -0
- package/lib/commands/ast-ground-truth.js +36 -0
- package/lib/commands/ast-ground-truth.js.map +1 -0
- package/lib/commands/generate-transfer-model-args.d.ts +38 -0
- package/lib/commands/generate-transfer-model-args.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-args.js +183 -0
- package/lib/commands/generate-transfer-model-args.js.map +1 -0
- package/lib/commands/generate-transfer-model-config.d.ts +32 -0
- package/lib/commands/generate-transfer-model-config.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model-config.js +122 -0
- package/lib/commands/generate-transfer-model-config.js.map +1 -0
- package/lib/commands/generate-transfer-model.d.ts +110 -0
- package/lib/commands/generate-transfer-model.d.ts.map +1 -0
- package/lib/commands/generate-transfer-model.js +587 -0
- package/lib/commands/generate-transfer-model.js.map +1 -0
- package/lib/commands/harness-args.d.ts +198 -0
- package/lib/commands/harness-args.d.ts.map +1 -0
- package/lib/commands/harness-args.js +339 -0
- package/lib/commands/harness-args.js.map +1 -0
- package/lib/commands/headless-harness.d.ts +97 -0
- package/lib/commands/headless-harness.d.ts.map +1 -0
- package/lib/commands/headless-harness.js +121 -0
- package/lib/commands/headless-harness.js.map +1 -0
- package/lib/commands/init-args.d.ts +81 -0
- package/lib/commands/init-args.d.ts.map +1 -0
- package/lib/commands/init-args.js +250 -0
- package/lib/commands/init-args.js.map +1 -0
- package/lib/commands/init-prompt.d.ts +66 -0
- package/lib/commands/init-prompt.d.ts.map +1 -0
- package/lib/commands/init-prompt.js +71 -0
- package/lib/commands/init-prompt.js.map +1 -0
- package/lib/commands/init-templates.d.ts +42 -0
- package/lib/commands/init-templates.d.ts.map +1 -0
- package/lib/commands/init-templates.js +1902 -0
- package/lib/commands/init-templates.js.map +1 -0
- package/lib/commands/init-wizard.d.ts +65 -0
- package/lib/commands/init-wizard.d.ts.map +1 -0
- package/lib/commands/init-wizard.js +302 -0
- package/lib/commands/init-wizard.js.map +1 -0
- package/lib/commands/init-workspace.d.ts +168 -0
- package/lib/commands/init-workspace.d.ts.map +1 -0
- package/lib/commands/init-workspace.js +372 -0
- package/lib/commands/init-workspace.js.map +1 -0
- package/lib/commands/init.d.ts +273 -0
- package/lib/commands/init.d.ts.map +1 -0
- package/lib/commands/init.js +303 -0
- package/lib/commands/init.js.map +1 -0
- package/lib/commands/lint-grammar-args.d.ts +23 -0
- package/lib/commands/lint-grammar-args.d.ts.map +1 -0
- package/lib/commands/lint-grammar-args.js +63 -0
- package/lib/commands/lint-grammar-args.js.map +1 -0
- package/lib/commands/lint-grammar-driver.d.ts +10 -0
- package/lib/commands/lint-grammar-driver.d.ts.map +1 -0
- package/lib/commands/lint-grammar-driver.js +72 -0
- package/lib/commands/lint-grammar-driver.js.map +1 -0
- package/lib/commands/lint-grammar-report.d.ts +24 -0
- package/lib/commands/lint-grammar-report.d.ts.map +1 -0
- package/lib/commands/lint-grammar-report.js +65 -0
- package/lib/commands/lint-grammar-report.js.map +1 -0
- package/lib/commands/lint-grammar.d.ts +33 -0
- package/lib/commands/lint-grammar.d.ts.map +1 -0
- package/lib/commands/lint-grammar.js +42 -0
- package/lib/commands/lint-grammar.js.map +1 -0
- package/lib/commands/measure-memory-args.d.ts +23 -0
- package/lib/commands/measure-memory-args.d.ts.map +1 -0
- package/lib/commands/measure-memory-args.js +106 -0
- package/lib/commands/measure-memory-args.js.map +1 -0
- package/lib/commands/measure-memory-driver.d.ts +10 -0
- package/lib/commands/measure-memory-driver.d.ts.map +1 -0
- package/lib/commands/measure-memory-driver.js +110 -0
- package/lib/commands/measure-memory-driver.js.map +1 -0
- package/lib/commands/measure-memory.d.ts +68 -0
- package/lib/commands/measure-memory.d.ts.map +1 -0
- package/lib/commands/measure-memory.js +100 -0
- package/lib/commands/measure-memory.js.map +1 -0
- package/lib/commands/model-docs-args.d.ts +23 -0
- package/lib/commands/model-docs-args.d.ts.map +1 -0
- package/lib/commands/model-docs-args.js +53 -0
- package/lib/commands/model-docs-args.js.map +1 -0
- package/lib/commands/model-docs-driver.d.ts +10 -0
- package/lib/commands/model-docs-driver.d.ts.map +1 -0
- package/lib/commands/model-docs-driver.js +61 -0
- package/lib/commands/model-docs-driver.js.map +1 -0
- package/lib/commands/model-docs-report.d.ts +18 -0
- package/lib/commands/model-docs-report.d.ts.map +1 -0
- package/lib/commands/model-docs-report.js +123 -0
- package/lib/commands/model-docs-report.js.map +1 -0
- package/lib/commands/model-docs.d.ts +30 -0
- package/lib/commands/model-docs.d.ts.map +1 -0
- package/lib/commands/model-docs.js +37 -0
- package/lib/commands/model-docs.js.map +1 -0
- package/lib/commands/projects-args.d.ts +25 -0
- package/lib/commands/projects-args.d.ts.map +1 -0
- package/lib/commands/projects-args.js +49 -0
- package/lib/commands/projects-args.js.map +1 -0
- package/lib/commands/projects.d.ts +45 -0
- package/lib/commands/projects.d.ts.map +1 -0
- package/lib/commands/projects.js +39 -0
- package/lib/commands/projects.js.map +1 -0
- package/lib/commands/query-args.d.ts +25 -0
- package/lib/commands/query-args.d.ts.map +1 -0
- package/lib/commands/query-args.js +46 -0
- package/lib/commands/query-args.js.map +1 -0
- package/lib/commands/query.d.ts +32 -0
- package/lib/commands/query.d.ts.map +1 -0
- package/lib/commands/query.js +29 -0
- package/lib/commands/query.js.map +1 -0
- package/lib/commands/reflect-args.d.ts +23 -0
- package/lib/commands/reflect-args.d.ts.map +1 -0
- package/lib/commands/reflect-args.js +59 -0
- package/lib/commands/reflect-args.js.map +1 -0
- package/lib/commands/reflect-driver.d.ts +10 -0
- package/lib/commands/reflect-driver.d.ts.map +1 -0
- package/lib/commands/reflect-driver.js +66 -0
- package/lib/commands/reflect-driver.js.map +1 -0
- package/lib/commands/reflect-report.d.ts +20 -0
- package/lib/commands/reflect-report.d.ts.map +1 -0
- package/lib/commands/reflect-report.js +127 -0
- package/lib/commands/reflect-report.js.map +1 -0
- package/lib/commands/reflect.d.ts +30 -0
- package/lib/commands/reflect.d.ts.map +1 -0
- package/lib/commands/reflect.js +38 -0
- package/lib/commands/reflect.js.map +1 -0
- package/lib/commands/save-args.d.ts +26 -0
- package/lib/commands/save-args.d.ts.map +1 -0
- package/lib/commands/save-args.js +59 -0
- package/lib/commands/save-args.js.map +1 -0
- package/lib/commands/save.d.ts +38 -0
- package/lib/commands/save.d.ts.map +1 -0
- package/lib/commands/save.js +48 -0
- package/lib/commands/save.js.map +1 -0
- package/lib/commands/validate-args.d.ts +23 -0
- package/lib/commands/validate-args.d.ts.map +1 -0
- package/lib/commands/validate-args.js +60 -0
- package/lib/commands/validate-args.js.map +1 -0
- package/lib/commands/validate-driver.d.ts +10 -0
- package/lib/commands/validate-driver.d.ts.map +1 -0
- package/lib/commands/validate-driver.js +72 -0
- package/lib/commands/validate-driver.js.map +1 -0
- package/lib/commands/validate-report.d.ts +30 -0
- package/lib/commands/validate-report.d.ts.map +1 -0
- package/lib/commands/validate-report.js +87 -0
- package/lib/commands/validate-report.js.map +1 -0
- package/lib/commands/validate.d.ts +34 -0
- package/lib/commands/validate.d.ts.map +1 -0
- package/lib/commands/validate.js +41 -0
- package/lib/commands/validate.js.map +1 -0
- package/lib/commands/watch-args.d.ts +26 -0
- package/lib/commands/watch-args.d.ts.map +1 -0
- package/lib/commands/watch-args.js +59 -0
- package/lib/commands/watch-args.js.map +1 -0
- package/lib/commands/watch.d.ts +75 -0
- package/lib/commands/watch.d.ts.map +1 -0
- package/lib/commands/watch.js +112 -0
- package/lib/commands/watch.js.map +1 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +16 -0
- package/lib/index.js.map +1 -0
- package/lib/log-level.d.ts +39 -0
- package/lib/log-level.d.ts.map +1 -0
- package/lib/log-level.js +47 -0
- package/lib/log-level.js.map +1 -0
- package/lib/package-name.d.ts +33 -0
- package/lib/package-name.d.ts.map +1 -0
- package/lib/package-name.js +37 -0
- package/lib/package-name.js.map +1 -0
- package/lib/spawn-data-server.d.ts +102 -0
- package/lib/spawn-data-server.d.ts.map +1 -0
- package/lib/spawn-data-server.js +154 -0
- package/lib/spawn-data-server.js.map +1 -0
- package/lib/testing/echo-server.d.ts +11 -0
- package/lib/testing/echo-server.d.ts.map +1 -0
- package/lib/testing/echo-server.js +79 -0
- package/lib/testing/echo-server.js.map +1 -0
- package/package.json +105 -0
- package/src/cli-version.ts +32 -0
- package/src/cli.ts +139 -0
- package/src/commands/analyze-heap-args.ts +184 -0
- package/src/commands/analyze-heap.ts +76 -0
- package/src/commands/ast-ground-truth-args.ts +63 -0
- package/src/commands/ast-ground-truth-driver.ts +81 -0
- package/src/commands/ast-ground-truth.ts +49 -0
- package/src/commands/generate-transfer-model-args.ts +203 -0
- package/src/commands/generate-transfer-model-config.ts +128 -0
- package/src/commands/generate-transfer-model.ts +716 -0
- package/src/commands/harness-args.ts +429 -0
- package/src/commands/headless-harness.ts +165 -0
- package/src/commands/init-args.ts +273 -0
- package/src/commands/init-prompt.ts +149 -0
- package/src/commands/init-templates.ts +2037 -0
- package/src/commands/init-wizard.ts +378 -0
- package/src/commands/init-workspace.ts +442 -0
- package/src/commands/init.ts +554 -0
- package/src/commands/lint-grammar-args.ts +79 -0
- package/src/commands/lint-grammar-driver.ts +83 -0
- package/src/commands/lint-grammar-report.ts +74 -0
- package/src/commands/lint-grammar.ts +57 -0
- package/src/commands/measure-memory-args.ts +123 -0
- package/src/commands/measure-memory-driver.ts +128 -0
- package/src/commands/measure-memory.ts +143 -0
- package/src/commands/model-docs-args.ts +69 -0
- package/src/commands/model-docs-driver.ts +68 -0
- package/src/commands/model-docs-report.ts +133 -0
- package/src/commands/model-docs.ts +48 -0
- package/src/commands/projects-args.ts +62 -0
- package/src/commands/projects.ts +76 -0
- package/src/commands/query-args.ts +61 -0
- package/src/commands/query.ts +58 -0
- package/src/commands/reflect-args.ts +75 -0
- package/src/commands/reflect-driver.ts +76 -0
- package/src/commands/reflect-report.ts +138 -0
- package/src/commands/reflect.ts +51 -0
- package/src/commands/save-args.ts +74 -0
- package/src/commands/save.ts +84 -0
- package/src/commands/validate-args.ts +76 -0
- package/src/commands/validate-driver.ts +84 -0
- package/src/commands/validate-report.ts +95 -0
- package/src/commands/validate.ts +58 -0
- package/src/commands/watch-args.ts +74 -0
- package/src/commands/watch.ts +185 -0
- package/src/index.ts +16 -0
- package/src/log-level.ts +49 -0
- package/src/package-name.ts +39 -0
- package/src/spawn-data-server.ts +247 -0
- package/src/testing/echo-server.ts +93 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
/**
|
|
10
|
+
* The argv machinery the subcommands share, and the error vocabulary they all
|
|
11
|
+
* speak.
|
|
12
|
+
*
|
|
13
|
+
* Its own module rather than functions in the CLI entry point, because the entry
|
|
14
|
+
* point runs `main()` on import: a test that wants to read an argv the way a
|
|
15
|
+
* command does would otherwise have to spawn the whole binary, so flag handling
|
|
16
|
+
* and exit codes could only be covered through a subprocess.
|
|
17
|
+
*
|
|
18
|
+
* The messages here are a contract — scripts and CI steps match on them — so
|
|
19
|
+
* changing one is a breaking change for a caller nobody in this repo can see.
|
|
20
|
+
*
|
|
21
|
+
* Every parser takes an `onError` defaulting to report-and-exit, which is what a
|
|
22
|
+
* command line wants; a caller that would rather handle the problem passes its
|
|
23
|
+
* own and the parser hands control to it instead of ending the process.
|
|
24
|
+
*
|
|
25
|
+
* Each subcommand lives in a sibling `<name>-args` module exporting three things
|
|
26
|
+
* a test can hold against each other: its flag list, its `--help` text as data,
|
|
27
|
+
* and the parser that turns an argv into the runner's options. Help that drifts
|
|
28
|
+
* from the parser is worse than none — it reads as authoritative, so a flag
|
|
29
|
+
* missing from it is a capability nobody finds, and one it describes but the
|
|
30
|
+
* parser rejects is an invocation that fails for no visible reason.
|
|
31
|
+
*/
|
|
32
|
+
import type { LogThreshold } from '@hydranium/protocol';
|
|
33
|
+
/**
|
|
34
|
+
* How a parser reports a usage problem. It never returns, so a parser can treat
|
|
35
|
+
* the call as a dead end and keep narrowing the value it was about to reject.
|
|
36
|
+
*/
|
|
37
|
+
export type UsageError = (message: string) => never;
|
|
38
|
+
/**
|
|
39
|
+
* The command-line failure mode: report the usage problem and stop.
|
|
40
|
+
*
|
|
41
|
+
* Exit 2, not 1: a CI step that treats "non-zero" as failure is unaffected, but
|
|
42
|
+
* one that reads 1 as "the workspace has errors" can tell that apart from "I
|
|
43
|
+
* passed a bad flag" only while the two codes differ. 2 is also what the
|
|
44
|
+
* bundled heap analyzer exits with for a usage error.
|
|
45
|
+
*/
|
|
46
|
+
export declare function exitWithUsage(message: string): never;
|
|
47
|
+
/** The value flag {@link parseHarnessArgs} consumes itself, before any the subcommand declares. */
|
|
48
|
+
export declare const SERVICES_FLAG = "--services";
|
|
49
|
+
/**
|
|
50
|
+
* The one spelling of the log threshold, shared by both subcommand families.
|
|
51
|
+
*
|
|
52
|
+
* Named rather than repeated because the two families reach the same variable by
|
|
53
|
+
* different routes — a spawned data-server for one, a spawned driver child for
|
|
54
|
+
* the other — and a second spelling would make the CLI's verbosity control depend
|
|
55
|
+
* on which command a caller happened to reach for.
|
|
56
|
+
*/
|
|
57
|
+
export declare const LOG_LEVEL_FLAG = "--log-level";
|
|
58
|
+
/** The value flags {@link parseServerSpawnOptions} consumes itself. */
|
|
59
|
+
export declare const SERVER_SPAWN_VALUE_FLAGS: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* The `--help` line every subcommand describes {@link LOG_LEVEL_FLAG} with.
|
|
62
|
+
*
|
|
63
|
+
* Shared text, per-command padding: `command-args.test.ts` compares the flags a
|
|
64
|
+
* help block DESCRIBES against the flags the parser accepts, and it recognises a
|
|
65
|
+
* description by the two-space indent — so the line has to be a real option entry
|
|
66
|
+
* in each block, and the column each block aligns its descriptions at is the
|
|
67
|
+
* block's own.
|
|
68
|
+
*/
|
|
69
|
+
export declare function logLevelHelpLine(padTo: number): string;
|
|
70
|
+
/**
|
|
71
|
+
* Narrow a raw {@link LOG_LEVEL_FLAG} value that {@link parseHarnessArgs}
|
|
72
|
+
* collected, leaving an absent flag absent.
|
|
73
|
+
*
|
|
74
|
+
* Throws rather than reaching `onError`, matching {@link parseServerSpawnOptions}:
|
|
75
|
+
* the level vocabulary belongs to the protocol package, which reports it, and the
|
|
76
|
+
* entry point's catch turns that into the same message-and-exit.
|
|
77
|
+
*/
|
|
78
|
+
export declare function logLevelOption(value: string | undefined): LogThreshold | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Whether the argv asks for the command's help rather than describing a run.
|
|
81
|
+
*
|
|
82
|
+
* `valueFlags` are the flags that take a value, and passing them is what makes
|
|
83
|
+
* the answer correct rather than approximately correct: a token sitting in a
|
|
84
|
+
* VALUE position is data, not a request. Scanning the whole argv for `--help`
|
|
85
|
+
* instead means `--content --help` prints help and exits 0 — so a caller writing
|
|
86
|
+
* that literal text saves nothing and reads success. Omit them only for a
|
|
87
|
+
* command whose flags take no values at all.
|
|
88
|
+
*/
|
|
89
|
+
export declare function helpRequested(args: readonly string[], valueFlags?: readonly string[]): boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Print a help block on stdout, not stderr: `<cmd> --help` succeeded, and a
|
|
92
|
+
* caller piping it into a pager reads the stream a successful command writes to.
|
|
93
|
+
*/
|
|
94
|
+
export declare function printHelp(lines: readonly string[]): void;
|
|
95
|
+
/** What {@link parseHarnessArgs} recovered from an argv. */
|
|
96
|
+
export interface ParsedHarnessArgs {
|
|
97
|
+
servicesModule: string;
|
|
98
|
+
/**
|
|
99
|
+
* An absolute directory path, resolved by {@link resolveWorkspaceArgument} and
|
|
100
|
+
* checked to exist. `''` when the subcommand declared `requireWorkspace: false`.
|
|
101
|
+
*/
|
|
102
|
+
workspace: string;
|
|
103
|
+
/** Value flags by flag name; a bool flag that appeared reads `'true'`. */
|
|
104
|
+
options: Record<string, string | undefined>;
|
|
105
|
+
/** Repeatable value flags by flag name, in the order they appeared. */
|
|
106
|
+
values: Record<string, string[]>;
|
|
107
|
+
}
|
|
108
|
+
/** Per-subcommand deviations from {@link parseHarnessArgs}'s defaults. */
|
|
109
|
+
export interface HarnessArgsConfig {
|
|
110
|
+
/**
|
|
111
|
+
* `false` for subcommands that take no workspace: a positional is then
|
|
112
|
+
* rejected as unexpected, and the returned `workspace` is `''`.
|
|
113
|
+
*/
|
|
114
|
+
requireWorkspace?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Value flags that may appear more than once. Each occurrence's value is
|
|
117
|
+
* collected into `values[flag]` rather than overwriting the single-valued
|
|
118
|
+
* `options[flag]`.
|
|
119
|
+
*/
|
|
120
|
+
repeatableValueFlags?: readonly string[];
|
|
121
|
+
onError?: UsageError;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Shared parser for the headless-harness subcommands. Consumes the required
|
|
125
|
+
* `--services <module>` value flag and a single positional `<workspace>`, plus
|
|
126
|
+
* the `valueFlags` (each takes a value) and `boolFlags` (presence-only, recorded
|
|
127
|
+
* as `'true'`) the subcommand recognises. Unknown flags or a missing
|
|
128
|
+
* `--services`/`<workspace>` are usage errors, and so is a `<workspace>` that
|
|
129
|
+
* reaches no directory — see {@link resolveWorkspaceArgument} for why that cannot
|
|
130
|
+
* be left to the run.
|
|
131
|
+
*/
|
|
132
|
+
export declare function parseHarnessArgs(args: string[], commandName: string, valueFlags: readonly string[], boolFlags: readonly string[], config?: HarnessArgsConfig): ParsedHarnessArgs;
|
|
133
|
+
/**
|
|
134
|
+
* Resolve the `<workspace>` positional to an absolute directory path, reporting
|
|
135
|
+
* a usage error when it reaches anything else.
|
|
136
|
+
*
|
|
137
|
+
* The check is here rather than left to the traversal because reaching no
|
|
138
|
+
* workspace fails NOWHERE downstream: the directory read's `ENOENT` is swallowed
|
|
139
|
+
* inside a `Promise.all`, the run then reports whatever documents the head
|
|
140
|
+
* contributes independently of the workspace, and the gate exits 0 — so a CI step
|
|
141
|
+
* whose path has rotted reads as a clean workspace rather than a broken
|
|
142
|
+
* invocation. The surviving document count is a property of the head and can be
|
|
143
|
+
* non-zero, so no caller can use the number as the tell.
|
|
144
|
+
*
|
|
145
|
+
* A `file:` URI is converted to a path rather than passed on, because the
|
|
146
|
+
* headless seams resolve a string workspace as a filesystem path — an
|
|
147
|
+
* unconverted URI becomes `<cwd>/file:/…` and reaches nothing, by the same silent
|
|
148
|
+
* route.
|
|
149
|
+
*
|
|
150
|
+
* A directory is required: a path naming a file traverses to nothing just as
|
|
151
|
+
* quietly. An EMPTY directory is accepted — a workspace with no documents
|
|
152
|
+
* validating clean is an answer, not a bad argument, and the binary is
|
|
153
|
+
* language-agnostic so it cannot judge whether the files present are the
|
|
154
|
+
* language's.
|
|
155
|
+
*/
|
|
156
|
+
export declare function resolveWorkspaceArgument(workspace: string, commandName: string, onError?: UsageError): string;
|
|
157
|
+
/** Demand a value the subcommand cannot run without, naming the help that lists it. */
|
|
158
|
+
export declare function assertRequired<T>(value: T | undefined, flag: string, command: string, onError?: UsageError): T;
|
|
159
|
+
/** Parse a numeric CLI option; a usage error on a non-numeric value, `undefined` when absent. */
|
|
160
|
+
export declare function numericOption(value: string | undefined, flag: string, onError?: UsageError): number | undefined;
|
|
161
|
+
/**
|
|
162
|
+
* Parse the remaining `extra` tokens after {@link parseServerSpawnOptions}
|
|
163
|
+
* consumed the shared flags. Maps `--<flag>` (kebab) to a `flag` property
|
|
164
|
+
* (camelCase, leading `--` stripped). Every token must be a recognised flag
|
|
165
|
+
* followed by its value — unrecognised ones are a usage error, which saves a
|
|
166
|
+
* silent typo from reading as an omitted option.
|
|
167
|
+
*/
|
|
168
|
+
export declare function parseFlagOptions(extra: string[], commandName: string, recognised: readonly string[], onError?: UsageError): Record<string, string | undefined>;
|
|
169
|
+
/** What {@link parseServerSpawnOptions} recovered from an argv. */
|
|
170
|
+
export interface SpawnOptions {
|
|
171
|
+
serverCommand: string;
|
|
172
|
+
serverArgs: string[];
|
|
173
|
+
cwd?: string;
|
|
174
|
+
logLevel?: LogThreshold;
|
|
175
|
+
/** The tokens this parser did not claim, for the subcommand's own options. */
|
|
176
|
+
extra: string[];
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Shared option parser for subcommands that spawn a data-server child. Consumes
|
|
180
|
+
* `--server` / `--cwd` / `--log-level` and returns the surviving tokens as
|
|
181
|
+
* `extra` for {@link parseFlagOptions} to finish.
|
|
182
|
+
*
|
|
183
|
+
* An unrecognised `--log-level` value throws rather than reaching `onError`: the
|
|
184
|
+
* level vocabulary belongs to the protocol package, which reports it, and the
|
|
185
|
+
* entry point's catch turns that into the same message-and-exit.
|
|
186
|
+
*
|
|
187
|
+
* **`--cwd` together with a relative `--server` path is refused, not repaired.**
|
|
188
|
+
* `--cwd` re-roots the CHILD, so the shell's own reading of `./lib/entry.js` —
|
|
189
|
+
* relative to where the user stands — is not what the child gets, and the failure
|
|
190
|
+
* arrives as a bare interpreter module-not-found that names neither flag. Refusing
|
|
191
|
+
* is the honest half of the choice: re-rooting the token against the parent would
|
|
192
|
+
* be friendlier for the entry path and would silently change what an argument
|
|
193
|
+
* meant to be read by the child resolves to, which is the same class of quiet
|
|
194
|
+
* misdirection one layer along. An absolute path, or dropping `--cwd` and passing
|
|
195
|
+
* the workspace to the entry, both say what they mean.
|
|
196
|
+
*/
|
|
197
|
+
export declare function parseServerSpawnOptions(args: string[], commandName: string, onError?: UsageError): SpawnOptions;
|
|
198
|
+
//# sourceMappingURL=harness-args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-args.d.ts","sourceRoot":"","sources":["../../src/commands/harness-args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import { statSync } from 'node:fs';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { parseLogLevelOption } from '../log-level.js';
|
|
13
|
+
/**
|
|
14
|
+
* The command-line failure mode: report the usage problem and stop.
|
|
15
|
+
*
|
|
16
|
+
* Exit 2, not 1: a CI step that treats "non-zero" as failure is unaffected, but
|
|
17
|
+
* one that reads 1 as "the workspace has errors" can tell that apart from "I
|
|
18
|
+
* passed a bad flag" only while the two codes differ. 2 is also what the
|
|
19
|
+
* bundled heap analyzer exits with for a usage error.
|
|
20
|
+
*/
|
|
21
|
+
export function exitWithUsage(message) {
|
|
22
|
+
console.error(message);
|
|
23
|
+
process.exit(2);
|
|
24
|
+
}
|
|
25
|
+
/** The value flag {@link parseHarnessArgs} consumes itself, before any the subcommand declares. */
|
|
26
|
+
export const SERVICES_FLAG = '--services';
|
|
27
|
+
/**
|
|
28
|
+
* The one spelling of the log threshold, shared by both subcommand families.
|
|
29
|
+
*
|
|
30
|
+
* Named rather than repeated because the two families reach the same variable by
|
|
31
|
+
* different routes — a spawned data-server for one, a spawned driver child for
|
|
32
|
+
* the other — and a second spelling would make the CLI's verbosity control depend
|
|
33
|
+
* on which command a caller happened to reach for.
|
|
34
|
+
*/
|
|
35
|
+
export const LOG_LEVEL_FLAG = '--log-level';
|
|
36
|
+
/** The value flags {@link parseServerSpawnOptions} consumes itself. */
|
|
37
|
+
export const SERVER_SPAWN_VALUE_FLAGS = ['--server', '--cwd', LOG_LEVEL_FLAG];
|
|
38
|
+
/**
|
|
39
|
+
* The `--help` line every subcommand describes {@link LOG_LEVEL_FLAG} with.
|
|
40
|
+
*
|
|
41
|
+
* Shared text, per-command padding: `command-args.test.ts` compares the flags a
|
|
42
|
+
* help block DESCRIBES against the flags the parser accepts, and it recognises a
|
|
43
|
+
* description by the two-space indent — so the line has to be a real option entry
|
|
44
|
+
* in each block, and the column each block aligns its descriptions at is the
|
|
45
|
+
* block's own.
|
|
46
|
+
*/
|
|
47
|
+
export function logLevelHelpLine(padTo) {
|
|
48
|
+
return ` ${`${LOG_LEVEL_FLAG} <lvl>`.padEnd(padTo)}Log threshold for the head this boots (off|error|warn|info|debug|trace).`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Narrow a raw {@link LOG_LEVEL_FLAG} value that {@link parseHarnessArgs}
|
|
52
|
+
* collected, leaving an absent flag absent.
|
|
53
|
+
*
|
|
54
|
+
* Throws rather than reaching `onError`, matching {@link parseServerSpawnOptions}:
|
|
55
|
+
* the level vocabulary belongs to the protocol package, which reports it, and the
|
|
56
|
+
* entry point's catch turns that into the same message-and-exit.
|
|
57
|
+
*/
|
|
58
|
+
export function logLevelOption(value) {
|
|
59
|
+
return value === undefined ? undefined : parseLogLevelOption(value);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether the argv asks for the command's help rather than describing a run.
|
|
63
|
+
*
|
|
64
|
+
* `valueFlags` are the flags that take a value, and passing them is what makes
|
|
65
|
+
* the answer correct rather than approximately correct: a token sitting in a
|
|
66
|
+
* VALUE position is data, not a request. Scanning the whole argv for `--help`
|
|
67
|
+
* instead means `--content --help` prints help and exits 0 — so a caller writing
|
|
68
|
+
* that literal text saves nothing and reads success. Omit them only for a
|
|
69
|
+
* command whose flags take no values at all.
|
|
70
|
+
*/
|
|
71
|
+
export function helpRequested(args, valueFlags = []) {
|
|
72
|
+
const valueSet = new Set(valueFlags);
|
|
73
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
74
|
+
const token = args[index];
|
|
75
|
+
if (token === '--help' || token === '-h') {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
if (valueSet.has(token)) {
|
|
79
|
+
index += 1;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Print a help block on stdout, not stderr: `<cmd> --help` succeeded, and a
|
|
86
|
+
* caller piping it into a pager reads the stream a successful command writes to.
|
|
87
|
+
*/
|
|
88
|
+
export function printHelp(lines) {
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
console.log(line);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Shared parser for the headless-harness subcommands. Consumes the required
|
|
95
|
+
* `--services <module>` value flag and a single positional `<workspace>`, plus
|
|
96
|
+
* the `valueFlags` (each takes a value) and `boolFlags` (presence-only, recorded
|
|
97
|
+
* as `'true'`) the subcommand recognises. Unknown flags or a missing
|
|
98
|
+
* `--services`/`<workspace>` are usage errors, and so is a `<workspace>` that
|
|
99
|
+
* reaches no directory — see {@link resolveWorkspaceArgument} for why that cannot
|
|
100
|
+
* be left to the run.
|
|
101
|
+
*/
|
|
102
|
+
export function parseHarnessArgs(args, commandName, valueFlags, boolFlags, config = {}) {
|
|
103
|
+
const requireWorkspace = config.requireWorkspace ?? true;
|
|
104
|
+
const onError = config.onError ?? exitWithUsage;
|
|
105
|
+
const valueSet = new Set(valueFlags);
|
|
106
|
+
const boolSet = new Set(boolFlags);
|
|
107
|
+
const repeatableSet = new Set(config.repeatableValueFlags ?? []);
|
|
108
|
+
const options = {};
|
|
109
|
+
const values = {};
|
|
110
|
+
let servicesModule;
|
|
111
|
+
let workspace;
|
|
112
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
113
|
+
const flag = args[index];
|
|
114
|
+
const next = () => {
|
|
115
|
+
const value = args[index + 1];
|
|
116
|
+
if (value === undefined) {
|
|
117
|
+
onError(`Missing value for ${flag}`);
|
|
118
|
+
}
|
|
119
|
+
index += 1;
|
|
120
|
+
return value;
|
|
121
|
+
};
|
|
122
|
+
if (flag === '--services') {
|
|
123
|
+
servicesModule = next();
|
|
124
|
+
}
|
|
125
|
+
else if (boolSet.has(flag)) {
|
|
126
|
+
options[flag] = 'true';
|
|
127
|
+
}
|
|
128
|
+
else if (repeatableSet.has(flag)) {
|
|
129
|
+
(values[flag] ??= []).push(next());
|
|
130
|
+
}
|
|
131
|
+
else if (valueSet.has(flag)) {
|
|
132
|
+
options[flag] = next();
|
|
133
|
+
}
|
|
134
|
+
else if (flag.startsWith('--')) {
|
|
135
|
+
onError(`Unknown option: ${flag} (hydranium-cli ${commandName} --help)`);
|
|
136
|
+
}
|
|
137
|
+
else if (requireWorkspace && workspace === undefined) {
|
|
138
|
+
workspace = flag;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
onError(`Unexpected argument: ${flag} (hydranium-cli ${commandName} --help)`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
servicesModule: assertRequired(servicesModule, '--services', commandName, onError),
|
|
146
|
+
workspace: requireWorkspace
|
|
147
|
+
? resolveWorkspaceArgument(assertRequired(workspace, '<workspace>', commandName, onError), commandName, onError)
|
|
148
|
+
: (workspace ?? ''),
|
|
149
|
+
options,
|
|
150
|
+
values
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolve the `<workspace>` positional to an absolute directory path, reporting
|
|
155
|
+
* a usage error when it reaches anything else.
|
|
156
|
+
*
|
|
157
|
+
* The check is here rather than left to the traversal because reaching no
|
|
158
|
+
* workspace fails NOWHERE downstream: the directory read's `ENOENT` is swallowed
|
|
159
|
+
* inside a `Promise.all`, the run then reports whatever documents the head
|
|
160
|
+
* contributes independently of the workspace, and the gate exits 0 — so a CI step
|
|
161
|
+
* whose path has rotted reads as a clean workspace rather than a broken
|
|
162
|
+
* invocation. The surviving document count is a property of the head and can be
|
|
163
|
+
* non-zero, so no caller can use the number as the tell.
|
|
164
|
+
*
|
|
165
|
+
* A `file:` URI is converted to a path rather than passed on, because the
|
|
166
|
+
* headless seams resolve a string workspace as a filesystem path — an
|
|
167
|
+
* unconverted URI becomes `<cwd>/file:/…` and reaches nothing, by the same silent
|
|
168
|
+
* route.
|
|
169
|
+
*
|
|
170
|
+
* A directory is required: a path naming a file traverses to nothing just as
|
|
171
|
+
* quietly. An EMPTY directory is accepted — a workspace with no documents
|
|
172
|
+
* validating clean is an answer, not a bad argument, and the binary is
|
|
173
|
+
* language-agnostic so it cannot judge whether the files present are the
|
|
174
|
+
* language's.
|
|
175
|
+
*/
|
|
176
|
+
export function resolveWorkspaceArgument(workspace, commandName, onError = exitWithUsage) {
|
|
177
|
+
const resolved = toWorkspacePath(workspace, commandName, onError);
|
|
178
|
+
const help = `(hydranium-cli ${commandName} --help)`;
|
|
179
|
+
// `throwIfNoEntry: false` suppresses ENOENT and nothing else, so a path the
|
|
180
|
+
// OS cannot even interrogate still throws raw. A `file://host/share` URI
|
|
181
|
+
// reaches this on Windows, where it converts to a UNC path rather than
|
|
182
|
+
// being rejected as unusable, and an unreachable host then surfaces as
|
|
183
|
+
// `UNKNOWN: unknown error` instead of a usage message naming the argument.
|
|
184
|
+
let stats;
|
|
185
|
+
try {
|
|
186
|
+
stats = statSync(resolved, { throwIfNoEntry: false });
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
190
|
+
onError(`<workspace> is not reachable: '${workspace}' resolved to ${resolved} (${detail}) ${help}`);
|
|
191
|
+
}
|
|
192
|
+
if (stats === undefined) {
|
|
193
|
+
onError(`<workspace> does not exist: '${workspace}' resolved to ${resolved}, which is not a path on this machine ${help}`);
|
|
194
|
+
}
|
|
195
|
+
if (!stats.isDirectory()) {
|
|
196
|
+
onError(`<workspace> is not a directory: '${workspace}' resolved to ${resolved}. Pass the directory holding the models ${help}`);
|
|
197
|
+
}
|
|
198
|
+
return resolved;
|
|
199
|
+
}
|
|
200
|
+
function toWorkspacePath(workspace, commandName, onError) {
|
|
201
|
+
if (!/^file:/i.test(workspace)) {
|
|
202
|
+
return path.resolve(workspace);
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
return fileURLToPath(workspace);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
209
|
+
onError(`<workspace> is not a usable file URI: '${workspace}' (${detail}) (hydranium-cli ${commandName} --help)`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/** Demand a value the subcommand cannot run without, naming the help that lists it. */
|
|
213
|
+
export function assertRequired(value, flag, command, onError = exitWithUsage) {
|
|
214
|
+
if (value === undefined) {
|
|
215
|
+
onError(`Missing required option: ${flag} (hydranium-cli ${command} --help)`);
|
|
216
|
+
}
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
/** Parse a numeric CLI option; a usage error on a non-numeric value, `undefined` when absent. */
|
|
220
|
+
export function numericOption(value, flag, onError = exitWithUsage) {
|
|
221
|
+
if (value === undefined) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const parsed = Number(value);
|
|
225
|
+
if (!Number.isFinite(parsed)) {
|
|
226
|
+
onError(`Option ${flag} expects a number, got: ${value}`);
|
|
227
|
+
}
|
|
228
|
+
return parsed;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Parse the remaining `extra` tokens after {@link parseServerSpawnOptions}
|
|
232
|
+
* consumed the shared flags. Maps `--<flag>` (kebab) to a `flag` property
|
|
233
|
+
* (camelCase, leading `--` stripped). Every token must be a recognised flag
|
|
234
|
+
* followed by its value — unrecognised ones are a usage error, which saves a
|
|
235
|
+
* silent typo from reading as an omitted option.
|
|
236
|
+
*/
|
|
237
|
+
export function parseFlagOptions(extra, commandName, recognised, onError = exitWithUsage) {
|
|
238
|
+
const recognisedSet = new Set(recognised);
|
|
239
|
+
const out = {};
|
|
240
|
+
for (let index = 0; index < extra.length; index += 1) {
|
|
241
|
+
const flag = extra[index];
|
|
242
|
+
if (!recognisedSet.has(flag)) {
|
|
243
|
+
onError(`Unknown option: ${flag} (hydranium-cli ${commandName} --help)`);
|
|
244
|
+
}
|
|
245
|
+
const value = extra[index + 1];
|
|
246
|
+
if (value === undefined) {
|
|
247
|
+
onError(`Missing value for ${flag}`);
|
|
248
|
+
}
|
|
249
|
+
out[kebabToCamel(flag.slice(2))] = value;
|
|
250
|
+
index += 1;
|
|
251
|
+
}
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
function kebabToCamel(name) {
|
|
255
|
+
return name.replace(/-([a-z])/g, (_, ch) => ch.toUpperCase());
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Whether a `--server` token names a path the shell would resolve against the
|
|
259
|
+
* working directory.
|
|
260
|
+
*
|
|
261
|
+
* Deliberately narrow: only an explicit `./` or `../` prefix, plus a COMMAND
|
|
262
|
+
* that carries a separator without being absolute. A bare `models` or a
|
|
263
|
+
* `--stdio` must not match — the point is to recognise the spellings that are
|
|
264
|
+
* unambiguously filesystem-relative, not to guess which arguments are paths.
|
|
265
|
+
*/
|
|
266
|
+
function isRelativePathToken(token, isCommand) {
|
|
267
|
+
if (token.startsWith('./') || token.startsWith('../') || token.startsWith('.\\') || token.startsWith('..\\')) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
return isCommand && !path.isAbsolute(token) && /[/\\]/.test(token);
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Shared option parser for subcommands that spawn a data-server child. Consumes
|
|
274
|
+
* `--server` / `--cwd` / `--log-level` and returns the surviving tokens as
|
|
275
|
+
* `extra` for {@link parseFlagOptions} to finish.
|
|
276
|
+
*
|
|
277
|
+
* An unrecognised `--log-level` value throws rather than reaching `onError`: the
|
|
278
|
+
* level vocabulary belongs to the protocol package, which reports it, and the
|
|
279
|
+
* entry point's catch turns that into the same message-and-exit.
|
|
280
|
+
*
|
|
281
|
+
* **`--cwd` together with a relative `--server` path is refused, not repaired.**
|
|
282
|
+
* `--cwd` re-roots the CHILD, so the shell's own reading of `./lib/entry.js` —
|
|
283
|
+
* relative to where the user stands — is not what the child gets, and the failure
|
|
284
|
+
* arrives as a bare interpreter module-not-found that names neither flag. Refusing
|
|
285
|
+
* is the honest half of the choice: re-rooting the token against the parent would
|
|
286
|
+
* be friendlier for the entry path and would silently change what an argument
|
|
287
|
+
* meant to be read by the child resolves to, which is the same class of quiet
|
|
288
|
+
* misdirection one layer along. An absolute path, or dropping `--cwd` and passing
|
|
289
|
+
* the workspace to the entry, both say what they mean.
|
|
290
|
+
*/
|
|
291
|
+
export function parseServerSpawnOptions(args, commandName, onError = exitWithUsage) {
|
|
292
|
+
let serverSpec;
|
|
293
|
+
let cwd;
|
|
294
|
+
let logLevel;
|
|
295
|
+
const extra = [];
|
|
296
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
297
|
+
const flag = args[index];
|
|
298
|
+
const claim = () => {
|
|
299
|
+
const value = args[index + 1];
|
|
300
|
+
if (value === undefined) {
|
|
301
|
+
onError(`Missing value for ${flag}`);
|
|
302
|
+
}
|
|
303
|
+
index += 1;
|
|
304
|
+
return value;
|
|
305
|
+
};
|
|
306
|
+
if (flag === '--server') {
|
|
307
|
+
serverSpec = claim();
|
|
308
|
+
}
|
|
309
|
+
else if (flag === '--cwd') {
|
|
310
|
+
cwd = claim();
|
|
311
|
+
}
|
|
312
|
+
else if (flag === LOG_LEVEL_FLAG) {
|
|
313
|
+
logLevel = parseLogLevelOption(claim());
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
extra.push(flag);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!serverSpec) {
|
|
320
|
+
onError(`Missing required option: --server (hydranium-cli ${commandName} --help)`);
|
|
321
|
+
}
|
|
322
|
+
// Split the spec on whitespace — quoting is the caller's shell's job;
|
|
323
|
+
// by the time we see the value here it's already a single token.
|
|
324
|
+
const tokens = serverSpec.split(/\s+/).filter(token => token.length > 0);
|
|
325
|
+
if (tokens.length === 0) {
|
|
326
|
+
onError('Empty --server value');
|
|
327
|
+
}
|
|
328
|
+
const [serverCommand, ...serverArgs] = tokens;
|
|
329
|
+
if (cwd !== undefined) {
|
|
330
|
+
const relative = tokens.find((token, position) => isRelativePathToken(token, position === 0));
|
|
331
|
+
if (relative !== undefined) {
|
|
332
|
+
onError(`--cwd re-roots the spawned child, so the relative path '${relative}' in --server would be resolved ` +
|
|
333
|
+
`against '${cwd}' rather than against the current directory. Pass an absolute path, or drop --cwd and ` +
|
|
334
|
+
`give the workspace to the entry as its own argument (hydranium-cli ${commandName} --help)`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { serverCommand, serverArgs, cwd, logLevel, extra };
|
|
338
|
+
}
|
|
339
|
+
//# sourceMappingURL=harness-args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harness-args.js","sourceRoot":"","sources":["../../src/commands/harness-args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (c) 2026 CrossBreeze, EclipseSource and others.
|
|
3
|
+
*
|
|
4
|
+
* This program and the accompanying materials are made available under the
|
|
5
|
+
* terms of the MIT License which is available in the project root.
|
|
6
|
+
*
|
|
7
|
+
* SPDX-License-Identifier: MIT
|
|
8
|
+
********************************************************************************/
|
|
9
|
+
import type * as HydraniumCoreNode from '@hydranium/core/node';
|
|
10
|
+
import type { LogThreshold } from '@hydranium/protocol';
|
|
11
|
+
/** The flag every report-producing subcommand names its destination file with. */
|
|
12
|
+
export declare const OUT_FILE_FLAG = "--out-file";
|
|
13
|
+
/** The `@hydranium/core/node` module surface the drivers use (type only). */
|
|
14
|
+
type CoreNodeModule = typeof HydraniumCoreNode;
|
|
15
|
+
/**
|
|
16
|
+
* The zero-arg service factory a head exports for the headless harness
|
|
17
|
+
* subcommands (`measure-memory`, `ast-ground-truth`). The head wires its own
|
|
18
|
+
* filesystem inside.
|
|
19
|
+
*/
|
|
20
|
+
export type ServicesFactory = HydraniumCoreNode.MeasureModelMemoryOptions['createServices'];
|
|
21
|
+
/** What {@link loadHeadlessContext} hands a driver: the head's factory + harness. */
|
|
22
|
+
export interface HeadlessContext {
|
|
23
|
+
/** The head's zero-arg `createServices(): { shared }` thunk. */
|
|
24
|
+
createServices: ServicesFactory;
|
|
25
|
+
/** `@hydranium/core/node`, resolved from the HEAD's dependency graph. */
|
|
26
|
+
coreNode: CoreNodeModule;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Child-side: load everything a driver needs from a head's `--services` module —
|
|
30
|
+
* its `createServices` thunk AND the `@hydranium/core/node` harness, both
|
|
31
|
+
* resolved from the HEAD's location rather than the CLI's.
|
|
32
|
+
*
|
|
33
|
+
* The module is an ESM file (typically a compiled `lib/*.js`) exporting a
|
|
34
|
+
* ZERO-ARG `createServices(): { shared }`. `hydranium-cli` is language-agnostic
|
|
35
|
+
* and cannot statically import a head's `create<Lang>Services`, so the dynamic
|
|
36
|
+
* import is the seam that keeps the binary head-neutral. Resolving the harness
|
|
37
|
+
* from the head's graph (via `createRequire` rooted at the head module) means the
|
|
38
|
+
* CLI itself needs no `@hydranium/core` dependency — the head always has it — and
|
|
39
|
+
* guarantees the harness runs against the SAME core copy as `createServices`.
|
|
40
|
+
*
|
|
41
|
+
* Throws a clear error when the path names no file, and when the export is
|
|
42
|
+
* missing or not a function.
|
|
43
|
+
*/
|
|
44
|
+
export declare function loadHeadlessContext(servicesModule: string): Promise<HeadlessContext>;
|
|
45
|
+
/**
|
|
46
|
+
* Child-side: deliver a finished report — to `outFile` when one was named, else
|
|
47
|
+
* to stdout.
|
|
48
|
+
*
|
|
49
|
+
* The file is created only once the report EXISTS, which is what the flag buys
|
|
50
|
+
* over the shell redirection it replaces: `> report.md` truncates the destination
|
|
51
|
+
* before the command runs, so a head that throws while booting leaves a
|
|
52
|
+
* zero-length file that a later step reads as an empty report rather than as a
|
|
53
|
+
* failed run.
|
|
54
|
+
*/
|
|
55
|
+
export declare function emitReport(report: string, outFile: string | undefined): void;
|
|
56
|
+
/**
|
|
57
|
+
* Parent-side: spawn `node <execArgs...>` inheriting stdio and resolve with the
|
|
58
|
+
* child's exit code. The harness subcommands need a child process anyway — the
|
|
59
|
+
* heads' services and a workspace build run in isolation, and `measure-memory`
|
|
60
|
+
* additionally needs `--expose-gc` for post-GC readings.
|
|
61
|
+
*
|
|
62
|
+
* `env` is MERGED over the parent's rather than replacing it: the child resolves
|
|
63
|
+
* the head's module graph, so dropping `PATH` / `NODE_OPTIONS` / the platform's
|
|
64
|
+
* own variables would break the import the driver exists to perform.
|
|
65
|
+
*/
|
|
66
|
+
export declare function spawnNodeChild(execArgs: string[], env?: Record<string, string>): Promise<number>;
|
|
67
|
+
/** How a subcommand parent reaches its driver child; the seam `__spawnForTest` replaces. */
|
|
68
|
+
export type SpawnDriverChild = (execArgs: string[], env?: Record<string, string>) => Promise<number>;
|
|
69
|
+
/** What every `--services` subcommand carries for the child it spawns, on top of its own options. */
|
|
70
|
+
export interface DriverSpawnOptions {
|
|
71
|
+
/**
|
|
72
|
+
* Log threshold for the head the driver boots. Absent leaves the child's
|
|
73
|
+
* inherited environment alone, so an ambient `HYDRANIUM_LOG_LEVEL` still wins
|
|
74
|
+
* where a caller set one.
|
|
75
|
+
*/
|
|
76
|
+
readonly logLevel?: LogThreshold;
|
|
77
|
+
/** Test-only: capture the node argv and env instead of spawning the real child. */
|
|
78
|
+
readonly __spawnForTest?: SpawnDriverChild;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parent-side: run a subcommand's driver child and propagate its exit code, so a
|
|
82
|
+
* shell or CI step sees the gate.
|
|
83
|
+
*
|
|
84
|
+
* **The log threshold travels in the child's ENVIRONMENT, not its argv.** The
|
|
85
|
+
* head's logger reads `HYDRANIUM_LOG_LEVEL` while `createServices` constructs it,
|
|
86
|
+
* which is inside the driver's dynamic import — a flag the driver parsed would
|
|
87
|
+
* arrive after the only moment it can be read. Setting it here rather than
|
|
88
|
+
* exporting it also leaves the CLI's own output alone, unlike an ambient
|
|
89
|
+
* `HYDRANIUM_LOG_LEVEL=…` the parent would carry too.
|
|
90
|
+
*
|
|
91
|
+
* A head that binds a logger of its own decides for itself whether the flag means
|
|
92
|
+
* anything, which is the intended seam: the CLI is language-agnostic and cannot
|
|
93
|
+
* reach past `createServices`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function runDriverChild(execArgs: string[], options: DriverSpawnOptions): Promise<void>;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=headless-harness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headless-harness.d.ts","sourceRoot":"","sources":["../../src/commands/headless-harness.ts"],"names":[],"mappings":"AAAA;;;;;;;kFAOkF;AAElF,OAAO,KAAK,KAAK,iBAAiB,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,kFAAkF;AAClF,eAAO,MAAM,aAAa,eAAe,CAAC;AAE1C,6EAA6E;AAC7E,KAAK,cAAc,GAAG,OAAO,iBAAiB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,iBAAiB,CAAC,yBAAyB,CAAC,gBAAgB,CAAC,CAAC;AAE5F,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC7B,gEAAgE;IAChE,cAAc,EAAE,eAAe,CAAC;IAChC,yEAAyE;IACzE,QAAQ,EAAE,cAAc,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA4B1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAS5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAShG;AAED,4FAA4F;AAC5F,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAErG,qGAAqG;AACrG,MAAM,WAAW,kBAAkB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC;IACjC,mFAAmF;IACnF,QAAQ,CAAC,cAAc,CAAC,EAAE,gBAAgB,CAAC;CAC7C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnG"}
|