@rehpic/vcli 0.1.0-beta.50.1 → 0.1.0-beta.52.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 +113 -315
- package/dist/index.js.map +1 -1
- package/dist/menubar.js +242 -0
- package/dist/menubar.js.map +1 -0
- package/package.json +4 -2
- package/scripts/postinstall.js +106 -0
package/dist/index.js
CHANGED
|
@@ -9,248 +9,6 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// src/menubar.ts
|
|
13
|
-
var menubar_exports = {};
|
|
14
|
-
__export(menubar_exports, {
|
|
15
|
-
startMenuBar: () => startMenuBar
|
|
16
|
-
});
|
|
17
|
-
import SysTrayModule from "systray2";
|
|
18
|
-
import { existsSync, readFileSync } from "fs";
|
|
19
|
-
import { homedir as homedir2 } from "os";
|
|
20
|
-
import { join } from "path";
|
|
21
|
-
import { execSync } from "child_process";
|
|
22
|
-
function loadIconBase64() {
|
|
23
|
-
const iconPath = join(CONFIG_DIR, "assets", "vector-menubar.png");
|
|
24
|
-
if (existsSync(iconPath)) {
|
|
25
|
-
return readFileSync(iconPath).toString("base64");
|
|
26
|
-
}
|
|
27
|
-
return "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==";
|
|
28
|
-
}
|
|
29
|
-
function loadConfig() {
|
|
30
|
-
try {
|
|
31
|
-
return JSON.parse(readFileSync(BRIDGE_CONFIG_FILE, "utf-8"));
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
function loadActivities() {
|
|
37
|
-
try {
|
|
38
|
-
return JSON.parse(readFileSync(LIVE_ACTIVITIES_FILE, "utf-8"));
|
|
39
|
-
} catch {
|
|
40
|
-
return [];
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
function isBridgeRunning() {
|
|
44
|
-
try {
|
|
45
|
-
const pid = Number(readFileSync(PID_FILE, "utf-8").trim());
|
|
46
|
-
process.kill(pid, 0);
|
|
47
|
-
return { running: true, pid };
|
|
48
|
-
} catch {
|
|
49
|
-
return { running: false };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function getSessionInfo() {
|
|
53
|
-
try {
|
|
54
|
-
const session = JSON.parse(
|
|
55
|
-
readFileSync(join(CONFIG_DIR, "cli-default.json"), "utf-8")
|
|
56
|
-
);
|
|
57
|
-
return {
|
|
58
|
-
orgSlug: session.activeOrgSlug ?? "oss-lab",
|
|
59
|
-
appUrl: session.appUrl
|
|
60
|
-
};
|
|
61
|
-
} catch {
|
|
62
|
-
return { orgSlug: "oss-lab" };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function getOrgSlug() {
|
|
66
|
-
try {
|
|
67
|
-
const session = JSON.parse(
|
|
68
|
-
readFileSync(join(CONFIG_DIR, "cli-default.json"), "utf-8")
|
|
69
|
-
);
|
|
70
|
-
return session.activeOrgSlug ?? "oss-lab";
|
|
71
|
-
} catch {
|
|
72
|
-
return "oss-lab";
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function providerLabel(provider) {
|
|
76
|
-
if (provider === "claude_code") return "Claude";
|
|
77
|
-
if (provider === "codex") return "Codex";
|
|
78
|
-
return provider;
|
|
79
|
-
}
|
|
80
|
-
function buildMenu() {
|
|
81
|
-
const config = loadConfig();
|
|
82
|
-
const { running, pid } = isBridgeRunning();
|
|
83
|
-
const activities = loadActivities();
|
|
84
|
-
const items = [];
|
|
85
|
-
const actions = /* @__PURE__ */ new Map();
|
|
86
|
-
let idx = 0;
|
|
87
|
-
if (running && config) {
|
|
88
|
-
items.push({
|
|
89
|
-
title: `Vector Bridge \u2014 Running (PID ${pid})`,
|
|
90
|
-
enabled: false
|
|
91
|
-
});
|
|
92
|
-
idx++;
|
|
93
|
-
items.push({ title: ` ${config.displayName}`, enabled: false });
|
|
94
|
-
idx++;
|
|
95
|
-
} else if (config) {
|
|
96
|
-
items.push({ title: "Vector Bridge \u2014 Offline", enabled: false });
|
|
97
|
-
idx++;
|
|
98
|
-
} else {
|
|
99
|
-
items.push({ title: "Vector Bridge \u2014 Not Configured", enabled: false });
|
|
100
|
-
idx++;
|
|
101
|
-
items.push({
|
|
102
|
-
title: " Run: vcli service start",
|
|
103
|
-
enabled: false
|
|
104
|
-
});
|
|
105
|
-
idx++;
|
|
106
|
-
}
|
|
107
|
-
items.push({ title: "---", enabled: false });
|
|
108
|
-
idx++;
|
|
109
|
-
if (activities.length > 0) {
|
|
110
|
-
items.push({ title: "Active Sessions", enabled: false });
|
|
111
|
-
idx++;
|
|
112
|
-
const orgSlug = getOrgSlug();
|
|
113
|
-
for (const a of activities) {
|
|
114
|
-
const label = `${a.issueKey} \u2014 ${a.title ?? a.issueTitle} (${providerLabel(a.provider)})`;
|
|
115
|
-
items.push({ title: label, tooltip: a.latestSummary });
|
|
116
|
-
const issueKey = a.issueKey;
|
|
117
|
-
actions.set(idx, () => {
|
|
118
|
-
const url = `http://localhost:3000/${orgSlug}/issues/${issueKey}`;
|
|
119
|
-
try {
|
|
120
|
-
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
121
|
-
} catch {
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
idx++;
|
|
125
|
-
}
|
|
126
|
-
items.push({ title: "---", enabled: false });
|
|
127
|
-
idx++;
|
|
128
|
-
}
|
|
129
|
-
if (running) {
|
|
130
|
-
items.push({ title: "Stop Bridge" });
|
|
131
|
-
actions.set(idx, () => {
|
|
132
|
-
try {
|
|
133
|
-
if (pid) process.kill(pid, "SIGTERM");
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
idx++;
|
|
138
|
-
items.push({ title: "Restart Bridge" });
|
|
139
|
-
actions.set(idx, () => {
|
|
140
|
-
try {
|
|
141
|
-
if (pid) process.kill(pid, "SIGTERM");
|
|
142
|
-
setTimeout(() => {
|
|
143
|
-
execSync("vcli service start", { stdio: "ignore" });
|
|
144
|
-
}, 2e3);
|
|
145
|
-
} catch {
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
idx++;
|
|
149
|
-
} else if (config) {
|
|
150
|
-
items.push({ title: "Start Bridge" });
|
|
151
|
-
actions.set(idx, () => {
|
|
152
|
-
try {
|
|
153
|
-
execSync("vcli service start", { stdio: "ignore" });
|
|
154
|
-
} catch {
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
idx++;
|
|
158
|
-
}
|
|
159
|
-
items.push({ title: "---", enabled: false });
|
|
160
|
-
idx++;
|
|
161
|
-
if (config) {
|
|
162
|
-
const { orgSlug, appUrl } = getSessionInfo();
|
|
163
|
-
items.push({
|
|
164
|
-
title: `Account: ${config.userId.slice(0, 12)}...`,
|
|
165
|
-
enabled: false
|
|
166
|
-
});
|
|
167
|
-
idx++;
|
|
168
|
-
if (orgSlug) {
|
|
169
|
-
items.push({ title: `Org: ${orgSlug}`, enabled: false });
|
|
170
|
-
idx++;
|
|
171
|
-
}
|
|
172
|
-
items.push({ title: "---", enabled: false });
|
|
173
|
-
idx++;
|
|
174
|
-
}
|
|
175
|
-
items.push({ title: "Open Vector" });
|
|
176
|
-
actions.set(idx, () => {
|
|
177
|
-
const { appUrl } = getSessionInfo();
|
|
178
|
-
const url = appUrl ?? "http://localhost:3000";
|
|
179
|
-
try {
|
|
180
|
-
execSync(`open "${url}"`, { stdio: "ignore" });
|
|
181
|
-
} catch {
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
idx++;
|
|
185
|
-
items.push({ title: "Quit Vector" });
|
|
186
|
-
actions.set(idx, () => {
|
|
187
|
-
try {
|
|
188
|
-
const { pid: bridgePid } = isBridgeRunning();
|
|
189
|
-
if (bridgePid) process.kill(bridgePid, "SIGTERM");
|
|
190
|
-
} catch {
|
|
191
|
-
}
|
|
192
|
-
process.exit(0);
|
|
193
|
-
});
|
|
194
|
-
idx++;
|
|
195
|
-
return { items, actions };
|
|
196
|
-
}
|
|
197
|
-
async function startMenuBar() {
|
|
198
|
-
try {
|
|
199
|
-
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
200
|
-
const { dirname: dirname2, join: pjoin } = await import("path");
|
|
201
|
-
const { chmodSync } = await import("fs");
|
|
202
|
-
const systrayIndex = fileURLToPath3(import.meta.resolve("systray2"));
|
|
203
|
-
const binName = process.platform === "darwin" ? "tray_darwin_release" : process.platform === "win32" ? "tray_windows_release.exe" : "tray_linux_release";
|
|
204
|
-
const trayBin = pjoin(dirname2(systrayIndex), "traybin", binName);
|
|
205
|
-
chmodSync(trayBin, 493);
|
|
206
|
-
} catch {
|
|
207
|
-
}
|
|
208
|
-
const icon = loadIconBase64();
|
|
209
|
-
const { items, actions } = buildMenu();
|
|
210
|
-
const systray = new SysTray({
|
|
211
|
-
menu: {
|
|
212
|
-
icon,
|
|
213
|
-
title: "",
|
|
214
|
-
tooltip: "Vector Bridge",
|
|
215
|
-
items: items.map((item) => ({
|
|
216
|
-
title: item.title,
|
|
217
|
-
tooltip: item.tooltip ?? "",
|
|
218
|
-
checked: item.checked ?? false,
|
|
219
|
-
enabled: item.enabled ?? true,
|
|
220
|
-
hidden: false
|
|
221
|
-
}))
|
|
222
|
-
},
|
|
223
|
-
debug: false,
|
|
224
|
-
copyDir: false
|
|
225
|
-
});
|
|
226
|
-
void systray.onClick((action) => {
|
|
227
|
-
const handler = actions.get(action.seq_id);
|
|
228
|
-
if (handler) handler();
|
|
229
|
-
});
|
|
230
|
-
setInterval(() => {
|
|
231
|
-
const { items: newItems, actions: newActions } = buildMenu();
|
|
232
|
-
void newItems;
|
|
233
|
-
void newActions;
|
|
234
|
-
}, 15e3);
|
|
235
|
-
}
|
|
236
|
-
var SysTray, CONFIG_DIR, BRIDGE_CONFIG_FILE, PID_FILE, LIVE_ACTIVITIES_FILE;
|
|
237
|
-
var init_menubar = __esm({
|
|
238
|
-
"src/menubar.ts"() {
|
|
239
|
-
"use strict";
|
|
240
|
-
SysTray = typeof SysTrayModule.default === "function" ? SysTrayModule.default : SysTrayModule;
|
|
241
|
-
CONFIG_DIR = join(homedir2(), ".vector");
|
|
242
|
-
BRIDGE_CONFIG_FILE = join(CONFIG_DIR, "bridge.json");
|
|
243
|
-
PID_FILE = join(CONFIG_DIR, "bridge.pid");
|
|
244
|
-
LIVE_ACTIVITIES_FILE = join(CONFIG_DIR, "live-activities.json");
|
|
245
|
-
if (process.argv[1]?.endsWith("menubar.ts") || process.argv[1]?.endsWith("menubar.js")) {
|
|
246
|
-
startMenuBar().catch((e) => {
|
|
247
|
-
console.error("Menu bar error:", e);
|
|
248
|
-
process.exit(1);
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
|
|
254
12
|
// ../../node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
|
|
255
13
|
import fs from "fs";
|
|
256
14
|
function hasDockerEnv() {
|
|
@@ -978,9 +736,9 @@ var init_open = __esm({
|
|
|
978
736
|
});
|
|
979
737
|
|
|
980
738
|
// src/index.ts
|
|
981
|
-
import { readFileSync as
|
|
739
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
982
740
|
import { readFile as readFile2 } from "fs/promises";
|
|
983
|
-
import { dirname, extname, join as
|
|
741
|
+
import { dirname, extname, join as join2 } from "path";
|
|
984
742
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
985
743
|
import { config as loadEnv } from "dotenv";
|
|
986
744
|
import { Command } from "commander";
|
|
@@ -1338,38 +1096,43 @@ function createEmptySession() {
|
|
|
1338
1096
|
|
|
1339
1097
|
// src/bridge-service.ts
|
|
1340
1098
|
import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
|
|
1341
|
-
import { execSync
|
|
1099
|
+
import { execSync } from "child_process";
|
|
1342
1100
|
import {
|
|
1343
|
-
existsSync
|
|
1101
|
+
existsSync,
|
|
1344
1102
|
mkdirSync,
|
|
1345
|
-
readFileSync
|
|
1103
|
+
readFileSync,
|
|
1346
1104
|
writeFileSync,
|
|
1347
1105
|
unlinkSync
|
|
1348
1106
|
} from "fs";
|
|
1349
|
-
import { homedir as
|
|
1350
|
-
import { join
|
|
1107
|
+
import { homedir as homedir2, hostname, platform } from "os";
|
|
1108
|
+
import { join } from "path";
|
|
1351
1109
|
import { randomUUID } from "crypto";
|
|
1352
|
-
var
|
|
1353
|
-
var
|
|
1354
|
-
var
|
|
1355
|
-
var LIVE_ACTIVITIES_CACHE =
|
|
1356
|
-
var LAUNCHAGENT_DIR =
|
|
1357
|
-
var LAUNCHAGENT_PLIST =
|
|
1110
|
+
var CONFIG_DIR = join(homedir2(), ".vector");
|
|
1111
|
+
var BRIDGE_CONFIG_FILE = join(CONFIG_DIR, "bridge.json");
|
|
1112
|
+
var PID_FILE = join(CONFIG_DIR, "bridge.pid");
|
|
1113
|
+
var LIVE_ACTIVITIES_CACHE = join(CONFIG_DIR, "live-activities.json");
|
|
1114
|
+
var LAUNCHAGENT_DIR = join(homedir2(), "Library", "LaunchAgents");
|
|
1115
|
+
var LAUNCHAGENT_PLIST = join(LAUNCHAGENT_DIR, "com.vector.bridge.plist");
|
|
1358
1116
|
var LAUNCHAGENT_LABEL = "com.vector.bridge";
|
|
1117
|
+
var LEGACY_MENUBAR_LAUNCHAGENT_LABEL = "com.vector.menubar";
|
|
1118
|
+
var LEGACY_MENUBAR_LAUNCHAGENT_PLIST = join(
|
|
1119
|
+
LAUNCHAGENT_DIR,
|
|
1120
|
+
`${LEGACY_MENUBAR_LAUNCHAGENT_LABEL}.plist`
|
|
1121
|
+
);
|
|
1359
1122
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
1360
1123
|
var COMMAND_POLL_INTERVAL_MS = 5e3;
|
|
1361
1124
|
var PROCESS_DISCOVERY_INTERVAL_MS = 6e4;
|
|
1362
1125
|
function loadBridgeConfig() {
|
|
1363
|
-
if (!
|
|
1126
|
+
if (!existsSync(BRIDGE_CONFIG_FILE)) return null;
|
|
1364
1127
|
try {
|
|
1365
|
-
return JSON.parse(
|
|
1128
|
+
return JSON.parse(readFileSync(BRIDGE_CONFIG_FILE, "utf-8"));
|
|
1366
1129
|
} catch {
|
|
1367
1130
|
return null;
|
|
1368
1131
|
}
|
|
1369
1132
|
}
|
|
1370
1133
|
function saveBridgeConfig(config) {
|
|
1371
|
-
if (!
|
|
1372
|
-
writeFileSync(
|
|
1134
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1135
|
+
writeFileSync(BRIDGE_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
1373
1136
|
}
|
|
1374
1137
|
function discoverLocalProcesses() {
|
|
1375
1138
|
const processes = [];
|
|
@@ -1384,7 +1147,7 @@ function discoverLocalProcesses() {
|
|
|
1384
1147
|
];
|
|
1385
1148
|
for (const { grep, provider, label, prefix } of patterns) {
|
|
1386
1149
|
try {
|
|
1387
|
-
const ps =
|
|
1150
|
+
const ps = execSync(
|
|
1388
1151
|
`ps aux | grep -E '${grep}' | grep -v vector-bridge | grep -v grep`,
|
|
1389
1152
|
{ encoding: "utf-8", timeout: 5e3 }
|
|
1390
1153
|
);
|
|
@@ -1393,7 +1156,7 @@ function discoverLocalProcesses() {
|
|
|
1393
1156
|
if (!pid) continue;
|
|
1394
1157
|
let cwd;
|
|
1395
1158
|
try {
|
|
1396
|
-
cwd =
|
|
1159
|
+
cwd = execSync(
|
|
1397
1160
|
`lsof -p ${pid} 2>/dev/null | grep cwd | awk '{print $NF}'`,
|
|
1398
1161
|
{ encoding: "utf-8", timeout: 3e3 }
|
|
1399
1162
|
).trim() || void 0;
|
|
@@ -1419,12 +1182,12 @@ function discoverLocalProcesses() {
|
|
|
1419
1182
|
}
|
|
1420
1183
|
function getGitInfo(cwd) {
|
|
1421
1184
|
try {
|
|
1422
|
-
const branch =
|
|
1185
|
+
const branch = execSync("git rev-parse --abbrev-ref HEAD", {
|
|
1423
1186
|
encoding: "utf-8",
|
|
1424
1187
|
cwd,
|
|
1425
1188
|
timeout: 3e3
|
|
1426
1189
|
}).trim();
|
|
1427
|
-
const repoRoot =
|
|
1190
|
+
const repoRoot = execSync("git rev-parse --show-toplevel", {
|
|
1428
1191
|
encoding: "utf-8",
|
|
1429
1192
|
cwd,
|
|
1430
1193
|
timeout: 3e3
|
|
@@ -1537,8 +1300,8 @@ var BridgeService = class {
|
|
|
1537
1300
|
console.log(` Convex: ${this.config.convexUrl}`);
|
|
1538
1301
|
console.log(` PID: ${process.pid}`);
|
|
1539
1302
|
console.log("");
|
|
1540
|
-
if (!
|
|
1541
|
-
writeFileSync(
|
|
1303
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1304
|
+
writeFileSync(PID_FILE, String(process.pid));
|
|
1542
1305
|
await this.heartbeat();
|
|
1543
1306
|
await this.reportProcesses();
|
|
1544
1307
|
await this.refreshLiveActivities();
|
|
@@ -1572,7 +1335,7 @@ var BridgeService = class {
|
|
|
1572
1335
|
[${ts()}] Shutting down...`);
|
|
1573
1336
|
for (const t of this.timers) clearInterval(t);
|
|
1574
1337
|
try {
|
|
1575
|
-
unlinkSync(
|
|
1338
|
+
unlinkSync(PID_FILE);
|
|
1576
1339
|
} catch {
|
|
1577
1340
|
}
|
|
1578
1341
|
process.exit(0);
|
|
@@ -1635,9 +1398,9 @@ function installLaunchAgent(vcliPath) {
|
|
|
1635
1398
|
<key>KeepAlive</key>
|
|
1636
1399
|
<true/>
|
|
1637
1400
|
<key>StandardOutPath</key>
|
|
1638
|
-
<string>${
|
|
1401
|
+
<string>${CONFIG_DIR}/bridge.log</string>
|
|
1639
1402
|
<key>StandardErrorPath</key>
|
|
1640
|
-
<string>${
|
|
1403
|
+
<string>${CONFIG_DIR}/bridge.err.log</string>
|
|
1641
1404
|
<key>EnvironmentVariables</key>
|
|
1642
1405
|
<dict>
|
|
1643
1406
|
<key>PATH</key>
|
|
@@ -1645,46 +1408,16 @@ function installLaunchAgent(vcliPath) {
|
|
|
1645
1408
|
</dict>
|
|
1646
1409
|
</dict>
|
|
1647
1410
|
</plist>`;
|
|
1648
|
-
|
|
1649
|
-
join2(CONFIG_DIR2, "VectorMenuBar"),
|
|
1650
|
-
"/usr/local/bin/VectorMenuBar",
|
|
1651
|
-
join2(homedir3(), ".local", "bin", "VectorMenuBar")
|
|
1652
|
-
];
|
|
1653
|
-
const menuBarBinary = menuBarCandidates.find((p) => existsSync2(p));
|
|
1654
|
-
if (menuBarBinary) {
|
|
1655
|
-
const menuBarPlist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1656
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
1657
|
-
<plist version="1.0">
|
|
1658
|
-
<dict>
|
|
1659
|
-
<key>Label</key>
|
|
1660
|
-
<string>com.vector.menubar</string>
|
|
1661
|
-
<key>ProgramArguments</key>
|
|
1662
|
-
<array>
|
|
1663
|
-
<string>${menuBarBinary}</string>
|
|
1664
|
-
</array>
|
|
1665
|
-
<key>RunAtLoad</key>
|
|
1666
|
-
<true/>
|
|
1667
|
-
<key>KeepAlive</key>
|
|
1668
|
-
<false/>
|
|
1669
|
-
</dict>
|
|
1670
|
-
</plist>`;
|
|
1671
|
-
const menuBarPlistPath = join2(LAUNCHAGENT_DIR, "com.vector.menubar.plist");
|
|
1672
|
-
writeFileSync(menuBarPlistPath, menuBarPlist);
|
|
1673
|
-
try {
|
|
1674
|
-
execSync2(`launchctl load ${menuBarPlistPath}`, { stdio: "pipe" });
|
|
1675
|
-
console.log("Menu bar helper installed.");
|
|
1676
|
-
} catch {
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
if (!existsSync2(LAUNCHAGENT_DIR)) {
|
|
1411
|
+
if (!existsSync(LAUNCHAGENT_DIR)) {
|
|
1680
1412
|
mkdirSync(LAUNCHAGENT_DIR, { recursive: true });
|
|
1681
1413
|
}
|
|
1414
|
+
removeLegacyMenuBarLaunchAgent();
|
|
1682
1415
|
writeFileSync(LAUNCHAGENT_PLIST, plist);
|
|
1683
1416
|
console.log(`Installed LaunchAgent: ${LAUNCHAGENT_PLIST}`);
|
|
1684
1417
|
}
|
|
1685
1418
|
function loadLaunchAgent() {
|
|
1686
1419
|
try {
|
|
1687
|
-
|
|
1420
|
+
execSync(`launchctl load ${LAUNCHAGENT_PLIST}`, { stdio: "inherit" });
|
|
1688
1421
|
console.log(
|
|
1689
1422
|
"LaunchAgent loaded. Bridge will start automatically on login."
|
|
1690
1423
|
);
|
|
@@ -1694,7 +1427,7 @@ function loadLaunchAgent() {
|
|
|
1694
1427
|
}
|
|
1695
1428
|
function unloadLaunchAgent() {
|
|
1696
1429
|
try {
|
|
1697
|
-
|
|
1430
|
+
execSync(`launchctl unload ${LAUNCHAGENT_PLIST}`, { stdio: "inherit" });
|
|
1698
1431
|
console.log("LaunchAgent unloaded.");
|
|
1699
1432
|
} catch {
|
|
1700
1433
|
console.error("Failed to unload LaunchAgent (may not be loaded)");
|
|
@@ -1702,17 +1435,82 @@ function unloadLaunchAgent() {
|
|
|
1702
1435
|
}
|
|
1703
1436
|
function uninstallLaunchAgent() {
|
|
1704
1437
|
unloadLaunchAgent();
|
|
1438
|
+
removeLegacyMenuBarLaunchAgent();
|
|
1705
1439
|
try {
|
|
1706
1440
|
unlinkSync(LAUNCHAGENT_PLIST);
|
|
1707
1441
|
console.log("LaunchAgent removed.");
|
|
1708
1442
|
} catch {
|
|
1709
1443
|
}
|
|
1710
1444
|
}
|
|
1445
|
+
var MENUBAR_PID_FILE = join(CONFIG_DIR, "menubar.pid");
|
|
1446
|
+
function removeLegacyMenuBarLaunchAgent() {
|
|
1447
|
+
if (platform() !== "darwin" || !existsSync(LEGACY_MENUBAR_LAUNCHAGENT_PLIST)) {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
try {
|
|
1451
|
+
execSync(`launchctl unload ${LEGACY_MENUBAR_LAUNCHAGENT_PLIST}`, {
|
|
1452
|
+
stdio: "pipe"
|
|
1453
|
+
});
|
|
1454
|
+
} catch {
|
|
1455
|
+
}
|
|
1456
|
+
try {
|
|
1457
|
+
unlinkSync(LEGACY_MENUBAR_LAUNCHAGENT_PLIST);
|
|
1458
|
+
} catch {
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
function findMenuBarScript() {
|
|
1462
|
+
const candidates = [
|
|
1463
|
+
join(import.meta.dirname ?? "", "menubar.js"),
|
|
1464
|
+
join(import.meta.dirname ?? "", "..", "dist", "menubar.js")
|
|
1465
|
+
];
|
|
1466
|
+
for (const p of candidates) {
|
|
1467
|
+
if (existsSync(p)) return p;
|
|
1468
|
+
}
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
function isKnownMenuBarProcess(pid) {
|
|
1472
|
+
try {
|
|
1473
|
+
const command = execSync(`ps -p ${pid} -o args=`, {
|
|
1474
|
+
encoding: "utf-8",
|
|
1475
|
+
timeout: 3e3
|
|
1476
|
+
});
|
|
1477
|
+
return command.includes("menubar.js") || command.includes("menubar.ts") || command.includes("VectorMenuBar");
|
|
1478
|
+
} catch {
|
|
1479
|
+
return false;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
function killExistingMenuBar() {
|
|
1483
|
+
if (existsSync(MENUBAR_PID_FILE)) {
|
|
1484
|
+
try {
|
|
1485
|
+
const pid = Number(readFileSync(MENUBAR_PID_FILE, "utf-8").trim());
|
|
1486
|
+
if (Number.isFinite(pid) && pid > 0 && isKnownMenuBarProcess(pid)) {
|
|
1487
|
+
process.kill(pid, "SIGTERM");
|
|
1488
|
+
}
|
|
1489
|
+
} catch {
|
|
1490
|
+
}
|
|
1491
|
+
try {
|
|
1492
|
+
unlinkSync(MENUBAR_PID_FILE);
|
|
1493
|
+
} catch {
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1711
1497
|
async function launchMenuBar() {
|
|
1498
|
+
if (platform() !== "darwin") return;
|
|
1499
|
+
removeLegacyMenuBarLaunchAgent();
|
|
1500
|
+
const script = findMenuBarScript();
|
|
1501
|
+
if (!script) return;
|
|
1502
|
+
killExistingMenuBar();
|
|
1712
1503
|
try {
|
|
1713
|
-
const {
|
|
1714
|
-
|
|
1715
|
-
|
|
1504
|
+
const { spawn: spawnChild } = await import("child_process");
|
|
1505
|
+
const child = spawnChild(process.execPath, [script], {
|
|
1506
|
+
detached: true,
|
|
1507
|
+
stdio: "ignore",
|
|
1508
|
+
env: { ...process.env }
|
|
1509
|
+
});
|
|
1510
|
+
child.unref();
|
|
1511
|
+
if (child.pid) {
|
|
1512
|
+
writeFileSync(MENUBAR_PID_FILE, String(child.pid));
|
|
1513
|
+
}
|
|
1716
1514
|
} catch {
|
|
1717
1515
|
}
|
|
1718
1516
|
}
|
|
@@ -1722,8 +1520,8 @@ function getBridgeStatus() {
|
|
|
1722
1520
|
let running = false;
|
|
1723
1521
|
let starting = false;
|
|
1724
1522
|
let pid;
|
|
1725
|
-
if (
|
|
1726
|
-
const pidStr =
|
|
1523
|
+
if (existsSync(PID_FILE)) {
|
|
1524
|
+
const pidStr = readFileSync(PID_FILE, "utf-8").trim();
|
|
1727
1525
|
pid = Number(pidStr);
|
|
1728
1526
|
try {
|
|
1729
1527
|
process.kill(pid, 0);
|
|
@@ -1734,7 +1532,7 @@ function getBridgeStatus() {
|
|
|
1734
1532
|
}
|
|
1735
1533
|
if (!running && platform() === "darwin") {
|
|
1736
1534
|
try {
|
|
1737
|
-
const result =
|
|
1535
|
+
const result = execSync(
|
|
1738
1536
|
`launchctl list ${LAUNCHAGENT_LABEL} 2>/dev/null`,
|
|
1739
1537
|
{ encoding: "utf-8", timeout: 3e3 }
|
|
1740
1538
|
);
|
|
@@ -1747,8 +1545,9 @@ function getBridgeStatus() {
|
|
|
1747
1545
|
return { configured: true, running, starting, pid, config };
|
|
1748
1546
|
}
|
|
1749
1547
|
function stopBridge() {
|
|
1750
|
-
|
|
1751
|
-
|
|
1548
|
+
killExistingMenuBar();
|
|
1549
|
+
if (!existsSync(PID_FILE)) return false;
|
|
1550
|
+
const pid = Number(readFileSync(PID_FILE, "utf-8").trim());
|
|
1752
1551
|
try {
|
|
1753
1552
|
process.kill(pid, "SIGTERM");
|
|
1754
1553
|
return true;
|
|
@@ -2204,7 +2003,7 @@ var program = new Command();
|
|
|
2204
2003
|
function readPackageVersionSync() {
|
|
2205
2004
|
try {
|
|
2206
2005
|
const dir = import.meta.dirname ?? dirname(fileURLToPath2(import.meta.url));
|
|
2207
|
-
const raw =
|
|
2006
|
+
const raw = readFileSync2(join2(dir, "..", "package.json"), "utf8");
|
|
2208
2007
|
return JSON.parse(raw).version ?? "unknown";
|
|
2209
2008
|
} catch {
|
|
2210
2009
|
return "unknown";
|
|
@@ -3783,7 +3582,6 @@ serviceCommand.command("start").description("Start the bridge service via Launch
|
|
|
3783
3582
|
const vcliPath = process.argv[1] ?? "vcli";
|
|
3784
3583
|
installLaunchAgent(vcliPath);
|
|
3785
3584
|
loadLaunchAgent();
|
|
3786
|
-
await launchMenuBar();
|
|
3787
3585
|
s.stop("Bridge service started.");
|
|
3788
3586
|
} else {
|
|
3789
3587
|
console.log(
|
|
@@ -3807,6 +3605,9 @@ serviceCommand.command("run").description("Run the bridge service in the foregro
|
|
|
3807
3605
|
if (!user) throw new Error("Not logged in. Run `vcli auth login` first.");
|
|
3808
3606
|
config = await setupBridgeDevice(runtime.convexUrl, user._id);
|
|
3809
3607
|
}
|
|
3608
|
+
if (osPlatform() === "darwin") {
|
|
3609
|
+
await launchMenuBar();
|
|
3610
|
+
}
|
|
3810
3611
|
const bridge = new BridgeService(config);
|
|
3811
3612
|
await bridge.run();
|
|
3812
3613
|
});
|
|
@@ -3868,9 +3669,6 @@ serviceCommand.command("install").description("Install the bridge as a system se
|
|
|
3868
3669
|
s.start("Starting bridge service...");
|
|
3869
3670
|
loadLaunchAgent();
|
|
3870
3671
|
s.stop("Bridge service started");
|
|
3871
|
-
s.start("Launching menu bar...");
|
|
3872
|
-
await launchMenuBar();
|
|
3873
|
-
s.stop("Menu bar ready");
|
|
3874
3672
|
console.log("");
|
|
3875
3673
|
console.log(
|
|
3876
3674
|
"Bridge installed and running. Will start automatically on login."
|