happy-coder 0.10.0-2 → 0.10.0-4

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 (46) hide show
  1. package/README.md +10 -1
  2. package/bin/happy-mcp.mjs +32 -0
  3. package/dist/codex/happyMcpStdioBridge.cjs +80 -0
  4. package/dist/codex/happyMcpStdioBridge.d.cts +2 -0
  5. package/dist/codex/happyMcpStdioBridge.d.mts +2 -0
  6. package/dist/codex/happyMcpStdioBridge.mjs +78 -0
  7. package/dist/index-67rskwL7.cjs +6033 -0
  8. package/dist/index-Dw96QD4T.mjs +6025 -0
  9. package/dist/index.cjs +33 -6024
  10. package/dist/index.mjs +33 -6024
  11. package/dist/lib.cjs +2 -1
  12. package/dist/lib.d.cts +54 -112
  13. package/dist/lib.d.mts +54 -112
  14. package/dist/lib.mjs +2 -1
  15. package/dist/runCodex-BLNf5zb1.cjs +1155 -0
  16. package/dist/runCodex-BNH8w4O9.mjs +1153 -0
  17. package/dist/{types-xfXKJHdM.mjs → types-2wHnX7UW.mjs} +311 -170
  18. package/dist/{types-WP9wteZE.cjs → types-BcDnTXMg.cjs} +348 -206
  19. package/package.json +20 -6
  20. package/scripts/ripgrep_launcher.cjs +2 -26
  21. package/scripts/unpack-tools.cjs +163 -0
  22. package/tools/archives/difftastic-LICENSE +21 -0
  23. package/tools/archives/difftastic-arm64-darwin.tar.gz +0 -0
  24. package/tools/archives/difftastic-arm64-linux.tar.gz +0 -0
  25. package/tools/archives/difftastic-x64-darwin.tar.gz +0 -0
  26. package/tools/archives/difftastic-x64-linux.tar.gz +0 -0
  27. package/tools/archives/difftastic-x64-win32.tar.gz +0 -0
  28. package/tools/archives/ripgrep-arm64-darwin.tar.gz +0 -0
  29. package/tools/archives/ripgrep-arm64-linux.tar.gz +0 -0
  30. package/tools/archives/ripgrep-x64-darwin.tar.gz +0 -0
  31. package/tools/archives/ripgrep-x64-linux.tar.gz +0 -0
  32. package/tools/archives/ripgrep-x64-win32.tar.gz +0 -0
  33. package/tools/licenses/difftastic-LICENSE +21 -0
  34. package/tools/licenses/ripgrep-LICENSE +3 -0
  35. package/tools/unpacked/difft +0 -0
  36. package/ripgrep/arm64-linux/rg +0 -0
  37. package/ripgrep/arm64-linux/ripgrep.node +0 -0
  38. package/ripgrep/x64-darwin/rg +0 -0
  39. package/ripgrep/x64-darwin/ripgrep.node +0 -0
  40. package/ripgrep/x64-linux/rg +0 -0
  41. package/ripgrep/x64-linux/ripgrep.node +0 -0
  42. package/ripgrep/x64-win32/rg.exe +0 -0
  43. package/ripgrep/x64-win32/ripgrep.node +0 -0
  44. /package/{ripgrep/COPYING → tools/archives/ripgrep-LICENSE} +0 -0
  45. /package/{ripgrep/arm64-darwin → tools/unpacked}/rg +0 -0
  46. /package/{ripgrep/arm64-darwin → tools/unpacked}/ripgrep.node +0 -0
@@ -7,7 +7,7 @@ import { join, basename } from 'node:path';
7
7
  import { readFile, open, stat, unlink, mkdir, writeFile, rename } from 'node:fs/promises';
8
8
  import * as z from 'zod';
9
9
  import { z as z$1 } from 'zod';
10
- import { randomBytes, randomUUID } from 'node:crypto';
10
+ import { randomBytes, createCipheriv, createDecipheriv, randomUUID } from 'node:crypto';
11
11
  import tweetnacl from 'tweetnacl';
12
12
  import { EventEmitter } from 'node:events';
13
13
  import { io } from 'socket.io-client';
@@ -17,11 +17,12 @@ import { readFile as readFile$1, stat as stat$1, writeFile as writeFile$1, readd
17
17
  import { createHash } from 'crypto';
18
18
  import { dirname, resolve, join as join$1 } from 'path';
19
19
  import { fileURLToPath } from 'url';
20
+ import { platform } from 'os';
20
21
  import { Expo } from 'expo-server-sdk';
21
22
 
22
23
  var name = "happy-coder";
23
- var version = "0.10.0-2";
24
- var description = "Claude Code session sharing CLI";
24
+ var version = "0.10.0-4";
25
+ var description = "Mobile and Web client for Claude Code and Codex";
25
26
  var author = "Kirill Dubovitskiy";
26
27
  var license = "MIT";
27
28
  var type = "module";
@@ -29,7 +30,8 @@ var homepage = "https://github.com/slopus/happy-cli";
29
30
  var bugs = "https://github.com/slopus/happy-cli/issues";
30
31
  var repository = "slopus/happy-cli";
31
32
  var bin = {
32
- happy: "./bin/happy.mjs"
33
+ happy: "./bin/happy.mjs",
34
+ "happy-mcp": "./bin/happy-mcp.mjs"
33
35
  };
34
36
  var main = "./dist/index.cjs";
35
37
  var module = "./dist/index.mjs";
@@ -54,13 +56,23 @@ var exports = {
54
56
  types: "./dist/lib.d.mts",
55
57
  "default": "./dist/lib.mjs"
56
58
  }
59
+ },
60
+ "./codex/happyMcpStdioBridge": {
61
+ require: {
62
+ types: "./dist/codex/happyMcpStdioBridge.d.cts",
63
+ "default": "./dist/codex/happyMcpStdioBridge.cjs"
64
+ },
65
+ "import": {
66
+ types: "./dist/codex/happyMcpStdioBridge.d.mts",
67
+ "default": "./dist/codex/happyMcpStdioBridge.mjs"
68
+ }
57
69
  }
