@span-io/agent-link 0.1.5 → 0.2.1
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/index.js +63 -1
- package/dist/process-runner.js +63 -5
- package/dist/remote-command.js +203 -0
- package/dist/transport.js +31 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import os from "os";
|
|
3
3
|
import process from "process";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { access } from "fs/promises";
|
|
6
|
+
import { constants as fsConstants } from "fs";
|
|
4
7
|
import { loadConfig, saveConfig } from "./config.js";
|
|
5
8
|
import { findAgentsOnPath, resolveAgentBinary } from "./agent.js";
|
|
6
9
|
import { spawnAgentProcess as spawnAgentProcessAdvanced } from "./process-runner.js";
|
|
7
10
|
import { LogBuffer } from "./log-buffer.js";
|
|
8
11
|
import { compactPrompt, resolvePromptCompactionPolicy } from "./prompt-compact.js";
|
|
9
12
|
import { BootstrapRunner } from "./bootstrap.js";
|
|
13
|
+
import { RemoteCommandRunner } from "./remote-command.js";
|
|
10
14
|
import { NoopTransport, WebSocketTransport } from "./transport.js";
|
|
11
15
|
const args = parseArgs(process.argv.slice(2));
|
|
12
16
|
if (args.list) {
|
|
@@ -47,6 +51,7 @@ const logBuffer = new LogBuffer();
|
|
|
47
51
|
const activeAgents = new Map();
|
|
48
52
|
const promptPolicy = resolvePromptCompactionPolicy();
|
|
49
53
|
const bootstrapRunner = new BootstrapRunner();
|
|
54
|
+
const remoteCommandRunner = new RemoteCommandRunner();
|
|
50
55
|
let transport;
|
|
51
56
|
try {
|
|
52
57
|
transport = await createTransport({
|
|
@@ -67,6 +72,14 @@ function handleControl(message) {
|
|
|
67
72
|
void bootstrapRunner.run(payload ?? {}, transport);
|
|
68
73
|
return;
|
|
69
74
|
}
|
|
75
|
+
if (action === "check_file") {
|
|
76
|
+
void runRemoteFileCheck(payload ?? {}, transport);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (action === "execute_command") {
|
|
80
|
+
void runRemoteCommand(payload ?? {}, transport);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
70
83
|
if (!agentId)
|
|
71
84
|
return;
|
|
72
85
|
switch (action) {
|
|
@@ -122,6 +135,9 @@ function handleControl(message) {
|
|
|
122
135
|
return;
|
|
123
136
|
}
|
|
124
137
|
const optionsArgs = [...(payload?.args || args.agentArgs)];
|
|
138
|
+
const promptMode = payload?.promptMode === "stdin" || payload?.promptMode === "args"
|
|
139
|
+
? payload.promptMode
|
|
140
|
+
: undefined;
|
|
125
141
|
const rawPrompt = payload?.prompt || "";
|
|
126
142
|
const normalized = compactPrompt(rawPrompt, promptPolicy);
|
|
127
143
|
if (normalized.action !== "none") {
|
|
@@ -135,7 +151,8 @@ function handleControl(message) {
|
|
|
135
151
|
},
|
|
136
152
|
prompt: normalized.prompt,
|
|
137
153
|
optionsArgs,
|
|
138
|
-
executablePath: agentCandidate.path
|
|
154
|
+
executablePath: agentCandidate.path,
|
|
155
|
+
promptModeOverride: promptMode,
|
|
139
156
|
});
|
|
140
157
|
activeAgents.set(agentId, proc);
|
|
141
158
|
console.log(`[${new Date().toLocaleTimeString()}] Spawning agent: ${agentCandidate.name} (${targetModel || "default model"})`);
|
|
@@ -166,9 +183,45 @@ function handleControl(message) {
|
|
|
166
183
|
}
|
|
167
184
|
}
|
|
168
185
|
}
|
|
186
|
+
async function runRemoteFileCheck(payload, transport) {
|
|
187
|
+
const runId = typeof payload.runId === "string" ? payload.runId.trim() : "";
|
|
188
|
+
if (!runId) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const workingDirectory = typeof payload.workingDirectory === "string" ? payload.workingDirectory.trim() : "";
|
|
192
|
+
if (!workingDirectory) {
|
|
193
|
+
transport.sendPreflight(runId, "error", false, "Missing workingDirectory.");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const filePath = typeof payload.filePath === "string" && payload.filePath.trim().length > 0
|
|
197
|
+
? payload.filePath.trim()
|
|
198
|
+
: "AGENTS.md";
|
|
199
|
+
const baseDir = path.resolve(workingDirectory);
|
|
200
|
+
const target = path.resolve(baseDir, filePath);
|
|
201
|
+
const relative = path.relative(baseDir, target);
|
|
202
|
+
if (relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
203
|
+
transport.sendPreflight(runId, "error", false, "Invalid filePath outside working directory.");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
await access(target, fsConstants.F_OK);
|
|
208
|
+
transport.sendPreflight(runId, "complete", true, filePath + " exists.");
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
transport.sendPreflight(runId, "complete", false, filePath + " not found.");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
async function runRemoteCommand(payload, transport) {
|
|
215
|
+
await remoteCommandRunner.run(payload, {
|
|
216
|
+
sendCommand: (requestId, status, details) => {
|
|
217
|
+
transport.sendCommand(requestId, status, details);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
169
221
|
function setupAgentPiping(agentId, proc) {
|
|
170
222
|
const rawFlushSize = Number.parseInt(process.env.AGENT_LINK_STREAM_FLUSH_CHARS ?? "16384", 10);
|
|
171
223
|
const flushSize = Number.isFinite(rawFlushSize) && rawFlushSize > 0 ? rawFlushSize : 16384;
|
|
224
|
+
const echoStdio = process.env.AGENT_LINK_ECHO_STDIO === "1";
|
|
172
225
|
const setupStream = (stream, name) => {
|
|
173
226
|
if (!stream)
|
|
174
227
|
return;
|
|
@@ -176,6 +229,15 @@ function setupAgentPiping(agentId, proc) {
|
|
|
176
229
|
stream.setEncoding("utf8");
|
|
177
230
|
const flushChunk = (chunk) => {
|
|
178
231
|
if (chunk.length > 0) {
|
|
232
|
+
if (echoStdio) {
|
|
233
|
+
const prefix = `[agent-link][${agentId}][${name}] `;
|
|
234
|
+
const lines = chunk.split("\n");
|
|
235
|
+
for (const line of lines) {
|
|
236
|
+
if (line.length > 0) {
|
|
237
|
+
console.log(`${prefix}${line}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
179
241
|
transport.sendLog(agentId, name, chunk);
|
|
180
242
|
}
|
|
181
243
|
};
|
package/dist/process-runner.js
CHANGED
|
@@ -8,7 +8,64 @@ const DEFAULT_PROMPT_FLAG = "";
|
|
|
8
8
|
const DEFAULT_TTY_MODE = "auto";
|
|
9
9
|
const DEFAULT_TTY_TERM = "dumb";
|
|
10
10
|
function splitArgs(raw) {
|
|
11
|
-
|
|
11
|
+
if (raw.trim() === "") {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const args = [];
|
|
15
|
+
let current = "";
|
|
16
|
+
let quote = null;
|
|
17
|
+
let escaped = false;
|
|
18
|
+
for (const char of raw) {
|
|
19
|
+
if (escaped) {
|
|
20
|
+
current += char;
|
|
21
|
+
escaped = false;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (quote === "'") {
|
|
25
|
+
if (char === "'") {
|
|
26
|
+
quote = null;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
current += char;
|
|
30
|
+
}
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (quote === '"') {
|
|
34
|
+
if (char === '"') {
|
|
35
|
+
quote = null;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (char === "\\") {
|
|
39
|
+
escaped = true;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
current += char;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (char === "'" || char === '"') {
|
|
46
|
+
quote = char;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (char === "\\") {
|
|
50
|
+
escaped = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (/\s/.test(char)) {
|
|
54
|
+
if (current !== "") {
|
|
55
|
+
args.push(current);
|
|
56
|
+
current = "";
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
current += char;
|
|
61
|
+
}
|
|
62
|
+
if (escaped) {
|
|
63
|
+
current += "\\";
|
|
64
|
+
}
|
|
65
|
+
if (current !== "") {
|
|
66
|
+
args.push(current);
|
|
67
|
+
}
|
|
68
|
+
return args;
|
|
12
69
|
}
|
|
13
70
|
function shellQuote(value) {
|
|
14
71
|
// Simple single-quote wrapping for display purposes
|
|
@@ -121,7 +178,7 @@ export function buildCommand({ agent, prompt, optionsArgs = [], executablePath }
|
|
|
121
178
|
const rawArgs = process.env.CODEX_ARGS ?? DEFAULT_CODEX_ARGS;
|
|
122
179
|
const promptFlag = process.env.CODEX_PROMPT_FLAG ?? DEFAULT_PROMPT_FLAG;
|
|
123
180
|
// Determine Prompt Mode
|
|
124
|
-
const envPromptMode = process.env.CODEX_PROMPT_MODE === "
|
|
181
|
+
const envPromptMode = process.env.CODEX_PROMPT_MODE === "args" ? "args" : "stdin";
|
|
125
182
|
const promptMode = promptModeOverride ?? envPromptMode;
|
|
126
183
|
const extraArgs = optionsArgs.filter((arg) => arg.trim() !== "");
|
|
127
184
|
const args = splitArgs(rawArgs);
|
|
@@ -163,12 +220,12 @@ export function spawnAgentProcess(config) {
|
|
|
163
220
|
const sanitizedOptions = optionsArgs;
|
|
164
221
|
const allowedRoots = parseAllowedRoots();
|
|
165
222
|
const spawnWithMode = (promptModeOverride) => {
|
|
166
|
-
const { command, args, promptMode } = buildCommand({ ...config, optionsArgs: sanitizedOptions }, promptModeOverride);
|
|
223
|
+
const { command, args, promptMode } = buildCommand({ ...config, optionsArgs: sanitizedOptions }, promptModeOverride ?? config.promptModeOverride);
|
|
167
224
|
// Ensure requested project/working directories exist before launching CLI.
|
|
168
225
|
ensureDirectoriesExist(collectDirectoriesFromArgs(args), allowedRoots);
|
|
169
226
|
const ttyMode = process.env.CODEX_TTY_MODE ?? DEFAULT_TTY_MODE;
|
|
170
227
|
const hasExec = args.includes("exec");
|
|
171
|
-
const useScriptWrapper = ttyMode === "script" || (ttyMode === "auto" && hasExec);
|
|
228
|
+
const useScriptWrapper = promptMode === "stdin" ? false : ttyMode === "script" || (ttyMode === "auto" && hasExec);
|
|
172
229
|
const ttyTerm = process.env.CODEX_TTY_TERM ?? DEFAULT_TTY_TERM;
|
|
173
230
|
const defaultCwd = process.cwd(); // Client uses current working dir
|
|
174
231
|
const workingDir = process.env.CODEX_CWD ?? defaultCwd;
|
|
@@ -197,7 +254,8 @@ export function spawnAgentProcess(config) {
|
|
|
197
254
|
}
|
|
198
255
|
});
|
|
199
256
|
if (promptMode === "stdin") {
|
|
200
|
-
|
|
257
|
+
const promptPayload = config.prompt.endsWith("\n") ? config.prompt : `${config.prompt}\n`;
|
|
258
|
+
child.stdin?.write(promptPayload);
|
|
201
259
|
child.stdin?.end();
|
|
202
260
|
}
|
|
203
261
|
return {
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { spawn } from "child_process";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import process from "process";
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
5
|
+
const MAX_TIMEOUT_MS = 15 * 60 * 1000;
|
|
6
|
+
const DEFAULT_ALLOWED_COMMANDS = ["ls", "pwd", "cat", "find", "git", "node", "npx", "npm"];
|
|
7
|
+
const MAX_ARGS = 128;
|
|
8
|
+
const MAX_ARG_LEN = 4096;
|
|
9
|
+
const DEFAULT_MAX_OUTPUT_CHARS = 64_000;
|
|
10
|
+
function parseCsvList(value, fallback) {
|
|
11
|
+
if (!value || !value.trim()) {
|
|
12
|
+
return new Set(fallback);
|
|
13
|
+
}
|
|
14
|
+
const entries = value
|
|
15
|
+
.split(",")
|
|
16
|
+
.map((part) => part.trim())
|
|
17
|
+
.filter((part) => part.length > 0);
|
|
18
|
+
return new Set(entries.length > 0 ? entries : fallback);
|
|
19
|
+
}
|
|
20
|
+
function parseAllowedRoots() {
|
|
21
|
+
const raw = process.env.AGENT_LINK_ALLOWED_WORKDIR_ROOTS?.trim();
|
|
22
|
+
if (!raw) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
return raw
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((entry) => entry.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.map((entry) => path.resolve(entry));
|
|
30
|
+
}
|
|
31
|
+
function isUnderRoot(candidate, root) {
|
|
32
|
+
const rel = path.relative(root, candidate);
|
|
33
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
34
|
+
}
|
|
35
|
+
function assertAllowedDirectory(directory, allowedRoots) {
|
|
36
|
+
if (allowedRoots.length === 0) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const resolved = path.resolve(directory);
|
|
40
|
+
const allowed = allowedRoots.some((root) => isUnderRoot(resolved, root));
|
|
41
|
+
if (!allowed) {
|
|
42
|
+
throw new Error(`Requested directory '${resolved}' is outside AGENT_LINK_ALLOWED_WORKDIR_ROOTS (${allowedRoots.join(", ")}).`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function normalizeRemoteCommandPayload(payload, commandAllowlist) {
|
|
46
|
+
const requestId = typeof payload?.requestId === "string" ? payload.requestId.trim() : "";
|
|
47
|
+
if (!requestId) {
|
|
48
|
+
return { ok: false, error: "Missing requestId." };
|
|
49
|
+
}
|
|
50
|
+
const command = typeof payload?.command === "string" ? payload.command.trim() : "";
|
|
51
|
+
if (!command) {
|
|
52
|
+
return { ok: false, requestId, error: "Missing command." };
|
|
53
|
+
}
|
|
54
|
+
if (!commandAllowlist.has(command)) {
|
|
55
|
+
return {
|
|
56
|
+
ok: false,
|
|
57
|
+
requestId,
|
|
58
|
+
error: `Command \"${command}\" is not allowed by AGENT_LINK_REMOTE_COMMAND_ALLOWLIST.`,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const rawArgs = Array.isArray(payload?.args) ? payload.args : [];
|
|
62
|
+
const args = rawArgs
|
|
63
|
+
.filter((arg) => typeof arg === "string")
|
|
64
|
+
.map((arg) => arg.trim())
|
|
65
|
+
.filter((arg) => arg.length > 0);
|
|
66
|
+
if (args.length > MAX_ARGS) {
|
|
67
|
+
return { ok: false, requestId, error: `Command args exceed max count (${MAX_ARGS}).` };
|
|
68
|
+
}
|
|
69
|
+
for (const arg of args) {
|
|
70
|
+
if (arg.length > MAX_ARG_LEN) {
|
|
71
|
+
return { ok: false, requestId, error: `Command arg exceeds max length (${MAX_ARG_LEN}).` };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const workingDirectoryRaw = typeof payload?.workingDirectory === "string" ? payload.workingDirectory.trim() : "";
|
|
75
|
+
const workingDirectory = path.resolve(workingDirectoryRaw || process.cwd());
|
|
76
|
+
const allowedRoots = parseAllowedRoots();
|
|
77
|
+
try {
|
|
78
|
+
assertAllowedDirectory(workingDirectory, allowedRoots);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
requestId,
|
|
84
|
+
error: error instanceof Error ? error.message : "Invalid workingDirectory.",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const requestedTimeout = typeof payload?.timeoutMs === "number" && Number.isFinite(payload.timeoutMs)
|
|
88
|
+
? payload.timeoutMs
|
|
89
|
+
: DEFAULT_TIMEOUT_MS;
|
|
90
|
+
const timeoutMs = Math.min(Math.max(requestedTimeout, 1), MAX_TIMEOUT_MS);
|
|
91
|
+
return {
|
|
92
|
+
ok: true,
|
|
93
|
+
value: {
|
|
94
|
+
requestId,
|
|
95
|
+
command,
|
|
96
|
+
args,
|
|
97
|
+
workingDirectory,
|
|
98
|
+
timeoutMs,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export class RemoteCommandRunner {
|
|
103
|
+
deps;
|
|
104
|
+
commandAllowlist;
|
|
105
|
+
maxOutputChars;
|
|
106
|
+
constructor(deps = {}, options = {}) {
|
|
107
|
+
this.deps = {
|
|
108
|
+
spawnFn: deps.spawnFn ??
|
|
109
|
+
((command, args, spawnOptions) => spawn(command, args, spawnOptions)),
|
|
110
|
+
};
|
|
111
|
+
this.commandAllowlist =
|
|
112
|
+
options.commandAllowlist ??
|
|
113
|
+
parseCsvList(process.env.AGENT_LINK_REMOTE_COMMAND_ALLOWLIST, DEFAULT_ALLOWED_COMMANDS);
|
|
114
|
+
const configuredMax = Number.parseInt(process.env.AGENT_LINK_COMMAND_MAX_OUTPUT_CHARS ?? "", 10);
|
|
115
|
+
this.maxOutputChars =
|
|
116
|
+
options.maxOutputChars ??
|
|
117
|
+
(Number.isFinite(configuredMax) && configuredMax > 0 ? configuredMax : DEFAULT_MAX_OUTPUT_CHARS);
|
|
118
|
+
}
|
|
119
|
+
async run(payload, transport) {
|
|
120
|
+
const normalized = normalizeRemoteCommandPayload(payload, this.commandAllowlist);
|
|
121
|
+
if (!normalized.ok) {
|
|
122
|
+
if (normalized.requestId) {
|
|
123
|
+
transport.sendCommand(normalized.requestId, "error", { message: normalized.error });
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const request = normalized.value;
|
|
128
|
+
transport.sendCommand(request.requestId, "started", {
|
|
129
|
+
message: `Running command: ${request.command} ${request.args.join(" ")} (cwd=${request.workingDirectory})`,
|
|
130
|
+
});
|
|
131
|
+
await this.runProcess(request, transport);
|
|
132
|
+
}
|
|
133
|
+
async runProcess(request, transport) {
|
|
134
|
+
await new Promise((resolve) => {
|
|
135
|
+
let child;
|
|
136
|
+
try {
|
|
137
|
+
child = this.deps.spawnFn(request.command, request.args, {
|
|
138
|
+
cwd: request.workingDirectory,
|
|
139
|
+
env: {
|
|
140
|
+
...process.env,
|
|
141
|
+
},
|
|
142
|
+
shell: false,
|
|
143
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
const message = error instanceof Error ? error.message : "unknown error";
|
|
148
|
+
transport.sendCommand(request.requestId, "error", { message: `Command process error: ${message}` });
|
|
149
|
+
resolve();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
let settled = false;
|
|
153
|
+
let stdout = "";
|
|
154
|
+
let stderr = "";
|
|
155
|
+
const appendClamped = (current, incoming) => {
|
|
156
|
+
const merged = current + incoming;
|
|
157
|
+
if (merged.length <= this.maxOutputChars) {
|
|
158
|
+
return merged;
|
|
159
|
+
}
|
|
160
|
+
return merged.slice(-this.maxOutputChars);
|
|
161
|
+
};
|
|
162
|
+
const complete = (status, details = {}) => {
|
|
163
|
+
if (settled) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
settled = true;
|
|
167
|
+
clearTimeout(timer);
|
|
168
|
+
transport.sendCommand(request.requestId, status, {
|
|
169
|
+
exitCode: details.exitCode,
|
|
170
|
+
stdout,
|
|
171
|
+
stderr,
|
|
172
|
+
message: details.message,
|
|
173
|
+
});
|
|
174
|
+
resolve();
|
|
175
|
+
};
|
|
176
|
+
const timer = setTimeout(() => {
|
|
177
|
+
child.kill("SIGKILL");
|
|
178
|
+
complete("error", { message: `Command timed out after ${request.timeoutMs}ms.` });
|
|
179
|
+
}, request.timeoutMs);
|
|
180
|
+
child.stdout?.setEncoding("utf8");
|
|
181
|
+
child.stderr?.setEncoding("utf8");
|
|
182
|
+
child.stdout?.on("data", (chunk) => {
|
|
183
|
+
stdout = appendClamped(stdout, chunk);
|
|
184
|
+
});
|
|
185
|
+
child.stderr?.on("data", (chunk) => {
|
|
186
|
+
stderr = appendClamped(stderr, chunk);
|
|
187
|
+
});
|
|
188
|
+
child.on("error", (error) => {
|
|
189
|
+
complete("error", { message: `Command process error: ${error.message}` });
|
|
190
|
+
});
|
|
191
|
+
child.on("exit", (code, signal) => {
|
|
192
|
+
if (code === 0) {
|
|
193
|
+
complete("completed", { exitCode: 0 });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
complete("error", {
|
|
197
|
+
exitCode: code ?? undefined,
|
|
198
|
+
message: `Command exited with code ${String(code ?? "null")} signal ${String(signal ?? "null")}.`,
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
package/dist/transport.js
CHANGED
|
@@ -35,6 +35,12 @@ export class NoopTransport {
|
|
|
35
35
|
sendBootstrap(_runId, _status, _message) {
|
|
36
36
|
return undefined;
|
|
37
37
|
}
|
|
38
|
+
sendPreflight(_runId, _status, _exists, _message) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
sendCommand(_requestId, _status, _details) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
38
44
|
close() {
|
|
39
45
|
return undefined;
|
|
40
46
|
}
|
|
@@ -188,6 +194,31 @@ export class WebSocketTransport {
|
|
|
188
194
|
},
|
|
189
195
|
}));
|
|
190
196
|
}
|
|
197
|
+
sendPreflight(runId, status, exists, message) {
|
|
198
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
199
|
+
return;
|
|
200
|
+
this.socket.send(JSON.stringify({
|
|
201
|
+
type: "preflight",
|
|
202
|
+
payload: {
|
|
203
|
+
runId,
|
|
204
|
+
status,
|
|
205
|
+
exists,
|
|
206
|
+
message,
|
|
207
|
+
},
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
sendCommand(requestId, status, details) {
|
|
211
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN)
|
|
212
|
+
return;
|
|
213
|
+
this.socket.send(JSON.stringify({
|
|
214
|
+
type: "command",
|
|
215
|
+
payload: {
|
|
216
|
+
requestId,
|
|
217
|
+
status,
|
|
218
|
+
...details,
|
|
219
|
+
},
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
191
222
|
close() {
|
|
192
223
|
this.isExplicitClose = true;
|
|
193
224
|
this.stopPingInterval();
|