@procedures/shell 0.0.0-stage → 0.1.0

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.
Files changed (85) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +21 -0
  3. package/README.md +421 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/command/index.d.ts +18 -0
  6. package/dist/command/index.d.ts.map +1 -0
  7. package/dist/command/index.js +17 -0
  8. package/dist/command/index.js.map +1 -0
  9. package/dist/command/output.d.ts +22 -0
  10. package/dist/command/output.d.ts.map +1 -0
  11. package/dist/command/output.js +67 -0
  12. package/dist/command/output.js.map +1 -0
  13. package/dist/command/processes.d.ts +89 -0
  14. package/dist/command/processes.d.ts.map +1 -0
  15. package/dist/command/processes.js +223 -0
  16. package/dist/command/processes.js.map +1 -0
  17. package/dist/command/run.d.ts +48 -0
  18. package/dist/command/run.d.ts.map +1 -0
  19. package/dist/command/run.js +89 -0
  20. package/dist/command/run.js.map +1 -0
  21. package/dist/command/spawn.d.ts +26 -0
  22. package/dist/command/spawn.d.ts.map +1 -0
  23. package/dist/command/spawn.js +28 -0
  24. package/dist/command/spawn.js.map +1 -0
  25. package/dist/command/stream.d.ts +42 -0
  26. package/dist/command/stream.d.ts.map +1 -0
  27. package/dist/command/stream.js +158 -0
  28. package/dist/command/stream.js.map +1 -0
  29. package/dist/command/tree-kill.d.ts +26 -0
  30. package/dist/command/tree-kill.d.ts.map +1 -0
  31. package/dist/command/tree-kill.js +122 -0
  32. package/dist/command/tree-kill.js.map +1 -0
  33. package/dist/index.d.ts +31 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +33 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/module.d.ts +13 -0
  38. package/dist/module.d.ts.map +1 -0
  39. package/dist/module.js +79 -0
  40. package/dist/module.js.map +1 -0
  41. package/dist/procedures/shell/exec.d.ts +21 -0
  42. package/dist/procedures/shell/exec.d.ts.map +1 -0
  43. package/dist/procedures/shell/exec.js +45 -0
  44. package/dist/procedures/shell/exec.js.map +1 -0
  45. package/dist/procedures/shell/index.d.ts +5 -0
  46. package/dist/procedures/shell/index.d.ts.map +1 -0
  47. package/dist/procedures/shell/index.js +5 -0
  48. package/dist/procedures/shell/index.js.map +1 -0
  49. package/dist/procedures/shell/run.d.ts +14 -0
  50. package/dist/procedures/shell/run.d.ts.map +1 -0
  51. package/dist/procedures/shell/run.js +40 -0
  52. package/dist/procedures/shell/run.js.map +1 -0
  53. package/dist/procedures/shell/stream.d.ts +20 -0
  54. package/dist/procedures/shell/stream.d.ts.map +1 -0
  55. package/dist/procedures/shell/stream.js +39 -0
  56. package/dist/procedures/shell/stream.js.map +1 -0
  57. package/dist/procedures/shell/which.d.ts +10 -0
  58. package/dist/procedures/shell/which.d.ts.map +1 -0
  59. package/dist/procedures/shell/which.js +21 -0
  60. package/dist/procedures/shell/which.js.map +1 -0
  61. package/dist/register.d.ts +8 -0
  62. package/dist/register.d.ts.map +1 -0
  63. package/dist/register.js +14 -0
  64. package/dist/register.js.map +1 -0
  65. package/dist/types.d.ts +79 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +89 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +78 -3
  70. package/src/command/index.ts +24 -0
  71. package/src/command/output.ts +67 -0
  72. package/src/command/processes.ts +271 -0
  73. package/src/command/run.ts +127 -0
  74. package/src/command/spawn.ts +44 -0
  75. package/src/command/stream.ts +186 -0
  76. package/src/command/tree-kill.ts +121 -0
  77. package/src/index.ts +53 -0
  78. package/src/module.ts +102 -0
  79. package/src/procedures/shell/exec.ts +55 -0
  80. package/src/procedures/shell/index.ts +4 -0
  81. package/src/procedures/shell/run.ts +47 -0
  82. package/src/procedures/shell/stream.ts +43 -0
  83. package/src/procedures/shell/which.ts +26 -0
  84. package/src/register.ts +17 -0
  85. package/src/types.ts +172 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * streamCommand: run a program and give each output line while it runs.
