@openclaw-cloud/agent-controller 1.0.0-beta.2 → 1.0.0-beta.21

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 (134) hide show
  1. package/bin/agent-controller.js +11 -2
  2. package/dist/api.d.ts +4 -0
  3. package/dist/api.js +10 -2
  4. package/dist/api.js.map +1 -1
  5. package/dist/commands/bootstrap.js +9 -34
  6. package/dist/commands/bootstrap.js.map +1 -1
  7. package/dist/commands/channel-server.d.ts +17 -0
  8. package/dist/commands/channel-server.js +71 -0
  9. package/dist/commands/channel-server.js.map +1 -0
  10. package/dist/commands/heartbeat-cli.js +5 -4
  11. package/dist/commands/heartbeat-cli.js.map +1 -1
  12. package/dist/commands/install-deps.js +18 -8
  13. package/dist/commands/install-deps.js.map +1 -1
  14. package/dist/commands/install.js +8 -8
  15. package/dist/commands/install.js.map +1 -1
  16. package/dist/commands/self-update.js +2 -1
  17. package/dist/commands/self-update.js.map +1 -1
  18. package/dist/config-file.js +20 -4
  19. package/dist/config-file.js.map +1 -1
  20. package/dist/config.d.ts +39 -0
  21. package/dist/config.js +101 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/connection.d.ts +2 -0
  24. package/dist/connection.js +38 -7
  25. package/dist/connection.js.map +1 -1
  26. package/dist/debug.js +2 -1
  27. package/dist/debug.js.map +1 -1
  28. package/dist/handlers/backup.js +4 -1
  29. package/dist/handlers/backup.js.map +1 -1
  30. package/dist/handlers/board-handler.d.ts +4 -0
  31. package/dist/handlers/board-handler.js +127 -100
  32. package/dist/handlers/board-handler.js.map +1 -1
  33. package/dist/handlers/chat.d.ts +17 -0
  34. package/dist/handlers/chat.js +187 -6
  35. package/dist/handlers/chat.js.map +1 -1
  36. package/dist/handlers/deploy.js +16 -29
  37. package/dist/handlers/deploy.js.map +1 -1
  38. package/dist/handlers/diagnostics.js +12 -0
  39. package/dist/handlers/diagnostics.js.map +1 -1
  40. package/dist/handlers/knowledge-sync.js +21 -1
  41. package/dist/handlers/knowledge-sync.js.map +1 -1
  42. package/dist/handlers/memory.d.ts +13 -0
  43. package/dist/handlers/memory.js +128 -0
  44. package/dist/handlers/memory.js.map +1 -0
  45. package/dist/handlers/onboarding.js +18 -2
  46. package/dist/handlers/onboarding.js.map +1 -1
  47. package/dist/handlers/package-install.js +44 -12
  48. package/dist/handlers/package-install.js.map +1 -1
  49. package/dist/handlers/pair.js +14 -1
  50. package/dist/handlers/pair.js.map +1 -1
  51. package/dist/handlers/restart.js +3 -1
  52. package/dist/handlers/restart.js.map +1 -1
  53. package/dist/handlers/self-update.js +3 -1
  54. package/dist/handlers/self-update.js.map +1 -1
  55. package/dist/handlers/stop.js +7 -0
  56. package/dist/handlers/stop.js.map +1 -1
  57. package/dist/handlers/telegram-webhook.js +23 -0
  58. package/dist/handlers/telegram-webhook.js.map +1 -1
  59. package/dist/handlers/update-config.js +8 -2
  60. package/dist/handlers/update-config.js.map +1 -1
  61. package/dist/heartbeat.d.ts +7 -0
  62. package/dist/heartbeat.js +15 -65
  63. package/dist/heartbeat.js.map +1 -1
  64. package/dist/index.d.ts +1 -1
  65. package/dist/index.js +140 -29
  66. package/dist/index.js.map +1 -1
  67. package/dist/mcp-client.js.map +1 -1
  68. package/dist/memory-mcp-server.d.ts +8 -0
  69. package/dist/memory-mcp-server.js +291 -0
  70. package/dist/memory-mcp-server.js.map +1 -0
  71. package/dist/platform/linux.js +7 -3
  72. package/dist/platform/linux.js.map +1 -1
  73. package/dist/platform/macos.js +4 -2
  74. package/dist/platform/macos.js.map +1 -1
  75. package/dist/platform/windows.js +4 -2
  76. package/dist/platform/windows.js.map +1 -1
  77. package/dist/providers/claude-code/channel-server.d.ts +60 -0
  78. package/dist/providers/claude-code/channel-server.js +155 -0
  79. package/dist/providers/claude-code/channel-server.js.map +1 -0
  80. package/dist/providers/claude-code/index.d.ts +68 -0
  81. package/dist/providers/claude-code/index.js +280 -0
  82. package/dist/providers/claude-code/index.js.map +1 -0
  83. package/dist/providers/claude-code/login-flow.d.ts +26 -0
  84. package/dist/providers/claude-code/login-flow.js +135 -0
  85. package/dist/providers/claude-code/login-flow.js.map +1 -0
  86. package/dist/providers/claude-code/settings-writer.d.ts +29 -0
  87. package/dist/providers/claude-code/settings-writer.js +94 -0
  88. package/dist/providers/claude-code/settings-writer.js.map +1 -0
  89. package/dist/providers/claude-code/socket-bridge.d.ts +98 -0
  90. package/dist/providers/claude-code/socket-bridge.js +301 -0
  91. package/dist/providers/claude-code/socket-bridge.js.map +1 -0
  92. package/dist/providers/claude-code/spawn-claude.d.ts +48 -0
  93. package/dist/providers/claude-code/spawn-claude.js +108 -0
  94. package/dist/providers/claude-code/spawn-claude.js.map +1 -0
  95. package/dist/providers/index.d.ts +4 -2
  96. package/dist/providers/index.js +16 -0
  97. package/dist/providers/index.js.map +1 -1
  98. package/dist/providers/mock/index.js.map +1 -1
  99. package/dist/providers/openclaw/device-identity.js +2 -2
  100. package/dist/providers/openclaw/device-identity.js.map +1 -1
  101. package/dist/providers/openclaw/gateway-adapter.js +19 -8
  102. package/dist/providers/openclaw/gateway-adapter.js.map +1 -1
  103. package/dist/providers/openclaw/gateway-client.js +9 -3
  104. package/dist/providers/openclaw/gateway-client.js.map +1 -1
  105. package/dist/providers/openclaw/index.js +14 -3
  106. package/dist/providers/openclaw/index.js.map +1 -1
  107. package/dist/types.d.ts +1 -1
  108. package/dist/utils/agent-controller-bin.d.ts +8 -0
  109. package/dist/utils/agent-controller-bin.js +58 -0
  110. package/dist/utils/agent-controller-bin.js.map +1 -0
  111. package/dist/utils/anthropic-auth.d.ts +25 -0
  112. package/dist/utils/anthropic-auth.js +13 -0
  113. package/dist/utils/anthropic-auth.js.map +1 -0
  114. package/dist/utils/apply-config.d.ts +28 -0
  115. package/dist/utils/apply-config.js +234 -4
  116. package/dist/utils/apply-config.js.map +1 -1
  117. package/dist/utils/claude-env.js +20 -9
  118. package/dist/utils/claude-env.js.map +1 -1
  119. package/dist/utils/config-merge-paths.d.ts +21 -0
  120. package/dist/utils/config-merge-paths.js +27 -0
  121. package/dist/utils/config-merge-paths.js.map +1 -0
  122. package/dist/utils/env.js +2 -1
  123. package/dist/utils/env.js.map +1 -1
  124. package/dist/utils/knowledge-graph.d.ts +66 -0
  125. package/dist/utils/knowledge-graph.js +163 -0
  126. package/dist/utils/knowledge-graph.js.map +1 -0
  127. package/dist/utils/release-channel.js +2 -4
  128. package/dist/utils/release-channel.js.map +1 -1
  129. package/dist/utils/write-workspace-files.d.ts +15 -0
  130. package/dist/utils/write-workspace-files.js +48 -0
  131. package/dist/utils/write-workspace-files.js.map +1 -0
  132. package/dist/workspace.js +4 -5
  133. package/dist/workspace.js.map +1 -1
  134. package/package.json +24 -3
