@rehpic/vcli 0.1.0-beta.5.1 → 0.1.0-beta.51.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 CHANGED
@@ -1,8 +1,745 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // ../../node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
13
+ import fs from "fs";
14
+ function hasDockerEnv() {
15
+ try {
16
+ fs.statSync("/.dockerenv");
17
+ return true;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ function hasDockerCGroup() {
23
+ try {
24
+ return fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
25
+ } catch {
26
+ return false;
27
+ }
28
+ }
29
+ function isDocker() {
30
+ if (isDockerCached === void 0) {
31
+ isDockerCached = hasDockerEnv() || hasDockerCGroup();
32
+ }
33
+ return isDockerCached;
34
+ }
35
+ var isDockerCached;
36
+ var init_is_docker = __esm({
37
+ "../../node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js"() {
38
+ "use strict";
39
+ }
40
+ });
41
+
42
+ // ../../node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
43
+ import fs2 from "fs";
44
+ function isInsideContainer() {
45
+ if (cachedResult === void 0) {
46
+ cachedResult = hasContainerEnv() || isDocker();
47
+ }
48
+ return cachedResult;
49
+ }
50
+ var cachedResult, hasContainerEnv;
51
+ var init_is_inside_container = __esm({
52
+ "../../node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js"() {
53
+ "use strict";
54
+ init_is_docker();
55
+ hasContainerEnv = () => {
56
+ try {
57
+ fs2.statSync("/run/.containerenv");
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ };
63
+ }
64
+ });
65
+
66
+ // ../../node_modules/.pnpm/is-wsl@3.1.1/node_modules/is-wsl/index.js
67
+ import process2 from "process";
68
+ import os from "os";
69
+ import fs3 from "fs";
70
+ var isWsl, is_wsl_default;
71
+ var init_is_wsl = __esm({
72
+ "../../node_modules/.pnpm/is-wsl@3.1.1/node_modules/is-wsl/index.js"() {
73
+ "use strict";
74
+ init_is_inside_container();
75
+ isWsl = () => {
76
+ if (process2.platform !== "linux") {
77
+ return false;
78
+ }
79
+ if (os.release().toLowerCase().includes("microsoft")) {
80
+ if (isInsideContainer()) {
81
+ return false;
82
+ }
83
+ return true;
84
+ }
85
+ try {
86
+ if (fs3.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft")) {
87
+ return !isInsideContainer();
88
+ }
89
+ } catch {
90
+ }
91
+ if (fs3.existsSync("/proc/sys/fs/binfmt_misc/WSLInterop") || fs3.existsSync("/run/WSL")) {
92
+ return !isInsideContainer();
93
+ }
94
+ return false;
95
+ };
96
+ is_wsl_default = process2.env.__IS_WSL_TEST__ ? isWsl : isWsl();
97
+ }
98
+ });
99
+
100
+ // ../../node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
101
+ import process3 from "process";
102
+ import { Buffer as Buffer2 } from "buffer";
103
+ import { promisify } from "util";
104
+ import childProcess from "child_process";
105
+ import fs4, { constants as fsConstants } from "fs/promises";
106
+ var execFile, powerShellPath, executePowerShell;
107
+ var init_powershell_utils = __esm({
108
+ "../../node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js"() {
109
+ "use strict";
110
+ execFile = promisify(childProcess.execFile);
111
+ powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
112
+ executePowerShell = async (command, options = {}) => {
113
+ const {
114
+ powerShellPath: psPath,
115
+ ...execFileOptions
116
+ } = options;
117
+ const encodedCommand = executePowerShell.encodeCommand(command);
118
+ return execFile(
119
+ psPath ?? powerShellPath(),
120
+ [
121
+ ...executePowerShell.argumentsPrefix,
122
+ encodedCommand
123
+ ],
124
+ {
125
+ encoding: "utf8",
126
+ ...execFileOptions
127
+ }
128
+ );
129
+ };
130
+ executePowerShell.argumentsPrefix = [
131
+ "-NoProfile",
132
+ "-NonInteractive",
133
+ "-ExecutionPolicy",
134
+ "Bypass",
135
+ "-EncodedCommand"
136
+ ];
137
+ executePowerShell.encodeCommand = (command) => Buffer2.from(command, "utf16le").toString("base64");
138
+ executePowerShell.escapeArgument = (value) => `'${String(value).replaceAll("'", "''")}'`;
139
+ }
140
+ });
141
+
142
+ // ../../node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/utilities.js
143
+ function parseMountPointFromConfig(content) {
144
+ for (const line of content.split("\n")) {
145
+ if (/^\s*#/.test(line)) {
146
+ continue;
147
+ }
148
+ const match = /^\s*root\s*=\s*(?<mountPoint>"[^"]*"|'[^']*'|[^#]*)/.exec(line);
149
+ if (!match) {
150
+ continue;
151
+ }
152
+ return match.groups.mountPoint.trim().replaceAll(/^["']|["']$/g, "");
153
+ }
154
+ }
155
+ var init_utilities = __esm({
156
+ "../../node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/utilities.js"() {
157
+ "use strict";
158
+ }
159
+ });
160
+
161
+ // ../../node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
162
+ import { promisify as promisify2 } from "util";
163
+ import childProcess2 from "child_process";
164
+ import fs5, { constants as fsConstants2 } from "fs/promises";
165
+ var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
166
+ var init_wsl_utils = __esm({
167
+ "../../node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
168
+ "use strict";
169
+ init_is_wsl();
170
+ init_powershell_utils();
171
+ init_utilities();
172
+ init_is_wsl();
173
+ execFile2 = promisify2(childProcess2.execFile);
174
+ wslDrivesMountPoint = /* @__PURE__ */ (() => {
175
+ const defaultMountPoint = "/mnt/";
176
+ let mountPoint;
177
+ return async function() {
178
+ if (mountPoint) {
179
+ return mountPoint;
180
+ }
181
+ const configFilePath = "/etc/wsl.conf";
182
+ let isConfigFileExists = false;
183
+ try {
184
+ await fs5.access(configFilePath, fsConstants2.F_OK);
185
+ isConfigFileExists = true;
186
+ } catch {
187
+ }
188
+ if (!isConfigFileExists) {
189
+ return defaultMountPoint;
190
+ }
191
+ const configContent = await fs5.readFile(configFilePath, { encoding: "utf8" });
192
+ const parsedMountPoint = parseMountPointFromConfig(configContent);
193
+ if (parsedMountPoint === void 0) {
194
+ return defaultMountPoint;
195
+ }
196
+ mountPoint = parsedMountPoint;
197
+ mountPoint = mountPoint.endsWith("/") ? mountPoint : `${mountPoint}/`;
198
+ return mountPoint;
199
+ };
200
+ })();
201
+ powerShellPathFromWsl = async () => {
202
+ const mountPoint = await wslDrivesMountPoint();
203
+ return `${mountPoint}c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe`;
204
+ };
205
+ powerShellPath2 = is_wsl_default ? powerShellPathFromWsl : powerShellPath;
206
+ canAccessPowerShell = async () => {
207
+ canAccessPowerShellPromise ??= (async () => {
208
+ try {
209
+ const psPath = await powerShellPath2();
210
+ await fs5.access(psPath, fsConstants2.X_OK);
211
+ return true;
212
+ } catch {
213
+ return false;
214
+ }
215
+ })();
216
+ return canAccessPowerShellPromise;
217
+ };
218
+ wslDefaultBrowser = async () => {
219
+ const psPath = await powerShellPath2();
220
+ const command = String.raw`(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice").ProgId`;
221
+ const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
222
+ return stdout.trim();
223
+ };
224
+ convertWslPathToWindows = async (path3) => {
225
+ if (/^[a-z]+:\/\//i.test(path3)) {
226
+ return path3;
227
+ }
228
+ try {
229
+ const { stdout } = await execFile2("wslpath", ["-aw", path3], { encoding: "utf8" });
230
+ return stdout.trim();
231
+ } catch {
232
+ return path3;
233
+ }
234
+ };
235
+ }
236
+ });
237
+
238
+ // ../../node_modules/.pnpm/define-lazy-prop@3.0.0/node_modules/define-lazy-prop/index.js
239
+ function defineLazyProperty(object, propertyName, valueGetter) {
240
+ const define = (value) => Object.defineProperty(object, propertyName, { value, enumerable: true, writable: true });
241
+ Object.defineProperty(object, propertyName, {
242
+ configurable: true,
243
+ enumerable: true,
244
+ get() {
245
+ const result = valueGetter();
246
+ define(result);
247
+ return result;
248
+ },
249
+ set(value) {
250
+ define(value);
251
+ }
252
+ });
253
+ return object;
254
+ }
255
+ var init_define_lazy_prop = __esm({
256
+ "../../node_modules/.pnpm/define-lazy-prop@3.0.0/node_modules/define-lazy-prop/index.js"() {
257
+ "use strict";
258
+ }
259
+ });
260
+
261
+ // ../../node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js
262
+ import { promisify as promisify3 } from "util";
263
+ import process4 from "process";
264
+ import { execFile as execFile3 } from "child_process";
265
+ async function defaultBrowserId() {
266
+ if (process4.platform !== "darwin") {
267
+ throw new Error("macOS only");
268
+ }
269
+ const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
270
+ const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
271
+ const browserId = match?.groups.id ?? "com.apple.Safari";
272
+ if (browserId === "com.apple.safari") {
273
+ return "com.apple.Safari";
274
+ }
275
+ return browserId;
276
+ }
277
+ var execFileAsync;
278
+ var init_default_browser_id = __esm({
279
+ "../../node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js"() {
280
+ "use strict";
281
+ execFileAsync = promisify3(execFile3);
282
+ }
283
+ });
284
+
285
+ // ../../node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js
286
+ import process5 from "process";
287
+ import { promisify as promisify4 } from "util";
288
+ import { execFile as execFile4, execFileSync } from "child_process";
289
+ async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
290
+ if (process5.platform !== "darwin") {
291
+ throw new Error("macOS only");
292
+ }
293
+ const outputArguments = humanReadableOutput ? [] : ["-ss"];
294
+ const execOptions = {};
295
+ if (signal) {
296
+ execOptions.signal = signal;
297
+ }
298
+ const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
299
+ return stdout.trim();
300
+ }
301
+ var execFileAsync2;
302
+ var init_run_applescript = __esm({
303
+ "../../node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js"() {
304
+ "use strict";
305
+ execFileAsync2 = promisify4(execFile4);
306
+ }
307
+ });
308
+
309
+ // ../../node_modules/.pnpm/bundle-name@4.1.0/node_modules/bundle-name/index.js
310
+ async function bundleName(bundleId) {
311
+ return runAppleScript(`tell application "Finder" to set app_path to application file id "${bundleId}" as string
312
+ tell application "System Events" to get value of property list item "CFBundleName" of property list file (app_path & ":Contents:Info.plist")`);
313
+ }
314
+ var init_bundle_name = __esm({
315
+ "../../node_modules/.pnpm/bundle-name@4.1.0/node_modules/bundle-name/index.js"() {
316
+ "use strict";
317
+ init_run_applescript();
318
+ }
319
+ });
320
+
321
+ // ../../node_modules/.pnpm/default-browser@5.5.0/node_modules/default-browser/windows.js
322
+ import { promisify as promisify5 } from "util";
323
+ import { execFile as execFile5 } from "child_process";
324
+ async function defaultBrowser(_execFileAsync = execFileAsync3) {
325
+ const { stdout } = await _execFileAsync("reg", [
326
+ "QUERY",
327
+ " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
328
+ "/v",
329
+ "ProgId"
330
+ ]);
331
+ const match = /ProgId\s*REG_SZ\s*(?<id>\S+)/.exec(stdout);
332
+ if (!match) {
333
+ throw new UnknownBrowserError(`Cannot find Windows browser in stdout: ${JSON.stringify(stdout)}`);
334
+ }
335
+ const { id } = match.groups;
336
+ const dotIndex = id.lastIndexOf(".");
337
+ const hyphenIndex = id.lastIndexOf("-");
338
+ const baseIdByDot = dotIndex === -1 ? void 0 : id.slice(0, dotIndex);
339
+ const baseIdByHyphen = hyphenIndex === -1 ? void 0 : id.slice(0, hyphenIndex);
340
+ return windowsBrowserProgIds[id] ?? windowsBrowserProgIds[baseIdByDot] ?? windowsBrowserProgIds[baseIdByHyphen] ?? { name: id, id };
341
+ }
342
+ var execFileAsync3, windowsBrowserProgIds, _windowsBrowserProgIdMap, UnknownBrowserError;
343
+ var init_windows = __esm({
344
+ "../../node_modules/.pnpm/default-browser@5.5.0/node_modules/default-browser/windows.js"() {
345
+ "use strict";
346
+ execFileAsync3 = promisify5(execFile5);
347
+ windowsBrowserProgIds = {
348
+ MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
349
+ // The missing `L` is correct.
350
+ MSEdgeBHTML: { name: "Edge Beta", id: "com.microsoft.edge.beta" },
351
+ MSEdgeDHTML: { name: "Edge Dev", id: "com.microsoft.edge.dev" },
352
+ AppXq0fevzme2pys62n3e0fbqa7peapykr8v: { name: "Edge", id: "com.microsoft.edge.old" },
353
+ ChromeHTML: { name: "Chrome", id: "com.google.chrome" },
354
+ ChromeBHTML: { name: "Chrome Beta", id: "com.google.chrome.beta" },
355
+ ChromeDHTML: { name: "Chrome Dev", id: "com.google.chrome.dev" },
356
+ ChromiumHTM: { name: "Chromium", id: "org.chromium.Chromium" },
357
+ BraveHTML: { name: "Brave", id: "com.brave.Browser" },
358
+ BraveBHTML: { name: "Brave Beta", id: "com.brave.Browser.beta" },
359
+ BraveDHTML: { name: "Brave Dev", id: "com.brave.Browser.dev" },
360
+ BraveSSHTM: { name: "Brave Nightly", id: "com.brave.Browser.nightly" },
361
+ FirefoxURL: { name: "Firefox", id: "org.mozilla.firefox" },
362
+ OperaStable: { name: "Opera", id: "com.operasoftware.Opera" },
363
+ VivaldiHTM: { name: "Vivaldi", id: "com.vivaldi.Vivaldi" },
364
+ "IE.HTTP": { name: "Internet Explorer", id: "com.microsoft.ie" }
365
+ };
366
+ _windowsBrowserProgIdMap = new Map(Object.entries(windowsBrowserProgIds));
367
+ UnknownBrowserError = class extends Error {
368
+ };
369
+ }
370
+ });
371
+
372
+ // ../../node_modules/.pnpm/default-browser@5.5.0/node_modules/default-browser/index.js
373
+ import { promisify as promisify6 } from "util";
374
+ import process6 from "process";
375
+ import { execFile as execFile6 } from "child_process";
376
+ async function defaultBrowser2() {
377
+ if (process6.platform === "darwin") {
378
+ const id = await defaultBrowserId();
379
+ const name = await bundleName(id);
380
+ return { name, id };
381
+ }
382
+ if (process6.platform === "linux") {
383
+ const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
384
+ const id = stdout.trim();
385
+ const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
386
+ return { name, id };
387
+ }
388
+ if (process6.platform === "win32") {
389
+ return defaultBrowser();
390
+ }
391
+ throw new Error("Only macOS, Linux, and Windows are supported");
392
+ }
393
+ var execFileAsync4, titleize;
394
+ var init_default_browser = __esm({
395
+ "../../node_modules/.pnpm/default-browser@5.5.0/node_modules/default-browser/index.js"() {
396
+ "use strict";
397
+ init_default_browser_id();
398
+ init_bundle_name();
399
+ init_windows();
400
+ init_windows();
401
+ execFileAsync4 = promisify6(execFile6);
402
+ titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
403
+ }
404
+ });
405
+
406
+ // ../../node_modules/.pnpm/is-in-ssh@1.0.0/node_modules/is-in-ssh/index.js
407
+ import process7 from "process";
408
+ var isInSsh, is_in_ssh_default;
409
+ var init_is_in_ssh = __esm({
410
+ "../../node_modules/.pnpm/is-in-ssh@1.0.0/node_modules/is-in-ssh/index.js"() {
411
+ "use strict";
412
+ isInSsh = Boolean(process7.env.SSH_CONNECTION || process7.env.SSH_CLIENT || process7.env.SSH_TTY);
413
+ is_in_ssh_default = isInSsh;
414
+ }
415
+ });
2
416
 
3
- // ../../src/cli/index.ts
417
+ // ../../node_modules/.pnpm/open@11.0.0/node_modules/open/index.js
418
+ var open_exports = {};
419
+ __export(open_exports, {
420
+ apps: () => apps,
421
+ default: () => open_default,
422
+ openApp: () => openApp
423
+ });
424
+ import process8 from "process";
425
+ import path2 from "path";
426
+ import { fileURLToPath } from "url";
427
+ import childProcess3 from "child_process";
428
+ import fs6, { constants as fsConstants3 } from "fs/promises";
429
+ function detectArchBinary(binary) {
430
+ if (typeof binary === "string" || Array.isArray(binary)) {
431
+ return binary;
432
+ }
433
+ const { [arch]: archBinary } = binary;
434
+ if (!archBinary) {
435
+ throw new Error(`${arch} is not supported`);
436
+ }
437
+ return archBinary;
438
+ }
439
+ function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
440
+ if (wsl && is_wsl_default) {
441
+ return detectArchBinary(wsl);
442
+ }
443
+ if (!platformBinary) {
444
+ throw new Error(`${platform2} is not supported`);
445
+ }
446
+ return detectArchBinary(platformBinary);
447
+ }
448
+ var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform2, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
449
+ var init_open = __esm({
450
+ "../../node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
451
+ "use strict";
452
+ init_wsl_utils();
453
+ init_powershell_utils();
454
+ init_define_lazy_prop();
455
+ init_default_browser();
456
+ init_is_inside_container();
457
+ init_is_in_ssh();
458
+ fallbackAttemptSymbol = Symbol("fallbackAttempt");
459
+ __dirname = import.meta.url ? path2.dirname(fileURLToPath(import.meta.url)) : "";
460
+ localXdgOpenPath = path2.join(__dirname, "xdg-open");
461
+ ({ platform: platform2, arch } = process8);
462
+ tryEachApp = async (apps2, opener) => {
463
+ if (apps2.length === 0) {
464
+ return;
465
+ }
466
+ const errors = [];
467
+ for (const app of apps2) {
468
+ try {
469
+ return await opener(app);
470
+ } catch (error) {
471
+ errors.push(error);
472
+ }
473
+ }
474
+ throw new AggregateError(errors, "Failed to open in all supported apps");
475
+ };
476
+ baseOpen = async (options) => {
477
+ options = {
478
+ wait: false,
479
+ background: false,
480
+ newInstance: false,
481
+ allowNonzeroExitCode: false,
482
+ ...options
483
+ };
484
+ const isFallbackAttempt = options[fallbackAttemptSymbol] === true;
485
+ delete options[fallbackAttemptSymbol];
486
+ if (Array.isArray(options.app)) {
487
+ return tryEachApp(options.app, (singleApp) => baseOpen({
488
+ ...options,
489
+ app: singleApp,
490
+ [fallbackAttemptSymbol]: true
491
+ }));
492
+ }
493
+ let { name: app, arguments: appArguments = [] } = options.app ?? {};
494
+ appArguments = [...appArguments];
495
+ if (Array.isArray(app)) {
496
+ return tryEachApp(app, (appName) => baseOpen({
497
+ ...options,
498
+ app: {
499
+ name: appName,
500
+ arguments: appArguments
501
+ },
502
+ [fallbackAttemptSymbol]: true
503
+ }));
504
+ }
505
+ if (app === "browser" || app === "browserPrivate") {
506
+ const ids = {
507
+ "com.google.chrome": "chrome",
508
+ "google-chrome.desktop": "chrome",
509
+ "com.brave.browser": "brave",
510
+ "org.mozilla.firefox": "firefox",
511
+ "firefox.desktop": "firefox",
512
+ "com.microsoft.msedge": "edge",
513
+ "com.microsoft.edge": "edge",
514
+ "com.microsoft.edgemac": "edge",
515
+ "microsoft-edge.desktop": "edge",
516
+ "com.apple.safari": "safari"
517
+ };
518
+ const flags = {
519
+ chrome: "--incognito",
520
+ brave: "--incognito",
521
+ firefox: "--private-window",
522
+ edge: "--inPrivate"
523
+ // Safari doesn't support private mode via command line
524
+ };
525
+ let browser;
526
+ if (is_wsl_default) {
527
+ const progId = await wslDefaultBrowser();
528
+ const browserInfo = _windowsBrowserProgIdMap.get(progId);
529
+ browser = browserInfo ?? {};
530
+ } else {
531
+ browser = await defaultBrowser2();
532
+ }
533
+ if (browser.id in ids) {
534
+ const browserName = ids[browser.id.toLowerCase()];
535
+ if (app === "browserPrivate") {
536
+ if (browserName === "safari") {
537
+ throw new Error("Safari doesn't support opening in private mode via command line");
538
+ }
539
+ appArguments.push(flags[browserName]);
540
+ }
541
+ return baseOpen({
542
+ ...options,
543
+ app: {
544
+ name: apps[browserName],
545
+ arguments: appArguments
546
+ }
547
+ });
548
+ }
549
+ throw new Error(`${browser.name} is not supported as a default browser`);
550
+ }
551
+ let command;
552
+ const cliArguments = [];
553
+ const childProcessOptions = {};
554
+ let shouldUseWindowsInWsl = false;
555
+ if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
556
+ shouldUseWindowsInWsl = await canAccessPowerShell();
557
+ }
558
+ if (platform2 === "darwin") {
559
+ command = "open";
560
+ if (options.wait) {
561
+ cliArguments.push("--wait-apps");
562
+ }
563
+ if (options.background) {
564
+ cliArguments.push("--background");
565
+ }
566
+ if (options.newInstance) {
567
+ cliArguments.push("--new");
568
+ }
569
+ if (app) {
570
+ cliArguments.push("-a", app);
571
+ }
572
+ } else if (platform2 === "win32" || shouldUseWindowsInWsl) {
573
+ command = await powerShellPath2();
574
+ cliArguments.push(...executePowerShell.argumentsPrefix);
575
+ if (!is_wsl_default) {
576
+ childProcessOptions.windowsVerbatimArguments = true;
577
+ }
578
+ if (is_wsl_default && options.target) {
579
+ options.target = await convertWslPathToWindows(options.target);
580
+ }
581
+ const encodedArguments = ["$ProgressPreference = 'SilentlyContinue';", "Start"];
582
+ if (options.wait) {
583
+ encodedArguments.push("-Wait");
584
+ }
585
+ if (app) {
586
+ encodedArguments.push(executePowerShell.escapeArgument(app));
587
+ if (options.target) {
588
+ appArguments.push(options.target);
589
+ }
590
+ } else if (options.target) {
591
+ encodedArguments.push(executePowerShell.escapeArgument(options.target));
592
+ }
593
+ if (appArguments.length > 0) {
594
+ appArguments = appArguments.map((argument) => executePowerShell.escapeArgument(argument));
595
+ encodedArguments.push("-ArgumentList", appArguments.join(","));
596
+ }
597
+ options.target = executePowerShell.encodeCommand(encodedArguments.join(" "));
598
+ if (!options.wait) {
599
+ childProcessOptions.stdio = "ignore";
600
+ }
601
+ } else {
602
+ if (app) {
603
+ command = app;
604
+ } else {
605
+ const isBundled = !__dirname || __dirname === "/";
606
+ let exeLocalXdgOpen = false;
607
+ try {
608
+ await fs6.access(localXdgOpenPath, fsConstants3.X_OK);
609
+ exeLocalXdgOpen = true;
610
+ } catch {
611
+ }
612
+ const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
613
+ command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
614
+ }
615
+ if (appArguments.length > 0) {
616
+ cliArguments.push(...appArguments);
617
+ }
618
+ if (!options.wait) {
619
+ childProcessOptions.stdio = "ignore";
620
+ childProcessOptions.detached = true;
621
+ }
622
+ }
623
+ if (platform2 === "darwin" && appArguments.length > 0) {
624
+ cliArguments.push("--args", ...appArguments);
625
+ }
626
+ if (options.target) {
627
+ cliArguments.push(options.target);
628
+ }
629
+ const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
630
+ if (options.wait) {
631
+ return new Promise((resolve, reject) => {
632
+ subprocess.once("error", reject);
633
+ subprocess.once("close", (exitCode) => {
634
+ if (!options.allowNonzeroExitCode && exitCode !== 0) {
635
+ reject(new Error(`Exited with code ${exitCode}`));
636
+ return;
637
+ }
638
+ resolve(subprocess);
639
+ });
640
+ });
641
+ }
642
+ if (isFallbackAttempt) {
643
+ return new Promise((resolve, reject) => {
644
+ subprocess.once("error", reject);
645
+ subprocess.once("spawn", () => {
646
+ subprocess.once("close", (exitCode) => {
647
+ subprocess.off("error", reject);
648
+ if (exitCode !== 0) {
649
+ reject(new Error(`Exited with code ${exitCode}`));
650
+ return;
651
+ }
652
+ subprocess.unref();
653
+ resolve(subprocess);
654
+ });
655
+ });
656
+ });
657
+ }
658
+ subprocess.unref();
659
+ return new Promise((resolve, reject) => {
660
+ subprocess.once("error", reject);
661
+ subprocess.once("spawn", () => {
662
+ subprocess.off("error", reject);
663
+ resolve(subprocess);
664
+ });
665
+ });
666
+ };
667
+ open = (target, options) => {
668
+ if (typeof target !== "string") {
669
+ throw new TypeError("Expected a `target`");
670
+ }
671
+ return baseOpen({
672
+ ...options,
673
+ target
674
+ });
675
+ };
676
+ openApp = (name, options) => {
677
+ if (typeof name !== "string" && !Array.isArray(name)) {
678
+ throw new TypeError("Expected a valid `name`");
679
+ }
680
+ const { arguments: appArguments = [] } = options ?? {};
681
+ if (appArguments !== void 0 && appArguments !== null && !Array.isArray(appArguments)) {
682
+ throw new TypeError("Expected `appArguments` as Array type");
683
+ }
684
+ return baseOpen({
685
+ ...options,
686
+ app: {
687
+ name,
688
+ arguments: appArguments
689
+ }
690
+ });
691
+ };
692
+ apps = {
693
+ browser: "browser",
694
+ browserPrivate: "browserPrivate"
695
+ };
696
+ defineLazyProperty(apps, "chrome", () => detectPlatformBinary({
697
+ darwin: "google chrome",
698
+ win32: "chrome",
699
+ // `chromium-browser` is the older deb package name used by Ubuntu/Debian before snap.
700
+ linux: ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]
701
+ }, {
702
+ wsl: {
703
+ ia32: "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe",
704
+ x64: ["/mnt/c/Program Files/Google/Chrome/Application/chrome.exe", "/mnt/c/Program Files (x86)/Google/Chrome/Application/chrome.exe"]
705
+ }
706
+ }));
707
+ defineLazyProperty(apps, "brave", () => detectPlatformBinary({
708
+ darwin: "brave browser",
709
+ win32: "brave",
710
+ linux: ["brave-browser", "brave"]
711
+ }, {
712
+ wsl: {
713
+ ia32: "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe",
714
+ x64: ["/mnt/c/Program Files/BraveSoftware/Brave-Browser/Application/brave.exe", "/mnt/c/Program Files (x86)/BraveSoftware/Brave-Browser/Application/brave.exe"]
715
+ }
716
+ }));
717
+ defineLazyProperty(apps, "firefox", () => detectPlatformBinary({
718
+ darwin: "firefox",
719
+ win32: String.raw`C:\Program Files\Mozilla Firefox\firefox.exe`,
720
+ linux: "firefox"
721
+ }, {
722
+ wsl: "/mnt/c/Program Files/Mozilla Firefox/firefox.exe"
723
+ }));
724
+ defineLazyProperty(apps, "edge", () => detectPlatformBinary({
725
+ darwin: "microsoft edge",
726
+ win32: "msedge",
727
+ linux: ["microsoft-edge", "microsoft-edge-dev"]
728
+ }, {
729
+ wsl: "/mnt/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe"
730
+ }));
731
+ defineLazyProperty(apps, "safari", () => detectPlatformBinary({
732
+ darwin: "Safari"
733
+ }));
734
+ open_default = open;
735
+ }
736
+ });
737
+
738
+ // src/index.ts
739
+ import { readFileSync as readFileSync2 } from "fs";
4
740
  import { readFile as readFile2 } from "fs/promises";
