@vibe-lark/larkpal 0.1.78 → 0.1.80
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 +68 -23
- package/package.json +1 -1
package/dist/main.mjs
CHANGED
|
@@ -4583,6 +4583,8 @@ async function handleSessionFileUpload(req, res, messageStore) {
|
|
|
4583
4583
|
traceId,
|
|
4584
4584
|
runtimeEventType: "session-file-attached",
|
|
4585
4585
|
fileName: file.fileName,
|
|
4586
|
+
originalName: file.originalName,
|
|
4587
|
+
displayName: file.displayName,
|
|
4586
4588
|
relativePath: file.relativePath,
|
|
4587
4589
|
mimeType: file.mimeType,
|
|
4588
4590
|
size: file.size,
|
|
@@ -4613,6 +4615,7 @@ function parseSessionFileUpload(req, sessionId) {
|
|
|
4613
4615
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
4614
4616
|
const busboy = Busboy({
|
|
4615
4617
|
headers: req.headers,
|
|
4618
|
+
defParamCharset: "utf8",
|
|
4616
4619
|
limits: {
|
|
4617
4620
|
fileSize: getSessionFileUploadMaxBytes(),
|
|
4618
4621
|
files: 1
|
|
@@ -4676,6 +4679,7 @@ function writeSessionFile(sessionId, params) {
|
|
|
4676
4679
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
4677
4680
|
const uploadsDir = resolve(resolveAgentCwd(sessionId), "uploads");
|
|
4678
4681
|
const fileName = buildSafeUploadFileName(params.originalName);
|
|
4682
|
+
const originalName = buildDisplayUploadFileName(params.originalName);
|
|
4679
4683
|
const destination = resolve(uploadsDir, fileName);
|
|
4680
4684
|
const relativePath = `uploads/${fileName}`;
|
|
4681
4685
|
const maxBytes = getSessionFileUploadMaxBytes();
|
|
@@ -4713,6 +4717,8 @@ function writeSessionFile(sessionId, params) {
|
|
|
4713
4717
|
sessionId,
|
|
4714
4718
|
kind: "session-file",
|
|
4715
4719
|
fileName,
|
|
4720
|
+
originalName,
|
|
4721
|
+
displayName: originalName,
|
|
4716
4722
|
relativePath,
|
|
4717
4723
|
uri: `session-file://${relativePath}`,
|
|
4718
4724
|
mimeType: params.mimeType,
|
|
@@ -4737,6 +4743,9 @@ function buildSafeUploadFileName(originalName) {
|
|
|
4737
4743
|
const stem = basename(base, extname(base)).replace(/[^A-Za-z0-9._-]/g, "_").replace(/^\.+$/, "").slice(0, 80) || "upload";
|
|
4738
4744
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}-${stem}${extension}`;
|
|
4739
4745
|
}
|
|
4746
|
+
function buildDisplayUploadFileName(originalName) {
|
|
4747
|
+
return basename(originalName).replace(/[\0\r\n]/g, "_").trim() || "upload.bin";
|
|
4748
|
+
}
|
|
4740
4749
|
function isPathInside(parent, child) {
|
|
4741
4750
|
const path = relative(parent, child);
|
|
4742
4751
|
return path !== "" && !path.startsWith("..") && !isAbsolute(path);
|
|
@@ -7332,6 +7341,7 @@ function asObject(value) {
|
|
|
7332
7341
|
* 所有 hook 当前只做日志记录,不做复杂业务处理。
|
|
7333
7342
|
*/
|
|
7334
7343
|
const logger$5 = larkLogger("gateway/hooks");
|
|
7344
|
+
const DELAYED_SANITIZER_RETRY_MS = 1e3;
|
|
7335
7345
|
/**
|
|
7336
7346
|
* 创建 HTTP Hooks 路由
|
|
7337
7347
|
*/
|
|
@@ -7384,7 +7394,7 @@ function createHooksRouter() {
|
|
|
7384
7394
|
});
|
|
7385
7395
|
router.post("/post-tool-use", async (req, res) => {
|
|
7386
7396
|
const body = req.body;
|
|
7387
|
-
const transcriptPath =
|
|
7397
|
+
const transcriptPath = getTranscriptPath(body);
|
|
7388
7398
|
logger$5.info("hook received: post-tool-use", {
|
|
7389
7399
|
method: req.method,
|
|
7390
7400
|
url: req.originalUrl,
|
|
@@ -7392,36 +7402,28 @@ function createHooksRouter() {
|
|
|
7392
7402
|
toolName: body.tool_name,
|
|
7393
7403
|
transcriptPath
|
|
7394
7404
|
});
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
hookEventName: "PostToolUse",
|
|
7405
|
-
additionalContext: `LarkPal externalized ${result.sanitizedImageBlocks} image tool result(s) from the persisted Claude transcript to reduce future replay payload.`
|
|
7406
|
-
} : void 0
|
|
7407
|
-
});
|
|
7408
|
-
} catch (err) {
|
|
7409
|
-
logger$5.warn("post-tool-use transcript sanitizer failed", {
|
|
7410
|
-
transcriptPath,
|
|
7411
|
-
error: err instanceof Error ? err.message : String(err)
|
|
7412
|
-
});
|
|
7413
|
-
res.json({ continue: true });
|
|
7414
|
-
}
|
|
7405
|
+
const result = await sanitizeTranscriptFromHook("PostToolUse", transcriptPath);
|
|
7406
|
+
if (result?.changed === false) scheduleDelayedTranscriptSanitizer("PostToolUse", transcriptPath);
|
|
7407
|
+
res.json({
|
|
7408
|
+
continue: true,
|
|
7409
|
+
hookSpecificOutput: result?.changed ? {
|
|
7410
|
+
hookEventName: "PostToolUse",
|
|
7411
|
+
additionalContext: `LarkPal externalized ${result.sanitizedImageBlocks} image tool result(s) from the persisted Claude transcript to reduce future replay payload.`
|
|
7412
|
+
} : void 0
|
|
7413
|
+
});
|
|
7415
7414
|
});
|
|
7416
|
-
router.post("/session-end", (req, res) => {
|
|
7415
|
+
router.post("/session-end", async (req, res) => {
|
|
7417
7416
|
const body = req.body;
|
|
7417
|
+
const transcriptPath = getTranscriptPath(body);
|
|
7418
7418
|
logger$5.info("hook received: session-end", {
|
|
7419
7419
|
method: req.method,
|
|
7420
7420
|
url: req.originalUrl,
|
|
7421
7421
|
sessionId: body.session_id,
|
|
7422
|
+
transcriptPath,
|
|
7422
7423
|
body
|
|
7423
7424
|
});
|
|
7424
|
-
resetImageReadGuard(
|
|
7425
|
+
resetImageReadGuard(transcriptPath);
|
|
7426
|
+
if ((await sanitizeTranscriptFromHook("SessionEnd", transcriptPath))?.changed === false) scheduleDelayedTranscriptSanitizer("SessionEnd", transcriptPath);
|
|
7425
7427
|
res.json({ received: true });
|
|
7426
7428
|
});
|
|
7427
7429
|
router.post("/notification", (req, res) => {
|
|
@@ -7435,6 +7437,47 @@ function createHooksRouter() {
|
|
|
7435
7437
|
});
|
|
7436
7438
|
return router;
|
|
7437
7439
|
}
|
|
7440
|
+
function getTranscriptPath(body) {
|
|
7441
|
+
return typeof body.transcript_path === "string" ? body.transcript_path : typeof body.transcriptPath === "string" ? body.transcriptPath : void 0;
|
|
7442
|
+
}
|
|
7443
|
+
async function sanitizeTranscriptFromHook(hookEventName, transcriptPath) {
|
|
7444
|
+
if (!transcriptPath) {
|
|
7445
|
+
logger$5.info("transcript sanitizer skipped: no transcript path", { hookEventName });
|
|
7446
|
+
return;
|
|
7447
|
+
}
|
|
7448
|
+
if (!isTranscriptSanitizerEnabled()) {
|
|
7449
|
+
logger$5.info("transcript sanitizer skipped: disabled", {
|
|
7450
|
+
hookEventName,
|
|
7451
|
+
transcriptPath
|
|
7452
|
+
});
|
|
7453
|
+
return;
|
|
7454
|
+
}
|
|
7455
|
+
try {
|
|
7456
|
+
const result = await sanitizeClaudeTranscriptFile(transcriptPath);
|
|
7457
|
+
logger$5.info("transcript sanitizer completed", {
|
|
7458
|
+
hookEventName,
|
|
7459
|
+
transcriptPath,
|
|
7460
|
+
changed: result.changed,
|
|
7461
|
+
lines: result.lines,
|
|
7462
|
+
sanitizedImageBlocks: result.sanitizedImageBlocks,
|
|
7463
|
+
artifacts: result.artifacts
|
|
7464
|
+
});
|
|
7465
|
+
return result;
|
|
7466
|
+
} catch (err) {
|
|
7467
|
+
logger$5.warn("transcript sanitizer failed", {
|
|
7468
|
+
hookEventName,
|
|
7469
|
+
transcriptPath,
|
|
7470
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7471
|
+
});
|
|
7472
|
+
return;
|
|
7473
|
+
}
|
|
7474
|
+
}
|
|
7475
|
+
function scheduleDelayedTranscriptSanitizer(hookEventName, transcriptPath) {
|
|
7476
|
+
if (!transcriptPath || !isTranscriptSanitizerEnabled()) return;
|
|
7477
|
+
setTimeout(() => {
|
|
7478
|
+
sanitizeTranscriptFromHook(`${hookEventName}:delayed`, transcriptPath);
|
|
7479
|
+
}, DELAYED_SANITIZER_RETRY_MS).unref();
|
|
7480
|
+
}
|
|
7438
7481
|
//#endregion
|
|
7439
7482
|
//#region src/memory/store.ts
|
|
7440
7483
|
const log$27 = larkLogger("memory/store");
|
|
@@ -20139,6 +20182,8 @@ async function main() {
|
|
|
20139
20182
|
logger.info("LarkPal 启动中...");
|
|
20140
20183
|
if (process.env.LARKPAL_HEADLESS === "1" || process.env.LARKPAL_HEADLESS === "true") {
|
|
20141
20184
|
logger.info("Headless 模式启动 — 跳过飞书凭证/preflight/WebSocket");
|
|
20185
|
+
await ensureDefaults();
|
|
20186
|
+
logger.info("Headless 默认配置检查完成");
|
|
20142
20187
|
await startHeadless();
|
|
20143
20188
|
return;
|
|
20144
20189
|
}
|