vibelet 1.2.210 → 1.2.215

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/vibelet.mjs CHANGED
@@ -11340,7 +11340,7 @@ module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("zlib");
11340
11340
 
11341
11341
  /***/ }),
11342
11342
 
11343
- /***/ 6895:
11343
+ /***/ 1484:
11344
11344
  /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => {
11345
11345
 
11346
11346
 
@@ -11362,218 +11362,16 @@ var external_node_fs_ = __nccwpck_require__(3024);
11362
11362
  var external_node_module_ = __nccwpck_require__(8995);
11363
11363
  // EXTERNAL MODULE: external "node:path"
11364
11364
  var external_node_path_ = __nccwpck_require__(6760);
11365
- // EXTERNAL MODULE: external "node:fs/promises"
11366
- var promises_ = __nccwpck_require__(1455);
11367
- // EXTERNAL MODULE: external "node:util"
11368
- var external_node_util_ = __nccwpck_require__(7975);
11369
- ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/managed-binary.mjs
11370
-
11371
-
11372
-
11373
-
11374
-
11375
-
11376
- const execFileAsync = (0,external_node_util_.promisify)(external_node_child_process_.execFile);
11377
- const DEFAULT_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
11378
- const DEFAULT_UPDATE_TIMEOUT_MS = 30_000;
11379
-
11380
- function parseCloudflaredVersion(output) {
11381
- return String(output || '').match(/cloudflared version\s+([0-9]+(?:\.[0-9]+){1,2})/i)?.[1] ?? null;
11382
- }
11383
-
11384
- async function readUpdateState(updateStatePath) {
11385
- try {
11386
- const state = JSON.parse(await (0,promises_.readFile)(updateStatePath, 'utf8'));
11387
- return state && typeof state === 'object' ? state : null;
11388
- } catch {
11389
- return null;
11390
- }
11391
- }
11392
-
11393
- async function writeUpdateState(updateStatePath, state) {
11394
- await (0,promises_.mkdir)((0,external_node_path_.dirname)(updateStatePath), { recursive: true });
11395
- const temporaryPath = `${updateStatePath}.tmp-${process.pid}-${Date.now()}`;
11396
- await (0,promises_.writeFile)(temporaryPath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
11397
- await (0,promises_.rename)(temporaryPath, updateStatePath);
11398
- }
11399
-
11400
- async function runVersion(binaryPath, timeoutMs, runCommand) {
11401
- if (!binaryPath || !(0,external_node_fs_.existsSync)(binaryPath)) return null;
11402
- try {
11403
- const { stdout, stderr } = await runCommand(binaryPath, ['--version'], {
11404
- encoding: 'utf8',
11405
- timeout: timeoutMs,
11406
- maxBuffer: 1024 * 1024,
11407
- });
11408
- return parseCloudflaredVersion(`${stdout || ''}\n${stderr || ''}`);
11409
- } catch {
11410
- return null;
11411
- }
11412
- }
11413
-
11414
- async function probeCloudflaredLaunchSpecVersion(
11415
- launchSpec,
11416
- { timeoutMs = 5_000, runCommand = execFileAsync } = {},
11417
- ) {
11418
- try {
11419
- const { stdout, stderr } = await runCommand(launchSpec.command, [...launchSpec.args, '--version'], {
11420
- encoding: 'utf8',
11421
- timeout: timeoutMs,
11422
- maxBuffer: 1024 * 1024,
11423
- ...(launchSpec.shell ? { shell: true } : {}),
11424
- });
11425
- return parseCloudflaredVersion(`${stdout || ''}\n${stderr || ''}`);
11426
- } catch {
11427
- return null;
11428
- }
11429
- }
11430
-
11431
- async function installLatestToTemporaryPath({ launchSpec, binaryPath, timeoutMs, platform, runCommand }) {
11432
- const temporaryDir = (0,external_node_path_.join)((0,external_node_path_.dirname)(binaryPath), `.cloudflared-update-${process.pid}-${Date.now()}`);
11433
- const temporaryBinaryPath = (0,external_node_path_.join)(temporaryDir, (0,external_node_path_.basename)(binaryPath));
11434
- await (0,promises_.mkdir)(temporaryDir, { recursive: true });
11435
- try {
11436
- await runCommand(launchSpec.command, [...launchSpec.args, 'bin', 'install', 'latest'], {
11437
- encoding: 'utf8',
11438
- timeout: timeoutMs,
11439
- maxBuffer: 4 * 1024 * 1024,
11440
- env: { ...process.env, CLOUDFLARED_BIN: temporaryBinaryPath },
11441
- ...(launchSpec.shell ? { shell: true } : {}),
11442
- });
11443
- const version = await runVersion(temporaryBinaryPath, timeoutMs, runCommand);
11444
- if (!version) throw new Error('downloaded cloudflared did not report a valid version');
11445
-
11446
- if (platform === 'win32' && (0,external_node_fs_.existsSync)(binaryPath)) {
11447
- const backupPath = `${binaryPath}.previous-${process.pid}`;
11448
- await (0,promises_.rename)(binaryPath, backupPath);
11449
- try {
11450
- await (0,promises_.rename)(temporaryBinaryPath, binaryPath);
11451
- await (0,promises_.rm)(backupPath, { force: true });
11452
- } catch (error) {
11453
- await (0,promises_.rename)(backupPath, binaryPath).catch(() => undefined);
11454
- throw error;
11455
- }
11456
- } else {
11457
- await (0,promises_.rename)(temporaryBinaryPath, binaryPath);
11458
- }
11459
- return version;
11460
- } finally {
11461
- await (0,promises_.rm)(temporaryDir, { recursive: true, force: true });
11462
- }
11463
- }
11464
-
11465
- async function resolveManagedCloudflared({
11466
- launchSpec,
11467
- updateStatePath,
11468
- now = Date.now(),
11469
- updateIntervalMs = DEFAULT_UPDATE_INTERVAL_MS,
11470
- timeoutMs = DEFAULT_UPDATE_TIMEOUT_MS,
11471
- platform = process.platform,
11472
- runCommand = execFileAsync,
11473
- }) {
11474
- const state = await readUpdateState(updateStatePath);
11475
- const lastAttemptAt = Date.parse(state?.lastAttemptAt || '');
11476
- const recentlyAttempted = Number.isFinite(lastAttemptAt) && now - lastAttemptAt < updateIntervalMs;
11477
- if (recentlyAttempted) {
11478
- return {
11479
- launchSpec,
11480
- version: state?.installedVersion ?? null,
11481
- update: 'skipped',
11482
- };
11483
- }
11484
-
11485
- const attemptedAt = new Date(now).toISOString();
11486
- const binaryPath = launchSpec.managedBinaryPath;
11487
- if (launchSpec.source !== 'dependency' || !binaryPath) {
11488
- const version = state?.installedVersion ?? null;
11489
- let stateWarning = '';
11490
- try {
11491
- await writeUpdateState(updateStatePath, {
11492
- ...state,
11493
- lastAttemptAt: attemptedAt,
11494
- installedVersion: version,
11495
- });
11496
- } catch (error) {
11497
- stateWarning = ` Update state could not be saved: ${error instanceof Error ? error.message : String(error)}`;
11498
- }
11499
- return {
11500
- launchSpec,
11501
- version,
11502
- update: 'skipped',
11503
- warning: `Skipping cloudflared self-update for unmanaged ${launchSpec.source} executable: ${launchSpec.description}.${stateWarning}`,
11504
- };
11505
- }
11506
-
11507
- const previousVersion = await runVersion(binaryPath, timeoutMs, runCommand);
11508
- try {
11509
- const installedVersion = await installLatestToTemporaryPath({
11510
- launchSpec,
11511
- binaryPath,
11512
- timeoutMs,
11513
- platform,
11514
- runCommand,
11515
- });
11516
- await writeUpdateState(updateStatePath, {
11517
- lastAttemptAt: attemptedAt,
11518
- lastSuccessAt: attemptedAt,
11519
- installedVersion,
11520
- });
11521
- return {
11522
- launchSpec,
11523
- version: installedVersion,
11524
- update: previousVersion ? 'updated' : 'fresh',
11525
- };
11526
- } catch (error) {
11527
- const fallbackVersion = previousVersion || (await runVersion(binaryPath, timeoutMs, runCommand));
11528
- await writeUpdateState(updateStatePath, {
11529
- ...state,
11530
- lastAttemptAt: attemptedAt,
11531
- installedVersion: fallbackVersion,
11532
- }).catch(() => undefined);
11533
- return {
11534
- launchSpec,
11535
- version: fallbackVersion,
11536
- update: 'failed',
11537
- warning: `cloudflared update failed; continuing with ${fallbackVersion || 'the existing launcher'}: ${error instanceof Error ? error.message : String(error)}`,
11538
- };
11539
- }
11540
- }
11541
-
11542
- ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/index.mjs
11543
-
11544
-
11545
-
11365
+ ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/launch-spec.mjs
11546
11366
 
11547
11367
 
11548
11368
 
11549
11369
 
11550
11370
 
11551
- const QUICK_TUNNEL_URL_PATTERN = /https:\/\/[a-z0-9-]+\.trycloudflare\.com(?=$|[\s"',)\]])/g;
11552
11371
  const npmFallbackArgs = ['--yes', '--package=cloudflared', 'cloudflared'];
11553
11372
  const npmFallbackDescription = `npx ${npmFallbackArgs.join(' ')}`;
11554
11373
  const defaultUrlTimeoutMs = 90_000;
11555
11374
 
11556
- function supportsFastAutoProtocolFallback(version) {
11557
- if (!version) return false;
11558
- const parts = version.split('.').map(Number);
11559
- const threshold = [2026, 5, 2];
11560
- for (let index = 0; index < threshold.length; index += 1) {
11561
- const value = parts[index] || 0;
11562
- if (value !== threshold[index]) return value > threshold[index];
11563
- }
11564
- return true;
11565
- }
11566
-
11567
- function extractQuickTunnelUrl(logContent) {
11568
- if (typeof logContent !== 'string' || logContent.length === 0) {
11569
- return null;
11570
- }
11571
-
11572
- const match = QUICK_TUNNEL_URL_PATTERN.exec(logContent);
11573
- QUICK_TUNNEL_URL_PATTERN.lastIndex = 0;
11574
- return match?.[0] ?? null;
11575
- }
11576
-
11577
11375
  function isQuickTunnelUrl(url) {
11578
11376
  if (typeof url !== 'string' || !url.trim()) return false;
11579
11377
  try {
@@ -11657,55 +11455,6 @@ function readProcessCommandLine(pid, { platform = process.platform, spawnSyncImp
11657
11455
  return result.status === 0 ? String(result.stdout || '').trim() : '';
11658
11456
  }
11659
11457
 
11660
- function extractCloudflaredMetricsBaseUrl(logContent) {
11661
- if (typeof logContent !== 'string' || logContent.length === 0) {
11662
- return null;
11663
- }
11664
- const match = logContent.match(
11665
- /Starting metrics server on (?:https?:\/\/)?(127\.0\.0\.1|localhost|\[::1\]):(\d+)\/metrics\b/iu,
11666
- );
11667
- if (!match) return null;
11668
- return `http://${match[1]}:${match[2]}`;
11669
- }
11670
-
11671
- async function probeCloudflaredReadiness(logContent, { fetchFn = globalThis.fetch, timeoutMs = 1000 } = {}) {
11672
- const metricsBaseUrl = extractCloudflaredMetricsBaseUrl(logContent);
11673
- if (!metricsBaseUrl || typeof fetchFn !== 'function') return false;
11674
-
11675
- const controller = new AbortController();
11676
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
11677
- try {
11678
- const response = await fetchFn(`${metricsBaseUrl}/ready`, { signal: controller.signal });
11679
- return response.ok;
11680
- } catch {
11681
- return false;
11682
- } finally {
11683
- clearTimeout(timeout);
11684
- }
11685
- }
11686
-
11687
- async function probeQuickTunnelPublicHealth(state, { fetchFn = globalThis.fetch, timeoutMs = 1_500 } = {}) {
11688
- if (!isQuickTunnelUrl(state?.url) || typeof fetchFn !== 'function') return false;
11689
-
11690
- const controller = new AbortController();
11691
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
11692
- try {
11693
- const healthUrl = new URL('/health', state.url);
11694
- healthUrl.searchParams.set('_vibelet_reuse_probe', String(Date.now()));
11695
- const response = await fetchFn(healthUrl, {
11696
- headers: { 'cache-control': 'no-cache' },
11697
- signal: controller.signal,
11698
- });
11699
- if (!response.ok) return false;
11700
- const payload = await response.json().catch(() => null);
11701
- return payload?.status === 'ok';
11702
- } catch {
11703
- return false;
11704
- } finally {
11705
- clearTimeout(timeout);
11706
- }
11707
- }
11708
-
11709
11458
  function getNpmFallbackCommand(platform = process.platform) {
11710
11459
  return platform === 'win32' ? 'npx.cmd' : 'npx';
11711
11460
  }
@@ -11803,79 +11552,389 @@ function resolveInstalledCloudflaredCliPath({
11803
11552
  return null;
11804
11553
  }
11805
11554
 
11806
- let packageJson;
11807
- try {
11808
- packageJson = JSON.parse((0,external_node_fs_.readFileSync)(packageJsonPath, 'utf8'));
11809
- } catch {
11810
- return null;
11811
- }
11555
+ let packageJson;
11556
+ try {
11557
+ packageJson = JSON.parse((0,external_node_fs_.readFileSync)(packageJsonPath, 'utf8'));
11558
+ } catch {
11559
+ return null;
11560
+ }
11561
+
11562
+ const relativeBinPath = typeof packageJson.bin === 'string' ? packageJson.bin : packageJson.bin?.cloudflared;
11563
+
11564
+ if (typeof relativeBinPath !== 'string' || !relativeBinPath) {
11565
+ return null;
11566
+ }
11567
+
11568
+ const cliPath = (0,external_node_path_.isAbsolute)(relativeBinPath) ? relativeBinPath : (0,external_node_path_.resolve)((0,external_node_path_.dirname)(packageJsonPath), relativeBinPath);
11569
+
11570
+ return (0,external_node_fs_.existsSync)(cliPath) ? cliPath : null;
11571
+ }
11572
+
11573
+ function createDependencyLaunchSpec(dependencyCliPath, platform = process.platform) {
11574
+ return {
11575
+ command: process.execPath,
11576
+ args: [dependencyCliPath],
11577
+ source: 'dependency',
11578
+ description: dependencyCliPath,
11579
+ managedBinaryPath: (0,external_node_path_.join)(
11580
+ (0,external_node_path_.dirname)((0,external_node_path_.dirname)(dependencyCliPath)),
11581
+ 'bin',
11582
+ platform === 'win32' ? 'cloudflared.exe' : 'cloudflared',
11583
+ ),
11584
+ urlTimeoutMs: defaultUrlTimeoutMs,
11585
+ };
11586
+ }
11587
+
11588
+ function resolveCloudflaredLaunchSpec({
11589
+ pathValue = process.env.PATH ?? '',
11590
+ pathExtValue = process.env.PATHEXT ?? '',
11591
+ platform = process.platform,
11592
+ resolvePackageJsonPath,
11593
+ } = {}) {
11594
+ const directPathCandidates = findExecutablesInPath('cloudflared', { pathValue, pathExtValue, platform });
11595
+ const directPath =
11596
+ directPathCandidates.find((candidatePath) => !isNodeModulesBinPath(candidatePath)) ??
11597
+ directPathCandidates[0] ??
11598
+ null;
11599
+ if (directPath) {
11600
+ return {
11601
+ command: directPath,
11602
+ args: [],
11603
+ source: 'path',
11604
+ description: directPath,
11605
+ ...(isWindowsCommandScript(directPath, platform) ? { shell: true } : {}),
11606
+ urlTimeoutMs: defaultUrlTimeoutMs,
11607
+ };
11608
+ }
11609
+
11610
+ const dependencyCliPath = resolveInstalledCloudflaredCliPath({ resolvePackageJsonPath });
11611
+ if (dependencyCliPath) {
11612
+ return createDependencyLaunchSpec(dependencyCliPath, platform);
11613
+ }
11614
+
11615
+ return {
11616
+ command: getNpmFallbackCommand(platform),
11617
+ args: [...npmFallbackArgs],
11618
+ source: 'npm',
11619
+ description: npmFallbackDescription,
11620
+ ...(platform === 'win32' ? { shell: true } : {}),
11621
+ urlTimeoutMs: defaultUrlTimeoutMs,
11622
+ };
11623
+ }
11624
+
11625
+ function resolvePreferredCloudflaredLaunchSpec() {
11626
+ const dependencyCliPath = resolveInstalledCloudflaredCliPath();
11627
+ return dependencyCliPath ? createDependencyLaunchSpec(dependencyCliPath) : resolveCloudflaredLaunchSpec();
11628
+ }
11629
+
11630
+ function cloudflaredChildEnv(launchSpec, env = process.env, versions = process.versions) {
11631
+ // Electron must run the npm CLI as Node; app mode can exit while its child
11632
+ // tunnel is still starting, losing ownership of the process.
11633
+ return launchSpec.source === 'dependency' && versions.electron ? { ...env, ELECTRON_RUN_AS_NODE: '1' } : env;
11634
+ }
11635
+
11636
+ ;// CONCATENATED MODULE: external "node:http"
11637
+ const external_node_http_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http");
11638
+ ;// CONCATENATED MODULE: external "node:https"
11639
+ const external_node_https_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:https");
11640
+ ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/health-fetch.mjs
11641
+
11642
+
11643
+
11644
+ // This package runs in Node and Electron plugin hosts. Renderer fetch applies
11645
+ // browser CORS to cloudflared's local metrics endpoint, which has no CORS headers.
11646
+ function fetchTunnelHealth(input, { signal, headers } = {}) {
11647
+ const url = new URL(input);
11648
+ const request = url.protocol === 'https:' ? external_node_https_namespaceObject.request : url.protocol === 'http:' ? external_node_http_namespaceObject.request : null;
11649
+ if (!request) return Promise.reject(new Error('Unsupported tunnel health protocol'));
11650
+ return new Promise((resolve, reject) => {
11651
+ const req = request(url, { signal, headers, method: 'GET' }, (res) => {
11652
+ const chunks = [];
11653
+ let size = 0;
11654
+ res.on('data', (chunk) => {
11655
+ size += chunk.length;
11656
+ if (size > 64 * 1024) {
11657
+ res.destroy(new Error('Tunnel health response is too large'));
11658
+ return;
11659
+ }
11660
+ chunks.push(chunk);
11661
+ });
11662
+ res.on('error', reject);
11663
+ res.on('end', () => {
11664
+ const body = Buffer.concat(chunks).toString('utf8');
11665
+ resolve({
11666
+ ok: res.statusCode >= 200 && res.statusCode < 300,
11667
+ json: async () => JSON.parse(body),
11668
+ });
11669
+ });
11670
+ });
11671
+ req.on('error', reject);
11672
+ req.end();
11673
+ });
11674
+ }
11675
+
11676
+ // EXTERNAL MODULE: external "node:fs/promises"
11677
+ var promises_ = __nccwpck_require__(1455);
11678
+ // EXTERNAL MODULE: external "node:util"
11679
+ var external_node_util_ = __nccwpck_require__(7975);
11680
+ ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/managed-binary.mjs
11681
+
11682
+
11683
+
11684
+
11685
+
11686
+
11687
+
11688
+ const execFileAsync = (0,external_node_util_.promisify)(external_node_child_process_.execFile);
11689
+ const DEFAULT_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
11690
+ const DEFAULT_UPDATE_TIMEOUT_MS = 30_000;
11691
+
11692
+ function parseCloudflaredVersion(output) {
11693
+ return String(output || '').match(/cloudflared version\s+([0-9]+(?:\.[0-9]+){1,2})/i)?.[1] ?? null;
11694
+ }
11695
+
11696
+ async function readUpdateState(updateStatePath) {
11697
+ try {
11698
+ const state = JSON.parse(await (0,promises_.readFile)(updateStatePath, 'utf8'));
11699
+ return state && typeof state === 'object' ? state : null;
11700
+ } catch {
11701
+ return null;
11702
+ }
11703
+ }
11704
+
11705
+ async function writeUpdateState(updateStatePath, state) {
11706
+ await (0,promises_.mkdir)((0,external_node_path_.dirname)(updateStatePath), { recursive: true });
11707
+ const temporaryPath = `${updateStatePath}.tmp-${process.pid}-${Date.now()}`;
11708
+ await (0,promises_.writeFile)(temporaryPath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
11709
+ await (0,promises_.rename)(temporaryPath, updateStatePath);
11710
+ }
11711
+
11712
+ async function runVersion(binaryPath, timeoutMs, runCommand) {
11713
+ if (!binaryPath || !(0,external_node_fs_.existsSync)(binaryPath)) return null;
11714
+ try {
11715
+ const { stdout, stderr } = await runCommand(binaryPath, ['--version'], {
11716
+ encoding: 'utf8',
11717
+ timeout: timeoutMs,
11718
+ maxBuffer: 1024 * 1024,
11719
+ });
11720
+ return parseCloudflaredVersion(`${stdout || ''}\n${stderr || ''}`);
11721
+ } catch {
11722
+ return null;
11723
+ }
11724
+ }
11725
+
11726
+ async function probeCloudflaredLaunchSpecVersion(
11727
+ launchSpec,
11728
+ { timeoutMs = 5_000, runCommand = execFileAsync } = {},
11729
+ ) {
11730
+ try {
11731
+ const { stdout, stderr } = await runCommand(launchSpec.command, [...launchSpec.args, '--version'], {
11732
+ encoding: 'utf8',
11733
+ timeout: timeoutMs,
11734
+ maxBuffer: 1024 * 1024,
11735
+ env: cloudflaredChildEnv(launchSpec),
11736
+ ...(launchSpec.shell ? { shell: true } : {}),
11737
+ });
11738
+ return parseCloudflaredVersion(`${stdout || ''}\n${stderr || ''}`);
11739
+ } catch {
11740
+ return null;
11741
+ }
11742
+ }
11743
+
11744
+ async function installLatestToTemporaryPath({ launchSpec, binaryPath, timeoutMs, platform, runCommand }) {
11745
+ const temporaryDir = (0,external_node_path_.join)((0,external_node_path_.dirname)(binaryPath), `.cloudflared-update-${process.pid}-${Date.now()}`);
11746
+ const temporaryBinaryPath = (0,external_node_path_.join)(temporaryDir, (0,external_node_path_.basename)(binaryPath));
11747
+ await (0,promises_.mkdir)(temporaryDir, { recursive: true });
11748
+ try {
11749
+ await runCommand(launchSpec.command, [...launchSpec.args, 'bin', 'install', 'latest'], {
11750
+ encoding: 'utf8',
11751
+ timeout: timeoutMs,
11752
+ maxBuffer: 4 * 1024 * 1024,
11753
+ env: cloudflaredChildEnv(launchSpec, { ...process.env, CLOUDFLARED_BIN: temporaryBinaryPath }),
11754
+ ...(launchSpec.shell ? { shell: true } : {}),
11755
+ });
11756
+ const version = await runVersion(temporaryBinaryPath, timeoutMs, runCommand);
11757
+ if (!version) throw new Error('downloaded cloudflared did not report a valid version');
11758
+
11759
+ if (platform === 'win32' && (0,external_node_fs_.existsSync)(binaryPath)) {
11760
+ const backupPath = `${binaryPath}.previous-${process.pid}`;
11761
+ await (0,promises_.rename)(binaryPath, backupPath);
11762
+ try {
11763
+ await (0,promises_.rename)(temporaryBinaryPath, binaryPath);
11764
+ await (0,promises_.rm)(backupPath, { force: true });
11765
+ } catch (error) {
11766
+ await (0,promises_.rename)(backupPath, binaryPath).catch(() => undefined);
11767
+ throw error;
11768
+ }
11769
+ } else {
11770
+ await (0,promises_.rename)(temporaryBinaryPath, binaryPath);
11771
+ }
11772
+ return version;
11773
+ } finally {
11774
+ await (0,promises_.rm)(temporaryDir, { recursive: true, force: true });
11775
+ }
11776
+ }
11777
+
11778
+ async function resolveManagedCloudflared({
11779
+ launchSpec,
11780
+ updateStatePath,
11781
+ now = Date.now(),
11782
+ updateIntervalMs = DEFAULT_UPDATE_INTERVAL_MS,
11783
+ timeoutMs = DEFAULT_UPDATE_TIMEOUT_MS,
11784
+ platform = process.platform,
11785
+ runCommand = execFileAsync,
11786
+ }) {
11787
+ const state = await readUpdateState(updateStatePath);
11788
+ const lastAttemptAt = Date.parse(state?.lastAttemptAt || '');
11789
+ const recentlyAttempted = Number.isFinite(lastAttemptAt) && now - lastAttemptAt < updateIntervalMs;
11790
+ if (recentlyAttempted) {
11791
+ return {
11792
+ launchSpec,
11793
+ version: state?.installedVersion ?? null,
11794
+ update: 'skipped',
11795
+ };
11796
+ }
11797
+
11798
+ const attemptedAt = new Date(now).toISOString();
11799
+ const binaryPath = launchSpec.managedBinaryPath;
11800
+ if (launchSpec.source !== 'dependency' || !binaryPath) {
11801
+ const version = state?.installedVersion ?? null;
11802
+ let stateWarning = '';
11803
+ try {
11804
+ await writeUpdateState(updateStatePath, {
11805
+ ...state,
11806
+ lastAttemptAt: attemptedAt,
11807
+ installedVersion: version,
11808
+ });
11809
+ } catch (error) {
11810
+ stateWarning = ` Update state could not be saved: ${error instanceof Error ? error.message : String(error)}`;
11811
+ }
11812
+ return {
11813
+ launchSpec,
11814
+ version,
11815
+ update: 'skipped',
11816
+ warning: `Skipping cloudflared self-update for unmanaged ${launchSpec.source} executable: ${launchSpec.description}.${stateWarning}`,
11817
+ };
11818
+ }
11819
+
11820
+ const previousVersion = await runVersion(binaryPath, timeoutMs, runCommand);
11821
+ try {
11822
+ const installedVersion = await installLatestToTemporaryPath({
11823
+ launchSpec,
11824
+ binaryPath,
11825
+ timeoutMs,
11826
+ platform,
11827
+ runCommand,
11828
+ });
11829
+ await writeUpdateState(updateStatePath, {
11830
+ lastAttemptAt: attemptedAt,
11831
+ lastSuccessAt: attemptedAt,
11832
+ installedVersion,
11833
+ });
11834
+ return {
11835
+ launchSpec,
11836
+ version: installedVersion,
11837
+ update: previousVersion ? 'updated' : 'fresh',
11838
+ };
11839
+ } catch (error) {
11840
+ const fallbackVersion = previousVersion || (await runVersion(binaryPath, timeoutMs, runCommand));
11841
+ await writeUpdateState(updateStatePath, {
11842
+ ...state,
11843
+ lastAttemptAt: attemptedAt,
11844
+ installedVersion: fallbackVersion,
11845
+ }).catch(() => undefined);
11846
+ return {
11847
+ launchSpec,
11848
+ version: fallbackVersion,
11849
+ update: 'failed',
11850
+ warning: `cloudflared update failed; continuing with ${fallbackVersion || 'the existing launcher'}: ${error instanceof Error ? error.message : String(error)}`,
11851
+ };
11852
+ }
11853
+ }
11854
+
11855
+ ;// CONCATENATED MODULE: ../../packages/cloudflared-tunnel/index.mjs
11856
+
11857
+
11858
+
11859
+
11860
+
11861
+
11862
+
11863
+
11864
+
11812
11865
 
11813
- const relativeBinPath = typeof packageJson.bin === 'string' ? packageJson.bin : packageJson.bin?.cloudflared;
11814
11866
 
11815
- if (typeof relativeBinPath !== 'string' || !relativeBinPath) {
11816
- return null;
11817
- }
11818
11867
 
11819
- const cliPath = (0,external_node_path_.isAbsolute)(relativeBinPath) ? relativeBinPath : (0,external_node_path_.resolve)((0,external_node_path_.dirname)(packageJsonPath), relativeBinPath);
11868
+ const QUICK_TUNNEL_URL_PATTERN = /https:\/\/[a-z0-9-]+\.trycloudflare\.com(?=$|[\s"',)\]])/g;
11820
11869
 
11821
- return (0,external_node_fs_.existsSync)(cliPath) ? cliPath : null;
11870
+ function supportsFastAutoProtocolFallback(version) {
11871
+ if (!version) return false;
11872
+ const parts = version.split('.').map(Number);
11873
+ const threshold = [2026, 5, 2];
11874
+ for (let index = 0; index < threshold.length; index += 1) {
11875
+ const value = parts[index] || 0;
11876
+ if (value !== threshold[index]) return value > threshold[index];
11877
+ }
11878
+ return true;
11822
11879
  }
11823
11880
 
11824
- function createDependencyLaunchSpec(dependencyCliPath, platform = process.platform) {
11825
- return {
11826
- command: process.execPath,
11827
- args: [dependencyCliPath],
11828
- source: 'dependency',
11829
- description: dependencyCliPath,
11830
- managedBinaryPath: (0,external_node_path_.join)(
11831
- (0,external_node_path_.dirname)((0,external_node_path_.dirname)(dependencyCliPath)),
11832
- 'bin',
11833
- platform === 'win32' ? 'cloudflared.exe' : 'cloudflared',
11834
- ),
11835
- urlTimeoutMs: defaultUrlTimeoutMs,
11836
- };
11881
+ function extractQuickTunnelUrl(logContent) {
11882
+ if (typeof logContent !== 'string' || logContent.length === 0) {
11883
+ return null;
11884
+ }
11885
+
11886
+ const match = QUICK_TUNNEL_URL_PATTERN.exec(logContent);
11887
+ QUICK_TUNNEL_URL_PATTERN.lastIndex = 0;
11888
+ return match?.[0] ?? null;
11837
11889
  }
11838
11890
 
11839
- function resolveCloudflaredLaunchSpec({
11840
- pathValue = process.env.PATH ?? '',
11841
- pathExtValue = process.env.PATHEXT ?? '',
11842
- platform = process.platform,
11843
- resolvePackageJsonPath,
11844
- } = {}) {
11845
- const directPathCandidates = findExecutablesInPath('cloudflared', { pathValue, pathExtValue, platform });
11846
- const directPath =
11847
- directPathCandidates.find((candidatePath) => !isNodeModulesBinPath(candidatePath)) ??
11848
- directPathCandidates[0] ??
11849
- null;
11850
- if (directPath) {
11851
- return {
11852
- command: directPath,
11853
- args: [],
11854
- source: 'path',
11855
- description: directPath,
11856
- ...(isWindowsCommandScript(directPath, platform) ? { shell: true } : {}),
11857
- urlTimeoutMs: defaultUrlTimeoutMs,
11858
- };
11891
+ function extractCloudflaredMetricsBaseUrl(logContent) {
11892
+ if (typeof logContent !== 'string' || logContent.length === 0) {
11893
+ return null;
11859
11894
  }
11895
+ const match = logContent.match(
11896
+ /Starting metrics server on (?:https?:\/\/)?(127\.0\.0\.1|localhost|\[::1\]):(\d+)\/metrics\b/iu,
11897
+ );
11898
+ if (!match) return null;
11899
+ return `http://${match[1]}:${match[2]}`;
11900
+ }
11860
11901
 
11861
- const dependencyCliPath = resolveInstalledCloudflaredCliPath({ resolvePackageJsonPath });
11862
- if (dependencyCliPath) {
11863
- return createDependencyLaunchSpec(dependencyCliPath, platform);
11864
- }
11902
+ async function probeCloudflaredReadiness(logContent, { fetchFn = fetchTunnelHealth, timeoutMs = 1000 } = {}) {
11903
+ const metricsBaseUrl = extractCloudflaredMetricsBaseUrl(logContent);
11904
+ if (!metricsBaseUrl || typeof fetchFn !== 'function') return false;
11865
11905
 
11866
- return {
11867
- command: getNpmFallbackCommand(platform),
11868
- args: [...npmFallbackArgs],
11869
- source: 'npm',
11870
- description: npmFallbackDescription,
11871
- ...(platform === 'win32' ? { shell: true } : {}),
11872
- urlTimeoutMs: defaultUrlTimeoutMs,
11873
- };
11906
+ const controller = new AbortController();
11907
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
11908
+ try {
11909
+ const response = await fetchFn(`${metricsBaseUrl}/ready`, { signal: controller.signal });
11910
+ return response.ok;
11911
+ } catch {
11912
+ return false;
11913
+ } finally {
11914
+ clearTimeout(timeout);
11915
+ }
11874
11916
  }
11875
11917
 
11876
- function resolvePreferredCloudflaredLaunchSpec() {
11877
- const dependencyCliPath = resolveInstalledCloudflaredCliPath();
11878
- return dependencyCliPath ? createDependencyLaunchSpec(dependencyCliPath) : resolveCloudflaredLaunchSpec();
11918
+ async function probeQuickTunnelPublicHealth(state, { fetchFn = fetchTunnelHealth, timeoutMs = 1_500 } = {}) {
11919
+ if (!isQuickTunnelUrl(state?.url) || typeof fetchFn !== 'function') return false;
11920
+
11921
+ const controller = new AbortController();
11922
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
11923
+ try {
11924
+ const healthUrl = new URL('/health', state.url);
11925
+ healthUrl.searchParams.set('_vibelet_reuse_probe', String(Date.now()));
11926
+ const response = await fetchFn(healthUrl, {
11927
+ headers: { 'cache-control': 'no-cache' },
11928
+ signal: controller.signal,
11929
+ });
11930
+ if (!response.ok) return false;
11931
+ const payload = await response.json().catch(() => null);
11932
+ return payload?.status === 'ok';
11933
+ } catch {
11934
+ return false;
11935
+ } finally {
11936
+ clearTimeout(timeout);
11937
+ }
11879
11938
  }
11880
11939
 
11881
11940
  function summarizeCloudflaredLog(logContent, { maxLines = 8, maxChars = 1200 } = {}) {
@@ -12187,6 +12246,7 @@ async function startTunnel({
12187
12246
  [...launchSpec.args, 'tunnel', '--no-autoupdate', '--protocol', effectiveProtocol, '--url', originUrl],
12188
12247
  {
12189
12248
  detached: true,
12249
+ env: cloudflaredChildEnv(launchSpec),
12190
12250
  stdio: ['ignore', stdoutLogFd, stderrLogFd],
12191
12251
  windowsHide: true,
12192
12252
  ...(launchSpec.shell ? { shell: true } : {}),
@@ -12382,7 +12442,7 @@ function canUseSystemdUserManager({ spawnSyncImpl = node_child_process__WEBPACK_
12382
12442
 
12383
12443
  /***/ }),
12384
12444
 
12385
- /***/ 877:
12445
+ /***/ 5028:
12386
12446
  /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => {
12387
12447
 
12388
12448
 
@@ -12711,9 +12771,196 @@ var websocket_server = __nccwpck_require__(3261);
12711
12771
 
12712
12772
 
12713
12773
 
12714
- /* harmony default export */ const wrapper = (websocket);
12774
+ /* harmony default export */ const wrapper = (websocket);
12775
+
12776
+ ;// CONCATENATED MODULE: ./bin/terminal-screen.mjs
12777
+ const ANSI_CLEAR_SCREEN = '\x1b[2J\x1b[H';
12778
+ const ANSI_HIDE_CURSOR = '\x1b[?25l';
12779
+ const terminal_screen_ANSI_RESET = '\x1b[0m';
12780
+ const ANSI_BOLD = '\x1b[1m';
12781
+ const ANSI_CYAN = '\x1b[36m';
12782
+ const ANSI_GREEN = '\x1b[32m';
12783
+ const ANSI_YELLOW = '\x1b[33m';
12784
+ const ANSI_RED = '\x1b[31m';
12785
+ const ANSI_BG_BLUE = '\x1b[44m';
12786
+ const ANSI_WHITE = '\x1b[97m';
12787
+
12788
+ function truncateMiddle(value, maxLength) {
12789
+ const text = String(value ?? '');
12790
+ if (text.length <= maxLength) return text;
12791
+ if (maxLength <= 5) return text.slice(0, maxLength);
12792
+ const headLength = Math.ceil((maxLength - 3) / 2);
12793
+ const tailLength = Math.floor((maxLength - 3) / 2);
12794
+ return `${text.slice(0, headLength)}...${text.slice(text.length - tailLength)}`;
12795
+ }
12796
+
12797
+ function truncateEnd(value, maxLength) {
12798
+ const text = String(value ?? '');
12799
+ if (text.length <= maxLength) return text;
12800
+ if (maxLength <= 5) return text.slice(0, maxLength);
12801
+ return `${text.slice(0, maxLength - 3)}...`;
12802
+ }
12803
+
12804
+ function wrapText(text, width) {
12805
+ const words = String(text).split(/\s+/).filter(Boolean);
12806
+ if (words.length === 0) return [''];
12807
+ const lines = [];
12808
+ let current = '';
12809
+ for (const word of words) {
12810
+ if (word.length > width) {
12811
+ if (current) {
12812
+ lines.push(current);
12813
+ current = '';
12814
+ }
12815
+ for (let index = 0; index < word.length; index += width) {
12816
+ lines.push(word.slice(index, index + width));
12817
+ }
12818
+ continue;
12819
+ }
12820
+ const next = current ? `${current} ${word}` : word;
12821
+ if (next.length > width) {
12822
+ lines.push(current);
12823
+ current = word;
12824
+ } else {
12825
+ current = next;
12826
+ }
12827
+ }
12828
+ if (current) lines.push(current);
12829
+ return lines;
12830
+ }
12831
+
12832
+ function padRight(text, width) {
12833
+ const value = String(text);
12834
+ if (value.length >= width) return value.slice(0, width);
12835
+ return `${value}${' '.repeat(width - value.length)}`;
12836
+ }
12837
+
12838
+ function colorize(enabled, style, text) {
12839
+ if (!enabled || !style) return text;
12840
+ return `${style}${text}${terminal_screen_ANSI_RESET}`;
12841
+ }
12842
+
12843
+ function getTerminalControlScreenTone(title) {
12844
+ if (/\bFAILED\b|\bBLOCKED\b/i.test(title)) return 'danger';
12845
+ if (/\bRESTORING\b|\bSTARTING\b/i.test(title)) return 'restore';
12846
+ return 'takeover';
12847
+ }
12848
+
12849
+ function getTerminalControlScreenPalette(tone) {
12850
+ if (tone === 'danger') {
12851
+ return {
12852
+ title: `${ANSI_BOLD}${ANSI_RED}`,
12853
+ subtitle: ANSI_RED,
12854
+ separator: ANSI_RED,
12855
+ label: ANSI_YELLOW,
12856
+ body: '',
12857
+ footer: `${ANSI_BOLD}${ANSI_YELLOW}`,
12858
+ };
12859
+ }
12860
+ if (tone === 'restore') {
12861
+ return {
12862
+ title: `${ANSI_BOLD}${ANSI_CYAN}`,
12863
+ subtitle: ANSI_CYAN,
12864
+ separator: ANSI_CYAN,
12865
+ label: ANSI_CYAN,
12866
+ body: '',
12867
+ footer: `${ANSI_BOLD}${ANSI_GREEN}`,
12868
+ };
12869
+ }
12870
+ return {
12871
+ title: `${ANSI_BOLD}${ANSI_BG_BLUE}${ANSI_WHITE}`,
12872
+ subtitle: ANSI_GREEN,
12873
+ separator: ANSI_GREEN,
12874
+ label: ANSI_CYAN,
12875
+ body: '',
12876
+ footer: `${ANSI_BOLD}${ANSI_YELLOW}`,
12877
+ };
12878
+ }
12879
+
12880
+ function formatTerminalFieldParts(label, value, width, { truncate = 'middle' } = {}) {
12881
+ const prefix = `${label}: `;
12882
+ const maxValueLength = Math.max(8, width - prefix.length);
12883
+ return {
12884
+ label: prefix,
12885
+ value: truncate === 'end' ? truncateEnd(value, maxValueLength) : truncateMiddle(value, maxValueLength),
12886
+ };
12887
+ }
12888
+
12889
+ function formatTerminalControlScreen({
12890
+ title,
12891
+ subtitle,
12892
+ agent,
12893
+ cwd,
12894
+ sessionId,
12895
+ task,
12896
+ bodyLines = [],
12897
+ footerLines = [],
12898
+ columns = 80,
12899
+ rows = 24,
12900
+ color = false,
12901
+ }) {
12902
+ const screenWidth = Math.max(20, Number(columns) || 80);
12903
+ const width = screenWidth < 60 ? screenWidth : Math.min(100, screenWidth);
12904
+ const height = Math.max(20, Number(rows) || 24);
12905
+ const innerWidth = width - 8;
12906
+ const separator = '='.repeat(width);
12907
+ const tone = getTerminalControlScreenTone(title);
12908
+ const palette = getTerminalControlScreenPalette(tone);
12909
+ const fieldLines = [
12910
+ { label: 'Agent', value: agent || 'unknown' },
12911
+ { label: 'Session', value: sessionId || 'pending' },
12912
+ ...(task ? [{ label: 'Task', value: task, truncate: 'end' }] : []),
12913
+ { label: 'Directory', value: cwd || process.cwd() },
12914
+ ].map(({ label, value, truncate }) => {
12915
+ const parts = formatTerminalFieldParts(label, value, innerWidth, { truncate });
12916
+ return `${colorize(color, palette.label, parts.label)}${parts.value}`;
12917
+ });
12918
+ const block = [
12919
+ colorize(color, palette.separator, separator),
12920
+ colorize(color, palette.title, padRight(title, width)),
12921
+ subtitle ? colorize(color, palette.subtitle, padRight(subtitle, width)) : '',
12922
+ colorize(color, palette.separator, separator),
12923
+ '',
12924
+ ...fieldLines,
12925
+ '',
12926
+ ];
12927
+
12928
+ for (const line of bodyLines) {
12929
+ for (const wrapped of wrapText(line, innerWidth)) {
12930
+ block.push(colorize(color, palette.body, padRight(wrapped, width)));
12931
+ }
12932
+ }
12933
+
12934
+ block.push('');
12935
+ for (const line of footerLines) {
12936
+ for (const wrapped of wrapText(line, innerWidth)) {
12937
+ block.push(colorize(color, palette.footer, padRight(wrapped, width)));
12938
+ }
12939
+ }
12940
+ block.push(colorize(color, palette.separator, separator));
12941
+
12942
+ const verticalPadding = Math.max(0, Math.floor((height - block.length) / 3));
12943
+ return `${'\n'.repeat(verticalPadding)}${block.join('\n')}\n`;
12944
+ }
12945
+
12946
+ function normalizeTerminalScreenForTty(screen) {
12947
+ return `\r${String(screen).replace(/\r?\n/g, '\r\n')}`;
12948
+ }
12949
+
12950
+ function renderTerminalControlScreen(stream, options) {
12951
+ const columns = Number(stream?.columns) || 80;
12952
+ const rows = Number(stream?.rows) || 24;
12953
+ const screen = formatTerminalControlScreen({ ...options, columns, rows, color: Boolean(stream?.isTTY) });
12954
+ if (stream?.isTTY) {
12955
+ stream.write(`${ANSI_CLEAR_SCREEN}${ANSI_HIDE_CURSOR}${normalizeTerminalScreenForTty(screen)}`);
12956
+ return;
12957
+ }
12958
+ stream.write(`\n${screen}`);
12959
+ }
12960
+
12961
+ ;// CONCATENATED MODULE: ./bin/terminal-wrapper.mjs
12962
+
12715
12963
 
12716
- ;// CONCATENATED MODULE: ./bin/terminal-wrapper.mjs
12717
12964
 
12718
12965
 
12719
12966
 
@@ -12736,21 +12983,25 @@ const SWITCHBACK_RESTORE_NOTICE_MS = 1200;
12736
12983
  const SWITCHBACK_BUSY_RETRY_DELAY_MS = 750;
12737
12984
  const TERMINAL_CONTROL_RECONNECT_DELAY_MS = 1000;
12738
12985
  const CLAUDE_HOOK_SECRET_HEADER = 'x-vibelet-claude-hook-secret';
12739
- const ANSI_CLEAR_SCREEN = '\x1b[2J\x1b[H';
12740
- const ANSI_HIDE_CURSOR = '\x1b[?25l';
12741
- const terminal_wrapper_ANSI_RESET = '\x1b[0m';
12742
- const ANSI_BOLD = '\x1b[1m';
12743
- const ANSI_CYAN = '\x1b[36m';
12744
- const ANSI_GREEN = '\x1b[32m';
12745
- const ANSI_YELLOW = '\x1b[33m';
12746
- const ANSI_RED = '\x1b[31m';
12747
- const ANSI_BG_BLUE = '\x1b[44m';
12748
- const ANSI_WHITE = '\x1b[97m';
12749
-
12750
12986
  function isTerminalAgentCommand(command) {
12751
12987
  return command === 'claude' || command === 'codex';
12752
12988
  }
12753
12989
 
12990
+ async function waitForTakeoverResponseSent(control, timeoutMs = 5000) {
12991
+ if (!control) return false;
12992
+ let timer;
12993
+ try {
12994
+ return await Promise.race([
12995
+ control.takeoverResponseSent,
12996
+ new Promise((resolve) => {
12997
+ timer = setTimeout(() => resolve(false), timeoutMs);
12998
+ }),
12999
+ ]);
13000
+ } finally {
13001
+ clearTimeout(timer);
13002
+ }
13003
+ }
13004
+
12754
13005
  function resolveNativeAgentCommand(agent, env = process.env) {
12755
13006
  if (agent === 'claude') return env.CLAUDE_PATH || 'claude';
12756
13007
  if (agent === 'codex') return env.CODEX_PATH || 'codex';
@@ -13103,179 +13354,6 @@ function createTerminalModeState(initialMode = 'localNative') {
13103
13354
  };
13104
13355
  }
13105
13356
 
13106
- function truncateMiddle(value, maxLength) {
13107
- const text = String(value ?? '');
13108
- if (text.length <= maxLength) return text;
13109
- if (maxLength <= 5) return text.slice(0, maxLength);
13110
- const headLength = Math.ceil((maxLength - 3) / 2);
13111
- const tailLength = Math.floor((maxLength - 3) / 2);
13112
- return `${text.slice(0, headLength)}...${text.slice(text.length - tailLength)}`;
13113
- }
13114
-
13115
- function truncateEnd(value, maxLength) {
13116
- const text = String(value ?? '');
13117
- if (text.length <= maxLength) return text;
13118
- if (maxLength <= 5) return text.slice(0, maxLength);
13119
- return `${text.slice(0, maxLength - 3)}...`;
13120
- }
13121
-
13122
- function wrapText(text, width) {
13123
- const words = String(text).split(/\s+/).filter(Boolean);
13124
- if (words.length === 0) return [''];
13125
- const lines = [];
13126
- let current = '';
13127
- for (const word of words) {
13128
- if (word.length > width) {
13129
- if (current) {
13130
- lines.push(current);
13131
- current = '';
13132
- }
13133
- for (let index = 0; index < word.length; index += width) {
13134
- lines.push(word.slice(index, index + width));
13135
- }
13136
- continue;
13137
- }
13138
- const next = current ? `${current} ${word}` : word;
13139
- if (next.length > width) {
13140
- lines.push(current);
13141
- current = word;
13142
- } else {
13143
- current = next;
13144
- }
13145
- }
13146
- if (current) lines.push(current);
13147
- return lines;
13148
- }
13149
-
13150
- function padRight(text, width) {
13151
- const value = String(text);
13152
- if (value.length >= width) return value.slice(0, width);
13153
- return `${value}${' '.repeat(width - value.length)}`;
13154
- }
13155
-
13156
- function colorize(enabled, style, text) {
13157
- if (!enabled || !style) return text;
13158
- return `${style}${text}${terminal_wrapper_ANSI_RESET}`;
13159
- }
13160
-
13161
- function getTerminalControlScreenTone(title) {
13162
- if (/\bFAILED\b|\bBLOCKED\b/i.test(title)) return 'danger';
13163
- if (/\bRESTORING\b|\bSTARTING\b/i.test(title)) return 'restore';
13164
- return 'takeover';
13165
- }
13166
-
13167
- function getTerminalControlScreenPalette(tone) {
13168
- if (tone === 'danger') {
13169
- return {
13170
- title: `${ANSI_BOLD}${ANSI_RED}`,
13171
- subtitle: ANSI_RED,
13172
- separator: ANSI_RED,
13173
- label: ANSI_YELLOW,
13174
- body: '',
13175
- footer: `${ANSI_BOLD}${ANSI_YELLOW}`,
13176
- };
13177
- }
13178
- if (tone === 'restore') {
13179
- return {
13180
- title: `${ANSI_BOLD}${ANSI_CYAN}`,
13181
- subtitle: ANSI_CYAN,
13182
- separator: ANSI_CYAN,
13183
- label: ANSI_CYAN,
13184
- body: '',
13185
- footer: `${ANSI_BOLD}${ANSI_GREEN}`,
13186
- };
13187
- }
13188
- return {
13189
- title: `${ANSI_BOLD}${ANSI_BG_BLUE}${ANSI_WHITE}`,
13190
- subtitle: ANSI_GREEN,
13191
- separator: ANSI_GREEN,
13192
- label: ANSI_CYAN,
13193
- body: '',
13194
- footer: `${ANSI_BOLD}${ANSI_YELLOW}`,
13195
- };
13196
- }
13197
-
13198
- function formatTerminalFieldParts(label, value, width, { truncate = 'middle' } = {}) {
13199
- const prefix = `${label}: `;
13200
- const maxValueLength = Math.max(8, width - prefix.length);
13201
- return {
13202
- label: prefix,
13203
- value: truncate === 'end' ? truncateEnd(value, maxValueLength) : truncateMiddle(value, maxValueLength),
13204
- };
13205
- }
13206
-
13207
- function formatTerminalControlScreen({
13208
- title,
13209
- subtitle,
13210
- agent,
13211
- cwd,
13212
- sessionId,
13213
- task,
13214
- bodyLines = [],
13215
- footerLines = [],
13216
- columns = 80,
13217
- rows = 24,
13218
- color = false,
13219
- }) {
13220
- const screenWidth = Math.max(20, Number(columns) || 80);
13221
- const width = screenWidth < 60 ? screenWidth : Math.min(100, screenWidth);
13222
- const height = Math.max(20, Number(rows) || 24);
13223
- const innerWidth = width - 8;
13224
- const separator = '='.repeat(width);
13225
- const tone = getTerminalControlScreenTone(title);
13226
- const palette = getTerminalControlScreenPalette(tone);
13227
- const fieldLines = [
13228
- { label: 'Agent', value: agent || 'unknown' },
13229
- { label: 'Session', value: sessionId || 'pending' },
13230
- ...(task ? [{ label: 'Task', value: task, truncate: 'end' }] : []),
13231
- { label: 'Directory', value: cwd || process.cwd() },
13232
- ].map(({ label, value, truncate }) => {
13233
- const parts = formatTerminalFieldParts(label, value, innerWidth, { truncate });
13234
- return `${colorize(color, palette.label, parts.label)}${parts.value}`;
13235
- });
13236
- const block = [
13237
- colorize(color, palette.separator, separator),
13238
- colorize(color, palette.title, padRight(title, width)),
13239
- subtitle ? colorize(color, palette.subtitle, padRight(subtitle, width)) : '',
13240
- colorize(color, palette.separator, separator),
13241
- '',
13242
- ...fieldLines,
13243
- '',
13244
- ];
13245
-
13246
- for (const line of bodyLines) {
13247
- for (const wrapped of wrapText(line, innerWidth)) {
13248
- block.push(colorize(color, palette.body, padRight(wrapped, width)));
13249
- }
13250
- }
13251
-
13252
- block.push('');
13253
- for (const line of footerLines) {
13254
- for (const wrapped of wrapText(line, innerWidth)) {
13255
- block.push(colorize(color, palette.footer, padRight(wrapped, width)));
13256
- }
13257
- }
13258
- block.push(colorize(color, palette.separator, separator));
13259
-
13260
- const verticalPadding = Math.max(0, Math.floor((height - block.length) / 3));
13261
- return `${'\n'.repeat(verticalPadding)}${block.join('\n')}\n`;
13262
- }
13263
-
13264
- function normalizeTerminalScreenForTty(screen) {
13265
- return `\r${String(screen).replace(/\r?\n/g, '\r\n')}`;
13266
- }
13267
-
13268
- function renderTerminalControlScreen(stream, options) {
13269
- const columns = Number(stream?.columns) || 80;
13270
- const rows = Number(stream?.rows) || 24;
13271
- const screen = formatTerminalControlScreen({ ...options, columns, rows, color: Boolean(stream?.isTTY) });
13272
- if (stream?.isTTY) {
13273
- stream.write(`${ANSI_CLEAR_SCREEN}${ANSI_HIDE_CURSOR}${normalizeTerminalScreenForTty(screen)}`);
13274
- return;
13275
- }
13276
- stream.write(`\n${screen}`);
13277
- }
13278
-
13279
13357
  async function waitForRetryOrSignal(signalExit, delayMs) {
13280
13358
  let timer;
13281
13359
  const retry = new Promise((resolve) => {
@@ -13401,6 +13479,10 @@ async function connectTerminalControl({
13401
13479
  let heartbeatTimer = null;
13402
13480
  let resolveClosed;
13403
13481
  let closedResolved = false;
13482
+ let resolveTakeoverResponseSent;
13483
+ const takeoverResponseSent = new Promise((resolve) => {
13484
+ resolveTakeoverResponseSent = resolve;
13485
+ });
13404
13486
  const closed = new Promise((resolve) => {
13405
13487
  resolveClosed = resolve;
13406
13488
  });
@@ -13411,6 +13493,7 @@ async function connectTerminalControl({
13411
13493
  heartbeatTimer = null;
13412
13494
  }
13413
13495
  requester.rejectAll('terminal_control_closed');
13496
+ resolveTakeoverResponseSent(false);
13414
13497
  if (closedResolved) return;
13415
13498
  closedResolved = true;
13416
13499
  resolveClosed({
@@ -13431,7 +13514,7 @@ async function connectTerminalControl({
13431
13514
  });
13432
13515
  }
13433
13516
 
13434
- function sendResponse(id, ok, data, error) {
13517
+ function sendResponse(id, ok, data, error, onSent) {
13435
13518
  ws.send(
13436
13519
  JSON.stringify({
13437
13520
  type: 'response',
@@ -13440,6 +13523,7 @@ async function connectTerminalControl({
13440
13523
  ...(data === undefined ? {} : { data }),
13441
13524
  ...(error ? { error } : {}),
13442
13525
  }),
13526
+ onSent,
13443
13527
  );
13444
13528
  }
13445
13529
 
@@ -13467,21 +13551,28 @@ async function connectTerminalControl({
13467
13551
  restoreTerminalAfterNativeTui(stderr, process.stdin, terminalLineDiscipline);
13468
13552
  setMode('remoteDaemon');
13469
13553
  await sendSession('remoteDaemon');
13470
- renderTerminalControlScreen(stderr, {
13471
- title: 'VIBELET APP CONTROL',
13472
- subtitle: 'Remote takeover is active',
13473
- agent,
13474
- cwd,
13475
- sessionId: currentSessionId,
13476
- task: currentTaskPreview,
13477
- bodyLines: [
13478
- 'The Vibelet app is now controlling this session.',
13479
- 'The native terminal UI is paused so messages and tool output stay in the app.',
13480
- 'Leave this terminal open while you work from the app.',
13481
- ],
13482
- footerLines: ['Press any key to restore the native terminal UI.', 'Press Ctrl-C to close this terminal monitor.'],
13554
+ if (agent !== 'codex') {
13555
+ renderTerminalControlScreen(stderr, {
13556
+ title: 'VIBELET APP CONTROL',
13557
+ subtitle: 'Remote takeover is active',
13558
+ agent,
13559
+ cwd,
13560
+ sessionId: currentSessionId,
13561
+ task: currentTaskPreview,
13562
+ bodyLines: [
13563
+ 'The Vibelet app is now controlling this session.',
13564
+ 'The native terminal UI is paused so messages and tool output stay in the app.',
13565
+ 'Leave this terminal open while you work from the app.',
13566
+ ],
13567
+ footerLines: [
13568
+ 'Press any key to restore the native terminal UI.',
13569
+ 'Press Ctrl-C to close this terminal monitor.',
13570
+ ],
13571
+ });
13572
+ }
13573
+ sendResponse(msg.id, true, { mode: 'remoteDaemon' }, undefined, (error) => {
13574
+ resolveTakeoverResponseSent(!error);
13483
13575
  });
13484
- sendResponse(msg.id, true, { mode: 'remoteDaemon' });
13485
13576
  });
13486
13577
 
13487
13578
  ws.on('close', (code, reason) => {
@@ -13522,6 +13613,7 @@ async function connectTerminalControl({
13522
13613
 
13523
13614
  return {
13524
13615
  closed,
13616
+ takeoverResponseSent,
13525
13617
  getTerminalId() {
13526
13618
  return terminalId;
13527
13619
  },
@@ -13617,9 +13709,10 @@ async function runManagedNativeTerminal({
13617
13709
  cwd = process.cwd(),
13618
13710
  env = process.env,
13619
13711
  stderr = process.stderr,
13712
+ controlToken,
13620
13713
  }) {
13621
13714
  const command = resolveNativeAgentCommand(agent, env);
13622
- const token = readTerminalControlToken();
13715
+ const token = controlToken ?? readTerminalControlToken();
13623
13716
  const terminalLineDiscipline = captureTerminalLineDiscipline();
13624
13717
  let initialSessionId = extractInitialSessionId(agent, args);
13625
13718
  let nativeArgs = args;
@@ -13736,6 +13829,15 @@ async function runManagedNativeTerminal({
13736
13829
  if (modeState.getMode() !== 'remoteDaemon') {
13737
13830
  return exitCodeFromChildExit(exit);
13738
13831
  }
13832
+ if (agent === 'codex') {
13833
+ const acknowledged = await waitForTakeoverResponseSent(control);
13834
+ stderr.write(
13835
+ acknowledged
13836
+ ? 'Codex session moved to Vibelet. This terminal session is closed.\n'
13837
+ : 'Codex terminal stopped, but Vibelet takeover was not acknowledged.\n',
13838
+ );
13839
+ return acknowledged ? 0 : 1;
13840
+ }
13739
13841
 
13740
13842
  const monitorAction = await waitForRemoteMonitorInput(signalHandlers.signalExit);
13741
13843
  if (monitorAction.type === 'exit') {
@@ -14040,7 +14142,7 @@ function parseAccessTargetArg(argv, fail, env = process.env) {
14040
14142
  /* harmony export */ });
14041
14143
  /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(3024);
14042
14144
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(6760);
14043
- /* harmony import */ var _cloudflared_tunnel_manager_mjs__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(6895);
14145
+ /* harmony import */ var _cloudflared_tunnel_manager_mjs__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(1484);
14044
14146
 
14045
14147
 
14046
14148
 
@@ -15975,7 +16077,7 @@ __nccwpck_require__.a(__webpack_module__, async (__webpack_handle_async_dependen
15975
16077
  /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_4__ = __nccwpck_require__(6760);
15976
16078
  /* harmony import */ var node_url__WEBPACK_IMPORTED_MODULE_5__ = __nccwpck_require__(3136);
15977
16079
  /* harmony import */ var _vibelet_cli_args_mjs__WEBPACK_IMPORTED_MODULE_17__ = __nccwpck_require__(6927);
15978
- /* harmony import */ var _cloudflared_tunnel_manager_mjs__WEBPACK_IMPORTED_MODULE_6__ = __nccwpck_require__(6895);
16080
+ /* harmony import */ var _cloudflared_tunnel_manager_mjs__WEBPACK_IMPORTED_MODULE_6__ = __nccwpck_require__(1484);
15979
16081
  /* harmony import */ var _vibelet_cloudflare_runtime_mjs__WEBPACK_IMPORTED_MODULE_7__ = __nccwpck_require__(3869);
15980
16082
  /* harmony import */ var _vibelet_launchd_mjs__WEBPACK_IMPORTED_MODULE_13__ = __nccwpck_require__(1010);
15981
16083
  /* harmony import */ var _linux_systemd_mjs__WEBPACK_IMPORTED_MODULE_8__ = __nccwpck_require__(4217);
@@ -15983,7 +16085,7 @@ __nccwpck_require__.a(__webpack_module__, async (__webpack_handle_async_dependen
15983
16085
  /* harmony import */ var _vibelet_help_mjs__WEBPACK_IMPORTED_MODULE_16__ = __nccwpck_require__(4162);
15984
16086
  /* harmony import */ var _vibelet_tailcat_config_mjs__WEBPACK_IMPORTED_MODULE_9__ = __nccwpck_require__(5798);
15985
16087
  /* harmony import */ var _vibelet_runtime_policy_mjs__WEBPACK_IMPORTED_MODULE_12__ = __nccwpck_require__(4);
15986
- /* harmony import */ var _terminal_wrapper_mjs__WEBPACK_IMPORTED_MODULE_10__ = __nccwpck_require__(877);
16088
+ /* harmony import */ var _terminal_wrapper_mjs__WEBPACK_IMPORTED_MODULE_10__ = __nccwpck_require__(5028);
15987
16089
  /* harmony import */ var _vibelet_pairing_connections_mjs__WEBPACK_IMPORTED_MODULE_15__ = __nccwpck_require__(7721);
15988
16090
  /* harmony import */ var _vibelet_pairing_output_mjs__WEBPACK_IMPORTED_MODULE_11__ = __nccwpck_require__(9485);
15989
16091