@zonease/happy 0.6.39 → 0.6.41

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.
Files changed (78) hide show
  1. package/bin/happy.mjs +10 -0
  2. package/dist/{api-CIZKyfSn.mjs → api-K2-jr9ar.mjs} +86 -6
  3. package/dist/{api-CwOCQeLH.cjs → api-XeHlmApX.cjs} +87 -7
  4. package/dist/{auth-BIleVCw0.cjs → auth-BQvEuSs5.cjs} +2 -2
  5. package/dist/{auth-Cas9DR3b.cjs → auth-BbvPcnkY.cjs} +4 -4
  6. package/dist/{auth-0VnlXT9f.mjs → auth-CjLkoM3m.mjs} +4 -4
  7. package/dist/{auth-BE8dD20T.mjs → auth-DS8fzlin.mjs} +2 -2
  8. package/dist/{browser-Dll5hxOV.mjs → browser-C05Ak6RL.mjs} +1 -1
  9. package/dist/{browser-Cc08m7of.cjs → browser-C05SP_Tt.cjs} +1 -1
  10. package/dist/{claudeLocal-WkgLbl1a.cjs → claudeLocal-CpDhLCcZ.cjs} +1 -1
  11. package/dist/{claudeLocal-CPT3zcIU.mjs → claudeLocal-DJNRlHIC.mjs} +1 -1
  12. package/dist/{config-Draa-5XC.cjs → config-B0mns9fF.cjs} +1 -1
  13. package/dist/{config-rTT4qTU5.mjs → config-DWWtjKQx.mjs} +1 -1
  14. package/dist/{connect-CJZ73syK.cjs → connect-BxCScVNX.cjs} +4 -4
  15. package/dist/{connect-BfC1tYP_.mjs → connect-DXQC1CJe.mjs} +4 -4
  16. package/dist/{controlClient-DZMIsKsT.cjs → controlClient-DgELDZgM.cjs} +10 -1
  17. package/dist/{controlClient-CM_QcJnB.mjs → controlClient-DgjVRN8D.mjs} +10 -2
  18. package/dist/deviceKeys-BpIAWz13.cjs +182 -0
  19. package/dist/deviceKeys-CejCdWpw.mjs +175 -0
  20. package/dist/{doctor-DZYB6IVc.mjs → doctor-B-LCMVN9.mjs} +3 -3
  21. package/dist/{doctor-Cya9YSA0.cjs → doctor-DznasgP3.cjs} +3 -3
  22. package/dist/{downloadImage-MQMrUUxS.cjs → downloadImage-B_ygLAXY.cjs} +5 -5
  23. package/dist/{downloadImage-DfSFLb-h.mjs → downloadImage-ia5SzlCV.mjs} +3 -3
  24. package/dist/{enroll-BxlbVftP.cjs → enroll-BvfpRUGJ.cjs} +3 -3
  25. package/dist/{enroll-C52x8oly.mjs → enroll-HjUqyGnD.mjs} +3 -3
  26. package/dist/{evidence-Ccd9iYcj.mjs → evidence-BEQd1dCh.mjs} +1 -1
  27. package/dist/{evidence-CjWBG5rX.cjs → evidence-CVgTjBrq.cjs} +1 -1
  28. package/dist/{formatImageMessage-DbyG8c2j.cjs → formatImageMessage-CmJTP_hU.cjs} +1 -1
  29. package/dist/{formatImageMessage-B4lSxlk2.mjs → formatImageMessage-DVs57t3x.mjs} +1 -1
  30. package/dist/index.cjs +58 -43
  31. package/dist/index.mjs +58 -43
  32. package/dist/{install-C_cp8b2-.cjs → install-2Yl_L582.cjs} +2 -2
  33. package/dist/{install-oRAjVLjP.mjs → install-B3mMGISS.mjs} +1 -1
  34. package/dist/{install-BokHRgFW.cjs → install-Ck22tYHP.cjs} +1 -1
  35. package/dist/{install-DAeCn8ae.mjs → install-UTz_vYvB.mjs} +2 -2
  36. package/dist/lib.cjs +3 -3
  37. package/dist/lib.d.cts +77 -0
  38. package/dist/lib.d.mts +77 -0
  39. package/dist/lib.mjs +3 -3
  40. package/dist/{loadableToolOutput-Cuygmd4K.mjs → loadableToolOutput-4EvraaNY.mjs} +3 -3
  41. package/dist/{loadableToolOutput-DDCJjywt.cjs → loadableToolOutput-BZYlaqHn.cjs} +3 -3
  42. package/dist/{persistence-BFjvtUad.mjs → persistence-CIUFyOwz.mjs} +2 -2
  43. package/dist/{persistence-JLUn_Nou.cjs → persistence-DrfN7TzY.cjs} +1 -1
  44. package/dist/push-CCOXCcM_.cjs +80 -0
  45. package/dist/push-USXj_pv0.mjs +78 -0
  46. package/dist/{run-BDn2aE3Q.cjs → run-D3CopmEb.cjs} +45 -9
  47. package/dist/{run-DBZqN6Kn.mjs → run-Dy6KX6m-.mjs} +45 -9
  48. package/dist/{runClaude-DNHvERX0.mjs → runClaude-CWux2RDq.mjs} +14 -14
  49. package/dist/{runClaude-uaKxPZPm.cjs → runClaude-aHzvx6Ha.cjs} +15 -15
  50. package/dist/{runCodex-C-RTM0wA.mjs → runCodex-D0ZxSj3O.mjs} +13 -13
  51. package/dist/{runCodex-82UrZLme.cjs → runCodex-G-Aq531U.cjs} +13 -13
  52. package/dist/{runGemini-Bm6-4Xm2.mjs → runGemini-DZC2i6vA.mjs} +15 -15
  53. package/dist/{runGemini-CXMr4Jwb.cjs → runGemini-DxGgDAIM.cjs} +15 -15
  54. package/dist/{runOneShot-1B6RMYT7.cjs → runOneShot-BgT-JNMA.cjs} +2 -2
  55. package/dist/{runOneShot-ioARRHrx.mjs → runOneShot-cxlk7VKA.mjs} +2 -2
  56. package/dist/{serve-CwibwU6N.mjs → serve-CKyvJNs0.mjs} +6 -6
  57. package/dist/{serve-BtdAhkIE.cjs → serve-Dr3wPrtK.cjs} +6 -6
  58. package/dist/{session-D--F87zL.cjs → session-dYzbOo_W.cjs} +5 -5
  59. package/dist/{session-2sU4Bu1R.mjs → session-vf2ZjFto.mjs} +5 -5
  60. package/dist/{sessionKeyCache-D2BwO91C.mjs → sessionKeyCache-D2u3JUys.mjs} +1 -1
  61. package/dist/{sessionKeyCache-4Tp8pJ3N.cjs → sessionKeyCache-dDquDzn0.cjs} +1 -1
  62. package/dist/{sessionList-BCSw3qYt.mjs → sessionList-C1k2PNg_.mjs} +2 -2
  63. package/dist/{sessionList-ICJsjNtJ.cjs → sessionList-DmmtC7uR.cjs} +2 -2
  64. package/dist/{sessionSummary-lHGBWQSb.mjs → sessionSummary-5a1QtFVS.mjs} +3 -3
  65. package/dist/{sessionSummary-Bkz52BPh.cjs → sessionSummary-DGLoVOxd.cjs} +3 -3
  66. package/dist/{spawnHappyCLI-D_V-AAoP.cjs → spawnHappyCLI-CarRI4GM.cjs} +1 -1
  67. package/dist/{spawnHappyCLI-CoORX0YW.mjs → spawnHappyCLI-D3EkTFIn.mjs} +1 -1
  68. package/dist/{ssh-DL_AkRAD.mjs → ssh-B0hvEYIl.mjs} +137 -103
  69. package/dist/{ssh-BTOlhhxi.cjs → ssh-CBgVwKsB.cjs} +142 -106
  70. package/dist/{task-CSpaC9HX.cjs → task-CMunPKCV.cjs} +6 -6
  71. package/dist/{task-CQzrHRZe.mjs → task-CRo6W-3_.mjs} +6 -6
  72. package/dist/{uninstall-nFMFGUVM.mjs → uninstall-BALE2up0.mjs} +2 -2
  73. package/dist/{uninstall-BiKyRAb1.cjs → uninstall-BJ3eEGM4.cjs} +2 -2
  74. package/dist/{update-Dw8pgsAT.cjs → update-Bb3iijy8.cjs} +3 -3
  75. package/dist/{update-BcqRHXf2.mjs → update-BfjtLrhO.mjs} +3 -3
  76. package/package.json +1 -1
  77. package/dist/deviceExec-CU1R9CRb.cjs +0 -84
  78. package/dist/deviceExec-D93AOVV4.mjs +0 -81