@@ -0,0 +1,98 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import type { ChannelReply, ChannelMessageMeta, ChannelReplyMeta } from './channel-server.js';
3
+ /**
4
+ * Unix-domain-socket bridge between the main agent-controller process and the
5
+ * channel-server worker that holds Claude Code's stdio MCP channel.
6
+ *
7
+ * Protocol: newline-delimited JSON. Ops:
8
+ * { op: "hello", pid } — worker → main
9
+ * { op: "reply", content, meta } — worker → main (Claude reply tool)
10
+ * { op: "edit", content, meta } — worker → main (Claude edit_message tool)
11
+ * { op: "push", content, meta } — main → worker (inbound chat msg)
12
+ *
13
+ * Liberal parsing: malformed lines are logged + dropped (never crash the link).
14
+ */
15
+ export declare const DEFAULT_SOCKET_PATH: string;
16
+ /**
17
+ * Feed a chunk into a newline-delimited JSON accumulator. Returns the updated
18
+ * buffer (remaining, unterminated partial line) after invoking `onMessage`
19
+ * for each completed line that parses as JSON. Malformed lines invoke
20
+ * `onBadLine` (if provided) and are dropped.
21
+ *
22
+ * Extracted so the ChannelBridgeServer and ChannelBridgeClient can share
23
+ * the identical framing logic without duplication.
24
+ */
25
+ export declare function ingestNdjsonLines<T>(buf: string, chunk: string, onMessage: (msg: Partial<T>) => void, onBadLine?: () => void): string;
26
+ export type SocketOp = {
27
+ op: 'hello';
28
+ pid: number;
29
+ } | {
30
+ op: 'reply';
31
+ content: string;
32
+ meta: ChannelReplyMeta;
33
+ } | {
34
+ op: 'edit';
35
+ content: string;
36
+ meta: ChannelReplyMeta;
37
+ } | {
38
+ op: 'push';
39
+ content: string;
40
+ meta: ChannelMessageMeta;
41
+ };
42
+ export interface ChannelBridgeServerEvents {
43
+ connect: [pid: number | null];
44
+ reply: [content: string, meta: ChannelReplyMeta, tool: 'reply' | 'edit_message'];
45
+ close: [];
46
+ error: [err: Error];
47
+ }
48
+ /**
49
+ * Server side — lives in the main agent-controller process.
50
+ *
51
+ * Accepts exactly one worker connection (extras are rejected + closed). Emits
52
+ * reply/edit ops; callers invoke `push(content, meta)` to emit an inbound
53
+ * chat message to the worker.
54
+ */
55
+ export declare class ChannelBridgeServer extends EventEmitter {
56
+ private readonly opts;
57
+ private server;
58
+ private active;
59
+ private buf;
60
+ constructor(opts?: {
61
+ socketPath?: string;
62
+ });
63
+ get socketPath(): string;
64
+ listen(): Promise<void>;
65
+ private handleConnection;
66
+ private ingest;
67
+ private dispatch;
68
+ /** Push an inbound chat message to the worker (→ Claude MCP notification). */
69
+ push(content: string, meta: ChannelMessageMeta): void;
70
+ private write;
71
+ close(): Promise<void>;
72
+ isConnected(): boolean;
73
+ }
74
+ /**
75
+ * Client side — lives in the channel-server worker subprocess.
76
+ *
77
+ * Connects to the bridge socket with retry/backoff (up to 30s). Parses
78
+ * incoming `push` ops and forwards outgoing `reply`/`edit` ops.
79
+ */
80
+ export declare class ChannelBridgeClient extends EventEmitter {
81
+ private readonly opts;
82
+ private sock;
83
+ private buf;
84
+ private closed;
85
+ constructor(opts?: {
86
+ socketPath?: string;
87
+ maxWaitMs?: number;
88
+ });
89
+ get socketPath(): string;
90
+ connect(): Promise<void>;
91
+ private connectOnce;
92
+ sendHello(pid: number): void;
93
+ sendReply(content: string, meta: ChannelReplyMeta, tool: 'reply' | 'edit_message'): void;
94
+ private ingest;
95
+ private write;
96
+ close(): void;
97
+ }
98
+ export type { ChannelReply, ChannelMessageMeta, ChannelReplyMeta };
@@ -0,0 +1,301 @@
1
+ import net from 'node:net';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { EventEmitter } from 'node:events';
6
+ import { logCollector } from '../../connection.js';
7
+ import { toErrorMessage } from '../../utils/response.js';
8
+ /**
9
+ * Unix-domain-socket bridge between the main agent-controller process and the
10
+ * channel-server worker that holds Claude Code's stdio MCP channel.
11
+ *
12
+ * Protocol: newline-delimited JSON. Ops:
13
+ * { op: "hello", pid } — worker → main
14
+ * { op: "reply", content, meta } — worker → main (Claude reply tool)
15
+ * { op: "edit", content, meta } — worker → main (Claude edit_message tool)
16
+ * { op: "push", content, meta } — main → worker (inbound chat msg)
17
+ *
18
+ * Liberal parsing: malformed lines are logged + dropped (never crash the link).
19
+ */
20
+ export const DEFAULT_SOCKET_PATH = path.join(os.homedir(), '.openclaw', 'claude-channel.sock');
21
+ /**
22
+ * Feed a chunk into a newline-delimited JSON accumulator. Returns the updated
23
+ * buffer (remaining, unterminated partial line) after invoking `onMessage`
24
+ * for each completed line that parses as JSON. Malformed lines invoke
25
+ * `onBadLine` (if provided) and are dropped.
26
+ *
27
+ * Extracted so the ChannelBridgeServer and ChannelBridgeClient can share
28
+ * the identical framing logic without duplication.
29
+ */
30
+ export function ingestNdjsonLines(buf, chunk, onMessage, onBadLine) {
31
+ let updated = buf + chunk;
32
+ let idx;
33
+ while ((idx = updated.indexOf('\n')) >= 0) {
34
+ const line = updated.slice(0, idx).trim();
35
+ updated = updated.slice(idx + 1);
36
+ if (!line)
37
+ continue;
38
+ try {
39
+ onMessage(JSON.parse(line));
40
+ }
41
+ catch {
42
+ onBadLine?.();
43
+ }
44
+ }
45
+ return updated;
46
+ }
47
+ /**
48
+ * Server side — lives in the main agent-controller process.
49
+ *
50
+ * Accepts exactly one worker connection (extras are rejected + closed). Emits
51
+ * reply/edit ops; callers invoke `push(content, meta)` to emit an inbound
52
+ * chat message to the worker.
53
+ */
54
+ export class ChannelBridgeServer extends EventEmitter {
55
+ opts;
56
+ server = null;
57
+ active = null;
58
+ buf = '';
59
+ constructor(opts = {}) {
60
+ super();
61
+ this.opts = opts;
62
+ // Default no-op 'error' listener so re-emitting a server error never turns
63
+ // into an unhandled-exception process crash if no external listener is
64
+ // registered. External callers can still attach their own listener.
65
+ this.on('error', () => {
66
+ /* no-op */
67
+ });
68
+ }
69
+ get socketPath() {
70
+ return this.opts.socketPath ?? DEFAULT_SOCKET_PATH;
71
+ }
72
+ async listen() {
73
+ const p = this.socketPath;
74
+ await fs.promises.mkdir(path.dirname(p), { recursive: true });
75
+ // Best-effort: remove stale socket file from a previous crash.
76
+ try {
77
+ await fs.promises.unlink(p);
78
+ }
79
+ catch {
80
+ /* not present */
81
+ }
82
+ return new Promise((resolve, reject) => {
83
+ const srv = net.createServer((sock) => this.handleConnection(sock));
84
+ let bound = false;
85
+ // Single error handler — before bind completes, a listen error rejects
86
+ // the promise; after bind, it's a runtime error emitted to listeners.
87
+ // This removes the previous two-listener dance (once + on) and the
88
+ // brittle cross-removeListener cleanup on success.
89
+ const onError = (err) => {
90
+ if (!bound) {
91
+ reject(err);
92
+ return;
93
+ }
94
+ logCollector?.push('channel_bridge_error', 'error', `bridge server error: ${err.message}`);
95
+ this.emit('error', err);
96
+ };
97
+ srv.on('error', onError);
98
+ srv.listen(p, () => {
99
+ bound = true;
100
+ this.server = srv;
101
+ logCollector?.push('channel_bridge_listening', 'info', `channel bridge on ${p}`);
102
+ resolve();
103
+ });
104
+ });
105
+ }
106
+ handleConnection(sock) {
107
+ if (this.active) {
108
+ // Reject extras — we expect exactly one worker.
109
+ logCollector?.push('channel_bridge_reject_extra', 'warn', 'extra worker connection rejected — one worker already active');
110
+ try {
111
+ sock.end();
112
+ }
113
+ catch {
114
+ /* ignore */
115
+ }
116
+ return;
117
+ }
118
+ this.active = sock;
119
+ sock.setEncoding('utf8');
120
+ sock.on('data', (chunk) => this.ingest(String(chunk)));
121
+ sock.on('close', () => {
122
+ this.active = null;
123
+ this.buf = '';
124
+ logCollector?.push('channel_bridge_closed', 'info', 'worker connection closed');
125
+ this.emit('close');
126
+ });
127
+ sock.on('error', (err) => {
128
+ logCollector?.push('channel_bridge_sock_error', 'warn', `worker socket error: ${err.message}`);
129
+ });
130
+ }
131
+ ingest(chunk) {
132
+ this.buf = ingestNdjsonLines(this.buf, chunk, (msg) => this.dispatch(msg), () => logCollector?.push('channel_bridge_bad_line', 'warn', 'dropped malformed bridge line'));
133
+ }
134
+ dispatch(msg) {
135
+ if (!msg || typeof msg !== 'object' || !msg.op)
136
+ return;
137
+ switch (msg.op) {
138
+ case 'hello': {
139
+ const pid = typeof msg.pid === 'number' ? msg.pid : null;
140
+ logCollector?.push('channel_bridge_hello', 'info', `worker hello pid=${pid ?? 'unknown'}`);
141
+ this.emit('connect', pid);
142
+ break;
143
+ }
144
+ case 'reply': {
145
+ const m = msg;
146
+ const content = typeof m.content === 'string' ? m.content : '';
147
+ const meta = (m.meta ?? {});
148
+ this.emit('reply', content, meta, 'reply');
149
+ break;
150
+ }
151
+ case 'edit': {
152
+ const m = msg;
153
+ const content = typeof m.content === 'string' ? m.content : '';
154
+ const meta = (m.meta ?? {});
155
+ this.emit('reply', content, meta, 'edit_message');
156
+ break;
157
+ }
158
+ default:
159
+ logCollector?.push('channel_bridge_unknown_op', 'warn', `unknown op: ${String(msg.op)}`);
160
+ }
161
+ }
162
+ /** Push an inbound chat message to the worker (→ Claude MCP notification). */
163
+ push(content, meta) {
164
+ this.write({ op: 'push', content, meta });
165
+ }
166
+ write(op) {
167
+ if (!this.active) {
168
+ logCollector?.push('channel_bridge_no_worker', 'warn', `dropped op ${op.op} — no worker`);
169
+ return;
170
+ }
171
+ try {
172
+ this.active.write(JSON.stringify(op) + '\n');
173
+ }
174
+ catch (err) {
175
+ logCollector?.push('channel_bridge_write_error', 'warn', `write ${op.op} failed: ${toErrorMessage(err)}`);
176
+ }
177
+ }
178
+ async close() {
179
+ try {
180
+ this.active?.end();
181
+ }
182
+ catch {
183
+ /* ignore */
184
+ }
185
+ this.active = null;
186
+ if (!this.server)
187
+ return;
188
+ const srv = this.server;
189
+ this.server = null;
190
+ await new Promise((resolve) => srv.close(() => resolve()));
191
+ try {
192
+ await fs.promises.unlink(this.socketPath);
193
+ }
194
+ catch {
195
+ /* ignore */
196
+ }
197
+ }
198
+ isConnected() {
199
+ return !!this.active;
200
+ }
201
+ }
202
+ /**
203
+ * Client side — lives in the channel-server worker subprocess.
204
+ *
205
+ * Connects to the bridge socket with retry/backoff (up to 30s). Parses
206
+ * incoming `push` ops and forwards outgoing `reply`/`edit` ops.
207
+ */
208
+ export class ChannelBridgeClient extends EventEmitter {
209
+ opts;
210
+ sock = null;
211
+ buf = '';
212
+ closed = false;
213
+ constructor(opts = {}) {
214
+ super();
215
+ this.opts = opts;
216
+ }
217
+ get socketPath() {
218
+ return this.opts.socketPath ?? DEFAULT_SOCKET_PATH;
219
+ }
220
+ async connect() {
221
+ const maxWait = this.opts.maxWaitMs ?? 30_000;
222
+ const started = Date.now();
223
+ let attempt = 0;
224
+ // Retry with capped exponential backoff while the main process comes up.
225
+ while (true) {
226
+ try {
227
+ await this.connectOnce();
228
+ return;
229
+ }
230
+ catch (err) {
231
+ if (Date.now() - started > maxWait) {
232
+ throw err instanceof Error ? err : new Error(String(err));
233
+ }
234
+ attempt += 1;
235
+ const delay = Math.min(2000, 100 * 2 ** Math.min(attempt, 6));
236
+ await new Promise((r) => setTimeout(r, delay));
237
+ }
238
+ }
239
+ }
240
+ connectOnce() {
241
+ return new Promise((resolve, reject) => {
242
+ const s = net.createConnection({ path: this.socketPath });
243
+ const onError = (err) => {
244
+ s.removeAllListeners();
245
+ reject(err);
246
+ };
247
+ s.once('error', onError);
248
+ s.once('connect', () => {
249
+ s.removeListener('error', onError);
250
+ s.setEncoding('utf8');
251
+ this.sock = s;
252
+ s.on('data', (chunk) => this.ingest(String(chunk)));
253
+ s.on('close', () => {
254
+ this.sock = null;
255
+ this.buf = '';
256
+ if (!this.closed)
257
+ this.emit('close');
258
+ });
259
+ s.on('error', (err) => this.emit('error', err));
260
+ resolve();
261
+ });
262
+ });
263
+ }
264
+ sendHello(pid) {
265
+ this.write({ op: 'hello', pid });
266
+ }
267
+ sendReply(content, meta, tool) {
268
+ this.write({ op: tool === 'edit_message' ? 'edit' : 'reply', content, meta });
269
+ }
270
+ ingest(chunk) {
271
+ this.buf = ingestNdjsonLines(this.buf, chunk, (msg) => {
272
+ if (msg && msg.op === 'push') {
273
+ const m = msg;
274
+ const content = typeof m.content === 'string' ? m.content : '';
275
+ const meta = (m.meta ?? {});
276
+ this.emit('push', content, meta);
277
+ }
278
+ });
279
+ }
280
+ write(op) {
281
+ if (!this.sock)
282
+ return;
283
+ try {
284
+ this.sock.write(JSON.stringify(op) + '\n');
285
+ }
286
+ catch {
287
+ /* ignore — socket will emit close/error */
288
+ }
289
+ }
290
+ close() {
291
+ this.closed = true;
292
+ try {
293
+ this.sock?.end();
294
+ }
295
+ catch {
296
+ /* ignore */
297
+ }
298
+ this.sock = null;
299
+ }
300
+ }
301
+ //# sourceMappingURL=socket-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-bridge.js","sourceRoot":"","sources":["../../../src/providers/claude-code/socket-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { spawn, type ChildProcess } from 'node:child_process';
2
+ import { EventEmitter } from 'node:events';
3
+ export interface SpawnClaudeDeps {
4
+ /** Working directory for the claude process (workspace). */
5
+ workspaceDir: string;
6
+ /** Channels plugin flag argument (defaults to `plugin:agent-controller`). */
7
+ channelsFlag?: string;
8
+ /** Extra CLI flags to append. */
9
+ extraArgs?: string[];
10
+ /** Environment variables to merge on top of process.env. */
11
+ env?: Record<string, string | undefined>;
12
+ /**
13
+ * Pass `--dangerously-load-development-channels` to claude. Only needed
14
+ * when the channels feature is routed through an MCP-based dev plugin;
15
+ * production flows do not require it. Default: `false`.
16
+ */
17
+ danglyDev?: boolean;
18
+ /** Override spawn (tests). */
19
+ spawnFn?: typeof spawn;
20
+ /** Max retries on crash (default 5). */
21
+ maxRetries?: number;
22
+ }
23
+ /**
24
+ * Supervise a `claude` subprocess. Emits:
25
+ * - `stderr` (Buffer)
26
+ * - `stdout` (Buffer)
27
+ * - `spawn` (ChildProcess)
28
+ * - `exit` (code|null, signal|null)
29
+ * - `gaveup` — too many crashes, no more restarts.
30
+ *
31
+ * Restart on crash with capped exponential backoff.
32
+ */
33
+ export declare class ClaudeProcess extends EventEmitter {
34
+ private readonly deps;
35
+ private child;
36
+ private retries;
37
+ private shuttingDown;
38
+ private lastSpawnAt;
39
+ constructor(deps: SpawnClaudeDeps);
40
+ start(): void;
41
+ private spawnOnce;
42
+ private buildArgs;
43
+ /** Send SIGINT to interrupt the current turn. */
44
+ interrupt(): void;
45
+ stop(): void;
46
+ isRunning(): boolean;
47
+ getChild(): ChildProcess | null;
48
+ }
@@ -0,0 +1,108 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { EventEmitter } from 'node:events';
3
+ import { logCollector } from '../../connection.js';
4
+ /**
5
+ * Supervise a `claude` subprocess. Emits:
6
+ * - `stderr` (Buffer)
7
+ * - `stdout` (Buffer)
8
+ * - `spawn` (ChildProcess)
9
+ * - `exit` (code|null, signal|null)
10
+ * - `gaveup` — too many crashes, no more restarts.
11
+ *
12
+ * Restart on crash with capped exponential backoff.
13
+ */
14
+ export class ClaudeProcess extends EventEmitter {
15
+ deps;
16
+ child = null;
17
+ retries = 0;
18
+ shuttingDown = false;
19
+ lastSpawnAt = 0;
20
+ constructor(deps) {
21
+ super();
22
+ this.deps = deps;
23
+ }
24
+ start() {
25
+ if (this.child)
26
+ return;
27
+ this.shuttingDown = false;
28
+ this.spawnOnce();
29
+ }
30
+ spawnOnce() {
31
+ const spawnFn = this.deps.spawnFn ?? spawn;
32
+ const args = this.buildArgs();
33
+ this.lastSpawnAt = Date.now();
34
+ const child = spawnFn('claude', args, {
35
+ cwd: this.deps.workspaceDir,
36
+ env: { ...process.env, ...(this.deps.env ?? {}) },
37
+ stdio: ['pipe', 'pipe', 'pipe'],
38
+ });
39
+ this.child = child;
40
+ child.stdout?.on('data', (data) => this.emit('stdout', data));
41
+ child.stderr?.on('data', (data) => {
42
+ this.emit('stderr', data);
43
+ logCollector?.push('claude_stderr', 'debug', data.toString().slice(0, 500));
44
+ });
45
+ child.on('error', (err) => {
46
+ logCollector?.push('claude_spawn_error', 'error', err.message);
47
+ this.emit('error', err);
48
+ });
49
+ child.on('exit', (code, signal) => {
50
+ this.emit('exit', code, signal);
51
+ this.child = null;
52
+ if (this.shuttingDown)
53
+ return;
54
+ // Reset retries on a healthy long run
55
+ if (Date.now() - this.lastSpawnAt > 60_000)
56
+ this.retries = 0;
57
+ const maxRetries = this.deps.maxRetries ?? 5;
58
+ // Semantics: maxRetries is the number of RESTART attempts allowed.
59
+ // Increment first, then check — so maxRetries=5 yields exactly 5 restarts
60
+ // (retry counts 1..5) before giving up on the 6th crash.
61
+ this.retries += 1;
62
+ if (this.retries > maxRetries) {
63
+ logCollector?.push('claude_giveup', 'error', `claude crashed too many times (${this.retries - 1}/${maxRetries}), giving up`);
64
+ this.emit('gaveup');
65
+ return;
66
+ }
67
+ const backoff = Math.min(30_000, 500 * 2 ** (this.retries - 1));
68
+ logCollector?.push('claude_restart_scheduled', 'warn', `claude exited code=${code ?? 'null'} sig=${signal ?? 'null'}; retry ${this.retries}/${maxRetries} in ${backoff}ms`);
69
+ setTimeout(() => {
70
+ if (!this.shuttingDown)
71
+ this.spawnOnce();
72
+ }, backoff).unref();
73
+ });
74
+ this.emit('spawn', child);
75
+ logCollector?.push('claude_spawned', 'info', `claude spawned (pid=${child.pid})`);
76
+ }
77
+ buildArgs() {
78
+ const args = ['--channels', this.deps.channelsFlag ?? 'plugin:agent-controller'];
79
+ if (this.deps.danglyDev === true)
80
+ args.push('--dangerously-load-development-channels');
81
+ if (this.deps.extraArgs)
82
+ args.push(...this.deps.extraArgs);
83
+ return args;
84
+ }
85
+ /** Send SIGINT to interrupt the current turn. */
86
+ interrupt() {
87
+ this.child?.kill('SIGINT');
88
+ }
89
+ stop() {
90
+ this.shuttingDown = true;
91
+ if (this.child) {
92
+ try {
93
+ this.child.kill('SIGTERM');
94
+ }
95
+ catch {
96
+ /* ignore */
97
+ }
98
+ }
99
+ this.child = null;
100
+ }
101
+ isRunning() {
102
+ return !!this.child && this.child.exitCode === null;
103
+ }
104
+ getChild() {
105
+ return this.child;
106
+ }
107
+ }
108
+ //# sourceMappingURL=spawn-claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn-claude.js","sourceRoot":"","sources":["../../../src/providers/claude-code/spawn-claude.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuBnD;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAc,SAAQ,YAAY;IAMhB;IALrB,KAAK,GAAwB,IAAI,CAAC;IAClC,OAAO,GAAG,CAAC,CAAC;IACZ,YAAY,GAAG,KAAK,CAAC;IACrB,WAAW,GAAG,CAAC,CAAC;IAExB,YAA6B,IAAqB;QAChD,KAAK,EAAE,CAAC;QADmB,SAAI,GAAJ,IAAI,CAAiB;IAElD,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO;QACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAEO,SAAS;QACf,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE;YACpC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;YAC3B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAAuB;YACtE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9D,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;YAChC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAC1B,YAAY,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9E,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,YAAY,EAAE,IAAI,CAAC,oBAAoB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC/D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YAChC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO;YAC9B,sCAAsC;YACtC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,WAAW,GAAG,MAAM;gBAAE,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;YAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;YAC7C,mEAAmE;YACnE,0EAA0E;YAC1E,yDAAyD;YACzD,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,IAAI,IAAI,CAAC,OAAO,GAAG,UAAU,EAAE,CAAC;gBAC9B,YAAY,EAAE,IAAI,CAChB,eAAe,EACf,OAAO,EACP,kCAAkC,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,UAAU,cAAc,CAC/E,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;YAChE,YAAY,EAAE,IAAI,CAChB,0BAA0B,EAC1B,MAAM,EACN,sBAAsB,IAAI,IAAI,MAAM,QAAQ,MAAM,IAAI,MAAM,WAAW,IAAI,CAAC,OAAO,IAAI,UAAU,OAAO,OAAO,IAAI,CACpH,CAAC;YACF,UAAU,CAAC,GAAG,EAAE;gBACd,IAAI,CAAC,IAAI,CAAC,YAAY;oBAAE,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3C,CAAC,EAAE,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC1B,YAAY,EAAE,IAAI,CAAC,gBAAgB,EAAE,MAAM,EAAE,uBAAuB,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IACpF,CAAC;IAEO,SAAS;QACf,MAAM,IAAI,GAAa,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,yBAAyB,CAAC,CAAC;QAC3F,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,IAAI;YAAE,IAAI,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;QACvF,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,iDAAiD;IACjD,SAAS;QACP,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAED,IAAI;QACF,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC7B,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,SAAS;QACP,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC;IACtD,CAAC;IAED,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;CACF"}
@@ -1,11 +1,13 @@
1
1
  import type { IAgentProvider } from './types.js';
