@openclaw-cloud/agent-controller 3.0.0-beta.2 → 3.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 (169) hide show
  1. package/bin/agent-controller.js +5 -9
  2. package/dist/api.d.ts +16 -0
  3. package/dist/api.js.map +1 -1
  4. package/dist/commands/agent-update-cli.js +23 -38
  5. package/dist/commands/agent-update-cli.js.map +1 -1
  6. package/dist/commands/backup-cli.js +6 -4
  7. package/dist/commands/backup-cli.js.map +1 -1
  8. package/dist/commands/bootstrap.d.ts +10 -1
  9. package/dist/commands/bootstrap.js +33 -73
  10. package/dist/commands/bootstrap.js.map +1 -1
  11. package/dist/commands/channel-server.js +4 -3
  12. package/dist/commands/channel-server.js.map +1 -1
  13. package/dist/commands/heartbeat-cli.js +2 -6
  14. package/dist/commands/heartbeat-cli.js.map +1 -1
  15. package/dist/commands/install-deps.d.ts +10 -0
  16. package/dist/commands/install-deps.js +33 -43
  17. package/dist/commands/install-deps.js.map +1 -1
  18. package/dist/commands/knowledge-sync-cli.js +6 -4
  19. package/dist/commands/knowledge-sync-cli.js.map +1 -1
  20. package/dist/commands/plugin-update-cli.js +4 -8
  21. package/dist/commands/plugin-update-cli.js.map +1 -1
  22. package/dist/commands/self-update.d.ts +1 -0
  23. package/dist/commands/self-update.js +76 -37
  24. package/dist/commands/self-update.js.map +1 -1
  25. package/dist/commands/update-skills.js +7 -6
  26. package/dist/commands/update-skills.js.map +1 -1
  27. package/dist/config-file.d.ts +24 -0
  28. package/dist/config-file.js +59 -20
  29. package/dist/config-file.js.map +1 -1
  30. package/dist/config.d.ts +10 -0
  31. package/dist/config.js +28 -0
  32. package/dist/config.js.map +1 -1
  33. package/dist/connection.d.ts +4 -0
  34. package/dist/connection.js +50 -5
  35. package/dist/connection.js.map +1 -1
  36. package/dist/handlers/agent-update.d.ts +4 -0
  37. package/dist/handlers/agent-update.js +16 -44
  38. package/dist/handlers/agent-update.js.map +1 -1
  39. package/dist/handlers/deploy.d.ts +1 -2
  40. package/dist/handlers/deploy.js +4 -20
  41. package/dist/handlers/deploy.js.map +1 -1
  42. package/dist/handlers/diagnostics.js +0 -12
  43. package/dist/handlers/diagnostics.js.map +1 -1
  44. package/dist/handlers/file-delete.js +4 -5
  45. package/dist/handlers/file-delete.js.map +1 -1
  46. package/dist/handlers/file-write.js +4 -4
  47. package/dist/handlers/file-write.js.map +1 -1
  48. package/dist/handlers/knowledge-sync.js +2 -14
  49. package/dist/handlers/knowledge-sync.js.map +1 -1
  50. package/dist/handlers/memory.d.ts +11 -1
  51. package/dist/handlers/memory.js.map +1 -1
  52. package/dist/handlers/onboarding.d.ts +1 -2
  53. package/dist/handlers/onboarding.js +3 -20
  54. package/dist/handlers/onboarding.js.map +1 -1
  55. package/dist/handlers/package-install.js +1 -1
  56. package/dist/handlers/package-install.js.map +1 -1
  57. package/dist/handlers/pair.js +0 -12
  58. package/dist/handlers/pair.js.map +1 -1
  59. package/dist/handlers/restart.d.ts +1 -2
  60. package/dist/handlers/restart.js +3 -8
  61. package/dist/handlers/restart.js.map +1 -1
  62. package/dist/handlers/slack-event.d.ts +13 -0
  63. package/dist/handlers/slack-event.js +40 -0
  64. package/dist/handlers/slack-event.js.map +1 -0
  65. package/dist/handlers/stop.js +0 -7
  66. package/dist/handlers/stop.js.map +1 -1
  67. package/dist/handlers/telegram-webhook.js +3 -22
  68. package/dist/handlers/telegram-webhook.js.map +1 -1
  69. package/dist/handlers/update-config.js +4 -13
  70. package/dist/handlers/update-config.js.map +1 -1
  71. package/dist/heartbeat.d.ts +7 -1
  72. package/dist/heartbeat.js +35 -6
  73. package/dist/heartbeat.js.map +1 -1
  74. package/dist/index.d.ts +0 -8
  75. package/dist/index.js +96 -119
  76. package/dist/index.js.map +1 -1
  77. package/dist/lockfile.js +2 -1
  78. package/dist/lockfile.js.map +1 -1
  79. package/dist/mock-board-worker.d.ts +31 -0
  80. package/dist/mock-board-worker.js +125 -0
  81. package/dist/mock-board-worker.js.map +1 -0
  82. package/dist/platform/linux.js +6 -5
  83. package/dist/platform/linux.js.map +1 -1
  84. package/dist/platform/macos.js +3 -2
  85. package/dist/platform/macos.js.map +1 -1
  86. package/dist/platform/windows.js +3 -2
  87. package/dist/platform/windows.js.map +1 -1
  88. package/dist/providers/claude-code/channel-server.js +2 -1
  89. package/dist/providers/claude-code/channel-server.js.map +1 -1
  90. package/dist/providers/claude-code/settings-writer.d.ts +3 -2
  91. package/dist/providers/claude-code/settings-writer.js +6 -15
  92. package/dist/providers/claude-code/settings-writer.js.map +1 -1
  93. package/dist/providers/index.d.ts +13 -0
  94. package/dist/providers/index.js +25 -0
  95. package/dist/providers/index.js.map +1 -1
  96. package/dist/providers/openclaw/gateway-adapter.js +2 -0
  97. package/dist/providers/openclaw/gateway-adapter.js.map +1 -1
  98. package/dist/providers/openclaw/gateway-client.js +6 -1
  99. package/dist/providers/openclaw/gateway-client.js.map +1 -1
  100. package/dist/providers/openclaw/index.js +34 -13
  101. package/dist/providers/openclaw/index.js.map +1 -1
  102. package/dist/slack-relay-server.d.ts +48 -0
  103. package/dist/slack-relay-server.js +197 -0
  104. package/dist/slack-relay-server.js.map +1 -0
  105. package/dist/types.d.ts +16 -1
  106. package/dist/utils/anthropic-auth.d.ts +14 -0
  107. package/dist/utils/anthropic-auth.js +24 -0
  108. package/dist/utils/anthropic-auth.js.map +1 -1
  109. package/dist/utils/apply-config.d.ts +65 -12
  110. package/dist/utils/apply-config.js +308 -100
  111. package/dist/utils/apply-config.js.map +1 -1
  112. package/dist/utils/claude-env.d.ts +6 -0
  113. package/dist/utils/claude-env.js +29 -28
  114. package/dist/utils/claude-env.js.map +1 -1
  115. package/dist/utils/config-merge-paths.d.ts +15 -1
  116. package/dist/utils/config-merge-paths.js +17 -3
  117. package/dist/utils/config-merge-paths.js.map +1 -1
  118. package/dist/utils/deploy-type.d.ts +16 -0
  119. package/dist/utils/deploy-type.js +34 -0
  120. package/dist/utils/deploy-type.js.map +1 -0
  121. package/dist/utils/env.d.ts +0 -1
  122. package/dist/utils/env.js +0 -8
  123. package/dist/utils/env.js.map +1 -1
  124. package/dist/utils/exec.d.ts +11 -0
  125. package/dist/utils/exec.js +47 -1
  126. package/dist/utils/exec.js.map +1 -1
  127. package/dist/utils/gateway-restart-handler.d.ts +6 -2
  128. package/dist/utils/gateway-restart-handler.js +11 -2
  129. package/dist/utils/gateway-restart-handler.js.map +1 -1
  130. package/dist/utils/gateway-restart.d.ts +7 -0
  131. package/dist/utils/gateway-restart.js +12 -4
  132. package/dist/utils/gateway-restart.js.map +1 -1
  133. package/dist/utils/knowledge-graph.d.ts +13 -1
  134. package/dist/utils/knowledge-graph.js +75 -29
  135. package/dist/utils/knowledge-graph.js.map +1 -1
  136. package/dist/utils/legacy-mcp-servers.d.ts +12 -0
  137. package/dist/utils/legacy-mcp-servers.js +37 -0
  138. package/dist/utils/legacy-mcp-servers.js.map +1 -0
  139. package/dist/utils/log-collector.js +2 -1
  140. package/dist/utils/log-collector.js.map +1 -1
  141. package/dist/utils/openclaw-ops.d.ts +45 -3
  142. package/dist/utils/openclaw-ops.js +94 -15
  143. package/dist/utils/openclaw-ops.js.map +1 -1
  144. package/dist/utils/openclaw-stack-update.d.ts +31 -0
  145. package/dist/utils/openclaw-stack-update.js +50 -0
  146. package/dist/utils/openclaw-stack-update.js.map +1 -0
  147. package/dist/utils/package-install.js +4 -12
  148. package/dist/utils/package-install.js.map +1 -1
  149. package/dist/utils/package-version.d.ts +10 -0
  150. package/dist/utils/package-version.js +18 -0
  151. package/dist/utils/package-version.js.map +1 -0
  152. package/dist/utils/parse-id-at-version.d.ts +15 -0
  153. package/dist/utils/parse-id-at-version.js +22 -0
  154. package/dist/utils/parse-id-at-version.js.map +1 -0
  155. package/dist/utils/plugin-reconcile.d.ts +46 -0
  156. package/dist/utils/plugin-reconcile.js +201 -0
  157. package/dist/utils/plugin-reconcile.js.map +1 -0
  158. package/dist/utils/response.js +1 -0
  159. package/dist/utils/response.js.map +1 -1
  160. package/dist/utils/write-home-files.d.ts +27 -0
  161. package/dist/utils/write-home-files.js +72 -0
  162. package/dist/utils/write-home-files.js.map +1 -0
  163. package/dist/utils/write-workspace-files.d.ts +1 -1
  164. package/dist/utils/write-workspace-files.js +19 -7
  165. package/dist/utils/write-workspace-files.js.map +1 -1
  166. package/dist/workspace.d.ts +11 -0
  167. package/dist/workspace.js +20 -0
  168. package/dist/workspace.js.map +1 -1
  169. package/package.json +9 -4
