echoclaw-relay-agent 0.22.6 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/ConfigureHandler-NXQ2MDTI.js +342 -0
  2. package/dist/SessionStore-YHBFPQV7.js +6 -0
  3. package/dist/chunk-H67DFP5T.js +6077 -0
  4. package/dist/chunk-STI2237I.js +66 -0
  5. package/dist/cli.js +897 -729
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +626 -35
  8. package/dist/install/InstallHandler.d.ts +0 -23
  9. package/dist/launchd-TQ5G4GUM.js +140 -0
  10. package/dist/local/LocalAgent.d.ts +1 -0
  11. package/dist/studio/StudioHandler.d.ts +91 -0
  12. package/dist/studio/types.d.ts +113 -0
  13. package/dist/systemd-MM76PMPH.js +170 -0
  14. package/dist/windows-MFD3SSHE.js +138 -0
  15. package/package.json +5 -4
  16. package/dist/RelayAgent.js +0 -527
  17. package/dist/RelayClient.js +0 -457
  18. package/dist/chat/ChatHandler.js +0 -1067
  19. package/dist/configure/ConfigureHandler.js +0 -196
  20. package/dist/configure/ModelVerifier.js +0 -80
  21. package/dist/configure/OpenClawConfig.js +0 -152
  22. package/dist/crypto/FrameCrypto.js +0 -104
  23. package/dist/gateway/AppRequestPrompt.js +0 -250
  24. package/dist/gateway/DeviceIdentity.js +0 -113
  25. package/dist/gateway/GatewayBridge.js +0 -425
  26. package/dist/gateway/GatewayManager.js +0 -247
  27. package/dist/gateway/GatewayWatchdog.js +0 -275
  28. package/dist/gateway/OpenClawWsClient.js +0 -755
  29. package/dist/gateway/ProcessRestart.js +0 -139
  30. package/dist/gateway/TokenDiscovery.js +0 -91
  31. package/dist/gateway/WorkspaceSync.js +0 -93
  32. package/dist/gateway/index.js +0 -12
  33. package/dist/gateway/types.js +0 -43
  34. package/dist/install/InstallHandler.js +0 -941
  35. package/dist/install/MarkerParser.js +0 -229
  36. package/dist/install/index.js +0 -6
  37. package/dist/install/types.js +0 -40
  38. package/dist/local/LocalAgent.js +0 -200
  39. package/dist/local/LocalDetector.js +0 -77
  40. package/dist/local/index.js +0 -5
  41. package/dist/ota/SelfUpdater.js +0 -217
  42. package/dist/relay/IdentityStore.js +0 -166
  43. package/dist/relay/PairingProtocol.js +0 -232
  44. package/dist/relay/ReconnectPolicy.js +0 -75
  45. package/dist/relay/RelayTransport.js +0 -318
  46. package/dist/relay/SessionStore.js +0 -83
  47. package/dist/service/launchd.js +0 -164
  48. package/dist/service/platform.js +0 -30
  49. package/dist/service/systemd.js +0 -218
  50. package/dist/service/windows.js +0 -172
  51. package/dist/types.js +0 -12
