poe-code 3.0.266 → 3.0.267

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "poe-code",
3
- "version": "3.0.266",
3
+ "version": "3.0.267",
4
4
  "description": "CLI tool to configure Poe API for developer workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,4 +1,4 @@
1
- import type { Readable, Writable } from "node:stream";
1
+ import { type Readable, type Writable } from "node:stream";
2
2
  import type { SpawnResult, SpawnUsage } from "../../agent-spawn/dist/index.js";
3
3
  export type SpawnProcess = typeof import("node:child_process").spawn;
4
4
  export type AgentChildProcessKind = "exec" | "execFile" | "spawn";
@@ -1,4 +1,5 @@
1
1
  import { spawn as defaultSpawn } from "node:child_process";
2
+ import { PassThrough } from "node:stream";
2
3
  import { StringDecoder } from "node:string_decoder";
3
4
  export class AgentChildProcessError extends Error {
4
5
  result;
@@ -9,7 +10,10 @@ export class AgentChildProcessError extends Error {
9
10
  }
10
11
  }
11
12
  export async function exec(command, options) {
12
- const shell = process.platform === "win32" ? process.env.ComSpec ?? "cmd.exe" : process.env.SHELL ?? "sh";
13
+ assertNonBlank(command, "command");
14
+ const shell = process.platform === "win32"
15
+ ? (options?.env?.ComSpec ?? process.env.ComSpec ?? "cmd.exe")
16
+ : (options?.env?.SHELL ?? process.env.SHELL ?? "sh");
13
17
  const shellArgs = process.platform === "win32" ? ["/d", "/s", "/c", command] : ["-c", command];
14
18
  return runExecution({
15
19
  kind: "exec",
@@ -21,6 +25,7 @@ export async function exec(command, options) {
21
25
  });
22
26
  }
23
27
  export async function execFile(file, argsOrOptions = [], maybeOptions) {
28
+ assertNonBlank(file, "file");
24
29
  const args = Array.isArray(argsOrOptions) ? argsOrOptions : [];
25
30
  const options = Array.isArray(argsOrOptions) ? maybeOptions : argsOrOptions;
26
31
  return runExecution({
@@ -33,10 +38,11 @@ export async function execFile(file, argsOrOptions = [], maybeOptions) {
33
38
  });
34
39
  }
35
40
  export function spawn(file, argsOrOptions = [], maybeOptions) {
36
- const args = Array.isArray(argsOrOptions) ? argsOrOptions : [];
37
- const options = Array.isArray(argsOrOptions) ? maybeOptions : argsOrOptions;
38
41
  let child;
39
42
  try {
43
+ assertNonBlank(file, "file");
44
+ const args = Array.isArray(argsOrOptions) ? argsOrOptions : [];
45
+ const options = Array.isArray(argsOrOptions) ? maybeOptions : argsOrOptions;
40
46
  child = startChildProcess({
41
47
  kind: "spawn",
42
48
  file,
@@ -59,8 +65,8 @@ export function spawn(file, argsOrOptions = [], maybeOptions) {
59
65
  return {
60
66
  pid: child.process.pid,
61
67
  stdin: child.process.stdin,
62
- stdout: child.process.stdout,
63
- stderr: child.process.stderr,
68
+ stdout: child.stdout,
69
+ stderr: child.stderr,
64
70
  kill(signal) {
65
71
  return child.process.kill(signal);
66
72
  },
@@ -72,8 +78,13 @@ async function runExecution(spec) {
72
78
  }
73
79
  function startChildProcess(spec) {
74
80
  const child = spawnChild(spec);
75
- const result = collectResult(child, spec).then((commandResult) => applyExitPolicy(commandResult, spec.options));
76
- return { process: child, result };
81
+ const stdout = createOutputPipes(child.stdout, spec.kind === "spawn");
82
+ const stderr = createOutputPipes(child.stderr, spec.kind === "spawn");
83
+ const result = collectResult(child, spec, {
84
+ stdout: stdout.result,
85
+ stderr: stderr.result
86
+ }).then((commandResult) => applyExitPolicy(commandResult, spec.options));
87
+ return { process: child, stdout: stdout.user, stderr: stderr.user, result };
77
88
  }
78
89
  function spawnChild(spec) {
79
90
  const options = spec.options;
@@ -89,22 +100,22 @@ function spawnChild(spec) {
89
100
  function getStdio(kind) {
90
101
  return kind === "spawn" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
91
102
  }
92
- function collectResult(child, spec) {
103
+ function collectResult(child, spec, streams) {
93
104
  const stdoutDecoder = new StringDecoder("utf8");
94
105
  const stderrDecoder = new StringDecoder("utf8");
95
106
  let stdout = "";
96
107
  let stderr = "";
97
108
  let childClosed = false;
98
- let stdoutFinished = child.stdout === null;
99
- let stderrFinished = child.stderr === null;
109
+ let stdoutFinished = streams.stdout === null;
110
+ let stderrFinished = streams.stderr === null;
100
111
  let exitCode = 1;
101
112
  let exitSignal = null;
102
113
  let processError;
103
114
  let outputError;
104
- child.stdout?.on("data", (chunk) => {
115
+ streams.stdout?.on("data", (chunk) => {
105
116
  stdout += typeof chunk === "string" ? chunk : stdoutDecoder.write(chunk);
106
117
  });
107
- child.stderr?.on("data", (chunk) => {
118
+ streams.stderr?.on("data", (chunk) => {
108
119
  stderr += typeof chunk === "string" ? chunk : stderrDecoder.write(chunk);
109
120
  });
110
121
  return new Promise((resolve) => {
@@ -127,20 +138,20 @@ function collectResult(child, spec) {
127
138
  });
128
139
  resolve({ ...attempt, attempts: [attempt] });
129
140
  };
130
- child.stdout?.once("end", () => {
141
+ streams.stdout?.once("end", () => {
131
142
  stdoutFinished = true;
132
143
  finish();
133
144
  });
134
- child.stderr?.once("end", () => {
145
+ streams.stderr?.once("end", () => {
135
146
  stderrFinished = true;
136
147
  finish();
137
148
  });
138
- child.stdout?.once("error", (error) => {
149
+ streams.stdout?.once("error", (error) => {
139
150
  outputError = error;
140
151
  stdoutFinished = true;
141
152
  finish();
142
153
  });
143
- child.stderr?.once("error", (error) => {
154
+ streams.stderr?.once("error", (error) => {
144
155
  outputError = error;
145
156
  stderrFinished = true;
146
157
  finish();
@@ -161,6 +172,22 @@ function collectResult(child, spec) {
161
172
  });
162
173
  });
163
174
  }
175
+ function createOutputPipes(source, exposeUserStream) {
176
+ if (source === null) {
177
+ return { result: null, user: null };
178
+ }
179
+ const result = new PassThrough();
180
+ const user = exposeUserStream ? new PassThrough() : null;
181
+ source.once("error", (error) => {
182
+ result.destroy(error);
183
+ user?.destroy();
184
+ });
185
+ source.pipe(result);
186
+ if (user !== null) {
187
+ source.pipe(user);
188
+ }
189
+ return { result, user };
190
+ }
164
191
  function createAttempt(spec, output) {
165
192
  return {
166
193
  kind: spec.kind,
@@ -195,6 +222,14 @@ async function maybeRunAgent(result, options) {
195
222
  cause: error
196
223
  });
197
224
  }
225
+ try {
226
+ validateExitPolicy(policy);
227
+ }
228
+ catch (error) {
229
+ throw new AgentChildProcessError("Agent exit policy is invalid", result, {
230
+ cause: error
231
+ });
232
+ }
198
233
  const runAgent = options.runAgent ?? defaultRunAgent;
199
234
  let agentResult;
200
235
  try {
@@ -225,6 +260,15 @@ async function maybeRunAgent(result, options) {
225
260
  }
226
261
  };
227
262
  }
263
+ function validateExitPolicy(policy) {
264
+ assertNonBlank(policy.agent, "onExit.agent");
265
+ assertNonBlank(policy.prompt, "onExit.prompt");
266
+ }
267
+ function assertNonBlank(value, field) {
268
+ if (value.trim().length === 0) {
269
+ throw new TypeError(`${field} must not be empty`);
270
+ }
271
+ }
228
272
  async function defaultRunAgent(input) {
229
273
  const { spawn: spawnAgent } = await import("@poe-code/agent-spawn");
230
274
  return spawnAgent(input.agent, {