@zonease/happy 0.6.39 → 0.6.40

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 (76) hide show
  1. package/bin/happy.mjs +10 -0
  2. package/dist/{api-CwOCQeLH.cjs → api-C7FyJfZ1.cjs} +87 -7
  3. package/dist/{api-CIZKyfSn.mjs → api-D6WL40-U.mjs} +86 -6
  4. package/dist/{auth-BIleVCw0.cjs → auth-C7ZScWEw.cjs} +2 -2
  5. package/dist/{auth-Cas9DR3b.cjs → auth-DCsIoZLT.cjs} +4 -4
  6. package/dist/{auth-0VnlXT9f.mjs → auth-DO9GexAR.mjs} +4 -4
  7. package/dist/{auth-BE8dD20T.mjs → auth-DxJ3VvrX.mjs} +2 -2
  8. package/dist/{browser-Dll5hxOV.mjs → browser-AoVGRX7y.mjs} +1 -1
  9. package/dist/{browser-Cc08m7of.cjs → browser-DzgO4oaD.cjs} +1 -1
  10. package/dist/{claudeLocal-WkgLbl1a.cjs → claudeLocal-C6BdzhDx.cjs} +1 -1
  11. package/dist/{claudeLocal-CPT3zcIU.mjs → claudeLocal-CWNGpJ0-.mjs} +1 -1
  12. package/dist/{config-Draa-5XC.cjs → config-CclB3QW9.cjs} +1 -1
  13. package/dist/{config-rTT4qTU5.mjs → config-DZk-mJ0K.mjs} +1 -1
  14. package/dist/{connect-BfC1tYP_.mjs → connect-BXLUKJ5P.mjs} +4 -4
  15. package/dist/{connect-CJZ73syK.cjs → connect-k9CqDJ-T.cjs} +4 -4
  16. package/dist/{controlClient-DZMIsKsT.cjs → controlClient-B23M-WPp.cjs} +10 -1
  17. package/dist/{controlClient-CM_QcJnB.mjs → controlClient-DB5tDU7V.mjs} +10 -2
  18. package/dist/deviceKeys-BTdo34Uz.mjs +175 -0
  19. package/dist/deviceKeys-C540DcXl.cjs +182 -0
  20. package/dist/{doctor-Cya9YSA0.cjs → doctor-BQGLhrcI.cjs} +2 -2
  21. package/dist/{doctor-DZYB6IVc.mjs → doctor-CU4JLGQ1.mjs} +2 -2
  22. package/dist/{downloadImage-DfSFLb-h.mjs → downloadImage-DZXI3mXL.mjs} +3 -3
  23. package/dist/{downloadImage-MQMrUUxS.cjs → downloadImage-cQ1Z7hkC.cjs} +5 -5
  24. package/dist/{enroll-C52x8oly.mjs → enroll-B55BQR56.mjs} +3 -3
  25. package/dist/{enroll-BxlbVftP.cjs → enroll-Bo-CNio1.cjs} +3 -3
  26. package/dist/{evidence-CjWBG5rX.cjs → evidence-DQp8QWcv.cjs} +1 -1
  27. package/dist/{evidence-Ccd9iYcj.mjs → evidence-DTYRAaOZ.mjs} +1 -1
  28. package/dist/{formatImageMessage-DbyG8c2j.cjs → formatImageMessage-CixDYY6G.cjs} +1 -1
  29. package/dist/{formatImageMessage-B4lSxlk2.mjs → formatImageMessage-DbKSSjMx.mjs} +1 -1
  30. package/dist/index.cjs +42 -42
  31. package/dist/index.mjs +42 -42
  32. package/dist/{install-DAeCn8ae.mjs → install-B1e6rdy7.mjs} +2 -2
  33. package/dist/{install-oRAjVLjP.mjs → install-CgUPGIoF.mjs} +1 -1
  34. package/dist/{install-C_cp8b2-.cjs → install-D7Ir7ciZ.cjs} +2 -2
  35. package/dist/{install-BokHRgFW.cjs → install-DSDufaPd.cjs} +1 -1
  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-Bfei9wPq.mjs} +3 -3
  41. package/dist/{loadableToolOutput-DDCJjywt.cjs → loadableToolOutput-CbLGtvST.cjs} +3 -3
  42. package/dist/{persistence-JLUn_Nou.cjs → persistence-CBTtC_dm.cjs} +1 -1
  43. package/dist/{persistence-BFjvtUad.mjs → persistence-DKfF81pd.mjs} +2 -2
  44. package/dist/{run-DBZqN6Kn.mjs → run-BVZsMNwN.mjs} +45 -9
  45. package/dist/{run-BDn2aE3Q.cjs → run-CURx7LhK.cjs} +45 -9
  46. package/dist/{runClaude-DNHvERX0.mjs → runClaude-6Gi0sYQy.mjs} +14 -14
  47. package/dist/{runClaude-uaKxPZPm.cjs → runClaude-CL3Kt5ex.cjs} +15 -15
  48. package/dist/{runCodex-C-RTM0wA.mjs → runCodex-ClwEgJ7M.mjs} +13 -13
  49. package/dist/{runCodex-82UrZLme.cjs → runCodex-YKyVBkJ3.cjs} +13 -13
  50. package/dist/{runGemini-Bm6-4Xm2.mjs → runGemini-BPWxVr60.mjs} +15 -15
  51. package/dist/{runGemini-CXMr4Jwb.cjs → runGemini-Xz2W1spn.cjs} +15 -15
  52. package/dist/{runOneShot-ioARRHrx.mjs → runOneShot-CdCWiiLr.mjs} +2 -2
  53. package/dist/{runOneShot-1B6RMYT7.cjs → runOneShot-DjZyCvDI.cjs} +2 -2
  54. package/dist/{serve-CwibwU6N.mjs → serve-B48JttQ3.mjs} +6 -6
  55. package/dist/{serve-BtdAhkIE.cjs → serve-B8vaWQA0.cjs} +6 -6
  56. package/dist/{session-2sU4Bu1R.mjs → session-BXboDn2V.mjs} +5 -5
  57. package/dist/{session-D--F87zL.cjs → session-BZO25zxp.cjs} +5 -5
  58. package/dist/{sessionKeyCache-D2BwO91C.mjs → sessionKeyCache-B6cGadYL.mjs} +1 -1
  59. package/dist/{sessionKeyCache-4Tp8pJ3N.cjs → sessionKeyCache-CDmexJ9p.cjs} +1 -1
  60. package/dist/{sessionList-BCSw3qYt.mjs → sessionList-CgAYZpNj.mjs} +2 -2
  61. package/dist/{sessionList-ICJsjNtJ.cjs → sessionList-lQ-nqEhc.cjs} +2 -2
  62. package/dist/{sessionSummary-Bkz52BPh.cjs → sessionSummary-CcORiQSq.cjs} +3 -3
  63. package/dist/{sessionSummary-lHGBWQSb.mjs → sessionSummary-DCWQoaDP.mjs} +3 -3
  64. package/dist/{spawnHappyCLI-D_V-AAoP.cjs → spawnHappyCLI-KiazE3kL.cjs} +1 -1
  65. package/dist/{spawnHappyCLI-CoORX0YW.mjs → spawnHappyCLI-yBIP_Oqi.mjs} +1 -1
  66. package/dist/{ssh-DL_AkRAD.mjs → ssh-q5uZhqBf.mjs} +134 -103
  67. package/dist/{ssh-BTOlhhxi.cjs → ssh-yMllSHu5.cjs} +138 -106
  68. package/dist/{task-CSpaC9HX.cjs → task-_nr7J19j.cjs} +6 -6
  69. package/dist/{task-CQzrHRZe.mjs → task-wNX1N2i4.mjs} +6 -6
  70. package/dist/{uninstall-BiKyRAb1.cjs → uninstall-Buaz7d-X.cjs} +2 -2
  71. package/dist/{uninstall-nFMFGUVM.mjs → uninstall-Dxf6_4mQ.mjs} +2 -2
  72. package/dist/{update-Dw8pgsAT.cjs → update-CGlIfa8e.cjs} +3 -3
  73. package/dist/{update-BcqRHXf2.mjs → update-CnnoX2cV.mjs} +3 -3
  74. package/package.json +1 -1
  75. package/dist/deviceExec-CU1R9CRb.cjs +0 -84
  76. package/dist/deviceExec-D93AOVV4.mjs +0 -81
