@clawos-dev/clawd 0.2.296 → 0.2.297
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 +128 -42
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -52021,14 +52021,15 @@ function createLarkBotCloudClient(opts) {
|
|
|
52021
52021
|
const json = await request("POST", "/api/lark-bot/events/claim");
|
|
52022
52022
|
return json.items ?? [];
|
|
52023
52023
|
},
|
|
52024
|
-
async fetchResource(appId, messageId, fileKey, maxBytes) {
|
|
52024
|
+
async fetchResource(appId, messageId, fileKey, maxBytes, type) {
|
|
52025
52025
|
const ac = new AbortController();
|
|
52026
52026
|
const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? 15e3);
|
|
52027
52027
|
const qs = new URLSearchParams({
|
|
52028
52028
|
appId,
|
|
52029
52029
|
messageId,
|
|
52030
52030
|
fileKey,
|
|
52031
|
-
...maxBytes ? { maxBytes: String(maxBytes) } : {}
|
|
52031
|
+
...maxBytes ? { maxBytes: String(maxBytes) } : {},
|
|
52032
|
+
...type ? { type } : {}
|
|
52032
52033
|
}).toString();
|
|
52033
52034
|
let res;
|
|
52034
52035
|
try {
|
|
@@ -52063,6 +52064,7 @@ var import_node_path28 = __toESM(require("path"), 1);
|
|
|
52063
52064
|
init_protocol();
|
|
52064
52065
|
var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
|
|
52065
52066
|
var DEFAULT_TURN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
52067
|
+
var DEBOUNCE_MS = 1e3;
|
|
52066
52068
|
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
52067
52069
|
var EXT_BY_MIME = {
|
|
52068
52070
|
"image/png": "png",
|
|
@@ -52072,27 +52074,35 @@ var EXT_BY_MIME = {
|
|
|
52072
52074
|
};
|
|
52073
52075
|
var IMAGE_OVERSIZE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u8D85\u51FA\u5927\u5C0F\u9650\u5236]";
|
|
52074
52076
|
var IMAGE_UNAVAILABLE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u6682\u4E0D\u652F\u6301\u67E5\u770B]";
|
|
52077
|
+
var MAX_FILE_BYTES = 20 * 1024 * 1024;
|
|
52078
|
+
var FILE_OVERSIZE_TEXT = (name) => `[\u6587\u4EF6: ${name}\uFF0C\u8D85\u51FA\u5927\u5C0F\u9650\u5236]`;
|
|
52079
|
+
var FILE_UNAVAILABLE_TEXT = (name) => `[\u6587\u4EF6: ${name}\uFF0C\u62C9\u53D6\u5931\u8D25]`;
|
|
52075
52080
|
function safeMediaName(raw) {
|
|
52076
52081
|
const cleaned = raw.replace(/[^a-zA-Z0-9_\-.]/g, "_").replace(/^\.+/, (d) => "_".repeat(d.length));
|
|
52077
52082
|
return (cleaned || "msg").replace(/\.\./g, "__");
|
|
52078
52083
|
}
|
|
52084
|
+
function safeAttachmentName(raw) {
|
|
52085
|
+
const cleaned = raw.replace(/[/\\\u0000-\u001f]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, (d) => "_".repeat(d.length));
|
|
52086
|
+
if (!cleaned) return "file";
|
|
52087
|
+
if (cleaned.length <= 100) return cleaned;
|
|
52088
|
+
const dot = cleaned.lastIndexOf(".");
|
|
52089
|
+
const ext = dot > 0 && cleaned.length - dot <= 20 ? cleaned.slice(dot) : "";
|
|
52090
|
+
return cleaned.slice(0, 100 - ext.length) + ext;
|
|
52091
|
+
}
|
|
52079
52092
|
function createLarkChatRouter(deps) {
|
|
52080
52093
|
const timeoutMs = deps.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
|
|
52081
52094
|
const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
|
|
52082
52095
|
const sleep2 = deps.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
52096
|
+
const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
|
|
52083
52097
|
const queues = /* @__PURE__ */ new Map();
|
|
52084
52098
|
const activity = /* @__PURE__ */ new Map();
|
|
52085
|
-
const
|
|
52086
|
-
|
|
52087
|
-
deps.logger?.warn(`larkBot chat-router: message envelope missing messageId, drop ${env.eventId}`);
|
|
52088
|
-
return;
|
|
52089
|
-
}
|
|
52090
|
-
const speaker = env.senderName || env.senderOpenId || "\u672A\u77E5\u6210\u5458";
|
|
52099
|
+
const pending = /* @__PURE__ */ new Map();
|
|
52100
|
+
const buildSection = async (env) => {
|
|
52091
52101
|
const imageLines = [];
|
|
52092
52102
|
for (const [i, key] of (env.imageKeys ?? []).entries()) {
|
|
52093
52103
|
try {
|
|
52094
52104
|
if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
|
|
52095
|
-
const r = await deps.fetchResource(env.appId, env.messageId, key, MAX_IMAGE_BYTES);
|
|
52105
|
+
const r = await deps.fetchResource(env.appId, env.messageId, key, MAX_IMAGE_BYTES, "image");
|
|
52096
52106
|
if (r.data.byteLength > MAX_IMAGE_BYTES) {
|
|
52097
52107
|
deps.logger?.warn(`larkBot image oversize ${key}: ${r.data.byteLength}B`);
|
|
52098
52108
|
imageLines.push(IMAGE_OVERSIZE_TEXT);
|
|
@@ -52114,25 +52124,69 @@ function createLarkChatRouter(deps) {
|
|
|
52114
52124
|
imageLines.push(IMAGE_UNAVAILABLE_TEXT);
|
|
52115
52125
|
}
|
|
52116
52126
|
}
|
|
52117
|
-
const
|
|
52118
|
-
const
|
|
52127
|
+
const fileLines = [];
|
|
52128
|
+
for (const [i, f] of (env.files ?? []).entries()) {
|
|
52129
|
+
try {
|
|
52130
|
+
if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
|
|
52131
|
+
const r = await deps.fetchResource(env.appId, env.messageId, f.key, MAX_FILE_BYTES, "file");
|
|
52132
|
+
if (r.data.byteLength > MAX_FILE_BYTES) {
|
|
52133
|
+
deps.logger?.warn(`larkBot file oversize ${f.key}: ${r.data.byteLength}B`);
|
|
52134
|
+
fileLines.push(FILE_OVERSIZE_TEXT(f.name));
|
|
52135
|
+
continue;
|
|
52136
|
+
}
|
|
52137
|
+
const dir = deps.mediaRootFor(env.chatId);
|
|
52138
|
+
import_node_fs27.default.mkdirSync(dir, { recursive: true });
|
|
52139
|
+
const p2 = import_node_path28.default.join(dir, `${safeMediaName(env.messageId)}-${i}-${safeAttachmentName(f.name)}`);
|
|
52140
|
+
import_node_fs27.default.writeFileSync(p2, r.data);
|
|
52141
|
+
fileLines.push(`[\u6587\u4EF6: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
52142
|
+
} catch (err) {
|
|
52143
|
+
deps.logger?.warn(`larkBot file fetch failed ${f.key}: ${err.message}`);
|
|
52144
|
+
fileLines.push(FILE_UNAVAILABLE_TEXT(f.name));
|
|
52145
|
+
}
|
|
52146
|
+
}
|
|
52147
|
+
return [env.text?.trim(), ...imageLines, ...fileLines].filter(Boolean).join("\n");
|
|
52148
|
+
};
|
|
52149
|
+
const quoteAnnotation = (env, i, idxByMessageId) => {
|
|
52150
|
+
if (!env.parentMessageId) return void 0;
|
|
52151
|
+
const j = idxByMessageId.get(env.parentMessageId);
|
|
52152
|
+
if (j === void 0) return "[\u56DE\u590D\u66F4\u65E9\u7684\u4E00\u6761\u6D88\u606F]";
|
|
52153
|
+
if (j === i - 1) return void 0;
|
|
52154
|
+
return `[\u56DE\u590D\u7B2C${j + 1}\u6761]`;
|
|
52155
|
+
};
|
|
52156
|
+
const runBatch = async (batchEnvs) => {
|
|
52157
|
+
const envs = batchEnvs.filter((e) => {
|
|
52158
|
+
if (e.messageId) return true;
|
|
52159
|
+
deps.logger?.warn(`larkBot chat-router: message envelope missing messageId, drop ${e.eventId}`);
|
|
52160
|
+
return false;
|
|
52161
|
+
});
|
|
52162
|
+
if (envs.length === 0) return;
|
|
52163
|
+
const last = envs[envs.length - 1];
|
|
52164
|
+
const speaker = last.senderName || last.senderOpenId || "\u672A\u77E5\u6210\u5458";
|
|
52165
|
+
const idxByMessageId = new Map(envs.map((e, i) => [e.messageId, i]));
|
|
52166
|
+
const sections = [];
|
|
52167
|
+
for (const [i, env] of envs.entries()) {
|
|
52168
|
+
const body2 = await buildSection(env);
|
|
52169
|
+
const section = [quoteAnnotation(env, i, idxByMessageId), body2].filter(Boolean).join("\n");
|
|
52170
|
+
if (section) sections.push(section);
|
|
52171
|
+
}
|
|
52172
|
+
const body = sections.join("\n") || "[\u975E\u6587\u672C\u6D88\u606F]";
|
|
52119
52173
|
const text = `${body}
|
|
52120
52174
|
|
|
52121
52175
|
${formatLarkSpeakerReminder(speaker)}`;
|
|
52122
52176
|
const turn = deps.manager.runLarkChatTurn({
|
|
52123
|
-
personaId:
|
|
52124
|
-
chatId:
|
|
52125
|
-
chatName:
|
|
52126
|
-
chatType:
|
|
52127
|
-
senderOpenId:
|
|
52128
|
-
senderName:
|
|
52177
|
+
personaId: last.personaId,
|
|
52178
|
+
chatId: last.chatId,
|
|
52179
|
+
chatName: last.chatName,
|
|
52180
|
+
chatType: last.chatType ?? "group",
|
|
52181
|
+
senderOpenId: last.senderOpenId ?? "",
|
|
52182
|
+
senderName: last.senderName ?? last.senderOpenId ?? "\u672A\u77E5\u6210\u5458",
|
|
52129
52183
|
text
|
|
52130
52184
|
});
|
|
52131
|
-
activity.set(
|
|
52132
|
-
chatId:
|
|
52133
|
-
chatName:
|
|
52185
|
+
activity.set(last.chatId, {
|
|
52186
|
+
chatId: last.chatId,
|
|
52187
|
+
chatName: last.chatName,
|
|
52134
52188
|
lastActiveAt: Date.now(),
|
|
52135
|
-
personaId:
|
|
52189
|
+
personaId: last.personaId
|
|
52136
52190
|
});
|
|
52137
52191
|
let timedOut = false;
|
|
52138
52192
|
let timer;
|
|
@@ -52152,46 +52206,78 @@ ${formatLarkSpeakerReminder(speaker)}`;
|
|
|
52152
52206
|
const replyText = "ok" in result && result.ok ? result.replyText : ERROR_REPLY_TEXT;
|
|
52153
52207
|
if (!("ok" in result)) {
|
|
52154
52208
|
deps.logger?.warn(
|
|
52155
|
-
`larkBot turn ${timedOut ? "timeout" : "error"} (${
|
|
52209
|
+
`larkBot turn ${timedOut ? "timeout" : "error"} (${last.chatId}): ${result.error}`
|
|
52156
52210
|
);
|
|
52157
52211
|
}
|
|
52212
|
+
const absorbed = envs.slice(0, -1).map((e) => e.eventId);
|
|
52158
52213
|
for (let attempt = 0; attempt <= replyDelays.length; attempt++) {
|
|
52159
52214
|
if (attempt > 0) await sleep2(replyDelays[attempt - 1]);
|
|
52160
52215
|
try {
|
|
52161
52216
|
await deps.cloud.reply({
|
|
52162
|
-
appId:
|
|
52163
|
-
chatId:
|
|
52164
|
-
replyToMessageId:
|
|
52217
|
+
appId: last.appId,
|
|
52218
|
+
chatId: last.chatId,
|
|
52219
|
+
replyToMessageId: last.messageId,
|
|
52165
52220
|
text: replyText,
|
|
52166
|
-
eventId:
|
|
52221
|
+
eventId: last.eventId,
|
|
52222
|
+
...absorbed.length > 0 ? { absorbedEventIds: absorbed } : {}
|
|
52167
52223
|
});
|
|
52168
52224
|
return;
|
|
52169
52225
|
} catch (err) {
|
|
52170
52226
|
deps.logger?.warn(
|
|
52171
|
-
`larkBot reply attempt ${attempt + 1} failed (${
|
|
52227
|
+
`larkBot reply attempt ${attempt + 1} failed (${last.chatId}): ${err.message}`
|
|
52172
52228
|
);
|
|
52173
52229
|
}
|
|
52174
52230
|
}
|
|
52175
52231
|
};
|
|
52232
|
+
const enqueueBatch = (batchEnvs) => {
|
|
52233
|
+
const chatId = batchEnvs[0].chatId;
|
|
52234
|
+
const prev = queues.get(chatId) ?? Promise.resolve();
|
|
52235
|
+
const next = prev.then(
|
|
52236
|
+
() => runBatch(batchEnvs).catch(
|
|
52237
|
+
(err) => deps.logger?.warn(`larkBot chat-router runBatch failed: ${err.message}`)
|
|
52238
|
+
)
|
|
52239
|
+
);
|
|
52240
|
+
queues.set(chatId, next);
|
|
52241
|
+
void next.finally(() => {
|
|
52242
|
+
if (queues.get(chatId) === next) queues.delete(chatId);
|
|
52243
|
+
});
|
|
52244
|
+
};
|
|
52245
|
+
const flushPending = (chatId) => {
|
|
52246
|
+
const batch = pending.get(chatId);
|
|
52247
|
+
if (!batch) return;
|
|
52248
|
+
clearTimeout(batch.timer);
|
|
52249
|
+
pending.delete(chatId);
|
|
52250
|
+
enqueueBatch(batch.envelopes);
|
|
52251
|
+
};
|
|
52176
52252
|
return {
|
|
52177
52253
|
handleEnvelope(env) {
|
|
52178
52254
|
if (env.type === "bot_removed") {
|
|
52179
52255
|
deps.store.addRemovedChat(env.personaId, env.chatId);
|
|
52180
52256
|
activity.delete(env.chatId);
|
|
52181
52257
|
queues.delete(env.chatId);
|
|
52258
|
+
const p2 = pending.get(env.chatId);
|
|
52259
|
+
if (p2) {
|
|
52260
|
+
clearTimeout(p2.timer);
|
|
52261
|
+
pending.delete(env.chatId);
|
|
52262
|
+
}
|
|
52182
52263
|
deps.logger?.info(`larkBot removed from chat ${env.chatId} (persona ${env.personaId})`);
|
|
52183
52264
|
return;
|
|
52184
52265
|
}
|
|
52185
|
-
const
|
|
52186
|
-
const
|
|
52187
|
-
|
|
52188
|
-
|
|
52189
|
-
|
|
52190
|
-
|
|
52191
|
-
|
|
52192
|
-
|
|
52193
|
-
|
|
52194
|
-
|
|
52266
|
+
const sender = env.senderOpenId ?? "";
|
|
52267
|
+
const cur = pending.get(env.chatId);
|
|
52268
|
+
if (cur && cur.senderOpenId !== sender) flushPending(env.chatId);
|
|
52269
|
+
const batch = pending.get(env.chatId);
|
|
52270
|
+
if (batch) {
|
|
52271
|
+
batch.envelopes.push(env);
|
|
52272
|
+
clearTimeout(batch.timer);
|
|
52273
|
+
batch.timer = setTimeout(() => flushPending(env.chatId), debounceMs);
|
|
52274
|
+
} else {
|
|
52275
|
+
pending.set(env.chatId, {
|
|
52276
|
+
envelopes: [env],
|
|
52277
|
+
senderOpenId: sender,
|
|
52278
|
+
timer: setTimeout(() => flushPending(env.chatId), debounceMs)
|
|
52279
|
+
});
|
|
52280
|
+
}
|
|
52195
52281
|
},
|
|
52196
52282
|
listLarkSessions(personaId2) {
|
|
52197
52283
|
return [...activity.values()].filter((a) => a.personaId === personaId2).map((a) => ({ chatId: a.chatId, chatName: a.chatName, lastActiveAt: a.lastActiveAt }));
|
|
@@ -52934,7 +53020,7 @@ var import_node_fs28 = __toESM(require("fs"), 1);
|
|
|
52934
53020
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
52935
53021
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
52936
53022
|
init_protocol();
|
|
52937
|
-
var
|
|
53023
|
+
var MAX_FILE_BYTES2 = 2 * 1024 * 1024;
|
|
52938
53024
|
function resolveInsideCwd(cwd, subpath) {
|
|
52939
53025
|
const absCwd = import_node_path30.default.resolve(cwd);
|
|
52940
53026
|
const joined = import_node_path30.default.resolve(absCwd, subpath ?? ".");
|
|
@@ -52990,8 +53076,8 @@ var WorkspaceBrowser = class {
|
|
|
52990
53076
|
if (!st.isFile()) {
|
|
52991
53077
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
|
|
52992
53078
|
}
|
|
52993
|
-
if (st.size >
|
|
52994
|
-
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${
|
|
53079
|
+
if (st.size > MAX_FILE_BYTES2) {
|
|
53080
|
+
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES2} bytes`);
|
|
52995
53081
|
}
|
|
52996
53082
|
const buf = import_node_fs28.default.readFileSync(full);
|
|
52997
53083
|
const isBinary = buf.includes(0);
|
|
@@ -58950,7 +59036,7 @@ function computeMethodAccess(args) {
|
|
|
58950
59036
|
}
|
|
58951
59037
|
|
|
58952
59038
|
// src/version.ts
|
|
58953
|
-
var version = "0.2.
|
|
59039
|
+
var version = "0.2.297".length > 0 ? "0.2.297" : "dev";
|
|
58954
59040
|
|
|
58955
59041
|
// src/cli-probe/probe.ts
|
|
58956
59042
|
var fs56 = __toESM(require("fs"), 1);
|
|
@@ -62968,7 +63054,7 @@ async function startDaemon(config) {
|
|
|
62968
63054
|
manager,
|
|
62969
63055
|
cloud: larkBotCloud,
|
|
62970
63056
|
store: larkBotStore,
|
|
62971
|
-
fetchResource: (appId, messageId, fileKey, maxBytes) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes),
|
|
63057
|
+
fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
|
|
62972
63058
|
// 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
|
|
62973
63059
|
mediaRootFor: (chatId) => import_node_path65.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
|
|
62974
63060
|
logger: {
|