@vibe-agent-toolkit/cli 0.1.41-rc.7 → 0.1.41-rc.8
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/dist/bin/vat +24 -1
- package/dist/bin/vat.js +24 -1
- package/dist/bin/vat.js.map +1 -1
- package/dist/bin.js +12 -1
- package/dist/bin.js.map +1 -1
- package/dist/commands/agent/list.d.ts +11 -0
- package/dist/commands/agent/list.d.ts.map +1 -1
- package/dist/commands/agent/list.js +30 -11
- package/dist/commands/agent/list.js.map +1 -1
- package/dist/commands/audit.d.ts +34 -1
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +220 -88
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/build.d.ts +14 -0
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +89 -24
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/claude/marketplace/validate.d.ts +97 -0
- package/dist/commands/claude/marketplace/validate.d.ts.map +1 -1
- package/dist/commands/claude/marketplace/validate.js +154 -40
- package/dist/commands/claude/marketplace/validate.js.map +1 -1
- package/dist/commands/claude/org/stubs.d.ts +9 -1
- package/dist/commands/claude/org/stubs.d.ts.map +1 -1
- package/dist/commands/claude/org/stubs.js +11 -3
- package/dist/commands/claude/org/stubs.js.map +1 -1
- package/dist/commands/claude/plugin/build.d.ts +25 -2
- package/dist/commands/claude/plugin/build.d.ts.map +1 -1
- package/dist/commands/claude/plugin/build.js +99 -10
- package/dist/commands/claude/plugin/build.js.map +1 -1
- package/dist/commands/claude/plugin/plugin-json-merge.d.ts.map +1 -1
- package/dist/commands/claude/plugin/plugin-json-merge.js +15 -0
- package/dist/commands/claude/plugin/plugin-json-merge.js.map +1 -1
- package/dist/commands/corpus/report.d.ts +22 -4
- package/dist/commands/corpus/report.d.ts.map +1 -1
- package/dist/commands/corpus/report.js +1 -0
- package/dist/commands/corpus/report.js.map +1 -1
- package/dist/commands/corpus/runner.d.ts +16 -1
- package/dist/commands/corpus/runner.d.ts.map +1 -1
- package/dist/commands/corpus/runner.js +40 -22
- package/dist/commands/corpus/runner.js.map +1 -1
- package/dist/commands/inventory.d.ts +2 -0
- package/dist/commands/inventory.d.ts.map +1 -1
- package/dist/commands/inventory.js +45 -4
- package/dist/commands/inventory.js.map +1 -1
- package/dist/commands/phase-utils.d.ts +183 -14
- package/dist/commands/phase-utils.d.ts.map +1 -1
- package/dist/commands/phase-utils.js +294 -29
- package/dist/commands/phase-utils.js.map +1 -1
- package/dist/commands/rag/query-command.d.ts +40 -0
- package/dist/commands/rag/query-command.d.ts.map +1 -1
- package/dist/commands/rag/query-command.js +47 -20
- package/dist/commands/rag/query-command.js.map +1 -1
- package/dist/commands/resources/index.d.ts.map +1 -1
- package/dist/commands/resources/index.js +27 -9
- package/dist/commands/resources/index.js.map +1 -1
- package/dist/commands/resources/scan.d.ts +37 -0
- package/dist/commands/resources/scan.d.ts.map +1 -1
- package/dist/commands/resources/scan.js +47 -32
- package/dist/commands/resources/scan.js.map +1 -1
- package/dist/commands/resources/validate.d.ts +136 -1
- package/dist/commands/resources/validate.d.ts.map +1 -1
- package/dist/commands/resources/validate.js +105 -66
- package/dist/commands/resources/validate.js.map +1 -1
- package/dist/commands/skill/review.d.ts.map +1 -1
- package/dist/commands/skill/review.js +7 -3
- package/dist/commands/skill/review.js.map +1 -1
- package/dist/commands/skill/test/run.d.ts +1 -1
- package/dist/commands/skill/test/run.d.ts.map +1 -1
- package/dist/commands/skill/test/run.js +8 -6
- package/dist/commands/skill/test/run.js.map +1 -1
- package/dist/commands/skills/build.d.ts +96 -7
- package/dist/commands/skills/build.d.ts.map +1 -1
- package/dist/commands/skills/build.js +277 -60
- package/dist/commands/skills/build.js.map +1 -1
- package/dist/commands/skills/command-helpers.d.ts +6 -10
- package/dist/commands/skills/command-helpers.d.ts.map +1 -1
- package/dist/commands/skills/command-helpers.js +6 -22
- package/dist/commands/skills/command-helpers.js.map +1 -1
- package/dist/commands/skills/list.d.ts +18 -0
- package/dist/commands/skills/list.d.ts.map +1 -1
- package/dist/commands/skills/list.js +41 -15
- package/dist/commands/skills/list.js.map +1 -1
- package/dist/commands/skills/package.d.ts +15 -0
- package/dist/commands/skills/package.d.ts.map +1 -1
- package/dist/commands/skills/package.js +30 -20
- package/dist/commands/skills/package.js.map +1 -1
- package/dist/commands/skills/validate-command.d.ts.map +1 -1
- package/dist/commands/skills/validate-command.js +22 -6
- package/dist/commands/skills/validate-command.js.map +1 -1
- package/dist/commands/skills/validate.d.ts +44 -3
- package/dist/commands/skills/validate.d.ts.map +1 -1
- package/dist/commands/skills/validate.js +180 -46
- package/dist/commands/skills/validate.js.map +1 -1
- package/dist/commands/validate.d.ts +18 -0
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +73 -66
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/verify.d.ts +38 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +203 -83
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/skill-resolution/index.d.ts +1 -1
- package/dist/skill-resolution/index.d.ts.map +1 -1
- package/dist/skill-resolution/index.js +1 -1
- package/dist/skill-resolution/index.js.map +1 -1
- package/dist/skill-resolution/packaging-config.d.ts +15 -2
- package/dist/skill-resolution/packaging-config.d.ts.map +1 -1
- package/dist/skill-resolution/packaging-config.js +39 -2
- package/dist/skill-resolution/packaging-config.js.map +1 -1
- package/dist/utils/command-error.d.ts +34 -1
- package/dist/utils/command-error.d.ts.map +1 -1
- package/dist/utils/command-error.js +32 -1
- package/dist/utils/command-error.js.map +1 -1
- package/dist/utils/help-loader.d.ts +6 -2
- package/dist/utils/help-loader.d.ts.map +1 -1
- package/dist/utils/help-loader.js +9 -16
- package/dist/utils/help-loader.js.map +1 -1
- package/dist/utils/issue-rendering.d.ts +88 -0
- package/dist/utils/issue-rendering.d.ts.map +1 -1
- package/dist/utils/issue-rendering.js +94 -0
- package/dist/utils/issue-rendering.js.map +1 -1
- package/dist/utils/output.d.ts +54 -5
- package/dist/utils/output.d.ts.map +1 -1
- package/dist/utils/output.js +111 -17
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/relativize-paths.d.ts +45 -0
- package/dist/utils/relativize-paths.d.ts.map +1 -0
- package/dist/utils/relativize-paths.js +67 -0
- package/dist/utils/relativize-paths.js.map +1 -0
- package/dist/utils/resource-loader.d.ts +21 -2
- package/dist/utils/resource-loader.d.ts.map +1 -1
- package/dist/utils/resource-loader.js +85 -11
- package/dist/utils/resource-loader.js.map +1 -1
- package/dist/utils/skill-packaging-config.d.ts +22 -1
- package/dist/utils/skill-packaging-config.d.ts.map +1 -1
- package/dist/utils/skill-packaging-config.js +26 -0
- package/dist/utils/skill-packaging-config.js.map +1 -1
- package/dist/utils/verdict-helpers.d.ts +4 -3
- package/dist/utils/verdict-helpers.d.ts.map +1 -1
- package/dist/utils/verdict-helpers.js +6 -13
- package/dist/utils/verdict-helpers.js.map +1 -1
- package/docs/rag.md +9 -4
- package/docs/resources.md +30 -14
- package/docs/skills.md +9 -5
- package/package.json +12 -12
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared command error handling utilities
|
|
2
|
+
* Shared command error handling utilities.
|
|
3
|
+
*
|
|
4
|
+
* THE single implementation of each failure exit. `commands/skills/` used to
|
|
5
|
+
* carry its own `handleCommandError` that logged to stderr and exited 2 having
|
|
6
|
+
* written nothing to stdout — same name, same signature, same documented
|
|
7
|
+
* contract, silently different behaviour. Every command family routes here.
|
|
3
8
|
*/
|
|
9
|
+
import { type SeverityCounts, type ValidationIssue } from '@vibe-agent-toolkit/agent-schema';
|
|
4
10
|
import type { Logger } from './logger.js';
|
|
5
11
|
/**
|
|
6
12
|
* Format duration for human readability
|
|
@@ -16,4 +22,31 @@ export declare function formatDuration(ms: number): string;
|
|
|
16
22
|
* @param commandName - Name of the command (for error message)
|
|
17
23
|
*/
|
|
18
24
|
export declare function handleCommandError(error: unknown, logger: Logger, startTime: number, commandName: string): never;
|
|
25
|
+
/** The payload a command publishes when its own validation gate stops it. */
|
|
26
|
+
export interface ValidationGateFailure {
|
|
27
|
+
status: 'error';
|
|
28
|
+
issueCounts: SeverityCounts;
|
|
29
|
+
/** What was being validated — a skill name or a path, whichever the lane has. */
|
|
30
|
+
skill: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Pure: the document {@link handleValidationGateFailure} writes.
|
|
34
|
+
*
|
|
35
|
+
* The per-severity distribution rides beside the status because `status: error`
|
|
36
|
+
* alone cannot say whether the run also carried warnings and info — and the
|
|
37
|
+
* findings themselves went to stderr, where a piped consumer never sees them.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildValidationGateFailure(subject: string, issues: readonly ValidationIssue[]): ValidationGateFailure;
|
|
40
|
+
/**
|
|
41
|
+
* Publish the documented failure payload for a command stopped by its OWN
|
|
42
|
+
* validation gate, then exit 1.
|
|
43
|
+
*
|
|
44
|
+
* {@link handleCommandError} above covers the UNEXPECTED failure (exit 2). This
|
|
45
|
+
* covers the expected one, and `vat skills build` / `vat skills package` both
|
|
46
|
+
* exited it after writing zero bytes to stdout — while their `--help` texts
|
|
47
|
+
* document a YAML summary on stdout and reserve exit 1 for exactly this case.
|
|
48
|
+
* A consumer running `vat skills build | jq .status` got an empty document and
|
|
49
|
+
* a bare non-zero code.
|
|
50
|
+
*/
|
|
51
|
+
export declare function handleValidationGateFailure(subject: string, issues: readonly ValidationIssue[]): never;
|
|
19
52
|
//# sourceMappingURL=command-error.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-error.d.ts","sourceRoot":"","sources":["../../src/utils/command-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"command-error.d.ts","sourceRoot":"","sources":["../../src/utils/command-error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAmB,KAAK,cAAc,EAAE,KAAK,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAE9G,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,KAAK,CAWP;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,cAAc,CAAC;IAC5B,iFAAiF;IACjF,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,eAAe,EAAE,GACjC,qBAAqB,CAEvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,SAAS,eAAe,EAAE,GACjC,KAAK,CAGP"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared command error handling utilities
|
|
2
|
+
* Shared command error handling utilities.
|
|
3
|
+
*
|
|
4
|
+
* THE single implementation of each failure exit. `commands/skills/` used to
|
|
5
|
+
* carry its own `handleCommandError` that logged to stderr and exited 2 having
|
|
6
|
+
* written nothing to stdout — same name, same signature, same documented
|
|
7
|
+
* contract, silently different behaviour. Every command family routes here.
|
|
3
8
|
*/
|
|
9
|
+
import { countBySeverity } from '@vibe-agent-toolkit/agent-schema';
|
|
4
10
|
import { writeYamlOutput } from './output.js';
|
|
5
11
|
/**
|
|
6
12
|
* Format duration for human readability
|
|
@@ -33,4 +39,29 @@ export function handleCommandError(error, logger, startTime, commandName) {
|
|
|
33
39
|
});
|
|
34
40
|
process.exit(2);
|
|
35
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Pure: the document {@link handleValidationGateFailure} writes.
|
|
44
|
+
*
|
|
45
|
+
* The per-severity distribution rides beside the status because `status: error`
|
|
46
|
+
* alone cannot say whether the run also carried warnings and info — and the
|
|
47
|
+
* findings themselves went to stderr, where a piped consumer never sees them.
|
|
48
|
+
*/
|
|
49
|
+
export function buildValidationGateFailure(subject, issues) {
|
|
50
|
+
return { status: 'error', issueCounts: countBySeverity(issues), skill: subject };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Publish the documented failure payload for a command stopped by its OWN
|
|
54
|
+
* validation gate, then exit 1.
|
|
55
|
+
*
|
|
56
|
+
* {@link handleCommandError} above covers the UNEXPECTED failure (exit 2). This
|
|
57
|
+
* covers the expected one, and `vat skills build` / `vat skills package` both
|
|
58
|
+
* exited it after writing zero bytes to stdout — while their `--help` texts
|
|
59
|
+
* document a YAML summary on stdout and reserve exit 1 for exactly this case.
|
|
60
|
+
* A consumer running `vat skills build | jq .status` got an empty document and
|
|
61
|
+
* a bare non-zero code.
|
|
62
|
+
*/
|
|
63
|
+
export function handleValidationGateFailure(subject, issues) {
|
|
64
|
+
writeYamlOutput(buildValidationGateFailure(subject, issues));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
36
67
|
//# sourceMappingURL=command-error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-error.js","sourceRoot":"","sources":["../../src/utils/command-error.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"command-error.js","sourceRoot":"","sources":["../../src/utils/command-error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAA6C,MAAM,kCAAkC,CAAC;AAG9G,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACd,OAAO,GAAG,EAAE,IAAI,CAAC;IACnB,CAAC;IACD,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACvC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAc,EACd,MAAc,EACd,SAAiB,EACjB,WAAmB;IAEnB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;IACxC,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW,YAAY,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;IAEnG,eAAe,CAAC;QACd,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;QAC/D,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;KACnC,CAAC,CAAC;IAEH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAUD;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAe,EACf,MAAkC;IAElC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AACnF,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CACzC,OAAe,EACf,MAAkC;IAElC,eAAe,CAAC,0BAA0B,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -21,8 +21,12 @@ export declare function loadVerboseHelp(section?: string): string;
|
|
|
21
21
|
* That directly violates this package's own rule that help must survive piping.
|
|
22
22
|
*
|
|
23
23
|
* `writeSync` in a drain loop is the fix: it cannot return before the bytes are
|
|
24
|
-
* handed to the pipe, so `process.exit` has nothing left to lose.
|
|
25
|
-
*
|
|
24
|
+
* handed to the pipe, so `process.exit` has nothing left to lose.
|
|
25
|
+
*
|
|
26
|
+
* The loop itself now lives in `output.ts` as `writeStdoutSync`, because the very
|
|
27
|
+
* same defect was later found in `writeYamlOutput` — every command's YAML summary
|
|
28
|
+
* was being truncated at one pipe buffer. Two copies of this reasoning is exactly
|
|
29
|
+
* how the second lane went unfixed for so long, so there is one.
|
|
26
30
|
*/
|
|
27
31
|
export declare function writeHelpSync(content: string): void;
|
|
28
32
|
//# sourceMappingURL=help-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-loader.d.ts","sourceRoot":"","sources":["../../src/utils/help-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"help-loader.d.ts","sourceRoot":"","sources":["../../src/utils/help-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBxD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEnD"}
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
* Help loader - reads verbose help from markdown files
|
|
3
3
|
* Markdown docs are the source of truth for CLI help
|
|
4
4
|
*/
|
|
5
|
-
import { readFileSync
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
6
|
import { dirname } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
8
|
import { safePath } from '@vibe-agent-toolkit/utils';
|
|
9
|
+
import { writeStdoutSync } from './output.js';
|
|
9
10
|
/**
|
|
10
11
|
* Load verbose help from markdown files in packages/cli/docs/
|
|
11
12
|
*
|
|
@@ -49,22 +50,14 @@ Please report this issue at: https://github.com/jdutton/vibe-agent-toolkit/issue
|
|
|
49
50
|
* That directly violates this package's own rule that help must survive piping.
|
|
50
51
|
*
|
|
51
52
|
* `writeSync` in a drain loop is the fix: it cannot return before the bytes are
|
|
52
|
-
* handed to the pipe, so `process.exit` has nothing left to lose.
|
|
53
|
-
*
|
|
53
|
+
* handed to the pipe, so `process.exit` has nothing left to lose.
|
|
54
|
+
*
|
|
55
|
+
* The loop itself now lives in `output.ts` as `writeStdoutSync`, because the very
|
|
56
|
+
* same defect was later found in `writeYamlOutput` — every command's YAML summary
|
|
57
|
+
* was being truncated at one pipe buffer. Two copies of this reasoning is exactly
|
|
58
|
+
* how the second lane went unfixed for so long, so there is one.
|
|
54
59
|
*/
|
|
55
60
|
export function writeHelpSync(content) {
|
|
56
|
-
|
|
57
|
-
let written = 0;
|
|
58
|
-
while (written < buffer.length) {
|
|
59
|
-
try {
|
|
60
|
-
written += writeSync(1, buffer, written, buffer.length - written);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
// A non-blocking pipe whose buffer is momentarily full raises EAGAIN; the
|
|
64
|
-
// reader will drain it, so retry. Anything else is a genuine write failure.
|
|
65
|
-
if (error.code !== 'EAGAIN')
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
61
|
+
writeStdoutSync(content.endsWith('\n') ? content : `${content}\n`);
|
|
69
62
|
}
|
|
70
63
|
//# sourceMappingURL=help-loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help-loader.js","sourceRoot":"","sources":["../../src/utils/help-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"help-loader.js","sourceRoot":"","sources":["../../src/utils/help-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,mCAAmC;IACnC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAEtD,wCAAwC;IACxC,MAAM,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC;IACxD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE9D,IAAI,CAAC;QACH,qHAAqH;QACrH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,sCAAsC;QACtC,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5E,OAAO;;0BAEe,QAAQ;;EAEhC,YAAY;;;CAGb,CAAC;IACA,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACrE,CAAC"}
|
|
@@ -42,6 +42,18 @@ export declare function formatIssueSetHeading(issues: readonly ValidationIssue[]
|
|
|
42
42
|
* a unit test can assert over every line without capturing output.
|
|
43
43
|
*/
|
|
44
44
|
export declare function formatIssueLines(issue: ValidationIssue, indent?: string): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Render the run-level findings section — findings that belong to the whole
|
|
47
|
+
* invocation rather than to any one skill (ALLOW_UNUSED is the only producer).
|
|
48
|
+
*
|
|
49
|
+
* Shared by `vat skills validate` and `vat skills build` so the two commands
|
|
50
|
+
* name the same concept the same way. Attributing these to a skill is the
|
|
51
|
+
* misreading the heading exists to prevent: `validation.allow` is declared once
|
|
52
|
+
* for the package, so an entry that matched nothing is a fact about the run.
|
|
53
|
+
*
|
|
54
|
+
* Returns `[]` for an empty set; callers own their surrounding blank lines.
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatRunIssueLines(runIssues: readonly ValidationIssue[]): string[];
|
|
45
57
|
/**
|
|
46
58
|
* Every post-build finding a `PackageSkillResult` carries, from BOTH channels.
|
|
47
59
|
*
|
|
@@ -56,6 +68,82 @@ export declare function formatIssueLines(issue: ValidationIssue, indent?: string
|
|
|
56
68
|
* (see its doc comment — the name lies), so info findings survive here.
|
|
57
69
|
*/
|
|
58
70
|
export declare function collectPostBuildIssues(result: PackageSkillResult): ValidationIssue[];
|
|
71
|
+
/**
|
|
72
|
+
* A compact count-only projection of one asset's findings, for summary output.
|
|
73
|
+
*
|
|
74
|
+
* Deliberately does NOT carry the asset's name: the three commands that publish
|
|
75
|
+
* these rows (`vat skills validate`, `vat claude marketplace validate`,
|
|
76
|
+
* `vat resources validate`) each key their row by a different identity — a skill
|
|
77
|
+
* name, a plugin name, a file path. A projection that named the asset would have
|
|
78
|
+
* to pick one, and the two lanes it did not fit would fork their own copy, which
|
|
79
|
+
* is how the severity collapse came to be spelled five different ways.
|
|
80
|
+
*
|
|
81
|
+
* A zero bucket is an ABSENT key, never `0`. Serialized as YAML, three zero
|
|
82
|
+
* columns per asset is what makes a summary unreadable at corpus scale, and
|
|
83
|
+
* `errors: 0` beside a red exit code reads as a contradiction rather than as the
|
|
84
|
+
* "this asset is not the one" it means. `exactOptionalPropertyTypes` is on, so
|
|
85
|
+
* these are built by spread — assigning `undefined` would emit the key.
|
|
86
|
+
*/
|
|
87
|
+
export interface FindingCountSummary {
|
|
88
|
+
errors?: number;
|
|
89
|
+
warnings?: number;
|
|
90
|
+
info?: number;
|
|
91
|
+
/** Per-code tally, ordered descending by count then by code name. */
|
|
92
|
+
codes: Record<string, number>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Project a set of issues onto its counts: severity buckets plus a per-code tally.
|
|
96
|
+
*
|
|
97
|
+
* Severity counting delegates to `countBySeverity` — there must be exactly one
|
|
98
|
+
* severity collapse in this codebase (see this module's header). That delegation
|
|
99
|
+
* carries its rule with it: `ignore` findings land in NO severity bucket, because
|
|
100
|
+
* the adopter's `validation.allow` config silenced them.
|
|
101
|
+
*
|
|
102
|
+
* They do still appear in `codes`, because they were emitted — a code the adopter
|
|
103
|
+
* allow-listed would otherwise be invisible in every summary, which is the one
|
|
104
|
+
* place a reviewer would look to notice an allow-list that has quietly grown.
|
|
105
|
+
*
|
|
106
|
+
* `codes` is ordered descending by count, ties broken by code name ascending.
|
|
107
|
+
* Insertion order is the YAML serialization order, so the ordering is the feature:
|
|
108
|
+
* the first row names the dominant finding without the reader tallying anything.
|
|
109
|
+
*/
|
|
110
|
+
export declare function summarizeFindings(issues: readonly ValidationIssue[]): FindingCountSummary;
|
|
111
|
+
/**
|
|
112
|
+
* The findings a human-facing report renders IN FULL at a given verbosity.
|
|
113
|
+
*
|
|
114
|
+
* One policy, spelled once, because the three verbs that report findings —
|
|
115
|
+
* `vat skills validate`, `vat skills build`, `vat audit` — each grew their own
|
|
116
|
+
* answer and landed on three different ones: validate collapsed every severity
|
|
117
|
+
* into a per-skill count line, while build and audit printed every occurrence.
|
|
118
|
+
* On a 90-skill adopter that read as 804 lines against 6,552 and 26,480, for
|
|
119
|
+
* the same corpus.
|
|
120
|
+
*
|
|
121
|
+
* The rule is asymmetric on purpose, and the asymmetry is the whole point:
|
|
122
|
+
*
|
|
123
|
+
* - **`error` is always rendered in full**, at every verbosity. An error is what
|
|
124
|
+
* failed the run, there are few of them, and every one is actionable. Collapsing
|
|
125
|
+
* an error into a count means the reader must re-run with a flag to learn what
|
|
126
|
+
* broke — which is strictly worse than the verbosity it was meant to fix. This
|
|
127
|
+
* is the direction the module header warns about: a collapse that hides the
|
|
128
|
+
* thing you needed produces silence, not a false alarm.
|
|
129
|
+
* - **`warning` and `info` collapse into the caller's summary line** unless
|
|
130
|
+
* `verbose`. These are the high-cardinality findings (one adopter skill carries
|
|
131
|
+
* 348 `LINK_DROPPED_BY_DEPTH` warnings alone), and a count plus a per-code tally
|
|
132
|
+
* answers "what is going on here?" without the reader scrolling past 348 near-
|
|
133
|
+
* identical blocks.
|
|
134
|
+
* - **`ignore` never renders**, at any verbosity: the adopter's `validation.allow`
|
|
135
|
+
* config silenced it deliberately. It stays visible in `summarizeFindings`'
|
|
136
|
+
* `codes` tally, which is where a quietly-growing allow-list is noticed.
|
|
137
|
+
*
|
|
138
|
+
* This decides only WHICH findings are rendered, never HOW. Each lane keeps its
|
|
139
|
+
* own heading and summary line — those legitimately differ — so this can be shared
|
|
140
|
+
* without forcing one layout onto three commands.
|
|
141
|
+
*
|
|
142
|
+
* The machine-readable document is NOT filtered by this. `vat audit`'s YAML and
|
|
143
|
+
* `vat skills build`'s `issueCounts` carry every finding at every verbosity;
|
|
144
|
+
* anything else would silently break consumers that parse them.
|
|
145
|
+
*/
|
|
146
|
+
export declare function issuesToRenderAtVerbosity(issues: readonly ValidationIssue[], verbose: boolean): readonly ValidationIssue[];
|
|
59
147
|
/** Sum severity counts across lanes (per-skill → per-plugin → per-run). */
|
|
60
148
|
export declare function sumSeverityCounts(counts: readonly SeverityCounts[]): SeverityCounts;
|
|
61
149
|
//# sourceMappingURL=issue-rendering.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-rendering.d.ts","sourceRoot":"","sources":["../../src/utils/issue-rendering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAiB3E,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,MAAM,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAYtE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,IAAI,CAAC,EAAE,MAAM,GACZ,MAAM,CAKR;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAY9E;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB,GAAG,eAAe,EAAE,CAapF;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,CASnF"}
|
|
1
|
+
{"version":3,"file":"issue-rendering.d.ts","sourceRoot":"","sources":["../../src/utils/issue-rendering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAiB3E,gDAAgD;AAChD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,MAAM,CAE3E;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAYtE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,IAAI,CAAC,EAAE,MAAM,GACZ,MAAM,CAKR;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,eAAe,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAY9E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,GAAG,MAAM,EAAE,CAUnF;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,kBAAkB,GAAG,eAAe,EAAE,CAapF;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,eAAe,EAAE,GAAG,mBAAmB,CAuBzF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,OAAO,EAAE,OAAO,GACf,SAAS,eAAe,EAAE,CAI5B;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,GAAG,cAAc,CASnF"}
|
|
@@ -85,6 +85,29 @@ export function formatIssueLines(issue, indent = '') {
|
|
|
85
85
|
}
|
|
86
86
|
return lines;
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Render the run-level findings section — findings that belong to the whole
|
|
90
|
+
* invocation rather than to any one skill (ALLOW_UNUSED is the only producer).
|
|
91
|
+
*
|
|
92
|
+
* Shared by `vat skills validate` and `vat skills build` so the two commands
|
|
93
|
+
* name the same concept the same way. Attributing these to a skill is the
|
|
94
|
+
* misreading the heading exists to prevent: `validation.allow` is declared once
|
|
95
|
+
* for the package, so an entry that matched nothing is a fact about the run.
|
|
96
|
+
*
|
|
97
|
+
* Returns `[]` for an empty set; callers own their surrounding blank lines.
|
|
98
|
+
*/
|
|
99
|
+
export function formatRunIssueLines(runIssues) {
|
|
100
|
+
if (runIssues.length === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const lines = [
|
|
103
|
+
'Run-level (project config, not any one skill):',
|
|
104
|
+
` ${formatIssueSetHeading(runIssues)}:`,
|
|
105
|
+
];
|
|
106
|
+
for (const issue of runIssues) {
|
|
107
|
+
lines.push(...formatIssueLines(issue, ' '));
|
|
108
|
+
}
|
|
109
|
+
return lines;
|
|
110
|
+
}
|
|
88
111
|
/** Identity of an issue for de-duplication across the two post-build channels. */
|
|
89
112
|
function issueIdentity(issue) {
|
|
90
113
|
return [
|
|
@@ -124,6 +147,77 @@ export function collectPostBuildIssues(result) {
|
|
|
124
147
|
}
|
|
125
148
|
return merged;
|
|
126
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Project a set of issues onto its counts: severity buckets plus a per-code tally.
|
|
152
|
+
*
|
|
153
|
+
* Severity counting delegates to `countBySeverity` — there must be exactly one
|
|
154
|
+
* severity collapse in this codebase (see this module's header). That delegation
|
|
155
|
+
* carries its rule with it: `ignore` findings land in NO severity bucket, because
|
|
156
|
+
* the adopter's `validation.allow` config silenced them.
|
|
157
|
+
*
|
|
158
|
+
* They do still appear in `codes`, because they were emitted — a code the adopter
|
|
159
|
+
* allow-listed would otherwise be invisible in every summary, which is the one
|
|
160
|
+
* place a reviewer would look to notice an allow-list that has quietly grown.
|
|
161
|
+
*
|
|
162
|
+
* `codes` is ordered descending by count, ties broken by code name ascending.
|
|
163
|
+
* Insertion order is the YAML serialization order, so the ordering is the feature:
|
|
164
|
+
* the first row names the dominant finding without the reader tallying anything.
|
|
165
|
+
*/
|
|
166
|
+
export function summarizeFindings(issues) {
|
|
167
|
+
const counts = countBySeverity(issues);
|
|
168
|
+
const tally = new Map();
|
|
169
|
+
for (const issue of issues) {
|
|
170
|
+
const code = String(issue.code);
|
|
171
|
+
tally.set(code, (tally.get(code) ?? 0) + 1);
|
|
172
|
+
}
|
|
173
|
+
// `Object.fromEntries` over a sorted array, rather than keyed assignment into a
|
|
174
|
+
// record: it preserves the sort as insertion order without a dynamic index write.
|
|
175
|
+
const codes = Object.fromEntries([...tally.entries()].sort(([codeA, countA], [codeB, countB]) => countB - countA || codeA.localeCompare(codeB)));
|
|
176
|
+
return {
|
|
177
|
+
...(counts.errors > 0 ? { errors: counts.errors } : {}),
|
|
178
|
+
...(counts.warnings > 0 ? { warnings: counts.warnings } : {}),
|
|
179
|
+
...(counts.info > 0 ? { info: counts.info } : {}),
|
|
180
|
+
codes,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The findings a human-facing report renders IN FULL at a given verbosity.
|
|
185
|
+
*
|
|
186
|
+
* One policy, spelled once, because the three verbs that report findings —
|
|
187
|
+
* `vat skills validate`, `vat skills build`, `vat audit` — each grew their own
|
|
188
|
+
* answer and landed on three different ones: validate collapsed every severity
|
|
189
|
+
* into a per-skill count line, while build and audit printed every occurrence.
|
|
190
|
+
* On a 90-skill adopter that read as 804 lines against 6,552 and 26,480, for
|
|
191
|
+
* the same corpus.
|
|
192
|
+
*
|
|
193
|
+
* The rule is asymmetric on purpose, and the asymmetry is the whole point:
|
|
194
|
+
*
|
|
195
|
+
* - **`error` is always rendered in full**, at every verbosity. An error is what
|
|
196
|
+
* failed the run, there are few of them, and every one is actionable. Collapsing
|
|
197
|
+
* an error into a count means the reader must re-run with a flag to learn what
|
|
198
|
+
* broke — which is strictly worse than the verbosity it was meant to fix. This
|
|
199
|
+
* is the direction the module header warns about: a collapse that hides the
|
|
200
|
+
* thing you needed produces silence, not a false alarm.
|
|
201
|
+
* - **`warning` and `info` collapse into the caller's summary line** unless
|
|
202
|
+
* `verbose`. These are the high-cardinality findings (one adopter skill carries
|
|
203
|
+
* 348 `LINK_DROPPED_BY_DEPTH` warnings alone), and a count plus a per-code tally
|
|
204
|
+
* answers "what is going on here?" without the reader scrolling past 348 near-
|
|
205
|
+
* identical blocks.
|
|
206
|
+
* - **`ignore` never renders**, at any verbosity: the adopter's `validation.allow`
|
|
207
|
+
* config silenced it deliberately. It stays visible in `summarizeFindings`'
|
|
208
|
+
* `codes` tally, which is where a quietly-growing allow-list is noticed.
|
|
209
|
+
*
|
|
210
|
+
* This decides only WHICH findings are rendered, never HOW. Each lane keeps its
|
|
211
|
+
* own heading and summary line — those legitimately differ — so this can be shared
|
|
212
|
+
* without forcing one layout onto three commands.
|
|
213
|
+
*
|
|
214
|
+
* The machine-readable document is NOT filtered by this. `vat audit`'s YAML and
|
|
215
|
+
* `vat skills build`'s `issueCounts` carry every finding at every verbosity;
|
|
216
|
+
* anything else would silently break consumers that parse them.
|
|
217
|
+
*/
|
|
218
|
+
export function issuesToRenderAtVerbosity(issues, verbose) {
|
|
219
|
+
return issues.filter((issue) => issue.severity !== 'ignore' && (verbose || issue.severity === 'error'));
|
|
220
|
+
}
|
|
127
221
|
/** Sum severity counts across lanes (per-skill → per-plugin → per-run). */
|
|
128
222
|
export function sumSeverityCounts(counts) {
|
|
129
223
|
return counts.reduce((acc, c) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-rendering.js","sourceRoot":"","sources":["../../src/utils/issue-rendering.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"issue-rendering.js","sourceRoot":"","sources":["../../src/utils/issue-rendering.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,eAAe,GAGhB,MAAM,kCAAkC,CAAC;AAG1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,eAAe,GAAgD;IACnE,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF,gDAAgD;AAChD,MAAM,UAAU,aAAa,CAAC,QAAqC;IACjE,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAsB;IAC5D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,QAAQ,WAAW,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QACpB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAkC,EAClC,IAAa;IAEb,MAAM,SAAS,GAAG,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5B,MAAM,SAAS,GAAG,uBAAuB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IACnE,OAAO,GAAG,KAAK,IAAI,SAAS,QAAQ,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,SAAS,GAAG,CAAC;AAC9E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAsB,EAAE,MAAM,GAAG,EAAE;IAClE,MAAM,KAAK,GAAG;QACZ,GAAG,MAAM,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;KAC/F,CAAC;IACF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,eAAe,MAAM,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,UAAU,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAAqC;IACvE,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG;QACZ,gDAAgD;QAChD,KAAK,qBAAqB,CAAC,SAAS,CAAC,GAAG;KACzC,CAAC;IACF,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kFAAkF;AAClF,SAAS,aAAa,CAAC,KAAsB;IAC3C,OAAO;QACL,KAAK,CAAC,IAAI;QACV,KAAK,CAAC,QAAQ;QACd,KAAK,CAAC,QAAQ,IAAI,EAAE;QACpB,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;QAClD,KAAK,CAAC,KAAK,IAAI,EAAE;QACjB,KAAK,CAAC,OAAO;KACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA0B;IAC/D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI;QAClB,GAAG,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,CAAC,mBAAmB,EAAE,SAAS,IAAI,EAAE,CAAC;KACjD,EAAE,CAAC;QACF,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YAAE,SAAS;QACjC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AA0BD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAkC;IAClE,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAEvC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,gFAAgF;IAChF,kFAAkF;IAClF,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAC9B,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,EAAE,CAC7D,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAC9C,CACF,CAAC;IAEF,OAAO;QACL,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,KAAK;KACN,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAkC,EAClC,OAAgB;IAEhB,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,KAAK,OAAO,CAAC,CAClF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,MAAiC;IACjE,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM;QAC7B,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ;QACnC,IAAI,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI;KACxB,CAAC,EACF,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CACpC,CAAC;AACJ,CAAC"}
|
package/dist/utils/output.d.ts
CHANGED
|
@@ -2,6 +2,60 @@
|
|
|
2
2
|
* Output utilities for structured data
|
|
3
3
|
* YAML output on stdout, logs on stderr
|
|
4
4
|
*/
|
|
5
|
+
/**
|
|
6
|
+
* Write `content` to stdout SYNCHRONOUSLY, in a loop until every byte is handed
|
|
7
|
+
* over.
|
|
8
|
+
*
|
|
9
|
+
* `process.stdout.write` is asynchronous whenever stdout is a PIPE, and every
|
|
10
|
+
* command in this CLI calls `process.exit()` the moment it has written its
|
|
11
|
+
* summary. `process.exit` does not drain a pending write, so everything past the
|
|
12
|
+
* first pipe buffer (64 KB on Linux/macOS) was silently discarded — with exit
|
|
13
|
+
* code 0, so nothing signalled the loss. An interactive TTY is unbuffered and
|
|
14
|
+
* looked perfect, which is why this survived: it only bites the piped consumer
|
|
15
|
+
* this CLI's own docs recommend (`vat command | jq .status`).
|
|
16
|
+
*
|
|
17
|
+
* `writeSync` cannot return before the bytes reach the pipe, so `process.exit`
|
|
18
|
+
* has nothing left to lose. The loop is required because a single `writeSync`
|
|
19
|
+
* may accept fewer bytes than offered.
|
|
20
|
+
*
|
|
21
|
+
* This is the same fix `help-loader.ts` applied to `--help --verbose`; it lives
|
|
22
|
+
* here so both callers share one implementation rather than two copies of a
|
|
23
|
+
* subtle loop.
|
|
24
|
+
*/
|
|
25
|
+
export declare function writeStdoutSync(content: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* A synchronous write primitive: `(buffer, offset, length) => bytesWritten`.
|
|
28
|
+
*
|
|
29
|
+
* Matches `fs.writeSync`'s shape so the real one drops straight in, while a test
|
|
30
|
+
* can supply one that returns short counts or throws EAGAIN on demand.
|
|
31
|
+
*/
|
|
32
|
+
export type SyncWriter = (buffer: Buffer, offset: number, length: number) => number;
|
|
33
|
+
export interface WriteAllSyncOptions {
|
|
34
|
+
/** Override {@link MAX_CONSECUTIVE_EAGAIN_RETRIES}. Tests use a tiny budget. */
|
|
35
|
+
maxConsecutiveRetries?: number;
|
|
36
|
+
/** Override the backoff. Tests pass a no-op to drive the bound instantly. */
|
|
37
|
+
waitBetweenRetries?: (consecutiveRetries: number) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Drive `write` until every byte of `buffer` has been accepted.
|
|
41
|
+
*
|
|
42
|
+
* Split out from {@link writeStdoutSync} because the things that make this loop
|
|
43
|
+
* necessary — a short write, and an EAGAIN retry on a momentarily-full
|
|
44
|
+
* non-blocking pipe — are both hard to provoke on a real fd and trivial to state
|
|
45
|
+
* against an injected writer. The fd-1 wiring stays a one-liner above, so the
|
|
46
|
+
* only part with branching logic is the part that can be unit tested.
|
|
47
|
+
*
|
|
48
|
+
* The EAGAIN retry is BOUNDED. A reader that stops draining without closing the
|
|
49
|
+
* pipe (`vat … | consumer-that-wanders-off`) leaves it permanently full, so
|
|
50
|
+
* every retry raises EAGAIN forever; an unbounded loop turns that into a hang
|
|
51
|
+
* with no output and no error — strictly worse than the truncation this replaced.
|
|
52
|
+
* Exhausting the budget throws, so the failure is visible and the exit code is
|
|
53
|
+
* non-zero, rather than the bytes quietly going missing.
|
|
54
|
+
*
|
|
55
|
+
* @throws the underlying error for any failure other than EAGAIN; an `Error`
|
|
56
|
+
* naming the unwritten remainder when the retry budget is exhausted.
|
|
57
|
+
*/
|
|
58
|
+
export declare function writeAllSync(write: SyncWriter, buffer: Buffer, options?: WriteAllSyncOptions): void;
|
|
5
59
|
/**
|
|
6
60
|
* Write a single YAML document to stdout, opened with `---`.
|
|
7
61
|
*
|
|
@@ -22,11 +76,6 @@
|
|
|
22
76
|
* @param data - Data to serialize as YAML
|
|
23
77
|
*/
|
|
24
78
|
export declare function writeYamlOutput(data: unknown): void;
|
|
25
|
-
/**
|
|
26
|
-
* Flush stdout before writing to stderr
|
|
27
|
-
* Prevents output corruption when streams are merged
|
|
28
|
-
*/
|
|
29
|
-
export declare function flushStdout(): Promise<void>;
|
|
30
79
|
/**
|
|
31
80
|
* Write a test-format finding to stderr.
|
|
32
81
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKrD;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;AAoCpF,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,CAAC,kBAAkB,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,mBAAwB,GAChC,IAAI,CA+BN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAWnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,IAAI,CAEN"}
|
package/dist/utils/output.js
CHANGED
|
@@ -2,7 +2,111 @@
|
|
|
2
2
|
* Output utilities for structured data
|
|
3
3
|
* YAML output on stdout, logs on stderr
|
|
4
4
|
*/
|
|
5
|
+
import { writeSync } from 'node:fs';
|
|
5
6
|
import * as yaml from 'yaml';
|
|
7
|
+
/**
|
|
8
|
+
* Write `content` to stdout SYNCHRONOUSLY, in a loop until every byte is handed
|
|
9
|
+
* over.
|
|
10
|
+
*
|
|
11
|
+
* `process.stdout.write` is asynchronous whenever stdout is a PIPE, and every
|
|
12
|
+
* command in this CLI calls `process.exit()` the moment it has written its
|
|
13
|
+
* summary. `process.exit` does not drain a pending write, so everything past the
|
|
14
|
+
* first pipe buffer (64 KB on Linux/macOS) was silently discarded — with exit
|
|
15
|
+
* code 0, so nothing signalled the loss. An interactive TTY is unbuffered and
|
|
16
|
+
* looked perfect, which is why this survived: it only bites the piped consumer
|
|
17
|
+
* this CLI's own docs recommend (`vat command | jq .status`).
|
|
18
|
+
*
|
|
19
|
+
* `writeSync` cannot return before the bytes reach the pipe, so `process.exit`
|
|
20
|
+
* has nothing left to lose. The loop is required because a single `writeSync`
|
|
21
|
+
* may accept fewer bytes than offered.
|
|
22
|
+
*
|
|
23
|
+
* This is the same fix `help-loader.ts` applied to `--help --verbose`; it lives
|
|
24
|
+
* here so both callers share one implementation rather than two copies of a
|
|
25
|
+
* subtle loop.
|
|
26
|
+
*/
|
|
27
|
+
export function writeStdoutSync(content) {
|
|
28
|
+
writeAllSync((buffer, offset, length) => writeSync(1, buffer, offset, length), Buffer.from(content, 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* How many CONSECUTIVE EAGAIN retries — retries during which the writer accepted
|
|
32
|
+
* zero bytes — are tolerated before the write is declared stalled.
|
|
33
|
+
*
|
|
34
|
+
* Consecutive, not cumulative: a large payload to a slow reader legitimately
|
|
35
|
+
* EAGAINs thousands of times in total, and every one of those is followed by
|
|
36
|
+
* progress. What is never legitimate is the pipe staying full while the process
|
|
37
|
+
* on the other end takes nothing at all. Combined with the wait below, the
|
|
38
|
+
* budget is roughly ten seconds of a completely stalled reader.
|
|
39
|
+
*/
|
|
40
|
+
const MAX_CONSECUTIVE_EAGAIN_RETRIES = 10_000;
|
|
41
|
+
/** Consecutive retries to spin on before sleeping between attempts. */
|
|
42
|
+
const EAGAIN_SPIN_RETRIES = 16;
|
|
43
|
+
/**
|
|
44
|
+
* Sleep synchronously. Required because this whole path exists to run to
|
|
45
|
+
* completion before `process.exit`, so it cannot yield to the event loop; a
|
|
46
|
+
* timer would never fire. `Atomics.wait` on a never-notified buffer is the one
|
|
47
|
+
* primitive that blocks the thread without burning CPU.
|
|
48
|
+
*/
|
|
49
|
+
function sleepSync(ms) {
|
|
50
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Spin for the first few retries — a draining pipe frees space in microseconds,
|
|
54
|
+
* and paying a millisecond for that would be absurd — then back off to a real
|
|
55
|
+
* sleep so a genuinely stalled reader is waited out rather than spun on.
|
|
56
|
+
*/
|
|
57
|
+
function defaultWaitBetweenRetries(consecutiveRetries) {
|
|
58
|
+
if (consecutiveRetries > EAGAIN_SPIN_RETRIES)
|
|
59
|
+
sleepSync(1);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Drive `write` until every byte of `buffer` has been accepted.
|
|
63
|
+
*
|
|
64
|
+
* Split out from {@link writeStdoutSync} because the things that make this loop
|
|
65
|
+
* necessary — a short write, and an EAGAIN retry on a momentarily-full
|
|
66
|
+
* non-blocking pipe — are both hard to provoke on a real fd and trivial to state
|
|
67
|
+
* against an injected writer. The fd-1 wiring stays a one-liner above, so the
|
|
68
|
+
* only part with branching logic is the part that can be unit tested.
|
|
69
|
+
*
|
|
70
|
+
* The EAGAIN retry is BOUNDED. A reader that stops draining without closing the
|
|
71
|
+
* pipe (`vat … | consumer-that-wanders-off`) leaves it permanently full, so
|
|
72
|
+
* every retry raises EAGAIN forever; an unbounded loop turns that into a hang
|
|
73
|
+
* with no output and no error — strictly worse than the truncation this replaced.
|
|
74
|
+
* Exhausting the budget throws, so the failure is visible and the exit code is
|
|
75
|
+
* non-zero, rather than the bytes quietly going missing.
|
|
76
|
+
*
|
|
77
|
+
* @throws the underlying error for any failure other than EAGAIN; an `Error`
|
|
78
|
+
* naming the unwritten remainder when the retry budget is exhausted.
|
|
79
|
+
*/
|
|
80
|
+
export function writeAllSync(write, buffer, options = {}) {
|
|
81
|
+
const maxRetries = options.maxConsecutiveRetries ?? MAX_CONSECUTIVE_EAGAIN_RETRIES;
|
|
82
|
+
const wait = options.waitBetweenRetries ?? defaultWaitBetweenRetries;
|
|
83
|
+
let written = 0;
|
|
84
|
+
let consecutiveRetries = 0;
|
|
85
|
+
while (written < buffer.length) {
|
|
86
|
+
try {
|
|
87
|
+
const n = write(buffer, written, buffer.length - written);
|
|
88
|
+
// A writer that reports no progress would spin forever. Treat it as a
|
|
89
|
+
// failure rather than hang the CLI.
|
|
90
|
+
if (n <= 0) {
|
|
91
|
+
throw new Error(`Refusing to loop forever: writer accepted ${n} bytes of ${buffer.length - written} remaining`);
|
|
92
|
+
}
|
|
93
|
+
written += n;
|
|
94
|
+
consecutiveRetries = 0;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// A non-blocking pipe whose buffer is momentarily full raises EAGAIN; a
|
|
98
|
+
// reader that is draining will free space, so retry. Anything else is a
|
|
99
|
+
// genuine write failure.
|
|
100
|
+
if (error.code !== 'EAGAIN')
|
|
101
|
+
throw error;
|
|
102
|
+
consecutiveRetries++;
|
|
103
|
+
if (consecutiveRetries > maxRetries) {
|
|
104
|
+
throw new Error(`Write stalled: ${maxRetries} consecutive EAGAIN retries with no progress after ${written} of ${buffer.length} bytes. The reader is not draining the pipe.`);
|
|
105
|
+
}
|
|
106
|
+
wait(consecutiveRetries);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
6
110
|
/**
|
|
7
111
|
* Write a single YAML document to stdout, opened with `---`.
|
|
8
112
|
*
|
|
@@ -23,26 +127,16 @@ import * as yaml from 'yaml';
|
|
|
23
127
|
* @param data - Data to serialize as YAML
|
|
24
128
|
*/
|
|
25
129
|
export function writeYamlOutput(data) {
|
|
26
|
-
|
|
27
|
-
|
|
130
|
+
// Deliberately the ordinary stream write, NOT writeStdoutSync: this summary
|
|
131
|
+
// follows human-readable progress output written via console.log, and a
|
|
132
|
+
// synchronous fd-1 write would jump ahead of anything still buffered there.
|
|
133
|
+
// Completeness is guaranteed instead by makeStdioBlocking() at startup, which
|
|
134
|
+
// makes BOTH channels synchronous and keeps them in order.
|
|
135
|
+
process.stdout.write(`---\n${yaml.stringify(data, {
|
|
28
136
|
indent: 2,
|
|
29
137
|
lineWidth: 120,
|
|
30
138
|
aliasDuplicateObjects: false,
|
|
31
|
-
}));
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Flush stdout before writing to stderr
|
|
35
|
-
* Prevents output corruption when streams are merged
|
|
36
|
-
*/
|
|
37
|
-
export async function flushStdout() {
|
|
38
|
-
return new Promise((resolve) => {
|
|
39
|
-
if (process.stdout.writableNeedDrain) {
|
|
40
|
-
process.stdout.once('drain', resolve);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
resolve();
|
|
44
|
-
}
|
|
45
|
-
});
|
|
139
|
+
})}`);
|
|
46
140
|
}
|
|
47
141
|
/**
|
|
48
142
|
* Write a test-format finding to stderr.
|