@vibe-lark/larkpal 0.1.77 → 0.1.78
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/main.mjs +153 -105
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -251,88 +251,98 @@ function getWorkspaceChatHistoryRoot(workspaceRoot = resolveWorkspaceRoot()) {
|
|
|
251
251
|
* 以及确保会话工作目录结构完整。
|
|
252
252
|
*/
|
|
253
253
|
const logger$10 = larkLogger("config/defaults");
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
timeout: 5
|
|
302
|
-
}] }],
|
|
303
|
-
Stop: [{ hooks: [{
|
|
304
|
-
type: "http",
|
|
305
|
-
url: "http://localhost:3000/hooks/stop",
|
|
306
|
-
timeout: 5
|
|
307
|
-
}] }],
|
|
308
|
-
PreToolUse: [{
|
|
309
|
-
matcher: "Read",
|
|
310
|
-
hooks: [{
|
|
254
|
+
function createDefaultSettings() {
|
|
255
|
+
const hookBaseUrl = getClaudeHookBaseUrl();
|
|
256
|
+
return {
|
|
257
|
+
permissions: {
|
|
258
|
+
allow: [
|
|
259
|
+
"Bash(*)",
|
|
260
|
+
"Read(*)",
|
|
261
|
+
"Write(*)",
|
|
262
|
+
"Edit(*)",
|
|
263
|
+
"MultiEdit(*)",
|
|
264
|
+
"Glob(*)",
|
|
265
|
+
"Grep(*)",
|
|
266
|
+
"LS(*)",
|
|
267
|
+
"TodoWrite(*)",
|
|
268
|
+
"WebFetch(*)",
|
|
269
|
+
"WebSearch(*)"
|
|
270
|
+
],
|
|
271
|
+
deny: [
|
|
272
|
+
"Write(//.claude/CLAUDE.md)",
|
|
273
|
+
"Edit(//.claude/CLAUDE.md)",
|
|
274
|
+
"Write(//.claude/settings.json)",
|
|
275
|
+
"Edit(//.claude/settings.json)",
|
|
276
|
+
"Read(//.lark-cli/**)",
|
|
277
|
+
"Read(//.config/lark/**)",
|
|
278
|
+
"Read(//.larkpal/credentials.json)",
|
|
279
|
+
"Read(//.env)",
|
|
280
|
+
"Read(//.env.*)",
|
|
281
|
+
"Bash(cat ~/.lark-cli:*)",
|
|
282
|
+
"Bash(cat ~/.config/lark:*)",
|
|
283
|
+
"Bash(cat ~/.larkpal/credentials:*)",
|
|
284
|
+
"Bash(head ~/.lark-cli:*)",
|
|
285
|
+
"Bash(head ~/.config/lark:*)",
|
|
286
|
+
"Bash(tail ~/.lark-cli:*)",
|
|
287
|
+
"Bash(tail ~/.config/lark:*)",
|
|
288
|
+
"Bash(less ~/.lark-cli:*)",
|
|
289
|
+
"Bash(less ~/.config/lark:*)",
|
|
290
|
+
"Bash(more ~/.lark-cli:*)",
|
|
291
|
+
"Bash(more ~/.config/lark:*)",
|
|
292
|
+
"Bash(env:*)",
|
|
293
|
+
"Bash(printenv:*)",
|
|
294
|
+
"Bash(export -p:*)",
|
|
295
|
+
"Bash(echo $LARK_APP_SECRET:*)",
|
|
296
|
+
"Bash(echo $ANTHROPIC_API_KEY:*)"
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
hooks: {
|
|
300
|
+
SessionStart: [{ hooks: [{
|
|
311
301
|
type: "http",
|
|
312
|
-
url:
|
|
302
|
+
url: `${hookBaseUrl}/hooks/session-start`,
|
|
313
303
|
timeout: 5
|
|
314
|
-
}]
|
|
315
|
-
|
|
316
|
-
PostToolUse: [{
|
|
317
|
-
matcher: "Read",
|
|
318
|
-
hooks: [{
|
|
304
|
+
}] }],
|
|
305
|
+
Stop: [{ hooks: [{
|
|
319
306
|
type: "http",
|
|
320
|
-
url:
|
|
307
|
+
url: `${hookBaseUrl}/hooks/stop`,
|
|
321
308
|
timeout: 5
|
|
322
|
-
}]
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
309
|
+
}] }],
|
|
310
|
+
PreToolUse: [{
|
|
311
|
+
matcher: "Read",
|
|
312
|
+
hooks: [{
|
|
313
|
+
type: "http",
|
|
314
|
+
url: `${hookBaseUrl}/hooks/pre-tool-use`,
|
|
315
|
+
timeout: 5
|
|
316
|
+
}]
|
|
317
|
+
}],
|
|
318
|
+
PostToolUse: [{
|
|
319
|
+
matcher: "Read",
|
|
320
|
+
hooks: [{
|
|
321
|
+
type: "http",
|
|
322
|
+
url: `${hookBaseUrl}/hooks/post-tool-use`,
|
|
323
|
+
timeout: 5
|
|
324
|
+
}]
|
|
325
|
+
}],
|
|
326
|
+
SessionEnd: [{ hooks: [{
|
|
327
|
+
type: "http",
|
|
328
|
+
url: `${hookBaseUrl}/hooks/session-end`,
|
|
329
|
+
timeout: 5
|
|
330
|
+
}] }],
|
|
331
|
+
Notification: [{ hooks: [{
|
|
332
|
+
type: "http",
|
|
333
|
+
url: `${hookBaseUrl}/hooks/notification`,
|
|
334
|
+
timeout: 5
|
|
335
|
+
}] }]
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function getClaudeHookBaseUrl() {
|
|
340
|
+
const explicit = process.env.LARKPAL_CC_HOOK_BASE_URL?.trim();
|
|
341
|
+
if (explicit) return explicit.replace(/\/+$/, "");
|
|
342
|
+
const port = process.env.LARKPAL_GATEWAY_PORT || "3000";
|
|
343
|
+
const rawHost = process.env.LARKPAL_GATEWAY_HOST || "localhost";
|
|
344
|
+
return `http://${rawHost === "0.0.0.0" || rawHost === "::" ? "127.0.0.1" : rawHost}:${port}`;
|
|
345
|
+
}
|
|
336
346
|
const DEFAULT_CLAUDE_MD = `# LarkPal
|
|
337
347
|
|
|
338
348
|
你是一个运行在飞书中的 AI 协作伙伴。这是你的初始状态。
|
|
@@ -430,7 +440,7 @@ async function ensureDefaults() {
|
|
|
430
440
|
const claudeMdPath = join(claudeDir, "CLAUDE.md");
|
|
431
441
|
await mkdir(claudeDir, { recursive: true });
|
|
432
442
|
logger$10.info("确保 ~/.claude 目录存在", { path: claudeDir });
|
|
433
|
-
await writeFile(settingsPath, JSON.stringify(
|
|
443
|
+
await writeFile(settingsPath, JSON.stringify(createDefaultSettings(), null, 2), "utf-8");
|
|
434
444
|
logger$10.info("settings.json 已同步(强制覆盖)", { path: settingsPath });
|
|
435
445
|
if (await fileExists(claudeMdPath)) logger$10.info("CLAUDE.md 已存在,跳过生成", { path: claudeMdPath });
|
|
436
446
|
else {
|
|
@@ -878,7 +888,7 @@ var CCStreamParser = class extends EventEmitter {
|
|
|
878
888
|
toolUseId: block?.id
|
|
879
889
|
});
|
|
880
890
|
if (block?.type === "tool_use" && block.name) {
|
|
881
|
-
this.recordToolUse(block.name, block.id);
|
|
891
|
+
if (!this.recordToolUse(block.name, block.id, block.input)) break;
|
|
882
892
|
log$37.info("工具调用开始", {
|
|
883
893
|
toolName: block.name,
|
|
884
894
|
displayName: getToolDisplayName(block.name),
|
|
@@ -926,7 +936,7 @@ var CCStreamParser = class extends EventEmitter {
|
|
|
926
936
|
});
|
|
927
937
|
if (Array.isArray(content)) {
|
|
928
938
|
for (const block of content) if (block.type === "tool_use" && block.name) {
|
|
929
|
-
this.recordToolUse(block.name, block.id);
|
|
939
|
+
if (!this.recordToolUse(block.name, block.id, block.input)) continue;
|
|
930
940
|
log$37.info("从 assistant 消息提取工具调用", {
|
|
931
941
|
toolName: block.name,
|
|
932
942
|
displayName: getToolDisplayName(block.name),
|
|
@@ -960,15 +970,19 @@ var CCStreamParser = class extends EventEmitter {
|
|
|
960
970
|
if (msg.subtype !== "init" || !Array.isArray(msg.tools)) return;
|
|
961
971
|
this.declaredTools = new Set(msg.tools);
|
|
962
972
|
}
|
|
963
|
-
recordToolUse(toolName, toolUseId) {
|
|
973
|
+
recordToolUse(toolName, toolUseId, toolInput) {
|
|
964
974
|
if (toolUseId) this.toolNamesById.set(toolUseId, toolName);
|
|
965
975
|
if (this.declaredTools && !this.declaredTools.has(toolName)) {
|
|
966
|
-
|
|
976
|
+
const availableTools = [...this.declaredTools];
|
|
977
|
+
log$37.warn("模型请求了未声明工具,已抑制正常 toolUseStart", {
|
|
967
978
|
toolName,
|
|
968
|
-
toolUseId
|
|
979
|
+
toolUseId,
|
|
980
|
+
availableTools
|
|
969
981
|
});
|
|
970
|
-
this.emit("unknownToolUse", toolName, toolUseId);
|
|
982
|
+
this.emit("unknownToolUse", toolName, toolUseId, toolInput, availableTools);
|
|
983
|
+
return false;
|
|
971
984
|
}
|
|
985
|
+
return true;
|
|
972
986
|
}
|
|
973
987
|
buildToolResultSummary(item, toolUseResult) {
|
|
974
988
|
const serializedContent = serializeToolResultContent(item.content);
|
|
@@ -1133,30 +1147,64 @@ async function sanitizeTranscriptEntry(entry, artifactRoot) {
|
|
|
1133
1147
|
let artifacts = 0;
|
|
1134
1148
|
const message = asObject$1(entry.message);
|
|
1135
1149
|
const content = message ? message.content : void 0;
|
|
1136
|
-
|
|
1150
|
+
const toolUseResult = asObject$1(entry.toolUseResult);
|
|
1151
|
+
let firstArtifactUri;
|
|
1152
|
+
if (Array.isArray(content)) for (let i = 0; i < content.length; i++) {
|
|
1153
|
+
const block = asObject$1(content[i]);
|
|
1154
|
+
if (!block) continue;
|
|
1155
|
+
if (block.type === "tool_result") {
|
|
1156
|
+
const toolUseId = typeof block.tool_use_id === "string" ? block.tool_use_id : void 0;
|
|
1157
|
+
const replacement = await sanitizeToolResultContent(block.content, artifactRoot, toolUseId);
|
|
1158
|
+
if (!replacement) continue;
|
|
1159
|
+
block.content = replacement.content;
|
|
1160
|
+
sanitizedImageBlocks += replacement.imageBlocks;
|
|
1161
|
+
artifacts += replacement.artifacts;
|
|
1162
|
+
firstArtifactUri ??= replacement.metadata[0]?.artifactUri;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
if (isBase64ImageBlock(block)) {
|
|
1166
|
+
const artifact = await writeImageArtifact(artifactRoot, block, void 0, i);
|
|
1167
|
+
content[i] = {
|
|
1168
|
+
type: "text",
|
|
1169
|
+
text: buildReplacementText([artifact])
|
|
1170
|
+
};
|
|
1171
|
+
sanitizedImageBlocks++;
|
|
1172
|
+
artifacts++;
|
|
1173
|
+
firstArtifactUri ??= artifact.artifactUri;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
const toolUseResultReplacement = await sanitizeToolUseResult(toolUseResult, artifactRoot, firstArtifactUri);
|
|
1177
|
+
if (toolUseResultReplacement) {
|
|
1178
|
+
sanitizedImageBlocks += toolUseResultReplacement.sanitizedImageBlocks;
|
|
1179
|
+
artifacts += toolUseResultReplacement.artifacts;
|
|
1180
|
+
}
|
|
1181
|
+
return {
|
|
1137
1182
|
sanitizedImageBlocks,
|
|
1138
1183
|
artifacts
|
|
1139
1184
|
};
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
file.
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
}
|
|
1185
|
+
}
|
|
1186
|
+
async function sanitizeToolUseResult(toolUseResult, artifactRoot, existingArtifactUri) {
|
|
1187
|
+
if (toolUseResult?.type !== "image") return null;
|
|
1188
|
+
const file = asObject$1(toolUseResult.file);
|
|
1189
|
+
if (!file || typeof file.base64 !== "string") return null;
|
|
1190
|
+
let artifactUri = existingArtifactUri;
|
|
1191
|
+
let artifacts = 0;
|
|
1192
|
+
if (!artifactUri) {
|
|
1193
|
+
artifactUri = (await writeImageArtifact(artifactRoot, {
|
|
1194
|
+
type: "image",
|
|
1195
|
+
source: {
|
|
1196
|
+
type: "base64",
|
|
1197
|
+
media_type: typeof file.type === "string" ? file.type : void 0,
|
|
1198
|
+
data: file.base64
|
|
1199
|
+
},
|
|
1200
|
+
filePath: typeof file.filePath === "string" ? file.filePath : void 0
|
|
1201
|
+
}, void 0, 0)).artifactUri;
|
|
1202
|
+
artifacts = 1;
|
|
1157
1203
|
}
|
|
1204
|
+
file.artifactUri = artifactUri;
|
|
1205
|
+
file.base64 = "[redacted by LarkPal: image artifact externalized]";
|
|
1158
1206
|
return {
|
|
1159
|
-
sanitizedImageBlocks,
|
|
1207
|
+
sanitizedImageBlocks: 1,
|
|
1160
1208
|
artifacts
|
|
1161
1209
|
};
|
|
1162
1210
|
}
|
|
@@ -1235,7 +1283,7 @@ function formatDimensions(block) {
|
|
|
1235
1283
|
return `${width}x${height}`;
|
|
1236
1284
|
}
|
|
1237
1285
|
function mightContainImagePayload(line) {
|
|
1238
|
-
return line.includes("\"
|
|
1286
|
+
return line.includes("\"base64\"") && (line.includes("\"image\"") || line.includes("\"source\""));
|
|
1239
1287
|
}
|
|
1240
1288
|
function asObject$1(value) {
|
|
1241
1289
|
return typeof value === "object" && value != null ? value : void 0;
|