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.
@@ -971,215 +971,124 @@ var AgentCanceledError = class extends Error {
971
971
  }
972
972
  };
973
973
 
974
- // ../../packages/bridge-core/src/agents/codex-event-parser.ts
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 sessionId(record) {
982
- const thread = asRecord(record.thread);
983
- return asText(record.thread_id) ?? asText(record.threadId) ?? asText(record.session_id) ?? asText(record.sessionId) ?? (thread ? asText(thread.id) : void 0);
984
- }
985
- function itemFiles(item) {
986
- const changes = Array.isArray(item.changes) ? item.changes : [];
987
- const files = changes.flatMap((change) => {
988
- const record = asRecord(change);
989
- if (!record) return [];
990
- return [asText(record.path) ?? asText(record.file_path) ?? asText(record.filePath)].filter(
991
- (path) => path !== void 0
992
- );
993
- });
994
- const direct = asText(item.path) ?? asText(item.file_path) ?? asText(item.filePath);
995
- if (direct) files.push(direct);
996
- return [...new Set(files)];
997
- }
998
- function stringArray(value) {
999
- return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? value : void 0;
1000
- }
1001
- function formatArgv(argv) {
1002
- return argv.map((argument) => /^[A-Za-z0-9_./:=@%+,-]+$/u.test(argument) ? argument : JSON.stringify(argument)).join(" ");
1003
- }
1004
- function isDirectExecItem(item) {
1005
- return item.type === "mcp_tool_call" && item.server === "visual_remote_exec" && item.tool === "run_readonly";
1006
- }
1007
- function directExecSummary(item) {
1008
- const arguments_ = asRecord(item.arguments);
1009
- const commands = Array.isArray(arguments_?.commands) ? arguments_.commands : [];
1010
- const summaries = commands.flatMap((candidate) => {
1011
- const command = asRecord(candidate);
1012
- const argv = stringArray(command?.argv);
1013
- return argv === void 0 ? [] : [formatArgv(argv)];
1014
- });
1015
- return summaries.length === 0 ? void 0 : summaries.join(" \xB7 ");
1016
- }
1017
- function directExecResults(item, defaultCwd) {
1018
- const result = asRecord(item.result);
1019
- const structured = asRecord(result?.structured_content ?? result?.structuredContent);
1020
- const results = Array.isArray(structured?.results) ? structured.results : [];
1021
- return results.flatMap((candidate) => {
1022
- const command = asRecord(candidate);
1023
- const argv = stringArray(command?.argv);
1024
- if (argv === void 0) return [];
1025
- const exitCode = typeof command?.exitCode === "number" ? command.exitCode : void 0;
1026
- const durationMs = typeof command?.durationMs === "number" ? command.durationMs : void 0;
1027
- return [{
1028
- command: formatArgv(argv),
1029
- cwd: asText(command?.cwd) ?? defaultCwd,
1030
- ok: exitCode === 0,
1031
- ...exitCode === void 0 ? {} : { exitCode },
1032
- ...durationMs === void 0 ? {} : { durationMs },
1033
- ...typeof command?.usedRtk === "boolean" ? { usedRtk: command.usedRtk } : {},
1034
- ...typeof command?.timedOut === "boolean" ? { timedOut: command.timedOut } : {},
1035
- ...typeof command?.truncated === "boolean" ? { truncated: command.truncated } : {}
1036
- }];
1037
- });
1038
- }
1039
- function normalizedUsage(record) {
1040
- const result = asRecord(record.result);
1041
- const usage = asRecord(record.usage) ?? (result ? asRecord(result.usage) : void 0);
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;
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 (itemType === "command_execution") {
1103
- const command = asText(item.command);
1104
- if (command) {
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: "command",
1107
- command,
1108
- cwd: asText(item.cwd) ?? defaultCwd
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
- const message = asText(record.message);
1135
- if (message) events.push({ type: "message", text: message });
1136
- return events;
1137
- }
1086
+ };
1138
1087
 
1139
- // ../../packages/bridge-core/src/agents/codex-adapter.ts
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
- async function waitForProcessTreeExit(child, processGroupId, timeoutMs) {
1248
- const deadline = Date.now() + timeoutMs;
1249
- while (processGroupId === void 0 ? childIsAlive(child) : processGroupIsAlive(processGroupId)) {
1250
- const remaining = deadline - Date.now();
1251
- if (remaining <= 0) return false;
1252
- await new Promise((resolve9) => {
1253
- setTimeout(resolve9, Math.min(25, remaining));
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
- return true;
1257
- }
1258
- async function terminateChildProcessTree(child, killGraceMs = 3e3) {
1259
- const processGroupId = safeDetachedProcessGroupId(child);
1260
- if (processGroupId === void 0 && !childIsAlive(child)) return;
1261
- const sendSignal = (signal) => {
1262
- signalChildProcessTree(child, signal);
1263
- };
1264
- sendSignal("SIGTERM");
1265
- if (await waitForProcessTreeExit(
1266
- child,
1267
- processGroupId,
1268
- Math.max(0, killGraceMs)
1269
- )) {
1270
- return;
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
- sendSignal("SIGKILL");
1273
- await waitForProcessTreeExit(
1274
- child,
1275
- processGroupId,
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 detached = process.platform !== "win32";
1285
- const child = spawn(
1286
- replacePortPlaceholder(executable, options.upstreamPort),
1287
- rawArguments.map((argument) => replacePortPlaceholder(argument, options.upstreamPort)),
1288
- {
1289
- cwd: options.cwd,
1290
- env: {
1291
- ...options.environment ?? process.env,
1292
- HOST: "0.0.0.0",
1293
- PORT: String(options.upstreamPort)
1294
- },
1295
- detached,
1296
- stdio: [
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
- const removeEmergencyExitHook = installEmergencyChildExitHook(child);
1305
- const exit = new Promise(
1306
- (resolve9) => {
1307
- child.once("exit", (code, signal) => {
1308
- resolve9({ code, signal });
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
- if (child.stdout !== null && options.stdout !== void 0) {
1313
- child.stdout.pipe(options.stdout, { end: false });
1314
- }
1315
- if (child.stderr !== null && options.stderr !== void 0) {
1316
- child.stderr.pipe(options.stderr, { end: false });
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
- } catch (error) {
1324
- removeEmergencyExitHook();
1325
- throw error;
1700
+ return events;
1326
1701
  }
1327
- let stopPromise;
1328
- return {
1329
- child,
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
- var execFileAsync = promisify(execFile);
1343
- var INHERITED_ENVIRONMENT = [
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 processEnv(overrides) {
1738
+ function processEnv2(overrides) {
1369
1739
  const environment = {};
1370
- for (const key of INHERITED_ENVIRONMENT) {
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 splitLines(chunk, previous, onLine) {
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 ??= execFileAsync(this.#rtkExecutable, ["--version"], {
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 = spawn2(this.#executable, ["--version"], {
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 = processEnv(input.environment);
1897
+ const environment = processEnv2(input.environment);
1513
1898
  const prompt = await this.#runtimePrompt(input, environment);
1514
- const child = spawn2(this.#executable, args, {
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 = splitLines(chunk, stdoutRemainder, (line) => {
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 = splitLines(chunk, stderrRemainder, (line) => {
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(filePath, required) {
2179
+ async function readYamlMapping(filePath2, required) {
1764
2180
  let source;
1765
2181
  try {
1766
- source = await readFile(filePath, "utf8");
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: ${filePath}`, {
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 ${filePath}`, {
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 spawn3 } from "node:child_process";
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 = spawn3("git", [...args], {
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 spawn4 } from "node:child_process";
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 = spawn4(
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 filePath = matches[0];
3105
- const absolutePath = resolve5(repoRoot, filePath);
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
- await this.#git.revert(id, task.beforeRef, task.afterRef);
3634
- const reverted = this.#transition(id, "reverted", {
3635
- completedAt: this.#now().toISOString()
3636
- });
3637
- this.#emit("task.reverted", { task: publicTask(reverted) }, id);
3638
- return publicTask(reverted);
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 && !this.#recovering) {
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
- const after = await this.#git.createSnapshot(taskId, "after");
3715
- this.#store.updateTask(taskId, { afterRef: after.ref });
3716
- const diff = await this.#git.diff(task.beforeRef, after.ref);
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 filePath = await this.#git.pathPolicy.assertFilesystemPathAllowed(
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 spawn5 } from "node:child_process";
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 = spawn5(executable, arguments_, {
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: new CodexAdapter(),
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 execFile2 } from "node:child_process";
5605
+ import { execFile as execFile3 } from "node:child_process";
5151
5606
  import { realpath as realpath9 } from "node:fs/promises";
5152
- import { promisify as promisify2 } from "node:util";
5153
- var execFileAsync2 = promisify2(execFile2);
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 execFileAsync2(
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 execFile3 } from "node:child_process";
5580
- import { promisify as promisify3 } from "node:util";
5581
- var execFileAsync3 = promisify3(execFile3);
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 execFileAsync3("git", ["-C", repoRoot, "check-ignore", "--quiet", "--", path], {
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 spawn6 } from "node:child_process";
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 = spawn6(command, args, {
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("0.3.1");
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,