@vibelet/cli 0.1.37 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -0
- package/bin/cloudflared-quick-tunnel.mjs +11 -0
- package/bin/cloudflared-resolver.mjs +171 -0
- package/bin/vibelet-runtime-policy.mjs +36 -0
- package/bin/vibelet.cjs +12 -0
- package/bin/vibelet.mjs +1062 -0
- package/dist/index.cjs +126 -0
- package/package.json +25 -24
- package/app.json +0 -5
- package/dist/advertised-hosts.d.ts +0 -34
- package/dist/advertised-hosts.d.ts.map +0 -1
- package/dist/advertised-hosts.js +0 -176
- package/dist/advertised-hosts.js.map +0 -1
- package/dist/advertised-hosts.test.d.ts +0 -2
- package/dist/advertised-hosts.test.d.ts.map +0 -1
- package/dist/advertised-hosts.test.js +0 -96
- package/dist/advertised-hosts.test.js.map +0 -1
- package/dist/audit.d.ts +0 -30
- package/dist/audit.d.ts.map +0 -1
- package/dist/audit.js +0 -73
- package/dist/audit.js.map +0 -1
- package/dist/audit.test.d.ts +0 -2
- package/dist/audit.test.d.ts.map +0 -1
- package/dist/audit.test.js +0 -33
- package/dist/audit.test.js.map +0 -1
- package/dist/auth.d.ts +0 -6
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js +0 -27
- package/dist/auth.js.map +0 -1
- package/dist/claude-hooks.d.ts +0 -58
- package/dist/claude-hooks.d.ts.map +0 -1
- package/dist/claude-hooks.js +0 -129
- package/dist/claude-hooks.js.map +0 -1
- package/dist/cli-version.d.ts +0 -3
- package/dist/cli-version.d.ts.map +0 -1
- package/dist/cli-version.js +0 -35
- package/dist/cli-version.js.map +0 -1
- package/dist/cli-version.test.d.ts +0 -2
- package/dist/cli-version.test.d.ts.map +0 -1
- package/dist/cli-version.test.js +0 -38
- package/dist/cli-version.test.js.map +0 -1
- package/dist/config.d.ts +0 -30
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -327
- package/dist/config.js.map +0 -1
- package/dist/config.test.d.ts +0 -2
- package/dist/config.test.d.ts.map +0 -1
- package/dist/config.test.js +0 -184
- package/dist/config.test.js.map +0 -1
- package/dist/dev-auth.test.d.ts +0 -2
- package/dist/dev-auth.test.d.ts.map +0 -1
- package/dist/dev-auth.test.js +0 -154
- package/dist/dev-auth.test.js.map +0 -1
- package/dist/dev-script.test.d.ts +0 -2
- package/dist/dev-script.test.d.ts.map +0 -1
- package/dist/dev-script.test.js +0 -412
- package/dist/dev-script.test.js.map +0 -1
- package/dist/drivers/claude.d.ts +0 -34
- package/dist/drivers/claude.d.ts.map +0 -1
- package/dist/drivers/claude.js +0 -413
- package/dist/drivers/claude.js.map +0 -1
- package/dist/drivers/claude.test.d.ts +0 -2
- package/dist/drivers/claude.test.d.ts.map +0 -1
- package/dist/drivers/claude.test.js +0 -951
- package/dist/drivers/claude.test.js.map +0 -1
- package/dist/drivers/codex.d.ts +0 -38
- package/dist/drivers/codex.d.ts.map +0 -1
- package/dist/drivers/codex.js +0 -771
- package/dist/drivers/codex.js.map +0 -1
- package/dist/drivers/codex.test.d.ts +0 -2
- package/dist/drivers/codex.test.d.ts.map +0 -1
- package/dist/drivers/codex.test.js +0 -939
- package/dist/drivers/codex.test.js.map +0 -1
- package/dist/drivers/types.d.ts +0 -14
- package/dist/drivers/types.d.ts.map +0 -1
- package/dist/drivers/types.js +0 -2
- package/dist/drivers/types.js.map +0 -1
- package/dist/e2e.test.d.ts +0 -2
- package/dist/e2e.test.d.ts.map +0 -1
- package/dist/e2e.test.js +0 -111
- package/dist/e2e.test.js.map +0 -1
- package/dist/identity.d.ts +0 -10
- package/dist/identity.d.ts.map +0 -1
- package/dist/identity.js +0 -66
- package/dist/identity.js.map +0 -1
- package/dist/identity.test.d.ts +0 -2
- package/dist/identity.test.d.ts.map +0 -1
- package/dist/identity.test.js +0 -25
- package/dist/identity.test.js.map +0 -1
- package/dist/index-entry.test.d.ts +0 -2
- package/dist/index-entry.test.d.ts.map +0 -1
- package/dist/index-entry.test.js +0 -272
- package/dist/index-entry.test.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -707
- package/dist/index.js.map +0 -1
- package/dist/logger.d.ts +0 -31
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -75
- package/dist/logger.js.map +0 -1
- package/dist/metrics.d.ts +0 -52
- package/dist/metrics.d.ts.map +0 -1
- package/dist/metrics.js +0 -89
- package/dist/metrics.js.map +0 -1
- package/dist/pairing-store.d.ts +0 -29
- package/dist/pairing-store.d.ts.map +0 -1
- package/dist/pairing-store.js +0 -131
- package/dist/pairing-store.js.map +0 -1
- package/dist/pairing-store.test.d.ts +0 -2
- package/dist/pairing-store.test.d.ts.map +0 -1
- package/dist/pairing-store.test.js +0 -47
- package/dist/pairing-store.test.js.map +0 -1
- package/dist/paths.d.ts +0 -16
- package/dist/paths.d.ts.map +0 -1
- package/dist/paths.js +0 -18
- package/dist/paths.js.map +0 -1
- package/dist/perf-compare.d.ts +0 -13
- package/dist/perf-compare.d.ts.map +0 -1
- package/dist/perf-compare.js +0 -125
- package/dist/perf-compare.js.map +0 -1
- package/dist/port-conflict.d.ts +0 -9
- package/dist/port-conflict.d.ts.map +0 -1
- package/dist/port-conflict.js +0 -33
- package/dist/port-conflict.js.map +0 -1
- package/dist/port-conflict.test.d.ts +0 -2
- package/dist/port-conflict.test.d.ts.map +0 -1
- package/dist/port-conflict.test.js +0 -38
- package/dist/port-conflict.test.js.map +0 -1
- package/dist/process-scanner.d.ts +0 -43
- package/dist/process-scanner.d.ts.map +0 -1
- package/dist/process-scanner.js +0 -453
- package/dist/process-scanner.js.map +0 -1
- package/dist/process-scanner.perf.test.d.ts +0 -2
- package/dist/process-scanner.perf.test.d.ts.map +0 -1
- package/dist/process-scanner.perf.test.js +0 -186
- package/dist/process-scanner.perf.test.js.map +0 -1
- package/dist/process-scanner.test.d.ts +0 -2
- package/dist/process-scanner.test.d.ts.map +0 -1
- package/dist/process-scanner.test.js +0 -399
- package/dist/process-scanner.test.js.map +0 -1
- package/dist/push-protocol.d.ts +0 -15
- package/dist/push-protocol.d.ts.map +0 -1
- package/dist/push-protocol.js +0 -23
- package/dist/push-protocol.js.map +0 -1
- package/dist/push-protocol.test.d.ts +0 -2
- package/dist/push-protocol.test.d.ts.map +0 -1
- package/dist/push-protocol.test.js +0 -57
- package/dist/push-protocol.test.js.map +0 -1
- package/dist/push-store.d.ts +0 -22
- package/dist/push-store.d.ts.map +0 -1
- package/dist/push-store.js +0 -103
- package/dist/push-store.js.map +0 -1
- package/dist/push-store.test.d.ts +0 -2
- package/dist/push-store.test.d.ts.map +0 -1
- package/dist/push-store.test.js +0 -79
- package/dist/push-store.test.js.map +0 -1
- package/dist/push.d.ts +0 -65
- package/dist/push.d.ts.map +0 -1
- package/dist/push.js +0 -202
- package/dist/push.js.map +0 -1
- package/dist/push.test.d.ts +0 -2
- package/dist/push.test.d.ts.map +0 -1
- package/dist/push.test.js +0 -199
- package/dist/push.test.js.map +0 -1
- package/dist/safe-stdio.d.ts +0 -3
- package/dist/safe-stdio.d.ts.map +0 -1
- package/dist/safe-stdio.js +0 -46
- package/dist/safe-stdio.js.map +0 -1
- package/dist/scanner.d.ts +0 -30
- package/dist/scanner.d.ts.map +0 -1
- package/dist/scanner.js +0 -859
- package/dist/scanner.js.map +0 -1
- package/dist/scanner.perf.test.d.ts +0 -2
- package/dist/scanner.perf.test.d.ts.map +0 -1
- package/dist/scanner.perf.test.js +0 -320
- package/dist/scanner.perf.test.js.map +0 -1
- package/dist/scanner.test.d.ts +0 -2
- package/dist/scanner.test.d.ts.map +0 -1
- package/dist/scanner.test.js +0 -948
- package/dist/scanner.test.js.map +0 -1
- package/dist/session-inventory.d.ts +0 -63
- package/dist/session-inventory.d.ts.map +0 -1
- package/dist/session-inventory.js +0 -525
- package/dist/session-inventory.js.map +0 -1
- package/dist/session-inventory.perf.test.d.ts +0 -2
- package/dist/session-inventory.perf.test.d.ts.map +0 -1
- package/dist/session-inventory.perf.test.js +0 -220
- package/dist/session-inventory.perf.test.js.map +0 -1
- package/dist/session-inventory.test.d.ts +0 -2
- package/dist/session-inventory.test.d.ts.map +0 -1
- package/dist/session-inventory.test.js +0 -712
- package/dist/session-inventory.test.js.map +0 -1
- package/dist/session-manager.d.ts +0 -75
- package/dist/session-manager.d.ts.map +0 -1
- package/dist/session-manager.js +0 -1515
- package/dist/session-manager.js.map +0 -1
- package/dist/session-manager.test.d.ts +0 -2
- package/dist/session-manager.test.d.ts.map +0 -1
- package/dist/session-manager.test.js +0 -2861
- package/dist/session-manager.test.js.map +0 -1
- package/dist/session-store.d.ts +0 -42
- package/dist/session-store.d.ts.map +0 -1
- package/dist/session-store.js +0 -163
- package/dist/session-store.js.map +0 -1
- package/dist/session-store.test.d.ts +0 -2
- package/dist/session-store.test.d.ts.map +0 -1
- package/dist/session-store.test.js +0 -236
- package/dist/session-store.test.js.map +0 -1
- package/dist/session-title.d.ts +0 -6
- package/dist/session-title.d.ts.map +0 -1
- package/dist/session-title.js +0 -105
- package/dist/session-title.js.map +0 -1
- package/dist/session-title.perf.test.d.ts +0 -2
- package/dist/session-title.perf.test.d.ts.map +0 -1
- package/dist/session-title.perf.test.js +0 -99
- package/dist/session-title.perf.test.js.map +0 -1
- package/dist/session-title.test.d.ts +0 -2
- package/dist/session-title.test.d.ts.map +0 -1
- package/dist/session-title.test.js +0 -199
- package/dist/session-title.test.js.map +0 -1
- package/dist/shutdown-endpoint.test.d.ts +0 -2
- package/dist/shutdown-endpoint.test.d.ts.map +0 -1
- package/dist/shutdown-endpoint.test.js +0 -93
- package/dist/shutdown-endpoint.test.js.map +0 -1
- package/dist/storage-housekeeping.d.ts +0 -28
- package/dist/storage-housekeeping.d.ts.map +0 -1
- package/dist/storage-housekeeping.js +0 -76
- package/dist/storage-housekeeping.js.map +0 -1
- package/dist/storage-housekeeping.test.d.ts +0 -2
- package/dist/storage-housekeeping.test.d.ts.map +0 -1
- package/dist/storage-housekeeping.test.js +0 -65
- package/dist/storage-housekeeping.test.js.map +0 -1
- package/dist/test-daemon-harness.d.ts +0 -31
- package/dist/test-daemon-harness.d.ts.map +0 -1
- package/dist/test-daemon-harness.js +0 -337
- package/dist/test-daemon-harness.js.map +0 -1
- package/dist/token-auth.test.d.ts +0 -2
- package/dist/token-auth.test.d.ts.map +0 -1
- package/dist/token-auth.test.js +0 -52
- package/dist/token-auth.test.js.map +0 -1
- package/dist/utils.d.ts +0 -4
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -40
- package/dist/utils.js.map +0 -1
- package/dist/utils.test.d.ts +0 -2
- package/dist/utils.test.d.ts.map +0 -1
- package/dist/utils.test.js +0 -54
- package/dist/utils.test.js.map +0 -1
- package/dist/ws-data.d.ts +0 -4
- package/dist/ws-data.d.ts.map +0 -1
- package/dist/ws-data.js +0 -20
- package/dist/ws-data.js.map +0 -1
- package/dist/ws-data.test.d.ts +0 -2
- package/dist/ws-data.test.d.ts.map +0 -1
- package/dist/ws-data.test.js +0 -17
- package/dist/ws-data.test.js.map +0 -1
- package/perf-reporter.mjs +0 -138
- package/scripts/build-release.mjs +0 -41
- package/scripts/dev.mjs +0 -537
- package/src/advertised-hosts.test.ts +0 -125
- package/src/advertised-hosts.ts +0 -225
- package/src/audit.test.ts +0 -38
- package/src/audit.ts +0 -117
- package/src/auth.ts +0 -31
- package/src/claude-hooks.ts +0 -195
- package/src/cli-version.test.ts +0 -36
- package/src/cli-version.ts +0 -46
- package/src/config.test.ts +0 -254
- package/src/config.ts +0 -324
- package/src/dev-auth.test.ts +0 -183
- package/src/dev-script.test.ts +0 -511
- package/src/drivers/claude.test.ts +0 -1186
- package/src/drivers/claude.ts +0 -443
- package/src/drivers/codex.test.ts +0 -1096
- package/src/drivers/codex.ts +0 -879
- package/src/drivers/types.ts +0 -15
- package/src/e2e.test.ts +0 -139
- package/src/identity.test.ts +0 -26
- package/src/identity.ts +0 -82
- package/src/index-entry.test.ts +0 -336
- package/src/index.ts +0 -781
- package/src/logger.ts +0 -112
- package/src/metrics.ts +0 -117
- package/src/pairing-store.test.ts +0 -53
- package/src/pairing-store.ts +0 -154
- package/src/paths.ts +0 -19
- package/src/perf-compare.ts +0 -164
- package/src/port-conflict.test.ts +0 -45
- package/src/port-conflict.ts +0 -44
- package/src/process-scanner.perf.test.ts +0 -222
- package/src/process-scanner.test.ts +0 -575
- package/src/process-scanner.ts +0 -514
- package/src/push-protocol.test.ts +0 -74
- package/src/push-protocol.ts +0 -36
- package/src/push-store.test.ts +0 -89
- package/src/push-store.ts +0 -126
- package/src/push.test.ts +0 -234
- package/src/push.ts +0 -318
- package/src/safe-stdio.ts +0 -51
- package/src/scanner.perf.test.ts +0 -359
- package/src/scanner.test.ts +0 -1045
- package/src/scanner.ts +0 -924
- package/src/session-inventory.perf.test.ts +0 -250
- package/src/session-inventory.test.ts +0 -1002
- package/src/session-inventory.ts +0 -721
- package/src/session-manager.test.ts +0 -3430
- package/src/session-manager.ts +0 -1775
- package/src/session-store.test.ts +0 -276
- package/src/session-store.ts +0 -202
- package/src/session-title.perf.test.ts +0 -118
- package/src/session-title.test.ts +0 -286
- package/src/session-title.ts +0 -108
- package/src/shutdown-endpoint.test.ts +0 -95
- package/src/storage-housekeeping.test.ts +0 -78
- package/src/storage-housekeeping.ts +0 -111
- package/src/test-daemon-harness.ts +0 -410
- package/src/token-auth.test.ts +0 -67
- package/src/utils.test.ts +0 -65
- package/src/utils.ts +0 -47
- package/src/ws-data.test.ts +0 -20
- package/src/ws-data.ts +0 -26
- package/tsconfig.json +0 -12
package/src/config.ts
DELETED
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
2
|
-
import { existsSync } from 'fs';
|
|
3
|
-
import { join, delimiter } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { logger as rootLogger } from './logger.js';
|
|
6
|
-
|
|
7
|
-
const TRANSIENT_PATH_MARKERS = ['fnm_multishells'];
|
|
8
|
-
const CACHE_TTL = 60_000;
|
|
9
|
-
const IS_WIN32 = process.platform === 'win32';
|
|
10
|
-
const log = rootLogger.child({ module: 'config' });
|
|
11
|
-
|
|
12
|
-
export function isTransientPath(p: string): boolean {
|
|
13
|
-
return TRANSIENT_PATH_MARKERS.some(marker => p.includes(marker));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getLoginShellCandidates(): string[] {
|
|
17
|
-
if (IS_WIN32) return []; // No login shell on Windows
|
|
18
|
-
return [process.env.SHELL, '/bin/zsh', '/bin/bash', '/bin/sh']
|
|
19
|
-
.filter((s): s is string => !!s)
|
|
20
|
-
.filter((s, i, a) => a.indexOf(s) === i);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// ===== Shell Environment =====
|
|
24
|
-
|
|
25
|
-
let cachedShellEnv: Record<string, string> | null = null;
|
|
26
|
-
|
|
27
|
-
function loadUserShellEnv(): Record<string, string> {
|
|
28
|
-
if (cachedShellEnv && !isTransientPath(cachedShellEnv.PATH ?? '')) return cachedShellEnv;
|
|
29
|
-
|
|
30
|
-
for (const shell of getLoginShellCandidates()) {
|
|
31
|
-
try {
|
|
32
|
-
const result = execFileSync(shell, ['-ilc', 'env'], {
|
|
33
|
-
timeout: 5000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
34
|
-
});
|
|
35
|
-
const env: Record<string, string> = {};
|
|
36
|
-
for (const line of result.split('\n')) {
|
|
37
|
-
const idx = line.indexOf('=');
|
|
38
|
-
if (idx > 0) env[line.slice(0, idx)] = line.slice(idx + 1);
|
|
39
|
-
}
|
|
40
|
-
if (!isTransientPath(env.PATH ?? '')) {
|
|
41
|
-
cachedShellEnv = env;
|
|
42
|
-
} else {
|
|
43
|
-
cachedShellEnv = null;
|
|
44
|
-
}
|
|
45
|
-
return env;
|
|
46
|
-
} catch { /* next */ }
|
|
47
|
-
}
|
|
48
|
-
cachedShellEnv = {};
|
|
49
|
-
return {};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getExpandedPath(shellEnv: Record<string, string> = loadUserShellEnv()): string {
|
|
53
|
-
const home = homedir();
|
|
54
|
-
const pnpmHome = shellEnv.PNPM_HOME || process.env.PNPM_HOME || (IS_WIN32 ? join(home, 'pnpm') : join(home, 'Library', 'pnpm'));
|
|
55
|
-
const basePath = shellEnv.PATH || process.env.PATH || '';
|
|
56
|
-
const extra = IS_WIN32
|
|
57
|
-
? [
|
|
58
|
-
join(home, '.npm-global', 'bin'),
|
|
59
|
-
join(home, '.local', 'bin'),
|
|
60
|
-
join(home, '.claude', 'bin'),
|
|
61
|
-
pnpmHome,
|
|
62
|
-
join(home, 'AppData', 'Roaming', 'npm'),
|
|
63
|
-
join(home, 'AppData', 'Local', 'Programs', 'node'),
|
|
64
|
-
]
|
|
65
|
-
: [
|
|
66
|
-
'/usr/local/bin', '/opt/homebrew/bin', '/usr/bin', '/bin',
|
|
67
|
-
join(home, '.npm-global', 'bin'),
|
|
68
|
-
join(home, '.local', 'bin'),
|
|
69
|
-
join(home, '.claude', 'bin'),
|
|
70
|
-
pnpmHome,
|
|
71
|
-
];
|
|
72
|
-
const current = basePath.split(delimiter).filter(Boolean);
|
|
73
|
-
const seen = new Set(current);
|
|
74
|
-
for (const p of extra) {
|
|
75
|
-
if (!seen.has(p)) { current.push(p); seen.add(p); }
|
|
76
|
-
}
|
|
77
|
-
return current.join(delimiter);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function getPositiveNumberEnv(name: string, fallback: number): number {
|
|
81
|
-
const raw = process.env[name];
|
|
82
|
-
if (!raw) return fallback;
|
|
83
|
-
const parsed = Number(raw);
|
|
84
|
-
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function getNonNegativeNumberEnv(name: string, fallback: number): number {
|
|
88
|
-
const raw = process.env[name];
|
|
89
|
-
if (raw == null || raw === '') return fallback;
|
|
90
|
-
const parsed = Number(raw);
|
|
91
|
-
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : fallback;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export function buildSanitizedEnv(): Record<string, string> {
|
|
95
|
-
const shellEnv = loadUserShellEnv();
|
|
96
|
-
const merged: Record<string, string> = {};
|
|
97
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
98
|
-
if (typeof v === 'string') merged[k] = v;
|
|
99
|
-
}
|
|
100
|
-
for (const [k, v] of Object.entries(shellEnv)) {
|
|
101
|
-
if (typeof v === 'string') merged[k] = v;
|
|
102
|
-
}
|
|
103
|
-
merged.PATH = getExpandedPath(shellEnv);
|
|
104
|
-
if (!merged.HOME) merged.HOME = homedir();
|
|
105
|
-
delete merged.CLAUDE_CODE_ENTRYPOINT;
|
|
106
|
-
delete merged.CLAUDECODE;
|
|
107
|
-
return merged;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ===== Binary Detection =====
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* On Windows, resolve a bare command name like "codex" to its real executable
|
|
114
|
-
* path (e.g. codex.exe) using `where.exe`. This avoids the common issue where
|
|
115
|
-
* Node's `spawn('codex')` fails with ENOENT because it doesn't search PATH
|
|
116
|
-
* for `.cmd`/`.ps1`/`.exe` extensions the way PowerShell/cmd do.
|
|
117
|
-
*/
|
|
118
|
-
function resolveViaWhere(name: string): string | undefined {
|
|
119
|
-
if (!IS_WIN32) return undefined;
|
|
120
|
-
try {
|
|
121
|
-
const output = execFileSync('where.exe', [name], {
|
|
122
|
-
timeout: 3000, stdio: 'pipe', encoding: 'utf-8',
|
|
123
|
-
}).trim();
|
|
124
|
-
// `where` returns one result per line. Prefer .exe over .cmd/.ps1
|
|
125
|
-
const lines = output.split(/\r?\n/).filter(Boolean);
|
|
126
|
-
const exe = lines.find(l => l.endsWith('.exe'));
|
|
127
|
-
return exe ?? lines[0];
|
|
128
|
-
} catch { return undefined; }
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function runVersionSync(binaryPath: string, timeout = 3000): string | null {
|
|
132
|
-
try {
|
|
133
|
-
return execFileSync(binaryPath, ['--version'], {
|
|
134
|
-
timeout, stdio: 'pipe', encoding: 'utf-8', env: buildSanitizedEnv(),
|
|
135
|
-
...(IS_WIN32 ? { shell: true } : {}),
|
|
136
|
-
}).trim() || null;
|
|
137
|
-
} catch { return null; }
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function runViaLoginShellSync(cmd: string, args: string[], timeout = 5000): string | null {
|
|
141
|
-
for (const shell of getLoginShellCandidates()) {
|
|
142
|
-
try {
|
|
143
|
-
return execFileSync(shell, ['-ilc', `command -v ${cmd} >/dev/null 2>&1 && exec ${cmd} "$@"`, '--', ...args], {
|
|
144
|
-
timeout, stdio: ['pipe', 'pipe', 'pipe'], encoding: 'utf-8',
|
|
145
|
-
}).trim() || null;
|
|
146
|
-
} catch { /* next */ }
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function findViaLoginShell(cmd: string): string | undefined {
|
|
152
|
-
for (const shell of getLoginShellCandidates()) {
|
|
153
|
-
try {
|
|
154
|
-
const result = execFileSync(shell, ['-ilc', `command -v ${cmd}`], {
|
|
155
|
-
timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], encoding: 'utf-8',
|
|
156
|
-
}).trim();
|
|
157
|
-
if (result) return result;
|
|
158
|
-
} catch { /* next */ }
|
|
159
|
-
}
|
|
160
|
-
return undefined;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// ===== Claude Detection =====
|
|
164
|
-
|
|
165
|
-
function getClaudeCandidates(): string[] {
|
|
166
|
-
const home = homedir();
|
|
167
|
-
return [
|
|
168
|
-
join(home, '.local', 'bin', 'claude'),
|
|
169
|
-
join(home, '.claude', 'bin', 'claude'),
|
|
170
|
-
'/usr/local/bin/claude',
|
|
171
|
-
'/opt/homebrew/bin/claude',
|
|
172
|
-
join(home, '.npm-global', 'bin', 'claude'),
|
|
173
|
-
];
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function findClaudeUncached(): string | undefined {
|
|
177
|
-
// On Windows, use `where.exe` instead of known Unix paths
|
|
178
|
-
if (IS_WIN32) {
|
|
179
|
-
const found = resolveViaWhere('claude');
|
|
180
|
-
if (found && runVersionSync(found)) return found;
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
// Check known paths
|
|
184
|
-
for (const p of getClaudeCandidates()) {
|
|
185
|
-
if (runVersionSync(p)) return p;
|
|
186
|
-
}
|
|
187
|
-
// which (with sanitized env that has expanded PATH)
|
|
188
|
-
try {
|
|
189
|
-
const result = execFileSync('/usr/bin/which', ['claude'], {
|
|
190
|
-
timeout: 3000, stdio: 'pipe', encoding: 'utf-8', env: buildSanitizedEnv(),
|
|
191
|
-
}).trim();
|
|
192
|
-
// Skip cmux wrappers
|
|
193
|
-
if (result && !result.includes('cmux') && runVersionSync(result)) return result;
|
|
194
|
-
} catch { /* not found */ }
|
|
195
|
-
return undefined;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// ===== Codex Detection =====
|
|
199
|
-
|
|
200
|
-
function getCodexCandidates(): string[] {
|
|
201
|
-
const home = homedir();
|
|
202
|
-
const pnpmHomes = [process.env.PNPM_HOME, join(home, 'Library', 'pnpm')]
|
|
203
|
-
.filter((p): p is string => !!p);
|
|
204
|
-
return [
|
|
205
|
-
'/usr/local/bin/codex',
|
|
206
|
-
'/opt/homebrew/bin/codex',
|
|
207
|
-
join(home, '.npm-global', 'bin', 'codex'),
|
|
208
|
-
join(home, '.local', 'bin', 'codex'),
|
|
209
|
-
...pnpmHomes.map(p => join(p, 'codex')),
|
|
210
|
-
];
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function findCodexUncached(): string | undefined {
|
|
214
|
-
// On Windows, use `where.exe` to find the real executable
|
|
215
|
-
if (IS_WIN32) {
|
|
216
|
-
const found = resolveViaWhere('codex');
|
|
217
|
-
if (found && runVersionSync(found)) return found;
|
|
218
|
-
return undefined;
|
|
219
|
-
}
|
|
220
|
-
let transientFallback: string | undefined;
|
|
221
|
-
// Check known paths
|
|
222
|
-
for (const p of getCodexCandidates()) {
|
|
223
|
-
if (!runVersionSync(p)) continue;
|
|
224
|
-
if (!isTransientPath(p)) return p;
|
|
225
|
-
transientFallback = p;
|
|
226
|
-
}
|
|
227
|
-
// which with sanitized env
|
|
228
|
-
try {
|
|
229
|
-
const result = execFileSync('/usr/bin/which', ['codex'], {
|
|
230
|
-
timeout: 3000, stdio: 'pipe', encoding: 'utf-8', env: buildSanitizedEnv(),
|
|
231
|
-
}).trim();
|
|
232
|
-
if (result && runVersionSync(result)) {
|
|
233
|
-
if (!isTransientPath(result)) return result;
|
|
234
|
-
transientFallback = transientFallback ?? result;
|
|
235
|
-
}
|
|
236
|
-
} catch { /* not found */ }
|
|
237
|
-
// Login shell fallback
|
|
238
|
-
const found = findViaLoginShell('codex');
|
|
239
|
-
if (found && runVersionSync(found)) {
|
|
240
|
-
if (!isTransientPath(found)) return found;
|
|
241
|
-
transientFallback = transientFallback ?? found;
|
|
242
|
-
}
|
|
243
|
-
return transientFallback;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// ===== Cached Resolvers =====
|
|
247
|
-
|
|
248
|
-
function createCachedResolver(finder: () => string | undefined, name: string) {
|
|
249
|
-
let cached: string | null = null;
|
|
250
|
-
let timestamp = 0;
|
|
251
|
-
return (): string => {
|
|
252
|
-
const envKey = name === 'claude' ? 'CLAUDE_PATH' : 'CODEX_PATH';
|
|
253
|
-
if (process.env[envKey]) return process.env[envKey]!;
|
|
254
|
-
|
|
255
|
-
const now = Date.now();
|
|
256
|
-
// Re-resolve if expired, missing, or transient
|
|
257
|
-
if (!cached || now - timestamp > CACHE_TTL || !existsSync(cached) || isTransientPath(cached)) {
|
|
258
|
-
const found = finder();
|
|
259
|
-
cached = found ?? name;
|
|
260
|
-
timestamp = now;
|
|
261
|
-
log.info({ name, path: cached, transient: isTransientPath(cached) }, 'binary resolved');
|
|
262
|
-
}
|
|
263
|
-
return cached;
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const resolveClaude = createCachedResolver(findClaudeUncached, 'claude');
|
|
268
|
-
const resolveCodex = createCachedResolver(findCodexUncached, 'codex');
|
|
269
|
-
|
|
270
|
-
// ===== CLI Args =====
|
|
271
|
-
|
|
272
|
-
function parseCliArgs(): Record<string, string> {
|
|
273
|
-
const result: Record<string, string> = {};
|
|
274
|
-
const args = process.argv.slice(2);
|
|
275
|
-
for (let i = 0; i < args.length; i++) {
|
|
276
|
-
const arg = args[i];
|
|
277
|
-
if (arg.startsWith('--') && i + 1 < args.length) {
|
|
278
|
-
const key = arg.slice(2);
|
|
279
|
-
result[key] = args[++i];
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return result;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
const cliArgs = parseCliArgs();
|
|
286
|
-
|
|
287
|
-
// ===== Export =====
|
|
288
|
-
|
|
289
|
-
export function execViaLoginShell(cmd: string, args: string[]): { command: string; args: string[] } {
|
|
290
|
-
if (IS_WIN32) {
|
|
291
|
-
// On Windows, resolve via where.exe and spawn directly
|
|
292
|
-
const resolved = resolveViaWhere(cmd);
|
|
293
|
-
return { command: resolved ?? cmd, args };
|
|
294
|
-
}
|
|
295
|
-
const shell = getLoginShellCandidates()[0] ?? '/bin/zsh';
|
|
296
|
-
return {
|
|
297
|
-
command: shell,
|
|
298
|
-
args: ['-lc', `command -v ${cmd} >/dev/null 2>&1 && exec ${cmd} "$@"`, '--', ...args],
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export const config = {
|
|
303
|
-
port: Number(process.env.VIBE_PORT) || 9876,
|
|
304
|
-
legacyToken: process.env.VIBE_TOKEN || '',
|
|
305
|
-
canonicalHost: cliArgs['host'] || process.env.VIBELET_CANONICAL_HOST || process.env.VIBELET_HOST || '',
|
|
306
|
-
fallbackHosts: cliArgs['fallback-hosts'] || process.env.VIBELET_FALLBACK_HOSTS || '',
|
|
307
|
-
/** Public relay URL (e.g. https://abc.trycloudflare.com). Overrides host/port in QR pairing payload. */
|
|
308
|
-
relayUrl: process.env.VIBELET_RELAY_URL || '',
|
|
309
|
-
/** Idle timeout in ms before a driver is automatically released. Default: 30 minutes. 0 = disabled. */
|
|
310
|
-
idleTimeoutMs: getNonNegativeNumberEnv('VIBE_IDLE_TIMEOUT_MS', 30 * 60 * 1000),
|
|
311
|
-
/** Maximum inactivity in ms for an in-flight turn before it is failed and the driver is stopped. 0 = disabled. */
|
|
312
|
-
turnStallTimeoutMs: getNonNegativeNumberEnv('VIBE_TURN_STALL_TIMEOUT_MS', 5 * 60 * 1000),
|
|
313
|
-
/** Maximum size for ~/.vibelet/data/audit.jsonl before it is trimmed in place. */
|
|
314
|
-
auditMaxBytes: getPositiveNumberEnv('VIBE_AUDIT_MAX_BYTES', 8 * 1024 * 1024),
|
|
315
|
-
/** Maximum size for each ~/.vibelet/logs/daemon.*.log file before it is trimmed in place. */
|
|
316
|
-
daemonLogMaxBytes: getPositiveNumberEnv('VIBE_DAEMON_LOG_MAX_BYTES', 16 * 1024 * 1024),
|
|
317
|
-
/** How often the daemon trims its own stdout/stderr logs. */
|
|
318
|
-
storageHousekeepingIntervalMs: getPositiveNumberEnv('VIBE_STORAGE_HOUSEKEEPING_INTERVAL_MS', 5 * 60 * 1000),
|
|
319
|
-
get claudePath() { return resolveClaude(); },
|
|
320
|
-
get codexPath() { return resolveCodex(); },
|
|
321
|
-
isTransientPath,
|
|
322
|
-
execViaLoginShell,
|
|
323
|
-
buildSanitizedEnv,
|
|
324
|
-
};
|
package/src/dev-auth.test.ts
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import assert from 'node:assert/strict';
|
|
2
|
-
import { spawn, type ChildProcess } from 'node:child_process';
|
|
3
|
-
import { rmSync, mkdtempSync } from 'node:fs';
|
|
4
|
-
import { createRequire } from 'node:module';
|
|
5
|
-
import { tmpdir } from 'node:os';
|
|
6
|
-
import { createServer, Socket } from 'node:net';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import test from 'node:test';
|
|
9
|
-
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import WebSocket from 'ws';
|
|
11
|
-
|
|
12
|
-
const require = createRequire(import.meta.url);
|
|
13
|
-
const srcDir = path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
const daemonDir = path.resolve(srcDir, '..');
|
|
15
|
-
const tsxPackageJsonPath = require.resolve('tsx/package.json');
|
|
16
|
-
const tsxCliPath = path.resolve(path.dirname(tsxPackageJsonPath), 'dist/cli.mjs');
|
|
17
|
-
|
|
18
|
-
function delay(ms: number): Promise<void> {
|
|
19
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async function getFreePort(): Promise<number> {
|
|
23
|
-
const server = createServer();
|
|
24
|
-
server.listen(0, '127.0.0.1');
|
|
25
|
-
await new Promise<void>((resolve) => server.once('listening', () => resolve()));
|
|
26
|
-
const address = server.address();
|
|
27
|
-
assert.ok(address && typeof address === 'object');
|
|
28
|
-
const { port } = address;
|
|
29
|
-
await new Promise<void>((resolve, reject) => {
|
|
30
|
-
server.close((error) => (error ? reject(error) : resolve()));
|
|
31
|
-
});
|
|
32
|
-
return port;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function isPortListening(port: number): Promise<boolean> {
|
|
36
|
-
return new Promise((resolve) => {
|
|
37
|
-
const socket = new Socket();
|
|
38
|
-
|
|
39
|
-
const finish = (result: boolean) => {
|
|
40
|
-
socket.removeAllListeners();
|
|
41
|
-
socket.destroy();
|
|
42
|
-
resolve(result);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
socket.setTimeout(200);
|
|
46
|
-
socket.once('connect', () => finish(true));
|
|
47
|
-
socket.once('timeout', () => finish(false));
|
|
48
|
-
socket.once('error', () => finish(false));
|
|
49
|
-
socket.connect(port, '127.0.0.1');
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function waitForPortState(port: number, expectedListening: boolean, timeoutMs: number): Promise<void> {
|
|
54
|
-
const deadline = Date.now() + timeoutMs;
|
|
55
|
-
|
|
56
|
-
while (Date.now() < deadline) {
|
|
57
|
-
if ((await isPortListening(port)) === expectedListening) {
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
await delay(100);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
throw new Error(`Timed out waiting for port ${port} to become ${expectedListening ? 'open' : 'closed'}.`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function waitForExit(child: ChildProcess, timeoutMs: number): Promise<void> {
|
|
67
|
-
if (child.exitCode !== null || child.signalCode !== null) return;
|
|
68
|
-
|
|
69
|
-
await new Promise<void>((resolve, reject) => {
|
|
70
|
-
const timer = setTimeout(() => reject(new Error('Timed out waiting for daemon to exit.')), timeoutMs);
|
|
71
|
-
|
|
72
|
-
child.once('exit', () => {
|
|
73
|
-
clearTimeout(timer);
|
|
74
|
-
resolve();
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async function cleanupChild(child: ChildProcess, port: number): Promise<void> {
|
|
80
|
-
if (child.exitCode === null && child.signalCode === null) {
|
|
81
|
-
child.kill('SIGTERM');
|
|
82
|
-
try {
|
|
83
|
-
await waitForExit(child, 5000);
|
|
84
|
-
} catch {}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
await waitForPortState(port, false, 5000).catch(() => {});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function openWebSocket(url: string): Promise<WebSocket> {
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
const ws = new WebSocket(url);
|
|
93
|
-
const timer = setTimeout(() => {
|
|
94
|
-
ws.terminate();
|
|
95
|
-
reject(new Error(`Timed out waiting for websocket open: ${url}`));
|
|
96
|
-
}, 5000);
|
|
97
|
-
|
|
98
|
-
ws.once('open', () => {
|
|
99
|
-
clearTimeout(timer);
|
|
100
|
-
resolve(ws);
|
|
101
|
-
});
|
|
102
|
-
ws.once('error', (error) => {
|
|
103
|
-
clearTimeout(timer);
|
|
104
|
-
reject(error);
|
|
105
|
-
});
|
|
106
|
-
ws.once('close', (code, reason) => {
|
|
107
|
-
clearTimeout(timer);
|
|
108
|
-
reject(new Error(`WebSocket closed before opening (${code}): ${reason.toString()}`));
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function waitForCloseCode(url: string): Promise<number> {
|
|
114
|
-
return new Promise((resolve, reject) => {
|
|
115
|
-
const ws = new WebSocket(url);
|
|
116
|
-
const timer = setTimeout(() => {
|
|
117
|
-
ws.terminate();
|
|
118
|
-
reject(new Error(`Timed out waiting for websocket close: ${url}`));
|
|
119
|
-
}, 5000);
|
|
120
|
-
|
|
121
|
-
ws.once('close', (code) => {
|
|
122
|
-
clearTimeout(timer);
|
|
123
|
-
resolve(code);
|
|
124
|
-
});
|
|
125
|
-
ws.once('error', () => {
|
|
126
|
-
// The server closes invalid connections quickly; rely on the close code.
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
test(
|
|
132
|
-
'dev mode still requires a valid token for protected HTTP and WebSocket access',
|
|
133
|
-
{ timeout: 30000 },
|
|
134
|
-
async (t) => {
|
|
135
|
-
const port = await getFreePort();
|
|
136
|
-
const homeDir = mkdtempSync(path.join(tmpdir(), 'vibelet-dev-auth-'));
|
|
137
|
-
const packageJsonPath = path.resolve(daemonDir, 'package.json');
|
|
138
|
-
const validToken = 'test-token';
|
|
139
|
-
|
|
140
|
-
const child = spawn(process.execPath, [tsxCliPath, 'src/index.ts'], {
|
|
141
|
-
cwd: daemonDir,
|
|
142
|
-
env: {
|
|
143
|
-
...process.env,
|
|
144
|
-
HOME: homeDir,
|
|
145
|
-
VIBE_PORT: String(port),
|
|
146
|
-
VIBE_DEV: '1',
|
|
147
|
-
VIBE_TOKEN: validToken,
|
|
148
|
-
CLAUDE_PATH: process.execPath,
|
|
149
|
-
CODEX_PATH: process.execPath,
|
|
150
|
-
},
|
|
151
|
-
stdio: 'ignore',
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
t.after(async () => {
|
|
155
|
-
await cleanupChild(child, port);
|
|
156
|
-
rmSync(homeDir, { recursive: true, force: true });
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
await waitForPortState(port, true, 15000);
|
|
160
|
-
|
|
161
|
-
const invalidUrl = new URL(`http://127.0.0.1:${port}/file`);
|
|
162
|
-
invalidUrl.searchParams.set('path', packageJsonPath);
|
|
163
|
-
invalidUrl.searchParams.set('token', '1');
|
|
164
|
-
|
|
165
|
-
const invalidResponse = await fetch(invalidUrl);
|
|
166
|
-
assert.equal(invalidResponse.status, 401);
|
|
167
|
-
|
|
168
|
-
const validUrl = new URL(`http://127.0.0.1:${port}/file`);
|
|
169
|
-
validUrl.searchParams.set('path', packageJsonPath);
|
|
170
|
-
validUrl.searchParams.set('token', validToken);
|
|
171
|
-
|
|
172
|
-
const validResponse = await fetch(validUrl);
|
|
173
|
-
assert.equal(validResponse.status, 200);
|
|
174
|
-
assert.match(await validResponse.text(), /"name": "@vibelet\/daemon"/);
|
|
175
|
-
|
|
176
|
-
const closedCode = await waitForCloseCode(`ws://127.0.0.1:${port}?token=1`);
|
|
177
|
-
assert.equal(closedCode, 4001);
|
|
178
|
-
|
|
179
|
-
const ws = await openWebSocket(`ws://127.0.0.1:${port}?token=${encodeURIComponent(validToken)}`);
|
|
180
|
-
ws.close();
|
|
181
|
-
await new Promise<void>((resolve) => ws.once('close', () => resolve()));
|
|
182
|
-
},
|
|
183
|
-
);
|