package/bin/happy.mjs CHANGED
@@ -2,8 +2,18 @@
2
2
 
3
3
  import { execFileSync } from 'child_process';
4
4
  import { fileURLToPath } from 'url';
5
+ import { homedir } from 'os';
5
6
  import { join, dirname } from 'path';
6
7
 
8
+ // V8 keeps compiled bytecode here between runs. Set before the child starts, so
9
+ // it covers the entry's own imports too — half a command's time goes into
10
+ // compiling this bundle. Mirrors configuration.happyHomeDir; a wrong guess only
11
+ // costs a cache miss.
12
+ if (!process.env.NODE_COMPILE_CACHE) {
13
+ const happyHome = process.env.HAPPY_HOME_DIR || join(homedir(), '.happy');
14
+ process.env.NODE_COMPILE_CACHE = join(happyHome, 'compile-cache');
15
+ }
16
+
7
17
  // Check if we're already running with the flags
8
18
  const hasNoWarnings = process.execArgv.includes('--no-warnings');
9
19
  const hasNoDeprecation = process.execArgv.includes('--no-deprecation');
@@ -1,5 +1,5 @@
1
1
  import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import axios from 'axios';
2
- import { l as logger, c as configuration, f as encodeBase64, g as encrypt, h as decrypt, e as decodeBase64, i as isDebug, B as encryptLegacy, a as readSettings, C as getRandomBytes, D as libsodiumEncryptForPublicKey } from './persistence-BFjvtUad.mjs';
2
+ import { l as logger, c as configuration, e as encodeBase64, f as encrypt, g as decrypt, h as decodeBase64, i as isDebug, B as encryptLegacy, a as readSettings, C as getRandomBytes, D as libsodiumEncryptForPublicKey } from './persistence-CIUFyOwz.mjs';
3
3
  import { EventEmitter } from 'node:events';
