visual-remote 0.3.1 → 0.3.2
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 +43 -8
- package/apps/cli/dist/direct-exec-mcp.js +108 -1
- package/apps/cli/dist/index.js +930 -352
- package/apps/cli/dist/next.js +661 -207
- package/apps/cli/dist/vite.js +651 -206
- package/package.json +9 -6
package/apps/cli/dist/index.js
CHANGED
|
@@ -971,27 +971,595 @@ var AgentCanceledError = class extends Error {
|
|
|
971
971
|
}
|
|
972
972
|
};
|
|
973
973
|
|
|
974
|
+
// ../../packages/bridge-core/src/agents/claude-event-parser.ts
|
|
975
|
+
function asRecord(value) {
|
|
976
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
977
|
+
}
|
|
978
|
+
function asText(value) {
|
|
979
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
980
|
+
}
|
|
981
|
+
function asNumber(value) {
|
|
982
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
983
|
+
}
|
|
984
|
+
function contentBlocks(record) {
|
|
985
|
+
const message = asRecord(record.message);
|
|
986
|
+
return Array.isArray(message?.content) ? message.content.flatMap((block) => {
|
|
987
|
+
const parsed = asRecord(block);
|
|
988
|
+
return parsed === void 0 ? [] : [parsed];
|
|
989
|
+
}) : [];
|
|
990
|
+
}
|
|
991
|
+
function toolSummary(name, input) {
|
|
992
|
+
return asText(input.description) ?? asText(input.command) ?? asText(input.file_path) ?? asText(input.path) ?? (name === "Bash" ? "Run command" : void 0);
|
|
993
|
+
}
|
|
994
|
+
function filePath(input) {
|
|
995
|
+
return asText(input.file_path) ?? asText(input.path) ?? asText(input.notebook_path);
|
|
996
|
+
}
|
|
997
|
+
var ClaudeEventParser = class {
|
|
998
|
+
#defaultCwd;
|
|
999
|
+
#tools = /* @__PURE__ */ new Map();
|
|
1000
|
+
#sessionId;
|
|
1001
|
+
constructor(defaultCwd = "") {
|
|
1002
|
+
this.#defaultCwd = defaultCwd;
|
|
1003
|
+
}
|
|
1004
|
+
parse(line) {
|
|
1005
|
+
const trimmed = line.trim();
|
|
1006
|
+
if (!trimmed) return [];
|
|
1007
|
+
let value;
|
|
1008
|
+
try {
|
|
1009
|
+
value = JSON.parse(trimmed);
|
|
1010
|
+
} catch {
|
|
1011
|
+
return [{ type: "warning", text: trimmed }];
|
|
1012
|
+
}
|
|
1013
|
+
const record = asRecord(value);
|
|
1014
|
+
if (!record) return [{ type: "message", text: trimmed }];
|
|
1015
|
+
const events = [];
|
|
1016
|
+
const foundSession = asText(record.session_id) ?? asText(record.sessionId);
|
|
1017
|
+
if (foundSession !== void 0 && foundSession !== this.#sessionId) {
|
|
1018
|
+
this.#sessionId = foundSession;
|
|
1019
|
+
events.push({ type: "session", sessionId: foundSession });
|
|
1020
|
+
}
|
|
1021
|
+
const type = asText(record.type) ?? "unknown";
|
|
1022
|
+
if (type === "system") {
|
|
1023
|
+
const subtype = asText(record.subtype);
|
|
1024
|
+
if (subtype) events.push({ type: "phase", name: subtype });
|
|
1025
|
+
return events;
|
|
1026
|
+
}
|
|
1027
|
+
if (type === "assistant") {
|
|
1028
|
+
for (const block of contentBlocks(record)) {
|
|
1029
|
+
if (block.type === "text") {
|
|
1030
|
+
const text = asText(block.text);
|
|
1031
|
+
if (text) events.push({ type: "message", text });
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (block.type !== "tool_use") continue;
|
|
1035
|
+
const name = asText(block.name) ?? "tool";
|
|
1036
|
+
const id = asText(block.id);
|
|
1037
|
+
const input = asRecord(block.input) ?? {};
|
|
1038
|
+
if (id) this.#tools.set(id, name);
|
|
1039
|
+
const summary = toolSummary(name, input);
|
|
1040
|
+
events.push(
|
|
1041
|
+
summary ? { type: "tool_start", name, summary } : { type: "tool_start", name }
|
|
1042
|
+
);
|
|
1043
|
+
const command = name === "Bash" ? asText(input.command) : void 0;
|
|
1044
|
+
if (command) {
|
|
1045
|
+
events.push({ type: "command", command, cwd: this.#defaultCwd });
|
|
1046
|
+
}
|
|
1047
|
+
const path = filePath(input);
|
|
1048
|
+
if (path && ["Edit", "Write", "NotebookEdit"].includes(name)) {
|
|
1049
|
+
events.push({ type: "file_hint", path });
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
return events;
|
|
1053
|
+
}
|
|
1054
|
+
if (type === "user") {
|
|
1055
|
+
for (const block of contentBlocks(record)) {
|
|
1056
|
+
if (block.type !== "tool_result") continue;
|
|
1057
|
+
const id = asText(block.tool_use_id);
|
|
1058
|
+
const name = (id ? this.#tools.get(id) : void 0) ?? "tool";
|
|
1059
|
+
if (id) this.#tools.delete(id);
|
|
1060
|
+
events.push({ type: "tool_end", name, ok: block.is_error !== true });
|
|
1061
|
+
}
|
|
1062
|
+
return events;
|
|
1063
|
+
}
|
|
1064
|
+
if (type === "result") {
|
|
1065
|
+
const usage = asRecord(record.usage);
|
|
1066
|
+
if (usage) {
|
|
1067
|
+
const cachedInputTokens = asNumber(usage.cache_read_input_tokens);
|
|
1068
|
+
events.push({
|
|
1069
|
+
type: "usage",
|
|
1070
|
+
inputTokens: asNumber(usage.input_tokens) + asNumber(usage.cache_creation_input_tokens) + cachedInputTokens,
|
|
1071
|
+
outputTokens: asNumber(usage.output_tokens),
|
|
1072
|
+
...cachedInputTokens > 0 ? { cachedInputTokens } : {}
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
const result = asText(record.result);
|
|
1076
|
+
const subtype = asText(record.subtype);
|
|
1077
|
+
if (record.is_error === true || subtype?.startsWith("error") === true) {
|
|
1078
|
+
events.push({ type: "error", text: result ?? "Claude reported an error" });
|
|
1079
|
+
} else {
|
|
1080
|
+
events.push(result ? { type: "complete", summary: result } : { type: "complete" });
|
|
1081
|
+
}
|
|
1082
|
+
return events;
|
|
1083
|
+
}
|
|
1084
|
+
return events;
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// ../../packages/bridge-core/src/agents/claude-adapter.ts
|
|
1089
|
+
import { execFile, spawn as spawn2 } from "node:child_process";
|
|
1090
|
+
import { promisify } from "node:util";
|
|
1091
|
+
|
|
1092
|
+
// ../../packages/bridge-core/src/runtime/managed-process.ts
|
|
1093
|
+
import { spawn } from "node:child_process";
|
|
1094
|
+
function replacePortPlaceholder(value, port) {
|
|
1095
|
+
return value.replaceAll("{upstreamPort}", String(port));
|
|
1096
|
+
}
|
|
1097
|
+
function safeChildProcessId(child) {
|
|
1098
|
+
const pid = child.pid;
|
|
1099
|
+
if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
|
|
1100
|
+
return void 0;
|
|
1101
|
+
}
|
|
1102
|
+
return pid;
|
|
1103
|
+
}
|
|
1104
|
+
function safeDetachedProcessGroupId(child) {
|
|
1105
|
+
return process.platform === "win32" ? void 0 : safeChildProcessId(child);
|
|
1106
|
+
}
|
|
1107
|
+
function isMissingProcess(error) {
|
|
1108
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
|
|
1109
|
+
}
|
|
1110
|
+
function processGroupIsAlive(processGroupId) {
|
|
1111
|
+
try {
|
|
1112
|
+
process.kill(-processGroupId, 0);
|
|
1113
|
+
return true;
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
if (isMissingProcess(error)) return false;
|
|
1116
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
|
|
1117
|
+
return true;
|
|
1118
|
+
}
|
|
1119
|
+
throw error;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
function childIsAlive(child) {
|
|
1123
|
+
return child.exitCode === null && child.signalCode === null;
|
|
1124
|
+
}
|
|
1125
|
+
function signalChildProcessTree(child, signal) {
|
|
1126
|
+
const pid = safeChildProcessId(child);
|
|
1127
|
+
if (pid === void 0) return false;
|
|
1128
|
+
try {
|
|
1129
|
+
if (process.platform === "win32") {
|
|
1130
|
+
if (!childIsAlive(child)) return false;
|
|
1131
|
+
return child.kill(signal);
|
|
1132
|
+
}
|
|
1133
|
+
process.kill(-pid, signal);
|
|
1134
|
+
return true;
|
|
1135
|
+
} catch (error) {
|
|
1136
|
+
if (isMissingProcess(error)) return false;
|
|
1137
|
+
throw error;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
function installEmergencyChildExitHook(child, processLike = process) {
|
|
1141
|
+
let removed = false;
|
|
1142
|
+
const emergencyExit = () => {
|
|
1143
|
+
removed = true;
|
|
1144
|
+
try {
|
|
1145
|
+
signalChildProcessTree(child, "SIGKILL");
|
|
1146
|
+
} catch {
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
processLike.once("exit", emergencyExit);
|
|
1150
|
+
return () => {
|
|
1151
|
+
if (removed) return;
|
|
1152
|
+
removed = true;
|
|
1153
|
+
processLike.off("exit", emergencyExit);
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
|
|
1157
|
+
const deadline = Date.now() + timeoutMs;
|
|
1158
|
+
while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
|
|
1159
|
+
const remaining = deadline - Date.now();
|
|
1160
|
+
if (remaining <= 0) return false;
|
|
1161
|
+
await new Promise((resolve9) => {
|
|
1162
|
+
setTimeout(resolve9, Math.min(25, remaining));
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
return true;
|
|
1166
|
+
}
|
|
1167
|
+
async function terminateChildProcessTree(child, killGraceMs = 3e3) {
|
|
1168
|
+
const processGroupId = safeDetachedProcessGroupId(child);
|
|
1169
|
+
if (processGroupId === void 0 && !childIsAlive(child)) return;
|
|
1170
|
+
const sendSignal = (signal) => {
|
|
1171
|
+
signalChildProcessTree(child, signal);
|
|
1172
|
+
};
|
|
1173
|
+
sendSignal("SIGTERM");
|
|
1174
|
+
if (await waitForProcessTreeExit(
|
|
1175
|
+
child,
|
|
1176
|
+
processGroupId,
|
|
1177
|
+
Math.max(0, killGraceMs)
|
|
1178
|
+
)) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
sendSignal("SIGKILL");
|
|
1182
|
+
await waitForProcessTreeExit(
|
|
1183
|
+
child,
|
|
1184
|
+
processGroupId,
|
|
1185
|
+
Math.min(Math.max(0, killGraceMs), 1e3)
|
|
1186
|
+
);
|
|
1187
|
+
}
|
|
1188
|
+
async function startManagedProcess(options) {
|
|
1189
|
+
const [executable, ...rawArguments] = options.command;
|
|
1190
|
+
if (executable === void 0) {
|
|
1191
|
+
throw new Error("Managed dev command is empty");
|
|
1192
|
+
}
|
|
1193
|
+
const detached = process.platform !== "win32";
|
|
1194
|
+
const child = spawn(
|
|
1195
|
+
replacePortPlaceholder(executable, options.upstreamPort),
|
|
1196
|
+
rawArguments.map((argument) => replacePortPlaceholder(argument, options.upstreamPort)),
|
|
1197
|
+
{
|
|
1198
|
+
cwd: options.cwd,
|
|
1199
|
+
env: {
|
|
1200
|
+
...options.environment ?? process.env,
|
|
1201
|
+
HOST: "0.0.0.0",
|
|
1202
|
+
PORT: String(options.upstreamPort)
|
|
1203
|
+
},
|
|
1204
|
+
detached,
|
|
1205
|
+
stdio: [
|
|
1206
|
+
"inherit",
|
|
1207
|
+
options.stdout === void 0 ? "inherit" : "pipe",
|
|
1208
|
+
options.stderr === void 0 ? "inherit" : "pipe"
|
|
1209
|
+
],
|
|
1210
|
+
windowsHide: true
|
|
1211
|
+
}
|
|
1212
|
+
);
|
|
1213
|
+
const removeEmergencyExitHook = installEmergencyChildExitHook(child);
|
|
1214
|
+
const exit = new Promise(
|
|
1215
|
+
(resolve9) => {
|
|
1216
|
+
child.once("exit", (code, signal) => {
|
|
1217
|
+
resolve9({ code, signal });
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
);
|
|
1221
|
+
if (child.stdout !== null && options.stdout !== void 0) {
|
|
1222
|
+
child.stdout.pipe(options.stdout, { end: false });
|
|
1223
|
+
}
|
|
1224
|
+
if (child.stderr !== null && options.stderr !== void 0) {
|
|
1225
|
+
child.stderr.pipe(options.stderr, { end: false });
|
|
1226
|
+
}
|
|
1227
|
+
try {
|
|
1228
|
+
await new Promise((resolve9, reject) => {
|
|
1229
|
+
child.once("spawn", resolve9);
|
|
1230
|
+
child.once("error", reject);
|
|
1231
|
+
});
|
|
1232
|
+
} catch (error) {
|
|
1233
|
+
removeEmergencyExitHook();
|
|
1234
|
+
throw error;
|
|
1235
|
+
}
|
|
1236
|
+
let stopPromise;
|
|
1237
|
+
return {
|
|
1238
|
+
child,
|
|
1239
|
+
exit,
|
|
1240
|
+
stop() {
|
|
1241
|
+
stopPromise ??= terminateChildProcessTree(
|
|
1242
|
+
child,
|
|
1243
|
+
options.killGraceMs
|
|
1244
|
+
).finally(removeEmergencyExitHook);
|
|
1245
|
+
return stopPromise;
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
// ../../packages/bridge-core/src/agents/async-queue.ts
|
|
1251
|
+
var AsyncQueue = class {
|
|
1252
|
+
#values = [];
|
|
1253
|
+
#waiters = [];
|
|
1254
|
+
#ended = false;
|
|
1255
|
+
#error;
|
|
1256
|
+
push(value) {
|
|
1257
|
+
if (this.#ended) return;
|
|
1258
|
+
const waiter = this.#waiters.shift();
|
|
1259
|
+
if (waiter) waiter.resolve({ value, done: false });
|
|
1260
|
+
else this.#values.push(value);
|
|
1261
|
+
}
|
|
1262
|
+
end(error) {
|
|
1263
|
+
if (this.#ended) return;
|
|
1264
|
+
this.#ended = true;
|
|
1265
|
+
this.#error = error;
|
|
1266
|
+
for (const waiter of this.#waiters.splice(0)) {
|
|
1267
|
+
if (error !== void 0) waiter.reject(error);
|
|
1268
|
+
else waiter.resolve({ value: void 0, done: true });
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
[Symbol.asyncIterator]() {
|
|
1272
|
+
return {
|
|
1273
|
+
next: async () => {
|
|
1274
|
+
const value = this.#values.shift();
|
|
1275
|
+
if (value !== void 0) return { value, done: false };
|
|
1276
|
+
if (this.#ended) {
|
|
1277
|
+
if (this.#error !== void 0) throw this.#error;
|
|
1278
|
+
return { value: void 0, done: true };
|
|
1279
|
+
}
|
|
1280
|
+
return await new Promise((resolve9, reject) => {
|
|
1281
|
+
this.#waiters.push({ resolve: resolve9, reject });
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
};
|
|
1287
|
+
|
|
1288
|
+
// ../../packages/bridge-core/src/agents/claude-adapter.ts
|
|
1289
|
+
var execFileAsync = promisify(execFile);
|
|
1290
|
+
var INHERITED_ENVIRONMENT = [
|
|
1291
|
+
"PATH",
|
|
1292
|
+
"HOME",
|
|
1293
|
+
"USER",
|
|
1294
|
+
"LOGNAME",
|
|
1295
|
+
"SHELL",
|
|
1296
|
+
"LANG",
|
|
1297
|
+
"LC_ALL",
|
|
1298
|
+
"TERM",
|
|
1299
|
+
"TMPDIR",
|
|
1300
|
+
"XDG_CONFIG_HOME",
|
|
1301
|
+
"XDG_DATA_HOME",
|
|
1302
|
+
"XDG_STATE_HOME",
|
|
1303
|
+
"ANTHROPIC_API_KEY",
|
|
1304
|
+
"CLAUDE_CODE_OAUTH_TOKEN",
|
|
1305
|
+
"HTTPS_PROXY",
|
|
1306
|
+
"HTTP_PROXY",
|
|
1307
|
+
"NO_PROXY",
|
|
1308
|
+
"USERPROFILE",
|
|
1309
|
+
"APPDATA",
|
|
1310
|
+
"LOCALAPPDATA",
|
|
1311
|
+
"SystemRoot",
|
|
1312
|
+
"COMSPEC",
|
|
1313
|
+
"PATHEXT"
|
|
1314
|
+
];
|
|
1315
|
+
var EMPTY_MCP_CONFIG = JSON.stringify({ mcpServers: {} });
|
|
1316
|
+
var CLAUDE_SETTINGS = JSON.stringify({
|
|
1317
|
+
permissions: {
|
|
1318
|
+
disableBypassPermissionsMode: "disable",
|
|
1319
|
+
deny: [
|
|
1320
|
+
"Read(./.env)",
|
|
1321
|
+
"Read(./.env.*)",
|
|
1322
|
+
"Read(./**/*.pem)",
|
|
1323
|
+
"Read(./**/*.key)",
|
|
1324
|
+
"Edit(./.git/**)",
|
|
1325
|
+
"Edit(./.visualdev/runtime/**)",
|
|
1326
|
+
"Edit(./node_modules/**)"
|
|
1327
|
+
]
|
|
1328
|
+
},
|
|
1329
|
+
sandbox: {
|
|
1330
|
+
enabled: true,
|
|
1331
|
+
autoAllowBashIfSandboxed: true,
|
|
1332
|
+
allowUnsandboxedCommands: false,
|
|
1333
|
+
network: { strictAllowlist: true }
|
|
1334
|
+
}
|
|
1335
|
+
});
|
|
1336
|
+
function processEnv(overrides) {
|
|
1337
|
+
const environment = {};
|
|
1338
|
+
for (const key of INHERITED_ENVIRONMENT) {
|
|
1339
|
+
const value = process.env[key];
|
|
1340
|
+
if (value !== void 0) environment[key] = value;
|
|
1341
|
+
}
|
|
1342
|
+
return { ...environment, ...overrides };
|
|
1343
|
+
}
|
|
1344
|
+
function splitLines(chunk, previous, onLine) {
|
|
1345
|
+
const combined = previous + chunk.toString();
|
|
1346
|
+
const lines = combined.split(/\r?\n/);
|
|
1347
|
+
const remainder = lines.pop() ?? "";
|
|
1348
|
+
for (const line of lines) onLine(line);
|
|
1349
|
+
return remainder;
|
|
1350
|
+
}
|
|
1351
|
+
var ClaudeAdapter = class {
|
|
1352
|
+
id = "claude";
|
|
1353
|
+
#executable;
|
|
1354
|
+
#killGraceMs;
|
|
1355
|
+
#model;
|
|
1356
|
+
#reasoningEffort;
|
|
1357
|
+
#rtkExecutable;
|
|
1358
|
+
#rtkVersion;
|
|
1359
|
+
constructor(options = {}) {
|
|
1360
|
+
this.#executable = options.executable ?? "claude";
|
|
1361
|
+
this.#killGraceMs = options.killGraceMs ?? 2e3;
|
|
1362
|
+
this.#model = options.model;
|
|
1363
|
+
this.#reasoningEffort = options.reasoningEffort;
|
|
1364
|
+
this.#rtkExecutable = options.rtkExecutable ?? "rtk";
|
|
1365
|
+
}
|
|
1366
|
+
#probeRtk(environment) {
|
|
1367
|
+
if (this.#rtkExecutable === false) return Promise.resolve(void 0);
|
|
1368
|
+
this.#rtkVersion ??= execFileAsync(this.#rtkExecutable, ["--version"], {
|
|
1369
|
+
encoding: "utf8",
|
|
1370
|
+
env: environment,
|
|
1371
|
+
timeout: 1e3,
|
|
1372
|
+
windowsHide: true,
|
|
1373
|
+
maxBuffer: 16 * 1024
|
|
1374
|
+
}).then(({ stdout }) => stdout.trim().split(/\r?\n/, 1)[0] || void 0).catch(() => void 0);
|
|
1375
|
+
return this.#rtkVersion;
|
|
1376
|
+
}
|
|
1377
|
+
async #runtimePrompt(input, environment) {
|
|
1378
|
+
if (this.#rtkExecutable === false) return input.prompt;
|
|
1379
|
+
const guidance = await this.#probeRtk(environment).then(
|
|
1380
|
+
(version) => version ? `RTK command proxy:
|
|
1381
|
+
- ${version} is installed and available in this runtime.
|
|
1382
|
+
- Prefix shell commands with RTK by default (for example: rtk git status, rtk rg <pattern>, rtk read <file>, rtk npm test).
|
|
1383
|
+
- 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:
|
|
1384
|
+
- RTK was not detected in this runtime. Use native repository commands directly and do not spend time searching for RTK.`
|
|
1385
|
+
);
|
|
1386
|
+
return `${input.prompt.trimEnd()}
|
|
1387
|
+
|
|
1388
|
+
${guidance}
|
|
1389
|
+
`;
|
|
1390
|
+
}
|
|
1391
|
+
#baseArgs() {
|
|
1392
|
+
return [
|
|
1393
|
+
"-p",
|
|
1394
|
+
"--output-format",
|
|
1395
|
+
"stream-json",
|
|
1396
|
+
"--verbose",
|
|
1397
|
+
"--permission-mode",
|
|
1398
|
+
"acceptEdits",
|
|
1399
|
+
"--strict-mcp-config",
|
|
1400
|
+
"--mcp-config",
|
|
1401
|
+
EMPTY_MCP_CONFIG,
|
|
1402
|
+
"--no-chrome",
|
|
1403
|
+
"--tools",
|
|
1404
|
+
"Read,Glob,Grep,Edit,Write,Bash",
|
|
1405
|
+
"--settings",
|
|
1406
|
+
CLAUDE_SETTINGS,
|
|
1407
|
+
...this.#model === void 0 ? [] : ["--model", this.#model],
|
|
1408
|
+
...this.#reasoningEffort === void 0 ? [] : ["--effort", this.#reasoningEffort]
|
|
1409
|
+
];
|
|
1410
|
+
}
|
|
1411
|
+
async probe() {
|
|
1412
|
+
return await new Promise((resolve9) => {
|
|
1413
|
+
const child = spawn2(this.#executable, ["--version"], {
|
|
1414
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
1415
|
+
shell: false
|
|
1416
|
+
});
|
|
1417
|
+
let output2 = "";
|
|
1418
|
+
child.stdout?.on("data", (chunk) => {
|
|
1419
|
+
output2 += chunk.toString();
|
|
1420
|
+
});
|
|
1421
|
+
child.once("error", () => {
|
|
1422
|
+
resolve9({ available: false, supportsResume: true, structuredOutput: true });
|
|
1423
|
+
});
|
|
1424
|
+
child.once("close", (code) => {
|
|
1425
|
+
const match = output2.match(/(\d+\.\d+\.\d+(?:[-+][^\s]+)?)/);
|
|
1426
|
+
const capabilities = {
|
|
1427
|
+
available: code === 0,
|
|
1428
|
+
supportsResume: true,
|
|
1429
|
+
structuredOutput: true
|
|
1430
|
+
};
|
|
1431
|
+
if (match?.[1]) capabilities.version = match[1];
|
|
1432
|
+
resolve9(capabilities);
|
|
1433
|
+
});
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
async *run(input, signal) {
|
|
1437
|
+
yield* this.#execute(input, signal, this.#baseArgs());
|
|
1438
|
+
}
|
|
1439
|
+
async *resume(input, signal) {
|
|
1440
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,255}$/.test(input.sessionId)) {
|
|
1441
|
+
throw new Error("Refusing to resume an invalid Claude session id");
|
|
1442
|
+
}
|
|
1443
|
+
yield* this.#execute(
|
|
1444
|
+
input,
|
|
1445
|
+
signal,
|
|
1446
|
+
[...this.#baseArgs(), "--resume", input.sessionId]
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
async *#execute(input, signal, args) {
|
|
1450
|
+
const queue = new AsyncQueue();
|
|
1451
|
+
const environment = processEnv(input.environment);
|
|
1452
|
+
const prompt = await this.#runtimePrompt(input, environment);
|
|
1453
|
+
const parser = new ClaudeEventParser(input.workspaceRoot);
|
|
1454
|
+
const child = spawn2(this.#executable, args, {
|
|
1455
|
+
cwd: input.workspaceRoot,
|
|
1456
|
+
env: environment,
|
|
1457
|
+
detached: process.platform !== "win32",
|
|
1458
|
+
shell: false,
|
|
1459
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1460
|
+
});
|
|
1461
|
+
const removeEmergencyExitHook = installEmergencyChildExitHook(child);
|
|
1462
|
+
let stdoutRemainder = "";
|
|
1463
|
+
let stderrRemainder = "";
|
|
1464
|
+
let timedOut = false;
|
|
1465
|
+
let aborted = signal.aborted;
|
|
1466
|
+
let termination;
|
|
1467
|
+
const requestTermination = () => {
|
|
1468
|
+
termination ??= terminateChildProcessTree(
|
|
1469
|
+
child,
|
|
1470
|
+
this.#killGraceMs
|
|
1471
|
+
).finally(removeEmergencyExitHook);
|
|
1472
|
+
return termination;
|
|
1473
|
+
};
|
|
1474
|
+
const timeout = setTimeout(() => {
|
|
1475
|
+
timedOut = true;
|
|
1476
|
+
void requestTermination();
|
|
1477
|
+
}, Math.max(1, input.maxRunMs));
|
|
1478
|
+
timeout.unref();
|
|
1479
|
+
const abort = () => {
|
|
1480
|
+
aborted = true;
|
|
1481
|
+
void requestTermination();
|
|
1482
|
+
};
|
|
1483
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
1484
|
+
if (signal.aborted) abort();
|
|
1485
|
+
child.stdout.on("data", (chunk) => {
|
|
1486
|
+
stdoutRemainder = splitLines(chunk, stdoutRemainder, (line) => {
|
|
1487
|
+
for (const event of parser.parse(line)) queue.push(event);
|
|
1488
|
+
});
|
|
1489
|
+
});
|
|
1490
|
+
child.stderr.on("data", (chunk) => {
|
|
1491
|
+
stderrRemainder = splitLines(chunk, stderrRemainder, (line) => {
|
|
1492
|
+
if (line.trim()) queue.push({ type: "warning", text: line });
|
|
1493
|
+
});
|
|
1494
|
+
});
|
|
1495
|
+
child.once("error", (error) => queue.end(error));
|
|
1496
|
+
child.once("close", (code, closeSignal) => {
|
|
1497
|
+
clearTimeout(timeout);
|
|
1498
|
+
signal.removeEventListener("abort", abort);
|
|
1499
|
+
void (async () => {
|
|
1500
|
+
await requestTermination();
|
|
1501
|
+
if (stdoutRemainder.trim()) {
|
|
1502
|
+
for (const event of parser.parse(stdoutRemainder)) queue.push(event);
|
|
1503
|
+
}
|
|
1504
|
+
if (stderrRemainder.trim()) queue.push({ type: "warning", text: stderrRemainder });
|
|
1505
|
+
if (timedOut) queue.end(new AgentTimeoutError());
|
|
1506
|
+
else if (aborted) queue.end(new AgentCanceledError());
|
|
1507
|
+
else if (code !== 0) {
|
|
1508
|
+
queue.end(
|
|
1509
|
+
new AgentProcessError(
|
|
1510
|
+
`Claude exited with code ${String(code)}`,
|
|
1511
|
+
code,
|
|
1512
|
+
closeSignal
|
|
1513
|
+
)
|
|
1514
|
+
);
|
|
1515
|
+
} else {
|
|
1516
|
+
queue.end();
|
|
1517
|
+
}
|
|
1518
|
+
})().catch((error) => queue.end(error));
|
|
1519
|
+
});
|
|
1520
|
+
child.stdin.on("error", (error) => {
|
|
1521
|
+
if (error.code !== "EPIPE") queue.end(error);
|
|
1522
|
+
});
|
|
1523
|
+
child.stdin.end(prompt);
|
|
1524
|
+
try {
|
|
1525
|
+
for await (const event of queue) yield event;
|
|
1526
|
+
} finally {
|
|
1527
|
+
clearTimeout(timeout);
|
|
1528
|
+
signal.removeEventListener("abort", abort);
|
|
1529
|
+
try {
|
|
1530
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
1531
|
+
await requestTermination();
|
|
1532
|
+
} else if (termination) {
|
|
1533
|
+
await termination;
|
|
1534
|
+
}
|
|
1535
|
+
} finally {
|
|
1536
|
+
removeEmergencyExitHook();
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
|
|
974
1542
|
// ../../packages/bridge-core/src/agents/codex-event-parser.ts
|
|
975
|
-
function
|
|
1543
|
+
function asRecord2(value) {
|
|
976
1544
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
977
1545
|
}
|
|
978
|
-
function
|
|
1546
|
+
function asText2(value) {
|
|
979
1547
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
980
1548
|
}
|
|
981
1549
|
function sessionId(record) {
|
|
982
|
-
const thread =
|
|
983
|
-
return
|
|
1550
|
+
const thread = asRecord2(record.thread);
|
|
1551
|
+
return asText2(record.thread_id) ?? asText2(record.threadId) ?? asText2(record.session_id) ?? asText2(record.sessionId) ?? (thread ? asText2(thread.id) : void 0);
|
|
984
1552
|
}
|
|
985
1553
|
function itemFiles(item) {
|
|
986
1554
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
987
1555
|
const files = changes.flatMap((change) => {
|
|
988
|
-
const record =
|
|
1556
|
+
const record = asRecord2(change);
|
|
989
1557
|
if (!record) return [];
|
|
990
|
-
return [
|
|
1558
|
+
return [asText2(record.path) ?? asText2(record.file_path) ?? asText2(record.filePath)].filter(
|
|
991
1559
|
(path) => path !== void 0
|
|
992
1560
|
);
|
|
993
1561
|
});
|
|
994
|
-
const direct =
|
|
1562
|
+
const direct = asText2(item.path) ?? asText2(item.file_path) ?? asText2(item.filePath);
|
|
995
1563
|
if (direct) files.push(direct);
|
|
996
1564
|
return [...new Set(files)];
|
|
997
1565
|
}
|
|
@@ -1005,28 +1573,28 @@ function isDirectExecItem(item) {
|
|
|
1005
1573
|
return item.type === "mcp_tool_call" && item.server === "visual_remote_exec" && item.tool === "run_readonly";
|
|
1006
1574
|
}
|
|
1007
1575
|
function directExecSummary(item) {
|
|
1008
|
-
const arguments_ =
|
|
1576
|
+
const arguments_ = asRecord2(item.arguments);
|
|
1009
1577
|
const commands = Array.isArray(arguments_?.commands) ? arguments_.commands : [];
|
|
1010
1578
|
const summaries = commands.flatMap((candidate) => {
|
|
1011
|
-
const command =
|
|
1579
|
+
const command = asRecord2(candidate);
|
|
1012
1580
|
const argv = stringArray(command?.argv);
|
|
1013
1581
|
return argv === void 0 ? [] : [formatArgv(argv)];
|
|
1014
1582
|
});
|
|
1015
1583
|
return summaries.length === 0 ? void 0 : summaries.join(" \xB7 ");
|
|
1016
1584
|
}
|
|
1017
1585
|
function directExecResults(item, defaultCwd) {
|
|
1018
|
-
const result =
|
|
1019
|
-
const structured =
|
|
1586
|
+
const result = asRecord2(item.result);
|
|
1587
|
+
const structured = asRecord2(result?.structured_content ?? result?.structuredContent);
|
|
1020
1588
|
const results = Array.isArray(structured?.results) ? structured.results : [];
|
|
1021
1589
|
return results.flatMap((candidate) => {
|
|
1022
|
-
const command =
|
|
1590
|
+
const command = asRecord2(candidate);
|
|
1023
1591
|
const argv = stringArray(command?.argv);
|
|
1024
1592
|
if (argv === void 0) return [];
|
|
1025
1593
|
const exitCode = typeof command?.exitCode === "number" ? command.exitCode : void 0;
|
|
1026
1594
|
const durationMs = typeof command?.durationMs === "number" ? command.durationMs : void 0;
|
|
1027
1595
|
return [{
|
|
1028
1596
|
command: formatArgv(argv),
|
|
1029
|
-
cwd:
|
|
1597
|
+
cwd: asText2(command?.cwd) ?? defaultCwd,
|
|
1030
1598
|
ok: exitCode === 0,
|
|
1031
1599
|
...exitCode === void 0 ? {} : { exitCode },
|
|
1032
1600
|
...durationMs === void 0 ? {} : { durationMs },
|
|
@@ -1036,311 +1604,113 @@ function directExecResults(item, defaultCwd) {
|
|
|
1036
1604
|
}];
|
|
1037
1605
|
});
|
|
1038
1606
|
}
|
|
1039
|
-
function normalizedUsage(record) {
|
|
1040
|
-
const result =
|
|
1041
|
-
const usage =
|
|
1042
|
-
if (usage === void 0) return void 0;
|
|
1043
|
-
const inputTokens = usage.input_tokens ?? usage.inputTokens;
|
|
1044
|
-
const outputTokens = usage.output_tokens ?? usage.outputTokens;
|
|
1045
|
-
const cachedInputTokens = usage.cached_input_tokens ?? usage.cachedInputTokens;
|
|
1046
|
-
if (typeof inputTokens !== "number" || typeof outputTokens !== "number") {
|
|
1047
|
-
return void 0;
|
|
1048
|
-
}
|
|
1049
|
-
return {
|
|
1050
|
-
type: "usage",
|
|
1051
|
-
inputTokens,
|
|
1052
|
-
outputTokens,
|
|
1053
|
-
...typeof cachedInputTokens === "number" ? { cachedInputTokens } : {}
|
|
1054
|
-
};
|
|
1055
|
-
}
|
|
1056
|
-
function parseCodexJsonLine(line, defaultCwd = "") {
|
|
1057
|
-
const trimmed = line.trim();
|
|
1058
|
-
if (!trimmed) return [];
|
|
1059
|
-
let value;
|
|
1060
|
-
try {
|
|
1061
|
-
value = JSON.parse(trimmed);
|
|
1062
|
-
} catch {
|
|
1063
|
-
return [{ type: "warning", text: trimmed }];
|
|
1064
|
-
}
|
|
1065
|
-
const record = asRecord(value);
|
|
1066
|
-
if (!record) return [{ type: "message", text: trimmed }];
|
|
1067
|
-
const type = asText(record.type) ?? "unknown";
|
|
1068
|
-
const events = [];
|
|
1069
|
-
const foundSession = sessionId(record);
|
|
1070
|
-
if (foundSession) events.push({ type: "session", sessionId: foundSession });
|
|
1071
|
-
if (type === "thread.started" || type === "thread.created") return events;
|
|
1072
|
-
if (type === "turn.started") return [...events, { type: "phase", name: "turn.started" }];
|
|
1073
|
-
if (type === "turn.completed") {
|
|
1074
|
-
const result = asRecord(record.result);
|
|
1075
|
-
const summary = asText(record.summary) ?? (result ? asText(result.summary) : void 0);
|
|
1076
|
-
const usage = normalizedUsage(record);
|
|
1077
|
-
return [
|
|
1078
|
-
...events,
|
|
1079
|
-
...usage === void 0 ? [] : [usage],
|
|
1080
|
-
summary ? { type: "complete", summary } : { type: "complete" }
|
|
1081
|
-
];
|
|
1082
|
-
}
|
|
1083
|
-
if (type === "turn.failed" || type === "error") {
|
|
1084
|
-
const error = asRecord(record.error);
|
|
1085
|
-
const text = asText(record.message) ?? (error ? asText(error.message) : void 0) ?? "Codex reported an error";
|
|
1086
|
-
return [...events, { type: "error", text }];
|
|
1087
|
-
}
|
|
1088
|
-
const item = asRecord(record.item);
|
|
1089
|
-
if (type === "item.started" && item) {
|
|
1090
|
-
const itemType = asText(item.type) ?? "item";
|
|
1091
|
-
const directExec = isDirectExecItem(item);
|
|
1092
|
-
const summary = directExec ? directExecSummary(item) : asText(item.command) ?? asText(item.text);
|
|
1093
|
-
const start = summary ? { type: "tool_start", name: directExec ? "direct_exec" : itemType, summary } : { type: "tool_start", name: directExec ? "direct_exec" : itemType };
|
|
1094
|
-
return [...events, start];
|
|
1095
|
-
}
|
|
1096
|
-
if (type === "item.completed" && item) {
|
|
1097
|
-
const itemType = asText(item.type) ?? "item";
|
|
1098
|
-
if (itemType === "agent_message") {
|
|
1099
|
-
const text = asText(item.text) ?? asText(item.message);
|
|
1100
|
-
return text ? [...events, { type: "message", text }] : events;
|
|
1101
|
-
}
|
|
1102
|
-
if (itemType === "command_execution") {
|
|
1103
|
-
const command = asText(item.command);
|
|
1104
|
-
if (command) {
|
|
1105
|
-
events.push({
|
|
1106
|
-
type: "command",
|
|
1107
|
-
command,
|
|
1108
|
-
cwd: asText(item.cwd) ?? defaultCwd
|
|
1109
|
-
});
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
const directResults = isDirectExecItem(item) ? directExecResults(item, defaultCwd) : [];
|
|
1113
|
-
for (const result of directResults) {
|
|
1114
|
-
events.push({
|
|
1115
|
-
type: "command",
|
|
1116
|
-
command: result.command,
|
|
1117
|
-
cwd: result.cwd,
|
|
1118
|
-
...result.exitCode === void 0 ? {} : { exitCode: result.exitCode },
|
|
1119
|
-
...result.durationMs === void 0 ? {} : { durationMs: result.durationMs },
|
|
1120
|
-
...result.usedRtk === void 0 ? {} : { usedRtk: result.usedRtk },
|
|
1121
|
-
...result.timedOut === void 0 ? {} : { timedOut: result.timedOut },
|
|
1122
|
-
...result.truncated === void 0 ? {} : { truncated: result.truncated }
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
for (const path of itemFiles(item)) events.push({ type: "file_hint", path });
|
|
1126
|
-
const exitCode = typeof item.exit_code === "number" ? item.exit_code : void 0;
|
|
1127
|
-
events.push({
|
|
1128
|
-
type: "tool_end",
|
|
1129
|
-
name: isDirectExecItem(item) ? "direct_exec" : itemType,
|
|
1130
|
-
ok: directResults.length > 0 ? directResults.every((result) => result.ok) : exitCode === void 0 || exitCode === 0
|
|
1131
|
-
});
|
|
1132
|
-
return events;
|
|
1133
|
-
}
|
|
1134
|
-
const message = asText(record.message);
|
|
1135
|
-
if (message) events.push({ type: "message", text: message });
|
|
1136
|
-
return events;
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
// ../../packages/bridge-core/src/agents/codex-adapter.ts
|
|
1140
|
-
import { execFile, spawn as spawn2 } from "node:child_process";
|
|
1141
|
-
import { existsSync } from "node:fs";
|
|
1142
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1143
|
-
import { promisify } from "node:util";
|
|
1144
|
-
|
|
1145
|
-
// ../../packages/bridge-core/src/agents/async-queue.ts
|
|
1146
|
-
var AsyncQueue = class {
|
|
1147
|
-
#values = [];
|
|
1148
|
-
#waiters = [];
|
|
1149
|
-
#ended = false;
|
|
1150
|
-
#error;
|
|
1151
|
-
push(value) {
|
|
1152
|
-
if (this.#ended) return;
|
|
1153
|
-
const waiter = this.#waiters.shift();
|
|
1154
|
-
if (waiter) waiter.resolve({ value, done: false });
|
|
1155
|
-
else this.#values.push(value);
|
|
1156
|
-
}
|
|
1157
|
-
end(error) {
|
|
1158
|
-
if (this.#ended) return;
|
|
1159
|
-
this.#ended = true;
|
|
1160
|
-
this.#error = error;
|
|
1161
|
-
for (const waiter of this.#waiters.splice(0)) {
|
|
1162
|
-
if (error !== void 0) waiter.reject(error);
|
|
1163
|
-
else waiter.resolve({ value: void 0, done: true });
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
[Symbol.asyncIterator]() {
|
|
1167
|
-
return {
|
|
1168
|
-
next: async () => {
|
|
1169
|
-
const value = this.#values.shift();
|
|
1170
|
-
if (value !== void 0) return { value, done: false };
|
|
1171
|
-
if (this.#ended) {
|
|
1172
|
-
if (this.#error !== void 0) throw this.#error;
|
|
1173
|
-
return { value: void 0, done: true };
|
|
1174
|
-
}
|
|
1175
|
-
return await new Promise((resolve9, reject) => {
|
|
1176
|
-
this.#waiters.push({ resolve: resolve9, reject });
|
|
1177
|
-
});
|
|
1178
|
-
}
|
|
1179
|
-
};
|
|
1180
|
-
}
|
|
1181
|
-
};
|
|
1182
|
-
|
|
1183
|
-
// ../../packages/bridge-core/src/runtime/managed-process.ts
|
|
1184
|
-
import { spawn } from "node:child_process";
|
|
1185
|
-
function replacePortPlaceholder(value, port) {
|
|
1186
|
-
return value.replaceAll("{upstreamPort}", String(port));
|
|
1187
|
-
}
|
|
1188
|
-
function safeChildProcessId(child) {
|
|
1189
|
-
const pid = child.pid;
|
|
1190
|
-
if (pid === void 0 || !Number.isSafeInteger(pid) || pid <= 1 || pid === process.pid) {
|
|
1191
|
-
return void 0;
|
|
1192
|
-
}
|
|
1193
|
-
return pid;
|
|
1194
|
-
}
|
|
1195
|
-
function safeDetachedProcessGroupId(child) {
|
|
1196
|
-
return process.platform === "win32" ? void 0 : safeChildProcessId(child);
|
|
1197
|
-
}
|
|
1198
|
-
function isMissingProcess(error) {
|
|
1199
|
-
return typeof error === "object" && error !== null && "code" in error && error.code === "ESRCH";
|
|
1200
|
-
}
|
|
1201
|
-
function processGroupIsAlive(processGroupId) {
|
|
1202
|
-
try {
|
|
1203
|
-
process.kill(-processGroupId, 0);
|
|
1204
|
-
return true;
|
|
1205
|
-
} catch (error) {
|
|
1206
|
-
if (isMissingProcess(error)) return false;
|
|
1207
|
-
if (typeof error === "object" && error !== null && "code" in error && error.code === "EPERM") {
|
|
1208
|
-
return true;
|
|
1209
|
-
}
|
|
1210
|
-
throw error;
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
function childIsAlive(child) {
|
|
1214
|
-
return child.exitCode === null && child.signalCode === null;
|
|
1215
|
-
}
|
|
1216
|
-
function signalChildProcessTree(child, signal) {
|
|
1217
|
-
const pid = safeChildProcessId(child);
|
|
1218
|
-
if (pid === void 0) return false;
|
|
1219
|
-
try {
|
|
1220
|
-
if (process.platform === "win32") {
|
|
1221
|
-
if (!childIsAlive(child)) return false;
|
|
1222
|
-
return child.kill(signal);
|
|
1223
|
-
}
|
|
1224
|
-
process.kill(-pid, signal);
|
|
1225
|
-
return true;
|
|
1226
|
-
} catch (error) {
|
|
1227
|
-
if (isMissingProcess(error)) return false;
|
|
1228
|
-
throw error;
|
|
1607
|
+
function normalizedUsage(record) {
|
|
1608
|
+
const result = asRecord2(record.result);
|
|
1609
|
+
const usage = asRecord2(record.usage) ?? (result ? asRecord2(result.usage) : void 0);
|
|
1610
|
+
if (usage === void 0) return void 0;
|
|
1611
|
+
const inputTokens = usage.input_tokens ?? usage.inputTokens;
|
|
1612
|
+
const outputTokens = usage.output_tokens ?? usage.outputTokens;
|
|
1613
|
+
const cachedInputTokens = usage.cached_input_tokens ?? usage.cachedInputTokens;
|
|
1614
|
+
if (typeof inputTokens !== "number" || typeof outputTokens !== "number") {
|
|
1615
|
+
return void 0;
|
|
1229
1616
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
try {
|
|
1236
|
-
signalChildProcessTree(child, "SIGKILL");
|
|
1237
|
-
} catch {
|
|
1238
|
-
}
|
|
1239
|
-
};
|
|
1240
|
-
processLike.once("exit", emergencyExit);
|
|
1241
|
-
return () => {
|
|
1242
|
-
if (removed) return;
|
|
1243
|
-
removed = true;
|
|
1244
|
-
processLike.off("exit", emergencyExit);
|
|
1617
|
+
return {
|
|
1618
|
+
type: "usage",
|
|
1619
|
+
inputTokens,
|
|
1620
|
+
outputTokens,
|
|
1621
|
+
...typeof cachedInputTokens === "number" ? { cachedInputTokens } : {}
|
|
1245
1622
|
};
|
|
1246
1623
|
}
|
|
1247
|
-
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
}
|
|
1624
|
+
function parseCodexJsonLine(line, defaultCwd = "") {
|
|
1625
|
+
const trimmed = line.trim();
|
|
1626
|
+
if (!trimmed) return [];
|
|
1627
|
+
let value;
|
|
1628
|
+
try {
|
|
1629
|
+
value = JSON.parse(trimmed);
|
|
1630
|
+
} catch {
|
|
1631
|
+
return [{ type: "warning", text: trimmed }];
|
|
1255
1632
|
}
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
};
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1633
|
+
const record = asRecord2(value);
|
|
1634
|
+
if (!record) return [{ type: "message", text: trimmed }];
|
|
1635
|
+
const type = asText2(record.type) ?? "unknown";
|
|
1636
|
+
const events = [];
|
|
1637
|
+
const foundSession = sessionId(record);
|
|
1638
|
+
if (foundSession) events.push({ type: "session", sessionId: foundSession });
|
|
1639
|
+
if (type === "thread.started" || type === "thread.created") return events;
|
|
1640
|
+
if (type === "turn.started") return [...events, { type: "phase", name: "turn.started" }];
|
|
1641
|
+
if (type === "turn.completed") {
|
|
1642
|
+
const result = asRecord2(record.result);
|
|
1643
|
+
const summary = asText2(record.summary) ?? (result ? asText2(result.summary) : void 0);
|
|
1644
|
+
const usage = normalizedUsage(record);
|
|
1645
|
+
return [
|
|
1646
|
+
...events,
|
|
1647
|
+
...usage === void 0 ? [] : [usage],
|
|
1648
|
+
summary ? { type: "complete", summary } : { type: "complete" }
|
|
1649
|
+
];
|
|
1271
1650
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
Math.min(Math.max(0, killGraceMs), 1e3)
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
async function startManagedProcess(options) {
|
|
1280
|
-
const [executable, ...rawArguments] = options.command;
|
|
1281
|
-
if (executable === void 0) {
|
|
1282
|
-
throw new Error("Managed dev command is empty");
|
|
1651
|
+
if (type === "turn.failed" || type === "error") {
|
|
1652
|
+
const error = asRecord2(record.error);
|
|
1653
|
+
const text = asText2(record.message) ?? (error ? asText2(error.message) : void 0) ?? "Codex reported an error";
|
|
1654
|
+
return [...events, { type: "error", text }];
|
|
1283
1655
|
}
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
"inherit",
|
|
1298
|
-
options.stdout === void 0 ? "inherit" : "pipe",
|
|
1299
|
-
options.stderr === void 0 ? "inherit" : "pipe"
|
|
1300
|
-
],
|
|
1301
|
-
windowsHide: true
|
|
1656
|
+
const item = asRecord2(record.item);
|
|
1657
|
+
if (type === "item.started" && item) {
|
|
1658
|
+
const itemType = asText2(item.type) ?? "item";
|
|
1659
|
+
const directExec = isDirectExecItem(item);
|
|
1660
|
+
const summary = directExec ? directExecSummary(item) : asText2(item.command) ?? asText2(item.text);
|
|
1661
|
+
const start = summary ? { type: "tool_start", name: directExec ? "direct_exec" : itemType, summary } : { type: "tool_start", name: directExec ? "direct_exec" : itemType };
|
|
1662
|
+
return [...events, start];
|
|
1663
|
+
}
|
|
1664
|
+
if (type === "item.completed" && item) {
|
|
1665
|
+
const itemType = asText2(item.type) ?? "item";
|
|
1666
|
+
if (itemType === "agent_message") {
|
|
1667
|
+
const text = asText2(item.text) ?? asText2(item.message);
|
|
1668
|
+
return text ? [...events, { type: "message", text }] : events;
|
|
1302
1669
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1670
|
+
if (itemType === "command_execution") {
|
|
1671
|
+
const command = asText2(item.command);
|
|
1672
|
+
if (command) {
|
|
1673
|
+
events.push({
|
|
1674
|
+
type: "command",
|
|
1675
|
+
command,
|
|
1676
|
+
cwd: asText2(item.cwd) ?? defaultCwd
|
|
1677
|
+
});
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
const directResults = isDirectExecItem(item) ? directExecResults(item, defaultCwd) : [];
|
|
1681
|
+
for (const result of directResults) {
|
|
1682
|
+
events.push({
|
|
1683
|
+
type: "command",
|
|
1684
|
+
command: result.command,
|
|
1685
|
+
cwd: result.cwd,
|
|
1686
|
+
...result.exitCode === void 0 ? {} : { exitCode: result.exitCode },
|
|
1687
|
+
...result.durationMs === void 0 ? {} : { durationMs: result.durationMs },
|
|
1688
|
+
...result.usedRtk === void 0 ? {} : { usedRtk: result.usedRtk },
|
|
1689
|
+
...result.timedOut === void 0 ? {} : { timedOut: result.timedOut },
|
|
1690
|
+
...result.truncated === void 0 ? {} : { truncated: result.truncated }
|
|
1309
1691
|
});
|
|
1310
1692
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
}
|
|
1318
|
-
try {
|
|
1319
|
-
await new Promise((resolve9, reject) => {
|
|
1320
|
-
child.once("spawn", resolve9);
|
|
1321
|
-
child.once("error", reject);
|
|
1693
|
+
for (const path of itemFiles(item)) events.push({ type: "file_hint", path });
|
|
1694
|
+
const exitCode = typeof item.exit_code === "number" ? item.exit_code : void 0;
|
|
1695
|
+
events.push({
|
|
1696
|
+
type: "tool_end",
|
|
1697
|
+
name: isDirectExecItem(item) ? "direct_exec" : itemType,
|
|
1698
|
+
ok: directResults.length > 0 ? directResults.every((result) => result.ok) : exitCode === void 0 || exitCode === 0
|
|
1322
1699
|
});
|
|
1323
|
-
|
|
1324
|
-
removeEmergencyExitHook();
|
|
1325
|
-
throw error;
|
|
1700
|
+
return events;
|
|
1326
1701
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
exit,
|
|
1331
|
-
stop() {
|
|
1332
|
-
stopPromise ??= terminateChildProcessTree(
|
|
1333
|
-
child,
|
|
1334
|
-
options.killGraceMs
|
|
1335
|
-
).finally(removeEmergencyExitHook);
|
|
1336
|
-
return stopPromise;
|
|
1337
|
-
}
|
|
1338
|
-
};
|
|
1702
|
+
const message = asText2(record.message);
|
|
1703
|
+
if (message) events.push({ type: "message", text: message });
|
|
1704
|
+
return events;
|
|
1339
1705
|
}
|
|
1340
1706
|
|
|
1341
1707
|
// ../../packages/bridge-core/src/agents/codex-adapter.ts
|
|
1342
|
-
|
|
1343
|
-
|
|
1708
|
+
import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
|
|
1709
|
+
import { existsSync } from "node:fs";
|
|
1710
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1711
|
+
import { promisify as promisify2 } from "node:util";
|
|
1712
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
1713
|
+
var INHERITED_ENVIRONMENT2 = [
|
|
1344
1714
|
"PATH",
|
|
1345
1715
|
"HOME",
|
|
1346
1716
|
"USER",
|
|
@@ -1365,15 +1735,15 @@ var INHERITED_ENVIRONMENT = [
|
|
|
1365
1735
|
"COMSPEC",
|
|
1366
1736
|
"PATHEXT"
|
|
1367
1737
|
];
|
|
1368
|
-
function
|
|
1738
|
+
function processEnv2(overrides) {
|
|
1369
1739
|
const environment = {};
|
|
1370
|
-
for (const key of
|
|
1740
|
+
for (const key of INHERITED_ENVIRONMENT2) {
|
|
1371
1741
|
const value = process.env[key];
|
|
1372
1742
|
if (value !== void 0) environment[key] = value;
|
|
1373
1743
|
}
|
|
1374
1744
|
return { ...environment, ...overrides };
|
|
1375
1745
|
}
|
|
1376
|
-
function
|
|
1746
|
+
function splitLines2(chunk, previous, onLine) {
|
|
1377
1747
|
const combined = previous + chunk.toString();
|
|
1378
1748
|
const lines = combined.split(/\r?\n/);
|
|
1379
1749
|
const remainder = lines.pop() ?? "";
|
|
@@ -1393,18 +1763,24 @@ var CodexAdapter = class {
|
|
|
1393
1763
|
id = "codex";
|
|
1394
1764
|
#executable;
|
|
1395
1765
|
#killGraceMs;
|
|
1766
|
+
#model;
|
|
1767
|
+
#profile;
|
|
1768
|
+
#reasoningEffort;
|
|
1396
1769
|
#rtkExecutable;
|
|
1397
1770
|
#directExecMcpScript;
|
|
1398
1771
|
#rtkVersion;
|
|
1399
1772
|
constructor(options = {}) {
|
|
1400
1773
|
this.#executable = options.executable ?? "codex";
|
|
1401
1774
|
this.#killGraceMs = options.killGraceMs ?? 2e3;
|
|
1775
|
+
this.#model = options.model;
|
|
1776
|
+
this.#profile = options.profile;
|
|
1777
|
+
this.#reasoningEffort = options.reasoningEffort;
|
|
1402
1778
|
this.#rtkExecutable = options.rtkExecutable ?? "rtk";
|
|
1403
1779
|
this.#directExecMcpScript = options.directExecMcpScript === false ? void 0 : options.directExecMcpScript ?? defaultDirectExecMcpScript();
|
|
1404
1780
|
}
|
|
1405
1781
|
#probeRtk(environment) {
|
|
1406
1782
|
if (this.#rtkExecutable === false) return Promise.resolve(void 0);
|
|
1407
|
-
this.#rtkVersion ??=
|
|
1783
|
+
this.#rtkVersion ??= execFileAsync2(this.#rtkExecutable, ["--version"], {
|
|
1408
1784
|
encoding: "utf8",
|
|
1409
1785
|
env: environment,
|
|
1410
1786
|
timeout: 1e3,
|
|
@@ -1447,9 +1823,16 @@ ${guidance}
|
|
|
1447
1823
|
`mcp_servers.visual_remote_exec.args=${JSON.stringify(serverArgs)}`
|
|
1448
1824
|
];
|
|
1449
1825
|
}
|
|
1826
|
+
#modelConfig() {
|
|
1827
|
+
return [
|
|
1828
|
+
...this.#profile === void 0 ? [] : ["--profile", this.#profile],
|
|
1829
|
+
...this.#model === void 0 ? [] : ["--model", this.#model],
|
|
1830
|
+
...this.#reasoningEffort === void 0 ? [] : ["-c", `model_reasoning_effort=${JSON.stringify(this.#reasoningEffort)}`]
|
|
1831
|
+
];
|
|
1832
|
+
}
|
|
1450
1833
|
async probe() {
|
|
1451
1834
|
return await new Promise((resolve9) => {
|
|
1452
|
-
const child =
|
|
1835
|
+
const child = spawn3(this.#executable, ["--version"], {
|
|
1453
1836
|
stdio: ["ignore", "pipe", "ignore"],
|
|
1454
1837
|
shell: false
|
|
1455
1838
|
});
|
|
@@ -1482,6 +1865,7 @@ ${guidance}
|
|
|
1482
1865
|
"workspace-write",
|
|
1483
1866
|
"-C",
|
|
1484
1867
|
input.workspaceRoot,
|
|
1868
|
+
...this.#modelConfig(),
|
|
1485
1869
|
...this.#directExecConfig(input),
|
|
1486
1870
|
"-"
|
|
1487
1871
|
];
|
|
@@ -1500,6 +1884,7 @@ ${guidance}
|
|
|
1500
1884
|
"workspace-write",
|
|
1501
1885
|
"-C",
|
|
1502
1886
|
input.workspaceRoot,
|
|
1887
|
+
...this.#modelConfig(),
|
|
1503
1888
|
...this.#directExecConfig(input),
|
|
1504
1889
|
"resume",
|
|
1505
1890
|
input.sessionId,
|
|
@@ -1509,9 +1894,9 @@ ${guidance}
|
|
|
1509
1894
|
}
|
|
1510
1895
|
async *#execute(input, signal, args) {
|
|
1511
1896
|
const queue = new AsyncQueue();
|
|
1512
|
-
const environment =
|
|
1897
|
+
const environment = processEnv2(input.environment);
|
|
1513
1898
|
const prompt = await this.#runtimePrompt(input, environment);
|
|
1514
|
-
const child =
|
|
1899
|
+
const child = spawn3(this.#executable, args, {
|
|
1515
1900
|
cwd: input.workspaceRoot,
|
|
1516
1901
|
env: environment,
|
|
1517
1902
|
detached: process.platform !== "win32",
|
|
@@ -1543,12 +1928,12 @@ ${guidance}
|
|
|
1543
1928
|
signal.addEventListener("abort", abort, { once: true });
|
|
1544
1929
|
if (signal.aborted) abort();
|
|
1545
1930
|
child.stdout.on("data", (chunk) => {
|
|
1546
|
-
stdoutRemainder =
|
|
1931
|
+
stdoutRemainder = splitLines2(chunk, stdoutRemainder, (line) => {
|
|
1547
1932
|
for (const event of parseCodexJsonLine(line, input.workspaceRoot)) queue.push(event);
|
|
1548
1933
|
});
|
|
1549
1934
|
});
|
|
1550
1935
|
child.stderr.on("data", (chunk) => {
|
|
1551
|
-
stderrRemainder =
|
|
1936
|
+
stderrRemainder = splitLines2(chunk, stderrRemainder, (line) => {
|
|
1552
1937
|
if (line.trim()) queue.push({ type: "warning", text: line });
|
|
1553
1938
|
});
|
|
1554
1939
|
});
|
|
@@ -1614,6 +1999,7 @@ import { ZodError } from "zod";
|
|
|
1614
1999
|
import { z } from "zod";
|
|
1615
2000
|
var servicePortSchema = z.number().int().min(10001).max(65535);
|
|
1616
2001
|
var commandSchema = z.array(z.string().min(1)).min(1);
|
|
2002
|
+
var environmentVariableSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Expected an environment variable name");
|
|
1617
2003
|
var readySchema = z.object({
|
|
1618
2004
|
path: z.string().startsWith("/").default("/"),
|
|
1619
2005
|
timeoutMs: z.number().int().positive().default(6e4)
|
|
@@ -1642,9 +2028,38 @@ var visualDevConfigSchema = z.object({
|
|
|
1642
2028
|
}).strict(),
|
|
1643
2029
|
agent: z.object({
|
|
1644
2030
|
adapter: z.enum(["codex", "claude", "opencode"]),
|
|
2031
|
+
model: z.string().trim().min(1).optional(),
|
|
2032
|
+
reasoningEffort: z.enum(["minimal", "low", "medium", "high", "xhigh", "max"]).optional(),
|
|
2033
|
+
profile: z.string().trim().regex(
|
|
2034
|
+
/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/,
|
|
2035
|
+
"Expected a Codex profile name"
|
|
2036
|
+
).optional(),
|
|
2037
|
+
inheritEnv: z.array(environmentVariableSchema).default([]),
|
|
1645
2038
|
maxRunMs: z.number().int().positive(),
|
|
1646
2039
|
resumeMode: z.enum(["auto", "new"]).default("auto")
|
|
1647
|
-
}).strict(),
|
|
2040
|
+
}).strict().superRefine((agent, context) => {
|
|
2041
|
+
if (agent.adapter === "claude" && agent.reasoningEffort === "minimal") {
|
|
2042
|
+
context.addIssue({
|
|
2043
|
+
code: "custom",
|
|
2044
|
+
path: ["reasoningEffort"],
|
|
2045
|
+
message: "minimal reasoning effort is only supported by the Codex adapter"
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
if (agent.adapter === "codex" && agent.reasoningEffort === "max") {
|
|
2049
|
+
context.addIssue({
|
|
2050
|
+
code: "custom",
|
|
2051
|
+
path: ["reasoningEffort"],
|
|
2052
|
+
message: "max reasoning effort is only supported by the Claude adapter"
|
|
2053
|
+
});
|
|
2054
|
+
}
|
|
2055
|
+
if (agent.adapter !== "codex" && agent.profile !== void 0) {
|
|
2056
|
+
context.addIssue({
|
|
2057
|
+
code: "custom",
|
|
2058
|
+
path: ["profile"],
|
|
2059
|
+
message: "agent.profile is only supported by the Codex adapter"
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
}),
|
|
1648
2063
|
queue: z.object({
|
|
1649
2064
|
maxPending: z.number().int().positive()
|
|
1650
2065
|
}).strict(),
|
|
@@ -1688,6 +2103,7 @@ function createDefaultConfig(projectId) {
|
|
|
1688
2103
|
},
|
|
1689
2104
|
agent: {
|
|
1690
2105
|
adapter: "codex",
|
|
2106
|
+
inheritEnv: [],
|
|
1691
2107
|
maxRunMs: 9e5,
|
|
1692
2108
|
resumeMode: "auto"
|
|
1693
2109
|
},
|
|
@@ -1760,17 +2176,17 @@ function mergeConfigValues(base, override) {
|
|
|
1760
2176
|
}
|
|
1761
2177
|
return merged;
|
|
1762
2178
|
}
|
|
1763
|
-
async function readYamlMapping(
|
|
2179
|
+
async function readYamlMapping(filePath2, required) {
|
|
1764
2180
|
let source;
|
|
1765
2181
|
try {
|
|
1766
|
-
source = await readFile(
|
|
2182
|
+
source = await readFile(filePath2, "utf8");
|
|
1767
2183
|
} catch (error) {
|
|
1768
2184
|
if (!required && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
1769
2185
|
return void 0;
|
|
1770
2186
|
}
|
|
1771
|
-
throw new VisualDevConfigError(`Unable to read config: ${
|
|
2187
|
+
throw new VisualDevConfigError(`Unable to read config: ${filePath2}`, {
|
|
1772
2188
|
cause: error,
|
|
1773
|
-
filePath
|
|
2189
|
+
filePath: filePath2
|
|
1774
2190
|
});
|
|
1775
2191
|
}
|
|
1776
2192
|
try {
|
|
@@ -1783,9 +2199,9 @@ async function readYamlMapping(filePath, required) {
|
|
|
1783
2199
|
}
|
|
1784
2200
|
return value;
|
|
1785
2201
|
} catch (error) {
|
|
1786
|
-
throw new VisualDevConfigError(`Invalid YAML in ${
|
|
2202
|
+
throw new VisualDevConfigError(`Invalid YAML in ${filePath2}`, {
|
|
1787
2203
|
cause: error,
|
|
1788
|
-
filePath
|
|
2204
|
+
filePath: filePath2
|
|
1789
2205
|
});
|
|
1790
2206
|
}
|
|
1791
2207
|
}
|
|
@@ -1924,10 +2340,10 @@ var RevertConflictError = class extends RepositorySafetyError {
|
|
|
1924
2340
|
};
|
|
1925
2341
|
|
|
1926
2342
|
// ../../packages/bridge-core/src/git/git-command.ts
|
|
1927
|
-
import { spawn as
|
|
2343
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
1928
2344
|
async function runGit(args, options) {
|
|
1929
2345
|
return await new Promise((resolve9, reject) => {
|
|
1930
|
-
const child =
|
|
2346
|
+
const child = spawn4("git", [...args], {
|
|
1931
2347
|
cwd: options.cwd,
|
|
1932
2348
|
env: { ...process.env, ...options.env ?? {} },
|
|
1933
2349
|
shell: false,
|
|
@@ -2996,7 +3412,7 @@ import { relative as relative5, resolve as resolve6, sep as sep5 } from "node:pa
|
|
|
2996
3412
|
// ../../packages/bridge-core/src/source/path-normalizer.ts
|
|
2997
3413
|
import { access, readFile as readFile3, realpath as realpath6 } from "node:fs/promises";
|
|
2998
3414
|
import { isAbsolute as isAbsolute3, relative as relative4, resolve as resolve5, sep as sep4 } from "node:path";
|
|
2999
|
-
import { spawn as
|
|
3415
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
3000
3416
|
function toPosix(value) {
|
|
3001
3417
|
return value.split(sep4).join("/").replaceAll("\\", "/");
|
|
3002
3418
|
}
|
|
@@ -3031,7 +3447,7 @@ function isWithin2(root, candidate) {
|
|
|
3031
3447
|
}
|
|
3032
3448
|
async function gitFiles(repoRoot) {
|
|
3033
3449
|
return await new Promise((resolvePromise, reject) => {
|
|
3034
|
-
const child =
|
|
3450
|
+
const child = spawn5(
|
|
3035
3451
|
"git",
|
|
3036
3452
|
["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
|
|
3037
3453
|
{
|
|
@@ -3101,8 +3517,8 @@ async function normalizeSourceLocation(input, repoRootInput) {
|
|
|
3101
3517
|
candidates: matches.slice(0, 20)
|
|
3102
3518
|
};
|
|
3103
3519
|
}
|
|
3104
|
-
const
|
|
3105
|
-
const absolutePath = resolve5(repoRoot,
|
|
3520
|
+
const filePath2 = matches[0];
|
|
3521
|
+
const absolutePath = resolve5(repoRoot, filePath2);
|
|
3106
3522
|
const canonical = await realpath6(absolutePath);
|
|
3107
3523
|
if (!isWithin2(repoRoot, canonical)) {
|
|
3108
3524
|
return { input, confidence: "unknown", candidates: [] };
|
|
@@ -3110,7 +3526,7 @@ async function normalizeSourceLocation(input, repoRootInput) {
|
|
|
3110
3526
|
const lineNumber = await boundedLine(canonical, input.lineNumber);
|
|
3111
3527
|
return {
|
|
3112
3528
|
input,
|
|
3113
|
-
filePath,
|
|
3529
|
+
filePath: filePath2,
|
|
3114
3530
|
absolutePath: canonical,
|
|
3115
3531
|
...lineNumber === void 0 ? {} : { lineNumber },
|
|
3116
3532
|
...input.columnNumber === void 0 ? {} : { columnNumber: input.columnNumber },
|
|
@@ -4038,11 +4454,11 @@ var TaskService = class {
|
|
|
4038
4454
|
for (const target of result.selection.targets) {
|
|
4039
4455
|
const sanitize = async (location) => {
|
|
4040
4456
|
try {
|
|
4041
|
-
const
|
|
4457
|
+
const filePath2 = await this.#git.pathPolicy.assertFilesystemPathAllowed(
|
|
4042
4458
|
location.filePath,
|
|
4043
4459
|
false
|
|
4044
4460
|
);
|
|
4045
|
-
return { ...location, filePath };
|
|
4461
|
+
return { ...location, filePath: filePath2 };
|
|
4046
4462
|
} catch {
|
|
4047
4463
|
return void 0;
|
|
4048
4464
|
}
|
|
@@ -4254,7 +4670,7 @@ var BrowserSessionManager = class {
|
|
|
4254
4670
|
};
|
|
4255
4671
|
|
|
4256
4672
|
// ../../packages/bridge-core/src/verification/commands.ts
|
|
4257
|
-
import { spawn as
|
|
4673
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
4258
4674
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
4259
4675
|
var KILL_GRACE_MS = 250;
|
|
4260
4676
|
function appendOutput(current, chunk) {
|
|
@@ -4268,7 +4684,7 @@ async function runVerificationCommand(configured, cwd, signal) {
|
|
|
4268
4684
|
}
|
|
4269
4685
|
const startedAt = Date.now();
|
|
4270
4686
|
return await new Promise((resolveResult, rejectResult) => {
|
|
4271
|
-
const child =
|
|
4687
|
+
const child = spawn6(executable, arguments_, {
|
|
4272
4688
|
cwd,
|
|
4273
4689
|
detached: process.platform !== "win32",
|
|
4274
4690
|
shell: false,
|
|
@@ -4843,15 +5259,40 @@ ${output2}` : ""}`
|
|
|
4843
5259
|
}
|
|
4844
5260
|
|
|
4845
5261
|
// ../../packages/bridge-core/src/bridge/default-control-service.ts
|
|
5262
|
+
function createAgentAdapter(agent) {
|
|
5263
|
+
if (agent.adapter === "claude") {
|
|
5264
|
+
if (agent.reasoningEffort === "minimal") {
|
|
5265
|
+
throw new Error("Claude does not support minimal reasoning effort");
|
|
5266
|
+
}
|
|
5267
|
+
return new ClaudeAdapter({
|
|
5268
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
5269
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort }
|
|
5270
|
+
});
|
|
5271
|
+
}
|
|
5272
|
+
if (agent.adapter === "codex") {
|
|
5273
|
+
if (agent.reasoningEffort === "max") {
|
|
5274
|
+
throw new Error("Codex does not support max reasoning effort");
|
|
5275
|
+
}
|
|
5276
|
+
return new CodexAdapter({
|
|
5277
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
5278
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort },
|
|
5279
|
+
...agent.profile === void 0 ? {} : { profile: agent.profile }
|
|
5280
|
+
});
|
|
5281
|
+
}
|
|
5282
|
+
throw new Error(`Agent adapter ${agent.adapter} is not implemented in this build`);
|
|
5283
|
+
}
|
|
5284
|
+
function inheritedAgentEnvironment(names, environment) {
|
|
5285
|
+
return Object.fromEntries(
|
|
5286
|
+
names.flatMap((name) => {
|
|
5287
|
+
const value = environment[name];
|
|
5288
|
+
return value === void 0 ? [] : [[name, value]];
|
|
5289
|
+
})
|
|
5290
|
+
);
|
|
5291
|
+
}
|
|
4846
5292
|
async function createDefaultControlService(context, environment = process.env) {
|
|
4847
5293
|
const loaded = await loadVisualDevConfig(context.repoRoot, {
|
|
4848
5294
|
...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
|
|
4849
5295
|
});
|
|
4850
|
-
if (loaded.config.agent.adapter !== "codex") {
|
|
4851
|
-
throw new Error(
|
|
4852
|
-
`Agent adapter ${loaded.config.agent.adapter} is not implemented in this MVP build`
|
|
4853
|
-
);
|
|
4854
|
-
}
|
|
4855
5296
|
const git = await GitTransactionManager.open(context.repoRoot, {
|
|
4856
5297
|
allowed: rebaseWorkspacePatterns(
|
|
4857
5298
|
context.repoRoot,
|
|
@@ -4870,13 +5311,16 @@ async function createDefaultControlService(context, environment = process.env) {
|
|
|
4870
5311
|
projectId: context.projectId,
|
|
4871
5312
|
workspaceRoot: context.workspaceRoot,
|
|
4872
5313
|
upstreamUrl: context.upstreamUrl,
|
|
4873
|
-
adapter:
|
|
5314
|
+
adapter: createAgentAdapter(loaded.config.agent),
|
|
4874
5315
|
store,
|
|
4875
5316
|
git,
|
|
4876
5317
|
maxRunMs: loaded.config.agent.maxRunMs,
|
|
4877
5318
|
maxPending: loaded.config.queue.maxPending,
|
|
4878
5319
|
resumeMode: loaded.config.agent.resumeMode,
|
|
4879
|
-
environment:
|
|
5320
|
+
environment: inheritedAgentEnvironment(
|
|
5321
|
+
loaded.config.agent.inheritEnv,
|
|
5322
|
+
environment
|
|
5323
|
+
)
|
|
4880
5324
|
});
|
|
4881
5325
|
const controlService = createTaskControlService({
|
|
4882
5326
|
taskService,
|
|
@@ -5147,10 +5591,10 @@ async function acquireWorktreeLock(repositoryRoot, options = {}) {
|
|
|
5147
5591
|
}
|
|
5148
5592
|
|
|
5149
5593
|
// ../../packages/bridge-core/src/runtime/repository.ts
|
|
5150
|
-
import { execFile as
|
|
5594
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
5151
5595
|
import { realpath as realpath9 } from "node:fs/promises";
|
|
5152
|
-
import { promisify as
|
|
5153
|
-
var
|
|
5596
|
+
import { promisify as promisify3 } from "node:util";
|
|
5597
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
5154
5598
|
var GitWorktreeNotFoundError = class extends Error {
|
|
5155
5599
|
constructor(cwd, options = {}) {
|
|
5156
5600
|
super(
|
|
@@ -5162,7 +5606,7 @@ var GitWorktreeNotFoundError = class extends Error {
|
|
|
5162
5606
|
};
|
|
5163
5607
|
async function discoverGitWorktreeRoot(cwd = process.cwd()) {
|
|
5164
5608
|
try {
|
|
5165
|
-
const { stdout } = await
|
|
5609
|
+
const { stdout } = await execFileAsync3(
|
|
5166
5610
|
"git",
|
|
5167
5611
|
["-C", cwd, "rev-parse", "--show-toplevel"],
|
|
5168
5612
|
{
|
|
@@ -5325,7 +5769,7 @@ async function startBridgeCore(options, dependencies) {
|
|
|
5325
5769
|
try {
|
|
5326
5770
|
lock = options.lock ?? await acquireWorktreeLock(loadedConfig.repoRoot, { environment });
|
|
5327
5771
|
const host = options.host ?? loadedConfig.config.gateway.host;
|
|
5328
|
-
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl;
|
|
5772
|
+
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl ?? options.fallbackPublicUrl;
|
|
5329
5773
|
const publicUrl = configuredPublicUrl === void 0 ? void 0 : normalizePublicUrl(configuredPublicUrl);
|
|
5330
5774
|
const gatewayPort = await findAvailablePort(startPort(loadedConfig, options.listen), host);
|
|
5331
5775
|
const token = generatePairingToken();
|
|
@@ -5445,7 +5889,8 @@ async function startAttachBridge(options, dependencies = {}) {
|
|
|
5445
5889
|
upstreamUrl: normalizeUpstream(options.upstream),
|
|
5446
5890
|
...options.listen === void 0 ? {} : { listen: options.listen },
|
|
5447
5891
|
...options.host === void 0 ? {} : { host: options.host },
|
|
5448
|
-
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl }
|
|
5892
|
+
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl },
|
|
5893
|
+
...options.fallbackPublicUrl === void 0 ? {} : { fallbackPublicUrl: options.fallbackPublicUrl }
|
|
5449
5894
|
},
|
|
5450
5895
|
dependencies
|
|
5451
5896
|
);
|
|
@@ -5576,9 +6021,9 @@ function formatBridgeSummary(bridge) {
|
|
|
5576
6021
|
import { constants } from "node:fs";
|
|
5577
6022
|
import { access as access2, stat as stat3 } from "node:fs/promises";
|
|
5578
6023
|
import { delimiter, isAbsolute as isAbsolute5, join as join4, relative as relative6, resolve as resolve7 } from "node:path";
|
|
5579
|
-
import { execFile as
|
|
5580
|
-
import { promisify as
|
|
5581
|
-
var
|
|
6024
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
6025
|
+
import { promisify as promisify4 } from "node:util";
|
|
6026
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
5582
6027
|
async function fileExists(path) {
|
|
5583
6028
|
try {
|
|
5584
6029
|
await stat3(path);
|
|
@@ -5592,7 +6037,7 @@ async function fileExists(path) {
|
|
|
5592
6037
|
}
|
|
5593
6038
|
async function isIgnored(repoRoot, path) {
|
|
5594
6039
|
try {
|
|
5595
|
-
await
|
|
6040
|
+
await execFileAsync4("git", ["-C", repoRoot, "check-ignore", "--quiet", "--", path], {
|
|
5596
6041
|
windowsHide: true
|
|
5597
6042
|
});
|
|
5598
6043
|
return true;
|
|
@@ -5664,7 +6109,7 @@ async function runDoctor(dependencies = {}) {
|
|
|
5664
6109
|
});
|
|
5665
6110
|
}
|
|
5666
6111
|
const adapter = loaded.config.agent.adapter;
|
|
5667
|
-
const adapterSupported = adapter === "codex";
|
|
6112
|
+
const adapterSupported = adapter === "codex" || adapter === "claude";
|
|
5668
6113
|
const agentAvailable = await executableAvailable(
|
|
5669
6114
|
adapter,
|
|
5670
6115
|
loaded.workspaceRoot,
|
|
@@ -5675,6 +6120,34 @@ async function runDoctor(dependencies = {}) {
|
|
|
5675
6120
|
status: adapterSupported && agentAvailable ? "pass" : "fail",
|
|
5676
6121
|
message: !adapterSupported ? `${adapter} is configured but is not implemented in this build.` : agentAvailable ? `${adapter} is executable.` : `${adapter} was not found or is not executable.`
|
|
5677
6122
|
});
|
|
6123
|
+
if (loaded.config.agent.inheritEnv.length > 0) {
|
|
6124
|
+
const missing = loaded.config.agent.inheritEnv.filter(
|
|
6125
|
+
(name) => environment[name] === void 0
|
|
6126
|
+
);
|
|
6127
|
+
checks.push({
|
|
6128
|
+
name: "agent-environment",
|
|
6129
|
+
status: missing.length === 0 ? "pass" : "fail",
|
|
6130
|
+
message: missing.length === 0 ? `${loaded.config.agent.inheritEnv.length} agent environment variable(s) are available.` : `Missing agent environment variable(s): ${missing.join(", ")}.`
|
|
6131
|
+
});
|
|
6132
|
+
}
|
|
6133
|
+
if (adapter === "claude") {
|
|
6134
|
+
const sandboxDependencies = await Promise.all(
|
|
6135
|
+
["bwrap", "socat"].map(async (executable) => ({
|
|
6136
|
+
executable,
|
|
6137
|
+
available: await executableAvailable(
|
|
6138
|
+
executable,
|
|
6139
|
+
loaded.workspaceRoot,
|
|
6140
|
+
environment
|
|
6141
|
+
)
|
|
6142
|
+
}))
|
|
6143
|
+
);
|
|
6144
|
+
const missing = sandboxDependencies.filter(({ available }) => !available).map(({ executable }) => executable);
|
|
6145
|
+
checks.push({
|
|
6146
|
+
name: "claude-sandbox",
|
|
6147
|
+
status: missing.length === 0 ? "pass" : "warning",
|
|
6148
|
+
message: missing.length === 0 ? "Claude Bash sandbox dependencies are available." : `Claude Bash sandbox is unavailable without: ${missing.join(", ")}.`
|
|
6149
|
+
});
|
|
6150
|
+
}
|
|
5678
6151
|
const rtkAvailable = await executableAvailable(
|
|
5679
6152
|
"rtk",
|
|
5680
6153
|
loaded.workspaceRoot,
|
|
@@ -5739,7 +6212,7 @@ function formatDoctorChecks(checks) {
|
|
|
5739
6212
|
}
|
|
5740
6213
|
|
|
5741
6214
|
// src/init.ts
|
|
5742
|
-
import { spawn as
|
|
6215
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
5743
6216
|
import { readFile as readFile5, mkdir as mkdir3, realpath as realpath10, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5744
6217
|
import { basename as basename2, dirname as dirname4, join as join5, relative as relative7 } from "node:path";
|
|
5745
6218
|
import { stringify as stringifyYaml } from "yaml";
|
|
@@ -6059,7 +6532,7 @@ function installCommand(request) {
|
|
|
6059
6532
|
async function installPackage(request) {
|
|
6060
6533
|
const { command, args } = installCommand(request);
|
|
6061
6534
|
await new Promise((resolvePromise, reject) => {
|
|
6062
|
-
const child =
|
|
6535
|
+
const child = spawn7(command, args, {
|
|
6063
6536
|
cwd: request.cwd,
|
|
6064
6537
|
env: process.env,
|
|
6065
6538
|
stdio: "inherit",
|
|
@@ -6196,6 +6669,110 @@ function formatBridgeStatus(status) {
|
|
|
6196
6669
|
return rows.join("\n");
|
|
6197
6670
|
}
|
|
6198
6671
|
|
|
6672
|
+
// ../../package.json
|
|
6673
|
+
var package_default = {
|
|
6674
|
+
name: "visual-remote",
|
|
6675
|
+
version: "0.3.2",
|
|
6676
|
+
description: "Visual bridge from a running web UI to a coding agent in its Git worktree",
|
|
6677
|
+
type: "module",
|
|
6678
|
+
packageManager: "pnpm@10.34.5",
|
|
6679
|
+
repository: {
|
|
6680
|
+
type: "git",
|
|
6681
|
+
url: "git+https://github.com/elicie/visual-remote.git"
|
|
6682
|
+
},
|
|
6683
|
+
homepage: "https://github.com/elicie/visual-remote#readme",
|
|
6684
|
+
bugs: {
|
|
6685
|
+
url: "https://github.com/elicie/visual-remote/issues"
|
|
6686
|
+
},
|
|
6687
|
+
files: [
|
|
6688
|
+
"apps/cli/dist/index.js",
|
|
6689
|
+
"apps/cli/dist/direct-exec-mcp.js",
|
|
6690
|
+
"apps/cli/dist/vite.js",
|
|
6691
|
+
"apps/cli/dist/next.js",
|
|
6692
|
+
"apps/cli/dist/next-client.js",
|
|
6693
|
+
"apps/cli/vite.d.ts",
|
|
6694
|
+
"apps/cli/next.d.ts",
|
|
6695
|
+
"apps/cli/next-client.d.ts",
|
|
6696
|
+
"packages/overlay/dist/client.js",
|
|
6697
|
+
"packages/overlay/dist/viewer.js"
|
|
6698
|
+
],
|
|
6699
|
+
bin: {
|
|
6700
|
+
visual: "./apps/cli/dist/index.js",
|
|
6701
|
+
"visual-remote": "./apps/cli/dist/index.js"
|
|
6702
|
+
},
|
|
6703
|
+
exports: {
|
|
6704
|
+
"./vite": {
|
|
6705
|
+
types: "./apps/cli/vite.d.ts",
|
|
6706
|
+
import: "./apps/cli/dist/vite.js"
|
|
6707
|
+
},
|
|
6708
|
+
"./next": {
|
|
6709
|
+
types: "./apps/cli/next.d.ts",
|
|
6710
|
+
import: "./apps/cli/dist/next.js",
|
|
6711
|
+
default: "./apps/cli/dist/next.js"
|
|
6712
|
+
},
|
|
6713
|
+
"./next/client": {
|
|
6714
|
+
types: "./apps/cli/next-client.d.ts",
|
|
6715
|
+
import: "./apps/cli/dist/next-client.js",
|
|
6716
|
+
default: "./apps/cli/dist/next-client.js"
|
|
6717
|
+
}
|
|
6718
|
+
},
|
|
6719
|
+
publishConfig: {
|
|
6720
|
+
access: "public",
|
|
6721
|
+
registry: "https://registry.npmjs.org"
|
|
6722
|
+
},
|
|
6723
|
+
engines: {
|
|
6724
|
+
node: ">=24"
|
|
6725
|
+
},
|
|
6726
|
+
scripts: {
|
|
6727
|
+
build: "corepack pnpm run build:overlay && corepack pnpm run build:server",
|
|
6728
|
+
"build:overlay": "corepack pnpm --filter @visual-remote/overlay build",
|
|
6729
|
+
"build:server": "corepack pnpm --filter @visual-remote/cli build",
|
|
6730
|
+
dev: "corepack pnpm run build:overlay && tsx apps/cli/src/index.ts",
|
|
6731
|
+
test: "vitest run",
|
|
6732
|
+
"test:e2e": "corepack pnpm build && corepack pnpm exec playwright test --config tests/e2e/playwright.config.ts",
|
|
6733
|
+
"test:watch": "vitest",
|
|
6734
|
+
typecheck: "corepack pnpm -r --if-present typecheck && tsc --noEmit -p tsconfig.tests.json",
|
|
6735
|
+
prepack: "corepack pnpm build"
|
|
6736
|
+
},
|
|
6737
|
+
dependencies: {
|
|
6738
|
+
commander: "^15.0.0",
|
|
6739
|
+
"http-proxy": "^1.18.1",
|
|
6740
|
+
ws: "^8.21.1",
|
|
6741
|
+
yaml: "^2.9.0",
|
|
6742
|
+
zod: "^4.4.3"
|
|
6743
|
+
},
|
|
6744
|
+
peerDependencies: {
|
|
6745
|
+
vite: ">=5"
|
|
6746
|
+
},
|
|
6747
|
+
peerDependenciesMeta: {
|
|
6748
|
+
vite: {
|
|
6749
|
+
optional: true
|
|
6750
|
+
}
|
|
6751
|
+
},
|
|
6752
|
+
devDependencies: {
|
|
6753
|
+
"@playwright/test": "^1.62.1",
|
|
6754
|
+
"@types/http-proxy": "^1.17.17",
|
|
6755
|
+
"@types/node": "^26.1.2",
|
|
6756
|
+
"@types/ws": "^8.18.1",
|
|
6757
|
+
"@visual-remote/bridge-core": "workspace:*",
|
|
6758
|
+
"@visual-remote/cli": "workspace:*",
|
|
6759
|
+
"@visual-remote/gateway": "workspace:*",
|
|
6760
|
+
"@visual-remote/overlay": "workspace:*",
|
|
6761
|
+
"@visual-remote/protocol": "workspace:*",
|
|
6762
|
+
esbuild: "^0.28.1",
|
|
6763
|
+
next: "15.5.16",
|
|
6764
|
+
react: "19.1.0",
|
|
6765
|
+
"react-dom": "19.1.0",
|
|
6766
|
+
tsx: "^4.23.1",
|
|
6767
|
+
typescript: "^7.0.2",
|
|
6768
|
+
vite: "^8.1.5",
|
|
6769
|
+
vitest: "^4.1.10"
|
|
6770
|
+
}
|
|
6771
|
+
};
|
|
6772
|
+
|
|
6773
|
+
// src/version.ts
|
|
6774
|
+
var VISUAL_REMOTE_VERSION = package_default.version;
|
|
6775
|
+
|
|
6199
6776
|
// src/index.ts
|
|
6200
6777
|
function parsePort(value) {
|
|
6201
6778
|
const port = Number(value);
|
|
@@ -6218,7 +6795,7 @@ function setExitCode(dependencies, code) {
|
|
|
6218
6795
|
}
|
|
6219
6796
|
}
|
|
6220
6797
|
function createCli(dependencies = {}) {
|
|
6221
|
-
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version(
|
|
6798
|
+
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version(VISUAL_REMOTE_VERSION);
|
|
6222
6799
|
program.command("init").description("Configure Visual Remote for the current Vite or Next.js project").action(async () => {
|
|
6223
6800
|
const result = await initializeVisualDev(dependencies);
|
|
6224
6801
|
output(dependencies, formatInitResult(result));
|
|
@@ -6286,6 +6863,7 @@ if (entryPath !== void 0 && isDirectEntry(entryPath)) {
|
|
|
6286
6863
|
});
|
|
6287
6864
|
}
|
|
6288
6865
|
export {
|
|
6866
|
+
VISUAL_REMOTE_VERSION,
|
|
6289
6867
|
createCli,
|
|
6290
6868
|
formatBridgeStatus,
|
|
6291
6869
|
formatBridgeSummary,
|