58
70
  };
59
71
  var files = [
60
72
  "dist",
61
73
  "bin",
62
74
  "scripts",
63
- "ripgrep",
75
+ "tools",
64
76
  "package.json"
65
77
  ];
66
78
  var scripts = {
@@ -69,17 +81,19 @@ var scripts = {
69
81
  build: "shx rm -rf dist && npx tsc --noEmit && pkgroll",
70
82
  test: "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
71
83
  start: "yarn build && ./bin/happy.mjs",
72
- dev: "yarn build && tsx --env-file .env.dev src/index.ts",
84
+ dev: "tsx --env-file .env.dev src/index.ts",
73
85
  "dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
74
86
  "dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
75
87
  prepublishOnly: "yarn build && yarn test",
76
- release: "release-it"
88
+ release: "release-it",
89
+ postinstall: "node scripts/unpack-tools.cjs"
77
90
  };
78
91
  var dependencies = {
79
92
  "@anthropic-ai/claude-code": "^1.0.102",
80
93
  "@anthropic-ai/sdk": "^0.56.0",
81
94
  "@modelcontextprotocol/sdk": "^1.15.1",
82
95
  "@stablelib/base64": "^2.0.1",
96
+ "@stablelib/hex": "^2.0.1",
83
97
  "@types/cross-spawn": "^6.0.6",
84
98
  "@types/http-proxy": "^1.17.16",
85
99
  "@types/ps-list": "^6.2.1",
@@ -99,6 +113,7 @@ var dependencies = {
99
113
  "qrcode-terminal": "^0.12.0",
100
114
  react: "^19.1.1",
101
115
  "socket.io-client": "^4.8.1",
116
+ tar: "^7.4.3",
102
117
  tweetnacl: "^1.0.3",
103
118
  zod: "^3.23.8"
104
119
  };
@@ -152,6 +167,7 @@ var packageJson = {
152
167
 
153
168
  class Configuration {
154
169
  serverUrl;
170
+ webappUrl;
155
171
  isDaemonProcess;
156
172
  // Directories and paths (from persistence)
157
173
  happyHomeDir;
@@ -164,6 +180,7 @@ class Configuration {
164
180
  isExperimentalEnabled;
165
181
  constructor() {
166
182
  this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api.cluster-fluster.com";
183
+ this.webappUrl = process.env.HAPPY_WEBAPP_URL || "https://app.happy.engineering";
167
184
  const args = process.argv.slice(2);
168
185
  this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
169
186
  if (process.env.HAPPY_HOME_DIR) {
@@ -208,7 +225,17 @@ function decodeBase64(base64, variant = "base64") {
208
225
  function getRandomBytes(size) {
209
226
  return new Uint8Array(randomBytes(size));
210
227
  }
211
- function encrypt(data, secret) {
228
+ function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
229
+ const ephemeralKeyPair = tweetnacl.box.keyPair();
230
+ const nonce = getRandomBytes(tweetnacl.box.nonceLength);
231
+ const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
232
+ const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
233
+ result.set(ephemeralKeyPair.publicKey, 0);
234
+ result.set(nonce, ephemeralKeyPair.publicKey.length);
235
+ result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
236
+ return result;
237
+ }
238
+ function encryptLegacy(data, secret) {
212
239
  const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
213
240
  const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
214
241
  const result = new Uint8Array(nonce.length + encrypted.length);
@@ -216,7 +243,7 @@ function encrypt(data, secret) {
216
243
  result.set(encrypted, nonce.length);
217
244
  return result;
218
245
  }
219
- function decrypt(data, secret) {
246
+ function decryptLegacy(data, secret) {
220
247
  const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
221
248
  const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
222
249
  const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
@@ -225,6 +252,61 @@ function decrypt(data, secret) {
225
252
  }
226
253
  return JSON.parse(new TextDecoder().decode(decrypted));
227
254
  }
255
+ function encryptWithDataKey(data, dataKey) {
256
+ const nonce = getRandomBytes(12);
257
+ const cipher = createCipheriv("aes-256-gcm", dataKey, nonce);
258
+ const plaintext = new TextEncoder().encode(JSON.stringify(data));
259
+ const encrypted = Buffer.concat([
260
+ cipher.update(plaintext),
261
+ cipher.final()
262
+ ]);
263
+ const authTag = cipher.getAuthTag();
264
+ const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
265
+ bundle.set([0], 0);
266
+ bundle.set(nonce, 1);
267
+ bundle.set(new Uint8Array(encrypted), 13);
268
+ bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
269
+ return bundle;
270
+ }
271
+ function decryptWithDataKey(bundle, dataKey) {
272
+ if (bundle.length < 1) {
273
+ return null;
274
+ }
275
+ if (bundle[0] !== 0) {
276
+ return null;
277
+ }
278
+ if (bundle.length < 12 + 16 + 1) {
279
+ return null;
280
+ }
281
+ const nonce = bundle.slice(1, 13);
282
+ const authTag = bundle.slice(bundle.length - 16);
283
+ const ciphertext = bundle.slice(13, bundle.length - 16);
284
+ try {
285
+ const decipher = createDecipheriv("aes-256-gcm", dataKey, nonce);
286
+ decipher.setAuthTag(authTag);
287
+ const decrypted = Buffer.concat([
288
+ decipher.update(ciphertext),
289
+ decipher.final()
290
+ ]);
291
+ return JSON.parse(new TextDecoder().decode(decrypted));
292
+ } catch (error) {
293
+ return null;
294
+ }
295
+ }
296
+ function encrypt(key, variant, data) {
297
+ if (variant === "legacy") {
298
+ return encryptLegacy(data, key);
299
+ } else {
300
+ return encryptWithDataKey(data, key);
301
+ }
302
+ }
303
+ function decrypt(key, variant, data) {
304
+ if (variant === "legacy") {
305
+ return decryptLegacy(data, key);
306
+ } else {
307
+ return decryptWithDataKey(data, key);
308
+ }
309
+ }
228
310
 
229
311
  const defaultSettings = {
230
312
  onboardingCompleted: false
@@ -288,8 +370,13 @@ async function updateSettings(updater) {
288
370
  }
289
371
  }
290
372
  const credentialsSchema = z.object({
291
- secret: z.string().base64(),
292
- token: z.string()
373
+ token: z.string(),
374
+ secret: z.string().base64().nullish(),
375
+ // Legacy
376
+ encryption: z.object({
377
+ publicKey: z.string().base64(),
378
+ machineKey: z.string().base64()
379
+ }).nullish()
293
380
  });
294
381
  async function readCredentials() {
295
382
  if (!existsSync(configuration.privateKeyFile)) {
@@ -298,15 +385,30 @@ async function readCredentials() {
298
385
  try {
299
386
  const keyBase64 = await readFile(configuration.privateKeyFile, "utf8");
300
387
  const credentials = credentialsSchema.parse(JSON.parse(keyBase64));
301
- return {
302
- secret: new Uint8Array(Buffer.from(credentials.secret, "base64")),
303
- token: credentials.token
304
- };
388
+ if (credentials.secret) {
389
+ return {
390
+ token: credentials.token,
391
+ encryption: {
392
+ type: "legacy",
393
+ secret: new Uint8Array(Buffer.from(credentials.secret, "base64"))
394
+ }
395
+ };
396
+ } else if (credentials.encryption) {
397
+ return {
398
+ token: credentials.token,
399
+ encryption: {
400
+ type: "dataKey",
401
+ publicKey: new Uint8Array(Buffer.from(credentials.encryption.publicKey, "base64")),
402
+ machineKey: new Uint8Array(Buffer.from(credentials.encryption.machineKey, "base64"))
403
+ }
404
+ };
405
+ }
305
406
  } catch {
306
407
  return null;
307
408
  }
409
+ return null;
308
410
  }
309
- async function writeCredentials(credentials) {
411
+ async function writeCredentialsLegacy(credentials) {
310
412
  if (!existsSync(configuration.happyHomeDir)) {
311
413
  await mkdir(configuration.happyHomeDir, { recursive: true });
312
414
  }
@@ -315,6 +417,15 @@ async function writeCredentials(credentials) {
315
417
  token: credentials.token
316
418
  }, null, 2));
317
419
  }
420
+ async function writeCredentialsDataKey(credentials) {
421
+ if (!existsSync(configuration.happyHomeDir)) {
422
+ await mkdir(configuration.happyHomeDir, { recursive: true });
423
+ }
424
+ await writeFile(configuration.privateKeyFile, JSON.stringify({
425
+ encryption: { publicKey: encodeBase64(credentials.publicKey), machineKey: encodeBase64(credentials.machineKey) },
426
+ token: credentials.token
427
+ }, null, 2));
428
+ }
318
429
  async function clearCredentials() {
319
430
  if (existsSync(configuration.privateKeyFile)) {
320
431
  await unlink(configuration.privateKeyFile);
@@ -482,6 +593,13 @@ class Logger {
482
593
  this.logToConsole("info", "[DEV]", message, ...args);
483
594
  }
484
595
  }
596
+ warn(message, ...args) {
597
+ this.logToConsole("warn", "", message, ...args);
598
+ this.debug(`[WARN] ${message}`, ...args);
599
+ }
600
+ getLogPath() {
601
+ return this.logFilePath;
602
+ }
485
603
  logToConsole(level, prefix, message, ...args) {
486
604
  switch (level) {
487
605
  case "debug": {
@@ -642,38 +760,13 @@ z$1.object({
642
760
  ]),
643
761
  createdAt: z$1.number()
644
762
  });
645
- z$1.object({
646
- createdAt: z$1.number(),
647
- id: z$1.string(),
648
- seq: z$1.number(),
649
- updatedAt: z$1.number(),
650
- metadata: z$1.any(),
651
- metadataVersion: z$1.number(),
652
- agentState: z$1.any().nullable(),
653
- agentStateVersion: z$1.number(),
654
- // Connectivity tracking (from server)
655
- connectivityStatus: z$1.union([
656
- z$1.enum(["neverConnected", "online", "offline"]),
657
- z$1.string()
658
- // Forward compatibility
659
- ]).optional(),
660
- connectivityStatusSince: z$1.number().optional(),
661
- connectivityStatusReason: z$1.string().optional(),
662
- // State tracking (from server)
663
- state: z$1.union([
664
- z$1.enum(["running", "archiveRequested", "archived"]),
665
- z$1.string()
666
- // Forward compatibility
667
- ]).optional(),
668
- stateSince: z$1.number().optional(),
669
- stateReason: z$1.string().optional()
670
- });
671
763
  z$1.object({
672
764
  host: z$1.string(),
673
765
  platform: z$1.string(),
674
766
  happyCliVersion: z$1.string(),
675
767
  homeDir: z$1.string(),
676
- happyHomeDir: z$1.string()
768
+ happyHomeDir: z$1.string(),
769
+ happyLibDir: z$1.string()
677
770
  });
678
771
  z$1.object({
679
772
  status: z$1.union([
@@ -691,37 +784,6 @@ z$1.object({
691
784
  // Forward compatibility
692
785
  ]).optional()
693
786
  });
694
- z$1.object({
695
- id: z$1.string(),
696
- metadata: z$1.any(),
697
- // Decrypted MachineMetadata
698
- metadataVersion: z$1.number(),
699
- daemonState: z$1.any().nullable(),
700
- // Decrypted DaemonState
701
- daemonStateVersion: z$1.number(),
702
- // We don't really care about these on the CLI for now
703
- // ApiMachineClient will not sync these
704
- active: z$1.boolean(),
705
- activeAt: z$1.number(),
706
- createdAt: z$1.number(),
707
- updatedAt: z$1.number(),
708
- // Connectivity tracking (from server)
709
- connectivityStatus: z$1.union([
710
- z$1.enum(["neverConnected", "online", "offline"]),
711
- z$1.string()
712
- // Forward compatibility
713
- ]).optional(),
714
- connectivityStatusSince: z$1.number().optional(),
715
- connectivityStatusReason: z$1.string().optional(),
716
- // State tracking (from server)
717
- state: z$1.union([
718
- z$1.enum(["running", "archiveRequested", "archived"]),
719
- z$1.string()
720
- // Forward compatibility
721
- ]).optional(),
722
- stateSince: z$1.number().optional(),
723
- stateReason: z$1.string().optional()
724
- });
725
787
  z$1.object({
726
788
  content: SessionMessageContentSchema,
727
789
  createdAt: z$1.number(),
@@ -845,12 +907,14 @@ class AsyncLock {
845
907
  class RpcHandlerManager {
846
908
  handlers = /* @__PURE__ */ new Map();
847
909
  scopePrefix;
848
- secret;
910
+ encryptionKey;
911
+ encryptionVariant;
849
912
  logger;
850
913
  socket = null;
851
914
  constructor(config) {
852
915
  this.scopePrefix = config.scopePrefix;
853
- this.secret = config.secret;
916
+ this.encryptionKey = config.encryptionKey;
917
+ this.encryptionVariant = config.encryptionVariant;
854
918
  this.logger = config.logger || ((msg, data) => logger.debug(msg, data));
855
919
  }
856
920
  /**
@@ -876,20 +940,19 @@ class RpcHandlerManager {
876
940
  if (!handler) {
877
941
  this.logger("[RPC] [ERROR] Method not found", { method: request.method });
878
942
  const errorResponse = { error: "Method not found" };
879
- const encryptedError = encodeBase64(encrypt(errorResponse, this.secret));
943
+ const encryptedError = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
880
944
  return encryptedError;
881
945
  }
882
- const decryptedParams = decrypt(decodeBase64(request.params), this.secret);
946
+ const decryptedParams = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(request.params));
883
947
  const result = await handler(decryptedParams);
884
- const encryptedResponse = encodeBase64(encrypt(result, this.secret));
948
+ const encryptedResponse = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, result));
885
949
  return encryptedResponse;
886
950
  } catch (error) {
887
951
  this.logger("[RPC] [ERROR] Error handling request", { error });
888
952
  const errorResponse = {
889
953
  error: error instanceof Error ? error.message : "Unknown error"
890
954
  };
891
- const encryptedError = encodeBase64(encrypt(errorResponse, this.secret));
892
- return encryptedError;
955
+ return encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
893
956
  }
894
957
  }
895
958
  onSocketConnect(socket) {
@@ -937,7 +1000,7 @@ function projectPath() {
937
1000
  return path;
938
1001
  }
939
1002
 
940
- function run(args, options) {
1003
+ function run$1(args, options) {
941
1004
  const RUNNER_PATH = resolve(join$1(projectPath(), "scripts", "ripgrep_launcher.cjs"));
942
1005
  return new Promise((resolve2, reject) => {
943
1006
  const child = spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
@@ -965,6 +1028,44 @@ function run(args, options) {
965
1028
  });
966
1029
  }
967
1030
 
1031
+ function getBinaryPath() {
1032
+ const platformName = platform();
1033
+ const binaryName = platformName === "win32" ? "difft.exe" : "difft";
1034
+ return resolve(join$1(projectPath(), "tools", "unpacked", binaryName));
1035
+ }
1036
+ function run(args, options) {
1037
+ const binaryPath = getBinaryPath();
1038
+ return new Promise((resolve2, reject) => {
1039
+ const child = spawn(binaryPath, args, {
1040
+ stdio: ["pipe", "pipe", "pipe"],
1041
+ cwd: options?.cwd,
1042
+ env: {
1043
+ ...process.env,
1044
+ // Force color output when needed
1045
+ FORCE_COLOR: "1"
1046
+ }
1047
+ });
1048
+ let stdout = "";
1049
+ let stderr = "";
1050
+ child.stdout.on("data", (data) => {
1051
+ stdout += data.toString();
1052
+ });
1053
+ child.stderr.on("data", (data) => {
1054
+ stderr += data.toString();
1055
+ });
1056
+ child.on("close", (code) => {
1057
+ resolve2({
1058
+ exitCode: code || 0,
1059
+ stdout,
1060
+ stderr
1061
+ });
1062
+ });
1063
+ child.on("error", (err) => {
1064
+ reject(err);
1065
+ });
1066
+ });
1067
+ }
1068
+
968
1069
  const execAsync = promisify(exec);
969
1070
  function registerCommonHandlers(rpcHandlerManager) {
970
1071
  rpcHandlerManager.registerHandler("bash", async (data) => {
@@ -1159,7 +1260,7 @@ function registerCommonHandlers(rpcHandlerManager) {
1159
1260
  rpcHandlerManager.registerHandler("ripgrep", async (data) => {
1160
1261
  logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
1161
1262
  try {
1162
- const result = await run(data.args, { cwd: data.cwd });
1263
+ const result = await run$1(data.args, { cwd: data.cwd });
1163
1264
  return {
1164
1265
  success: true,
1165
1266
  exitCode: result.exitCode,
@@ -1174,11 +1275,28 @@ function registerCommonHandlers(rpcHandlerManager) {
1174
1275
  };
1175
1276
  }
1176
1277
  });
1278
+ rpcHandlerManager.registerHandler("difftastic", async (data) => {
1279
+ logger.debug("Difftastic request with args:", data.args, "cwd:", data.cwd);
1280
+ try {
1281
+ const result = await run(data.args, { cwd: data.cwd });
1282
+ return {
1283
+ success: true,
1284
+ exitCode: result.exitCode,
1285
+ stdout: result.stdout.toString(),
1286
+ stderr: result.stderr.toString()
1287
+ };
1288
+ } catch (error) {
1289
+ logger.debug("Failed to run difftastic:", error);
1290
+ return {
1291
+ success: false,
1292
+ error: error instanceof Error ? error.message : "Failed to run difftastic"
1293
+ };
1294
+ }
1295
+ });
1177
1296
  }
1178
1297
 
1179
1298
  class ApiSessionClient extends EventEmitter {
1180
1299
  token;
1181
- secret;
1182
1300
  sessionId;
1183
1301
  metadata;
1184
1302
  metadataVersion;
@@ -1190,18 +1308,22 @@ class ApiSessionClient extends EventEmitter {
1190
1308
  rpcHandlerManager;
1191
1309
  agentStateLock = new AsyncLock();
1192
1310
  metadataLock = new AsyncLock();
1193
- constructor(token, secret, session) {
1311
+ encryptionKey;
1312
+ encryptionVariant;
1313
+ constructor(token, session) {
1194
1314
  super();
1195
1315
  this.token = token;
1196
- this.secret = secret;
1197
1316
  this.sessionId = session.id;
1198
1317
  this.metadata = session.metadata;
1199
1318
  this.metadataVersion = session.metadataVersion;
1200
1319
  this.agentState = session.agentState;
1201
1320
  this.agentStateVersion = session.agentStateVersion;
1321
+ this.encryptionKey = session.encryptionKey;
1322
+ this.encryptionVariant = session.encryptionVariant;
1202
1323
  this.rpcHandlerManager = new RpcHandlerManager({
1203
1324
  scopePrefix: this.sessionId,
1204
- secret: this.secret,
1325
+ encryptionKey: this.encryptionKey,
1326
+ encryptionVariant: this.encryptionVariant,
1205
1327
  logger: (msg, data) => logger.debug(msg, data)
1206
1328
  });
1207
1329
  registerCommonHandlers(this.rpcHandlerManager);
@@ -1243,7 +1365,7 @@ class ApiSessionClient extends EventEmitter {
1243
1365
  return;
1244
1366
  }
1245
1367
  if (data.body.t === "new-message" && data.body.message.content.t === "encrypted") {
1246
- const body = decrypt(decodeBase64(data.body.message.content.c), this.secret);
1368
+ const body = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.message.content.c));
1247
1369
  logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
1248
1370
  const userResult = UserMessageSchema.safeParse(body);
1249
1371
  if (userResult.success) {
@@ -1257,11 +1379,11 @@ class ApiSessionClient extends EventEmitter {
1257
1379
  }
1258
1380
  } else if (data.body.t === "update-session") {
1259
1381
  if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
1260
- this.metadata = decrypt(decodeBase64(data.body.metadata.value), this.secret);
1382
+ this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.metadata.value));
1261
1383
  this.metadataVersion = data.body.metadata.version;
1262
1384
  }
1263
1385
  if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
1264
- this.agentState = data.body.agentState.value ? decrypt(decodeBase64(data.body.agentState.value), this.secret) : null;
1386
+ this.agentState = data.body.agentState.value ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.agentState.value)) : null;
1265
1387
  this.agentStateVersion = data.body.agentState.version;
1266
1388
  }
1267
1389
  } else if (data.body.t === "update-machine") {
@@ -1315,7 +1437,7 @@ class ApiSessionClient extends EventEmitter {
1315
1437
  };
1316
1438
  }
1317
1439
  logger.debugLargeJson("[SOCKET] Sending message through socket:", content);
1318
- const encrypted = encodeBase64(encrypt(content, this.secret));
1440
+ const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
1319
1441
  this.socket.emit("message", {
1320
1442
  sid: this.sessionId,
1321
1443
  message: encrypted
@@ -1337,6 +1459,24 @@ class ApiSessionClient extends EventEmitter {
1337
1459
  }));
1338
1460
  }
1339
1461
  }
1462
+ sendCodexMessage(body) {
1463
+ let content = {
1464
+ role: "agent",
1465
+ content: {
1466
+ type: "codex",
1467
+ data: body
1468
+ // This wraps the entire Claude message
1469
+ },
1470
+ meta: {
1471
+ sentFrom: "cli"
1472
+ }
1473
+ };
1474
+ const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
1475
+ this.socket.emit("message", {
1476
+ sid: this.sessionId,
1477
+ message: encrypted
1478
+ });
1479
+ }
1340
1480
  sendSessionEvent(event, id) {
1341
1481
  let content = {
1342
1482
  role: "agent",
@@ -1346,7 +1486,7 @@ class ApiSessionClient extends EventEmitter {
1346
1486
  data: event
1347
1487
  }
1348
1488
  };
1349
- const encrypted = encodeBase64(encrypt(content, this.secret));
1489
+ const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
1350
1490
  this.socket.emit("message", {
1351
1491
  sid: this.sessionId,
1352
1492
  message: encrypted
@@ -1406,14 +1546,14 @@ class ApiSessionClient extends EventEmitter {
1406
1546
  this.metadataLock.inLock(async () => {
1407
1547
  await backoff(async () => {
1408
1548
  let updated = handler(this.metadata);
1409
- const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(updated, this.secret)) });
1549
+ const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) });
1410
1550
  if (answer.result === "success") {
1411
- this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
1551
+ this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
1412
1552
  this.metadataVersion = answer.version;
1413
1553
  } else if (answer.result === "version-mismatch") {
1414
1554
  if (answer.version > this.metadataVersion) {
1415
1555
  this.metadataVersion = answer.version;
1416
- this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
1556
+ this.metadata = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata));
1417
1557
  }
1418
1558
  throw new Error("Metadata version mismatch");
1419
1559
  } else if (answer.result === "error") ;
@@ -1429,15 +1569,15 @@ class ApiSessionClient extends EventEmitter {
1429
1569
  this.agentStateLock.inLock(async () => {
1430
1570
  await backoff(async () => {
1431
1571
  let updated = handler(this.agentState || {});
1432
- const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(updated, this.secret)) : null });
1572
+ const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
1433
1573
  if (answer.result === "success") {
1434
- this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
1574
+ this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
1435
1575
  this.agentStateVersion = answer.version;
1436
1576
  logger.debug("Agent state updated", this.agentState);
1437
1577
  } else if (answer.result === "version-mismatch") {
1438
1578
  if (answer.version > this.agentStateVersion) {
1439
1579
  this.agentStateVersion = answer.version;
1440
- this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
1580
+ this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
1441
1581
  }
1442
1582
  throw new Error("Agent state version mismatch");
1443
1583
  } else if (answer.result === "error") ;
@@ -1461,18 +1601,19 @@ class ApiSessionClient extends EventEmitter {
1461
1601
  });
1462
1602
  }
1463
1603
  async close() {
1604
+ logger.debug("[API] socket.close() called");
1464
1605
  this.socket.close();
1465
1606
  }
1466
1607
  }
1467
1608
 
1468
1609
  class ApiMachineClient {
1469
- constructor(token, secret, machine) {
1610
+ constructor(token, machine) {
1470
1611
  this.token = token;
1471
- this.secret = secret;
1472
1612
  this.machine = machine;
1473
1613
  this.rpcHandlerManager = new RpcHandlerManager({
1474
1614
  scopePrefix: this.machine.id,
1475
- secret: this.secret,
1615
+ encryptionKey: this.machine.encryptionKey,
1616
+ encryptionVariant: this.machine.encryptionVariant,
1476
1617
  logger: (msg, data) => logger.debug(msg, data)
1477
1618
  });
1478
1619
  registerCommonHandlers(this.rpcHandlerManager);
@@ -1533,17 +1674,17 @@ class ApiMachineClient {
1533
1674
  const updated = handler(this.machine.metadata);
1534
1675
  const answer = await this.socket.emitWithAck("machine-update-metadata", {
1535
1676
  machineId: this.machine.id,
1536
- metadata: encodeBase64(encrypt(updated, this.secret)),
1677
+ metadata: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
1537
1678
  expectedVersion: this.machine.metadataVersion
1538
1679
  });
1539
1680
  if (answer.result === "success") {
1540
- this.machine.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
1681
+ this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.metadata));
1541
1682
  this.machine.metadataVersion = answer.version;
1542
1683
  logger.debug("[API MACHINE] Metadata updated successfully");
1543
1684
  } else if (answer.result === "version-mismatch") {
1544
1685
  if (answer.version > this.machine.metadataVersion) {
1545
1686
  this.machine.metadataVersion = answer.version;
1546
- this.machine.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
1687
+ this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.metadata));
1547
1688
  }
1548
1689
  throw new Error("Metadata version mismatch");
1549
1690
  }
@@ -1558,17 +1699,17 @@ class ApiMachineClient {
1558
1699
  const updated = handler(this.machine.daemonState);
1559
1700
  const answer = await this.socket.emitWithAck("machine-update-state", {
1560
1701
  machineId: this.machine.id,
1561
- daemonState: encodeBase64(encrypt(updated, this.secret)),
1702
+ daemonState: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
1562
1703
  expectedVersion: this.machine.daemonStateVersion
1563
1704
  });
1564
1705
  if (answer.result === "success") {
1565
- this.machine.daemonState = decrypt(decodeBase64(answer.daemonState), this.secret);
1706
+ this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
1566
1707
  this.machine.daemonStateVersion = answer.version;
1567
1708
  logger.debug("[API MACHINE] Daemon state updated successfully");
1568
1709
  } else if (answer.result === "version-mismatch") {
1569
1710
  if (answer.version > this.machine.daemonStateVersion) {
1570
1711
  this.machine.daemonStateVersion = answer.version;
1571
- this.machine.daemonState = decrypt(decodeBase64(answer.daemonState), this.secret);
1712
+ this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
1572
1713
  }
1573
1714
  throw new Error("Daemon state version mismatch");
1574
1715
  }
@@ -1615,12 +1756,12 @@ class ApiMachineClient {
1615
1756
  const update = data.body;
1616
1757
  if (update.metadata) {
1617
1758
  logger.debug("[API MACHINE] Received external metadata update");
1618
- this.machine.metadata = decrypt(decodeBase64(update.metadata.value), this.secret);
1759
+ this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(update.metadata.value));
1619
1760
  this.machine.metadataVersion = update.metadata.version;
1620
1761
  }
1621
1762
  if (update.daemonState) {
1622
1763
  logger.debug("[API MACHINE] Received external daemon state update");
1623
- this.machine.daemonState = decrypt(decodeBase64(update.daemonState.value), this.secret);
1764
+ this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(update.daemonState.value));
1624
1765
  this.machine.daemonStateVersion = update.daemonState.version;
1625
1766
  }
1626
1767
  } else {
@@ -1792,46 +1933,62 @@ class PushNotificationClient {
1792
1933
  }
1793
1934
 
1794
1935
  class ApiClient {
1795
- token;
1796
- secret;
1936
+ static async create(credential) {
1937
+ return new ApiClient(credential);
1938
+ }
1939
+ credential;
1797
1940
  pushClient;
1798
- constructor(token, secret) {
1799
- this.token = token;
1800
- this.secret = secret;
1801
- this.pushClient = new PushNotificationClient(token);
1941
+ constructor(credential) {
1942
+ this.credential = credential;
1943
+ this.pushClient = new PushNotificationClient(credential.token, configuration.serverUrl);
1802
1944
  }
1803
1945
  /**
1804
1946
  * Create a new session or load existing one with the given tag
1805
1947
  */
1806
1948
  async getOrCreateSession(opts) {
1949
+ let dataEncryptionKey = null;
1950
+ let encryptionKey;
1951
+ let encryptionVariant;
1952
+ if (this.credential.encryption.type === "dataKey") {
1953
+ encryptionKey = getRandomBytes(32);
1954
+ encryptionVariant = "dataKey";
1955
+ let encryptedDataKey = libsodiumEncryptForPublicKey(encryptionKey, this.credential.encryption.publicKey);
1956
+ dataEncryptionKey = new Uint8Array(encryptedDataKey.length + 1);
1957
+ dataEncryptionKey.set([0], 0);
1958
+ dataEncryptionKey.set(encryptedDataKey, 1);
1959
+ } else {
1960
+ encryptionKey = this.credential.encryption.secret;
1961
+ encryptionVariant = "legacy";
1962
+ }
1807
1963
  try {
1808
1964
  const response = await axios.post(
1809
1965
  `${configuration.serverUrl}/v1/sessions`,
1810
1966
  {
1811
1967
  tag: opts.tag,
1812
- metadata: encodeBase64(encrypt(opts.metadata, this.secret)),
1813
- agentState: opts.state ? encodeBase64(encrypt(opts.state, this.secret)) : null
1968
+ metadata: encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.metadata)),
1969
+ agentState: opts.state ? encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.state)) : null,
1970
+ dataEncryptionKey: dataEncryptionKey ? encodeBase64(dataEncryptionKey) : null
1814
1971
  },
1815
1972
  {
1816
1973
  headers: {
1817
- "Authorization": `Bearer ${this.token}`,
1974
+ "Authorization": `Bearer ${this.credential.token}`,
1818
1975
  "Content-Type": "application/json"
1819
1976
  },
1820
- timeout: 5e3
1821
- // 5 second timeout
1977
+ timeout: 6e4
1978
+ // 1 minute timeout for very bad network connections
1822
1979
  }
1823
1980
  );
1824
1981
  logger.debug(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
1825
1982
  let raw = response.data.session;
1826
1983
  let session = {
1827
1984
  id: raw.id,
1828
- createdAt: raw.createdAt,
1829
- updatedAt: raw.updatedAt,
1830
1985
  seq: raw.seq,
1831
- metadata: decrypt(decodeBase64(raw.metadata), this.secret),
1986
+ metadata: decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.metadata)),
1832
1987
  metadataVersion: raw.metadataVersion,
1833
- agentState: raw.agentState ? decrypt(decodeBase64(raw.agentState), this.secret) : null,
1834
- agentStateVersion: raw.agentStateVersion
1988
+ agentState: raw.agentState ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.agentState)) : null,
1989
+ agentStateVersion: raw.agentStateVersion,
1990
+ encryptionKey,
1991
+ encryptionVariant
1835
1992
  };
1836
1993
  return session;
1837
1994
  } catch (error) {
@@ -1839,54 +1996,40 @@ class ApiClient {
1839
1996
  throw new Error(`Failed to get or create session: ${error instanceof Error ? error.message : "Unknown error"}`);
1840
1997
  }
1841
1998
  }
1842
- /**
1843
- * Get machine by ID from the server
1844
- * Returns the current machine state from the server with decrypted metadata and daemonState
1845
- */
1846
- async getMachine(machineId) {
1847
- const response = await axios.get(`${configuration.serverUrl}/v1/machines/${machineId}`, {
1848
- headers: {
1849
- "Authorization": `Bearer ${this.token}`,
1850
- "Content-Type": "application/json"
1851
- },
1852
- timeout: 2e3
1853
- });
1854
- const raw = response.data.machine;
1855
- if (!raw) {
1856
- return null;
1857
- }
1858
- logger.debug(`[API] Machine ${machineId} fetched from server`);
1859
- const machine = {
1860
- id: raw.id,
1861
- metadata: raw.metadata ? decrypt(decodeBase64(raw.metadata), this.secret) : null,
1862
- metadataVersion: raw.metadataVersion || 0,
1863
- daemonState: raw.daemonState ? decrypt(decodeBase64(raw.daemonState), this.secret) : null,
1864
- daemonStateVersion: raw.daemonStateVersion || 0,
1865
- active: raw.active,
1866
- activeAt: raw.activeAt,
1867
- createdAt: raw.createdAt,
1868
- updatedAt: raw.updatedAt
1869
- };
1870
- return machine;
1871
- }
1872
1999
  /**
1873
2000
  * Register or update machine with the server
1874
2001
  * Returns the current machine state from the server with decrypted metadata and daemonState
1875
2002
  */
1876
2003
  async getOrCreateMachine(opts) {
2004
+ let dataEncryptionKey = null;
2005
+ let encryptionKey;
2006
+ let encryptionVariant;
2007
+ if (this.credential.encryption.type === "dataKey") {
2008
+ encryptionVariant = "dataKey";
2009
+ encryptionKey = this.credential.encryption.machineKey;
2010
+ let encryptedDataKey = libsodiumEncryptForPublicKey(this.credential.encryption.machineKey, this.credential.encryption.publicKey);
2011
+ dataEncryptionKey = new Uint8Array(encryptedDataKey.length + 1);
2012
+ dataEncryptionKey.set([0], 0);
2013
+ dataEncryptionKey.set(encryptedDataKey, 1);
2014
+ } else {
2015
+ encryptionKey = this.credential.encryption.secret;
2016
+ encryptionVariant = "legacy";
2017
+ }
1877
2018
  const response = await axios.post(
1878
2019
  `${configuration.serverUrl}/v1/machines`,
1879
2020
  {
1880
2021
  id: opts.machineId,
1881
- metadata: encodeBase64(encrypt(opts.metadata, this.secret)),
1882
- daemonState: opts.daemonState ? encodeBase64(encrypt(opts.daemonState, this.secret)) : void 0
2022
+ metadata: encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.metadata)),
2023
+ daemonState: opts.daemonState ? encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.daemonState)) : void 0,
2024
+ dataEncryptionKey: dataEncryptionKey ? encodeBase64(dataEncryptionKey) : void 0
1883
2025
  },
1884
2026
  {
1885
2027
  headers: {
1886
- "Authorization": `Bearer ${this.token}`,
2028
+ "Authorization": `Bearer ${this.credential.token}`,
1887
2029
  "Content-Type": "application/json"
1888
2030
  },
1889
- timeout: 5e3
2031
+ timeout: 6e4
2032
+ // 1 minute timeout for very bad network connections
1890
2033
  }
1891
2034
  );
1892
2035
  if (response.status !== 200) {
@@ -1898,22 +2041,20 @@ class ApiClient {
1898
2041
  logger.debug(`[API] Machine ${opts.machineId} registered/updated with server`);
1899
2042
  const machine = {
1900
2043
  id: raw.id,
1901
- metadata: raw.metadata ? decrypt(decodeBase64(raw.metadata), this.secret) : null,
2044
+ encryptionKey,
2045
+ encryptionVariant,
2046
+ metadata: raw.metadata ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.metadata)) : null,
1902
2047
  metadataVersion: raw.metadataVersion || 0,
1903
- daemonState: raw.daemonState ? decrypt(decodeBase64(raw.daemonState), this.secret) : null,
1904
- daemonStateVersion: raw.daemonStateVersion || 0,
1905
- active: raw.active,
1906
- activeAt: raw.activeAt,
1907
- createdAt: raw.createdAt,
1908
- updatedAt: raw.updatedAt
2048
+ daemonState: raw.daemonState ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.daemonState)) : null,
2049
+ daemonStateVersion: raw.daemonStateVersion || 0
1909
2050
  };
1910
2051
  return machine;
1911
2052
  }
1912
2053
  sessionSyncClient(session) {
1913
- return new ApiSessionClient(this.token, this.secret, session);
2054
+ return new ApiSessionClient(this.credential.token, session);
1914
2055
  }
1915
2056
  machineSyncClient(machine) {
1916
- return new ApiMachineClient(this.token, this.secret, machine);
2057
+ return new ApiMachineClient(this.credential.token, machine);
1917
2058
  }
1918
2059
  push() {
1919
2060
  return this.pushClient;
@@ -1931,7 +2072,7 @@ class ApiClient {
1931
2072
  },
1932
2073
  {
1933
2074
  headers: {
1934
- "Authorization": `Bearer ${this.token}`,
2075
+ "Authorization": `Bearer ${this.credential.token}`,
1935
2076
  "Content-Type": "application/json"
1936
2077
  },
1937
2078
  timeout: 5e3
@@ -1996,4 +2137,4 @@ const RawJSONLinesSchema = z$1.discriminatedUnion("type", [
1996
2137
  }).passthrough()
1997
2138
  ]);
1998
2139
 
1999
- export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e, clearDaemonState as f, packageJson as g, readSettings as h, readCredentials as i, encodeBase64 as j, encodeBase64Url as k, logger as l, decodeBase64 as m, acquireDaemonLock as n, writeDaemonState as o, projectPath as p, releaseDaemonLock as q, readDaemonState as r, clearCredentials as s, clearMachineId as t, updateSettings as u, getLatestDaemonLog as v, writeCredentials as w };
2140
+ export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, packageJson as b, configuration as c, backoff as d, delay as e, AsyncLock as f, readDaemonState as g, clearDaemonState as h, readCredentials as i, encodeBase64 as j, encodeBase64Url as k, logger as l, decodeBase64 as m, writeCredentialsDataKey as n, acquireDaemonLock as o, projectPath as p, writeDaemonState as q, readSettings as r, releaseDaemonLock as s, clearCredentials as t, updateSettings as u, clearMachineId as v, writeCredentialsLegacy as w, getLatestDaemonLog as x };