vibelet 0.1.29 → 0.1.31

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 CHANGED
@@ -29,7 +29,7 @@ The CLI prints the official website on each run so users can always jump back to
29
29
  ## Commands
30
30
 
31
31
  ```bash
32
- vibelet # Install/start daemon and print pairing QR code
32
+ vibelet # Install/start daemon, auto-enable remote access, and print pairing QR code
33
33
  vibelet start # Same as above
34
34
  vibelet stop # Stop the daemon
35
35
  vibelet restart # Restart the daemon
@@ -43,12 +43,16 @@ vibelet --version # Show CLI version
43
43
  ## Options
44
44
 
45
45
  ```bash
46
+ vibelet --local # Skip the default Cloudflare Tunnel for this run
47
+ vibelet --remote --force # Force a fresh tunnel URL
46
48
  vibelet --relay <url> # Use a tunnel URL for remote access
47
49
  vibelet --relay "" # Clear saved relay URL
48
50
  vibelet --host <ip> # Set primary host/IP advertised for connections
49
51
  vibelet --fallback-hosts <ip1>,<ip2> # Comma-separated fallback IPs
50
52
  ```
51
53
 
54
+ Remote access is enabled by default for `vibelet`, `vibelet start`, `vibelet restart`, and `vibelet reset`. Use `--local` when you want a LAN-only QR code for that run.
55
+
52
56
  ## Release
53
57
 
54
58
  ```bash
@@ -7,3 +7,30 @@ export function shouldReuseHealthyDaemon({
7
7
  if (command !== 'default' && command !== 'start') return false;
8
8
  return !hasExplicitConfigOverrides;
9
9
  }
10
+
11
+ export function doesHealthMatchRequestedConnectionConfig({
12
+ health,
13
+ relayUrl = '',
14
+ canonicalHost = '',
15
+ fallbackHosts = '',
16
+ localMode = false,
17
+ }) {
18
+ if (!health) return false;
19
+
20
+ const currentRelayUrl = typeof health.relayUrl === 'string' ? health.relayUrl : '';
21
+ if (relayUrl) {
22
+ return currentRelayUrl === relayUrl;
23
+ }
24
+
25
+ if (localMode) {
26
+ return currentRelayUrl === '';
27
+ }
28
+
29
+ if (canonicalHost) {
30
+ if (fallbackHosts) return false;
31
+ return currentRelayUrl === '' && health.canonicalHost === canonicalHost;
32
+ }
33
+
34
+ if (fallbackHosts) return false;
35
+ return currentRelayUrl === '';
36
+ }
package/bin/vibelet.mjs CHANGED
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'node:url';
8
8
  import QRCode from 'qrcode';
9
9
  import { extractQuickTunnelUrl } from './cloudflared-quick-tunnel.mjs';
10
10
  import { resolveCloudflaredBin } from './cloudflared-resolver.mjs';
11
- import { shouldReuseHealthyDaemon } from './vibelet-runtime-policy.mjs';
11
+ import { doesHealthMatchRequestedConnectionConfig, shouldReuseHealthyDaemon } from './vibelet-runtime-policy.mjs';
12
12
 
13
13
  // ─── Paths & constants ─────────────────────────────────────────────────────────
14
14
 
@@ -571,6 +571,19 @@ async function requestShutdown() {
571
571
  }
572
572
  }
573
573
 
574
+ function isDaemonStartCommand(command) {
575
+ return command === 'default' || command === 'start' || command === 'restart' || command === 'reset';
576
+ }
577
+
578
+ async function stopRunningDaemon(backend) {
579
+ await requestShutdown();
580
+ backend.stop();
581
+ const stillAlive = await probeHealth(5_000);
582
+ if (stillAlive) {
583
+ fail('Daemon did not stop in time.');
584
+ }
585
+ }
586
+
574
587
  // ─── Commands ───────────────────────────────────────────────────────────────────
575
588
 