package/dist/lib.d.mts CHANGED
@@ -1606,6 +1606,80 @@ type SpawnSessionResult = {
1606
1606
  * Similar to ApiSessionClient but for machine-scoped connections
1607
1607
  */
1608
1608
 
1609
+ interface ServerToDaemonEvents {
1610
+ update: (data: Update) => void;
1611
+ 'rpc-request': (data: {
1612
+ method: string;
1613
+ params: string;
1614
+ }, callback: (response: string) => void) => void;
1615
+ 'rpc-registered': (data: {
1616
+ method: string;
1617
+ }) => void;
1618
+ 'rpc-unregistered': (data: {
1619
+ method: string;
1620
+ }) => void;
1621
+ 'rpc-error': (data: {
1622
+ type: string;
1623
+ error: string;
1624
+ }) => void;
1625
+ auth: (data: {
1626
+ success: boolean;
1627
+ user: string;
1628
+ }) => void;
1629
+ error: (data: {
1630
+ message: string;
1631
+ }) => void;
1632
+ }
1633
+ interface DaemonToServerEvents {
1634
+ 'machine-alive': (data: {
1635
+ machineId: string;
1636
+ time: number;
1637
+ }) => void;
1638
+ 'machine-update-metadata': (data: {
1639
+ machineId: string;
1640
+ metadata: string;
1641
+ expectedVersion: number;
1642
+ }, cb: (answer: {
1643
+ result: 'error';
1644
+ } | {
1645
+ result: 'version-mismatch';
1646
+ version: number;
1647
+ metadata: string;
1648
+ } | {
1649
+ result: 'success';
1650
+ version: number;
1651
+ metadata: string;
1652
+ }) => void) => void;
1653
+ 'machine-update-state': (data: {
1654
+ machineId: string;
1655
+ daemonState: string;
1656
+ expectedVersion: number;
1657
+ }, cb: (answer: {
1658
+ result: 'error';
1659
+ } | {
1660
+ result: 'version-mismatch';
1661
+ version: number;
1662
+ daemonState: string;
1663
+ } | {
1664
+ result: 'success';
1665
+ version: number;
1666
+ daemonState: string;
1667
+ }) => void) => void;
1668
+ 'rpc-register': (data: {
1669
+ method: string;
1670
+ }) => void;
1671
+ 'rpc-unregister': (data: {
1672
+ method: string;
1673
+ }) => void;
1674
+ 'rpc-call': (data: {
1675
+ method: string;
1676
+ params: any;
1677
+ }, callback: (response: {
1678
+ ok: boolean;
1679
+ result?: any;
1680
+ error?: string;
1681
+ }) => void) => void;
1682
+ }
1609
1683
  type MachineRpcHandlers = {
1610
1684
  spawnSession: (options: SpawnSessionOptions) => Promise<SpawnSessionResult>;
1611
1685
  stopSession: (sessionId: string) => boolean;
@@ -1641,6 +1715,9 @@ declare class ApiMachineClient {
1641
1715
  private machine;
1642
1716
  private currentRuntimeDaemonState?;
1643
1717
  private socket;
1718
+ /** The daemon's live connection. Reused for device RPC so a CLI command does
1719
+ * not spend ~500ms building its own TLS + WebSocket handshake. */
1720
+ get rpcSocket(): Socket<ServerToDaemonEvents, DaemonToServerEvents>;
1644
1721
  private keepAliveInterval;
1645
1722
  private rpcHandlerManager;
1646
1723
  private claudeCache;
package/dist/lib.mjs CHANGED
@@ -1,5 +1,5 @@
1
- export { A as ApiClient, a as ApiSessionClient } from './api-CIZKyfSn.mjs';
2
- export { c as configuration, l as logger } from './persistence-BFjvtUad.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient } from './api-D6WL40-U.mjs';
2
+ export { c as configuration, l as logger } from './persistence-DKfF81pd.mjs';
3
3
  export { R as RawJSONLinesSchema } from './types-BoJh4Qfv.mjs';
4
4
  import 'axios';
5
5
  import 'node:events';
@@ -28,6 +28,6 @@ import 'tweetnacl';
28
28
  import '@stablelib/base64';
29
29
  import 'node:readline';
30
30
  import 'expo-server-sdk';
31
- import './sessionKeyCache-D2BwO91C.mjs';
31
+ import './sessionKeyCache-B6cGadYL.mjs';
32
32
  import 'chalk';
33
33
  import 'fs';
@@ -1,11 +1,11 @@
1
- import { l as logger, c as configuration, p as packageJson } from './persistence-BFjvtUad.mjs';
1
+ import { l as logger, c as configuration, p as packageJson } from './persistence-DKfF81pd.mjs';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { readFileSync, existsSync, readdirSync } from 'node:fs';
4
4
  import os, { homedir } from 'node:os';
5
5
  import { join, basename, dirname } from 'node:path';
6
6
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
7
- import { r as resolveToolsDir, s as startOfflineReconnection, b as saveToolOutputRecord } from './api-CIZKyfSn.mjs';
8
- import { a as detectGitWorktree } from './downloadImage-DfSFLb-h.mjs';
7
+ import { r as resolveToolsDir, s as startOfflineReconnection, b as saveToolOutputRecord } from './api-D6WL40-U.mjs';
8
+ import { a as detectGitWorktree } from './downloadImage-DZXI3mXL.mjs';
9
9
 
10
10
  class BasePermissionHandler {
11
11
  pendingRequests = /* @__PURE__ */ new Map();
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var logger = require('./persistence-JLUn_Nou.cjs');
3
+ var logger = require('./persistence-CBTtC_dm.cjs');
4
4
  var node_crypto = require('node:crypto');
5
5
  var fs = require('node:fs');
6
6
  var os = require('node:os');
7
7
  var path = require('node:path');
8
8
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
9
- var api = require('./api-CwOCQeLH.cjs');
10
- var downloadImage = require('./downloadImage-MQMrUUxS.cjs');
9
+ var api = require('./api-C7FyJfZ1.cjs');
10
+ var downloadImage = require('./downloadImage-cQ1Z7hkC.cjs');
11
11
 
12
12
  class BasePermissionHandler {
13
13
  pendingRequests = /* @__PURE__ */ new Map();
@@ -35,7 +35,7 @@ function isDebug() {
35
35
  }
36
36
 
37
37
  var name = "@zonease/happy";
38
- var version = "0.6.39";
38
+ var version = "0.6.40";
39
39
  var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
40
40
  var author = "ZonEaseTech";
41
41
  var license = "MIT";
@@ -14,7 +14,7 @@ function isDebug() {
14
14
  }
15
15
 
16
16
  var name = "@zonease/happy";
17
- var version = "0.6.39";
17
+ var version = "0.6.40";
18
18
  var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
19
19
  var author = "ZonEaseTech";
20
20
  var license = "MIT";
@@ -941,4 +941,4 @@ var persistence = /*#__PURE__*/Object.freeze({
941
941
  writeDaemonState: writeDaemonState
942
942
  });
943
943
 
944
- export { getProfileEnvironmentVariables as A, encryptLegacy as B, getRandomBytes as C, libsodiumEncryptForPublicKey as D, logger$1 as E, persistence as F, readSettings as a, clearCredentials as b, configuration as c, clearMachineId as d, decodeBase64 as e, encodeBase64 as f, encrypt as g, decrypt as h, isDebug as i, readDaemonState as j, encryptWithDataKey as k, logger as l, decryptWithDataKey as m, clearDaemonState as n, libsodiumPublicKeyFromSecretKey as o, packageJson as p, decryptLegacy as q, readCredentials as r, encodeBase64Url as s, writeCredentialsLegacy as t, updateSettings as u, acquireDaemonLock as v, writeCredentialsDataKey as w, writeDaemonState as x, releaseDaemonLock as y, validateProfileForAgent as z };
944
+ export { getProfileEnvironmentVariables as A, encryptLegacy as B, getRandomBytes as C, libsodiumEncryptForPublicKey as D, logger$1 as E, persistence as F, readSettings as a, clearCredentials as b, configuration as c, clearMachineId as d, decrypt as e, decodeBase64 as f, encodeBase64 as g, encrypt as h, isDebug as i, readDaemonState as j, encryptWithDataKey as k, logger as l, decryptWithDataKey as m, clearDaemonState as n, libsodiumPublicKeyFromSecretKey as o, packageJson as p, decryptLegacy as q, readCredentials as r, encodeBase64Url as s, writeCredentialsLegacy as t, updateSettings as u, acquireDaemonLock as v, writeCredentialsDataKey as w, writeDaemonState as x, releaseDaemonLock as y, validateProfileForAgent as z };
@@ -1,14 +1,15 @@
1
1
  import fs from 'fs/promises';
2
2
  import os from 'os';
3
3
  import * as tmp from 'tmp';
4
- import { A as ApiClient } from './api-CIZKyfSn.mjs';
5
- import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, x as writeDaemonState, j as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-BFjvtUad.mjs';
6
- import { authAndSetupMachineIfNeeded } from './auth-BE8dD20T.mjs';
4
+ import { A as ApiClient } from './api-D6WL40-U.mjs';
5
+ import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, g as encodeBase64, x as writeDaemonState, j as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-DKfF81pd.mjs';
6
+ import { authAndSetupMachineIfNeeded } from './auth-DxJ3VvrX.mjs';
7
7
  import { spawn, execSync, exec } from 'child_process';
8
- import { getEnvironmentInfo } from './doctor-DZYB6IVc.mjs';
9
- import { s as spawnHappyCLI } from './spawnHappyCLI-CoORX0YW.mjs';
10
- import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-CM_QcJnB.mjs';
11
- import { P as PLIST_LABEL } from './install-oRAjVLjP.mjs';
8
+ import { getEnvironmentInfo } from './doctor-CU4JLGQ1.mjs';
9
+ import { s as spawnHappyCLI } from './spawnHappyCLI-yBIP_Oqi.mjs';
10
+ import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-DB5tDU7V.mjs';
11
+ import { a as readCachedDeviceKey, d as deviceExec } from './deviceKeys-BTdo34Uz.mjs';
12
+ import { P as PLIST_LABEL } from './install-CgUPGIoF.mjs';
12
13
  import fastify from 'fastify';
13
14
  import { createReadStream } from 'node:fs';
14
15
  import { stat } from 'node:fs/promises';
@@ -161,7 +162,8 @@ function startDaemonControlServer({
161
162
  stopSession,
162
163
  spawnSession,
163
164
  requestShutdown,
164
- onHappySessionWebhook
165
+ onHappySessionWebhook,
166
+ execOnDevice
165
167
  }) {
166
168
  return new Promise((resolve) => {
167
169
  const app = fastify({
@@ -267,6 +269,23 @@ function startDaemonControlServer({
267
269
  }))
268
270
  };
269
271
  });
272
+ typed.post("/device-exec", {
273
+ schema: {
274
+ body: z.object({
275
+ deviceId: z.string(),
276
+ command: z.string(),
277
+ timeoutMs: z.number().optional()
278
+ })
279
+ }
280
+ }, async (request, reply) => {
281
+ const { deviceId, command, timeoutMs } = request.body;
282
+ logger.debug(`[CONTROL SERVER] Device exec request for ${deviceId}`);
283
+ try {
284
+ return { ok: true, result: await execOnDevice(deviceId, command, timeoutMs ?? 3e5) };
285
+ } catch (error) {
286
+ return reply.code(200).send({ ok: false, error: error instanceof Error ? error.message : String(error) });
287
+ }
288
+ });
270
289
  typed.post("/stop-session", {
271
290
  schema: {
272
291
  body: z.object({
@@ -1975,12 +1994,28 @@ ${error.message}
1975
1994
  }
1976
1995
  pidToTrackedSession.delete(pid);
1977
1996
  };
1997
+ let connectedMachine = null;
1978
1998
  const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
1979
1999
  getChildren: getCurrentChildren,
1980
2000
  stopSession,
1981
2001
  spawnSession,
1982
2002
  requestShutdown: () => requestShutdown("happy-cli"),
1983
- onHappySessionWebhook
2003
+ onHappySessionWebhook,
2004
+ execOnDevice: async (deviceId, command, timeoutMs) => {
2005
+ if (!connectedMachine) {
2006
+ throw new Error("Daemon is not connected yet");
2007
+ }
2008
+ const material = await readCachedDeviceKey(deviceId);
2009
+ if (!material) {
2010
+ throw new Error(`No cached key for device ${deviceId}`);
2011
+ }
2012
+ return await deviceExec(connectedMachine.rpcSocket, credentials, deviceId, command, {
2013
+ deviceKeyBase64: encodeBase64(material.key),
2014
+ deviceKeyVariant: material.variant,
2015
+ timeout: timeoutMs,
2016
+ skipMachineLookup: true
2017
+ });
2018
+ }
1984
2019
  });
1985
2020
  const fileState = {
1986
2021
  pid: process.pid,
@@ -2014,6 +2049,7 @@ ${error.message}
2014
2049
  orchestratorCancel: handleOrchestratorCancel
2015
2050
  });
2016
2051
  apiMachine.connect();
2052
+ connectedMachine = apiMachine;
2017
2053
  apiMachine.updateMachineMetadata((current) => ({ ...current, ...machineMetadata })).catch((error) => {
2018
2054
  logger.debug("[DAEMON RUN] Failed to update machine metadata", error);
2019
2055
  });
@@ -3,14 +3,15 @@
3
3
  var fs$1 = require('fs/promises');
4
4
  var os = require('os');
5
5
  var tmp = require('tmp');
6
- var api = require('./api-CwOCQeLH.cjs');
7
- var logger = require('./persistence-JLUn_Nou.cjs');
8
- var auth = require('./auth-BIleVCw0.cjs');
6
+ var api = require('./api-C7FyJfZ1.cjs');
7
+ var logger = require('./persistence-CBTtC_dm.cjs');
8
+ var auth = require('./auth-C7ZScWEw.cjs');
9
9
  var child_process = require('child_process');
10
- var doctor = require('./doctor-Cya9YSA0.cjs');
11
- var spawnHappyCLI = require('./spawnHappyCLI-D_V-AAoP.cjs');
12
- var controlClient = require('./controlClient-DZMIsKsT.cjs');
13
- var install = require('./install-BokHRgFW.cjs');
10
+ var doctor = require('./doctor-BQGLhrcI.cjs');
11
+ var spawnHappyCLI = require('./spawnHappyCLI-KiazE3kL.cjs');
12
+ var controlClient = require('./controlClient-B23M-WPp.cjs');
13
+ var deviceKeys = require('./deviceKeys-C540DcXl.cjs');
14
+ var install = require('./install-DSDufaPd.cjs');
14
15
  var fastify = require('fastify');
15
16
  var fs = require('node:fs');
16
17
  var promises = require('node:fs/promises');
@@ -182,7 +183,8 @@ function startDaemonControlServer({
182
183
  stopSession,
183
184
  spawnSession,
184
185
  requestShutdown,
185
- onHappySessionWebhook
186
+ onHappySessionWebhook,
187
+ execOnDevice
186
188
  }) {
187
189
  return new Promise((resolve) => {
188
190
  const app = fastify({
@@ -288,6 +290,23 @@ function startDaemonControlServer({
288
290
  }))
289
291
  };
290
292
  });
293
+ typed.post("/device-exec", {
294
+ schema: {
295
+ body: z.z.object({
296
+ deviceId: z.z.string(),
297
+ command: z.z.string(),
298
+ timeoutMs: z.z.number().optional()
299
+ })
300
+ }
301
+ }, async (request, reply) => {
302
+ const { deviceId, command, timeoutMs } = request.body;
303
+ logger.logger.debug(`[CONTROL SERVER] Device exec request for ${deviceId}`);
304
+ try {
305
+ return { ok: true, result: await execOnDevice(deviceId, command, timeoutMs ?? 3e5) };
306
+ } catch (error) {
307
+ return reply.code(200).send({ ok: false, error: error instanceof Error ? error.message : String(error) });
308
+ }
309
+ });
291
310
  typed.post("/stop-session", {
292
311
  schema: {
293
312
  body: z.z.object({
@@ -1996,12 +2015,28 @@ ${error.message}
1996
2015
  }
1997
2016
  pidToTrackedSession.delete(pid);
1998
2017
  };
2018
+ let connectedMachine = null;
1999
2019
  const { port: controlPort, stop: stopControlServer } = await startDaemonControlServer({
2000
2020
  getChildren: getCurrentChildren,
2001
2021
  stopSession,
2002
2022
  spawnSession,
2003
2023
  requestShutdown: () => requestShutdown("happy-cli"),
2004
- onHappySessionWebhook
2024
+ onHappySessionWebhook,
2025
+ execOnDevice: async (deviceId, command, timeoutMs) => {
2026
+ if (!connectedMachine) {
2027
+ throw new Error("Daemon is not connected yet");
2028
+ }
2029
+ const material = await deviceKeys.readCachedDeviceKey(deviceId);
2030
+ if (!material) {
2031
+ throw new Error(`No cached key for device ${deviceId}`);
2032
+ }
2033
+ return await deviceKeys.deviceExec(connectedMachine.rpcSocket, credentials, deviceId, command, {
2034
+ deviceKeyBase64: logger.encodeBase64(material.key),
2035
+ deviceKeyVariant: material.variant,
2036
+ timeout: timeoutMs,
2037
+ skipMachineLookup: true
2038
+ });
2039
+ }
2005
2040
  });
2006
2041
  const fileState = {
2007
2042
  pid: process.pid,
@@ -2035,6 +2070,7 @@ ${error.message}
2035
2070
  orchestratorCancel: handleOrchestratorCancel
2036
2071
  });
2037
2072
  apiMachine.connect();
2073
+ connectedMachine = apiMachine;
2038
2074
  apiMachine.updateMachineMetadata((current) => ({ ...current, ...machineMetadata })).catch((error) => {
2039
2075
  logger.logger.debug("[DAEMON RUN] Failed to update machine metadata", error);
2040
2076
  });
@@ -2,20 +2,20 @@ import os, { homedir } from 'node:os';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
4
4
  import { readFile, mkdir, writeFile, stat, copyFile } from 'node:fs/promises';
5
- import { I as InvalidateSync, k as AsyncLock, d as connectionState, A as ApiClient, r as resolveToolsDir, s as startOfflineReconnection, l as findClaudeProjectId } from './api-CIZKyfSn.mjs';
6
- import { l as logger, i as isDebug, c as configuration, a as readSettings, p as packageJson } from './persistence-BFjvtUad.mjs';
7
- import { g as getProjectPath, a as claudeLocal, E as ExitCodeError, b as claudeCheckSession, s as systemPrompt } from './claudeLocal-CPT3zcIU.mjs';
5
+ import { I as InvalidateSync, k as AsyncLock, d as connectionState, A as ApiClient, r as resolveToolsDir, s as startOfflineReconnection, l as findClaudeProjectId } from './api-D6WL40-U.mjs';
6
+ import { l as logger, i as isDebug, c as configuration, a as readSettings, p as packageJson } from './persistence-DKfF81pd.mjs';
7
+ import { g as getProjectPath, a as claudeLocal, E as ExitCodeError, b as claudeCheckSession, s as systemPrompt } from './claudeLocal-CWNGpJ0-.mjs';
8
8
  import { R as RawJSONLinesSchema } from './types-BoJh4Qfv.mjs';
9
9
  import { join, resolve } from 'node:path';
10
10
  import { d as delay } from './time-DgsBLJA_.mjs';
11
11
  import { watch, access } from 'fs/promises';
12
12
  import { useStdout, useInput, Box, Text, render } from 'ink';
13
- import { e as parseSpecialCommand, b as MessageBuffer, c as createMcpContext, M as MessageQueue2, h as hashObject, r as registerKillSessionHandler, a as detectGitWorktree } from './downloadImage-DfSFLb-h.mjs';
13
+ import { e as parseSpecialCommand, b as MessageBuffer, c as createMcpContext, M as MessageQueue2, h as hashObject, r as registerKillSessionHandler, a as detectGitWorktree } from './downloadImage-DZXI3mXL.mjs';
14
14
  import React, { useState, useRef, useEffect, useCallback } from 'react';
15
15
  import { execSync, spawn } from 'node:child_process';
16
16
  import { createInterface } from 'node:readline';
17
17
  import { fileURLToPath } from 'node:url';
18
- import { i as isBun } from './spawnHappyCLI-CoORX0YW.mjs';
18
+ import { i as isBun } from './spawnHappyCLI-yBIP_Oqi.mjs';
19
19
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
20
20
  import 'axios';
21
21
  import 'node:events';
@@ -26,11 +26,11 @@ import 'crypto';
26
26
  import '@stablelib/base64';
27
27
  import 'expo-server-sdk';
28
28
  import 'chalk';
29
- import { a as formatMessageForClaude } from './formatImageMessage-B4lSxlk2.mjs';
29
+ import { a as formatMessageForClaude } from './formatImageMessage-DbKSSjMx.mjs';
30
30
  import { isDeepStrictEqual } from 'node:util';
31
- import { i as initialMachineMetadata, s as stopCaffeinate, a as startCaffeinate } from './run-DBZqN6Kn.mjs';
32
- import { getEnvironmentInfo } from './doctor-DZYB6IVc.mjs';
33
- import { notifyDaemonSessionStarted } from './controlClient-CM_QcJnB.mjs';
31
+ import { i as initialMachineMetadata, s as stopCaffeinate, a as startCaffeinate } from './run-BVZsMNwN.mjs';
32
+ import { getEnvironmentInfo } from './doctor-CU4JLGQ1.mjs';
33
+ import { notifyDaemonSessionStarted } from './controlClient-DB5tDU7V.mjs';
34
34
  import { createServer } from 'node:http';
35
35
  import 'zod';
36
36
  import './index-CPKdvi7J.mjs';
@@ -39,7 +39,7 @@ import 'child_process';
39
39
  import 'util';
40
40
  import 'path';
41
41
  import 'node:module';
42
- import './sessionKeyCache-D2BwO91C.mjs';
42
+ import './sessionKeyCache-B6cGadYL.mjs';
43
43
  import 'fs';
44
44
  import './prompt-BsAZsPbo.mjs';
45
45
  import './trimIdent-BAjmv_TG.mjs';
@@ -47,16 +47,16 @@ import './common-BdNgghUJ.mjs';
47
47
  import '@modelcontextprotocol/sdk/server/mcp.js';
48
48
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
49
49
  import '@modelcontextprotocol/sdk/types.js';
50
- import './deviceExec-D93AOVV4.mjs';
51
- import './auth-BE8dD20T.mjs';
50
+ import './deviceKeys-BTdo34Uz.mjs';
51
+ import './auth-DxJ3VvrX.mjs';
52
52
  import 'qrcode-terminal';
53
- import './browser-Dll5hxOV.mjs';
53
+ import './browser-AoVGRX7y.mjs';
54
54
  import 'open';
55
55
  import './mcpToolSchemas-CjeTfrAs.mjs';
56
56
  import 'url';
57
57
  import 'os';
58
58
  import 'tmp';
59
- import './install-oRAjVLjP.mjs';
59
+ import './install-CgUPGIoF.mjs';
60
60
  import 'fastify';
61
61
  import 'fastify-type-provider-zod';
62
62
  import './doctor-ON7pN5G5.mjs';
@@ -4,20 +4,20 @@ var os = require('node:os');
4
4
  var node_crypto = require('node:crypto');
5
5
  var fs$1 = require('node:fs');
6
6
  var promises = require('node:fs/promises');
7
- var api = require('./api-CwOCQeLH.cjs');
8
- var logger = require('./persistence-JLUn_Nou.cjs');
9
- var claudeLocal = require('./claudeLocal-WkgLbl1a.cjs');
7
+ var api = require('./api-C7FyJfZ1.cjs');
8
+ var logger = require('./persistence-CBTtC_dm.cjs');
9
+ var claudeLocal = require('./claudeLocal-C6BdzhDx.cjs');
10
10
  var types = require('./types-DWeB7aqh.cjs');
11
11
  var path = require('node:path');
12
12
  var time = require('./time-Bj2HRkKH.cjs');
13
13
  var fs = require('fs/promises');
14
14
  var ink = require('ink');
15
- var downloadImage = require('./downloadImage-MQMrUUxS.cjs');
15
+ var downloadImage = require('./downloadImage-cQ1Z7hkC.cjs');
16
16
  var React = require('react');
17
17
  var node_child_process = require('node:child_process');
18
18
  var node_readline = require('node:readline');
19
19
  var node_url = require('node:url');
20
- var spawnHappyCLI = require('./spawnHappyCLI-D_V-AAoP.cjs');
20
+ var spawnHappyCLI = require('./spawnHappyCLI-KiazE3kL.cjs');
21
21
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
22
22
  require('axios');
23
23
  require('node:events');
@@ -28,11 +28,11 @@ require('crypto');
28
28
  require('@stablelib/base64');
29
29
  require('expo-server-sdk');
30
30
  require('chalk');
31
- var formatImageMessage = require('./formatImageMessage-DbyG8c2j.cjs');
31
+ var formatImageMessage = require('./formatImageMessage-CixDYY6G.cjs');
32
32
  var node_util = require('node:util');
33
- var run = require('./run-BDn2aE3Q.cjs');
34
- var doctor = require('./doctor-Cya9YSA0.cjs');
35
- var controlClient = require('./controlClient-DZMIsKsT.cjs');
33
+ var run = require('./run-CURx7LhK.cjs');
34
+ var doctor = require('./doctor-BQGLhrcI.cjs');
35
+ var controlClient = require('./controlClient-B23M-WPp.cjs');
36
36
  var node_http = require('node:http');
37
37
  require('zod');
38
38
  require('./index-CDTNgfUf.cjs');
@@ -41,7 +41,7 @@ require('child_process');
41
41
  require('util');
42
42
  require('path');
43
43
  require('node:module');
44
- require('./sessionKeyCache-4Tp8pJ3N.cjs');
44
+ require('./sessionKeyCache-CDmexJ9p.cjs');
45
45
  require('fs');
46
46
  require('./prompt-BTWDZZO0.cjs');
47
47
  require('./trimIdent-4gvk9Wk6.cjs');
@@ -49,16 +49,16 @@ require('./common-CZvSRU4p.cjs');
49
49
  require('@modelcontextprotocol/sdk/server/mcp.js');
50
50
  require('@modelcontextprotocol/sdk/server/streamableHttp.js');
51
51
  require('@modelcontextprotocol/sdk/types.js');
52
- require('./deviceExec-CU1R9CRb.cjs');
53
- require('./auth-BIleVCw0.cjs');
52
+ require('./deviceKeys-C540DcXl.cjs');
53
+ require('./auth-C7ZScWEw.cjs');
54
54
  require('qrcode-terminal');
55
- require('./browser-Cc08m7of.cjs');
55
+ require('./browser-DzgO4oaD.cjs');
56
56
  require('open');
57
57
  require('./mcpToolSchemas-DK42yoZ_.cjs');
58
58
  require('url');
59
59
  require('os');
60
60
  require('tmp');
61
- require('./install-BokHRgFW.cjs');
61
+ require('./install-DSDufaPd.cjs');
62
62
  require('fastify');
63
63
  require('fastify-type-provider-zod');
64
64
  require('./doctor-C08m5Z9f.cjs');
@@ -731,7 +731,7 @@ class AbortError extends Error {
731
731
  }
732
732
  }
733
733
 
734
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-uaKxPZPm.cjs', document.baseURI).href)));
734
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-CL3Kt5ex.cjs', document.baseURI).href)));
735
735
  const __dirname$1 = path.join(__filename$1, "..");
736
736
  function getGlobalClaudeVersion() {
737
737
  try {
@@ -1,15 +1,15 @@
1
1
  import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
- import { c as saveDiffRecords, e as extractUserText, i as isSystemMessage, g as generateStableUuid, f as findCodexSessionFile, d as connectionState, A as ApiClient } from './api-CIZKyfSn.mjs';
3
+ import { c as saveDiffRecords, e as extractUserText, i as isSystemMessage, g as generateStableUuid, f as findCodexSessionFile, d as connectionState, A as ApiClient } from './api-D6WL40-U.mjs';
4
4
  import { spawn, execSync } from 'node:child_process';
5
5
  import { createInterface } from 'node:readline';
6
- import { l as logger, i as isDebug, a as readSettings } from './persistence-BFjvtUad.mjs';
7
- import { B as BasePermissionHandler, a as BaseReasoningProcessor, c as createSessionMetadata, s as setupOfflineReconnection, h as handleConfigMetadataEvent, b as summarizeBashToolOutput } from './loadableToolOutput-Cuygmd4K.mjs';
6
+ import { l as logger, i as isDebug, a as readSettings } from './persistence-DKfF81pd.mjs';
7
+ import { B as BasePermissionHandler, a as BaseReasoningProcessor, c as createSessionMetadata, s as setupOfflineReconnection, h as handleConfigMetadataEvent, b as summarizeBashToolOutput } from './loadableToolOutput-Bfei9wPq.mjs';
8
8
  import { randomUUID } from 'node:crypto';
9
- import { M as MessageQueue2, h as hashObject, c as createMcpContext, r as registerKillSessionHandler, b as MessageBuffer, p as parseClear, d as downloadImage } from './downloadImage-DfSFLb-h.mjs';
10
- import { i as initialMachineMetadata, s as stopCaffeinate } from './run-DBZqN6Kn.mjs';
9
+ import { M as MessageQueue2, h as hashObject, c as createMcpContext, r as registerKillSessionHandler, b as MessageBuffer, p as parseClear, d as downloadImage } from './downloadImage-DZXI3mXL.mjs';
10
+ import { i as initialMachineMetadata, s as stopCaffeinate } from './run-BVZsMNwN.mjs';
11
11
  import { g as getFirstTurnInstruction } from './constants-pmXZGibX.mjs';
12
- import { notifyDaemonSessionStarted } from './controlClient-CM_QcJnB.mjs';
12
+ import { notifyDaemonSessionStarted } from './controlClient-DB5tDU7V.mjs';
13
13
  import { inspect } from 'node:util';
14
14
  import { readFile } from 'node:fs/promises';
15
15
  import { basename, isAbsolute } from 'node:path';
@@ -34,17 +34,17 @@ import 'crypto';
34
34
  import 'tweetnacl';
35
35
  import '@stablelib/base64';
36
36
  import 'expo-server-sdk';
37
- import './sessionKeyCache-D2BwO91C.mjs';
37
+ import './sessionKeyCache-B6cGadYL.mjs';
38
38
  import 'chalk';
39
39
  import 'fs';
40
40
  import '@modelcontextprotocol/sdk/server/mcp.js';
41
41
  import 'node:http';
42
42
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
43
43
  import '@modelcontextprotocol/sdk/types.js';
44
- import './deviceExec-D93AOVV4.mjs';
45
- import './auth-BE8dD20T.mjs';
44
+ import './deviceKeys-BTdo34Uz.mjs';
45
+ import './auth-DxJ3VvrX.mjs';
46
46
  import 'qrcode-terminal';
47
- import './browser-Dll5hxOV.mjs';
47
+ import './browser-AoVGRX7y.mjs';
48
48
  import 'open';
49
49
  import './prompt-BsAZsPbo.mjs';
50
50
  import './trimIdent-BAjmv_TG.mjs';
@@ -53,12 +53,12 @@ import './mcpToolSchemas-CjeTfrAs.mjs';
53
53
  import './types-BoJh4Qfv.mjs';
54
54
  import 'os';
55
55
  import 'tmp';
56
- import './doctor-DZYB6IVc.mjs';
56
+ import './doctor-CU4JLGQ1.mjs';
57
57
  import './doctor-ON7pN5G5.mjs';
58
58
  import 'ps-list';
59
59
  import 'cross-spawn';
60
- import './spawnHappyCLI-CoORX0YW.mjs';
61
- import './install-oRAjVLjP.mjs';
60
+ import './spawnHappyCLI-yBIP_Oqi.mjs';
61
+ import './install-CgUPGIoF.mjs';
62
62
  import 'fastify';
63
63
  import 'fastify-type-provider-zod';
64
64
 
@@ -2,16 +2,16 @@
2
2
 
3
3
  var ink = require('ink');
4
4
  var React = require('react');
5
- var api = require('./api-CwOCQeLH.cjs');
5
+ var api = require('./api-C7FyJfZ1.cjs');
6
6
  var node_child_process = require('node:child_process');
7
7
  var node_readline = require('node:readline');
8
- var logger = require('./persistence-JLUn_Nou.cjs');
9
- var loadableToolOutput = require('./loadableToolOutput-DDCJjywt.cjs');
8
+ var logger = require('./persistence-CBTtC_dm.cjs');
9
+ var loadableToolOutput = require('./loadableToolOutput-CbLGtvST.cjs');
10
10
  var node_crypto = require('node:crypto');
11
- var downloadImage = require('./downloadImage-MQMrUUxS.cjs');
12
- var run = require('./run-BDn2aE3Q.cjs');
11
+ var downloadImage = require('./downloadImage-cQ1Z7hkC.cjs');
12
+ var run = require('./run-CURx7LhK.cjs');
13
13
  var constants = require('./constants-E_UWYTo3.cjs');
14
- var controlClient = require('./controlClient-DZMIsKsT.cjs');
14
+ var controlClient = require('./controlClient-B23M-WPp.cjs');
15
15
  var node_util = require('node:util');
16
16
  var promises = require('node:fs/promises');
17
17
  var path = require('node:path');
@@ -36,17 +36,17 @@ require('crypto');
36
36
  require('tweetnacl');
37
37
  require('@stablelib/base64');
38
38
  require('expo-server-sdk');
39
- require('./sessionKeyCache-4Tp8pJ3N.cjs');
39
+ require('./sessionKeyCache-CDmexJ9p.cjs');
40
40
  require('chalk');
41
41
  require('fs');
42
42
  require('@modelcontextprotocol/sdk/server/mcp.js');
43
43
  require('node:http');
44
44
  require('@modelcontextprotocol/sdk/server/streamableHttp.js');
45
45
  require('@modelcontextprotocol/sdk/types.js');
46
- require('./deviceExec-CU1R9CRb.cjs');
47
- require('./auth-BIleVCw0.cjs');
46
+ require('./deviceKeys-C540DcXl.cjs');
47
+ require('./auth-C7ZScWEw.cjs');
48
48
  require('qrcode-terminal');
49
- require('./browser-Cc08m7of.cjs');
49
+ require('./browser-DzgO4oaD.cjs');
50
50
  require('open');
51
51
  require('./prompt-BTWDZZO0.cjs');
52
52
  require('./trimIdent-4gvk9Wk6.cjs');
@@ -55,12 +55,12 @@ require('./mcpToolSchemas-DK42yoZ_.cjs');
55
55
  require('./types-DWeB7aqh.cjs');
56
56
  require('os');
57
57
  require('tmp');
58
- require('./doctor-Cya9YSA0.cjs');
58
+ require('./doctor-BQGLhrcI.cjs');
59
59
  require('./doctor-C08m5Z9f.cjs');
60
60
  require('ps-list');
61
61
  require('cross-spawn');
62
- require('./spawnHappyCLI-D_V-AAoP.cjs');
63
- require('./install-BokHRgFW.cjs');
62
+ require('./spawnHappyCLI-KiazE3kL.cjs');
63
+ require('./install-DSDufaPd.cjs');
64
64
  require('fastify');
65
65
  require('fastify-type-provider-zod');
66
66