@zonease/happy 0.6.38 → 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.
- package/bin/happy.mjs +10 -0
- package/dist/{types-DvTsFaqR.cjs → api-C7FyJfZ1.cjs} +400 -1964
- package/dist/{types-BWpRU1VN.mjs → api-D6WL40-U.mjs} +220 -1729
- package/dist/auth-C7ZScWEw.cjs +274 -0
- package/dist/auth-DCsIoZLT.cjs +183 -0
- package/dist/auth-DO9GexAR.mjs +181 -0
- package/dist/auth-DxJ3VvrX.mjs +270 -0
- package/dist/browser-AoVGRX7y.mjs +20 -0
- package/dist/browser-DzgO4oaD.cjs +22 -0
- package/dist/claudeLocal-C6BdzhDx.cjs +352 -0
- package/dist/claudeLocal-CWNGpJ0-.mjs +344 -0
- package/dist/codex/happyMcpStdioBridge.cjs +4 -1
- package/dist/codex/happyMcpStdioBridge.mjs +4 -1
- package/dist/common-BdNgghUJ.mjs +75 -0
- package/dist/common-CZvSRU4p.cjs +83 -0
- package/dist/{config-0Xnc8ARM.cjs → config-CclB3QW9.cjs} +21 -34
- package/dist/{config-_GW4YBCP.mjs → config-DZk-mJ0K.mjs} +7 -20
- package/dist/connect-BXLUKJ5P.mjs +679 -0
- package/dist/connect-k9CqDJ-T.cjs +681 -0
- package/dist/{constants-B4x1K7by.cjs → constants-E_UWYTo3.cjs} +2 -2
- package/dist/{constants-BO9OAFOT.mjs → constants-pmXZGibX.mjs} +1 -1
- package/dist/controlClient-B23M-WPp.cjs +177 -0
- package/dist/controlClient-DB5tDU7V.mjs +167 -0
- package/dist/deviceKeys-BTdo34Uz.mjs +175 -0
- package/dist/deviceKeys-C540DcXl.cjs +182 -0
- package/dist/diagnose-B3D1a_v0.cjs +60 -0
- package/dist/diagnose-CuRKXxt1.mjs +58 -0
- package/dist/doctor-BQGLhrcI.cjs +214 -0
- package/dist/doctor-C08m5Z9f.cjs +78 -0
- package/dist/doctor-CU4JLGQ1.mjs +211 -0
- package/dist/doctor-ON7pN5G5.mjs +74 -0
- package/dist/downloadImage-DZXI3mXL.mjs +887 -0
- package/dist/downloadImage-cQ1Z7hkC.cjs +897 -0
- package/dist/enroll-B55BQR56.mjs +113 -0
- package/dist/enroll-Bo-CNio1.cjs +115 -0
- package/dist/evidence-DQp8QWcv.cjs +129 -0
- package/dist/evidence-DTYRAaOZ.mjs +127 -0
- package/dist/formatImageMessage-CixDYY6G.cjs +46 -0
- package/dist/formatImageMessage-DbKSSjMx.mjs +43 -0
- package/dist/github-DKcyF1zp.mjs +57 -0
- package/dist/github-h9GhJJ_C.cjs +59 -0
- package/dist/index-CDTNgfUf.cjs +578 -0
- package/dist/index-CPKdvi7J.mjs +554 -0
- package/dist/index.cjs +812 -40
- package/dist/index.mjs +813 -40
- package/dist/install-B1e6rdy7.mjs +94 -0
- package/dist/install-CgUPGIoF.mjs +95 -0
- package/dist/install-D7Ir7ciZ.cjs +96 -0
- package/dist/install-DSDufaPd.cjs +98 -0
- package/dist/lib.cjs +18 -12
- package/dist/lib.d.cts +77 -0
- package/dist/lib.d.mts +77 -0
- package/dist/lib.mjs +13 -7
- package/dist/{loadableToolOutput-va3RLplh.mjs → loadableToolOutput-Bfei9wPq.mjs} +4 -2
- package/dist/{loadableToolOutput-5vdQB98M.cjs → loadableToolOutput-CbLGtvST.cjs} +63 -61
- package/dist/mcpToolSchemas-CjeTfrAs.mjs +62 -0
- package/dist/mcpToolSchemas-DK42yoZ_.cjs +64 -0
- package/dist/persistence-CBTtC_dm.cjs +996 -0
- package/dist/persistence-DKfF81pd.mjs +944 -0
- package/dist/projectPath-BTBIMB57.mjs +10 -0
- package/dist/projectPath-ChLhdOyt.cjs +13 -0
- package/dist/prompt-BTWDZZO0.cjs +25 -0
- package/dist/prompt-BsAZsPbo.mjs +22 -0
- package/dist/release-BMEzNONE.mjs +168 -0
- package/dist/release-EhOJeAZe.cjs +171 -0
- package/dist/run-BVZsMNwN.mjs +2164 -0
- package/dist/run-CURx7LhK.cjs +2188 -0
- package/dist/runClaude-6Gi0sYQy.mjs +3763 -0
- package/dist/runClaude-CL3Kt5ex.cjs +3766 -0
- package/dist/{runCodex-DiKzBf9q.mjs → runCodex-ClwEgJ7M.mjs} +36 -20
- package/dist/{runCodex-skq0j05d.cjs → runCodex-YKyVBkJ3.cjs} +169 -153
- package/dist/{runGemini-BeqrVq8R.mjs → runGemini-BPWxVr60.mjs} +34 -17
- package/dist/{runGemini-CVp-CEIh.cjs → runGemini-Xz2W1spn.cjs} +212 -195
- package/dist/{runOneShot-ltCpHbBB.mjs → runOneShot-CdCWiiLr.mjs} +14 -38
- package/dist/{runOneShot-BQNkNW61.cjs → runOneShot-DjZyCvDI.cjs} +31 -55
- package/dist/{serve-BNdoEDVx.mjs → serve-B48JttQ3.mjs} +12 -35
- package/dist/{serve-BECdtivY.cjs → serve-B8vaWQA0.cjs} +35 -58
- package/dist/session-BXboDn2V.mjs +108 -0
- package/dist/session-BZO25zxp.cjs +110 -0
- package/dist/sessionKeyCache-B6cGadYL.mjs +33 -0
- package/dist/sessionKeyCache-CDmexJ9p.cjs +36 -0
- package/dist/sessionList-CgAYZpNj.mjs +51 -0
- package/dist/sessionList-lQ-nqEhc.cjs +54 -0
- package/dist/sessionSummary-CcORiQSq.cjs +393 -0
- package/dist/sessionSummary-DCWQoaDP.mjs +387 -0
- package/dist/spawnHappyCLI-KiazE3kL.cjs +70 -0
- package/dist/spawnHappyCLI-yBIP_Oqi.mjs +66 -0
- package/dist/ssh-q5uZhqBf.mjs +359 -0
- package/dist/ssh-yMllSHu5.cjs +365 -0
- package/dist/systemdScope-BeOtAkhk.mjs +25 -0
- package/dist/systemdScope-DW3-UoKB.cjs +29 -0
- package/dist/task-_nr7J19j.cjs +167 -0
- package/dist/task-wNX1N2i4.mjs +165 -0
- package/dist/time-Bj2HRkKH.cjs +33 -0
- package/dist/time-DgsBLJA_.mjs +29 -0
- package/dist/trimIdent-4gvk9Wk6.cjs +22 -0
- package/dist/trimIdent-BAjmv_TG.mjs +20 -0
- package/dist/types-BoJh4Qfv.mjs +57 -0
- package/dist/types-DWeB7aqh.cjs +59 -0
- package/dist/uninstall-Buaz7d-X.cjs +77 -0
- package/dist/uninstall-Dxf6_4mQ.mjs +75 -0
- package/dist/update-CGlIfa8e.cjs +111 -0
- package/dist/update-CnnoX2cV.mjs +109 -0
- package/dist/workflowCommon-B0d01Y_a.mjs +56 -0
- package/dist/workflowCommon-Bl568-rt.cjs +62 -0
- package/dist/workflowReports-BEWuQesA.cjs +113 -0
- package/dist/workflowReports-Dd8-4-8f.mjs +108 -0
- package/package.json +1 -1
- package/dist/index-BbCDDvoB.cjs +0 -11376
- package/dist/index-vvZXdswy.mjs +0 -11336
- package/dist/mcpToolSchemas-CdPXJQmb.mjs +0 -175
- package/dist/mcpToolSchemas-f-kttjFw.cjs +0 -186
|
@@ -1,1050 +1,35 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var axios = require('axios');
|
|
4
|
-
var
|
|
5
|
-
var fs$1 = require('fs');
|
|
6
|
-
var fs = require('node:fs');
|
|
7
|
-
var os = require('node:os');
|
|
8
|
-
var path = require('node:path');
|
|
4
|
+
var logger = require('./persistence-CBTtC_dm.cjs');
|
|
9
5
|
var node_events = require('node:events');
|
|
10
6
|
var socket_ioClient = require('socket.io-client');
|
|
11
7
|
var z = require('zod');
|
|
12
|
-
|
|
8
|
+
require('./index-CDTNgfUf.cjs');
|
|
9
|
+
var time = require('./time-Bj2HRkKH.cjs');
|
|
13
10
|
var node_crypto = require('node:crypto');
|
|
14
|
-
var
|
|
11
|
+
var fs = require('node:fs');
|
|
12
|
+
var path = require('node:path');
|
|
15
13
|
var node_child_process = require('node:child_process');
|
|
16
14
|
var promises = require('node:fs/promises');
|
|
15
|
+
var os = require('node:os');
|
|
17
16
|
var node_util = require('node:util');
|
|
18
17
|
var child_process = require('child_process');
|
|
19
18
|
var util = require('util');
|
|
20
|
-
var fs$
|
|
19
|
+
var fs$1 = require('fs/promises');
|
|
21
20
|
var path$1 = require('path');
|
|
22
|
-
var
|
|
21
|
+
var projectPath = require('./projectPath-ChLhdOyt.cjs');
|
|
23
22
|
var node_module = require('node:module');
|
|
24
23
|
var WebSocket = require('ws');
|
|
25
24
|
var crypto = require('crypto');
|
|
25
|
+
var tweetnacl = require('tweetnacl');
|
|
26
26
|
var base64 = require('@stablelib/base64');
|
|
27
27
|
var node_readline = require('node:readline');
|
|
28
28
|
var expoServerSdk = require('expo-server-sdk');
|
|
29
|
+
var sessionKeyCache = require('./sessionKeyCache-CDmexJ9p.cjs');
|
|
30
|
+
var chalk = require('chalk');
|
|
29
31
|
|
|
30
32
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
31
|
-
function _interopNamespaceDefault(e) {
|
|
32
|
-
var n = Object.create(null);
|
|
33
|
-
if (e) {
|
|
34
|
-
Object.keys(e).forEach(function (k) {
|
|
35
|
-
if (k !== 'default') {
|
|
36
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
37
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
38
|
-
enumerable: true,
|
|
39
|
-
get: function () { return e[k]; }
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
n.default = e;
|
|
45
|
-
return Object.freeze(n);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
var z__namespace = /*#__PURE__*/_interopNamespaceDefault(z);
|
|
49
|
-
|
|
50
|
-
const DEBUG_TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true"]);
|
|
51
|
-
function isDebug() {
|
|
52
|
-
return DEBUG_TRUE_VALUES.has(process.env.DEBUG?.toLowerCase() ?? "");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
var name = "@zonease/happy";
|
|
56
|
-
var version = "0.6.38";
|
|
57
|
-
var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
|
|
58
|
-
var author = "ZonEaseTech";
|
|
59
|
-
var license = "MIT";
|
|
60
|
-
var type = "module";
|
|
61
|
-
var homepage = "https://github.com/ZonEaseTech/happy-wfs";
|
|
62
|
-
var bugs = "https://github.com/ZonEaseTech/happy-wfs/issues";
|
|
63
|
-
var repository = {
|
|
64
|
-
type: "git",
|
|
65
|
-
url: "git+https://github.com/ZonEaseTech/happy-wfs.git",
|
|
66
|
-
directory: "packages/happy-cli"
|
|
67
|
-
};
|
|
68
|
-
var bin = {
|
|
69
|
-
happy: "./bin/happy.mjs",
|
|
70
|
-
"happy-mcp": "./bin/happy-mcp.mjs"
|
|
71
|
-
};
|
|
72
|
-
var main = "./dist/index.cjs";
|
|
73
|
-
var module$1 = "./dist/index.mjs";
|
|
74
|
-
var types = "./dist/index.d.cts";
|
|
75
|
-
var exports$1 = {
|
|
76
|
-
".": {
|
|
77
|
-
require: {
|
|
78
|
-
types: "./dist/index.d.cts",
|
|
79
|
-
"default": "./dist/index.cjs"
|
|
80
|
-
},
|
|
81
|
-
"import": {
|
|
82
|
-
types: "./dist/index.d.mts",
|
|
83
|
-
"default": "./dist/index.mjs"
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
"./lib": {
|
|
87
|
-
require: {
|
|
88
|
-
types: "./dist/lib.d.cts",
|
|
89
|
-
"default": "./dist/lib.cjs"
|
|
90
|
-
},
|
|
91
|
-
"import": {
|
|
92
|
-
types: "./dist/lib.d.mts",
|
|
93
|
-
"default": "./dist/lib.mjs"
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
"./codex/happyMcpStdioBridge": {
|
|
97
|
-
require: {
|
|
98
|
-
types: "./dist/codex/happyMcpStdioBridge.d.cts",
|
|
99
|
-
"default": "./dist/codex/happyMcpStdioBridge.cjs"
|
|
100
|
-
},
|
|
101
|
-
"import": {
|
|
102
|
-
types: "./dist/codex/happyMcpStdioBridge.d.mts",
|
|
103
|
-
"default": "./dist/codex/happyMcpStdioBridge.mjs"
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
var files = [
|
|
108
|
-
"dist",
|
|
109
|
-
"bin",
|
|
110
|
-
"scripts",
|
|
111
|
-
"package.json"
|
|
112
|
-
];
|
|
113
|
-
var scripts = {
|
|
114
|
-
"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",
|
|
115
|
-
typecheck: "tsc --noEmit",
|
|
116
|
-
build: "shx rm -rf dist && tsc --noEmit && pkgroll",
|
|
117
|
-
test: "$npm_execpath run build && vitest run",
|
|
118
|
-
"test:integration": "$npm_execpath run build && vitest run --config vitest.integration.config.ts",
|
|
119
|
-
start: "$npm_execpath run build && node ./bin/happy.mjs",
|
|
120
|
-
dev: "tsx src/index.ts",
|
|
121
|
-
"dev:local-server": "$npm_execpath run build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
122
|
-
"dev:integration-test-env": "$npm_execpath run build && tsx --env-file .env.integration-test src/index.ts",
|
|
123
|
-
prepublishOnly: "$npm_execpath run build && $npm_execpath test",
|
|
124
|
-
release: "$npm_execpath install && release-it",
|
|
125
|
-
postinstall: "node scripts/unpack-tools.cjs",
|
|
126
|
-
"// ==== Dev/Stable Variant Management ====": "",
|
|
127
|
-
stable: "node scripts/env-wrapper.cjs stable",
|
|
128
|
-
"dev:variant": "node scripts/env-wrapper.cjs dev",
|
|
129
|
-
"// ==== Stable Version Quick Commands ====": "",
|
|
130
|
-
"stable:daemon:start": "node scripts/env-wrapper.cjs stable daemon start",
|
|
131
|
-
"stable:daemon:stop": "node scripts/env-wrapper.cjs stable daemon stop",
|
|
132
|
-
"stable:daemon:status": "node scripts/env-wrapper.cjs stable daemon status",
|
|
133
|
-
"stable:auth": "node scripts/env-wrapper.cjs stable auth",
|
|
134
|
-
"// ==== Development Version Quick Commands ====": "",
|
|
135
|
-
"dev:daemon:start": "node scripts/env-wrapper.cjs dev daemon start",
|
|
136
|
-
"dev:daemon:stop": "node scripts/env-wrapper.cjs dev daemon stop",
|
|
137
|
-
"dev:daemon:status": "node scripts/env-wrapper.cjs dev daemon status",
|
|
138
|
-
"dev:auth": "node scripts/env-wrapper.cjs dev auth",
|
|
139
|
-
"// ==== Setup ====": "",
|
|
140
|
-
"setup:dev": "node scripts/setup-dev.cjs",
|
|
141
|
-
doctor: "node scripts/env-wrapper.cjs stable doctor",
|
|
142
|
-
"// ==== Development Linking ====": "",
|
|
143
|
-
"link:dev": "node scripts/link-dev.cjs",
|
|
144
|
-
"unlink:dev": "node scripts/link-dev.cjs unlink"
|
|
145
|
-
};
|
|
146
|
-
var dependencies = {
|
|
147
|
-
"@agentclientprotocol/sdk": "^0.14.1",
|
|
148
|
-
"@paralleldrive/cuid2": "^2.2.2",
|
|
149
|
-
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
150
|
-
"@stablelib/base64": "^2.0.1",
|
|
151
|
-
"@stablelib/hex": "^2.0.1",
|
|
152
|
-
"@types/cross-spawn": "^6.0.6",
|
|
153
|
-
"@types/http-proxy": "^1.17.17",
|
|
154
|
-
"@types/qrcode-terminal": "^0.12.2",
|
|
155
|
-
"@types/react": "^19.2.7",
|
|
156
|
-
"@types/tmp": "^0.2.6",
|
|
157
|
-
"@types/ws": "^8.18.1",
|
|
158
|
-
ai: "^5.0.107",
|
|
159
|
-
axios: "^1.13.2",
|
|
160
|
-
chalk: "^5.6.2",
|
|
161
|
-
"cross-spawn": "^7.0.6",
|
|
162
|
-
"expo-server-sdk": "^3.15.0",
|
|
163
|
-
fastify: "^5.6.2",
|
|
164
|
-
"fastify-type-provider-zod": "4.0.2",
|
|
165
|
-
"http-proxy": "^1.18.1",
|
|
166
|
-
"http-proxy-middleware": "^3.0.5",
|
|
167
|
-
ink: "^6.5.1",
|
|
168
|
-
open: "^10.2.0",
|
|
169
|
-
"ps-list": "^8.1.1",
|
|
170
|
-
"qrcode-terminal": "^0.12.0",
|
|
171
|
-
react: "^19.2.0",
|
|
172
|
-
"socket.io-client": "^4.8.1",
|
|
173
|
-
tar: "^7.5.2",
|
|
174
|
-
tmp: "^0.2.5",
|
|
175
|
-
tweetnacl: "^1.0.3",
|
|
176
|
-
ws: "^8.19.0",
|
|
177
|
-
zod: "3.25.76"
|
|
178
|
-
};
|
|
179
|
-
var devDependencies = {
|
|
180
|
-
"@eslint/compat": "^1",
|
|
181
|
-
"@types/node": ">=20",
|
|
182
|
-
"cross-env": "^10.1.0",
|
|
183
|
-
dotenv: "^16.6.1",
|
|
184
|
-
eslint: "^9",
|
|
185
|
-
"eslint-config-prettier": "^10",
|
|
186
|
-
pkgroll: "^2.14.2",
|
|
187
|
-
"release-it": "^19.0.6",
|
|
188
|
-
shx: "^0.3.3",
|
|
189
|
-
"ts-node": "^10",
|
|
190
|
-
tsx: "^4.20.6",
|
|
191
|
-
typescript: "5.9.3",
|
|
192
|
-
vitest: "^3.2.4",
|
|
193
|
-
"happy-wire": "*"
|
|
194
|
-
};
|
|
195
|
-
var resolutions = {
|
|
196
|
-
"whatwg-url": "14.2.0",
|
|
197
|
-
"parse-path": "7.0.3",
|
|
198
|
-
"@types/parse-path": "7.0.3"
|
|
199
|
-
};
|
|
200
|
-
var publishConfig = {
|
|
201
|
-
registry: "https://registry.npmjs.org",
|
|
202
|
-
access: "public"
|
|
203
|
-
};
|
|
204
|
-
var packageManager = "yarn@1.22.22";
|
|
205
|
-
var optionalDependencies = {
|
|
206
|
-
"node-pty": "^1.0.0",
|
|
207
|
-
"@zonease/happy-tools-arm64-darwin": "1.0.0",
|
|
208
|
-
"@zonease/happy-tools-x64-darwin": "1.0.0",
|
|
209
|
-
"@zonease/happy-tools-arm64-linux": "1.0.0",
|
|
210
|
-
"@zonease/happy-tools-x64-linux": "1.0.0",
|
|
211
|
-
"@zonease/happy-tools-x64-win32": "1.0.0",
|
|
212
|
-
"@zonease/happy-tools-arm64-win32": "1.0.0"
|
|
213
|
-
};
|
|
214
|
-
var packageJson = {
|
|
215
|
-
name: name,
|
|
216
|
-
version: version,
|
|
217
|
-
description: description,
|
|
218
|
-
author: author,
|
|
219
|
-
license: license,
|
|
220
|
-
type: type,
|
|
221
|
-
homepage: homepage,
|
|
222
|
-
bugs: bugs,
|
|
223
|
-
repository: repository,
|
|
224
|
-
bin: bin,
|
|
225
|
-
main: main,
|
|
226
|
-
module: module$1,
|
|
227
|
-
types: types,
|
|
228
|
-
exports: exports$1,
|
|
229
|
-
files: files,
|
|
230
|
-
scripts: scripts,
|
|
231
|
-
dependencies: dependencies,
|
|
232
|
-
devDependencies: devDependencies,
|
|
233
|
-
resolutions: resolutions,
|
|
234
|
-
publishConfig: publishConfig,
|
|
235
|
-
packageManager: packageManager,
|
|
236
|
-
optionalDependencies: optionalDependencies
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
class Configuration {
|
|
240
|
-
serverUrl;
|
|
241
|
-
webappUrl;
|
|
242
|
-
isDaemonProcess;
|
|
243
|
-
// Directories and paths (from persistence)
|
|
244
|
-
happyHomeDir;
|
|
245
|
-
logsDir;
|
|
246
|
-
settingsFile;
|
|
247
|
-
privateKeyFile;
|
|
248
|
-
daemonStateFile;
|
|
249
|
-
daemonLockFile;
|
|
250
|
-
sessionKeysDir;
|
|
251
|
-
currentCliVersion;
|
|
252
|
-
isExperimentalEnabled;
|
|
253
|
-
disableCaffeinate;
|
|
254
|
-
constructor() {
|
|
255
|
-
this.serverUrl = process.env.HAPPY_SERVER_URL || "https://api-happy.zonease.org";
|
|
256
|
-
this.webappUrl = process.env.HAPPY_WEBAPP_URL || "https://happy.zonease.org";
|
|
257
|
-
const args = process.argv.slice(2);
|
|
258
|
-
this.isDaemonProcess = args.length >= 2 && args[0] === "daemon" && args[1] === "start-sync";
|
|
259
|
-
if (process.env.HAPPY_HOME_DIR) {
|
|
260
|
-
const expandedPath = process.env.HAPPY_HOME_DIR.replace(/^~/, os.homedir());
|
|
261
|
-
this.happyHomeDir = expandedPath;
|
|
262
|
-
} else {
|
|
263
|
-
this.happyHomeDir = path.join(os.homedir(), ".happy");
|
|
264
|
-
}
|
|
265
|
-
this.logsDir = path.join(this.happyHomeDir, "logs");
|
|
266
|
-
this.settingsFile = path.join(this.happyHomeDir, "settings.json");
|
|
267
|
-
this.privateKeyFile = path.join(this.happyHomeDir, "access.key");
|
|
268
|
-
this.daemonStateFile = path.join(this.happyHomeDir, "daemon.state.json");
|
|
269
|
-
this.daemonLockFile = path.join(this.happyHomeDir, "daemon.state.json.lock");
|
|
270
|
-
this.sessionKeysDir = path.join(this.happyHomeDir, "session-keys");
|
|
271
|
-
this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
272
|
-
this.disableCaffeinate = ["true", "1", "yes"].includes(process.env.HAPPY_DISABLE_CAFFEINATE?.toLowerCase() || "");
|
|
273
|
-
this.currentCliVersion = packageJson.version;
|
|
274
|
-
const variant = process.env.HAPPY_VARIANT || "stable";
|
|
275
|
-
if (variant === "dev" && !this.happyHomeDir.includes("dev")) {
|
|
276
|
-
console.warn('\u26A0\uFE0F WARNING: HAPPY_VARIANT=dev but HAPPY_HOME_DIR does not contain "dev"');
|
|
277
|
-
console.warn(` Current: ${this.happyHomeDir}`);
|
|
278
|
-
console.warn(` Expected: Should contain "dev" (e.g., ~/.happy-dev)`);
|
|
279
|
-
}
|
|
280
|
-
if (!this.isDaemonProcess && variant === "dev") {
|
|
281
|
-
console.log("\x1B[33m\u{1F527} DEV MODE\x1B[0m - Data: " + this.happyHomeDir);
|
|
282
|
-
}
|
|
283
|
-
if (!fs.existsSync(this.happyHomeDir)) {
|
|
284
|
-
fs.mkdirSync(this.happyHomeDir, { recursive: true });
|
|
285
|
-
}
|
|
286
|
-
if (!fs.existsSync(this.logsDir)) {
|
|
287
|
-
fs.mkdirSync(this.logsDir, { recursive: true });
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
const configuration = new Configuration();
|
|
292
|
-
|
|
293
|
-
function createTimestampForFilename(date = /* @__PURE__ */ new Date()) {
|
|
294
|
-
return date.toLocaleString("sv-SE", {
|
|
295
|
-
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
296
|
-
year: "numeric",
|
|
297
|
-
month: "2-digit",
|
|
298
|
-
day: "2-digit",
|
|
299
|
-
hour: "2-digit",
|
|
300
|
-
minute: "2-digit",
|
|
301
|
-
second: "2-digit"
|
|
302
|
-
}).replace(/[: ]/g, "-").replace(/,/g, "") + "-pid-" + process.pid;
|
|
303
|
-
}
|
|
304
|
-
function createTimestampForLogEntry(date = /* @__PURE__ */ new Date()) {
|
|
305
|
-
return date.toLocaleTimeString("en-US", {
|
|
306
|
-
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
307
|
-
hour12: false,
|
|
308
|
-
hour: "2-digit",
|
|
309
|
-
minute: "2-digit",
|
|
310
|
-
second: "2-digit",
|
|
311
|
-
fractionalSecondDigits: 3
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
function getSessionLogPath() {
|
|
315
|
-
const timestamp = createTimestampForFilename();
|
|
316
|
-
const filename = configuration.isDaemonProcess ? `${timestamp}-daemon.log` : `${timestamp}.log`;
|
|
317
|
-
return path.join(configuration.logsDir, filename);
|
|
318
|
-
}
|
|
319
|
-
class Logger {
|
|
320
|
-
constructor(logFilePath = getSessionLogPath()) {
|
|
321
|
-
this.logFilePath = logFilePath;
|
|
322
|
-
if (process.env.DANGEROUSLY_LOG_TO_SERVER_FOR_AI_AUTO_DEBUGGING && process.env.HAPPY_SERVER_URL) {
|
|
323
|
-
this.dangerouslyUnencryptedServerLoggingUrl = process.env.HAPPY_SERVER_URL;
|
|
324
|
-
console.log(chalk.yellow("[REMOTE LOGGING] Sending logs to server for AI debugging"));
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
dangerouslyUnencryptedServerLoggingUrl;
|
|
328
|
-
// Use local timezone for simplicity of locating the logs,
|
|
329
|
-
// in practice you will not need absolute timestamps
|
|
330
|
-
localTimezoneTimestamp() {
|
|
331
|
-
return createTimestampForLogEntry();
|
|
332
|
-
}
|
|
333
|
-
debug(message, ...args) {
|
|
334
|
-
this.logToFile(`[${this.localTimezoneTimestamp()}]`, message, ...args);
|
|
335
|
-
}
|
|
336
|
-
debugLargeJson(message, object, maxStringLength = 100, maxArrayLength = 10) {
|
|
337
|
-
if (!isDebug()) {
|
|
338
|
-
this.debug(`In production, skipping message inspection`);
|
|
339
|
-
}
|
|
340
|
-
const truncateStrings = (obj) => {
|
|
341
|
-
if (typeof obj === "string") {
|
|
342
|
-
return obj.length > maxStringLength ? obj.substring(0, maxStringLength) + "... [truncated for logs]" : obj;
|
|
343
|
-
}
|
|
344
|
-
if (Array.isArray(obj)) {
|
|
345
|
-
const truncatedArray = obj.map((item) => truncateStrings(item)).slice(0, maxArrayLength);
|
|
346
|
-
if (obj.length > maxArrayLength) {
|
|
347
|
-
truncatedArray.push(`... [truncated array for logs up to ${maxArrayLength} items]`);
|
|
348
|
-
}
|
|
349
|
-
return truncatedArray;
|
|
350
|
-
}
|
|
351
|
-
if (obj && typeof obj === "object") {
|
|
352
|
-
const result = {};
|
|
353
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
354
|
-
if (key === "usage") {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
result[key] = truncateStrings(value);
|
|
358
|
-
}
|
|
359
|
-
return result;
|
|
360
|
-
}
|
|
361
|
-
return obj;
|
|
362
|
-
};
|
|
363
|
-
const truncatedObject = truncateStrings(object);
|
|
364
|
-
const json = JSON.stringify(truncatedObject, null, 2);
|
|
365
|
-
this.logToFile(`[${this.localTimezoneTimestamp()}]`, message, "\n", json);
|
|
366
|
-
}
|
|
367
|
-
info(message, ...args) {
|
|
368
|
-
this.logToConsole("info", "", message, ...args);
|
|
369
|
-
this.debug(message, args);
|
|
370
|
-
}
|
|
371
|
-
infoDeveloper(message, ...args) {
|
|
372
|
-
this.debug(message, ...args);
|
|
373
|
-
if (isDebug()) {
|
|
374
|
-
this.logToConsole("info", "[DEV]", message, ...args);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
warn(message, ...args) {
|
|
378
|
-
this.logToConsole("warn", "", message, ...args);
|
|
379
|
-
this.debug(`[WARN] ${message}`, ...args);
|
|
380
|
-
}
|
|
381
|
-
getLogPath() {
|
|
382
|
-
return this.logFilePath;
|
|
383
|
-
}
|
|
384
|
-
logToConsole(level, prefix, message, ...args) {
|
|
385
|
-
switch (level) {
|
|
386
|
-
case "debug": {
|
|
387
|
-
console.log(chalk.gray(prefix), message, ...args);
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
case "error": {
|
|
391
|
-
console.error(chalk.red(prefix), message, ...args);
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
case "info": {
|
|
395
|
-
console.log(chalk.blue(prefix), message, ...args);
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
case "warn": {
|
|
399
|
-
console.log(chalk.yellow(prefix), message, ...args);
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
default: {
|
|
403
|
-
this.debug("Unknown log level:", level);
|
|
404
|
-
console.log(chalk.blue(prefix), message, ...args);
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
async sendToRemoteServer(level, message, ...args) {
|
|
410
|
-
if (!this.dangerouslyUnencryptedServerLoggingUrl) return;
|
|
411
|
-
try {
|
|
412
|
-
await fetch(this.dangerouslyUnencryptedServerLoggingUrl + "/logs-combined-from-cli-and-mobile-for-simple-ai-debugging", {
|
|
413
|
-
method: "POST",
|
|
414
|
-
headers: { "Content-Type": "application/json" },
|
|
415
|
-
body: JSON.stringify({
|
|
416
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
417
|
-
level,
|
|
418
|
-
message: `${message} ${args.map(
|
|
419
|
-
(a) => typeof a === "object" ? JSON.stringify(a, null, 2) : String(a)
|
|
420
|
-
).join(" ")}`,
|
|
421
|
-
source: "cli",
|
|
422
|
-
platform: process.platform
|
|
423
|
-
})
|
|
424
|
-
});
|
|
425
|
-
} catch (error) {
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
logToFile(prefix, message, ...args) {
|
|
429
|
-
const logLine = `${prefix} ${message} ${args.map((arg) => {
|
|
430
|
-
if (typeof arg === "string") return arg;
|
|
431
|
-
if (arg instanceof Error) return `${arg.message}
|
|
432
|
-
${arg.stack ?? ""}`;
|
|
433
|
-
return JSON.stringify(arg);
|
|
434
|
-
}).join(" ")}
|
|
435
|
-
`;
|
|
436
|
-
if (this.dangerouslyUnencryptedServerLoggingUrl) {
|
|
437
|
-
let level = "info";
|
|
438
|
-
if (prefix.includes(this.localTimezoneTimestamp())) {
|
|
439
|
-
level = "debug";
|
|
440
|
-
}
|
|
441
|
-
this.sendToRemoteServer(level, message, ...args).catch(() => {
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
try {
|
|
445
|
-
fs$1.appendFileSync(this.logFilePath, logLine);
|
|
446
|
-
} catch (appendError) {
|
|
447
|
-
if (isDebug()) {
|
|
448
|
-
console.error("[DEV MODE ONLY THROWING] Failed to append to log file:", appendError);
|
|
449
|
-
throw appendError;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
let logger = new Logger();
|
|
455
|
-
async function listDaemonLogFiles(limit = 50) {
|
|
456
|
-
try {
|
|
457
|
-
const logsDir = configuration.logsDir;
|
|
458
|
-
if (!fs.existsSync(logsDir)) {
|
|
459
|
-
return [];
|
|
460
|
-
}
|
|
461
|
-
const logs = fs.readdirSync(logsDir).filter((file) => file.endsWith("-daemon.log")).map((file) => {
|
|
462
|
-
const fullPath = path.join(logsDir, file);
|
|
463
|
-
const stats = fs.statSync(fullPath);
|
|
464
|
-
return { file, path: fullPath, modified: stats.mtime };
|
|
465
|
-
}).sort((a, b) => b.modified.getTime() - a.modified.getTime());
|
|
466
|
-
try {
|
|
467
|
-
const { readDaemonState } = await Promise.resolve().then(function () { return persistence; });
|
|
468
|
-
const state = await readDaemonState();
|
|
469
|
-
if (!state) {
|
|
470
|
-
return logs;
|
|
471
|
-
}
|
|
472
|
-
if (state.daemonLogPath && fs.existsSync(state.daemonLogPath)) {
|
|
473
|
-
const stats = fs.statSync(state.daemonLogPath);
|
|
474
|
-
const persisted = {
|
|
475
|
-
file: path.basename(state.daemonLogPath),
|
|
476
|
-
path: state.daemonLogPath,
|
|
477
|
-
modified: stats.mtime
|
|
478
|
-
};
|
|
479
|
-
const idx = logs.findIndex((l) => l.path === persisted.path);
|
|
480
|
-
if (idx >= 0) {
|
|
481
|
-
const [found] = logs.splice(idx, 1);
|
|
482
|
-
logs.unshift(found);
|
|
483
|
-
} else {
|
|
484
|
-
logs.unshift(persisted);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
} catch {
|
|
488
|
-
}
|
|
489
|
-
return logs.slice(0, Math.max(0, limit));
|
|
490
|
-
} catch {
|
|
491
|
-
return [];
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
async function getLatestDaemonLog() {
|
|
495
|
-
const [latest] = await listDaemonLogFiles(1);
|
|
496
|
-
return latest || null;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
const sessionRoleSchema = z__namespace.enum(["user", "agent"]);
|
|
500
|
-
const sessionTextEventSchema = z__namespace.object({
|
|
501
|
-
t: z__namespace.literal("text"),
|
|
502
|
-
text: z__namespace.string(),
|
|
503
|
-
thinking: z__namespace.boolean().optional()
|
|
504
|
-
});
|
|
505
|
-
const sessionServiceMessageEventSchema = z__namespace.object({
|
|
506
|
-
t: z__namespace.literal("service"),
|
|
507
|
-
text: z__namespace.string()
|
|
508
|
-
});
|
|
509
|
-
const sessionToolCallStartEventSchema = z__namespace.object({
|
|
510
|
-
t: z__namespace.literal("tool-call-start"),
|
|
511
|
-
call: z__namespace.string(),
|
|
512
|
-
name: z__namespace.string(),
|
|
513
|
-
title: z__namespace.string(),
|
|
514
|
-
description: z__namespace.string(),
|
|
515
|
-
args: z__namespace.record(z__namespace.string(), z__namespace.unknown())
|
|
516
|
-
});
|
|
517
|
-
const sessionToolCallEndEventSchema = z__namespace.object({
|
|
518
|
-
t: z__namespace.literal("tool-call-end"),
|
|
519
|
-
call: z__namespace.string()
|
|
520
|
-
});
|
|
521
|
-
const sessionFileEventSchema = z__namespace.object({
|
|
522
|
-
t: z__namespace.literal("file"),
|
|
523
|
-
ref: z__namespace.string(),
|
|
524
|
-
name: z__namespace.string(),
|
|
525
|
-
size: z__namespace.number(),
|
|
526
|
-
image: z__namespace.object({
|
|
527
|
-
width: z__namespace.number(),
|
|
528
|
-
height: z__namespace.number(),
|
|
529
|
-
thumbhash: z__namespace.string()
|
|
530
|
-
}).optional()
|
|
531
|
-
});
|
|
532
|
-
const sessionTurnStartEventSchema = z__namespace.object({
|
|
533
|
-
t: z__namespace.literal("turn-start")
|
|
534
|
-
});
|
|
535
|
-
const sessionStartEventSchema = z__namespace.object({
|
|
536
|
-
t: z__namespace.literal("start"),
|
|
537
|
-
title: z__namespace.string().optional()
|
|
538
|
-
});
|
|
539
|
-
const sessionTurnEndStatusSchema = z__namespace.enum(["completed", "failed", "cancelled"]);
|
|
540
|
-
const sessionTurnEndEventSchema = z__namespace.object({
|
|
541
|
-
t: z__namespace.literal("turn-end"),
|
|
542
|
-
status: sessionTurnEndStatusSchema
|
|
543
|
-
});
|
|
544
|
-
const sessionStopEventSchema = z__namespace.object({
|
|
545
|
-
t: z__namespace.literal("stop")
|
|
546
|
-
});
|
|
547
|
-
const sessionEventSchema = z__namespace.discriminatedUnion("t", [
|
|
548
|
-
sessionTextEventSchema,
|
|
549
|
-
sessionServiceMessageEventSchema,
|
|
550
|
-
sessionToolCallStartEventSchema,
|
|
551
|
-
sessionToolCallEndEventSchema,
|
|
552
|
-
sessionFileEventSchema,
|
|
553
|
-
sessionTurnStartEventSchema,
|
|
554
|
-
sessionStartEventSchema,
|
|
555
|
-
sessionTurnEndEventSchema,
|
|
556
|
-
sessionStopEventSchema
|
|
557
|
-
]);
|
|
558
|
-
const sessionEnvelopeSchema = z__namespace.object({
|
|
559
|
-
id: z__namespace.string(),
|
|
560
|
-
time: z__namespace.number(),
|
|
561
|
-
role: sessionRoleSchema,
|
|
562
|
-
turn: z__namespace.string().optional(),
|
|
563
|
-
subagent: z__namespace.string().refine((value) => cuid2.isCuid(value), {
|
|
564
|
-
message: "subagent must be a cuid2 value"
|
|
565
|
-
}).optional(),
|
|
566
|
-
ev: sessionEventSchema
|
|
567
|
-
}).superRefine((envelope, ctx) => {
|
|
568
|
-
if (envelope.ev.t === "service" && envelope.role !== "agent") {
|
|
569
|
-
ctx.addIssue({
|
|
570
|
-
code: z__namespace.ZodIssueCode.custom,
|
|
571
|
-
message: 'service events must use role "agent"',
|
|
572
|
-
path: ["role"]
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
if ((envelope.ev.t === "start" || envelope.ev.t === "stop") && envelope.role !== "agent") {
|
|
576
|
-
ctx.addIssue({
|
|
577
|
-
code: z__namespace.ZodIssueCode.custom,
|
|
578
|
-
message: `${envelope.ev.t} events must use role "agent"`,
|
|
579
|
-
path: ["role"]
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
});
|
|
583
|
-
const MessageMetaSchema$1 = z__namespace.object({
|
|
584
|
-
sentFrom: z__namespace.string().optional(),
|
|
585
|
-
permissionMode: z__namespace.enum(["default", "acceptEdits", "bypassPermissions", "plan", "read-only", "safe-yolo", "yolo"]).optional(),
|
|
586
|
-
model: z__namespace.string().nullable().optional(),
|
|
587
|
-
fallbackModel: z__namespace.string().nullable().optional(),
|
|
588
|
-
reasoningEffort: z__namespace.string().nullable().optional(),
|
|
589
|
-
customSystemPrompt: z__namespace.string().nullable().optional(),
|
|
590
|
-
appendSystemPrompt: z__namespace.string().nullable().optional(),
|
|
591
|
-
allowedTools: z__namespace.array(z__namespace.string()).nullable().optional(),
|
|
592
|
-
disallowedTools: z__namespace.array(z__namespace.string()).nullable().optional(),
|
|
593
|
-
displayText: z__namespace.string().optional()
|
|
594
|
-
});
|
|
595
|
-
const UserMessageSchema$1 = z__namespace.object({
|
|
596
|
-
role: z__namespace.literal("user"),
|
|
597
|
-
content: z__namespace.object({
|
|
598
|
-
type: z__namespace.literal("text"),
|
|
599
|
-
text: z__namespace.string()
|
|
600
|
-
}),
|
|
601
|
-
localKey: z__namespace.string().optional(),
|
|
602
|
-
meta: MessageMetaSchema$1.optional()
|
|
603
|
-
});
|
|
604
|
-
const AgentMessageSchema$1 = z__namespace.object({
|
|
605
|
-
role: z__namespace.literal("agent"),
|
|
606
|
-
content: z__namespace.object({
|
|
607
|
-
type: z__namespace.string()
|
|
608
|
-
}).passthrough(),
|
|
609
|
-
meta: MessageMetaSchema$1.optional()
|
|
610
|
-
});
|
|
611
|
-
z__namespace.discriminatedUnion("role", [UserMessageSchema$1, AgentMessageSchema$1]);
|
|
612
|
-
const SessionMessageContentSchema = z__namespace.object({
|
|
613
|
-
c: z__namespace.string(),
|
|
614
|
-
t: z__namespace.literal("encrypted")
|
|
615
|
-
});
|
|
616
|
-
const SessionMessageSchema = z__namespace.object({
|
|
617
|
-
id: z__namespace.string(),
|
|
618
|
-
seq: z__namespace.number(),
|
|
619
|
-
localId: z__namespace.string().nullish(),
|
|
620
|
-
content: SessionMessageContentSchema,
|
|
621
|
-
createdAt: z__namespace.number(),
|
|
622
|
-
updatedAt: z__namespace.number()
|
|
623
|
-
});
|
|
624
|
-
const SessionProtocolMessageSchema = z__namespace.object({
|
|
625
|
-
role: z__namespace.literal("session"),
|
|
626
|
-
content: sessionEnvelopeSchema,
|
|
627
|
-
meta: MessageMetaSchema$1.optional()
|
|
628
|
-
});
|
|
629
|
-
z__namespace.discriminatedUnion("role", [
|
|
630
|
-
UserMessageSchema$1,
|
|
631
|
-
AgentMessageSchema$1,
|
|
632
|
-
SessionProtocolMessageSchema
|
|
633
|
-
]);
|
|
634
|
-
const VersionedEncryptedValueSchema = z__namespace.object({
|
|
635
|
-
version: z__namespace.number(),
|
|
636
|
-
value: z__namespace.string()
|
|
637
|
-
});
|
|
638
|
-
const VersionedNullableEncryptedValueSchema = z__namespace.object({
|
|
639
|
-
version: z__namespace.number(),
|
|
640
|
-
value: z__namespace.string().nullable()
|
|
641
|
-
});
|
|
642
|
-
const UpdateNewMessageBodySchema = z__namespace.object({
|
|
643
|
-
t: z__namespace.literal("new-message"),
|
|
644
|
-
sid: z__namespace.string(),
|
|
645
|
-
message: SessionMessageSchema
|
|
646
|
-
});
|
|
647
|
-
const UpdateSessionBodySchema = z__namespace.object({
|
|
648
|
-
t: z__namespace.literal("update-session"),
|
|
649
|
-
id: z__namespace.string(),
|
|
650
|
-
metadata: VersionedEncryptedValueSchema.nullish(),
|
|
651
|
-
agentState: VersionedNullableEncryptedValueSchema.nullish()
|
|
652
|
-
});
|
|
653
|
-
const VersionedMachineEncryptedValueSchema = z__namespace.object({
|
|
654
|
-
version: z__namespace.number(),
|
|
655
|
-
value: z__namespace.string()
|
|
656
|
-
});
|
|
657
|
-
const UpdateMachineBodySchema = z__namespace.object({
|
|
658
|
-
t: z__namespace.literal("update-machine"),
|
|
659
|
-
machineId: z__namespace.string(),
|
|
660
|
-
metadata: VersionedMachineEncryptedValueSchema.nullish(),
|
|
661
|
-
daemonState: VersionedMachineEncryptedValueSchema.nullish(),
|
|
662
|
-
active: z__namespace.boolean().optional(),
|
|
663
|
-
activeAt: z__namespace.number().optional()
|
|
664
|
-
});
|
|
665
|
-
const CoreUpdateBodySchema = z__namespace.discriminatedUnion("t", [
|
|
666
|
-
UpdateNewMessageBodySchema,
|
|
667
|
-
UpdateSessionBodySchema,
|
|
668
|
-
UpdateMachineBodySchema
|
|
669
|
-
]);
|
|
670
|
-
z__namespace.object({
|
|
671
|
-
id: z__namespace.string(),
|
|
672
|
-
seq: z__namespace.number(),
|
|
673
|
-
body: CoreUpdateBodySchema,
|
|
674
|
-
createdAt: z__namespace.number()
|
|
675
|
-
});
|
|
676
|
-
const MODEL_MODE_DEFAULT = "default";
|
|
677
|
-
const MODEL_MODES = [
|
|
678
|
-
MODEL_MODE_DEFAULT,
|
|
679
|
-
"claude-fable-5",
|
|
680
|
-
"claude-fable-5[1m]",
|
|
681
|
-
"claude-opus-5",
|
|
682
|
-
"claude-opus-5[1m]",
|
|
683
|
-
"claude-opus-4-8",
|
|
684
|
-
"claude-opus-4-8[1m]",
|
|
685
|
-
"claude-opus-4-6",
|
|
686
|
-
"claude-opus-4-6[1m]",
|
|
687
|
-
"claude-sonnet-4-6",
|
|
688
|
-
"claude-sonnet-4-6[1m]",
|
|
689
|
-
"claude-haiku-4-5",
|
|
690
|
-
"claude-fable-5-low",
|
|
691
|
-
"claude-fable-5-medium",
|
|
692
|
-
"claude-fable-5-high",
|
|
693
|
-
"claude-fable-5-xhigh",
|
|
694
|
-
"claude-fable-5-max",
|
|
695
|
-
"claude-fable-5[1m]-low",
|
|
696
|
-
"claude-fable-5[1m]-medium",
|
|
697
|
-
"claude-fable-5[1m]-high",
|
|
698
|
-
"claude-fable-5[1m]-xhigh",
|
|
699
|
-
"claude-fable-5[1m]-max",
|
|
700
|
-
"claude-opus-5-low",
|
|
701
|
-
"claude-opus-5-medium",
|
|
702
|
-
"claude-opus-5-high",
|
|
703
|
-
"claude-opus-5-xhigh",
|
|
704
|
-
"claude-opus-5-max",
|
|
705
|
-
"claude-opus-5[1m]-low",
|
|
706
|
-
"claude-opus-5[1m]-medium",
|
|
707
|
-
"claude-opus-5[1m]-high",
|
|
708
|
-
"claude-opus-5[1m]-xhigh",
|
|
709
|
-
"claude-opus-5[1m]-max",
|
|
710
|
-
"claude-opus-4-8-low",
|
|
711
|
-
"claude-opus-4-8-medium",
|
|
712
|
-
"claude-opus-4-8-high",
|
|
713
|
-
"claude-opus-4-8-xhigh",
|
|
714
|
-
"claude-opus-4-8-max",
|
|
715
|
-
"claude-opus-4-8[1m]-low",
|
|
716
|
-
"claude-opus-4-8[1m]-medium",
|
|
717
|
-
"claude-opus-4-8[1m]-high",
|
|
718
|
-
"claude-opus-4-8[1m]-xhigh",
|
|
719
|
-
"claude-opus-4-8[1m]-max",
|
|
720
|
-
"claude-opus-4-6-low",
|
|
721
|
-
"claude-opus-4-6-medium",
|
|
722
|
-
"claude-opus-4-6-high",
|
|
723
|
-
"claude-opus-4-6-max",
|
|
724
|
-
"claude-opus-4-6[1m]-low",
|
|
725
|
-
"claude-opus-4-6[1m]-medium",
|
|
726
|
-
"claude-opus-4-6[1m]-high",
|
|
727
|
-
"claude-opus-4-6[1m]-max",
|
|
728
|
-
"claude-sonnet-4-6-low",
|
|
729
|
-
"claude-sonnet-4-6-medium",
|
|
730
|
-
"claude-sonnet-4-6-high",
|
|
731
|
-
"claude-sonnet-4-6[1m]-low",
|
|
732
|
-
"claude-sonnet-4-6[1m]-medium",
|
|
733
|
-
"claude-sonnet-4-6[1m]-high",
|
|
734
|
-
"claude-haiku-4-5-low",
|
|
735
|
-
"claude-haiku-4-5-medium",
|
|
736
|
-
"claude-haiku-4-5-high",
|
|
737
|
-
"gpt-5.6-sol-low",
|
|
738
|
-
"gpt-5.6-sol-medium",
|
|
739
|
-
"gpt-5.6-sol-high",
|
|
740
|
-
"gpt-5.6-sol-xhigh",
|
|
741
|
-
"gpt-5.6-sol-max",
|
|
742
|
-
"gpt-5.6-terra-low",
|
|
743
|
-
"gpt-5.6-terra-medium",
|
|
744
|
-
"gpt-5.6-terra-high",
|
|
745
|
-
"gpt-5.6-terra-xhigh",
|
|
746
|
-
"gpt-5.6-luna-low",
|
|
747
|
-
"gpt-5.6-luna-medium",
|
|
748
|
-
"gpt-5.6-luna-high",
|
|
749
|
-
"gpt-5.6-luna-xhigh",
|
|
750
|
-
"gpt-5.5-low",
|
|
751
|
-
"gpt-5.5-medium",
|
|
752
|
-
"gpt-5.5-high",
|
|
753
|
-
"gpt-5.5-xhigh",
|
|
754
|
-
"gpt-5.4-low",
|
|
755
|
-
"gpt-5.4-medium",
|
|
756
|
-
"gpt-5.4-high",
|
|
757
|
-
"gpt-5.4-xhigh",
|
|
758
|
-
"gpt-5.3-codex-low",
|
|
759
|
-
"gpt-5.3-codex-medium",
|
|
760
|
-
"gpt-5.3-codex-high",
|
|
761
|
-
"gpt-5.3-codex-xhigh",
|
|
762
|
-
"gpt-5.2-codex-low",
|
|
763
|
-
"gpt-5.2-codex-medium",
|
|
764
|
-
"gpt-5.2-codex-high",
|
|
765
|
-
"gpt-5.2-codex-xhigh",
|
|
766
|
-
"gpt-5.2-low",
|
|
767
|
-
"gpt-5.2-medium",
|
|
768
|
-
"gpt-5.2-high",
|
|
769
|
-
"gpt-5.2-xhigh",
|
|
770
|
-
"gpt-5.1-codex-max-low",
|
|
771
|
-
"gpt-5.1-codex-max-medium",
|
|
772
|
-
"gpt-5.1-codex-max-high",
|
|
773
|
-
"gpt-5.1-codex-max-xhigh",
|
|
774
|
-
"gpt-5.1-codex-mini-medium",
|
|
775
|
-
"gpt-5.1-codex-mini-high",
|
|
776
|
-
"gemini-3-pro-preview",
|
|
777
|
-
"gemini-3-flash-preview",
|
|
778
|
-
"gemini-2.5-pro"
|
|
779
|
-
];
|
|
780
|
-
const CLAUDE_MODEL_MODES = [
|
|
781
|
-
MODEL_MODE_DEFAULT,
|
|
782
|
-
"claude-fable-5",
|
|
783
|
-
"claude-fable-5[1m]",
|
|
784
|
-
"claude-opus-5",
|
|
785
|
-
"claude-opus-5[1m]",
|
|
786
|
-
"claude-opus-4-8",
|
|
787
|
-
"claude-opus-4-8[1m]",
|
|
788
|
-
"claude-opus-4-6",
|
|
789
|
-
"claude-opus-4-6[1m]",
|
|
790
|
-
"claude-sonnet-4-6",
|
|
791
|
-
"claude-sonnet-4-6[1m]",
|
|
792
|
-
"claude-haiku-4-5",
|
|
793
|
-
"claude-fable-5-low",
|
|
794
|
-
"claude-fable-5-medium",
|
|
795
|
-
"claude-fable-5-high",
|
|
796
|
-
"claude-fable-5-xhigh",
|
|
797
|
-
"claude-fable-5-max",
|
|
798
|
-
"claude-fable-5[1m]-low",
|
|
799
|
-
"claude-fable-5[1m]-medium",
|
|
800
|
-
"claude-fable-5[1m]-high",
|
|
801
|
-
"claude-fable-5[1m]-xhigh",
|
|
802
|
-
"claude-fable-5[1m]-max",
|
|
803
|
-
"claude-opus-5-low",
|
|
804
|
-
"claude-opus-5-medium",
|
|
805
|
-
"claude-opus-5-high",
|
|
806
|
-
"claude-opus-5-xhigh",
|
|
807
|
-
"claude-opus-5-max",
|
|
808
|
-
"claude-opus-5[1m]-low",
|
|
809
|
-
"claude-opus-5[1m]-medium",
|
|
810
|
-
"claude-opus-5[1m]-high",
|
|
811
|
-
"claude-opus-5[1m]-xhigh",
|
|
812
|
-
"claude-opus-5[1m]-max",
|
|
813
|
-
"claude-opus-4-8-low",
|
|
814
|
-
"claude-opus-4-8-medium",
|
|
815
|
-
"claude-opus-4-8-high",
|
|
816
|
-
"claude-opus-4-8-xhigh",
|
|
817
|
-
"claude-opus-4-8-max",
|
|
818
|
-
"claude-opus-4-8[1m]-low",
|
|
819
|
-
"claude-opus-4-8[1m]-medium",
|
|
820
|
-
"claude-opus-4-8[1m]-high",
|
|
821
|
-
"claude-opus-4-8[1m]-xhigh",
|
|
822
|
-
"claude-opus-4-8[1m]-max",
|
|
823
|
-
"claude-opus-4-6-low",
|
|
824
|
-
"claude-opus-4-6-medium",
|
|
825
|
-
"claude-opus-4-6-high",
|
|
826
|
-
"claude-opus-4-6-max",
|
|
827
|
-
"claude-opus-4-6[1m]-low",
|
|
828
|
-
"claude-opus-4-6[1m]-medium",
|
|
829
|
-
"claude-opus-4-6[1m]-high",
|
|
830
|
-
"claude-opus-4-6[1m]-max",
|
|
831
|
-
"claude-sonnet-4-6-low",
|
|
832
|
-
"claude-sonnet-4-6-medium",
|
|
833
|
-
"claude-sonnet-4-6-high",
|
|
834
|
-
"claude-sonnet-4-6[1m]-low",
|
|
835
|
-
"claude-sonnet-4-6[1m]-medium",
|
|
836
|
-
"claude-sonnet-4-6[1m]-high",
|
|
837
|
-
"claude-haiku-4-5-low",
|
|
838
|
-
"claude-haiku-4-5-medium",
|
|
839
|
-
"claude-haiku-4-5-high"
|
|
840
|
-
];
|
|
841
|
-
const GEMINI_MODEL_MODES = [
|
|
842
|
-
MODEL_MODE_DEFAULT,
|
|
843
|
-
"gemini-3-pro-preview",
|
|
844
|
-
"gemini-3-flash-preview",
|
|
845
|
-
"gemini-2.5-pro"
|
|
846
|
-
];
|
|
847
|
-
const CODEX_MODEL_MODES = [
|
|
848
|
-
MODEL_MODE_DEFAULT,
|
|
849
|
-
"gpt-5.6-sol-low",
|
|
850
|
-
"gpt-5.6-sol-medium",
|
|
851
|
-
"gpt-5.6-sol-high",
|
|
852
|
-
"gpt-5.6-sol-xhigh",
|
|
853
|
-
"gpt-5.6-sol-max",
|
|
854
|
-
"gpt-5.6-terra-low",
|
|
855
|
-
"gpt-5.6-terra-medium",
|
|
856
|
-
"gpt-5.6-terra-high",
|
|
857
|
-
"gpt-5.6-terra-xhigh",
|
|
858
|
-
"gpt-5.6-luna-low",
|
|
859
|
-
"gpt-5.6-luna-medium",
|
|
860
|
-
"gpt-5.6-luna-high",
|
|
861
|
-
"gpt-5.6-luna-xhigh",
|
|
862
|
-
"gpt-5.5-low",
|
|
863
|
-
"gpt-5.5-medium",
|
|
864
|
-
"gpt-5.5-high",
|
|
865
|
-
"gpt-5.5-xhigh",
|
|
866
|
-
"gpt-5.4-low",
|
|
867
|
-
"gpt-5.4-medium",
|
|
868
|
-
"gpt-5.4-high",
|
|
869
|
-
"gpt-5.4-xhigh",
|
|
870
|
-
"gpt-5.3-codex-low",
|
|
871
|
-
"gpt-5.3-codex-medium",
|
|
872
|
-
"gpt-5.3-codex-high",
|
|
873
|
-
"gpt-5.3-codex-xhigh",
|
|
874
|
-
"gpt-5.2-codex-low",
|
|
875
|
-
"gpt-5.2-codex-medium",
|
|
876
|
-
"gpt-5.2-codex-high",
|
|
877
|
-
"gpt-5.2-codex-xhigh",
|
|
878
|
-
"gpt-5.2-low",
|
|
879
|
-
"gpt-5.2-medium",
|
|
880
|
-
"gpt-5.2-high",
|
|
881
|
-
"gpt-5.2-xhigh",
|
|
882
|
-
"gpt-5.1-codex-max-low",
|
|
883
|
-
"gpt-5.1-codex-max-medium",
|
|
884
|
-
"gpt-5.1-codex-max-high",
|
|
885
|
-
"gpt-5.1-codex-max-xhigh",
|
|
886
|
-
"gpt-5.1-codex-mini-medium",
|
|
887
|
-
"gpt-5.1-codex-mini-high"
|
|
888
|
-
];
|
|
889
|
-
const MODEL_MODE_SET = new Set(MODEL_MODES);
|
|
890
|
-
const CLAUDE_MODEL_MODE_SET = new Set(CLAUDE_MODEL_MODES);
|
|
891
|
-
const GEMINI_MODEL_MODE_SET = new Set(GEMINI_MODEL_MODES);
|
|
892
|
-
const CODEX_MODEL_MODE_SET = new Set(CODEX_MODEL_MODES);
|
|
893
|
-
function isModelMode(value) {
|
|
894
|
-
return MODEL_MODE_SET.has(value);
|
|
895
|
-
}
|
|
896
|
-
function isModelModeForAgent(agent, mode) {
|
|
897
|
-
if (!isModelMode(mode)) return false;
|
|
898
|
-
if (agent === "claude") return CLAUDE_MODEL_MODE_SET.has(mode);
|
|
899
|
-
if (agent === "gemini") return GEMINI_MODEL_MODE_SET.has(mode);
|
|
900
|
-
return CODEX_MODEL_MODE_SET.has(mode);
|
|
901
|
-
}
|
|
902
|
-
const CLAUDE_MODE_TO_SELECTION = {
|
|
903
|
-
"claude-fable-5-low": { family: "claude-fable-5", effort: "low" },
|
|
904
|
-
"claude-fable-5-medium": { family: "claude-fable-5", effort: "medium" },
|
|
905
|
-
"claude-fable-5-high": { family: "claude-fable-5", effort: "high" },
|
|
906
|
-
"claude-fable-5-xhigh": { family: "claude-fable-5", effort: "xhigh" },
|
|
907
|
-
"claude-fable-5-max": { family: "claude-fable-5", effort: "max" },
|
|
908
|
-
"claude-fable-5[1m]-low": { family: "claude-fable-5[1m]", effort: "low" },
|
|
909
|
-
"claude-fable-5[1m]-medium": { family: "claude-fable-5[1m]", effort: "medium" },
|
|
910
|
-
"claude-fable-5[1m]-high": { family: "claude-fable-5[1m]", effort: "high" },
|
|
911
|
-
"claude-fable-5[1m]-xhigh": { family: "claude-fable-5[1m]", effort: "xhigh" },
|
|
912
|
-
"claude-fable-5[1m]-max": { family: "claude-fable-5[1m]", effort: "max" },
|
|
913
|
-
"claude-opus-5-low": { family: "claude-opus-5", effort: "low" },
|
|
914
|
-
"claude-opus-5-medium": { family: "claude-opus-5", effort: "medium" },
|
|
915
|
-
"claude-opus-5-high": { family: "claude-opus-5", effort: "high" },
|
|
916
|
-
"claude-opus-5-xhigh": { family: "claude-opus-5", effort: "xhigh" },
|
|
917
|
-
"claude-opus-5-max": { family: "claude-opus-5", effort: "max" },
|
|
918
|
-
"claude-opus-5[1m]-low": { family: "claude-opus-5[1m]", effort: "low" },
|
|
919
|
-
"claude-opus-5[1m]-medium": { family: "claude-opus-5[1m]", effort: "medium" },
|
|
920
|
-
"claude-opus-5[1m]-high": { family: "claude-opus-5[1m]", effort: "high" },
|
|
921
|
-
"claude-opus-5[1m]-xhigh": { family: "claude-opus-5[1m]", effort: "xhigh" },
|
|
922
|
-
"claude-opus-5[1m]-max": { family: "claude-opus-5[1m]", effort: "max" },
|
|
923
|
-
"claude-opus-4-8-low": { family: "claude-opus-4-8", effort: "low" },
|
|
924
|
-
"claude-opus-4-8-medium": { family: "claude-opus-4-8", effort: "medium" },
|
|
925
|
-
"claude-opus-4-8-high": { family: "claude-opus-4-8", effort: "high" },
|
|
926
|
-
"claude-opus-4-8-xhigh": { family: "claude-opus-4-8", effort: "xhigh" },
|
|
927
|
-
"claude-opus-4-8-max": { family: "claude-opus-4-8", effort: "max" },
|
|
928
|
-
"claude-opus-4-8[1m]-low": { family: "claude-opus-4-8[1m]", effort: "low" },
|
|
929
|
-
"claude-opus-4-8[1m]-medium": { family: "claude-opus-4-8[1m]", effort: "medium" },
|
|
930
|
-
"claude-opus-4-8[1m]-high": { family: "claude-opus-4-8[1m]", effort: "high" },
|
|
931
|
-
"claude-opus-4-8[1m]-xhigh": { family: "claude-opus-4-8[1m]", effort: "xhigh" },
|
|
932
|
-
"claude-opus-4-8[1m]-max": { family: "claude-opus-4-8[1m]", effort: "max" },
|
|
933
|
-
"claude-opus-4-6-low": { family: "claude-opus-4-6", effort: "low" },
|
|
934
|
-
"claude-opus-4-6-medium": { family: "claude-opus-4-6", effort: "medium" },
|
|
935
|
-
"claude-opus-4-6-high": { family: "claude-opus-4-6", effort: "high" },
|
|
936
|
-
"claude-opus-4-6-max": { family: "claude-opus-4-6", effort: "max" },
|
|
937
|
-
"claude-opus-4-6[1m]-low": { family: "claude-opus-4-6[1m]", effort: "low" },
|
|
938
|
-
"claude-opus-4-6[1m]-medium": { family: "claude-opus-4-6[1m]", effort: "medium" },
|
|
939
|
-
"claude-opus-4-6[1m]-high": { family: "claude-opus-4-6[1m]", effort: "high" },
|
|
940
|
-
"claude-opus-4-6[1m]-max": { family: "claude-opus-4-6[1m]", effort: "max" },
|
|
941
|
-
"claude-sonnet-4-6-low": { family: "claude-sonnet-4-6", effort: "low" },
|
|
942
|
-
"claude-sonnet-4-6-medium": { family: "claude-sonnet-4-6", effort: "medium" },
|
|
943
|
-
"claude-sonnet-4-6-high": { family: "claude-sonnet-4-6", effort: "high" },
|
|
944
|
-
"claude-sonnet-4-6[1m]-low": { family: "claude-sonnet-4-6[1m]", effort: "low" },
|
|
945
|
-
"claude-sonnet-4-6[1m]-medium": { family: "claude-sonnet-4-6[1m]", effort: "medium" },
|
|
946
|
-
"claude-sonnet-4-6[1m]-high": { family: "claude-sonnet-4-6[1m]", effort: "high" },
|
|
947
|
-
"claude-haiku-4-5-low": { family: "claude-haiku-4-5", effort: "low" },
|
|
948
|
-
"claude-haiku-4-5-medium": { family: "claude-haiku-4-5", effort: "medium" },
|
|
949
|
-
"claude-haiku-4-5-high": { family: "claude-haiku-4-5", effort: "high" }
|
|
950
|
-
};
|
|
951
|
-
const CODEX_MODE_TO_SELECTION = {
|
|
952
|
-
"gpt-5.6-sol-low": { family: "gpt-5.6-sol", effort: "low" },
|
|
953
|
-
"gpt-5.6-sol-medium": { family: "gpt-5.6-sol", effort: "medium" },
|
|
954
|
-
"gpt-5.6-sol-high": { family: "gpt-5.6-sol", effort: "high" },
|
|
955
|
-
"gpt-5.6-sol-xhigh": { family: "gpt-5.6-sol", effort: "xhigh" },
|
|
956
|
-
"gpt-5.6-sol-max": { family: "gpt-5.6-sol", effort: "max" },
|
|
957
|
-
"gpt-5.6-terra-low": { family: "gpt-5.6-terra", effort: "low" },
|
|
958
|
-
"gpt-5.6-terra-medium": { family: "gpt-5.6-terra", effort: "medium" },
|
|
959
|
-
"gpt-5.6-terra-high": { family: "gpt-5.6-terra", effort: "high" },
|
|
960
|
-
"gpt-5.6-terra-xhigh": { family: "gpt-5.6-terra", effort: "xhigh" },
|
|
961
|
-
"gpt-5.6-luna-low": { family: "gpt-5.6-luna", effort: "low" },
|
|
962
|
-
"gpt-5.6-luna-medium": { family: "gpt-5.6-luna", effort: "medium" },
|
|
963
|
-
"gpt-5.6-luna-high": { family: "gpt-5.6-luna", effort: "high" },
|
|
964
|
-
"gpt-5.6-luna-xhigh": { family: "gpt-5.6-luna", effort: "xhigh" },
|
|
965
|
-
"gpt-5.5-low": { family: "gpt-5.5", effort: "low" },
|
|
966
|
-
"gpt-5.5-medium": { family: "gpt-5.5", effort: "medium" },
|
|
967
|
-
"gpt-5.5-high": { family: "gpt-5.5", effort: "high" },
|
|
968
|
-
"gpt-5.5-xhigh": { family: "gpt-5.5", effort: "xhigh" },
|
|
969
|
-
"gpt-5.4-low": { family: "gpt-5.4", effort: "low" },
|
|
970
|
-
"gpt-5.4-medium": { family: "gpt-5.4", effort: "medium" },
|
|
971
|
-
"gpt-5.4-high": { family: "gpt-5.4", effort: "high" },
|
|
972
|
-
"gpt-5.4-xhigh": { family: "gpt-5.4", effort: "xhigh" },
|
|
973
|
-
"gpt-5.3-codex-low": { family: "gpt-5.3-codex", effort: "low" },
|
|
974
|
-
"gpt-5.3-codex-medium": { family: "gpt-5.3-codex", effort: "medium" },
|
|
975
|
-
"gpt-5.3-codex-high": { family: "gpt-5.3-codex", effort: "high" },
|
|
976
|
-
"gpt-5.3-codex-xhigh": { family: "gpt-5.3-codex", effort: "xhigh" },
|
|
977
|
-
"gpt-5.2-codex-low": { family: "gpt-5.2-codex", effort: "low" },
|
|
978
|
-
"gpt-5.2-codex-medium": { family: "gpt-5.2-codex", effort: "medium" },
|
|
979
|
-
"gpt-5.2-codex-high": { family: "gpt-5.2-codex", effort: "high" },
|
|
980
|
-
"gpt-5.2-codex-xhigh": { family: "gpt-5.2-codex", effort: "xhigh" },
|
|
981
|
-
"gpt-5.2-low": { family: "gpt-5.2", effort: "low" },
|
|
982
|
-
"gpt-5.2-medium": { family: "gpt-5.2", effort: "medium" },
|
|
983
|
-
"gpt-5.2-high": { family: "gpt-5.2", effort: "high" },
|
|
984
|
-
"gpt-5.2-xhigh": { family: "gpt-5.2", effort: "xhigh" },
|
|
985
|
-
"gpt-5.1-codex-max-low": { family: "gpt-5.1-codex-max", effort: "low" },
|
|
986
|
-
"gpt-5.1-codex-max-medium": { family: "gpt-5.1-codex-max", effort: "medium" },
|
|
987
|
-
"gpt-5.1-codex-max-high": { family: "gpt-5.1-codex-max", effort: "high" },
|
|
988
|
-
"gpt-5.1-codex-max-xhigh": { family: "gpt-5.1-codex-max", effort: "xhigh" },
|
|
989
|
-
"gpt-5.1-codex-mini-medium": { family: "gpt-5.1-codex-mini", effort: "medium" },
|
|
990
|
-
"gpt-5.1-codex-mini-high": { family: "gpt-5.1-codex-mini", effort: "high" }
|
|
991
|
-
};
|
|
992
|
-
function parseClaudeModelMode(mode) {
|
|
993
|
-
const entry = CLAUDE_MODE_TO_SELECTION[mode];
|
|
994
|
-
if (entry) return entry;
|
|
995
|
-
if (mode === MODEL_MODE_DEFAULT) return { family: MODEL_MODE_DEFAULT, effort: null };
|
|
996
|
-
return { family: mode, effort: null };
|
|
997
|
-
}
|
|
998
|
-
function parseCodexModelMode(mode) {
|
|
999
|
-
return CODEX_MODE_TO_SELECTION[mode] ?? { family: MODEL_MODE_DEFAULT, effort: "medium" };
|
|
1000
|
-
}
|
|
1001
|
-
const FeishuWebhookConfigSchema = z.z.object({
|
|
1002
|
-
url: z.z.string().url(),
|
|
1003
|
-
secret: z.z.string().optional(),
|
|
1004
|
-
enabled: z.z.boolean()
|
|
1005
|
-
});
|
|
1006
|
-
z.z.object({
|
|
1007
|
-
url: z.z.string().nullable(),
|
|
1008
|
-
secret_set: z.z.boolean(),
|
|
1009
|
-
enabled: z.z.boolean(),
|
|
1010
|
-
lastTestedAt: z.z.number().nullable()
|
|
1011
|
-
});
|
|
1012
|
-
z.z.object({
|
|
1013
|
-
feishu: FeishuWebhookConfigSchema.extend({
|
|
1014
|
-
lastTestedAt: z.z.number().optional()
|
|
1015
|
-
}).optional(),
|
|
1016
|
-
feishuMention: FeishuWebhookConfigSchema.extend({
|
|
1017
|
-
lastTestedAt: z.z.number().optional()
|
|
1018
|
-
}).optional(),
|
|
1019
|
-
/**
|
|
1020
|
-
* The account owner's own Feishu identity (open_id `ou_…` or tenant
|
|
1021
|
-
* user_id). When set, collaboration @ notifications sent to any owner's
|
|
1022
|
-
* mention webhook render this user as a real `<at>` ping instead of
|
|
1023
|
-
* plain text. The ping only fires if the user is in the bot's group.
|
|
1024
|
-
*/
|
|
1025
|
-
feishuUserId: z.z.string().optional()
|
|
1026
|
-
});
|
|
1027
|
-
const MemorySourceSchema = z.z.enum(["manual", "message-pin"]);
|
|
1028
|
-
z.z.object({
|
|
1029
|
-
id: z.z.string(),
|
|
1030
|
-
content: z.z.string().min(1).max(8e3),
|
|
1031
|
-
source: MemorySourceSchema,
|
|
1032
|
-
sourceSessionId: z.z.string().nullable(),
|
|
1033
|
-
sourceMessageId: z.z.string().nullable(),
|
|
1034
|
-
archivedAt: z.z.number().nullable(),
|
|
1035
|
-
createdAt: z.z.number(),
|
|
1036
|
-
updatedAt: z.z.number()
|
|
1037
|
-
});
|
|
1038
|
-
z.z.object({
|
|
1039
|
-
content: z.z.string().min(1).max(8e3),
|
|
1040
|
-
source: MemorySourceSchema.optional().default("manual"),
|
|
1041
|
-
sourceSessionId: z.z.string().optional(),
|
|
1042
|
-
sourceMessageId: z.z.string().optional()
|
|
1043
|
-
});
|
|
1044
|
-
z.z.object({
|
|
1045
|
-
content: z.z.string().min(1).max(8e3)
|
|
1046
|
-
});
|
|
1047
|
-
|
|
1048
33
|
z.z.object({
|
|
1049
34
|
host: z.z.string(),
|
|
1050
35
|
platform: z.z.string(),
|
|
@@ -1150,141 +135,6 @@ const AgentMessageSchema = z.z.object({
|
|
|
1150
135
|
});
|
|
1151
136
|
z.z.union([UserMessageSchema, AgentMessageSchema]);
|
|
1152
137
|
|
|
1153
|
-
function encodeBase64(buffer, variant = "base64") {
|
|
1154
|
-
if (variant === "base64url") {
|
|
1155
|
-
return encodeBase64Url(buffer);
|
|
1156
|
-
}
|
|
1157
|
-
return Buffer.from(buffer).toString("base64");
|
|
1158
|
-
}
|
|
1159
|
-
function encodeBase64Url(buffer) {
|
|
1160
|
-
return Buffer.from(buffer).toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
|
|
1161
|
-
}
|
|
1162
|
-
function decodeBase64(base64, variant = "base64") {
|
|
1163
|
-
if (variant === "base64url") {
|
|
1164
|
-
const base64Standard = base64.replaceAll("-", "+").replaceAll("_", "/") + "=".repeat((4 - base64.length % 4) % 4);
|
|
1165
|
-
return new Uint8Array(Buffer.from(base64Standard, "base64"));
|
|
1166
|
-
}
|
|
1167
|
-
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
1168
|
-
}
|
|
1169
|
-
function getRandomBytes(size) {
|
|
1170
|
-
return new Uint8Array(node_crypto.randomBytes(size));
|
|
1171
|
-
}
|
|
1172
|
-
function libsodiumPublicKeyFromSecretKey(seed) {
|
|
1173
|
-
const hashedSeed = new Uint8Array(node_crypto.createHash("sha512").update(seed).digest());
|
|
1174
|
-
const secretKey = hashedSeed.slice(0, 32);
|
|
1175
|
-
return new Uint8Array(tweetnacl.box.keyPair.fromSecretKey(secretKey).publicKey);
|
|
1176
|
-
}
|
|
1177
|
-
function libsodiumEncryptForPublicKey(data, recipientPublicKey) {
|
|
1178
|
-
const ephemeralKeyPair = tweetnacl.box.keyPair();
|
|
1179
|
-
const nonce = getRandomBytes(tweetnacl.box.nonceLength);
|
|
1180
|
-
const encrypted = tweetnacl.box(data, nonce, recipientPublicKey, ephemeralKeyPair.secretKey);
|
|
1181
|
-
const result = new Uint8Array(ephemeralKeyPair.publicKey.length + nonce.length + encrypted.length);
|
|
1182
|
-
result.set(ephemeralKeyPair.publicKey, 0);
|
|
1183
|
-
result.set(nonce, ephemeralKeyPair.publicKey.length);
|
|
1184
|
-
result.set(encrypted, ephemeralKeyPair.publicKey.length + nonce.length);
|
|
1185
|
-
return result;
|
|
1186
|
-
}
|
|
1187
|
-
function encryptLegacy(data, secret) {
|
|
1188
|
-
const nonce = getRandomBytes(tweetnacl.secretbox.nonceLength);
|
|
1189
|
-
const encrypted = tweetnacl.secretbox(new TextEncoder().encode(JSON.stringify(data)), nonce, secret);
|
|
1190
|
-
const result = new Uint8Array(nonce.length + encrypted.length);
|
|
1191
|
-
result.set(nonce);
|
|
1192
|
-
result.set(encrypted, nonce.length);
|
|
1193
|
-
return result;
|
|
1194
|
-
}
|
|
1195
|
-
function decryptLegacy(data, secret) {
|
|
1196
|
-
const nonce = data.slice(0, tweetnacl.secretbox.nonceLength);
|
|
1197
|
-
const encrypted = data.slice(tweetnacl.secretbox.nonceLength);
|
|
1198
|
-
const decrypted = tweetnacl.secretbox.open(encrypted, nonce, secret);
|
|
1199
|
-
if (!decrypted) {
|
|
1200
|
-
return null;
|
|
1201
|
-
}
|
|
1202
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
1203
|
-
}
|
|
1204
|
-
function encryptWithDataKey(data, dataKey) {
|
|
1205
|
-
const nonce = getRandomBytes(12);
|
|
1206
|
-
const cipher = node_crypto.createCipheriv("aes-256-gcm", dataKey, nonce);
|
|
1207
|
-
const plaintext = new TextEncoder().encode(JSON.stringify(data));
|
|
1208
|
-
const encrypted = Buffer.concat([
|
|
1209
|
-
cipher.update(plaintext),
|
|
1210
|
-
cipher.final()
|
|
1211
|
-
]);
|
|
1212
|
-
const authTag = cipher.getAuthTag();
|
|
1213
|
-
const bundle = new Uint8Array(12 + encrypted.length + 16 + 1);
|
|
1214
|
-
bundle.set([0], 0);
|
|
1215
|
-
bundle.set(nonce, 1);
|
|
1216
|
-
bundle.set(new Uint8Array(encrypted), 13);
|
|
1217
|
-
bundle.set(new Uint8Array(authTag), 13 + encrypted.length);
|
|
1218
|
-
return bundle;
|
|
1219
|
-
}
|
|
1220
|
-
function decryptWithDataKey(bundle, dataKey) {
|
|
1221
|
-
if (bundle.length < 1) {
|
|
1222
|
-
return null;
|
|
1223
|
-
}
|
|
1224
|
-
if (bundle[0] !== 0) {
|
|
1225
|
-
return null;
|
|
1226
|
-
}
|
|
1227
|
-
if (bundle.length < 12 + 16 + 1) {
|
|
1228
|
-
return null;
|
|
1229
|
-
}
|
|
1230
|
-
const nonce = bundle.slice(1, 13);
|
|
1231
|
-
const authTag = bundle.slice(bundle.length - 16);
|
|
1232
|
-
const ciphertext = bundle.slice(13, bundle.length - 16);
|
|
1233
|
-
try {
|
|
1234
|
-
const decipher = node_crypto.createDecipheriv("aes-256-gcm", dataKey, nonce);
|
|
1235
|
-
decipher.setAuthTag(authTag);
|
|
1236
|
-
const decrypted = Buffer.concat([
|
|
1237
|
-
decipher.update(ciphertext),
|
|
1238
|
-
decipher.final()
|
|
1239
|
-
]);
|
|
1240
|
-
return JSON.parse(new TextDecoder().decode(decrypted));
|
|
1241
|
-
} catch (error) {
|
|
1242
|
-
return null;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
function encrypt(key, variant, data) {
|
|
1246
|
-
if (variant === "legacy") {
|
|
1247
|
-
return encryptLegacy(data, key);
|
|
1248
|
-
} else {
|
|
1249
|
-
return encryptWithDataKey(data, key);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
function decrypt(key, variant, data) {
|
|
1253
|
-
if (variant === "legacy") {
|
|
1254
|
-
return decryptLegacy(data, key);
|
|
1255
|
-
} else {
|
|
1256
|
-
return decryptWithDataKey(data, key);
|
|
1257
|
-
}
|
|
1258
|
-
}
|
|
1259
|
-
|
|
1260
|
-
async function delay(ms) {
|
|
1261
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1262
|
-
}
|
|
1263
|
-
function exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount) {
|
|
1264
|
-
let maxDelayRet = minDelay + (maxDelay - minDelay) / maxFailureCount * Math.min(currentFailureCount, maxFailureCount);
|
|
1265
|
-
return Math.round(Math.random() * maxDelayRet);
|
|
1266
|
-
}
|
|
1267
|
-
function createBackoff(opts) {
|
|
1268
|
-
return async (callback) => {
|
|
1269
|
-
let currentFailureCount = 0;
|
|
1270
|
-
const minDelay = 250;
|
|
1271
|
-
const maxDelay = 1e3;
|
|
1272
|
-
const maxFailureCount = 50;
|
|
1273
|
-
while (true) {
|
|
1274
|
-
try {
|
|
1275
|
-
return await callback();
|
|
1276
|
-
} catch (e) {
|
|
1277
|
-
if (currentFailureCount < maxFailureCount) {
|
|
1278
|
-
currentFailureCount++;
|
|
1279
|
-
}
|
|
1280
|
-
let waitForRequest = exponentialBackoffDelay(currentFailureCount, minDelay, maxDelay, maxFailureCount);
|
|
1281
|
-
await delay(waitForRequest);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1286
|
-
let backoff = createBackoff();
|
|
1287
|
-
|
|
1288
138
|
class AsyncLock {
|
|
1289
139
|
permits = 1;
|
|
1290
140
|
promiseResolverQueue = [];
|
|
@@ -1365,7 +215,7 @@ class InvalidateSync {
|
|
|
1365
215
|
this._pendings = [];
|
|
1366
216
|
};
|
|
1367
217
|
_doSync = async () => {
|
|
1368
|
-
await backoff(async () => {
|
|
218
|
+
await time.backoff(async () => {
|
|
1369
219
|
if (this._stopped) {
|
|
1370
220
|
return;
|
|
1371
221
|
}
|
|
@@ -1385,7 +235,7 @@ class InvalidateSync {
|
|
|
1385
235
|
};
|
|
1386
236
|
}
|
|
1387
237
|
|
|
1388
|
-
const diffsDir = path.join(configuration.happyHomeDir, "diffs");
|
|
238
|
+
const diffsDir = path.join(logger.configuration.happyHomeDir, "diffs");
|
|
1389
239
|
function ensureDiffsDir() {
|
|
1390
240
|
if (!fs.existsSync(diffsDir)) {
|
|
1391
241
|
fs.mkdirSync(diffsDir, { recursive: true });
|
|
@@ -1409,9 +259,9 @@ function saveDiffRecords(sessionId, records) {
|
|
|
1409
259
|
}
|
|
1410
260
|
existing.push(...records);
|
|
1411
261
|
fs.writeFileSync(filePath, JSON.stringify(existing), "utf-8");
|
|
1412
|
-
logger.debug(`[DiffStore] Saved ${records.length} record(s) for session ${sessionId}`);
|
|
262
|
+
logger.logger.debug(`[DiffStore] Saved ${records.length} record(s) for session ${sessionId}`);
|
|
1413
263
|
} catch (error) {
|
|
1414
|
-
logger.warn(`[DiffStore] Failed to save diff records:`, error);
|
|
264
|
+
logger.logger.warn(`[DiffStore] Failed to save diff records:`, error);
|
|
1415
265
|
}
|
|
1416
266
|
}
|
|
1417
267
|
function getDiffDetail(sessionId, callId, filePath) {
|
|
@@ -1423,12 +273,12 @@ function getDiffDetail(sessionId, callId, filePath) {
|
|
|
1423
273
|
if (!record) return null;
|
|
1424
274
|
return { diff: record.diff, additions: record.additions, deletions: record.deletions };
|
|
1425
275
|
} catch (error) {
|
|
1426
|
-
logger.warn(`[DiffStore] Failed to get diff detail:`, error);
|
|
276
|
+
logger.logger.warn(`[DiffStore] Failed to get diff detail:`, error);
|
|
1427
277
|
return null;
|
|
1428
278
|
}
|
|
1429
279
|
}
|
|
1430
280
|
|
|
1431
|
-
const toolOutputsDir = path.join(configuration.happyHomeDir, "tool-outputs");
|
|
281
|
+
const toolOutputsDir = path.join(logger.configuration.happyHomeDir, "tool-outputs");
|
|
1432
282
|
function ensureToolOutputsDir() {
|
|
1433
283
|
if (!fs.existsSync(toolOutputsDir)) {
|
|
1434
284
|
fs.mkdirSync(toolOutputsDir, { recursive: true });
|
|
@@ -1451,9 +301,9 @@ function saveToolOutputRecord(sessionId, record) {
|
|
|
1451
301
|
}
|
|
1452
302
|
existing.push(record);
|
|
1453
303
|
fs.writeFileSync(filePath, JSON.stringify(existing), "utf-8");
|
|
1454
|
-
logger.debug(`[ToolOutputStore] Saved output for ${record.toolName} (${record.callId}) in session ${sessionId}`);
|
|
304
|
+
logger.logger.debug(`[ToolOutputStore] Saved output for ${record.toolName} (${record.callId}) in session ${sessionId}`);
|
|
1455
305
|
} catch (error) {
|
|
1456
|
-
logger.warn("[ToolOutputStore] Failed to save tool output record:", error);
|
|
306
|
+
logger.logger.warn("[ToolOutputStore] Failed to save tool output record:", error);
|
|
1457
307
|
}
|
|
1458
308
|
}
|
|
1459
309
|
function getToolOutputRecord(sessionId, callId) {
|
|
@@ -1463,7 +313,7 @@ function getToolOutputRecord(sessionId, callId) {
|
|
|
1463
313
|
const records = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
1464
314
|
return records.find((record) => record.callId === callId) || null;
|
|
1465
315
|
} catch (error) {
|
|
1466
|
-
logger.warn("[ToolOutputStore] Failed to get tool output record:", error);
|
|
316
|
+
logger.logger.warn("[ToolOutputStore] Failed to get tool output record:", error);
|
|
1467
317
|
return null;
|
|
1468
318
|
}
|
|
1469
319
|
}
|
|
@@ -1769,7 +619,7 @@ class RpcHandlerManager {
|
|
|
1769
619
|
this.scopePrefix = config.scopePrefix;
|
|
1770
620
|
this.encryptionKey = config.encryptionKey;
|
|
1771
621
|
this.encryptionVariant = config.encryptionVariant;
|
|
1772
|
-
this.logger = config.logger || ((msg, data) => logger.debug(msg, data));
|
|
622
|
+
this.logger = config.logger || ((msg, data) => logger.logger.debug(msg, data));
|
|
1773
623
|
}
|
|
1774
624
|
/** Encryption key — exposed so feature modules can encrypt outbound non-RPC frames. */
|
|
1775
625
|
getEncryptionKey() {
|
|
@@ -1824,11 +674,11 @@ class RpcHandlerManager {
|
|
|
1824
674
|
if (!handler) {
|
|
1825
675
|
this.logger("[RPC] [ERROR] Method not found", { method: request.method });
|
|
1826
676
|
const errorResponse = { error: "Method not found" };
|
|
1827
|
-
const encryptedError = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
677
|
+
const encryptedError = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
1828
678
|
return encryptedError;
|
|
1829
679
|
}
|
|
1830
680
|
const isPlaintext = typeof request.params !== "string";
|
|
1831
|
-
const decryptedParams = isPlaintext ? request.params : decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(request.params));
|
|
681
|
+
const decryptedParams = isPlaintext ? request.params : logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(request.params));
|
|
1832
682
|
this.logger("[RPC] Calling handler", { method: request.method });
|
|
1833
683
|
const result = await handler(decryptedParams);
|
|
1834
684
|
this.logger("[RPC] Handler returned", { method: request.method, hasResult: result !== void 0 });
|
|
@@ -1839,7 +689,7 @@ class RpcHandlerManager {
|
|
|
1839
689
|
this.logger("[RPC] Handler returned pre-encrypted response", { method: request.method });
|
|
1840
690
|
return result.data;
|
|
1841
691
|
}
|
|
1842
|
-
const encryptedResponse = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, result));
|
|
692
|
+
const encryptedResponse = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, result));
|
|
1843
693
|
this.logger("[RPC] Sending encrypted response", { method: request.method, responseLength: encryptedResponse.length });
|
|
1844
694
|
return encryptedResponse;
|
|
1845
695
|
} catch (error) {
|
|
@@ -1850,7 +700,7 @@ class RpcHandlerManager {
|
|
|
1850
700
|
if (typeof request.params !== "string") {
|
|
1851
701
|
return errorResponse;
|
|
1852
702
|
}
|
|
1853
|
-
return encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
703
|
+
return logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
1854
704
|
}
|
|
1855
705
|
}
|
|
1856
706
|
onSocketConnect(socket) {
|
|
@@ -2500,14 +1350,8 @@ No git repository was found from session cwd or workspace repo metadata. Session
|
|
|
2500
1350
|
};
|
|
2501
1351
|
}
|
|
2502
1352
|
|
|
2503
|
-
const __dirname$1 = path$1.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('types-DvTsFaqR.cjs', document.baseURI).href))));
|
|
2504
|
-
function projectPath() {
|
|
2505
|
-
const path = path$1.resolve(__dirname$1, "..");
|
|
2506
|
-
return path;
|
|
2507
|
-
}
|
|
2508
|
-
|
|
2509
1353
|
function run$1(args, options) {
|
|
2510
|
-
const RUNNER_PATH = path$1.resolve(path$1.join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
|
|
1354
|
+
const RUNNER_PATH = path$1.resolve(path$1.join(projectPath.projectPath(), "scripts", "ripgrep_launcher.cjs"));
|
|
2511
1355
|
return new Promise((resolve2, reject) => {
|
|
2512
1356
|
const child = child_process.spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
|
|
2513
1357
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -2541,7 +1385,7 @@ function toolsPackageName() {
|
|
|
2541
1385
|
return `@zonease/happy-tools-${toolsPlatform()}`;
|
|
2542
1386
|
}
|
|
2543
1387
|
function legacyToolsDir() {
|
|
2544
|
-
return path.resolve(projectPath(), "tools", "unpacked");
|
|
1388
|
+
return path.resolve(projectPath.projectPath(), "tools", "unpacked");
|
|
2545
1389
|
}
|
|
2546
1390
|
let cached$1;
|
|
2547
1391
|
function resolveToolsDir() {
|
|
@@ -2551,7 +1395,7 @@ function resolveToolsDir() {
|
|
|
2551
1395
|
}
|
|
2552
1396
|
function findToolsDir() {
|
|
2553
1397
|
try {
|
|
2554
|
-
const manifest = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('
|
|
1398
|
+
const manifest = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('api-C7FyJfZ1.cjs', document.baseURI).href))).resolve(`${toolsPackageName()}/package.json`);
|
|
2555
1399
|
return path.dirname(manifest);
|
|
2556
1400
|
} catch {
|
|
2557
1401
|
}
|
|
@@ -2625,10 +1469,10 @@ function ensureSpawnHelperExecutable() {
|
|
|
2625
1469
|
for (const candidate of candidates) {
|
|
2626
1470
|
if (!fs.existsSync(candidate) || (fs.statSync(candidate).mode & 73) !== 0) continue;
|
|
2627
1471
|
fs.chmodSync(candidate, 493);
|
|
2628
|
-
logger.debug(`[pty] restored exec bit on ${candidate}`);
|
|
1472
|
+
logger.logger.debug(`[pty] restored exec bit on ${candidate}`);
|
|
2629
1473
|
}
|
|
2630
1474
|
} catch (err) {
|
|
2631
|
-
logger.debug("[pty] could not check spawn-helper permissions:", err);
|
|
1475
|
+
logger.logger.debug("[pty] could not check spawn-helper permissions:", err);
|
|
2632
1476
|
}
|
|
2633
1477
|
}
|
|
2634
1478
|
function loadNodePty() {
|
|
@@ -2643,7 +1487,7 @@ function loadNodePty() {
|
|
|
2643
1487
|
return mod;
|
|
2644
1488
|
} catch (err) {
|
|
2645
1489
|
loadError = err instanceof Error ? err : new Error(String(err));
|
|
2646
|
-
logger.debug("[pty] node-pty unavailable:", loadError.message);
|
|
1490
|
+
logger.logger.debug("[pty] node-pty unavailable:", loadError.message);
|
|
2647
1491
|
return null;
|
|
2648
1492
|
}
|
|
2649
1493
|
}
|
|
@@ -2701,13 +1545,13 @@ function spawnShell(opts) {
|
|
|
2701
1545
|
if (!mod) {
|
|
2702
1546
|
const err = new Error(PTY_UNAVAILABLE);
|
|
2703
1547
|
if (loadError) {
|
|
2704
|
-
logger.debug("[pty] underlying load error:", loadError.stack ?? loadError.message);
|
|
1548
|
+
logger.logger.debug("[pty] underlying load error:", loadError.stack ?? loadError.message);
|
|
2705
1549
|
}
|
|
2706
1550
|
throw err;
|
|
2707
1551
|
}
|
|
2708
1552
|
const shell = process.env.SHELL || (process.platform === "win32" ? "powershell.exe" : "bash");
|
|
2709
1553
|
const cwd = opts.cwd ?? process.cwd();
|
|
2710
|
-
const shellArgs = process.platform
|
|
1554
|
+
const shellArgs = process.platform === "win32" ? [] : isBashShell(shell) ? ["--rcfile", createHappyBashRc(), "-i"] : ["-l", "-i"];
|
|
2711
1555
|
const shellEnv = { ...process.env };
|
|
2712
1556
|
delete shellEnv.NO_COLOR;
|
|
2713
1557
|
delete shellEnv.NODE_DISABLE_COLORS;
|
|
@@ -2736,7 +1580,7 @@ function spawnShell(opts) {
|
|
|
2736
1580
|
});
|
|
2737
1581
|
const ptyId = node_crypto.randomUUID();
|
|
2738
1582
|
ptys.set(ptyId, term);
|
|
2739
|
-
logger.debug(`[pty] spawned ${ptyId} pid=${term.pid} shell=${shell} cols=${opts.cols} rows=${opts.rows} cwd=${cwd}`);
|
|
1583
|
+
logger.logger.debug(`[pty] spawned ${ptyId} pid=${term.pid} shell=${shell} cols=${opts.cols} rows=${opts.rows} cwd=${cwd}`);
|
|
2740
1584
|
return { ptyId, term };
|
|
2741
1585
|
}
|
|
2742
1586
|
function resizePty(ptyId, cols, rows) {
|
|
@@ -2746,7 +1590,7 @@ function resizePty(ptyId, cols, rows) {
|
|
|
2746
1590
|
term.resize(cols, rows);
|
|
2747
1591
|
return true;
|
|
2748
1592
|
} catch (err) {
|
|
2749
|
-
logger.debug(`[pty] resize ${ptyId} failed:`, err);
|
|
1593
|
+
logger.logger.debug(`[pty] resize ${ptyId} failed:`, err);
|
|
2750
1594
|
return false;
|
|
2751
1595
|
}
|
|
2752
1596
|
}
|
|
@@ -2757,7 +1601,7 @@ function writeToPty(ptyId, data) {
|
|
|
2757
1601
|
term.write(data);
|
|
2758
1602
|
return true;
|
|
2759
1603
|
} catch (err) {
|
|
2760
|
-
logger.debug(`[pty] write ${ptyId} failed:`, err);
|
|
1604
|
+
logger.logger.debug(`[pty] write ${ptyId} failed:`, err);
|
|
2761
1605
|
return false;
|
|
2762
1606
|
}
|
|
2763
1607
|
}
|
|
@@ -2768,7 +1612,7 @@ function closePty(ptyId) {
|
|
|
2768
1612
|
try {
|
|
2769
1613
|
term.kill();
|
|
2770
1614
|
} catch (err) {
|
|
2771
|
-
logger.debug(`[pty] kill ${ptyId} failed (likely already exited):`, err);
|
|
1615
|
+
logger.logger.debug(`[pty] kill ${ptyId} failed (likely already exited):`, err);
|
|
2772
1616
|
}
|
|
2773
1617
|
return true;
|
|
2774
1618
|
}
|
|
@@ -2801,7 +1645,7 @@ function runLoginShell(shell) {
|
|
|
2801
1645
|
(error, stdout) => {
|
|
2802
1646
|
const parsed = parseEnvDump(stdout ?? "");
|
|
2803
1647
|
if (!parsed && error) {
|
|
2804
|
-
logger.debug("[shellEnv] login shell did not return an environment:", error.message);
|
|
1648
|
+
logger.logger.debug("[shellEnv] login shell did not return an environment:", error.message);
|
|
2805
1649
|
}
|
|
2806
1650
|
resolve(parsed);
|
|
2807
1651
|
}
|
|
@@ -2822,7 +1666,7 @@ function resolveLoginShellEnv() {
|
|
|
2822
1666
|
if (!shell) return process.env;
|
|
2823
1667
|
const resolved = await runLoginShell(shell);
|
|
2824
1668
|
if (!resolved) return process.env;
|
|
2825
|
-
logger.debug(`[shellEnv] resolved ${Object.keys(resolved).length} variables from ${shell}`);
|
|
1669
|
+
logger.logger.debug(`[shellEnv] resolved ${Object.keys(resolved).length} variables from ${shell}`);
|
|
2826
1670
|
return { ...resolved, ...daemonOwnedEnv() };
|
|
2827
1671
|
})();
|
|
2828
1672
|
return cached;
|
|
@@ -2871,7 +1715,7 @@ const PTY_OUTPUT_FLUSH_MS = 16;
|
|
|
2871
1715
|
const PTY_OUTPUT_FLUSH_BYTES = 32 * 1024;
|
|
2872
1716
|
function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId) {
|
|
2873
1717
|
rpcHandlerManager.registerHandler("bash", async (data) => {
|
|
2874
|
-
logger.debug("Shell command request:", data.command);
|
|
1718
|
+
logger.logger.debug("Shell command request:", data.command);
|
|
2875
1719
|
if (data.cwd && data.cwd !== "/") {
|
|
2876
1720
|
const validation = validatePath(data.cwd, workingDirectory);
|
|
2877
1721
|
if (!validation.valid) {
|
|
@@ -2883,21 +1727,25 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
2883
1727
|
cwd: data.cwd === "/" ? void 0 : data.cwd,
|
|
2884
1728
|
timeout: data.timeout || 3e4,
|
|
2885
1729
|
// Default 30 seconds timeout
|
|
1730
|
+
// node's 1MB default kills the child and reports failure once
|
|
1731
|
+
// output crosses it — the command has already succeeded by
|
|
1732
|
+
// then, and the caller sees a truncated result with an error.
|
|
1733
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
2886
1734
|
// launchd and systemd hand the daemon a bare PATH, so without
|
|
2887
1735
|
// this a command that works when typed into the device terminal
|
|
2888
1736
|
// fails here with "command not found".
|
|
2889
1737
|
env: await resolveLoginShellEnv()
|
|
2890
1738
|
};
|
|
2891
|
-
logger.debug("Shell command executing...", { cwd: options.cwd, timeout: options.timeout });
|
|
1739
|
+
logger.logger.debug("Shell command executing...", { cwd: options.cwd, timeout: options.timeout });
|
|
2892
1740
|
const { stdout, stderr } = await execAsync(data.command, options);
|
|
2893
|
-
logger.debug("Shell command executed, processing result...");
|
|
1741
|
+
logger.logger.debug("Shell command executed, processing result...");
|
|
2894
1742
|
const result = {
|
|
2895
1743
|
success: true,
|
|
2896
1744
|
stdout: stdout ? stdout.toString() : "",
|
|
2897
1745
|
stderr: stderr ? stderr.toString() : "",
|
|
2898
1746
|
exitCode: 0
|
|
2899
1747
|
};
|
|
2900
|
-
logger.debug("Shell command result:", {
|
|
1748
|
+
logger.logger.debug("Shell command result:", {
|
|
2901
1749
|
success: true,
|
|
2902
1750
|
exitCode: 0,
|
|
2903
1751
|
stdoutLen: result.stdout.length,
|
|
@@ -2914,7 +1762,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
2914
1762
|
exitCode: typeof execError.code === "number" ? execError.code : -1,
|
|
2915
1763
|
error: "Command timed out"
|
|
2916
1764
|
};
|
|
2917
|
-
logger.debug("Shell command timed out:", {
|
|
1765
|
+
logger.logger.debug("Shell command timed out:", {
|
|
2918
1766
|
success: false,
|
|
2919
1767
|
exitCode: result2.exitCode,
|
|
2920
1768
|
error: "Command timed out"
|
|
@@ -2928,7 +1776,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
2928
1776
|
exitCode: typeof execError.code === "number" ? execError.code : 1,
|
|
2929
1777
|
error: execError.message || "Command failed"
|
|
2930
1778
|
};
|
|
2931
|
-
logger.debug("Shell command failed:", {
|
|
1779
|
+
logger.logger.debug("Shell command failed:", {
|
|
2932
1780
|
success: false,
|
|
2933
1781
|
exitCode: result.exitCode,
|
|
2934
1782
|
error: result.error,
|
|
@@ -2939,40 +1787,40 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
2939
1787
|
}
|
|
2940
1788
|
});
|
|
2941
1789
|
rpcHandlerManager.registerHandler("readFile", async (data) => {
|
|
2942
|
-
logger.debug("Read file request:", data.path);
|
|
1790
|
+
logger.logger.debug("Read file request:", data.path);
|
|
2943
1791
|
const validation = validatePath(data.path, workingDirectory);
|
|
2944
1792
|
if (!validation.valid) {
|
|
2945
1793
|
return { success: false, error: validation.error };
|
|
2946
1794
|
}
|
|
2947
1795
|
try {
|
|
2948
|
-
const buffer = await fs$
|
|
1796
|
+
const buffer = await fs$1.readFile(data.path);
|
|
2949
1797
|
const content = buffer.toString("base64");
|
|
2950
1798
|
return { success: true, content };
|
|
2951
1799
|
} catch (error) {
|
|
2952
|
-
logger.debug("Failed to read file:", error);
|
|
1800
|
+
logger.logger.debug("Failed to read file:", error);
|
|
2953
1801
|
return { success: false, error: error instanceof Error ? error.message : "Failed to read file" };
|
|
2954
1802
|
}
|
|
2955
1803
|
});
|
|
2956
1804
|
rpcHandlerManager.registerHandler("writeFile", async (data) => {
|
|
2957
|
-
logger.debug("Write file request:", data.path);
|
|
1805
|
+
logger.logger.debug("Write file request:", data.path);
|
|
2958
1806
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
2959
1807
|
const validation = validatePath(data.path, workingDirectory);
|
|
2960
1808
|
if (!validation.valid) {
|
|
2961
|
-
logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, validation.error);
|
|
1809
|
+
logger.logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, validation.error);
|
|
2962
1810
|
return { success: false, error: validation.error };
|
|
2963
1811
|
}
|
|
2964
1812
|
if (!isWritableForSession(absPath)) {
|
|
2965
|
-
logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
1813
|
+
logger.logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
2966
1814
|
return { success: false, error: `Path '${absPath}' is on the system write deny-list` };
|
|
2967
1815
|
}
|
|
2968
1816
|
try {
|
|
2969
1817
|
const buf = Buffer.from(data.content, "base64");
|
|
2970
|
-
await fs$
|
|
2971
|
-
logger.debug("[writeFile] path=%s bytes=%d ok=%s", absPath, buf.length, true);
|
|
1818
|
+
await fs$1.writeFile(absPath, buf);
|
|
1819
|
+
logger.logger.debug("[writeFile] path=%s bytes=%d ok=%s", absPath, buf.length, true);
|
|
2972
1820
|
return { success: true, bytesWritten: buf.length };
|
|
2973
1821
|
} catch (error) {
|
|
2974
1822
|
const message = error instanceof Error ? error.message : "Failed to write file";
|
|
2975
|
-
logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
1823
|
+
logger.logger.debug("[writeFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
2976
1824
|
return { success: false, error: message };
|
|
2977
1825
|
}
|
|
2978
1826
|
});
|
|
@@ -3001,10 +1849,10 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3001
1849
|
return { success: false, error: `Path '${paths.absolutePath}' is on the system write deny-list` };
|
|
3002
1850
|
}
|
|
3003
1851
|
try {
|
|
3004
|
-
await fs$
|
|
3005
|
-
await fs$
|
|
3006
|
-
await fs$
|
|
3007
|
-
await fs$
|
|
1852
|
+
await fs$1.mkdir(paths.directory, { recursive: true });
|
|
1853
|
+
await fs$1.rm(paths.tempAbsolutePath, { force: true });
|
|
1854
|
+
await fs$1.rm(paths.absolutePath, { force: true });
|
|
1855
|
+
await fs$1.writeFile(paths.tempAbsolutePath, Buffer.alloc(0));
|
|
3008
1856
|
return {
|
|
3009
1857
|
success: true,
|
|
3010
1858
|
relativePath: paths.relativePath,
|
|
@@ -3033,12 +1881,12 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3033
1881
|
}
|
|
3034
1882
|
try {
|
|
3035
1883
|
const chunk = Buffer.from(data.chunkBase64, "base64");
|
|
3036
|
-
const current = await fs$
|
|
1884
|
+
const current = await fs$1.stat(paths.tempAbsolutePath).catch(() => null);
|
|
3037
1885
|
const currentSize = current?.size ?? 0;
|
|
3038
1886
|
if (currentSize + chunk.length > MAX_SESSION_UPLOAD_FILE_BYTES) {
|
|
3039
1887
|
return { success: false, error: "File exceeds 100MB limit" };
|
|
3040
1888
|
}
|
|
3041
|
-
await fs$
|
|
1889
|
+
await fs$1.appendFile(paths.tempAbsolutePath, chunk);
|
|
3042
1890
|
return {
|
|
3043
1891
|
success: true,
|
|
3044
1892
|
relativePath: paths.relativePath,
|
|
@@ -3074,11 +1922,11 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3074
1922
|
return { success: false, error: `Path '${paths.absolutePath}' is on the system write deny-list` };
|
|
3075
1923
|
}
|
|
3076
1924
|
try {
|
|
3077
|
-
const info = await fs$
|
|
1925
|
+
const info = await fs$1.stat(paths.tempAbsolutePath);
|
|
3078
1926
|
if (info.size !== data.fileSize) {
|
|
3079
1927
|
return { success: false, error: `Upload size mismatch: expected ${data.fileSize}, got ${info.size}` };
|
|
3080
1928
|
}
|
|
3081
|
-
await fs$
|
|
1929
|
+
await fs$1.rename(paths.tempAbsolutePath, paths.absolutePath);
|
|
3082
1930
|
return {
|
|
3083
1931
|
success: true,
|
|
3084
1932
|
relativePath: paths.relativePath,
|
|
@@ -3096,14 +1944,14 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3096
1944
|
uploadId: data.uploadId,
|
|
3097
1945
|
fileName: data.fileName
|
|
3098
1946
|
});
|
|
3099
|
-
await fs$
|
|
1947
|
+
await fs$1.rm(paths.tempAbsolutePath, { force: true });
|
|
3100
1948
|
return { success: true };
|
|
3101
1949
|
} catch (error) {
|
|
3102
1950
|
return { success: false, error: error instanceof Error ? error.message : "Failed to abort upload" };
|
|
3103
1951
|
}
|
|
3104
1952
|
});
|
|
3105
1953
|
rpcHandlerManager.registerHandler("rename", async (data) => {
|
|
3106
|
-
logger.debug("Rename request:", data.from, "->", data.to);
|
|
1954
|
+
logger.logger.debug("Rename request:", data.from, "->", data.to);
|
|
3107
1955
|
const absFrom = path$1.resolve(workingDirectory, data.from);
|
|
3108
1956
|
const absTo = path$1.resolve(workingDirectory, data.to);
|
|
3109
1957
|
const fromValidation = validatePath(data.from, workingDirectory);
|
|
@@ -3115,68 +1963,68 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3115
1963
|
return { success: false, error: toValidation.error };
|
|
3116
1964
|
}
|
|
3117
1965
|
if (!isWritableForSession(absFrom)) {
|
|
3118
|
-
logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
1966
|
+
logger.logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
3119
1967
|
return { success: false, error: `Path '${absFrom}' is on the system write deny-list` };
|
|
3120
1968
|
}
|
|
3121
1969
|
if (!isWritableForSession(absTo)) {
|
|
3122
|
-
logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
1970
|
+
logger.logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
3123
1971
|
return { success: false, error: `Path '${absTo}' is on the system write deny-list` };
|
|
3124
1972
|
}
|
|
3125
1973
|
try {
|
|
3126
|
-
await fs$
|
|
3127
|
-
logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, true);
|
|
1974
|
+
await fs$1.rename(absFrom, absTo);
|
|
1975
|
+
logger.logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, true);
|
|
3128
1976
|
return { success: true };
|
|
3129
1977
|
} catch (error) {
|
|
3130
1978
|
const message = error instanceof Error ? error.message : "Failed to rename";
|
|
3131
|
-
logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
1979
|
+
logger.logger.info("[rename] %s -> %s ok=%s", absFrom, absTo, false);
|
|
3132
1980
|
return { success: false, error: message };
|
|
3133
1981
|
}
|
|
3134
1982
|
});
|
|
3135
1983
|
rpcHandlerManager.registerHandler("deleteFile", async (data) => {
|
|
3136
|
-
logger.debug("Delete file request:", data.path);
|
|
1984
|
+
logger.logger.debug("Delete file request:", data.path);
|
|
3137
1985
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
3138
1986
|
const validation = validatePath(data.path, workingDirectory);
|
|
3139
1987
|
if (!validation.valid) {
|
|
3140
1988
|
return { success: false, error: validation.error };
|
|
3141
1989
|
}
|
|
3142
1990
|
if (!isWritableForSession(absPath)) {
|
|
3143
|
-
logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
1991
|
+
logger.logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
3144
1992
|
return { success: false, error: `Path '${absPath}' is on the system write deny-list` };
|
|
3145
1993
|
}
|
|
3146
1994
|
try {
|
|
3147
|
-
const st = await fs$
|
|
1995
|
+
const st = await fs$1.lstat(absPath);
|
|
3148
1996
|
if (st.isDirectory()) {
|
|
3149
|
-
logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, "is directory");
|
|
1997
|
+
logger.logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, "is directory");
|
|
3150
1998
|
return { success: false, error: "Not a file, use deleteDirectory" };
|
|
3151
1999
|
}
|
|
3152
|
-
await fs$
|
|
3153
|
-
logger.info("[deleteFile] path=%s ok=%s", absPath, true);
|
|
2000
|
+
await fs$1.unlink(absPath);
|
|
2001
|
+
logger.logger.info("[deleteFile] path=%s ok=%s", absPath, true);
|
|
3154
2002
|
return { success: true };
|
|
3155
2003
|
} catch (error) {
|
|
3156
2004
|
const message = error instanceof Error ? error.message : "Failed to delete file";
|
|
3157
|
-
logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
2005
|
+
logger.logger.info("[deleteFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
3158
2006
|
return { success: false, error: message };
|
|
3159
2007
|
}
|
|
3160
2008
|
});
|
|
3161
2009
|
rpcHandlerManager.registerHandler("deleteDirectory", async (data) => {
|
|
3162
|
-
logger.debug("Delete directory request:", data.path);
|
|
2010
|
+
logger.logger.debug("Delete directory request:", data.path);
|
|
3163
2011
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
3164
2012
|
const validation = validatePath(data.path, workingDirectory);
|
|
3165
2013
|
if (!validation.valid) {
|
|
3166
2014
|
return { success: false, error: validation.error };
|
|
3167
2015
|
}
|
|
3168
2016
|
if (!isWritableForSession(absPath)) {
|
|
3169
|
-
logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
2017
|
+
logger.logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
3170
2018
|
return { success: false, error: `Path '${absPath}' is on the system write deny-list` };
|
|
3171
2019
|
}
|
|
3172
2020
|
try {
|
|
3173
|
-
const top = await fs$
|
|
2021
|
+
const top = await fs$1.lstat(absPath);
|
|
3174
2022
|
if (!top.isDirectory()) {
|
|
3175
|
-
logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, "not a directory");
|
|
2023
|
+
logger.logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, "not a directory");
|
|
3176
2024
|
return { success: false, error: "Not a directory" };
|
|
3177
2025
|
}
|
|
3178
2026
|
async function countEntries(dir) {
|
|
3179
|
-
const entries = await fs$
|
|
2027
|
+
const entries = await fs$1.readdir(dir, { withFileTypes: true });
|
|
3180
2028
|
let n = 0;
|
|
3181
2029
|
for (const entry of entries) {
|
|
3182
2030
|
n += 1;
|
|
@@ -3187,70 +2035,70 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3187
2035
|
return n;
|
|
3188
2036
|
}
|
|
3189
2037
|
const deletedCount = await countEntries(absPath);
|
|
3190
|
-
await fs$
|
|
3191
|
-
logger.info("[deleteDirectory] path=%s deletedCount=%d ok=%s", absPath, deletedCount, true);
|
|
2038
|
+
await fs$1.rm(absPath, { recursive: true, force: false });
|
|
2039
|
+
logger.logger.info("[deleteDirectory] path=%s deletedCount=%d ok=%s", absPath, deletedCount, true);
|
|
3192
2040
|
return { success: true, deletedCount };
|
|
3193
2041
|
} catch (error) {
|
|
3194
2042
|
const message = error instanceof Error ? error.message : "Failed to delete directory";
|
|
3195
|
-
logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, message);
|
|
2043
|
+
logger.logger.info("[deleteDirectory] path=%s ok=%s reason=%s", absPath, false, message);
|
|
3196
2044
|
return { success: false, error: message };
|
|
3197
2045
|
}
|
|
3198
2046
|
});
|
|
3199
2047
|
rpcHandlerManager.registerHandler("createFile", async (data) => {
|
|
3200
|
-
logger.debug("Create file request:", data.path);
|
|
2048
|
+
logger.logger.debug("Create file request:", data.path);
|
|
3201
2049
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
3202
2050
|
const validation = validatePath(data.path, workingDirectory);
|
|
3203
2051
|
if (!validation.valid) {
|
|
3204
2052
|
return { success: false, error: validation.error };
|
|
3205
2053
|
}
|
|
3206
2054
|
if (!isWritableForSession(absPath)) {
|
|
3207
|
-
logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
2055
|
+
logger.logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
3208
2056
|
return { success: false, error: `Path '${absPath}' is on the system write deny-list` };
|
|
3209
2057
|
}
|
|
3210
2058
|
try {
|
|
3211
2059
|
const buf = data.content ? Buffer.from(data.content, "base64") : Buffer.alloc(0);
|
|
3212
|
-
await fs$
|
|
3213
|
-
logger.info("[createFile] path=%s bytes=%d ok=%s", absPath, buf.length, true);
|
|
2060
|
+
await fs$1.writeFile(absPath, buf, { flag: "wx" });
|
|
2061
|
+
logger.logger.info("[createFile] path=%s bytes=%d ok=%s", absPath, buf.length, true);
|
|
3214
2062
|
return { success: true };
|
|
3215
2063
|
} catch (error) {
|
|
3216
2064
|
const errno = error.code;
|
|
3217
2065
|
if (errno === "EEXIST") {
|
|
3218
|
-
logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, "file exists");
|
|
2066
|
+
logger.logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, "file exists");
|
|
3219
2067
|
return { success: false, error: "File exists" };
|
|
3220
2068
|
}
|
|
3221
2069
|
const message = error instanceof Error ? error.message : "Failed to create file";
|
|
3222
|
-
logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
2070
|
+
logger.logger.info("[createFile] path=%s ok=%s reason=%s", absPath, false, message);
|
|
3223
2071
|
return { success: false, error: message };
|
|
3224
2072
|
}
|
|
3225
2073
|
});
|
|
3226
2074
|
rpcHandlerManager.registerHandler("createDirectory", async (data) => {
|
|
3227
|
-
logger.debug("Create directory request:", data.path);
|
|
2075
|
+
logger.logger.debug("Create directory request:", data.path);
|
|
3228
2076
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
3229
2077
|
const validation = validatePath(data.path, workingDirectory);
|
|
3230
2078
|
if (!validation.valid) {
|
|
3231
2079
|
return { success: false, error: validation.error };
|
|
3232
2080
|
}
|
|
3233
2081
|
if (!isWritableForSession(absPath)) {
|
|
3234
|
-
logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
2082
|
+
logger.logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, "system path is read-only");
|
|
3235
2083
|
return { success: false, error: `Path '${absPath}' is on the system write deny-list` };
|
|
3236
2084
|
}
|
|
3237
2085
|
try {
|
|
3238
|
-
await fs$
|
|
3239
|
-
logger.info("[createDirectory] path=%s ok=%s", absPath, true);
|
|
2086
|
+
await fs$1.mkdir(absPath);
|
|
2087
|
+
logger.logger.info("[createDirectory] path=%s ok=%s", absPath, true);
|
|
3240
2088
|
return { success: true };
|
|
3241
2089
|
} catch (error) {
|
|
3242
2090
|
const errno = error.code;
|
|
3243
2091
|
if (errno === "EEXIST") {
|
|
3244
|
-
logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, "directory exists");
|
|
2092
|
+
logger.logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, "directory exists");
|
|
3245
2093
|
return { success: false, error: "Directory exists" };
|
|
3246
2094
|
}
|
|
3247
2095
|
const message = error instanceof Error ? error.message : "Failed to create directory";
|
|
3248
|
-
logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, message);
|
|
2096
|
+
logger.logger.info("[createDirectory] path=%s ok=%s reason=%s", absPath, false, message);
|
|
3249
2097
|
return { success: false, error: message };
|
|
3250
2098
|
}
|
|
3251
2099
|
});
|
|
3252
2100
|
rpcHandlerManager.registerHandler("listDirectory", async (data) => {
|
|
3253
|
-
logger.debug("List directory request:", data.path);
|
|
2101
|
+
logger.logger.debug("List directory request:", data.path);
|
|
3254
2102
|
const absPath = path$1.resolve(workingDirectory, data.path);
|
|
3255
2103
|
const validation = validatePath(data.path, workingDirectory);
|
|
3256
2104
|
if (!validation.valid) {
|
|
@@ -3258,7 +2106,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3258
2106
|
}
|
|
3259
2107
|
const hideSystem = data.hideSystem !== false;
|
|
3260
2108
|
try {
|
|
3261
|
-
const rawEntries = await fs$
|
|
2109
|
+
const rawEntries = await fs$1.readdir(absPath, { withFileTypes: true });
|
|
3262
2110
|
const noSymlinks = rawEntries.filter((e) => !e.isSymbolicLink());
|
|
3263
2111
|
const visible = hideSystem ? noSymlinks.filter((e) => !isSystemNoise(e.name)) : noSymlinks;
|
|
3264
2112
|
const entries = await Promise.all(
|
|
@@ -3268,11 +2116,11 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3268
2116
|
let size;
|
|
3269
2117
|
let mtime;
|
|
3270
2118
|
try {
|
|
3271
|
-
const stats = await fs$
|
|
2119
|
+
const stats = await fs$1.stat(fullPath);
|
|
3272
2120
|
size = stats.size;
|
|
3273
2121
|
mtime = stats.mtime.getTime();
|
|
3274
2122
|
} catch (error) {
|
|
3275
|
-
logger.debug(`Failed to stat ${fullPath}:`, error);
|
|
2123
|
+
logger.logger.debug(`Failed to stat ${fullPath}:`, error);
|
|
3276
2124
|
}
|
|
3277
2125
|
return { name: entry.name, path: fullPath, type, size, mtime };
|
|
3278
2126
|
})
|
|
@@ -3284,19 +2132,19 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3284
2132
|
});
|
|
3285
2133
|
return { success: true, entries };
|
|
3286
2134
|
} catch (error) {
|
|
3287
|
-
logger.debug("Failed to list directory:", error);
|
|
2135
|
+
logger.logger.debug("Failed to list directory:", error);
|
|
3288
2136
|
return { success: false, error: error instanceof Error ? error.message : "Failed to list directory" };
|
|
3289
2137
|
}
|
|
3290
2138
|
});
|
|
3291
2139
|
rpcHandlerManager.registerHandler("getDirectoryTree", async (data) => {
|
|
3292
|
-
logger.debug("Get directory tree request:", data.path, "maxDepth:", data.maxDepth);
|
|
2140
|
+
logger.logger.debug("Get directory tree request:", data.path, "maxDepth:", data.maxDepth);
|
|
3293
2141
|
const validation = validatePath(data.path, workingDirectory);
|
|
3294
2142
|
if (!validation.valid) {
|
|
3295
2143
|
return { success: false, error: validation.error };
|
|
3296
2144
|
}
|
|
3297
2145
|
async function buildTree(path, name, currentDepth) {
|
|
3298
2146
|
try {
|
|
3299
|
-
const stats = await fs$
|
|
2147
|
+
const stats = await fs$1.stat(path);
|
|
3300
2148
|
const node = {
|
|
3301
2149
|
name,
|
|
3302
2150
|
path,
|
|
@@ -3305,12 +2153,12 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3305
2153
|
modified: stats.mtime.getTime()
|
|
3306
2154
|
};
|
|
3307
2155
|
if (stats.isDirectory() && currentDepth < data.maxDepth) {
|
|
3308
|
-
const entries = await fs$
|
|
2156
|
+
const entries = await fs$1.readdir(path, { withFileTypes: true });
|
|
3309
2157
|
const children = [];
|
|
3310
2158
|
await Promise.all(
|
|
3311
2159
|
entries.map(async (entry) => {
|
|
3312
2160
|
if (entry.isSymbolicLink()) {
|
|
3313
|
-
logger.debug(`Skipping symlink: ${path$1.join(path, entry.name)}`);
|
|
2161
|
+
logger.logger.debug(`Skipping symlink: ${path$1.join(path, entry.name)}`);
|
|
3314
2162
|
return;
|
|
3315
2163
|
}
|
|
3316
2164
|
const childPath = path$1.join(path, entry.name);
|
|
@@ -3329,7 +2177,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3329
2177
|
}
|
|
3330
2178
|
return node;
|
|
3331
2179
|
} catch (error) {
|
|
3332
|
-
logger.debug(`Failed to process ${path}:`, error instanceof Error ? error.message : String(error));
|
|
2180
|
+
logger.logger.debug(`Failed to process ${path}:`, error instanceof Error ? error.message : String(error));
|
|
3333
2181
|
return null;
|
|
3334
2182
|
}
|
|
3335
2183
|
}
|
|
@@ -3344,12 +2192,12 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3344
2192
|
}
|
|
3345
2193
|
return { success: true, tree };
|
|
3346
2194
|
} catch (error) {
|
|
3347
|
-
logger.debug("Failed to get directory tree:", error);
|
|
2195
|
+
logger.logger.debug("Failed to get directory tree:", error);
|
|
3348
2196
|
return { success: false, error: error instanceof Error ? error.message : "Failed to get directory tree" };
|
|
3349
2197
|
}
|
|
3350
2198
|
});
|
|
3351
2199
|
rpcHandlerManager.registerHandler("ripgrep", async (data) => {
|
|
3352
|
-
logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
|
|
2200
|
+
logger.logger.debug("Ripgrep request with args:", data.args, "cwd:", data.cwd);
|
|
3353
2201
|
if (data.cwd) {
|
|
3354
2202
|
const validation = validatePath(data.cwd, workingDirectory);
|
|
3355
2203
|
if (!validation.valid) {
|
|
@@ -3365,7 +2213,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3365
2213
|
stderr: result.stderr.toString()
|
|
3366
2214
|
};
|
|
3367
2215
|
} catch (error) {
|
|
3368
|
-
logger.debug("Failed to run ripgrep:", error);
|
|
2216
|
+
logger.logger.debug("Failed to run ripgrep:", error);
|
|
3369
2217
|
return {
|
|
3370
2218
|
success: false,
|
|
3371
2219
|
error: error instanceof Error ? error.message : "Failed to run ripgrep"
|
|
@@ -3373,7 +2221,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3373
2221
|
}
|
|
3374
2222
|
});
|
|
3375
2223
|
rpcHandlerManager.registerHandler("difftastic", async (data) => {
|
|
3376
|
-
logger.debug("Difftastic request with args:", data.args, "cwd:", data.cwd);
|
|
2224
|
+
logger.logger.debug("Difftastic request with args:", data.args, "cwd:", data.cwd);
|
|
3377
2225
|
if (data.cwd) {
|
|
3378
2226
|
const validation = validatePath(data.cwd, workingDirectory);
|
|
3379
2227
|
if (!validation.valid) {
|
|
@@ -3389,7 +2237,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3389
2237
|
stderr: result.stderr.toString()
|
|
3390
2238
|
};
|
|
3391
2239
|
} catch (error) {
|
|
3392
|
-
logger.debug("Failed to run difftastic:", error);
|
|
2240
|
+
logger.logger.debug("Failed to run difftastic:", error);
|
|
3393
2241
|
return {
|
|
3394
2242
|
success: false,
|
|
3395
2243
|
error: error instanceof Error ? error.message : "Failed to run difftastic"
|
|
@@ -3398,7 +2246,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3398
2246
|
});
|
|
3399
2247
|
if (sessionId) {
|
|
3400
2248
|
rpcHandlerManager.registerHandler("getDiffDetail", async (data) => {
|
|
3401
|
-
logger.debug("getDiffDetail request:", data.callId, data.filePath);
|
|
2249
|
+
logger.logger.debug("getDiffDetail request:", data.callId, data.filePath);
|
|
3402
2250
|
const result = getDiffDetail(sessionId, data.callId, data.filePath);
|
|
3403
2251
|
if (!result) {
|
|
3404
2252
|
return { success: false, error: "not_found" };
|
|
@@ -3411,7 +2259,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3411
2259
|
};
|
|
3412
2260
|
});
|
|
3413
2261
|
rpcHandlerManager.registerHandler("getToolOutput", async (data) => {
|
|
3414
|
-
logger.debug("getToolOutput request:", data.callId);
|
|
2262
|
+
logger.logger.debug("getToolOutput request:", data.callId);
|
|
3415
2263
|
const record = getToolOutputRecord(sessionId, data.callId);
|
|
3416
2264
|
if (!record) {
|
|
3417
2265
|
return { success: false, error: "not_found" };
|
|
@@ -3424,19 +2272,24 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3424
2272
|
};
|
|
3425
2273
|
});
|
|
3426
2274
|
const outboundStates = /* @__PURE__ */ new Map();
|
|
3427
|
-
const emitEncrypted = (event, payload) => {
|
|
2275
|
+
const emitEncrypted = (event, payload, reliable = false) => {
|
|
3428
2276
|
const socket = rpcHandlerManager.getSocket();
|
|
3429
2277
|
if (!socket || !socket.connected) {
|
|
3430
2278
|
return;
|
|
3431
2279
|
}
|
|
3432
2280
|
const envelope = { sessionId: payload.sessionId, ptyId: payload.ptyId };
|
|
3433
|
-
if (
|
|
2281
|
+
if (reliable) envelope.reliable = true;
|
|
2282
|
+
if (event === "pty-output" && payload.data !== void 0) {
|
|
3434
2283
|
const key = rpcHandlerManager.getEncryptionKey();
|
|
3435
2284
|
const variant = rpcHandlerManager.getEncryptionVariant();
|
|
3436
|
-
envelope.data = encodeBase64(encrypt(key, variant, payload.data));
|
|
2285
|
+
envelope.data = logger.encodeBase64(logger.encrypt(key, variant, payload.data));
|
|
3437
2286
|
} else if (event === "pty-exit" && typeof payload.exitCode === "number") {
|
|
3438
2287
|
envelope.exitCode = payload.exitCode;
|
|
3439
2288
|
}
|
|
2289
|
+
if (reliable) {
|
|
2290
|
+
socket.emit(event, envelope);
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
3440
2293
|
socket.volatile.emit(event, envelope);
|
|
3441
2294
|
};
|
|
3442
2295
|
const splitOnUtf8Boundary = (merged) => {
|
|
@@ -3493,7 +2346,73 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3493
2346
|
outboundStates.delete(ptyId);
|
|
3494
2347
|
};
|
|
3495
2348
|
rpcHandlerManager.registerHandler("pty-start", async (data) => {
|
|
3496
|
-
logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd });
|
|
2349
|
+
logger.logger.debug("[pty-start] request", { cols: data.cols, rows: data.rows, cwd: data.cwd, exec: !!data.exec });
|
|
2350
|
+
if (data.exec) {
|
|
2351
|
+
const execId = node_crypto.randomUUID();
|
|
2352
|
+
const child = child_process.spawn(process.env.SHELL || "/bin/sh", ["-c", data.exec.command], {
|
|
2353
|
+
cwd: data.cwd,
|
|
2354
|
+
env: await resolveLoginShellEnv(),
|
|
2355
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2356
|
+
});
|
|
2357
|
+
const killTimer = setTimeout(() => child.kill("SIGTERM"), data.exec.timeoutMs ?? 3e5);
|
|
2358
|
+
child.stdout?.pause();
|
|
2359
|
+
child.stderr?.pause();
|
|
2360
|
+
setImmediate(() => {
|
|
2361
|
+
child.stdout?.resume();
|
|
2362
|
+
child.stderr?.resume();
|
|
2363
|
+
});
|
|
2364
|
+
const pending = { stdout: [], stderr: [] };
|
|
2365
|
+
let pendingBytes = 0;
|
|
2366
|
+
let frameCount = 0;
|
|
2367
|
+
let flushTimer = null;
|
|
2368
|
+
const flushExec = () => {
|
|
2369
|
+
if (flushTimer) {
|
|
2370
|
+
clearTimeout(flushTimer);
|
|
2371
|
+
flushTimer = null;
|
|
2372
|
+
}
|
|
2373
|
+
pendingBytes = 0;
|
|
2374
|
+
frameCount++;
|
|
2375
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
2376
|
+
if (pending[stream].length === 0) continue;
|
|
2377
|
+
const chunk = Buffer.concat(pending[stream]);
|
|
2378
|
+
pending[stream] = [];
|
|
2379
|
+
emitEncrypted("pty-output", {
|
|
2380
|
+
sessionId,
|
|
2381
|
+
ptyId: execId,
|
|
2382
|
+
data: { stream, chunk: chunk.toString("base64") }
|
|
2383
|
+
}, true);
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
for (const [stream, source] of [["stdout", child.stdout], ["stderr", child.stderr]]) {
|
|
2387
|
+
source?.on("data", (chunk) => {
|
|
2388
|
+
pending[stream].push(chunk);
|
|
2389
|
+
pendingBytes += chunk.length;
|
|
2390
|
+
if (pendingBytes >= PTY_OUTPUT_FLUSH_BYTES) {
|
|
2391
|
+
flushExec();
|
|
2392
|
+
return;
|
|
2393
|
+
}
|
|
2394
|
+
if (!flushTimer) flushTimer = setTimeout(flushExec, PTY_OUTPUT_FLUSH_MS);
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
child.on("close", (code, signal) => {
|
|
2398
|
+
clearTimeout(killTimer);
|
|
2399
|
+
logger.logger.debug(`[exec] close code=${code} signal=${signal} pending=${pendingBytes}`);
|
|
2400
|
+
flushExec();
|
|
2401
|
+
logger.logger.debug(`[exec] flushed ${frameCount} frames, emitting exit`);
|
|
2402
|
+
emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: code ?? (signal ? 143 : 1) }, true);
|
|
2403
|
+
});
|
|
2404
|
+
child.on("error", (error) => {
|
|
2405
|
+
clearTimeout(killTimer);
|
|
2406
|
+
emitEncrypted("pty-output", {
|
|
2407
|
+
sessionId,
|
|
2408
|
+
ptyId: execId,
|
|
2409
|
+
data: { stream: "stderr", chunk: Buffer.from(`${error.message}
|
|
2410
|
+
`).toString("base64") }
|
|
2411
|
+
}, true);
|
|
2412
|
+
emitEncrypted("pty-exit", { sessionId, ptyId: execId, exitCode: 1 }, true);
|
|
2413
|
+
});
|
|
2414
|
+
return { ok: true, ptyId: execId };
|
|
2415
|
+
}
|
|
3497
2416
|
try {
|
|
3498
2417
|
const { ptyId, term } = spawnShell({
|
|
3499
2418
|
cols: data.cols,
|
|
@@ -3515,7 +2434,7 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3515
2434
|
}
|
|
3516
2435
|
});
|
|
3517
2436
|
const exitDispose = term.onExit(({ exitCode }) => {
|
|
3518
|
-
logger.debug(`[pty-exit] ${ptyId} exitCode=${exitCode}`);
|
|
2437
|
+
logger.logger.debug(`[pty-exit] ${ptyId} exitCode=${exitCode}`);
|
|
3519
2438
|
flushPtyOutput(ptyId);
|
|
3520
2439
|
emitEncrypted("pty-exit", { sessionId, ptyId, exitCode });
|
|
3521
2440
|
cleanupPtyState(ptyId);
|
|
@@ -3532,27 +2451,27 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3532
2451
|
} catch (err) {
|
|
3533
2452
|
const message = err instanceof Error ? err.message : String(err);
|
|
3534
2453
|
if (message === PTY_UNAVAILABLE) {
|
|
3535
|
-
logger.debug("[pty-start] node-pty unavailable on this host");
|
|
2454
|
+
logger.logger.debug("[pty-start] node-pty unavailable on this host");
|
|
3536
2455
|
return { ok: false, error: PTY_UNAVAILABLE };
|
|
3537
2456
|
}
|
|
3538
|
-
logger.debug("[pty-start] spawn failed:", message);
|
|
2457
|
+
logger.logger.debug("[pty-start] spawn failed:", message);
|
|
3539
2458
|
return { ok: false, error: message };
|
|
3540
2459
|
}
|
|
3541
2460
|
});
|
|
3542
2461
|
rpcHandlerManager.registerHandler("pty-resize", async (data) => {
|
|
3543
|
-
logger.debug(`[pty-resize] ${data.ptyId} ${data.cols}x${data.rows}`);
|
|
2462
|
+
logger.logger.debug(`[pty-resize] ${data.ptyId} ${data.cols}x${data.rows}`);
|
|
3544
2463
|
const ok = resizePty(data.ptyId, data.cols, data.rows);
|
|
3545
2464
|
return { ok };
|
|
3546
2465
|
});
|
|
3547
2466
|
rpcHandlerManager.registerHandler("pty-close", async (data) => {
|
|
3548
|
-
logger.debug(`[pty-close] ${data.ptyId}`);
|
|
2467
|
+
logger.logger.debug(`[pty-close] ${data.ptyId}`);
|
|
3549
2468
|
cleanupPtyState(data.ptyId);
|
|
3550
2469
|
const ok = closePty(data.ptyId);
|
|
3551
2470
|
return { ok };
|
|
3552
2471
|
});
|
|
3553
2472
|
rpcHandlerManager.registerSocketEvent("pty-input", (envelope) => {
|
|
3554
2473
|
if (!envelope || typeof envelope !== "object") {
|
|
3555
|
-
logger.debug("[pty-input] unexpected envelope shape, dropping");
|
|
2474
|
+
logger.logger.debug("[pty-input] unexpected envelope shape, dropping");
|
|
3556
2475
|
return;
|
|
3557
2476
|
}
|
|
3558
2477
|
if (envelope.sessionId !== sessionId) return;
|
|
@@ -3561,14 +2480,14 @@ function registerCommonHandlers(rpcHandlerManager, workingDirectory, sessionId)
|
|
|
3561
2480
|
try {
|
|
3562
2481
|
const key = rpcHandlerManager.getEncryptionKey();
|
|
3563
2482
|
const variant = rpcHandlerManager.getEncryptionVariant();
|
|
3564
|
-
const decoded = decrypt(key, variant, decodeBase64(envelope.data));
|
|
2483
|
+
const decoded = logger.decrypt(key, variant, logger.decodeBase64(envelope.data));
|
|
3565
2484
|
if (typeof decoded !== "string") {
|
|
3566
|
-
logger.debug("[pty-input] decrypted payload not a string, dropping");
|
|
2485
|
+
logger.logger.debug("[pty-input] decrypted payload not a string, dropping");
|
|
3567
2486
|
return;
|
|
3568
2487
|
}
|
|
3569
2488
|
writeToPty(envelope.ptyId, decoded);
|
|
3570
2489
|
} catch (err) {
|
|
3571
|
-
logger.debug("[pty-input] decrypt/dispatch failed:", err);
|
|
2490
|
+
logger.logger.debug("[pty-input] decrypt/dispatch failed:", err);
|
|
3572
2491
|
}
|
|
3573
2492
|
});
|
|
3574
2493
|
}
|
|
@@ -3836,7 +2755,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3836
2755
|
initialTitleSet = false;
|
|
3837
2756
|
queueOrDeliverUserMessage(message, source) {
|
|
3838
2757
|
if (isHumanOnlyMessageRecord(message)) {
|
|
3839
|
-
logger.debug(`[API] Ignoring human-only collaboration message from ${source}`);
|
|
2758
|
+
logger.logger.debug(`[API] Ignoring human-only collaboration message from ${source}`);
|
|
3840
2759
|
return false;
|
|
3841
2760
|
}
|
|
3842
2761
|
if (this.pendingMessageCallback) {
|
|
@@ -3861,7 +2780,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3861
2780
|
scopePrefix: this.sessionId,
|
|
3862
2781
|
encryptionKey: this.encryptionKey,
|
|
3863
2782
|
encryptionVariant: this.encryptionVariant,
|
|
3864
|
-
logger: (msg, data) => logger.debug(msg, data)
|
|
2783
|
+
logger: (msg, data) => logger.logger.debug(msg, data)
|
|
3865
2784
|
});
|
|
3866
2785
|
registerCommonHandlers(this.rpcHandlerManager, this.metadata.path, this.sessionId);
|
|
3867
2786
|
this.autoReviewGuard = this.createAutoReviewGuard();
|
|
@@ -3878,7 +2797,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3878
2797
|
}
|
|
3879
2798
|
});
|
|
3880
2799
|
this.sendSync = new InvalidateSync(() => this.flushOutbox());
|
|
3881
|
-
this.socket = socket_ioClient.io(configuration.serverUrl, {
|
|
2800
|
+
this.socket = socket_ioClient.io(logger.configuration.serverUrl, {
|
|
3882
2801
|
auth: {
|
|
3883
2802
|
token: this.token,
|
|
3884
2803
|
clientType: "session-scoped",
|
|
@@ -3895,18 +2814,18 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3895
2814
|
autoConnect: false
|
|
3896
2815
|
});
|
|
3897
2816
|
this.socket.on("connect", () => {
|
|
3898
|
-
logger.debug("Socket connected successfully");
|
|
2817
|
+
logger.logger.debug("Socket connected successfully");
|
|
3899
2818
|
this.rpcHandlerManager.onSocketConnect(this.socket);
|
|
3900
2819
|
});
|
|
3901
2820
|
this.socket.on("rpc-request", async (data, callback) => {
|
|
3902
2821
|
callback(await this.rpcHandlerManager.handleRequest(data));
|
|
3903
2822
|
});
|
|
3904
2823
|
this.socket.on("disconnect", (reason) => {
|
|
3905
|
-
logger.debug("[API] Socket disconnected:", reason);
|
|
2824
|
+
logger.logger.debug("[API] Socket disconnected:", reason);
|
|
3906
2825
|
this.rpcHandlerManager.onSocketDisconnect();
|
|
3907
2826
|
});
|
|
3908
2827
|
this.socket.on("connect_error", (error) => {
|
|
3909
|
-
logger.debug("[API] Socket connection error:", error);
|
|
2828
|
+
logger.logger.debug("[API] Socket connection error:", error);
|
|
3910
2829
|
this.rpcHandlerManager.onSocketDisconnect();
|
|
3911
2830
|
});
|
|
3912
2831
|
this.socket.on("ephemeral", (payload) => {
|
|
@@ -3939,20 +2858,20 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3939
2858
|
});
|
|
3940
2859
|
};
|
|
3941
2860
|
try {
|
|
3942
|
-
logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", data);
|
|
2861
|
+
logger.logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", data);
|
|
3943
2862
|
if (!data.body) {
|
|
3944
|
-
logger.debug("[SOCKET] [UPDATE] [ERROR] No body in update!");
|
|
2863
|
+
logger.logger.debug("[SOCKET] [UPDATE] [ERROR] No body in update!");
|
|
3945
2864
|
return;
|
|
3946
2865
|
}
|
|
3947
2866
|
if (data.body.t === "new-message" && data.body.message.content.t === "encrypted") {
|
|
3948
|
-
const body = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.message.content.c));
|
|
3949
|
-
logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
|
|
2867
|
+
const body = logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(data.body.message.content.c));
|
|
2868
|
+
logger.logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
|
|
3950
2869
|
const userResult = UserMessageSchema.safeParse(body);
|
|
3951
2870
|
if (userResult.success) {
|
|
3952
2871
|
if (isHumanOnlyMessageRecord(body)) {
|
|
3953
|
-
logger.debug("[SOCKET] [UPDATE] Ignoring human-only collaboration message");
|
|
2872
|
+
logger.logger.debug("[SOCKET] [UPDATE] Ignoring human-only collaboration message");
|
|
3954
2873
|
} else if (userResult.data.meta?.sentFrom === "cli") {
|
|
3955
|
-
logger.debug("[SOCKET] [UPDATE] Ignoring echo of CLI-originated user message");
|
|
2874
|
+
logger.logger.debug("[SOCKET] [UPDATE] Ignoring echo of CLI-originated user message");
|
|
3956
2875
|
} else {
|
|
3957
2876
|
this.maybeSetInitialTitleFromUserText(userResult.data.content.text);
|
|
3958
2877
|
if (this.queueOrDeliverUserMessage(userResult.data, "socket-update")) {
|
|
@@ -3971,22 +2890,22 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
3971
2890
|
if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
|
|
3972
2891
|
this.metadata = adoptServerSessionMetadata(
|
|
3973
2892
|
this.metadata,
|
|
3974
|
-
decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.metadata.value))
|
|
2893
|
+
logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(data.body.metadata.value))
|
|
3975
2894
|
);
|
|
3976
2895
|
this.metadataVersion = data.body.metadata.version;
|
|
3977
2896
|
this.syncedModel = this.metadata?.model?.trim() || null;
|
|
3978
2897
|
}
|
|
3979
2898
|
if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
|
|
3980
|
-
this.agentState = data.body.agentState.value ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(data.body.agentState.value)) : null;
|
|
2899
|
+
this.agentState = data.body.agentState.value ? logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(data.body.agentState.value)) : null;
|
|
3981
2900
|
this.agentStateVersion = data.body.agentState.version;
|
|
3982
2901
|
}
|
|
3983
2902
|
} else if (data.body.t === "update-machine") {
|
|
3984
|
-
logger.debug(`[SOCKET] WARNING: Session client received unexpected machine update - ignoring`);
|
|
2903
|
+
logger.logger.debug(`[SOCKET] WARNING: Session client received unexpected machine update - ignoring`);
|
|
3985
2904
|
} else {
|
|
3986
2905
|
this.emit("message", data.body);
|
|
3987
2906
|
}
|
|
3988
2907
|
} catch (error) {
|
|
3989
|
-
logger.debug("[SOCKET] [UPDATE] [ERROR] Error handling update", { error });
|
|
2908
|
+
logger.logger.debug("[SOCKET] [UPDATE] [ERROR] Error handling update", { error });
|
|
3990
2909
|
if (data?.body?.t === "new-message") {
|
|
3991
2910
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3992
2911
|
emitMessageReceipt({
|
|
@@ -4000,7 +2919,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4000
2919
|
}
|
|
4001
2920
|
});
|
|
4002
2921
|
this.socket.on("error", (error) => {
|
|
4003
|
-
logger.debug("[API] Socket error:", error);
|
|
2922
|
+
logger.logger.debug("[API] Socket error:", error);
|
|
4004
2923
|
});
|
|
4005
2924
|
this.socket.connect();
|
|
4006
2925
|
}
|
|
@@ -4015,7 +2934,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4015
2934
|
}
|
|
4016
2935
|
async orchestratorSubmit(body) {
|
|
4017
2936
|
const response = await axios.post(
|
|
4018
|
-
`${configuration.serverUrl}/v1/orchestrator/submit`,
|
|
2937
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/submit`,
|
|
4019
2938
|
body,
|
|
4020
2939
|
{
|
|
4021
2940
|
headers: this.orchestratorHeaders(),
|
|
@@ -4026,7 +2945,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4026
2945
|
}
|
|
4027
2946
|
async orchestratorGetContext() {
|
|
4028
2947
|
const response = await axios.get(
|
|
4029
|
-
`${configuration.serverUrl}/v1/orchestrator/context`,
|
|
2948
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/context`,
|
|
4030
2949
|
{
|
|
4031
2950
|
headers: this.orchestratorHeaders(),
|
|
4032
2951
|
timeout: 3e4
|
|
@@ -4036,7 +2955,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4036
2955
|
}
|
|
4037
2956
|
async orchestratorGetRun(runId, includeTasks = true) {
|
|
4038
2957
|
const response = await axios.get(
|
|
4039
|
-
`${configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}`,
|
|
2958
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}`,
|
|
4040
2959
|
{
|
|
4041
2960
|
headers: this.orchestratorHeaders(),
|
|
4042
2961
|
params: { includeTasks },
|
|
@@ -4047,7 +2966,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4047
2966
|
}
|
|
4048
2967
|
async orchestratorListRuns(query = {}) {
|
|
4049
2968
|
const response = await axios.get(
|
|
4050
|
-
`${configuration.serverUrl}/v1/orchestrator/runs`,
|
|
2969
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/runs`,
|
|
4051
2970
|
{
|
|
4052
2971
|
headers: this.orchestratorHeaders(),
|
|
4053
2972
|
params: query,
|
|
@@ -4058,7 +2977,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4058
2977
|
}
|
|
4059
2978
|
async orchestratorPend(runId, query = {}) {
|
|
4060
2979
|
const response = await axios.get(
|
|
4061
|
-
`${configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}/pend`,
|
|
2980
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}/pend`,
|
|
4062
2981
|
{
|
|
4063
2982
|
headers: this.orchestratorHeaders(),
|
|
4064
2983
|
params: query,
|
|
@@ -4069,7 +2988,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4069
2988
|
}
|
|
4070
2989
|
async orchestratorSendMessage(body) {
|
|
4071
2990
|
const response = await axios.post(
|
|
4072
|
-
`${configuration.serverUrl}/v1/orchestrator/tasks/${encodeURIComponent(body.taskId)}/send-message`,
|
|
2991
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/tasks/${encodeURIComponent(body.taskId)}/send-message`,
|
|
4073
2992
|
{
|
|
4074
2993
|
message: body.message
|
|
4075
2994
|
},
|
|
@@ -4082,7 +3001,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4082
3001
|
}
|
|
4083
3002
|
async orchestratorCancel(runId, body) {
|
|
4084
3003
|
const response = await axios.post(
|
|
4085
|
-
`${configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}/cancel`,
|
|
3004
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/runs/${encodeURIComponent(runId)}/cancel`,
|
|
4086
3005
|
body ?? {},
|
|
4087
3006
|
{
|
|
4088
3007
|
headers: this.orchestratorHeaders(),
|
|
@@ -4321,7 +3240,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4321
3240
|
for (let offset = 0; offset < batch.length; offset += OUTBOX_FLUSH_BATCH_SIZE) {
|
|
4322
3241
|
const chunk = batch.slice(offset, offset + OUTBOX_FLUSH_BATCH_SIZE);
|
|
4323
3242
|
await axios.post(
|
|
4324
|
-
`${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
3243
|
+
`${logger.configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
4325
3244
|
{
|
|
4326
3245
|
messages: chunk
|
|
4327
3246
|
},
|
|
@@ -4341,7 +3260,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4341
3260
|
* This is the parallel reliable path alongside the fast-path socket emit.
|
|
4342
3261
|
*/
|
|
4343
3262
|
enqueueMessage(content) {
|
|
4344
|
-
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
3263
|
+
const encrypted = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
4345
3264
|
this.pendingOutbox.push({
|
|
4346
3265
|
content: encrypted,
|
|
4347
3266
|
localId: node_crypto.randomUUID()
|
|
@@ -4358,7 +3277,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4358
3277
|
const messages = await this.fetchRecentReviewMessages(80);
|
|
4359
3278
|
return messages.some((message) => message.role === "user" && normalizeAutoReviewTextForDedupe(message.text) === normalized);
|
|
4360
3279
|
} catch (error) {
|
|
4361
|
-
logger.debug("[AUTO_REVIEW] Failed to check duplicate follow-up messages:", error);
|
|
3280
|
+
logger.logger.debug("[AUTO_REVIEW] Failed to check duplicate follow-up messages:", error);
|
|
4362
3281
|
return false;
|
|
4363
3282
|
}
|
|
4364
3283
|
}
|
|
@@ -4380,9 +3299,9 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4380
3299
|
displayText: trimmed
|
|
4381
3300
|
}
|
|
4382
3301
|
};
|
|
4383
|
-
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
3302
|
+
const encrypted = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
4384
3303
|
await axios.post(
|
|
4385
|
-
`${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/send`,
|
|
3304
|
+
`${logger.configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/send`,
|
|
4386
3305
|
{
|
|
4387
3306
|
content: encrypted,
|
|
4388
3307
|
localId,
|
|
@@ -4399,7 +3318,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4399
3318
|
}
|
|
4400
3319
|
async fetchRecentReviewMessages(limit = 200) {
|
|
4401
3320
|
const response = await axios.get(
|
|
4402
|
-
`${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
3321
|
+
`${logger.configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
4403
3322
|
{
|
|
4404
3323
|
params: { limit },
|
|
4405
3324
|
headers: { "Authorization": `Bearer ${this.token}` },
|
|
@@ -4412,7 +3331,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4412
3331
|
const encrypted = row?.content?.t === "encrypted" ? row.content.c : null;
|
|
4413
3332
|
if (!encrypted) continue;
|
|
4414
3333
|
try {
|
|
4415
|
-
const decrypted = decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(encrypted));
|
|
3334
|
+
const decrypted = logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(encrypted));
|
|
4416
3335
|
if (isHumanOnlyMessageRecord(decrypted)) {
|
|
4417
3336
|
continue;
|
|
4418
3337
|
}
|
|
@@ -4420,7 +3339,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4420
3339
|
const text = extractPlainTextForAutoReview(decrypted);
|
|
4421
3340
|
if (text) result.push({ role, text });
|
|
4422
3341
|
} catch (error) {
|
|
4423
|
-
logger.debug("[AUTO_REVIEW] Failed to decrypt review history message:", error);
|
|
3342
|
+
logger.logger.debug("[AUTO_REVIEW] Failed to decrypt review history message:", error);
|
|
4424
3343
|
}
|
|
4425
3344
|
}
|
|
4426
3345
|
return result;
|
|
@@ -4431,13 +3350,13 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4431
3350
|
const isSidechain = body.isSidechain === true;
|
|
4432
3351
|
this.sendUsageData(body.message.usage, isSidechain ? void 0 : body.message.model);
|
|
4433
3352
|
} catch (error) {
|
|
4434
|
-
logger.debug("[SOCKET] Failed to send usage data:", error);
|
|
3353
|
+
logger.logger.debug("[SOCKET] Failed to send usage data:", error);
|
|
4435
3354
|
}
|
|
4436
3355
|
}
|
|
4437
3356
|
if (body.type === "summary" && "summary" in body && "leafUuid" in body) {
|
|
4438
3357
|
this.updateMetadata((metadata) => {
|
|
4439
3358
|
if (metadata.summaryPinned) {
|
|
4440
|
-
logger.debug("[SOCKET] Skipping summary update: title is pinned by user");
|
|
3359
|
+
logger.logger.debug("[SOCKET] Skipping summary update: title is pinned by user");
|
|
4441
3360
|
return metadata;
|
|
4442
3361
|
}
|
|
4443
3362
|
const newSummary = body.summary;
|
|
@@ -4445,7 +3364,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4445
3364
|
const dirName = metadata.path?.split(/[\\/]/).filter(Boolean).pop();
|
|
4446
3365
|
const isNewSummaryDirName = dirName && newSummary === dirName;
|
|
4447
3366
|
if (isNewSummaryDirName && currentSummary && currentSummary !== dirName) {
|
|
4448
|
-
logger.debug("[SOCKET] Skipping summary update: new summary is directory name but current title is better", {
|
|
3367
|
+
logger.logger.debug("[SOCKET] Skipping summary update: new summary is directory name but current title is better", {
|
|
4449
3368
|
newSummary,
|
|
4450
3369
|
currentSummary,
|
|
4451
3370
|
dirName
|
|
@@ -4471,18 +3390,18 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4471
3390
|
if (content.role === "agent") {
|
|
4472
3391
|
this.autoReviewGuard.onAgentText(extractPlainTextForAutoReview(content), String(body.uuid || node_crypto.randomUUID()));
|
|
4473
3392
|
}
|
|
4474
|
-
logger.debugLargeJson("[SOCKET] Sending message through socket:", content);
|
|
3393
|
+
logger.logger.debugLargeJson("[SOCKET] Sending message through socket:", content);
|
|
4475
3394
|
this.enqueueMessage(content);
|
|
4476
3395
|
this.postSendProcessing(body);
|
|
4477
3396
|
}
|
|
4478
3397
|
async sendClaudeSessionMessageBatch(messages, mode = "append") {
|
|
4479
3398
|
if (!this.socket.connected) {
|
|
4480
|
-
logger.debug("[API] Socket not connected, cannot send Claude session message batch. Messages will be lost:", { count: messages.length });
|
|
3399
|
+
logger.logger.debug("[API] Socket not connected, cannot send Claude session message batch. Messages will be lost:", { count: messages.length });
|
|
4481
3400
|
return { result: "error" };
|
|
4482
3401
|
}
|
|
4483
3402
|
const payload = messages.map((item) => {
|
|
4484
3403
|
const content = this.buildMessageContent(item.message);
|
|
4485
|
-
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
3404
|
+
const encrypted = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
4486
3405
|
return {
|
|
4487
3406
|
message: encrypted,
|
|
4488
3407
|
localId: item.localId ?? null
|
|
@@ -4537,7 +3456,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4537
3456
|
sentFrom: "cli"
|
|
4538
3457
|
}
|
|
4539
3458
|
};
|
|
4540
|
-
logger.debug(`[SOCKET] Sending ACP message from ${provider}:`, { type: body.type, hasMessage: "message" in body });
|
|
3459
|
+
logger.logger.debug(`[SOCKET] Sending ACP message from ${provider}:`, { type: body.type, hasMessage: "message" in body });
|
|
4541
3460
|
this.autoReviewGuard.onAgentText(normalizeReviewableAgentText(body), `${provider}-${Date.now()}`);
|
|
4542
3461
|
this.enqueueMessage(content);
|
|
4543
3462
|
}
|
|
@@ -4548,17 +3467,17 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4548
3467
|
*/
|
|
4549
3468
|
async sendBackfillBatch(messages, mode = "append") {
|
|
4550
3469
|
if (!this.socket.connected) {
|
|
4551
|
-
logger.debug(`[API] Socket not connected, cannot send backfill batch (${messages.length} messages)`);
|
|
3470
|
+
logger.logger.debug(`[API] Socket not connected, cannot send backfill batch (${messages.length} messages)`);
|
|
4552
3471
|
return { result: "error" };
|
|
4553
3472
|
}
|
|
4554
3473
|
const payload = messages.map((item) => {
|
|
4555
|
-
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, item.content));
|
|
3474
|
+
const encrypted = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, item.content));
|
|
4556
3475
|
return {
|
|
4557
3476
|
message: encrypted,
|
|
4558
3477
|
localId: item.localId
|
|
4559
3478
|
};
|
|
4560
3479
|
});
|
|
4561
|
-
logger.debug(`[SOCKET] Sending backfill batch: ${messages.length} messages, mode=${mode}`);
|
|
3480
|
+
logger.logger.debug(`[SOCKET] Sending backfill batch: ${messages.length} messages, mode=${mode}`);
|
|
4562
3481
|
const response = await this.socket.emitWithAck("message-batch", {
|
|
4563
3482
|
sid: this.sessionId,
|
|
4564
3483
|
messages: payload,
|
|
@@ -4575,7 +3494,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4575
3494
|
data: event
|
|
4576
3495
|
}
|
|
4577
3496
|
};
|
|
4578
|
-
const encrypted = encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
3497
|
+
const encrypted = logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, content));
|
|
4579
3498
|
this.socket.emit("message", {
|
|
4580
3499
|
sid: this.sessionId,
|
|
4581
3500
|
message: encrypted
|
|
@@ -4585,8 +3504,8 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4585
3504
|
* Send a ping message to keep the connection alive
|
|
4586
3505
|
*/
|
|
4587
3506
|
keepAlive(thinking, mode) {
|
|
4588
|
-
if (isDebug()) {
|
|
4589
|
-
logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
3507
|
+
if (logger.isDebug()) {
|
|
3508
|
+
logger.logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
4590
3509
|
}
|
|
4591
3510
|
this.socket.volatile.emit("session-alive", {
|
|
4592
3511
|
sid: this.sessionId,
|
|
@@ -4605,7 +3524,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4605
3524
|
* Send a generic usage report to the server for any provider
|
|
4606
3525
|
*/
|
|
4607
3526
|
sendUsageReport(report) {
|
|
4608
|
-
logger.debugLargeJson("[SOCKET] Sending usage report:", report);
|
|
3527
|
+
logger.logger.debugLargeJson("[SOCKET] Sending usage report:", report);
|
|
4609
3528
|
this.socket.emit("usage-report", {
|
|
4610
3529
|
key: report.key,
|
|
4611
3530
|
sessionId: this.sessionId,
|
|
@@ -4613,7 +3532,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4613
3532
|
cost: report.cost
|
|
4614
3533
|
}, (response) => {
|
|
4615
3534
|
if (!response?.success) {
|
|
4616
|
-
logger.debug("[SOCKET] Usage report rejected by server", {
|
|
3535
|
+
logger.logger.debug("[SOCKET] Usage report rejected by server", {
|
|
4617
3536
|
key: report.key,
|
|
4618
3537
|
sessionId: this.sessionId,
|
|
4619
3538
|
error: response?.error || "unknown error"
|
|
@@ -4700,22 +3619,22 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4700
3619
|
}
|
|
4701
3620
|
async updateMetadataAsync(handler) {
|
|
4702
3621
|
await this.metadataLock.inLock(async () => {
|
|
4703
|
-
await backoff(async () => {
|
|
3622
|
+
await time.backoff(async () => {
|
|
4704
3623
|
const base = this.metadata;
|
|
4705
3624
|
if (!base) {
|
|
4706
|
-
logger.debug("[API] skipping metadata update: no local metadata to build on");
|
|
3625
|
+
logger.logger.debug("[API] skipping metadata update: no local metadata to build on");
|
|
4707
3626
|
return;
|
|
4708
3627
|
}
|
|
4709
3628
|
const updated = adoptServerSessionMetadata(base, handler(base));
|
|
4710
|
-
const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) });
|
|
3629
|
+
const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, updated)) });
|
|
4711
3630
|
if (answer.result === "success") {
|
|
4712
|
-
this.metadata = adoptServerSessionMetadata(this.metadata, decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata)));
|
|
3631
|
+
this.metadata = adoptServerSessionMetadata(this.metadata, logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(answer.metadata)));
|
|
4713
3632
|
this.metadataVersion = answer.version;
|
|
4714
3633
|
this.syncedModel = this.metadata?.model?.trim() || null;
|
|
4715
3634
|
} else if (answer.result === "version-mismatch") {
|
|
4716
3635
|
if (answer.version > this.metadataVersion) {
|
|
4717
3636
|
this.metadataVersion = answer.version;
|
|
4718
|
-
this.metadata = adoptServerSessionMetadata(this.metadata, decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.metadata)));
|
|
3637
|
+
this.metadata = adoptServerSessionMetadata(this.metadata, logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(answer.metadata)));
|
|
4719
3638
|
this.syncedModel = this.metadata?.model?.trim() || null;
|
|
4720
3639
|
}
|
|
4721
3640
|
throw new Error("Metadata version mismatch");
|
|
@@ -4728,19 +3647,19 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4728
3647
|
* @param handler - Handler function that returns the updated agent state
|
|
4729
3648
|
*/
|
|
4730
3649
|
updateAgentState(handler) {
|
|
4731
|
-
logger.debugLargeJson("Updating agent state", this.agentState);
|
|
3650
|
+
logger.logger.debugLargeJson("Updating agent state", this.agentState);
|
|
4732
3651
|
this.agentStateLock.inLock(async () => {
|
|
4733
|
-
await backoff(async () => {
|
|
3652
|
+
await time.backoff(async () => {
|
|
4734
3653
|
let updated = handler(this.agentState || {});
|
|
4735
|
-
const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
|
|
3654
|
+
const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? logger.encodeBase64(logger.encrypt(this.encryptionKey, this.encryptionVariant, updated)) : null });
|
|
4736
3655
|
if (answer.result === "success") {
|
|
4737
|
-
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
3656
|
+
this.agentState = answer.agentState ? logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(answer.agentState)) : null;
|
|
4738
3657
|
this.agentStateVersion = answer.version;
|
|
4739
|
-
logger.debug("Agent state updated", this.agentState);
|
|
3658
|
+
logger.logger.debug("Agent state updated", this.agentState);
|
|
4740
3659
|
} else if (answer.result === "version-mismatch") {
|
|
4741
3660
|
if (answer.version > this.agentStateVersion) {
|
|
4742
3661
|
this.agentStateVersion = answer.version;
|
|
4743
|
-
this.agentState = answer.agentState ? decrypt(this.encryptionKey, this.encryptionVariant, decodeBase64(answer.agentState)) : null;
|
|
3662
|
+
this.agentState = answer.agentState ? logger.decrypt(this.encryptionKey, this.encryptionVariant, logger.decodeBase64(answer.agentState)) : null;
|
|
4744
3663
|
}
|
|
4745
3664
|
throw new Error("Agent state version mismatch");
|
|
4746
3665
|
} else if (answer.result === "error") ;
|
|
@@ -4755,7 +3674,7 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4755
3674
|
try {
|
|
4756
3675
|
await this.sendSync.invalidateAndAwait();
|
|
4757
3676
|
} catch (error) {
|
|
4758
|
-
logger.debug("[API] flush: outbox flush failed, some messages may be lost:", error);
|
|
3677
|
+
logger.logger.debug("[API] flush: outbox flush failed, some messages may be lost:", error);
|
|
4759
3678
|
}
|
|
4760
3679
|
if (!this.socket.connected) {
|
|
4761
3680
|
return;
|
|
@@ -4770,387 +3689,17 @@ class ApiSessionClient extends node_events.EventEmitter {
|
|
|
4770
3689
|
});
|
|
4771
3690
|
}
|
|
4772
3691
|
async close() {
|
|
4773
|
-
logger.debug("[API] socket.close() called");
|
|
3692
|
+
logger.logger.debug("[API] socket.close() called");
|
|
4774
3693
|
try {
|
|
4775
3694
|
await this.sendSync.invalidateAndAwait();
|
|
4776
3695
|
} catch (error) {
|
|
4777
|
-
logger.debug("[API] close: final outbox flush failed:", error);
|
|
3696
|
+
logger.logger.debug("[API] close: final outbox flush failed:", error);
|
|
4778
3697
|
}
|
|
4779
3698
|
this.sendSync.stop();
|
|
4780
3699
|
this.socket.close();
|
|
4781
3700
|
}
|
|
4782
3701
|
}
|
|
4783
3702
|
|
|
4784
|
-
const AnthropicConfigSchema = z__namespace.object({
|
|
4785
|
-
baseUrl: z__namespace.string().url().optional(),
|
|
4786
|
-
authToken: z__namespace.string().optional(),
|
|
4787
|
-
model: z__namespace.string().optional()
|
|
4788
|
-
});
|
|
4789
|
-
const OpenAIConfigSchema = z__namespace.object({
|
|
4790
|
-
apiKey: z__namespace.string().optional(),
|
|
4791
|
-
baseUrl: z__namespace.string().url().optional(),
|
|
4792
|
-
model: z__namespace.string().optional()
|
|
4793
|
-
});
|
|
4794
|
-
const AzureOpenAIConfigSchema = z__namespace.object({
|
|
4795
|
-
apiKey: z__namespace.string().optional(),
|
|
4796
|
-
endpoint: z__namespace.string().url().optional(),
|
|
4797
|
-
apiVersion: z__namespace.string().optional(),
|
|
4798
|
-
deploymentName: z__namespace.string().optional()
|
|
4799
|
-
});
|
|
4800
|
-
const TogetherAIConfigSchema = z__namespace.object({
|
|
4801
|
-
apiKey: z__namespace.string().optional(),
|
|
4802
|
-
model: z__namespace.string().optional()
|
|
4803
|
-
});
|
|
4804
|
-
const TmuxConfigSchema = z__namespace.object({
|
|
4805
|
-
sessionName: z__namespace.string().optional(),
|
|
4806
|
-
tmpDir: z__namespace.string().optional(),
|
|
4807
|
-
updateEnvironment: z__namespace.boolean().optional()
|
|
4808
|
-
});
|
|
4809
|
-
const EnvironmentVariableSchema = z__namespace.object({
|
|
4810
|
-
name: z__namespace.string().regex(/^[A-Z_][A-Z0-9_]*$/, "Invalid environment variable name"),
|
|
4811
|
-
value: z__namespace.string()
|
|
4812
|
-
});
|
|
4813
|
-
const ProfileCompatibilitySchema = z__namespace.object({
|
|
4814
|
-
claude: z__namespace.boolean().default(true),
|
|
4815
|
-
codex: z__namespace.boolean().default(true),
|
|
4816
|
-
gemini: z__namespace.boolean().default(true)
|
|
4817
|
-
});
|
|
4818
|
-
const AIBackendProfileSchema = z__namespace.object({
|
|
4819
|
-
id: z__namespace.string().uuid(),
|
|
4820
|
-
name: z__namespace.string().min(1).max(100),
|
|
4821
|
-
description: z__namespace.string().max(500).optional(),
|
|
4822
|
-
// Agent-specific configurations
|
|
4823
|
-
anthropicConfig: AnthropicConfigSchema.optional(),
|
|
4824
|
-
openaiConfig: OpenAIConfigSchema.optional(),
|
|
4825
|
-
azureOpenAIConfig: AzureOpenAIConfigSchema.optional(),
|
|
4826
|
-
togetherAIConfig: TogetherAIConfigSchema.optional(),
|
|
4827
|
-
// Tmux configuration
|
|
4828
|
-
tmuxConfig: TmuxConfigSchema.optional(),
|
|
4829
|
-
// Environment variables (validated)
|
|
4830
|
-
environmentVariables: z__namespace.array(EnvironmentVariableSchema).default([]),
|
|
4831
|
-
// Default session type for this profile
|
|
4832
|
-
defaultSessionType: z__namespace.enum(["simple", "worktree"]).optional(),
|
|
4833
|
-
// Default permission mode for this profile (supports both Claude and Codex modes)
|
|
4834
|
-
defaultPermissionMode: z__namespace.enum([
|
|
4835
|
-
"default",
|
|
4836
|
-
"acceptEdits",
|
|
4837
|
-
"bypassPermissions",
|
|
4838
|
-
"plan",
|
|
4839
|
-
// Claude modes
|
|
4840
|
-
"read-only",
|
|
4841
|
-
"safe-yolo",
|
|
4842
|
-
"yolo"
|
|
4843
|
-
// Codex modes
|
|
4844
|
-
]).optional(),
|
|
4845
|
-
// Default model mode for this profile
|
|
4846
|
-
defaultModelMode: z__namespace.string().optional(),
|
|
4847
|
-
// Compatibility metadata
|
|
4848
|
-
compatibility: ProfileCompatibilitySchema.default({ claude: true, codex: true, gemini: true }),
|
|
4849
|
-
// Built-in profile indicator
|
|
4850
|
-
isBuiltIn: z__namespace.boolean().default(false),
|
|
4851
|
-
// Metadata
|
|
4852
|
-
createdAt: z__namespace.number().default(() => Date.now()),
|
|
4853
|
-
updatedAt: z__namespace.number().default(() => Date.now()),
|
|
4854
|
-
version: z__namespace.string().default("1.0.0")
|
|
4855
|
-
});
|
|
4856
|
-
function validateProfileForAgent(profile, agent) {
|
|
4857
|
-
return profile.compatibility[agent];
|
|
4858
|
-
}
|
|
4859
|
-
function getProfileEnvironmentVariables(profile) {
|
|
4860
|
-
const envVars = {};
|
|
4861
|
-
profile.environmentVariables.forEach((envVar) => {
|
|
4862
|
-
envVars[envVar.name] = envVar.value;
|
|
4863
|
-
});
|
|
4864
|
-
if (profile.anthropicConfig) {
|
|
4865
|
-
if (profile.anthropicConfig.baseUrl) envVars.ANTHROPIC_BASE_URL = profile.anthropicConfig.baseUrl;
|
|
4866
|
-
if (profile.anthropicConfig.authToken) envVars.ANTHROPIC_AUTH_TOKEN = profile.anthropicConfig.authToken;
|
|
4867
|
-
if (profile.anthropicConfig.model) envVars.ANTHROPIC_MODEL = profile.anthropicConfig.model;
|
|
4868
|
-
}
|
|
4869
|
-
if (profile.openaiConfig) {
|
|
4870
|
-
if (profile.openaiConfig.apiKey) envVars.OPENAI_API_KEY = profile.openaiConfig.apiKey;
|
|
4871
|
-
if (profile.openaiConfig.baseUrl) envVars.OPENAI_BASE_URL = profile.openaiConfig.baseUrl;
|
|
4872
|
-
if (profile.openaiConfig.model) envVars.OPENAI_MODEL = profile.openaiConfig.model;
|
|
4873
|
-
}
|
|
4874
|
-
if (profile.azureOpenAIConfig) {
|
|
4875
|
-
if (profile.azureOpenAIConfig.apiKey) envVars.AZURE_OPENAI_API_KEY = profile.azureOpenAIConfig.apiKey;
|
|
4876
|
-
if (profile.azureOpenAIConfig.endpoint) envVars.AZURE_OPENAI_ENDPOINT = profile.azureOpenAIConfig.endpoint;
|
|
4877
|
-
if (profile.azureOpenAIConfig.apiVersion) envVars.AZURE_OPENAI_API_VERSION = profile.azureOpenAIConfig.apiVersion;
|
|
4878
|
-
if (profile.azureOpenAIConfig.deploymentName) envVars.AZURE_OPENAI_DEPLOYMENT_NAME = profile.azureOpenAIConfig.deploymentName;
|
|
4879
|
-
}
|
|
4880
|
-
if (profile.togetherAIConfig) {
|
|
4881
|
-
if (profile.togetherAIConfig.apiKey) envVars.TOGETHER_API_KEY = profile.togetherAIConfig.apiKey;
|
|
4882
|
-
if (profile.togetherAIConfig.model) envVars.TOGETHER_MODEL = profile.togetherAIConfig.model;
|
|
4883
|
-
}
|
|
4884
|
-
if (profile.tmuxConfig) {
|
|
4885
|
-
if (profile.tmuxConfig.sessionName !== void 0) envVars.TMUX_SESSION_NAME = profile.tmuxConfig.sessionName;
|
|
4886
|
-
if (profile.tmuxConfig.tmpDir) envVars.TMUX_TMPDIR = profile.tmuxConfig.tmpDir;
|
|
4887
|
-
if (profile.tmuxConfig.updateEnvironment !== void 0) {
|
|
4888
|
-
envVars.TMUX_UPDATE_ENVIRONMENT = profile.tmuxConfig.updateEnvironment.toString();
|
|
4889
|
-
}
|
|
4890
|
-
}
|
|
4891
|
-
return envVars;
|
|
4892
|
-
}
|
|
4893
|
-
const SUPPORTED_SCHEMA_VERSION = 2;
|
|
4894
|
-
const defaultSettings = {
|
|
4895
|
-
schemaVersion: SUPPORTED_SCHEMA_VERSION,
|
|
4896
|
-
onboardingCompleted: false,
|
|
4897
|
-
profiles: [],
|
|
4898
|
-
localEnvironmentVariables: {}
|
|
4899
|
-
};
|
|
4900
|
-
function migrateSettings(raw, fromVersion) {
|
|
4901
|
-
let migrated = { ...raw };
|
|
4902
|
-
if (fromVersion < 2) {
|
|
4903
|
-
if (!migrated.profiles) {
|
|
4904
|
-
migrated.profiles = [];
|
|
4905
|
-
}
|
|
4906
|
-
if (!migrated.localEnvironmentVariables) {
|
|
4907
|
-
migrated.localEnvironmentVariables = {};
|
|
4908
|
-
}
|
|
4909
|
-
migrated.schemaVersion = 2;
|
|
4910
|
-
}
|
|
4911
|
-
return migrated;
|
|
4912
|
-
}
|
|
4913
|
-
async function readSettings() {
|
|
4914
|
-
if (!fs.existsSync(configuration.settingsFile)) {
|
|
4915
|
-
return { ...defaultSettings };
|
|
4916
|
-
}
|
|
4917
|
-
try {
|
|
4918
|
-
const content = await promises.readFile(configuration.settingsFile, "utf8");
|
|
4919
|
-
const raw = JSON.parse(content);
|
|
4920
|
-
const schemaVersion = raw.schemaVersion ?? 1;
|
|
4921
|
-
if (schemaVersion > SUPPORTED_SCHEMA_VERSION) {
|
|
4922
|
-
logger.warn(
|
|
4923
|
-
`\u26A0\uFE0F Settings schema v${schemaVersion} > supported v${SUPPORTED_SCHEMA_VERSION}. Update happy-cli for full functionality.`
|
|
4924
|
-
);
|
|
4925
|
-
}
|
|
4926
|
-
const migrated = migrateSettings(raw, schemaVersion);
|
|
4927
|
-
if (migrated.profiles && Array.isArray(migrated.profiles)) {
|
|
4928
|
-
const validProfiles = [];
|
|
4929
|
-
for (const profile of migrated.profiles) {
|
|
4930
|
-
try {
|
|
4931
|
-
const validated = AIBackendProfileSchema.parse(profile);
|
|
4932
|
-
validProfiles.push(validated);
|
|
4933
|
-
} catch (error) {
|
|
4934
|
-
logger.warn(
|
|
4935
|
-
`\u26A0\uFE0F Invalid profile "${profile?.name || profile?.id || "unknown"}" - skipping. Error: ${error.message}`
|
|
4936
|
-
);
|
|
4937
|
-
}
|
|
4938
|
-
}
|
|
4939
|
-
migrated.profiles = validProfiles;
|
|
4940
|
-
}
|
|
4941
|
-
return { ...defaultSettings, ...migrated };
|
|
4942
|
-
} catch (error) {
|
|
4943
|
-
logger.warn(`Failed to read settings: ${error.message}`);
|
|
4944
|
-
return { ...defaultSettings };
|
|
4945
|
-
}
|
|
4946
|
-
}
|
|
4947
|
-
async function updateSettings(updater) {
|
|
4948
|
-
const LOCK_RETRY_INTERVAL_MS = 100;
|
|
4949
|
-
const MAX_LOCK_ATTEMPTS = 50;
|
|
4950
|
-
const STALE_LOCK_TIMEOUT_MS = 1e4;
|
|
4951
|
-
const lockFile = configuration.settingsFile + ".lock";
|
|
4952
|
-
const tmpFile = configuration.settingsFile + ".tmp";
|
|
4953
|
-
let fileHandle;
|
|
4954
|
-
let attempts = 0;
|
|
4955
|
-
while (attempts < MAX_LOCK_ATTEMPTS) {
|
|
4956
|
-
try {
|
|
4957
|
-
fileHandle = await promises.open(lockFile, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY);
|
|
4958
|
-
break;
|
|
4959
|
-
} catch (err) {
|
|
4960
|
-
if (err.code === "EEXIST") {
|
|
4961
|
-
attempts++;
|
|
4962
|
-
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_INTERVAL_MS));
|
|
4963
|
-
try {
|
|
4964
|
-
const stats = await promises.stat(lockFile);
|
|
4965
|
-
if (Date.now() - stats.mtimeMs > STALE_LOCK_TIMEOUT_MS) {
|
|
4966
|
-
await promises.unlink(lockFile).catch(() => {
|
|
4967
|
-
});
|
|
4968
|
-
}
|
|
4969
|
-
} catch {
|
|
4970
|
-
}
|
|
4971
|
-
} else {
|
|
4972
|
-
throw err;
|
|
4973
|
-
}
|
|
4974
|
-
}
|
|
4975
|
-
}
|
|
4976
|
-
if (!fileHandle) {
|
|
4977
|
-
throw new Error(`Failed to acquire settings lock after ${MAX_LOCK_ATTEMPTS * LOCK_RETRY_INTERVAL_MS / 1e3} seconds`);
|
|
4978
|
-
}
|
|
4979
|
-
try {
|
|
4980
|
-
const current = await readSettings() || { ...defaultSettings };
|
|
4981
|
-
const updated = await updater(current);
|
|
4982
|
-
if (!fs.existsSync(configuration.happyHomeDir)) {
|
|
4983
|
-
await promises.mkdir(configuration.happyHomeDir, { recursive: true });
|
|
4984
|
-
}
|
|
4985
|
-
await promises.writeFile(tmpFile, JSON.stringify(updated, null, 2));
|
|
4986
|
-
await promises.rename(tmpFile, configuration.settingsFile);
|
|
4987
|
-
return updated;
|
|
4988
|
-
} finally {
|
|
4989
|
-
await fileHandle.close();
|
|
4990
|
-
await promises.unlink(lockFile).catch(() => {
|
|
4991
|
-
});
|
|
4992
|
-
}
|
|
4993
|
-
}
|
|
4994
|
-
const credentialsSchema = z__namespace.object({
|
|
4995
|
-
token: z__namespace.string(),
|
|
4996
|
-
secret: z__namespace.string().base64().nullish(),
|
|
4997
|
-
// Legacy
|
|
4998
|
-
encryption: z__namespace.object({
|
|
4999
|
-
publicKey: z__namespace.string().base64(),
|
|
5000
|
-
machineKey: z__namespace.string().base64()
|
|
5001
|
-
}).nullish()
|
|
5002
|
-
});
|
|
5003
|
-
async function readCredentials() {
|
|
5004
|
-
if (!fs.existsSync(configuration.privateKeyFile)) {
|
|
5005
|
-
return null;
|
|
5006
|
-
}
|
|
5007
|
-
try {
|
|
5008
|
-
const keyBase64 = await promises.readFile(configuration.privateKeyFile, "utf8");
|
|
5009
|
-
const credentials = credentialsSchema.parse(JSON.parse(keyBase64));
|
|
5010
|
-
if (credentials.secret) {
|
|
5011
|
-
return {
|
|
5012
|
-
token: credentials.token,
|
|
5013
|
-
encryption: {
|
|
5014
|
-
type: "legacy",
|
|
5015
|
-
secret: new Uint8Array(Buffer.from(credentials.secret, "base64"))
|
|
5016
|
-
}
|
|
5017
|
-
};
|
|
5018
|
-
} else if (credentials.encryption) {
|
|
5019
|
-
return {
|
|
5020
|
-
token: credentials.token,
|
|
5021
|
-
encryption: {
|
|
5022
|
-
type: "dataKey",
|
|
5023
|
-
publicKey: new Uint8Array(Buffer.from(credentials.encryption.publicKey, "base64")),
|
|
5024
|
-
machineKey: new Uint8Array(Buffer.from(credentials.encryption.machineKey, "base64"))
|
|
5025
|
-
}
|
|
5026
|
-
};
|
|
5027
|
-
}
|
|
5028
|
-
} catch {
|
|
5029
|
-
return null;
|
|
5030
|
-
}
|
|
5031
|
-
return null;
|
|
5032
|
-
}
|
|
5033
|
-
async function writeCredentialsLegacy(credentials) {
|
|
5034
|
-
if (!fs.existsSync(configuration.happyHomeDir)) {
|
|
5035
|
-
await promises.mkdir(configuration.happyHomeDir, { recursive: true });
|
|
5036
|
-
}
|
|
5037
|
-
await promises.writeFile(configuration.privateKeyFile, JSON.stringify({
|
|
5038
|
-
secret: encodeBase64(credentials.secret),
|
|
5039
|
-
token: credentials.token
|
|
5040
|
-
}, null, 2));
|
|
5041
|
-
}
|
|
5042
|
-
async function writeCredentialsDataKey(credentials) {
|
|
5043
|
-
if (!fs.existsSync(configuration.happyHomeDir)) {
|
|
5044
|
-
await promises.mkdir(configuration.happyHomeDir, { recursive: true });
|
|
5045
|
-
}
|
|
5046
|
-
await promises.writeFile(configuration.privateKeyFile, JSON.stringify({
|
|
5047
|
-
encryption: { publicKey: encodeBase64(credentials.publicKey), machineKey: encodeBase64(credentials.machineKey) },
|
|
5048
|
-
token: credentials.token
|
|
5049
|
-
}, null, 2));
|
|
5050
|
-
}
|
|
5051
|
-
async function clearCredentials() {
|
|
5052
|
-
if (fs.existsSync(configuration.privateKeyFile)) {
|
|
5053
|
-
await promises.unlink(configuration.privateKeyFile);
|
|
5054
|
-
}
|
|
5055
|
-
}
|
|
5056
|
-
async function clearMachineId() {
|
|
5057
|
-
await updateSettings((settings) => ({
|
|
5058
|
-
...settings,
|
|
5059
|
-
machineId: void 0
|
|
5060
|
-
}));
|
|
5061
|
-
}
|
|
5062
|
-
async function readDaemonState() {
|
|
5063
|
-
try {
|
|
5064
|
-
if (!fs.existsSync(configuration.daemonStateFile)) {
|
|
5065
|
-
return null;
|
|
5066
|
-
}
|
|
5067
|
-
const content = await promises.readFile(configuration.daemonStateFile, "utf-8");
|
|
5068
|
-
return JSON.parse(content);
|
|
5069
|
-
} catch (error) {
|
|
5070
|
-
console.error(`[PERSISTENCE] Daemon state file corrupted: ${configuration.daemonStateFile}`, error);
|
|
5071
|
-
return null;
|
|
5072
|
-
}
|
|
5073
|
-
}
|
|
5074
|
-
function writeDaemonState(state) {
|
|
5075
|
-
fs.writeFileSync(configuration.daemonStateFile, JSON.stringify(state, null, 2), "utf-8");
|
|
5076
|
-
}
|
|
5077
|
-
async function clearDaemonState() {
|
|
5078
|
-
if (fs.existsSync(configuration.daemonStateFile)) {
|
|
5079
|
-
await promises.unlink(configuration.daemonStateFile);
|
|
5080
|
-
}
|
|
5081
|
-
if (fs.existsSync(configuration.daemonLockFile)) {
|
|
5082
|
-
try {
|
|
5083
|
-
await promises.unlink(configuration.daemonLockFile);
|
|
5084
|
-
} catch {
|
|
5085
|
-
}
|
|
5086
|
-
}
|
|
5087
|
-
}
|
|
5088
|
-
async function acquireDaemonLock(maxAttempts = 5, delayIncrementMs = 200) {
|
|
5089
|
-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
5090
|
-
try {
|
|
5091
|
-
const fileHandle = await promises.open(
|
|
5092
|
-
configuration.daemonLockFile,
|
|
5093
|
-
fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY
|
|
5094
|
-
);
|
|
5095
|
-
await fileHandle.writeFile(String(process.pid));
|
|
5096
|
-
return fileHandle;
|
|
5097
|
-
} catch (error) {
|
|
5098
|
-
if (error.code === "EEXIST") {
|
|
5099
|
-
try {
|
|
5100
|
-
const lockPid = fs.readFileSync(configuration.daemonLockFile, "utf-8").trim();
|
|
5101
|
-
if (lockPid && !isNaN(Number(lockPid))) {
|
|
5102
|
-
try {
|
|
5103
|
-
process.kill(Number(lockPid), 0);
|
|
5104
|
-
} catch {
|
|
5105
|
-
fs.unlinkSync(configuration.daemonLockFile);
|
|
5106
|
-
continue;
|
|
5107
|
-
}
|
|
5108
|
-
}
|
|
5109
|
-
} catch {
|
|
5110
|
-
}
|
|
5111
|
-
}
|
|
5112
|
-
if (attempt === maxAttempts) {
|
|
5113
|
-
return null;
|
|
5114
|
-
}
|
|
5115
|
-
const delayMs = attempt * delayIncrementMs;
|
|
5116
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
5117
|
-
}
|
|
5118
|
-
}
|
|
5119
|
-
return null;
|
|
5120
|
-
}
|
|
5121
|
-
async function releaseDaemonLock(lockHandle) {
|
|
5122
|
-
try {
|
|
5123
|
-
await lockHandle.close();
|
|
5124
|
-
} catch {
|
|
5125
|
-
}
|
|
5126
|
-
try {
|
|
5127
|
-
if (fs.existsSync(configuration.daemonLockFile)) {
|
|
5128
|
-
fs.unlinkSync(configuration.daemonLockFile);
|
|
5129
|
-
}
|
|
5130
|
-
} catch {
|
|
5131
|
-
}
|
|
5132
|
-
}
|
|
5133
|
-
|
|
5134
|
-
var persistence = /*#__PURE__*/Object.freeze({
|
|
5135
|
-
__proto__: null,
|
|
5136
|
-
AIBackendProfileSchema: AIBackendProfileSchema,
|
|
5137
|
-
SUPPORTED_SCHEMA_VERSION: SUPPORTED_SCHEMA_VERSION,
|
|
5138
|
-
acquireDaemonLock: acquireDaemonLock,
|
|
5139
|
-
clearCredentials: clearCredentials,
|
|
5140
|
-
clearDaemonState: clearDaemonState,
|
|
5141
|
-
clearMachineId: clearMachineId,
|
|
5142
|
-
getProfileEnvironmentVariables: getProfileEnvironmentVariables,
|
|
5143
|
-
readCredentials: readCredentials,
|
|
5144
|
-
readDaemonState: readDaemonState,
|
|
5145
|
-
readSettings: readSettings,
|
|
5146
|
-
releaseDaemonLock: releaseDaemonLock,
|
|
5147
|
-
updateSettings: updateSettings,
|
|
5148
|
-
validateProfileForAgent: validateProfileForAgent,
|
|
5149
|
-
writeCredentialsDataKey: writeCredentialsDataKey,
|
|
5150
|
-
writeCredentialsLegacy: writeCredentialsLegacy,
|
|
5151
|
-
writeDaemonState: writeDaemonState
|
|
5152
|
-
});
|
|
5153
|
-
|
|
5154
3703
|
function buildRunningDaemonState(previousState, currentRuntimeState, opts = {}) {
|
|
5155
3704
|
const currentHttpPort = currentRuntimeState?.httpPort;
|
|
5156
3705
|
const currentStartedAt = currentRuntimeState?.startedAt;
|
|
@@ -5216,11 +3765,11 @@ class OpenClawTunnelManager {
|
|
|
5216
3765
|
this.tunnels.set(tunnelId, tunnel);
|
|
5217
3766
|
return new Promise((resolve) => {
|
|
5218
3767
|
try {
|
|
5219
|
-
logger.debug(`[OpenClawTunnel] Connecting to ${config.url}`);
|
|
3768
|
+
logger.logger.debug(`[OpenClawTunnel] Connecting to ${config.url}`);
|
|
5220
3769
|
const ws = new WebSocket(config.url);
|
|
5221
3770
|
tunnel.ws = ws;
|
|
5222
3771
|
ws.on("open", () => {
|
|
5223
|
-
logger.debug(`[OpenClawTunnel] WebSocket opened for tunnel ${tunnelId}`);
|
|
3772
|
+
logger.logger.debug(`[OpenClawTunnel] WebSocket opened for tunnel ${tunnelId}`);
|
|
5224
3773
|
});
|
|
5225
3774
|
ws.on("message", async (data) => {
|
|
5226
3775
|
const result = await this.handleMessage(tunnelId, tunnel, data.toString());
|
|
@@ -5229,7 +3778,7 @@ class OpenClawTunnelManager {
|
|
|
5229
3778
|
}
|
|
5230
3779
|
});
|
|
5231
3780
|
ws.on("error", (error) => {
|
|
5232
|
-
logger.debug(`[OpenClawTunnel] WebSocket error for tunnel ${tunnelId}:`, error);
|
|
3781
|
+
logger.logger.debug(`[OpenClawTunnel] WebSocket error for tunnel ${tunnelId}:`, error);
|
|
5233
3782
|
tunnel.status = "error";
|
|
5234
3783
|
this.failAllPending(tunnel, new Error("WebSocket error"));
|
|
5235
3784
|
resolve({
|
|
@@ -5240,7 +3789,7 @@ class OpenClawTunnelManager {
|
|
|
5240
3789
|
});
|
|
5241
3790
|
ws.on("close", (code, reason) => {
|
|
5242
3791
|
const reasonStr = reason?.toString() || "";
|
|
5243
|
-
logger.debug(`[OpenClawTunnel] WebSocket closed for tunnel ${tunnelId}: ${code} ${reasonStr}`);
|
|
3792
|
+
logger.logger.debug(`[OpenClawTunnel] WebSocket closed for tunnel ${tunnelId}: ${code} ${reasonStr}`);
|
|
5244
3793
|
const isPairingRequired = reasonStr.includes("unauthorized") || reasonStr.includes("NOT_PAIRED");
|
|
5245
3794
|
this.failAllPending(tunnel, new Error(reasonStr || "Connection closed"));
|
|
5246
3795
|
if (tunnel.status === "connecting") {
|
|
@@ -5372,7 +3921,7 @@ class OpenClawTunnelManager {
|
|
|
5372
3921
|
tunnel.ws = null;
|
|
5373
3922
|
}
|
|
5374
3923
|
this.tunnels.delete(tunnelId);
|
|
5375
|
-
logger.debug(`[OpenClawTunnel] Closed tunnel ${tunnelId}`);
|
|
3924
|
+
logger.logger.debug(`[OpenClawTunnel] Closed tunnel ${tunnelId}`);
|
|
5376
3925
|
return true;
|
|
5377
3926
|
}
|
|
5378
3927
|
async handleMessage(tunnelId, tunnel, data) {
|
|
@@ -5380,7 +3929,7 @@ class OpenClawTunnelManager {
|
|
|
5380
3929
|
try {
|
|
5381
3930
|
frame = JSON.parse(data);
|
|
5382
3931
|
} catch {
|
|
5383
|
-
logger.debug(`[OpenClawTunnel] Invalid JSON: ${data.slice(0, 100)}`);
|
|
3932
|
+
logger.logger.debug(`[OpenClawTunnel] Invalid JSON: ${data.slice(0, 100)}`);
|
|
5384
3933
|
return null;
|
|
5385
3934
|
}
|
|
5386
3935
|
if (frame.type === "res") {
|
|
@@ -5405,7 +3954,7 @@ class OpenClawTunnelManager {
|
|
|
5405
3954
|
if (frame.event === "connect.challenge" && !tunnel.connectSent) {
|
|
5406
3955
|
const nonce = payload?.nonce;
|
|
5407
3956
|
if (nonce) {
|
|
5408
|
-
logger.debug(`[OpenClawTunnel] Received challenge nonce for tunnel ${tunnelId}`);
|
|
3957
|
+
logger.logger.debug(`[OpenClawTunnel] Received challenge nonce for tunnel ${tunnelId}`);
|
|
5409
3958
|
tunnel.connectNonce = nonce;
|
|
5410
3959
|
}
|
|
5411
3960
|
return this.sendConnect(tunnelId, tunnel);
|
|
@@ -5485,7 +4034,7 @@ class OpenClawTunnelManager {
|
|
|
5485
4034
|
tunnel.mainSessionKey = result.snapshot?.sessionDefaults?.mainSessionKey ?? null;
|
|
5486
4035
|
tunnel.serverHost = result.server?.host ?? null;
|
|
5487
4036
|
tunnel.deviceToken = result.auth?.deviceToken ?? null;
|
|
5488
|
-
logger.debug(`[OpenClawTunnel] Connected tunnel ${tunnelId}, server: ${tunnel.serverHost}`);
|
|
4037
|
+
logger.logger.debug(`[OpenClawTunnel] Connected tunnel ${tunnelId}, server: ${tunnel.serverHost}`);
|
|
5489
4038
|
resolve({
|
|
5490
4039
|
ok: true,
|
|
5491
4040
|
status: "connected",
|
|
@@ -5603,7 +4152,7 @@ function registerOpenClawHandlers(rpcManager, encryptionContext) {
|
|
|
5603
4152
|
if (Array.isArray(payload.messages) && payload.messages.length > 0) {
|
|
5604
4153
|
return {
|
|
5605
4154
|
__preEncrypted: true,
|
|
5606
|
-
data: encodeBase64(encryptLegacy(result, encryptionContext.key))
|
|
4155
|
+
data: logger.encodeBase64(logger.encryptLegacy(result, encryptionContext.key))
|
|
5607
4156
|
};
|
|
5608
4157
|
}
|
|
5609
4158
|
}
|
|
@@ -5655,7 +4204,7 @@ const SESSION_CACHE_REFRESH_MODES = /* @__PURE__ */ new Set([
|
|
|
5655
4204
|
"background"
|
|
5656
4205
|
]);
|
|
5657
4206
|
function getSessionMetadataCachePath(cacheFileName) {
|
|
5658
|
-
return path.join(configuration.happyHomeDir, cacheFileName);
|
|
4207
|
+
return path.join(logger.configuration.happyHomeDir, cacheFileName);
|
|
5659
4208
|
}
|
|
5660
4209
|
function getCacheLockPath(cachePath) {
|
|
5661
4210
|
return `${cachePath}.lock`;
|
|
@@ -6702,7 +5251,7 @@ const GeminiSessionLineSchema = z.z.discriminatedUnion("type", [
|
|
|
6702
5251
|
|
|
6703
5252
|
const SESSIONS_DIR_NAME = "gemini_sessions";
|
|
6704
5253
|
function getGeminiSessionsDir() {
|
|
6705
|
-
return path.join(configuration.happyHomeDir, SESSIONS_DIR_NAME);
|
|
5254
|
+
return path.join(logger.configuration.happyHomeDir, SESSIONS_DIR_NAME);
|
|
6706
5255
|
}
|
|
6707
5256
|
function getGeminiSessionFilePath(sessionId) {
|
|
6708
5257
|
return path.join(getGeminiSessionsDir(), `${sessionId}.jsonl`);
|
|
@@ -6722,9 +5271,9 @@ class GeminiSessionWriter {
|
|
|
6722
5271
|
value: meta ?? {},
|
|
6723
5272
|
timestamp: Date.now()
|
|
6724
5273
|
});
|
|
6725
|
-
logger.debug(`[GeminiSessionWriter] Initialized: ${this.filePath}`);
|
|
5274
|
+
logger.logger.debug(`[GeminiSessionWriter] Initialized: ${this.filePath}`);
|
|
6726
5275
|
} catch (error) {
|
|
6727
|
-
logger.debug(`[GeminiSessionWriter] Failed to init:`, error);
|
|
5276
|
+
logger.logger.debug(`[GeminiSessionWriter] Failed to init:`, error);
|
|
6728
5277
|
}
|
|
6729
5278
|
}
|
|
6730
5279
|
getFilePath() {
|
|
@@ -6783,7 +5332,7 @@ class GeminiSessionWriter {
|
|
|
6783
5332
|
*/
|
|
6784
5333
|
appendLine(line) {
|
|
6785
5334
|
this.writePromise = this.writePromise.then(() => promises.appendFile(this.filePath, JSON.stringify(line) + "\n", "utf-8")).catch((error) => {
|
|
6786
|
-
logger.debug(`[GeminiSessionWriter] Write failed:`, error);
|
|
5335
|
+
logger.logger.debug(`[GeminiSessionWriter] Write failed:`, error);
|
|
6787
5336
|
});
|
|
6788
5337
|
}
|
|
6789
5338
|
}
|
|
@@ -6794,7 +5343,7 @@ async function readGeminiSessionLog(sessionId) {
|
|
|
6794
5343
|
try {
|
|
6795
5344
|
content = await promises.readFile(filePath, "utf-8");
|
|
6796
5345
|
} catch (error) {
|
|
6797
|
-
logger.debug(`[GeminiSessionReader] Session file not found: ${filePath}`);
|
|
5346
|
+
logger.logger.debug(`[GeminiSessionReader] Session file not found: ${filePath}`);
|
|
6798
5347
|
return [];
|
|
6799
5348
|
}
|
|
6800
5349
|
const lines = content.split("\n");
|
|
@@ -6807,10 +5356,10 @@ async function readGeminiSessionLog(sessionId) {
|
|
|
6807
5356
|
if (result.success) {
|
|
6808
5357
|
entries.push(result.data);
|
|
6809
5358
|
} else {
|
|
6810
|
-
logger.debug(`[GeminiSessionReader] Skipping invalid line: ${line.substring(0, 100)}`);
|
|
5359
|
+
logger.logger.debug(`[GeminiSessionReader] Skipping invalid line: ${line.substring(0, 100)}`);
|
|
6811
5360
|
}
|
|
6812
5361
|
} catch {
|
|
6813
|
-
logger.debug(`[GeminiSessionReader] Skipping non-JSON line: ${line.substring(0, 100)}`);
|
|
5362
|
+
logger.logger.debug(`[GeminiSessionReader] Skipping non-JSON line: ${line.substring(0, 100)}`);
|
|
6814
5363
|
}
|
|
6815
5364
|
}
|
|
6816
5365
|
return entries;
|
|
@@ -7032,7 +5581,7 @@ async function forkAndTruncateGeminiSession(sessionId, truncateBeforeUuid) {
|
|
|
7032
5581
|
return { success: false, errorMessage: truncateResult.errorMessage };
|
|
7033
5582
|
}
|
|
7034
5583
|
}
|
|
7035
|
-
logger.debug(`[GeminiSessionFork] Forked ${sessionId} -> ${newSessionId}`);
|
|
5584
|
+
logger.logger.debug(`[GeminiSessionFork] Forked ${sessionId} -> ${newSessionId}`);
|
|
7036
5585
|
return { success: true, newSessionId };
|
|
7037
5586
|
} catch (error) {
|
|
7038
5587
|
try {
|
|
@@ -7169,14 +5718,14 @@ function isSystemMessage(text) {
|
|
|
7169
5718
|
async function readCodexSessionUserMessages(codexSessionId, limit = 50) {
|
|
7170
5719
|
const filePath = findCodexSessionFile(codexSessionId);
|
|
7171
5720
|
if (!filePath) {
|
|
7172
|
-
logger.debug(`[CodexSessionReader] Session file not found for: ${codexSessionId}`);
|
|
5721
|
+
logger.logger.debug(`[CodexSessionReader] Session file not found for: ${codexSessionId}`);
|
|
7173
5722
|
return [];
|
|
7174
5723
|
}
|
|
7175
5724
|
let content;
|
|
7176
5725
|
try {
|
|
7177
5726
|
content = await promises.readFile(filePath, "utf-8");
|
|
7178
5727
|
} catch (error) {
|
|
7179
|
-
logger.debug(`[CodexSessionReader] Failed to read file: ${filePath}`, error);
|
|
5728
|
+
logger.logger.debug(`[CodexSessionReader] Failed to read file: ${filePath}`, error);
|
|
7180
5729
|
return [];
|
|
7181
5730
|
}
|
|
7182
5731
|
const lines = content.split("\n");
|
|
@@ -7473,7 +6022,7 @@ async function forkAndTruncateCodexSession(codexSessionId, truncateBeforeUuid) {
|
|
|
7473
6022
|
}
|
|
7474
6023
|
await promises.writeFile(newPath, outputLines.join("\n") + "\n", "utf-8");
|
|
7475
6024
|
}
|
|
7476
|
-
logger.debug(`[CodexSessionFork] Forked ${path.basename(originalPath)} -> ${path.basename(newPath)}`);
|
|
6025
|
+
logger.logger.debug(`[CodexSessionFork] Forked ${path.basename(originalPath)} -> ${path.basename(newPath)}`);
|
|
7477
6026
|
return { success: true, newFilePath: newPath };
|
|
7478
6027
|
} catch (error) {
|
|
7479
6028
|
try {
|
|
@@ -7660,7 +6209,7 @@ function createSessionCacheStatsReporter(saveStats, label) {
|
|
|
7660
6209
|
const stats = pendingStats;
|
|
7661
6210
|
pendingStats = null;
|
|
7662
6211
|
flushPromise = saveStats(stats).catch((error) => {
|
|
7663
|
-
logger.debug(`[API MACHINE] Failed to persist ${label} session cache stats`, error);
|
|
6212
|
+
logger.logger.debug(`[API MACHINE] Failed to persist ${label} session cache stats`, error);
|
|
7664
6213
|
}).then(() => {
|
|
7665
6214
|
flushPromise = null;
|
|
7666
6215
|
});
|
|
@@ -7692,7 +6241,7 @@ class ApiMachineClient {
|
|
|
7692
6241
|
scopePrefix: this.machine.id,
|
|
7693
6242
|
encryptionKey: this.machine.encryptionKey,
|
|
7694
6243
|
encryptionVariant: this.machine.encryptionVariant,
|
|
7695
|
-
logger: (msg, data) => logger.debug(msg, data)
|
|
6244
|
+
logger: (msg, data) => logger.logger.debug(msg, data)
|
|
7696
6245
|
});
|
|
7697
6246
|
registerCommonHandlers(this.rpcHandlerManager, process.env.HAPPY_DAEMON_ROOT || "/", this.machine.id);
|
|
7698
6247
|
registerOpenClawHandlers(this.rpcHandlerManager, {
|
|
@@ -7708,6 +6257,11 @@ class ApiMachineClient {
|
|
|
7708
6257
|
});
|
|
7709
6258
|
}
|
|
7710
6259
|
socket;
|
|
6260
|
+
/** The daemon's live connection. Reused for device RPC so a CLI command does
|
|
6261
|
+
* not spend ~500ms building its own TLS + WebSocket handshake. */
|
|
6262
|
+
get rpcSocket() {
|
|
6263
|
+
return this.socket;
|
|
6264
|
+
}
|
|
7711
6265
|
keepAliveInterval = null;
|
|
7712
6266
|
rpcHandlerManager;
|
|
7713
6267
|
claudeCache = new SessionCache({
|
|
@@ -7741,7 +6295,7 @@ class ApiMachineClient {
|
|
|
7741
6295
|
event,
|
|
7742
6296
|
payload
|
|
7743
6297
|
};
|
|
7744
|
-
const encryptedData = encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, eventData));
|
|
6298
|
+
const encryptedData = logger.encodeBase64(logger.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, eventData));
|
|
7745
6299
|
const rpcMethod = `${this.machine.id}:openclaw-tunnel-event`;
|
|
7746
6300
|
this.socket.emit("rpc-call", {
|
|
7747
6301
|
method: rpcMethod,
|
|
@@ -7757,12 +6311,12 @@ class ApiMachineClient {
|
|
|
7757
6311
|
orchestratorCancel
|
|
7758
6312
|
}) {
|
|
7759
6313
|
this.rpcHandlerManager.registerHandler("spawn-happy-session", async (params) => {
|
|
7760
|
-
const settings = await readSettings();
|
|
6314
|
+
const settings = await logger.readSettings();
|
|
7761
6315
|
if (settings.deviceMode) {
|
|
7762
6316
|
throw new Error("This machine is enrolled as a device and does not host sessions");
|
|
7763
6317
|
}
|
|
7764
6318
|
const { directory, sessionId, resumeSessionId, intent, sessionTitle, skipForkSession, machineId, approvedNewDirectoryCreation, agent, token, environmentVariables, worktreeBasePath, worktreeBranchName, workspaceRepos, workspacePath, repoScripts, mcpServers } = params || {};
|
|
7765
|
-
logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
|
|
6319
|
+
logger.logger.debug(`[API MACHINE] Spawning session with params: ${JSON.stringify(params)}`);
|
|
7766
6320
|
if (!directory) {
|
|
7767
6321
|
throw new Error("Directory is required");
|
|
7768
6322
|
}
|
|
@@ -7772,10 +6326,10 @@ class ApiMachineClient {
|
|
|
7772
6326
|
this.claudeCache.invalidate();
|
|
7773
6327
|
this.geminiCache.invalidate();
|
|
7774
6328
|
this.codexCache.invalidate();
|
|
7775
|
-
logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
|
|
6329
|
+
logger.logger.debug(`[API MACHINE] Spawned session ${result.sessionId}`);
|
|
7776
6330
|
return { type: "success", sessionId: result.sessionId };
|
|
7777
6331
|
case "requestToApproveDirectoryCreation":
|
|
7778
|
-
logger.debug(`[API MACHINE] Requesting directory creation approval for: ${result.directory}`);
|
|
6332
|
+
logger.logger.debug(`[API MACHINE] Requesting directory creation approval for: ${result.directory}`);
|
|
7779
6333
|
return { type: "requestToApproveDirectoryCreation", directory: result.directory };
|
|
7780
6334
|
case "error":
|
|
7781
6335
|
throw new Error(result.errorMessage);
|
|
@@ -7791,9 +6345,9 @@ class ApiMachineClient {
|
|
|
7791
6345
|
const { worktreePath, basePath, branchName, archiveScript, deleteBranch } = repo;
|
|
7792
6346
|
try {
|
|
7793
6347
|
if (archiveScript) {
|
|
7794
|
-
logger.info(`[DAEMON] Running archive script in ${worktreePath}...`);
|
|
6348
|
+
logger.logger.info(`[DAEMON] Running archive script in ${worktreePath}...`);
|
|
7795
6349
|
node_child_process.execSync(archiveScript, { cwd: worktreePath, stdio: "pipe", timeout: 3e5 });
|
|
7796
|
-
logger.info(`[DAEMON] Archive script completed for ${worktreePath}`);
|
|
6350
|
+
logger.logger.info(`[DAEMON] Archive script completed for ${worktreePath}`);
|
|
7797
6351
|
}
|
|
7798
6352
|
if (worktreePath !== basePath) {
|
|
7799
6353
|
try {
|
|
@@ -7803,7 +6357,7 @@ class ApiMachineClient {
|
|
|
7803
6357
|
timeout: 3e4
|
|
7804
6358
|
});
|
|
7805
6359
|
} catch (err) {
|
|
7806
|
-
logger.warn(`[DAEMON] git worktree remove failed: ${err.message}`);
|
|
6360
|
+
logger.logger.warn(`[DAEMON] git worktree remove failed: ${err.message}`);
|
|
7807
6361
|
}
|
|
7808
6362
|
if (deleteBranch && branchName) {
|
|
7809
6363
|
try {
|
|
@@ -7813,13 +6367,13 @@ class ApiMachineClient {
|
|
|
7813
6367
|
timeout: 1e4
|
|
7814
6368
|
});
|
|
7815
6369
|
} catch (err) {
|
|
7816
|
-
logger.warn(`[DAEMON] git branch -D failed: ${err.message}`);
|
|
6370
|
+
logger.logger.warn(`[DAEMON] git branch -D failed: ${err.message}`);
|
|
7817
6371
|
}
|
|
7818
6372
|
}
|
|
7819
6373
|
}
|
|
7820
6374
|
results.push({ repo: worktreePath, success: true });
|
|
7821
6375
|
} catch (err) {
|
|
7822
|
-
logger.warn(`[DAEMON] Archive failed for ${worktreePath}: ${err.message}`);
|
|
6376
|
+
logger.logger.warn(`[DAEMON] Archive failed for ${worktreePath}: ${err.message}`);
|
|
7823
6377
|
results.push({ repo: worktreePath, success: false, error: err.message });
|
|
7824
6378
|
}
|
|
7825
6379
|
}
|
|
@@ -7828,7 +6382,7 @@ class ApiMachineClient {
|
|
|
7828
6382
|
const entries = fs.readdirSync(workspacePath);
|
|
7829
6383
|
if (entries.length === 0) {
|
|
7830
6384
|
fs.rmdirSync(workspacePath);
|
|
7831
|
-
logger.info(`[DAEMON] Removed empty workspace directory: ${workspacePath}`);
|
|
6385
|
+
logger.logger.info(`[DAEMON] Removed empty workspace directory: ${workspacePath}`);
|
|
7832
6386
|
}
|
|
7833
6387
|
} catch {
|
|
7834
6388
|
}
|
|
@@ -7847,13 +6401,13 @@ class ApiMachineClient {
|
|
|
7847
6401
|
this.claudeCache.invalidate();
|
|
7848
6402
|
this.geminiCache.invalidate();
|
|
7849
6403
|
this.codexCache.invalidate();
|
|
7850
|
-
logger.debug(`[API MACHINE] Stopped session ${sessionId}`);
|
|
6404
|
+
logger.logger.debug(`[API MACHINE] Stopped session ${sessionId}`);
|
|
7851
6405
|
return { message: "Session stopped" };
|
|
7852
6406
|
});
|
|
7853
6407
|
this.rpcHandlerManager.registerHandler("stop-daemon", () => {
|
|
7854
|
-
logger.debug("[API MACHINE] Received stop-daemon RPC request");
|
|
6408
|
+
logger.logger.debug("[API MACHINE] Received stop-daemon RPC request");
|
|
7855
6409
|
setTimeout(() => {
|
|
7856
|
-
logger.debug("[API MACHINE] Initiating daemon shutdown from RPC");
|
|
6410
|
+
logger.logger.debug("[API MACHINE] Initiating daemon shutdown from RPC");
|
|
7857
6411
|
requestShutdown();
|
|
7858
6412
|
}, 100);
|
|
7859
6413
|
return { message: "Daemon stop request acknowledged, starting shutdown sequence..." };
|
|
@@ -8091,21 +6645,21 @@ class ApiMachineClient {
|
|
|
8091
6645
|
* for example to set a custom name.
|
|
8092
6646
|
*/
|
|
8093
6647
|
async updateMachineMetadata(handler) {
|
|
8094
|
-
await backoff(async () => {
|
|
6648
|
+
await time.backoff(async () => {
|
|
8095
6649
|
const updated = handler(this.machine.metadata);
|
|
8096
6650
|
const answer = await this.socket.emitWithAck("machine-update-metadata", {
|
|
8097
6651
|
machineId: this.machine.id,
|
|
8098
|
-
metadata: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
|
|
6652
|
+
metadata: logger.encodeBase64(logger.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
|
|
8099
6653
|
expectedVersion: this.machine.metadataVersion
|
|
8100
6654
|
});
|
|
8101
6655
|
if (answer.result === "success") {
|
|
8102
|
-
this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.metadata));
|
|
6656
|
+
this.machine.metadata = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(answer.metadata));
|
|
8103
6657
|
this.machine.metadataVersion = answer.version;
|
|
8104
|
-
logger.debug("[API MACHINE] Metadata updated successfully");
|
|
6658
|
+
logger.logger.debug("[API MACHINE] Metadata updated successfully");
|
|
8105
6659
|
} else if (answer.result === "version-mismatch") {
|
|
8106
6660
|
if (answer.version > this.machine.metadataVersion) {
|
|
8107
6661
|
this.machine.metadataVersion = answer.version;
|
|
8108
|
-
this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.metadata));
|
|
6662
|
+
this.machine.metadata = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(answer.metadata));
|
|
8109
6663
|
}
|
|
8110
6664
|
throw new Error("Metadata version mismatch");
|
|
8111
6665
|
}
|
|
@@ -8116,29 +6670,29 @@ class ApiMachineClient {
|
|
|
8116
6670
|
* Simplified without lock - relies on backoff for retry
|
|
8117
6671
|
*/
|
|
8118
6672
|
async updateDaemonState(handler) {
|
|
8119
|
-
await backoff(async () => {
|
|
6673
|
+
await time.backoff(async () => {
|
|
8120
6674
|
const updated = handler(this.machine.daemonState);
|
|
8121
6675
|
const answer = await this.socket.emitWithAck("machine-update-state", {
|
|
8122
6676
|
machineId: this.machine.id,
|
|
8123
|
-
daemonState: encodeBase64(encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
|
|
6677
|
+
daemonState: logger.encodeBase64(logger.encrypt(this.machine.encryptionKey, this.machine.encryptionVariant, updated)),
|
|
8124
6678
|
expectedVersion: this.machine.daemonStateVersion
|
|
8125
6679
|
});
|
|
8126
6680
|
if (answer.result === "success") {
|
|
8127
|
-
this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
|
|
6681
|
+
this.machine.daemonState = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(answer.daemonState));
|
|
8128
6682
|
this.machine.daemonStateVersion = answer.version;
|
|
8129
|
-
logger.debug("[API MACHINE] Daemon state updated successfully");
|
|
6683
|
+
logger.logger.debug("[API MACHINE] Daemon state updated successfully");
|
|
8130
6684
|
} else if (answer.result === "version-mismatch") {
|
|
8131
6685
|
if (answer.version > this.machine.daemonStateVersion) {
|
|
8132
6686
|
this.machine.daemonStateVersion = answer.version;
|
|
8133
|
-
this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(answer.daemonState));
|
|
6687
|
+
this.machine.daemonState = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(answer.daemonState));
|
|
8134
6688
|
}
|
|
8135
6689
|
throw new Error("Daemon state version mismatch");
|
|
8136
6690
|
}
|
|
8137
6691
|
});
|
|
8138
6692
|
}
|
|
8139
6693
|
connect() {
|
|
8140
|
-
const serverUrl = configuration.serverUrl.replace(/^http/, "ws");
|
|
8141
|
-
logger.debug(`[API MACHINE] Connecting to ${serverUrl}`);
|
|
6694
|
+
const serverUrl = logger.configuration.serverUrl.replace(/^http/, "ws");
|
|
6695
|
+
logger.logger.debug(`[API MACHINE] Connecting to ${serverUrl}`);
|
|
8142
6696
|
this.socket = socket_ioClient.io(serverUrl, {
|
|
8143
6697
|
transports: ["websocket"],
|
|
8144
6698
|
auth: {
|
|
@@ -8152,7 +6706,7 @@ class ApiMachineClient {
|
|
|
8152
6706
|
reconnectionDelayMax: 5e3
|
|
8153
6707
|
});
|
|
8154
6708
|
this.socket.on("connect", () => {
|
|
8155
|
-
logger.debug("[API MACHINE] Connected to server");
|
|
6709
|
+
logger.logger.debug("[API MACHINE] Connected to server");
|
|
8156
6710
|
this.updateDaemonState((state) => buildRunningDaemonState(
|
|
8157
6711
|
state,
|
|
8158
6712
|
this.currentRuntimeDaemonState ?? this.machine.daemonState
|
|
@@ -8161,36 +6715,36 @@ class ApiMachineClient {
|
|
|
8161
6715
|
this.startKeepAlive();
|
|
8162
6716
|
});
|
|
8163
6717
|
this.socket.on("disconnect", () => {
|
|
8164
|
-
logger.debug("[API MACHINE] Disconnected from server");
|
|
6718
|
+
logger.logger.debug("[API MACHINE] Disconnected from server");
|
|
8165
6719
|
this.rpcHandlerManager.onSocketDisconnect();
|
|
8166
6720
|
this.stopKeepAlive();
|
|
8167
6721
|
});
|
|
8168
6722
|
this.socket.on("rpc-request", async (data, callback) => {
|
|
8169
|
-
logger.debugLargeJson(`[API MACHINE] Received RPC request:`, data);
|
|
6723
|
+
logger.logger.debugLargeJson(`[API MACHINE] Received RPC request:`, data);
|
|
8170
6724
|
callback(await this.rpcHandlerManager.handleRequest(data));
|
|
8171
6725
|
});
|
|
8172
6726
|
this.socket.on("update", (data) => {
|
|
8173
6727
|
if (data.body.t === "update-machine" && data.body.machineId === this.machine.id) {
|
|
8174
6728
|
const update = data.body;
|
|
8175
6729
|
if (update.metadata) {
|
|
8176
|
-
logger.debug("[API MACHINE] Received external metadata update");
|
|
8177
|
-
this.machine.metadata = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(update.metadata.value));
|
|
6730
|
+
logger.logger.debug("[API MACHINE] Received external metadata update");
|
|
6731
|
+
this.machine.metadata = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(update.metadata.value));
|
|
8178
6732
|
this.machine.metadataVersion = update.metadata.version;
|
|
8179
6733
|
}
|
|
8180
6734
|
if (update.daemonState) {
|
|
8181
|
-
logger.debug("[API MACHINE] Received external daemon state update");
|
|
8182
|
-
this.machine.daemonState = decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, decodeBase64(update.daemonState.value));
|
|
6735
|
+
logger.logger.debug("[API MACHINE] Received external daemon state update");
|
|
6736
|
+
this.machine.daemonState = logger.decrypt(this.machine.encryptionKey, this.machine.encryptionVariant, logger.decodeBase64(update.daemonState.value));
|
|
8183
6737
|
this.machine.daemonStateVersion = update.daemonState.version;
|
|
8184
6738
|
}
|
|
8185
6739
|
} else {
|
|
8186
|
-
logger.debug(`[API MACHINE] Received unknown update type: ${data.body.t}`);
|
|
6740
|
+
logger.logger.debug(`[API MACHINE] Received unknown update type: ${data.body.t}`);
|
|
8187
6741
|
}
|
|
8188
6742
|
});
|
|
8189
6743
|
this.socket.on("connect_error", (error) => {
|
|
8190
|
-
logger.debug(`[API MACHINE] Connection error: ${error.message}`);
|
|
6744
|
+
logger.logger.debug(`[API MACHINE] Connection error: ${error.message}`);
|
|
8191
6745
|
});
|
|
8192
6746
|
this.socket.io.on("error", (error) => {
|
|
8193
|
-
logger.debug("[API MACHINE] Socket error:", error);
|
|
6747
|
+
logger.logger.debug("[API MACHINE] Socket error:", error);
|
|
8194
6748
|
});
|
|
8195
6749
|
}
|
|
8196
6750
|
startKeepAlive() {
|
|
@@ -8200,27 +6754,27 @@ class ApiMachineClient {
|
|
|
8200
6754
|
machineId: this.machine.id,
|
|
8201
6755
|
time: Date.now()
|
|
8202
6756
|
};
|
|
8203
|
-
if (isDebug()) {
|
|
8204
|
-
logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
|
|
6757
|
+
if (logger.isDebug()) {
|
|
6758
|
+
logger.logger.debugLargeJson(`[API MACHINE] Emitting machine-alive`, payload);
|
|
8205
6759
|
}
|
|
8206
6760
|
this.socket.emit("machine-alive", payload);
|
|
8207
6761
|
}, 2e4);
|
|
8208
|
-
logger.debug("[API MACHINE] Keep-alive started (20s interval)");
|
|
6762
|
+
logger.logger.debug("[API MACHINE] Keep-alive started (20s interval)");
|
|
8209
6763
|
}
|
|
8210
6764
|
stopKeepAlive() {
|
|
8211
6765
|
if (this.keepAliveInterval) {
|
|
8212
6766
|
clearInterval(this.keepAliveInterval);
|
|
8213
6767
|
this.keepAliveInterval = null;
|
|
8214
|
-
logger.debug("[API MACHINE] Keep-alive stopped");
|
|
6768
|
+
logger.logger.debug("[API MACHINE] Keep-alive stopped");
|
|
8215
6769
|
}
|
|
8216
6770
|
}
|
|
8217
6771
|
shutdown() {
|
|
8218
|
-
logger.debug("[API MACHINE] Shutting down");
|
|
6772
|
+
logger.logger.debug("[API MACHINE] Shutting down");
|
|
8219
6773
|
openClawTunnelManager.closeAll();
|
|
8220
6774
|
this.stopKeepAlive();
|
|
8221
6775
|
if (this.socket) {
|
|
8222
6776
|
this.socket.close();
|
|
8223
|
-
logger.debug("[API MACHINE] Socket closed");
|
|
6777
|
+
logger.logger.debug("[API MACHINE] Socket closed");
|
|
8224
6778
|
}
|
|
8225
6779
|
}
|
|
8226
6780
|
}
|
|
@@ -8248,13 +6802,13 @@ class PushNotificationClient {
|
|
|
8248
6802
|
}
|
|
8249
6803
|
}
|
|
8250
6804
|
);
|
|
8251
|
-
logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
|
|
6805
|
+
logger.logger.debug(`Fetched ${response.data.tokens.length} push tokens`);
|
|
8252
6806
|
response.data.tokens.forEach((token, index) => {
|
|
8253
|
-
logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
|
|
6807
|
+
logger.logger.debug(`[PUSH] Token ${index + 1}: id=${token.id}, created=${new Date(token.createdAt).toISOString()}, updated=${new Date(token.updatedAt).toISOString()}`);
|
|
8254
6808
|
});
|
|
8255
6809
|
return response.data.tokens;
|
|
8256
6810
|
} catch (error) {
|
|
8257
|
-
logger.debug("[PUSH] [ERROR] Failed to fetch push tokens:", error);
|
|
6811
|
+
logger.logger.debug("[PUSH] [ERROR] Failed to fetch push tokens:", error);
|
|
8258
6812
|
throw new Error(`Failed to fetch push tokens: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
8259
6813
|
}
|
|
8260
6814
|
}
|
|
@@ -8263,7 +6817,7 @@ class PushNotificationClient {
|
|
|
8263
6817
|
* @param messages - Array of push messages to send
|
|
8264
6818
|
*/
|
|
8265
6819
|
async sendPushNotifications(messages) {
|
|
8266
|
-
logger.debug(`Sending ${messages.length} push notifications`);
|
|
6820
|
+
logger.logger.debug(`Sending ${messages.length} push notifications`);
|
|
8267
6821
|
const validMessages = messages.filter((message) => {
|
|
8268
6822
|
if (Array.isArray(message.to)) {
|
|
8269
6823
|
return message.to.every((token) => expoServerSdk.Expo.isExpoPushToken(token));
|
|
@@ -8271,7 +6825,7 @@ class PushNotificationClient {
|
|
|
8271
6825
|
return expoServerSdk.Expo.isExpoPushToken(message.to);
|
|
8272
6826
|
});
|
|
8273
6827
|
if (validMessages.length === 0) {
|
|
8274
|
-
logger.debug("No valid Expo push tokens found");
|
|
6828
|
+
logger.logger.debug("No valid Expo push tokens found");
|
|
8275
6829
|
return;
|
|
8276
6830
|
}
|
|
8277
6831
|
const chunks = this.expo.chunkPushNotifications(validMessages);
|
|
@@ -8285,7 +6839,7 @@ class PushNotificationClient {
|
|
|
8285
6839
|
const errors = ticketChunk.filter((ticket) => ticket.status === "error");
|
|
8286
6840
|
if (errors.length > 0) {
|
|
8287
6841
|
const errorDetails = errors.map((e) => ({ message: e.message, details: e.details }));
|
|
8288
|
-
logger.debug("[PUSH] Some notifications failed:", errorDetails);
|
|
6842
|
+
logger.logger.debug("[PUSH] Some notifications failed:", errorDetails);
|
|
8289
6843
|
}
|
|
8290
6844
|
if (errors.length === ticketChunk.length) {
|
|
8291
6845
|
throw new Error("All push notifications in chunk failed");
|
|
@@ -8294,7 +6848,7 @@ class PushNotificationClient {
|
|
|
8294
6848
|
} catch (error) {
|
|
8295
6849
|
const elapsed = Date.now() - startTime;
|
|
8296
6850
|
if (elapsed >= timeout) {
|
|
8297
|
-
logger.debug("[PUSH] Timeout reached after 5 minutes, giving up on chunk");
|
|
6851
|
+
logger.logger.debug("[PUSH] Timeout reached after 5 minutes, giving up on chunk");
|
|
8298
6852
|
break;
|
|
8299
6853
|
}
|
|
8300
6854
|
attempt++;
|
|
@@ -8302,13 +6856,13 @@ class PushNotificationClient {
|
|
|
8302
6856
|
const remainingTime = timeout - elapsed;
|
|
8303
6857
|
const waitTime = Math.min(delay, remainingTime);
|
|
8304
6858
|
if (waitTime > 0) {
|
|
8305
|
-
logger.debug(`[PUSH] Retrying in ${waitTime}ms (attempt ${attempt})`);
|
|
6859
|
+
logger.logger.debug(`[PUSH] Retrying in ${waitTime}ms (attempt ${attempt})`);
|
|
8306
6860
|
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
8307
6861
|
}
|
|
8308
6862
|
}
|
|
8309
6863
|
}
|
|
8310
6864
|
}
|
|
8311
|
-
logger.debug(`Push notifications sent successfully`);
|
|
6865
|
+
logger.logger.debug(`Push notifications sent successfully`);
|
|
8312
6866
|
}
|
|
8313
6867
|
/**
|
|
8314
6868
|
* Increment the badge count on the server and return the new value
|
|
@@ -8327,7 +6881,7 @@ class PushNotificationClient {
|
|
|
8327
6881
|
);
|
|
8328
6882
|
return response.data.badgeCount;
|
|
8329
6883
|
} catch (error) {
|
|
8330
|
-
logger.debug("[PUSH] Failed to increment badge count:", error);
|
|
6884
|
+
logger.logger.debug("[PUSH] Failed to increment badge count:", error);
|
|
8331
6885
|
return 1;
|
|
8332
6886
|
}
|
|
8333
6887
|
}
|
|
@@ -8338,24 +6892,24 @@ class PushNotificationClient {
|
|
|
8338
6892
|
* @param data - Additional data to send with the notification
|
|
8339
6893
|
*/
|
|
8340
6894
|
sendToAllDevices(title, body, data) {
|
|
8341
|
-
logger.debug(`[PUSH] sendToAllDevices called with title: "${title}", body: "${body}"`);
|
|
6895
|
+
logger.logger.debug(`[PUSH] sendToAllDevices called with title: "${title}", body: "${body}"`);
|
|
8342
6896
|
(async () => {
|
|
8343
6897
|
try {
|
|
8344
|
-
logger.debug("[PUSH] Fetching push tokens and incrementing badge...");
|
|
6898
|
+
logger.logger.debug("[PUSH] Fetching push tokens and incrementing badge...");
|
|
8345
6899
|
const [tokens, badgeCount] = await Promise.all([
|
|
8346
6900
|
this.fetchPushTokens(),
|
|
8347
6901
|
this.incrementBadgeCount()
|
|
8348
6902
|
]);
|
|
8349
|
-
logger.debug(`[PUSH] Fetched ${tokens.length} push tokens, badge: ${badgeCount}`);
|
|
6903
|
+
logger.logger.debug(`[PUSH] Fetched ${tokens.length} push tokens, badge: ${badgeCount}`);
|
|
8350
6904
|
tokens.forEach((token, index) => {
|
|
8351
|
-
logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}`);
|
|
6905
|
+
logger.logger.debug(`[PUSH] Using token ${index + 1}: id=${token.id}`);
|
|
8352
6906
|
});
|
|
8353
6907
|
if (tokens.length === 0) {
|
|
8354
|
-
logger.debug("No push tokens found for user");
|
|
6908
|
+
logger.logger.debug("No push tokens found for user");
|
|
8355
6909
|
return;
|
|
8356
6910
|
}
|
|
8357
6911
|
const messages = tokens.map((token, index) => {
|
|
8358
|
-
logger.debug(`[PUSH] Creating message ${index + 1} for token`);
|
|
6912
|
+
logger.logger.debug(`[PUSH] Creating message ${index + 1} for token`);
|
|
8359
6913
|
return {
|
|
8360
6914
|
to: token.token,
|
|
8361
6915
|
title,
|
|
@@ -8367,43 +6921,16 @@ class PushNotificationClient {
|
|
|
8367
6921
|
badge: badgeCount
|
|
8368
6922
|
};
|
|
8369
6923
|
});
|
|
8370
|
-
logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
|
|
6924
|
+
logger.logger.debug(`[PUSH] Sending ${messages.length} push notifications...`);
|
|
8371
6925
|
await this.sendPushNotifications(messages);
|
|
8372
|
-
logger.debug("[PUSH] Push notifications sent successfully");
|
|
6926
|
+
logger.logger.debug("[PUSH] Push notifications sent successfully");
|
|
8373
6927
|
} catch (error) {
|
|
8374
|
-
logger.debug("[PUSH] Error sending to all devices:", error);
|
|
6928
|
+
logger.logger.debug("[PUSH] Error sending to all devices:", error);
|
|
8375
6929
|
}
|
|
8376
6930
|
})();
|
|
8377
6931
|
}
|
|
8378
6932
|
}
|
|
8379
6933
|
|
|
8380
|
-
function keyPath(sessionId) {
|
|
8381
|
-
return path.join(configuration.sessionKeysDir, `${sessionId}.key`);
|
|
8382
|
-
}
|
|
8383
|
-
async function cacheSessionKey(sessionId, sessionAESKey, machineKey) {
|
|
8384
|
-
if (!fs.existsSync(configuration.sessionKeysDir)) {
|
|
8385
|
-
fs.mkdirSync(configuration.sessionKeysDir, { recursive: true });
|
|
8386
|
-
}
|
|
8387
|
-
const encrypted = encryptWithDataKey(encodeBase64(sessionAESKey), machineKey);
|
|
8388
|
-
await promises.writeFile(keyPath(sessionId), encrypted);
|
|
8389
|
-
}
|
|
8390
|
-
function readCachedSessionKey(sessionId, machineKey) {
|
|
8391
|
-
const p = keyPath(sessionId);
|
|
8392
|
-
if (!fs.existsSync(p)) {
|
|
8393
|
-
return null;
|
|
8394
|
-
}
|
|
8395
|
-
try {
|
|
8396
|
-
const blob = new Uint8Array(fs.readFileSync(p));
|
|
8397
|
-
const b64 = decryptWithDataKey(blob, machineKey);
|
|
8398
|
-
if (typeof b64 !== "string") {
|
|
8399
|
-
return null;
|
|
8400
|
-
}
|
|
8401
|
-
return decodeBase64(b64);
|
|
8402
|
-
} catch {
|
|
8403
|
-
return null;
|
|
8404
|
-
}
|
|
8405
|
-
}
|
|
8406
|
-
|
|
8407
6934
|
function startOfflineReconnection(config) {
|
|
8408
6935
|
let reconnected = false;
|
|
8409
6936
|
let session = null;
|
|
@@ -8428,16 +6955,16 @@ function startOfflineReconnection(config) {
|
|
|
8428
6955
|
if (cancelled) return;
|
|
8429
6956
|
reconnected = true;
|
|
8430
6957
|
config.onNotify("\u2705 Reconnected! Session syncing in background.");
|
|
8431
|
-
logger.debug("[OfflineReconnection] Successfully reconnected");
|
|
6958
|
+
logger.logger.debug("[OfflineReconnection] Successfully reconnected");
|
|
8432
6959
|
} catch (e) {
|
|
8433
6960
|
if (axios.isAxiosError(e) && e.response?.status === 401) {
|
|
8434
|
-
logger.debug("[OfflineReconnection] Authentication error, stopping retries");
|
|
6961
|
+
logger.logger.debug("[OfflineReconnection] Authentication error, stopping retries");
|
|
8435
6962
|
config.onNotify("\u274C Authentication failed. Please re-authenticate with `happy auth`.");
|
|
8436
6963
|
return;
|
|
8437
6964
|
}
|
|
8438
6965
|
failureCount++;
|
|
8439
|
-
const delay = exponentialBackoffDelay(failureCount, 5e3, 6e4, 10);
|
|
8440
|
-
logger.debug(`[OfflineReconnection] Attempt ${failureCount} failed, retrying in ${delay}ms`);
|
|
6966
|
+
const delay = time.exponentialBackoffDelay(failureCount, 5e3, 6e4, 10);
|
|
6967
|
+
logger.logger.debug(`[OfflineReconnection] Attempt ${failureCount} failed, retrying in ${delay}ms`);
|
|
8441
6968
|
if (!cancelled) {
|
|
8442
6969
|
timeoutId = setTimeout(attemptReconnect, delay);
|
|
8443
6970
|
}
|
|
@@ -8537,7 +7064,7 @@ class ApiClient {
|
|
|
8537
7064
|
pushClient;
|
|
8538
7065
|
constructor(credential) {
|
|
8539
7066
|
this.credential = credential;
|
|
8540
|
-
this.pushClient = new PushNotificationClient(credential.token, configuration.serverUrl);
|
|
7067
|
+
this.pushClient = new PushNotificationClient(credential.token, logger.configuration.serverUrl);
|
|
8541
7068
|
}
|
|
8542
7069
|
/**
|
|
8543
7070
|
* Create a new session or load existing one with the given tag
|
|
@@ -8547,9 +7074,9 @@ class ApiClient {
|
|
|
8547
7074
|
let encryptionKey;
|
|
8548
7075
|
let encryptionVariant;
|
|
8549
7076
|
if (this.credential.encryption.type === "dataKey") {
|
|
8550
|
-
encryptionKey = getRandomBytes(32);
|
|
7077
|
+
encryptionKey = logger.getRandomBytes(32);
|
|
8551
7078
|
encryptionVariant = "dataKey";
|
|
8552
|
-
let encryptedDataKey = libsodiumEncryptForPublicKey(encryptionKey, this.credential.encryption.publicKey);
|
|
7079
|
+
let encryptedDataKey = logger.libsodiumEncryptForPublicKey(encryptionKey, this.credential.encryption.publicKey);
|
|
8553
7080
|
dataEncryptionKey = new Uint8Array(encryptedDataKey.length + 1);
|
|
8554
7081
|
dataEncryptionKey.set([0], 0);
|
|
8555
7082
|
dataEncryptionKey.set(encryptedDataKey, 1);
|
|
@@ -8559,12 +7086,12 @@ class ApiClient {
|
|
|
8559
7086
|
}
|
|
8560
7087
|
try {
|
|
8561
7088
|
const response = await axios.post(
|
|
8562
|
-
`${configuration.serverUrl}/v1/sessions`,
|
|
7089
|
+
`${logger.configuration.serverUrl}/v1/sessions`,
|
|
8563
7090
|
{
|
|
8564
7091
|
tag: opts.tag,
|
|
8565
|
-
metadata: encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.metadata)),
|
|
8566
|
-
agentState: opts.state ? encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.state)) : null,
|
|
8567
|
-
dataEncryptionKey: dataEncryptionKey ? encodeBase64(dataEncryptionKey) : null
|
|
7092
|
+
metadata: logger.encodeBase64(logger.encrypt(encryptionKey, encryptionVariant, opts.metadata)),
|
|
7093
|
+
agentState: opts.state ? logger.encodeBase64(logger.encrypt(encryptionKey, encryptionVariant, opts.state)) : null,
|
|
7094
|
+
dataEncryptionKey: dataEncryptionKey ? logger.encodeBase64(dataEncryptionKey) : null
|
|
8568
7095
|
},
|
|
8569
7096
|
{
|
|
8570
7097
|
headers: {
|
|
@@ -8575,25 +7102,25 @@ class ApiClient {
|
|
|
8575
7102
|
// 1 minute timeout for very bad network connections
|
|
8576
7103
|
}
|
|
8577
7104
|
);
|
|
8578
|
-
logger.debug(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
|
|
7105
|
+
logger.logger.debug(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
|
|
8579
7106
|
let raw = response.data.session;
|
|
8580
7107
|
let session = {
|
|
8581
7108
|
id: raw.id,
|
|
8582
7109
|
seq: raw.seq,
|
|
8583
|
-
metadata: decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.metadata)),
|
|
7110
|
+
metadata: logger.decrypt(encryptionKey, encryptionVariant, logger.decodeBase64(raw.metadata)),
|
|
8584
7111
|
metadataVersion: raw.metadataVersion,
|
|
8585
|
-
agentState: raw.agentState ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.agentState)) : null,
|
|
7112
|
+
agentState: raw.agentState ? logger.decrypt(encryptionKey, encryptionVariant, logger.decodeBase64(raw.agentState)) : null,
|
|
8586
7113
|
agentStateVersion: raw.agentStateVersion,
|
|
8587
7114
|
encryptionKey,
|
|
8588
7115
|
encryptionVariant
|
|
8589
7116
|
};
|
|
8590
7117
|
if (this.credential.encryption.type === "dataKey") {
|
|
8591
|
-
cacheSessionKey(raw.id, encryptionKey, this.credential.encryption.machineKey).catch(() => {
|
|
7118
|
+
sessionKeyCache.cacheSessionKey(raw.id, encryptionKey, this.credential.encryption.machineKey).catch(() => {
|
|
8592
7119
|
});
|
|
8593
7120
|
}
|
|
8594
7121
|
return session;
|
|
8595
7122
|
} catch (error) {
|
|
8596
|
-
logger.debug("[API] [ERROR] Failed to get or create session:", error);
|
|
7123
|
+
logger.logger.debug("[API] [ERROR] Failed to get or create session:", error);
|
|
8597
7124
|
if (error && typeof error === "object" && "code" in error) {
|
|
8598
7125
|
const errorCode = error.code;
|
|
8599
7126
|
if (isNetworkError(errorCode)) {
|
|
@@ -8601,7 +7128,7 @@ class ApiClient {
|
|
|
8601
7128
|
operation: "Session creation",
|
|
8602
7129
|
caller: "api.getOrCreateSession",
|
|
8603
7130
|
errorCode,
|
|
8604
|
-
url: `${configuration.serverUrl}/v1/sessions`
|
|
7131
|
+
url: `${logger.configuration.serverUrl}/v1/sessions`
|
|
8605
7132
|
});
|
|
8606
7133
|
return null;
|
|
8607
7134
|
}
|
|
@@ -8611,7 +7138,7 @@ class ApiClient {
|
|
|
8611
7138
|
connectionState.fail({
|
|
8612
7139
|
operation: "Session creation",
|
|
8613
7140
|
errorCode: "404",
|
|
8614
|
-
url: `${configuration.serverUrl}/v1/sessions`
|
|
7141
|
+
url: `${logger.configuration.serverUrl}/v1/sessions`
|
|
8615
7142
|
});
|
|
8616
7143
|
return null;
|
|
8617
7144
|
}
|
|
@@ -8621,7 +7148,7 @@ class ApiClient {
|
|
|
8621
7148
|
connectionState.fail({
|
|
8622
7149
|
operation: "Session creation",
|
|
8623
7150
|
errorCode: String(status),
|
|
8624
|
-
url: `${configuration.serverUrl}/v1/sessions`,
|
|
7151
|
+
url: `${logger.configuration.serverUrl}/v1/sessions`,
|
|
8625
7152
|
details: ["Server encountered an error, will retry automatically"]
|
|
8626
7153
|
});
|
|
8627
7154
|
return null;
|
|
@@ -8641,7 +7168,7 @@ class ApiClient {
|
|
|
8641
7168
|
if (this.credential.encryption.type === "dataKey") {
|
|
8642
7169
|
encryptionVariant = "dataKey";
|
|
8643
7170
|
encryptionKey = this.credential.encryption.machineKey;
|
|
8644
|
-
let encryptedDataKey = libsodiumEncryptForPublicKey(this.credential.encryption.machineKey, this.credential.encryption.publicKey);
|
|
7171
|
+
let encryptedDataKey = logger.libsodiumEncryptForPublicKey(this.credential.encryption.machineKey, this.credential.encryption.publicKey);
|
|
8645
7172
|
dataEncryptionKey = new Uint8Array(encryptedDataKey.length + 1);
|
|
8646
7173
|
dataEncryptionKey.set([0], 0);
|
|
8647
7174
|
dataEncryptionKey.set(encryptedDataKey, 1);
|
|
@@ -8660,12 +7187,12 @@ class ApiClient {
|
|
|
8660
7187
|
});
|
|
8661
7188
|
try {
|
|
8662
7189
|
const response = await axios.post(
|
|
8663
|
-
`${configuration.serverUrl}/v1/machines`,
|
|
7190
|
+
`${logger.configuration.serverUrl}/v1/machines`,
|
|
8664
7191
|
{
|
|
8665
7192
|
id: opts.machineId,
|
|
8666
|
-
metadata: encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.metadata)),
|
|
8667
|
-
daemonState: opts.daemonState ? encodeBase64(encrypt(encryptionKey, encryptionVariant, opts.daemonState)) : void 0,
|
|
8668
|
-
dataEncryptionKey: dataEncryptionKey ? encodeBase64(dataEncryptionKey) : void 0,
|
|
7193
|
+
metadata: logger.encodeBase64(logger.encrypt(encryptionKey, encryptionVariant, opts.metadata)),
|
|
7194
|
+
daemonState: opts.daemonState ? logger.encodeBase64(logger.encrypt(encryptionKey, encryptionVariant, opts.daemonState)) : void 0,
|
|
7195
|
+
dataEncryptionKey: dataEncryptionKey ? logger.encodeBase64(dataEncryptionKey) : void 0,
|
|
8669
7196
|
// Plaintext label so other clients can render a device list without
|
|
8670
7197
|
// holding this machine's key. Everything else stays encrypted.
|
|
8671
7198
|
displayName: opts.metadata.displayName || opts.metadata.host || void 0,
|
|
@@ -8683,14 +7210,14 @@ class ApiClient {
|
|
|
8683
7210
|
}
|
|
8684
7211
|
);
|
|
8685
7212
|
const raw = response.data.machine;
|
|
8686
|
-
logger.debug(`[API] Machine ${opts.machineId} registered/updated with server`);
|
|
7213
|
+
logger.logger.debug(`[API] Machine ${opts.machineId} registered/updated with server`);
|
|
8687
7214
|
const machine = {
|
|
8688
7215
|
id: raw.id,
|
|
8689
7216
|
encryptionKey,
|
|
8690
7217
|
encryptionVariant,
|
|
8691
|
-
metadata: raw.metadata ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.metadata)) : null,
|
|
7218
|
+
metadata: raw.metadata ? logger.decrypt(encryptionKey, encryptionVariant, logger.decodeBase64(raw.metadata)) : null,
|
|
8692
7219
|
metadataVersion: raw.metadataVersion || 0,
|
|
8693
|
-
daemonState: raw.daemonState ? decrypt(encryptionKey, encryptionVariant, decodeBase64(raw.daemonState)) : null,
|
|
7220
|
+
daemonState: raw.daemonState ? logger.decrypt(encryptionKey, encryptionVariant, logger.decodeBase64(raw.daemonState)) : null,
|
|
8694
7221
|
daemonStateVersion: raw.daemonStateVersion || 0
|
|
8695
7222
|
};
|
|
8696
7223
|
return machine;
|
|
@@ -8700,7 +7227,7 @@ class ApiClient {
|
|
|
8700
7227
|
operation: "Machine registration",
|
|
8701
7228
|
caller: "api.getOrCreateMachine",
|
|
8702
7229
|
errorCode: error.code,
|
|
8703
|
-
url: `${configuration.serverUrl}/v1/machines`
|
|
7230
|
+
url: `${logger.configuration.serverUrl}/v1/machines`
|
|
8704
7231
|
});
|
|
8705
7232
|
return createMinimalMachine();
|
|
8706
7233
|
}
|
|
@@ -8728,7 +7255,7 @@ class ApiClient {
|
|
|
8728
7255
|
connectionState.fail({
|
|
8729
7256
|
operation: "Machine registration",
|
|
8730
7257
|
errorCode: String(status),
|
|
8731
|
-
url: `${configuration.serverUrl}/v1/machines`,
|
|
7258
|
+
url: `${logger.configuration.serverUrl}/v1/machines`,
|
|
8732
7259
|
details: ["Server encountered an error, will retry automatically"]
|
|
8733
7260
|
});
|
|
8734
7261
|
return createMinimalMachine();
|
|
@@ -8737,7 +7264,7 @@ class ApiClient {
|
|
|
8737
7264
|
connectionState.fail({
|
|
8738
7265
|
operation: "Machine registration",
|
|
8739
7266
|
errorCode: "404",
|
|
8740
|
-
url: `${configuration.serverUrl}/v1/machines`
|
|
7267
|
+
url: `${logger.configuration.serverUrl}/v1/machines`
|
|
8741
7268
|
});
|
|
8742
7269
|
return createMinimalMachine();
|
|
8743
7270
|
}
|
|
@@ -8759,7 +7286,7 @@ class ApiClient {
|
|
|
8759
7286
|
*/
|
|
8760
7287
|
async reportOrchestratorExecutionStart(opts) {
|
|
8761
7288
|
await axios.post(
|
|
8762
|
-
`${configuration.serverUrl}/v1/orchestrator/executions/${opts.executionId}/start`,
|
|
7289
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/executions/${opts.executionId}/start`,
|
|
8763
7290
|
{
|
|
8764
7291
|
dispatchToken: opts.dispatchToken,
|
|
8765
7292
|
startedAt: opts.startedAt,
|
|
@@ -8779,7 +7306,7 @@ class ApiClient {
|
|
|
8779
7306
|
*/
|
|
8780
7307
|
async reportOrchestratorExecutionFinish(opts) {
|
|
8781
7308
|
await axios.post(
|
|
8782
|
-
`${configuration.serverUrl}/v1/orchestrator/executions/${opts.executionId}/finish`,
|
|
7309
|
+
`${logger.configuration.serverUrl}/v1/orchestrator/executions/${opts.executionId}/finish`,
|
|
8783
7310
|
{
|
|
8784
7311
|
dispatchToken: opts.dispatchToken,
|
|
8785
7312
|
status: opts.status,
|
|
@@ -8808,7 +7335,7 @@ class ApiClient {
|
|
|
8808
7335
|
async registerVendorToken(vendor, apiKey) {
|
|
8809
7336
|
try {
|
|
8810
7337
|
const response = await axios.post(
|
|
8811
|
-
`${configuration.serverUrl}/v1/connect/${vendor}/register`,
|
|
7338
|
+
`${logger.configuration.serverUrl}/v1/connect/${vendor}/register`,
|
|
8812
7339
|
{
|
|
8813
7340
|
token: JSON.stringify(apiKey)
|
|
8814
7341
|
},
|
|
@@ -8823,9 +7350,9 @@ class ApiClient {
|
|
|
8823
7350
|
if (response.status !== 200 && response.status !== 201) {
|
|
8824
7351
|
throw new Error(`Server returned status ${response.status}`);
|
|
8825
7352
|
}
|
|
8826
|
-
logger.debug(`[API] Vendor token for ${vendor} registered successfully`);
|
|
7353
|
+
logger.logger.debug(`[API] Vendor token for ${vendor} registered successfully`);
|
|
8827
7354
|
} catch (error) {
|
|
8828
|
-
logger.debug(`[API] [ERROR] Failed to register vendor token:`, error);
|
|
7355
|
+
logger.logger.debug(`[API] [ERROR] Failed to register vendor token:`, error);
|
|
8829
7356
|
throw new Error(`Failed to register vendor token: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
8830
7357
|
}
|
|
8831
7358
|
}
|
|
@@ -8838,7 +7365,7 @@ class ApiClient {
|
|
|
8838
7365
|
async listMemories() {
|
|
8839
7366
|
try {
|
|
8840
7367
|
const response = await axios.get(
|
|
8841
|
-
`${configuration.serverUrl}/v1/memory`,
|
|
7368
|
+
`${logger.configuration.serverUrl}/v1/memory`,
|
|
8842
7369
|
{
|
|
8843
7370
|
headers: {
|
|
8844
7371
|
"Authorization": `Bearer ${this.credential.token}`,
|
|
@@ -8852,7 +7379,7 @@ class ApiClient {
|
|
|
8852
7379
|
}
|
|
8853
7380
|
return response.data.memories;
|
|
8854
7381
|
} catch (err) {
|
|
8855
|
-
logger.debug("[API] listMemories failed (continuing without memory injection):", err);
|
|
7382
|
+
logger.logger.debug("[API] listMemories failed (continuing without memory injection):", err);
|
|
8856
7383
|
return [];
|
|
8857
7384
|
}
|
|
8858
7385
|
}
|
|
@@ -8863,7 +7390,7 @@ class ApiClient {
|
|
|
8863
7390
|
async getVendorToken(vendor) {
|
|
8864
7391
|
try {
|
|
8865
7392
|
const response = await axios.get(
|
|
8866
|
-
`${configuration.serverUrl}/v1/connect/${vendor}/token`,
|
|
7393
|
+
`${logger.configuration.serverUrl}/v1/connect/${vendor}/token`,
|
|
8867
7394
|
{
|
|
8868
7395
|
headers: {
|
|
8869
7396
|
"Authorization": `Bearer ${this.credential.token}`,
|
|
@@ -8873,13 +7400,13 @@ class ApiClient {
|
|
|
8873
7400
|
}
|
|
8874
7401
|
);
|
|
8875
7402
|
if (response.status === 404) {
|
|
8876
|
-
logger.debug(`[API] No vendor token found for ${vendor}`);
|
|
7403
|
+
logger.logger.debug(`[API] No vendor token found for ${vendor}`);
|
|
8877
7404
|
return null;
|
|
8878
7405
|
}
|
|
8879
7406
|
if (response.status !== 200) {
|
|
8880
7407
|
throw new Error(`Server returned status ${response.status}`);
|
|
8881
7408
|
}
|
|
8882
|
-
logger.debug(`[API] Raw vendor token response:`, {
|
|
7409
|
+
logger.logger.debug(`[API] Raw vendor token response:`, {
|
|
8883
7410
|
status: response.status,
|
|
8884
7411
|
dataKeys: Object.keys(response.data || {}),
|
|
8885
7412
|
hasToken: "token" in (response.data || {}),
|
|
@@ -8891,153 +7418,62 @@ class ApiClient {
|
|
|
8891
7418
|
try {
|
|
8892
7419
|
tokenData = JSON.parse(response.data.token);
|
|
8893
7420
|
} catch (parseError) {
|
|
8894
|
-
logger.debug(`[API] Failed to parse token as JSON, using as string:`, parseError);
|
|
7421
|
+
logger.logger.debug(`[API] Failed to parse token as JSON, using as string:`, parseError);
|
|
8895
7422
|
tokenData = response.data.token;
|
|
8896
7423
|
}
|
|
8897
7424
|
} else if (response.data.token !== null) {
|
|
8898
7425
|
tokenData = response.data.token;
|
|
8899
7426
|
} else {
|
|
8900
|
-
logger.debug(`[API] Token is null for ${vendor}, treating as not found`);
|
|
7427
|
+
logger.logger.debug(`[API] Token is null for ${vendor}, treating as not found`);
|
|
8901
7428
|
return null;
|
|
8902
7429
|
}
|
|
8903
7430
|
} else if (response.data && typeof response.data === "object") {
|
|
8904
7431
|
if (response.data.token === null && Object.keys(response.data).length === 1) {
|
|
8905
|
-
logger.debug(`[API] Response contains only null token for ${vendor}, treating as not found`);
|
|
7432
|
+
logger.logger.debug(`[API] Response contains only null token for ${vendor}, treating as not found`);
|
|
8906
7433
|
return null;
|
|
8907
7434
|
}
|
|
8908
7435
|
tokenData = response.data;
|
|
8909
7436
|
}
|
|
8910
7437
|
if (tokenData === null || tokenData && typeof tokenData === "object" && tokenData.token === null && Object.keys(tokenData).length === 1) {
|
|
8911
|
-
logger.debug(`[API] Token data is null for ${vendor}`);
|
|
7438
|
+
logger.logger.debug(`[API] Token data is null for ${vendor}`);
|
|
8912
7439
|
return null;
|
|
8913
7440
|
}
|
|
8914
|
-
logger.debug(`[API] Vendor token for ${vendor} retrieved successfully`, {
|
|
7441
|
+
logger.logger.debug(`[API] Vendor token for ${vendor} retrieved successfully`, {
|
|
8915
7442
|
tokenDataType: typeof tokenData,
|
|
8916
7443
|
tokenDataKeys: tokenData && typeof tokenData === "object" ? Object.keys(tokenData) : "not an object"
|
|
8917
7444
|
});
|
|
8918
7445
|
return tokenData;
|
|
8919
7446
|
} catch (error) {
|
|
8920
7447
|
if (error.response?.status === 404) {
|
|
8921
|
-
logger.debug(`[API] No vendor token found for ${vendor}`);
|
|
7448
|
+
logger.logger.debug(`[API] No vendor token found for ${vendor}`);
|
|
8922
7449
|
return null;
|
|
8923
7450
|
}
|
|
8924
|
-
logger.debug(`[API] [ERROR] Failed to get vendor token:`, error);
|
|
7451
|
+
logger.logger.debug(`[API] [ERROR] Failed to get vendor token:`, error);
|
|
8925
7452
|
return null;
|
|
8926
7453
|
}
|
|
8927
7454
|
}
|
|
8928
7455
|
}
|
|
8929
7456
|
|
|
8930
7457
|
var api = /*#__PURE__*/Object.freeze({
|
|
8931
|
-
|
|
8932
|
-
|
|
7458
|
+
__proto__: null,
|
|
7459
|
+
ApiClient: ApiClient
|
|
8933
7460
|
});
|
|
8934
7461
|
|
|
8935
|
-
const UsageSchema = z.z.object({
|
|
8936
|
-
input_tokens: z.z.number().int().nonnegative(),
|
|
8937
|
-
cache_creation_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
8938
|
-
cache_read_input_tokens: z.z.number().int().nonnegative().optional(),
|
|
8939
|
-
output_tokens: z.z.number().int().nonnegative(),
|
|
8940
|
-
service_tier: z.z.string().nullish()
|
|
8941
|
-
}).passthrough();
|
|
8942
|
-
const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
8943
|
-
// User message - validates uuid and message.content
|
|
8944
|
-
z.z.object({
|
|
8945
|
-
type: z.z.literal("user"),
|
|
8946
|
-
isSidechain: z.z.boolean().optional(),
|
|
8947
|
-
isMeta: z.z.boolean().optional(),
|
|
8948
|
-
uuid: z.z.string(),
|
|
8949
|
-
// Used in getMessageKey()
|
|
8950
|
-
message: z.z.object({
|
|
8951
|
-
content: z.z.union([z.z.string(), z.z.any()])
|
|
8952
|
-
// Used in sessionScanner.ts
|
|
8953
|
-
}).passthrough()
|
|
8954
|
-
}).passthrough(),
|
|
8955
|
-
// Assistant message - only validates uuid and type
|
|
8956
|
-
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
|
8957
|
-
// which may have different structure than normal assistant messages
|
|
8958
|
-
z.z.object({
|
|
8959
|
-
uuid: z.z.string(),
|
|
8960
|
-
type: z.z.literal("assistant"),
|
|
8961
|
-
message: z.z.object({
|
|
8962
|
-
usage: UsageSchema.optional(),
|
|
8963
|
-
// Used in apiSession.ts
|
|
8964
|
-
model: z.z.string().optional()
|
|
8965
|
-
// Used for cost calculation
|
|
8966
|
-
}).passthrough().optional()
|
|
8967
|
-
}).passthrough(),
|
|
8968
|
-
// Summary message - validates summary and leafUuid
|
|
8969
|
-
z.z.object({
|
|
8970
|
-
type: z.z.literal("summary"),
|
|
8971
|
-
summary: z.z.string(),
|
|
8972
|
-
// Used in apiSession.ts
|
|
8973
|
-
leafUuid: z.z.string()
|
|
8974
|
-
// Used in getMessageKey()
|
|
8975
|
-
}).passthrough(),
|
|
8976
|
-
// System message - validates uuid
|
|
8977
|
-
z.z.object({
|
|
8978
|
-
type: z.z.literal("system"),
|
|
8979
|
-
uuid: z.z.string()
|
|
8980
|
-
// Used in getMessageKey()
|
|
8981
|
-
}).passthrough(),
|
|
8982
|
-
// Progress message - hook progress events, not counted toward message limits
|
|
8983
|
-
z.z.object({
|
|
8984
|
-
type: z.z.literal("progress"),
|
|
8985
|
-
uuid: z.z.string()
|
|
8986
|
-
}).passthrough()
|
|
8987
|
-
]);
|
|
8988
|
-
|
|
8989
7462
|
exports.ApiClient = ApiClient;
|
|
8990
7463
|
exports.ApiSessionClient = ApiSessionClient;
|
|
8991
7464
|
exports.AsyncLock = AsyncLock;
|
|
8992
|
-
exports.GEMINI_MODEL_MODES = GEMINI_MODEL_MODES;
|
|
8993
7465
|
exports.GeminiSessionWriter = GeminiSessionWriter;
|
|
8994
7466
|
exports.InvalidateSync = InvalidateSync;
|
|
8995
|
-
exports.MODEL_MODE_DEFAULT = MODEL_MODE_DEFAULT;
|
|
8996
|
-
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
8997
|
-
exports.acquireDaemonLock = acquireDaemonLock;
|
|
8998
7467
|
exports.api = api;
|
|
8999
7468
|
exports.buildResumeContextFromSessionLog = buildResumeContextFromSessionLog;
|
|
9000
|
-
exports.clearCredentials = clearCredentials;
|
|
9001
|
-
exports.clearDaemonState = clearDaemonState;
|
|
9002
|
-
exports.clearMachineId = clearMachineId;
|
|
9003
|
-
exports.configuration = configuration;
|
|
9004
7469
|
exports.connectionState = connectionState;
|
|
9005
|
-
exports.decodeBase64 = decodeBase64;
|
|
9006
|
-
exports.decrypt = decrypt;
|
|
9007
|
-
exports.decryptLegacy = decryptLegacy;
|
|
9008
|
-
exports.decryptWithDataKey = decryptWithDataKey;
|
|
9009
|
-
exports.delay = delay;
|
|
9010
|
-
exports.encodeBase64 = encodeBase64;
|
|
9011
|
-
exports.encodeBase64Url = encodeBase64Url;
|
|
9012
|
-
exports.encrypt = encrypt;
|
|
9013
7470
|
exports.extractUserText = extractUserText;
|
|
9014
7471
|
exports.findClaudeProjectId = findClaudeProjectId;
|
|
9015
7472
|
exports.findCodexSessionFile = findCodexSessionFile;
|
|
9016
7473
|
exports.generateStableUuid = generateStableUuid;
|
|
9017
|
-
exports.getLatestDaemonLog = getLatestDaemonLog;
|
|
9018
|
-
exports.getProfileEnvironmentVariables = getProfileEnvironmentVariables;
|
|
9019
|
-
exports.isDebug = isDebug;
|
|
9020
|
-
exports.isModelModeForAgent = isModelModeForAgent;
|
|
9021
7474
|
exports.isSystemMessage = isSystemMessage;
|
|
9022
|
-
exports.libsodiumPublicKeyFromSecretKey = libsodiumPublicKeyFromSecretKey;
|
|
9023
|
-
exports.logger = logger;
|
|
9024
|
-
exports.packageJson = packageJson;
|
|
9025
|
-
exports.parseClaudeModelMode = parseClaudeModelMode;
|
|
9026
|
-
exports.parseCodexModelMode = parseCodexModelMode;
|
|
9027
|
-
exports.persistence = persistence;
|
|
9028
|
-
exports.projectPath = projectPath;
|
|
9029
|
-
exports.readCachedSessionKey = readCachedSessionKey;
|
|
9030
|
-
exports.readCredentials = readCredentials;
|
|
9031
|
-
exports.readDaemonState = readDaemonState;
|
|
9032
7475
|
exports.readGeminiSessionLog = readGeminiSessionLog;
|
|
9033
|
-
exports.readSettings = readSettings;
|
|
9034
|
-
exports.releaseDaemonLock = releaseDaemonLock;
|
|
9035
7476
|
exports.resolveToolsDir = resolveToolsDir;
|
|
9036
7477
|
exports.saveDiffRecords = saveDiffRecords;
|
|
9037
7478
|
exports.saveToolOutputRecord = saveToolOutputRecord;
|
|
9038
7479
|
exports.startOfflineReconnection = startOfflineReconnection;
|
|
9039
|
-
exports.updateSettings = updateSettings;
|
|
9040
|
-
exports.validateProfileForAgent = validateProfileForAgent;
|
|
9041
|
-
exports.writeCredentialsDataKey = writeCredentialsDataKey;
|
|
9042
|
-
exports.writeCredentialsLegacy = writeCredentialsLegacy;
|
|
9043
|
-
exports.writeDaemonState = writeDaemonState;
|