@@ -0,0 +1,197 @@
1
+ import { WebSocket, WebSocketServer } from 'ws';
2
+ import { config } from './config.js';
3
+ import { toErrorMessage } from './utils/response.js';
4
+ /**
5
+ * Daemon-hosted local Slack relay WS **server** (transport variant B).
6
+ *
7
+ * The openclaw runtime in this same pod runs `channels.slack.mode:'relay'` and
8
+ * dials us at `ws://127.0.0.1:8788/slack-relay?gateway_id=<AGENT_ID>` with an
9
+ * `Authorization: Bearer <token>` header (see the client in openclaw
10
+ * `extensions/slack/src/monitor/relay-source.ts`). We speak the server half of
11
+ * that wire protocol: send a `hello` on connect and push `slack_event` frames.
12
+ *
13
+ * We resolve each push as soon as the frame is written to the connected client
14
+ * socket (the `ws.send` callback fires with no error) — NOT when openclaw later
15
+ * returns its dispatch `ack`. openclaw only acks after it has dispatched the
16
+ * message to a possibly-busy agent, so waiting on that ack blocked the backend's
17
+ * serial (prefetch(1)) messages-broker for up to a full agent turn and produced
18
+ * false ack-timeouts. openclaw still sends the ack; we simply ignore it. The
19
+ * residual loss window (openclaw crashes after receiving the frame but before
20
+ * dispatch) is covered by openclaw's own channel+ts dedup on redelivery.
21
+ *
22
+ * The backend delivers events over Centrifugo as `slack_event` system commands;
23
+ * `handlers/slack-event.ts` calls `pushSlackEvent()` here. We bind loopback only
24
+ * — the auth token is defense-in-depth on top of that.
25
+ */
26
+ /** Default port; overridable via `SLACK_RELAY_PORT` (see `config.slackRelayPort`). */
27
+ export const SLACK_RELAY_PORT = 8788;
28
+ const SLACK_RELAY_PATH = '/slack-relay';
29
+ /** Matches the client's `SLACK_RELAY_MAX_PAYLOAD_BYTES` (1 MiB). */
30
+ const SLACK_RELAY_MAX_PAYLOAD_BYTES = 1024 * 1024;
31
+ /**
32
+ * Safety net only — a healthy loopback `ws.send` calls back in ~ms. This bounds
33
+ * the ONE remaining wait (the send callback) so a stalled/half-open client can't
34
+ * hang the backend's serial broker. It is NOT a wait for openclaw's dispatch-ack
35
+ * (that ack is ignored); the previous 15s ACK_TIMEOUT_MS was that wait and is
36
+ * gone. A successful send never approaches this budget.
37
+ */
38
+ const SEND_TIMEOUT_MS = 2_000;
39
+ // Module-level singleton so the slack_event handler can reach pushSlackEvent()
40
+ // without threading the server through buildHandlers() — mirrors how handlers
41
+ // reach the shared `logCollector`. Set on start, cleared on close.
42
+ let state = null;
43
+ export function startSlackRelayServer() {
44
+ const port = config.slackRelayPort ?? SLACK_RELAY_PORT;
45
+ const wss = new WebSocketServer({
46
+ host: '127.0.0.1',
47
+ port,
48
+ path: SLACK_RELAY_PATH,
49
+ maxPayload: SLACK_RELAY_MAX_PAYLOAD_BYTES,
50
+ perMessageDeflate: false,
51
+ verifyClient: verifyRelayClient,
52
+ });
53
+ const serverState = { wss, socket: null };
54
+ state = serverState;
55
+ wss.on('connection', (ws) => handleConnection(serverState, ws));
56
+ wss.on('listening', () => {
57
+ console.log(`[slack-relay] Slack relay server listening on ws://127.0.0.1:${port}${SLACK_RELAY_PATH}`);
58
+ });
59
+ // Bind errors (e.g. EADDRINUSE) arrive here asynchronously; log and stay up —
60
+ // Slack relay is optional, the rest of the controller must not crash for it.
61
+ wss.on('error', (err) => {
62
+ console.error('[slack-relay] server error:', toErrorMessage(err));
63
+ });
64
+ return {
65
+ close: () => {
66
+ // No pending-ack map to drain — pushSlackEvent resolves on frame-send, so
67
+ // nothing is in flight here.
68
+ serverState.socket?.close();
69
+ serverState.socket = null;
70
+ wss.close();
71
+ if (state === serverState)
72
+ state = null;
73
+ },
74
+ };
75
+ }
76
+ /**
77
+ * Send a `slack_event` frame to the connected openclaw client and resolve as
78
+ * soon as the frame is written to that socket (the `ws.send` callback fires with
79
+ * no error). We do NOT wait for openclaw's dispatch `ack` — see the module
80
+ * header for why. `delivered:true` on the backend therefore means "openclaw's
81
+ * relay client received the frame", not "openclaw dispatched it to the agent".
82
+ *
83
+ * Rejects on: no connected client, a send error, or the send stalling past
84
+ * `SEND_TIMEOUT_MS`. The backend redelivers on rejection (at-least-once), keyed
85
+ * by the same `delivery_id`; the no-client rejection is the durability contract
86
+ * that makes the backend retry rather than drop when nothing is connected.
87
+ */
88
+ export function pushSlackEvent(payload) {
89
+ const serverState = state;
90
+ if (!serverState) {
91
+ return Promise.reject(new Error('Slack relay server not started'));
92
+ }
93
+ const ws = serverState.socket;
94
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
95
+ return Promise.reject(new Error('No Slack relay client connected'));
96
+ }
97
+ const { event, deliveryId, route } = payload;
98
+ const frame = JSON.stringify({
99
+ type: 'slack_event',
100
+ delivery_id: deliveryId,
101
+ route,
102
+ payload: { event },
103
+ });
104
+ return new Promise((resolve, reject) => {
105
+ let settled = false;
106
+ // Bounds the send itself, not openclaw's dispatch. Fires only if the socket
107
+ // stalls (half-open client, full send buffer) so a broken client can't hang
108
+ // the backend's serial broker.
109
+ const timer = setTimeout(() => {
110
+ if (settled)
111
+ return;
112
+ settled = true;
113
+ reject(new Error(`Slack relay send stalled after ${SEND_TIMEOUT_MS}ms (delivery ${deliveryId})`));
114
+ }, SEND_TIMEOUT_MS);
115
+ // A hanging send must never hold the process open during shutdown.
116
+ timer.unref?.();
117
+ ws.send(frame, (err) => {
118
+ if (settled)
119
+ return;
120
+ settled = true;
121
+ clearTimeout(timer);
122
+ if (err)
123
+ reject(err);
124
+ else
125
+ resolve(); // frame handed to the client socket — ack no longer awaited
126
+ });
127
+ });
128
+ }
129
+ function verifyRelayClient(info, cb) {
130
+ const { req } = info;
131
+ const url = new URL(req.url ?? '', 'http://127.0.0.1');
132
+ const gatewayId = url.searchParams.get('gateway_id');
133
+ // Fail closed: reject unless gateway_id names THIS agent.
134
+ if (!gatewayId || gatewayId !== config.agentId) {
135
+ console.warn(`[slack-relay] rejecting connection: gateway_id mismatch (got ${gatewayId ?? 'none'})`);
136
+ cb(false, 403, 'Forbidden');
137
+ return;
138
+ }
139
+ const expectedToken = config.slackRelayAuthToken;
140
+ // Token is defense-in-depth on a loopback-only bind: enforced only when set.
141
+ if (expectedToken && bearerToken(req.headers.authorization) !== expectedToken) {
142
+ console.warn('[slack-relay] rejecting connection: bad or missing bearer token');
143
+ cb(false, 401, 'Unauthorized');
144
+ return;
145
+ }
146
+ cb(true);
147
+ }
148
+ function bearerToken(header) {
149
+ const match = header ? /^Bearer\s+(.+)$/i.exec(header.trim()) : null;
150
+ return match?.[1]?.trim() || undefined;
151
+ }
152
+ function handleConnection(serverState, ws) {
153
+ // openclaw reconnects with backoff — always keep the newest socket and drop
154
+ // any stale one so pushSlackEvent talks to the live connection.
155
+ if (serverState.socket && serverState.socket !== ws) {
156
+ serverState.socket.close();
157
+ }
158
+ serverState.socket = ws;
159
+ console.log('[slack-relay] openclaw relay client connected');
160
+ // hello first — the client reads an optional slack_identity from it; we omit it.
161
+ ws.send(JSON.stringify({ type: 'hello' }));
162
+ ws.on('message', (data) => handleClientFrame(data));
163
+ ws.on('error', (err) => {
164
+ console.error('[slack-relay] client socket error:', toErrorMessage(err));
165
+ });
166
+ ws.on('close', () => {
167
+ console.log('[slack-relay] openclaw relay client disconnected');
168
+ if (serverState.socket === ws) {
169
+ serverState.socket = null;
170
+ }
171
+ // Nothing to fail on close — pushSlackEvent already resolved on frame-send,
172
+ // so there are no in-flight deliveries tied to this socket.
173
+ });
174
+ }
175
+ function handleClientFrame(data) {
176
+ // openclaw still sends `{ type:'ack', delivery_id }` after it dispatches each
177
+ // event to the agent. We resolved the push on frame-send and keep no pending
178
+ // map, so every ack — for any delivery_id, already-resolved or unknown — is a
179
+ // harmless no-op. Parse only to surface a malformed frame; the result is
180
+ // deliberately discarded.
181
+ try {
182
+ JSON.parse(rawDataToString(data));
183
+ }
184
+ catch {
185
+ console.warn('[slack-relay] dropping malformed JSON frame from client');
186
+ }
187
+ }
188
+ function rawDataToString(data) {
189
+ if (typeof data === 'string')
190
+ return data;
191
+ if (Buffer.isBuffer(data))
192
+ return data.toString('utf8');
193
+ if (Array.isArray(data))
194
+ return Buffer.concat(data).toString('utf8');
195
+ return Buffer.from(data).toString('utf8');
196
+ }
197
+ //# sourceMappingURL=slack-relay-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-relay-server.js","sourceRoot":"","sources":["../src/slack-relay-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAgB,MAAM,IAAI,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AACxC,oEAAoE;AACpE,MAAM,6BAA6B,GAAG,IAAI,GAAG,IAAI,CAAC;AAClD;;;;;;GAMG;AACH,MAAM,eAAe,GAAG,KAAK,CAAC;AAa9B,+EAA+E;AAC/E,8EAA8E;AAC9E,mEAAmE;AACnE,IAAI,KAAK,GAA4B,IAAI,CAAC;AAE1C,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,IAAI,gBAAgB,CAAC;IACvD,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC;QAC9B,IAAI,EAAE,WAAW;QACjB,IAAI;QACJ,IAAI,EAAE,gBAAgB;QACtB,UAAU,EAAE,6BAA6B;QACzC,iBAAiB,EAAE,KAAK;QACxB,YAAY,EAAE,iBAAiB;KAChC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAqB,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC5D,KAAK,GAAG,WAAW,CAAC;IAEpB,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,gBAAgB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,GAAG,EAAE;QACvB,OAAO,CAAC,GAAG,CACT,gEAAgE,IAAI,GAAG,gBAAgB,EAAE,CAC1F,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,8EAA8E;IAC9E,6EAA6E;IAC7E,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACtB,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,GAAG,EAAE;YACV,0EAA0E;YAC1E,6BAA6B;YAC7B,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;YAC5B,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC;YAC1B,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,IAAI,KAAK,KAAK,WAAW;gBAAE,KAAK,GAAG,IAAI,CAAC;QAC1C,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,OAA0B;IACvD,MAAM,WAAW,GAAG,KAAK,CAAC;IAC1B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,CAAC;IAC9B,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;QAC5C,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE,UAAU;QACvB,KAAK;QACL,OAAO,EAAE,EAAE,KAAK,EAAE;KACnB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,4EAA4E;QAC5E,4EAA4E;QAC5E,+BAA+B;QAC/B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,MAAM,CACJ,IAAI,KAAK,CAAC,kCAAkC,eAAe,gBAAgB,UAAU,GAAG,CAAC,CAC1F,CAAC;QACJ,CAAC,EAAE,eAAe,CAAC,CAAC;QACpB,mEAAmE;QACnE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAEhB,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;YACrB,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,GAAG;gBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;gBAChB,OAAO,EAAE,CAAC,CAAC,4DAA4D;QAC9E,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CACxB,IAA8B,EAC9B,EAA2D;IAE3D,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IACrB,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,kBAAkB,CAAC,CAAC;IACvD,MAAM,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACrD,0DAA0D;IAC1D,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,CACV,gEAAgE,SAAS,IAAI,MAAM,GAAG,CACvF,CAAC;QACF,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,mBAAmB,CAAC;IACjD,6EAA6E;IAC7E,IAAI,aAAa,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,aAAa,EAAE,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;QAChF,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,CAAC;QAC/B,OAAO;IACT,CAAC;IACD,EAAE,CAAC,IAAI,CAAC,CAAC;AACX,CAAC;AAED,SAAS,WAAW,CAAC,MAA0B;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;AACzC,CAAC;AAED,SAAS,gBAAgB,CAAC,WAA6B,EAAE,EAAa;IACpE,4EAA4E;IAC5E,gEAAgE;IAChE,IAAI,WAAW,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACpD,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;IACD,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAE7D,iFAAiF;IACjF,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAE3C,EAAE,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACrB,OAAO,CAAC,KAAK,CAAC,oCAAoC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAClB,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;QAChE,IAAI,WAAW,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;YAC9B,WAAW,CAAC,MAAM,GAAG,IAAI,CAAC;QAC5B,CAAC;QACD,4EAA4E;QAC5E,4DAA4D;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAa;IACtC,8EAA8E;IAC9E,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,0BAA0B;IAC1B,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,IAAI,CAAC,yDAAyD,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAmB,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC"}
package/dist/types.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * chat:<agentId> — chat commands (list sessions, history, send)
5
5
  * heartbeat:<agentId> — heartbeat publications from agent