4
4
  import { io } from 'socket.io-client';
5
5
  import { z } from 'zod';
@@ -24,7 +24,7 @@ import tweetnacl from 'tweetnacl';
24
24
  import { encode, decode } from '@stablelib/base64';
25
25
  import { createInterface } from 'node:readline';
26
26
  import { Expo } from 'expo-server-sdk';
27
- import { c as cacheSessionKey } from './sessionKeyCache-D2BwO91C.mjs';
27
+ import { c as cacheSessionKey } from './sessionKeyCache-D2u3JUys.mjs';
28
28
  import chalk from 'chalk';
29
29
 
30
30
  z.object({
@@ -1548,7 +1548,7 @@ function spawnShell(opts) {
1548
1548
  }
1549
1549
  const shell = process.env.SHELL || (process.platform === "win32" ? "powershell.exe" : "bash");
1550
1550
  const cwd = opts.cwd ?? process.cwd();
1551
- const shellArgs = process.platform !== "win32" && isBashShell(shell) ? ["--rcfile", createHappyBashRc(), "-i"] : [];
1551
+ const shellArgs = process.platform === "win32" ? [] : isBashShell(shell) ? ["--rcfile", createHappyBashRc(), "-i"] : ["-l", "-i"];
1552
1552
  const shellEnv = { ...process.env };
1553
1553
  delete shellEnv.NO_COLOR;
1554
1554
  delete shellEnv.NODE_DISABLE_COLORS;
@@ -1724,6 +1724,10 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
1724
1724
  cwd: data.cwd === "/" ? void 0 : data.cwd,
1725
1725
  timeout: data.timeout || 3e4,
1726
1726
  // Default 30 seconds timeout
1727
+ // node's 1MB default kills the child and reports failure once
1728
+ // output crosses it — the command has already succeeded by
1729
+ // then, and the caller sees a truncated result with an error.
1730
+ maxBuffer: 64 * 1024 * 1024,
1727
1731
  // launchd and systemd hand the daemon a bare PATH, so without
1728
1732
  // this a command that works when typed into the device terminal
1729
1733
  // fails here with "command not found".
@@ -2265,19 +2269,24 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
2265
2269
  };
2266
2270
  });
2267
2271
  const outboundStates = /* @__PURE__ */ new Map();