3
+ *
4
+ * The queue between the program and the reader has a limit (deep dive WRP-9). When the queue
5
+ * is full, the helper stops reading the pipes of the program. The pipes fill, and the program
6
+ * waits in its next write. When the reader takes items again, the helper reads again. Before,
7
+ * the queue had no limit: a program with a large output filled the memory of the host.
8
+ */
9
+ import { StringDecoder } from "node:string_decoder";
10
+ import { killTree } from "./tree-kill.js";
11
+ import { startChild } from "./spawn.js";
12
+ const DEFAULT_HIGH_WATER_MARK = 1000;
13
+ const DEFAULT_MAX_LINE_LENGTH = 1024 * 1024;
14
+ /**
15
+ * This function runs `command` and yields each line of stdout and stderr, then one exit item.
16
+ * When the reader stops early, or the signal aborts, the process tree is killed. A program that
17
+ * cannot start makes the generator throw.
18
+ */
19
+ export async function* streamCommand(command, options = {}) {
20
+ const started = Date.now();
21
+ const highWaterMark = Math.max(1, options.highWaterMark ?? DEFAULT_HIGH_WATER_MARK);
22
+ const lowWaterMark = Math.floor(highWaterMark / 2);
23
+ const maxLineLength = Math.max(1, options.maxLineLength ?? DEFAULT_MAX_LINE_LENGTH);
24
+ const encoding = options.encoding ?? "utf8";
25
+ const items = [];
26
+ let ended = false;
27
+ let failure;
28
+ let wake;
29
+ const notify = () => {
30
+ const resume = wake;
31
+ wake = undefined;
32
+ resume?.();
33
+ };
34
+ if (options.signal?.aborted) {
35
+ yield { type: "exit", exitCode: 1, duration: 0, aborted: true };
36
+ return;
37
+ }
38
+ const child = startChild(command, options, options.input !== undefined ? "pipe" : "ignore");
39
+ let paused = false;
40
+ let timedOut = false;
41
+ let aborted = false;
42
+ const pause = () => {
43
+ if (paused)
44
+ return;
45
+ paused = true;
46
+ child.stdout.pause();
47
+ child.stderr.pause();
48
+ };
49
+ const resume = () => {
50
+ if (!paused)
51
+ return;
52
+ paused = false;
53
+ child.stdout.resume();
54
+ child.stderr.resume();
55
+ };
56
+ const push = (item) => {
57
+ items.push(item);
58
+ if (items.length >= highWaterMark)
59
+ pause();
60
+ notify();
61
+ };
62
+ // A decoder for each stream keeps a character whose bytes arrive in two chunks (deep dive WRP-2)
63
+ const partial = { stdout: "", stderr: "" };
64
+ const decoders = { stdout: new StringDecoder(encoding), stderr: new StringDecoder(encoding) };
65
+ const emit = (type, text) => {
66
+ if (text.length <= maxLineLength) {
67
+ push({ type, line: text });
68
+ return;
69
+ }
70
+ for (let at = 0; at < text.length; at += maxLineLength) {
71
+ push({ type, line: text.slice(at, at + maxLineLength) });
72
+ }
73
+ };
74
+ const onData = (type) => (data) => {
75
+ const lines = (partial[type] + decoders[type].write(data)).split(/\r?\n/);
76
+ let rest = lines.pop() ?? "";
77
+ for (const line of lines)
78
+ emit(type, line);
79
+ // A line without its end yet: give the full parts now, keep the remainder
80
+ if (rest.length >= maxLineLength) {
81
+ const whole = rest.length - (rest.length % maxLineLength);
82
+ emit(type, rest.slice(0, whole));
83
+ rest = rest.slice(whole);
84
+ }
85
+ partial[type] = rest;
86
+ };
87
+ child.stdout.on("data", onData("stdout"));
88
+ child.stderr.on("data", onData("stderr"));
89
+ if (options.input !== undefined) {
90
+ child.stdin.on("error", () => { });
91
+ child.stdin.end(options.input);
92
+ }
93
+ const timer = options.timeout !== undefined
94
+ ? setTimeout(() => {
95
+ timedOut = true;
96
+ killTree(child);
97
+ }, options.timeout)
98
+ : undefined;
99
+ const onAbort = () => {
100
+ aborted = true;
101
+ killTree(child);
102
+ };
103
+ options.signal?.addEventListener("abort", onAbort, { once: true });
104
+ child.on("error", (error) => {
105
+ if (child.pid === undefined) {
106
+ failure = { error };
107
+ notify();
108
+ }
109
+ });
110
+ child.on("close", (code, signal) => {
111
+ for (const type of ["stdout", "stderr"]) {
112
+ const rest = partial[type] + decoders[type].end();
113
+ if (rest)
114
+ emit(type, rest);
115
+ }
116
+ const exit = { type: "exit", exitCode: code ?? 1, duration: Date.now() - started };
117
+ const endSignal = signal ?? (timedOut || aborted ? "SIGTERM" : undefined);
118
+ if (endSignal)
119
+ exit.signal = endSignal;
120
+ if (timedOut)
121
+ exit.timedOut = true;
122
+ if (aborted)
123
+ exit.aborted = true;
124
+ items.push(exit);
125
+ ended = true;
126
+ notify();
127
+ });
128
+ try {
129
+ for (;;) {
130
+ if (items.length > 0) {
131
+ const item = items.shift();
132
+ if (items.length <= lowWaterMark)
133
+ resume();
134
+ yield item;
135
+ continue;
136
+ }
137
+ if (failure)
138
+ throw failure.error;
139
+ if (ended)
140
+ return;
141
+ await new Promise((resolve) => {
142
+ wake = resolve;
143
+ });
144
+ }
145
+ }
146
+ finally {
147
+ if (timer)
148
+ clearTimeout(timer);
149
+ options.signal?.removeEventListener("abort", onAbort);
150
+ // The reader stopped early: end the program and its descendants. The pipes read again, so
151
+ // they can close.
152
+ if (!ended) {
153
+ resume();
154
+ killTree(child);
155
+ }
156
+ }
157
+ }
158
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/command/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Kill a child process and all the processes that it started (deep dive WRP-10).
3
+ *
4
+ * `child.kill()` reaches only the direct child. On Windows it calls `TerminateProcess`, so the
5
+ * grandchildren keep running (a `pnpm` that starts `node`, a `vite` that starts `esbuild`).
6
+ * - On Windows, `taskkill /T /F` ends the tree of the process id.
7
+ * - Elsewhere, the child starts in its own process group (`detached`), and a signal to the
8
+ * negative pid reaches every process of the group.
9
+ *
10
+ * The module also records each live child. When the host process ends (a normal exit, or
11
+ * SIGINT, SIGTERM or SIGHUP), it kills each recorded tree, so no child keeps running alone.
12
+ */
13
+ import { type ChildProcess } from "node:child_process";
14
+ /**
15
+ * True when a child must start with `detached: true`. On POSIX, `detached` puts the child in a
16
+ * new process group, which `killTree` needs. On Windows, `detached` opens a new console.
17
+ */
18
+ export declare const OWN_PROCESS_GROUP: boolean;
19
+ /** This function kills the process tree of `child`. It does nothing when the child has ended. */
20
+ export declare function killTree(child: ChildProcess, signal?: NodeJS.Signals): void;
21
+ /**
22
+ * This function records a child until it ends. While at least one child is recorded, the hooks
23
+ * kill the recorded trees when the host ends. With no recorded child, the host has no hooks.
24
+ */
25
+ export declare function track(child: ChildProcess): void;
26
+ //# sourceMappingURL=tree-kill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-kill.d.ts","sourceRoot":"","sources":["../../src/command/tree-kill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAI5E;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,OAAkB,CAAC;AAMnD,iGAAiG;AACjG,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,IAAI,CAgBtF;AA8DD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAU/C"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Kill a child process and all the processes that it started (deep dive WRP-10).
3
+ *
4
+ * `child.kill()` reaches only the direct child. On Windows it calls `TerminateProcess`, so the
5
+ * grandchildren keep running (a `pnpm` that starts `node`, a `vite` that starts `esbuild`).
6
+ * - On Windows, `taskkill /T /F` ends the tree of the process id.
7
+ * - Elsewhere, the child starts in its own process group (`detached`), and a signal to the
8
+ * negative pid reaches every process of the group.
9
+ *
10
+ * The module also records each live child. When the host process ends (a normal exit, or
11
+ * SIGINT, SIGTERM or SIGHUP), it kills each recorded tree, so no child keeps running alone.
12
+ */
13
+ import { execFile, spawnSync } from "node:child_process";
14
+ const WINDOWS = process.platform === "win32";
15
+ /**
16
+ * True when a child must start with `detached: true`. On POSIX, `detached` puts the child in a
17
+ * new process group, which `killTree` needs. On Windows, `detached` opens a new console.
18
+ */
19
+ export const OWN_PROCESS_GROUP = !WINDOWS;
20
+ function running(child) {
21
+ return child.pid !== undefined && child.exitCode === null && child.signalCode === null;
22
+ }
23
+ /** This function kills the process tree of `child`. It does nothing when the child has ended. */
24
+ export function killTree(child, signal = "SIGTERM") {
25
+ if (!running(child))
26
+ return;
27
+ const pid = child.pid;
28
+ if (WINDOWS) {
29
+ execFile("taskkill", ["/T", "/F", "/PID", String(pid)], { windowsHide: true }, (error) => {
30
+ // taskkill fails when the process ended in the meantime, or when taskkill is missing
31
+ if (error && running(child))
32
+ child.kill(signal);
33
+ });
34
+ return;
35
+ }
36
+ try {
37
+ process.kill(-pid, signal);
38
+ }
39
+ catch {
40
+ // The child is not a group leader (it did not start detached): kill only the child
41
+ child.kill(signal);
42
+ }
43
+ }
44
+ /** The synchronous form, for the exit of the host, when callbacks do not run any more. */
45
+ function killTreeSync(child) {
46
+ if (!running(child))
47
+ return;
48
+ const pid = child.pid;
49
+ try {
50
+ if (WINDOWS) {
51
+ spawnSync("taskkill", ["/T", "/F", "/PID", String(pid)], { windowsHide: true, stdio: "ignore" });
52
+ }
53
+ else {
54
+ process.kill(-pid, "SIGKILL");
55
+ }
56
+ }
57
+ catch {
58
+ try {
59
+ child.kill("SIGKILL");
60
+ }
61
+ catch {
62
+ // The process ended
63
+ }
64
+ }
65
+ }
66
+ // =============================================================================
67
+ // The live children, and the hooks that kill them when the host ends
68
+ // =============================================================================
69
+ const live = new Set();
70
+ const SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
71
+ let hooked = false;
72
+ function killAll() {
73
+ for (const child of live)
74
+ killTreeSync(child);
75
+ live.clear();
76
+ }
77
+ function onExit() {
78
+ killAll();
79
+ }
80
+ function onSignal(signal) {
81
+ killAll();
82
+ // Without this handler, Node ends the process on the signal. When this handler is the only one,
83
+ // do the same: remove the hooks and send the signal again.
84
+ if (process.listenerCount(signal) === 1) {
85
+ unhook();
86
+ process.kill(process.pid, signal);
87
+ }
88
+ }
89
+ function hook() {
90
+ if (hooked)
91
+ return;
92
+ hooked = true;
93
+ process.on("exit", onExit);
94
+ for (const signal of SIGNALS)
95
+ process.on(signal, onSignal);
96
+ }
97
+ function unhook() {
98
+ if (!hooked)
99
+ return;
100
+ hooked = false;
101
+ process.off("exit", onExit);
102
+ for (const signal of SIGNALS)
103
+ process.off(signal, onSignal);
104
+ }
105
+ /**
106
+ * This function records a child until it ends. While at least one child is recorded, the hooks
107
+ * kill the recorded trees when the host ends. With no recorded child, the host has no hooks.
108
+ */
109
+ export function track(child) {
110
+ if (!running(child))
111
+ return;
112
+ live.add(child);
113
+ hook();
114
+ const forget = () => {
115
+ live.delete(child);
116
+ if (live.size === 0)
117
+ unhook();
118
+ };
119
+ child.once("exit", forget);
120
+ child.once("error", forget);
121
+ }
122
+ //# sourceMappingURL=tree-kill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree-kill.js","sourceRoot":"","sources":["../../src/command/tree-kill.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @procedures/shell
3
+ *
4
+ * Generic shell command execution procedures.
5
+ *
6
+ * Provides procedures for running CLI commands:
7
+ * - shell.run - Run command with args
8
+ * - shell.exec - Execute command string via shell
9
+ * - shell.which - Find command path
10
+ * - shell.stream - Run command with args and stream each output line
11
+ *
12
+ * The command helpers (`runCommand`, `streamCommand`, `processes`) are also available without
13
+ * the registration of the procedures, from `@procedures/shell/command`.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { Client } from "@procedures/client";
18
+ *
19
+ * const result = await client.call(["shell", "run"], {
20
+ * command: "git",
21
+ * args: ["status"],
22
+ * cwd: "/path/to/repo"
23
+ * });
24
+ * ```
25
+ */
26
+ export type { ShellRunInput, ShellRunOutput, ShellExecInput, ShellExecOutput, ShellWhichInput, ShellWhichOutput, ShellStreamInput, ShellStreamItem, } from "./types.js";
27
+ export { ShellRunInputSchema, ShellExecInputSchema, ShellWhichInputSchema, ShellStreamInputSchema, } from "./types.js";
28
+ export { shellRun, shellExec, shellWhich, shellStream } from "./procedures/shell/index.js";
29
+ export * from "./command/index.js";
30
+ export { registerShellProcedures } from "./register.js";
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,YAAY,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAG3F,cAAc,oBAAoB,CAAC;AAGnC,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @procedures/shell
3
+ *
4
+ * Generic shell command execution procedures.
5
+ *
6
+ * Provides procedures for running CLI commands:
7
+ * - shell.run - Run command with args
8
+ * - shell.exec - Execute command string via shell
9
+ * - shell.which - Find command path
10
+ * - shell.stream - Run command with args and stream each output line
11
+ *
12
+ * The command helpers (`runCommand`, `streamCommand`, `processes`) are also available without
13
+ * the registration of the procedures, from `@procedures/shell/command`.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { Client } from "@procedures/client";
18
+ *
19
+ * const result = await client.call(["shell", "run"], {
20
+ * command: "git",
21
+ * args: ["status"],
22
+ * cwd: "/path/to/repo"
23
+ * });
24
+ * ```
25
+ */
26
+ export { ShellRunInputSchema, ShellExecInputSchema, ShellWhichInputSchema, ShellStreamInputSchema, } from "./types.js";
27
+ // Procedures
28
+ export { shellRun, shellExec, shellWhich, shellStream } from "./procedures/shell/index.js";
29
+ // Command helpers
30
+ export * from "./command/index.js";
31
+ // Registration
32
+ export { registerShellProcedures } from "./register.js";
33
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAcH,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,aAAa;AACb,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE3F,kBAAkB;AAClB,cAAc,oBAAoB,CAAC;AAEnC,eAAe;AACf,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Module (no side effects): the procedures of this package as data, for `createRegistry()`.
3
+ *
4
+ * Procedure Registration for shell operations
5
+ */
6
+ import { type ProcedureModule } from "@procedures/client";
7
+ /**
8
+ * The module of this package, for `createRegistry()`: its procedures as data. Importing this
9
+ * file registers nothing. (`register.ts` registers the same procedures in the global registry.)
10
+ */
11
+ export declare const shellModule: ProcedureModule;
12
+ export default shellModule;
13
+ //# sourceMappingURL=module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAA6C,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoFrG;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,eAKtB,CAAC;eAEU,WAAW"}
package/dist/module.js ADDED
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Module (no side effects): the procedures of this package as data, for `createRegistry()`.
3
+ *
4
+ * Procedure Registration for shell operations
5
+ */
6
+ import { createProcedure, zodAdapter, defineModule } from "@procedures/client";
7
+ import { shellRun } from "./procedures/shell/run.js";
8
+ import { shellExec } from "./procedures/shell/exec.js";
9
+ import { shellWhich } from "./procedures/shell/which.js";
10
+ import { shellStream } from "./procedures/shell/stream.js";
11
+ import { ShellRunInputSchema, ShellExecInputSchema, ShellWhichInputSchema, ShellStreamInputSchema, ShellRunOutputSchema, ShellExecOutputSchema, ShellWhichOutputSchema, ShellStreamItemSchema, } from "./types.js";
12
+ // Procedure definitions
13
+ const shellRunProcedure = createProcedure()
14
+ .path(["shell", "run"])
15
+ .input(zodAdapter(ShellRunInputSchema))
16
+ .output(zodAdapter(ShellRunOutputSchema))
17
+ .meta({
18
+ description: "Run a command with arguments",
19
+ args: ["command"],
20
+ shorts: { cwd: "C", timeout: "t" },
21
+ output: "json",
22
+ })
23
+ .handler(async (input, ctx) => {
24
+ return shellRun(input, ctx);
25
+ })
26
+ .build();
27
+ const shellExecProcedure = createProcedure()
28
+ .path(["shell", "exec"])
29
+ .input(zodAdapter(ShellExecInputSchema))
30
+ .output(zodAdapter(ShellExecOutputSchema))
31
+ .meta({
32
+ description: "Execute command string via shell",
33
+ args: ["command"],
34
+ shorts: { cwd: "C", timeout: "t" },
35
+ output: "json",
36
+ })
37
+ .handler(async (input, ctx) => {
38
+ return shellExec(input, ctx);
39
+ })
40
+ .build();
41
+ const shellWhichProcedure = createProcedure()
42
+ .path(["shell", "which"])
43
+ .input(zodAdapter(ShellWhichInputSchema))
44
+ .output(zodAdapter(ShellWhichOutputSchema))
45
+ .meta({
46
+ description: "Find the path to a command",
47
+ args: ["command"],
48
+ shorts: {},
49
+ output: "json",
50
+ })
51
+ .handler(async (input, ctx) => {
52
+ return shellWhich(input, ctx);
53
+ })
54
+ .build();
55
+ const shellStreamProcedure = createProcedure()
56
+ .path(["shell", "stream"])
57
+ .input(zodAdapter(ShellStreamInputSchema))
58
+ .output(zodAdapter(ShellStreamItemSchema))
59
+ .meta({
60
+ description: "Run a command and stream each output line as it arrives",
61
+ args: ["command"],
62
+ shorts: { cwd: "C", timeout: "t" },
63
+ output: "json",
64
+ streaming: true,
65
+ })
66
+ .handler(shellStream)
67
+ .build();
68
+ /**
69
+ * The module of this package, for `createRegistry()`: its procedures as data. Importing this
70
+ * file registers nothing. (`register.ts` registers the same procedures in the global registry.)
71
+ */
72
+ export const shellModule = defineModule("@procedures/shell", [
73
+ shellRunProcedure,
74
+ shellExecProcedure,
75
+ shellWhichProcedure,
76
+ shellStreamProcedure,
77
+ ]);
78
+ export default shellModule;
79
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * shell.exec procedure
3
+ *
4
+ * Execute a command string via shell.
5
+ *
6
+ * This is the one procedure that runs a shell: the command is a shell command line. It is not an
7
+ * MCP tool, and a data-driven procedure cannot call it (BUGS-2026-07 H18). The shell runs through
8
+ * the command helpers, so the timeout and `ctx.signal` kill the shell and every program that it
9
+ * started (deep dive WRP-10), and the `stdin` input reaches the command.
10
+ */
11
+ import type { ShellExecInput, ShellExecOutput } from "../../types.js";
12
+ /** The program and the arguments that run `command` in a shell, as Node's `exec` does. */
13
+ export declare function shellCommandLine(command: string, shell: boolean | string): {
14
+ file: string;
15
+ args: string[];
16
+ verbatim: boolean;
17
+ };
18
+ export declare function shellExec(input: ShellExecInput, ctx?: {
19
+ signal?: AbortSignal | undefined;
20
+ }): Promise<ShellExecOutput>;
21
+ //# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../../src/procedures/shell/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtE,0FAA0F;AAC1F,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,OAAO,GAAG,MAAM,GACtB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAUrD;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,cAAc,EACrB,GAAG,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAE,GACzC,OAAO,CAAC,eAAe,CAAC,CAqB1B"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * shell.exec procedure
3
+ *
4
+ * Execute a command string via shell.
5
+ *
6
+ * This is the one procedure that runs a shell: the command is a shell command line. It is not an
7
+ * MCP tool, and a data-driven procedure cannot call it (BUGS-2026-07 H18). The shell runs through
8
+ * the command helpers, so the timeout and `ctx.signal` kill the shell and every program that it
9
+ * started (deep dive WRP-10), and the `stdin` input reaches the command.
10
+ */
11
+ import { runCommand } from "../../command/run.js";
12
+ /** The program and the arguments that run `command` in a shell, as Node's `exec` does. */
13
+ export function shellCommandLine(command, shell) {
14
+ if (process.platform === "win32") {
15
+ const file = typeof shell === "string" ? shell : process.env["ComSpec"] ?? "cmd.exe";
16
+ if (/(?:^|[\\/])cmd(?:\.exe)?$/i.test(file)) {
17
+ // cmd.exe reads the rest of its command line as it is, so the arguments get no quotes
18
+ return { file, args: ["/d", "/s", "/c", `"${command}"`], verbatim: true };
19
+ }
20
+ return { file, args: ["-c", command], verbatim: false };
21
+ }
22
+ return { file: typeof shell === "string" ? shell : "/bin/sh", args: ["-c", command], verbatim: false };
23
+ }
24
+ export async function shellExec(input, ctx) {
25
+ const { file, args, verbatim } = shellCommandLine(input.command, input.shell);
26
+ const result = await runCommand(file, {
27
+ args,
28
+ windowsVerbatimArguments: verbatim,
29
+ cwd: input.cwd,
30
+ env: input.env,
31
+ timeout: input.timeout,
32
+ signal: ctx?.signal,
33
+ input: input.stdin,
34
+ maxOutputBytes: input.maxBuffer,
35
+ });
36
+ return {
37
+ exitCode: result.exitCode,
38
+ stdout: result.stdout,
39
+ stderr: result.stderr,
40
+ success: result.success,
41
+ duration: result.duration,
42
+ signal: result.signal,
43
+ };
44
+ }
45
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../../src/procedures/shell/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGlD,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,KAAuB;IAEvB,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC;QACrF,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,sFAAsF;YACtF,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,OAAO,GAAG,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5E,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACzG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,KAAqB,EACrB,GAA0C;IAE1C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9E,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE;QACpC,IAAI;QACJ,wBAAwB,EAAE,QAAQ;QAClC,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,GAAG,EAAE,MAAM;QACnB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,cAAc,EAAE,KAAK,CAAC,SAAS;KAChC,CAAC,CAAC;IAEH,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { shellRun } from "./run.js";
2
+ export { shellExec } from "./exec.js";
3
+ export { shellWhich } from "./which.js";
4
+ export { shellStream } from "./stream.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/procedures/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { shellRun } from "./run.js";
2
+ export { shellExec } from "./exec.js";
3
+ export { shellWhich } from "./which.js";
4
+ export { shellStream } from "./stream.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/procedures/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * shell.run procedure
3
+ *
4
+ * Run a command with arguments and return output.
5
+ */
6
+ import type { ShellRunInput, ShellRunOutput } from "../../types.js";
7
+ /**
8
+ * Run a command with its arguments and no shell. The timeout and `ctx.signal` kill the whole
9
+ * process tree (deep dive WRP-10).
10
+ */
11
+ export declare function shellRun(input: ShellRunInput, ctx?: {
12
+ signal?: AbortSignal | undefined;
13
+ }): Promise<ShellRunOutput>;
14
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/procedures/shell/run.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQpE;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,aAAa,EACpB,GAAG,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAAE,GACzC,OAAO,CAAC,cAAc,CAAC,CAwBzB"}