@clawos-dev/clawd 0.2.298 → 0.2.300
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 +137 -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,8 @@ 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;
|
|
52068
|
+
var CONTENT_HOLD_MS = 6e4;
|
|
52066
52069
|
var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
|
|
52067
52070
|
var EXT_BY_MIME = {
|
|
52068
52071
|
"image/png": "png",
|
|
@@ -52072,27 +52075,43 @@ var EXT_BY_MIME = {
|
|
|
52072
52075
|
};
|
|
52073
52076
|
var IMAGE_OVERSIZE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u8D85\u51FA\u5927\u5C0F\u9650\u5236]";
|
|
52074
52077
|
var IMAGE_UNAVAILABLE_TEXT = "[\u56FE\u7247\u6D88\u606F\uFF0C\u6682\u4E0D\u652F\u6301\u67E5\u770B]";
|
|
52078
|
+
var MAX_FILE_BYTES = 20 * 1024 * 1024;
|
|
52079
|
+
var FILE_OVERSIZE_TEXT = (name) => `[\u6587\u4EF6: ${name}\uFF0C\u8D85\u51FA\u5927\u5C0F\u9650\u5236]`;
|
|
52080
|
+
var FILE_UNAVAILABLE_TEXT = (name) => `[\u6587\u4EF6: ${name}\uFF0C\u62C9\u53D6\u5931\u8D25]`;
|
|
52075
52081
|
function safeMediaName(raw) {
|
|
52076
52082
|
const cleaned = raw.replace(/[^a-zA-Z0-9_\-.]/g, "_").replace(/^\.+/, (d) => "_".repeat(d.length));
|
|
52077
52083
|
return (cleaned || "msg").replace(/\.\./g, "__");
|
|
52078
52084
|
}
|
|
52085
|
+
function safeAttachmentName(raw) {
|
|
52086
|
+
const cleaned = raw.replace(/[/\\\u0000-\u001f]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, (d) => "_".repeat(d.length));
|
|
52087
|
+
if (!cleaned) return "file";
|
|
52088
|
+
if (cleaned.length <= 100) return cleaned;
|
|
52089
|
+
const dot = cleaned.lastIndexOf(".");
|
|
52090
|
+
const ext = dot > 0 && cleaned.length - dot <= 20 ? cleaned.slice(dot) : "";
|
|
52091
|
+
return cleaned.slice(0, 100 - ext.length) + ext;
|
|
52092
|
+
}
|
|
52079
52093
|
function createLarkChatRouter(deps) {
|
|
52080
52094
|
const timeoutMs = deps.turnTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS;
|
|
52081
52095
|
const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
|
|
52082
52096
|
const sleep2 = deps.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
52097
|
+
const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
|
|
52098
|
+
const contentHoldMs = deps.contentHoldMs ?? CONTENT_HOLD_MS;
|
|
52099
|
+
const timerMsFor = (envs) => {
|
|
52100
|
+
if (envs.some((e) => e.text?.trim())) return debounceMs;
|
|
52101
|
+
const hasContent = envs.some(
|
|
52102
|
+
(e) => (e.imageKeys?.length ?? 0) > 0 || (e.files?.length ?? 0) > 0
|
|
52103
|
+
);
|
|
52104
|
+
return hasContent ? contentHoldMs : debounceMs;
|
|
52105
|
+
};
|
|
52083
52106
|
const queues = /* @__PURE__ */ new Map();
|
|
52084
52107
|
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";
|
|
52108
|
+
const pending = /* @__PURE__ */ new Map();
|
|
52109
|
+
const buildSection = async (env) => {
|
|
52091
52110
|
const imageLines = [];
|
|
52092
52111
|
for (const [i, key] of (env.imageKeys ?? []).entries()) {
|
|
52093
52112
|
try {
|
|
52094
52113
|
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);
|
|
52114
|
+
const r = await deps.fetchResource(env.appId, env.messageId, key, MAX_IMAGE_BYTES, "image");
|
|
52096
52115
|
if (r.data.byteLength > MAX_IMAGE_BYTES) {
|
|
52097
52116
|
deps.logger?.warn(`larkBot image oversize ${key}: ${r.data.byteLength}B`);
|
|
52098
52117
|
imageLines.push(IMAGE_OVERSIZE_TEXT);
|
|
@@ -52114,25 +52133,69 @@ function createLarkChatRouter(deps) {
|
|
|
52114
52133
|
imageLines.push(IMAGE_UNAVAILABLE_TEXT);
|
|
52115
52134
|
}
|
|
52116
52135
|
}
|
|
52117
|
-
const
|
|
52118
|
-
const
|
|
52136
|
+
const fileLines = [];
|
|
52137
|
+
for (const [i, f] of (env.files ?? []).entries()) {
|
|
52138
|
+
try {
|
|
52139
|
+
if (!deps.fetchResource || !deps.mediaRootFor) throw new Error("resource pipeline not wired");
|
|
52140
|
+
const r = await deps.fetchResource(env.appId, env.messageId, f.key, MAX_FILE_BYTES, "file");
|
|
52141
|
+
if (r.data.byteLength > MAX_FILE_BYTES) {
|
|
52142
|
+
deps.logger?.warn(`larkBot file oversize ${f.key}: ${r.data.byteLength}B`);
|
|
52143
|
+
fileLines.push(FILE_OVERSIZE_TEXT(f.name));
|
|
52144
|
+
continue;
|
|
52145
|
+
}
|
|
52146
|
+
const dir = deps.mediaRootFor(env.chatId);
|
|
52147
|
+
import_node_fs27.default.mkdirSync(dir, { recursive: true });
|
|
52148
|
+
const p2 = import_node_path28.default.join(dir, `${safeMediaName(env.messageId)}-${i}-${safeAttachmentName(f.name)}`);
|
|
52149
|
+
import_node_fs27.default.writeFileSync(p2, r.data);
|
|
52150
|
+
fileLines.push(`[\u6587\u4EF6: ${p2}]\uFF08\u7528 Read \u5DE5\u5177\u67E5\u770B\uFF09`);
|
|
52151
|
+
} catch (err) {
|
|
52152
|
+
deps.logger?.warn(`larkBot file fetch failed ${f.key}: ${err.message}`);
|
|
52153
|
+
fileLines.push(FILE_UNAVAILABLE_TEXT(f.name));
|
|
52154
|
+
}
|
|
52155
|
+
}
|
|
52156
|
+
return [env.text?.trim(), ...imageLines, ...fileLines].filter(Boolean).join("\n");
|
|
52157
|
+
};
|
|
52158
|
+
const quoteAnnotation = (env, i, idxByMessageId) => {
|
|
52159
|
+
if (!env.parentMessageId) return void 0;
|
|
52160
|
+
const j = idxByMessageId.get(env.parentMessageId);
|
|
52161
|
+
if (j === void 0) return "[\u56DE\u590D\u66F4\u65E9\u7684\u4E00\u6761\u6D88\u606F]";
|
|
52162
|
+
if (j === i - 1) return void 0;
|
|
52163
|
+
return `[\u56DE\u590D\u7B2C${j + 1}\u6761]`;
|
|
52164
|
+
};
|
|
52165
|
+
const runBatch = async (batchEnvs) => {
|
|
52166
|
+
const envs = batchEnvs.filter((e) => {
|
|
52167
|
+
if (e.messageId) return true;
|
|
52168
|
+
deps.logger?.warn(`larkBot chat-router: message envelope missing messageId, drop ${e.eventId}`);
|
|
52169
|
+
return false;
|
|
52170
|
+
});
|
|
52171
|
+
if (envs.length === 0) return;
|
|
52172
|
+
const last = envs[envs.length - 1];
|
|
52173
|
+
const speaker = last.senderName || last.senderOpenId || "\u672A\u77E5\u6210\u5458";
|
|
52174
|
+
const idxByMessageId = new Map(envs.map((e, i) => [e.messageId, i]));
|
|
52175
|
+
const sections = [];
|
|
52176
|
+
for (const [i, env] of envs.entries()) {
|
|
52177
|
+
const body2 = await buildSection(env);
|
|
52178
|
+
const section = [quoteAnnotation(env, i, idxByMessageId), body2].filter(Boolean).join("\n");
|
|
52179
|
+
if (section) sections.push(section);
|
|
52180
|
+
}
|
|
52181
|
+
const body = sections.join("\n") || "[\u975E\u6587\u672C\u6D88\u606F]";
|
|
52119
52182
|
const text = `${body}
|
|
52120
52183
|
|
|
52121
52184
|
${formatLarkSpeakerReminder(speaker)}`;
|
|
52122
52185
|
const turn = deps.manager.runLarkChatTurn({
|
|
52123
|
-
personaId:
|
|
52124
|
-
chatId:
|
|
52125
|
-
chatName:
|
|
52126
|
-
chatType:
|
|
52127
|
-
senderOpenId:
|
|
52128
|
-
senderName:
|
|
52186
|
+
personaId: last.personaId,
|
|
52187
|
+
chatId: last.chatId,
|
|
52188
|
+
chatName: last.chatName,
|
|
52189
|
+
chatType: last.chatType ?? "group",
|
|
52190
|
+
senderOpenId: last.senderOpenId ?? "",
|
|
52191
|
+
senderName: last.senderName ?? last.senderOpenId ?? "\u672A\u77E5\u6210\u5458",
|
|
52129
52192
|
text
|
|
52130
52193
|
});
|
|
52131
|
-
activity.set(
|
|
52132
|
-
chatId:
|
|
52133
|
-
chatName:
|
|
52194
|
+
activity.set(last.chatId, {
|
|
52195
|
+
chatId: last.chatId,
|
|
52196
|
+
chatName: last.chatName,
|
|
52134
52197
|
lastActiveAt: Date.now(),
|
|
52135
|
-
personaId:
|
|
52198
|
+
personaId: last.personaId
|
|
52136
52199
|
});
|
|
52137
52200
|
let timedOut = false;
|
|
52138
52201
|
let timer;
|
|
@@ -52152,46 +52215,78 @@ ${formatLarkSpeakerReminder(speaker)}`;
|
|
|
52152
52215
|
const replyText = "ok" in result && result.ok ? result.replyText : ERROR_REPLY_TEXT;
|
|
52153
52216
|
if (!("ok" in result)) {
|
|
52154
52217
|
deps.logger?.warn(
|
|
52155
|
-
`larkBot turn ${timedOut ? "timeout" : "error"} (${
|
|
52218
|
+
`larkBot turn ${timedOut ? "timeout" : "error"} (${last.chatId}): ${result.error}`
|
|
52156
52219
|
);
|
|
52157
52220
|
}
|
|
52221
|
+
const absorbed = envs.slice(0, -1).map((e) => e.eventId);
|
|
52158
52222
|
for (let attempt = 0; attempt <= replyDelays.length; attempt++) {
|
|
52159
52223
|
if (attempt > 0) await sleep2(replyDelays[attempt - 1]);
|
|
52160
52224
|
try {
|
|
52161
52225
|
await deps.cloud.reply({
|
|
52162
|
-
appId:
|
|
52163
|
-
chatId:
|
|
52164
|
-
replyToMessageId:
|
|
52226
|
+
appId: last.appId,
|
|
52227
|
+
chatId: last.chatId,
|
|
52228
|
+
replyToMessageId: last.messageId,
|
|
52165
52229
|
text: replyText,
|
|
52166
|
-
eventId:
|
|
52230
|
+
eventId: last.eventId,
|
|
52231
|
+
...absorbed.length > 0 ? { absorbedEventIds: absorbed } : {}
|
|
52167
52232
|
});
|
|
52168
52233
|
return;
|
|
52169
52234
|
} catch (err) {
|
|
52170
52235
|
deps.logger?.warn(
|
|
52171
|
-
`larkBot reply attempt ${attempt + 1} failed (${
|
|
52236
|
+
`larkBot reply attempt ${attempt + 1} failed (${last.chatId}): ${err.message}`
|
|
52172
52237
|
);
|
|
52173
52238
|
}
|
|
52174
52239
|
}
|
|
52175
52240
|
};
|
|
52241
|
+
const enqueueBatch = (batchEnvs) => {
|
|
52242
|
+
const chatId = batchEnvs[0].chatId;
|
|
52243
|
+
const prev = queues.get(chatId) ?? Promise.resolve();
|
|
52244
|
+
const next = prev.then(
|
|
52245
|
+
() => runBatch(batchEnvs).catch(
|
|
52246
|
+
(err) => deps.logger?.warn(`larkBot chat-router runBatch failed: ${err.message}`)
|
|
52247
|
+
)
|
|
52248
|
+
);
|
|
52249
|
+
queues.set(chatId, next);
|
|
52250
|
+
void next.finally(() => {
|
|
52251
|
+
if (queues.get(chatId) === next) queues.delete(chatId);
|
|
52252
|
+
});
|
|
52253
|
+
};
|
|
52254
|
+
const flushPending = (chatId) => {
|
|
52255
|
+
const batch = pending.get(chatId);
|
|
52256
|
+
if (!batch) return;
|
|
52257
|
+
clearTimeout(batch.timer);
|
|
52258
|
+
pending.delete(chatId);
|
|
52259
|
+
enqueueBatch(batch.envelopes);
|
|
52260
|
+
};
|
|
52176
52261
|
return {
|
|
52177
52262
|
handleEnvelope(env) {
|
|
52178
52263
|
if (env.type === "bot_removed") {
|
|
52179
52264
|
deps.store.addRemovedChat(env.personaId, env.chatId);
|
|
52180
52265
|
activity.delete(env.chatId);
|
|
52181
52266
|
queues.delete(env.chatId);
|
|
52267
|
+
const p2 = pending.get(env.chatId);
|
|
52268
|
+
if (p2) {
|
|
52269
|
+
clearTimeout(p2.timer);
|
|
52270
|
+
pending.delete(env.chatId);
|
|
52271
|
+
}
|
|
52182
52272
|
deps.logger?.info(`larkBot removed from chat ${env.chatId} (persona ${env.personaId})`);
|
|
52183
52273
|
return;
|
|
52184
52274
|
}
|
|
52185
|
-
const
|
|
52186
|
-
const
|
|
52187
|
-
|
|
52188
|
-
|
|
52189
|
-
|
|
52190
|
-
|
|
52191
|
-
|
|
52192
|
-
|
|
52193
|
-
|
|
52194
|
-
|
|
52275
|
+
const sender = env.senderOpenId ?? "";
|
|
52276
|
+
const cur = pending.get(env.chatId);
|
|
52277
|
+
if (cur && cur.senderOpenId !== sender) flushPending(env.chatId);
|
|
52278
|
+
const batch = pending.get(env.chatId);
|
|
52279
|
+
if (batch) {
|
|
52280
|
+
batch.envelopes.push(env);
|
|
52281
|
+
clearTimeout(batch.timer);
|
|
52282
|
+
batch.timer = setTimeout(() => flushPending(env.chatId), timerMsFor(batch.envelopes));
|
|
52283
|
+
} else {
|
|
52284
|
+
pending.set(env.chatId, {
|
|
52285
|
+
envelopes: [env],
|
|
52286
|
+
senderOpenId: sender,
|
|
52287
|
+
timer: setTimeout(() => flushPending(env.chatId), timerMsFor([env]))
|
|
52288
|
+
});
|
|
52289
|
+
}
|
|
52195
52290
|
},
|
|
52196
52291
|
listLarkSessions(personaId2) {
|
|
52197
52292
|
return [...activity.values()].filter((a) => a.personaId === personaId2).map((a) => ({ chatId: a.chatId, chatName: a.chatName, lastActiveAt: a.lastActiveAt }));
|
|
@@ -52934,7 +53029,7 @@ var import_node_fs28 = __toESM(require("fs"), 1);
|
|
|
52934
53029
|
var import_node_os8 = __toESM(require("os"), 1);
|
|
52935
53030
|
var import_node_path30 = __toESM(require("path"), 1);
|
|
52936
53031
|
init_protocol();
|
|
52937
|
-
var
|
|
53032
|
+
var MAX_FILE_BYTES2 = 2 * 1024 * 1024;
|
|
52938
53033
|
function resolveInsideCwd(cwd, subpath) {
|
|
52939
53034
|
const absCwd = import_node_path30.default.resolve(cwd);
|
|
52940
53035
|
const joined = import_node_path30.default.resolve(absCwd, subpath ?? ".");
|
|
@@ -52990,8 +53085,8 @@ var WorkspaceBrowser = class {
|
|
|
52990
53085
|
if (!st.isFile()) {
|
|
52991
53086
|
throw new ClawdError(ERROR_CODES.INVALID_PATH, `not a file: ${args.path}`);
|
|
52992
53087
|
}
|
|
52993
|
-
if (st.size >
|
|
52994
|
-
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${
|
|
53088
|
+
if (st.size > MAX_FILE_BYTES2) {
|
|
53089
|
+
throw new ClawdError(ERROR_CODES.FILE_TOO_LARGE, `file > ${MAX_FILE_BYTES2} bytes`);
|
|
52995
53090
|
}
|
|
52996
53091
|
const buf = import_node_fs28.default.readFileSync(full);
|
|
52997
53092
|
const isBinary = buf.includes(0);
|
|
@@ -58950,7 +59045,7 @@ function computeMethodAccess(args) {
|
|
|
58950
59045
|
}
|
|
58951
59046
|
|
|
58952
59047
|
// src/version.ts
|
|
58953
|
-
var version = "0.2.
|
|
59048
|
+
var version = "0.2.300".length > 0 ? "0.2.300" : "dev";
|
|
58954
59049
|
|
|
58955
59050
|
// src/cli-probe/probe.ts
|
|
58956
59051
|
var fs56 = __toESM(require("fs"), 1);
|
|
@@ -62968,7 +63063,7 @@ async function startDaemon(config) {
|
|
|
62968
63063
|
manager,
|
|
62969
63064
|
cloud: larkBotCloud,
|
|
62970
63065
|
store: larkBotStore,
|
|
62971
|
-
fetchResource: (appId, messageId, fileKey, maxBytes) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes),
|
|
63066
|
+
fetchResource: (appId, messageId, fileKey, maxBytes, type) => larkBotCloud.fetchResource(appId, messageId, fileKey, maxBytes, type),
|
|
62972
63067
|
// 图片落 guest userWorkDir 下(capId = lark-<chat_id>):CC 经 --add-dir + allowRead carve 可 Read
|
|
62973
63068
|
mediaRootFor: (chatId) => import_node_path65.default.join(deriveUserWorkDir(`lark-${chatId}`, usersRoot), "lark-media"),
|
|
62974
63069
|
logger: {
|