@clawling/clawchat-plugin-openclaw 2026.5.12-39 → 2026.5.13-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +7 -0
- package/dist/setup-entry.js +31 -1
- package/dist/src/api-client.js +164 -26
- package/dist/src/client.js +4 -1
- package/dist/src/config-compat.js +120 -0
- package/dist/src/inbound.js +21 -4
- package/dist/src/login.runtime.js +4 -0
- package/dist/src/outbound.js +43 -8
- package/dist/src/plugin-report.js +36 -0
- package/dist/src/protocol-types.js +2 -0
- package/dist/src/refresh-manager.js +278 -0
- package/dist/src/reply-dispatcher.js +5 -2
- package/dist/src/runtime.js +597 -30
- package/dist/src/storage.js +81 -5
- package/dist/src/ws-alignment.js +69 -1
- package/dist/src/ws-client.js +55 -5
- package/index.ts +7 -0
- package/openclaw.plugin.json +7 -0
- package/package.json +1 -1
- package/setup-entry.ts +51 -1
- package/src/api-client.ts +210 -31
- package/src/client.ts +12 -1
- package/src/config-compat.ts +154 -0
- package/src/inbound.ts +24 -5
- package/src/login.runtime.ts +4 -0
- package/src/outbound.ts +47 -9
- package/src/plugin-report.ts +53 -0
- package/src/protocol-types.ts +34 -2
- package/src/refresh-manager.ts +371 -0
- package/src/reply-dispatcher.ts +5 -2
- package/src/runtime.ts +679 -27
- package/src/storage.ts +124 -4
- package/src/ws-alignment.ts +99 -1
- package/src/ws-client.ts +51 -5
- package/dist/src/buffered-stream.js +0 -177
- package/dist/src/streaming.js +0 -65
package/dist/src/storage.js
CHANGED
|
@@ -126,6 +126,13 @@ ALTER TABLE clawchat_messages ADD COLUMN send_status TEXT;
|
|
|
126
126
|
ALTER TABLE clawchat_messages ADD COLUMN protocol_message_id TEXT;
|
|
127
127
|
ALTER TABLE clawchat_messages ADD COLUMN acked_at INTEGER;
|
|
128
128
|
ALTER TABLE clawchat_messages ADD COLUMN send_error TEXT;
|
|
129
|
+
`,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
version: 7,
|
|
133
|
+
name: "activation_device_id",
|
|
134
|
+
sql: `
|
|
135
|
+
ALTER TABLE activations ADD COLUMN device_id TEXT;
|
|
129
136
|
`,
|
|
130
137
|
},
|
|
131
138
|
];
|
|
@@ -193,12 +200,13 @@ export class ClawChatStore {
|
|
|
193
200
|
const ownerUserId = input.ownerUserId?.trim() || null;
|
|
194
201
|
const accessToken = input.accessToken?.trim() || null;
|
|
195
202
|
const refreshToken = input.refreshToken?.trim() || null;
|
|
203
|
+
const deviceId = input.deviceId?.trim() || null;
|
|
196
204
|
this.requireDb()
|
|
197
205
|
.prepare(`INSERT INTO activations(
|
|
198
206
|
platform, account_id, user_id, owner_user_id, access_token, refresh_token,
|
|
199
207
|
activated_at, login_method, conversation_id, bootstrap_sent,
|
|
200
|
-
bootstrap_claimed_at, updated_at
|
|
201
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
208
|
+
bootstrap_claimed_at, device_id, updated_at
|
|
209
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
202
210
|
ON CONFLICT(platform, account_id) DO UPDATE SET
|
|
203
211
|
user_id = excluded.user_id,
|
|
204
212
|
owner_user_id = excluded.owner_user_id,
|
|
@@ -209,15 +217,53 @@ export class ClawChatStore {
|
|
|
209
217
|
conversation_id = excluded.conversation_id,
|
|
210
218
|
bootstrap_sent = excluded.bootstrap_sent,
|
|
211
219
|
bootstrap_claimed_at = NULL,
|
|
220
|
+
device_id = excluded.device_id,
|
|
212
221
|
updated_at = excluded.updated_at`)
|
|
213
|
-
.run(input.platform, input.accountId, userId, ownerUserId, accessToken, refreshToken, now, input.loginMethod ?? null, conversationId, conversationId ? 0 : 1, null, now);
|
|
222
|
+
.run(input.platform, input.accountId, userId, ownerUserId, accessToken, refreshToken, now, input.loginMethod ?? null, conversationId, conversationId ? 0 : 1, null, deviceId, now);
|
|
214
223
|
void conversationId;
|
|
215
224
|
});
|
|
216
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* §0/§A.3 — persist a rotated access+refresh pair durably BEFORE the caller
|
|
228
|
+
* swaps the in-memory token. Identity columns are untouched. Returns whether
|
|
229
|
+
* a row was updated (false ⇒ no activation row exists yet).
|
|
230
|
+
*/
|
|
231
|
+
rotateActivationTokens(input) {
|
|
232
|
+
return this.write(() => {
|
|
233
|
+
const now = input.rotatedAt ?? Date.now();
|
|
234
|
+
const result = this.requireDb()
|
|
235
|
+
.prepare(`UPDATE activations SET
|
|
236
|
+
access_token = ?,
|
|
237
|
+
refresh_token = ?,
|
|
238
|
+
activated_at = ?,
|
|
239
|
+
updated_at = ?
|
|
240
|
+
WHERE platform = ? AND account_id = ?`)
|
|
241
|
+
.run(input.accessToken, input.refreshToken, now, now, input.platform, input.accountId);
|
|
242
|
+
return result.changes > 0;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* §C.1 — auto-logout: blank the `access_token` / `refresh_token` columns so
|
|
247
|
+
* the agent can no longer mint tokens, but KEEP `user_id` / `owner_user_id` /
|
|
248
|
+
* `device_id` so a `/clawchat-activate <code>` re-pair reuses the identity.
|
|
249
|
+
*/
|
|
250
|
+
clearActivationCredentials(input) {
|
|
251
|
+
return this.write(() => {
|
|
252
|
+
const now = Date.now();
|
|
253
|
+
const result = this.requireDb()
|
|
254
|
+
.prepare(`UPDATE activations SET
|
|
255
|
+
access_token = NULL,
|
|
256
|
+
refresh_token = NULL,
|
|
257
|
+
updated_at = ?
|
|
258
|
+
WHERE platform = ? AND account_id = ?`)
|
|
259
|
+
.run(now, input.platform, input.accountId);
|
|
260
|
+
return result.changes > 0;
|
|
261
|
+
});
|
|
262
|
+
}
|
|
217
263
|
getActivationCredentials(input) {
|
|
218
264
|
return this.read(() => {
|
|
219
265
|
const row = this.requireDb()
|
|
220
|
-
.prepare(`SELECT user_id, owner_user_id, access_token, refresh_token
|
|
266
|
+
.prepare(`SELECT user_id, owner_user_id, access_token, refresh_token, activated_at, device_id
|
|
221
267
|
FROM activations
|
|
222
268
|
WHERE platform = ?
|
|
223
269
|
AND account_id = ?`)
|
|
@@ -228,9 +274,15 @@ export class ClawChatStore {
|
|
|
228
274
|
const refreshToken = typeof row?.refresh_token === "string" && row.refresh_token.trim()
|
|
229
275
|
? row.refresh_token.trim()
|
|
230
276
|
: null;
|
|
277
|
+
const activatedAt = typeof row?.activated_at === "number" && Number.isFinite(row.activated_at)
|
|
278
|
+
? row.activated_at
|
|
279
|
+
: null;
|
|
280
|
+
const deviceId = typeof row?.device_id === "string" && row.device_id.trim()
|
|
281
|
+
? row.device_id.trim()
|
|
282
|
+
: null;
|
|
231
283
|
if (!userId || !ownerUserId || !accessToken)
|
|
232
284
|
return null;
|
|
233
|
-
return { userId, ownerUserId, accessToken, refreshToken };
|
|
285
|
+
return { userId, ownerUserId, accessToken, refreshToken, activatedAt, deviceId };
|
|
234
286
|
}) ?? null;
|
|
235
287
|
}
|
|
236
288
|
getActivationConversation(input) {
|
|
@@ -398,6 +450,30 @@ export class ClawChatStore {
|
|
|
398
450
|
.run(input.state, now, input.closeCode ?? null, input.closeReason ?? null, input.error ?? null, now, connectionId);
|
|
399
451
|
});
|
|
400
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Return the most recent server-resolved `device_id` recorded from a
|
|
455
|
+
* `hello-ok` for this account, if any. Reused on reconnect so a pod restart
|
|
456
|
+
* (which mints a fresh hostname and therefore a fresh derived device id)
|
|
457
|
+
* does not present a brand-new device to the server — that would trigger a
|
|
458
|
+
* full inbox replay and orphan the previous device's cursor.
|
|
459
|
+
*/
|
|
460
|
+
getLastResolvedDeviceId(input) {
|
|
461
|
+
return this.read(() => {
|
|
462
|
+
const row = this.requireDb()
|
|
463
|
+
.prepare(`SELECT resolved_device_id
|
|
464
|
+
FROM connections
|
|
465
|
+
WHERE platform = ?
|
|
466
|
+
AND account_id = ?
|
|
467
|
+
AND resolved_device_id IS NOT NULL
|
|
468
|
+
AND resolved_device_id <> ''
|
|
469
|
+
ORDER BY id DESC
|
|
470
|
+
LIMIT 1`)
|
|
471
|
+
.get(input.platform, input.accountId);
|
|
472
|
+
return typeof row?.resolved_device_id === "string" && row.resolved_device_id.trim()
|
|
473
|
+
? row.resolved_device_id.trim()
|
|
474
|
+
: null;
|
|
475
|
+
}) ?? null;
|
|
476
|
+
}
|
|
401
477
|
recordToolCall(input) {
|
|
402
478
|
this.write(() => {
|
|
403
479
|
const startedAt = input.startedAt ?? Date.now();
|
package/dist/src/ws-alignment.js
CHANGED
|
@@ -159,7 +159,8 @@ export function createProtocolControlHandler(options) {
|
|
|
159
159
|
version: "2",
|
|
160
160
|
event: "pong",
|
|
161
161
|
trace_id: env.trace_id ?? "-",
|
|
162
|
-
|
|
162
|
+
// §12: echo the sender's emitted_at verbatim (do not restamp).
|
|
163
|
+
emitted_at: env.emitted_at ?? Date.now(),
|
|
163
164
|
payload: {},
|
|
164
165
|
}));
|
|
165
166
|
return true;
|
|
@@ -176,3 +177,70 @@ export function createProtocolControlHandler(options) {
|
|
|
176
177
|
},
|
|
177
178
|
};
|
|
178
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Observes reliable `notify.signal` frames (§9.4). The agent plugin keeps no
|
|
182
|
+
* friend/roster cache (friends are fetched on demand via REST tools), so there
|
|
183
|
+
* is nothing to invalidate — this is a pure observability hook: it dedups by
|
|
184
|
+
* `event_id` (the live frame and its reliable-inbox replay collapse to one),
|
|
185
|
+
* structured-logs the signal, and returns the outcome. It deliberately takes no
|
|
186
|
+
* action on the agent; wire a real reaction here if the product later needs one.
|
|
187
|
+
*/
|
|
188
|
+
export function createNotifySignalObserver(options) {
|
|
189
|
+
const maxSeen = options.maxSeen ?? 512;
|
|
190
|
+
const seen = new Set();
|
|
191
|
+
const order = [];
|
|
192
|
+
const context = () => options.context?.() ?? { attempt: 1, reconnectCount: 0, state: "ready" };
|
|
193
|
+
const logSignal = (event, action, fields) => {
|
|
194
|
+
const current = context();
|
|
195
|
+
options.log(formatWsLog({
|
|
196
|
+
event,
|
|
197
|
+
accountId: options.accountId,
|
|
198
|
+
attempt: current.attempt,
|
|
199
|
+
reconnectCount: current.reconnectCount,
|
|
200
|
+
state: current.state,
|
|
201
|
+
action,
|
|
202
|
+
fields,
|
|
203
|
+
}));
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
/** Returns whether this signal was newly observed, a duplicate, or malformed. */
|
|
207
|
+
observe(env) {
|
|
208
|
+
const payload = env.payload && typeof env.payload === "object"
|
|
209
|
+
? env.payload
|
|
210
|
+
: undefined;
|
|
211
|
+
const eventId = typeof payload?.event_id === "string" ? payload.event_id : "";
|
|
212
|
+
const type = typeof payload?.type === "string" ? payload.type : "";
|
|
213
|
+
const entityId = typeof payload?.entity_id === "string" ? payload.entity_id : "";
|
|
214
|
+
const version = typeof payload?.version === "number" ? payload.version : undefined;
|
|
215
|
+
if (!eventId || !type) {
|
|
216
|
+
logSignal("notify_signal_invalid", "ignore", [
|
|
217
|
+
["trace_id", env.trace_id],
|
|
218
|
+
["type", type || null],
|
|
219
|
+
["event_id", eventId || null],
|
|
220
|
+
]);
|
|
221
|
+
return "invalid";
|
|
222
|
+
}
|
|
223
|
+
if (seen.has(eventId)) {
|
|
224
|
+
logSignal("notify_signal_duplicate", "ignore", [
|
|
225
|
+
["type", type],
|
|
226
|
+
["event_id", eventId],
|
|
227
|
+
]);
|
|
228
|
+
return "duplicate";
|
|
229
|
+
}
|
|
230
|
+
seen.add(eventId);
|
|
231
|
+
order.push(eventId);
|
|
232
|
+
while (order.length > maxSeen) {
|
|
233
|
+
const evicted = order.shift();
|
|
234
|
+
if (evicted !== undefined)
|
|
235
|
+
seen.delete(evicted);
|
|
236
|
+
}
|
|
237
|
+
logSignal("notify_signal_observed", "observe", [
|
|
238
|
+
["type", type],
|
|
239
|
+
["entity_id", entityId || null],
|
|
240
|
+
["version", version ?? null],
|
|
241
|
+
["event_id", eventId],
|
|
242
|
+
]);
|
|
243
|
+
return "observed";
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
package/dist/src/ws-client.js
CHANGED
|
@@ -301,6 +301,10 @@ export class ClawChatClient extends EventEmitter {
|
|
|
301
301
|
this.emit("typing", env);
|
|
302
302
|
if (env.event === EVENT.CHAT_METADATA_INVALIDATED)
|
|
303
303
|
this.emit("metadata:invalidated", env);
|
|
304
|
+
if (env.event === EVENT.NOTIFY_SIGNAL)
|
|
305
|
+
this.emit("notify:signal", env);
|
|
306
|
+
if (env.event === EVENT.REPLAY_DONE)
|
|
307
|
+
this.emit("replay:done", env);
|
|
304
308
|
if (env.event === EVENT.OFFLINE_DONE)
|
|
305
309
|
this.emit("offline:done");
|
|
306
310
|
}
|
|
@@ -322,7 +326,19 @@ export class ClawChatClient extends EventEmitter {
|
|
|
322
326
|
token: this.opts.token,
|
|
323
327
|
nonce,
|
|
324
328
|
...(this.opts.deviceId ? { device_id: this.opts.deviceId } : {}),
|
|
325
|
-
|
|
329
|
+
// Agent runtime is single-device: multi_device stays off so the server
|
|
330
|
+
// never self-fans-out this connection's own messages. notify_signals is
|
|
331
|
+
// advertised because we now handle the notify.signal frame (§9.4).
|
|
332
|
+
// history_sync (§11.4) is intentionally omitted: it is only required to
|
|
333
|
+
// *send* history.transit, which a single-device agent never does, and we
|
|
334
|
+
// do not handle inbound history.transit — advertising it would invite
|
|
335
|
+
// frames we cannot process. This is a deliberate, spec-legal omission.
|
|
336
|
+
capabilities: {
|
|
337
|
+
multi_device: false,
|
|
338
|
+
device_replay: true,
|
|
339
|
+
chat_meta_events: true,
|
|
340
|
+
notify_signals: true,
|
|
341
|
+
},
|
|
326
342
|
};
|
|
327
343
|
const traceId = this.nextTraceId();
|
|
328
344
|
this.expectedConnectTraceId = traceId;
|
|
@@ -374,7 +390,34 @@ export class ClawChatClient extends EventEmitter {
|
|
|
374
390
|
this.failHandshake(new ProtocolError("invalid hello-fail payload", env), 4002, "protocol error");
|
|
375
391
|
return;
|
|
376
392
|
}
|
|
377
|
-
|
|
393
|
+
// §14.1: distinguish upstream auth-service unavailability (5xx) from token
|
|
394
|
+
// rejection (4xx). On a 5xx the token may still be valid and the auth backend
|
|
395
|
+
// (member-backend) is down — backoff-reconnect with the SAME token and do
|
|
396
|
+
// NOT refresh (a 5xx storm must not become a mass token-refresh storm). Until
|
|
397
|
+
// the server emits the distinct 5xx reason, every other hello-fail is treated
|
|
398
|
+
// as a terminal token rejection (the caller acquires a fresh token first).
|
|
399
|
+
if (/auth service unavailable/i.test(reason)) {
|
|
400
|
+
const err = new TransportError(reason);
|
|
401
|
+
this.expectedConnectTraceId = undefined;
|
|
402
|
+
this.clearTimers();
|
|
403
|
+
this.rejectPending(err);
|
|
404
|
+
this.connectReject?.(err);
|
|
405
|
+
this.connectResolve = undefined;
|
|
406
|
+
this.connectReject = undefined;
|
|
407
|
+
this.emitError(err);
|
|
408
|
+
if (this.opts.transport.state !== "closed") {
|
|
409
|
+
// Close WITHOUT marking closing/authFailed so handleClose backoff-reconnects.
|
|
410
|
+
this.opts.transport.close(4001, "auth service unavailable");
|
|
411
|
+
}
|
|
412
|
+
else if (!this.closing && this.opts.reconnect.enabled) {
|
|
413
|
+
this.scheduleReconnect(reason);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
this.transition("disconnected");
|
|
417
|
+
}
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const err = new AuthError(reason);
|
|
378
421
|
this.authFailed = true;
|
|
379
422
|
this.expectedConnectTraceId = undefined;
|
|
380
423
|
this.sendQueue.length = 0;
|
|
@@ -421,10 +464,17 @@ export class ClawChatClient extends EventEmitter {
|
|
|
421
464
|
}
|
|
422
465
|
clearTimeout(entry.timer);
|
|
423
466
|
this.pending.delete(env.trace_id);
|
|
424
|
-
const payload = env.payload && typeof env.payload === "object"
|
|
467
|
+
const payload = env.payload && typeof env.payload === "object"
|
|
468
|
+
? env.payload
|
|
469
|
+
: undefined;
|
|
425
470
|
const code = typeof payload?.code === "string" && payload.code ? payload.code : "unknown";
|
|
426
|
-
|
|
427
|
-
|
|
471
|
+
// §14.3: the human-readable hint is `reason` (fall back to legacy `message`).
|
|
472
|
+
const hint = typeof payload?.reason === "string" && payload.reason
|
|
473
|
+
? payload.reason
|
|
474
|
+
: typeof payload?.message === "string" && payload.message
|
|
475
|
+
? payload.message
|
|
476
|
+
: "message send failed";
|
|
477
|
+
entry.reject(new MessageSendError(env.trace_id, code, hint, env.chat_id));
|
|
428
478
|
}
|
|
429
479
|
startHeartbeat() {
|
|
430
480
|
if (!this.opts.heartbeat.enabled)
|
package/index.ts
CHANGED
|
@@ -20,5 +20,12 @@ export default defineChannelPluginEntry({
|
|
|
20
20
|
registerOpenclawClawlingCommands(api);
|
|
21
21
|
registerClawChatPromptInjection(api as unknown as ClawChatPromptInjectionApi);
|
|
22
22
|
registerOpenclawClawlingTools(api);
|
|
23
|
+
// NOTE: the legacy-rename config migration is intentionally NOT registered
|
|
24
|
+
// here. The host's setup-migration runner only loads a plugin's SETUP source
|
|
25
|
+
// (`openclaw.setupEntry`/`runtimeSetupEntry` → setup-entry.ts) and calls its
|
|
26
|
+
// `register(api)` in "setup-only" mode; registrations made in `registerFull`
|
|
27
|
+
// (full/tool-discovery modes) are never collected for migrations, and this
|
|
28
|
+
// gated full-load path doesn't run for the rename scenario anyway. The
|
|
29
|
+
// migration is wired into setup-entry.ts instead.
|
|
23
30
|
},
|
|
24
31
|
});
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "clawchat-plugin-openclaw",
|
|
3
3
|
"kind": "channel",
|
|
4
|
+
"legacyPluginIds": ["openclaw-clawchat"],
|
|
5
|
+
"configContracts": {
|
|
6
|
+
"compatibilityMigrationPaths": [
|
|
7
|
+
"channels.openclaw-clawchat",
|
|
8
|
+
"plugins.entries.openclaw-clawchat"
|
|
9
|
+
]
|
|
10
|
+
},
|
|
4
11
|
"channels": ["clawchat-plugin-openclaw"],
|
|
5
12
|
"skills": ["./skills"],
|
|
6
13
|
"activation": {
|
package/package.json
CHANGED
package/setup-entry.ts
CHANGED
|
@@ -1,4 +1,54 @@
|
|
|
1
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
|
|
1
2
|
import { defineSetupPluginEntry } from "openclaw/plugin-sdk/channel-core";
|
|
3
|
+
import { CHANNEL_ID } from "./src/config.ts";
|
|
2
4
|
import { openclawClawlingSetupPlugin } from "./src/channel.setup.ts";
|
|
5
|
+
import { migrateLegacyClawChatChannelConfig } from "./src/config-compat.ts";
|
|
3
6
|
|
|
4
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Minimal shape of the host setup-registration api we touch. The host calls
|
|
9
|
+
* `register(api)` on the SETUP source (this module) in `registrationMode:
|
|
10
|
+
* "setup-only"`; `registerConfigMigration` is the hook that collects
|
|
11
|
+
* compatibility migrations (run by `openclaw doctor` / setup). It is optional
|
|
12
|
+
* here so a host that doesn't expose it can't throw.
|
|
13
|
+
*/
|
|
14
|
+
interface SetupRegisterApi {
|
|
15
|
+
registerConfigMigration?: (
|
|
16
|
+
migration: (config: OpenClawConfig) => {
|
|
17
|
+
config: OpenClawConfig;
|
|
18
|
+
changes: string[];
|
|
19
|
+
},
|
|
20
|
+
) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Host setup-source `register` hook.
|
|
25
|
+
*
|
|
26
|
+
* The OpenClaw setup-migration runner (`setup-registry`) loads this module as
|
|
27
|
+
* the plugin's setup source (resolved from `package.json` `openclaw.setupEntry`
|
|
28
|
+
* / `runtimeSetupEntry`), unwraps the default export, and — when that export is
|
|
29
|
+
* an object exposing a `register` function whose `id` matches the plugin id —
|
|
30
|
+
* calls `register(api)` in setup-only mode. This is the ONLY ungated path that
|
|
31
|
+
* collects config migrations for the plugin-rename scenario.
|
|
32
|
+
*
|
|
33
|
+
* Mirrors the host's own built-in pattern (e.g. amazon-bedrock setup-api.js:
|
|
34
|
+
* `api.registerConfigMigration((config) => migrateAmazonBedrockLegacyConfig(config))`).
|
|
35
|
+
*/
|
|
36
|
+
export function register(api: SetupRegisterApi): void {
|
|
37
|
+
api.registerConfigMigration?.((config) =>
|
|
38
|
+
migrateLegacyClawChatChannelConfig(config),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Default export consumed by BOTH host loaders of this setup source:
|
|
44
|
+
* - the channel-setup loader unwraps `default` and reads `.plugin`
|
|
45
|
+
* (`defineSetupPluginEntry` yields `{ plugin }`);
|
|
46
|
+
* - the setup-migration runner unwraps `default` and reads `.register`,
|
|
47
|
+
* rejecting it unless `.id` matches the plugin id.
|
|
48
|
+
* So the default export must carry `plugin`, `id`, and `register` together.
|
|
49
|
+
*/
|
|
50
|
+
export default {
|
|
51
|
+
...defineSetupPluginEntry(openclawClawlingSetupPlugin),
|
|
52
|
+
id: CHANNEL_ID,
|
|
53
|
+
register,
|
|
54
|
+
};
|