@clawos-dev/clawd 0.2.303 → 0.2.305
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/cli.cjs +124 -78
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -52240,8 +52240,18 @@ function createLarkBotCloudClient(opts) {
|
|
|
52240
52240
|
await request("POST", "/api/lark-bot/reply", { ...args });
|
|
52241
52241
|
},
|
|
52242
52242
|
async claimEvents() {
|
|
52243
|
-
const json = await request("POST", "/api/lark-bot/events/claim");
|
|
52244
|
-
|
|
52243
|
+
const json = await request("POST", "/api/lark-bot/events/claim?v=2");
|
|
52244
|
+
const items = json.items ?? [];
|
|
52245
|
+
return items.map((it) => {
|
|
52246
|
+
const rec3 = it;
|
|
52247
|
+
if (rec3 && typeof rec3 === "object" && "payload" in rec3) {
|
|
52248
|
+
return {
|
|
52249
|
+
status: rec3.status === "forwarded" ? "forwarded" : "received",
|
|
52250
|
+
payload: rec3.payload
|
|
52251
|
+
};
|
|
52252
|
+
}
|
|
52253
|
+
return { status: "received", payload: it };
|
|
52254
|
+
});
|
|
52245
52255
|
},
|
|
52246
52256
|
async fetchResource(appId, messageId, fileKey, maxBytes, type) {
|
|
52247
52257
|
const ac = new AbortController();
|
|
@@ -52285,9 +52295,9 @@ var import_node_fs27 = __toESM(require("fs"), 1);
|
|
|
52285
52295
|
var import_node_path28 = __toESM(require("path"), 1);
|
|
52286
52296
|
init_protocol();
|
|
52287
52297
|
var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
|
|
52288
|
-
var
|
|
52298
|
+
var LOCAL_TURN_RETRIES = 2;
|
|
52299
|
+
var STOPPED_ERROR = "session stopped before reply";
|
|
52289
52300
|
var DEBOUNCE_MS = 1e3;
|
|
52290
|
-
var CONTENT_HOLD_MS = 6e4;
|
|
52291
52301
|
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
52292
52302
|
var EXT_BY_MIME = {
|
|
52293
52303
|
"image/png": "png",
|
|
@@ -52312,70 +52322,83 @@ function safeAttachmentName(raw) {
|
|
|
52312
52322
|
const ext = dot > 0 && cleaned.length - dot <= 20 ? cleaned.slice(dot) : "";
|
|
52313
52323
|
return cleaned.slice(0, 100 - ext.length) + ext;
|
|
52314
52324
|
}
|
|
52325
|
+
function routeClaimedEvents(items, opts) {
|
|
52326
|
+
const resume = [];
|
|
52327
|
+
for (const it of items) {
|
|
52328
|
+
opts.dedupe.add(it.payload.eventId);
|
|
52329
|
+
const env = { ...it.payload, iat: opts.nowSec, exp: opts.nowSec + 300 };
|
|
52330
|
+
if (it.status === "forwarded" && it.payload.type === "message") resume.push(env);
|
|
52331
|
+
else opts.router.handleEnvelope(env);
|
|
52332
|
+
}
|
|
52333
|
+
if (resume.length > 0) opts.router.handleResume(resume);
|
|
52334
|
+
}
|
|
52315
52335
|
function createLarkChatRouter(deps) {
|
|
52316
|
-
const timeoutMs = deps.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
|
|
52317
52336
|
const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
|
|
52318
52337
|
const sleep2 = deps.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
52319
52338
|
const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
|
|
52320
|
-
const contentHoldMs = deps.contentHoldMs ?? CONTENT_HOLD_MS;
|
|
52321
|
-
const timerMsFor = (envs) => {
|
|
52322
|
-
if (envs.some((e) => e.text?.trim())) return debounceMs;
|
|
52323
|
-
const hasContent = envs.some(
|
|
52324
|
-
(e) => (e.imageKeys?.length ?? 0) > 0 || (e.files?.length ?? 0) > 0
|
|
52325
|
-
);
|
|
52326
|
-
return hasContent ? contentHoldMs : debounceMs;
|
|
52327
|
-
};
|
|
52328
52339
|
const queues = /* @__PURE__ */ new Map();
|
|
52329
52340
|
const activity = /* @__PURE__ */ new Map();
|
|
52330
52341
|
const pending = /* @__PURE__ */ new Map();
|
|
52331
|
-
const
|
|
52332
|
-
const
|
|
52333
|
-
for (const [i, key] of
|
|
52342
|
+
const imageLinesFor = async (env, ownerMessageId, keys) => {
|
|
52343
|
+
const lines = [];
|
|
52344
|
+
for (const [i, key] of keys.entries()) {
|
|
52334
52345
|
try {
|
|
52335
52346
|
if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
|
|
52336
|
-
const r = await deps.fetchResource(env.appId,
|
|
52347
|
+
const r = await deps.fetchResource(env.appId, ownerMessageId, key, MAX_IMAGE_BYTES, "image");
|
|
52337
52348
|
if (r.data.byteLength > MAX_IMAGE_BYTES) {
|
|
52338
52349
|
deps.logger?.warn(`larkBot image oversize ${key}: ${r.data.byteLength}B`);
|
|
52339
|
-
|
|
52350
|
+
lines.push(IMAGE_OVERSIZE_TEXT);
|
|
52340
52351
|
continue;
|
|
52341
52352
|
}
|
|
52342
52353
|
const ext = EXT_BY_MIME[r.contentType];
|
|
52343
52354
|
if (!ext) {
|
|
52344
52355
|
deps.logger?.warn(`larkBot image unsupported mime ${key}: ${r.contentType}`);
|
|
52345
|
-
|
|
52356
|
+
lines.push(IMAGE_UNAVAILABLE_TEXT);
|
|
52346
52357
|
continue;
|
|
52347
52358
|
}
|
|
52348
52359
|
const dir = deps.mediaRootFor(env.chatId);
|
|
52349
52360
|
import_node_fs27.default.mkdirSync(dir, { recursive: true });
|
|
52350
|
-
const p2 = import_node_path28.default.join(dir, `${safeMediaName(
|
|
52361
|
+
const p2 = import_node_path28.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}.${ext}`);
|
|
52351
52362
|
import_node_fs27.default.writeFileSync(p2, r.data);
|
|
52352
|
-
|
|
52363
|
+
lines.push(`[\u56FE\u7247: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
52353
52364
|
} catch (err) {
|
|
52354
52365
|
deps.logger?.warn(`larkBot image fetch failed ${key}: ${err.message}`);
|
|
52355
|
-
|
|
52366
|
+
lines.push(IMAGE_UNAVAILABLE_TEXT);
|
|
52356
52367
|
}
|
|
52357
52368
|
}
|
|
52358
|
-
|
|
52359
|
-
|
|
52369
|
+
return lines;
|
|
52370
|
+
};
|
|
52371
|
+
const fileLinesFor = async (env, ownerMessageId, entries) => {
|
|
52372
|
+
const lines = [];
|
|
52373
|
+
for (const [i, f] of entries.entries()) {
|
|
52360
52374
|
try {
|
|
52361
52375
|
if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
|
|
52362
|
-
const r = await deps.fetchResource(env.appId,
|
|
52376
|
+
const r = await deps.fetchResource(env.appId, ownerMessageId, f.key, MAX_FILE_BYTES, "file");
|
|
52363
52377
|
if (r.data.byteLength > MAX_FILE_BYTES) {
|
|
52364
52378
|
deps.logger?.warn(`larkBot file oversize ${f.key}: ${r.data.byteLength}B`);
|
|
52365
|
-
|
|
52379
|
+
lines.push(FILE_OVERSIZE_TEXT(f.name));
|
|
52366
52380
|
continue;
|
|
52367
52381
|
}
|
|
52368
52382
|
const dir = deps.mediaRootFor(env.chatId);
|
|
52369
52383
|
import_node_fs27.default.mkdirSync(dir, { recursive: true });
|
|
52370
|
-
const p2 = import_node_path28.default.join(dir, `${safeMediaName(
|
|
52384
|
+
const p2 = import_node_path28.default.join(dir, `${safeMediaName(ownerMessageId)}-${i}-${safeAttachmentName(f.name)}`);
|
|
52371
52385
|
import_node_fs27.default.writeFileSync(p2, r.data);
|
|
52372
|
-
|
|
52386
|
+
lines.push(`[\u6587\u4EF6: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
52373
52387
|
} catch (err) {
|
|
52374
52388
|
deps.logger?.warn(`larkBot file fetch failed ${f.key}: ${err.message}`);
|
|
52375
|
-
|
|
52389
|
+
lines.push(FILE_UNAVAILABLE_TEXT(f.name));
|
|
52376
52390
|
}
|
|
52377
52391
|
}
|
|
52378
|
-
return
|
|
52392
|
+
return lines;
|
|
52393
|
+
};
|
|
52394
|
+
const buildSection = async (env) => {
|
|
52395
|
+
const parentLines = env.parentMessageId ? [
|
|
52396
|
+
...await imageLinesFor(env, env.parentMessageId, env.parentImageKeys ?? []),
|
|
52397
|
+
...await fileLinesFor(env, env.parentMessageId, env.parentFiles ?? [])
|
|
52398
|
+
] : [];
|
|
52399
|
+
const imageLines = await imageLinesFor(env, env.messageId, env.imageKeys ?? []);
|
|
52400
|
+
const fileLines = await fileLinesFor(env, env.messageId, env.files ?? []);
|
|
52401
|
+
return [...parentLines, env.text?.trim(), ...imageLines, ...fileLines].filter(Boolean).join("\n");
|
|
52379
52402
|
};
|
|
52380
52403
|
const quoteAnnotation = (env, i, idxByMessageId) => {
|
|
52381
52404
|
if (!env.parentMessageId) return void 0;
|
|
@@ -52384,7 +52407,15 @@ function createLarkChatRouter(deps) {
|
|
|
52384
52407
|
if (j === i - 1) return void 0;
|
|
52385
52408
|
return `[\u56DE\u590D\u7B2C${j + 1}\u6761]`;
|
|
52386
52409
|
};
|
|
52387
|
-
const
|
|
52410
|
+
const makeResumeText = (body, speaker) => `\u7EE7\u7EED
|
|
52411
|
+
|
|
52412
|
+
<system-reminder>\u300C\u7EE7\u7EED\u300D\u7531\u7CFB\u7EDF\u4EE3\u53D1\uFF1A\u6B64\u524D\u5904\u7406\u4EE5\u4E0B\u6D88\u606F\u65F6\u6267\u884C\u88AB\u6253\u65AD\u3002\u82E5\u4E0A\u6587\u5DF2\u6709\u5B83\u7684\u5904\u7406\u8FDB\u5EA6\uFF0C\u63A5\u7740\u5B8C\u6210\u672A\u5B8C\u6210\u7684\u90E8\u5206\uFF1B\u82E5\u4E0A\u6587\u6CA1\u6709\u76F8\u5173\u8BB0\u5F55\uFF0C\u628A\u4EE5\u4E0B\u5185\u5BB9\u5F53\u4F5C\u65B0\u6536\u5230\u7684\u6D88\u606F\u5904\u7406\u3002\u5904\u7406\u5B8C\u6309\u5E73\u5E38\u65B9\u5F0F\u56DE\u590D\uFF1A
|
|
52413
|
+
---
|
|
52414
|
+
${body}
|
|
52415
|
+
---
|
|
52416
|
+
\uFF08\u6765\u81EA\u98DE\u4E66\u7FA4\u6210\u5458 ${speaker}\uFF09</system-reminder>`;
|
|
52417
|
+
const runBatch = async (batchEnvs, opts) => {
|
|
52418
|
+
if (deps.isShuttingDown?.()) return;
|
|
52388
52419
|
const envs = batchEnvs.filter((e) => {
|
|
52389
52420
|
if (e.messageId) return true;
|
|
52390
52421
|
deps.logger?.warn(`larkBot chat-router: message envelope missing messageId, drop ${e.eventId}`);
|
|
@@ -52396,49 +52427,50 @@ function createLarkChatRouter(deps) {
|
|
|
52396
52427
|
const idxByMessageId = new Map(envs.map((e, i) => [e.messageId, i]));
|
|
52397
52428
|
const sections = [];
|
|
52398
52429
|
for (const [i, env] of envs.entries()) {
|
|
52399
|
-
const
|
|
52400
|
-
const section = [quoteAnnotation(env, i, idxByMessageId),
|
|
52430
|
+
const sectionBody = await buildSection(env);
|
|
52431
|
+
const section = [quoteAnnotation(env, i, idxByMessageId), sectionBody].filter(Boolean).join("\n");
|
|
52401
52432
|
if (section) sections.push(section);
|
|
52402
52433
|
}
|
|
52403
52434
|
const body = sections.join("\n") || "[\u975E\u6587\u672C\u6D88\u606F]";
|
|
52404
|
-
const
|
|
52405
|
-
|
|
52406
|
-
|
|
52407
|
-
|
|
52408
|
-
|
|
52409
|
-
|
|
52410
|
-
|
|
52411
|
-
|
|
52412
|
-
|
|
52413
|
-
|
|
52414
|
-
|
|
52415
|
-
|
|
52416
|
-
|
|
52417
|
-
|
|
52418
|
-
|
|
52419
|
-
|
|
52420
|
-
|
|
52421
|
-
}
|
|
52422
|
-
let
|
|
52423
|
-
|
|
52424
|
-
|
|
52425
|
-
|
|
52426
|
-
|
|
52427
|
-
|
|
52428
|
-
|
|
52429
|
-
|
|
52430
|
-
|
|
52431
|
-
|
|
52432
|
-
|
|
52433
|
-
|
|
52434
|
-
|
|
52435
|
-
|
|
52435
|
+
const runTurn = (text) => {
|
|
52436
|
+
const turn = deps.manager.runLarkChatTurn({
|
|
52437
|
+
personaId: last.personaId,
|
|
52438
|
+
chatId: last.chatId,
|
|
52439
|
+
chatName: last.chatName,
|
|
52440
|
+
chatType: last.chatType ?? "group",
|
|
52441
|
+
senderOpenId: last.senderOpenId ?? "",
|
|
52442
|
+
senderName: last.senderName ?? last.senderOpenId ?? "\u672A\u77E5\u6210\u5458",
|
|
52443
|
+
text
|
|
52444
|
+
});
|
|
52445
|
+
activity.set(last.chatId, {
|
|
52446
|
+
chatId: last.chatId,
|
|
52447
|
+
chatName: last.chatName,
|
|
52448
|
+
lastActiveAt: Date.now(),
|
|
52449
|
+
personaId: last.personaId
|
|
52450
|
+
});
|
|
52451
|
+
return turn.ended;
|
|
52452
|
+
};
|
|
52453
|
+
let result = await runTurn(
|
|
52454
|
+
opts?.resume ? makeResumeText(body, speaker) : `${body}
|
|
52455
|
+
|
|
52456
|
+
${formatLarkSpeakerReminder(speaker)}`
|
|
52457
|
+
);
|
|
52458
|
+
for (let retry = 0; !("ok" in result); ) {
|
|
52459
|
+
if (deps.isShuttingDown?.()) {
|
|
52460
|
+
deps.logger?.info(`larkBot turn interrupted by shutdown (${last.chatId}), left for claim`);
|
|
52461
|
+
return;
|
|
52462
|
+
}
|
|
52463
|
+
if (result.error === STOPPED_ERROR) break;
|
|
52464
|
+
if (retry >= LOCAL_TURN_RETRIES) break;
|
|
52465
|
+
retry++;
|
|
52466
|
+
deps.logger?.warn(
|
|
52467
|
+
`larkBot turn error (${last.chatId}): ${result.error}; resume retry ${retry}/${LOCAL_TURN_RETRIES}`
|
|
52468
|
+
);
|
|
52469
|
+
result = await runTurn(makeResumeText(body, speaker));
|
|
52436
52470
|
}
|
|
52437
52471
|
const replyText = "ok" in result && result.ok ? result.replyText : ERROR_REPLY_TEXT;
|
|
52438
52472
|
if (!("ok" in result)) {
|
|
52439
|
-
deps.logger?.warn(
|
|
52440
|
-
`larkBot turn ${timedOut ? "timeout" : "error"} (${last.chatId}): ${result.error}`
|
|
52441
|
-
);
|
|
52473
|
+
deps.logger?.warn(`larkBot turn error (${last.chatId}): ${result.error}`);
|
|
52442
52474
|
}
|
|
52443
52475
|
const absorbed = envs.slice(0, -1).map((e) => e.eventId);
|
|
52444
52476
|
for (let attempt = 0; attempt <= replyDelays.length; attempt++) {
|
|
@@ -52460,11 +52492,11 @@ ${formatLarkSpeakerReminder(speaker)}`;
|
|
|
52460
52492
|
}
|
|
52461
52493
|
}
|
|
52462
52494
|
};
|
|
52463
|
-
const enqueueBatch = (batchEnvs) => {
|
|
52495
|
+
const enqueueBatch = (batchEnvs, opts) => {
|
|
52464
52496
|
const chatId = batchEnvs[0].chatId;
|
|
52465
52497
|
const prev = queues.get(chatId) ?? Promise.resolve();
|
|
52466
52498
|
const next = prev.then(
|
|
52467
|
-
() => runBatch(batchEnvs).catch(
|
|
52499
|
+
() => runBatch(batchEnvs, opts).catch(
|
|
52468
52500
|
(err) => deps.logger?.warn(`larkBot chat-router runBatch failed: ${err.message}`)
|
|
52469
52501
|
)
|
|
52470
52502
|
);
|
|
@@ -52501,15 +52533,26 @@ ${formatLarkSpeakerReminder(speaker)}`;
|
|
|
52501
52533
|
if (batch) {
|
|
52502
52534
|
batch.envelopes.push(env);
|
|
52503
52535
|
clearTimeout(batch.timer);
|
|
52504
|
-
batch.timer = setTimeout(() => flushPending(env.chatId),
|
|
52536
|
+
batch.timer = setTimeout(() => flushPending(env.chatId), debounceMs);
|
|
52505
52537
|
} else {
|
|
52506
52538
|
pending.set(env.chatId, {
|
|
52507
52539
|
envelopes: [env],
|
|
52508
52540
|
senderOpenId: sender,
|
|
52509
|
-
timer: setTimeout(() => flushPending(env.chatId),
|
|
52541
|
+
timer: setTimeout(() => flushPending(env.chatId), debounceMs)
|
|
52510
52542
|
});
|
|
52511
52543
|
}
|
|
52512
52544
|
},
|
|
52545
|
+
handleResume(envelopes) {
|
|
52546
|
+
const groups = /* @__PURE__ */ new Map();
|
|
52547
|
+
for (const env of envelopes) {
|
|
52548
|
+
if (env.type !== "message") continue;
|
|
52549
|
+
const key = `${env.chatId}|${env.senderOpenId ?? ""}`;
|
|
52550
|
+
const group = groups.get(key);
|
|
52551
|
+
if (group) group.push(env);
|
|
52552
|
+
else groups.set(key, [env]);
|
|
52553
|
+
}
|
|
52554
|
+
for (const group of groups.values()) enqueueBatch(group, { resume: true });
|
|
52555
|
+
},
|
|
52513
52556
|
listLarkSessions(personaId2) {
|
|
52514
52557
|
return [...activity.values()].filter((a) => a.personaId === personaId2).map((a) => ({ chatId: a.chatId, chatName: a.chatName, lastActiveAt: a.lastActiveAt }));
|
|
52515
52558
|
}
|
|
@@ -59269,7 +59312,7 @@ function computeMethodAccess(args) {
|
|
|
59269
59312
|
}
|
|
59270
59313
|
|
|
59271
59314
|
// src/version.ts
|
|
59272
|
-
var version = "0.2.
|
|
59315
|
+
var version = "0.2.305".length > 0 ? "0.2.305" : "dev";
|
|
59273
59316
|
|
|
59274
59317
|
// src/cli-probe/probe.ts
|
|
59275
59318
|
var fs56 = __toESM(require("fs"), 1);
|
|
@@ -63283,6 +63326,7 @@ async function startDaemon(config) {
|
|
|
63283
63326
|
deviceId: authFile.deviceId,
|
|
63284
63327
|
displayName: ownerDisplayName
|
|
63285
63328
|
});
|
|
63329
|
+
let larkDraining = false;
|
|
63286
63330
|
const larkChatRouter = createLarkChatRouter({
|
|
63287
63331
|
manager,
|
|
63288
63332
|
cloud: larkBotCloud,
|
|
@@ -63290,6 +63334,7 @@ async function startDaemon(config) {
|
|
|
63290
63334
|
fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
|
|
63291
63335
|
// 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
|
|
63292
63336
|
mediaRootFor: (chatId) => import_node_path65.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
|
|
63337
|
+
isShuttingDown: () => larkDraining,
|
|
63293
63338
|
logger: {
|
|
63294
63339
|
warn: (msg) => logger.warn(msg),
|
|
63295
63340
|
info: (msg) => logger.info(msg)
|
|
@@ -63558,11 +63603,11 @@ async function startDaemon(config) {
|
|
|
63558
63603
|
if (larkBotDeps.listBoundPersonaIds().length === 0) return;
|
|
63559
63604
|
try {
|
|
63560
63605
|
const items = await larkBotCloud.claimEvents();
|
|
63561
|
-
|
|
63562
|
-
|
|
63563
|
-
larkEventDedupe
|
|
63564
|
-
|
|
63565
|
-
}
|
|
63606
|
+
routeClaimedEvents(items, {
|
|
63607
|
+
router: larkChatRouter,
|
|
63608
|
+
dedupe: larkEventDedupe,
|
|
63609
|
+
nowSec: Math.floor(Date.now() / 1e3)
|
|
63610
|
+
});
|
|
63566
63611
|
if (items.length > 0) logger.info("larkBot pending events claimed", { count: items.length });
|
|
63567
63612
|
} catch (err) {
|
|
63568
63613
|
logger.warn("larkBot pending events claim failed", { err: err.message });
|
|
@@ -63638,6 +63683,7 @@ ${bar}
|
|
|
63638
63683
|
attachmentGcInterval.unref();
|
|
63639
63684
|
const shutdown = async () => {
|
|
63640
63685
|
logger.info("stopping clawd");
|
|
63686
|
+
larkDraining = true;
|
|
63641
63687
|
clearInterval(attachmentGcInterval);
|
|
63642
63688
|
observer.stopAll();
|
|
63643
63689
|
manager.stopAll();
|