576
589
  async function printPairingSummary(existingHealth = null) {
@@ -584,9 +597,21 @@ async function printPairingSummary(existingHealth = null) {
584
597
  process.stdout.write(`Port: ${pairingPayload.port}\n`);
585
598
  process.stdout.write(`Paired devices: ${health.pairedDevices}\n`);
586
599
 
587
- const qr = await QRCode.toString(JSON.stringify(pairingPayload), {
600
+ // Use compact keys to keep QR code small
601
+ const compactPayload = {
602
+ t: 'vp',
603
+ d: pairingPayload.daemonId,
604
+ n: pairingPayload.displayName,
605
+ h: pairingPayload.canonicalHost,
606
+ p: pairingPayload.port,
607
+ c: pairingPayload.pairNonce,
608
+ e: pairingPayload.expiresAt,
609
+ };
610
+ if (pairingPayload.fallbackHosts) compactPayload.f = pairingPayload.fallbackHosts;
611
+ const qr = await QRCode.toString(JSON.stringify(compactPayload), {
588
612
  type: 'terminal',
589
613
  small: true,
614
+ errorCorrectionLevel: 'L',
590
615
  });
591
616
  process.stdout.write(`\nScan this QR code with the Vibelet app:\n\n${qr}\n`);
592
617
  }
@@ -597,10 +622,10 @@ function printHelp() {
597
622
  process.stdout.write(` @vibelet/cli\n`);
598
623
  process.stdout.write(` vibelet\n\n`);
599
624
  process.stdout.write(`Usage:\n`);
600
- process.stdout.write(` npx ${packageJson.name} Install/start the daemon and print a pairing QR code\n`);
625
+ process.stdout.write(` npx ${packageJson.name} Install/start the daemon, auto-enable remote access, and print a pairing QR code\n`);
601
626
  process.stdout.write(` npx ${packageJson.name} start Same as above\n`);
602
- process.stdout.write(` npx ${packageJson.name} --tunnel Auto-start a Cloudflare Tunnel for remote access\n`);
603
- process.stdout.write(` npx ${packageJson.name} --tunnel --force Force a new tunnel (discard existing)\n`);
627
+ process.stdout.write(` npx ${packageJson.name} --local Skip the default Cloudflare Tunnel for this run\n`);
628
+ process.stdout.write(` npx ${packageJson.name} --remote --force Force a new Cloudflare Tunnel URL\n`);
604
629
  process.stdout.write(` npx ${packageJson.name} --relay <url> Use a custom tunnel URL for remote access\n`);
605
630
  process.stdout.write(` npx ${packageJson.name} --host <ip> Set the primary host/IP address\n`);
606
631
  process.stdout.write(` npx ${packageJson.name} --fallback-hosts <ips> Comma-separated fallback IPs\n`);
@@ -616,8 +641,12 @@ function printHelp() {
616
641
  process.stdout.write(` npx ${packageJson.name === 'vibelet' ? '@vibelet/cli' : 'vibelet'}\n`);
617
642
  process.stdout.write(` vibelet\n\n`);
618
643
  process.stdout.write(`Remote access:\n`);
619
- process.stdout.write(` # Easiest one command, powered by Cloudflare Tunnel (free, no account)\n`);
620
- process.stdout.write(` npx ${packageJson.name} --tunnel\n\n`);
644
+ process.stdout.write(` # Remote access is on by default for start/reset/restart\n`);
645
+ process.stdout.write(` npx ${packageJson.name}\n\n`);
646
+ process.stdout.write(` # Want LAN-only pairing for this run?\n`);
647
+ process.stdout.write(` npx ${packageJson.name} --local\n\n`);
648
+ process.stdout.write(` # Need a fresh Cloudflare Tunnel URL?\n`);
649
+ process.stdout.write(` npx ${packageJson.name} --remote --force\n\n`);
621
650
  process.stdout.write(` # Or bring your own tunnel and pass the URL manually:\n`);
622
651
  process.stdout.write(` npx cloudflared tunnel --protocol http2 --url http://localhost:${port}\n`);
623
652
  process.stdout.write(` ngrok http ${port}\n`);
@@ -781,15 +810,33 @@ async function main() {
781
810
  checkForUpdateFromCache();
782
811
  fetchLatestVersionInBackground();
783
812
 
784
- const tunnelFlag = consumeFlag('tunnel') || readNpmConfigFlag('tunnel');
813
+ consumeFlag('remote') || consumeFlag('tunnel') || readNpmConfigFlag('remote') || readNpmConfigFlag('tunnel');
814
+ const localFlag = consumeFlag('local') || readNpmConfigFlag('local');
785
815
  const forceFlag = consumeFlag('force') || readNpmConfigFlag('force');
786
816
  const relayArg = parseRelayArg();
787
817
  const hostArg = parseNamedArg('host', '100.x.x.x');
788
818
  const fallbackHostsArg = parseNamedArg('fallback-hosts', '100.x.x.x,192.168.1.x');
789
- const hasExplicitConfigOverrides = relayArg !== null || Boolean(hostArg) || Boolean(fallbackHostsArg) || tunnelFlag;
819
+ const command = process.argv[2] ?? 'default';
820
+ const startCommand = isDaemonStartCommand(command);
821
+ // --remote/--tunnel remain accepted for compatibility, but startup commands
822
+ // now default to managed remote access unless another connection target wins.
823
+ const shouldManageTunnel = startCommand
824
+ && !localFlag
825
+ && relayArg === null
826
+ && !hostArg
827
+ && !fallbackHostsArg;
828
+
829
+ if (command === '--help' || command === '-h' || command === 'help') {
830
+ printHelp();
831
+ return;
832
+ }
790
833
 
791
- // --tunnel: auto-start or reuse a Cloudflare Tunnel
792
- if (tunnelFlag) {
834
+ if (command === '--version' || command === '-v' || command === 'version') {
835
+ process.stdout.write(`${packageJson.version}\n`);
836
+ return;
837
+ }
838
+
839
+ if (shouldManageTunnel) {
793
840
  const existing = forceFlag ? null : getAliveTunnel();
794
841
  if (existing) {
795
842
  process.stdout.write(`Reusing tunnel: ${existing.url} (pid ${existing.pid})\n`);
@@ -815,47 +862,32 @@ async function main() {
815
862
  clearRelayConfig();
816
863
  }
817
864
  }
818
- const relayUrl = relayArg || loadRelayConfig();
865
+ const shouldIgnoreSavedRelay = relayArg === null && (localFlag || Boolean(hostArg) || Boolean(fallbackHostsArg));
866
+ const relayUrl = relayArg !== null
867
+ ? relayArg
868
+ : (shouldIgnoreSavedRelay ? '' : loadRelayConfig());
819
869
  if (relayUrl) {
820
870
  process.env.VIBELET_RELAY_URL = relayUrl;
871
+ } else {
872
+ delete process.env.VIBELET_RELAY_URL;
821
873
  }
822
874
  if (hostArg) {
823
875
  process.env.VIBELET_CANONICAL_HOST = hostArg;
876
+ } else {
877
+ delete process.env.VIBELET_CANONICAL_HOST;
824
878
  }
825
879
  if (fallbackHostsArg) {
826
880
  process.env.VIBELET_FALLBACK_HOSTS = fallbackHostsArg;
881
+ } else {
882
+ delete process.env.VIBELET_FALLBACK_HOSTS;
827
883
  }
828
884
  const backend = resolveBackend();
829
- const command = process.argv[2] ?? 'default';
830
-
831
- if (command === '--help' || command === '-h' || command === 'help') {
832
- printHelp();
833
- return;
834
- }
835
-
836
- if (command === '--version' || command === '-v' || command === 'version') {
837
- process.stdout.write(`${packageJson.version}\n`);
838
- return;
839
- }
840
885
 
841
886
  if (command === 'stop') {
842
887
  process.stdout.write('Stopping vibelet daemon...\n');
843
888
  // Always try graceful HTTP shutdown first — gives the daemon time to
844
889
  // close sessions and flush logs before the service manager kills it.
845
- await requestShutdown();
846
- if (backend.handlesProcessLifecycle) {
847
- // macOS launchd / Linux systemd: unregister the service.
848
- backend.stop();
849
- } else {
850
- // Detached process: clean up PID file.
851
- backend.stop();
852
- }
853
- // Verify daemon is actually gone (daemon's own shutdown timeout is 3s,
854
- // plus a small margin for process teardown).
855
- const stillAlive = await probeHealth(5_000);
856
- if (stillAlive) {
857
- fail('Daemon did not stop in time.');
858
- }
890
+ await stopRunningDaemon(backend);
859
891
  // Also stop tunnel if running
860
892
  const tunnelState = getAliveTunnel();
861
893
  if (tunnelState) {
@@ -910,13 +942,7 @@ async function main() {
910
942
 
911
943
  if (command === 'restart') {
912
944
  process.stdout.write('Restarting vibelet daemon...\n');
913
- await requestShutdown();
914
- backend.stop();
915
- // Wait for daemon to actually stop
916
- const stillAlive = await probeHealth(5_000);
917
- if (stillAlive) {
918
- fail('Daemon did not stop in time.');
919
- }
945
+ await stopRunningDaemon(backend);
920
946
  process.stdout.write('Daemon stopped. Starting...\n');
921
947
  ensureRuntimeInstalled();
922
948
  backend.install();
@@ -926,6 +952,17 @@ async function main() {
926
952
  }
927
953
 
928
954
  if (command === 'reset') {
955
+ const healthyDaemon = await probeHealth(1_500);
956
+ const hasExplicitConfigOverrides = !doesHealthMatchRequestedConnectionConfig({
957
+ health: healthyDaemon,
958
+ relayUrl,
959
+ canonicalHost: hostArg || '',
960
+ fallbackHosts: fallbackHostsArg || '',
961
+ localMode: localFlag,
962
+ }) && (localFlag || Boolean(relayUrl) || Boolean(hostArg) || Boolean(fallbackHostsArg));
963
+ if (healthyDaemon && hasExplicitConfigOverrides) {
964
+ await stopRunningDaemon(backend);
965
+ }
929
966
  ensureRuntimeInstalled();
930
967
  backend.install();
931
968
  backend.start();
@@ -940,7 +977,14 @@ async function main() {
940
977
  fail(`Unknown command: ${command}`);
941
978
  }
942
979
 
943
- const healthyDaemon = hasExplicitConfigOverrides ? null : await probeHealth(1_500);
980
+ const healthyDaemon = await probeHealth(1_500);
981
+ const hasExplicitConfigOverrides = !doesHealthMatchRequestedConnectionConfig({
982
+ health: healthyDaemon,
983
+ relayUrl,
984
+ canonicalHost: hostArg || '',
985
+ fallbackHosts: fallbackHostsArg || '',
986
+ localMode: localFlag,
987
+ }) && (localFlag || Boolean(relayUrl) || Boolean(hostArg) || Boolean(fallbackHostsArg));
944
988
  const existingHealth = shouldReuseHealthyDaemon({
945
989
  command,
946
990
  daemonHealthy: Boolean(healthyDaemon),
@@ -955,6 +999,10 @@ async function main() {
955
999
  return;
956
1000
  }
957
1001
 
1002
+ if (healthyDaemon && hasExplicitConfigOverrides) {
1003
+ await stopRunningDaemon(backend);
1004
+ }
1005
+
958
1006
  ensureRuntimeInstalled();
959
1007
  backend.install();
960
1008
  backend.start();
package/dist/index.cjs CHANGED
@@ -81,9 +81,9 @@ process.stdin.on('end', () => {
81
81
  process.stdin.resume();
82
82
  `}function pc(t,e){let n=(0,nn.join)((0,dc.tmpdir)(),`vibelet-claude-hooks-${process.pid}-${(0,fc.randomUUID)()}`);(0,Le.mkdirSync)(n,{recursive:!0});let r=(0,nn.join)(n,"settings.json"),s=(0,nn.join)(n,"session_hook_forwarder.cjs"),i=(0,nn.join)(n,"permission_hook_forwarder.cjs");return(0,Le.writeFileSync)(s,tg(t,e),"utf8"),(0,Le.writeFileSync)(i,ng(t,e),"utf8"),(0,Le.writeFileSync)(r,JSON.stringify({hooks:{SessionStart:[{matcher:"*",hooks:[{type:"command",command:`${JSON.stringify(process.execPath)} ${JSON.stringify(s)}`}]}],PreToolUse:[{matcher:"*",hooks:[{type:"command",command:`${JSON.stringify(process.execPath)} ${JSON.stringify(i)}`}]}]}},null,2),"utf8"),{secret:e,dirPath:n,settingsPath:r,sessionScriptPath:s,permissionScriptPath:i}}function ar(t){if(t)try{(0,Le.rmSync)(t.dirPath,{recursive:!0,force:!0})}catch{}}var te=P.child({module:"claude"}),Sc="claude-permission-denial:",rg=/\u001B\[[0-?]*[ -/]*[@-~]/g;function sg(){return process.env.VIBE_TEST!=="1"&&!process.argv.includes("--test")}function Zs(t){sg()&&rr(`\x1B[33m\u26A1 [Vibelet] ${t}\x1B[0m
83
83
  `)}function ig(t){return Array.isArray(t)?t.map(e=>!e||typeof e!="object"?"":typeof e.text=="string"?e.text:typeof e.content=="string"?e.content:"").filter(Boolean).join(`
84
- `):""}function og(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&typeof e=="object"&&e.type==="tool_reference")}function ag(t){if(typeof t=="string")return t;let e=ig(t);return e||(og(t)?"":JSON.stringify(t))}function lg(t){return/requested permissions|haven't granted/i.test(t)}function hc(t){return t.replace(rg,"").replace(/\s+/g," ").trim()}function cg(t){let e=t.toLowerCase();return e==="error"||e==="failed"||e==="unknown error"}function ug(t){return/\brate limit\b/i.test(t)||/\busage limit\b/i.test(t)||/\bquota\b/i.test(t)||/\btoo many requests\b/i.test(t)||/\bcredit balance\b/i.test(t)||/\bcredits? remaining\b/i.test(t)||/\bmax(?:imum)? usage\b/i.test(t)}function dg(t){return t.toLowerCase().startsWith("claude ")?t:`Claude usage limit reached. ${t}`}function gc(t){let e=hc(t.resultText??""),n=(t.stderrLines??[]).map(hc).filter(Boolean).slice(-3),r=[e,...n].find(i=>!!i&&ug(i));if(r)return dg(r);let s=[e,...n].find(i=>!!i&&!cg(i));return s?t.exitCode!=null&&s!==e?`Claude exited with code ${t.exitCode}: ${s}`:s:t.exitCode!=null?`Claude exited with code ${t.exitCode}`:"Claude returned an error result."}function lr(t){return t.startsWith(Sc)}var on=class{proc=null;handler=null;sessionId="";buffer="";cwd="";approvalMode;sawFinalResult=!1;interrupted=!1;exitHandler=null;lastStderr=[];pendingPermissionDescriptions=new Map;emittedToolCallIds=new Set;replayPhase=!1;hookPort=null;hookSecret=null;hookFiles=null;buildClaudeEnv(){let e=v.buildSanitizedEnv();for(let n of Object.keys(e))n.startsWith("CMUX_")&&delete e[n];return e}async start(e,n,r){return this.cwd=e,this.approvalMode=r,n&&(this.sessionId=n),this.sessionId||(this.sessionId=`pending_${Date.now()}`),te.info({sessionId:this.sessionId,cwd:e},"session initialized"),_.emit("driver.spawn",{agent:"claude",sessionId:this.sessionId,cwd:e}),this.sessionId}configureHookBridge(e,n){this.hookPort=e,this.hookSecret=n}sendPrompt(e){let n=["-p",e,"--output-format","stream-json","--verbose","--include-partial-messages"];this.sessionId&&!this.sessionId.startsWith("pending_")&&n.push("--resume",this.sessionId),ar(this.hookFiles),this.hookFiles=null,this.approvalMode!=="acceptEdits"&&this.approvalMode!=="autoApprove"&&this.hookPort&&this.hookSecret&&(this.hookFiles=pc(this.hookPort,this.hookSecret),n.push("--settings",this.hookFiles.settingsPath)),this.approvalMode==="acceptEdits"&&n.push("--permission-mode","acceptEdits"),this.approvalMode==="autoApprove"&&n.push("--dangerously-skip-permissions");let r=this.sessionId.startsWith("pending_")?"(new)":`(resume ${this.sessionId.slice(0,8)})`;te.info({sessionId:this.sessionId,label:r,promptPreview:e.slice(0,50)},"running claude"),Zs(`New message: ${e.slice(0,60)}`),this.sawFinalResult=!1,this.interrupted=!1,this.lastStderr=[],this.pendingPermissionDescriptions.clear(),this.emittedToolCallIds.clear(),this.replayPhase=!0,this.proc=(0,mc.spawn)(v.claudePath,n,{cwd:this.cwd||void 0,stdio:["ignore","pipe","pipe"],env:this.buildClaudeEnv(),...process.platform==="win32"?{shell:!0}:{}}),this.proc.on("error",s=>{let i=s.message;s.code==="ENOENT"&&this.cwd&&!(0,yc.existsSync)(this.cwd)&&(i=`Working directory does not exist: ${this.cwd}`),te.error({sessionId:this.sessionId,error:i,cwd:this.cwd},"spawn error"),_.emit("driver.error",{agent:"claude",sessionId:this.sessionId,error:i}),this.handler?.({type:"error",sessionId:this.sessionId,message:i})}),this.buffer="",this.proc.stdout.on("data",s=>{this.buffer+=s.toString();let i=this.buffer.split(`
84
+ `):""}function og(t){return Array.isArray(t)&&t.length>0&&t.every(e=>e&&typeof e=="object"&&e.type==="tool_reference")}function ag(t){if(typeof t=="string")return t;let e=ig(t);return e||(og(t)?"":JSON.stringify(t))}function lg(t){return/requested permissions|haven't granted/i.test(t)}function hc(t){return t.replace(rg,"").replace(/\s+/g," ").trim()}function cg(t){let e=t.toLowerCase();return e==="error"||e==="failed"||e==="unknown error"}function ug(t){return/\brate limit\b/i.test(t)||/\busage limit\b/i.test(t)||/\bquota\b/i.test(t)||/\btoo many requests\b/i.test(t)||/\bcredit balance\b/i.test(t)||/\bcredits? remaining\b/i.test(t)||/\bmax(?:imum)? usage\b/i.test(t)}function dg(t){return t.toLowerCase().startsWith("claude ")?t:`Claude usage limit reached. ${t}`}function gc(t){let e=hc(t.resultText??""),n=(t.stderrLines??[]).map(hc).filter(Boolean).slice(-3),r=[e,...n].find(i=>!!i&&ug(i));if(r)return dg(r);let s=[e,...n].find(i=>!!i&&!cg(i));return s?t.exitCode!=null&&s!==e?`Claude exited with code ${t.exitCode}: ${s}`:s:t.exitCode!=null?`Claude exited with code ${t.exitCode}`:"Claude returned an error result."}function lr(t){return t.startsWith(Sc)}var on=class{proc=null;handler=null;sessionId="";buffer="";cwd="";approvalMode;sawFinalResult=!1;interrupted=!1;exitHandler=null;lastStderr=[];pendingPermissionDescriptions=new Map;emittedToolCallIds=new Set;replayPhase=!1;hookPort=null;hookSecret=null;hookFiles=null;buildClaudeEnv(){let e=v.buildSanitizedEnv();for(let n of Object.keys(e))n.startsWith("CMUX_")&&delete e[n];return e}async start(e,n,r){return this.cwd=e,this.approvalMode=r,n&&(this.sessionId=n),this.sessionId||(this.sessionId=`pending_${Date.now()}`),te.info({sessionId:this.sessionId,cwd:e},"session initialized"),_.emit("driver.spawn",{agent:"claude",sessionId:this.sessionId,cwd:e}),this.sessionId}configureHookBridge(e,n){this.hookPort=e,this.hookSecret=n}sendPrompt(e){let n=["-p",e,"--output-format","stream-json","--verbose","--include-partial-messages"];this.sessionId&&!this.sessionId.startsWith("pending_")&&n.push("--resume",this.sessionId),ar(this.hookFiles),this.hookFiles=null,this.approvalMode!=="plan"&&this.approvalMode!=="acceptEdits"&&this.approvalMode!=="autoApprove"&&this.hookPort&&this.hookSecret&&(this.hookFiles=pc(this.hookPort,this.hookSecret),n.push("--settings",this.hookFiles.settingsPath)),this.approvalMode==="plan"&&n.push("--permission-mode","plan"),this.approvalMode==="acceptEdits"&&n.push("--permission-mode","acceptEdits"),this.approvalMode==="autoApprove"&&n.push("--dangerously-skip-permissions");let r=this.sessionId.startsWith("pending_")?"(new)":`(resume ${this.sessionId.slice(0,8)})`;te.info({sessionId:this.sessionId,label:r,promptPreview:e.slice(0,50)},"running claude"),Zs(`New message: ${e.slice(0,60)}`),this.sawFinalResult=!1,this.interrupted=!1,this.lastStderr=[],this.pendingPermissionDescriptions.clear(),this.emittedToolCallIds.clear(),this.replayPhase=!0,this.proc=(0,mc.spawn)(v.claudePath,n,{cwd:this.cwd||void 0,stdio:["ignore","pipe","pipe"],env:this.buildClaudeEnv(),...process.platform==="win32"?{shell:!0}:{}}),this.proc.on("error",s=>{let i=s.message;s.code==="ENOENT"&&this.cwd&&!(0,yc.existsSync)(this.cwd)&&(i=`Working directory does not exist: ${this.cwd}`),te.error({sessionId:this.sessionId,error:i,cwd:this.cwd},"spawn error"),_.emit("driver.error",{agent:"claude",sessionId:this.sessionId,error:i}),this.handler?.({type:"error",sessionId:this.sessionId,message:i})}),this.buffer="",this.proc.stdout.on("data",s=>{this.buffer+=s.toString();let i=this.buffer.split(`
85
85
  `);this.buffer=i.pop();for(let o of i)if(o.trim())try{this.handleRaw(JSON.parse(o))}catch{te.warn({sessionId:this.sessionId,linePreview:o.slice(0,100)},"failed to parse stdout line")}}),this.proc.stderr.on("data",s=>{let i=s.toString().trim();i&&(te.debug({sessionId:this.sessionId,stderr:i},"stderr"),this.lastStderr.push(i),this.lastStderr.length>10&&this.lastStderr.shift())}),this.proc.on("exit",(s,i)=>{te.info({sessionId:this.sessionId,exitCode:s,signal:i},"process exited");let o=this.interrupted;this.proc=null,ar(this.hookFiles),this.hookFiles=null,this.interrupted=!1,o&&!this.sawFinalResult?this.handler?.({type:"session.interrupted",sessionId:this.sessionId}):s&&s!==0&&!this.sawFinalResult&&(te.error({sessionId:this.sessionId,exitCode:s,lastStderr:this.lastStderr.slice(-3)},"abnormal exit"),this.handler?.({type:"error",sessionId:this.sessionId,message:gc({stderrLines:this.lastStderr,exitCode:s})})),this.exitHandler?.(s)})}respondApproval(e,n){if(te.info({sessionId:this.sessionId,requestId:e,approved:n},"approval response"),!this.proc?.stdin?.writable)return te.error({sessionId:this.sessionId},"cannot send approval: stdin not writable"),!1;let r=JSON.stringify({type:"control_response",request_id:e,permission_granted:n});return this.proc.stdin.write(r+`
86
- `),!0}setApprovalMode(e){this.approvalMode=e,te.info({sessionId:this.sessionId,approvalMode:e},"approval mode updated")}interrupt(){this.proc&&!this.proc.killed&&(this.interrupted=!0,this.proc.kill("SIGTERM"),te.info({sessionId:this.sessionId},"interrupted"))}stop(){if(!this.proc)return;this.proc.kill("SIGTERM");let e=this.proc;setTimeout(()=>{e.killed||e.kill("SIGKILL")},5e3).unref(),this.proc=null,ar(this.hookFiles),this.hookFiles=null}onMessage(e){this.handler=e}onExit(e){this.exitHandler=e}handleRaw(e){let n=e.type;if(n==="system"&&e.subtype==="init"){let r=e.session_id??"";r&&r!==this.sessionId&&(te.info({oldSessionId:this.sessionId,newSessionId:r},"session ID resolved"),_.emit("driver.init",{agent:"claude",sessionId:r}),this.sessionId=r);return}if(this.handler)switch(n){case"assistant":{if(this.replayPhase)break;let r=e.message?.content;if(!Array.isArray(r))break;for(let s of r)s.type==="tool_use"&&!this.emittedToolCallIds.has(s.id)&&(this.emittedToolCallIds.add(s.id),this.handler({type:"tool.call",sessionId:this.sessionId,toolName:s.name,input:s.input??{},toolCallId:s.id}));break}case"user":{if(this.replayPhase)break;let r=e.message?.content;if(!Array.isArray(r))break;for(let s of r)if(s.type==="tool_result"){let i=ag(s.content);if(!i)continue;if(s.is_error===!0&&typeof s.tool_use_id=="string"&&lg(i)){this.pendingPermissionDescriptions.set(s.tool_use_id,i);continue}this.handler({type:"tool.result",sessionId:this.sessionId,toolCallId:s.tool_use_id,output:i})}break}case"control_request":{this.replayPhase=!1;let r=e.request;r?.subtype==="can_use_tool"&&(_.emit("approval.request",{agent:"claude",sessionId:this.sessionId,toolName:r.tool_name}),this.handler({type:"approval.request",sessionId:this.sessionId,requestId:e.request_id,toolName:r.tool_name??"unknown",input:r.input??{},description:r.description??r.title??""}));break}case"stream_event":{this.replayPhase=!1;let r=e.event;r?.type==="content_block_delta"&&r?.delta?.type==="text_delta"&&r?.delta?.text&&this.handler({type:"text.delta",sessionId:this.sessionId,content:r.delta.text});break}case"result":{this.replayPhase=!1,this.sawFinalResult=!0;let r=typeof e.result=="string"?e.result:"";if(e.is_error){Zs("Claude failed."),this.handler({type:"error",sessionId:this.sessionId,message:gc({resultText:r,stderrLines:this.lastStderr})});break}let s=Array.isArray(e.permission_denials)?e.permission_denials:[];if(s.length>0){let a=s[0]??{},l=typeof a.tool_use_id=="string"?a.tool_use_id:`missing_${Date.now()}`,c=typeof a.tool_name=="string"?a.tool_name:"unknown",u=a.tool_input&&typeof a.tool_input=="object"?a.tool_input:{},d=this.pendingPermissionDescriptions.get(l)??`Claude requested permissions to use ${c}.`;_.emit("approval.request",{agent:"claude",sessionId:this.sessionId,toolName:c}),this.handler({type:"approval.request",sessionId:this.sessionId,requestId:`${Sc}${l}`,toolName:c,input:u,description:d})}this.pendingPermissionDescriptions.clear();let i=e.total_cost_usd,o=e.usage?{inputTokens:e.usage.input_tokens,outputTokens:e.usage.output_tokens}:void 0;Zs("Claude finished. Run `claude --continue` to continue on desktop."),_.emit("session.done",{agent:"claude",sessionId:this.sessionId,cost:i,usage:o}),this.handler({type:"session.done",sessionId:this.sessionId,cost:i,usage:o});break}}}};var wc=require("child_process"),Ic=require("fs");var Qs=class{counters=new Map;timers=new Map;gauges=new Map;startTime=Date.now();logInterval=null;increment(e,n={}){this.counters.has(e)||this.counters.set(e,[]);let r=this.counters.get(e),s=JSON.stringify(n),i=r.find(o=>JSON.stringify(o.labels)===s);i?i.value++:r.push({value:1,labels:n})}gauge(e,n){this.gauges.set(e,n)}startTimer(e){let n=performance.now();return()=>{let r=Math.round(performance.now()-n),s=this.timers.get(e)??{count:0,totalMs:0,minMs:1/0,maxMs:0,lastMs:0};return s.count++,s.totalMs+=r,s.minMs=Math.min(s.minMs,r),s.maxMs=Math.max(s.maxMs,r),s.lastMs=r,this.timers.set(e,s),r}}snapshot(){let e={};for(let[s,i]of this.counters)e[s]=i.map(o=>({...o}));let n={};for(let[s,i]of this.timers)n[s]={...i,minMs:i.minMs===1/0?0:i.minMs};let r={};for(let[s,i]of this.gauges)r[s]=i;return{uptimeMs:Date.now()-this.startTime,counters:e,timers:n,gauges:r}}startPeriodicLog(e=6e4){this.logInterval||(this.logInterval=setInterval(()=>{let n=this.snapshot();P.info({metrics:n},"periodic metrics snapshot")},e),this.logInterval.unref())}stopPeriodicLog(){this.logInterval&&(clearInterval(this.logInterval),this.logInterval=null)}},w=new Qs;var vc=require("node:fs"),ei=require("node:path"),fg="@vibelet/cli";function pg(t){try{let e=JSON.parse((0,vc.readFileSync)(t,"utf8"));if(e.name===fg&&typeof e.version=="string"&&e.version.length>0)return e.version}catch{}return null}function hg(){return"0.1.29"}var St=hg();var x=P.child({module:"codex"});function gg(t){switch(t.kind){case"request-user-input-approval":return{provider:"codex",kind:t.kind,rpcId:t.rpcId,questionId:t.questionId,approveLabel:t.approveLabel,denyLabel:t.denyLabel};default:return{provider:"codex",kind:t.kind,rpcId:t.rpcId}}}function mg(t){let e=t.approvalContext;if(!e||e.provider!=="codex")return null;switch(e.kind){case"command-execution":return{kind:e.kind,responseKind:"v2",rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"file-change":return{kind:e.kind,responseKind:"v2",rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"request-user-input-approval":return!e.questionId||!e.approveLabel||!e.denyLabel?null:{kind:e.kind,rpcId:e.rpcId,questionId:e.questionId,approveLabel:e.approveLabel,denyLabel:e.denyLabel,toolName:t.toolName,input:t.input};case"exec-command-legacy":return{kind:e.kind,rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"apply-patch-legacy":return{kind:e.kind,rpcId:e.rpcId,toolName:t.toolName,input:t.input};default:return null}}function $(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function k(t){return typeof t=="string"&&t.trim().length>0?t.trim():null}var yg=new Set(["aborted","interrupted","cancelled","canceled"]);function Sg(t){let e=k(t);return e?e.toLowerCase():null}function vg(t){let e=k(t);return e?e.replace(/[^a-z0-9]/gi,"").toLowerCase():null}function ti(t){return t?k(t.itemId)??k(t.id)??k(t.callId)??k(t.call_id):null}function ni(t,e){let n={};for(let[r,s]of Object.entries(t))e.includes(r)||(n[r]=s);return n}function Ac(t){return/\bapprove\b|\ballow\b|\baccept\b|\byes\b|\bcontinue\b|\bproceed\b|\brun\b|\bapply\b/i.test(t)}function xc(t){return/\bdeny\b|\breject\b|\bdecline\b|\bno\b|\bcancel\b|\babort\b|\bstop\b/i.test(t)}function _g(t){if(!Array.isArray(t))return null;for(let e of t){let n=$(e);if(!n)continue;let r=k(n.id),s=Array.isArray(n.options)?n.options.map(l=>$(l)).filter(l=>!!l):[];if(!r||s.length===0)continue;let i=s.map(l=>k(l.label)).filter(l=>!!l);if(i.length<2)continue;let o=i.find(l=>Ac(l))??i[0],a=i.find(l=>xc(l))??i[i.length-1];if(!(!o||!a||o===a))return{questionId:r,approveLabel:o,denyLabel:a}}return null}function bg(t){if(!Array.isArray(t)||t.length===0)return!1;let e=t.map(s=>$(s)).filter(s=>!!s).flatMap(s=>(Array.isArray(s.options)?s.options:[]).map(o=>$(o)).filter(o=>!!o).map(o=>k(o.label)).filter(o=>!!o)),n=e.some(s=>Ac(s)),r=e.some(s=>xc(s));return n&&r}function _c(t){let e=vg(t.type??t.itemType);if(e==="commandexecution")return{toolName:"Bash",input:ni(t,["id","itemId","type","itemType","stdout","stderr","exitCode","exit_code","status","success","error"])};if(e==="filechange")return{toolName:"Patch",input:ni(t,["id","itemId","type","itemType","stdout","stderr","exitCode","exit_code","status","success","error"])};if(e==="mcptoolcall"){let n=k(t.server),r=k(t.tool)??k(t.name);return r?{toolName:n?`mcp__${n}__${r}`:r,input:$(t.arguments)??$(t.input)??{}}:null}return null}function wg(t){if(!Array.isArray(t))return"";for(let e of t){let n=$(e);if(!n)continue;let r=k(n.question)??k(n.header);if(r)return r}return""}function bc(t,e){return t==="autoApprove"?{approvalPolicy:"never",sandbox:"danger-full-access",sandboxPolicy:{type:"dangerFullAccess"}}:{approvalPolicy:"on-request",sandbox:"workspace-write",sandboxPolicy:{type:"workspaceWrite",writableRoots:[e],readOnlyAccess:{type:"fullAccess"},networkAccess:!0,excludeTmpdirEnvVar:!1,excludeSlashTmp:!1}}}var cr=class{proc=null;handler=null;exitHandler=null;buffer="";rpcId=0;pending=new Map;threadId="";lastStderr=[];approvalRequests=new Map;toolContextByCallId=new Map;turnStartInFlight=!1;interruptRequestedDuringTurnStart=!1;approvalMode;cwd="";async start(e,n,r){this.approvalMode=r,this.cwd=e,this.approvalRequests.clear(),this.toolContextByCallId.clear(),this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart=!1;let s=v.codexPath,i,o=this.buildSpawnArgs();if(v.isTransientPath(s)){let c=v.execViaLoginShell("codex",o);i=c.command,o=c.args,x.info({spawnCmd:i,argsPreview:o.slice(0,2)},"spawning via login shell")}else i=s,x.info({spawnCmd:i,args:o},"spawning");_.emit("driver.spawn",{agent:"codex",cwd:e,resumeSessionId:n}),this.lastStderr=[],this.proc=(0,wc.spawn)(i,o,{cwd:e||void 0,stdio:["pipe","pipe","pipe"],env:v.buildSanitizedEnv(),...process.platform==="win32"?{shell:!0}:{}}),this.proc.on("error",c=>{let u=c.message;c.code==="ENOENT"&&e&&!(0,Ic.existsSync)(e)&&(u=`Working directory does not exist: ${e}`),x.error({error:u,cwd:e},"spawn error"),_.emit("driver.error",{agent:"codex",error:u})}),this.proc.stdout.on("data",c=>{this.buffer+=c.toString();let u=this.buffer.split(`
86
+ `),!0}setApprovalMode(e){this.approvalMode=e,te.info({sessionId:this.sessionId,approvalMode:e},"approval mode updated")}interrupt(){this.proc&&!this.proc.killed&&(this.interrupted=!0,this.proc.kill("SIGTERM"),te.info({sessionId:this.sessionId},"interrupted"))}stop(){if(!this.proc)return;this.proc.kill("SIGTERM");let e=this.proc;setTimeout(()=>{e.killed||e.kill("SIGKILL")},5e3).unref(),this.proc=null,ar(this.hookFiles),this.hookFiles=null}onMessage(e){this.handler=e}onExit(e){this.exitHandler=e}handleRaw(e){let n=e.type;if(n==="system"&&e.subtype==="init"){let r=e.session_id??"";r&&r!==this.sessionId&&(te.info({oldSessionId:this.sessionId,newSessionId:r},"session ID resolved"),_.emit("driver.init",{agent:"claude",sessionId:r}),this.sessionId=r);return}if(this.handler)switch(n){case"assistant":{if(this.replayPhase)break;let r=e.message?.content;if(!Array.isArray(r))break;for(let s of r)s.type==="tool_use"&&!this.emittedToolCallIds.has(s.id)&&(this.emittedToolCallIds.add(s.id),this.handler({type:"tool.call",sessionId:this.sessionId,toolName:s.name,input:s.input??{},toolCallId:s.id}));break}case"user":{if(this.replayPhase)break;let r=e.message?.content;if(!Array.isArray(r))break;for(let s of r)if(s.type==="tool_result"){let i=ag(s.content);if(!i)continue;if(s.is_error===!0&&typeof s.tool_use_id=="string"&&lg(i)){this.pendingPermissionDescriptions.set(s.tool_use_id,i);continue}this.handler({type:"tool.result",sessionId:this.sessionId,toolCallId:s.tool_use_id,output:i})}break}case"control_request":{this.replayPhase=!1;let r=e.request;r?.subtype==="can_use_tool"&&(_.emit("approval.request",{agent:"claude",sessionId:this.sessionId,toolName:r.tool_name}),this.handler({type:"approval.request",sessionId:this.sessionId,requestId:e.request_id,toolName:r.tool_name??"unknown",input:r.input??{},description:r.description??r.title??""}));break}case"stream_event":{this.replayPhase=!1;let r=e.event;r?.type==="content_block_delta"&&r?.delta?.type==="text_delta"&&r?.delta?.text&&this.handler({type:"text.delta",sessionId:this.sessionId,content:r.delta.text});break}case"result":{this.replayPhase=!1,this.sawFinalResult=!0;let r=typeof e.result=="string"?e.result:"";if(e.is_error){Zs("Claude failed."),this.handler({type:"error",sessionId:this.sessionId,message:gc({resultText:r,stderrLines:this.lastStderr})});break}let s=Array.isArray(e.permission_denials)?e.permission_denials:[];if(s.length>0){let a=s[0]??{},l=typeof a.tool_use_id=="string"?a.tool_use_id:`missing_${Date.now()}`,c=typeof a.tool_name=="string"?a.tool_name:"unknown",u=a.tool_input&&typeof a.tool_input=="object"?a.tool_input:{},d=this.pendingPermissionDescriptions.get(l)??`Claude requested permissions to use ${c}.`;_.emit("approval.request",{agent:"claude",sessionId:this.sessionId,toolName:c}),this.handler({type:"approval.request",sessionId:this.sessionId,requestId:`${Sc}${l}`,toolName:c,input:u,description:d})}this.pendingPermissionDescriptions.clear();let i=e.total_cost_usd,o=e.usage?{inputTokens:e.usage.input_tokens,outputTokens:e.usage.output_tokens}:void 0;Zs("Claude finished. Run `claude --continue` to continue on desktop."),_.emit("session.done",{agent:"claude",sessionId:this.sessionId,cost:i,usage:o}),this.handler({type:"session.done",sessionId:this.sessionId,cost:i,usage:o});break}}}};var wc=require("child_process"),Ic=require("fs");var Qs=class{counters=new Map;timers=new Map;gauges=new Map;startTime=Date.now();logInterval=null;increment(e,n={}){this.counters.has(e)||this.counters.set(e,[]);let r=this.counters.get(e),s=JSON.stringify(n),i=r.find(o=>JSON.stringify(o.labels)===s);i?i.value++:r.push({value:1,labels:n})}gauge(e,n){this.gauges.set(e,n)}startTimer(e){let n=performance.now();return()=>{let r=Math.round(performance.now()-n),s=this.timers.get(e)??{count:0,totalMs:0,minMs:1/0,maxMs:0,lastMs:0};return s.count++,s.totalMs+=r,s.minMs=Math.min(s.minMs,r),s.maxMs=Math.max(s.maxMs,r),s.lastMs=r,this.timers.set(e,s),r}}snapshot(){let e={};for(let[s,i]of this.counters)e[s]=i.map(o=>({...o}));let n={};for(let[s,i]of this.timers)n[s]={...i,minMs:i.minMs===1/0?0:i.minMs};let r={};for(let[s,i]of this.gauges)r[s]=i;return{uptimeMs:Date.now()-this.startTime,counters:e,timers:n,gauges:r}}startPeriodicLog(e=6e4){this.logInterval||(this.logInterval=setInterval(()=>{let n=this.snapshot();P.info({metrics:n},"periodic metrics snapshot")},e),this.logInterval.unref())}stopPeriodicLog(){this.logInterval&&(clearInterval(this.logInterval),this.logInterval=null)}},w=new Qs;var vc=require("node:fs"),ei=require("node:path"),fg="@vibelet/cli";function pg(t){try{let e=JSON.parse((0,vc.readFileSync)(t,"utf8"));if(e.name===fg&&typeof e.version=="string"&&e.version.length>0)return e.version}catch{}return null}function hg(){return"0.1.31"}var St=hg();var x=P.child({module:"codex"});function gg(t){switch(t.kind){case"request-user-input-approval":return{provider:"codex",kind:t.kind,rpcId:t.rpcId,questionId:t.questionId,approveLabel:t.approveLabel,denyLabel:t.denyLabel};default:return{provider:"codex",kind:t.kind,rpcId:t.rpcId}}}function mg(t){let e=t.approvalContext;if(!e||e.provider!=="codex")return null;switch(e.kind){case"command-execution":return{kind:e.kind,responseKind:"v2",rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"file-change":return{kind:e.kind,responseKind:"v2",rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"request-user-input-approval":return!e.questionId||!e.approveLabel||!e.denyLabel?null:{kind:e.kind,rpcId:e.rpcId,questionId:e.questionId,approveLabel:e.approveLabel,denyLabel:e.denyLabel,toolName:t.toolName,input:t.input};case"exec-command-legacy":return{kind:e.kind,rpcId:e.rpcId,toolName:t.toolName,input:t.input};case"apply-patch-legacy":return{kind:e.kind,rpcId:e.rpcId,toolName:t.toolName,input:t.input};default:return null}}function $(t){return!t||typeof t!="object"||Array.isArray(t)?null:t}function k(t){return typeof t=="string"&&t.trim().length>0?t.trim():null}var yg=new Set(["aborted","interrupted","cancelled","canceled"]);function Sg(t){let e=k(t);return e?e.toLowerCase():null}function vg(t){let e=k(t);return e?e.replace(/[^a-z0-9]/gi,"").toLowerCase():null}function ti(t){return t?k(t.itemId)??k(t.id)??k(t.callId)??k(t.call_id):null}function ni(t,e){let n={};for(let[r,s]of Object.entries(t))e.includes(r)||(n[r]=s);return n}function Ac(t){return/\bapprove\b|\ballow\b|\baccept\b|\byes\b|\bcontinue\b|\bproceed\b|\brun\b|\bapply\b/i.test(t)}function xc(t){return/\bdeny\b|\breject\b|\bdecline\b|\bno\b|\bcancel\b|\babort\b|\bstop\b/i.test(t)}function _g(t){if(!Array.isArray(t))return null;for(let e of t){let n=$(e);if(!n)continue;let r=k(n.id),s=Array.isArray(n.options)?n.options.map(l=>$(l)).filter(l=>!!l):[];if(!r||s.length===0)continue;let i=s.map(l=>k(l.label)).filter(l=>!!l);if(i.length<2)continue;let o=i.find(l=>Ac(l))??i[0],a=i.find(l=>xc(l))??i[i.length-1];if(!(!o||!a||o===a))return{questionId:r,approveLabel:o,denyLabel:a}}return null}function bg(t){if(!Array.isArray(t)||t.length===0)return!1;let e=t.map(s=>$(s)).filter(s=>!!s).flatMap(s=>(Array.isArray(s.options)?s.options:[]).map(o=>$(o)).filter(o=>!!o).map(o=>k(o.label)).filter(o=>!!o)),n=e.some(s=>Ac(s)),r=e.some(s=>xc(s));return n&&r}function _c(t){let e=vg(t.type??t.itemType);if(e==="commandexecution")return{toolName:"Bash",input:ni(t,["id","itemId","type","itemType","stdout","stderr","exitCode","exit_code","status","success","error"])};if(e==="filechange")return{toolName:"Patch",input:ni(t,["id","itemId","type","itemType","stdout","stderr","exitCode","exit_code","status","success","error"])};if(e==="mcptoolcall"){let n=k(t.server),r=k(t.tool)??k(t.name);return r?{toolName:n?`mcp__${n}__${r}`:r,input:$(t.arguments)??$(t.input)??{}}:null}return null}function wg(t){if(!Array.isArray(t))return"";for(let e of t){let n=$(e);if(!n)continue;let r=k(n.question)??k(n.header);if(r)return r}return""}function bc(t,e){return t==="autoApprove"?{approvalPolicy:"never",sandbox:"danger-full-access",sandboxPolicy:{type:"dangerFullAccess"}}:t==="plan"?{approvalPolicy:"on-request",sandbox:"workspace-write",sandboxPolicy:{type:"workspaceWrite",writableRoots:[],readOnlyAccess:{type:"fullAccess"},networkAccess:!0,excludeTmpdirEnvVar:!1,excludeSlashTmp:!1}}:{approvalPolicy:"on-request",sandbox:"workspace-write",sandboxPolicy:{type:"workspaceWrite",writableRoots:[e],readOnlyAccess:{type:"fullAccess"},networkAccess:!0,excludeTmpdirEnvVar:!1,excludeSlashTmp:!1}}}var cr=class{proc=null;handler=null;exitHandler=null;buffer="";rpcId=0;pending=new Map;threadId="";lastStderr=[];approvalRequests=new Map;toolContextByCallId=new Map;turnStartInFlight=!1;interruptRequestedDuringTurnStart=!1;approvalMode;cwd="";async start(e,n,r){this.approvalMode=r,this.cwd=e,this.approvalRequests.clear(),this.toolContextByCallId.clear(),this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart=!1;let s=v.codexPath,i,o=this.buildSpawnArgs();if(v.isTransientPath(s)){let c=v.execViaLoginShell("codex",o);i=c.command,o=c.args,x.info({spawnCmd:i,argsPreview:o.slice(0,2)},"spawning via login shell")}else i=s,x.info({spawnCmd:i,args:o},"spawning");_.emit("driver.spawn",{agent:"codex",cwd:e,resumeSessionId:n}),this.lastStderr=[],this.proc=(0,wc.spawn)(i,o,{cwd:e||void 0,stdio:["pipe","pipe","pipe"],env:v.buildSanitizedEnv(),...process.platform==="win32"?{shell:!0}:{}}),this.proc.on("error",c=>{let u=c.message;c.code==="ENOENT"&&e&&!(0,Ic.existsSync)(e)&&(u=`Working directory does not exist: ${e}`),x.error({error:u,cwd:e},"spawn error"),_.emit("driver.error",{agent:"codex",error:u})}),this.proc.stdout.on("data",c=>{this.buffer+=c.toString();let u=this.buffer.split(`
87
87
  `);this.buffer=u.pop();for(let d of u)if(d.trim())try{this.handleRaw(JSON.parse(d))}catch{x.warn({linePreview:d.slice(0,200)},"failed to parse stdout line")}}),this.proc.stderr.on("data",c=>{let u=c.toString().trim();u&&(x.debug({stderr:u},"stderr"),this.lastStderr.push(u),this.lastStderr.length>10&&this.lastStderr.shift())}),this.proc.on("exit",c=>{x.info({threadId:this.threadId,exitCode:c},"process exited"),c&&c!==0&&x.error({threadId:this.threadId,exitCode:c,lastStderr:this.lastStderr.slice(-3)},"abnormal exit"),this.proc=null,this.approvalRequests.clear(),this.toolContextByCallId.clear(),this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart=!1;for(let[,{reject:u}]of this.pending)u(new Error(`Codex process exited with code ${c}`));this.pending.clear(),this.exitHandler?.(c),c&&this.handler&&this.threadId&&this.handler({type:"error",sessionId:this.threadId,message:`Codex process exited with code ${c}`})});let a=w.startTimer("rpc.duration");await this.rpc("initialize",{clientInfo:{name:"@vibelet/cli",version:St},capabilities:{experimentalApi:!0}}),a(),this.rpcNotify("initialized",{});let l=bc(this.approvalMode,e);if(n){let c=await this.rpc("thread/resume",{threadId:n,approvalPolicy:l.approvalPolicy,sandbox:l.sandbox,persistExtendedHistory:!0});this.threadId=c?.thread?.id??n,x.info({threadId:this.threadId},"thread resumed"),_.emit("driver.init",{agent:"codex",sessionId:this.threadId})}else{let c=await this.rpc("thread/start",{cwd:e,approvalPolicy:l.approvalPolicy,sandbox:l.sandbox,experimentalRawEvents:!0,persistExtendedHistory:!0});this.threadId=c?.thread?.id??c?.threadId??"",x.info({threadId:this.threadId},"thread created"),_.emit("driver.init",{agent:"codex",sessionId:this.threadId})}return this.threadId}buildSpawnArgs(){return["app-server","--listen","stdio://"]}sendPrompt(e){let n=bc(this.approvalMode,this.cwd||process.cwd());this.turnStartInFlight=!0,this.interruptRequestedDuringTurnStart=!1,x.info({threadId:this.threadId,promptPreview:e.slice(0,50)},"turn/start"),this.rpc("turn/start",{threadId:this.threadId,input:[{type:"text",text:e}],approvalPolicy:n.approvalPolicy,sandboxPolicy:n.sandboxPolicy}).then(r=>{x.debug({resultPreview:JSON.stringify(r).slice(0,200)},"turn/start result"),this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart&&(this.interruptRequestedDuringTurnStart=!1,this.requestTurnInterrupt())}).catch(r=>{this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart=!1,x.error({threadId:this.threadId,error:r.message},"sendPrompt error"),this.handler?.({type:"error",sessionId:this.threadId,message:r.message})})}respondApproval(e,n){if(!this.proc?.stdin?.writable)return!1;let r=this.approvalRequests.get(e),s=r?.rpcId??Number(e);if(!Number.isFinite(s))return!1;let i;if(!r)i=n?{decision:"approve"}:{decision:"deny",reason:"User denied from Vibelet"};else{switch(r.kind){case"command-execution":case"file-change":i={decision:n?"accept":"decline"};break;case"request-user-input-approval":i={answers:{[r.questionId]:{answers:[n?r.approveLabel:r.denyLabel]}}};break;case"exec-command-legacy":case"apply-patch-legacy":i={decision:n?"approved":"denied"};break;default:i={decision:n?"accept":"decline"};break}this.approvalRequests.delete(e)}return x.info({threadId:this.threadId,rpcId:s,approved:n},"approval response"),this.proc.stdin.write(JSON.stringify({jsonrpc:"2.0",id:s,result:i})+`
88
88
  `),!0}restorePendingApproval(e){let n=mg(e);n&&this.approvalRequests.set(e.requestId,n)}interrupt(){this.turnStartInFlight&&(this.interruptRequestedDuringTurnStart=!0),this.requestTurnInterrupt()}setApprovalMode(e){this.approvalMode=e,x.info({approvalMode:e},"approval mode updated (takes effect on subsequent turns)")}stop(){if(!this.proc)return;this.proc.kill("SIGTERM");let e=this.proc;setTimeout(()=>{e.killed||e.kill("SIGKILL")},5e3).unref(),this.proc=null,this.approvalRequests.clear(),this.toolContextByCallId.clear(),this.turnStartInFlight=!1,this.interruptRequestedDuringTurnStart=!1;for(let[,{reject:n}]of this.pending)n(new Error("Process stopped"));this.pending.clear()}onMessage(e){this.handler=e}onExit(e){this.exitHandler=e}rpc(e,n){let r=++this.rpcId,s=w.startTimer("rpc.duration");return new Promise((i,o)=>{if(this.pending.set(r,{resolve:a=>{s(),i(a)},reject:a=>{s(),o(a)}}),!this.proc?.stdin?.writable){this.pending.delete(r),s(),o(new Error("Process not available"));return}this.proc.stdin.write(JSON.stringify({jsonrpc:"2.0",method:e,id:r,params:n})+`
89
89
  `),setTimeout(()=>{this.pending.has(r)&&(this.pending.delete(r),x.error({method:e,rpcId:r,threadId:this.threadId},"RPC timeout"),s(),o(new Error(`RPC timeout: ${e}`)))},3e4).unref()})}requestTurnInterrupt(){this.rpc("turn/interrupt",{threadId:this.threadId}).catch(()=>{})}rpcNotify(e,n){this.proc?.stdin?.writable&&this.proc.stdin.write(JSON.stringify({jsonrpc:"2.0",method:e,params:n})+`
@@ -116,8 +116,8 @@ Fallbacks: ${n.fallbackHosts?.join(", ")||"(none)"}
116
116
 
117
117
  Claude: ${v.claudePath}
118
118
  Codex: ${v.codexPath}
119
- `);try{let r=await Bu.default.toString(JSON.stringify(n),{type:"terminal",small:!0});Qt(`
119
+ `);try{let r={t:"vp",d:n.daemonId,n:n.displayName,h:n.canonicalHost,p:n.port,c:n.pairNonce,e:n.expiresAt};n.fallbackHosts&&(r.f=n.fallbackHosts);let s=await Bu.default.toString(JSON.stringify(r),{type:"terminal",small:!0,errorCorrectionLevel:"L"});Qt(`
120
120
  Scan this QR code with Vibelet app:
121
121
 
122
- ${r}
122
+ ${s}
123
123
  `)}catch{}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibelet",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "macOS CLI for installing and running the Vibelet daemon",
5
5
  "homepage": "https://vibelet.icu",
6
6
  "private": false,
@@ -42,7 +42,10 @@
42
42
  "publish:dual:dry-run": "node ./scripts/publish-dual-npm.mjs --dry-run"
43
43
  },
44
44
  "dependencies": {
45
- "qrcode": "^1.5.4"
45
+ "qrcode": "^1.5.4",
46
+ "expo": "~55.0.7",
47
+ "react": "19.2.0",
48
+ "react-native": "0.83.2"
46
49
  },
47
50
  "devDependencies": {
48
51
  "nx": "^22.1.3"