2268
- const emitEncrypted = (event, payload) => {
2272
+ const emitEncrypted = (event, payload, reliable = false) => {
2269
2273
  const socket = rpcHandlerManager.getSocket();
2270
2274
  if (!socket || !socket.connected) {
2271
2275
  return;
2272
2276
  }
2273
2277
  const envelope = { sessionId: payload.sessionId, ptyId: payload.ptyId };
2274
- if (event === "pty-output" && typeof payload.data === "string") {
2278
+ if (reliable) envelope.reliable = true;
2279
+ if (event === "pty-output" && payload.data !== void 0) {
2275
2280
  const key = rpcHandlerManager.getEncryptionKey();
2276
2281
  const variant = rpcHandlerManager.getEncryptionVariant();
2277
2282
  envelope.data = encodeBase64(encrypt(key, variant, payload.data));
2278
2283
  } else if (event === "pty-exit" && typeof payload.exitCode === "number") {
2279
2284
  envelope.exitCode = payload.exitCode;
2280
2285
  }
2286
+ if (reliable) {
2287
+ socket.emit(event, envelope);
2288
+ return;
2289
+ }
2281
2290
  socket.volatile.emit(event, envelope);
2282
2291
  };
2283
2292
  const splitOnUtf8Boundary = (merged) => {
@@ -2334,7 +2343,73 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
2334
2343
  outboundStates.delete(ptyId);
2335
2344
  };
2336
2345
  rpcHandlerManager.registerHandler("pty-start", async (data) => {
2337
- logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd });
2346
+ logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd, exec: !!data.exec });
2347
+ if (data.exec) {
2348
+ const execId = randomUUID();
2349
+ const child = spawn$1(process.env.SHELL || "/bin/sh", ["-c", data.exec.command], {
2350
+ cwd: data.cwd,
2351
+ env: await resolveLoginShellEnv(),
2352
+ stdio: ["ignore", "pipe", "pipe"]
2353
+ });
2354
+ const killTimer = setTimeout(() => child.kill("SIGTERM"), data.exec.timeoutMs ?? 3e5);
2355
+ child.stdout?.pause();
2356
+ child.stderr?.pause();
2357
+ setImmediate(() => {
2358
+ child.stdout?.resume();
2359
+ child.stderr?.resume();
2360
+ });
2361
+ const pending = { stdout: [], stderr: [] };
2362
+ let pendingBytes = 0;
2363
+ let frameCount = 0;
2364
+ let flushTimer = null;
2365
+ const flushExec = () => {
2366
+ if (flushTimer) {
2367
+ clearTimeout(flushTimer);
2368
+ flushTimer = null;
2369
+ }
2370
+ pendingBytes = 0;
2371
+ frameCount++;
2372
+ for (const stream of ["stdout", "stderr"]) {
2373
+ if (pending[stream].length === 0) continue;
2374
+ const chunk = Buffer.concat(pending[stream]);
2375
+ pending[stream] = [];
2376
+ emitEncrypted("pty-output", {
2377
+ sessionId,
2378
+ ptyId: execId,
2379
+ data: { stream, chunk: chunk.toString("base64") }
2380
+ }, true);
2381
+ }
2382
+ };
2383
+ for (const [stream, source] of [["stdout", child.stdout], ["stderr", child.stderr]]) {
2384
+ source?.on("data", (chunk) => {
2385
+ pending[stream].push(chunk);
2386
+ pendingBytes += chunk.length;
2387
+ if (pendingBytes >= PTY_OUTPUT_FLUSH_BYTES) {
2388
+ flushExec();
2389
+ return;
2390
+ }
2391
+ if (!flushTimer) flushTimer = setTimeout(flushExec, PTY_OUTPUT_FLUSH_MS);
2392
+ });
2393
+ }
2394
+ child.on("close", (code, signal) => {
2395
+ clearTimeout(killTimer);
2396
+ logger.debug(`[exec] close code=${code} signal=${signal} pending=${pendingBytes}`);
2397
+ flushExec();
2398
+ logger.debug(`[exec] flushed ${frameCount} frames, emitting exit`);
2399
+ emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: code ?? (signal ? 143 : 1) }, true);
2400
+ });
2401
+ child.on("error", (error) => {
2402
+ clearTimeout(killTimer);
2403
+ emitEncrypted("pty-output", {
2404
+ sessionId,
2405
+ ptyId: execId,
2406
+ data: { stream: "stderr", chunk: Buffer.from(`${error.message}
2407
+ `).toString("base64") }
2408
+ }, true);
2409
+ emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: 1 }, true);
2410
+ });
2411
+ return { ok: true, ptyId: execId };
2412
+ }
2338
2413
  try {
2339
2414
  const { ptyId, term } = spawnShell({
2340
2415
  cols: data.cols,
@@ -6179,6 +6254,11 @@ class ApiMachineClient {
6179
6254
  });
6180
6255
  }
6181
6256
  socket;
6257
+ /** The daemon's live connection. Reused for device RPC so a CLI command does
6258
+ * not spend ~500ms building its own TLS + WebSocket handshake. */
6259
+ get rpcSocket() {
6260
+ return this.socket;
6261
+ }
6182
6262
  keepAliveInterval = null;
6183
6263
  rpcHandlerManager;
6184
6264
  claudeCache = new SessionCache({
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var axios = require('axios');
4
- var logger = require('./persistence-JLUn_Nou.cjs');
4
+ var logger = require('./persistence-DrfN7TzY.cjs');
5
5
  var node_events = require('node:events');
6
6
  var socket_ioClient = require('socket.io-client');
7
7
  var z = require('zod');
@@ -26,7 +26,7 @@ var tweetnacl = require('tweetnacl');
26
26
  var base64 = require('@stablelib/base64');
27
27
  var node_readline = require('node:readline');
28
28
  var expoServerSdk = require('expo-server-sdk');
29
- var sessionKeyCache = require('./sessionKeyCache-4Tp8pJ3N.cjs');
29
+ var sessionKeyCache = require('./sessionKeyCache-dDquDzn0.cjs');
30
30
  var chalk = require('chalk');
31
31
 
32
32
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
@@ -1395,7 +1395,7 @@ function resolveToolsDir() {
1395
1395
  }
1396
1396
  function findToolsDir() {
1397
1397
  try {
1398
- const manifest = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('api-CwOCQeLH.cjs', document.baseURI).href))).resolve(`${toolsPackageName()}/package.json`);
1398
+ const manifest = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('api-XeHlmApX.cjs', document.baseURI).href))).resolve(`${toolsPackageName()}/package.json`);
1399
1399
  return path.dirname(manifest);
1400
1400
  } catch {
1401
1401
  }
@@ -1551,7 +1551,7 @@ function spawnShell(opts) {
1551
1551
  }
1552
1552
  const shell = process.env.SHELL || (process.platform === "win32" ? "powershell.exe" : "bash");
