vibelet 1.2.137 → 1.2.138

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.
@@ -1,2 +1,2 @@
1
- var c=require("node:fs"),n=require("node:path"),p="@vibelet/cli";function i(r){try{let e=JSON.parse((0,c.readFileSync)(r,"utf8"));if(e.name===p&&typeof e.version=="string"&&e.version.length>0)return e.version}catch{}return null}function l(){return"1.2.137"}var a=l();process.stdout.write(`${a}
1
+ var c=require("node:fs"),n=require("node:path"),p="@vibelet/cli";function i(r){try{let e=JSON.parse((0,c.readFileSync)(r,"utf8"));if(e.name===p&&typeof e.version=="string"&&e.version.length>0)return e.version}catch{}return null}function l(){return"1.2.138"}var a=l();process.stdout.write(`${a}
2
2
  `);
package/dist/vibelet.mjs CHANGED
@@ -11768,7 +11768,7 @@ __nccwpck_require__.d(__webpack_exports__, {
11768
11768
  gJ: () => (/* binding */ terminalControlTokenPath)
11769
11769
  });
11770
11770
 
11771
- // UNUSED EXPORTS: buildClaudeSessionIdArgs, buildClaudeTerminalArgs, buildResumeNativeArgs, cleanupClaudeTerminalHookFiles, collectProcessTreePids, createClaudeTerminalHookFiles, createTerminalModeState, exitCodeFromChildExit, extractInitialSessionId, formatTerminalControlScreen, hasClaudeContinueArg, hasClaudeResumePickerArg, hasClaudeSettingsArg, requestSwitchBackWhenReady, resolveNativeAgentCommand, stopNativeChild
11771
+ // UNUSED EXPORTS: buildClaudeSessionIdArgs, buildClaudeTerminalArgs, buildResumeNativeArgs, cleanupClaudeTerminalHookFiles, collectProcessTreePids, connectTerminalControl, createClaudeTerminalHookFiles, createTerminalModeState, exitCodeFromChildExit, extractInitialSessionId, formatTerminalControlScreen, hasClaudeContinueArg, hasClaudeResumePickerArg, hasClaudeSettingsArg, monitorTerminalControlUntilChildExit, requestSwitchBackWhenReady, resolveNativeAgentCommand, stopNativeChild
11772
11772
 
11773
11773
  // EXTERNAL MODULE: external "node:child_process"
11774
11774
  var external_node_child_process_ = __nccwpck_require__(1421);
@@ -11814,6 +11814,7 @@ const TAKEOVER_STOP_TIMEOUT_MS = 3000;
11814
11814
  const TAKEOVER_MODE_SETTLE_TIMEOUT_MS = TAKEOVER_STOP_TIMEOUT_MS + 2000;
11815
11815
  const SWITCHBACK_RESTORE_NOTICE_MS = 1200;
11816
11816
  const SWITCHBACK_BUSY_RETRY_DELAY_MS = 750;
11817
+ const TERMINAL_CONTROL_RECONNECT_DELAY_MS = 1000;
11817
11818
  const CLAUDE_HOOK_SECRET_HEADER = 'x-vibelet-claude-hook-secret';
11818
11819
  const ANSI_CLEAR_SCREEN = '\x1b[2J\x1b[H';
11819
11820
  const ANSI_HIDE_CURSOR = '\x1b[?25l';
@@ -12549,6 +12550,25 @@ async function connectTerminalControl({
12549
12550
  let activeChild = child;
12550
12551
  let activeChildExit = childExit;
12551
12552
  let heartbeatTimer = null;
12553
+ let resolveClosed;
12554
+ let closedResolved = false;
12555
+ const closed = new Promise((resolve) => {
12556
+ resolveClosed = resolve;
12557
+ });
12558
+
12559
+ function resolveCloseOnce(code, reason) {
12560
+ if (heartbeatTimer) {
12561
+ clearInterval(heartbeatTimer);
12562
+ heartbeatTimer = null;
12563
+ }
12564
+ requester.rejectAll('terminal_control_closed');
12565
+ if (closedResolved) return;
12566
+ closedResolved = true;
12567
+ resolveClosed({
12568
+ code,
12569
+ reason: Buffer.isBuffer(reason) ? reason.toString('utf8') : String(reason ?? ''),
12570
+ });
12571
+ }
12552
12572
 
12553
12573
  async function sendSession(mode = getMode()) {
12554
12574
  if (!terminalId || !currentSessionId) return { ok: true };
@@ -12613,9 +12633,8 @@ async function connectTerminalControl({
12613
12633
  sendResponse(msg.id, true, { mode: 'remoteDaemon' });
12614
12634
  });
12615
12635
 
12616
- ws.on('close', () => {
12617
- if (heartbeatTimer) clearInterval(heartbeatTimer);
12618
- requester.rejectAll('terminal_control_closed');
12636
+ ws.on('close', (code, reason) => {
12637
+ resolveCloseOnce(code, reason);
12619
12638
  });
12620
12639
 
12621
12640
  const hello = await requester.sendRequest('terminal.hello', {
@@ -12651,6 +12670,10 @@ async function connectTerminalControl({
12651
12670
  if (typeof heartbeatTimer.unref === 'function') heartbeatTimer.unref();
12652
12671
 
12653
12672
  return {
12673
+ closed,
12674
+ getTerminalId() {
12675
+ return terminalId;
12676
+ },
12654
12677
  getSessionId() {
12655
12678
  return currentSessionId;
12656
12679
  },
@@ -12673,12 +12696,68 @@ async function connectTerminalControl({
12673
12696
  });
12674
12697
  },
12675
12698
  close() {
12676
- if (heartbeatTimer) clearInterval(heartbeatTimer);
12677
- ws.close();
12699
+ if (heartbeatTimer) {
12700
+ clearInterval(heartbeatTimer);
12701
+ heartbeatTimer = null;
12702
+ }
12703
+ if (ws.readyState === wrapper.OPEN || ws.readyState === wrapper.CONNECTING) {
12704
+ ws.close();
12705
+ }
12678
12706
  },
12679
12707
  };
12680
12708
  }
12681
12709
 
12710
+ async function monitorTerminalControlUntilChildExit({
12711
+ childExit,
12712
+ getControl,
12713
+ setControl,
12714
+ connectControl,
12715
+ shouldReconnect = () => true,
12716
+ retryDelayMs = TERMINAL_CONTROL_RECONNECT_DELAY_MS,
12717
+ onControlDisconnected,
12718
+ onReconnectFailure,
12719
+ }) {
12720
+ const delayMs = Math.max(1, Number(retryDelayMs) || TERMINAL_CONTROL_RECONNECT_DELAY_MS);
12721
+ const childExitEvent = childExit.then((exit) => ({ type: 'childExit', exit }));
12722
+
12723
+ while (true) {
12724
+ const currentControl = getControl();
12725
+ const waiters = [childExitEvent];
12726
+ if (currentControl?.closed) {
12727
+ waiters.push(
12728
+ currentControl.closed.then((closeInfo) => ({
12729
+ type: 'controlClosed',
12730
+ control: currentControl,
12731
+ closeInfo,
12732
+ })),
12733
+ );
12734
+ }
12735
+ if (!currentControl || !shouldReconnect()) {
12736
+ waiters.push(waitMs(delayMs).then(() => ({ type: 'retry' })));
12737
+ }
12738
+
12739
+ const event = await Promise.race(waiters);
12740
+ if (event.type === 'childExit') {
12741
+ return event.exit;
12742
+ }
12743
+
12744
+ if (event.type === 'controlClosed' && getControl() === event.control) {
12745
+ setControl(null);
12746
+ onControlDisconnected?.(event.control, event.closeInfo);
12747
+ }
12748
+
12749
+ if (getControl() || !shouldReconnect()) {
12750
+ continue;
12751
+ }
12752
+
12753
+ try {
12754
+ setControl(await connectControl());
12755
+ } catch (error) {
12756
+ onReconnectFailure?.(error);
12757
+ }
12758
+ }
12759
+ }
12760
+
12682
12761
  async function runManagedNativeTerminal({
12683
12762
  agent,
12684
12763
  args,
@@ -12716,11 +12795,49 @@ async function runManagedNativeTerminal({
12716
12795
  let signalHandlers = null;
12717
12796
  const modeState = createTerminalModeState('localNative');
12718
12797
  let control = null;
12798
+ let terminalId = claudeHookFiles?.terminalId ?? null;
12799
+
12800
+ function rememberControlState(nextControl) {
12801
+ const nextTerminalId = nextControl?.getTerminalId?.();
12802
+ if (nextTerminalId) terminalId = nextTerminalId;
12803
+ const nextSessionId = nextControl?.getSessionId?.();
12804
+ if (nextSessionId) initialSessionId = nextSessionId;
12805
+ }
12719
12806
 
12720
12807
  try {
12721
12808
  while (true) {
12722
12809
  activeChild = await spawnNativeChild(command, nativeArgs, { cwd, env });
12723
12810
  const childExit = waitForChildExit(activeChild);
12811
+ let reportedTerminalControlFailure = false;
12812
+ const reportTerminalControlFailure = (prefix, error) => {
12813
+ if (reportedTerminalControlFailure) return;
12814
+ reportedTerminalControlFailure = true;
12815
+ stderr.write(`${prefix}: ${error instanceof Error ? error.message : String(error)}\n`);
12816
+ };
12817
+ const connectActiveControl = async () => {
12818
+ const nextControl = await connectTerminalControl({
12819
+ agent,
12820
+ cwd,
12821
+ argv,
12822
+ child: activeChild,
12823
+ childExit,
12824
+ cliVersion,
12825
+ port,
12826
+ token,
12827
+ initialSessionId,
12828
+ requestedTerminalId: terminalId ?? claudeHookFiles?.terminalId,
12829
+ getMode: () => modeState.getMode(),
12830
+ setMode: (nextMode) => modeState.setMode(nextMode),
12831
+ stderr,
12832
+ });
12833
+ rememberControlState(nextControl);
12834
+ reportedTerminalControlFailure = false;
12835
+ return nextControl;
12836
+ };
12837
+ const setActiveControl = (nextControl) => {
12838
+ control = nextControl;
12839
+ rememberControlState(nextControl);
12840
+ };
12724
12841
  if (!signalHandlers) {
12725
12842
  signalHandlers = installTerminalSignalHandlers(
12726
12843
  () => activeChild,
@@ -12732,30 +12849,32 @@ async function runManagedNativeTerminal({
12732
12849
  stderr.write('Vibelet terminal control is unavailable; running native TUI without App takeover.\n');
12733
12850
  } else if (!control) {
12734
12851
  try {
12735
- control = await connectTerminalControl({
12736
- agent,
12737
- cwd,
12738
- argv,
12739
- child: activeChild,
12740
- childExit,
12741
- cliVersion,
12742
- port,
12743
- token,
12744
- initialSessionId,
12745
- requestedTerminalId: claudeHookFiles?.terminalId,
12746
- getMode: () => modeState.getMode(),
12747
- setMode: (nextMode) => modeState.setMode(nextMode),
12748
- stderr,
12749
- });
12852
+ setActiveControl(await connectActiveControl());
12750
12853
  } catch (error) {
12751
- stderr.write(`Vibelet terminal control failed: ${error instanceof Error ? error.message : String(error)}\n`);
12854
+ reportTerminalControlFailure('Vibelet terminal control failed', error);
12752
12855
  }
12753
12856
  } else {
12754
12857
  control.setChild(activeChild, childExit);
12755
12858
  await control.sendSession('localNative');
12859
+ rememberControlState(control);
12756
12860
  }
12757
12861
 
12758
- const exit = await childExit;
12862
+ const exit = token
12863
+ ? await monitorTerminalControlUntilChildExit({
12864
+ childExit,
12865
+ getControl: () => control,
12866
+ setControl: setActiveControl,
12867
+ connectControl: connectActiveControl,
12868
+ shouldReconnect: () => modeState.getMode() === 'localNative',
12869
+ onControlDisconnected: (closedControl) => {
12870
+ rememberControlState(closedControl);
12871
+ stderr.write('Vibelet terminal control disconnected; reconnecting so App takeover can resume.\n');
12872
+ },
12873
+ onReconnectFailure: (error) => {
12874
+ reportTerminalControlFailure('Vibelet terminal control reconnect failed', error);
12875
+ },
12876
+ })
12877
+ : await childExit;
12759
12878
  if (modeState.getMode() === 'switchingToRemote') {
12760
12879
  await modeState.waitUntilModeLeaves('switchingToRemote', TAKEOVER_MODE_SETTLE_TIMEOUT_MS);
12761
12880
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibelet",
3
- "version": "1.2.137",
3
+ "version": "1.2.138",
4
4
  "description": "Cross-platform CLI for installing and running the Vibelet daemon",
5
5
  "homepage": "https://vibelet.icu",
6
6
  "files": [