@zonease/happy 0.6.37 → 0.6.39

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 (111) hide show
  1. package/dist/{types-DYCJXXdA.mjs → api-CIZKyfSn.mjs} +136 -1725
  2. package/dist/{types-BrTorNAy.cjs → api-CwOCQeLH.cjs} +317 -1961
  3. package/dist/auth-0VnlXT9f.mjs +181 -0
  4. package/dist/auth-BE8dD20T.mjs +270 -0
  5. package/dist/auth-BIleVCw0.cjs +274 -0
  6. package/dist/auth-Cas9DR3b.cjs +183 -0
  7. package/dist/browser-Cc08m7of.cjs +22 -0
  8. package/dist/browser-Dll5hxOV.mjs +20 -0
  9. package/dist/claudeLocal-CPT3zcIU.mjs +344 -0
  10. package/dist/claudeLocal-WkgLbl1a.cjs +352 -0
  11. package/dist/codex/happyMcpStdioBridge.cjs +4 -1
  12. package/dist/codex/happyMcpStdioBridge.mjs +4 -1
  13. package/dist/common-BdNgghUJ.mjs +75 -0
  14. package/dist/common-CZvSRU4p.cjs +83 -0
  15. package/dist/{config-D_g6ohaV.cjs → config-Draa-5XC.cjs} +21 -34
  16. package/dist/{config-DPn-cbpz.mjs → config-rTT4qTU5.mjs} +7 -20
  17. package/dist/connect-BfC1tYP_.mjs +679 -0
  18. package/dist/connect-CJZ73syK.cjs +681 -0
  19. package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
  20. package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
  21. package/dist/controlClient-CM_QcJnB.mjs +159 -0
  22. package/dist/controlClient-DZMIsKsT.cjs +168 -0
  23. package/dist/deviceExec-CU1R9CRb.cjs +84 -0
  24. package/dist/deviceExec-D93AOVV4.mjs +81 -0
  25. package/dist/diagnose-B3D1a_v0.cjs +60 -0
  26. package/dist/diagnose-CuRKXxt1.mjs +58 -0
  27. package/dist/doctor-C08m5Z9f.cjs +78 -0
  28. package/dist/doctor-Cya9YSA0.cjs +214 -0
  29. package/dist/doctor-DZYB6IVc.mjs +211 -0
  30. package/dist/doctor-ON7pN5G5.mjs +74 -0
  31. package/dist/downloadImage-DfSFLb-h.mjs +887 -0
  32. package/dist/downloadImage-MQMrUUxS.cjs +897 -0
  33. package/dist/enroll-BxlbVftP.cjs +115 -0
  34. package/dist/enroll-C52x8oly.mjs +113 -0
  35. package/dist/evidence-Ccd9iYcj.mjs +127 -0
  36. package/dist/evidence-CjWBG5rX.cjs +129 -0
  37. package/dist/formatImageMessage-B4lSxlk2.mjs +43 -0
  38. package/dist/formatImageMessage-DbyG8c2j.cjs +46 -0
  39. package/dist/github-DKcyF1zp.mjs +57 -0
  40. package/dist/github-h9GhJJ_C.cjs +59 -0
  41. package/dist/index-CDTNgfUf.cjs +578 -0
  42. package/dist/index-CPKdvi7J.mjs +554 -0
  43. package/dist/index.cjs +812 -40
  44. package/dist/index.mjs +813 -40
  45. package/dist/install-BokHRgFW.cjs +98 -0
  46. package/dist/install-C_cp8b2-.cjs +96 -0
  47. package/dist/install-DAeCn8ae.mjs +94 -0
  48. package/dist/install-oRAjVLjP.mjs +95 -0
  49. package/dist/lib.cjs +18 -12
  50. package/dist/lib.d.cts +16 -16
  51. package/dist/lib.d.mts +16 -16
  52. package/dist/lib.mjs +13 -7
  53. package/dist/{loadableToolOutput-lQH8Kb_w.mjs → loadableToolOutput-Cuygmd4K.mjs} +4 -2
  54. package/dist/{loadableToolOutput-CasonFIt.cjs → loadableToolOutput-DDCJjywt.cjs} +63 -61
  55. package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
  56. package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
  57. package/dist/persistence-BFjvtUad.mjs +944 -0
  58. package/dist/persistence-JLUn_Nou.cjs +996 -0
  59. package/dist/projectPath-BTBIMB57.mjs +10 -0
  60. package/dist/projectPath-ChLhdOyt.cjs +13 -0
  61. package/dist/prompt-BTWDZZO0.cjs +25 -0
  62. package/dist/prompt-BsAZsPbo.mjs +22 -0
  63. package/dist/release-BMEzNONE.mjs +168 -0
  64. package/dist/release-EhOJeAZe.cjs +171 -0
  65. package/dist/run-BDn2aE3Q.cjs +2152 -0
  66. package/dist/run-DBZqN6Kn.mjs +2128 -0
  67. package/dist/runClaude-DNHvERX0.mjs +3763 -0
  68. package/dist/runClaude-uaKxPZPm.cjs +3766 -0
  69. package/dist/{runCodex-BTt3fxG4.cjs → runCodex-82UrZLme.cjs} +169 -153
  70. package/dist/{runCodex-BXCB5trB.mjs → runCodex-C-RTM0wA.mjs} +36 -20
  71. package/dist/{runGemini-37HHbp9n.mjs → runGemini-Bm6-4Xm2.mjs} +34 -17
  72. package/dist/{runGemini-C-0nSFBv.cjs → runGemini-CXMr4Jwb.cjs} +212 -195
  73. package/dist/{runOneShot-CXhbFJ2j.cjs → runOneShot-1B6RMYT7.cjs} +31 -55
  74. package/dist/{runOneShot-USX8QPfE.mjs → runOneShot-ioARRHrx.mjs} +14 -38
  75. package/dist/{serve-DZAq-_1F.cjs → serve-BtdAhkIE.cjs} +35 -58
  76. package/dist/{serve-BRedJPzL.mjs → serve-CwibwU6N.mjs} +12 -35
  77. package/dist/session-2sU4Bu1R.mjs +108 -0
  78. package/dist/session-D--F87zL.cjs +110 -0
  79. package/dist/sessionKeyCache-4Tp8pJ3N.cjs +36 -0
  80. package/dist/sessionKeyCache-D2BwO91C.mjs +33 -0
  81. package/dist/sessionList-BCSw3qYt.mjs +51 -0
  82. package/dist/sessionList-ICJsjNtJ.cjs +54 -0
  83. package/dist/sessionSummary-Bkz52BPh.cjs +393 -0
  84. package/dist/sessionSummary-lHGBWQSb.mjs +387 -0
  85. package/dist/spawnHappyCLI-CoORX0YW.mjs +66 -0
  86. package/dist/spawnHappyCLI-D_V-AAoP.cjs +70 -0
  87. package/dist/ssh-BTOlhhxi.cjs +333 -0
  88. package/dist/ssh-DL_AkRAD.mjs +328 -0
  89. package/dist/systemdScope-BeOtAkhk.mjs +25 -0
  90. package/dist/systemdScope-DW3-UoKB.cjs +29 -0
  91. package/dist/task-CQzrHRZe.mjs +165 -0
  92. package/dist/task-CSpaC9HX.cjs +167 -0
  93. package/dist/time-Bj2HRkKH.cjs +33 -0
  94. package/dist/time-DgsBLJA_.mjs +29 -0
  95. package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
  96. package/dist/trimIdent-BAjmv_TG.mjs +20 -0
  97. package/dist/types-BoJh4Qfv.mjs +57 -0
  98. package/dist/types-DWeB7aqh.cjs +59 -0
  99. package/dist/uninstall-BiKyRAb1.cjs +77 -0
  100. package/dist/uninstall-nFMFGUVM.mjs +75 -0
  101. package/dist/update-BcqRHXf2.mjs +109 -0
  102. package/dist/update-Dw8pgsAT.cjs +111 -0
  103. package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
  104. package/dist/workflowCommon-Bl568-rt.cjs +62 -0
  105. package/dist/workflowReports-BEWuQesA.cjs +113 -0
  106. package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
  107. package/package.json +1 -1
  108. package/dist/index-BlkHOkB3.cjs +0 -11312
  109. package/dist/index-DJ2VpGiR.mjs +0 -11272
  110. package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
  111. package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