1553
1553
  const cwd = opts.cwd ?? process.cwd();
1554
- const shellArgs = process.platform !== "win32" && isBashShell(shell) ? ["--rcfile", createHappyBashRc(), "-i"] : [];
1554
+ const shellArgs = process.platform === "win32" ? [] : isBashShell(shell) ? ["--rcfile", createHappyBashRc(), "-i"] : ["-l", "-i"];
1555
1555
  const shellEnv = { ...process.env };
1556
1556
  delete shellEnv.NO_COLOR;
1557
1557
  delete shellEnv.NODE_DISABLE_COLORS;
@@ -1727,6 +1727,10 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
1727
1727
  cwd: data.cwd === "/" ? void 0 : data.cwd,
1728
1728
  timeout: data.timeout || 3e4,
1729
1729
  // Default 30 seconds timeout
1730
+ // node's 1MB default kills the child and reports failure once
1731
+ // output crosses it — the command has already succeeded by
1732
+ // then, and the caller sees a truncated result with an error.
1733
+ maxBuffer: 64 * 1024 * 1024,
1730
1734
  // launchd and systemd hand the daemon a bare PATH, so without
1731
1735
  // this a command that works when typed into the device terminal
1732
1736
  // fails here with "command not found".
@@ -2268,19 +2272,24 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
2268
2272
  };
2269
2273
  });
2270
2274
  const outboundStates = /* @__PURE__ */ new Map();
