kantban-cli 0.1.15 → 0.1.17

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.
@@ -0,0 +1,102 @@
1
+ import {
2
+ parseTimeout
3
+ } from "./chunk-MN4H5NSU.js";
4
+
5
+ // src/lib/gate-runner.ts
6
+ import { execFile } from "child_process";
7
+ async function runGate(gate, options = {}) {
8
+ const timeoutMs = gate.timeout ? parseTimeout(gate.timeout) : options.timeoutMs ?? 6e4;
9
+ const start = Date.now();
10
+ return new Promise((resolve) => {
11
+ const child = execFile("sh", ["-c", gate.run], {
12
+ timeout: timeoutMs,
13
+ cwd: options.cwd,
14
+ env: { ...process.env, ...options.env },
15
+ maxBuffer: options.maxBuffer ?? 1024 * 1024
16
+ // 1MB output cap
17
+ }, (error, stdout, stderr) => {
18
+ const duration_ms = Date.now() - start;
19
+ const output = (stdout + stderr).trim();
20
+ const timed_out = error?.killed === true;
21
+ const buffer_exceeded = error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
22
+ if (error) {
23
+ let annotation = "";
24
+ if (timed_out) annotation = `
25
+ [TIMED OUT after ${timeoutMs}ms]`;
26
+ else if (buffer_exceeded) annotation = `
27
+ [OUTPUT TRUNCATED \u2014 buffer limit exceeded]`;
28
+ resolve({
29
+ name: gate.name,
30
+ passed: false,
31
+ required: gate.required ?? true,
32
+ duration_ms,
33
+ output: output + annotation,
34
+ stderr: stderr.trim(),
35
+ exit_code: child.exitCode ?? (typeof error.code === "number" ? error.code : 1),
36
+ timed_out: timed_out || buffer_exceeded
37
+ });
38
+ return;
39
+ }
40
+ resolve({
41
+ name: gate.name,
42
+ passed: true,
43
+ required: gate.required ?? true,
44
+ duration_ms,
45
+ output,
46
+ stderr: stderr.trim(),
47
+ exit_code: 0,
48
+ timed_out: false
49
+ });
50
+ });
51
+ });
52
+ }
53
+ function formatGateErrors(results) {
54
+ const failures = results.filter((r) => !r.passed);
55
+ if (failures.length === 0) return "All gates passed.";
56
+ return failures.map((r) => {
57
+ const lines = [`Gate "${r.name}" (${r.required ? "required" : "advisory"}): FAILED`];
58
+ lines.push(` Exit code: ${r.exit_code}`);
59
+ if (r.timed_out) lines.push(" Timed out: yes");
60
+ if (r.output) {
61
+ const stderr = r.output.split("\n").slice(-20).join("\n");
62
+ lines.push(` Output:
63
+ ${stderr.replace(/\n/g, "\n ")}`);
64
+ }
65
+ return lines.join("\n");
66
+ }).join("\n\n");
67
+ }
68
+ async function runGates(gates, options = {}) {
69
+ const results = [];
70
+ const totalStart = Date.now();
71
+ for (const gate of gates) {
72
+ if (options.totalTimeoutMs) {
73
+ const elapsed = Date.now() - totalStart;
74
+ if (elapsed >= options.totalTimeoutMs) {
75
+ results.push({
76
+ name: gate.name,
77
+ passed: false,
78
+ required: gate.required ?? true,
79
+ duration_ms: 0,
80
+ output: "[SKIPPED \u2014 total timeout exceeded]",
81
+ stderr: "",
82
+ exit_code: -1,
83
+ timed_out: true
84
+ });
85
+ continue;
86
+ }
87
+ const remainingMs = options.totalTimeoutMs - elapsed;
88
+ const gateTimeout = gate.timeout ? parseTimeout(gate.timeout) : options.timeoutMs ?? 6e4;
89
+ const effectiveTimeout = Math.min(gateTimeout, remainingMs);
90
+ results.push(await runGate(gate, { ...options, timeoutMs: effectiveTimeout }));
91
+ } else {
92
+ results.push(await runGate(gate, options));
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+
98
+ export {
99
+ formatGateErrors,
100
+ runGates
101
+ };
102
+ //# sourceMappingURL=chunk-4IUZAIFL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/gate-runner.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { parseTimeout } from './gate-config.js';\nimport type { GateDefinition, GateResult } from '@kantban/types';\n\nexport interface RunOptions {\n timeoutMs?: number;\n totalTimeoutMs?: number;\n cwd?: string;\n env?: Record<string, string>;\n maxBuffer?: number;\n}\n\nexport async function runGate(\n gate: GateDefinition,\n options: RunOptions = {},\n): Promise<GateResult> {\n const timeoutMs = gate.timeout ? parseTimeout(gate.timeout) : (options.timeoutMs ?? 60_000);\n const start = Date.now();\n\n return new Promise<GateResult>((resolve) => {\n // SECURITY: gate.run is an operator-authored shell command from pipeline.gates.yaml.\n // It is executed with full process privileges via `sh -c`. This is intentional —\n // gates are trusted operator code, not user input. If gate config sources are ever\n // extended to accept user-supplied values, sanitization must be added here.\n const child = execFile('sh', ['-c', gate.run], {\n timeout: timeoutMs,\n cwd: options.cwd,\n env: { ...process.env, ...options.env },\n maxBuffer: options.maxBuffer ?? 1024 * 1024, // 1MB output cap\n }, (error, stdout, stderr) => {\n const duration_ms = Date.now() - start;\n const output = (stdout + stderr).trim();\n const timed_out = error?.killed === true;\n const buffer_exceeded = (error as NodeJS.ErrnoException)?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER';\n\n if (error) {\n let annotation = '';\n if (timed_out) annotation = `\\n[TIMED OUT after ${timeoutMs}ms]`;\n else if (buffer_exceeded) annotation = `\\n[OUTPUT TRUNCATED — buffer limit exceeded]`;\n\n resolve({\n name: gate.name,\n passed: false,\n required: gate.required ?? true,\n duration_ms,\n output: output + annotation,\n stderr: stderr.trim(),\n exit_code: child.exitCode ?? (typeof error.code === 'number' ? error.code : 1),\n timed_out: timed_out || buffer_exceeded,\n });\n return;\n }\n\n resolve({\n name: gate.name,\n passed: true,\n required: gate.required ?? true,\n duration_ms,\n output,\n stderr: stderr.trim(),\n exit_code: 0,\n timed_out: false,\n });\n });\n });\n}\n\n/** Format gate failures into a structured error string for agent/CLI display. */\nexport function formatGateErrors(results: GateResult[]): string {\n const failures = results.filter((r) => !r.passed);\n if (failures.length === 0) return 'All gates passed.';\n\n return failures\n .map((r) => {\n const lines = [`Gate \"${r.name}\" (${r.required ? 'required' : 'advisory'}): FAILED`];\n lines.push(` Exit code: ${r.exit_code}`);\n if (r.timed_out) lines.push(' Timed out: yes');\n if (r.output) {\n const stderr = r.output.split('\\n').slice(-20).join('\\n');\n lines.push(` Output:\\n ${stderr.replace(/\\n/g, '\\n ')}`);\n }\n return lines.join('\\n');\n })\n .join('\\n\\n');\n}\n\nexport async function runGates(\n gates: GateDefinition[],\n options: RunOptions = {},\n): Promise<GateResult[]> {\n const results: GateResult[] = [];\n const totalStart = Date.now();\n\n for (const gate of gates) {\n // Check total timeout\n if (options.totalTimeoutMs) {\n const elapsed = Date.now() - totalStart;\n if (elapsed >= options.totalTimeoutMs) {\n // Remaining gates get timed-out results\n results.push({\n name: gate.name,\n passed: false,\n required: gate.required ?? true,\n duration_ms: 0,\n output: '[SKIPPED — total timeout exceeded]',\n stderr: '',\n exit_code: -1,\n timed_out: true,\n });\n continue;\n }\n // Reduce per-gate timeout by elapsed time\n const remainingMs = options.totalTimeoutMs - elapsed;\n const gateTimeout = gate.timeout ? parseTimeout(gate.timeout) : (options.timeoutMs ?? 60_000);\n const effectiveTimeout = Math.min(gateTimeout, remainingMs);\n results.push(await runGate(gate, { ...options, timeoutMs: effectiveTimeout }));\n } else {\n results.push(await runGate(gate, options));\n }\n }\n\n return results;\n}\n"],"mappings":"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","names":[]}