@@ -0,0 +1,996 @@
1
+ 'use strict';
2
+
3
+ var promises = require('node:fs/promises');
4
+ var fs = require('node:fs');
5
+ var os = require('node:os');
6
+ var path = require('node:path');
7
+ var z = require('zod');
8
+ var node_crypto = require('node:crypto');
9
+ var tweetnacl = require('tweetnacl');
10
+ var chalk = require('chalk');
11
+ var fs$1 = require('fs');
12
+
13
+ function _interopNamespaceDefault(e) {
14
+ var n = Object.create(null);
15
+ if (e) {
16
+ Object.keys(e).forEach(function (k) {
17
+ if (k !== 'default') {
18
+ var d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: function () { return e[k]; }
22
+ });
23
+ }
24
+ });
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+
30
+ var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
31
+
32
+ const DEBUG_TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true"]);
33
+ function isDebug() {
34
+ return DEBUG_TRUE_VALUES.has(process.env.DEBUG?.toLowerCase() ?? "");
35
+ }
36
+
37
+ var name = "@zonease/happy";
38
+ var version = "0.6.39";
39
+ var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
40
+ var author = "ZonEaseTech";
41
+ var license = "MIT";
42
+ var type = "module";
43
+ var homepage = "https://github.com/ZonEaseTech/happy-wfs";
44
+ var bugs = "https://github.com/ZonEaseTech/happy-wfs/issues";
45
+ var repository = {
46
+ type: "git",
47
+ url: "git+https://github.com/ZonEaseTech/happy-wfs.git",
48
+ directory: "packages/happy-cli"
49
+ };
50
+ var bin = {
51
+ happy: "./bin/happy.mjs",
52
+ "happy-mcp": "./bin/happy-mcp.mjs"
53
+ };
54
+ var main = "./dist/index.cjs";
55
+ var module$1 = "./dist/index.mjs";
56
+ var types = "./dist/index.d.cts";
57
+ var exports$1 = {
58
+ ".": {
59
+ require: {
60
+ types: "./dist/index.d.cts",
61
+ "default": "./dist/index.cjs"
62
+ },
63
+ "import": {
64
+ types: "./dist/index.d.mts",
65
+ "default": "./dist/index.mjs"
66
+ }
67
+ },
68
+ "./lib": {
69
+ require: {
70
+ types: "./dist/lib.d.cts",
71
+ "default": "./dist/lib.cjs"
72
+ },
73
+ "import": {
74
+ types: "./dist/lib.d.mts",
75
+ "default": "./dist/lib.mjs"
76
+ }
77
+ },
78
+ "./codex/happyMcpStdioBridge": {
79
+ require: {
80
+ types: "./dist/codex/happyMcpStdioBridge.d.cts",
81
+ "default": "./dist/codex/happyMcpStdioBridge.cjs"
82
+ },
83
+ "import": {
84
+ types: "./dist/codex/happyMcpStdioBridge.d.mts",
85
+ "default": "./dist/codex/happyMcpStdioBridge.mjs"
86
+ }
87
+ }
88
+ };
89
+ var files = [
90
+ "dist",
91
+ "bin",
92
+ "scripts",
93
+ "package.json"
94
+ ];
95
+ var scripts = {
96
+ "why do we need to build before running tests / dev?": "We need the binary to be built so we run daemon commands which directly run the binary - we don't want them to go out of sync or have custom spawn logic depending how we started happy",
97
+ typecheck: "tsc --noEmit",
98
+ build: "shx rm -rf dist && tsc --noEmit && pkgroll",
99
+ test: "$npm_execpath run build && vitest run",
100
+ "test:integration": "$npm_execpath run build && vitest run --config vitest.integration.config.ts",
101
+ start: "$npm_execpath run build && node ./bin/happy.mjs",
102
+ dev: "tsx src/index.ts",
103
+ "dev:local-server": "$npm_execpath run build && tsx --env-file .env.dev-local-server src/index.ts",
104
+ "dev:integration-test-env": "$npm_execpath run build && tsx --env-file .env.integration-test src/index.ts",
105
+ prepublishOnly: "$npm_execpath run build && $npm_execpath test",
106
+ release: "$npm_execpath install && release-it",
107
+ postinstall: "node scripts/unpack-tools.cjs",
108
+ "// ==== Dev/Stable Variant Management ====": "",
109
+ stable: "node scripts/env-wrapper.cjs stable",
110
+ "dev:variant": "node scripts/env-wrapper.cjs dev",
111
+ "// ==== Stable Version Quick Commands ====": "",
112
+ "stable:daemon:start": "node scripts/env-wrapper.cjs stable daemon start",
113
+ "stable:daemon:stop": "node scripts/env-wrapper.cjs stable daemon stop",
114
+ "stable:daemon:status": "node scripts/env-wrapper.cjs stable daemon status",
115
+ "stable:auth": "node scripts/env-wrapper.cjs stable auth",
116
+ "// ==== Development Version Quick Commands ====": "",
117
+ "dev:daemon:start": "node scripts/env-wrapper.cjs dev daemon start",
118
+ "dev:daemon:stop": "node scripts/env-wrapper.cjs dev daemon stop",
119
+ "dev:daemon:status": "node scripts/env-wrapper.cjs dev daemon status",
120
+ "dev:auth": "node scripts/env-wrapper.cjs dev auth",
121
+ "// ==== Setup ====": "",
122
+ "setup:dev": "node scripts/setup-dev.cjs",
123
+ doctor: "node scripts/env-wrapper.cjs stable doctor",
124
+ "// ==== Development Linking ====": "",
125
+ "link:dev": "node scripts/link-dev.cjs",
126
+ "unlink:dev": "node scripts/link-dev.cjs unlink"
127
+ };
128
+ var dependencies = {
129
+ "@agentclientprotocol/sdk": "^0.14.1",
130
+ "@paralleldrive/cuid2": "^2.2.2",
131
+ "@modelcontextprotocol/sdk": "^1.25.3",
132
+ "@stablelib/base64": "^2.0.1",
133
+ "@stablelib/hex": "^2.0.1",
134
+ "@types/cross-spawn": "^6.0.6",
135
+ "@types/http-proxy": "^1.17.17",
136
+ "@types/qrcode-terminal": "^0.12.2",
137
+ "@types/react": "^19.2.7",
138
+ "@types/tmp": "^0.2.6",
139
+ "@types/ws": "^8.18.1",
140
+ ai: "^5.0.107",
141
+ axios: "^1.13.2",
142
+ chalk: "^5.6.2",
143
+ "cross-spawn": "^7.0.6",
144
+ "expo-server-sdk": "^3.15.0",
145
+ fastify: "^5.6.2",
146
+ "fastify-type-provider-zod": "4.0.2",
147
+ "http-proxy": "^1.18.1",
148
+ "http-proxy-middleware": "^3.0.5",
149
+ ink: "^6.5.1",
150
+ open: "^10.2.0",
151
+ "ps-list": "^8.1.1",
152
+ "qrcode-terminal": "^0.12.0",
153
+ react: "^19.2.0",
154
+ "socket.io-client": "^4.8.1",
155
+ tar: "^7.5.2",
156
+ tmp: "^0.2.5",
157
+ tweetnacl: "^1.0.3",
158
+ ws: "^8.19.0",
159
+ zod: "3.25.76"
160
+ };
161
+ var devDependencies = {
162
+ "@eslint/compat": "^1",
163
+ "@types/node": ">=20",
164
+ "cross-env": "^10.1.0",
165
+ dotenv: "^16.6.1",
166
+ eslint: "^9",
167
+ "eslint-config-prettier": "^10",
168
+ pkgroll: "^2.14.2",
169
+ "release-it": "^19.0.6",
170
+ shx: "^0.3.3",
171
+ "ts-node": "^10",
172
+ tsx: "^4.20.6",
173
+ typescript: "5.9.3",
174
+ vitest: "^3.2.4",
175
+ "happy-wire": "*"
176
+ };
177
+ var resolutions = {
178
+ "whatwg-url": "14.2.0",
179
+ "parse-path": "7.0.3",
180
+ "@types/parse-path": "7.0.3"
181
+ };
182
+ var publishConfig = {
183
+ registry: "https://registry.npmjs.org",
184
+ access: "public"
185
+ };
186
+ var packageManager = "yarn@1.22.22";
187
+ var optionalDependencies = {
188
+ "node-pty": "^1.0.0",
189
+ "@zonease/happy-tools-arm64-darwin": "1.0.0",
190
+ "@zonease/happy-tools-x64-darwin": "1.0.0",
191
+ "@zonease/happy-tools-arm64-linux": "1.0.0",
192
+ "@zonease/happy-tools-x64-linux": "1.0.0",
193
+ "@zonease/happy-tools-x64-win32": "1.0.0",
194
+ "@zonease/happy-tools-arm64-win32": "1.0.0"
195
+ };
196
+ var packageJson = {
197
+ name: name,
198
+ version: version,
199
+ description: description,
200
+ author: author,
201
+ license: license,
202
+ type: type,
203
+ homepage: homepage,
204
+ bugs: bugs,
205
+ repository: repository,
206
+ bin: bin,
207
+ main: main,
208
+ module: module$1,
209
+ types: types,
210
+ exports: exports$1,
211
+ files: files,
212
+ scripts: scripts,
213
+ dependencies: dependencies,
214
+ devDependencies: devDependencies,
215
+ resolutions: resolutions,
216
+ publishConfig: publishConfig,
217
+ packageManager: packageManager,
218
+ optionalDependencies: optionalDependencies
219
+ };
220
+
221
+ class Configuration {
222
+ serverUrl;
223
+ webappUrl;
224
+ isDaemonProcess;
225
+ // Directories and paths (from persistence)
226
+ happyHomeDir;
227
+ logsDir;
228
+ settingsFile;
229
+ privateKeyFile;
230
+ daemonStateFile;
231
+ daemonLockFile;
232
+ sessionKeysDir;
233
+ currentCliVersion;
234
+ isExperimentalEnabled;
235
+ disableCaffeinate;
236
+ constructor() {
237
+ this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api-happy.zonease.org";
238
+ this.webappUrl = process.env.HAPPY_WEBAPP_URL || "https://happy.zonease.org";
239
+ const args = process.argv.slice(2);
240
+ this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
241
+ if (process.env.HAPPY_HOME_DIR) {
242
+ const expandedPath = process.env.HAPPY_HOME_DIR.replace(/^~/, os.homedir());
243
+ this.happyHomeDir = expandedPath;
244
+ } else {
245
+ this.happyHomeDir = path.join(os.homedir(), ".happy");
246
+ }
247
+ this.logsDir = path.join(this.happyHomeDir, "logs");
248
+ this.settingsFile = path.join(this.happyHomeDir, "settings.json");
249
+ this.privateKeyFile = path.join(this.happyHomeDir, "access.key");
250
+ this.daemonStateFile = path.join(this.happyHomeDir, "daemon.state.json");
251
+ this.daemonLockFile = path.join(this.happyHomeDir, "daemon.state.json.lock");
252
+ this.sessionKeysDir = path.join(this.happyHomeDir, "session-keys");
253
+ this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
254
+ this.disableCaffeinate = ["true", "1", "yes"].includes(process.env.HAPPY_DISABLE_CAFFEINATE?.toLowerCase() || "");
255
+ this.currentCliVersion = packageJson.version;
256
+ const variant = process.env.HAPPY_VARIANT || "stable";
257
+ if (variant === "dev" && !this.happyHomeDir.includes("dev")) {
258
+ console.warn('\u26A0\uFE0F WARNING: HAPPY_VARIANT=dev but HAPPY_HOME_DIR does not contain "dev"');
259
+ console.warn(` Current: ${this.happyHomeDir}`);
260
+ console.warn(` Expected: Should contain "dev" (e.g., ~/.happy-dev)`);
261
+ }
262
+ if (!this.isDaemonProcess && variant === "dev") {
263
+ console.log("\x1B[33m\u{1F527} DEV MODE\x1B[0m - Data: " + this.happyHomeDir);
264
+ }
265
+ if (!fs.existsSync(this.happyHomeDir)) {
266
+ fs.mkdirSync(this.happyHomeDir, { recursive: true });
267
+ }
268
+ if (!fs.existsSync(this.logsDir)) {
269
+ fs.mkdirSync(this.logsDir, { recursive: true });
270
+ }
271
+ }
272
+ }
273
+ const configuration = new Configuration();
274
+
275
+ function createTimestampForFilename(date = /* @__PURE__ */ new Date()) {
276
+ return date.toLocaleString("sv-SE", {
277
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
278
+ year: "numeric",
279
+ month: "2-digit",
280
+ day: "2-digit",
281
+ hour: "2-digit",
282
+ minute: "2-digit",
283
+ second: "2-digit"
284
+ }).replace(/[: ]/g, "-").replace(/,/g, "") + "-pid-" + process.pid;
285
+ }
286
+ function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
287
+ return date.toLocaleTimeString("en-US", {
288
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
289
+ hour12: false,
290
+ hour: "2-digit",
291
+ minute: "2-digit",
292
+ second: "2-digit",
293
+ fractionalSecondDigits: 3
294
+ });
295
+ }
296
+ function getSessionLogPath() {
297
+ const timestamp = createTimestampForFilename();
298
+ const filename = configuration.isDaemonProcess ? `${timestamp}-daemon.log` : `${timestamp}.log`;
299
+ return path.join(configuration.logsDir, filename);
300
+ }
301
+ class Logger {
302
+ constructor(logFilePath = getSessionLogPath()) {
303
+ this.logFilePath = logFilePath;
304
+ if (process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING && process.env.HAPPY_SERVER_URL) {
305
+ this.dangerouslyUnencryptedServerLoggingUrl = process.env.HAPPY_SERVER_URL;
306
+ console.log(chalk.yellow("[REMOTE LOGGING] Sending logs to server for AI debugging"));
307
+ }
308
+ }
309
+ dangerouslyUnencryptedServerLoggingUrl;
310
+ // Use local timezone for simplicity of locating the logs,
311
+ // in practice you will not need absolute timestamps
312
+ localTimezoneTimestamp() {
313
+ return createTimestampForLogEntry();
314
+ }
315
+ debug(message, ...args) {
316
+ this.logToFile(`[${this.localTimezoneTimestamp()}]`, message, ...args);
317
+ }
318
+ debugLargeJson(message, object, maxStringLength = 100, maxArrayLength = 10) {
319
+ if (!isDebug()) {
320
+ this.debug(`In production, skipping message inspection`);
321
+ }
322
+ const truncateStrings = (obj) => {
323
+ if (typeof obj === "string") {
324
+ return obj.length > maxStringLength ? obj.substring(0, maxStringLength) + "... [truncated for logs]" : obj;
325
+ }
326
+ if (Array.isArray(obj)) {
327
+ const truncatedArray = obj.map((item) => truncateStrings(item)).slice(0, maxArrayLength);
328
+ if (obj.length > maxArrayLength) {
329
+ truncatedArray.push(`... [truncated array for logs up to ${maxArrayLength} items]`);
330
+ }
331
+ return truncatedArray;
332
+ }
333
+ if (obj && typeof obj === "object") {
334
+ const result = {};
335
+ for (const [key, value] of Object.entries(obj)) {
336
+ if (key === "usage") {
337
+ continue;
338
+ }
339
+ result[key] = truncateStrings(value);
340
+ }
341
+ return result;
342
+ }
343
+ return obj;
344
+ };
345
+ const truncatedObject = truncateStrings(object);
346
+ const json = JSON.stringify(truncatedObject, null, 2);
347
+ this.logToFile(`[${this.localTimezoneTimestamp()}]`, message, "\n", json);
348
+ }
349
+ info(message, ...args) {
350
+ this.logToConsole("info", "", message, ...args);
351
+ this.debug(message, args);
352
+ }
353
+ infoDeveloper(message, ...args) {
354
+ this.debug(message, ...args);
355
+ if (isDebug()) {
356
+ this.logToConsole("info", "[DEV]", message, ...args);
357
+ }
358
+ }
359
+ warn(message, ...args) {
360
+ this.logToConsole("warn", "", message, ...args);
361
+ this.debug(`[WARN] ${message}`, ...args);
362
+ }
363
+ getLogPath() {
364
+ return this.logFilePath;
365
+ }
366
+ logToConsole(level, prefix, message, ...args) {
367
+ switch (level) {
368
+ case "debug": {
369
+ console.log(chalk.gray(prefix), message, ...args);
370
+ break;
371
+ }
372
+ case "error": {
373
+ console.error(chalk.red(prefix), message, ...args);
374
+ break;
375
+ }
376
+ case "info": {
377
+ console.log(chalk.blue(prefix), message, ...args);
378
+ break;
379
+ }
380
+ case "warn": {
381
+ console.log(chalk.yellow(prefix), message, ...args);
382
+ break;
383
+ }
384
+ default: {
385
+ this.debug("Unknown log level:", level);
386
+ console.log(chalk.blue(prefix), message, ...args);
387
+ break;
388
+ }
389
+ }
390
+ }
391
+ async sendToRemoteServer(level, message, ...args) {
392
+ if (!this.dangerouslyUnencryptedServerLoggingUrl) return;
393
+ try {
394
+ await fetch(this.dangerouslyUnencryptedServerLoggingUrl + "/logs-combined-from-cli-and-mobile-for-simple-ai-debugging", {
395
+ method: "POST",
396
+ headers: { "Content-Type": "application/json" },
397
+ body: JSON.stringify({
398
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
399
+ level,
400
+ message: `${message} ${args.map(
401
+ (a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)
402
+ ).join(" ")}`,
403
+ source: "cli",
404
+ platform: process.platform
405
+ })
406
+ });
407
+ } catch (error) {
408
+ }
409
+ }
410
+ logToFile(prefix, message, ...args) {
411
+ const logLine = `${prefix} ${message} ${args.map((arg) => {
412
+ if (typeof arg === "string") return arg;
413
+ if (arg instanceof Error) return `${arg.message}
414
+ ${arg.stack ?? ""}`;
415
+ return JSON.stringify(arg);
416
+ }).join(" ")}
417
+ `;
418
+ if (this.dangerouslyUnencryptedServerLoggingUrl) {
419
+ let level = "info";
420
+ if (prefix.includes(this.localTimezoneTimestamp())) {
421
+ level = "debug";
422
+ }
423
+ this.sendToRemoteServer(level, message, ...args).catch(() => {
424
+ });
425
+ }
426
+ try {
427
+ fs$1.appendFileSync(this.logFilePath, logLine);
428
+ } catch (appendError) {
429
+ if (isDebug()) {
430
+ console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
431
+ throw appendError;
432
+ }
433
+ }
434
+ }
435
+ }
436
+ let logger = new Logger();
437
+ async function listDaemonLogFiles(limit = 50) {
438
+ try {
439
+ const logsDir = configuration.logsDir;
440
+ if (!fs.existsSync(logsDir)) {
441
+ return [];
442
+ }
443
+ const logs = fs.readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
444
+ const fullPath = path.join(logsDir, file);
445
+ const stats = fs.statSync(fullPath);
446
+ return { file, path: fullPath, modified: stats.mtime };
447
+ }).sort((a, b) => b.modified.getTime() - a.modified.getTime());
448
+ try {
449
+ const { readDaemonState } = await Promise.resolve().then(function () { return persistence; });
450
+ const state = await readDaemonState();
451
+ if (!state) {
452
+ return logs;
453
+ }
454
+ if (state.daemonLogPath && fs.existsSync(state.daemonLogPath)) {
455
+ const stats = fs.statSync(state.daemonLogPath);
456
+ const persisted = {
457
+ file: path.basename(state.daemonLogPath),
458
+ path: state.daemonLogPath,
459
+ modified: stats.mtime
460
+ };
461
+ const idx = logs.findIndex((l) => l.path === persisted.path);
462
+ if (idx >= 0) {
463
+ const [found] = logs.splice(idx, 1);
464
+ logs.unshift(found);
465
+ } else {
466
+ logs.unshift(persisted);
467
+ }
468
+ }
469
+ } catch {
470
+ }
471
+ return logs.slice(0, Math.max(0, limit));
472
+ } catch {
473
+ return [];
474
+ }
475
+ }
476
+ async function getLatestDaemonLog() {
477
+ const [latest] = await listDaemonLogFiles(1);
478
+ return latest || null;
479
+ }
480
+
481
+ var logger$1 = /*#__PURE__*/Object.freeze({
482
+ __proto__: null,
483
+ getLatestDaemonLog: getLatestDaemonLog,
484
+ listDaemonLogFiles: listDaemonLogFiles,
485
+ logger: logger
486
+ });
487
+
488
+ function encodeBase64(buffer, variant = "base64") {
489
+ if (variant === "base64url") {
490
+ return encodeBase64Url(buffer);
491
+ }
492
+ return Buffer.from(buffer).toString("base64");
493
+ }
494
+ function encodeBase64Url(buffer) {
495
+ return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
496
+ }
497
+ function decodeBase64(base64, variant = "base64") {
498
+ if (variant === "base64url") {
499
+ const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
500
+ return new Uint8Array(Buffer.from(base64Standard, "base64"));
501
+ }
502
+ return new Uint8Array(Buffer.from(base64, "base64"));
503
+ }
504
+ function getRandomBytes(size) {
505
+ return new Uint8Array(node_crypto.randomBytes(size));
506
+ }
507
+ function libsodiumPublicKeyFromSecretKey(seed) {
508
+ const hashedSeed = new Uint8Array(node_crypto.createHash("sha512").update(seed).digest());
509
+ const secretKey = hashedSeed.slice(0, 32);
510
+ return new Uint8Array(tweetnacl.box.keyPair.fromSecretKey(secretKey).publicKey);
511
+ }
512
+ function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
513
+ const ephemeralKeyPair = tweetnacl.box.keyPair();
514
+ const nonce = getRandomBytes(tweetnacl.box.nonceLength);
515
+ const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
516
+ const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
517
+ result.set(ephemeralKeyPair.publicKey, 0);
518
+ result.set(nonce, ephemeralKeyPair.publicKey.length);
519
+ result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
520
+ return result;
521
+ }
522
+ function encryptLegacy(data, secret) {
523
+ const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
524
+ const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
525
+ const result = new Uint8Array(nonce.length + encrypted.length);
526
+ result.set(nonce);
527
+ result.set(encrypted, nonce.length);
528
+ return result;
529
+ }
530
+ function decryptLegacy(data, secret) {
531
+ const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
532
+ const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
533
+ const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
534
+ if (!decrypted) {
535
+ return null;
536
+ }
537
+ return JSON.parse(new TextDecoder().decode(decrypted));
538
+ }
539
+ function encryptWithDataKey(data, dataKey) {
540
+ const nonce = getRandomBytes(12);
541
+ const cipher = node_crypto.createCipheriv("aes-256-gcm", dataKey, nonce);
542
+ const plaintext = new TextEncoder().encode(JSON.stringify(data));
543
+ const encrypted = Buffer.concat([
544
+ cipher.update(plaintext),
545
+ cipher.final()
546
+ ]);
547
+ const authTag = cipher.getAuthTag();
548
+ const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
549
+ bundle.set([0], 0);
550
+ bundle.set(nonce, 1);
551
+ bundle.set(new Uint8Array(encrypted), 13);
552
+ bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
553
+ return bundle;
554
+ }
555
+ function decryptWithDataKey(bundle, dataKey) {
556
+ if (bundle.length < 1) {
557
+ return null;
558
+ }
559
+ if (bundle[0] !== 0) {
560
+ return null;
561
+ }
562
+ if (bundle.length < 12 + 16 + 1) {
563
+ return null;
564
+ }
565
+ const nonce = bundle.slice(1, 13);
566
+ const authTag = bundle.slice(bundle.length - 16);
567
+ const ciphertext = bundle.slice(13, bundle.length - 16);
568
+ try {
569
+ const decipher = node_crypto.createDecipheriv("aes-256-gcm", dataKey, nonce);
570
+ decipher.setAuthTag(authTag);
571
+ const decrypted = Buffer.concat([
572
+ decipher.update(ciphertext),
573
+ decipher.final()
574
+ ]);
575
+ return JSON.parse(new TextDecoder().decode(decrypted));
576
+ } catch (error) {
577
+ return null;
578
+ }
579
+ }
580
+ function encrypt(key, variant, data) {
581
+ if (variant === "legacy") {
582
+ return encryptLegacy(data, key);
583
+ } else {
584
+ return encryptWithDataKey(data, key);
585
+ }
586
+ }
587
+ function decrypt(key, variant, data) {
588
+ if (variant === "legacy") {
589
+ return decryptLegacy(data, key);
590
+ } else {
591
+ return decryptWithDataKey(data, key);
592
+ }
593
+ }
594
+
595
+ const AnthropicConfigSchema = z__namespace.object({
596
+ baseUrl: z__namespace.string().url().optional(),
597
+ authToken: z__namespace.string().optional(),
598
+ model: z__namespace.string().optional()
599
+ });
600
+ const OpenAIConfigSchema = z__namespace.object({
601
+ apiKey: z__namespace.string().optional(),
602
+ baseUrl: z__namespace.string().url().optional(),
603
+ model: z__namespace.string().optional()
604
+ });
605
+ const AzureOpenAIConfigSchema = z__namespace.object({
606
+ apiKey: z__namespace.string().optional(),
607
+ endpoint: z__namespace.string().url().optional(),
608
+ apiVersion: z__namespace.string().optional(),
609
+ deploymentName: z__namespace.string().optional()
610
+ });
611
+ const TogetherAIConfigSchema = z__namespace.object({
612
+ apiKey: z__namespace.string().optional(),
613
+ model: z__namespace.string().optional()
614
+ });
615
+ const TmuxConfigSchema = z__namespace.object({
616
+ sessionName: z__namespace.string().optional(),
617
+ tmpDir: z__namespace.string().optional(),
618
+ updateEnvironment: z__namespace.boolean().optional()
619
+ });
620
+ const EnvironmentVariableSchema = z__namespace.object({
621
+ name: z__namespace.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
622
+ value: z__namespace.string()
623
+ });
624
+ const ProfileCompatibilitySchema = z__namespace.object({
625
+ claude: z__namespace.boolean().default(true),
626
+ codex: z__namespace.boolean().default(true),
627
+ gemini: z__namespace.boolean().default(true)
628
+ });
629
+ const AIBackendProfileSchema = z__namespace.object({
630
+ id: z__namespace.string().uuid(),
631
+ name: z__namespace.string().min(1).max(100),
632
+ description: z__namespace.string().max(500).optional(),
633
+ // Agent-specific configurations
634
+ anthropicConfig: AnthropicConfigSchema.optional(),
635
+ openaiConfig: OpenAIConfigSchema.optional(),
636
+ azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
637
+ togetherAIConfig: TogetherAIConfigSchema.optional(),
638
+ // Tmux configuration
639
+ tmuxConfig: TmuxConfigSchema.optional(),
640
+ // Environment variables (validated)
641
+ environmentVariables: z__namespace.array(EnvironmentVariableSchema).default([]),
642
+ // Default session type for this profile
643
+ defaultSessionType: z__namespace.enum(["simple", "worktree"]).optional(),
644
+ // Default permission mode for this profile (supports both Claude and Codex modes)
645
+ defaultPermissionMode: z__namespace.enum([
646
+ "default",
647
+ "acceptEdits",
648
+ "bypassPermissions",
649
+ "plan",
650
+ // Claude modes
651
+ "read-only",
652
+ "safe-yolo",
653
+ "yolo"
654
+ // Codex modes
655
+ ]).optional(),
656
+ // Default model mode for this profile
657
+ defaultModelMode: z__namespace.string().optional(),
658
+ // Compatibility metadata
659
+ compatibility: ProfileCompatibilitySchema.default({ claude: true, codex: true, gemini: true }),
660
+ // Built-in profile indicator
661
+ isBuiltIn: z__namespace.boolean().default(false),
662
+ // Metadata
663
+ createdAt: z__namespace.number().default(() => Date.now()),
664
+ updatedAt: z__namespace.number().default(() => Date.now()),
665
+ version: z__namespace.string().default("1.0.0")
666
+ });
667
+ function validateProfileForAgent(profile, agent) {
668
+ return profile.compatibility[agent];
669
+ }
670
+ function getProfileEnvironmentVariables(profile) {
671
+ const envVars = {};
672
+ profile.environmentVariables.forEach((envVar) => {
673
+ envVars[envVar.name] = envVar.value;
674
+ });
675
+ if (profile.anthropicConfig) {
676
+ if (profile.anthropicConfig.baseUrl) envVars.ANTHROPIC_BASE_URL = profile.anthropicConfig.baseUrl;
677
+ if (profile.anthropicConfig.authToken) envVars.ANTHROPIC_AUTH_TOKEN = profile.anthropicConfig.authToken;
678
+ if (profile.anthropicConfig.model) envVars.ANTHROPIC_MODEL = profile.anthropicConfig.model;
679
+ }
680
+ if (profile.openaiConfig) {
681
+ if (profile.openaiConfig.apiKey) envVars.OPENAI_API_KEY = profile.openaiConfig.apiKey;
682
+ if (profile.openaiConfig.baseUrl) envVars.OPENAI_BASE_URL = profile.openaiConfig.baseUrl;
683
+ if (profile.openaiConfig.model) envVars.OPENAI_MODEL = profile.openaiConfig.model;
684
+ }
685
+ if (profile.azureOpenAIConfig) {
686
+ if (profile.azureOpenAIConfig.apiKey) envVars.AZURE_OPENAI_API_KEY = profile.azureOpenAIConfig.apiKey;
687
+ if (profile.azureOpenAIConfig.endpoint) envVars.AZURE_OPENAI_ENDPOINT = profile.azureOpenAIConfig.endpoint;
688
+ if (profile.azureOpenAIConfig.apiVersion) envVars.AZURE_OPENAI_API_VERSION = profile.azureOpenAIConfig.apiVersion;
689
+ if (profile.azureOpenAIConfig.deploymentName) envVars.AZURE_OPENAI_DEPLOYMENT_NAME = profile.azureOpenAIConfig.deploymentName;
690
+ }
691
+ if (profile.togetherAIConfig) {
692
+ if (profile.togetherAIConfig.apiKey) envVars.TOGETHER_API_KEY = profile.togetherAIConfig.apiKey;
693
+ if (profile.togetherAIConfig.model) envVars.TOGETHER_MODEL = profile.togetherAIConfig.model;
694
+ }
695
+ if (profile.tmuxConfig) {
696
+ if (profile.tmuxConfig.sessionName !== void 0) envVars.TMUX_SESSION_NAME = profile.tmuxConfig.sessionName;
697
+ if (profile.tmuxConfig.tmpDir) envVars.TMUX_TMPDIR = profile.tmuxConfig.tmpDir;
698
+ if (profile.tmuxConfig.updateEnvironment !== void 0) {
699
+ envVars.TMUX_UPDATE_ENVIRONMENT = profile.tmuxConfig.updateEnvironment.toString();
700
+ }
701
+ }
702
+ return envVars;
703
+ }
704
+ const SUPPORTED_SCHEMA_VERSION = 2;
705
+ const defaultSettings = {
706
+ schemaVersion: SUPPORTED_SCHEMA_VERSION,
707
+ onboardingCompleted: false,
708
+ profiles: [],
709
+ localEnvironmentVariables: {}
710
+ };
711
+ function migrateSettings(raw, fromVersion) {
712
+ let migrated = { ...raw };
713
+ if (fromVersion < 2) {
714
+ if (!migrated.profiles) {
715
+ migrated.profiles = [];
716
+ }
717
+ if (!migrated.localEnvironmentVariables) {
718
+ migrated.localEnvironmentVariables = {};
719
+ }
720
+ migrated.schemaVersion = 2;
721
+ }
722
+ return migrated;
723
+ }
724
+ async function readSettings() {
725
+ if (!fs.existsSync(configuration.settingsFile)) {
726
+ return { ...defaultSettings };
727
+ }
728
+ try {
729
+ const content = await promises.readFile(configuration.settingsFile, "utf8");
730
+ const raw = JSON.parse(content);
731
+ const schemaVersion = raw.schemaVersion ?? 1;
732
+ if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
733
+ logger.warn(
734
+ `\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
735
+ );
736
+ }
737
+ const migrated = migrateSettings(raw, schemaVersion);
738
+ if (migrated.profiles && Array.isArray(migrated.profiles)) {
739
+ const validProfiles = [];
740
+ for (const profile of migrated.profiles) {
741
+ try {
742
+ const validated = AIBackendProfileSchema.parse(profile);
743
+ validProfiles.push(validated);
744
+ } catch (error) {
745
+ logger.warn(
746
+ `\u26A0\uFE0F Invalid profile "${profile?.name || profile?.id || "unknown"}" - skipping. Error: ${error.message}`
747
+ );
748
+ }
749
+ }
750
+ migrated.profiles = validProfiles;
751
+ }
752
+ return { ...defaultSettings, ...migrated };
753
+ } catch (error) {
754
+ logger.warn(`Failed to read settings: ${error.message}`);
755
+ return { ...defaultSettings };
756
+ }
757
+ }
758
+ async function updateSettings(updater) {
759
+ const LOCK_RETRY_INTERVAL_MS = 100;
760
+ const MAX_LOCK_ATTEMPTS = 50;
761
+ const STALE_LOCK_TIMEOUT_MS = 1e4;
762
+ const lockFile = configuration.settingsFile + ".lock";
763
+ const tmpFile = configuration.settingsFile + ".tmp";
764
+ let fileHandle;
765
+ let attempts = 0;
766
+ while (attempts < MAX_LOCK_ATTEMPTS) {
767
+ try {
768
+ fileHandle = await promises.open(lockFile, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
769
+ break;
770
+ } catch (err) {
771
+ if (err.code === "EEXIST") {
772
+ attempts++;
773
+ await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
774
+ try {
775
+ const stats = await promises.stat(lockFile);
776
+ if (Date.now() - stats.mtimeMs > STALE_LOCK_TIMEOUT_MS) {
777
+ await promises.unlink(lockFile).catch(() => {
778
+ });
779
+ }
780
+ } catch {
781
+ }
782
+ } else {
783
+ throw err;
784
+ }
785
+ }
786
+ }
787
+ if (!fileHandle) {
788
+ throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
789
+ }
790
+ try {
791
+ const current = await readSettings() || { ...defaultSettings };
792
+ const updated = await updater(current);
793
+ if (!fs.existsSync(configuration.happyHomeDir)) {
794
+ await promises.mkdir(configuration.happyHomeDir, { recursive: true });
795
+ }
796
+ await promises.writeFile(tmpFile, JSON.stringify(updated, null, 2));
797
+ await promises.rename(tmpFile, configuration.settingsFile);
798
+ return updated;
799
+ } finally {
800
+ await fileHandle.close();
801
+ await promises.unlink(lockFile).catch(() => {
802
+ });
803
+ }
804
+ }
805
+ const credentialsSchema = z__namespace.object({
806
+ token: z__namespace.string(),
807
+ secret: z__namespace.string().base64().nullish(),
808
+ // Legacy
809
+ encryption: z__namespace.object({
810
+ publicKey: z__namespace.string().base64(),
811
+ machineKey: z__namespace.string().base64()
812
+ }).nullish()
813
+ });
814
+ async function readCredentials() {
815
+ if (!fs.existsSync(configuration.privateKeyFile)) {
816
+ return null;
817
+ }
818
+ try {
819
+ const keyBase64 = await promises.readFile(configuration.privateKeyFile, "utf8");
820
+ const credentials = credentialsSchema.parse(JSON.parse(keyBase64));
821
+ if (credentials.secret) {
822
+ return {
823
+ token: credentials.token,
824
+ encryption: {
825
+ type: "legacy",
826
+ secret: new Uint8Array(Buffer.from(credentials.secret, "base64"))
827
+ }
828
+ };
829
+ } else if (credentials.encryption) {
830
+ return {
831
+ token: credentials.token,
832
+ encryption: {
833
+ type: "dataKey",
834
+ publicKey: new Uint8Array(Buffer.from(credentials.encryption.publicKey, "base64")),
835
+ machineKey: new Uint8Array(Buffer.from(credentials.encryption.machineKey, "base64"))
836
+ }
837
+ };
838
+ }
839
+ } catch {
840
+ return null;
841
+ }
842
+ return null;
843
+ }
844
+ async function writeCredentialsLegacy(credentials) {
845
+ if (!fs.existsSync(configuration.happyHomeDir)) {
846
+ await promises.mkdir(configuration.happyHomeDir, { recursive: true });
847
+ }
848
+ await promises.writeFile(configuration.privateKeyFile, JSON.stringify({
849
+ secret: encodeBase64(credentials.secret),
850
+ token: credentials.token
851
+ }, null, 2));
852
+ }
853
+ async function writeCredentialsDataKey(credentials) {
854
+ if (!fs.existsSync(configuration.happyHomeDir)) {
855
+ await promises.mkdir(configuration.happyHomeDir, { recursive: true });
856
+ }
857
+ await promises.writeFile(configuration.privateKeyFile, JSON.stringify({
858
+ encryption: { publicKey: encodeBase64(credentials.publicKey), machineKey: encodeBase64(credentials.machineKey) },
859
+ token: credentials.token
860
+ }, null, 2));
861
+ }
862
+ async function clearCredentials() {
863
+ if (fs.existsSync(configuration.privateKeyFile)) {
864
+ await promises.unlink(configuration.privateKeyFile);
865
+ }
866
+ }
867
+ async function clearMachineId() {
868
+ await updateSettings((settings) => ({
869
+ ...settings,
870
+ machineId: void 0
871
+ }));
872
+ }
873
+ async function readDaemonState() {
874
+ try {
875
+ if (!fs.existsSync(configuration.daemonStateFile)) {
876
+ return null;
877
+ }
878
+ const content = await promises.readFile(configuration.daemonStateFile, "utf-8");
879
+ return JSON.parse(content);
880
+ } catch (error) {
881
+ console.error(`[PERSISTENCE] Daemon state file corrupted: ${configuration.daemonStateFile}`, error);
882
+ return null;
883
+ }
884
+ }
885
+ function writeDaemonState(state) {
886
+ fs.writeFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2), "utf-8");
887
+ }
888
+ async function clearDaemonState() {
889
+ if (fs.existsSync(configuration.daemonStateFile)) {
890
+ await promises.unlink(configuration.daemonStateFile);
891
+ }
892
+ if (fs.existsSync(configuration.daemonLockFile)) {
893
+ try {
894
+ await promises.unlink(configuration.daemonLockFile);
895
+ } catch {
896
+ }
897
+ }
898
+ }
899
+ async function acquireDaemonLock(maxAttempts = 5, delayIncrementMs = 200) {
900
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
901
+ try {
902
+ const fileHandle = await promises.open(
903
+ configuration.daemonLockFile,
904
+ fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY
905
+ );
906
+ await fileHandle.writeFile(String(process.pid));
907
+ return fileHandle;
908
+ } catch (error) {
909
+ if (error.code === "EEXIST") {
910
+ try {
911
+ const lockPid = fs.readFileSync(configuration.daemonLockFile, "utf-8").trim();
912
+ if (lockPid && !isNaN(Number(lockPid))) {
913
+ try {
914
+ process.kill(Number(lockPid), 0);
915
+ } catch {
916
+ fs.unlinkSync(configuration.daemonLockFile);
917
+ continue;
918
+ }
919
+ }
920
+ } catch {
921
+ }
922
+ }
923
+ if (attempt === maxAttempts) {
924
+ return null;
925
+ }
926
+ const delayMs = attempt * delayIncrementMs;
927
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
928
+ }
929
+ }
930
+ return null;
931
+ }
932
+ async function releaseDaemonLock(lockHandle) {
933
+ try {
934
+ await lockHandle.close();
935
+ } catch {
936
+ }
937
+ try {
938
+ if (fs.existsSync(configuration.daemonLockFile)) {
939
+ fs.unlinkSync(configuration.daemonLockFile);
940
+ }
941
+ } catch {
942
+ }
943
+ }
944
+
945
+ var persistence = /*#__PURE__*/Object.freeze({
946
+ __proto__: null,
947
+ AIBackendProfileSchema: AIBackendProfileSchema,
948
+ SUPPORTED_SCHEMA_VERSION: SUPPORTED_SCHEMA_VERSION,
949
+ acquireDaemonLock: acquireDaemonLock,
950
+ clearCredentials: clearCredentials,
951
+ clearDaemonState: clearDaemonState,
952
+ clearMachineId: clearMachineId,
953
+ getProfileEnvironmentVariables: getProfileEnvironmentVariables,
954
+ readCredentials: readCredentials,
955
+ readDaemonState: readDaemonState,
956
+ readSettings: readSettings,
957
+ releaseDaemonLock: releaseDaemonLock,
958
+ updateSettings: updateSettings,
959
+ validateProfileForAgent: validateProfileForAgent,
960
+ writeCredentialsDataKey: writeCredentialsDataKey,
961
+ writeCredentialsLegacy: writeCredentialsLegacy,
962
+ writeDaemonState: writeDaemonState
963
+ });
964
+
965
+ exports.acquireDaemonLock = acquireDaemonLock;
966
+ exports.clearCredentials = clearCredentials;
967
+ exports.clearDaemonState = clearDaemonState;
968
+ exports.clearMachineId = clearMachineId;
969
+ exports.configuration = configuration;
970
+ exports.decodeBase64 = decodeBase64;
971
+ exports.decrypt = decrypt;
972
+ exports.decryptLegacy = decryptLegacy;
973
+ exports.decryptWithDataKey = decryptWithDataKey;
974
+ exports.encodeBase64 = encodeBase64;
975
+ exports.encodeBase64Url = encodeBase64Url;
976
+ exports.encrypt = encrypt;
977
+ exports.encryptLegacy = encryptLegacy;
978
+ exports.encryptWithDataKey = encryptWithDataKey;
979
+ exports.getProfileEnvironmentVariables = getProfileEnvironmentVariables;
980
+ exports.getRandomBytes = getRandomBytes;
981
+ exports.isDebug = isDebug;
982
+ exports.libsodiumEncryptForPublicKey = libsodiumEncryptForPublicKey;
983
+ exports.libsodiumPublicKeyFromSecretKey = libsodiumPublicKeyFromSecretKey;
984
+ exports.logger = logger;
985
+ exports.logger$1 = logger$1;
986
+ exports.packageJson = packageJson;
987
+ exports.persistence = persistence;
988
+ exports.readCredentials = readCredentials;
989
+ exports.readDaemonState = readDaemonState;
990
+ exports.readSettings = readSettings;
991
+ exports.releaseDaemonLock = releaseDaemonLock;
992
+ exports.updateSettings = updateSettings;
993
+ exports.validateProfileForAgent = validateProfileForAgent;
994
+ exports.writeCredentialsDataKey = writeCredentialsDataKey;
995
+ exports.writeCredentialsLegacy = writeCredentialsLegacy;
996
+ exports.writeDaemonState = writeDaemonState;