echoclaw-relay-agent 0.22.7 → 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 (50) 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/launchd-TQ5G4GUM.js +140 -0
  9. package/dist/local/LocalAgent.d.ts +1 -0
  10. package/dist/studio/StudioHandler.d.ts +91 -0
  11. package/dist/studio/types.d.ts +113 -0
  12. package/dist/systemd-MM76PMPH.js +170 -0
  13. package/dist/windows-MFD3SSHE.js +138 -0
  14. package/package.json +5 -2
  15. package/dist/RelayAgent.js +0 -527
  16. package/dist/RelayClient.js +0 -457
  17. package/dist/chat/ChatHandler.js +0 -1067
  18. package/dist/configure/ConfigureHandler.js +0 -196
  19. package/dist/configure/ModelVerifier.js +0 -80
  20. package/dist/configure/OpenClawConfig.js +0 -152
  21. package/dist/crypto/FrameCrypto.js +0 -104
  22. package/dist/gateway/AppRequestPrompt.js +0 -250
  23. package/dist/gateway/DeviceIdentity.js +0 -113
  24. package/dist/gateway/GatewayBridge.js +0 -425
  25. package/dist/gateway/GatewayManager.js +0 -247
  26. package/dist/gateway/GatewayWatchdog.js +0 -275
  27. package/dist/gateway/OpenClawWsClient.js +0 -755
  28. package/dist/gateway/ProcessRestart.js +0 -139
  29. package/dist/gateway/TokenDiscovery.js +0 -91
  30. package/dist/gateway/WorkspaceSync.js +0 -93
  31. package/dist/gateway/index.js +0 -12
  32. package/dist/gateway/types.js +0 -43
  33. package/dist/install/InstallHandler.js +0 -647
  34. package/dist/install/MarkerParser.js +0 -229
  35. package/dist/install/index.js +0 -6
  36. package/dist/install/types.js +0 -40
  37. package/dist/local/LocalAgent.js +0 -200
  38. package/dist/local/LocalDetector.js +0 -77
  39. package/dist/local/index.js +0 -5
  40. package/dist/ota/SelfUpdater.js +0 -217
  41. package/dist/relay/IdentityStore.js +0 -166
  42. package/dist/relay/PairingProtocol.js +0 -232
  43. package/dist/relay/ReconnectPolicy.js +0 -75
  44. package/dist/relay/RelayTransport.js +0 -318
  45. package/dist/relay/SessionStore.js +0 -83
  46. package/dist/service/launchd.js +0 -164
  47. package/dist/service/platform.js +0 -30
  48. package/dist/service/systemd.js +0 -218
  49. package/dist/service/windows.js +0 -172
  50. 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
+ };