claw-control-center 0.1.1 → 0.1.2

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 CHANGED
@@ -2238,7 +2238,7 @@ var require_websocket = __commonJS({
2238
2238
  var http = require("http");
2239
2239
  var net = require("net");
2240
2240
  var tls = require("tls");
2241
- var { randomBytes, createHash } = require("crypto");
2241
+ var { randomBytes, createHash: createHash2 } = require("crypto");
2242
2242
  var { Duplex, Readable } = require("stream");
2243
2243
  var { URL: URL2 } = require("url");
2244
2244
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -2898,7 +2898,7 @@ var require_websocket = __commonJS({
2898
2898
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2899
2899
  return;
2900
2900
  }
2901
- const digest = createHash("sha1").update(key + GUID).digest("base64");
2901
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
2902
2902
  if (res.headers["sec-websocket-accept"] !== digest) {
2903
2903
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2904
2904
  return;
@@ -3265,7 +3265,7 @@ var require_websocket_server = __commonJS({
3265
3265
  var EventEmitter = require("events");
3266
3266
  var http = require("http");
3267
3267
  var { Duplex } = require("stream");
3268
- var { createHash } = require("crypto");
3268
+ var { createHash: createHash2 } = require("crypto");
3269
3269
  var extension2 = require_extension();
3270
3270
  var PerMessageDeflate2 = require_permessage_deflate();
3271
3271
  var subprotocol2 = require_subprotocol();
@@ -3566,7 +3566,7 @@ var require_websocket_server = __commonJS({
3566
3566
  );
3567
3567
  }
3568
3568
  if (this._state > RUNNING) return abortHandshake(socket, 503);
3569
- const digest = createHash("sha1").update(key + GUID).digest("base64");
3569
+ const digest = createHash2("sha1").update(key + GUID).digest("base64");
3570
3570
  const headers = [
3571
3571
  "HTTP/1.1 101 Switching Protocols",
3572
3572
  "Upgrade: websocket",
@@ -3666,7 +3666,7 @@ __export(index_exports, {
3666
3666
  module.exports = __toCommonJS(index_exports);
3667
3667
  var import_node_fs5 = require("fs");
3668
3668
  var import_node_path7 = require("path");
3669
- var import_node_crypto4 = require("crypto");
3669
+ var import_node_crypto5 = require("crypto");
3670
3670
 
3671
3671
  // src/agent-event-probe.ts
3672
3672
  var RECENT_EVENT_LIMIT = 200;
@@ -4203,6 +4203,7 @@ function createHub53AIBridge(input) {
4203
4203
  async function resolveCurrentSessionRPC(payload) {
4204
4204
  const record = toRecord2(payload);
4205
4205
  const userObject = toRecord2(record.user);
4206
+ const userName = stringOr(record.userName, record.user_name, userObject.name, userObject.userName, userObject.username);
4206
4207
  const chatId = stringOr(
4207
4208
  record.chat_id,
4208
4209
  record.chatId,
@@ -4215,11 +4216,14 @@ function createHub53AIBridge(input) {
4215
4216
  if (!chatId) {
4216
4217
  throw new HubRPCError("PARAM_ERROR", "chat_id or user is required");
4217
4218
  }
4219
+ if (isOpenClawSessionId(chatId)) {
4220
+ return input.gateway.getSession(chatId);
4221
+ }
4218
4222
  const mappedSession = await getMappedSession(chatId);
4219
4223
  if (mappedSession) {
4220
4224
  return mappedSession;
4221
4225
  }
4222
- return null;
4226
+ return restoreLatestHubSession(chatId, userName);
4223
4227
  }
4224
4228
  async function getMappedSession(chatId) {
4225
4229
  const mappedId = state.mappings[chatId];
@@ -4227,13 +4231,36 @@ function createHub53AIBridge(input) {
4227
4231
  return null;
4228
4232
  }
4229
4233
  try {
4230
- return await input.gateway.getSession(mappedId);
4234
+ const session = await input.gateway.getSession(mappedId);
4235
+ if (!isHubManagedSessionTitle(session.title, chatId)) {
4236
+ delete state.mappings[chatId];
4237
+ await persistState();
4238
+ input.logger?.warn?.(
4239
+ `Ignored stale 53AIHub session mapping for ${chatId}: mapped session ${mappedId} is "${session.title}"`
4240
+ );
4241
+ return null;
4242
+ }
4243
+ return session;
4231
4244
  } catch {
4232
4245
  delete state.mappings[chatId];
4233
4246
  await persistState();
4234
4247
  return null;
4235
4248
  }
4236
4249
  }
4250
+ async function restoreLatestHubSession(chatId, userName) {
4251
+ const page = await input.gateway.listSessionPage({
4252
+ limit: 100,
4253
+ offset: 0
4254
+ });
4255
+ const sessions = page.sessions.filter((session2) => isRestorableHubSession(session2, chatId, userName)).sort((left, right) => toTime(right.updatedAt || right.createdAt) - toTime(left.updatedAt || left.createdAt));
4256
+ const session = sessions[0];
4257
+ if (!session) {
4258
+ return null;
4259
+ }
4260
+ state.mappings[chatId] = session.id;
4261
+ await persistState();
4262
+ return session;
4263
+ }
4237
4264
  async function buildFallbackStatus() {
4238
4265
  const [gatewayHealth, runtimeInfo] = await Promise.allSettled([input.gateway.getHealth(), input.gateway.getRuntimeInfo()]);
4239
4266
  return {
@@ -4405,7 +4432,7 @@ function createHub53AIBridge(input) {
4405
4432
  if (event.kind === "tool.call" || event.kind === "tool.result") {
4406
4433
  const summary = summarizeVisibleActivity(event);
4407
4434
  if (summary && input.config.sendThinkingMessage) {
4408
- await recordBridgeThinkingEvent(resolveMappedSessionId(message), summary);
4435
+ await recordBridgeThinkingEvent(sessionId, summary);
4409
4436
  await sendReply({
4410
4437
  reqId: message.reqId,
4411
4438
  text: summary,
@@ -4478,9 +4505,6 @@ function createHub53AIBridge(input) {
4478
4505
  const payload = toRecord2(event.payload);
4479
4506
  return event.kind === "status.update" && String(payload.phase ?? payload.status ?? "") === "running";
4480
4507
  }
4481
- function resolveMappedSessionId(message) {
4482
- return state.mappings[message.chatId] ?? message.chatId;
4483
- }
4484
4508
  async function recordBridgeThinkingEvent(sessionId, content) {
4485
4509
  const normalized = content.trim();
4486
4510
  if (!normalized || !input.callbacks.onBridgeThinkingEvent) {
@@ -4523,13 +4547,6 @@ function createHub53AIBridge(input) {
4523
4547
  }
4524
4548
  async function resolveSession(message) {
4525
4549
  const desiredTitle = buildHubSessionTitle(message);
4526
- const mappedId = state.mappings[message.chatId];
4527
- if (mappedId) {
4528
- const session2 = await input.gateway.getSession(mappedId);
4529
- const nextSession = await renamePlaceholderSessionIfNeeded(session2, message, desiredTitle);
4530
- await input.callbacks.onSessionUpsert(nextSession);
4531
- return nextSession;
4532
- }
4533
4550
  if (isOpenClawSessionId(message.chatId)) {
4534
4551
  const session2 = await input.gateway.getSession(message.chatId);
4535
4552
  state.mappings[message.chatId] = session2.id;
@@ -4537,6 +4554,18 @@ function createHub53AIBridge(input) {
4537
4554
  await input.callbacks.onSessionUpsert(session2);
4538
4555
  return session2;
4539
4556
  }
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
+ const restoredSession = await restoreLatestHubSession(message.chatId, message.userName || message.userId);
4564
+ if (restoredSession) {
4565
+ const nextSession = await renamePlaceholderSessionIfNeeded(restoredSession, message, desiredTitle);
4566
+ await input.callbacks.onSessionUpsert(nextSession);
4567
+ return nextSession;
4568
+ }
4540
4569
  const session = await createSessionWithUniqueTitle(desiredTitle);
4541
4570
  state.mappings[message.chatId] = session.id;
4542
4571
  await persistState();
@@ -4920,6 +4949,25 @@ function isOldHubPlaceholderTitle(title, chatId) {
4920
4949
  const normalized = title.trim();
4921
4950
  return [`53AIHub ${chatId}`, `53AIHub:${chatId}`, `53AIHub-${chatId}`, chatId].includes(normalized);
4922
4951
  }
4952
+ function isHubManagedSessionTitle(title, chatId) {
4953
+ const normalized = title.trim();
4954
+ return normalized.startsWith(HUB_SESSION_TITLE_PREFIX) || isOldHubPlaceholderTitle(normalized, chatId);
4955
+ }
4956
+ function isRestorableHubSession(session, chatId, userName) {
4957
+ const normalized = session.title.trim();
4958
+ if (isOldHubPlaceholderTitle(normalized, chatId)) {
4959
+ return true;
4960
+ }
4961
+ if (!normalized.startsWith(HUB_SESSION_TITLE_PREFIX) || !userName) {
4962
+ return false;
4963
+ }
4964
+ return normalized.startsWith(`${HUB_SESSION_TITLE_PREFIX}${sanitizeTitlePart(userName)}\uFF1A`);
4965
+ }
4966
+ function toTime(value) {
4967
+ if (!value) return 0;
4968
+ const time = Date.parse(value);
4969
+ return Number.isFinite(time) ? time : 0;
4970
+ }
4923
4971
  function isOpenClawSessionId(value) {
4924
4972
  return value.startsWith("agent:");
4925
4973
  }
@@ -5049,7 +5097,10 @@ function normalizeUrlList(primary, fallback) {
5049
5097
  }).filter(Boolean);
5050
5098
  }
5051
5099
  function summarizeVisibleActivity(event) {
5052
- const name = String(event.payload?.name ?? event.payload?.toolName ?? event.payload?.skillName ?? "").trim();
5100
+ const data = toRecord2(event.payload?.data);
5101
+ const name = String(
5102
+ event.payload?.name ?? event.payload?.toolName ?? event.payload?.skillName ?? data.name ?? data.toolName ?? data.skillName ?? ""
5103
+ ).trim();
5053
5104
  if (event.kind === "tool.call") {
5054
5105
  return name ? `Used tool ${name}` : "Used a tool";
5055
5106
  }
@@ -8617,11 +8668,13 @@ function toRecord3(value) {
8617
8668
  }
8618
8669
 
8619
8670
  // src/install-qclaw.ts
8671
+ var import_node_crypto4 = require("crypto");
8620
8672
  var import_promises4 = require("fs/promises");
8621
8673
  var import_node_fs4 = require("fs");
8622
8674
  var import_node_os2 = require("os");
8623
8675
  var import_node_path6 = require("path");
8624
8676
  var PLUGIN_ID = "claw-control-center";
8677
+ var LEGACY_PLUGIN_ID = "53ai-openclaw";
8625
8678
  var DEFAULT_QCLAW_HOME = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".qclaw");
8626
8679
  var DEFAULT_OPENCLAW_HOME = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".openclaw");
8627
8680
  var DEFAULT_EXTENSIONS_DIR = (0, import_node_path6.resolve)(
@@ -8681,10 +8734,18 @@ async function installIntoHost(input, hostLabel) {
8681
8734
  const consolePort = normalizePort(input.consolePort);
8682
8735
  const plugins = ensureObject(config, "plugins");
8683
8736
  plugins.enabled = true;
8684
- plugins.allow = dedupeStrings([...Array.isArray(plugins.allow) ? plugins.allow : [], PLUGIN_ID]);
8737
+ plugins.allow = dedupeStrings([
8738
+ ...Array.isArray(plugins.allow) ? plugins.allow.filter((entry) => entry !== LEGACY_PLUGIN_ID) : [],
8739
+ PLUGIN_ID
8740
+ ]);
8685
8741
  const load = ensureObject(plugins, "load");
8686
8742
  load.paths = dedupeStrings([...Array.isArray(load.paths) ? load.paths : [], input.extensionsDir]);
8687
8743
  const entries = ensureObject(plugins, "entries");
8744
+ const legacyEntry = entries[LEGACY_PLUGIN_ID] && typeof entries[LEGACY_PLUGIN_ID] === "object" && !Array.isArray(entries[LEGACY_PLUGIN_ID]) ? entries[LEGACY_PLUGIN_ID] : void 0;
8745
+ if (legacyEntry) {
8746
+ legacyEntry.enabled = false;
8747
+ entries[LEGACY_PLUGIN_ID] = legacyEntry;
8748
+ }
8688
8749
  const previousEntry = ensureObject(entries, PLUGIN_ID);
8689
8750
  const previousConfig = ensureObject(previousEntry, "config");
8690
8751
  const previousGateway = ensureObject(previousConfig, "gateway");
@@ -8737,7 +8798,8 @@ async function installIntoHost(input, hostLabel) {
8737
8798
  extensionsDir: input.extensionsDir,
8738
8799
  destination,
8739
8800
  gatewayBaseUrl,
8740
- hub53aiConfigured: hubConfigured
8801
+ hub53aiConfigured: hubConfigured,
8802
+ pluginBuild: await readPluginBuildInfo(destination)
8741
8803
  };
8742
8804
  }
8743
8805
  async function runInstallCommand(input) {
@@ -8777,6 +8839,7 @@ async function runInstallCommand(input) {
8777
8839
  `Config: ${result.configPath}`,
8778
8840
  `Gateway: ${result.gatewayBaseUrl}`,
8779
8841
  `53AIHub: ${result.hub53aiConfigured ? "configured" : "not configured"}`,
8842
+ `Plugin build: ${result.pluginBuild}`,
8780
8843
  `Restart ${targetInfo.label} to load the plugin.`
8781
8844
  ].join("\n") + "\n"
8782
8845
  );
@@ -8818,6 +8881,26 @@ async function copyPublishablePackage(packageRoot, destination) {
8818
8881
  await (0, import_promises4.cp)(source, target, { recursive: true, force: true });
8819
8882
  }
8820
8883
  }
8884
+ async function readPluginBuildInfo(destination) {
8885
+ const packagePath = (0, import_node_path6.join)(destination, "package.json");
8886
+ const entryPath = (0, import_node_path6.join)(destination, "dist", "index.cjs");
8887
+ let version = "unknown";
8888
+ try {
8889
+ const packageJson = JSON.parse(await (0, import_promises4.readFile)(packagePath, "utf8"));
8890
+ if (typeof packageJson.version === "string" && packageJson.version.trim()) {
8891
+ version = packageJson.version.trim();
8892
+ }
8893
+ } catch {
8894
+ version = "unknown";
8895
+ }
8896
+ try {
8897
+ const entry = await (0, import_promises4.readFile)(entryPath);
8898
+ const digest = (0, import_node_crypto4.createHash)("sha256").update(entry).digest("hex").slice(0, 12);
8899
+ return `${PLUGIN_ID}@${version} sha256:${digest}`;
8900
+ } catch {
8901
+ return `${PLUGIN_ID}@${version} sha256:missing`;
8902
+ }
8903
+ }
8821
8904
  async function readOpenClawConfig(configPath) {
8822
8905
  if (!(0, import_node_fs4.existsSync)(configPath)) {
8823
8906
  return {};
@@ -8963,7 +9046,7 @@ async function createRuntime(input) {
8963
9046
  configPath: input.configPath,
8964
9047
  hostKind,
8965
9048
  pluginVersion: input.version,
8966
- token: (0, import_node_crypto4.randomUUID)(),
9049
+ token: (0, import_node_crypto5.randomUUID)(),
8967
9050
  gatewayConfig,
8968
9051
  hub53aiConfig: config.hub53ai,
8969
9052
  consoleConfig: config.console,
@@ -25,11 +25,13 @@ __export(install_qclaw_exports, {
25
25
  runInstallCommand: () => runInstallCommand
26
26
  });
27
27
  module.exports = __toCommonJS(install_qclaw_exports);
28
+ var import_node_crypto = require("crypto");
28
29
  var import_promises = require("fs/promises");
29
30
  var import_node_fs = require("fs");
30
31
  var import_node_os = require("os");
31
32
  var import_node_path = require("path");
32
33
  var PLUGIN_ID = "claw-control-center";
34
+ var LEGACY_PLUGIN_ID = "53ai-openclaw";
33
35
  var DEFAULT_QCLAW_HOME = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".qclaw");
34
36
  var DEFAULT_OPENCLAW_HOME = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".openclaw");
35
37
  var DEFAULT_EXTENSIONS_DIR = (0, import_node_path.resolve)(
@@ -89,10 +91,18 @@ async function installIntoHost(input, hostLabel) {
89
91
  const consolePort = normalizePort(input.consolePort);
90
92
  const plugins = ensureObject(config, "plugins");
91
93
  plugins.enabled = true;
92
- plugins.allow = dedupeStrings([...Array.isArray(plugins.allow) ? plugins.allow : [], PLUGIN_ID]);
94
+ plugins.allow = dedupeStrings([
95
+ ...Array.isArray(plugins.allow) ? plugins.allow.filter((entry) => entry !== LEGACY_PLUGIN_ID) : [],
96
+ PLUGIN_ID
97
+ ]);
93
98
  const load = ensureObject(plugins, "load");
94
99
  load.paths = dedupeStrings([...Array.isArray(load.paths) ? load.paths : [], input.extensionsDir]);
95
100
  const entries = ensureObject(plugins, "entries");
101
+ const legacyEntry = entries[LEGACY_PLUGIN_ID] && typeof entries[LEGACY_PLUGIN_ID] === "object" && !Array.isArray(entries[LEGACY_PLUGIN_ID]) ? entries[LEGACY_PLUGIN_ID] : void 0;
102
+ if (legacyEntry) {
103
+ legacyEntry.enabled = false;
104
+ entries[LEGACY_PLUGIN_ID] = legacyEntry;
105
+ }
96
106
  const previousEntry = ensureObject(entries, PLUGIN_ID);
97
107
  const previousConfig = ensureObject(previousEntry, "config");
98
108
  const previousGateway = ensureObject(previousConfig, "gateway");
@@ -145,7 +155,8 @@ async function installIntoHost(input, hostLabel) {
145
155
  extensionsDir: input.extensionsDir,
146
156
  destination,
147
157
  gatewayBaseUrl,
148
- hub53aiConfigured: hubConfigured
158
+ hub53aiConfigured: hubConfigured,
159
+ pluginBuild: await readPluginBuildInfo(destination)
149
160
  };
150
161
  }
151
162
  async function runInstallCommand(input) {
@@ -185,6 +196,7 @@ async function runInstallCommand(input) {
185
196
  `Config: ${result.configPath}`,
186
197
  `Gateway: ${result.gatewayBaseUrl}`,
187
198
  `53AIHub: ${result.hub53aiConfigured ? "configured" : "not configured"}`,
199
+ `Plugin build: ${result.pluginBuild}`,
188
200
  `Restart ${targetInfo.label} to load the plugin.`
189
201
  ].join("\n") + "\n"
190
202
  );
@@ -226,6 +238,26 @@ async function copyPublishablePackage(packageRoot, destination) {
226
238
  await (0, import_promises.cp)(source, target, { recursive: true, force: true });
227
239
  }
228
240
  }
241
+ async function readPluginBuildInfo(destination) {
242
+ const packagePath = (0, import_node_path.join)(destination, "package.json");
243
+ const entryPath = (0, import_node_path.join)(destination, "dist", "index.cjs");
244
+ let version = "unknown";
245
+ try {
246
+ const packageJson = JSON.parse(await (0, import_promises.readFile)(packagePath, "utf8"));
247
+ if (typeof packageJson.version === "string" && packageJson.version.trim()) {
248
+ version = packageJson.version.trim();
249
+ }
250
+ } catch {
251
+ version = "unknown";
252
+ }
253
+ try {
254
+ const entry = await (0, import_promises.readFile)(entryPath);
255
+ const digest = (0, import_node_crypto.createHash)("sha256").update(entry).digest("hex").slice(0, 12);
256
+ return `${PLUGIN_ID}@${version} sha256:${digest}`;
257
+ } catch {
258
+ return `${PLUGIN_ID}@${version} sha256:missing`;
259
+ }
260
+ }
229
261
  async function readOpenClawConfig(configPath) {
230
262
  if (!(0, import_node_fs.existsSync)(configPath)) {
231
263
  return {};
@@ -20,6 +20,7 @@ declare function installIntoQClaw(input: InstallInput): Promise<{
20
20
  destination: string;
21
21
  gatewayBaseUrl: string;
22
22
  hub53aiConfigured: boolean;
23
+ pluginBuild: string;
23
24
  }>;
24
25
  declare function installIntoOpenClaw(input: InstallInput): Promise<{
25
26
  configPath: string;
@@ -27,6 +28,7 @@ declare function installIntoOpenClaw(input: InstallInput): Promise<{
27
28
  destination: string;
28
29
  gatewayBaseUrl: string;
29
30
  hub53aiConfigured: boolean;
31
+ pluginBuild: string;
30
32
  }>;
31
33
  declare function runInstallCommand(input: {
32
34
  argv?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-control-center",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "types": "dist/index.d.ts",