@openclaw/clickclack 2026.10.1 → 2026.10.5-beta.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/.setup/{channel-DI3hKiSg.mjs → channel-BFuhVeiX.mjs} +130 -141
- package/dist/.setup/{resolve-DPtjHQHt.mjs → resolve-Br3ylpSz.mjs} +13 -6
- package/dist/.setup/{runtime-api-Dm_MQHNq.mjs → runtime-api-hAV5Muz-.mjs} +107 -81
- package/dist/.setup/{setup-claim-Cfv5ikVH.mjs → setup-claim-C-9Pvumz.mjs} +3 -8
- package/dist/.setup/setup-plugin-api-DpKXmzV1.mjs +2 -0
- package/dist/.setup/{setup-plugin-api-CqfAb-CU.mjs → setup-plugin-api-Qi8u_lra.mjs} +1 -1
- package/dist/.setup/{setup-surface-DcF_7-VV.mjs → setup-surface-BJ5D55LV.mjs} +12 -35
- package/dist/.setup/{setup-verify-POQZk8SG.mjs → setup-verify-2ruJKFPj.mjs} +2 -2
- package/dist/api.js +3 -3
- package/dist/channel-plugin-api.js +1 -1
- package/dist/index.js +1 -4
- package/dist/runtime-api.js +4 -4
- package/dist/setup-entry.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/node_modules/ws/lib/websocket.js +17 -3
- package/node_modules/ws/package.json +1 -1
- package/package.json +5 -5
- package/dist/.setup/setup-plugin-api-BXhjNJRg.mjs +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as __reExport, t as __exportAll } from "./rolldown-runtime-B4FMCO8f.mjs";
|
|
2
|
-
import { c as CLICKCLACK_CHANNEL_ID, d as DEFAULT_ACCOUNT_ID, f as isClickClackAccountCurrent, g as resolveClickClackAccountConfig, h as resolveClickClackAccount, l as clickClackConfigAdapter, m as listEnabledClickClackAccounts, n as clickClackSetupContract, s as clickClackConfigSchema, t as clickClackSetupWizard, u as clickClackMeta } from "./setup-surface-
|
|
3
|
-
import { a as createClickClackClient, i as ClickClackHttpError, n as resolveChannelId, r as resolveWorkspaceId, s as normalizeClickClackCorrelationId } from "./resolve-
|
|
2
|
+
import { c as CLICKCLACK_CHANNEL_ID, d as DEFAULT_ACCOUNT_ID, f as isClickClackAccountCurrent, g as resolveClickClackAccountConfig, h as resolveClickClackAccount, l as clickClackConfigAdapter, m as listEnabledClickClackAccounts, n as clickClackSetupContract, s as clickClackConfigSchema, t as clickClackSetupWizard, u as clickClackMeta } from "./setup-surface-BJ5D55LV.mjs";
|
|
3
|
+
import { a as createClickClackClient, i as ClickClackHttpError, n as resolveChannelId, r as resolveWorkspaceId, s as normalizeClickClackCorrelationId } from "./resolve-Br3ylpSz.mjs";
|
|
4
4
|
import { n as collectRuntimeConfigAssignments, r as secretTargetRegistryEntries } from "./secret-contract-CF7TKtkU.mjs";
|
|
5
5
|
import { resolveChannelMediaMaxBytes } from "openclaw/plugin-sdk/account-helpers";
|
|
6
6
|
import { parseDateStringTimestampMs } from "openclaw/plugin-sdk/number-runtime";
|
|
@@ -22,7 +22,7 @@ import { createAsyncLock } from "openclaw/plugin-sdk/async-lock-runtime";
|
|
|
22
22
|
import { truncateCodePoints } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
23
23
|
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
|
|
24
24
|
import { listNativeCommandSpecsForConfig } from "openclaw/plugin-sdk/native-command-registry";
|
|
25
|
-
import { buildChannelInboundEventContext,
|
|
25
|
+
import { buildChannelInboundEventContext, createChannelInboundEnvelopeBuilderAsync, createChannelPartialDeliveryError, recordChannelBotPairLoopAndCheckSuppression } from "openclaw/plugin-sdk/channel-inbound";
|
|
26
26
|
import { extensionForMime } from "openclaw/plugin-sdk/media-mime";
|
|
27
27
|
import { loadOutboundMediaFromUrl } from "openclaw/plugin-sdk/outbound-media";
|
|
28
28
|
import { sanitizeAssistantVisibleText } from "openclaw/plugin-sdk/text-chunking";
|
|
@@ -174,61 +174,6 @@ async function hasPendingDiscussionOpenForDestination(params) {
|
|
|
174
174
|
return (await listPendingDiscussionOpens(params.runtime)).some((pending) => pending.serverBaseUrl === serverBaseUrl && pending.workspaceId === params.workspaceId);
|
|
175
175
|
}
|
|
176
176
|
//#endregion
|
|
177
|
-
//#region extensions/clickclack/src/discussions/naming.ts
|
|
178
|
-
function shortSessionHash(sessionKey) {
|
|
179
|
-
return createHash("sha256").update(sessionKey).digest("hex").slice(0, 32);
|
|
180
|
-
}
|
|
181
|
-
function slugifyLabel(label) {
|
|
182
|
-
return label.normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 80).replace(/-+$/gu, "");
|
|
183
|
-
}
|
|
184
|
-
function fallbackDiscussionLabel(sessionKey, agentId) {
|
|
185
|
-
const agentSegment = agentId?.trim() ? slugifyLabel(agentId) : "";
|
|
186
|
-
const hash = shortSessionHash(sessionKey).slice(0, 8);
|
|
187
|
-
return `s-${agentSegment ? `${agentSegment}-` : ""}${hash}`;
|
|
188
|
-
}
|
|
189
|
-
function resolveDiscussionLabel(entry, sessionKey, agentId) {
|
|
190
|
-
return entry.label?.trim() || entry.displayName?.trim() || entry.subject?.trim() || fallbackDiscussionLabel(sessionKey, agentId);
|
|
191
|
-
}
|
|
192
|
-
function truncateDiscussionDisplayTitle(label) {
|
|
193
|
-
return truncateCodePoints(label, 200).trim();
|
|
194
|
-
}
|
|
195
|
-
function slugifyDiscussionLabel(label, sessionKey) {
|
|
196
|
-
return slugifyLabel(label) || fallbackDiscussionLabel(sessionKey);
|
|
197
|
-
}
|
|
198
|
-
function discussionSessionPeerId(identity) {
|
|
199
|
-
return `disc-${createHash("sha256").update([
|
|
200
|
-
identity.mainSessionKey,
|
|
201
|
-
identity.sessionId,
|
|
202
|
-
identity.serverBaseUrl,
|
|
203
|
-
identity.channelId,
|
|
204
|
-
identity.externalRef
|
|
205
|
-
].join("\0")).digest("hex").slice(0, 32)}`;
|
|
206
|
-
}
|
|
207
|
-
function isDiscussionSessionKey(sessionKey) {
|
|
208
|
-
return sessionKey.includes(":clickclack:") && /:channel:disc-[0-9a-f]{32}$/u.test(sessionKey);
|
|
209
|
-
}
|
|
210
|
-
function discussionExternalRef(installationId, mainSessionKey, destinationIdentity, bindingGeneration) {
|
|
211
|
-
return `openclaw:${installationId}:${shortSessionHash([
|
|
212
|
-
mainSessionKey,
|
|
213
|
-
destinationIdentity,
|
|
214
|
-
bindingGeneration
|
|
215
|
-
].join("\0"))}`;
|
|
216
|
-
}
|
|
217
|
-
function discussionCredentialFingerprint(token) {
|
|
218
|
-
return createHash("sha256").update(token).digest("hex");
|
|
219
|
-
}
|
|
220
|
-
function discussionSessionKey(params) {
|
|
221
|
-
return params.runtime.channel.routing.buildAgentSessionKey({
|
|
222
|
-
agentId: params.agentId,
|
|
223
|
-
channel: "clickclack",
|
|
224
|
-
accountId: params.accountId,
|
|
225
|
-
peer: {
|
|
226
|
-
kind: "channel",
|
|
227
|
-
id: discussionSessionPeerId(params)
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
//#endregion
|
|
232
177
|
//#region extensions/clickclack/src/discussions/binding-store.ts
|
|
233
178
|
function bindingMatchesActiveSessionIncarnation(runtime, sessionKey, binding) {
|
|
234
179
|
const entry = runtime.agent.session.getSessionEntry({
|
|
@@ -237,6 +182,13 @@ function bindingMatchesActiveSessionIncarnation(runtime, sessionKey, binding) {
|
|
|
237
182
|
});
|
|
238
183
|
return Boolean(entry && binding.sessionId && entry.sessionId === binding.sessionId && entry.archivedAt === void 0);
|
|
239
184
|
}
|
|
185
|
+
async function readDiscussionSessionEntry(runtime, sessionKey) {
|
|
186
|
+
const params = {
|
|
187
|
+
sessionKey,
|
|
188
|
+
readConsistency: "latest"
|
|
189
|
+
};
|
|
190
|
+
return runtime.agent.session.getSessionEntryAsync ? await runtime.agent.session.getSessionEntryAsync(params) : runtime.agent.session.getSessionEntry(params);
|
|
191
|
+
}
|
|
240
192
|
/**
|
|
241
193
|
* Refresh the replaceable session attachment without changing the durable room identity.
|
|
242
194
|
* The store registers persisted state before reindexing, so a failed write leaves the
|
|
@@ -259,6 +211,11 @@ function attachBindingToCurrentActiveSession(params) {
|
|
|
259
211
|
}
|
|
260
212
|
const DISCUSSION_BINDINGS_NAMESPACE = "discussion-bindings";
|
|
261
213
|
const MAX_DISCUSSION_BINDINGS = 1e4;
|
|
214
|
+
const BINDING_STORE_OPTIONS = {
|
|
215
|
+
namespace: DISCUSSION_BINDINGS_NAMESPACE,
|
|
216
|
+
maxEntries: MAX_DISCUSSION_BINDINGS,
|
|
217
|
+
overflowPolicy: "reject-new"
|
|
218
|
+
};
|
|
262
219
|
const MAX_RETAINED_DETACHED_DISCUSSION_BINDINGS = 1e3;
|
|
263
220
|
const storesByRuntime$1 = /* @__PURE__ */ new WeakMap();
|
|
264
221
|
function channelKey(serverBaseUrl, channelId) {
|
|
@@ -267,31 +224,36 @@ function channelKey(serverBaseUrl, channelId) {
|
|
|
267
224
|
/** SQLite-backed session/channel bindings with a process-local inbound lookup index. */
|
|
268
225
|
var ClickClackDiscussionBindingStore = class {
|
|
269
226
|
#store;
|
|
227
|
+
#asyncStore;
|
|
270
228
|
#sessionByChannel = /* @__PURE__ */ new Map();
|
|
271
|
-
#mainByDiscussionSession = /* @__PURE__ */ new Map();
|
|
272
229
|
#detachedAtBySession = /* @__PURE__ */ new Map();
|
|
230
|
+
#channelBySession = /* @__PURE__ */ new Map();
|
|
273
231
|
#runtime;
|
|
232
|
+
#prepared = false;
|
|
233
|
+
#preparing;
|
|
234
|
+
#changedDuringPreparation = /* @__PURE__ */ new Set();
|
|
274
235
|
constructor(runtime) {
|
|
275
236
|
this.#runtime = runtime;
|
|
276
|
-
this.#store = runtime.state.openSyncKeyedStore(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
237
|
+
this.#store = runtime.state.openSyncKeyedStore(BINDING_STORE_OPTIONS);
|
|
238
|
+
}
|
|
239
|
+
async prepare() {
|
|
240
|
+
if (!this.#prepared) await this.entries();
|
|
241
|
+
}
|
|
242
|
+
async getAsync(sessionKey) {
|
|
243
|
+
return await this.#workerStore().lookup(sessionKey);
|
|
282
244
|
}
|
|
283
245
|
get(sessionKey) {
|
|
284
246
|
return this.#store.lookup(sessionKey);
|
|
285
247
|
}
|
|
286
|
-
hasCapacity(sessionKey) {
|
|
287
|
-
return this.
|
|
248
|
+
async hasCapacity(sessionKey) {
|
|
249
|
+
return await this.getAsync(sessionKey) !== void 0 || await this.countAsync() < MAX_DISCUSSION_BINDINGS;
|
|
288
250
|
}
|
|
289
251
|
getByChannel(serverBaseUrl, channelId) {
|
|
290
252
|
const key = channelKey(serverBaseUrl, channelId);
|
|
291
253
|
const sessionKey = this.#sessionByChannel.get(key);
|
|
292
254
|
if (!sessionKey) return;
|
|
293
255
|
const binding = this.get(sessionKey);
|
|
294
|
-
if (!binding) {
|
|
256
|
+
if (!binding || channelKey(binding.serverBaseUrl, binding.channelId) !== key) {
|
|
295
257
|
this.#sessionByChannel.delete(key);
|
|
296
258
|
return;
|
|
297
259
|
}
|
|
@@ -301,38 +263,43 @@ var ClickClackDiscussionBindingStore = class {
|
|
|
301
263
|
};
|
|
302
264
|
}
|
|
303
265
|
set(sessionKey, binding) {
|
|
304
|
-
const previous = this.get(sessionKey);
|
|
305
266
|
this.#store.register(sessionKey, binding);
|
|
306
|
-
if (
|
|
267
|
+
if (!this.#prepared) this.#changedDuringPreparation.add(sessionKey);
|
|
268
|
+
this.#unindex(sessionKey);
|
|
307
269
|
this.#index(sessionKey, binding);
|
|
308
270
|
}
|
|
309
271
|
delete(sessionKey) {
|
|
310
|
-
const previous = this.get(sessionKey);
|
|
311
272
|
const deleted = this.#store.delete(sessionKey);
|
|
312
|
-
if (deleted &&
|
|
273
|
+
if (deleted && !this.#prepared) this.#changedDuringPreparation.add(sessionKey);
|
|
274
|
+
if (deleted) this.#unindex(sessionKey);
|
|
313
275
|
return deleted;
|
|
314
276
|
}
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
if (!sessionKey) return;
|
|
318
|
-
const binding = this.get(sessionKey);
|
|
319
|
-
return binding ? {
|
|
320
|
-
sessionKey,
|
|
321
|
-
binding
|
|
322
|
-
} : void 0;
|
|
323
|
-
}
|
|
324
|
-
entries() {
|
|
325
|
-
return this.#store.entries().map((entry) => ({
|
|
277
|
+
async entries() {
|
|
278
|
+
const load = async () => (await this.#workerStore().entries()).map((entry) => ({
|
|
326
279
|
sessionKey: entry.key,
|
|
327
280
|
binding: entry.value
|
|
328
281
|
}));
|
|
282
|
+
if (this.#prepared) return await load();
|
|
283
|
+
this.#preparing ??= load().then((entries) => {
|
|
284
|
+
for (const { sessionKey, binding } of entries) if (!this.#changedDuringPreparation.has(sessionKey)) this.#index(sessionKey, binding);
|
|
285
|
+
this.#prepared = true;
|
|
286
|
+
this.#changedDuringPreparation.clear();
|
|
287
|
+
return entries;
|
|
288
|
+
}).finally(() => {
|
|
289
|
+
this.#preparing = void 0;
|
|
290
|
+
});
|
|
291
|
+
return await this.#preparing;
|
|
329
292
|
}
|
|
330
|
-
|
|
331
|
-
|
|
293
|
+
async countAsync() {
|
|
294
|
+
const store = this.#workerStore();
|
|
295
|
+
return store.count ? await store.count() : (await this.entries()).length;
|
|
332
296
|
}
|
|
333
297
|
detachedCount() {
|
|
334
298
|
return this.#detachedAtBySession.size;
|
|
335
299
|
}
|
|
300
|
+
#workerStore() {
|
|
301
|
+
return this.#asyncStore ??= this.#runtime.state.openKeyedStore(BINDING_STORE_OPTIONS);
|
|
302
|
+
}
|
|
336
303
|
oldestDetached() {
|
|
337
304
|
let oldestSessionKey;
|
|
338
305
|
let oldestDetachedAt = Number.POSITIVE_INFINITY;
|
|
@@ -352,33 +319,15 @@ var ClickClackDiscussionBindingStore = class {
|
|
|
352
319
|
};
|
|
353
320
|
}
|
|
354
321
|
#index(sessionKey, binding) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
agentId: binding.agentId,
|
|
359
|
-
mainSessionKey: sessionKey,
|
|
360
|
-
sessionId: binding.sessionId,
|
|
361
|
-
accountId: binding.accountId,
|
|
362
|
-
serverBaseUrl: binding.serverBaseUrl,
|
|
363
|
-
channelId: binding.channelId,
|
|
364
|
-
externalRef: binding.externalRef
|
|
365
|
-
});
|
|
366
|
-
if (sideSessionKey) this.#mainByDiscussionSession.set(sideSessionKey, sessionKey);
|
|
322
|
+
const channel = channelKey(binding.serverBaseUrl, binding.channelId);
|
|
323
|
+
this.#channelBySession.set(sessionKey, channel);
|
|
324
|
+
this.#sessionByChannel.set(channel, sessionKey);
|
|
367
325
|
if (binding.detachedAt !== void 0) this.#detachedAtBySession.set(sessionKey, binding.detachedAt);
|
|
368
326
|
}
|
|
369
|
-
#unindex(sessionKey
|
|
370
|
-
this.#
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
agentId: binding.agentId,
|
|
374
|
-
mainSessionKey: sessionKey,
|
|
375
|
-
sessionId: binding.sessionId,
|
|
376
|
-
accountId: binding.accountId,
|
|
377
|
-
serverBaseUrl: binding.serverBaseUrl,
|
|
378
|
-
channelId: binding.channelId,
|
|
379
|
-
externalRef: binding.externalRef
|
|
380
|
-
});
|
|
381
|
-
if (sideSessionKey) this.#mainByDiscussionSession.delete(sideSessionKey);
|
|
327
|
+
#unindex(sessionKey) {
|
|
328
|
+
const channel = this.#channelBySession.get(sessionKey);
|
|
329
|
+
this.#channelBySession.delete(sessionKey);
|
|
330
|
+
if (channel) this.#sessionByChannel.delete(channel);
|
|
382
331
|
this.#detachedAtBySession.delete(sessionKey);
|
|
383
332
|
}
|
|
384
333
|
};
|
|
@@ -390,6 +339,61 @@ function getClickClackDiscussionBindingStore(runtime) {
|
|
|
390
339
|
return created;
|
|
391
340
|
}
|
|
392
341
|
//#endregion
|
|
342
|
+
//#region extensions/clickclack/src/discussions/naming.ts
|
|
343
|
+
function shortSessionHash(sessionKey) {
|
|
344
|
+
return createHash("sha256").update(sessionKey).digest("hex").slice(0, 32);
|
|
345
|
+
}
|
|
346
|
+
function slugifyLabel(label) {
|
|
347
|
+
return label.normalize("NFKD").replace(/[\u0300-\u036f]/gu, "").toLowerCase().replace(/[^a-z0-9]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 80).replace(/-+$/gu, "");
|
|
348
|
+
}
|
|
349
|
+
function fallbackDiscussionLabel(sessionKey, agentId) {
|
|
350
|
+
const agentSegment = agentId?.trim() ? slugifyLabel(agentId) : "";
|
|
351
|
+
const hash = shortSessionHash(sessionKey).slice(0, 8);
|
|
352
|
+
return `s-${agentSegment ? `${agentSegment}-` : ""}${hash}`;
|
|
353
|
+
}
|
|
354
|
+
function resolveDiscussionLabel(entry, sessionKey, agentId) {
|
|
355
|
+
return entry.label?.trim() || entry.displayName?.trim() || entry.subject?.trim() || fallbackDiscussionLabel(sessionKey, agentId);
|
|
356
|
+
}
|
|
357
|
+
function truncateDiscussionDisplayTitle(label) {
|
|
358
|
+
return truncateCodePoints(label, 200).trim();
|
|
359
|
+
}
|
|
360
|
+
function slugifyDiscussionLabel(label, sessionKey) {
|
|
361
|
+
return slugifyLabel(label) || fallbackDiscussionLabel(sessionKey);
|
|
362
|
+
}
|
|
363
|
+
function discussionSessionPeerId(identity) {
|
|
364
|
+
return `disc-${createHash("sha256").update([
|
|
365
|
+
identity.mainSessionKey,
|
|
366
|
+
identity.sessionId,
|
|
367
|
+
identity.serverBaseUrl,
|
|
368
|
+
identity.channelId,
|
|
369
|
+
identity.externalRef
|
|
370
|
+
].join("\0")).digest("hex").slice(0, 32)}`;
|
|
371
|
+
}
|
|
372
|
+
function isDiscussionSessionKey(sessionKey) {
|
|
373
|
+
return sessionKey.includes(":clickclack:") && /:channel:disc-[0-9a-f]{32}$/u.test(sessionKey);
|
|
374
|
+
}
|
|
375
|
+
function discussionExternalRef(installationId, mainSessionKey, destinationIdentity, bindingGeneration) {
|
|
376
|
+
return `openclaw:${installationId}:${shortSessionHash([
|
|
377
|
+
mainSessionKey,
|
|
378
|
+
destinationIdentity,
|
|
379
|
+
bindingGeneration
|
|
380
|
+
].join("\0"))}`;
|
|
381
|
+
}
|
|
382
|
+
function discussionCredentialFingerprint(token) {
|
|
383
|
+
return createHash("sha256").update(token).digest("hex");
|
|
384
|
+
}
|
|
385
|
+
function discussionSessionKey(params) {
|
|
386
|
+
return params.runtime.channel.routing.buildAgentSessionKey({
|
|
387
|
+
agentId: params.agentId,
|
|
388
|
+
channel: "clickclack",
|
|
389
|
+
accountId: params.accountId,
|
|
390
|
+
peer: {
|
|
391
|
+
kind: "channel",
|
|
392
|
+
id: discussionSessionPeerId(params)
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
//#endregion
|
|
393
397
|
//#region extensions/clickclack/src/discussions/eligibility.ts
|
|
394
398
|
function discussionAccounts(cfg) {
|
|
395
399
|
return listEnabledClickClackAccounts(cfg).filter((account) => account.configured && account.discussions.enabled);
|
|
@@ -470,6 +474,7 @@ function isClickClackDiscussionChannelRevoked(params) {
|
|
|
470
474
|
//#region extensions/clickclack/src/discussions/routing.ts
|
|
471
475
|
async function resolveClickClackDiscussionRoute(params) {
|
|
472
476
|
const store = getClickClackDiscussionBindingStore(params.runtime);
|
|
477
|
+
await store.prepare();
|
|
473
478
|
let matched = store.getByChannel(params.serverBaseUrl, params.channelId);
|
|
474
479
|
let pending = false;
|
|
475
480
|
if (!matched) {
|
|
@@ -636,17 +641,14 @@ function parseClickClackTarget(raw) {
|
|
|
636
641
|
};
|
|
637
642
|
throw new Error(`Unsupported ClickClack target: ${raw}`);
|
|
638
643
|
}
|
|
639
|
-
/** Formats a parsed ClickClack target back into canonical target syntax. */
|
|
640
644
|
function buildClickClackTarget(target) {
|
|
641
645
|
return `${target.kind}:${target.id}`;
|
|
642
646
|
}
|
|
643
|
-
/** Normalizes user-entered ClickClack target text for channel routing. */
|
|
644
647
|
function normalizeClickClackTarget(raw) {
|
|
645
648
|
return buildClickClackTarget(parseClickClackTarget(raw));
|
|
646
649
|
}
|
|
647
|
-
/** Reports whether a target string can be offered to the ClickClack parser. */
|
|
648
650
|
function looksLikeClickClackTarget(raw) {
|
|
649
|
-
return
|
|
651
|
+
return raw.trim().length > 0;
|
|
650
652
|
}
|
|
651
653
|
//#endregion
|
|
652
654
|
//#region extensions/clickclack/src/access.ts
|
|
@@ -1017,7 +1019,6 @@ const STREAMING_COMMENTARY_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
|
1017
1019
|
"thinking",
|
|
1018
1020
|
"reasoning"
|
|
1019
1021
|
]);
|
|
1020
|
-
/** Provider-specific reasoning lanes normalized into one ClickClack label. */
|
|
1021
1022
|
const THINKING_ITEM_KINDS = /* @__PURE__ */ new Set([
|
|
1022
1023
|
"analysis",
|
|
1023
1024
|
"thinking",
|
|
@@ -1043,9 +1044,7 @@ function activityBody(payload) {
|
|
|
1043
1044
|
const head = payload.name?.trim() || payload.title?.trim();
|
|
1044
1045
|
const text = payload.progressText?.trim() || payload.summary?.trim();
|
|
1045
1046
|
if (head && text) return `**${head}**\n\n${text}`;
|
|
1046
|
-
|
|
1047
|
-
if (head) return head;
|
|
1048
|
-
return payload.status?.trim() || payload.kind?.trim() || "";
|
|
1047
|
+
return text || head || payload.status?.trim() || payload.kind?.trim() || "";
|
|
1049
1048
|
}
|
|
1050
1049
|
/**
|
|
1051
1050
|
* Creates a per-turn activity publisher. Publishing is best-effort: transport
|
|
@@ -1078,7 +1077,7 @@ function createClickClackActivityPublisher(params) {
|
|
|
1078
1077
|
clearTimeout(segment.timer);
|
|
1079
1078
|
segment.timer = void 0;
|
|
1080
1079
|
}
|
|
1081
|
-
if (!segment.dirty || !segment.body
|
|
1080
|
+
if (!segment.dirty || !segment.body) return Promise.resolve();
|
|
1082
1081
|
segment.dirty = false;
|
|
1083
1082
|
const body = segment.body;
|
|
1084
1083
|
return enqueue(async () => {
|
|
@@ -1098,7 +1097,7 @@ function createClickClackActivityPublisher(params) {
|
|
|
1098
1097
|
const body = commentaryBody(payload);
|
|
1099
1098
|
const key = payload.itemId?.trim() || "turn";
|
|
1100
1099
|
let segment = commentaryByItem.get(key);
|
|
1101
|
-
if (!body
|
|
1100
|
+
if (!body) {
|
|
1102
1101
|
if (segment) {
|
|
1103
1102
|
clearTimeout(segment.timer);
|
|
1104
1103
|
commentaryByItem.delete(key);
|
|
@@ -1177,9 +1176,11 @@ function createClickClackActivityPublisher(params) {
|
|
|
1177
1176
|
handleDiscreteItem(payload);
|
|
1178
1177
|
return false;
|
|
1179
1178
|
},
|
|
1179
|
+
/** Model/thinking from onModelSelected is stamped onto subsequent activity rows. */
|
|
1180
1180
|
setProvenance: (next) => {
|
|
1181
1181
|
provenance = next;
|
|
1182
1182
|
},
|
|
1183
|
+
/** Flushes pending commentary and awaits all outstanding POST/PATCH work. */
|
|
1183
1184
|
finalize: async () => {
|
|
1184
1185
|
await flushAllCommentary();
|
|
1185
1186
|
await chain;
|
|
@@ -1258,25 +1259,16 @@ function createOutboundContext(params) {
|
|
|
1258
1259
|
cfg: params.cfg,
|
|
1259
1260
|
accountId: params.accountId
|
|
1260
1261
|
});
|
|
1261
|
-
const assertDirectAdapterHandoff = params.assertDirectAdapterHandoff;
|
|
1262
|
-
const fetcher = fetch;
|
|
1263
1262
|
return {
|
|
1264
1263
|
account,
|
|
1265
1264
|
client: createClickClackClient({
|
|
1266
1265
|
baseUrl: account.apiEndpoint,
|
|
1267
1266
|
token: account.token,
|
|
1268
1267
|
correlationId: params.correlationId,
|
|
1269
|
-
|
|
1270
|
-
assertDirectAdapterHandoff();
|
|
1271
|
-
return fetcher(input, init);
|
|
1272
|
-
} : void 0
|
|
1268
|
+
beforeRequest: params.assertDirectAdapterHandoff
|
|
1273
1269
|
})
|
|
1274
1270
|
};
|
|
1275
1271
|
}
|
|
1276
|
-
/**
|
|
1277
|
-
* Sends visible text to a normalized ClickClack target and returns the created
|
|
1278
|
-
* message id, or undefined when sanitization removes all content.
|
|
1279
|
-
*/
|
|
1280
1272
|
async function sendClickClackText(params) {
|
|
1281
1273
|
const text = sanitizeAssistantVisibleText(params.text);
|
|
1282
1274
|
if (!text) return;
|
|
@@ -1793,10 +1785,10 @@ async function handleClickClackInbound(params) {
|
|
|
1793
1785
|
}
|
|
1794
1786
|
});
|
|
1795
1787
|
const senderName = message.author?.display_name || message.author_id;
|
|
1796
|
-
const body =
|
|
1788
|
+
const body = (await createChannelInboundEnvelopeBuilderAsync({
|
|
1797
1789
|
cfg: params.config,
|
|
1798
1790
|
route
|
|
1799
|
-
})({
|
|
1791
|
+
}))({
|
|
1800
1792
|
channel: "ClickClack",
|
|
1801
1793
|
from: senderName,
|
|
1802
1794
|
timestamp: new Date(message.created_at),
|
|
@@ -1939,9 +1931,6 @@ const CLICKCLACK_EVENT_PAGE_LIMIT = 500;
|
|
|
1939
1931
|
function payloadString(event, key) {
|
|
1940
1932
|
return readStringField(event.payload, key) ?? "";
|
|
1941
1933
|
}
|
|
1942
|
-
function eventCorrelationId(event) {
|
|
1943
|
-
return normalizeClickClackCorrelationId(event.payload?.correlation_id);
|
|
1944
|
-
}
|
|
1945
1934
|
async function resolveEventMessage(params) {
|
|
1946
1935
|
const messageId = payloadString(params.event, "message_id");
|
|
1947
1936
|
if (!messageId) return null;
|
|
@@ -1962,7 +1951,7 @@ function parseSocketEvent(data) {
|
|
|
1962
1951
|
async function processEvent(params) {
|
|
1963
1952
|
if (params.event.type !== "message.created" && params.event.type !== "thread.reply_created") return;
|
|
1964
1953
|
if (params.abortSignal.aborted || payloadString(params.event, "author_id") === params.botUserId) return;
|
|
1965
|
-
const correlationId =
|
|
1954
|
+
const correlationId = normalizeClickClackCorrelationId(params.event.payload?.correlation_id);
|
|
1966
1955
|
const message = await resolveEventMessage({
|
|
1967
1956
|
client: correlationId ? createClickClackClient({
|
|
1968
1957
|
baseUrl: params.account.apiEndpoint,
|
|
@@ -1998,17 +1987,17 @@ async function processEvent(params) {
|
|
|
1998
1987
|
async function drainEventBacklog(params) {
|
|
1999
1988
|
let afterCursor = params.afterCursor;
|
|
2000
1989
|
while (!params.abortSignal.aborted) {
|
|
2001
|
-
const
|
|
1990
|
+
const page = await params.client.eventPage(params.workspaceId, {
|
|
2002
1991
|
afterCursor,
|
|
2003
1992
|
limit: CLICKCLACK_EVENT_PAGE_LIMIT
|
|
2004
|
-
})
|
|
2005
|
-
for (const event of events) {
|
|
1993
|
+
});
|
|
1994
|
+
for (const event of page.events) {
|
|
2006
1995
|
if (params.abortSignal.aborted) return afterCursor;
|
|
2007
1996
|
if (!event.cursor || event.cursor === afterCursor) throw new Error("ClickClack event backlog returned a non-advancing cursor");
|
|
2008
1997
|
await params.onEvent(event);
|
|
2009
1998
|
afterCursor = event.cursor;
|
|
2010
1999
|
}
|
|
2011
|
-
if (events.length === 0) return afterCursor;
|
|
2000
|
+
if (page.events.length === 0) return afterCursor;
|
|
2012
2001
|
}
|
|
2013
2002
|
return afterCursor;
|
|
2014
2003
|
}
|
|
@@ -2334,4 +2323,4 @@ const clickClackPlugin = createChatChannelPlugin({
|
|
|
2334
2323
|
}
|
|
2335
2324
|
});
|
|
2336
2325
|
//#endregion
|
|
2337
|
-
export {
|
|
2326
|
+
export { recordPendingDiscussionOpen as A, attachBindingToCurrentActiveSession as C, clearDiscussionBindingGeneration as D, readDiscussionSessionEntry as E, clearPendingDiscussionOpen as O, MAX_RETAINED_DETACHED_DISCUSSION_BINDINGS as S, getClickClackDiscussionBindingStore as T, fallbackDiscussionLabel as _, setClickClackRuntime as a, slugifyDiscussionLabel as b, markClickClackDiscussionChannelIdentityRevoked as c, discussionInfoForBinding as d, normalizedServerBaseUrl as f, discussionSessionKey as g, discussionExternalRef as h, getClickClackRuntime as i, reserveDiscussionBindingGeneration as j, listPendingDiscussionOpens as k, markClickClackDiscussionChannelRevoked as l, discussionCredentialFingerprint as m, buildClickClackTarget as n, clearClickClackDiscussionChannelRevoked as o, resolveDiscussionBindingAccount as p, parseClickClackTarget as r, isClickClackDiscussionChannelRevoked as s, clickClackPlugin as t, discussionAccounts as u, isDiscussionSessionKey as v, bindingMatchesActiveSessionIncarnation as w, truncateDiscussionDisplayTitle as x, resolveDiscussionLabel as y };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { bufferToBlobPart } from "openclaw/plugin-sdk/blob-runtime";
|
|
3
|
+
import { captureEffectAuthority } from "openclaw/plugin-sdk/fetch-runtime";
|
|
3
4
|
import { redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
4
5
|
import { readProviderJsonResponse, readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
|
|
5
6
|
import path from "node:path";
|
|
@@ -72,10 +73,13 @@ function createClickClackClient(options) {
|
|
|
72
73
|
const controller = requestOptions.timeoutMs !== void 0 && !init.signal ? new AbortController() : void 0;
|
|
73
74
|
const timeout = controller ? setTimeout(() => controller.abort(), requestOptions.timeoutMs) : void 0;
|
|
74
75
|
try {
|
|
75
|
-
const response = await
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
const response = await captureEffectAuthority().initiate(() => {
|
|
77
|
+
options.beforeRequest?.();
|
|
78
|
+
return fetcher(`${baseUrl}${path}`, {
|
|
79
|
+
...init,
|
|
80
|
+
...controller ? { signal: controller.signal } : {},
|
|
81
|
+
headers: requestHeaders
|
|
82
|
+
});
|
|
79
83
|
});
|
|
80
84
|
if (!response.ok) {
|
|
81
85
|
const detail = await readResponseTextLimited(response, CLICKCLACK_ERROR_BODY_LIMIT_BYTES);
|
|
@@ -132,6 +136,9 @@ function createClickClackClient(options) {
|
|
|
132
136
|
throw error;
|
|
133
137
|
}
|
|
134
138
|
}
|
|
139
|
+
function messageList(resource) {
|
|
140
|
+
return async (id, afterSeq, limit = 20) => requestObject("messages", `/api/${resource}/${encodeURIComponent(id)}/messages?after_seq=${afterSeq}&limit=${limit}`);
|
|
141
|
+
}
|
|
135
142
|
return {
|
|
136
143
|
me: async () => requestObject("user", "/api/me"),
|
|
137
144
|
setBotCommands: async (commands) => requestObject("bot_commands", "/api/bots/self/commands", {
|
|
@@ -148,7 +155,7 @@ function createClickClackClient(options) {
|
|
|
148
155
|
method: "PATCH",
|
|
149
156
|
body: JSON.stringify(patch)
|
|
150
157
|
}),
|
|
151
|
-
channelMessages:
|
|
158
|
+
channelMessages: messageList("channels"),
|
|
152
159
|
latestChannelMessages: async (channelId, limit = 30) => {
|
|
153
160
|
const boundedLimit = Math.max(1, Math.min(CLICKCLACK_MESSAGE_PAGE_LIMIT, limit));
|
|
154
161
|
let beforeSeq;
|
|
@@ -191,7 +198,7 @@ function createClickClackClient(options) {
|
|
|
191
198
|
beforeSeq = page.oldest_seq;
|
|
192
199
|
}
|
|
193
200
|
},
|
|
194
|
-
directMessages:
|
|
201
|
+
directMessages: messageList("dms"),
|
|
195
202
|
thread: async (messageId) => await request(`/api/messages/${encodeURIComponent(messageId)}/thread`),
|
|
196
203
|
message: async (messageId) => requestObject("message", `/api/messages/${encodeURIComponent(messageId)}`),
|
|
197
204
|
findMessageByNonce: async (params) => findByNonce("message", params),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { h as resolveClickClackAccount, p as listClickClackAccountIds } from "./setup-surface-
|
|
2
|
-
import {
|
|
3
|
-
import { a as createClickClackClient, i as ClickClackHttpError, o as isClickClackChannelNameConflict, t as findClickClackWorkspace } from "./resolve-
|
|
1
|
+
import { h as resolveClickClackAccount, p as listClickClackAccountIds } from "./setup-surface-BJ5D55LV.mjs";
|
|
2
|
+
import { A as recordPendingDiscussionOpen, C as attachBindingToCurrentActiveSession, D as clearDiscussionBindingGeneration, E as readDiscussionSessionEntry, O as clearPendingDiscussionOpen, T as getClickClackDiscussionBindingStore, _ as fallbackDiscussionLabel, b as slugifyDiscussionLabel, c as markClickClackDiscussionChannelIdentityRevoked, d as discussionInfoForBinding, f as normalizedServerBaseUrl, g as discussionSessionKey, h as discussionExternalRef, j as reserveDiscussionBindingGeneration, k as listPendingDiscussionOpens, l as markClickClackDiscussionChannelRevoked, m as discussionCredentialFingerprint, o as clearClickClackDiscussionChannelRevoked, p as resolveDiscussionBindingAccount, s as isClickClackDiscussionChannelRevoked, u as discussionAccounts, v as isDiscussionSessionKey, w as bindingMatchesActiveSessionIncarnation, x as truncateDiscussionDisplayTitle, y as resolveDiscussionLabel } from "./channel-BFuhVeiX.mjs";
|
|
3
|
+
import { a as createClickClackClient, i as ClickClackHttpError, o as isClickClackChannelNameConflict, t as findClickClackWorkspace } from "./resolve-Br3ylpSz.mjs";
|
|
4
4
|
import "../api.js";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import { createAsyncLock } from "openclaw/plugin-sdk/async-lock-runtime";
|
|
@@ -36,15 +36,15 @@ var DetachedDiscussionBindingRetention = class {
|
|
|
36
36
|
this.#store.set(sessionKey, retained);
|
|
37
37
|
return retained;
|
|
38
38
|
}
|
|
39
|
-
ensureCapacity(sessionKey) {
|
|
40
|
-
|
|
39
|
+
async ensureCapacity(sessionKey) {
|
|
40
|
+
await this.#store.prepare();
|
|
41
|
+
while (!await this.#store.hasCapacity(sessionKey)) if (!this.#pruneOldest()) throw new Error("ClickClack discussion binding capacity is exhausted");
|
|
41
42
|
}
|
|
42
43
|
#pruneOldest() {
|
|
43
44
|
for (;;) {
|
|
44
45
|
const oldest = this.#store.oldestDetached();
|
|
45
46
|
if (!oldest) return false;
|
|
46
|
-
const current =
|
|
47
|
-
if (!current || current.detachedAt === void 0) continue;
|
|
47
|
+
const current = oldest.binding;
|
|
48
48
|
if (this.#runtime.agent.session.getSessionEntry({
|
|
49
49
|
sessionKey: oldest.sessionKey,
|
|
50
50
|
readConsistency: "latest"
|
|
@@ -242,10 +242,7 @@ function assertManagedChannelListContract(channels) {
|
|
|
242
242
|
}
|
|
243
243
|
async function openClickClackDiscussionBinding(params) {
|
|
244
244
|
const { account, runtime, sessionKey, store } = params;
|
|
245
|
-
const entry = runtime
|
|
246
|
-
sessionKey,
|
|
247
|
-
readConsistency: "latest"
|
|
248
|
-
});
|
|
245
|
+
const entry = await readDiscussionSessionEntry(runtime, sessionKey);
|
|
249
246
|
if (!entry || entry.archivedAt !== void 0) return;
|
|
250
247
|
if (!entry.sessionId?.trim()) throw new Error("OpenClaw session does not yet have a concrete session id");
|
|
251
248
|
const client = params.clientFactory(account);
|
|
@@ -255,6 +252,12 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
255
252
|
if (!workspace.route_id) throw new Error("ClickClack discussions workspace is missing its route id");
|
|
256
253
|
const serverBaseUrl = normalizedServerBaseUrl(account);
|
|
257
254
|
const credentialFingerprint = discussionCredentialFingerprint(account.token);
|
|
255
|
+
const revokeChannel = (channelId) => markClickClackDiscussionChannelIdentityRevoked({
|
|
256
|
+
runtime,
|
|
257
|
+
accountId: account.accountId,
|
|
258
|
+
serverBaseUrl,
|
|
259
|
+
channelId
|
|
260
|
+
});
|
|
258
261
|
const unresolved = (await listPendingDiscussionOpens(runtime)).find((pending) => pending.sessionKey === sessionKey);
|
|
259
262
|
if (unresolved && (unresolved.accountId !== account.accountId || unresolved.credentialFingerprint !== credentialFingerprint || unresolved.serverBaseUrl !== serverBaseUrl || unresolved.workspaceId !== workspace.id)) {
|
|
260
263
|
await params.reconcilePendingOpen(unresolved);
|
|
@@ -266,10 +269,16 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
266
269
|
sessionKey
|
|
267
270
|
});
|
|
268
271
|
const fallback = fallbackDiscussionLabel(sessionKey, agentId);
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
272
|
+
const describeChannel = (session) => {
|
|
273
|
+
const label = resolveDiscussionLabel(session, sessionKey, agentId);
|
|
274
|
+
return {
|
|
275
|
+
label,
|
|
276
|
+
displayTitle: label === fallback ? "" : truncateDiscussionDisplayTitle(label),
|
|
277
|
+
section: session.category?.trim() || account.discussions.section,
|
|
278
|
+
externalUrl: controlSessionUrl(account.discussions.controlUrlBase, sessionKey, account.agentId ?? agentId, config.session?.mainKey, label)
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
const { label, displayTitle, section, externalUrl } = describeChannel(entry);
|
|
273
282
|
const assertCurrentAuthority = () => {
|
|
274
283
|
const currentAccounts = discussionAccounts(runtime.config.current());
|
|
275
284
|
const currentAccount = currentAccounts.length === 1 ? currentAccounts[0] : void 0;
|
|
@@ -278,9 +287,10 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
278
287
|
readConsistency: "latest"
|
|
279
288
|
});
|
|
280
289
|
if (!currentAccount || currentAccount.accountId !== account.accountId || normalizedServerBaseUrl(currentAccount) !== serverBaseUrl || currentAccount.apiEndpoint !== account.apiEndpoint || discussionCredentialFingerprint(currentAccount.token) !== credentialFingerprint || currentAccount.discussions.workspace !== account.discussions.workspace || !currentEntry?.sessionId || currentEntry.archivedAt !== void 0) throw new Error("ClickClack discussion authority changed while opening the channel");
|
|
290
|
+
return currentEntry;
|
|
281
291
|
};
|
|
282
292
|
return await params.withChannelMutationLock(async () => {
|
|
283
|
-
params.ensureBindingCapacity(sessionKey);
|
|
293
|
+
await params.ensureBindingCapacity(sessionKey);
|
|
284
294
|
let channels = await client.channels(workspace.id);
|
|
285
295
|
assertManagedChannelListContract(channels);
|
|
286
296
|
const destinationIdentity = [serverBaseUrl, workspace.id].join("\0");
|
|
@@ -336,24 +346,14 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
336
346
|
assertCurrentAuthority();
|
|
337
347
|
try {
|
|
338
348
|
if (adopted) {
|
|
339
|
-
|
|
340
|
-
runtime,
|
|
341
|
-
accountId: account.accountId,
|
|
342
|
-
serverBaseUrl,
|
|
343
|
-
channelId: adopted.id
|
|
344
|
-
});
|
|
349
|
+
revokeChannel(adopted.id);
|
|
345
350
|
resolved = await client.updateChannel(adopted.id, managedFields);
|
|
346
351
|
} else {
|
|
347
352
|
resolved = await client.createChannel(workspace.id, {
|
|
348
353
|
...managedFields,
|
|
349
354
|
kind: "public"
|
|
350
355
|
});
|
|
351
|
-
|
|
352
|
-
runtime,
|
|
353
|
-
accountId: account.accountId,
|
|
354
|
-
serverBaseUrl,
|
|
355
|
-
channelId: resolved.id
|
|
356
|
-
});
|
|
356
|
+
revokeChannel(resolved.id);
|
|
357
357
|
}
|
|
358
358
|
break;
|
|
359
359
|
} catch (error) {
|
|
@@ -369,12 +369,7 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
369
369
|
const recovered = relisted.find((candidate) => candidate.external_managed === true && candidate.external_ref === externalRef);
|
|
370
370
|
if (recovered) {
|
|
371
371
|
adopted = recovered;
|
|
372
|
-
|
|
373
|
-
runtime,
|
|
374
|
-
accountId: account.accountId,
|
|
375
|
-
serverBaseUrl,
|
|
376
|
-
channelId: recovered.id
|
|
377
|
-
});
|
|
372
|
+
revokeChannel(recovered.id);
|
|
378
373
|
assertCurrentAuthority();
|
|
379
374
|
resolved = await client.updateChannel(recovered.id, managedFields);
|
|
380
375
|
break;
|
|
@@ -407,19 +402,14 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
407
402
|
throw new Error("ClickClack discussion channel is missing its route id");
|
|
408
403
|
}
|
|
409
404
|
const channel = resolved;
|
|
410
|
-
const currentEntry = runtime
|
|
411
|
-
sessionKey,
|
|
412
|
-
readConsistency: "latest"
|
|
413
|
-
});
|
|
405
|
+
const currentEntry = await readDiscussionSessionEntry(runtime, sessionKey);
|
|
414
406
|
if (!currentEntry?.sessionId || currentEntry.archivedAt !== void 0) {
|
|
415
407
|
await clearPendingDiscussionOpen(generationScope);
|
|
416
408
|
params.warn(`unattached discussion channel remains quarantined: ${channel.id}`);
|
|
417
409
|
throw new Error("OpenClaw session became inactive while opening its ClickClack discussion");
|
|
418
410
|
}
|
|
419
|
-
const currentLabel
|
|
420
|
-
const
|
|
421
|
-
const currentSection = currentEntry.category?.trim() || account.discussions.section;
|
|
422
|
-
const currentExternalUrl = controlSessionUrl(account.discussions.controlUrlBase, sessionKey, account.agentId ?? agentId, config.session?.mainKey, currentLabel) ?? "";
|
|
411
|
+
const { label: currentLabel, displayTitle: currentDisplayTitle, section: currentSection, externalUrl: updatedExternalUrl } = describeChannel(currentEntry);
|
|
412
|
+
const currentExternalUrl = updatedExternalUrl ?? "";
|
|
423
413
|
let currentChannel = channel;
|
|
424
414
|
if (currentEntry.sessionId !== entry.sessionId || currentLabel !== label || currentDisplayTitle !== displayTitle || currentSection !== section || currentExternalUrl !== (externalUrl ?? "")) try {
|
|
425
415
|
currentChannel = await renameDiscussionChannel({
|
|
@@ -461,7 +451,7 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
461
451
|
...currentChannel.display_title !== void 0 ? { displayTitle: currentChannel.display_title } : {}
|
|
462
452
|
};
|
|
463
453
|
try {
|
|
464
|
-
assertCurrentAuthority();
|
|
454
|
+
nextBinding.sessionId = assertCurrentAuthority().sessionId;
|
|
465
455
|
store.set(sessionKey, nextBinding);
|
|
466
456
|
} catch (error) {
|
|
467
457
|
await clearPendingDiscussionOpen(generationScope);
|
|
@@ -474,7 +464,7 @@ async function openClickClackDiscussionBinding(params) {
|
|
|
474
464
|
}
|
|
475
465
|
async function reconcilePendingDiscussionOpen(params) {
|
|
476
466
|
const { pending } = params;
|
|
477
|
-
const currentBinding = params.store.
|
|
467
|
+
const currentBinding = await params.store.getAsync(pending.sessionKey);
|
|
478
468
|
if (currentBinding?.externalRef === pending.externalRef) {
|
|
479
469
|
await params.finalizePendingBinding(pending.sessionKey, currentBinding);
|
|
480
470
|
return;
|
|
@@ -486,10 +476,7 @@ async function reconcilePendingDiscussionOpen(params) {
|
|
|
486
476
|
})).find((candidate) => candidate.configured && normalizedServerBaseUrl(candidate) === pending.serverBaseUrl && discussionCredentialFingerprint(candidate.token) === pending.credentialFingerprint);
|
|
487
477
|
if (!account) return;
|
|
488
478
|
const client = params.clientFactory(account);
|
|
489
|
-
const entry = params.runtime.
|
|
490
|
-
sessionKey: pending.sessionKey,
|
|
491
|
-
readConsistency: "latest"
|
|
492
|
-
});
|
|
479
|
+
const entry = await readDiscussionSessionEntry(params.runtime, pending.sessionKey);
|
|
493
480
|
const activeAccounts = discussionAccounts(cfg);
|
|
494
481
|
const retryAccount = activeAccounts.length === 1 ? activeAccounts[0] : void 0;
|
|
495
482
|
if (params.allowRetry !== false && entry?.sessionId && entry.archivedAt === void 0 && retryAccount && normalizedServerBaseUrl(retryAccount) === pending.serverBaseUrl && discussionCredentialFingerprint(retryAccount.token) === pending.credentialFingerprint) {
|
|
@@ -559,8 +546,8 @@ var ClickClackDiscussionService = class {
|
|
|
559
546
|
});
|
|
560
547
|
this.provider = {
|
|
561
548
|
id: "clickclack",
|
|
562
|
-
info:
|
|
563
|
-
open:
|
|
549
|
+
info: ({ sessionKey }) => this.info(sessionKey),
|
|
550
|
+
open: ({ sessionKey }) => this.open(sessionKey)
|
|
564
551
|
};
|
|
565
552
|
}
|
|
566
553
|
async bindGatewayEvents(gatewayEvents, scheduler) {
|
|
@@ -580,7 +567,7 @@ var ClickClackDiscussionService = class {
|
|
|
580
567
|
});
|
|
581
568
|
this.#unsubscribeSessionsChanged = gatewayEvents?.onSessionsChanged((event) => {
|
|
582
569
|
this.#withOperation(async () => {
|
|
583
|
-
if (this.#store.
|
|
570
|
+
if (await this.#store.getAsync(event.sessionKey) || (await listPendingDiscussionOpens(this.#runtime)).some((pending) => pending.sessionKey === event.sessionKey)) {
|
|
584
571
|
if (activation === this.#activation) this.#reconcileScheduler?.schedule(event.sessionKey);
|
|
585
572
|
}
|
|
586
573
|
}).catch((error) => {
|
|
@@ -600,8 +587,9 @@ var ClickClackDiscussionService = class {
|
|
|
600
587
|
}
|
|
601
588
|
async info(sessionKey) {
|
|
602
589
|
return await this.#withOperation(() => this.#sessionQueue.enqueue(sessionKey, async () => {
|
|
590
|
+
await this.#store.prepare();
|
|
603
591
|
if (discussionAccounts(this.#currentConfig()).length !== 1) return { state: "none" };
|
|
604
|
-
const existing = this.#store.
|
|
592
|
+
const existing = await this.#store.getAsync(sessionKey);
|
|
605
593
|
if (existing) {
|
|
606
594
|
const resolved = await this.#resolveBindingForUse(existing);
|
|
607
595
|
if (resolved.state === "retargeted") {
|
|
@@ -615,7 +603,7 @@ var ClickClackDiscussionService = class {
|
|
|
615
603
|
if (resolved.state !== "active") return { state: "none" };
|
|
616
604
|
await this.#finalizePendingBinding(sessionKey, existing);
|
|
617
605
|
await this.#reconcileBinding(sessionKey, existing, resolved.account);
|
|
618
|
-
const current = this.#store.
|
|
606
|
+
const current = await this.#store.getAsync(sessionKey);
|
|
619
607
|
if (!current) return { state: this.hasEnabledAccount() ? "available" : "none" };
|
|
620
608
|
return discussionInfoForBinding(current, resolved.account);
|
|
621
609
|
}
|
|
@@ -623,7 +611,10 @@ var ClickClackDiscussionService = class {
|
|
|
623
611
|
}));
|
|
624
612
|
}
|
|
625
613
|
async open(sessionKey) {
|
|
626
|
-
return await this.#withOperation(() => this.#sessionQueue.enqueue(sessionKey, () =>
|
|
614
|
+
return await this.#withOperation(() => this.#sessionQueue.enqueue(sessionKey, async () => {
|
|
615
|
+
await this.#store.prepare();
|
|
616
|
+
return await this.#open(sessionKey);
|
|
617
|
+
}));
|
|
627
618
|
}
|
|
628
619
|
async #open(sessionKey) {
|
|
629
620
|
this.#installationId ??= await getClickClackDiscussionInstallationId(this.#runtime);
|
|
@@ -631,7 +622,7 @@ var ClickClackDiscussionService = class {
|
|
|
631
622
|
if (accounts.length > 1) throw new Error("ClickClack discussions require exactly one enabled discussion account");
|
|
632
623
|
const account = accounts[0];
|
|
633
624
|
if (!account) return { state: "none" };
|
|
634
|
-
const existing = this.#store.
|
|
625
|
+
const existing = await this.#store.getAsync(sessionKey);
|
|
635
626
|
if (existing) {
|
|
636
627
|
const resolved = await this.#resolveBindingForUse(existing);
|
|
637
628
|
if (resolved.state === "retargeted") this.#revokeAndDeleteBinding(sessionKey, existing);
|
|
@@ -639,7 +630,7 @@ var ClickClackDiscussionService = class {
|
|
|
639
630
|
else if (resolved.state === "active") {
|
|
640
631
|
await this.#finalizePendingBinding(sessionKey, existing);
|
|
641
632
|
await this.#reconcileBinding(sessionKey, existing, resolved.account);
|
|
642
|
-
const current = this.#store.
|
|
633
|
+
const current = await this.#store.getAsync(sessionKey);
|
|
643
634
|
if (current) return discussionInfoForBinding(current, resolved.account);
|
|
644
635
|
}
|
|
645
636
|
}
|
|
@@ -654,8 +645,8 @@ var ClickClackDiscussionService = class {
|
|
|
654
645
|
bindingGenerationFactory: this.#bindingGenerationFactory,
|
|
655
646
|
sessionKey,
|
|
656
647
|
ensureTimer: () => this.#ensureTimer(),
|
|
657
|
-
reconcilePendingOpen:
|
|
658
|
-
withChannelMutationLock:
|
|
648
|
+
reconcilePendingOpen: (pending) => this.#reconcilePendingOpen(pending, { allowRetry: false }),
|
|
649
|
+
withChannelMutationLock: (run) => this.#withChannelMutationLock(run),
|
|
659
650
|
ensureBindingCapacity: (key) => this.#detachedBindings.ensureCapacity(key),
|
|
660
651
|
finalizePendingBinding: (key, nextBinding) => this.#finalizePendingBinding(key, nextBinding),
|
|
661
652
|
warn: (message) => this.#logger().warn(message)
|
|
@@ -672,7 +663,8 @@ var ClickClackDiscussionService = class {
|
|
|
672
663
|
async #reconcile(sessionKey) {
|
|
673
664
|
try {
|
|
674
665
|
await this.#sessionQueue.enqueue(sessionKey, async () => {
|
|
675
|
-
|
|
666
|
+
await this.#store.prepare();
|
|
667
|
+
const binding = await this.#store.getAsync(sessionKey);
|
|
676
668
|
if (binding) await this.#reconcileBinding(sessionKey, binding);
|
|
677
669
|
const pending = (await listPendingDiscussionOpens(this.#runtime)).find((candidate) => candidate.sessionKey === sessionKey);
|
|
678
670
|
if (pending) await this.#reconcilePendingOpen(pending);
|
|
@@ -702,7 +694,8 @@ var ClickClackDiscussionService = class {
|
|
|
702
694
|
}
|
|
703
695
|
async readLatestMessages(sessionKey, limit) {
|
|
704
696
|
return await this.#withOperation(() => this.#sessionQueue.enqueue(sessionKey, async () => {
|
|
705
|
-
|
|
697
|
+
await this.#store.prepare();
|
|
698
|
+
const binding = await this.#store.getAsync(sessionKey);
|
|
706
699
|
if (!binding) return { text: "No discussion is bound to this session." };
|
|
707
700
|
const resolved = await this.#resolveBindingForUse(binding);
|
|
708
701
|
if (resolved.state !== "active") return { text: "No discussion is bound to this session." };
|
|
@@ -759,10 +752,31 @@ var ClickClackDiscussionService = class {
|
|
|
759
752
|
return;
|
|
760
753
|
}
|
|
761
754
|
if (resolved.state !== "active") return;
|
|
762
|
-
|
|
763
|
-
if (!account.baseUrl || !account.token) throw new Error(`ClickClack discussion account is no longer configured: ${activeBinding.accountId}`);
|
|
755
|
+
if (!resolved.account.baseUrl || !resolved.account.token) throw new Error(`ClickClack discussion account is no longer configured: ${activeBinding.accountId}`);
|
|
764
756
|
if (entry.archivedAt !== void 0) return;
|
|
765
|
-
const
|
|
757
|
+
const labelForBackfill = resolveDiscussionLabel(entry, sessionKey, activeBinding.agentId);
|
|
758
|
+
const desiredBackfillTitle = labelForBackfill === fallbackDiscussionLabel(sessionKey, activeBinding.agentId) ? "" : truncateDiscussionDisplayTitle(labelForBackfill);
|
|
759
|
+
let bindingForAttachment = activeBinding;
|
|
760
|
+
let observedSiblingTitleSupport = false;
|
|
761
|
+
if (labelForBackfill === activeBinding.label && desiredBackfillTitle !== "" && activeBinding.displayTitle === void 0) {
|
|
762
|
+
const bindings = await this.#store.entries();
|
|
763
|
+
const freshBinding = this.#store.get(sessionKey);
|
|
764
|
+
if (!freshBinding || freshBinding.serverBaseUrl !== activeBinding.serverBaseUrl || freshBinding.channelId !== activeBinding.channelId || freshBinding.externalRef !== activeBinding.externalRef) return;
|
|
765
|
+
if (isClickClackDiscussionChannelRevoked({
|
|
766
|
+
runtime: this.#runtime,
|
|
767
|
+
serverBaseUrl: freshBinding.serverBaseUrl,
|
|
768
|
+
channelId: freshBinding.channelId
|
|
769
|
+
})) {
|
|
770
|
+
this.#store.delete(sessionKey);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
bindingForAttachment = freshBinding;
|
|
774
|
+
observedSiblingTitleSupport = bindings.some(({ binding: candidate }) => candidate.displayTitle !== void 0 && candidate.serverBaseUrl === freshBinding.serverBaseUrl && candidate.accountId === freshBinding.accountId);
|
|
775
|
+
}
|
|
776
|
+
const current = resolveDiscussionBindingAccount(this.#currentConfig(), bindingForAttachment);
|
|
777
|
+
if (current.state !== "active") return;
|
|
778
|
+
const account = current.account;
|
|
779
|
+
const attached = this.#refreshSessionAttachment(sessionKey, bindingForAttachment);
|
|
766
780
|
if (!attached) return;
|
|
767
781
|
const currentBinding = attached;
|
|
768
782
|
const fallback = fallbackDiscussionLabel(sessionKey, currentBinding.agentId);
|
|
@@ -772,7 +786,7 @@ var ClickClackDiscussionService = class {
|
|
|
772
786
|
const patch = {};
|
|
773
787
|
const labelChanged = label !== currentBinding.label;
|
|
774
788
|
const desiredDisplayTitle = label === fallback ? "" : truncateDiscussionDisplayTitle(label);
|
|
775
|
-
const serverSupportsDisplayTitle =
|
|
789
|
+
const serverSupportsDisplayTitle = currentBinding.displayTitle !== void 0 || observedSiblingTitleSupport;
|
|
776
790
|
const shouldBackfillDisplayTitle = desiredDisplayTitle !== "" && currentBinding.displayTitle !== desiredDisplayTitle && serverSupportsDisplayTitle;
|
|
777
791
|
if (labelChanged || shouldBackfillDisplayTitle) patch.display_title = desiredDisplayTitle;
|
|
778
792
|
if (section !== currentBinding.section) patch.sidebar_section = section;
|
|
@@ -870,7 +884,7 @@ var ClickClackDiscussionService = class {
|
|
|
870
884
|
async #ensureTimer() {
|
|
871
885
|
if (this.#closed) return;
|
|
872
886
|
const hasPendingOpens = (await listPendingDiscussionOpens(this.#runtime)).length > 0;
|
|
873
|
-
const needsBindingPoll = this.#unsubscribeSessionsChanged === void 0 && this.#store.
|
|
887
|
+
const needsBindingPoll = this.#unsubscribeSessionsChanged === void 0 && await this.#store.countAsync() > 0;
|
|
874
888
|
if (this.#closed || !hasPendingOpens && !needsBindingPoll) {
|
|
875
889
|
if (this.#timer) {
|
|
876
890
|
this.#timer.cancel();
|
|
@@ -889,7 +903,7 @@ var ClickClackDiscussionService = class {
|
|
|
889
903
|
});
|
|
890
904
|
}
|
|
891
905
|
async #listReconcileSessionKeys() {
|
|
892
|
-
return /* @__PURE__ */ new Set([...this.#store.entries().map(({ sessionKey }) => sessionKey), ...(await listPendingDiscussionOpens(this.#runtime)).map(({ sessionKey }) => sessionKey)]);
|
|
906
|
+
return /* @__PURE__ */ new Set([...(await this.#store.entries()).map(({ sessionKey }) => sessionKey), ...(await listPendingDiscussionOpens(this.#runtime)).map(({ sessionKey }) => sessionKey)]);
|
|
893
907
|
}
|
|
894
908
|
#logger() {
|
|
895
909
|
return this.#runtime.logging.getChildLogger({
|
|
@@ -919,30 +933,42 @@ function blockedResult() {
|
|
|
919
933
|
};
|
|
920
934
|
}
|
|
921
935
|
function isClickClackDiscussionSessionTarget(params) {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
}
|
|
936
|
+
if (!isDiscussionSessionKey(params.requesterSessionKey)) return;
|
|
937
|
+
const { runtime, targetSessionKey } = params;
|
|
938
|
+
let binding;
|
|
939
|
+
try {
|
|
940
|
+
binding = getClickClackDiscussionBindingStore(runtime).get(targetSessionKey);
|
|
941
|
+
} catch {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (binding && discussionSessionKey({
|
|
945
|
+
runtime,
|
|
946
|
+
mainSessionKey: targetSessionKey,
|
|
947
|
+
...binding
|
|
948
|
+
}) === params.requesterSessionKey && !isClickClackDiscussionChannelRevoked({
|
|
949
|
+
runtime,
|
|
950
|
+
serverBaseUrl: binding.serverBaseUrl,
|
|
951
|
+
channelId: binding.channelId
|
|
952
|
+
}) && bindingMatchesActiveSessionIncarnation(runtime, targetSessionKey, binding) && resolveDiscussionBindingAccount(runtime.config.current(), binding).state === "active") return {
|
|
953
|
+
sessionKey: targetSessionKey,
|
|
954
|
+
binding
|
|
955
|
+
};
|
|
928
956
|
}
|
|
929
957
|
/** Restricts a discussion side session's session tools to its attached main session. */
|
|
930
958
|
function enforceClickClackDiscussionToolTarget(params) {
|
|
931
959
|
const callerSessionKey = params.context.sessionKey;
|
|
932
|
-
if (!callerSessionKey) return;
|
|
960
|
+
if (!callerSessionKey || !isDiscussionSessionKey(callerSessionKey)) return;
|
|
933
961
|
const { toolName } = params.event;
|
|
934
962
|
if (toolName !== "session_status" && !toolName.startsWith("sessions_")) return;
|
|
935
|
-
const matched = getClickClackDiscussionBindingStore(params.runtime).getByDiscussionSession(callerSessionKey);
|
|
936
|
-
if (!matched) return isDiscussionSessionKey(callerSessionKey) ? blockedResult() : void 0;
|
|
937
|
-
const targetsMain = Boolean(isClickClackDiscussionSessionTarget({
|
|
938
|
-
runtime: params.runtime,
|
|
939
|
-
requesterSessionKey: callerSessionKey,
|
|
940
|
-
targetSessionKey: matched.sessionKey
|
|
941
|
-
})) && TARGETED_SESSION_TOOLS.has(toolName) && params.event.params.sessionKey === matched.sessionKey;
|
|
942
963
|
const usesAlternateSendTarget = toolName === "sessions_send" && (params.event.params.label !== void 0 || params.event.params.agentId !== void 0);
|
|
943
964
|
const mutatesStatus = toolName === "session_status" && params.event.params.model !== void 0;
|
|
944
965
|
const selectsHistoryIncarnation = toolName === "sessions_history" && params.event.params.sessionId !== void 0;
|
|
945
|
-
|
|
966
|
+
const targetSessionKey = params.event.params.sessionKey;
|
|
967
|
+
if (TARGETED_SESSION_TOOLS.has(toolName) && typeof targetSessionKey === "string" && !usesAlternateSendTarget && !mutatesStatus && !selectsHistoryIncarnation && isClickClackDiscussionSessionTarget({
|
|
968
|
+
runtime: params.runtime,
|
|
969
|
+
requesterSessionKey: callerSessionKey,
|
|
970
|
+
targetSessionKey
|
|
971
|
+
})) return;
|
|
946
972
|
return blockedResult();
|
|
947
973
|
}
|
|
948
974
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as isClickClackSetupLoopbackHost, i as buildClickClackSetupClaimUrl, o as requireClickClackSetupApiBaseUrl } from "./setup-surface-
|
|
1
|
+
import { a as isClickClackSetupLoopbackHost, i as buildClickClackSetupClaimUrl, o as requireClickClackSetupApiBaseUrl } from "./setup-surface-BJ5D55LV.mjs";
|
|
2
2
|
import { withTimeout } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
3
3
|
import { createProviderOperationDeadline, readProviderJsonResponse, readResponseTextLimited, resolveProviderOperationTimeoutMs } from "openclaw/plugin-sdk/provider-http";
|
|
4
4
|
import { fetchWithSsrFGuard, resolvePinnedHostnameWithPolicy, ssrfPolicyFromHttpBaseUrlAllowedHostname } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
@@ -70,13 +70,8 @@ function parseClickClackSetupCodeClaim(value, expectedClaimUrl) {
|
|
|
70
70
|
}
|
|
71
71
|
/** Claims a one-time setup code without sending any existing bot credential. */
|
|
72
72
|
async function claimClickClackSetupCode(params) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
parsedClaimUrl = new URL(params.claimUrl);
|
|
76
|
-
} catch {
|
|
77
|
-
throw new Error("ClickClack setup code claim URL must be a valid HTTP(S) endpoint.");
|
|
78
|
-
}
|
|
79
|
-
if (parsedClaimUrl.protocol !== "http:" && parsedClaimUrl.protocol !== "https:" || parsedClaimUrl.username || parsedClaimUrl.password || parsedClaimUrl.search || parsedClaimUrl.hash || !parsedClaimUrl.pathname.endsWith("/api/bot-setup-codes/claim")) throw new Error("ClickClack setup code claim URL must be a valid HTTP(S) endpoint.");
|
|
73
|
+
const parsedClaimUrl = URL.parse(params.claimUrl);
|
|
74
|
+
if (!parsedClaimUrl || parsedClaimUrl.protocol !== "http:" && parsedClaimUrl.protocol !== "https:" || parsedClaimUrl.username || parsedClaimUrl.password || parsedClaimUrl.search || parsedClaimUrl.hash || !parsedClaimUrl.pathname.endsWith("/api/bot-setup-codes/claim")) throw new Error("ClickClack setup code claim URL must be a valid HTTP(S) endpoint.");
|
|
80
75
|
const deadline = createProviderOperationDeadline({
|
|
81
76
|
label: "ClickClack setup code claim",
|
|
82
77
|
timeoutMs: CLICKCLACK_SETUP_CODE_CLAIM_TIMEOUT_MS
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as clickClackConfigAdapter, n as clickClackSetupContract, s as clickClackConfigSchema, t as clickClackSetupWizard, u as clickClackMeta } from "./setup-surface-
|
|
1
|
+
import { l as clickClackConfigAdapter, n as clickClackSetupContract, s as clickClackConfigSchema, t as clickClackSetupWizard, u as clickClackMeta } from "./setup-surface-BJ5D55LV.mjs";
|
|
2
2
|
//#region extensions/clickclack/src/channel.setup.ts
|
|
3
3
|
const clickClackSetupPlugin = {
|
|
4
4
|
id: "clickclack",
|
|
@@ -232,9 +232,6 @@ const clickClackConfigAdapter = {
|
|
|
232
232
|
};
|
|
233
233
|
//#endregion
|
|
234
234
|
//#region extensions/clickclack/src/config-schema.ts
|
|
235
|
-
/**
|
|
236
|
-
* Zod-backed config schema for ClickClack channel accounts.
|
|
237
|
-
*/
|
|
238
235
|
const ClickClackAccountConfigSchema = z.object({
|
|
239
236
|
name: z.string().optional(),
|
|
240
237
|
enabled: z.boolean().optional(),
|
|
@@ -315,13 +312,8 @@ function isClickClackSetupLoopbackHost(hostname) {
|
|
|
315
312
|
}
|
|
316
313
|
function requireClickClackSetupApiBaseUrl(value, label) {
|
|
317
314
|
if (!value || value !== value.trim()) throw new Error(`ClickClack ${label} is invalid`);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
parsed = new URL(value);
|
|
321
|
-
} catch {
|
|
322
|
-
throw new Error(`ClickClack ${label} is invalid`);
|
|
323
|
-
}
|
|
324
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || !parsed.hostname || parsed.hostname.endsWith(".") || parsed.username || parsed.password || parsed.search || parsed.hash) throw new Error(`ClickClack ${label} is invalid`);
|
|
315
|
+
const parsed = URL.parse(value);
|
|
316
|
+
if (!parsed || parsed.protocol !== "http:" && parsed.protocol !== "https:" || !parsed.hostname || parsed.hostname.endsWith(".") || parsed.username || parsed.password || parsed.search || parsed.hash) throw new Error(`ClickClack ${label} is invalid`);
|
|
325
317
|
if (parsed.protocol === "http:" && !isClickClackSetupLoopbackHost(parsed.hostname)) throw new Error(`ClickClack ${label} must use HTTPS unless it is on loopback`);
|
|
326
318
|
const pathname = parsed.pathname;
|
|
327
319
|
let basePath = "";
|
|
@@ -334,13 +326,8 @@ function requireClickClackSetupApiBaseUrl(value, label) {
|
|
|
334
326
|
return canonical;
|
|
335
327
|
}
|
|
336
328
|
function requireClickClackSetupClaimUrl(value) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
parsed = new URL(value);
|
|
340
|
-
} catch {
|
|
341
|
-
throw new Error("ClickClack setup URL has an invalid claim endpoint.");
|
|
342
|
-
}
|
|
343
|
-
if (parsed.username || parsed.password || parsed.search || parsed.hash || !parsed.pathname.endsWith("/api/bot-setup-codes/claim")) throw new Error("ClickClack setup URL has an invalid claim endpoint.");
|
|
329
|
+
const parsed = URL.parse(value);
|
|
330
|
+
if (!parsed || parsed.username || parsed.password || parsed.search || parsed.hash || !parsed.pathname.endsWith("/api/bot-setup-codes/claim")) throw new Error("ClickClack setup URL has an invalid claim endpoint.");
|
|
344
331
|
const basePath = parsed.pathname.slice(0, -26);
|
|
345
332
|
const apiBaseUrl = requireClickClackSetupApiBaseUrl(parsed.origin + basePath, "setup URL API base");
|
|
346
333
|
const claimUrl = apiBaseUrl + CLICKCLACK_SETUP_CODE_CLAIM_PATH;
|
|
@@ -362,15 +349,9 @@ const REQUIRED_INPUT_ERROR = "ClickClack requires --token, --base-url, and --wor
|
|
|
362
349
|
const INVALID_BASE_URL_ERROR = "ClickClack base URL must be a valid http(s) URL.";
|
|
363
350
|
const SETUP_CODE_CONFLICT_ERROR = "ClickClack --code cannot be combined with --token, --token-file, or --use-env.";
|
|
364
351
|
function normalizeClickClackBaseUrl(value) {
|
|
365
|
-
const
|
|
366
|
-
if (!
|
|
367
|
-
|
|
368
|
-
const parsed = new URL(trimmed);
|
|
369
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
|
|
370
|
-
return parsed.toString().replace(/\/+$/, "");
|
|
371
|
-
} catch {
|
|
372
|
-
return;
|
|
373
|
-
}
|
|
352
|
+
const parsed = URL.parse(value?.trim() ?? "");
|
|
353
|
+
if (!parsed || parsed.protocol !== "http:" && parsed.protocol !== "https:") return;
|
|
354
|
+
return parsed.toString().replace(/\/+$/, "");
|
|
374
355
|
}
|
|
375
356
|
function normalizeClickClackSetupCode(value) {
|
|
376
357
|
const normalized = value.trim().toUpperCase().replaceAll("-", "").replaceAll(" ", "");
|
|
@@ -389,12 +370,8 @@ function parseClickClackSetupCodeInput(params) {
|
|
|
389
370
|
let baseUrl;
|
|
390
371
|
let exactClaimUrl;
|
|
391
372
|
if (/^[a-z][a-z\d+.-]*:\/\//iu.test(rawCode)) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
setupUrl = new URL(rawCode);
|
|
395
|
-
} catch {
|
|
396
|
-
throw new Error("ClickClack --code must be a valid HTTP(S) setup URL or a bare setup code.");
|
|
397
|
-
}
|
|
373
|
+
const setupUrl = URL.parse(rawCode);
|
|
374
|
+
if (!setupUrl) throw new Error("ClickClack --code must be a valid HTTP(S) setup URL or a bare setup code.");
|
|
398
375
|
if (setupUrl.protocol !== "http:" && setupUrl.protocol !== "https:") throw new Error("ClickClack setup codes require an HTTP(S) URL.");
|
|
399
376
|
if (setupUrl.username || setupUrl.password) throw new Error("ClickClack setup URLs must not include credentials.");
|
|
400
377
|
if (setupUrl.search) throw new Error("ClickClack setup URLs must not include a query.");
|
|
@@ -473,7 +450,7 @@ const clickClackSetupAdapter = {
|
|
|
473
450
|
const claimUrl = setup.exactClaimUrl && !privateApiBaseUrl ? setup.exactClaimUrl : buildClickClackSetupClaimUrl(privateApiBaseUrl ?? setup.baseUrl);
|
|
474
451
|
let claim;
|
|
475
452
|
try {
|
|
476
|
-
const { claimClickClackSetupCode } = await import("./setup-claim-
|
|
453
|
+
const { claimClickClackSetupCode } = await import("./setup-claim-C-9Pvumz.mjs");
|
|
477
454
|
claim = await claimClickClackSetupCode({
|
|
478
455
|
claimUrl,
|
|
479
456
|
code: setup.code,
|
|
@@ -553,7 +530,7 @@ const clickClackSetupAdapter = {
|
|
|
553
530
|
});
|
|
554
531
|
},
|
|
555
532
|
afterAccountConfigWritten: async ({ cfg, accountId, runtime }) => {
|
|
556
|
-
const { verifyClickClackAccountAfterSetup } = await import("./setup-verify-
|
|
533
|
+
const { verifyClickClackAccountAfterSetup } = await import("./setup-verify-2ruJKFPj.mjs");
|
|
557
534
|
await verifyClickClackAccountAfterSetup({
|
|
558
535
|
cfg,
|
|
559
536
|
accountId,
|
|
@@ -735,7 +712,7 @@ const clickClackSetupWizard = {
|
|
|
735
712
|
})
|
|
736
713
|
}],
|
|
737
714
|
finalize: async ({ cfg, accountId, credentialValues, prompter }) => {
|
|
738
|
-
const { checkClickClackSetupConnection } = await import("./setup-verify-
|
|
715
|
+
const { checkClickClackSetupConnection } = await import("./setup-verify-2ruJKFPj.mjs");
|
|
739
716
|
const result = await checkClickClackSetupConnection({
|
|
740
717
|
cfg,
|
|
741
718
|
accountId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as resolveClickClackAccount } from "./setup-surface-
|
|
2
|
-
import { a as createClickClackClient, r as resolveWorkspaceId } from "./resolve-
|
|
1
|
+
import { h as resolveClickClackAccount } from "./setup-surface-BJ5D55LV.mjs";
|
|
2
|
+
import { a as createClickClackClient, r as resolveWorkspaceId } from "./resolve-Br3ylpSz.mjs";
|
|
3
3
|
import { DEFAULT_ACCOUNT_ID } from "openclaw/plugin-sdk/account-id";
|
|
4
4
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
5
|
import { hasConfiguredSecretInput } from "openclaw/plugin-sdk/setup";
|
package/dist/api.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as resolveDefaultClickClackAccountId, d as DEFAULT_ACCOUNT_ID, h as resolveClickClackAccount, m as listEnabledClickClackAccounts, p as listClickClackAccountIds, s as clickClackConfigSchema } from "./.setup/setup-surface-
|
|
2
|
-
import { a as setClickClackRuntime, i as getClickClackRuntime, n as buildClickClackTarget, r as parseClickClackTarget, t as clickClackPlugin } from "./.setup/channel-
|
|
3
|
-
import { a as createClickClackClient } from "./.setup/resolve-
|
|
1
|
+
import { _ as resolveDefaultClickClackAccountId, d as DEFAULT_ACCOUNT_ID, h as resolveClickClackAccount, m as listEnabledClickClackAccounts, p as listClickClackAccountIds, s as clickClackConfigSchema } from "./.setup/setup-surface-BJ5D55LV.mjs";
|
|
2
|
+
import { a as setClickClackRuntime, i as getClickClackRuntime, n as buildClickClackTarget, r as parseClickClackTarget, t as clickClackPlugin } from "./.setup/channel-BFuhVeiX.mjs";
|
|
3
|
+
import { a as createClickClackClient } from "./.setup/resolve-Br3ylpSz.mjs";
|
|
4
4
|
export { DEFAULT_ACCOUNT_ID, buildClickClackTarget, clickClackConfigSchema, clickClackPlugin, createClickClackClient, getClickClackRuntime, listClickClackAccountIds, listEnabledClickClackAccounts, parseClickClackTarget, resolveClickClackAccount, resolveDefaultClickClackAccountId, setClickClackRuntime };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as clickClackPlugin } from "./.setup/channel-
|
|
1
|
+
import { t as clickClackPlugin } from "./.setup/channel-BFuhVeiX.mjs";
|
|
2
2
|
export { clickClackPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { t as registerClickClackDiscussions } from "./.setup/runtime-api-
|
|
1
|
+
import { t as registerClickClackDiscussions } from "./.setup/runtime-api-hAV5Muz-.mjs";
|
|
2
2
|
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
3
3
|
//#region extensions/clickclack/index.ts
|
|
4
|
-
/**
|
|
5
|
-
* Bundled channel entry metadata for the ClickClack plugin.
|
|
6
|
-
*/
|
|
7
4
|
var clickclack_default = defineBundledChannelEntry({
|
|
8
5
|
id: "clickclack",
|
|
9
6
|
name: "ClickClack",
|
package/dist/runtime-api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as resolveClickClackAccount } from "./.setup/setup-surface-
|
|
2
|
-
import { a as setClickClackRuntime, r as parseClickClackTarget } from "./.setup/channel-
|
|
3
|
-
import { a as createClickClackClient } from "./.setup/resolve-
|
|
4
|
-
import { t as registerClickClackDiscussions } from "./.setup/runtime-api-
|
|
1
|
+
import { h as resolveClickClackAccount } from "./.setup/setup-surface-BJ5D55LV.mjs";
|
|
2
|
+
import { a as setClickClackRuntime, r as parseClickClackTarget } from "./.setup/channel-BFuhVeiX.mjs";
|
|
3
|
+
import { a as createClickClackClient } from "./.setup/resolve-Br3ylpSz.mjs";
|
|
4
|
+
import { t as registerClickClackDiscussions } from "./.setup/runtime-api-hAV5Muz-.mjs";
|
|
5
5
|
export { createClickClackClient, parseClickClackTarget, registerClickClackDiscussions, resolveClickClackAccount, setClickClackRuntime };
|
package/dist/setup-entry.js
CHANGED
|
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
var setup_entry_default = defineBundledChannelSetupEntry({
|
|
5
5
|
importMetaUrl: import.meta.url,
|
|
6
6
|
plugin: {
|
|
7
|
-
specifier: fileURLToPath(new URL(".setup/setup-plugin-api-
|
|
7
|
+
specifier: fileURLToPath(new URL(".setup/setup-plugin-api-DpKXmzV1.mjs", import.meta.url).href),
|
|
8
8
|
exportName: "clickClackSetupPlugin"
|
|
9
9
|
},
|
|
10
10
|
secrets: {
|
package/dist/setup-plugin-api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as clickClackSetupPlugin } from "./.setup/setup-plugin-api-
|
|
1
|
+
import { t as clickClackSetupPlugin } from "./.setup/setup-plugin-api-Qi8u_lra.mjs";
|
|
2
2
|
export { clickClackSetupPlugin };
|
|
@@ -75,11 +75,24 @@ class WebSocket extends EventEmitter {
|
|
|
75
75
|
this._redirects = 0;
|
|
76
76
|
|
|
77
77
|
if (protocols === undefined) {
|
|
78
|
-
protocols
|
|
78
|
+
if (!options || options.protocols === undefined) {
|
|
79
|
+
protocols = [];
|
|
80
|
+
} else if (Array.isArray(options.protocols)) {
|
|
81
|
+
protocols = options.protocols;
|
|
82
|
+
} else {
|
|
83
|
+
protocols = [options.protocols];
|
|
84
|
+
}
|
|
79
85
|
} else if (!Array.isArray(protocols)) {
|
|
80
86
|
if (typeof protocols === 'object' && protocols !== null) {
|
|
81
87
|
options = protocols;
|
|
82
|
-
|
|
88
|
+
|
|
89
|
+
if (options.protocols === undefined) {
|
|
90
|
+
protocols = [];
|
|
91
|
+
} else if (Array.isArray(options.protocols)) {
|
|
92
|
+
protocols = options.protocols;
|
|
93
|
+
} else {
|
|
94
|
+
protocols = [options.protocols];
|
|
95
|
+
}
|
|
83
96
|
} else {
|
|
84
97
|
protocols = [protocols];
|
|
85
98
|
}
|
|
@@ -318,7 +331,6 @@ class WebSocket extends EventEmitter {
|
|
|
318
331
|
return;
|
|
319
332
|
}
|
|
320
333
|
|
|
321
|
-
this._readyState = WebSocket.CLOSING;
|
|
322
334
|
this._sender.close(code, data, !this._isServer, (err) => {
|
|
323
335
|
//
|
|
324
336
|
// This error is handled by the `'error'` listener on the socket. We only
|
|
@@ -336,6 +348,7 @@ class WebSocket extends EventEmitter {
|
|
|
336
348
|
}
|
|
337
349
|
});
|
|
338
350
|
|
|
351
|
+
this._readyState = WebSocket.CLOSING;
|
|
339
352
|
setCloseTimer(this);
|
|
340
353
|
}
|
|
341
354
|
|
|
@@ -681,6 +694,7 @@ function initAsClient(websocket, address, protocols, options) {
|
|
|
681
694
|
socketPath: undefined,
|
|
682
695
|
hostname: undefined,
|
|
683
696
|
protocol: undefined,
|
|
697
|
+
protocols: undefined,
|
|
684
698
|
timeout: undefined,
|
|
685
699
|
method: 'GET',
|
|
686
700
|
host: undefined,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/clickclack",
|
|
3
|
-
"version": "2026.10.1",
|
|
3
|
+
"version": "2026.10.5-beta.1",
|
|
4
4
|
"description": "OpenClaw ClickClack channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"./runtime-api.js": "./dist/runtime-api.js"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"ws": "8.
|
|
12
|
+
"ws": "8.22.0",
|
|
13
13
|
"zod": "4.6.5"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.10.1"
|
|
16
|
+
"openclaw": ">=2026.10.5-beta.1"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -139,10 +139,10 @@
|
|
|
139
139
|
"allowInvalidConfigRecovery": true
|
|
140
140
|
},
|
|
141
141
|
"compat": {
|
|
142
|
-
"pluginApi": ">=2026.10.1"
|
|
142
|
+
"pluginApi": ">=2026.10.5-beta.1"
|
|
143
143
|
},
|
|
144
144
|
"build": {
|
|
145
|
-
"openclawVersion": "2026.10.1",
|
|
145
|
+
"openclawVersion": "2026.10.5-beta.1",
|
|
146
146
|
"bundledDist": false
|
|
147
147
|
},
|
|
148
148
|
"release": {
|