2271
- const emitEncrypted = (event, payload) => {
2275
+ const emitEncrypted = (event, payload, reliable = false) => {
2272
2276
  const socket = rpcHandlerManager.getSocket();
2273
2277
  if (!socket || !socket.connected) {
2274
2278
  return;
2275
2279
  }
2276
2280
  const envelope = { sessionId: payload.sessionId, ptyId: payload.ptyId };
2277
- if (event === "pty-output" && typeof payload.data === "string") {
2281
+ if (reliable) envelope.reliable = true;
2282
+ if (event === "pty-output" && payload.data !== void 0) {
2278
2283
  const key = rpcHandlerManager.getEncryptionKey();
2279
2284
  const variant = rpcHandlerManager.getEncryptionVariant();
2280
2285
  envelope.data = logger.encodeBase64(logger.encrypt(key, variant, payload.data));
2281
2286
  } else if (event === "pty-exit" && typeof payload.exitCode === "number") {
2282
2287
  envelope.exitCode = payload.exitCode;
2283
2288
  }
2289
+ if (reliable) {
2290
+ socket.emit(event, envelope);
2291
+ return;
2292
+ }
2284
2293
  socket.volatile.emit(event, envelope);
2285
2294
  };
2286
2295
  const splitOnUtf8Boundary = (merged) => {
@@ -2337,7 +2346,73 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
2337
2346
  outboundStates.delete(ptyId);
2338
2347
  };
2339
2348
  rpcHandlerManager.registerHandler("pty-start", async (data) => {
2340
- logger.logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd });
2349
+ logger.logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd, exec: !!data.exec });
2350
+ if (data.exec) {
2351
+ const execId = node_crypto.randomUUID();
2352
+ const child = child_process.spawn(process.env.SHELL || "/bin/sh", ["-c", data.exec.command], {
2353
+ cwd: data.cwd,
2354
+ env: await resolveLoginShellEnv(),
2355
+ stdio: ["ignore", "pipe", "pipe"]
2356
+ });
2357
+ const killTimer = setTimeout(() => child.kill("SIGTERM"), data.exec.timeoutMs ?? 3e5);
2358
+ child.stdout?.pause();
2359
+ child.stderr?.pause();
2360
+ setImmediate(() => {
2361
+ child.stdout?.resume();
2362
+ child.stderr?.resume();
2363
+ });
2364
+ const pending = { stdout: [], stderr: [] };
2365
+ let pendingBytes = 0;
2366
+ let frameCount = 0;
2367
+ let flushTimer = null;
2368
+ const flushExec = () => {
2369
+ if (flushTimer) {
2370
+ clearTimeout(flushTimer);
2371
+ flushTimer = null;
2372
+ }
2373
+ pendingBytes = 0;
2374
+ frameCount++;
2375
+ for (const stream of ["stdout", "stderr"]) {
2376
+ if (pending[stream].length === 0) continue;
2377
+ const chunk = Buffer.concat(pending[stream]);
2378
+ pending[stream] = [];
2379
+ emitEncrypted("pty-output", {
2380
+ sessionId,
2381
+ ptyId: execId,
2382
+ data: { stream, chunk: chunk.toString("base64") }
2383
+ }, true);
2384
+ }
2385
+ };
2386
+ for (const [stream, source] of [["stdout", child.stdout], ["stderr", child.stderr]]) {
2387
+ source?.on("data", (chunk) => {
2388
+ pending[stream].push(chunk);
2389
+ pendingBytes += chunk.length;
2390
+ if (pendingBytes >= PTY_OUTPUT_FLUSH_BYTES) {
2391
+ flushExec();
2392
+ return;
2393
+ }
2394
+ if (!flushTimer) flushTimer = setTimeout(flushExec, PTY_OUTPUT_FLUSH_MS);
2395
+ });
2396
+ }
2397
+ child.on("close", (code, signal) => {
2398
+ clearTimeout(killTimer);
2399
+ logger.logger.debug(`[exec] close code=${code} signal=${signal} pending=${pendingBytes}`);
2400
+ flushExec();
2401
+ logger.logger.debug(`[exec] flushed ${frameCount} frames, emitting exit`);
2402
+ emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: code ?? (signal ? 143 : 1) }, true);
2403
+ });
2404
+ child.on("error", (error) => {
2405
+ clearTimeout(killTimer);
2406
+ emitEncrypted("pty-output", {
2407
+ sessionId,
2408
+ ptyId: execId,
2409
+ data: { stream: "stderr", chunk: Buffer.from(`${error.message}
2410
+ `).toString("base64") }
2411
+ }, true);
2412
+ emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: 1 }, true);
2413
+ });
2414
+ return { ok: true, ptyId: execId };
2415
+ }
2341
2416
  try {
2342
2417
  const { ptyId, term } = spawnShell({
2343
2418
  cols: data.cols,
@@ -6182,6 +6257,11 @@ class ApiMachineClient {
6182
6257
  });
6183
6258
  }
6184
6259
  socket;
6260
+ /** The daemon's live connection. Reused for device RPC so a CLI command does
6261
+ * not spend ~500ms building its own TLS + WebSocket handshake. */
6262
+ get rpcSocket() {
6263
+ return this.socket;
6264
+ }
6185
6265
  keepAliveInterval = null;
6186
6266
  rpcHandlerManager;
6187
6267
  claudeCache = new SessionCache({
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./persistence-JLUn_Nou.cjs');
3
+ var logger = require('./persistence-DrfN7TzY.cjs');
4
4
  var node_crypto = require('node:crypto');
5
5
  var tweetnacl = require('tweetnacl');
6
6
  var axios = require('axios');
7
7
  var qrcode = require('qrcode-terminal');
8
8
  var time = require('./time-Bj2HRkKH.cjs');
9
- var browser = require('./browser-Cc08m7of.cjs');
9
+ var browser = require('./browser-C05SP_Tt.cjs');
10
10
  var React = require('react');
11
11
  var ink = require('ink');
12
12
  require('node:fs/promises');
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var logger = require('./persistence-JLUn_Nou.cjs');
5
- var auth = require('./auth-BIleVCw0.cjs');
4
+ var logger = require('./persistence-DrfN7TzY.cjs');
5
+ var auth = require('./auth-BQvEuSs5.cjs');
6
6
  var fs = require('node:fs');
7
7
  var node_readline = require('node:readline');
8
- var controlClient = require('./controlClient-DZMIsKsT.cjs');
8
+ var controlClient = require('./controlClient-DgELDZgM.cjs');
9
9
  var os = require('node:os');
10
10
  require('node:fs/promises');
11
11
  require('node:path');
@@ -16,7 +16,7 @@ require('fs');
16
16
  require('axios');
17
17
  require('qrcode-terminal');
18
18
  require('./time-Bj2HRkKH.cjs');
19
- require('./browser-Cc08m7of.cjs');
19
+ require('./browser-C05SP_Tt.cjs');
20
20
  require('open');
21
21
  require('react');
22
22
  require('ink');
@@ -1,9 +1,9 @@
1
1
  import chalk from 'chalk';
2
- import { r as readCredentials, a as readSettings, c as configuration, l as logger, b as clearCredentials, d as clearMachineId } from './persistence-BFjvtUad.mjs';
3
- import { authAndSetupMachineIfNeeded } from './auth-BE8dD20T.mjs';
2
+ import { r as readCredentials, a as readSettings, c as configuration, l as logger, b as clearCredentials, d as clearMachineId } from './persistence-CIUFyOwz.mjs';
3
+ import { authAndSetupMachineIfNeeded } from './auth-DS8fzlin.mjs';
4
4
  import { existsSync, rmSync } from 'node:fs';
5
5
  import { createInterface } from 'node:readline';
6
- import { checkIfDaemonRunningAndCleanupStaleState, stopDaemon } from './controlClient-CM_QcJnB.mjs';
6
+ import { checkIfDaemonRunningAndCleanupStaleState, stopDaemon } from './controlClient-DgjVRN8D.mjs';
7
7
  import os from 'node:os';
8
8
  import 'node:fs/promises';
9
9
  import 'node:path';
@@ -14,7 +14,7 @@ import 'fs';
14
14
  import 'axios';
15
15
  import 'qrcode-terminal';
16
16
  import './time-DgsBLJA_.mjs';
17
- import './browser-Dll5hxOV.mjs';
17
+ import './browser-C05Ak6RL.mjs';
18
18
  import 'open';
19
19
  import 'react';
20
20
  import 'ink';
@@ -1,10 +1,10 @@
1
- import { f as encodeBase64, c as configuration, l as logger, r as readCredentials, u as updateSettings, i as isDebug, s as encodeBase64Url, e as decodeBase64, t as writeCredentialsLegacy, w as writeCredentialsDataKey } from './persistence-BFjvtUad.mjs';
1
+ import { e as encodeBase64, c as configuration, l as logger, r as readCredentials, u as updateSettings, i as isDebug, s as encodeBase64Url, h as decodeBase64, t as writeCredentialsLegacy, w as writeCredentialsDataKey } from './persistence-CIUFyOwz.mjs';
2
2
  import { randomBytes, randomUUID } from 'node:crypto';
3
3
  import tweetnacl from 'tweetnacl';
4
4
  import axios from 'axios';
5
5
  import qrcode from 'qrcode-terminal';
6
6
  import { d as delay } from './time-DgsBLJA_.mjs';
7
- import { o as openBrowser } from './browser-Dll5hxOV.mjs';
7
+ import { o as openBrowser } from './browser-C05Ak6RL.mjs';
8
8
  import React, { useState } from 'react';
9
9
  import { useInput, Box, Text, render } from 'ink';
10
10
  import 'node:fs/promises';
@@ -1,5 +1,5 @@
1
1
  import open from 'open';
2
- import { l as logger } from './persistence-BFjvtUad.mjs';
2
+ import { l as logger } from './persistence-CIUFyOwz.mjs';
3
3
 
4
4
  async function openBrowser(url) {
5
5
  try {
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var open = require('open');
4
- var logger = require('./persistence-JLUn_Nou.cjs');
4
+ var logger = require('./persistence-DrfN7TzY.cjs');
5
5
 
6
6
  async function openBrowser(url) {
7
7
  try {
@@ -5,7 +5,7 @@ var path = require('node:path');
5
5
  var node_readline = require('node:readline');
6
6
  var fs = require('node:fs');
7
7
  var node_crypto = require('node:crypto');
8
- var logger = require('./persistence-JLUn_Nou.cjs');
8
+ var logger = require('./persistence-DrfN7TzY.cjs');
9
9
  var os = require('node:os');
10
10
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
11
11
  var prompt = require('./prompt-BTWDZZO0.cjs');
@@ -3,7 +3,7 @@ import { resolve, join } from 'node:path';
3
3
  import { createInterface } from 'node:readline';
4
4
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync } from 'node:fs';
5
5
  import { randomUUID } from 'node:crypto';
6
- import { l as logger } from './persistence-BFjvtUad.mjs';
6
+ import { l as logger } from './persistence-CIUFyOwz.mjs';
7
7
  import { homedir } from 'node:os';
8
8
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
9
9
  import { g as getBaseSystemPrompt } from './prompt-BsAZsPbo.mjs';
@@ -4,7 +4,7 @@ var fs = require('fs');
4
4
  var path = require('path');
5
5
  var os = require('os');
6
6
  var child_process = require('child_process');
7
- var logger = require('./persistence-JLUn_Nou.cjs');
7
+ var logger = require('./persistence-DrfN7TzY.cjs');
8
8
  var constants = require('./constants-E_UWYTo3.cjs');
9
9
  require('node:fs/promises');
10
10
  require('node:fs');
@@ -2,7 +2,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { execSync } from 'child_process';
5
- import { l as logger } from './persistence-BFjvtUad.mjs';
5
+ import { l as logger } from './persistence-CIUFyOwz.mjs';
6
6
  import { D as DEFAULT_GEMINI_MODEL, G as GEMINI_MODEL_ENV } from './constants-pmXZGibX.mjs';
7
7
  import 'node:fs/promises';
8
8
  import 'node:fs';
@@ -4,11 +4,11 @@ var chalk = require('chalk');
4
4
  var fs = require('fs');
5
5
  var os = require('os');
6
6
  var path = require('path');
7
- var logger = require('./persistence-JLUn_Nou.cjs');
8
- var api = require('./api-CwOCQeLH.cjs');
7
+ var logger = require('./persistence-DrfN7TzY.cjs');
8
+ var api = require('./api-XeHlmApX.cjs');
9
9
  var http = require('http');
10
10
  var crypto = require('crypto');
11
- var browser = require('./browser-Cc08m7of.cjs');
11
+ var browser = require('./browser-C05SP_Tt.cjs');
12
12
  var child_process = require('child_process');
13
13
  var util = require('util');
14
14
  require('node:fs/promises');
@@ -34,7 +34,7 @@ require('ws');
34
34
  require('@stablelib/base64');
35
35
  require('node:readline');
36
36
  require('expo-server-sdk');
37
- require('./sessionKeyCache-4Tp8pJ3N.cjs');
37
+ require('./sessionKeyCache-dDquDzn0.cjs');
38
38
  require('open');
39
39
 
40
40
  const CLIENT_ID$2 = "app_EMoamEEZ73f0CkXaXp7hrann";
@@ -2,11 +2,11 @@ import chalk from 'chalk';
2
2
  import { existsSync, mkdirSync, writeFileSync } from 'fs';
3
3
  import { homedir } from 'os';
4
4
  import { join } from 'path';
5
- import { r as readCredentials } from './persistence-BFjvtUad.mjs';
6
- import { A as ApiClient } from './api-CIZKyfSn.mjs';
5
+ import { r as readCredentials } from './persistence-CIUFyOwz.mjs';
6
+ import { A as ApiClient } from './api-K2-jr9ar.mjs';
7
7
  import { createServer } from 'http';
8
8
  import { randomBytes, createHash } from 'crypto';
9
- import { o as openBrowser } from './browser-Dll5hxOV.mjs';
9
+ import { o as openBrowser } from './browser-C05Ak6RL.mjs';
10
10
  import { exec } from 'child_process';
11
11
  import { promisify } from 'util';
12
12
  import 'node:fs/promises';
@@ -32,7 +32,7 @@ import 'ws';
32
32
  import '@stablelib/base64';
33
33
  import 'node:readline';
34
34
  import 'expo-server-sdk';
35
- import './sessionKeyCache-D2BwO91C.mjs';
35
+ import './sessionKeyCache-D2u3JUys.mjs';
36
36
  import 'open';
37
37
 
38
38
  const CLIENT_ID$2 = "app_EMoamEEZ73f0CkXaXp7hrann";
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./persistence-JLUn_Nou.cjs');
3
+ var logger = require('./persistence-DrfN7TzY.cjs');
4
4
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
5
5
  var fs = require('fs');
6
6
  var path = require('path');
@@ -63,6 +63,14 @@ async function notifyDaemonSessionStarted(sessionId, metadata) {
63
63
  metadata
64
64
  });
65
65
  }
66
+ async function execOnDeviceViaDaemon(deviceId, command, timeoutMs) {
67
+ const response = await daemonPost("/device-exec", { deviceId, command, timeoutMs });
68
+ if (!response || response.error || response.ok !== true) {
69
+ logger.logger.debug(`[CONTROL CLIENT] Device exec via daemon unavailable: ${response?.error ?? "no daemon"}`);
70
+ return null;
71
+ }
72
+ return response.result;
73
+ }
66
74
  async function listDaemonSessions() {
67
75
  const result = await daemonPost("/list");
68
76
  return result.children || [];
@@ -160,6 +168,7 @@ async function waitForProcessDeath(pid, timeout) {
160
168
 
161
169
  exports.checkIfDaemonRunningAndCleanupStaleState = checkIfDaemonRunningAndCleanupStaleState;
162
170
  exports.cleanupDaemonState = cleanupDaemonState;
171
+ exports.execOnDeviceViaDaemon = execOnDeviceViaDaemon;
163
172
  exports.isDaemonRunningCurrentlyInstalledHappyVersion = isDaemonRunningCurrentlyInstalledHappyVersion;
164
173
  exports.listDaemonSessions = listDaemonSessions;
165
174
  exports.notifyDaemonSessionStarted = notifyDaemonSessionStarted;
@@ -1,4 +1,4 @@
1
- import { l as logger, j as readDaemonState, n as clearDaemonState } from './persistence-BFjvtUad.mjs';
1
+ import { l as logger, m as readDaemonState, n as clearDaemonState } from './persistence-CIUFyOwz.mjs';
2
2
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
3
3
  import { readFileSync } from 'fs';
4
4
  import { join } from 'path';
@@ -61,6 +61,14 @@ async function notifyDaemonSessionStarted(sessionId, metadata) {
61
61
  metadata
62
62
  });
63
63
  }
64
+ async function execOnDeviceViaDaemon(deviceId, command, timeoutMs) {
65
+ const response = await daemonPost("/device-exec", { deviceId, command, timeoutMs });
66
+ if (!response || response.error || response.ok !== true) {
67
+ logger.debug(`[CONTROL CLIENT] Device exec via daemon unavailable: ${response?.error ?? "no daemon"}`);
68
+ return null;
69
+ }
70
+ return response.result;
71
+ }
64
72
  async function listDaemonSessions() {
65
73
  const result = await daemonPost("/list");
66
74
  return result.children || [];
@@ -156,4 +164,4 @@ async function waitForProcessDeath(pid, timeout) {
156
164
  throw new Error("Process did not die within timeout");
157
165
  }
158
166
 
159
- export { checkIfDaemonRunningAndCleanupStaleState, cleanupDaemonState, isDaemonRunningCurrentlyInstalledHappyVersion, listDaemonSessions, notifyDaemonSessionStarted, stopDaemon, stopDaemonHttp, stopDaemonSession };
167
+ export { checkIfDaemonRunningAndCleanupStaleState, cleanupDaemonState, execOnDeviceViaDaemon, isDaemonRunningCurrentlyInstalledHappyVersion, listDaemonSessions, notifyDaemonSessionStarted, stopDaemon, stopDaemonHttp, stopDaemonSession };