opensteer 0.6.13 → 0.7.1
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/README.md +256 -184
- package/dist/chunk-7QTHGTGN.js +32589 -0
- package/dist/chunk-7QTHGTGN.js.map +1 -0
- package/dist/cli/bin.cjs +38219 -0
- package/dist/cli/bin.cjs.map +1 -0
- package/dist/cli/bin.d.cts +1 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +5612 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/index.cjs +31327 -16009
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +4440 -670
- package/dist/index.d.ts +4440 -670
- package/dist/index.js +438 -378
- package/dist/index.js.map +1 -0
- package/package.json +58 -62
- package/skills/README.md +21 -20
- package/skills/opensteer/SKILL.md +60 -194
- package/skills/opensteer/references/cli-reference.md +69 -113
- package/skills/opensteer/references/request-workflow.md +81 -0
- package/skills/opensteer/references/sdk-reference.md +101 -154
- package/CHANGELOG.md +0 -75
- package/bin/opensteer.mjs +0 -1423
- package/dist/browser-profile-client-CGXc0-P9.d.cts +0 -228
- package/dist/browser-profile-client-DHLzMf-K.d.ts +0 -228
- package/dist/chunk-2ES46WCO.js +0 -1437
- package/dist/chunk-3H5RRIMZ.js +0 -69
- package/dist/chunk-AVXUMEDG.js +0 -62
- package/dist/chunk-DN3GI5CH.js +0 -57
- package/dist/chunk-FAHE5DB2.js +0 -190
- package/dist/chunk-HBTSQ2V4.js +0 -15259
- package/dist/chunk-K5CL76MG.js +0 -81
- package/dist/chunk-U724TBY6.js +0 -1262
- package/dist/chunk-ZRCFF546.js +0 -77
- package/dist/cli/auth.cjs +0 -2022
- package/dist/cli/auth.d.cts +0 -114
- package/dist/cli/auth.d.ts +0 -114
- package/dist/cli/auth.js +0 -15
- package/dist/cli/local-profile.cjs +0 -197
- package/dist/cli/local-profile.d.cts +0 -18
- package/dist/cli/local-profile.d.ts +0 -18
- package/dist/cli/local-profile.js +0 -97
- package/dist/cli/profile.cjs +0 -18548
- package/dist/cli/profile.d.cts +0 -79
- package/dist/cli/profile.d.ts +0 -79
- package/dist/cli/profile.js +0 -1328
- package/dist/cli/server.cjs +0 -17232
- package/dist/cli/server.d.cts +0 -2
- package/dist/cli/server.d.ts +0 -2
- package/dist/cli/server.js +0 -977
- package/dist/cli/skills-installer.cjs +0 -230
- package/dist/cli/skills-installer.d.cts +0 -28
- package/dist/cli/skills-installer.d.ts +0 -28
- package/dist/cli/skills-installer.js +0 -201
- package/dist/extractor-4Q3TFZJB.js +0 -8
- package/dist/resolver-MGN64KCP.js +0 -7
- package/dist/types-Cr10igF3.d.cts +0 -345
- package/dist/types-Cr10igF3.d.ts +0 -345
- package/skills/electron/SKILL.md +0 -87
- package/skills/electron/references/opensteer-electron-recipes.md +0 -88
- package/skills/electron/references/opensteer-electron-workflow.md +0 -85
- package/skills/opensteer/references/examples.md +0 -118
package/dist/cli/server.js
DELETED
|
@@ -1,977 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Opensteer
|
|
3
|
-
} from "../chunk-HBTSQ2V4.js";
|
|
4
|
-
import {
|
|
5
|
-
normalizeError,
|
|
6
|
-
resolveCloudSelection,
|
|
7
|
-
resolveConfigWithEnv
|
|
8
|
-
} from "../chunk-2ES46WCO.js";
|
|
9
|
-
import "../chunk-K5CL76MG.js";
|
|
10
|
-
import "../chunk-3H5RRIMZ.js";
|
|
11
|
-
|
|
12
|
-
// src/cli/server.ts
|
|
13
|
-
import { createServer } from "net";
|
|
14
|
-
import { writeFileSync, unlinkSync, existsSync } from "fs";
|
|
15
|
-
|
|
16
|
-
// src/cli/paths.ts
|
|
17
|
-
import { tmpdir } from "os";
|
|
18
|
-
import { join } from "path";
|
|
19
|
-
function prefix(session2) {
|
|
20
|
-
return `opensteer-${session2}`;
|
|
21
|
-
}
|
|
22
|
-
function getSocketPath(session2) {
|
|
23
|
-
return join(tmpdir(), `${prefix(session2)}.sock`);
|
|
24
|
-
}
|
|
25
|
-
function getPidPath(session2) {
|
|
26
|
-
return join(tmpdir(), `${prefix(session2)}.pid`);
|
|
27
|
-
}
|
|
28
|
-
function getMetadataPath(session2) {
|
|
29
|
-
return join(tmpdir(), `${prefix(session2)}.meta.json`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// src/cli/commands.ts
|
|
33
|
-
import { writeFile } from "fs/promises";
|
|
34
|
-
var commands = {
|
|
35
|
-
async navigate(opensteer, args) {
|
|
36
|
-
const url = args.url;
|
|
37
|
-
if (!url) throw new Error("Missing required argument: url");
|
|
38
|
-
await opensteer.goto(url, {
|
|
39
|
-
timeout: args.timeout,
|
|
40
|
-
settleMs: args.settleMs,
|
|
41
|
-
waitUntil: args.waitUntil
|
|
42
|
-
});
|
|
43
|
-
return { url: opensteer.page.url() };
|
|
44
|
-
},
|
|
45
|
-
async back(opensteer) {
|
|
46
|
-
await opensteer.page.goBack();
|
|
47
|
-
return { url: opensteer.page.url() };
|
|
48
|
-
},
|
|
49
|
-
async forward(opensteer) {
|
|
50
|
-
await opensteer.page.goForward();
|
|
51
|
-
return { url: opensteer.page.url() };
|
|
52
|
-
},
|
|
53
|
-
async reload(opensteer) {
|
|
54
|
-
await opensteer.page.reload();
|
|
55
|
-
return { url: opensteer.page.url() };
|
|
56
|
-
},
|
|
57
|
-
async snapshot(opensteer, args) {
|
|
58
|
-
const mode = args.mode || "action";
|
|
59
|
-
const html = await opensteer.snapshot({ mode });
|
|
60
|
-
return { html };
|
|
61
|
-
},
|
|
62
|
-
async state(opensteer) {
|
|
63
|
-
return await opensteer.state();
|
|
64
|
-
},
|
|
65
|
-
async cursor(opensteer, args) {
|
|
66
|
-
const mode = typeof args.mode === "string" ? args.mode : "status";
|
|
67
|
-
if (mode === "on") {
|
|
68
|
-
opensteer.setCursorEnabled(true);
|
|
69
|
-
} else if (mode === "off") {
|
|
70
|
-
opensteer.setCursorEnabled(false);
|
|
71
|
-
} else if (mode !== "status") {
|
|
72
|
-
throw new Error(
|
|
73
|
-
`Invalid cursor mode "${String(mode)}". Use "on", "off", or "status".`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
return {
|
|
77
|
-
cursor: opensteer.getCursorState()
|
|
78
|
-
};
|
|
79
|
-
},
|
|
80
|
-
async screenshot(opensteer, args) {
|
|
81
|
-
const file = args.file || "screenshot.png";
|
|
82
|
-
const type = file.endsWith(".jpg") || file.endsWith(".jpeg") ? "jpeg" : "png";
|
|
83
|
-
const buffer = await opensteer.screenshot({
|
|
84
|
-
fullPage: args.fullPage,
|
|
85
|
-
type
|
|
86
|
-
});
|
|
87
|
-
await writeFile(file, buffer);
|
|
88
|
-
return { file };
|
|
89
|
-
},
|
|
90
|
-
async click(opensteer, args) {
|
|
91
|
-
return await opensteer.click({
|
|
92
|
-
element: args.element,
|
|
93
|
-
selector: args.selector,
|
|
94
|
-
description: args.description,
|
|
95
|
-
button: args.button,
|
|
96
|
-
clickCount: args.clickCount
|
|
97
|
-
});
|
|
98
|
-
},
|
|
99
|
-
async dblclick(opensteer, args) {
|
|
100
|
-
return await opensteer.dblclick({
|
|
101
|
-
element: args.element,
|
|
102
|
-
selector: args.selector,
|
|
103
|
-
description: args.description
|
|
104
|
-
});
|
|
105
|
-
},
|
|
106
|
-
async rightclick(opensteer, args) {
|
|
107
|
-
return await opensteer.rightclick({
|
|
108
|
-
element: args.element,
|
|
109
|
-
selector: args.selector,
|
|
110
|
-
description: args.description
|
|
111
|
-
});
|
|
112
|
-
},
|
|
113
|
-
async hover(opensteer, args) {
|
|
114
|
-
return await opensteer.hover({
|
|
115
|
-
element: args.element,
|
|
116
|
-
selector: args.selector,
|
|
117
|
-
description: args.description
|
|
118
|
-
});
|
|
119
|
-
},
|
|
120
|
-
async input(opensteer, args) {
|
|
121
|
-
const text = args.text;
|
|
122
|
-
if (text == null) throw new Error("Missing required argument: text");
|
|
123
|
-
return await opensteer.input({
|
|
124
|
-
element: args.element,
|
|
125
|
-
selector: args.selector,
|
|
126
|
-
description: args.description,
|
|
127
|
-
text,
|
|
128
|
-
clear: args.clear,
|
|
129
|
-
pressEnter: args.pressEnter
|
|
130
|
-
});
|
|
131
|
-
},
|
|
132
|
-
async select(opensteer, args) {
|
|
133
|
-
return await opensteer.select({
|
|
134
|
-
element: args.element,
|
|
135
|
-
selector: args.selector,
|
|
136
|
-
description: args.description,
|
|
137
|
-
value: args.value,
|
|
138
|
-
label: args.label,
|
|
139
|
-
index: args.index
|
|
140
|
-
});
|
|
141
|
-
},
|
|
142
|
-
async scroll(opensteer, args) {
|
|
143
|
-
return await opensteer.scroll({
|
|
144
|
-
element: args.element,
|
|
145
|
-
selector: args.selector,
|
|
146
|
-
description: args.description,
|
|
147
|
-
direction: args.direction,
|
|
148
|
-
amount: args.amount
|
|
149
|
-
});
|
|
150
|
-
},
|
|
151
|
-
async press(opensteer, args) {
|
|
152
|
-
const key = args.key;
|
|
153
|
-
if (!key) throw new Error("Missing required argument: key");
|
|
154
|
-
await opensteer.pressKey(key);
|
|
155
|
-
return { key };
|
|
156
|
-
},
|
|
157
|
-
async type(opensteer, args) {
|
|
158
|
-
const text = args.text;
|
|
159
|
-
if (text == null) throw new Error("Missing required argument: text");
|
|
160
|
-
await opensteer.type(text);
|
|
161
|
-
return { text };
|
|
162
|
-
},
|
|
163
|
-
async "get-text"(opensteer, args) {
|
|
164
|
-
const text = await opensteer.getElementText({
|
|
165
|
-
element: args.element,
|
|
166
|
-
selector: args.selector,
|
|
167
|
-
description: args.description
|
|
168
|
-
});
|
|
169
|
-
return { text };
|
|
170
|
-
},
|
|
171
|
-
async "get-value"(opensteer, args) {
|
|
172
|
-
const value = await opensteer.getElementValue({
|
|
173
|
-
element: args.element,
|
|
174
|
-
selector: args.selector,
|
|
175
|
-
description: args.description
|
|
176
|
-
});
|
|
177
|
-
return { value };
|
|
178
|
-
},
|
|
179
|
-
async "get-attrs"(opensteer, args) {
|
|
180
|
-
const attributes = await opensteer.getElementAttributes({
|
|
181
|
-
element: args.element,
|
|
182
|
-
selector: args.selector,
|
|
183
|
-
description: args.description
|
|
184
|
-
});
|
|
185
|
-
return { attributes };
|
|
186
|
-
},
|
|
187
|
-
async "get-html"(opensteer, args) {
|
|
188
|
-
const html = await opensteer.getHtml(args.selector);
|
|
189
|
-
return { html };
|
|
190
|
-
},
|
|
191
|
-
async tabs(opensteer) {
|
|
192
|
-
const tabs = await opensteer.tabs();
|
|
193
|
-
return { tabs };
|
|
194
|
-
},
|
|
195
|
-
async "tab-new"(opensteer, args) {
|
|
196
|
-
return await opensteer.newTab(args.url);
|
|
197
|
-
},
|
|
198
|
-
async "tab-switch"(opensteer, args) {
|
|
199
|
-
const index = args.index;
|
|
200
|
-
if (index == null) throw new Error("Missing required argument: index");
|
|
201
|
-
await opensteer.switchTab(index);
|
|
202
|
-
return { index };
|
|
203
|
-
},
|
|
204
|
-
async "tab-close"(opensteer, args) {
|
|
205
|
-
await opensteer.closeTab(args.index);
|
|
206
|
-
return {};
|
|
207
|
-
},
|
|
208
|
-
async cookies(opensteer, args) {
|
|
209
|
-
const cookies = await opensteer.getCookies(args.url);
|
|
210
|
-
return { cookies };
|
|
211
|
-
},
|
|
212
|
-
async "cookie-set"(opensteer, args) {
|
|
213
|
-
await opensteer.setCookie({
|
|
214
|
-
name: args.name,
|
|
215
|
-
value: args.value,
|
|
216
|
-
url: args.url,
|
|
217
|
-
domain: args.domain,
|
|
218
|
-
path: args.path,
|
|
219
|
-
expires: args.expires,
|
|
220
|
-
httpOnly: args.httpOnly,
|
|
221
|
-
secure: args.secure,
|
|
222
|
-
sameSite: args.sameSite
|
|
223
|
-
});
|
|
224
|
-
return {};
|
|
225
|
-
},
|
|
226
|
-
async "cookies-clear"(opensteer) {
|
|
227
|
-
await opensteer.clearCookies();
|
|
228
|
-
return {};
|
|
229
|
-
},
|
|
230
|
-
async "cookies-export"(opensteer, args) {
|
|
231
|
-
const file = args.file;
|
|
232
|
-
if (!file) throw new Error("Missing required argument: file");
|
|
233
|
-
await opensteer.exportCookies(file, args.url);
|
|
234
|
-
return { file };
|
|
235
|
-
},
|
|
236
|
-
async "cookies-import"(opensteer, args) {
|
|
237
|
-
const file = args.file;
|
|
238
|
-
if (!file) throw new Error("Missing required argument: file");
|
|
239
|
-
await opensteer.importCookies(file);
|
|
240
|
-
return { file };
|
|
241
|
-
},
|
|
242
|
-
async eval(opensteer, args) {
|
|
243
|
-
const expression = args.expression;
|
|
244
|
-
if (!expression)
|
|
245
|
-
throw new Error("Missing required argument: expression");
|
|
246
|
-
const result = await opensteer.page.evaluate(expression);
|
|
247
|
-
return { result };
|
|
248
|
-
},
|
|
249
|
-
async "wait-for"(opensteer, args) {
|
|
250
|
-
const text = args.text;
|
|
251
|
-
if (!text) throw new Error("Missing required argument: text");
|
|
252
|
-
await opensteer.waitForText(text, {
|
|
253
|
-
timeout: args.timeout
|
|
254
|
-
});
|
|
255
|
-
return { text };
|
|
256
|
-
},
|
|
257
|
-
async "wait-selector"(opensteer, args) {
|
|
258
|
-
const selector = args.selector;
|
|
259
|
-
if (!selector) throw new Error("Missing required argument: selector");
|
|
260
|
-
await opensteer.page.waitForSelector(selector, {
|
|
261
|
-
timeout: args.timeout ?? 3e4
|
|
262
|
-
});
|
|
263
|
-
return { selector };
|
|
264
|
-
},
|
|
265
|
-
async extract(opensteer, args) {
|
|
266
|
-
const schema = args.schema;
|
|
267
|
-
const data = await opensteer.extract({
|
|
268
|
-
schema,
|
|
269
|
-
description: args.description,
|
|
270
|
-
prompt: args.prompt
|
|
271
|
-
});
|
|
272
|
-
return { data };
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
function getCommandHandler(name) {
|
|
276
|
-
return commands[name];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// src/cli/cloud-profile-binding.ts
|
|
280
|
-
function normalizeCloudProfileBinding(value) {
|
|
281
|
-
if (!value) {
|
|
282
|
-
return null;
|
|
283
|
-
}
|
|
284
|
-
const profileId = typeof value.profileId === "string" ? value.profileId.trim() : "";
|
|
285
|
-
if (!profileId) {
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
return {
|
|
289
|
-
profileId,
|
|
290
|
-
reuseIfActive: typeof value.reuseIfActive === "boolean" ? value.reuseIfActive : void 0
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
function resolveConfiguredCloudProfileBinding(config) {
|
|
294
|
-
if (!isCloudConfigured(config)) {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
return normalizeCloudProfileBinding(config.cloud.browserProfile);
|
|
298
|
-
}
|
|
299
|
-
function resolveSessionCloudProfileBinding(config, requested) {
|
|
300
|
-
if (!isCloudConfigured(config)) {
|
|
301
|
-
return null;
|
|
302
|
-
}
|
|
303
|
-
return requested ?? resolveConfiguredCloudProfileBinding(config);
|
|
304
|
-
}
|
|
305
|
-
function assertCompatibleCloudProfileBinding(sessionId, active, requested) {
|
|
306
|
-
if (!requested) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
if (!active) {
|
|
310
|
-
throw new Error(
|
|
311
|
-
[
|
|
312
|
-
`Session '${sessionId}' is already running without a bound cloud browser profile.`,
|
|
313
|
-
"Cloud browser profile selection only applies when the session is first opened.",
|
|
314
|
-
"Close this session or use a different --session to target another profile."
|
|
315
|
-
].join(" ")
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
if (active.profileId === requested.profileId && active.reuseIfActive === requested.reuseIfActive) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
throw new Error(
|
|
322
|
-
[
|
|
323
|
-
`Session '${sessionId}' is already bound to cloud browser profile ${formatCloudProfileBinding(active)}.`,
|
|
324
|
-
`Requested ${formatCloudProfileBinding(requested)} does not match.`,
|
|
325
|
-
"Use the same cloud profile for this session, or start a different --session."
|
|
326
|
-
].join(" ")
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
function formatCloudProfileBinding(binding) {
|
|
330
|
-
if (binding.reuseIfActive === void 0) {
|
|
331
|
-
return `'${binding.profileId}'`;
|
|
332
|
-
}
|
|
333
|
-
return `'${binding.profileId}' (reuseIfActive=${String(
|
|
334
|
-
binding.reuseIfActive
|
|
335
|
-
)})`;
|
|
336
|
-
}
|
|
337
|
-
function isCloudConfigured(config) {
|
|
338
|
-
return Boolean(
|
|
339
|
-
config.cloud && typeof config.cloud === "object" && !Array.isArray(config.cloud)
|
|
340
|
-
);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// src/cli/open-cloud-auth.ts
|
|
344
|
-
function normalizeCliOpenCloudAuth(value) {
|
|
345
|
-
if (value == null) {
|
|
346
|
-
return null;
|
|
347
|
-
}
|
|
348
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
349
|
-
throw new Error("Invalid open request cloud auth payload.");
|
|
350
|
-
}
|
|
351
|
-
const record = value;
|
|
352
|
-
const apiKey = normalizeNonEmptyString(record.apiKey);
|
|
353
|
-
const accessToken = normalizeNonEmptyString(record.accessToken);
|
|
354
|
-
const baseUrl = normalizeNonEmptyString(record.baseUrl);
|
|
355
|
-
const authScheme = normalizeAuthScheme(record.authScheme);
|
|
356
|
-
if (!baseUrl) {
|
|
357
|
-
throw new Error("Open request cloud auth payload is missing baseUrl.");
|
|
358
|
-
}
|
|
359
|
-
if ((apiKey ? 1 : 0) + (accessToken ? 1 : 0) !== 1) {
|
|
360
|
-
throw new Error(
|
|
361
|
-
"Open request cloud auth payload must include exactly one credential."
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
if (accessToken && authScheme !== "bearer") {
|
|
365
|
-
throw new Error(
|
|
366
|
-
'Open request cloud auth payload must use authScheme "bearer" with accessToken.'
|
|
367
|
-
);
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
...apiKey ? { apiKey } : {},
|
|
371
|
-
...accessToken ? { accessToken } : {},
|
|
372
|
-
baseUrl,
|
|
373
|
-
authScheme
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
function buildServerOpenConfig(options) {
|
|
377
|
-
const config = {
|
|
378
|
-
name: options.name,
|
|
379
|
-
storage: {
|
|
380
|
-
rootDir: options.scopeDir
|
|
381
|
-
},
|
|
382
|
-
cursor: {
|
|
383
|
-
enabled: options.cursorEnabled
|
|
384
|
-
},
|
|
385
|
-
browser: {
|
|
386
|
-
headless: options.headless,
|
|
387
|
-
mode: options.mode,
|
|
388
|
-
cdpUrl: options.cdpUrl,
|
|
389
|
-
userDataDir: options.userDataDir,
|
|
390
|
-
profileDirectory: options.profileDirectory,
|
|
391
|
-
executablePath: options.executablePath
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
if (!options.cloudAuth) {
|
|
395
|
-
return config;
|
|
396
|
-
}
|
|
397
|
-
const resolved = resolveConfigWithEnv(
|
|
398
|
-
{
|
|
399
|
-
storage: {
|
|
400
|
-
rootDir: options.scopeDir
|
|
401
|
-
}
|
|
402
|
-
},
|
|
403
|
-
{
|
|
404
|
-
env: options.env
|
|
405
|
-
}
|
|
406
|
-
);
|
|
407
|
-
const cloudSelection = resolveCloudSelection(
|
|
408
|
-
{
|
|
409
|
-
cloud: resolved.config.cloud
|
|
410
|
-
},
|
|
411
|
-
resolved.env
|
|
412
|
-
);
|
|
413
|
-
if (!cloudSelection.cloud) {
|
|
414
|
-
return config;
|
|
415
|
-
}
|
|
416
|
-
config.cloud = toOpensteerCloudOptions(options.cloudAuth);
|
|
417
|
-
return config;
|
|
418
|
-
}
|
|
419
|
-
function toOpensteerCloudOptions(auth) {
|
|
420
|
-
return {
|
|
421
|
-
...auth.apiKey ? { apiKey: auth.apiKey } : {},
|
|
422
|
-
...auth.accessToken ? { accessToken: auth.accessToken } : {},
|
|
423
|
-
baseUrl: auth.baseUrl,
|
|
424
|
-
authScheme: auth.authScheme
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
function normalizeNonEmptyString(value) {
|
|
428
|
-
if (typeof value !== "string") {
|
|
429
|
-
return void 0;
|
|
430
|
-
}
|
|
431
|
-
const trimmed = value.trim();
|
|
432
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
433
|
-
}
|
|
434
|
-
function normalizeAuthScheme(value) {
|
|
435
|
-
if (value === "api-key" || value === "bearer") {
|
|
436
|
-
return value;
|
|
437
|
-
}
|
|
438
|
-
throw new Error(
|
|
439
|
-
'Open request cloud auth payload must use authScheme "api-key" or "bearer".'
|
|
440
|
-
);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// src/cli/open-browser-config.ts
|
|
444
|
-
function resolveCliBrowserRequestConfig(options) {
|
|
445
|
-
const mode = options.browser ?? (options.profileDirectory || options.userDataDir || options.executablePath ? "real" : void 0);
|
|
446
|
-
return {
|
|
447
|
-
mode,
|
|
448
|
-
headless: options.headless ?? (mode === "real" ? true : void 0),
|
|
449
|
-
cdpUrl: options.cdpUrl,
|
|
450
|
-
profileDirectory: options.profileDirectory,
|
|
451
|
-
userDataDir: options.userDataDir,
|
|
452
|
-
executablePath: options.executablePath
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// src/cli/server.ts
|
|
457
|
-
var instance = null;
|
|
458
|
-
var launchPromise = null;
|
|
459
|
-
var selectorNamespace = null;
|
|
460
|
-
var cloudProfileBinding = null;
|
|
461
|
-
var cloudAuthOverride = null;
|
|
462
|
-
var cursorEnabledPreference = readCursorPreferenceFromEnv();
|
|
463
|
-
var requestQueue = Promise.resolve();
|
|
464
|
-
var shuttingDown = false;
|
|
465
|
-
function sanitizeNamespace(value) {
|
|
466
|
-
const trimmed = String(value || "").trim();
|
|
467
|
-
if (!trimmed || trimmed === "." || trimmed === "..") {
|
|
468
|
-
return "default";
|
|
469
|
-
}
|
|
470
|
-
const replaced = trimmed.replace(/[^a-zA-Z0-9_-]+/g, "_");
|
|
471
|
-
const collapsed = replaced.replace(/_+/g, "_");
|
|
472
|
-
const bounded = collapsed.replace(/^_+|_+$/g, "");
|
|
473
|
-
return bounded || "default";
|
|
474
|
-
}
|
|
475
|
-
function invalidateInstance() {
|
|
476
|
-
if (!instance) return;
|
|
477
|
-
instance.close().catch(() => {
|
|
478
|
-
});
|
|
479
|
-
instance = null;
|
|
480
|
-
cloudProfileBinding = null;
|
|
481
|
-
}
|
|
482
|
-
function normalizeCursorFlag(value) {
|
|
483
|
-
if (value === void 0 || value === null) {
|
|
484
|
-
return null;
|
|
485
|
-
}
|
|
486
|
-
if (typeof value === "boolean") {
|
|
487
|
-
return value;
|
|
488
|
-
}
|
|
489
|
-
if (typeof value === "number") {
|
|
490
|
-
if (value === 1) return true;
|
|
491
|
-
if (value === 0) return false;
|
|
492
|
-
}
|
|
493
|
-
throw new Error(
|
|
494
|
-
'--cursor must be a boolean value ("true" or "false").'
|
|
495
|
-
);
|
|
496
|
-
}
|
|
497
|
-
function readCursorPreferenceFromEnv() {
|
|
498
|
-
const value = process.env.OPENSTEER_CURSOR;
|
|
499
|
-
if (typeof value !== "string") {
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
const normalized = value.trim().toLowerCase();
|
|
503
|
-
if (normalized === "true" || normalized === "1") {
|
|
504
|
-
return true;
|
|
505
|
-
}
|
|
506
|
-
if (normalized === "false" || normalized === "0") {
|
|
507
|
-
return false;
|
|
508
|
-
}
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
function attachLifecycleListeners(inst) {
|
|
512
|
-
try {
|
|
513
|
-
inst.page.on("close", invalidateInstance);
|
|
514
|
-
inst.context.on("close", invalidateInstance);
|
|
515
|
-
} catch {
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
var sessionEnv = process.env.OPENSTEER_SESSION?.trim();
|
|
519
|
-
if (!sessionEnv) {
|
|
520
|
-
process.stderr.write("Missing OPENSTEER_SESSION environment variable.\n");
|
|
521
|
-
process.exit(1);
|
|
522
|
-
}
|
|
523
|
-
var session = sessionEnv;
|
|
524
|
-
var logicalSession = process.env.OPENSTEER_LOGICAL_SESSION?.trim() || session;
|
|
525
|
-
var scopeDir = process.env.OPENSTEER_SCOPE_DIR?.trim() || process.cwd();
|
|
526
|
-
var socketPath = getSocketPath(session);
|
|
527
|
-
var pidPath = getPidPath(session);
|
|
528
|
-
function cleanup() {
|
|
529
|
-
try {
|
|
530
|
-
unlinkSync(socketPath);
|
|
531
|
-
} catch {
|
|
532
|
-
}
|
|
533
|
-
try {
|
|
534
|
-
unlinkSync(pidPath);
|
|
535
|
-
} catch {
|
|
536
|
-
}
|
|
537
|
-
try {
|
|
538
|
-
unlinkSync(getMetadataPath(session));
|
|
539
|
-
} catch {
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function beginShutdown() {
|
|
543
|
-
if (shuttingDown) return;
|
|
544
|
-
shuttingDown = true;
|
|
545
|
-
cleanup();
|
|
546
|
-
server.close(() => {
|
|
547
|
-
process.exit(0);
|
|
548
|
-
});
|
|
549
|
-
setTimeout(() => {
|
|
550
|
-
process.exit(0);
|
|
551
|
-
}, 250).unref();
|
|
552
|
-
}
|
|
553
|
-
function sendResponse(socket, response) {
|
|
554
|
-
try {
|
|
555
|
-
socket.write(JSON.stringify(response) + "\n");
|
|
556
|
-
} catch {
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
function enqueueRequest(request, socket) {
|
|
560
|
-
if (request.command === "ping") {
|
|
561
|
-
void handleRequest(request, socket);
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
requestQueue = requestQueue.then(() => handleRequest(request, socket)).catch((error) => {
|
|
565
|
-
sendResponse(
|
|
566
|
-
socket,
|
|
567
|
-
buildErrorResponse(
|
|
568
|
-
request.id,
|
|
569
|
-
error,
|
|
570
|
-
"Unexpected server error while handling request.",
|
|
571
|
-
"CLI_INTERNAL_ERROR"
|
|
572
|
-
)
|
|
573
|
-
);
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
async function handleRequest(request, socket) {
|
|
577
|
-
const { id, command, args } = request;
|
|
578
|
-
if (command === "ping" && shuttingDown) {
|
|
579
|
-
sendResponse(socket, {
|
|
580
|
-
id,
|
|
581
|
-
ok: false,
|
|
582
|
-
error: `Session '${logicalSession}' is shutting down.`,
|
|
583
|
-
errorInfo: {
|
|
584
|
-
message: `Session '${logicalSession}' is shutting down.`,
|
|
585
|
-
code: "SESSION_SHUTTING_DOWN",
|
|
586
|
-
details: {
|
|
587
|
-
session: logicalSession,
|
|
588
|
-
runtimeSession: session,
|
|
589
|
-
scopeDir
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
});
|
|
593
|
-
return;
|
|
594
|
-
}
|
|
595
|
-
if (shuttingDown) {
|
|
596
|
-
sendResponse(socket, {
|
|
597
|
-
id,
|
|
598
|
-
ok: false,
|
|
599
|
-
error: `Session '${logicalSession}' is shutting down. Retry your command.`,
|
|
600
|
-
errorInfo: {
|
|
601
|
-
message: `Session '${logicalSession}' is shutting down. Retry your command.`,
|
|
602
|
-
code: "SESSION_SHUTTING_DOWN",
|
|
603
|
-
details: {
|
|
604
|
-
session: logicalSession,
|
|
605
|
-
runtimeSession: session,
|
|
606
|
-
scopeDir
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (command === "open") {
|
|
613
|
-
try {
|
|
614
|
-
const url = args.url;
|
|
615
|
-
const headless = args.headless;
|
|
616
|
-
const browser = args.browser === "real" || args.browser === "chromium" ? args.browser : args.browser === void 0 ? void 0 : null;
|
|
617
|
-
const cdpUrl = args["cdp-url"];
|
|
618
|
-
const profileDirectory = args.profile;
|
|
619
|
-
const userDataDir = args["user-data-dir"];
|
|
620
|
-
const executablePath = args["browser-path"];
|
|
621
|
-
const cloudProfileId = typeof args["cloud-profile-id"] === "string" ? args["cloud-profile-id"].trim() : void 0;
|
|
622
|
-
const cloudProfileReuseIfActive = typeof args["cloud-profile-reuse-if-active"] === "boolean" ? args["cloud-profile-reuse-if-active"] : void 0;
|
|
623
|
-
const requestedCloudProfileBinding = normalizeCloudProfileBinding({
|
|
624
|
-
profileId: cloudProfileId,
|
|
625
|
-
reuseIfActive: cloudProfileReuseIfActive
|
|
626
|
-
});
|
|
627
|
-
const requestedCloudAuth = normalizeCliOpenCloudAuth(
|
|
628
|
-
args["cloud-auth"]
|
|
629
|
-
);
|
|
630
|
-
if (cloudProfileReuseIfActive !== void 0 && !cloudProfileId) {
|
|
631
|
-
throw new Error(
|
|
632
|
-
"--cloud-profile-reuse-if-active requires --cloud-profile-id."
|
|
633
|
-
);
|
|
634
|
-
}
|
|
635
|
-
if (browser === null) {
|
|
636
|
-
throw new Error(
|
|
637
|
-
'--browser must be either "chromium" or "real".'
|
|
638
|
-
);
|
|
639
|
-
}
|
|
640
|
-
if (browser === "chromium" && (profileDirectory || userDataDir || executablePath)) {
|
|
641
|
-
throw new Error(
|
|
642
|
-
"--profile, --user-data-dir, and --browser-path require --browser real."
|
|
643
|
-
);
|
|
644
|
-
}
|
|
645
|
-
if (cdpUrl && browser === "real") {
|
|
646
|
-
throw new Error(
|
|
647
|
-
"--cdp-url cannot be combined with --browser real."
|
|
648
|
-
);
|
|
649
|
-
}
|
|
650
|
-
const requestedCursor = normalizeCursorFlag(args.cursor);
|
|
651
|
-
const requestedName = typeof args.name === "string" && args.name.trim().length > 0 ? sanitizeNamespace(args.name) : null;
|
|
652
|
-
if (requestedCursor !== null) {
|
|
653
|
-
cursorEnabledPreference = requestedCursor;
|
|
654
|
-
}
|
|
655
|
-
const effectiveCursorEnabled = cursorEnabledPreference !== null ? cursorEnabledPreference : true;
|
|
656
|
-
if (selectorNamespace && requestedName && requestedName !== selectorNamespace) {
|
|
657
|
-
sendResponse(socket, {
|
|
658
|
-
id,
|
|
659
|
-
ok: false,
|
|
660
|
-
error: `Session '${logicalSession}' is already bound to selector namespace '${selectorNamespace}'. Requested '${requestedName}' does not match. Use the same --name for this session or start a different --session.`,
|
|
661
|
-
errorInfo: {
|
|
662
|
-
message: `Session '${logicalSession}' is already bound to selector namespace '${selectorNamespace}'. Requested '${requestedName}' does not match. Use the same --name for this session or start a different --session.`,
|
|
663
|
-
code: "SESSION_NAMESPACE_MISMATCH",
|
|
664
|
-
details: {
|
|
665
|
-
session: logicalSession,
|
|
666
|
-
runtimeSession: session,
|
|
667
|
-
scopeDir,
|
|
668
|
-
activeNamespace: selectorNamespace,
|
|
669
|
-
requestedNamespace: requestedName
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
return;
|
|
674
|
-
}
|
|
675
|
-
if (!selectorNamespace) {
|
|
676
|
-
selectorNamespace = requestedName ?? logicalSession;
|
|
677
|
-
}
|
|
678
|
-
const activeNamespace = selectorNamespace ?? logicalSession;
|
|
679
|
-
if (requestedCloudAuth) {
|
|
680
|
-
cloudAuthOverride = requestedCloudAuth;
|
|
681
|
-
}
|
|
682
|
-
if (instance && !launchPromise) {
|
|
683
|
-
try {
|
|
684
|
-
if (instance.page.isClosed()) {
|
|
685
|
-
invalidateInstance();
|
|
686
|
-
}
|
|
687
|
-
} catch {
|
|
688
|
-
invalidateInstance();
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
const requestedBrowserConfig = resolveCliBrowserRequestConfig({
|
|
692
|
-
browser: browser ?? void 0,
|
|
693
|
-
headless,
|
|
694
|
-
cdpUrl,
|
|
695
|
-
profileDirectory,
|
|
696
|
-
userDataDir,
|
|
697
|
-
executablePath
|
|
698
|
-
});
|
|
699
|
-
if (instance && !launchPromise) {
|
|
700
|
-
assertCompatibleCloudProfileBinding(
|
|
701
|
-
logicalSession,
|
|
702
|
-
cloudProfileBinding,
|
|
703
|
-
requestedCloudProfileBinding
|
|
704
|
-
);
|
|
705
|
-
const existingBrowserConfig = instance.getConfig().browser || {};
|
|
706
|
-
const existingBrowserRecord = existingBrowserConfig;
|
|
707
|
-
const mismatch = Object.entries(requestedBrowserConfig).find(
|
|
708
|
-
([key, value]) => value !== void 0 && existingBrowserRecord[key] !== value
|
|
709
|
-
);
|
|
710
|
-
if (mismatch) {
|
|
711
|
-
const [key, value] = mismatch;
|
|
712
|
-
throw new Error(
|
|
713
|
-
`Session '${logicalSession}' is already bound to browser setting "${key}"=${JSON.stringify(existingBrowserRecord[key])}. Requested ${JSON.stringify(value)} does not match. Use the same browser flags for this session or start a different --session.`
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
let shouldLaunchInitialUrl = false;
|
|
718
|
-
if (!instance) {
|
|
719
|
-
instance = new Opensteer(
|
|
720
|
-
buildServerOpenConfig({
|
|
721
|
-
scopeDir,
|
|
722
|
-
name: activeNamespace,
|
|
723
|
-
cursorEnabled: effectiveCursorEnabled,
|
|
724
|
-
...requestedBrowserConfig,
|
|
725
|
-
cloudAuth: cloudAuthOverride
|
|
726
|
-
})
|
|
727
|
-
);
|
|
728
|
-
const resolvedBrowserConfig = instance.getConfig().browser || {};
|
|
729
|
-
shouldLaunchInitialUrl = Boolean(url) && resolvedBrowserConfig.mode === "real" && !resolvedBrowserConfig.cdpUrl;
|
|
730
|
-
const nextCloudProfileBinding = resolveSessionCloudProfileBinding(
|
|
731
|
-
instance.getConfig(),
|
|
732
|
-
requestedCloudProfileBinding
|
|
733
|
-
);
|
|
734
|
-
if (requestedCloudProfileBinding && !nextCloudProfileBinding) {
|
|
735
|
-
instance = null;
|
|
736
|
-
throw new Error(
|
|
737
|
-
"--cloud-profile-id can only be used when cloud mode is enabled for this session."
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
|
-
launchPromise = instance.launch({
|
|
741
|
-
initialUrl: shouldLaunchInitialUrl ? url : void 0,
|
|
742
|
-
...requestedBrowserConfig,
|
|
743
|
-
cloudBrowserProfile: cloudProfileId ? {
|
|
744
|
-
profileId: cloudProfileId,
|
|
745
|
-
reuseIfActive: cloudProfileReuseIfActive
|
|
746
|
-
} : void 0,
|
|
747
|
-
timeout: cdpUrl ? 12e4 : 3e4
|
|
748
|
-
});
|
|
749
|
-
try {
|
|
750
|
-
await launchPromise;
|
|
751
|
-
attachLifecycleListeners(instance);
|
|
752
|
-
cloudProfileBinding = nextCloudProfileBinding;
|
|
753
|
-
} catch (err) {
|
|
754
|
-
instance = null;
|
|
755
|
-
cloudProfileBinding = null;
|
|
756
|
-
throw err;
|
|
757
|
-
} finally {
|
|
758
|
-
launchPromise = null;
|
|
759
|
-
}
|
|
760
|
-
} else if (launchPromise) {
|
|
761
|
-
await launchPromise;
|
|
762
|
-
} else if (requestedCursor !== null) {
|
|
763
|
-
instance.setCursorEnabled(requestedCursor);
|
|
764
|
-
}
|
|
765
|
-
if (url && !shouldLaunchInitialUrl) {
|
|
766
|
-
await instance.goto(url);
|
|
767
|
-
}
|
|
768
|
-
sendResponse(socket, {
|
|
769
|
-
id,
|
|
770
|
-
ok: true,
|
|
771
|
-
result: {
|
|
772
|
-
url: instance.page.url(),
|
|
773
|
-
session: logicalSession,
|
|
774
|
-
logicalSession,
|
|
775
|
-
runtimeSession: session,
|
|
776
|
-
scopeDir,
|
|
777
|
-
name: activeNamespace,
|
|
778
|
-
cursor: instance.getCursorState(),
|
|
779
|
-
cloudSessionId: instance.getCloudSessionId() ?? void 0,
|
|
780
|
-
cloudSessionUrl: instance.getCloudSessionUrl() ?? void 0
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
} catch (err) {
|
|
784
|
-
sendResponse(
|
|
785
|
-
socket,
|
|
786
|
-
buildErrorResponse(id, err, "Failed to open browser session.")
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
if (command === "cursor") {
|
|
792
|
-
try {
|
|
793
|
-
const mode = typeof args.mode === "string" ? args.mode : "status";
|
|
794
|
-
if (mode === "on") {
|
|
795
|
-
cursorEnabledPreference = true;
|
|
796
|
-
instance?.setCursorEnabled(true);
|
|
797
|
-
} else if (mode === "off") {
|
|
798
|
-
cursorEnabledPreference = false;
|
|
799
|
-
instance?.setCursorEnabled(false);
|
|
800
|
-
} else if (mode !== "status") {
|
|
801
|
-
throw new Error(
|
|
802
|
-
`Invalid cursor mode "${mode}". Use "on", "off", or "status".`
|
|
803
|
-
);
|
|
804
|
-
}
|
|
805
|
-
const defaultEnabled = cursorEnabledPreference !== null ? cursorEnabledPreference : true;
|
|
806
|
-
const cursor = instance ? instance.getCursorState() : {
|
|
807
|
-
enabled: defaultEnabled,
|
|
808
|
-
active: false,
|
|
809
|
-
reason: "session_not_open"
|
|
810
|
-
};
|
|
811
|
-
sendResponse(socket, {
|
|
812
|
-
id,
|
|
813
|
-
ok: true,
|
|
814
|
-
result: {
|
|
815
|
-
cursor
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
} catch (err) {
|
|
819
|
-
sendResponse(
|
|
820
|
-
socket,
|
|
821
|
-
buildErrorResponse(id, err, "Failed to update cursor mode.")
|
|
822
|
-
);
|
|
823
|
-
}
|
|
824
|
-
return;
|
|
825
|
-
}
|
|
826
|
-
if (command === "close") {
|
|
827
|
-
try {
|
|
828
|
-
if (instance) {
|
|
829
|
-
await instance.close();
|
|
830
|
-
instance = null;
|
|
831
|
-
}
|
|
832
|
-
sendResponse(socket, {
|
|
833
|
-
id,
|
|
834
|
-
ok: true,
|
|
835
|
-
result: { sessionClosed: true }
|
|
836
|
-
});
|
|
837
|
-
} catch (err) {
|
|
838
|
-
sendResponse(
|
|
839
|
-
socket,
|
|
840
|
-
buildErrorResponse(id, err, "Failed to close browser session.")
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
beginShutdown();
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
if (command === "ping") {
|
|
847
|
-
sendResponse(socket, { id, ok: true, result: { pong: true } });
|
|
848
|
-
return;
|
|
849
|
-
}
|
|
850
|
-
if (!instance) {
|
|
851
|
-
sendResponse(socket, {
|
|
852
|
-
id,
|
|
853
|
-
ok: false,
|
|
854
|
-
error: `No browser session in session '${logicalSession}' (scope '${scopeDir}'). Call 'opensteer open --session ${logicalSession}' first, or use 'opensteer sessions' to list active sessions.`,
|
|
855
|
-
errorInfo: {
|
|
856
|
-
message: `No browser session in session '${logicalSession}' (scope '${scopeDir}'). Call 'opensteer open --session ${logicalSession}' first, or use 'opensteer sessions' to list active sessions.`,
|
|
857
|
-
code: "SESSION_NOT_OPEN",
|
|
858
|
-
details: {
|
|
859
|
-
session: logicalSession,
|
|
860
|
-
runtimeSession: session,
|
|
861
|
-
scopeDir
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
});
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
const handler = getCommandHandler(command);
|
|
868
|
-
if (!handler) {
|
|
869
|
-
sendResponse(socket, {
|
|
870
|
-
id,
|
|
871
|
-
ok: false,
|
|
872
|
-
error: `Unknown command: ${command}`,
|
|
873
|
-
errorInfo: {
|
|
874
|
-
message: `Unknown command: ${command}`,
|
|
875
|
-
code: "UNKNOWN_COMMAND",
|
|
876
|
-
details: {
|
|
877
|
-
command
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
try {
|
|
884
|
-
const result = await handler(instance, args);
|
|
885
|
-
sendResponse(socket, { id, ok: true, result });
|
|
886
|
-
} catch (err) {
|
|
887
|
-
sendResponse(
|
|
888
|
-
socket,
|
|
889
|
-
buildErrorResponse(id, err, `Command "${command}" failed.`, void 0, {
|
|
890
|
-
command
|
|
891
|
-
})
|
|
892
|
-
);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
if (existsSync(socketPath)) {
|
|
896
|
-
unlinkSync(socketPath);
|
|
897
|
-
}
|
|
898
|
-
var server = createServer((socket) => {
|
|
899
|
-
let buffer = "";
|
|
900
|
-
socket.on("data", (chunk) => {
|
|
901
|
-
buffer += chunk.toString();
|
|
902
|
-
const lines = buffer.split("\n");
|
|
903
|
-
buffer = lines.pop() || "";
|
|
904
|
-
for (const line of lines) {
|
|
905
|
-
if (!line.trim()) continue;
|
|
906
|
-
try {
|
|
907
|
-
const request = JSON.parse(line);
|
|
908
|
-
enqueueRequest(request, socket);
|
|
909
|
-
} catch {
|
|
910
|
-
sendResponse(socket, {
|
|
911
|
-
id: 0,
|
|
912
|
-
ok: false,
|
|
913
|
-
error: "Invalid JSON request",
|
|
914
|
-
errorInfo: {
|
|
915
|
-
message: "Invalid JSON request",
|
|
916
|
-
code: "INVALID_JSON_REQUEST"
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
});
|
|
922
|
-
socket.on("error", () => {
|
|
923
|
-
});
|
|
924
|
-
});
|
|
925
|
-
server.listen(socketPath, () => {
|
|
926
|
-
writeFileSync(pidPath, String(process.pid));
|
|
927
|
-
if (process.send) {
|
|
928
|
-
process.send("ready");
|
|
929
|
-
}
|
|
930
|
-
});
|
|
931
|
-
server.on("error", (err) => {
|
|
932
|
-
console.error("Server error:", err.message);
|
|
933
|
-
cleanup();
|
|
934
|
-
process.exit(1);
|
|
935
|
-
});
|
|
936
|
-
async function shutdown() {
|
|
937
|
-
if (shuttingDown) return;
|
|
938
|
-
shuttingDown = true;
|
|
939
|
-
if (instance) {
|
|
940
|
-
try {
|
|
941
|
-
await instance.close();
|
|
942
|
-
} catch {
|
|
943
|
-
}
|
|
944
|
-
instance = null;
|
|
945
|
-
}
|
|
946
|
-
cleanup();
|
|
947
|
-
server.close(() => {
|
|
948
|
-
process.exit(0);
|
|
949
|
-
});
|
|
950
|
-
setTimeout(() => {
|
|
951
|
-
process.exit(0);
|
|
952
|
-
}, 250).unref();
|
|
953
|
-
}
|
|
954
|
-
process.on("SIGTERM", shutdown);
|
|
955
|
-
process.on("SIGINT", shutdown);
|
|
956
|
-
function buildErrorResponse(id, error, fallbackMessage, fallbackCode, details) {
|
|
957
|
-
const normalized = normalizeError(error, fallbackMessage);
|
|
958
|
-
let mergedDetails;
|
|
959
|
-
if (normalized.details || details) {
|
|
960
|
-
mergedDetails = {
|
|
961
|
-
...normalized.details || {},
|
|
962
|
-
...details || {}
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
return {
|
|
966
|
-
id,
|
|
967
|
-
ok: false,
|
|
968
|
-
error: normalized.message,
|
|
969
|
-
errorInfo: {
|
|
970
|
-
message: normalized.message,
|
|
971
|
-
...normalized.code || fallbackCode ? { code: normalized.code || fallbackCode } : {},
|
|
972
|
-
...normalized.name ? { name: normalized.name } : {},
|
|
973
|
-
...mergedDetails ? { details: mergedDetails } : {},
|
|
974
|
-
...normalized.cause ? { cause: normalized.cause } : {}
|
|
975
|
-
}
|
|
976
|
-
};
|
|
977
|
-
}
|