6
6
  */
7
- export type CommandType = 'diagnostics' | 'restart' | 'deploy' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'chat_delete_session' | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete' | 'knowledge_sync' | 'self_update' | 'plugin_update' | 'agent_update' | 'telegram_webhook' | 'update_skills' | 'update_config' | 'memory_create_entities' | 'memory_create_relations' | 'memory_add_observations' | 'memory_delete_entities' | 'memory_delete_observations' | 'memory_delete_relations' | 'memory_read_graph' | 'memory_search_nodes' | 'memory_open_nodes';
7
+ export type CommandType = 'diagnostics' | 'restart' | 'deploy' | 'pair' | 'stop' | 'backup' | 'chat_list_sessions' | 'chat_history' | 'chat_send' | 'chat_delete_session' | 'package_install' | 'file_write' | 'file_delete' | 'onboarding_complete' | 'knowledge_sync' | 'self_update' | 'plugin_update' | 'agent_update' | 'telegram_webhook' | 'slack_event' | 'update_skills' | 'update_config' | 'memory_create_entities' | 'memory_create_relations' | 'memory_add_observations' | 'memory_delete_entities' | 'memory_delete_observations' | 'memory_delete_relations' | 'memory_read_graph' | 'memory_search_nodes' | 'memory_open_nodes';
8
8
  export interface AgentCommand {
9
9
  id: string;
10
10
  type: CommandType;
@@ -22,9 +22,23 @@ export interface GatewayStatus {
22
22
  rpcOk: boolean;
23
23
  pid?: number;
24
24
  }
25
+ /**
26
+ * Live snapshot of the Centrifugo WS connection state, so backend can tell
27
+ * whether this agent is actually able to receive commands (heartbeat 200 alone
28
+ * does not prove the WS is up — the controller can be alive while the Centrifugo
29
+ * subscription is in error / reconnecting).
30
+ */
31
+ export interface CentrifugoStatus {
32
+ connected: boolean;
33
+ state: 'connected' | 'connecting' | 'disconnected';
34
+ commandSubState: 'subscribed' | 'subscribing' | 'unsubscribed';
35
+ chatSubState: 'subscribed' | 'subscribing' | 'unsubscribed';
36
+ lastEventAt?: string | null;
37
+ }
25
38
  export interface InstalledVersions {
26
39
  agentController: string;
27
40
  agentRuntime?: string;
41
+ nodeVersion?: string;
28
42
  plugins?: Array<{
29
43
  id: string;
30
44
  version: string;
@@ -42,5 +56,6 @@ export interface HeartbeatPayload {
42
56
  uptime: number;
43
57
  };
44
58
  gatewayStatus?: GatewayStatus;
59
+ centrifugoStatus?: CentrifugoStatus;
45
60
  lastMessageAt?: string | null;
46
61
  }
@@ -23,3 +23,17 @@ export declare function classifyAnthropicAuth(input: {
23
23
  anthropicAuthToken?: string;
24
24
  anthropicApiKey?: string;
25
25
  }): AnthropicAuth;
26
+ /** The permissions block both Claude settings writers emit, verbatim. */
27
+ export declare const CLAUDE_PERMISSIONS: {
28
+ allow: string[];
29
+ };
30
+ /**
31
+ * The settings.json base shared by both writers: the permissions block plus the
32
+ * `env` entry the auth classification implies.
33
+ *
34
+ * `providers/claude-code/settings-writer.ts` uses this as its complete output;
35
+ * `utils/claude-env.ts` uses it for the OAuth branch and composes its own
36
+ * apiKeyHelper variant for the API-key branch (that flow reads the key out of
37
+ * the VM `.env` at runtime instead of embedding it).
38
+ */
39
+ export declare function buildClaudeSettings(auth: AnthropicAuth): Record<string, unknown>;
@@ -10,4 +10,28 @@ export function classifyAnthropicAuth(input) {
10
10
  return { kind: 'auth_token', value: apiKey };
11
11
  return { kind: 'api_key', value: apiKey };
12
12
  }
13
+ /** The permissions block both Claude settings writers emit, verbatim. */
14
+ export const CLAUDE_PERMISSIONS = {
15
+ allow: ['Bash(*)', 'Read(*)', 'Write(*)', 'Edit(*)', 'mcp__*'],
16
+ };
17
+ /**
18
+ * The settings.json base shared by both writers: the permissions block plus the
19
+ * `env` entry the auth classification implies.
20
+ *
21
+ * `providers/claude-code/settings-writer.ts` uses this as its complete output;
22
+ * `utils/claude-env.ts` uses it for the OAuth branch and composes its own
23
+ * apiKeyHelper variant for the API-key branch (that flow reads the key out of
24
+ * the VM `.env` at runtime instead of embedding it).
25
+ */
26
+ export function buildClaudeSettings(auth) {
27
+ switch (auth.kind) {
28
+ case 'auth_token':
29
+ return { permissions: CLAUDE_PERMISSIONS, env: { ANTHROPIC_AUTH_TOKEN: auth.value } };
30
+ case 'api_key':
31
+ return { permissions: CLAUDE_PERMISSIONS, env: { ANTHROPIC_API_KEY: auth.value } };
32
+ case 'none':
33
+ // No static credentials: rely on ~/.claude/.credentials.json (subscription login).
34
+ return { permissions: CLAUDE_PERMISSIONS };
35
+ }
36
+ }
13
37
  //# sourceMappingURL=anthropic-auth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-auth.js","sourceRoot":"","sources":["../../src/utils/anthropic-auth.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,qBAAqB,CAAC,KAGrC;IACC,MAAM,SAAS,GAAG,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAE/D,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAErC,yEAAyE;IACzE,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAElF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"anthropic-auth.js","sourceRoot":"","sources":["../../src/utils/anthropic-auth.ts"],"names":[],"mappings":"AAiBA,MAAM,UAAU,qBAAqB,CAAC,KAGrC;IACC,MAAM,SAAS,GAAG,KAAK,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACnD,IAAI,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAE/D,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAErC,yEAAyE;IACzE,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAElF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,KAAK,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;CAC/D,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAmB;IACrD,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,GAAG,EAAE,EAAE,oBAAoB,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QACxF,KAAK,SAAS;YACZ,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,GAAG,EAAE,EAAE,iBAAiB,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;QACrF,KAAK,MAAM;YACT,mFAAmF;YACnF,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAC/C,CAAC;AACH,CAAC"}
@@ -1,14 +1,29 @@
1
- import type { BootstrapResult } from '../api.js';
1
+ import type { AgentApi, BootstrapResult } from '../api.js';
2
+ /** Canonical openclaw.json location — `<stateDir>/openclaw.json`. */
3
+ export declare function getOpenclawJsonPath(): string;
2
4
  /**
3
- * Apply config from bootstrap API response:
5
+ * The ONE writer for openclaw.json — atomic (tmp + rename) and mode 0600.
6
+ *
7
+ * Both properties are load-bearing and used to be split across three
8
+ * independent write sites: the full apply wrote 0600 but non-atomically (a
9
+ * reader could see a truncated file), the scoped merge wrote atomically but
10
+ * with no mode (tmp + rename RESETS a pre-existing 0600 file to 0644, leaking
11
+ * `models.providers[*].apiKey`), and the startup memory-MCP injection did
12
+ * neither. Everything goes through here now.
13
+ */
14
+ export declare function writeOpenclawJson(data: Record<string, unknown>): Promise<void>;
15
+ /**
16
+ * FULL apply — only ever legal on a managed install (see `isManagedDeploy`).
17
+ * Rewrites files wholesale:
4
18
  * 1. Write openclaw.json
5
- * 2. Write auth-profiles.json + settings.json (from envVars)
6
- * 3. Write .env (for VM agents)
7
- * 4. Restart gateway
19
+ * 2. Write workspace files + skills
20
+ * 3. Write auth-profiles.json + settings.json (from envVars)
21
+ * 4. MERGE .env (for VM agents) — never a wholesale replace, see step 4 below
22
+ * 5. Restart gateway
8
23
  */
9
24
  export declare function applyBootstrapConfig(data: BootstrapResult): Promise<void>;
10
- type MergeOutcome = 'added' | 'equal' | 'conflict' | 'merged';
11
- interface MergeResult {
25
+ type MergeOutcome = 'added' | 'equal' | 'conflict' | 'merged' | 'overwritten';
26
+ export interface MergeResult {
12
27
  changed: boolean;
13
28
  outcomes: Array<{
14
29
  path: string;
@@ -26,12 +41,50 @@ interface MergeResult {
26
41
  * - Missing local value → remote is written.
27
42
  * - Equal values → no-op.
28
43
  * - Differing values → conflict, local wins, warn+skip logged.
29
- * - For `mcp.servers` (and other object paths): conflict detection is
30
- * per-key inside the dict.
31
- * - Does NOT restart the gateway (standalone owns their own lifecycle).
32
- * - Settings.json (~/.claude/settings.json) gets the same treatment for
33
- * `mcp.servers` and `env` only — apiKeyHelper/permissions/etc untouched.
44
+ * - For object paths: conflict detection is per-key inside the dict.
45
+ * - **Exception — `mcp.servers` in openclaw.json is REMOTE-wins per key.**
46
+ * That section is backend-managed: the URL, transport and bearer token of a
47
+ * backend-generated server (e.g. `openclaw-cloud`) change over time, and
48
+ * under local-wins an updated entry could never reach a standalone agent —
49
+ * a rotated token was skipped forever (observed live 2026-08-02). Every
50
+ * server NAME present in the payload is overwritten; names that exist only
51
+ * locally are left untouched.
52
+ * LIMITATION — no deletions. A server removed in the backend stays in the
53
+ * local openclaw.json: the payload cannot distinguish "removed upstream"
54
+ * from "never managed here", and dropping local-only entries would delete
55
+ * the user's own servers. Removal on standalone is a manual edit.
56
+ * - Restarts the gateway **iff** the `mcp.servers` subtree actually changed.
57
+ * This is a FALLBACK, not the primary delivery mechanism: openclaw 2026.7.1
58
+ * does self-reload this section (verified live 2026-08-03 — the gateway
59
+ * logged `[reload] config hot reload applied (mcp.servers.<name>.url,
60
+ * mcp.servers.<name>.headers)` on an overwrite, with no restart). The
61
+ * restart covers builds/paths where that self-reload does not fire, and
62
+ * mirrors what applyBootstrapConfig does on managed installs. It is
63
+ * best-effort: a failure is logged and does not fail the apply — on a
64
+ * launchd box where `openclaw` is not on the service PATH it never runs at
65
+ * all, and the merge still lands.
66
+ * - Settings.json (~/.claude/settings.json) keeps LOCAL-wins for
67
+ * `mcp.servers` ONLY — apiKeyHelper/permissions/env/etc untouched. `env` is
68
+ * never written here: it would redirect the user's own Claude Code sessions
69
+ * to the platform proxy (see MERGEABLE_SETTINGS_PATHS).
34
70
  * - auth-profiles.json and .env are NOT touched on standalone.
35
71
  */
36
72
  export declare function applyMergeableConfig(data: BootstrapResult): Promise<MergeResult>;
73
+ export interface ApplyConfigOutcome {
74
+ /** True when the full apply ran (managed install), false for scoped merge. */
75
+ managed: boolean;
76
+ /** Present only on the scoped-merge path. */
77
+ mergeResult?: MergeResult;
78
+ }
79
+ /**
80
+ * The one owner of "fetch bootstrap and apply it". Every caller — the
81
+ * `update_config` WS handler, the `bootstrap` CLI, the opt-in startup refresh —
82
+ * goes through here so the deploy-type decision cannot drift between them.
83
+ *
84
+ * Managed installs ('vm'/'k8s') are provisioned end to end by the platform and
85
+ * get the full apply. Anything else — 'standalone', 'external', or a payload
86
+ * from an older backend that omits `deploymentType` — is a third-party machine
87
+ * and gets the scoped, additive merge only. Never rewrite files we don't own.
88
+ */
89
+ export declare function fetchAndApplyConfig(api: AgentApi, agentId: string): Promise<ApplyConfigOutcome>;
37
90
  export {};