@@ -0,0 +1,342 @@
1
+ // src/configure/ConfigureHandler.ts
2
+ import { execFile } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+
5
+ // src/configure/OpenClawConfig.ts
6
+ import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
7
+ import { existsSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { homedir } from "node:os";
10
+ import { randomBytes } from "node:crypto";
11
+ var OPENCLAW_CONFIG_DIR = join(homedir(), ".openclaw");
12
+ var OPENCLAW_CONFIG_PATH = join(OPENCLAW_CONFIG_DIR, "openclaw.json");
13
+ function detectProvider(apiKey, explicitProvider) {
14
+ if (!apiKey || typeof apiKey !== "string" || apiKey.trim().length < 10) {
15
+ throw new Error("API Key \u65E0\u6548\uFF1A\u957F\u5EA6\u8FC7\u77ED");
16
+ }
17
+ if (/[\x00-\x1f\x7f]/.test(apiKey)) {
18
+ throw new Error("API Key \u65E0\u6548\uFF1A\u5305\u542B\u63A7\u5236\u5B57\u7B26");
19
+ }
20
+ if (explicitProvider) {
21
+ return {
22
+ provider: explicitProvider,
23
+ modelId: getDefaultModel(explicitProvider),
24
+ apiKey
25
+ };
26
+ }
27
+ if (apiKey.startsWith("sk-ant-")) {
28
+ return { provider: "anthropic", modelId: "anthropic/claude-sonnet-4-5", apiKey };
29
+ }
30
+ if (apiKey.startsWith("sk-proj-") || apiKey.startsWith("sk-svcacct-") || apiKey.startsWith("sk-") && apiKey.length > 40) {
31
+ return { provider: "openai", modelId: "openai/gpt-4o", apiKey };
32
+ }
33
+ throw new Error(
34
+ "\u65E0\u6CD5\u81EA\u52A8\u8BC6\u522B API Key \u63D0\u4F9B\u5546\u3002\u8BF7\u6307\u5B9A provider \u53C2\u6570 (deepseek / anthropic / openai / openai-compatible)"
35
+ );
36
+ }
37
+ function getDefaultModel(provider) {
38
+ switch (provider) {
39
+ case "anthropic":
40
+ return "anthropic/claude-sonnet-4-5";
41
+ case "openai":
42
+ return "openai/gpt-4o";
43
+ case "deepseek":
44
+ return "deepseek/deepseek-chat";
45
+ default:
46
+ return `${provider}/default`;
47
+ }
48
+ }
49
+ async function readOpenClawConfig() {
50
+ if (!existsSync(OPENCLAW_CONFIG_PATH)) {
51
+ return {};
52
+ }
53
+ try {
54
+ const raw = await readFile(OPENCLAW_CONFIG_PATH, "utf-8");
55
+ return JSON.parse(raw);
56
+ } catch {
57
+ return {};
58
+ }
59
+ }
60
+ async function writeOpenClawConfig(config) {
61
+ await mkdir(OPENCLAW_CONFIG_DIR, { recursive: true, mode: 448 });
62
+ const tmpPath = `${OPENCLAW_CONFIG_PATH}.${randomBytes(4).toString("hex")}.tmp`;
63
+ await writeFile(tmpPath, JSON.stringify(config, null, 2), { encoding: "utf-8", mode: 384 });
64
+ await rename(tmpPath, OPENCLAW_CONFIG_PATH);
65
+ }
66
+ var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
67
+ function setNestedValue(obj, path, value) {
68
+ const parts = path.split(".");
69
+ let current = obj;
70
+ for (let i = 0; i < parts.length - 1; i++) {
71
+ if (FORBIDDEN_KEYS.has(parts[i])) throw new Error(`Invalid config path: ${path}`);
72
+ if (current[parts[i]] === void 0 || typeof current[parts[i]] !== "object") {
73
+ current[parts[i]] = {};
74
+ }
75
+ current = current[parts[i]];
76
+ }
77
+ const lastKey = parts[parts.length - 1];
78
+ if (FORBIDDEN_KEYS.has(lastKey)) throw new Error(`Invalid config path: ${path}`);
79
+ current[lastKey] = value;
80
+ }
81
+ function getNestedValue(obj, path) {
82
+ const parts = path.split(".");
83
+ let current = obj;
84
+ for (const part of parts) {
85
+ if (current === void 0 || current === null) return void 0;
86
+ current = current[part];
87
+ }
88
+ return current;
89
+ }
90
+ async function configureModel(providerConfig) {
91
+ const config = await readOpenClawConfig();
92
+ setNestedValue(config, "agents.defaults.model", providerConfig.modelId);
93
+ setNestedValue(
94
+ config,
95
+ `models.providers.${providerConfig.provider}.apiKey`,
96
+ providerConfig.apiKey
97
+ );
98
+ if (providerConfig.baseUrl) {
99
+ setNestedValue(
100
+ config,
101
+ `models.providers.${providerConfig.provider}.baseUrl`,
102
+ providerConfig.baseUrl
103
+ );
104
+ }
105
+ setNestedValue(config, "gateway.http.endpoints.chatCompletions.enabled", true);
106
+ await writeOpenClawConfig(config);
107
+ }
108
+ async function getGatewayToken() {
109
+ const config = await readOpenClawConfig();
110
+ return getNestedValue(config, "gateway.auth.token");
111
+ }
112
+ async function hasApiKeyConfigured() {
113
+ const config = await readOpenClawConfig();
114
+ const providers = getNestedValue(config, "models.providers");
115
+ if (!providers || typeof providers !== "object") return false;
116
+ for (const p of Object.values(providers)) {
117
+ if (typeof p === "object" && p !== null && "apiKey" in p && p.apiKey) {
118
+ return true;
119
+ }
120
+ }
121
+ return false;
122
+ }
123
+
124
+ // src/configure/ModelVerifier.ts
125
+ async function verifyModel(gatewayPort, gatewayToken, model, timeoutMs = 3e4) {
126
+ const url = `http://localhost:${gatewayPort}/v1/chat/completions`;
127
+ try {
128
+ const res = await fetch(url, {
129
+ method: "POST",
130
+ headers: {
131
+ "Content-Type": "application/json",
132
+ Authorization: `Bearer ${gatewayToken}`
133
+ },
134
+ body: JSON.stringify({
135
+ model,
136
+ messages: [{ role: "user", content: "hi" }],
137
+ max_tokens: 5
138
+ }),
139
+ signal: AbortSignal.timeout(timeoutMs)
140
+ });
141
+ if (!res.ok) {
142
+ const body = await res.text().catch(() => "");
143
+ if (res.status === 401) {
144
+ return { success: false, error: "Gateway token \u65E0\u6548" };
145
+ }
146
+ if (res.status === 400 && body.includes("model")) {
147
+ return { success: false, error: `\u6A21\u578B ${model} \u4E0D\u53EF\u7528` };
148
+ }
149
+ if (res.status === 402 || body.includes("insufficient") || body.includes("quota")) {
150
+ return { success: false, error: "API \u4F59\u989D\u4E0D\u8DB3" };
151
+ }
152
+ return { success: false, error: `Gateway \u8FD4\u56DE ${res.status}: ${body.slice(0, 200)}` };
153
+ }
154
+ const data = await res.json();
155
+ if (data.choices && data.choices.length > 0) {
156
+ return { success: true, model };
157
+ }
158
+ return { success: false, error: "\u6A21\u578B\u8FD4\u56DE\u4E86\u7A7A\u54CD\u5E94" };
159
+ } catch (err) {
160
+ if (err.name === "AbortError" || err.name === "TimeoutError") {
161
+ return { success: false, error: `\u6A21\u578B\u8BF7\u6C42\u8D85\u65F6 (${timeoutMs / 1e3}s)` };
162
+ }
163
+ if (err.code === "ECONNREFUSED") {
164
+ return { success: false, error: `Gateway \u672A\u8FD0\u884C (localhost:${gatewayPort})` };
165
+ }
166
+ return { success: false, error: `\u8FDE\u63A5\u5931\u8D25: ${err.message}` };
167
+ }
168
+ }
169
+ async function waitForGateway(gatewayPort, gatewayToken, timeoutMs = 3e4, pollIntervalMs = 2e3) {
170
+ const deadline = Date.now() + timeoutMs;
171
+ while (Date.now() < deadline) {
172
+ try {
173
+ const res = await fetch(`http://localhost:${gatewayPort}/v1/models`, {
174
+ headers: { Authorization: `Bearer ${gatewayToken}` },
175
+ signal: AbortSignal.timeout(5e3)
176
+ });
177
+ if (res.ok) return true;
178
+ } catch {
179
+ }
180
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
181
+ }
182
+ return false;
183
+ }
184
+
185
+ // src/configure/ConfigureHandler.ts
186
+ var execFileAsync = promisify(execFile);
187
+ var DEFAULT_GATEWAY_PORT = 18789;
188
+ async function findOpenClaw() {
189
+ try {
190
+ const { stdout } = await execFileAsync("which", ["openclaw"]);
191
+ return stdout.trim() || null;
192
+ } catch {
193
+ return null;
194
+ }
195
+ }
196
+ async function runOpenClaw(args, timeoutMs = 6e4) {
197
+ const openclawBin = await findOpenClaw();
198
+ if (!openclawBin) throw new Error("OpenClaw binary not found");
199
+ return execFileAsync(openclawBin, args, {
200
+ timeout: timeoutMs,
201
+ env: { ...process.env }
202
+ });
203
+ }
204
+ async function checkOpenClawInstalled() {
205
+ try {
206
+ const { stdout } = await runOpenClaw(["--version"], 1e4);
207
+ const version = stdout.trim();
208
+ if (version) return { installed: true, version };
209
+ return { installed: false };
210
+ } catch {
211
+ return { installed: false };
212
+ }
213
+ }
214
+ async function checkNeedsApiKey() {
215
+ return !await hasApiKeyConfigured();
216
+ }
217
+ async function handleConfigure(request, sendStatus) {
218
+ try {
219
+ sendStatus({
220
+ type: "agent_setup_status",
221
+ state: "configuring",
222
+ detail: "\u6B63\u5728\u68C0\u6D4B\u6A21\u578B\u63D0\u4F9B\u5546..."
223
+ }).catch(() => {
224
+ });
225
+ const providerConfig = detectProvider(request.apiKey, request.provider);
226
+ if (request.baseUrl) {
227
+ providerConfig.baseUrl = request.baseUrl;
228
+ }
229
+ sendStatus({
230
+ type: "agent_setup_status",
231
+ state: "configuring",
232
+ detail: `\u6B63\u5728\u914D\u7F6E ${providerConfig.provider} \u6A21\u578B...`
233
+ }).catch(() => {
234
+ });
235
+ await configureModel(providerConfig);
236
+ let gatewayToken = await getGatewayToken();
237
+ if (!gatewayToken) {
238
+ sendStatus({
239
+ type: "agent_setup_status",
240
+ state: "configuring",
241
+ detail: "\u6B63\u5728\u751F\u6210 Gateway \u8BA4\u8BC1\u4EE4\u724C..."
242
+ }).catch(() => {
243
+ });
244
+ try {
245
+ await runOpenClaw(["doctor", "--generate-gateway-token", "--non-interactive"], 3e4);
246
+ } catch (err) {
247
+ return {
248
+ type: "agent_setup_status",
249
+ state: "error",
250
+ error: `\u751F\u6210 Gateway Token \u5931\u8D25: ${err.message}`
251
+ };
252
+ }
253
+ gatewayToken = await getGatewayToken();
254
+ if (!gatewayToken) {
255
+ return {
256
+ type: "agent_setup_status",
257
+ state: "error",
258
+ error: "Gateway Token \u751F\u6210\u540E\u4ECD\u4E0D\u53EF\u7528"
259
+ };
260
+ }
261
+ }
262
+ sendStatus({
263
+ type: "agent_setup_status",
264
+ state: "configuring",
265
+ detail: "\u6B63\u5728\u542F\u52A8 OpenClaw Gateway..."
266
+ }).catch(() => {
267
+ });
268
+ try {
269
+ await runOpenClaw(["gateway", "install"], 3e4);
270
+ } catch (installErr) {
271
+ sendStatus({
272
+ type: "agent_setup_status",
273
+ state: "configuring",
274
+ detail: `Gateway install: ${installErr.message?.slice(0, 100) ?? "skipped (may already be installed)"}`
275
+ }).catch(() => {
276
+ });
277
+ }
278
+ try {
279
+ await runOpenClaw(["gateway", "start"], 15e3);
280
+ } catch (startErr) {
281
+ sendStatus({
282
+ type: "agent_setup_status",
283
+ state: "configuring",
284
+ detail: `Gateway start: ${startErr.message?.slice(0, 100) ?? "skipped (may already be running)"}`
285
+ }).catch(() => {
286
+ });
287
+ }
288
+ sendStatus({
289
+ type: "agent_setup_status",
290
+ state: "configuring",
291
+ detail: "\u7B49\u5F85 Gateway \u5C31\u7EEA..."
292
+ }).catch(() => {
293
+ });
294
+ const gatewayReady = await waitForGateway(DEFAULT_GATEWAY_PORT, gatewayToken, 3e4);
295
+ if (!gatewayReady) {
296
+ try {
297
+ await runOpenClaw(["gateway", "status", "--require-rpc"], 1e4);
298
+ } catch {
299
+ return {
300
+ type: "agent_setup_status",
301
+ state: "error",
302
+ error: "Gateway \u542F\u52A8\u8D85\u65F6\uFF0830s\uFF09\uFF0C\u8BF7\u68C0\u67E5 OpenClaw \u5B89\u88C5"
303
+ };
304
+ }
305
+ }
306
+ sendStatus({
307
+ type: "agent_setup_status",
308
+ state: "configuring",
309
+ detail: "\u6B63\u5728\u9A8C\u8BC1\u6A21\u578B\u8FDE\u63A5..."
310
+ }).catch(() => {
311
+ });
312
+ const verifyResult = await verifyModel(
313
+ DEFAULT_GATEWAY_PORT,
314
+ gatewayToken,
315
+ providerConfig.modelId
316
+ );
317
+ if (!verifyResult.success) {
318
+ return {
319
+ type: "agent_setup_status",
320
+ state: "error",
321
+ error: verifyResult.error ?? "\u6A21\u578B\u9A8C\u8BC1\u5931\u8D25"
322
+ };
323
+ }
324
+ return {
325
+ type: "agent_setup_status",
326
+ state: "ready",
327
+ detail: `${providerConfig.provider} \u6A21\u578B\u5DF2\u5C31\u7EEA`,
328
+ gatewayPort: DEFAULT_GATEWAY_PORT
329
+ };
330
+ } catch (err) {
331
+ return {
332
+ type: "agent_setup_status",
333
+ state: "error",
334
+ error: `\u914D\u7F6E\u5931\u8D25: ${err.message}`
335
+ };
336
+ }
337
+ }
338
+ export {
339
+ checkNeedsApiKey,
340
+ checkOpenClawInstalled,
341
+ handleConfigure
342
+ };
@@ -0,0 +1,6 @@
1
+ import {
2
+ SessionStore
3
+ } from "./chunk-STI2237I.js";
4
+ export {
5
+ SessionStore
6
+ };