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 +5 -1
- package/bin/vibelet-runtime-policy.mjs +27 -0
- package/bin/vibelet.mjs +93 -45
- package/dist/index.cjs +4 -4
- package/package.json +5 -2
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
|
-
|
|
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} --
|
|
603
|
-
process.stdout.write(` npx ${packageJson.name} --
|
|
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(` #
|
|
620
|
-
process.stdout.write(` npx ${packageJson.name}
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
792
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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.
|
|
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(
|
|
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
|
-
${
|
|
122
|
+
${s}
|
|
123
123
|
`)}catch{}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibelet",
|
|
3
|
-
"version": "0.1.
|
|
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"
|