5
- import { extname } from "path";
741
+ import { dirname, extname, join as join2 } from "path";
742
+ import { fileURLToPath as fileURLToPath2 } from "url";
6
743
  import { config as loadEnv } from "dotenv";
7
744
  import { Command } from "commander";
8
745
  import { makeFunctionReference } from "convex/server";
@@ -12,7 +749,7 @@ import { anyApi, componentsGeneric } from "convex/server";
12
749
  var api = anyApi;
13
750
  var components = componentsGeneric();
14
751
 
15
- // ../../src/cli/auth.ts
752
+ // src/auth.ts
16
753
  import { isCancel, password as passwordPrompt, text } from "@clack/prompts";
17
754
  function buildUrl(appUrl, pathname) {
18
755
  return new URL(pathname, appUrl).toString();
@@ -52,6 +789,9 @@ function applySetCookieHeaders(session, response) {
52
789
  async function authRequest(session, appUrl, pathname, init = {}) {
53
790
  const headers = new Headers(init.headers);
54
791
  const origin = new URL(appUrl).origin;
792
+ if (session.bearerToken && !headers.has("authorization")) {
793
+ headers.set("authorization", `Bearer ${session.bearerToken}`);
794
+ }
55
795
  if (Object.keys(session.cookies).length > 0) {
56
796
  headers.set("cookie", cookieHeader(session.cookies));
57
797
  }
@@ -146,7 +886,7 @@ async function fetchAuthSession(session, appUrl) {
146
886
  const data = await response.json();
147
887
  return {
148
888
  session: nextSession,
149
- user: data.user ?? null
889
+ user: data?.user ?? null
150
890
  };
151
891
  }
152
892
  async function fetchConvexToken(session, appUrl) {
@@ -170,6 +910,65 @@ async function fetchConvexToken(session, appUrl) {
170
910
  token: data.token
171
911
  };
172
912
  }
913
+ async function requestDeviceCode(appUrl, clientId) {
914
+ const response = await fetch(buildUrl(appUrl, "/api/auth/device/code"), {
915
+ method: "POST",
916
+ headers: { "content-type": "application/json" },
917
+ body: JSON.stringify({ client_id: clientId })
918
+ });
919
+ if (!response.ok) {
920
+ throw new Error(`Failed to request device code: HTTP ${response.status}`);
921
+ }
922
+ return await response.json();
923
+ }
924
+ async function pollDeviceToken(session, appUrl, deviceCode, clientId, interval, expiresIn) {
925
+ const deadline = Date.now() + expiresIn * 1e3;
926
+ let pollInterval = interval * 1e3;
927
+ while (Date.now() < deadline) {
928
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
929
+ const { response, session: nextSession } = await authRequest(
930
+ session,
931
+ appUrl,
932
+ "/api/auth/device/token",
933
+ {
934
+ method: "POST",
935
+ body: JSON.stringify({
936
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
937
+ device_code: deviceCode,
938
+ client_id: clientId
939
+ })
940
+ }
941
+ );
942
+ session = nextSession;
943
+ if (response.ok) {
944
+ const data = await response.json();
945
+ if (data.access_token) {
946
+ session.bearerToken = data.access_token;
947
+ return session;
948
+ }
949
+ }
950
+ let errorData;
951
+ try {
952
+ errorData = await response.json();
953
+ } catch {
954
+ errorData = { error: `HTTP ${response.status}` };
955
+ }
956
+ switch (errorData.error) {
957
+ case "authorization_pending":
958
+ break;
959
+ case "slow_down":
960
+ pollInterval += 5e3;
961
+ break;
962
+ case "access_denied":
963
+ throw new Error("Authorization denied by user.");
964
+ case "expired_token":
965
+ throw new Error("Device code expired. Please try again.");
966
+ default:
967
+ throw new Error(`Device auth error: ${errorData.error}`);
968
+ }
969
+ }
970
+ throw new Error("Device code expired. Please try again.");
971
+ }
173
972
  async function prompt(question) {
174
973
  const value = await text({
175
974
  message: question.replace(/:\s*$/, "")
@@ -190,7 +989,7 @@ async function promptSecret(question) {
190
989
  return String(value);
191
990
  }
192
991
 
193
- // ../../src/cli/convex.ts
992
+ // src/convex.ts
194
993
  import { ConvexHttpClient } from "convex/browser";
195
994
  async function createConvexClient(session, appUrl, convexUrl) {
196
995
  const { token } = await fetchConvexToken(session, appUrl);
@@ -208,7 +1007,7 @@ async function runAction(client, ref, ...args) {
208
1007
  return await client.action(ref, ...args);
209
1008
  }
210
1009
 
211
- // ../../src/cli/output.ts
1010
+ // src/output.ts
212
1011
  function simplify(value) {
213
1012
  if (value === null || value === void 0) {
214
1013
  return value;
@@ -255,7 +1054,7 @@ function printOutput(data, json = false) {
255
1054
  console.log(String(data));
256
1055
  }
257
1056
 
258
- // ../../src/cli/session.ts
1057
+ // src/session.ts
259
1058
  import { mkdir, readFile, rm, writeFile } from "fs/promises";
260
1059
  import { homedir } from "os";
261
1060
  import path from "path";
@@ -295,7 +1094,487 @@ function createEmptySession() {
295
1094
  };
296
1095
  }
297
1096
 
298
- // ../../src/cli/index.ts
1097
+ // src/bridge-service.ts
1098
+ import { ConvexHttpClient as ConvexHttpClient2 } from "convex/browser";
1099
+ import { execSync } from "child_process";
1100
+ import {
1101
+ existsSync,
1102
+ mkdirSync,
1103
+ readFileSync,
1104
+ writeFileSync,
1105
+ unlinkSync
1106
+ } from "fs";
1107
+ import { homedir as homedir2, hostname, platform } from "os";
1108
+ import { join } from "path";
1109
+ import { randomUUID } from "crypto";
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");
1116
+ var LAUNCHAGENT_LABEL = "com.vector.bridge";
1117
+ var HEARTBEAT_INTERVAL_MS = 3e4;
1118
+ var COMMAND_POLL_INTERVAL_MS = 5e3;
1119
+ var PROCESS_DISCOVERY_INTERVAL_MS = 6e4;
1120
+ function loadBridgeConfig() {
1121
+ if (!existsSync(BRIDGE_CONFIG_FILE)) return null;
1122
+ try {
1123
+ return JSON.parse(readFileSync(BRIDGE_CONFIG_FILE, "utf-8"));
1124
+ } catch {
1125
+ return null;
1126
+ }
1127
+ }
1128
+ function saveBridgeConfig(config) {
1129
+ if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
1130
+ writeFileSync(BRIDGE_CONFIG_FILE, JSON.stringify(config, null, 2));
1131
+ }
1132
+ function discoverLocalProcesses() {
1133
+ const processes = [];
1134
+ const patterns = [
1135
+ {
1136
+ grep: "[c]laude",
1137
+ provider: "claude_code",
1138
+ label: "Claude",
1139
+ prefix: "claude"
1140
+ },
1141
+ { grep: "[c]odex", provider: "codex", label: "Codex", prefix: "codex" }
1142
+ ];
1143
+ for (const { grep, provider, label, prefix } of patterns) {
1144
+ try {
1145
+ const ps = execSync(
1146
+ `ps aux | grep -E '${grep}' | grep -v vector-bridge | grep -v grep`,
1147
+ { encoding: "utf-8", timeout: 5e3 }
1148
+ );
1149
+ for (const line of ps.trim().split("\n").filter(Boolean)) {
1150
+ const pid = line.split(/\s+/)[1];
1151
+ if (!pid) continue;
1152
+ let cwd;
1153
+ try {
1154
+ cwd = execSync(
1155
+ `lsof -p ${pid} 2>/dev/null | grep cwd | awk '{print $NF}'`,
1156
+ { encoding: "utf-8", timeout: 3e3 }
1157
+ ).trim() || void 0;
1158
+ } catch {
1159
+ }
1160
+ const gitInfo = cwd ? getGitInfo(cwd) : {};
1161
+ processes.push({
1162
+ provider,
1163
+ providerLabel: label,
1164
+ localProcessId: pid,
1165
+ sessionKey: `${prefix}-${pid}`,
1166
+ cwd,
1167
+ ...gitInfo,
1168
+ mode: "observed",
1169
+ status: "observed",
1170
+ supportsInboundMessages: false
1171
+ });
1172
+ }
1173
+ } catch {
1174
+ }
1175
+ }
1176
+ return processes;
1177
+ }
1178
+ function getGitInfo(cwd) {
1179
+ try {
1180
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
1181
+ encoding: "utf-8",
1182
+ cwd,
1183
+ timeout: 3e3
1184
+ }).trim();
1185
+ const repoRoot = execSync("git rev-parse --show-toplevel", {
1186
+ encoding: "utf-8",
1187
+ cwd,
1188
+ timeout: 3e3
1189
+ }).trim();
1190
+ return { branch, repoRoot };
1191
+ } catch {
1192
+ return {};
1193
+ }
1194
+ }
1195
+ function generateReply(userMessage) {
1196
+ const lower = userMessage.toLowerCase().trim();
1197
+ if (["hey", "hi", "hello"].includes(lower)) {
1198
+ return "Hey! I'm running on your local machine via the Vector bridge. What would you like me to work on?";
1199
+ }
1200
+ if (lower.includes("status") || lower.includes("progress")) {
1201
+ return "I'm making good progress. Currently reviewing the changes and running tests.";
1202
+ }
1203
+ if (lower.includes("stop") || lower.includes("cancel")) {
1204
+ return "Understood \u2014 wrapping up the current step.";
1205
+ }
1206
+ return `Got it \u2014 "${userMessage}". I'll incorporate that into my current work.`;
1207
+ }
1208
+ var BridgeService = class {
1209
+ constructor(config) {
1210
+ this.timers = [];
1211
+ this.config = config;
1212
+ this.client = new ConvexHttpClient2(config.convexUrl);
1213
+ }
1214
+ async heartbeat() {
1215
+ await this.client.mutation(api.agentBridge.bridgePublic.heartbeat, {
1216
+ deviceId: this.config.deviceId,
1217
+ deviceSecret: this.config.deviceSecret
1218
+ });
1219
+ }
1220
+ async pollCommands() {
1221
+ const commands = await this.client.query(
1222
+ api.agentBridge.bridgePublic.getPendingCommands,
1223
+ {
1224
+ deviceId: this.config.deviceId,
1225
+ deviceSecret: this.config.deviceSecret
1226
+ }
1227
+ );
1228
+ if (commands.length > 0) {
1229
+ console.log(`[${ts()}] ${commands.length} pending command(s)`);
1230
+ }
1231
+ for (const cmd of commands) {
1232
+ await this.handleCommand(cmd);
1233
+ }
1234
+ }
1235
+ async handleCommand(cmd) {
1236
+ console.log(` ${cmd.kind}: ${cmd._id}`);
1237
+ if (cmd.kind === "message" && cmd.liveActivityId) {
1238
+ const payload = cmd.payload;
1239
+ const body = payload?.body ?? "";
1240
+ console.log(` > "${body}"`);
1241
+ const reply = generateReply(body);
1242
+ await this.client.mutation(
1243
+ api.agentBridge.bridgePublic.postAgentMessage,
1244
+ {
1245
+ deviceId: this.config.deviceId,
1246
+ deviceSecret: this.config.deviceSecret,
1247
+ liveActivityId: cmd.liveActivityId,
1248
+ role: "assistant",
1249
+ body: reply
1250
+ }
1251
+ );
1252
+ console.log(` < "${reply.slice(0, 60)}..."`);
1253
+ }
1254
+ await this.client.mutation(api.agentBridge.bridgePublic.completeCommand, {
1255
+ deviceId: this.config.deviceId,
1256
+ deviceSecret: this.config.deviceSecret,
1257
+ commandId: cmd._id,
1258
+ status: "delivered"
1259
+ });
1260
+ }
1261
+ async reportProcesses() {
1262
+ const processes = discoverLocalProcesses();
1263
+ for (const proc of processes) {
1264
+ try {
1265
+ await this.client.mutation(api.agentBridge.bridgePublic.reportProcess, {
1266
+ deviceId: this.config.deviceId,
1267
+ deviceSecret: this.config.deviceSecret,
1268
+ ...proc
1269
+ });
1270
+ } catch {
1271
+ }
1272
+ }
1273
+ if (processes.length > 0) {
1274
+ console.log(`[${ts()}] Discovered ${processes.length} local process(es)`);
1275
+ }
1276
+ }
1277
+ async refreshLiveActivities() {
1278
+ try {
1279
+ const activities = await this.client.query(
1280
+ api.agentBridge.bridgePublic.getDeviceLiveActivities,
1281
+ {
1282
+ deviceId: this.config.deviceId,
1283
+ deviceSecret: this.config.deviceSecret
1284
+ }
1285
+ );
1286
+ writeFileSync(LIVE_ACTIVITIES_CACHE, JSON.stringify(activities, null, 2));
1287
+ } catch {
1288
+ }
1289
+ }
1290
+ async run() {
1291
+ console.log("Vector Bridge Service");
1292
+ console.log(
1293
+ ` Device: ${this.config.displayName} (${this.config.deviceId})`
1294
+ );
1295
+ console.log(` Convex: ${this.config.convexUrl}`);
1296
+ console.log(` PID: ${process.pid}`);
1297
+ console.log("");
1298
+ if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true });
1299
+ writeFileSync(PID_FILE, String(process.pid));
1300
+ await this.heartbeat();
1301
+ await this.reportProcesses();
1302
+ await this.refreshLiveActivities();
1303
+ console.log(`[${ts()}] Service running. Ctrl+C to stop.
1304
+ `);
1305
+ this.timers.push(
1306
+ setInterval(() => {
1307
+ this.heartbeat().catch(
1308
+ (e) => console.error(`[${ts()}] Heartbeat error:`, e.message)
1309
+ );
1310
+ this.refreshLiveActivities().catch(() => {
1311
+ });
1312
+ }, HEARTBEAT_INTERVAL_MS)
1313
+ );
1314
+ this.timers.push(
1315
+ setInterval(() => {
1316
+ this.pollCommands().catch(
1317
+ (e) => console.error(`[${ts()}] Command poll error:`, e.message)
1318
+ );
1319
+ }, COMMAND_POLL_INTERVAL_MS)
1320
+ );
1321
+ this.timers.push(
1322
+ setInterval(() => {
1323
+ this.reportProcesses().catch(
1324
+ (e) => console.error(`[${ts()}] Discovery error:`, e.message)
1325
+ );
1326
+ }, PROCESS_DISCOVERY_INTERVAL_MS)
1327
+ );
1328
+ const shutdown = () => {
1329
+ console.log(`
1330
+ [${ts()}] Shutting down...`);
1331
+ for (const t of this.timers) clearInterval(t);
1332
+ try {
1333
+ unlinkSync(PID_FILE);
1334
+ } catch {
1335
+ }
1336
+ process.exit(0);
1337
+ };
1338
+ process.on("SIGINT", shutdown);
1339
+ process.on("SIGTERM", shutdown);
1340
+ await new Promise(() => {
1341
+ });
1342
+ }
1343
+ };
1344
+ async function setupBridgeDevice(convexUrl, userId) {
1345
+ const client = new ConvexHttpClient2(convexUrl);
1346
+ const deviceKey = `${hostname()}-${randomUUID().slice(0, 8)}`;
1347
+ const deviceSecret = randomUUID();
1348
+ const displayName = `${process.env.USER ?? "user"}'s ${platform() === "darwin" ? "Mac" : "machine"}`;
1349
+ const result = await client.mutation(
1350
+ api.agentBridge.bridgePublic.setupDevice,
1351
+ {
1352
+ userId,
1353
+ deviceKey,
1354
+ deviceSecret,
1355
+ displayName,
1356
+ hostname: hostname(),
1357
+ platform: platform(),
1358
+ cliVersion: "0.1.0",
1359
+ capabilities: ["codex", "claude_code"]
1360
+ }
1361
+ );
1362
+ const config = {
1363
+ deviceId: result.deviceId,
1364
+ deviceKey,
1365
+ deviceSecret,
1366
+ userId,
1367
+ displayName,
1368
+ convexUrl,
1369
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString()
1370
+ };
1371
+ saveBridgeConfig(config);
1372
+ return config;
1373
+ }
1374
+ function installLaunchAgent(vcliPath) {
1375
+ if (platform() !== "darwin") {
1376
+ console.error("LaunchAgent is macOS only. Use systemd on Linux.");
1377
+ return;
1378
+ }
1379
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
1380
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1381
+ <plist version="1.0">
1382
+ <dict>
1383
+ <key>Label</key>
1384
+ <string>${LAUNCHAGENT_LABEL}</string>
1385
+ <key>ProgramArguments</key>
1386
+ <array>
1387
+ <string>${vcliPath}</string>
1388
+ <string>service</string>
1389
+ <string>run</string>
1390
+ </array>
1391
+ <key>RunAtLoad</key>
1392
+ <true/>
1393
+ <key>KeepAlive</key>
1394
+ <true/>
1395
+ <key>StandardOutPath</key>
1396
+ <string>${CONFIG_DIR}/bridge.log</string>
1397
+ <key>StandardErrorPath</key>
1398
+ <string>${CONFIG_DIR}/bridge.err.log</string>
1399
+ <key>EnvironmentVariables</key>
1400
+ <dict>
1401
+ <key>PATH</key>
1402
+ <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>
1403
+ </dict>
1404
+ </dict>
1405
+ </plist>`;
1406
+ const menuBarCandidates = [
1407
+ join(CONFIG_DIR, "VectorMenuBar"),
1408
+ "/usr/local/bin/VectorMenuBar",
1409
+ join(homedir2(), ".local", "bin", "VectorMenuBar")
1410
+ ];
1411
+ const menuBarBinary = menuBarCandidates.find((p) => existsSync(p));
1412
+ if (menuBarBinary) {
1413
+ const menuBarPlist = `<?xml version="1.0" encoding="UTF-8"?>
1414
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
1415
+ <plist version="1.0">
1416
+ <dict>
1417
+ <key>Label</key>
1418
+ <string>com.vector.menubar</string>
1419
+ <key>ProgramArguments</key>
1420
+ <array>
1421
+ <string>${menuBarBinary}</string>
1422
+ </array>
1423
+ <key>RunAtLoad</key>
1424
+ <true/>
1425
+ <key>KeepAlive</key>
1426
+ <false/>
1427
+ </dict>
1428
+ </plist>`;
1429
+ const menuBarPlistPath = join(LAUNCHAGENT_DIR, "com.vector.menubar.plist");
1430
+ writeFileSync(menuBarPlistPath, menuBarPlist);
1431
+ try {
1432
+ execSync(`launchctl load ${menuBarPlistPath}`, { stdio: "pipe" });
1433
+ console.log("Menu bar helper installed.");
1434
+ } catch {
1435
+ }
1436
+ }
1437
+ if (!existsSync(LAUNCHAGENT_DIR)) {
1438
+ mkdirSync(LAUNCHAGENT_DIR, { recursive: true });
1439
+ }
1440
+ writeFileSync(LAUNCHAGENT_PLIST, plist);
1441
+ console.log(`Installed LaunchAgent: ${LAUNCHAGENT_PLIST}`);
1442
+ }
1443
+ function loadLaunchAgent() {
1444
+ try {
1445
+ execSync(`launchctl load ${LAUNCHAGENT_PLIST}`, { stdio: "inherit" });
1446
+ console.log(
1447
+ "LaunchAgent loaded. Bridge will start automatically on login."
1448
+ );
1449
+ } catch {
1450
+ console.error("Failed to load LaunchAgent");
1451
+ }
1452
+ }
1453
+ function unloadLaunchAgent() {
1454
+ try {
1455
+ execSync(`launchctl unload ${LAUNCHAGENT_PLIST}`, { stdio: "inherit" });
1456
+ console.log("LaunchAgent unloaded.");
1457
+ } catch {
1458
+ console.error("Failed to unload LaunchAgent (may not be loaded)");
1459
+ }
1460
+ }
1461
+ function uninstallLaunchAgent() {
1462
+ unloadLaunchAgent();
1463
+ try {
1464
+ unlinkSync(LAUNCHAGENT_PLIST);
1465
+ console.log("LaunchAgent removed.");
1466
+ } catch {
1467
+ }
1468
+ }
1469
+ var MENUBAR_BINARY = join(CONFIG_DIR, "VectorMenuBar");
1470
+ var MENUBAR_SWIFT_URL = "https://raw.githubusercontent.com/xrehpicx/vector/main/cli/macos/VectorMenuBar.swift";
1471
+ async function ensureMenuBarBinary() {
1472
+ if (platform() !== "darwin") return null;
1473
+ if (existsSync(MENUBAR_BINARY)) return MENUBAR_BINARY;
1474
+ try {
1475
+ execSync("which swiftc", { stdio: "pipe" });
1476
+ } catch {
1477
+ return null;
1478
+ }
1479
+ const localSource = join(
1480
+ process.cwd(),
1481
+ "cli",
1482
+ "macos",
1483
+ "VectorMenuBar.swift"
1484
+ );
1485
+ let swiftSource;
1486
+ if (existsSync(localSource)) {
1487
+ swiftSource = localSource;
1488
+ } else {
1489
+ const downloadPath = join(CONFIG_DIR, "VectorMenuBar.swift");
1490
+ try {
1491
+ execSync(`curl -fsSL "${MENUBAR_SWIFT_URL}" -o "${downloadPath}"`, {
1492
+ stdio: "pipe",
1493
+ timeout: 15e3
1494
+ });
1495
+ swiftSource = downloadPath;
1496
+ } catch {
1497
+ return null;
1498
+ }
1499
+ }
1500
+ try {
1501
+ execSync(
1502
+ `swiftc -o "${MENUBAR_BINARY}" "${swiftSource}" -framework AppKit`,
1503
+ { stdio: "pipe", timeout: 3e4 }
1504
+ );
1505
+ const assetsSource = join(swiftSource, "..", "assets");
1506
+ const assetsDest = join(CONFIG_DIR, "assets");
1507
+ if (existsSync(assetsSource)) {
1508
+ mkdirSync(assetsDest, { recursive: true });
1509
+ for (const f of ["vector-menubar.png", "vector-menubar@2x.png"]) {
1510
+ const src = join(assetsSource, f);
1511
+ if (existsSync(src))
1512
+ writeFileSync(join(assetsDest, f), readFileSync(src));
1513
+ }
1514
+ }
1515
+ return MENUBAR_BINARY;
1516
+ } catch {
1517
+ return null;
1518
+ }
1519
+ }
1520
+ async function launchMenuBar() {
1521
+ if (platform() !== "darwin") return;
1522
+ const binary = await ensureMenuBarBinary();
1523
+ if (!binary) return;
1524
+ try {
1525
+ const { spawn: spawnChild } = await import("child_process");
1526
+ const child = spawnChild(binary, [], { detached: true, stdio: "ignore" });
1527
+ child.unref();
1528
+ console.log("Menu bar started.");
1529
+ } catch {
1530
+ }
1531
+ }
1532
+ function getBridgeStatus() {
1533
+ const config = loadBridgeConfig();
1534
+ if (!config) return { configured: false, running: false, starting: false };
1535
+ let running = false;
1536
+ let starting = false;
1537
+ let pid;
1538
+ if (existsSync(PID_FILE)) {
1539
+ const pidStr = readFileSync(PID_FILE, "utf-8").trim();
1540
+ pid = Number(pidStr);
1541
+ try {
1542
+ process.kill(pid, 0);
1543
+ running = true;
1544
+ } catch {
1545
+ running = false;
1546
+ }
1547
+ }
1548
+ if (!running && platform() === "darwin") {
1549
+ try {
1550
+ const result = execSync(
1551
+ `launchctl list ${LAUNCHAGENT_LABEL} 2>/dev/null`,
1552
+ { encoding: "utf-8", timeout: 3e3 }
1553
+ );
1554
+ if (result.includes(LAUNCHAGENT_LABEL)) {
1555
+ starting = true;
1556
+ }
1557
+ } catch {
1558
+ }
1559
+ }
1560
+ return { configured: true, running, starting, pid, config };
1561
+ }
1562
+ function stopBridge() {
1563
+ if (!existsSync(PID_FILE)) return false;
1564
+ const pid = Number(readFileSync(PID_FILE, "utf-8").trim());
1565
+ try {
1566
+ process.kill(pid, "SIGTERM");
1567
+ return true;
1568
+ } catch {
1569
+ return false;
1570
+ }
1571
+ }
1572
+ function ts() {
1573
+ return (/* @__PURE__ */ new Date()).toLocaleTimeString();
1574
+ }
1575
+
1576
+ // src/index.ts
1577
+ import { platform as osPlatform } from "os";
299
1578
  loadEnv({ path: ".env.local", override: false });
300
1579
  loadEnv({ path: ".env", override: false });
301
1580
  var cliApi = {
@@ -404,13 +1683,128 @@ function buildPaginationOptions(limit, cursor) {
404
1683
  function normalizeMatch(value) {
405
1684
  return value?.trim().toLowerCase();
406
1685
  }
1686
+ function parseDate(value) {
1687
+ const ms = Date.parse(value);
1688
+ if (!Number.isFinite(ms)) {
1689
+ throw new Error(`Invalid date: ${value}`);
1690
+ }
1691
+ return ms;
1692
+ }
1693
+ function applyListFilters(items, options) {
1694
+ let result = [...items];
1695
+ if (options.createdAfter) {
1696
+ const threshold = parseDate(options.createdAfter);
1697
+ result = result.filter(
1698
+ (item) => typeof item.createdAt === "number" && item.createdAt >= threshold
1699
+ );
1700
+ }
1701
+ if (options.createdBefore) {
1702
+ const threshold = parseDate(options.createdBefore);
1703
+ result = result.filter(
1704
+ (item) => typeof item.createdAt === "number" && item.createdAt <= threshold
1705
+ );
1706
+ }
1707
+ if (options.updatedAfter) {
1708
+ const threshold = parseDate(options.updatedAfter);
1709
+ result = result.filter(
1710
+ (item) => typeof item.lastEditedAt === "number" && item.lastEditedAt >= threshold
1711
+ );
1712
+ }
1713
+ if (options.updatedBefore) {
1714
+ const threshold = parseDate(options.updatedBefore);
1715
+ result = result.filter(
1716
+ (item) => typeof item.lastEditedAt === "number" && item.lastEditedAt <= threshold
1717
+ );
1718
+ }
1719
+ if (options.sort) {
1720
+ const field = options.sort;
1721
+ const desc = options.order?.toLowerCase() === "desc";
1722
+ result.sort((a, b) => {
1723
+ const aVal = a[field];
1724
+ const bVal = b[field];
1725
+ if (aVal == null && bVal == null) return 0;
1726
+ if (aVal == null) return 1;
1727
+ if (bVal == null) return -1;
1728
+ if (typeof aVal === "number" && typeof bVal === "number")
1729
+ return desc ? bVal - aVal : aVal - bVal;
1730
+ return desc ? String(bVal).localeCompare(String(aVal)) : String(aVal).localeCompare(String(bVal));
1731
+ });
1732
+ }
1733
+ if (options.limit) {
1734
+ const limit = Number(options.limit);
1735
+ if (Number.isFinite(limit) && limit > 0) {
1736
+ result = result.slice(0, limit);
1737
+ }
1738
+ }
1739
+ return result;
1740
+ }
1741
+ function addEntityUrls(items, appUrl, orgSlug, entityType) {
1742
+ return items.map((item) => {
1743
+ let path3;
1744
+ switch (entityType) {
1745
+ case "issues":
1746
+ path3 = `/${orgSlug}/issues/${item.key}`;
1747
+ break;
1748
+ case "projects":
1749
+ path3 = `/${orgSlug}/projects/${item.key}`;
1750
+ break;
1751
+ case "teams":
1752
+ path3 = `/${orgSlug}/teams/${item.key}`;
1753
+ break;
1754
+ case "documents":
1755
+ path3 = `/${orgSlug}/documents/${item.id}`;
1756
+ break;
1757
+ case "folders":
1758
+ path3 = `/${orgSlug}/documents/folders/${item.id}`;
1759
+ break;
1760
+ }
1761
+ return { ...item, url: `${appUrl}${path3}` };
1762
+ });
1763
+ }
1764
+ function normalizeAppUrl(raw) {
1765
+ let url = raw.trim();
1766
+ if (!/^https?:\/\//i.test(url)) {
1767
+ const isLocal = /^localhost(:\d+)?/i.test(url) || /^127\.0\.0\.1(:\d+)?/.test(url);
1768
+ url = isLocal ? `http://${url}` : `https://${url}`;
1769
+ }
1770
+ return url.replace(/\/+$/, "");
1771
+ }
1772
+ async function resolveAppUrl(raw) {
1773
+ const url = normalizeAppUrl(raw);
1774
+ try {
1775
+ const response = await fetch(url, { method: "HEAD", redirect: "follow" });
1776
+ const resolved = new URL(response.url).origin;
1777
+ return resolved;
1778
+ } catch {
1779
+ return url;
1780
+ }
1781
+ }
1782
+ async function fetchConvexUrl(appUrl) {
1783
+ try {
1784
+ const url = new URL("/api/config", appUrl).toString();
1785
+ const response = await fetch(url);
1786
+ if (!response.ok) {
1787
+ throw new Error(`HTTP ${response.status}`);
1788
+ }
1789
+ const data = await response.json();
1790
+ if (data.convexUrl) {
1791
+ return data.convexUrl;
1792
+ }
1793
+ } catch {
1794
+ }
1795
+ return "http://127.0.0.1:3210";
1796
+ }
407
1797
  async function getRuntime(command) {
408
1798
  const options = command.optsWithGlobals();
409
1799
  const profile = options.profile ?? "default";
410
1800
  const session = await readSession(profile);
411
1801
  const appUrlSource = options.appUrl ?? session?.appUrl ?? process.env.NEXT_PUBLIC_APP_URL;
412
- const appUrl = requiredString(appUrlSource, "app URL");
413
- const convexUrl = options.convexUrl ?? session?.convexUrl ?? process.env.NEXT_PUBLIC_CONVEX_URL ?? process.env.CONVEX_URL ?? "http://127.0.0.1:3210";
1802
+ const appUrl = await resolveAppUrl(requiredString(appUrlSource, "app URL"));
1803
+ let convexUrl = options.convexUrl ?? session?.convexUrl;
1804
+ if (!convexUrl) {
1805
+ const fetchedUrl = await fetchConvexUrl(appUrl);
1806
+ convexUrl = fetchedUrl !== "http://127.0.0.1:3210" ? fetchedUrl : process.env.NEXT_PUBLIC_CONVEX_URL ?? process.env.CONVEX_URL ?? fetchedUrl;
1807
+ }
414
1808
  return {
415
1809
  appUrl,
416
1810
  convexUrl,
@@ -421,7 +1815,7 @@ async function getRuntime(command) {
421
1815
  };
422
1816
  }
423
1817
  function requireSession(runtime) {
424
- if (!runtime.session || Object.keys(runtime.session.cookies).length === 0) {
1818
+ if (!runtime.session || Object.keys(runtime.session.cookies).length === 0 && !runtime.session.bearerToken) {
425
1819
  throw new Error("Not logged in. Run `vcli auth login` first.");
426
1820
  }
427
1821
  return runtime.session;
@@ -620,7 +2014,16 @@ async function parseEstimatedTimes(client, orgSlug, value) {
620
2014
  return estimatedTimes;
621
2015
  }
622
2016
  var program = new Command();
623
- program.name("vcli").description("Vector CLI").showHelpAfterError().option(
2017
+ function readPackageVersionSync() {
2018
+ try {
2019
+ const dir = import.meta.dirname ?? dirname(fileURLToPath2(import.meta.url));
2020
+ const raw = readFileSync2(join2(dir, "..", "package.json"), "utf8");
2021
+ return JSON.parse(raw).version ?? "unknown";
2022
+ } catch {
2023
+ return "unknown";
2024
+ }
2025
+ }
2026
+ program.name("vcli").description("Vector CLI").version(readPackageVersionSync(), "-v, --version").showHelpAfterError().option(
624
2027
  "--app-url <url>",
625
2028
  "Vector app URL. Required unless saved in the profile or NEXT_PUBLIC_APP_URL is set."
626
2029
  ).option("--convex-url <url>", "Convex deployment URL").option("--org <slug>", "Organization slug override").option("--profile <name>", "CLI profile name", "default").option("--json", "Output JSON");
@@ -664,19 +2067,48 @@ authCommand.command("signup").option("--email <email>", "Email address").option(
664
2067
  runtime.json
665
2068
  );
666
2069
  });
667
- authCommand.command("login [identifier]").option("--password <password>", "Password").action(async (identifier, options, command) => {
2070
+ authCommand.command("login [identifier]").option("--password <password>", "Password (uses device flow if omitted)").action(async (identifier, options, command) => {
668
2071
  const runtime = await getRuntime(command);
669
- const loginId = identifier?.trim() || await prompt("Email or username: ");
670
- const password = options.password?.trim() || await promptSecret("Password: ");
671
2072
  let session = createEmptySession();
672
2073
  session.appUrl = runtime.appUrl;
673
2074
  session.convexUrl = runtime.convexUrl;
674
- session = await loginWithPassword(
675
- session,
676
- runtime.appUrl,
677
- loginId,
678
- password
679
- );
2075
+ const usePassword = Boolean(identifier || options.password);
2076
+ if (usePassword) {
2077
+ const loginId = identifier?.trim() || await prompt("Email or username: ");
2078
+ const password = options.password?.trim() || await promptSecret("Password: ");
2079
+ session = await loginWithPassword(
2080
+ session,
2081
+ runtime.appUrl,
2082
+ loginId,
2083
+ password
2084
+ );
2085
+ } else {
2086
+ const deviceResp = await requestDeviceCode(runtime.appUrl, "vcli");
2087
+ const verifyUrl = `${runtime.appUrl}/device?user_code=${deviceResp.user_code}`;
2088
+ console.log();
2089
+ console.log(` Open this URL in your browser to log in:`);
2090
+ console.log();
2091
+ console.log(` ${verifyUrl}`);
2092
+ console.log();
2093
+ console.log(` Or go to ${runtime.appUrl}/device and enter code:`);
2094
+ console.log();
2095
+ console.log(` ${deviceResp.user_code}`);
2096
+ console.log();
2097
+ const open2 = await Promise.resolve().then(() => (init_open(), open_exports)).then((m) => m.default).catch(() => null);
2098
+ if (open2) {
2099
+ await open2(verifyUrl).catch(() => {
2100
+ });
2101
+ }
2102
+ console.log(" Waiting for authorization...");
2103
+ session = await pollDeviceToken(
2104
+ session,
2105
+ runtime.appUrl,
2106
+ deviceResp.device_code,
2107
+ "vcli",
2108
+ deviceResp.interval,
2109
+ deviceResp.expires_in
2110
+ );
2111
+ }
680
2112
  const authState = await fetchAuthSession(session, runtime.appUrl);
681
2113
  session = authState.session;
682
2114
  const client = await createConvexClient(
@@ -1047,6 +2479,75 @@ permissionCommand.command("check-many <permissions>").option("--team <teamKey>")
1047
2479
  printOutput(result, runtime.json);
1048
2480
  });
1049
2481
  var activityCommand = program.command("activity").description("Activity feed");
2482
+ activityCommand.command("list").description(
2483
+ "List org-wide activity with optional filters by entity type, event type, and time range"
2484
+ ).option(
2485
+ "--entity-type <type>",
2486
+ "Filter by entity type: issue, project, team, document"
2487
+ ).option(
2488
+ "--event-type <type>",
2489
+ "Filter by event type (e.g. issue_created, issue_priority_changed)"
2490
+ ).option(
2491
+ "--since <datetime>",
2492
+ "Start of time range (ISO date or shorthand: today, yesterday, 7d, 30d)"
2493
+ ).option(
2494
+ "--until <datetime>",
2495
+ "End of time range (ISO date or shorthand: today, now)"
2496
+ ).option("--limit <n>").option("--cursor <cursor>").action(async (options, command) => {
2497
+ const { client, runtime } = await getClient(command);
2498
+ const orgSlug = requireOrg(runtime);
2499
+ function parseTimeArg(value, bound) {
2500
+ if (!value) return void 0;
2501
+ const now = /* @__PURE__ */ new Date();
2502
+ const startOfToday = new Date(
2503
+ now.getFullYear(),
2504
+ now.getMonth(),
2505
+ now.getDate()
2506
+ );
2507
+ const endOfToday = new Date(
2508
+ now.getFullYear(),
2509
+ now.getMonth(),
2510
+ now.getDate(),
2511
+ 23,
2512
+ 59,
2513
+ 59,
2514
+ 999
2515
+ );
2516
+ switch (value) {
2517
+ case "now":
2518
+ return now.getTime();
2519
+ case "today":
2520
+ return bound === "start" ? startOfToday.getTime() : endOfToday.getTime();
2521
+ case "yesterday":
2522
+ return bound === "start" ? startOfToday.getTime() - 864e5 : startOfToday.getTime() - 1;
2523
+ default: {
2524
+ const daysMatch = value.match(/^(\d+)d$/);
2525
+ if (daysMatch) {
2526
+ return now.getTime() - Number(daysMatch[1]) * 864e5;
2527
+ }
2528
+ const parsed = new Date(value).getTime();
2529
+ if (Number.isNaN(parsed)) {
2530
+ throw new Error(`Invalid time value: ${value}`);
2531
+ }
2532
+ return parsed;
2533
+ }
2534
+ }
2535
+ }
2536
+ const result = await runQuery(
2537
+ client,
2538
+ api.activities.queries.listOrgActivity,
2539
+ {
2540
+ orgSlug,
2541
+ entityType: options.entityType ?? void 0,
2542
+ eventType: options.eventType ?? void 0,
2543
+ since: parseTimeArg(options.since, "start"),
2544
+ until: parseTimeArg(options.until, "end"),
2545
+ limit: optionalNumber(options.limit, "limit") ?? void 0,
2546
+ cursor: options.cursor ?? void 0
2547
+ }
2548
+ );
2549
+ printOutput(result, runtime.json);
2550
+ });
1050
2551
  activityCommand.command("project <projectKey>").option("--limit <n>").option("--cursor <cursor>").action(async (projectKey, options, command) => {
1051
2552
  const { client, runtime } = await getClient(command);
1052
2553
  const orgSlug = requireOrg(runtime);
@@ -1503,13 +3004,14 @@ adminCommand.command("sync-disposable-domains").action(async (_options, command)
1503
3004
  printOutput(result, runtime.json);
1504
3005
  });
1505
3006
  var teamCommand = program.command("team").description("Teams");
1506
- teamCommand.command("list [slug]").option("--limit <n>").action(async (slug, options, command) => {
3007
+ teamCommand.command("list [slug]").option("--limit <n>").option("--created-after <date>", "Filter: created on or after date (ISO)").option("--created-before <date>", "Filter: created on or before date (ISO)").option("--sort <field>", "Sort by field (e.g. createdAt, name, key)").option("--order <direction>", "Sort order: asc or desc (default: asc)").action(async (slug, options, command) => {
1507
3008
  const { client, runtime } = await getClient(command);
1508
3009
  const orgSlug = requireOrg(runtime, slug);
1509
- const result = await runAction(client, cliApi.listTeams, {
1510
- orgSlug,
1511
- limit: options.limit ? Number(options.limit) : void 0
3010
+ const raw = await runAction(client, cliApi.listTeams, {
3011
+ orgSlug
1512
3012
  });
3013
+ const filtered = applyListFilters(raw, options);
3014
+ const result = addEntityUrls(filtered, runtime.appUrl, orgSlug, "teams");
1513
3015
  printOutput(result, runtime.json);
1514
3016
  });
1515
3017
  teamCommand.command("get <teamKey>").action(async (teamKey, _options, command) => {
@@ -1603,14 +3105,15 @@ teamCommand.command("set-lead <teamKey> <member>").action(async (teamKey, member
1603
3105
  printOutput(result, runtime.json);
1604
3106
  });
1605
3107
  var projectCommand = program.command("project").description("Projects");
1606
- projectCommand.command("list [slug]").option("--team <teamKey>").option("--limit <n>").action(async (slug, options, command) => {
3108
+ projectCommand.command("list [slug]").option("--team <teamKey>").option("--limit <n>").option("--created-after <date>", "Filter: created on or after date (ISO)").option("--created-before <date>", "Filter: created on or before date (ISO)").option("--sort <field>", "Sort by field (e.g. createdAt, name, key)").option("--order <direction>", "Sort order: asc or desc (default: asc)").action(async (slug, options, command) => {
1607
3109
  const { client, runtime } = await getClient(command);
1608
3110
  const orgSlug = requireOrg(runtime, slug);
1609
- const result = await runAction(client, cliApi.listProjects, {
3111
+ const raw = await runAction(client, cliApi.listProjects, {
1610
3112
  orgSlug,
1611
- teamKey: options.team,
1612
- limit: options.limit ? Number(options.limit) : void 0
3113
+ teamKey: options.team
1613
3114
  });
3115
+ const filtered = applyListFilters(raw, options);
3116
+ const result = addEntityUrls(filtered, runtime.appUrl, orgSlug, "projects");
1614
3117
  printOutput(result, runtime.json);
1615
3118
  });
1616
3119
  projectCommand.command("get <projectKey>").action(async (projectKey, _options, command) => {
@@ -1710,15 +3213,17 @@ projectCommand.command("set-lead <projectKey> <member>").action(async (projectKe
1710
3213
  printOutput(result, runtime.json);
1711
3214
  });
1712
3215
  var issueCommand = program.command("issue").description("Issues");
1713
- issueCommand.command("list [slug]").option("--project <projectKey>").option("--team <teamKey>").option("--limit <n>").action(async (slug, options, command) => {
3216
+ issueCommand.command("list [slug]").option("--project <projectKey>").option("--team <teamKey>").option("--assignee <name>", "Filter by assignee name or email").option("--limit <n>").option("--created-after <date>", "Filter: created on or after date (ISO)").option("--created-before <date>", "Filter: created on or before date (ISO)").option("--sort <field>", "Sort by field (e.g. createdAt, title, key)").option("--order <direction>", "Sort order: asc or desc (default: asc)").action(async (slug, options, command) => {
1714
3217
  const { client, runtime } = await getClient(command);
1715
3218
  const orgSlug = requireOrg(runtime, slug);
1716
- const result = await runAction(client, cliApi.listIssues, {
3219
+ const raw = await runAction(client, cliApi.listIssues, {
1717
3220
  orgSlug,
1718
3221
  projectKey: options.project,
1719
3222
  teamKey: options.team,
1720
- limit: options.limit ? Number(options.limit) : void 0
3223
+ assigneeName: options.assignee
1721
3224
  });
3225
+ const filtered = applyListFilters(raw, options);
3226
+ const result = addEntityUrls(filtered, runtime.appUrl, orgSlug, "issues");
1722
3227
  printOutput(result, runtime.json);
1723
3228
  });
1724
3229
  issueCommand.command("get <issueKey>").action(async (issueKey, _options, command) => {
@@ -1915,15 +3420,40 @@ issueCommand.command("comment <issueKey>").requiredOption("--body <body>").actio
1915
3420
  });
1916
3421
  printOutput(result, runtime.json);
1917
3422
  });
3423
+ issueCommand.command("link-github <issueKey> <url>").description("Link a GitHub pull request, issue, or commit URL to an issue").action(async (issueKey, url, _options, command) => {
3424
+ const { client, runtime } = await getClient(command);
3425
+ const orgSlug = requireOrg(runtime);
3426
+ await runAction(client, api.github.actions.linkArtifactByUrl, {
3427
+ orgSlug,
3428
+ issueKey,
3429
+ url
3430
+ });
3431
+ printOutput({ success: true, issueKey, url }, runtime.json);
3432
+ });
1918
3433
  var documentCommand = program.command("document").description("Documents");
1919
- documentCommand.command("list [slug]").option("--folder-id <id>").option("--limit <n>").action(async (slug, options, command) => {
3434
+ documentCommand.command("list [slug]").option("--folder-id <id>").option("--limit <n>").option("--created-after <date>", "Filter: created on or after date (ISO)").option("--created-before <date>", "Filter: created on or before date (ISO)").option(
3435
+ "--updated-after <date>",
3436
+ "Filter: last edited on or after date (ISO)"
3437
+ ).option(
3438
+ "--updated-before <date>",
3439
+ "Filter: last edited on or before date (ISO)"
3440
+ ).option(
3441
+ "--sort <field>",
3442
+ "Sort by field (e.g. createdAt, title, lastEditedAt)"
3443
+ ).option("--order <direction>", "Sort order: asc or desc (default: asc)").action(async (slug, options, command) => {
1920
3444
  const { client, runtime } = await getClient(command);
1921
3445
  const orgSlug = requireOrg(runtime, slug);
1922
- const result = await runAction(client, cliApi.listDocuments, {
3446
+ const raw = await runAction(client, cliApi.listDocuments, {
1923
3447
  orgSlug,
1924
- folderId: options.folderId,
1925
- limit: options.limit ? Number(options.limit) : void 0
3448
+ folderId: options.folderId
1926
3449
  });
3450
+ const filtered = applyListFilters(raw, options);
3451
+ const result = addEntityUrls(
3452
+ filtered,
3453
+ runtime.appUrl,
3454
+ orgSlug,
3455
+ "documents"
3456
+ );
1927
3457
  printOutput(result, runtime.json);
1928
3458
  });
1929
3459
  documentCommand.command("get <documentId>").action(async (documentId, _options, command) => {
@@ -1989,10 +3519,12 @@ documentCommand.command("delete <documentId>").action(async (documentId, _option
1989
3519
  printOutput(result, runtime.json);
1990
3520
  });
1991
3521
  var folderCommand = program.command("folder").description("Document folders");
1992
- folderCommand.command("list [slug]").action(async (slug, _options, command) => {
3522
+ folderCommand.command("list [slug]").option("--limit <n>").option("--created-after <date>", "Filter: created on or after date (ISO)").option("--created-before <date>", "Filter: created on or before date (ISO)").option("--sort <field>", "Sort by field (e.g. createdAt, name)").option("--order <direction>", "Sort order: asc or desc (default: asc)").action(async (slug, options, command) => {
1993
3523
  const { client, runtime } = await getClient(command);
1994
3524
  const orgSlug = requireOrg(runtime, slug);
1995
- const result = await runAction(client, cliApi.listFolders, { orgSlug });
3525
+ const raw = await runAction(client, cliApi.listFolders, { orgSlug });
3526
+ const filtered = applyListFilters(raw, options);
3527
+ const result = addEntityUrls(filtered, runtime.appUrl, orgSlug, "folders");
1996
3528
  printOutput(result, runtime.json);
1997
3529
  });
1998
3530
  folderCommand.command("create").requiredOption("--name <name>").option("--description <description>").option("--icon <icon>").option("--color <color>").action(async (options, command) => {
@@ -2032,6 +3564,216 @@ folderCommand.command("delete <folderId>").action(async (folderId, _options, com
2032
3564
  });
2033
3565
  printOutput(result, runtime.json);
2034
3566
  });
3567
+ var serviceCommand = program.command("service").description("Manage the local bridge service");
3568
+ serviceCommand.command("start").description("Start the bridge service via LaunchAgent (macOS) or foreground").action(async (_options, command) => {
3569
+ const existing = getBridgeStatus();
3570
+ if (existing.running) {
3571
+ console.log(`Bridge is already running (PID ${existing.pid}).`);
3572
+ return;
3573
+ }
3574
+ const { spinner } = await import("@clack/prompts");
3575
+ const s = spinner();
3576
+ let config = loadBridgeConfig();
3577
+ if (!config) {
3578
+ s.start("Registering device...");
3579
+ const runtime = await getRuntime(command);
3580
+ const session = requireSession(runtime);
3581
+ const client = await createConvexClient(
3582
+ session,
3583
+ runtime.appUrl,
3584
+ runtime.convexUrl
3585
+ );
3586
+ const user = await runQuery(client, api.users.currentUser);
3587
+ if (!user) {
3588
+ s.stop("Failed");
3589
+ throw new Error("Not logged in. Run `vcli auth login` first.");
3590
+ }
3591
+ config = await setupBridgeDevice(runtime.convexUrl, user._id);
3592
+ s.stop(`Device registered: ${config.displayName}`);
3593
+ }
3594
+ if (osPlatform() === "darwin") {
3595
+ s.start("Starting bridge service...");
3596
+ const vcliPath = process.argv[1] ?? "vcli";
3597
+ installLaunchAgent(vcliPath);
3598
+ loadLaunchAgent();
3599
+ await launchMenuBar();
3600
+ s.stop("Bridge service started.");
3601
+ } else {
3602
+ console.log(
3603
+ "Starting bridge in foreground (use systemd for background)..."
3604
+ );
3605
+ const bridge = new BridgeService(config);
3606
+ await bridge.run();
3607
+ }
3608
+ });
3609
+ serviceCommand.command("run").description("Run the bridge service in the foreground (used by LaunchAgent)").action(async (_options, command) => {
3610
+ let config = loadBridgeConfig();
3611
+ if (!config) {
3612
+ const runtime = await getRuntime(command);
3613
+ const session = requireSession(runtime);
3614
+ const client = await createConvexClient(
3615
+ session,
3616
+ runtime.appUrl,
3617
+ runtime.convexUrl
3618
+ );
3619
+ const user = await runQuery(client, api.users.currentUser);
3620
+ if (!user) throw new Error("Not logged in. Run `vcli auth login` first.");
3621
+ config = await setupBridgeDevice(runtime.convexUrl, user._id);
3622
+ }
3623
+ const bridge = new BridgeService(config);
3624
+ await bridge.run();
3625
+ });
3626
+ serviceCommand.command("stop").description("Stop the bridge service and menu bar").action(() => {
3627
+ if (osPlatform() === "darwin") {
3628
+ unloadLaunchAgent();
3629
+ }
3630
+ if (stopBridge()) {
3631
+ console.log("Bridge stopped.");
3632
+ } else if (osPlatform() !== "darwin") {
3633
+ console.log("Bridge is not running.");
3634
+ }
3635
+ });
3636
+ serviceCommand.command("status").description("Show bridge service status").action((_options, command) => {
3637
+ const status = getBridgeStatus();
3638
+ if (!status.configured) {
3639
+ console.log("Bridge not configured. Run: vcli service start");
3640
+ return;
3641
+ }
3642
+ console.log("Vector Bridge");
3643
+ console.log(
3644
+ ` Device: ${status.config.displayName} (${status.config.deviceId})`
3645
+ );
3646
+ console.log(` User: ${status.config.userId}`);
3647
+ const statusLabel = status.running ? `Running (PID ${status.pid})` : status.starting ? "Starting..." : "Not running";
3648
+ console.log(` Status: ${statusLabel}`);
3649
+ console.log(` Config: ~/.vector/bridge.json`);
3650
+ });
3651
+ serviceCommand.command("install").description("Install the bridge as a system service (macOS LaunchAgent)").action(async (_options, command) => {
3652
+ if (osPlatform() !== "darwin") {
3653
+ console.error("Service install is currently macOS only (LaunchAgent).");
3654
+ console.error("On Linux, use systemd --user manually for now.");
3655
+ return;
3656
+ }
3657
+ const { spinner } = await import("@clack/prompts");
3658
+ const s = spinner();
3659
+ let config = loadBridgeConfig();
3660
+ if (!config) {
3661
+ s.start("Registering device...");
3662
+ const runtime = await getRuntime(command);
3663
+ const session = requireSession(runtime);
3664
+ const client = await createConvexClient(
3665
+ session,
3666
+ runtime.appUrl,
3667
+ runtime.convexUrl
3668
+ );
3669
+ const user = await runQuery(client, api.users.currentUser);
3670
+ if (!user) {
3671
+ s.stop("Failed");
3672
+ throw new Error("Not logged in. Run `vcli auth login` first.");
3673
+ }
3674
+ config = await setupBridgeDevice(runtime.convexUrl, user._id);
3675
+ s.stop(`Device registered: ${config.displayName}`);
3676
+ }
3677
+ s.start("Installing LaunchAgent...");
3678
+ const vcliPath = process.argv[1] ?? "vcli";
3679
+ installLaunchAgent(vcliPath);
3680
+ s.stop("LaunchAgent installed");
3681
+ s.start("Starting bridge service...");
3682
+ loadLaunchAgent();
3683
+ s.stop("Bridge service started");
3684
+ s.start("Launching menu bar...");
3685
+ await launchMenuBar();
3686
+ s.stop("Menu bar ready");
3687
+ console.log("");
3688
+ console.log(
3689
+ "Bridge installed and running. Will start automatically on login."
3690
+ );
3691
+ });
3692
+ serviceCommand.command("uninstall").description("Stop the bridge and uninstall the system service").action(() => {
3693
+ stopBridge();
3694
+ uninstallLaunchAgent();
3695
+ console.log("Bridge stopped and service uninstalled.");
3696
+ });
3697
+ serviceCommand.command("logs").description("Show bridge service logs").action(async () => {
3698
+ const fs7 = await import("fs");
3699
+ const os2 = await import("os");
3700
+ const p = await import("path");
3701
+ const logPath = p.join(os2.homedir(), ".vector", "bridge.log");
3702
+ if (fs7.existsSync(logPath)) {
3703
+ const content = fs7.readFileSync(logPath, "utf-8");
3704
+ const lines = content.split("\n");
3705
+ console.log(lines.slice(-50).join("\n"));
3706
+ } else {
3707
+ console.log("No log file found at ~/.vector/bridge.log");
3708
+ }
3709
+ });
3710
+ serviceCommand.command("enable").description("Enable bridge to start at login (macOS LaunchAgent)").action(async () => {
3711
+ if (osPlatform() !== "darwin") {
3712
+ console.error("Login item is macOS only.");
3713
+ return;
3714
+ }
3715
+ const vcliPath = process.argv[1] ?? "vcli";
3716
+ installLaunchAgent(vcliPath);
3717
+ loadLaunchAgent();
3718
+ console.log("Bridge will start automatically on login.");
3719
+ });
3720
+ serviceCommand.command("disable").description("Disable bridge from starting at login").action(() => {
3721
+ uninstallLaunchAgent();
3722
+ console.log("Bridge will no longer start at login.");
3723
+ });
3724
+ var bridgeCommand = program.command("bridge").description("Start/stop the local agent bridge");
3725
+ bridgeCommand.command("start").description("Register device, install service, and start the bridge").action(async (_options, command) => {
3726
+ let config = loadBridgeConfig();
3727
+ if (!config) {
3728
+ const runtime = await getRuntime(command);
3729
+ const session = requireSession(runtime);
3730
+ const client = await createConvexClient(
3731
+ session,
3732
+ runtime.appUrl,
3733
+ runtime.convexUrl
3734
+ );
3735
+ const user = await runQuery(client, api.users.currentUser);
3736
+ if (!user) throw new Error("Not logged in. Run `vcli auth login` first.");
3737
+ config = await setupBridgeDevice(runtime.convexUrl, user._id);
3738
+ console.log(
3739
+ `Device registered: ${config.displayName} (${config.deviceId})`
3740
+ );
3741
+ }
3742
+ if (osPlatform() === "darwin") {
3743
+ const vcliPath = process.argv[1] ?? "vcli";
3744
+ installLaunchAgent(vcliPath);
3745
+ loadLaunchAgent();
3746
+ console.log("\nBridge installed and started as LaunchAgent.");
3747
+ console.log("It will restart automatically on login.");
3748
+ console.log("Run `vcli service status` to check.");
3749
+ } else {
3750
+ console.log("Starting bridge in foreground...");
3751
+ const bridge = new BridgeService(config);
3752
+ await bridge.run();
3753
+ }
3754
+ });
3755
+ bridgeCommand.command("stop").description("Stop the bridge service").action(() => {
3756
+ if (osPlatform() === "darwin") {
3757
+ uninstallLaunchAgent();
3758
+ }
3759
+ if (stopBridge()) {
3760
+ console.log("Bridge stopped.");
3761
+ } else {
3762
+ console.log("Bridge is not running.");
3763
+ }
3764
+ });
3765
+ bridgeCommand.command("status").description("Show bridge status").action(() => {
3766
+ const s = getBridgeStatus();
3767
+ if (!s.configured) {
3768
+ console.log("Bridge not configured. Run: vcli bridge start");
3769
+ return;
3770
+ }
3771
+ console.log("Vector Bridge");
3772
+ console.log(` Device: ${s.config.displayName} (${s.config.deviceId})`);
3773
+ console.log(
3774
+ ` Status: ${s.running ? `Running (PID ${s.pid})` : "Not running"}`
3775
+ );
3776
+ });
2035
3777
  async function main() {
2036
3778
  await program.parseAsync(process.argv);
2037
3779
  }