2
2
  export type { IAgentProvider, AgentHealthStatus, SendMessageParams } from './types.js';
3
- export type { ChatSession, ChatMessage, ChatAttachment, ChatContentBlock, IChatProvider } from './chat-types.js';
3
+ export type { ChatSession, ChatMessage, ChatAttachment, ChatContentBlock, IChatProvider, } from './chat-types.js';
4
4
  export { OpenclawProvider } from './openclaw/index.js';
5
5
  export { MockProvider } from './mock/index.js';
6
6
  export type { MockAction } from './mock/index.js';
7
+ export { ClaudeCodeProvider } from './claude-code/index.js';
8
+ export type { ClaudeCodeProviderConfig } from './claude-code/index.js';
7
9
  export declare function configureProvider(provider: IAgentProvider): void;
8
10
  export declare function getProvider(): IAgentProvider | null;
9
11
  /** For testing only — reset the global provider */
10
12
  export declare function resetProvider(): void;
11
- export declare function createProvider(type: string, config: Record<string, string>): IAgentProvider;
13
+ export declare function createProvider(type: string, config: Record<string, string | undefined>): IAgentProvider;
@@ -1,7 +1,10 @@
1
1
  import { OpenclawProvider } from './openclaw/index.js';
2
2
  import { MockProvider } from './mock/index.js';
