duclaw-cli 1.8.2 → 1.8.4

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/bundle.js CHANGED
@@ -30242,7 +30242,7 @@ function printHelp() {
30242
30242
  `);
30243
30243
  }
30244
30244
  function printVersion() {
30245
- console.log(`duclaw-cli v${true ? "1.8.2" : "unknown"}`);
30245
+ console.log(`duclaw-cli v${true ? "1.8.4" : "unknown"}`);
30246
30246
  }
30247
30247
  function getDuclawTemplate() {
30248
30248
  return {
@@ -45312,10 +45312,24 @@ var executeJob = async (job) => {
45312
45312
  const resultFilePath = saveResultToFile(job, result);
45313
45313
  console.log(`[cron\u6267\u884C\u5B8C\u6210] \u5B9A\u65F6\u4EFB\u52A1\u6267\u884C\u7ED3\u679C: ${resultFilePath}`);
45314
45314
  updateJobLastRunTime(job.id);
45315
+ return { ok: true, resultFilePath };
45315
45316
  } catch (error) {
45316
- console.error(`[cron] \u4EFB\u52A1\u6267\u884C\u5931\u8D25: ${job.id} - ${error}`);
45317
+ const message = error instanceof Error ? error.message : String(error);
45318
+ console.error(`[cron] \u4EFB\u52A1\u6267\u884C\u5931\u8D25: ${job.id} - ${message}`);
45319
+ return { ok: false, error: message };
45317
45320
  }
45318
45321
  };
45322
+ var runJobById = async (jobId) => {
45323
+ const job = listJobs().find((item) => item.id === jobId);
45324
+ if (!job) {
45325
+ return { ok: false, error: "job_not_found" };
45326
+ }
45327
+ if (!job.enabled) {
45328
+ return { ok: false, error: "job_disabled", job };
45329
+ }
45330
+ const result = await executeJob(job);
45331
+ return { ...result, job };
45332
+ };
45319
45333
  var executeAgentJob = async (job) => {
45320
45334
  const { registry: registry2, executor } = getCronTools();
45321
45335
  const tools = getAllTools(registry2);
@@ -49998,6 +50012,23 @@ cronRoutes.get("/cron/jobs/:id/history", (c) => {
49998
50012
  return c.json({ error: err.message || "Failed to get job history" }, 500);
49999
50013
  }
50000
50014
  });
50015
+ cronRoutes.post("/cron/jobs/:id/run", async (c) => {
50016
+ const id = c.req.param("id");
50017
+ try {
50018
+ const result = await runJobById(id);
50019
+ if (!result.ok) {
50020
+ const status = result.error === "job_not_found" ? 404 : 400;
50021
+ return c.json({ error: result.error || "Failed to run job" }, status);
50022
+ }
50023
+ return c.json({
50024
+ ok: true,
50025
+ jobId: result.job?.id ?? id,
50026
+ resultFilePath: result.resultFilePath
50027
+ });
50028
+ } catch (err) {
50029
+ return c.json({ error: err.message || "Failed to run job" }, 500);
50030
+ }
50031
+ });
50001
50032
 
50002
50033
  // src/server/routes/mailbox.ts
50003
50034
  var mailboxRoutes = new Hono2();
@@ -50175,6 +50206,7 @@ var userKeyPatterns = [
50175
50206
  ];
50176
50207
  var conversationKeyPattern = { match: "agent:mem:*", prefix: "agent:mem:" };
50177
50208
  var dateSegmentPattern = /^\d{8}$/;
50209
+ var MAIN_MANAGER_USER_ID = "__main_manager__";
50178
50210
  var isPlausibleUserId = (userId) => {
50179
50211
  if (!userId) return false;
50180
50212
  if (userId.length > 240) return false;
@@ -50276,7 +50308,16 @@ var extractMemoryUserIdsFromKeys = (keys) => {
50276
50308
  };
50277
50309
  var collectCompactSummaries = async (userId) => {
50278
50310
  if (isFileBackedRuntime()) {
50279
- return [];
50311
+ const { compactSummaryStorage: compactSummaryStorage2 } = getSharedDeps();
50312
+ const records2 = [];
50313
+ const prefix2 = `agent:compact:summary:${userId}:`;
50314
+ for (const key of extractFileBackedStorageKeysForTest(getDuclawDataDir())) {
50315
+ if (!key.startsWith(prefix2)) continue;
50316
+ const logicalKey = key.startsWith("agent:") ? key.slice("agent:".length) : key;
50317
+ const value = await compactSummaryStorage2.get(logicalKey);
50318
+ if (value?.length) records2.push(...value);
50319
+ }
50320
+ return records2.sort((a, b) => b.createdAt - a.createdAt);
50280
50321
  }
50281
50322
  const client2 = await getRedisClient();
50282
50323
  const { compactSummaryStorage } = getSharedDeps();
@@ -50291,6 +50332,78 @@ var collectCompactSummaries = async (userId) => {
50291
50332
  }
50292
50333
  return records.sort((a, b) => b.createdAt - a.createdAt);
50293
50334
  };
50335
+ var isMailboxScopedUserId = (userId) => userId === "manager" || userId.includes("::") || userId.startsWith("cron-") || userId.startsWith("kanban:goal:");
50336
+ var classifyMemoryUser = (userId) => {
50337
+ if (userId === MAIN_MANAGER_USER_ID) return "main-manager";
50338
+ if (userId.startsWith("kanban:")) return "kanban";
50339
+ if (userId.includes("::")) return "department";
50340
+ if (userId.startsWith("oc_") || userId.startsWith("chat_")) return "chat";
50341
+ return "user";
50342
+ };
50343
+ var collectManagerRelatedUserIds = (userId) => {
50344
+ if (isMailboxScopedUserId(userId)) return [userId];
50345
+ try {
50346
+ const db3 = createSqliteDB();
50347
+ const rows = db3.prepare(
50348
+ `SELECT DISTINCT to_mailbox_id AS toMailboxId, from_mailbox_id AS fromMailboxId
50349
+ FROM mailbox
50350
+ WHERE origin_user_id = ?
50351
+ ORDER BY to_mailbox_id ASC, from_mailbox_id ASC`
50352
+ ).all(userId);
50353
+ const ids = /* @__PURE__ */ new Set([userId]);
50354
+ for (const row of rows) {
50355
+ addPlausibleUserId(ids, row.toMailboxId || void 0);
50356
+ addPlausibleUserId(ids, row.fromMailboxId || void 0);
50357
+ }
50358
+ ids.delete("manager");
50359
+ return Array.from(ids);
50360
+ } catch (err) {
50361
+ console.warn(`[memoryRoutes] \u83B7\u53D6 Main Manager \u5173\u8054\u4E0A\u4E0B\u6587\u5931\u8D25 userId=${userId}: ${err.message}`);
50362
+ return [userId];
50363
+ }
50364
+ };
50365
+ var collectMainManagerScopeUserIds = async () => {
50366
+ const { userIds } = await collectUserIds();
50367
+ return Array.from(new Set(userIds.filter((id) => id !== MAIN_MANAGER_USER_ID && !isMailboxScopedUserId(id))));
50368
+ };
50369
+ var collectMemoryScopeUserIds = async (userId) => {
50370
+ if (userId === MAIN_MANAGER_USER_ID) return collectMainManagerScopeUserIds();
50371
+ return collectManagerRelatedUserIds(userId);
50372
+ };
50373
+ var aggregateMemorySummaries = async (userId, localConversationUserIds) => {
50374
+ const { memoryEngine, dreamStorage, dreamStateStorage, topicStorage } = getSharedDeps();
50375
+ const relatedUserIds = await collectMemoryScopeUserIds(userId);
50376
+ const details = await Promise.all(relatedUserIds.map(async (scopeUserId) => {
50377
+ const [memories, dreamContent, dreamState, topics] = await Promise.all([
50378
+ memoryEngine.list(scopeUserId),
50379
+ dreamStorage.get(`dream:latest:${scopeUserId}`),
50380
+ dreamStateStorage.get(`dream:state:${scopeUserId}`),
50381
+ topicStorage.get(`topics:${scopeUserId}`)
50382
+ ]);
50383
+ return {
50384
+ userId: scopeUserId,
50385
+ memoryCount: memories.length,
50386
+ hasDream: !!dreamContent,
50387
+ hasConversation: (topics?.length ?? 0) > 0 || localConversationUserIds.has(scopeUserId),
50388
+ lastDreamAt: dreamState?.lastDreamAt ?? null,
50389
+ lastActivityAt: dreamState?.lastActivityAt ?? null
50390
+ };
50391
+ }));
50392
+ const memoryCount = details.reduce((sum, detail) => sum + detail.memoryCount, 0);
50393
+ return {
50394
+ userId,
50395
+ displayName: userId === MAIN_MANAGER_USER_ID ? "Main Manager \u6C47\u603B" : void 0,
50396
+ kind: classifyMemoryUser(userId),
50397
+ memoryCount,
50398
+ hasMemory: memoryCount > 0,
50399
+ hasDream: details.some((detail) => detail.hasDream),
50400
+ hasConversation: details.some((detail) => detail.hasConversation),
50401
+ lastDreamAt: Math.max(0, ...details.map((detail) => detail.lastDreamAt ?? 0)) || null,
50402
+ lastActivityAt: Math.max(0, ...details.map((detail) => detail.lastActivityAt ?? 0)) || null,
50403
+ relatedUserIds: relatedUserIds.length > 0 && (userId === MAIN_MANAGER_USER_ID || relatedUserIds.length > 1) ? relatedUserIds : void 0,
50404
+ isAggregate: userId === MAIN_MANAGER_USER_ID || relatedUserIds.length > 1 || void 0
50405
+ };
50406
+ };
50294
50407
  var collectUserIds = async () => {
50295
50408
  const userIds = /* @__PURE__ */ new Set();
50296
50409
  const localConversationUserIds = collectLocalConversationUserIds();
@@ -50312,28 +50425,15 @@ var collectUserIds = async () => {
50312
50425
  var memoryRoutes = new Hono2();
50313
50426
  memoryRoutes.get("/memory/users", async (c) => {
50314
50427
  try {
50315
- const { memoryEngine, dreamStorage, dreamStateStorage, topicStorage } = getSharedDeps();
50316
50428
  const { userIds, localConversationUserIds } = await collectUserIds();
50317
- const summaries = await Promise.all(userIds.map(async (userId) => {
50318
- const [memories, dreamContent, dreamState, topics] = await Promise.all([
50319
- memoryEngine.list(userId),
50320
- dreamStorage.get(`dream:latest:${userId}`),
50321
- dreamStateStorage.get(`dream:state:${userId}`),
50322
- topicStorage.get(`topics:${userId}`)
50323
- ]);
50324
- const summary = {
50325
- userId,
50326
- memoryCount: memories.length,
50327
- hasMemory: memories.length > 0,
50328
- hasDream: !!dreamContent,
50329
- hasConversation: (topics?.length ?? 0) > 0 || localConversationUserIds.has(userId),
50330
- lastDreamAt: dreamState?.lastDreamAt ?? null,
50331
- lastActivityAt: dreamState?.lastActivityAt ?? null
50332
- };
50333
- return summary;
50334
- }));
50429
+ const summaries = await Promise.all(userIds.map(
50430
+ (userId) => aggregateMemorySummaries(userId, localConversationUserIds)
50431
+ ));
50432
+ if (userIds.length > 0) {
50433
+ summaries.unshift(await aggregateMemorySummaries(MAIN_MANAGER_USER_ID, localConversationUserIds));
50434
+ }
50335
50435
  summaries.sort(
50336
- (a, b) => (b.lastActivityAt ?? 0) - (a.lastActivityAt ?? 0) || a.userId.localeCompare(b.userId)
50436
+ (a, b) => Number(b.userId === MAIN_MANAGER_USER_ID) - Number(a.userId === MAIN_MANAGER_USER_ID) || (b.lastActivityAt ?? 0) - (a.lastActivityAt ?? 0) || a.userId.localeCompare(b.userId)
50337
50437
  );
50338
50438
  return c.json(summaries);
50339
50439
  } catch (err) {
@@ -50345,7 +50445,10 @@ memoryRoutes.get("/memory", async (c) => {
50345
50445
  if (!userId) return c.json({ error: "userId is required" }, 400);
50346
50446
  try {
50347
50447
  const { memoryEngine } = getSharedDeps();
50348
- const memories = await memoryEngine.list(userId);
50448
+ const scopeUserIds = await collectMemoryScopeUserIds(userId);
50449
+ const memories = (await Promise.all(
50450
+ scopeUserIds.map((scopeUserId) => memoryEngine.list(scopeUserId))
50451
+ )).flat();
50349
50452
  return c.json(sortMemories(memories));
50350
50453
  } catch (err) {
50351
50454
  return c.json({ error: err.message || "Failed to list memories" }, 500);
@@ -50408,16 +50511,28 @@ memoryRoutes.get("/memory/dream", async (c) => {
50408
50511
  if (!userId) return c.json({ error: "userId is required" }, 400);
50409
50512
  try {
50410
50513
  const { dreamStorage, dreamHistoryStorage, dreamStateStorage } = getSharedDeps();
50411
- const [dreamContent, dreamHistory, state] = await Promise.all([
50412
- dreamStorage.get(`dream:latest:${userId}`),
50413
- dreamHistoryStorage.get(`dream:history:${userId}`),
50414
- dreamStateStorage.get(`dream:state:${userId}`)
50415
- ]);
50514
+ const scopeUserIds = await collectMemoryScopeUserIds(userId);
50515
+ const scoped = await Promise.all(scopeUserIds.map(async (scopeUserId) => {
50516
+ const [dreamContent2, dreamHistory2, state2] = await Promise.all([
50517
+ dreamStorage.get(`dream:latest:${scopeUserId}`),
50518
+ dreamHistoryStorage.get(`dream:history:${scopeUserId}`),
50519
+ dreamStateStorage.get(`dream:state:${scopeUserId}`)
50520
+ ]);
50521
+ return { userId: scopeUserId, dreamContent: dreamContent2 || "", dreamHistory: dreamHistory2 || [], state: state2 || null };
50522
+ }));
50523
+ const dreamContent = scoped.filter((item) => item.dreamContent.trim()).map((item) => scoped.length > 1 ? `## ${item.userId}
50524
+ ${item.dreamContent}` : item.dreamContent).join("\n\n");
50525
+ const dreamHistory = scoped.flatMap((item) => item.dreamHistory.map((entry) => ({
50526
+ ...entry,
50527
+ content: scoped.length > 1 ? `[${item.userId}]
50528
+ ${entry.content}` : entry.content
50529
+ }))).sort((a, b) => b.createdAt - a.createdAt);
50530
+ const state = scoped.map((item) => item.state).filter((item) => !!item).sort((a, b) => b.lastActivityAt - a.lastActivityAt)[0] ?? null;
50416
50531
  return c.json({
50417
50532
  userId,
50418
- dreamContent: dreamContent || "",
50419
- dreamHistory: dreamHistory || [],
50420
- state: state || null
50533
+ dreamContent,
50534
+ dreamHistory,
50535
+ state
50421
50536
  });
50422
50537
  } catch (err) {
50423
50538
  return c.json({ error: err.message || "Failed to get dream summary" }, 500);
@@ -50427,7 +50542,9 @@ memoryRoutes.get("/memory/compact", async (c) => {
50427
50542
  const userId = requireUserId(c.req.query("userId"));
50428
50543
  if (!userId) return c.json({ error: "userId is required" }, 400);
50429
50544
  try {
50430
- const summaries = await collectCompactSummaries(userId);
50545
+ const summaries = (await Promise.all(
50546
+ (await collectMemoryScopeUserIds(userId)).map((scopeUserId) => collectCompactSummaries(scopeUserId))
50547
+ )).flat().sort((a, b) => b.createdAt - a.createdAt);
50431
50548
  return c.json({
50432
50549
  userId,
50433
50550
  summaries
@@ -50444,19 +50561,25 @@ memoryRoutes.get("/memory/recall", async (c) => {
50444
50561
  const limit = Math.min(Number(c.req.query("limit")) || 20, 50);
50445
50562
  try {
50446
50563
  const { recallIndexStorage } = getSharedDeps();
50564
+ const scopeUserIds = await collectMemoryScopeUserIds(userId);
50447
50565
  if (query) {
50448
- const result = await searchRecallIndex(recallIndexStorage, userId, query, date, limit);
50566
+ const scopedResults = await Promise.all(scopeUserIds.map(
50567
+ (scopeUserId) => searchRecallIndex(recallIndexStorage, scopeUserId, query, date, limit)
50568
+ ));
50569
+ const matches = scopedResults.flatMap((result) => result.matches).sort((a, b) => b.score - a.score).slice(0, limit);
50449
50570
  return c.json({
50450
50571
  userId,
50451
50572
  query,
50452
- total: result.total,
50453
- entries: result.matches.map((match2) => ({
50573
+ total: scopedResults.reduce((sum, result) => sum + result.total, 0),
50574
+ entries: matches.map((match2) => ({
50454
50575
  ...match2.entry,
50455
50576
  score: match2.score
50456
50577
  }))
50457
50578
  });
50458
50579
  }
50459
- const entries = await recallIndexStorage.get(`recall:index:${userId}`) || [];
50580
+ const entries = (await Promise.all(
50581
+ scopeUserIds.map((scopeUserId) => recallIndexStorage.get(`recall:index:${scopeUserId}`))
50582
+ )).flatMap((items) => items ?? []);
50460
50583
  const filtered = date ? entries.filter((entry) => entry.date === date) : entries;
50461
50584
  filtered.sort((a, b) => b.createdAt - a.createdAt);
50462
50585
  return c.json({
@@ -50474,10 +50597,19 @@ memoryRoutes.get("/memory/context", async (c) => {
50474
50597
  if (!userId) return c.json({ error: "userId is required" }, 400);
50475
50598
  try {
50476
50599
  const { memoryEngine, dreamStorage } = getSharedDeps();
50477
- const [memoryContextBlock, dreamContent] = await Promise.all([
50478
- memoryEngine.buildContextBlock(userId),
50479
- dreamStorage.get(`dream:latest:${userId}`)
50480
- ]);
50600
+ const scopeUserIds = await collectMemoryScopeUserIds(userId);
50601
+ const scoped = await Promise.all(scopeUserIds.map(async (scopeUserId) => {
50602
+ const [memoryContextBlock2, dreamContent2] = await Promise.all([
50603
+ memoryEngine.buildContextBlock(scopeUserId),
50604
+ dreamStorage.get(`dream:latest:${scopeUserId}`)
50605
+ ]);
50606
+ return { userId: scopeUserId, memoryContextBlock: memoryContextBlock2, dreamContent: dreamContent2 };
50607
+ }));
50608
+ const memoryContextBlock = scoped.filter((item) => item.memoryContextBlock.trim()).map((item) => scoped.length > 1 ? `<main-manager-context-source userId="${item.userId}">
50609
+ ${item.memoryContextBlock}
50610
+ </main-manager-context-source>` : item.memoryContextBlock).join("\n\n");
50611
+ const dreamContent = scoped.filter((item) => item.dreamContent?.trim()).map((item) => scoped.length > 1 ? `## ${item.userId}
50612
+ ${item.dreamContent}` : item.dreamContent).join("\n\n");
50481
50613
  const dreamContextBlock = dreamContent ? [
50482
50614
  "<memory-context>",
50483
50615
  "\u4EE5\u4E0B\u662F\u4F60\u901A\u8FC7\u505A\u68A6\u4FDD\u7559\u7684\u8DE8\u5929\u8BB0\u5FC6\u3002\u8BB0\u4F4F\u8FD9\u4E9B\u4FE1\u606F\uFF0C\u4F46\u4E0D\u8981\u4E3B\u52A8\u63D0\u53CA\u2014\u2014\u9664\u975E\u7528\u6237\u7684\u8BDD\u9898\u76F8\u5173\u3002",
@@ -50550,7 +50682,7 @@ var systemRoutes = new Hono2();
50550
50682
  var startTime = Date.now();
50551
50683
  systemRoutes.get("/system/info", (c) => {
50552
50684
  return c.json({
50553
- version: true ? "1.8.2" : "unknown",
50685
+ version: true ? "1.8.4" : "unknown",
50554
50686
  uptime: Math.floor((Date.now() - startTime) / 1e3),
50555
50687
  env: process.env.NODE_ENV || "development",
50556
50688
  nodeVersion: process.version
@@ -50651,8 +50783,12 @@ async function main() {
50651
50783
  } else {
50652
50784
  console.log(`[main] Core Channel Gateway \u5DF2\u8DF3\u8FC7\uFF0C\u5F53\u524D\u5916\u90E8\u6D88\u606F\u5165\u53E3\u7531 SaaS \u5C42\u63A5\u7BA1`);
50653
50785
  }
50654
- startScheduler();
50655
- console.log(`[main] \u5B9A\u65F6\u4EFB\u52A1\u8C03\u5EA6\u5668\u5DF2\u542F\u52A8`);
50786
+ if (process.env.DUCLAW_CRON_SCHEDULER === "saas") {
50787
+ console.log(`[main] \u5B9A\u65F6\u4EFB\u52A1\u8C03\u5EA6\u5668\u5DF2\u8DF3\u8FC7\uFF0C\u5F53\u524D\u7531 SaaS \u5C42\u63A5\u7BA1`);
50788
+ } else {
50789
+ startScheduler();
50790
+ console.log(`[main] \u5B9A\u65F6\u4EFB\u52A1\u8C03\u5EA6\u5668\u5DF2\u542F\u52A8`);
50791
+ }
50656
50792
  startMailboxPoller();
50657
50793
  console.log(`[main] \u591A\u667A\u80FD\u4F53\u90AE\u7BB1\u8F6E\u8BE2\u5DF2\u542F\u52A8`);
50658
50794
  const kanbanPort = Number(process.env.KANBAN_PORT || 3e3);