visual-remote 0.3.1 → 0.3.3
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/README.md +55 -8
- package/apps/cli/dist/direct-exec-mcp.js +108 -1
- package/apps/cli/dist/index.js +948 -351
- package/apps/cli/dist/next.js +692 -218
- package/apps/cli/dist/vite.js +764 -250
- package/package.json +8 -5
- package/packages/overlay/dist/client.js +5 -5
- package/packages/overlay/dist/viewer.js +3 -3
package/apps/cli/dist/next.js
CHANGED
|
@@ -25,27 +25,530 @@ var AgentCanceledError = class extends Error {
|
|
|
25
25
|
}
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
// ../../packages/bridge-core/src/agents/claude-event-parser.ts
|
|
29
|
+
function asRecord(value) {
|
|
30
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
31
|
+
}
|
|
32
|
+
function asText(value) {
|
|
33
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
34
|
+
}
|
|
35
|
+
function asNumber(value) {
|
|
36
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
37
|
+
}
|
|
38
|
+
function contentBlocks(record) {
|
|
39
|
+
const message = asRecord(record.message);
|
|
40
|
+
return Array.isArray(message?.content) ? message.content.flatMap((block) => {
|
|
41
|
+
const parsed = asRecord(block);
|
|
42
|
+
return parsed === void 0 ? [] : [parsed];
|
|
43
|
+
}) : [];
|
|
44
|
+
}
|
|
45
|
+
function toolSummary(name, input) {
|
|
46
|
+
return asText(input.description) ?? asText(input.command) ?? asText(input.file_path) ?? asText(input.path) ?? (name === "Bash" ? "Run command" : void 0);
|
|
47
|
+
}
|
|
48
|
+
function filePath(input) {
|
|
49
|
+
return asText(input.file_path) ?? asText(input.path) ?? asText(input.notebook_path);
|
|
50
|
+
}
|
|
51
|
+
var ClaudeEventParser = class {
|
|
52
|
+
#defaultCwd;
|
|
53
|
+
#tools = /* @__PURE__ */ new Map();
|
|
54
|
+
#sessionId;
|
|
55
|
+
constructor(defaultCwd = "") {
|
|
56
|
+
this.#defaultCwd = defaultCwd;
|
|
57
|
+
}
|
|
58
|
+
parse(line) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (!trimmed) return [];
|
|
61
|
+
let value;
|
|
62
|
+
try {
|
|
63
|
+
value = JSON.parse(trimmed);
|
|
64
|
+
} catch {
|
|
65
|
+
return [{ type: "warning", text: trimmed }];
|
|
66
|
+
}
|
|
67
|
+
const record = asRecord(value);
|
|
68
|
+
if (!record) return [{ type: "message", text: trimmed }];
|
|
69
|
+
const events = [];
|
|
70
|
+
const foundSession = asText(record.session_id) ?? asText(record.sessionId);
|
|
71
|
+
if (foundSession !== void 0 && foundSession !== this.#sessionId) {
|
|
72
|
+
this.#sessionId = foundSession;
|
|
73
|
+
events.push({ type: "session", sessionId: foundSession });
|
|
74
|
+
}
|
|
75
|
+
const type = asText(record.type) ?? "unknown";
|
|
76
|
+
if (type === "system") {
|
|
77
|
+
const subtype = asText(record.subtype);
|
|
78
|
+
if (subtype) events.push({ type: "phase", name: subtype });
|
|
79
|
+
return events;
|
|
80
|
+
}
|
|
81
|
+
if (type === "assistant") {
|
|
82
|
+
for (const block of contentBlocks(record)) {
|
|
83
|
+
if (block.type === "text") {
|
|
84
|
+
const text = asText(block.text);
|
|
85
|
+
if (text) events.push({ type: "message", text });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (block.type !== "tool_use") continue;
|
|
89
|
+
const name = asText(block.name) ?? "tool";
|
|
90
|
+
const id = asText(block.id);
|
|
91
|
+
const input = asRecord(block.input) ?? {};
|
|
92
|
+
if (id) this.#tools.set(id, name);
|
|
93
|
+
const summary = toolSummary(name, input);
|
|
94
|
+
events.push(
|
|
95
|
+
summary ? { type: "tool_start", name, summary } : { type: "tool_start", name }
|
|
96
|
+
);
|
|
97
|
+
const command = name === "Bash" ? asText(input.command) : void 0;
|
|
98
|
+
if (command) {
|
|
99
|
+
events.push({ type: "command", command, cwd: this.#defaultCwd });
|
|
100
|
+
}
|
|
101
|
+
const path = filePath(input);
|
|
102
|
+
if (path && ["Edit", "Write", "NotebookEdit"].includes(name)) {
|
|
103
|
+
events.push({ type: "file_hint", path });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return events;
|
|
107
|
+
}
|
|
108
|
+
if (type === "user") {
|
|
109
|
+
for (const block of contentBlocks(record)) {
|
|
110
|
+
if (block.type !== "tool_result") continue;
|
|
111
|
+
const id = asText(block.tool_use_id);
|
|
112
|
+
const name = (id ? this.#tools.get(id) : void 0) ?? "tool";
|
|
113
|
+
if (id) this.#tools.delete(id);
|
|
114
|
+
events.push({ type: "tool_end", name, ok: block.is_error !== true });
|
|
115
|
+
}
|
|
116
|
+
return events;
|
|
117
|
+
}
|
|
118
|
+
if (type === "result") {
|
|
119
|
+
const usage = asRecord(record.usage);
|
|
120
|
+
if (usage) {
|
|
121
|
+
const cachedInputTokens = asNumber(usage.cache_read_input_tokens);
|
|
122
|
+
events.push({
|
|
123
|
+
type: "usage",
|
|
124
|
+
inputTokens: asNumber(usage.input_tokens) + asNumber(usage.cache_creation_input_tokens) + cachedInputTokens,
|
|
125
|
+
outputTokens: asNumber(usage.output_tokens),
|
|
126
|
+
...cachedInputTokens > 0 ? { cachedInputTokens } : {}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const result = asText(record.result);
|
|
130
|
+
const subtype = asText(record.subtype);
|
|
131
|
+
if (record.is_error === true || subtype?.startsWith("error") === true) {
|
|
132
|
+
events.push({ type: "error", text: result ?? "Claude reported an error" });
|
|
133
|
+
} else {
|
|
134
|
+
events.push(result ? { type: "complete", summary: result } : { type: "complete" });
|
|
135
|
+
}
|
|
136
|
+
return events;
|
|
137
|
+
}
|
|
138
|
+
return events;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// ../../packages/bridge-core/src/agents/claude-adapter.ts
|
|
143
|
+
import { execFile, spawn } from "node:child_process";
|
|
144
|
+
import { promisify } from "node:util";
|
|
145
|
+
|
|
146
|
+
// ../../packages/bridge-core/src/runtime/managed-process.ts
|
|
147
|
+
function safeChildProcessId(child) {
|
|
148
|
+
const pid = child.pid;
|
|
149
|
+
if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
|
|
150
|
+
return void 0;
|
|
151
|
+
}
|
|
152
|
+
return pid;
|
|
153
|
+
}
|
|
154
|
+
function safeDetachedProcessGroupId(child) {
|
|
155
|
+
return process.platform === "win32" ? void 0 : safeChildProcessId(child);
|
|
156
|
+
}
|
|
157
|
+
function isMissingProcess(error) {
|
|
158
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
|
|
159
|
+
}
|
|
160
|
+
function processGroupIsAlive(processGroupId) {
|
|
161
|
+
try {
|
|
162
|
+
process.kill(-processGroupId, 0);
|
|
163
|
+
return true;
|
|
164
|
+
} catch (error) {
|
|
165
|
+
if (isMissingProcess(error)) return false;
|
|
166
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function childIsAlive(child) {
|
|
173
|
+
return child.exitCode === null && child.signalCode === null;
|
|
174
|
+
}
|
|
175
|
+
function signalChildProcessTree(child, signal) {
|
|
176
|
+
const pid = safeChildProcessId(child);
|
|
177
|
+
if (pid === void 0) return false;
|
|
178
|
+
try {
|
|
179
|
+
if (process.platform === "win32") {
|
|
180
|
+
if (!childIsAlive(child)) return false;
|
|
181
|
+
return child.kill(signal);
|
|
182
|
+
}
|
|
183
|
+
process.kill(-pid, signal);
|
|
184
|
+
return true;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
if (isMissingProcess(error)) return false;
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function installEmergencyChildExitHook(child, processLike = process) {
|
|
191
|
+
let removed = false;
|
|
192
|
+
const emergencyExit = () => {
|
|
193
|
+
removed = true;
|
|
194
|
+
try {
|
|
195
|
+
signalChildProcessTree(child, "SIGKILL");
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
processLike.once("exit", emergencyExit);
|
|
200
|
+
return () => {
|
|
201
|
+
if (removed) return;
|
|
202
|
+
removed = true;
|
|
203
|
+
processLike.off("exit", emergencyExit);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
|
|
207
|
+
const deadline = Date.now() + timeoutMs;
|
|
208
|
+
while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
|
|
209
|
+
const remaining = deadline - Date.now();
|
|
210
|
+
if (remaining <= 0) return false;
|
|
211
|
+
await new Promise((resolve8) => {
|
|
212
|
+
setTimeout(resolve8, Math.min(25, remaining));
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
async function terminateChildProcessTree(child, killGraceMs = 3e3) {
|
|
218
|
+
const processGroupId = safeDetachedProcessGroupId(child);
|
|
219
|
+
if (processGroupId === void 0 && !childIsAlive(child)) return;
|
|
220
|
+
const sendSignal = (signal) => {
|
|
221
|
+
signalChildProcessTree(child, signal);
|
|
222
|
+
};
|
|
223
|
+
sendSignal("SIGTERM");
|
|
224
|
+
if (await waitForProcessTreeExit(
|
|
225
|
+
child,
|
|
226
|
+
processGroupId,
|
|
227
|
+
Math.max(0, killGraceMs)
|
|
228
|
+
)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
sendSignal("SIGKILL");
|
|
232
|
+
await waitForProcessTreeExit(
|
|
233
|
+
child,
|
|
234
|
+
processGroupId,
|
|
235
|
+
Math.min(Math.max(0, killGraceMs), 1e3)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ../../packages/bridge-core/src/agents/async-queue.ts
|
|
240
|
+
var AsyncQueue = class {
|
|
241
|
+
#values = [];
|
|
242
|
+
#waiters = [];
|
|
243
|
+
#ended = false;
|
|
244
|
+
#error;
|
|
245
|
+
push(value) {
|
|
246
|
+
if (this.#ended) return;
|
|
247
|
+
const waiter = this.#waiters.shift();
|
|
248
|
+
if (waiter) waiter.resolve({ value, done: false });
|
|
249
|
+
else this.#values.push(value);
|
|
250
|
+
}
|
|
251
|
+
end(error) {
|
|
252
|
+
if (this.#ended) return;
|
|
253
|
+
this.#ended = true;
|
|
254
|
+
this.#error = error;
|
|
255
|
+
for (const waiter of this.#waiters.splice(0)) {
|
|
256
|
+
if (error !== void 0) waiter.reject(error);
|
|
257
|
+
else waiter.resolve({ value: void 0, done: true });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
[Symbol.asyncIterator]() {
|
|
261
|
+
return {
|
|
262
|
+
next: async () => {
|
|
263
|
+
const value = this.#values.shift();
|
|
264
|
+
if (value !== void 0) return { value, done: false };
|
|
265
|
+
if (this.#ended) {
|
|
266
|
+
if (this.#error !== void 0) throw this.#error;
|
|
267
|
+
return { value: void 0, done: true };
|
|
268
|
+
}
|
|
269
|
+
return await new Promise((resolve8, reject) => {
|
|
270
|
+
this.#waiters.push({ resolve: resolve8, reject });
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// ../../packages/bridge-core/src/agents/claude-adapter.ts
|
|
278
|
+
var execFileAsync = promisify(execFile);
|
|
279
|
+
var INHERITED_ENVIRONMENT = [
|
|
280
|
+
"PATH",
|
|
281
|
+
"HOME",
|
|
282
|
+
"USER",
|
|
283
|
+
"LOGNAME",
|
|
284
|
+
"SHELL",
|
|
285
|
+
"LANG",
|
|
286
|
+
"LC_ALL",
|
|
287
|
+
"TERM",
|
|
288
|
+
"TMPDIR",
|
|
289
|
+
"XDG_CONFIG_HOME",
|
|
290
|
+
"XDG_DATA_HOME",
|
|
291
|
+
"XDG_STATE_HOME",
|
|
292
|
+
"ANTHROPIC_API_KEY",
|
|
293
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
294
|
+
"HTTPS_PROXY",
|
|
295
|
+
"HTTP_PROXY",
|
|
296
|
+
"NO_PROXY",
|
|
297
|
+
"USERPROFILE",
|
|
298
|
+
"APPDATA",
|
|
299
|
+
"LOCALAPPDATA",
|
|
300
|
+
"SystemRoot",
|
|
301
|
+
"COMSPEC",
|
|
302
|
+
"PATHEXT"
|
|
303
|
+
];
|
|
304
|
+
var EMPTY_MCP_CONFIG = JSON.stringify({ mcpServers: {} });
|
|
305
|
+
var CLAUDE_SETTINGS = JSON.stringify({
|
|
306
|
+
permissions: {
|
|
307
|
+
disableBypassPermissionsMode: "disable",
|
|
308
|
+
deny: [
|
|
309
|
+
"Read(./.env)",
|
|
310
|
+
"Read(./.env.*)",
|
|
311
|
+
"Read(./**/*.pem)",
|
|
312
|
+
"Read(./**/*.key)",
|
|
313
|
+
"Edit(./.git/**)",
|
|
314
|
+
"Edit(./.visualdev/runtime/**)",
|
|
315
|
+
"Edit(./node_modules/**)"
|
|
316
|
+
]
|
|
317
|
+
},
|
|
318
|
+
sandbox: {
|
|
319
|
+
enabled: true,
|
|
320
|
+
autoAllowBashIfSandboxed: true,
|
|
321
|
+
allowUnsandboxedCommands: false,
|
|
322
|
+
network: { strictAllowlist: true }
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
function processEnv(overrides) {
|
|
326
|
+
const environment = {};
|
|
327
|
+
for (const key of INHERITED_ENVIRONMENT) {
|
|
328
|
+
const value = process.env[key];
|
|
329
|
+
if (value !== void 0) environment[key] = value;
|
|
330
|
+
}
|
|
331
|
+
return { ...environment, ...overrides };
|
|
332
|
+
}
|
|
333
|
+
function splitLines(chunk, previous, onLine) {
|
|
334
|
+
const combined = previous + chunk.toString();
|
|
335
|
+
const lines = combined.split(/\r?\n/);
|
|
336
|
+
const remainder = lines.pop() ?? "";
|
|
337
|
+
for (const line of lines) onLine(line);
|
|
338
|
+
return remainder;
|
|
339
|
+
}
|
|
340
|
+
var ClaudeAdapter = class {
|
|
341
|
+
id = "claude";
|
|
342
|
+
#executable;
|
|
343
|
+
#killGraceMs;
|
|
344
|
+
#model;
|
|
345
|
+
#reasoningEffort;
|
|
346
|
+
#rtkExecutable;
|
|
347
|
+
#rtkVersion;
|
|
348
|
+
constructor(options = {}) {
|
|
349
|
+
this.#executable = options.executable ?? "claude";
|
|
350
|
+
this.#killGraceMs = options.killGraceMs ?? 2e3;
|
|
351
|
+
this.#model = options.model;
|
|
352
|
+
this.#reasoningEffort = options.reasoningEffort;
|
|
353
|
+
this.#rtkExecutable = options.rtkExecutable ?? "rtk";
|
|
354
|
+
}
|
|
355
|
+
#probeRtk(environment) {
|
|
356
|
+
if (this.#rtkExecutable === false) return Promise.resolve(void 0);
|
|
357
|
+
this.#rtkVersion ??= execFileAsync(this.#rtkExecutable, ["--version"], {
|
|
358
|
+
encoding: "utf8",
|
|
359
|
+
env: environment,
|
|
360
|
+
timeout: 1e3,
|
|
361
|
+
windowsHide: true,
|
|
362
|
+
maxBuffer: 16 * 1024
|
|
363
|
+
}).then(({ stdout }) => stdout.trim().split(/\r?\n/, 1)[0] || void 0).catch(() => void 0);
|
|
364
|
+
return this.#rtkVersion;
|
|
365
|
+
}
|
|
366
|
+
async #runtimePrompt(input, environment) {
|
|
367
|
+
if (this.#rtkExecutable === false) return input.prompt;
|
|
368
|
+
const guidance = await this.#probeRtk(environment).then(
|
|
369
|
+
(version) => version ? `RTK command proxy:
|
|
370
|
+
- ${version} is installed and available in this runtime.
|
|
371
|
+
- Prefix shell commands with RTK by default (for example: rtk git status, rtk rg <pattern>, rtk read <file>, rtk npm test).
|
|
372
|
+
- Use the native command only when RTK has no suitable proxy or RTK execution fails. Do not spend time rediscovering or reinstalling RTK.` : `RTK command proxy:
|
|
373
|
+
- RTK was not detected in this runtime. Use native repository commands directly and do not spend time searching for RTK.`
|
|
374
|
+
);
|
|
375
|
+
return `${input.prompt.trimEnd()}
|
|
376
|
+
|
|
377
|
+
${guidance}
|
|
378
|
+
`;
|
|
379
|
+
}
|
|
380
|
+
#baseArgs() {
|
|
381
|
+
return [
|
|
382
|
+
"-p",
|
|
383
|
+
"--output-format",
|
|
384
|
+
"stream-json",
|
|
385
|
+
"--verbose",
|
|
386
|
+
"--permission-mode",
|
|
387
|
+
"acceptEdits",
|
|
388
|
+
"--strict-mcp-config",
|
|
389
|
+
"--mcp-config",
|
|
390
|
+
EMPTY_MCP_CONFIG,
|
|
391
|
+
"--no-chrome",
|
|
392
|
+
"--tools",
|
|
393
|
+
"Read,Glob,Grep,Edit,Write,Bash",
|
|
394
|
+
"--settings",
|
|
395
|
+
CLAUDE_SETTINGS,
|
|
396
|
+
...this.#model === void 0 ? [] : ["--model", this.#model],
|
|
397
|
+
...this.#reasoningEffort === void 0 ? [] : ["--effort", this.#reasoningEffort]
|
|
398
|
+
];
|
|
399
|
+
}
|
|
400
|
+
async probe() {
|
|
401
|
+
return await new Promise((resolve8) => {
|
|
402
|
+
const child = spawn(this.#executable, ["--version"], {
|
|
403
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
404
|
+
shell: false
|
|
405
|
+
});
|
|
406
|
+
let output = "";
|
|
407
|
+
child.stdout?.on("data", (chunk) => {
|
|
408
|
+
output += chunk.toString();
|
|
409
|
+
});
|
|
410
|
+
child.once("error", () => {
|
|
411
|
+
resolve8({ available: false, supportsResume: true, structuredOutput: true });
|
|
412
|
+
});
|
|
413
|
+
child.once("close", (code) => {
|
|
414
|
+
const match = output.match(/(\d+\.\d+\.\d+(?:[-+][^\s]+)?)/);
|
|
415
|
+
const capabilities = {
|
|
416
|
+
available: code === 0,
|
|
417
|
+
supportsResume: true,
|
|
418
|
+
structuredOutput: true
|
|
419
|
+
};
|
|
420
|
+
if (match?.[1]) capabilities.version = match[1];
|
|
421
|
+
resolve8(capabilities);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
async *run(input, signal) {
|
|
426
|
+
yield* this.#execute(input, signal, this.#baseArgs());
|
|
427
|
+
}
|
|
428
|
+
async *resume(input, signal) {
|
|
429
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(input.sessionId)) {
|
|
430
|
+
throw new Error("Refusing to resume an invalid Claude session id");
|
|
431
|
+
}
|
|
432
|
+
yield* this.#execute(
|
|
433
|
+
input,
|
|
434
|
+
signal,
|
|
435
|
+
[...this.#baseArgs(), "--resume", input.sessionId]
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
async *#execute(input, signal, args) {
|
|
439
|
+
const queue = new AsyncQueue();
|
|
440
|
+
const environment = processEnv(input.environment);
|
|
441
|
+
const prompt = await this.#runtimePrompt(input, environment);
|
|
442
|
+
const parser = new ClaudeEventParser(input.workspaceRoot);
|
|
443
|
+
const child = spawn(this.#executable, args, {
|
|
444
|
+
cwd: input.workspaceRoot,
|
|
445
|
+
env: environment,
|
|
446
|
+
detached: process.platform !== "win32",
|
|
447
|
+
shell: false,
|
|
448
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
449
|
+
});
|
|
450
|
+
const removeEmergencyExitHook = installEmergencyChildExitHook(child);
|
|
451
|
+
let stdoutRemainder = "";
|
|
452
|
+
let stderrRemainder = "";
|
|
453
|
+
let timedOut = false;
|
|
454
|
+
let aborted = signal.aborted;
|
|
455
|
+
let termination;
|
|
456
|
+
const requestTermination = () => {
|
|
457
|
+
termination ??= terminateChildProcessTree(
|
|
458
|
+
child,
|
|
459
|
+
this.#killGraceMs
|
|
460
|
+
).finally(removeEmergencyExitHook);
|
|
461
|
+
return termination;
|
|
462
|
+
};
|
|
463
|
+
const timeout = setTimeout(() => {
|
|
464
|
+
timedOut = true;
|
|
465
|
+
void requestTermination();
|
|
466
|
+
}, Math.max(1, input.maxRunMs));
|
|
467
|
+
timeout.unref();
|
|
468
|
+
const abort = () => {
|
|
469
|
+
aborted = true;
|
|
470
|
+
void requestTermination();
|
|
471
|
+
};
|
|
472
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
473
|
+
if (signal.aborted) abort();
|
|
474
|
+
child.stdout.on("data", (chunk) => {
|
|
475
|
+
stdoutRemainder = splitLines(chunk, stdoutRemainder, (line) => {
|
|
476
|
+
for (const event of parser.parse(line)) queue.push(event);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
child.stderr.on("data", (chunk) => {
|
|
480
|
+
stderrRemainder = splitLines(chunk, stderrRemainder, (line) => {
|
|
481
|
+
if (line.trim()) queue.push({ type: "warning", text: line });
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
child.once("error", (error) => queue.end(error));
|
|
485
|
+
child.once("close", (code, closeSignal) => {
|
|
486
|
+
clearTimeout(timeout);
|
|
487
|
+
signal.removeEventListener("abort", abort);
|
|
488
|
+
void (async () => {
|
|
489
|
+
await requestTermination();
|
|
490
|
+
if (stdoutRemainder.trim()) {
|
|
491
|
+
for (const event of parser.parse(stdoutRemainder)) queue.push(event);
|
|
492
|
+
}
|
|
493
|
+
if (stderrRemainder.trim()) queue.push({ type: "warning", text: stderrRemainder });
|
|
494
|
+
if (timedOut) queue.end(new AgentTimeoutError());
|
|
495
|
+
else if (aborted) queue.end(new AgentCanceledError());
|
|
496
|
+
else if (code !== 0) {
|
|
497
|
+
queue.end(
|
|
498
|
+
new AgentProcessError(
|
|
499
|
+
`Claude exited with code ${String(code)}`,
|
|
500
|
+
code,
|
|
501
|
+
closeSignal
|
|
502
|
+
)
|
|
503
|
+
);
|
|
504
|
+
} else {
|
|
505
|
+
queue.end();
|
|
506
|
+
}
|
|
507
|
+
})().catch((error) => queue.end(error));
|
|
508
|
+
});
|
|
509
|
+
child.stdin.on("error", (error) => {
|
|
510
|
+
if (error.code !== "EPIPE") queue.end(error);
|
|
511
|
+
});
|
|
512
|
+
child.stdin.end(prompt);
|
|
513
|
+
try {
|
|
514
|
+
for await (const event of queue) yield event;
|
|
515
|
+
} finally {
|
|
516
|
+
clearTimeout(timeout);
|
|
517
|
+
signal.removeEventListener("abort", abort);
|
|
518
|
+
try {
|
|
519
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
520
|
+
await requestTermination();
|
|
521
|
+
} else if (termination) {
|
|
522
|
+
await termination;
|
|
523
|
+
}
|
|
524
|
+
} finally {
|
|
525
|
+
removeEmergencyExitHook();
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
};
|
|
530
|
+
|
|
28
531
|
// ../../packages/bridge-core/src/agents/codex-event-parser.ts
|
|
29
|
-
function
|
|
532
|
+
function asRecord2(value) {
|
|
30
533
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
31
534
|
}
|
|
32
|
-
function
|
|
535
|
+
function asText2(value) {
|
|
33
536
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
34
537
|
}
|
|
35
538
|
function sessionId(record) {
|
|
36
|
-
const thread =
|
|
37
|
-
return
|
|
539
|
+
const thread = asRecord2(record.thread);
|
|
540
|
+
return asText2(record.thread_id) ?? asText2(record.threadId) ?? asText2(record.session_id) ?? asText2(record.sessionId) ?? (thread ? asText2(thread.id) : void 0);
|
|
38
541
|
}
|
|
39
542
|
function itemFiles(item) {
|
|
40
543
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
41
544
|
const files = changes.flatMap((change) => {
|
|
42
|
-
const record =
|
|
545
|
+
const record = asRecord2(change);
|
|
43
546
|
if (!record) return [];
|
|
44
|
-
return [
|
|
547
|
+
return [asText2(record.path) ?? asText2(record.file_path) ?? asText2(record.filePath)].filter(
|
|
45
548
|
(path) => path !== void 0
|
|
46
549
|
);
|
|
47
550
|
});
|
|
48
|
-
const direct =
|
|
551
|
+
const direct = asText2(item.path) ?? asText2(item.file_path) ?? asText2(item.filePath);
|
|
49
552
|
if (direct) files.push(direct);
|
|
50
553
|
return [...new Set(files)];
|
|
51
554
|
}
|
|
@@ -59,28 +562,28 @@ function isDirectExecItem(item) {
|
|
|
59
562
|
return item.type === "mcp_tool_call" && item.server === "visual_remote_exec" && item.tool === "run_readonly";
|
|
60
563
|
}
|
|
61
564
|
function directExecSummary(item) {
|
|
62
|
-
const arguments_ =
|
|
565
|
+
const arguments_ = asRecord2(item.arguments);
|
|
63
566
|
const commands = Array.isArray(arguments_?.commands) ? arguments_.commands : [];
|
|
64
567
|
const summaries = commands.flatMap((candidate) => {
|
|
65
|
-
const command =
|
|
568
|
+
const command = asRecord2(candidate);
|
|
66
569
|
const argv = stringArray(command?.argv);
|
|
67
570
|
return argv === void 0 ? [] : [formatArgv(argv)];
|
|
68
571
|
});
|
|
69
572
|
return summaries.length === 0 ? void 0 : summaries.join(" \xB7 ");
|
|
70
573
|
}
|
|
71
574
|
function directExecResults(item, defaultCwd) {
|
|
72
|
-
const result =
|
|
73
|
-
const structured =
|
|
575
|
+
const result = asRecord2(item.result);
|
|
576
|
+
const structured = asRecord2(result?.structured_content ?? result?.structuredContent);
|
|
74
577
|
const results = Array.isArray(structured?.results) ? structured.results : [];
|
|
75
578
|
return results.flatMap((candidate) => {
|
|
76
|
-
const command =
|
|
579
|
+
const command = asRecord2(candidate);
|
|
77
580
|
const argv = stringArray(command?.argv);
|
|
78
581
|
if (argv === void 0) return [];
|
|
79
582
|
const exitCode = typeof command?.exitCode === "number" ? command.exitCode : void 0;
|
|
80
583
|
const durationMs = typeof command?.durationMs === "number" ? command.durationMs : void 0;
|
|
81
584
|
return [{
|
|
82
585
|
command: formatArgv(argv),
|
|
83
|
-
cwd:
|
|
586
|
+
cwd: asText2(command?.cwd) ?? defaultCwd,
|
|
84
587
|
ok: exitCode === 0,
|
|
85
588
|
...exitCode === void 0 ? {} : { exitCode },
|
|
86
589
|
...durationMs === void 0 ? {} : { durationMs },
|
|
@@ -91,8 +594,8 @@ function directExecResults(item, defaultCwd) {
|
|
|
91
594
|
});
|
|
92
595
|
}
|
|
93
596
|
function normalizedUsage(record) {
|
|
94
|
-
const result =
|
|
95
|
-
const usage =
|
|
597
|
+
const result = asRecord2(record.result);
|
|
598
|
+
const usage = asRecord2(record.usage) ?? (result ? asRecord2(result.usage) : void 0);
|
|
96
599
|
if (usage === void 0) return void 0;
|
|
97
600
|
const inputTokens = usage.input_tokens ?? usage.inputTokens;
|
|
98
601
|
const outputTokens = usage.output_tokens ?? usage.outputTokens;
|
|
@@ -116,17 +619,17 @@ function parseCodexJsonLine(line, defaultCwd = "") {
|
|
|
116
619
|
} catch {
|
|
117
620
|
return [{ type: "warning", text: trimmed }];
|
|
118
621
|
}
|
|
119
|
-
const record =
|
|
622
|
+
const record = asRecord2(value);
|
|
120
623
|
if (!record) return [{ type: "message", text: trimmed }];
|
|
121
|
-
const type =
|
|
624
|
+
const type = asText2(record.type) ?? "unknown";
|
|
122
625
|
const events = [];
|
|
123
626
|
const foundSession = sessionId(record);
|
|
124
627
|
if (foundSession) events.push({ type: "session", sessionId: foundSession });
|
|
125
628
|
if (type === "thread.started" || type === "thread.created") return events;
|
|
126
629
|
if (type === "turn.started") return [...events, { type: "phase", name: "turn.started" }];
|
|
127
630
|
if (type === "turn.completed") {
|
|
128
|
-
const result =
|
|
129
|
-
const summary =
|
|
631
|
+
const result = asRecord2(record.result);
|
|
632
|
+
const summary = asText2(record.summary) ?? (result ? asText2(result.summary) : void 0);
|
|
130
633
|
const usage = normalizedUsage(record);
|
|
131
634
|
return [
|
|
132
635
|
...events,
|
|
@@ -135,31 +638,31 @@ function parseCodexJsonLine(line, defaultCwd = "") {
|
|
|
135
638
|
];
|
|
136
639
|
}
|
|
137
640
|
if (type === "turn.failed" || type === "error") {
|
|
138
|
-
const error =
|
|
139
|
-
const text =
|
|
641
|
+
const error = asRecord2(record.error);
|
|
642
|
+
const text = asText2(record.message) ?? (error ? asText2(error.message) : void 0) ?? "Codex reported an error";
|
|
140
643
|
return [...events, { type: "error", text }];
|
|
141
644
|
}
|
|
142
|
-
const item =
|
|
645
|
+
const item = asRecord2(record.item);
|
|
143
646
|
if (type === "item.started" && item) {
|
|
144
|
-
const itemType =
|
|
647
|
+
const itemType = asText2(item.type) ?? "item";
|
|
145
648
|
const directExec = isDirectExecItem(item);
|
|
146
|
-
const summary = directExec ? directExecSummary(item) :
|
|
649
|
+
const summary = directExec ? directExecSummary(item) : asText2(item.command) ?? asText2(item.text);
|
|
147
650
|
const start = summary ? { type: "tool_start", name: directExec ? "direct_exec" : itemType, summary } : { type: "tool_start", name: directExec ? "direct_exec" : itemType };
|
|
148
651
|
return [...events, start];
|
|
149
652
|
}
|
|
150
653
|
if (type === "item.completed" && item) {
|
|
151
|
-
const itemType =
|
|
654
|
+
const itemType = asText2(item.type) ?? "item";
|
|
152
655
|
if (itemType === "agent_message") {
|
|
153
|
-
const text =
|
|
656
|
+
const text = asText2(item.text) ?? asText2(item.message);
|
|
154
657
|
return text ? [...events, { type: "message", text }] : events;
|
|
155
658
|
}
|
|
156
659
|
if (itemType === "command_execution") {
|
|
157
|
-
const command =
|
|
660
|
+
const command = asText2(item.command);
|
|
158
661
|
if (command) {
|
|
159
662
|
events.push({
|
|
160
663
|
type: "command",
|
|
161
664
|
command,
|
|
162
|
-
cwd:
|
|
665
|
+
cwd: asText2(item.cwd) ?? defaultCwd
|
|
163
666
|
});
|
|
164
667
|
}
|
|
165
668
|
}
|
|
@@ -185,151 +688,18 @@ function parseCodexJsonLine(line, defaultCwd = "") {
|
|
|
185
688
|
});
|
|
186
689
|
return events;
|
|
187
690
|
}
|
|
188
|
-
const message =
|
|
691
|
+
const message = asText2(record.message);
|
|
189
692
|
if (message) events.push({ type: "message", text: message });
|
|
190
693
|
return events;
|
|
191
694
|
}
|
|
192
695
|
|
|
193
696
|
// ../../packages/bridge-core/src/agents/codex-adapter.ts
|
|
194
|
-
import { execFile, spawn } from "node:child_process";
|
|
697
|
+
import { execFile as execFile2, spawn as spawn2 } from "node:child_process";
|
|
195
698
|
import { existsSync } from "node:fs";
|
|
196
699
|
import { fileURLToPath } from "node:url";
|
|
197
|
-
import { promisify } from "node:util";
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
var AsyncQueue = class {
|
|
201
|
-
#values = [];
|
|
202
|
-
#waiters = [];
|
|
203
|
-
#ended = false;
|
|
204
|
-
#error;
|
|
205
|
-
push(value) {
|
|
206
|
-
if (this.#ended) return;
|
|
207
|
-
const waiter = this.#waiters.shift();
|
|
208
|
-
if (waiter) waiter.resolve({ value, done: false });
|
|
209
|
-
else this.#values.push(value);
|
|
210
|
-
}
|
|
211
|
-
end(error) {
|
|
212
|
-
if (this.#ended) return;
|
|
213
|
-
this.#ended = true;
|
|
214
|
-
this.#error = error;
|
|
215
|
-
for (const waiter of this.#waiters.splice(0)) {
|
|
216
|
-
if (error !== void 0) waiter.reject(error);
|
|
217
|
-
else waiter.resolve({ value: void 0, done: true });
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
[Symbol.asyncIterator]() {
|
|
221
|
-
return {
|
|
222
|
-
next: async () => {
|
|
223
|
-
const value = this.#values.shift();
|
|
224
|
-
if (value !== void 0) return { value, done: false };
|
|
225
|
-
if (this.#ended) {
|
|
226
|
-
if (this.#error !== void 0) throw this.#error;
|
|
227
|
-
return { value: void 0, done: true };
|
|
228
|
-
}
|
|
229
|
-
return await new Promise((resolve8, reject) => {
|
|
230
|
-
this.#waiters.push({ resolve: resolve8, reject });
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// ../../packages/bridge-core/src/runtime/managed-process.ts
|
|
238
|
-
function safeChildProcessId(child) {
|
|
239
|
-
const pid = child.pid;
|
|
240
|
-
if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
|
|
241
|
-
return void 0;
|
|
242
|
-
}
|
|
243
|
-
return pid;
|
|
244
|
-
}
|
|
245
|
-
function safeDetachedProcessGroupId(child) {
|
|
246
|
-
return process.platform === "win32" ? void 0 : safeChildProcessId(child);
|
|
247
|
-
}
|
|
248
|
-
function isMissingProcess(error) {
|
|
249
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
|
|
250
|
-
}
|
|
251
|
-
function processGroupIsAlive(processGroupId) {
|
|
252
|
-
try {
|
|
253
|
-
process.kill(-processGroupId, 0);
|
|
254
|
-
return true;
|
|
255
|
-
} catch (error) {
|
|
256
|
-
if (isMissingProcess(error)) return false;
|
|
257
|
-
if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
throw error;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
function childIsAlive(child) {
|
|
264
|
-
return child.exitCode === null && child.signalCode === null;
|
|
265
|
-
}
|
|
266
|
-
function signalChildProcessTree(child, signal) {
|
|
267
|
-
const pid = safeChildProcessId(child);
|
|
268
|
-
if (pid === void 0) return false;
|
|
269
|
-
try {
|
|
270
|
-
if (process.platform === "win32") {
|
|
271
|
-
if (!childIsAlive(child)) return false;
|
|
272
|
-
return child.kill(signal);
|
|
273
|
-
}
|
|
274
|
-
process.kill(-pid, signal);
|
|
275
|
-
return true;
|
|
276
|
-
} catch (error) {
|
|
277
|
-
if (isMissingProcess(error)) return false;
|
|
278
|
-
throw error;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function installEmergencyChildExitHook(child, processLike = process) {
|
|
282
|
-
let removed = false;
|
|
283
|
-
const emergencyExit = () => {
|
|
284
|
-
removed = true;
|
|
285
|
-
try {
|
|
286
|
-
signalChildProcessTree(child, "SIGKILL");
|
|
287
|
-
} catch {
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
processLike.once("exit", emergencyExit);
|
|
291
|
-
return () => {
|
|
292
|
-
if (removed) return;
|
|
293
|
-
removed = true;
|
|
294
|
-
processLike.off("exit", emergencyExit);
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
|
|
298
|
-
const deadline = Date.now() + timeoutMs;
|
|
299
|
-
while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
|
|
300
|
-
const remaining = deadline - Date.now();
|
|
301
|
-
if (remaining <= 0) return false;
|
|
302
|
-
await new Promise((resolve8) => {
|
|
303
|
-
setTimeout(resolve8, Math.min(25, remaining));
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
return true;
|
|
307
|
-
}
|
|
308
|
-
async function terminateChildProcessTree(child, killGraceMs = 3e3) {
|
|
309
|
-
const processGroupId = safeDetachedProcessGroupId(child);
|
|
310
|
-
if (processGroupId === void 0 && !childIsAlive(child)) return;
|
|
311
|
-
const sendSignal = (signal) => {
|
|
312
|
-
signalChildProcessTree(child, signal);
|
|
313
|
-
};
|
|
314
|
-
sendSignal("SIGTERM");
|
|
315
|
-
if (await waitForProcessTreeExit(
|
|
316
|
-
child,
|
|
317
|
-
processGroupId,
|
|
318
|
-
Math.max(0, killGraceMs)
|
|
319
|
-
)) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
sendSignal("SIGKILL");
|
|
323
|
-
await waitForProcessTreeExit(
|
|
324
|
-
child,
|
|
325
|
-
processGroupId,
|
|
326
|
-
Math.min(Math.max(0, killGraceMs), 1e3)
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// ../../packages/bridge-core/src/agents/codex-adapter.ts
|
|
331
|
-
var execFileAsync = promisify(execFile);
|
|
332
|
-
var INHERITED_ENVIRONMENT = [
|
|
700
|
+
import { promisify as promisify2 } from "node:util";
|
|
701
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
702
|
+
var INHERITED_ENVIRONMENT2 = [
|
|
333
703
|
"PATH",
|
|
334
704
|
"HOME",
|
|
335
705
|
"USER",
|
|
@@ -354,15 +724,15 @@ var INHERITED_ENVIRONMENT = [
|
|
|
354
724
|
"COMSPEC",
|
|
355
725
|
"PATHEXT"
|
|
356
726
|
];
|
|
357
|
-
function
|
|
727
|
+
function processEnv2(overrides) {
|
|
358
728
|
const environment = {};
|
|
359
|
-
for (const key of
|
|
729
|
+
for (const key of INHERITED_ENVIRONMENT2) {
|
|
360
730
|
const value = process.env[key];
|
|
361
731
|
if (value !== void 0) environment[key] = value;
|
|
362
732
|
}
|
|
363
733
|
return { ...environment, ...overrides };
|
|
364
734
|
}
|
|
365
|
-
function
|
|
735
|
+
function splitLines2(chunk, previous, onLine) {
|
|
366
736
|
const combined = previous + chunk.toString();
|
|
367
737
|
const lines = combined.split(/\r?\n/);
|
|
368
738
|
const remainder = lines.pop() ?? "";
|
|
@@ -382,18 +752,24 @@ var CodexAdapter = class {
|
|
|
382
752
|
id = "codex";
|
|
383
753
|
#executable;
|
|
384
754
|
#killGraceMs;
|
|
755
|
+
#model;
|
|
756
|
+
#profile;
|
|
757
|
+
#reasoningEffort;
|
|
385
758
|
#rtkExecutable;
|
|
386
759
|
#directExecMcpScript;
|
|
387
760
|
#rtkVersion;
|
|
388
761
|
constructor(options = {}) {
|
|
389
762
|
this.#executable = options.executable ?? "codex";
|
|
390
763
|
this.#killGraceMs = options.killGraceMs ?? 2e3;
|
|
764
|
+
this.#model = options.model;
|
|
765
|
+
this.#profile = options.profile;
|
|
766
|
+
this.#reasoningEffort = options.reasoningEffort;
|
|
391
767
|
this.#rtkExecutable = options.rtkExecutable ?? "rtk";
|
|
392
768
|
this.#directExecMcpScript = options.directExecMcpScript === false ? void 0 : options.directExecMcpScript ?? defaultDirectExecMcpScript();
|
|
393
769
|
}
|
|
394
770
|
#probeRtk(environment) {
|
|
395
771
|
if (this.#rtkExecutable === false) return Promise.resolve(void 0);
|
|
396
|
-
this.#rtkVersion ??=
|
|
772
|
+
this.#rtkVersion ??= execFileAsync2(this.#rtkExecutable, ["--version"], {
|
|
397
773
|
encoding: "utf8",
|
|
398
774
|
env: environment,
|
|
399
775
|
timeout: 1e3,
|
|
@@ -436,9 +812,16 @@ ${guidance}
|
|
|
436
812
|
`mcp_servers.visual_remote_exec.args=${JSON.stringify(serverArgs)}`
|
|
437
813
|
];
|
|
438
814
|
}
|
|
815
|
+
#modelConfig() {
|
|
816
|
+
return [
|
|
817
|
+
...this.#profile === void 0 ? [] : ["--profile", this.#profile],
|
|
818
|
+
...this.#model === void 0 ? [] : ["--model", this.#model],
|
|
819
|
+
...this.#reasoningEffort === void 0 ? [] : ["-c", `model_reasoning_effort=${JSON.stringify(this.#reasoningEffort)}`]
|
|
820
|
+
];
|
|
821
|
+
}
|
|
439
822
|
async probe() {
|
|
440
823
|
return await new Promise((resolve8) => {
|
|
441
|
-
const child =
|
|
824
|
+
const child = spawn2(this.#executable, ["--version"], {
|
|
442
825
|
stdio: ["ignore", "pipe", "ignore"],
|
|
443
826
|
shell: false
|
|
444
827
|
});
|
|
@@ -471,6 +854,7 @@ ${guidance}
|
|
|
471
854
|
"workspace-write",
|
|
472
855
|
"-C",
|
|
473
856
|
input.workspaceRoot,
|
|
857
|
+
...this.#modelConfig(),
|
|
474
858
|
...this.#directExecConfig(input),
|
|
475
859
|
"-"
|
|
476
860
|
];
|
|
@@ -489,6 +873,7 @@ ${guidance}
|
|
|
489
873
|
"workspace-write",
|
|
490
874
|
"-C",
|
|
491
875
|
input.workspaceRoot,
|
|
876
|
+
...this.#modelConfig(),
|
|
492
877
|
...this.#directExecConfig(input),
|
|
493
878
|
"resume",
|
|
494
879
|
input.sessionId,
|
|
@@ -498,9 +883,9 @@ ${guidance}
|
|
|
498
883
|
}
|
|
499
884
|
async *#execute(input, signal, args) {
|
|
500
885
|
const queue = new AsyncQueue();
|
|
501
|
-
const environment =
|
|
886
|
+
const environment = processEnv2(input.environment);
|
|
502
887
|
const prompt = await this.#runtimePrompt(input, environment);
|
|
503
|
-
const child =
|
|
888
|
+
const child = spawn2(this.#executable, args, {
|
|
504
889
|
cwd: input.workspaceRoot,
|
|
505
890
|
env: environment,
|
|
506
891
|
detached: process.platform !== "win32",
|
|
@@ -532,12 +917,12 @@ ${guidance}
|
|
|
532
917
|
signal.addEventListener("abort", abort, { once: true });
|
|
533
918
|
if (signal.aborted) abort();
|
|
534
919
|
child.stdout.on("data", (chunk) => {
|
|
535
|
-
stdoutRemainder =
|
|
920
|
+
stdoutRemainder = splitLines2(chunk, stdoutRemainder, (line) => {
|
|
536
921
|
for (const event of parseCodexJsonLine(line, input.workspaceRoot)) queue.push(event);
|
|
537
922
|
});
|
|
538
923
|
});
|
|
539
924
|
child.stderr.on("data", (chunk) => {
|
|
540
|
-
stderrRemainder =
|
|
925
|
+
stderrRemainder = splitLines2(chunk, stderrRemainder, (line) => {
|
|
541
926
|
if (line.trim()) queue.push({ type: "warning", text: line });
|
|
542
927
|
});
|
|
543
928
|
});
|
|
@@ -615,6 +1000,7 @@ import { ZodError } from "zod";
|
|
|
615
1000
|
import { z } from "zod";
|
|
616
1001
|
var servicePortSchema = z.number().int().min(10001).max(65535);
|
|
617
1002
|
var commandSchema = z.array(z.string().min(1)).min(1);
|
|
1003
|
+
var environmentVariableSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Expected an environment variable name");
|
|
618
1004
|
var readySchema = z.object({
|
|
619
1005
|
path: z.string().startsWith("/").default("/"),
|
|
620
1006
|
timeoutMs: z.number().int().positive().default(6e4)
|
|
@@ -643,9 +1029,38 @@ var visualDevConfigSchema = z.object({
|
|
|
643
1029
|
}).strict(),
|
|
644
1030
|
agent: z.object({
|
|
645
1031
|
adapter: z.enum(["codex", "claude", "opencode"]),
|
|
1032
|
+
model: z.string().trim().min(1).optional(),
|
|
1033
|
+
reasoningEffort: z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]).optional(),
|
|
1034
|
+
profile: z.string().trim().regex(
|
|
1035
|
+
/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,
|
|
1036
|
+
"Expected a Codex profile name"
|
|
1037
|
+
).optional(),
|
|
1038
|
+
inheritEnv: z.array(environmentVariableSchema).default([]),
|
|
646
1039
|
maxRunMs: z.number().int().positive(),
|
|
647
1040
|
resumeMode: z.enum(["auto", "new"]).default("auto")
|
|
648
|
-
}).strict(),
|
|
1041
|
+
}).strict().superRefine((agent, context) => {
|
|
1042
|
+
if (agent.adapter === "claude" && agent.reasoningEffort === "minimal") {
|
|
1043
|
+
context.addIssue({
|
|
1044
|
+
code: "custom",
|
|
1045
|
+
path: ["reasoningEffort"],
|
|
1046
|
+
message: "minimal reasoning effort is only supported by the Codex adapter"
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
if (agent.adapter === "codex" && agent.reasoningEffort === "max") {
|
|
1050
|
+
context.addIssue({
|
|
1051
|
+
code: "custom",
|
|
1052
|
+
path: ["reasoningEffort"],
|
|
1053
|
+
message: "max reasoning effort is only supported by the Claude adapter"
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
if (agent.adapter !== "codex" && agent.profile !== void 0) {
|
|
1057
|
+
context.addIssue({
|
|
1058
|
+
code: "custom",
|
|
1059
|
+
path: ["profile"],
|
|
1060
|
+
message: "agent.profile is only supported by the Codex adapter"
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}),
|
|
649
1064
|
queue: z.object({
|
|
650
1065
|
maxPending: z.number().int().positive()
|
|
651
1066
|
}).strict(),
|
|
@@ -689,6 +1104,7 @@ function createDefaultConfig(projectId) {
|
|
|
689
1104
|
},
|
|
690
1105
|
agent: {
|
|
691
1106
|
adapter: "codex",
|
|
1107
|
+
inheritEnv: [],
|
|
692
1108
|
maxRunMs: 9e5,
|
|
693
1109
|
resumeMode: "auto"
|
|
694
1110
|
},
|
|
@@ -761,17 +1177,17 @@ function mergeConfigValues(base, override) {
|
|
|
761
1177
|
}
|
|
762
1178
|
return merged;
|
|
763
1179
|
}
|
|
764
|
-
async function readYamlMapping(
|
|
1180
|
+
async function readYamlMapping(filePath2, required) {
|
|
765
1181
|
let source;
|
|
766
1182
|
try {
|
|
767
|
-
source = await readFile(
|
|
1183
|
+
source = await readFile(filePath2, "utf8");
|
|
768
1184
|
} catch (error) {
|
|
769
1185
|
if (!required && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
770
1186
|
return void 0;
|
|
771
1187
|
}
|
|
772
|
-
throw new VisualDevConfigError(`Unable to read config: ${
|
|
1188
|
+
throw new VisualDevConfigError(`Unable to read config: ${filePath2}`, {
|
|
773
1189
|
cause: error,
|
|
774
|
-
filePath
|
|
1190
|
+
filePath: filePath2
|
|
775
1191
|
});
|
|
776
1192
|
}
|
|
777
1193
|
try {
|
|
@@ -784,9 +1200,9 @@ async function readYamlMapping(filePath, required) {
|
|
|
784
1200
|
}
|
|
785
1201
|
return value;
|
|
786
1202
|
} catch (error) {
|
|
787
|
-
throw new VisualDevConfigError(`Invalid YAML in ${
|
|
1203
|
+
throw new VisualDevConfigError(`Invalid YAML in ${filePath2}`, {
|
|
788
1204
|
cause: error,
|
|
789
|
-
filePath
|
|
1205
|
+
filePath: filePath2
|
|
790
1206
|
});
|
|
791
1207
|
}
|
|
792
1208
|
}
|
|
@@ -925,10 +1341,10 @@ var RevertConflictError = class extends RepositorySafetyError {
|
|
|
925
1341
|
};
|
|
926
1342
|
|
|
927
1343
|
// ../../packages/bridge-core/src/git/git-command.ts
|
|
928
|
-
import { spawn as
|
|
1344
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
929
1345
|
async function runGit(args, options) {
|
|
930
1346
|
return await new Promise((resolve8, reject) => {
|
|
931
|
-
const child =
|
|
1347
|
+
const child = spawn3("git", [...args], {
|
|
932
1348
|
cwd: options.cwd,
|
|
933
1349
|
env: { ...process.env, ...options.env ?? {} },
|
|
934
1350
|
shell: false,
|
|
@@ -1997,7 +2413,7 @@ import { relative as relative5, resolve as resolve6, sep as sep5 } from "node:pa
|
|
|
1997
2413
|
// ../../packages/bridge-core/src/source/path-normalizer.ts
|
|
1998
2414
|
import { access, readFile as readFile3, realpath as realpath6 } from "node:fs/promises";
|
|
1999
2415
|
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep4 } from "node:path";
|
|
2000
|
-
import { spawn as
|
|
2416
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
2001
2417
|
function toPosix(value) {
|
|
2002
2418
|
return value.split(sep4).join("/").replaceAll("\\", "/");
|
|
2003
2419
|
}
|
|
@@ -2032,7 +2448,7 @@ function isWithin2(root, candidate) {
|
|
|
2032
2448
|
}
|
|
2033
2449
|
async function gitFiles(repoRoot) {
|
|
2034
2450
|
return await new Promise((resolvePromise, reject) => {
|
|
2035
|
-
const child =
|
|
2451
|
+
const child = spawn4(
|
|
2036
2452
|
"git",
|
|
2037
2453
|
["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
|
|
2038
2454
|
{
|
|
@@ -2102,8 +2518,8 @@ async function normalizeSourceLocation(input, repoRootInput) {
|
|
|
2102
2518
|
candidates: matches.slice(0, 20)
|
|
2103
2519
|
};
|
|
2104
2520
|
}
|
|
2105
|
-
const
|
|
2106
|
-
const absolutePath = resolve5(repoRoot,
|
|
2521
|
+
const filePath2 = matches[0];
|
|
2522
|
+
const absolutePath = resolve5(repoRoot, filePath2);
|
|
2107
2523
|
const canonical = await realpath6(absolutePath);
|
|
2108
2524
|
if (!isWithin2(repoRoot, canonical)) {
|
|
2109
2525
|
return { input, confidence: "unknown", candidates: [] };
|
|
@@ -2111,7 +2527,7 @@ async function normalizeSourceLocation(input, repoRootInput) {
|
|
|
2111
2527
|
const lineNumber = await boundedLine(canonical, input.lineNumber);
|
|
2112
2528
|
return {
|
|
2113
2529
|
input,
|
|
2114
|
-
filePath,
|
|
2530
|
+
filePath: filePath2,
|
|
2115
2531
|
absolutePath: canonical,
|
|
2116
2532
|
...lineNumber === void 0 ? {} : { lineNumber },
|
|
2117
2533
|
...input.columnNumber === void 0 ? {} : { columnNumber: input.columnNumber },
|
|
@@ -2616,6 +3032,7 @@ var TaskService = class {
|
|
|
2616
3032
|
return publicTask(accepted);
|
|
2617
3033
|
}
|
|
2618
3034
|
async revert(id) {
|
|
3035
|
+
if (this.#closed) throw new TaskServiceError("SERVICE_CLOSED", "Task service is closed");
|
|
2619
3036
|
if (this.#activeTaskId || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
2620
3037
|
throw new TaskServiceError(
|
|
2621
3038
|
"WRITER_BUSY",
|
|
@@ -2631,12 +3048,19 @@ var TaskService = class {
|
|
|
2631
3048
|
if (!latest || latest.id !== id) {
|
|
2632
3049
|
throw new TaskServiceError("NOT_LATEST_TASK", "Only the latest completed task can be reverted");
|
|
2633
3050
|
}
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
3051
|
+
this.#activeTaskId = id;
|
|
3052
|
+
try {
|
|
3053
|
+
await this.#git.revert(id, task.beforeRef, task.afterRef);
|
|
3054
|
+
const reverted = this.#transition(id, "reverted", {
|
|
3055
|
+
completedAt: this.#now().toISOString()
|
|
3056
|
+
});
|
|
3057
|
+
this.#emit("task.reverted", { task: publicTask(reverted) }, id);
|
|
3058
|
+
return publicTask(reverted);
|
|
3059
|
+
} finally {
|
|
3060
|
+
this.#activeTaskId = void 0;
|
|
3061
|
+
if (!this.#closed) void this.#drain();
|
|
3062
|
+
this.#resolveIdleIfNeeded();
|
|
3063
|
+
}
|
|
2640
3064
|
}
|
|
2641
3065
|
async waitForIdle() {
|
|
2642
3066
|
if (!this.#activeTaskId && this.#queue.length === 0 && this.#recoveryQueue.length === 0 && !this.#recovering && !this.#draining) {
|
|
@@ -2647,7 +3071,7 @@ var TaskService = class {
|
|
|
2647
3071
|
async close() {
|
|
2648
3072
|
if (this.#closed) return;
|
|
2649
3073
|
this.#closed = true;
|
|
2650
|
-
if (this.#activeTaskId &&
|
|
3074
|
+
if (this.#activeTaskId && this.#activeAbort) {
|
|
2651
3075
|
this.#cancelRequested.add(this.#activeTaskId);
|
|
2652
3076
|
this.#activeAbort?.abort(new AgentCanceledError("Task service is closing"));
|
|
2653
3077
|
}
|
|
@@ -2712,9 +3136,12 @@ var TaskService = class {
|
|
|
2712
3136
|
if (!task.beforeRef) {
|
|
2713
3137
|
throw new Error("Interrupted task is missing its before snapshot");
|
|
2714
3138
|
}
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
3139
|
+
let afterRef = task.afterRef;
|
|
3140
|
+
if (!afterRef) {
|
|
3141
|
+
afterRef = (await this.#git.createSnapshot(taskId, "after")).ref;
|
|
3142
|
+
this.#store.updateTask(taskId, { afterRef });
|
|
3143
|
+
}
|
|
3144
|
+
const diff = await this.#git.diff(task.beforeRef, afterRef);
|
|
2718
3145
|
this.#store.updateTask(taskId, {
|
|
2719
3146
|
diffText: diff.text,
|
|
2720
3147
|
changedFiles: diff.files
|
|
@@ -2786,7 +3213,7 @@ var TaskService = class {
|
|
|
2786
3213
|
}
|
|
2787
3214
|
}
|
|
2788
3215
|
async #drain() {
|
|
2789
|
-
if (this.#draining || this.#closed || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
3216
|
+
if (this.#draining || this.#activeTaskId || this.#closed || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
2790
3217
|
return;
|
|
2791
3218
|
}
|
|
2792
3219
|
this.#draining = true;
|
|
@@ -3039,11 +3466,11 @@ var TaskService = class {
|
|
|
3039
3466
|
for (const target of result.selection.targets) {
|
|
3040
3467
|
const sanitize = async (location) => {
|
|
3041
3468
|
try {
|
|
3042
|
-
const
|
|
3469
|
+
const filePath2 = await this.#git.pathPolicy.assertFilesystemPathAllowed(
|
|
3043
3470
|
location.filePath,
|
|
3044
3471
|
false
|
|
3045
3472
|
);
|
|
3046
|
-
return { ...location, filePath };
|
|
3473
|
+
return { ...location, filePath: filePath2 };
|
|
3047
3474
|
} catch {
|
|
3048
3475
|
return void 0;
|
|
3049
3476
|
}
|
|
@@ -3255,7 +3682,7 @@ var BrowserSessionManager = class {
|
|
|
3255
3682
|
};
|
|
3256
3683
|
|
|
3257
3684
|
// ../../packages/bridge-core/src/verification/commands.ts
|
|
3258
|
-
import { spawn as
|
|
3685
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
3259
3686
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
3260
3687
|
var KILL_GRACE_MS = 250;
|
|
3261
3688
|
function appendOutput(current, chunk) {
|
|
@@ -3269,7 +3696,7 @@ async function runVerificationCommand(configured, cwd, signal) {
|
|
|
3269
3696
|
}
|
|
3270
3697
|
const startedAt = Date.now();
|
|
3271
3698
|
return await new Promise((resolveResult, rejectResult) => {
|
|
3272
|
-
const child =
|
|
3699
|
+
const child = spawn5(executable, arguments_, {
|
|
3273
3700
|
cwd,
|
|
3274
3701
|
detached: process.platform !== "win32",
|
|
3275
3702
|
shell: false,
|
|
@@ -3844,15 +4271,40 @@ ${output}` : ""}`
|
|
|
3844
4271
|
}
|
|
3845
4272
|
|
|
3846
4273
|
// ../../packages/bridge-core/src/bridge/default-control-service.ts
|
|
4274
|
+
function createAgentAdapter(agent) {
|
|
4275
|
+
if (agent.adapter === "claude") {
|
|
4276
|
+
if (agent.reasoningEffort === "minimal") {
|
|
4277
|
+
throw new Error("Claude does not support minimal reasoning effort");
|
|
4278
|
+
}
|
|
4279
|
+
return new ClaudeAdapter({
|
|
4280
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
4281
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort }
|
|
4282
|
+
});
|
|
4283
|
+
}
|
|
4284
|
+
if (agent.adapter === "codex") {
|
|
4285
|
+
if (agent.reasoningEffort === "max") {
|
|
4286
|
+
throw new Error("Codex does not support max reasoning effort");
|
|
4287
|
+
}
|
|
4288
|
+
return new CodexAdapter({
|
|
4289
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
4290
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort },
|
|
4291
|
+
...agent.profile === void 0 ? {} : { profile: agent.profile }
|
|
4292
|
+
});
|
|
4293
|
+
}
|
|
4294
|
+
throw new Error(`Agent adapter ${agent.adapter} is not implemented in this build`);
|
|
4295
|
+
}
|
|
4296
|
+
function inheritedAgentEnvironment(names, environment) {
|
|
4297
|
+
return Object.fromEntries(
|
|
4298
|
+
names.flatMap((name) => {
|
|
4299
|
+
const value = environment[name];
|
|
4300
|
+
return value === void 0 ? [] : [[name, value]];
|
|
4301
|
+
})
|
|
4302
|
+
);
|
|
4303
|
+
}
|
|
3847
4304
|
async function createDefaultControlService(context, environment = process.env) {
|
|
3848
4305
|
const loaded = await loadVisualDevConfig(context.repoRoot, {
|
|
3849
4306
|
...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
|
|
3850
4307
|
});
|
|
3851
|
-
if (loaded.config.agent.adapter !== "codex") {
|
|
3852
|
-
throw new Error(
|
|
3853
|
-
`Agent adapter ${loaded.config.agent.adapter} is not implemented in this MVP build`
|
|
3854
|
-
);
|
|
3855
|
-
}
|
|
3856
4308
|
const git = await GitTransactionManager.open(context.repoRoot, {
|
|
3857
4309
|
allowed: rebaseWorkspacePatterns(
|
|
3858
4310
|
context.repoRoot,
|
|
@@ -3871,13 +4323,16 @@ async function createDefaultControlService(context, environment = process.env) {
|
|
|
3871
4323
|
projectId: context.projectId,
|
|
3872
4324
|
workspaceRoot: context.workspaceRoot,
|
|
3873
4325
|
upstreamUrl: context.upstreamUrl,
|
|
3874
|
-
adapter:
|
|
4326
|
+
adapter: createAgentAdapter(loaded.config.agent),
|
|
3875
4327
|
store,
|
|
3876
4328
|
git,
|
|
3877
4329
|
maxRunMs: loaded.config.agent.maxRunMs,
|
|
3878
4330
|
maxPending: loaded.config.queue.maxPending,
|
|
3879
4331
|
resumeMode: loaded.config.agent.resumeMode,
|
|
3880
|
-
environment:
|
|
4332
|
+
environment: inheritedAgentEnvironment(
|
|
4333
|
+
loaded.config.agent.inheritEnv,
|
|
4334
|
+
environment
|
|
4335
|
+
)
|
|
3881
4336
|
});
|
|
3882
4337
|
const controlService = createTaskControlService({
|
|
3883
4338
|
taskService,
|
|
@@ -4208,10 +4663,10 @@ async function acquireWorktreeLock(repositoryRoot, options = {}) {
|
|
|
4208
4663
|
}
|
|
4209
4664
|
|
|
4210
4665
|
// ../../packages/bridge-core/src/runtime/repository.ts
|
|
4211
|
-
import { execFile as
|
|
4666
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
4212
4667
|
import { realpath as realpath9 } from "node:fs/promises";
|
|
4213
|
-
import { promisify as
|
|
4214
|
-
var
|
|
4668
|
+
import { promisify as promisify3 } from "node:util";
|
|
4669
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
4215
4670
|
var GitWorktreeNotFoundError = class extends Error {
|
|
4216
4671
|
constructor(cwd, options = {}) {
|
|
4217
4672
|
super(
|
|
@@ -4223,7 +4678,7 @@ var GitWorktreeNotFoundError = class extends Error {
|
|
|
4223
4678
|
};
|
|
4224
4679
|
async function discoverGitWorktreeRoot(cwd = process.cwd()) {
|
|
4225
4680
|
try {
|
|
4226
|
-
const { stdout } = await
|
|
4681
|
+
const { stdout } = await execFileAsync3(
|
|
4227
4682
|
"git",
|
|
4228
4683
|
["-C", cwd, "rev-parse", "--show-toplevel"],
|
|
4229
4684
|
{
|
|
@@ -5253,7 +5708,7 @@ async function startBridgeCore(options, dependencies) {
|
|
|
5253
5708
|
try {
|
|
5254
5709
|
lock = options.lock ?? await acquireWorktreeLock(loadedConfig.repoRoot, { environment });
|
|
5255
5710
|
const host = options.host ?? loadedConfig.config.gateway.host;
|
|
5256
|
-
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl;
|
|
5711
|
+
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl ?? options.fallbackPublicUrl;
|
|
5257
5712
|
const publicUrl = configuredPublicUrl === void 0 ? void 0 : normalizePublicUrl(configuredPublicUrl);
|
|
5258
5713
|
const gatewayPort = await findAvailablePort(startPort(loadedConfig, options.listen), host);
|
|
5259
5714
|
const token = generatePairingToken();
|
|
@@ -5271,6 +5726,13 @@ async function startBridgeCore(options, dependencies) {
|
|
|
5271
5726
|
controlService = await resolveControlService(dependencies, controlContext);
|
|
5272
5727
|
const allowedOrigins = new Set(loadedConfig.config.security.allowedOrigins);
|
|
5273
5728
|
if (publicUrl !== void 0) allowedOrigins.add(new URL(publicUrl).origin);
|
|
5729
|
+
if (options.fallbackLoopbackOrigins === true && options.publicUrl === void 0 && loadedConfig.config.gateway.publicUrl === void 0 && loadedConfig.config.security.allowedOrigins.length === 0 && publicUrl !== void 0) {
|
|
5730
|
+
const loopbackUrl = new URL(publicUrl);
|
|
5731
|
+
if (loopbackUrl.hostname === "localhost" || loopbackUrl.hostname === "127.0.0.1") {
|
|
5732
|
+
loopbackUrl.hostname = loopbackUrl.hostname === "localhost" ? "127.0.0.1" : "localhost";
|
|
5733
|
+
allowedOrigins.add(loopbackUrl.origin);
|
|
5734
|
+
}
|
|
5735
|
+
}
|
|
5274
5736
|
gateway = createGatewayServer({
|
|
5275
5737
|
upstream: options.upstreamUrl,
|
|
5276
5738
|
pairingToken: token,
|
|
@@ -5373,7 +5835,9 @@ async function startAttachBridge(options, dependencies = {}) {
|
|
|
5373
5835
|
upstreamUrl: normalizeUpstream(options.upstream),
|
|
5374
5836
|
...options.listen === void 0 ? {} : { listen: options.listen },
|
|
5375
5837
|
...options.host === void 0 ? {} : { host: options.host },
|
|
5376
|
-
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl }
|
|
5838
|
+
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl },
|
|
5839
|
+
...options.fallbackPublicUrl === void 0 ? {} : { fallbackPublicUrl: options.fallbackPublicUrl },
|
|
5840
|
+
...options.fallbackLoopbackOrigins === void 0 ? {} : { fallbackLoopbackOrigins: options.fallbackLoopbackOrigins }
|
|
5377
5841
|
},
|
|
5378
5842
|
dependencies
|
|
5379
5843
|
);
|
|
@@ -5409,10 +5873,17 @@ function commandLinePort(argv) {
|
|
|
5409
5873
|
}
|
|
5410
5874
|
return void 0;
|
|
5411
5875
|
}
|
|
5876
|
+
function resolveNextPort(options, argv, environment) {
|
|
5877
|
+
return validPort(options.appPort) ?? commandLinePort(argv) ?? validPort(environment.PORT) ?? 3e3;
|
|
5878
|
+
}
|
|
5412
5879
|
function resolveNextUpstream(options = {}, argv = process.argv, environment = process.env) {
|
|
5413
|
-
const port =
|
|
5880
|
+
const port = resolveNextPort(options, argv, environment);
|
|
5414
5881
|
return `http://127.0.0.1:${port}`;
|
|
5415
5882
|
}
|
|
5883
|
+
function resolveNextPublicUrl(options = {}, argv = process.argv, environment = process.env) {
|
|
5884
|
+
const port = resolveNextPort(options, argv, environment);
|
|
5885
|
+
return `http://localhost:${port}`;
|
|
5886
|
+
}
|
|
5416
5887
|
function isNextDetachedTelemetryProcess(argv = process.argv) {
|
|
5417
5888
|
return argv.some(
|
|
5418
5889
|
(argument) => /[\\/]next[\\/]dist[\\/]telemetry[\\/]detached-flush\.js$/.test(argument)
|
|
@@ -5460,6 +5931,8 @@ async function startOrReuseBridge(options, cwd) {
|
|
|
5460
5931
|
const ownedBridge = await startAttachBridge(
|
|
5461
5932
|
{
|
|
5462
5933
|
upstream: resolveNextUpstream(options),
|
|
5934
|
+
fallbackPublicUrl: resolveNextPublicUrl(options),
|
|
5935
|
+
fallbackLoopbackOrigins: true,
|
|
5463
5936
|
...options.bridgeHost === void 0 ? {} : { host: options.bridgeHost },
|
|
5464
5937
|
...options.bridgePort === void 0 ? {} : { listen: options.bridgePort }
|
|
5465
5938
|
},
|
|
@@ -5531,6 +6004,7 @@ export {
|
|
|
5531
6004
|
next_default as default,
|
|
5532
6005
|
isNextDetachedTelemetryProcess,
|
|
5533
6006
|
mergeNextRewrites,
|
|
6007
|
+
resolveNextPublicUrl,
|
|
5534
6008
|
resolveNextUpstream,
|
|
5535
6009
|
withVisualRemote
|
|
5536
6010
|
};
|