3
+ import { ClaudeCodeProvider } from './claude-code/index.js';
4
+ import { getWorkspaceDir } from '../workspace.js';
3
5
  export { OpenclawProvider } from './openclaw/index.js';
4
6
  export { MockProvider } from './mock/index.js';
7
+ export { ClaudeCodeProvider } from './claude-code/index.js';
5
8
  let _provider = null;
6
9
  export function configureProvider(provider) {
7
10
  _provider = provider;
@@ -19,6 +22,19 @@ export function createProvider(type, config) {
19
22
  return new OpenclawProvider(config.wsUrl ?? 'ws://localhost:18789', config.wsToken ?? '');
20
23
  case 'mock':
21
24
  return new MockProvider();
25
+ case 'claude-code':
26
+ return new ClaudeCodeProvider({
27
+ // Prefer explicit workspaceDir from caller; otherwise fall back to the
28
+ // shared workspace util so K8s / VM / standalone all resolve
29
+ // consistently (no hardcoded /home/ubuntu).
30
+ workspaceDir: config.workspaceDir ?? getWorkspaceDir(),
31
+ anthropicApiKey: config.anthropicApiKey,
32
+ anthropicAuthToken: config.anthropicAuthToken,
33
+ claudeCodeOauthToken: config.claudeCodeOauthToken,
34
+ backendUrl: config.backendUrl,
35
+ agentToken: config.agentToken,
36
+ claudeMdContent: config.claudeMdContent,
37
+ });
22
38
  default:
23
39
  throw new Error(`Unknown provider type: ${type}`);
24
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAI/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,IAAI,SAAS,GAA0B,IAAI,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,QAAwB;IACxD,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa;IAC3B,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,MAA8B;IACzE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,UAAU;YACb,OAAO,IAAI,gBAAgB,CACzB,MAAM,CAAC,KAAK,IAAI,sBAAsB,EACtC,MAAM,CAAC,OAAO,IAAI,EAAE,CACrB,CAAC;QACJ,KAAK,MAAM;YACT,OAAO,IAAI,YAAY,EAAE,CAAC;QAC5B;YACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAUlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,IAAI,SAAS,GAA0B,IAAI,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,QAAwB;IACxD,SAAS,GAAG,QAAQ,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa;IAC3B,SAAS,GAAG,IAAI,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,MAA0C;IAE1C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,UAAU;YACb,OAAO,IAAI,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,sBAAsB,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC5F,KAAK,MAAM;YACT,OAAO,IAAI,YAAY,EAAE,CAAC;QAC5B,KAAK,aAAa;YAChB,OAAO,IAAI,kBAAkB,CAAC;gBAC5B,uEAAuE;gBACvE,6DAA6D;gBAC7D,4CAA4C;gBAC5C,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,eAAe,EAAE;gBACtD,eAAe,EAAE,MAAM,CAAC,eAAe;gBACvC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,oBAAoB,EAAE,MAAM,CAAC,oBAAoB;gBACjD,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,eAAe,EAAE,MAAM,CAAC,eAAe;aACxC,CAAC,CAAC;QACL;YACE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/mock/index.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,MAAM,CAAC;IACf,UAAU,GAAG,KAAK,CAAC;IAE3B,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAyB;QACzC,MAAM,YAAY,GAAG,gCAAgC,CAAC;QACtD,MAAM,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,WAAmB,EAAE,MAAe;QACnD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,WAAmB,EAAE,MAAe;QAC9C,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,QAAQ;IACV,CAAC;CAEF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/mock/index.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,OAAO,YAAY;IACd,IAAI,GAAG,MAAM,CAAC;IACf,UAAU,GAAG,KAAK,CAAC;IAE3B,KAAK,CAAC,OAAO;QACX,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAyB;QACzC,MAAM,YAAY,GAAG,gCAAgC,CAAC;QACtD,MAAM,CAAC,MAAM,EAAE,CAAC,YAAY,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,WAAmB,EAAE,MAAe;QACnD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,WAAmB,EAAE,MAAe;QAC9C,QAAQ;IACV,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAO;QACX,QAAQ;IACV,CAAC;CACF"}
@@ -1,7 +1,7 @@
1
1
  import crypto from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- import os from 'node:os';
4
+ import { config } from '../../config.js';
5
5
  const ED25519_SPKI_PREFIX = Buffer.from('302a300506032b6570032100', 'hex');
6
6
  function base64UrlEncode(buf) {
7
7
  return buf.toString('base64').replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/g, '');
@@ -43,7 +43,7 @@ export function buildDeviceAuthPayloadV3(params) {
43
43
  ].join('|');
44
44
  }
45
45
  function getIdentityPath() {
46
- const stateDir = process.env.OPENCLAW_STATE_DIR?.trim() || path.join(os.homedir(), '.openclaw');
46
+ const stateDir = config.stateDir;
47
47
  return path.join(stateDir, 'identity', 'device.json');
48
48
  }
49
49
  export function loadOrCreateDeviceIdentity() {