@rehpic/vcli 0.1.0-beta.68.1 → 0.1.0-beta.69.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +99 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -285,7 +285,7 @@ var init_default_browser_id = __esm({
|
|
|
285
285
|
// ../../node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js
|
|
286
286
|
import process5 from "process";
|
|
287
287
|
import { promisify as promisify4 } from "util";
|
|
288
|
-
import { execFile as execFile4, execFileSync as
|
|
288
|
+
import { execFile as execFile4, execFileSync as execFileSync3 } from "child_process";
|
|
289
289
|
async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
|
|
290
290
|
if (process5.platform !== "darwin") {
|
|
291
291
|
throw new Error("macOS only");
|
|
@@ -1152,7 +1152,7 @@ function createEmptySession() {
|
|
|
1152
1152
|
|
|
1153
1153
|
// src/bridge-service.ts
|
|
1154
1154
|
import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
|
|
1155
|
-
import { execFileSync, execSync as execSync2 } from "child_process";
|
|
1155
|
+
import { execFileSync as execFileSync2, execSync as execSync2 } from "child_process";
|
|
1156
1156
|
|
|
1157
1157
|
// src/terminal-peer.ts
|
|
1158
1158
|
import { createServer } from "http";
|
|
@@ -1161,6 +1161,7 @@ import { ConvexClient } from "convex/browser";
|
|
|
1161
1161
|
import * as pty from "node-pty";
|
|
1162
1162
|
import { existsSync } from "fs";
|
|
1163
1163
|
import { randomUUID } from "crypto";
|
|
1164
|
+
import { execFileSync } from "child_process";
|
|
1164
1165
|
import localtunnel from "localtunnel";
|
|
1165
1166
|
function findTmuxPath() {
|
|
1166
1167
|
for (const p of [
|
|
@@ -1172,20 +1173,51 @@ function findTmuxPath() {
|
|
|
1172
1173
|
}
|
|
1173
1174
|
return "tmux";
|
|
1174
1175
|
}
|
|
1176
|
+
var TMUX = findTmuxPath();
|
|
1175
1177
|
function ts() {
|
|
1176
1178
|
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
1177
1179
|
}
|
|
1178
|
-
function findPort(
|
|
1180
|
+
function findPort() {
|
|
1179
1181
|
return new Promise((resolve, reject) => {
|
|
1180
1182
|
const srv = createServer();
|
|
1181
1183
|
srv.listen(0, "127.0.0.1", () => {
|
|
1182
1184
|
const addr = srv.address();
|
|
1183
|
-
const port = typeof addr === "object" && addr ? addr.port :
|
|
1185
|
+
const port = typeof addr === "object" && addr ? addr.port : 9100;
|
|
1184
1186
|
srv.close(() => resolve(port));
|
|
1185
1187
|
});
|
|
1186
1188
|
srv.on("error", reject);
|
|
1187
1189
|
});
|
|
1188
1190
|
}
|
|
1191
|
+
function createViewerSession(targetSession, paneId) {
|
|
1192
|
+
const viewerName = `viewer-${randomUUID().slice(0, 8)}`;
|
|
1193
|
+
try {
|
|
1194
|
+
execFileSync(TMUX, [
|
|
1195
|
+
"new-session",
|
|
1196
|
+
"-d",
|
|
1197
|
+
"-s",
|
|
1198
|
+
viewerName,
|
|
1199
|
+
"-t",
|
|
1200
|
+
targetSession
|
|
1201
|
+
]);
|
|
1202
|
+
execFileSync(TMUX, ["set-option", "-t", viewerName, "status", "off"]);
|
|
1203
|
+
if (paneId) {
|
|
1204
|
+
try {
|
|
1205
|
+
execFileSync(TMUX, ["select-pane", "-t", paneId]);
|
|
1206
|
+
} catch {
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return viewerName;
|
|
1210
|
+
} catch (err) {
|
|
1211
|
+
console.error(`[${ts()}] Failed to create viewer session:`, err);
|
|
1212
|
+
return targetSession;
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
function killViewerSession(sessionName) {
|
|
1216
|
+
try {
|
|
1217
|
+
execFileSync(TMUX, ["kill-session", "-t", sessionName]);
|
|
1218
|
+
} catch {
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1189
1221
|
var TerminalPeerManager = class {
|
|
1190
1222
|
constructor(config) {
|
|
1191
1223
|
this.terminals = /* @__PURE__ */ new Map();
|
|
@@ -1195,7 +1227,7 @@ var TerminalPeerManager = class {
|
|
|
1195
1227
|
this.config = config;
|
|
1196
1228
|
this.client = new ConvexClient(config.convexUrl);
|
|
1197
1229
|
}
|
|
1198
|
-
watchSession(workSessionId, tmuxSessionName) {
|
|
1230
|
+
watchSession(workSessionId, tmuxSessionName, tmuxPaneId) {
|
|
1199
1231
|
if (this.unsubscribers.has(workSessionId)) return;
|
|
1200
1232
|
const unsub = this.client.onUpdate(
|
|
1201
1233
|
api.agentBridge.bridgePublic.getWorkSessionTerminalState,
|
|
@@ -1217,6 +1249,7 @@ var TerminalPeerManager = class {
|
|
|
1217
1249
|
void this.startTerminal(
|
|
1218
1250
|
workSessionId,
|
|
1219
1251
|
tmuxSessionName,
|
|
1252
|
+
tmuxPaneId,
|
|
1220
1253
|
state.terminalCols,
|
|
1221
1254
|
state.terminalRows
|
|
1222
1255
|
);
|
|
@@ -1245,17 +1278,21 @@ var TerminalPeerManager = class {
|
|
|
1245
1278
|
}
|
|
1246
1279
|
this.stopTerminal(workSessionId);
|
|
1247
1280
|
}
|
|
1248
|
-
async startTerminal(workSessionId, tmuxSessionName, cols, rows) {
|
|
1281
|
+
async startTerminal(workSessionId, tmuxSessionName, tmuxPaneId, cols, rows) {
|
|
1249
1282
|
if (this.terminals.has(workSessionId)) return;
|
|
1250
|
-
const tmuxBin = findTmuxPath();
|
|
1251
1283
|
try {
|
|
1252
1284
|
const port = await findPort();
|
|
1285
|
+
const viewerSession = createViewerSession(tmuxSessionName, tmuxPaneId);
|
|
1286
|
+
const isLinked = viewerSession !== tmuxSessionName;
|
|
1287
|
+
console.log(
|
|
1288
|
+
`[${ts()}] Viewer session: ${viewerSession}${isLinked ? " (linked)" : ""}`
|
|
1289
|
+
);
|
|
1253
1290
|
console.log(
|
|
1254
|
-
`[${ts()}] Spawning PTY: ${
|
|
1291
|
+
`[${ts()}] Spawning PTY: ${TMUX} attach-session -t ${viewerSession}`
|
|
1255
1292
|
);
|
|
1256
1293
|
const ptyProcess = pty.spawn(
|
|
1257
|
-
|
|
1258
|
-
["attach-session", "-t",
|
|
1294
|
+
TMUX,
|
|
1295
|
+
["attach-session", "-t", viewerSession],
|
|
1259
1296
|
{
|
|
1260
1297
|
name: "xterm-256color",
|
|
1261
1298
|
cols: Math.max(cols, 10),
|
|
@@ -1264,7 +1301,7 @@ var TerminalPeerManager = class {
|
|
|
1264
1301
|
env: { ...process.env, TERM: "xterm-256color" }
|
|
1265
1302
|
}
|
|
1266
1303
|
);
|
|
1267
|
-
console.log(`[${ts()}] PTY started
|
|
1304
|
+
console.log(`[${ts()}] PTY started`);
|
|
1268
1305
|
const token = randomUUID();
|
|
1269
1306
|
const httpServer = createServer();
|
|
1270
1307
|
const wss = new WebSocketServer({ server: httpServer });
|
|
@@ -1272,13 +1309,11 @@ var TerminalPeerManager = class {
|
|
|
1272
1309
|
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
1273
1310
|
const clientToken = url.searchParams.get("token");
|
|
1274
1311
|
if (clientToken !== token) {
|
|
1275
|
-
console.log(`[${ts()}] Rejected unauthorized
|
|
1312
|
+
console.log(`[${ts()}] Rejected unauthorized connection`);
|
|
1276
1313
|
ws.close(4401, "Unauthorized");
|
|
1277
1314
|
return;
|
|
1278
1315
|
}
|
|
1279
|
-
console.log(
|
|
1280
|
-
`[${ts()}] WebSocket client connected (${tmuxSessionName})`
|
|
1281
|
-
);
|
|
1316
|
+
console.log(`[${ts()}] Client connected (${tmuxSessionName})`);
|
|
1282
1317
|
const dataHandler = ptyProcess.onData((data) => {
|
|
1283
1318
|
if (ws.readyState === WebSocket.OPEN) {
|
|
1284
1319
|
ws.send(data);
|
|
@@ -1302,18 +1337,22 @@ var TerminalPeerManager = class {
|
|
|
1302
1337
|
ptyProcess.write(str);
|
|
1303
1338
|
});
|
|
1304
1339
|
ws.on("close", () => {
|
|
1305
|
-
console.log(
|
|
1306
|
-
`[${ts()}] WebSocket client disconnected (${tmuxSessionName})`
|
|
1307
|
-
);
|
|
1340
|
+
console.log(`[${ts()}] Client disconnected (${tmuxSessionName})`);
|
|
1308
1341
|
dataHandler.dispose();
|
|
1309
1342
|
});
|
|
1310
1343
|
});
|
|
1311
1344
|
await new Promise((resolve) => {
|
|
1312
|
-
httpServer.listen(port, "
|
|
1345
|
+
httpServer.listen(port, "0.0.0.0", resolve);
|
|
1313
1346
|
});
|
|
1314
1347
|
console.log(`[${ts()}] WS server on port ${port}`);
|
|
1315
|
-
|
|
1316
|
-
|
|
1348
|
+
const tunnelOpts = { port };
|
|
1349
|
+
if (this.config.tunnelHost) {
|
|
1350
|
+
tunnelOpts.host = this.config.tunnelHost;
|
|
1351
|
+
}
|
|
1352
|
+
console.log(
|
|
1353
|
+
`[${ts()}] Opening tunnel...${this.config.tunnelHost ? ` (host: ${this.config.tunnelHost})` : ""}`
|
|
1354
|
+
);
|
|
1355
|
+
const tunnel = await localtunnel(tunnelOpts);
|
|
1317
1356
|
const tunnelUrl = tunnel.url;
|
|
1318
1357
|
console.log(`[${ts()}] Tunnel: ${tunnelUrl}`);
|
|
1319
1358
|
const wsUrl = tunnelUrl.replace(/^https?:\/\//, "wss://");
|
|
@@ -1322,7 +1361,7 @@ var TerminalPeerManager = class {
|
|
|
1322
1361
|
httpServer,
|
|
1323
1362
|
wss,
|
|
1324
1363
|
tunnel,
|
|
1325
|
-
|
|
1364
|
+
viewerSessionName: isLinked ? viewerSession : null,
|
|
1326
1365
|
token,
|
|
1327
1366
|
workSessionId,
|
|
1328
1367
|
tmuxSessionName,
|
|
@@ -1336,7 +1375,8 @@ var TerminalPeerManager = class {
|
|
|
1336
1375
|
deviceSecret: this.config.deviceSecret,
|
|
1337
1376
|
workSessionId,
|
|
1338
1377
|
terminalUrl: wsUrl,
|
|
1339
|
-
terminalToken: token
|
|
1378
|
+
terminalToken: token,
|
|
1379
|
+
terminalLocalPort: port
|
|
1340
1380
|
}
|
|
1341
1381
|
);
|
|
1342
1382
|
ptyProcess.onExit(() => {
|
|
@@ -1367,6 +1407,9 @@ var TerminalPeerManager = class {
|
|
|
1367
1407
|
terminal.httpServer.close();
|
|
1368
1408
|
} catch {
|
|
1369
1409
|
}
|
|
1410
|
+
if (terminal.viewerSessionName) {
|
|
1411
|
+
killViewerSession(terminal.viewerSessionName);
|
|
1412
|
+
}
|
|
1370
1413
|
this.terminals.delete(workSessionId);
|
|
1371
1414
|
console.log(`[${ts()}] Terminal stopped for ${terminal.tmuxSessionName}`);
|
|
1372
1415
|
}
|
|
@@ -2394,7 +2437,8 @@ var BridgeService = class {
|
|
|
2394
2437
|
if (activity.workSessionId && activity.tmuxSessionName) {
|
|
2395
2438
|
this.terminalPeer.watchSession(
|
|
2396
2439
|
activity.workSessionId,
|
|
2397
|
-
activity.tmuxSessionName
|
|
2440
|
+
activity.tmuxSessionName,
|
|
2441
|
+
activity.tmuxPaneId
|
|
2398
2442
|
);
|
|
2399
2443
|
}
|
|
2400
2444
|
}
|
|
@@ -2493,9 +2537,12 @@ var BridgeService = class {
|
|
|
2493
2537
|
this.terminalPeer = new TerminalPeerManager({
|
|
2494
2538
|
deviceId: this.config.deviceId,
|
|
2495
2539
|
deviceSecret: this.config.deviceSecret,
|
|
2496
|
-
convexUrl: this.config.convexUrl
|
|
2540
|
+
convexUrl: this.config.convexUrl,
|
|
2541
|
+
tunnelHost: this.config.tunnelHost
|
|
2497
2542
|
});
|
|
2498
|
-
console.log(
|
|
2543
|
+
console.log(
|
|
2544
|
+
` Terminal: ready${this.config.tunnelHost ? ` (tunnel: ${this.config.tunnelHost})` : ""}`
|
|
2545
|
+
);
|
|
2499
2546
|
} catch (e) {
|
|
2500
2547
|
console.error(
|
|
2501
2548
|
` WebRTC: failed (${e instanceof Error ? e.message : "unknown"})`
|
|
@@ -2859,7 +2906,7 @@ function createTmuxWorkSession(args) {
|
|
|
2859
2906
|
const windowName = sanitizeTmuxName(
|
|
2860
2907
|
args.provider === "codex" ? "codex" : args.provider === "claude_code" ? "claude" : "shell"
|
|
2861
2908
|
);
|
|
2862
|
-
|
|
2909
|
+
execFileSync2("tmux", [
|
|
2863
2910
|
"new-session",
|
|
2864
2911
|
"-d",
|
|
2865
2912
|
"-s",
|
|
@@ -2869,7 +2916,7 @@ function createTmuxWorkSession(args) {
|
|
|
2869
2916
|
"-c",
|
|
2870
2917
|
args.workspacePath
|
|
2871
2918
|
]);
|
|
2872
|
-
const paneId =
|
|
2919
|
+
const paneId = execFileSync2(
|
|
2873
2920
|
"tmux",
|
|
2874
2921
|
[
|
|
2875
2922
|
"display-message",
|
|
@@ -2880,13 +2927,13 @@ function createTmuxWorkSession(args) {
|
|
|
2880
2927
|
],
|
|
2881
2928
|
{ encoding: "utf-8" }
|
|
2882
2929
|
).trim();
|
|
2883
|
-
const paneProcessId =
|
|
2930
|
+
const paneProcessId = execFileSync2(
|
|
2884
2931
|
"tmux",
|
|
2885
2932
|
["display-message", "-p", "-t", paneId, "#{pane_pid}"],
|
|
2886
2933
|
{ encoding: "utf-8" }
|
|
2887
2934
|
).trim();
|
|
2888
2935
|
if (args.provider) {
|
|
2889
|
-
|
|
2936
|
+
execFileSync2("tmux", [
|
|
2890
2937
|
"send-keys",
|
|
2891
2938
|
"-t",
|
|
2892
2939
|
paneId,
|
|
@@ -2894,7 +2941,7 @@ function createTmuxWorkSession(args) {
|
|
|
2894
2941
|
"Enter"
|
|
2895
2942
|
]);
|
|
2896
2943
|
} else {
|
|
2897
|
-
|
|
2944
|
+
execFileSync2("tmux", [
|
|
2898
2945
|
"send-keys",
|
|
2899
2946
|
"-t",
|
|
2900
2947
|
paneId,
|
|
@@ -2910,13 +2957,13 @@ function createTmuxWorkSession(args) {
|
|
|
2910
2957
|
};
|
|
2911
2958
|
}
|
|
2912
2959
|
function sendTextToTmuxPane(paneId, text2) {
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2960
|
+
execFileSync2("tmux", ["set-buffer", "--", text2]);
|
|
2961
|
+
execFileSync2("tmux", ["paste-buffer", "-t", paneId]);
|
|
2962
|
+
execFileSync2("tmux", ["send-keys", "-t", paneId, "Enter"]);
|
|
2963
|
+
execFileSync2("tmux", ["delete-buffer"]);
|
|
2917
2964
|
}
|
|
2918
2965
|
function captureTmuxPane(paneId) {
|
|
2919
|
-
return
|
|
2966
|
+
return execFileSync2(
|
|
2920
2967
|
"tmux",
|
|
2921
2968
|
["capture-pane", "-p", "-e", "-t", paneId, "-S", "-120"],
|
|
2922
2969
|
{ encoding: "utf-8" }
|
|
@@ -2924,7 +2971,7 @@ function captureTmuxPane(paneId) {
|
|
|
2924
2971
|
}
|
|
2925
2972
|
function resizeTmuxPane(paneId, cols, rows) {
|
|
2926
2973
|
try {
|
|
2927
|
-
|
|
2974
|
+
execFileSync2("tmux", [
|
|
2928
2975
|
"resize-pane",
|
|
2929
2976
|
"-t",
|
|
2930
2977
|
paneId,
|
|
@@ -3246,7 +3293,7 @@ function launchctlGuiDomain() {
|
|
|
3246
3293
|
}
|
|
3247
3294
|
function runLaunchctl(args) {
|
|
3248
3295
|
try {
|
|
3249
|
-
|
|
3296
|
+
execFileSync2("launchctl", args, {
|
|
3250
3297
|
stdio: "ignore"
|
|
3251
3298
|
});
|
|
3252
3299
|
return true;
|
|
@@ -3623,7 +3670,7 @@ async function resolveAppUrl(raw) {
|
|
|
3623
3670
|
return url;
|
|
3624
3671
|
}
|
|
3625
3672
|
}
|
|
3626
|
-
async function
|
|
3673
|
+
async function fetchAppConfig(appUrl) {
|
|
3627
3674
|
try {
|
|
3628
3675
|
const url = new URL("/api/config", appUrl).toString();
|
|
3629
3676
|
const response = await fetch(url);
|
|
@@ -3632,11 +3679,18 @@ async function fetchConvexUrl(appUrl) {
|
|
|
3632
3679
|
}
|
|
3633
3680
|
const data = await response.json();
|
|
3634
3681
|
if (data.convexUrl) {
|
|
3635
|
-
return
|
|
3682
|
+
return {
|
|
3683
|
+
convexUrl: data.convexUrl,
|
|
3684
|
+
tunnelHost: data.tunnelHost || void 0
|
|
3685
|
+
};
|
|
3636
3686
|
}
|
|
3637
3687
|
} catch {
|
|
3638
3688
|
}
|
|
3639
|
-
return "http://127.0.0.1:3210";
|
|
3689
|
+
return { convexUrl: "http://127.0.0.1:3210" };
|
|
3690
|
+
}
|
|
3691
|
+
async function fetchConvexUrl(appUrl) {
|
|
3692
|
+
const config = await fetchAppConfig(appUrl);
|
|
3693
|
+
return config.convexUrl;
|
|
3640
3694
|
}
|
|
3641
3695
|
async function getRuntime(command) {
|
|
3642
3696
|
const options = command.optsWithGlobals();
|
|
@@ -3781,6 +3835,10 @@ async function ensureBridgeConfig(command) {
|
|
|
3781
3835
|
if (!config) {
|
|
3782
3836
|
throw new Error("Bridge device is not configured.");
|
|
3783
3837
|
}
|
|
3838
|
+
const appConfig = await fetchAppConfig(runtime.appUrl);
|
|
3839
|
+
if (appConfig.tunnelHost) {
|
|
3840
|
+
config.tunnelHost = appConfig.tunnelHost;
|
|
3841
|
+
}
|
|
3784
3842
|
saveBridgeConfig(config);
|
|
3785
3843
|
return config;
|
|
3786
3844
|
} catch (error) {
|