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/index.js
CHANGED
|
@@ -971,215 +971,124 @@ var AgentCanceledError = class extends Error {
|
|
|
971
971
|
}
|
|
972
972
|
};
|
|
973
973
|
|
|
974
|
-
// ../../packages/bridge-core/src/agents/
|
|
974
|
+
// ../../packages/bridge-core/src/agents/claude-event-parser.ts
|
|
975
975
|
function asRecord(value) {
|
|
976
976
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
977
977
|
}
|
|
978
978
|
function asText(value) {
|
|
979
979
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
980
980
|
}
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
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;
|
|
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;
|
|
1101
1063
|
}
|
|
1102
|
-
if (
|
|
1103
|
-
const
|
|
1104
|
-
if (
|
|
1064
|
+
if (type === "result") {
|
|
1065
|
+
const usage = asRecord(record.usage);
|
|
1066
|
+
if (usage) {
|
|
1067
|
+
const cachedInputTokens = asNumber(usage.cache_read_input_tokens);
|
|
1105
1068
|
events.push({
|
|
1106
|
-
type: "
|
|
1107
|
-
|
|
1108
|
-
|
|
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 } : {}
|
|
1109
1073
|
});
|
|
1110
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;
|
|
1111
1083
|
}
|
|
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
1084
|
return events;
|
|
1133
1085
|
}
|
|
1134
|
-
|
|
1135
|
-
if (message) events.push({ type: "message", text: message });
|
|
1136
|
-
return events;
|
|
1137
|
-
}
|
|
1086
|
+
};
|
|
1138
1087
|
|
|
1139
|
-
// ../../packages/bridge-core/src/agents/
|
|
1088
|
+
// ../../packages/bridge-core/src/agents/claude-adapter.ts
|
|
1140
1089
|
import { execFile, spawn as spawn2 } from "node:child_process";
|
|
1141
|
-
import { existsSync } from "node:fs";
|
|
1142
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
1143
1090
|
import { promisify } from "node:util";
|
|
1144
1091
|
|
|
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
1092
|
// ../../packages/bridge-core/src/runtime/managed-process.ts
|
|
1184
1093
|
import { spawn } from "node:child_process";
|
|
1185
1094
|
function replacePortPlaceholder(value, port) {
|
|
@@ -1228,119 +1137,580 @@ function signalChildProcessTree(child, signal) {
|
|
|
1228
1137
|
throw error;
|
|
1229
1138
|
}
|
|
1230
1139
|
}
|
|
1231
|
-
function installEmergencyChildExitHook(child, processLike = process) {
|
|
1232
|
-
let removed = false;
|
|
1233
|
-
const emergencyExit = () => {
|
|
1234
|
-
removed = true;
|
|
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);
|
|
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
|
+
|
|
1542
|
+
// ../../packages/bridge-core/src/agents/codex-event-parser.ts
|
|
1543
|
+
function asRecord2(value) {
|
|
1544
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1545
|
+
}
|
|
1546
|
+
function asText2(value) {
|
|
1547
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1548
|
+
}
|
|
1549
|
+
function sessionId(record) {
|
|
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);
|
|
1552
|
+
}
|
|
1553
|
+
function itemFiles(item) {
|
|
1554
|
+
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
1555
|
+
const files = changes.flatMap((change) => {
|
|
1556
|
+
const record = asRecord2(change);
|
|
1557
|
+
if (!record) return [];
|
|
1558
|
+
return [asText2(record.path) ?? asText2(record.file_path) ?? asText2(record.filePath)].filter(
|
|
1559
|
+
(path) => path !== void 0
|
|
1560
|
+
);
|
|
1561
|
+
});
|
|
1562
|
+
const direct = asText2(item.path) ?? asText2(item.file_path) ?? asText2(item.filePath);
|
|
1563
|
+
if (direct) files.push(direct);
|
|
1564
|
+
return [...new Set(files)];
|
|
1565
|
+
}
|
|
1566
|
+
function stringArray(value) {
|
|
1567
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
|
|
1568
|
+
}
|
|
1569
|
+
function formatArgv(argv) {
|
|
1570
|
+
return argv.map((argument) => /^[A-Za-z0-9_./:=@%+,-]+$/u.test(argument) ? argument : JSON.stringify(argument)).join(" ");
|
|
1571
|
+
}
|
|
1572
|
+
function isDirectExecItem(item) {
|
|
1573
|
+
return item.type === "mcp_tool_call" && item.server === "visual_remote_exec" && item.tool === "run_readonly";
|
|
1574
|
+
}
|
|
1575
|
+
function directExecSummary(item) {
|
|
1576
|
+
const arguments_ = asRecord2(item.arguments);
|
|
1577
|
+
const commands = Array.isArray(arguments_?.commands) ? arguments_.commands : [];
|
|
1578
|
+
const summaries = commands.flatMap((candidate) => {
|
|
1579
|
+
const command = asRecord2(candidate);
|
|
1580
|
+
const argv = stringArray(command?.argv);
|
|
1581
|
+
return argv === void 0 ? [] : [formatArgv(argv)];
|
|
1582
|
+
});
|
|
1583
|
+
return summaries.length === 0 ? void 0 : summaries.join(" \xB7 ");
|
|
1584
|
+
}
|
|
1585
|
+
function directExecResults(item, defaultCwd) {
|
|
1586
|
+
const result = asRecord2(item.result);
|
|
1587
|
+
const structured = asRecord2(result?.structured_content ?? result?.structuredContent);
|
|
1588
|
+
const results = Array.isArray(structured?.results) ? structured.results : [];
|
|
1589
|
+
return results.flatMap((candidate) => {
|
|
1590
|
+
const command = asRecord2(candidate);
|
|
1591
|
+
const argv = stringArray(command?.argv);
|
|
1592
|
+
if (argv === void 0) return [];
|
|
1593
|
+
const exitCode = typeof command?.exitCode === "number" ? command.exitCode : void 0;
|
|
1594
|
+
const durationMs = typeof command?.durationMs === "number" ? command.durationMs : void 0;
|
|
1595
|
+
return [{
|
|
1596
|
+
command: formatArgv(argv),
|
|
1597
|
+
cwd: asText2(command?.cwd) ?? defaultCwd,
|
|
1598
|
+
ok: exitCode === 0,
|
|
1599
|
+
...exitCode === void 0 ? {} : { exitCode },
|
|
1600
|
+
...durationMs === void 0 ? {} : { durationMs },
|
|
1601
|
+
...typeof command?.usedRtk === "boolean" ? { usedRtk: command.usedRtk } : {},
|
|
1602
|
+
...typeof command?.timedOut === "boolean" ? { timedOut: command.timedOut } : {},
|
|
1603
|
+
...typeof command?.truncated === "boolean" ? { truncated: command.truncated } : {}
|
|
1604
|
+
}];
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
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;
|
|
1616
|
+
}
|
|
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 },
|
|
@@ -3615,6 +4031,7 @@ var TaskService = class {
|
|
|
3615
4031
|
return publicTask(accepted);
|
|
3616
4032
|
}
|
|
3617
4033
|
async revert(id) {
|
|
4034
|
+
if (this.#closed) throw new TaskServiceError("SERVICE_CLOSED", "Task service is closed");
|
|
3618
4035
|
if (this.#activeTaskId || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
3619
4036
|
throw new TaskServiceError(
|
|
3620
4037
|
"WRITER_BUSY",
|
|
@@ -3630,12 +4047,19 @@ var TaskService = class {
|
|
|
3630
4047
|
if (!latest || latest.id !== id) {
|
|
3631
4048
|
throw new TaskServiceError("NOT_LATEST_TASK", "Only the latest completed task can be reverted");
|
|
3632
4049
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
4050
|
+
this.#activeTaskId = id;
|
|
4051
|
+
try {
|
|
4052
|
+
await this.#git.revert(id, task.beforeRef, task.afterRef);
|
|
4053
|
+
const reverted = this.#transition(id, "reverted", {
|
|
4054
|
+
completedAt: this.#now().toISOString()
|
|
4055
|
+
});
|
|
4056
|
+
this.#emit("task.reverted", { task: publicTask(reverted) }, id);
|
|
4057
|
+
return publicTask(reverted);
|
|
4058
|
+
} finally {
|
|
4059
|
+
this.#activeTaskId = void 0;
|
|
4060
|
+
if (!this.#closed) void this.#drain();
|
|
4061
|
+
this.#resolveIdleIfNeeded();
|
|
4062
|
+
}
|
|
3639
4063
|
}
|
|
3640
4064
|
async waitForIdle() {
|
|
3641
4065
|
if (!this.#activeTaskId && this.#queue.length === 0 && this.#recoveryQueue.length === 0 && !this.#recovering && !this.#draining) {
|
|
@@ -3646,7 +4070,7 @@ var TaskService = class {
|
|
|
3646
4070
|
async close() {
|
|
3647
4071
|
if (this.#closed) return;
|
|
3648
4072
|
this.#closed = true;
|
|
3649
|
-
if (this.#activeTaskId &&
|
|
4073
|
+
if (this.#activeTaskId && this.#activeAbort) {
|
|
3650
4074
|
this.#cancelRequested.add(this.#activeTaskId);
|
|
3651
4075
|
this.#activeAbort?.abort(new AgentCanceledError("Task service is closing"));
|
|
3652
4076
|
}
|
|
@@ -3711,9 +4135,12 @@ var TaskService = class {
|
|
|
3711
4135
|
if (!task.beforeRef) {
|
|
3712
4136
|
throw new Error("Interrupted task is missing its before snapshot");
|
|
3713
4137
|
}
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
4138
|
+
let afterRef = task.afterRef;
|
|
4139
|
+
if (!afterRef) {
|
|
4140
|
+
afterRef = (await this.#git.createSnapshot(taskId, "after")).ref;
|
|
4141
|
+
this.#store.updateTask(taskId, { afterRef });
|
|
4142
|
+
}
|
|
4143
|
+
const diff = await this.#git.diff(task.beforeRef, afterRef);
|
|
3717
4144
|
this.#store.updateTask(taskId, {
|
|
3718
4145
|
diffText: diff.text,
|
|
3719
4146
|
changedFiles: diff.files
|
|
@@ -3785,7 +4212,7 @@ var TaskService = class {
|
|
|
3785
4212
|
}
|
|
3786
4213
|
}
|
|
3787
4214
|
async #drain() {
|
|
3788
|
-
if (this.#draining || this.#closed || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
4215
|
+
if (this.#draining || this.#activeTaskId || this.#closed || this.#recovering || this.#recoveryQueue.length > 0) {
|
|
3789
4216
|
return;
|
|
3790
4217
|
}
|
|
3791
4218
|
this.#draining = true;
|
|
@@ -4038,11 +4465,11 @@ var TaskService = class {
|
|
|
4038
4465
|
for (const target of result.selection.targets) {
|
|
4039
4466
|
const sanitize = async (location) => {
|
|
4040
4467
|
try {
|
|
4041
|
-
const
|
|
4468
|
+
const filePath2 = await this.#git.pathPolicy.assertFilesystemPathAllowed(
|
|
4042
4469
|
location.filePath,
|
|
4043
4470
|
false
|
|
4044
4471
|
);
|
|
4045
|
-
return { ...location, filePath };
|
|
4472
|
+
return { ...location, filePath: filePath2 };
|
|
4046
4473
|
} catch {
|
|
4047
4474
|
return void 0;
|
|
4048
4475
|
}
|
|
@@ -4254,7 +4681,7 @@ var BrowserSessionManager = class {
|
|
|
4254
4681
|
};
|
|
4255
4682
|
|
|
4256
4683
|
// ../../packages/bridge-core/src/verification/commands.ts
|
|
4257
|
-
import { spawn as
|
|
4684
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
4258
4685
|
var MAX_OUTPUT_CHARS = 8e3;
|
|
4259
4686
|
var KILL_GRACE_MS = 250;
|
|
4260
4687
|
function appendOutput(current, chunk) {
|
|
@@ -4268,7 +4695,7 @@ async function runVerificationCommand(configured, cwd, signal) {
|
|
|
4268
4695
|
}
|
|
4269
4696
|
const startedAt = Date.now();
|
|
4270
4697
|
return await new Promise((resolveResult, rejectResult) => {
|
|
4271
|
-
const child =
|
|
4698
|
+
const child = spawn6(executable, arguments_, {
|
|
4272
4699
|
cwd,
|
|
4273
4700
|
detached: process.platform !== "win32",
|
|
4274
4701
|
shell: false,
|
|
@@ -4843,15 +5270,40 @@ ${output2}` : ""}`
|
|
|
4843
5270
|
}
|
|
4844
5271
|
|
|
4845
5272
|
// ../../packages/bridge-core/src/bridge/default-control-service.ts
|
|
5273
|
+
function createAgentAdapter(agent) {
|
|
5274
|
+
if (agent.adapter === "claude") {
|
|
5275
|
+
if (agent.reasoningEffort === "minimal") {
|
|
5276
|
+
throw new Error("Claude does not support minimal reasoning effort");
|
|
5277
|
+
}
|
|
5278
|
+
return new ClaudeAdapter({
|
|
5279
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
5280
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort }
|
|
5281
|
+
});
|
|
5282
|
+
}
|
|
5283
|
+
if (agent.adapter === "codex") {
|
|
5284
|
+
if (agent.reasoningEffort === "max") {
|
|
5285
|
+
throw new Error("Codex does not support max reasoning effort");
|
|
5286
|
+
}
|
|
5287
|
+
return new CodexAdapter({
|
|
5288
|
+
...agent.model === void 0 ? {} : { model: agent.model },
|
|
5289
|
+
...agent.reasoningEffort === void 0 ? {} : { reasoningEffort: agent.reasoningEffort },
|
|
5290
|
+
...agent.profile === void 0 ? {} : { profile: agent.profile }
|
|
5291
|
+
});
|
|
5292
|
+
}
|
|
5293
|
+
throw new Error(`Agent adapter ${agent.adapter} is not implemented in this build`);
|
|
5294
|
+
}
|
|
5295
|
+
function inheritedAgentEnvironment(names, environment) {
|
|
5296
|
+
return Object.fromEntries(
|
|
5297
|
+
names.flatMap((name) => {
|
|
5298
|
+
const value = environment[name];
|
|
5299
|
+
return value === void 0 ? [] : [[name, value]];
|
|
5300
|
+
})
|
|
5301
|
+
);
|
|
5302
|
+
}
|
|
4846
5303
|
async function createDefaultControlService(context, environment = process.env) {
|
|
4847
5304
|
const loaded = await loadVisualDevConfig(context.repoRoot, {
|
|
4848
5305
|
...context.configRoot === void 0 ? {} : { configRoot: context.configRoot }
|
|
4849
5306
|
});
|
|
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
5307
|
const git = await GitTransactionManager.open(context.repoRoot, {
|
|
4856
5308
|
allowed: rebaseWorkspacePatterns(
|
|
4857
5309
|
context.repoRoot,
|
|
@@ -4870,13 +5322,16 @@ async function createDefaultControlService(context, environment = process.env) {
|
|
|
4870
5322
|
projectId: context.projectId,
|
|
4871
5323
|
workspaceRoot: context.workspaceRoot,
|
|
4872
5324
|
upstreamUrl: context.upstreamUrl,
|
|
4873
|
-
adapter:
|
|
5325
|
+
adapter: createAgentAdapter(loaded.config.agent),
|
|
4874
5326
|
store,
|
|
4875
5327
|
git,
|
|
4876
5328
|
maxRunMs: loaded.config.agent.maxRunMs,
|
|
4877
5329
|
maxPending: loaded.config.queue.maxPending,
|
|
4878
5330
|
resumeMode: loaded.config.agent.resumeMode,
|
|
4879
|
-
environment:
|
|
5331
|
+
environment: inheritedAgentEnvironment(
|
|
5332
|
+
loaded.config.agent.inheritEnv,
|
|
5333
|
+
environment
|
|
5334
|
+
)
|
|
4880
5335
|
});
|
|
4881
5336
|
const controlService = createTaskControlService({
|
|
4882
5337
|
taskService,
|
|
@@ -5147,10 +5602,10 @@ async function acquireWorktreeLock(repositoryRoot, options = {}) {
|
|
|
5147
5602
|
}
|
|
5148
5603
|
|
|
5149
5604
|
// ../../packages/bridge-core/src/runtime/repository.ts
|
|
5150
|
-
import { execFile as
|
|
5605
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
5151
5606
|
import { realpath as realpath9 } from "node:fs/promises";
|
|
5152
|
-
import { promisify as
|
|
5153
|
-
var
|
|
5607
|
+
import { promisify as promisify3 } from "node:util";
|
|
5608
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
5154
5609
|
var GitWorktreeNotFoundError = class extends Error {
|
|
5155
5610
|
constructor(cwd, options = {}) {
|
|
5156
5611
|
super(
|
|
@@ -5162,7 +5617,7 @@ var GitWorktreeNotFoundError = class extends Error {
|
|
|
5162
5617
|
};
|
|
5163
5618
|
async function discoverGitWorktreeRoot(cwd = process.cwd()) {
|
|
5164
5619
|
try {
|
|
5165
|
-
const { stdout } = await
|
|
5620
|
+
const { stdout } = await execFileAsync3(
|
|
5166
5621
|
"git",
|
|
5167
5622
|
["-C", cwd, "rev-parse", "--show-toplevel"],
|
|
5168
5623
|
{
|
|
@@ -5325,7 +5780,7 @@ async function startBridgeCore(options, dependencies) {
|
|
|
5325
5780
|
try {
|
|
5326
5781
|
lock = options.lock ?? await acquireWorktreeLock(loadedConfig.repoRoot, { environment });
|
|
5327
5782
|
const host = options.host ?? loadedConfig.config.gateway.host;
|
|
5328
|
-
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl;
|
|
5783
|
+
const configuredPublicUrl = options.publicUrl ?? loadedConfig.config.gateway.publicUrl ?? options.fallbackPublicUrl;
|
|
5329
5784
|
const publicUrl = configuredPublicUrl === void 0 ? void 0 : normalizePublicUrl(configuredPublicUrl);
|
|
5330
5785
|
const gatewayPort = await findAvailablePort(startPort(loadedConfig, options.listen), host);
|
|
5331
5786
|
const token = generatePairingToken();
|
|
@@ -5343,6 +5798,13 @@ async function startBridgeCore(options, dependencies) {
|
|
|
5343
5798
|
controlService = await resolveControlService(dependencies, controlContext);
|
|
5344
5799
|
const allowedOrigins = new Set(loadedConfig.config.security.allowedOrigins);
|
|
5345
5800
|
if (publicUrl !== void 0) allowedOrigins.add(new URL(publicUrl).origin);
|
|
5801
|
+
if (options.fallbackLoopbackOrigins === true && options.publicUrl === void 0 && loadedConfig.config.gateway.publicUrl === void 0 && loadedConfig.config.security.allowedOrigins.length === 0 && publicUrl !== void 0) {
|
|
5802
|
+
const loopbackUrl = new URL(publicUrl);
|
|
5803
|
+
if (loopbackUrl.hostname === "localhost" || loopbackUrl.hostname === "127.0.0.1") {
|
|
5804
|
+
loopbackUrl.hostname = loopbackUrl.hostname === "localhost" ? "127.0.0.1" : "localhost";
|
|
5805
|
+
allowedOrigins.add(loopbackUrl.origin);
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5346
5808
|
gateway = createGatewayServer({
|
|
5347
5809
|
upstream: options.upstreamUrl,
|
|
5348
5810
|
pairingToken: token,
|
|
@@ -5445,7 +5907,9 @@ async function startAttachBridge(options, dependencies = {}) {
|
|
|
5445
5907
|
upstreamUrl: normalizeUpstream(options.upstream),
|
|
5446
5908
|
...options.listen === void 0 ? {} : { listen: options.listen },
|
|
5447
5909
|
...options.host === void 0 ? {} : { host: options.host },
|
|
5448
|
-
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl }
|
|
5910
|
+
...options.publicUrl === void 0 ? {} : { publicUrl: options.publicUrl },
|
|
5911
|
+
...options.fallbackPublicUrl === void 0 ? {} : { fallbackPublicUrl: options.fallbackPublicUrl },
|
|
5912
|
+
...options.fallbackLoopbackOrigins === void 0 ? {} : { fallbackLoopbackOrigins: options.fallbackLoopbackOrigins }
|
|
5449
5913
|
},
|
|
5450
5914
|
dependencies
|
|
5451
5915
|
);
|
|
@@ -5576,9 +6040,9 @@ function formatBridgeSummary(bridge) {
|
|
|
5576
6040
|
import { constants } from "node:fs";
|
|
5577
6041
|
import { access as access2, stat as stat3 } from "node:fs/promises";
|
|
5578
6042
|
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
|
|
6043
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
6044
|
+
import { promisify as promisify4 } from "node:util";
|
|
6045
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
5582
6046
|
async function fileExists(path) {
|
|
5583
6047
|
try {
|
|
5584
6048
|
await stat3(path);
|
|
@@ -5592,7 +6056,7 @@ async function fileExists(path) {
|
|
|
5592
6056
|
}
|
|
5593
6057
|
async function isIgnored(repoRoot, path) {
|
|
5594
6058
|
try {
|
|
5595
|
-
await
|
|
6059
|
+
await execFileAsync4("git", ["-C", repoRoot, "check-ignore", "--quiet", "--", path], {
|
|
5596
6060
|
windowsHide: true
|
|
5597
6061
|
});
|
|
5598
6062
|
return true;
|
|
@@ -5664,7 +6128,7 @@ async function runDoctor(dependencies = {}) {
|
|
|
5664
6128
|
});
|
|
5665
6129
|
}
|
|
5666
6130
|
const adapter = loaded.config.agent.adapter;
|
|
5667
|
-
const adapterSupported = adapter === "codex";
|
|
6131
|
+
const adapterSupported = adapter === "codex" || adapter === "claude";
|
|
5668
6132
|
const agentAvailable = await executableAvailable(
|
|
5669
6133
|
adapter,
|
|
5670
6134
|
loaded.workspaceRoot,
|
|
@@ -5675,6 +6139,34 @@ async function runDoctor(dependencies = {}) {
|
|
|
5675
6139
|
status: adapterSupported && agentAvailable ? "pass" : "fail",
|
|
5676
6140
|
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
6141
|
});
|
|
6142
|
+
if (loaded.config.agent.inheritEnv.length > 0) {
|
|
6143
|
+
const missing = loaded.config.agent.inheritEnv.filter(
|
|
6144
|
+
(name) => environment[name] === void 0
|
|
6145
|
+
);
|
|
6146
|
+
checks.push({
|
|
6147
|
+
name: "agent-environment",
|
|
6148
|
+
status: missing.length === 0 ? "pass" : "fail",
|
|
6149
|
+
message: missing.length === 0 ? `${loaded.config.agent.inheritEnv.length} agent environment variable(s) are available.` : `Missing agent environment variable(s): ${missing.join(", ")}.`
|
|
6150
|
+
});
|
|
6151
|
+
}
|
|
6152
|
+
if (adapter === "claude") {
|
|
6153
|
+
const sandboxDependencies = await Promise.all(
|
|
6154
|
+
["bwrap", "socat"].map(async (executable) => ({
|
|
6155
|
+
executable,
|
|
6156
|
+
available: await executableAvailable(
|
|
6157
|
+
executable,
|
|
6158
|
+
loaded.workspaceRoot,
|
|
6159
|
+
environment
|
|
6160
|
+
)
|
|
6161
|
+
}))
|
|
6162
|
+
);
|
|
6163
|
+
const missing = sandboxDependencies.filter(({ available }) => !available).map(({ executable }) => executable);
|
|
6164
|
+
checks.push({
|
|
6165
|
+
name: "claude-sandbox",
|
|
6166
|
+
status: missing.length === 0 ? "pass" : "warning",
|
|
6167
|
+
message: missing.length === 0 ? "Claude Bash sandbox dependencies are available." : `Claude Bash sandbox is unavailable without: ${missing.join(", ")}.`
|
|
6168
|
+
});
|
|
6169
|
+
}
|
|
5678
6170
|
const rtkAvailable = await executableAvailable(
|
|
5679
6171
|
"rtk",
|
|
5680
6172
|
loaded.workspaceRoot,
|
|
@@ -5739,7 +6231,7 @@ function formatDoctorChecks(checks) {
|
|
|
5739
6231
|
}
|
|
5740
6232
|
|
|
5741
6233
|
// src/init.ts
|
|
5742
|
-
import { spawn as
|
|
6234
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
5743
6235
|
import { readFile as readFile5, mkdir as mkdir3, realpath as realpath10, stat as stat4, writeFile as writeFile3 } from "node:fs/promises";
|
|
5744
6236
|
import { basename as basename2, dirname as dirname4, join as join5, relative as relative7 } from "node:path";
|
|
5745
6237
|
import { stringify as stringifyYaml } from "yaml";
|
|
@@ -6059,7 +6551,7 @@ function installCommand(request) {
|
|
|
6059
6551
|
async function installPackage(request) {
|
|
6060
6552
|
const { command, args } = installCommand(request);
|
|
6061
6553
|
await new Promise((resolvePromise, reject) => {
|
|
6062
|
-
const child =
|
|
6554
|
+
const child = spawn7(command, args, {
|
|
6063
6555
|
cwd: request.cwd,
|
|
6064
6556
|
env: process.env,
|
|
6065
6557
|
stdio: "inherit",
|
|
@@ -6196,6 +6688,110 @@ function formatBridgeStatus(status) {
|
|
|
6196
6688
|
return rows.join("\n");
|
|
6197
6689
|
}
|
|
6198
6690
|
|
|
6691
|
+
// ../../package.json
|
|
6692
|
+
var package_default = {
|
|
6693
|
+
name: "visual-remote",
|
|
6694
|
+
version: "0.3.3",
|
|
6695
|
+
description: "Visual bridge from a running web UI to a coding agent in its Git worktree",
|
|
6696
|
+
type: "module",
|
|
6697
|
+
packageManager: "pnpm@10.34.5",
|
|
6698
|
+
repository: {
|
|
6699
|
+
type: "git",
|
|
6700
|
+
url: "git+https://github.com/elicie/visual-remote.git"
|
|
6701
|
+
},
|
|
6702
|
+
homepage: "https://github.com/elicie/visual-remote#readme",
|
|
6703
|
+
bugs: {
|
|
6704
|
+
url: "https://github.com/elicie/visual-remote/issues"
|
|
6705
|
+
},
|
|
6706
|
+
files: [
|
|
6707
|
+
"apps/cli/dist/index.js",
|
|
6708
|
+
"apps/cli/dist/direct-exec-mcp.js",
|
|
6709
|
+
"apps/cli/dist/vite.js",
|
|
6710
|
+
"apps/cli/dist/next.js",
|
|
6711
|
+
"apps/cli/dist/next-client.js",
|
|
6712
|
+
"apps/cli/vite.d.ts",
|
|
6713
|
+
"apps/cli/next.d.ts",
|
|
6714
|
+
"apps/cli/next-client.d.ts",
|
|
6715
|
+
"packages/overlay/dist/client.js",
|
|
6716
|
+
"packages/overlay/dist/viewer.js"
|
|
6717
|
+
],
|
|
6718
|
+
bin: {
|
|
6719
|
+
visual: "./apps/cli/dist/index.js",
|
|
6720
|
+
"visual-remote": "./apps/cli/dist/index.js"
|
|
6721
|
+
},
|
|
6722
|
+
exports: {
|
|
6723
|
+
"./vite": {
|
|
6724
|
+
types: "./apps/cli/vite.d.ts",
|
|
6725
|
+
import: "./apps/cli/dist/vite.js"
|
|
6726
|
+
},
|
|
6727
|
+
"./next": {
|
|
6728
|
+
types: "./apps/cli/next.d.ts",
|
|
6729
|
+
import: "./apps/cli/dist/next.js",
|
|
6730
|
+
default: "./apps/cli/dist/next.js"
|
|
6731
|
+
},
|
|
6732
|
+
"./next/client": {
|
|
6733
|
+
types: "./apps/cli/next-client.d.ts",
|
|
6734
|
+
import: "./apps/cli/dist/next-client.js",
|
|
6735
|
+
default: "./apps/cli/dist/next-client.js"
|
|
6736
|
+
}
|
|
6737
|
+
},
|
|
6738
|
+
publishConfig: {
|
|
6739
|
+
access: "public",
|
|
6740
|
+
registry: "https://registry.npmjs.org"
|
|
6741
|
+
},
|
|
6742
|
+
engines: {
|
|
6743
|
+
node: ">=24"
|
|
6744
|
+
},
|
|
6745
|
+
scripts: {
|
|
6746
|
+
build: "corepack pnpm run build:overlay && corepack pnpm run build:server",
|
|
6747
|
+
"build:overlay": "corepack pnpm --filter @visual-remote/overlay build",
|
|
6748
|
+
"build:server": "corepack pnpm --filter @visual-remote/cli build",
|
|
6749
|
+
dev: "corepack pnpm run build:overlay && tsx apps/cli/src/index.ts",
|
|
6750
|
+
test: "vitest run",
|
|
6751
|
+
"test:e2e": "corepack pnpm build && corepack pnpm exec playwright test --config tests/e2e/playwright.config.ts",
|
|
6752
|
+
"test:watch": "vitest",
|
|
6753
|
+
typecheck: "corepack pnpm -r --if-present typecheck && tsc --noEmit -p tsconfig.tests.json",
|
|
6754
|
+
prepack: "corepack pnpm build"
|
|
6755
|
+
},
|
|
6756
|
+
dependencies: {
|
|
6757
|
+
commander: "^15.0.0",
|
|
6758
|
+
"http-proxy": "^1.18.1",
|
|
6759
|
+
ws: "^8.21.1",
|
|
6760
|
+
yaml: "^2.9.0",
|
|
6761
|
+
zod: "^4.4.3"
|
|
6762
|
+
},
|
|
6763
|
+
peerDependencies: {
|
|
6764
|
+
vite: ">=5"
|
|
6765
|
+
},
|
|
6766
|
+
peerDependenciesMeta: {
|
|
6767
|
+
vite: {
|
|
6768
|
+
optional: true
|
|
6769
|
+
}
|
|
6770
|
+
},
|
|
6771
|
+
devDependencies: {
|
|
6772
|
+
"@playwright/test": "^1.62.1",
|
|
6773
|
+
"@types/http-proxy": "^1.17.17",
|
|
6774
|
+
"@types/node": "^26.1.2",
|
|
6775
|
+
"@types/ws": "^8.18.1",
|
|
6776
|
+
"@visual-remote/bridge-core": "workspace:*",
|
|
6777
|
+
"@visual-remote/cli": "workspace:*",
|
|
6778
|
+
"@visual-remote/gateway": "workspace:*",
|
|
6779
|
+
"@visual-remote/overlay": "workspace:*",
|
|
6780
|
+
"@visual-remote/protocol": "workspace:*",
|
|
6781
|
+
esbuild: "^0.28.1",
|
|
6782
|
+
next: "15.5.16",
|
|
6783
|
+
react: "19.1.0",
|
|
6784
|
+
"react-dom": "19.1.0",
|
|
6785
|
+
tsx: "^4.23.1",
|
|
6786
|
+
typescript: "^7.0.2",
|
|
6787
|
+
vite: "^8.1.5",
|
|
6788
|
+
vitest: "^4.1.10"
|
|
6789
|
+
}
|
|
6790
|
+
};
|
|
6791
|
+
|
|
6792
|
+
// src/version.ts
|
|
6793
|
+
var VISUAL_REMOTE_VERSION = package_default.version;
|
|
6794
|
+
|
|
6199
6795
|
// src/index.ts
|
|
6200
6796
|
function parsePort(value) {
|
|
6201
6797
|
const port = Number(value);
|
|
@@ -6218,7 +6814,7 @@ function setExitCode(dependencies, code) {
|
|
|
6218
6814
|
}
|
|
6219
6815
|
}
|
|
6220
6816
|
function createCli(dependencies = {}) {
|
|
6221
|
-
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version(
|
|
6817
|
+
const program = new Command().name("visual").description("Visual Remote Dev Bridge").version(VISUAL_REMOTE_VERSION);
|
|
6222
6818
|
program.command("init").description("Configure Visual Remote for the current Vite or Next.js project").action(async () => {
|
|
6223
6819
|
const result = await initializeVisualDev(dependencies);
|
|
6224
6820
|
output(dependencies, formatInitResult(result));
|
|
@@ -6286,6 +6882,7 @@ if (entryPath !== void 0 && isDirectEntry(entryPath)) {
|
|
|
6286
6882
|
});
|
|
6287
6883
|
}
|
|
6288
6884
|
export {
|
|
6885
|
+
VISUAL_REMOTE_VERSION,
|
|
6289
6886
|
createCli,
|
|
6290
6887
|
formatBridgeStatus,
|
|
6291
6888
|
formatBridgeSummary,
|