claw-control-center 0.1.2 → 0.1.3
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.cjs +125 -27
- package/dist/index.d.cts +2 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3850,6 +3850,7 @@ var DEFAULT_THINKING_MESSAGE = "\u6B63\u5728\u5904\u7406\u60A8\u7684\u8BF7\u6C42
|
|
|
3850
3850
|
var MAX_OUTBOX_FRAMES = 200;
|
|
3851
3851
|
var RUN_WAIT_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
3852
3852
|
var HUB_SESSION_TITLE_PREFIX = "53AI Hub-";
|
|
3853
|
+
var CONTROL_CENTER_SESSION_TITLE = "Claw Control Center";
|
|
3853
3854
|
var HUB_TITLE_SUMMARY_LENGTH = 40;
|
|
3854
3855
|
var HUB_RPC_ACTIONS = /* @__PURE__ */ new Set([
|
|
3855
3856
|
"sessions.list",
|
|
@@ -4069,10 +4070,11 @@ function createHub53AIBridge(input) {
|
|
|
4069
4070
|
async function resolveRPCRequest(request) {
|
|
4070
4071
|
if (request.action === "sessions.list") {
|
|
4071
4072
|
const pagination = readRPCPagination(request.data, 50);
|
|
4072
|
-
|
|
4073
|
+
const page = await input.gateway.listSessionPage({
|
|
4073
4074
|
limit: pagination.limit,
|
|
4074
4075
|
offset: pagination.offset
|
|
4075
4076
|
});
|
|
4077
|
+
return mergeKnownHubSessionTitles(page);
|
|
4076
4078
|
}
|
|
4077
4079
|
if (request.action === "sessions.current") {
|
|
4078
4080
|
return resolveCurrentSessionRPC(request.data);
|
|
@@ -4216,13 +4218,6 @@ function createHub53AIBridge(input) {
|
|
|
4216
4218
|
if (!chatId) {
|
|
4217
4219
|
throw new HubRPCError("PARAM_ERROR", "chat_id or user is required");
|
|
4218
4220
|
}
|
|
4219
|
-
if (isOpenClawSessionId(chatId)) {
|
|
4220
|
-
return input.gateway.getSession(chatId);
|
|
4221
|
-
}
|
|
4222
|
-
const mappedSession = await getMappedSession(chatId);
|
|
4223
|
-
if (mappedSession) {
|
|
4224
|
-
return mappedSession;
|
|
4225
|
-
}
|
|
4226
4221
|
return restoreLatestHubSession(chatId, userName);
|
|
4227
4222
|
}
|
|
4228
4223
|
async function getMappedSession(chatId) {
|
|
@@ -4252,15 +4247,34 @@ function createHub53AIBridge(input) {
|
|
|
4252
4247
|
limit: 100,
|
|
4253
4248
|
offset: 0
|
|
4254
4249
|
});
|
|
4255
|
-
const
|
|
4250
|
+
const knownSessions = await listKnownSessions();
|
|
4251
|
+
const sessions = mergeKnownHubSessions(page.sessions, knownSessions).filter((session2) => isRestorableHubSession(session2, chatId, userName)).sort((left, right) => toTime(right.updatedAt || right.createdAt) - toTime(left.updatedAt || left.createdAt));
|
|
4256
4252
|
const session = sessions[0];
|
|
4257
4253
|
if (!session) {
|
|
4258
4254
|
return null;
|
|
4259
4255
|
}
|
|
4260
|
-
state.mappings[chatId] = session.id;
|
|
4261
|
-
await persistState();
|
|
4262
4256
|
return session;
|
|
4263
4257
|
}
|
|
4258
|
+
async function mergeKnownHubSessionTitles(page) {
|
|
4259
|
+
const knownSessions = await listKnownSessions();
|
|
4260
|
+
if (!knownSessions.length) {
|
|
4261
|
+
return page;
|
|
4262
|
+
}
|
|
4263
|
+
return {
|
|
4264
|
+
...page,
|
|
4265
|
+
sessions: mergeKnownHubSessions(page.sessions, knownSessions)
|
|
4266
|
+
};
|
|
4267
|
+
}
|
|
4268
|
+
async function listKnownSessions() {
|
|
4269
|
+
try {
|
|
4270
|
+
return await input.callbacks.listKnownSessions?.() ?? [];
|
|
4271
|
+
} catch (error) {
|
|
4272
|
+
input.logger?.warn?.(
|
|
4273
|
+
`Failed to read known 53AIHub sessions: ${error instanceof Error ? error.message : String(error)}`
|
|
4274
|
+
);
|
|
4275
|
+
return [];
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4264
4278
|
async function buildFallbackStatus() {
|
|
4265
4279
|
const [gatewayHealth, runtimeInfo] = await Promise.allSettled([input.gateway.getHealth(), input.gateway.getRuntimeInfo()]);
|
|
4266
4280
|
return {
|
|
@@ -4548,18 +4562,10 @@ function createHub53AIBridge(input) {
|
|
|
4548
4562
|
async function resolveSession(message) {
|
|
4549
4563
|
const desiredTitle = buildHubSessionTitle(message);
|
|
4550
4564
|
if (isOpenClawSessionId(message.chatId)) {
|
|
4551
|
-
const session2 = await
|
|
4552
|
-
state.mappings[message.chatId] = session2.id;
|
|
4553
|
-
await persistState();
|
|
4565
|
+
const session2 = await getSessionWithKnownHubTitle(message.chatId, message.conversationTitle);
|
|
4554
4566
|
await input.callbacks.onSessionUpsert(session2);
|
|
4555
4567
|
return session2;
|
|
4556
4568
|
}
|
|
4557
|
-
const mappedSession = await getMappedSession(message.chatId);
|
|
4558
|
-
if (mappedSession) {
|
|
4559
|
-
const nextSession = await renamePlaceholderSessionIfNeeded(mappedSession, message, desiredTitle);
|
|
4560
|
-
await input.callbacks.onSessionUpsert(nextSession);
|
|
4561
|
-
return nextSession;
|
|
4562
|
-
}
|
|
4563
4569
|
const restoredSession = await restoreLatestHubSession(message.chatId, message.userName || message.userId);
|
|
4564
4570
|
if (restoredSession) {
|
|
4565
4571
|
const nextSession = await renamePlaceholderSessionIfNeeded(restoredSession, message, desiredTitle);
|
|
@@ -4567,11 +4573,15 @@ function createHub53AIBridge(input) {
|
|
|
4567
4573
|
return nextSession;
|
|
4568
4574
|
}
|
|
4569
4575
|
const session = await createSessionWithUniqueTitle(desiredTitle);
|
|
4570
|
-
state.mappings[message.chatId] = session.id;
|
|
4571
|
-
await persistState();
|
|
4572
4576
|
await input.callbacks.onSessionUpsert(session);
|
|
4573
4577
|
return session;
|
|
4574
4578
|
}
|
|
4579
|
+
async function getSessionWithKnownHubTitle(sessionId, titleHint) {
|
|
4580
|
+
const session = await input.gateway.getSession(sessionId);
|
|
4581
|
+
const knownSessions = await listKnownSessions();
|
|
4582
|
+
const titleHintSession = applyHubTitleHint(session, titleHint);
|
|
4583
|
+
return mergeKnownHubSessions([titleHintSession], knownSessions)[0] ?? titleHintSession;
|
|
4584
|
+
}
|
|
4575
4585
|
async function createSessionWithUniqueTitle(baseTitle) {
|
|
4576
4586
|
let lastDuplicateError;
|
|
4577
4587
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
@@ -4737,6 +4747,7 @@ function parseIncomingMessage(rawJson) {
|
|
|
4737
4747
|
chatId: chatId2,
|
|
4738
4748
|
userId: userId2,
|
|
4739
4749
|
userName: extractUserName(openAIReq, metadata, userObject2, userMessage),
|
|
4750
|
+
conversationTitle: extractConversationTitle(openAIReq, metadata),
|
|
4740
4751
|
text: extractTextFromContent(content),
|
|
4741
4752
|
imageUrls: extractImagesFromContent(content),
|
|
4742
4753
|
fileUrls: extractFilesFromContent(content)
|
|
@@ -4756,6 +4767,7 @@ function parseIncomingMessage(rawJson) {
|
|
|
4756
4767
|
chatId,
|
|
4757
4768
|
userId,
|
|
4758
4769
|
userName: extractUserName(data, userObject),
|
|
4770
|
+
conversationTitle: extractConversationTitle(data),
|
|
4759
4771
|
text: stringOr(data.text, data.content, ""),
|
|
4760
4772
|
imageUrls: normalizeUrlList(data.imageUrls, data.images),
|
|
4761
4773
|
fileUrls: normalizeUrlList(data.fileUrls, data.files),
|
|
@@ -4895,6 +4907,23 @@ function extractUserName(...sources) {
|
|
|
4895
4907
|
}
|
|
4896
4908
|
return void 0;
|
|
4897
4909
|
}
|
|
4910
|
+
function extractConversationTitle(...sources) {
|
|
4911
|
+
const titleKeys = [
|
|
4912
|
+
"openclaw_conversation_title",
|
|
4913
|
+
"openclawConversationTitle",
|
|
4914
|
+
"conversation_title",
|
|
4915
|
+
"conversationTitle",
|
|
4916
|
+
"title"
|
|
4917
|
+
];
|
|
4918
|
+
for (const source of sources) {
|
|
4919
|
+
const record = toRecord2(source);
|
|
4920
|
+
const title = stringFromKeys(record, titleKeys);
|
|
4921
|
+
if (title) {
|
|
4922
|
+
return title;
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
return void 0;
|
|
4926
|
+
}
|
|
4898
4927
|
function stringFromKeys(record, keys) {
|
|
4899
4928
|
for (const key of keys) {
|
|
4900
4929
|
const value = record[key];
|
|
@@ -4963,6 +4992,44 @@ function isRestorableHubSession(session, chatId, userName) {
|
|
|
4963
4992
|
}
|
|
4964
4993
|
return normalized.startsWith(`${HUB_SESSION_TITLE_PREFIX}${sanitizeTitlePart(userName)}\uFF1A`);
|
|
4965
4994
|
}
|
|
4995
|
+
function mergeKnownHubSessions(gatewaySessions, knownSessions) {
|
|
4996
|
+
const knownHubById = new Map(
|
|
4997
|
+
knownSessions.filter((session) => isHubTitle(session.title)).map((session) => [session.id, session])
|
|
4998
|
+
);
|
|
4999
|
+
if (!knownHubById.size) {
|
|
5000
|
+
return gatewaySessions;
|
|
5001
|
+
}
|
|
5002
|
+
const merged = gatewaySessions.map((session) => {
|
|
5003
|
+
const knownSession = knownHubById.get(session.id);
|
|
5004
|
+
if (!knownSession || !shouldPreferKnownHubTitle(session.title, knownSession.title)) {
|
|
5005
|
+
return session;
|
|
5006
|
+
}
|
|
5007
|
+
return {
|
|
5008
|
+
...session,
|
|
5009
|
+
title: knownSession.title
|
|
5010
|
+
};
|
|
5011
|
+
});
|
|
5012
|
+
return merged;
|
|
5013
|
+
}
|
|
5014
|
+
function applyHubTitleHint(session, titleHint) {
|
|
5015
|
+
const normalizedTitle = titleHint?.trim();
|
|
5016
|
+
if (!normalizedTitle || !isHubTitle(normalizedTitle)) {
|
|
5017
|
+
return session;
|
|
5018
|
+
}
|
|
5019
|
+
return {
|
|
5020
|
+
...session,
|
|
5021
|
+
title: normalizedTitle
|
|
5022
|
+
};
|
|
5023
|
+
}
|
|
5024
|
+
function shouldPreferKnownHubTitle(gatewayTitle, knownTitle) {
|
|
5025
|
+
return isHubTitle(knownTitle) && (isControlCenterTitle(gatewayTitle) || !isHubTitle(gatewayTitle));
|
|
5026
|
+
}
|
|
5027
|
+
function isHubTitle(title) {
|
|
5028
|
+
return title.trim().startsWith(HUB_SESSION_TITLE_PREFIX);
|
|
5029
|
+
}
|
|
5030
|
+
function isControlCenterTitle(title) {
|
|
5031
|
+
return title.trim() === CONTROL_CENTER_SESSION_TITLE;
|
|
5032
|
+
}
|
|
4966
5033
|
function toTime(value) {
|
|
4967
5034
|
if (!value) return 0;
|
|
4968
5035
|
const time = Date.parse(value);
|
|
@@ -5181,6 +5248,8 @@ function toRecord2(value) {
|
|
|
5181
5248
|
// src/file-store.ts
|
|
5182
5249
|
var import_promises2 = require("fs/promises");
|
|
5183
5250
|
var import_node_path2 = require("path");
|
|
5251
|
+
var HUB_SESSION_TITLE_PREFIX2 = "53AI Hub-";
|
|
5252
|
+
var CONTROL_CENTER_SESSION_TITLE2 = "Claw Control Center";
|
|
5184
5253
|
var FileSessionStore = class {
|
|
5185
5254
|
constructor(filePath, maxSessions) {
|
|
5186
5255
|
this.filePath = filePath;
|
|
@@ -5213,9 +5282,29 @@ var FileSessionStore = class {
|
|
|
5213
5282
|
return this.state.sessions[sessionId]?.hydrated ?? false;
|
|
5214
5283
|
}
|
|
5215
5284
|
async upsertSession(session) {
|
|
5285
|
+
this.mergeSession(session);
|
|
5286
|
+
this.trimSessions();
|
|
5287
|
+
await this.persist();
|
|
5288
|
+
}
|
|
5289
|
+
async replaceSessions(sessions) {
|
|
5290
|
+
const remoteIds = new Set(sessions.map((session) => session.id));
|
|
5291
|
+
for (const session of sessions) {
|
|
5292
|
+
this.mergeSession(session);
|
|
5293
|
+
}
|
|
5294
|
+
for (const sessionId of Object.keys(this.state.sessions)) {
|
|
5295
|
+
if (!remoteIds.has(sessionId)) {
|
|
5296
|
+
delete this.state.sessions[sessionId];
|
|
5297
|
+
}
|
|
5298
|
+
}
|
|
5299
|
+
this.trimSessions();
|
|
5300
|
+
await this.persist();
|
|
5301
|
+
}
|
|
5302
|
+
mergeSession(session) {
|
|
5216
5303
|
const existing = this.state.sessions[session.id];
|
|
5304
|
+
const title = preserveExistingHubTitle(existing?.session.title, session.title);
|
|
5217
5305
|
const mergedSession = {
|
|
5218
5306
|
...session,
|
|
5307
|
+
title,
|
|
5219
5308
|
lastEventSeq: Math.max(session.lastEventSeq, existing?.session.lastEventSeq ?? 0)
|
|
5220
5309
|
};
|
|
5221
5310
|
this.state.sessions[session.id] = {
|
|
@@ -5224,8 +5313,6 @@ var FileSessionStore = class {
|
|
|
5224
5313
|
events: existing?.events ?? [],
|
|
5225
5314
|
hydrated: existing?.hydrated ?? false
|
|
5226
5315
|
};
|
|
5227
|
-
this.trimSessions();
|
|
5228
|
-
await this.persist();
|
|
5229
5316
|
}
|
|
5230
5317
|
async renameSession(sessionId, title) {
|
|
5231
5318
|
const record = this.requireSession(sessionId);
|
|
@@ -5294,6 +5381,18 @@ var FileSessionStore = class {
|
|
|
5294
5381
|
await this.persistChain;
|
|
5295
5382
|
}
|
|
5296
5383
|
};
|
|
5384
|
+
function preserveExistingHubTitle(existingTitle, incomingTitle) {
|
|
5385
|
+
if (isHubTitle2(existingTitle) && isControlCenterTitle2(incomingTitle)) {
|
|
5386
|
+
return existingTitle;
|
|
5387
|
+
}
|
|
5388
|
+
return incomingTitle;
|
|
5389
|
+
}
|
|
5390
|
+
function isHubTitle2(title) {
|
|
5391
|
+
return typeof title === "string" && title.trim().startsWith(HUB_SESSION_TITLE_PREFIX2);
|
|
5392
|
+
}
|
|
5393
|
+
function isControlCenterTitle2(title) {
|
|
5394
|
+
return title.trim() === CONTROL_CENTER_SESSION_TITLE2;
|
|
5395
|
+
}
|
|
5297
5396
|
function dedupeMessages(messages) {
|
|
5298
5397
|
return Array.from(new Map(messages.map((message) => [message.id, message])).values()).sort(
|
|
5299
5398
|
(left, right) => left.createdAt.localeCompare(right.createdAt)
|
|
@@ -5639,6 +5738,7 @@ function createConsoleServer(input) {
|
|
|
5639
5738
|
broadcastSessionEvent(event.sessionId, event);
|
|
5640
5739
|
},
|
|
5641
5740
|
listSessionEvents: (sessionId) => store.getSession(sessionId)?.events ?? [],
|
|
5741
|
+
listKnownSessions: () => store.listSessions(),
|
|
5642
5742
|
onEnsureSessionStream: ensureSessionStream,
|
|
5643
5743
|
getLastEventSeq: (sessionId) => store.getLastEventSeq(sessionId),
|
|
5644
5744
|
onStatusChange: broadcastStatus
|
|
@@ -5980,9 +6080,7 @@ function createConsoleServer(input) {
|
|
|
5980
6080
|
const before = sessionListSignature(store.listSessions());
|
|
5981
6081
|
const remoteSessions = await input.gateway.listSessions(input.persistence.maxSessions);
|
|
5982
6082
|
lastGatewayError = null;
|
|
5983
|
-
|
|
5984
|
-
await store.upsertSession(session);
|
|
5985
|
-
}
|
|
6083
|
+
await store.replaceSessions(remoteSessions);
|
|
5986
6084
|
return before !== sessionListSignature(store.listSessions());
|
|
5987
6085
|
} catch (error) {
|
|
5988
6086
|
lastGatewayError = error instanceof Error ? error : new Error(String(error));
|
package/dist/index.d.cts
CHANGED
|
@@ -142,6 +142,7 @@ type Hub53AIIncomingMessage = {
|
|
|
142
142
|
imageUrls?: string[];
|
|
143
143
|
fileUrls?: string[];
|
|
144
144
|
quoteContent?: string;
|
|
145
|
+
conversationTitle?: string;
|
|
145
146
|
};
|
|
146
147
|
type Hub53AIOutgoingChunk = {
|
|
147
148
|
req_id: string;
|
|
@@ -187,6 +188,7 @@ type HubBridgeCallbacks = {
|
|
|
187
188
|
onSessionStatus(sessionId: string, status: SessionStatus): Promise<void>;
|
|
188
189
|
onBridgeThinkingEvent?(event: TimelineEvent): Promise<void>;
|
|
189
190
|
listSessionEvents?(sessionId: string): TimelineEvent[] | Promise<TimelineEvent[]>;
|
|
191
|
+
listKnownSessions?(): GatewaySession[] | Promise<GatewaySession[]>;
|
|
190
192
|
onEnsureSessionStream(sessionId: string): Promise<void>;
|
|
191
193
|
getLastEventSeq(sessionId: string): number;
|
|
192
194
|
onStatusChange(): void;
|