@posthog/agent 2.3.388 → 2.3.401
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/adapters/claude/conversion/tool-use-to-acp.js.map +1 -1
- package/dist/adapters/claude/mcp/tool-metadata.d.ts +24 -0
- package/dist/adapters/claude/mcp/tool-metadata.js +165 -0
- package/dist/adapters/claude/mcp/tool-metadata.js.map +1 -0
- package/dist/adapters/claude/tools.js.map +1 -1
- package/dist/agent.js +113 -3
- package/dist/agent.js.map +1 -1
- package/dist/handoff-checkpoint.d.ts +1 -0
- package/dist/handoff-checkpoint.js +17 -1
- package/dist/handoff-checkpoint.js.map +1 -1
- package/dist/index.d.ts +7 -9
- package/dist/index.js.map +1 -1
- package/dist/posthog-api.js +5 -1
- package/dist/posthog-api.js.map +1 -1
- package/dist/server/agent-server.js +258 -101
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +248 -98
- package/dist/server/bin.cjs.map +1 -1
- package/dist/tree-tracker.js +128 -97
- package/dist/tree-tracker.js.map +1 -1
- package/package.json +7 -3
- package/src/adapters/claude/claude-agent.ts +5 -0
- package/src/adapters/claude/mcp/tool-metadata.test.ts +93 -0
- package/src/adapters/claude/mcp/tool-metadata.ts +33 -0
- package/src/adapters/claude/permissions/permission-handlers.test.ts +165 -0
- package/src/adapters/claude/permissions/permission-handlers.ts +105 -0
- package/src/adapters/claude/session/instructions.ts +9 -1
- package/src/adapters/claude/types.ts +2 -0
- package/src/handoff-checkpoint.test.ts +1 -0
- package/src/handoff-checkpoint.ts +17 -1
- package/src/sagas/apply-snapshot-saga.test.ts +1 -0
- package/src/sagas/apply-snapshot-saga.ts +68 -54
- package/src/sagas/capture-tree-saga.test.ts +18 -0
- package/src/sagas/capture-tree-saga.ts +64 -49
|
@@ -8605,7 +8605,7 @@ import { z as z4 } from "zod";
|
|
|
8605
8605
|
// package.json
|
|
8606
8606
|
var package_default = {
|
|
8607
8607
|
name: "@posthog/agent",
|
|
8608
|
-
version: "2.3.
|
|
8608
|
+
version: "2.3.401",
|
|
8609
8609
|
repository: "https://github.com/PostHog/code",
|
|
8610
8610
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
8611
8611
|
exports: {
|
|
@@ -8657,6 +8657,10 @@ var package_default = {
|
|
|
8657
8657
|
types: "./dist/adapters/reasoning-effort.d.ts",
|
|
8658
8658
|
import: "./dist/adapters/reasoning-effort.js"
|
|
8659
8659
|
},
|
|
8660
|
+
"./adapters/claude/mcp/tool-metadata": {
|
|
8661
|
+
types: "./dist/adapters/claude/mcp/tool-metadata.d.ts",
|
|
8662
|
+
import: "./dist/adapters/claude/mcp/tool-metadata.js"
|
|
8663
|
+
},
|
|
8660
8664
|
"./execution-mode": {
|
|
8661
8665
|
types: "./dist/execution-mode.d.ts",
|
|
8662
8666
|
import: "./dist/execution-mode.js"
|
|
@@ -13657,10 +13661,12 @@ async function fetchMcpToolMetadata(q, logger = new Logger({ debug: false, prefi
|
|
|
13657
13661
|
for (const tool of server.tools) {
|
|
13658
13662
|
const toolKey = buildToolKey(server.name, tool.name);
|
|
13659
13663
|
const readOnly = tool.annotations?.readOnly === true;
|
|
13664
|
+
const existing = mcpToolMetadataCache.get(toolKey);
|
|
13660
13665
|
mcpToolMetadataCache.set(toolKey, {
|
|
13661
13666
|
readOnly,
|
|
13662
13667
|
name: tool.name,
|
|
13663
|
-
description: tool.description
|
|
13668
|
+
description: tool.description,
|
|
13669
|
+
approvalState: existing?.approvalState
|
|
13664
13670
|
});
|
|
13665
13671
|
if (readOnly) readOnlyCount++;
|
|
13666
13672
|
}
|
|
@@ -13702,6 +13708,23 @@ function getConnectedMcpServerNames() {
|
|
|
13702
13708
|
}
|
|
13703
13709
|
return [...names];
|
|
13704
13710
|
}
|
|
13711
|
+
function getMcpToolApprovalState(toolName) {
|
|
13712
|
+
return mcpToolMetadataCache.get(toolName)?.approvalState;
|
|
13713
|
+
}
|
|
13714
|
+
function setMcpToolApprovalStates(approvals) {
|
|
13715
|
+
for (const [toolKey, approvalState] of Object.entries(approvals)) {
|
|
13716
|
+
const existing = mcpToolMetadataCache.get(toolKey);
|
|
13717
|
+
if (existing) {
|
|
13718
|
+
existing.approvalState = approvalState;
|
|
13719
|
+
} else {
|
|
13720
|
+
mcpToolMetadataCache.set(toolKey, {
|
|
13721
|
+
readOnly: false,
|
|
13722
|
+
name: toolKey,
|
|
13723
|
+
approvalState
|
|
13724
|
+
});
|
|
13725
|
+
}
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13705
13728
|
|
|
13706
13729
|
// src/adapters/claude/conversion/tool-use-to-acp.ts
|
|
13707
13730
|
var SYSTEM_REMINDER_REGEX = /\s*<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
@@ -15397,6 +15420,72 @@ async function handleDefaultPermissionFlow(context) {
|
|
|
15397
15420
|
return { behavior: "deny", message, interrupt: !feedback };
|
|
15398
15421
|
}
|
|
15399
15422
|
}
|
|
15423
|
+
function parseMcpToolName(toolName) {
|
|
15424
|
+
const parts2 = toolName.split("__");
|
|
15425
|
+
return {
|
|
15426
|
+
serverName: parts2[1] ?? toolName,
|
|
15427
|
+
tool: parts2.slice(2).join("__") || toolName
|
|
15428
|
+
};
|
|
15429
|
+
}
|
|
15430
|
+
async function handleMcpApprovalFlow(context) {
|
|
15431
|
+
const { toolName, toolInput, toolUseID, client, sessionId } = context;
|
|
15432
|
+
const { serverName, tool: displayTool } = parseMcpToolName(toolName);
|
|
15433
|
+
const metadata2 = getMcpToolMetadata(toolName);
|
|
15434
|
+
const description = metadata2?.description ? `
|
|
15435
|
+
|
|
15436
|
+
${metadata2.description}` : "";
|
|
15437
|
+
const response = await client.requestPermission({
|
|
15438
|
+
options: [
|
|
15439
|
+
{ kind: "allow_once", name: "Yes", optionId: "allow" },
|
|
15440
|
+
{
|
|
15441
|
+
kind: "allow_always",
|
|
15442
|
+
name: "Yes, always allow",
|
|
15443
|
+
optionId: "allow_always"
|
|
15444
|
+
},
|
|
15445
|
+
{
|
|
15446
|
+
kind: "reject_once",
|
|
15447
|
+
name: "Type here to tell the agent what to do differently",
|
|
15448
|
+
optionId: "reject",
|
|
15449
|
+
_meta: { customInput: true }
|
|
15450
|
+
}
|
|
15451
|
+
],
|
|
15452
|
+
sessionId,
|
|
15453
|
+
toolCall: {
|
|
15454
|
+
toolCallId: toolUseID,
|
|
15455
|
+
title: `The agent wants to call ${displayTool} (${serverName})`,
|
|
15456
|
+
kind: "other",
|
|
15457
|
+
content: description ? [{ type: "content", content: text(description) }] : [],
|
|
15458
|
+
rawInput: { ...toolInput, toolName }
|
|
15459
|
+
}
|
|
15460
|
+
});
|
|
15461
|
+
if (context.signal?.aborted || response.outcome?.outcome === "cancelled") {
|
|
15462
|
+
throw new Error("Tool use aborted");
|
|
15463
|
+
}
|
|
15464
|
+
if (response.outcome?.outcome === "selected" && (response.outcome.optionId === "allow" || response.outcome.optionId === "allow_always")) {
|
|
15465
|
+
if (response.outcome.optionId === "allow_always") {
|
|
15466
|
+
return {
|
|
15467
|
+
behavior: "allow",
|
|
15468
|
+
updatedInput: toolInput,
|
|
15469
|
+
updatedPermissions: [
|
|
15470
|
+
{
|
|
15471
|
+
type: "addRules",
|
|
15472
|
+
rules: [{ toolName }],
|
|
15473
|
+
behavior: "allow",
|
|
15474
|
+
destination: "localSettings"
|
|
15475
|
+
}
|
|
15476
|
+
]
|
|
15477
|
+
};
|
|
15478
|
+
}
|
|
15479
|
+
return {
|
|
15480
|
+
behavior: "allow",
|
|
15481
|
+
updatedInput: toolInput
|
|
15482
|
+
};
|
|
15483
|
+
}
|
|
15484
|
+
const feedback = response._meta?.customInput?.trim();
|
|
15485
|
+
const message = feedback ? `User refused permission to run tool with feedback: ${feedback}` : "User refused permission to run tool";
|
|
15486
|
+
await emitToolDenial(context, message);
|
|
15487
|
+
return { behavior: "deny", message, interrupt: !feedback };
|
|
15488
|
+
}
|
|
15400
15489
|
function handlePlanFileException(context) {
|
|
15401
15490
|
const { session, toolName, toolInput } = context;
|
|
15402
15491
|
if (session.permissionMode !== "plan" || !WRITE_TOOLS.has(toolName)) {
|
|
@@ -15467,6 +15556,17 @@ async function canUseTool(context) {
|
|
|
15467
15556
|
}
|
|
15468
15557
|
}
|
|
15469
15558
|
}
|
|
15559
|
+
if (toolName.startsWith("mcp__")) {
|
|
15560
|
+
const approvalState = getMcpToolApprovalState(toolName);
|
|
15561
|
+
if (approvalState === "do_not_use") {
|
|
15562
|
+
const message = "This tool has been blocked. To re-enable it, go to Settings > MCP Servers in PostHog Code.";
|
|
15563
|
+
await emitToolDenial(context, message);
|
|
15564
|
+
return { behavior: "deny", message, interrupt: false };
|
|
15565
|
+
}
|
|
15566
|
+
if (approvalState === "needs_approval") {
|
|
15567
|
+
return handleMcpApprovalFlow(context);
|
|
15568
|
+
}
|
|
15569
|
+
}
|
|
15470
15570
|
if (isToolAllowedForMode(toolName, session.permissionMode)) {
|
|
15471
15571
|
return {
|
|
15472
15572
|
behavior: "allow",
|
|
@@ -15679,7 +15779,14 @@ Only enter plan mode (EnterPlanMode) when the user is requesting a significant c
|
|
|
15679
15779
|
|
|
15680
15780
|
When in doubt, continue executing and incorporate the feedback inline.
|
|
15681
15781
|
`;
|
|
15682
|
-
var
|
|
15782
|
+
var MCP_TOOLS = `
|
|
15783
|
+
# MCP Tool Access
|
|
15784
|
+
|
|
15785
|
+
If an MCP tool call is explicitly denied with a message, relay that denial message to the user exactly as given. Do NOT suggest checking "Claude Code settings."
|
|
15786
|
+
|
|
15787
|
+
If an MCP tool call returns an error, treat it as a normal tool error \u2014 troubleshoot, retry, or inform the user about the specific error. Do NOT assume it is a permissions issue and do NOT direct the user to any settings page.
|
|
15788
|
+
`;
|
|
15789
|
+
var APPENDED_INSTRUCTIONS = BRANCH_NAMING + PLAN_MODE + MCP_TOOLS;
|
|
15683
15790
|
|
|
15684
15791
|
// src/adapters/claude/session/options.ts
|
|
15685
15792
|
function buildSystemPrompt(customPrompt) {
|
|
@@ -17008,6 +17115,9 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
17008
17115
|
const earlyModelId = settingsManager.getSettings().model || meta?.model || "";
|
|
17009
17116
|
const mcpServers = supportsMcpInjection(earlyModelId) ? parseMcpServers(params) : {};
|
|
17010
17117
|
const systemPrompt = buildSystemPrompt(meta?.systemPrompt);
|
|
17118
|
+
if (meta?.mcpToolApprovals) {
|
|
17119
|
+
setMcpToolApprovalStates(meta.mcpToolApprovals);
|
|
17120
|
+
}
|
|
17011
17121
|
const outputFormat = meta?.jsonSchema && this.options?.onStructuredOutput ? { type: "json_schema", schema: meta.jsonSchema } : void 0;
|
|
17012
17122
|
this.logger.debug(isResume ? "Resuming session" : "Creating new session", {
|
|
17013
17123
|
sessionId,
|
|
@@ -18334,7 +18444,14 @@ function createCodexConnection(config) {
|
|
|
18334
18444
|
}
|
|
18335
18445
|
|
|
18336
18446
|
// src/handoff-checkpoint.ts
|
|
18337
|
-
import {
|
|
18447
|
+
import {
|
|
18448
|
+
mkdir as mkdir4,
|
|
18449
|
+
readdir,
|
|
18450
|
+
readFile as readFile4,
|
|
18451
|
+
rm as rm4,
|
|
18452
|
+
rmdir,
|
|
18453
|
+
writeFile as writeFile2
|
|
18454
|
+
} from "fs/promises";
|
|
18338
18455
|
import { join as join9 } from "path";
|
|
18339
18456
|
|
|
18340
18457
|
// ../git/dist/handoff.js
|
|
@@ -19165,6 +19282,7 @@ var HandoffCheckpointTracker = class {
|
|
|
19165
19282
|
} finally {
|
|
19166
19283
|
await this.removeIfPresent(packPath);
|
|
19167
19284
|
await this.removeIfPresent(indexPath);
|
|
19285
|
+
await this.removeTmpDirIfEmpty(tmpDir);
|
|
19168
19286
|
}
|
|
19169
19287
|
}
|
|
19170
19288
|
toGitCheckpoint(checkpoint) {
|
|
@@ -19323,6 +19441,14 @@ var HandoffCheckpointTracker = class {
|
|
|
19323
19441
|
await rm4(filePath, { force: true }).catch(() => {
|
|
19324
19442
|
});
|
|
19325
19443
|
}
|
|
19444
|
+
async removeTmpDirIfEmpty(tmpDir) {
|
|
19445
|
+
const entries = await readdir(tmpDir).catch(() => null);
|
|
19446
|
+
if (!entries || entries.length > 0) {
|
|
19447
|
+
return;
|
|
19448
|
+
}
|
|
19449
|
+
await rmdir(tmpDir).catch(() => {
|
|
19450
|
+
});
|
|
19451
|
+
}
|
|
19326
19452
|
};
|
|
19327
19453
|
|
|
19328
19454
|
// src/utils/gateway.ts
|
|
@@ -20213,7 +20339,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
20213
20339
|
};
|
|
20214
20340
|
|
|
20215
20341
|
// src/sagas/apply-snapshot-saga.ts
|
|
20216
|
-
import { mkdir as mkdir7, rm as rm6, writeFile as writeFile5 } from "fs/promises";
|
|
20342
|
+
import { mkdir as mkdir7, readdir as readdir2, rm as rm6, rmdir as rmdir2, writeFile as writeFile5 } from "fs/promises";
|
|
20217
20343
|
import { join as join12 } from "path";
|
|
20218
20344
|
|
|
20219
20345
|
// ../git/dist/sagas/tree.js
|
|
@@ -20487,68 +20613,83 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
20487
20613
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
20488
20614
|
}
|
|
20489
20615
|
const archiveUrl = snapshot.archiveUrl;
|
|
20490
|
-
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
|
|
20494
|
-
|
|
20495
|
-
});
|
|
20496
|
-
const archivePath = join12(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
20497
|
-
this.archivePath = archivePath;
|
|
20498
|
-
await this.step({
|
|
20499
|
-
name: "download_archive",
|
|
20500
|
-
execute: async () => {
|
|
20501
|
-
const arrayBuffer = await apiClient.downloadArtifact(
|
|
20502
|
-
taskId,
|
|
20503
|
-
runId,
|
|
20504
|
-
archiveUrl
|
|
20505
|
-
);
|
|
20506
|
-
if (!arrayBuffer) {
|
|
20507
|
-
throw new Error("Failed to download archive");
|
|
20616
|
+
try {
|
|
20617
|
+
await this.step({
|
|
20618
|
+
name: "create_tmp_dir",
|
|
20619
|
+
execute: () => mkdir7(tmpDir, { recursive: true }),
|
|
20620
|
+
rollback: async () => {
|
|
20508
20621
|
}
|
|
20509
|
-
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
|
|
20517
|
-
|
|
20518
|
-
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20622
|
+
});
|
|
20623
|
+
const archivePath = join12(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
20624
|
+
this.archivePath = archivePath;
|
|
20625
|
+
await this.step({
|
|
20626
|
+
name: "download_archive",
|
|
20627
|
+
execute: async () => {
|
|
20628
|
+
const arrayBuffer = await apiClient.downloadArtifact(
|
|
20629
|
+
taskId,
|
|
20630
|
+
runId,
|
|
20631
|
+
archiveUrl
|
|
20632
|
+
);
|
|
20633
|
+
if (!arrayBuffer) {
|
|
20634
|
+
throw new Error("Failed to download archive");
|
|
20635
|
+
}
|
|
20636
|
+
const base64Content = Buffer.from(arrayBuffer).toString("utf-8");
|
|
20637
|
+
const binaryContent = Buffer.from(base64Content, "base64");
|
|
20638
|
+
await writeFile5(archivePath, binaryContent);
|
|
20639
|
+
this.log.info("Tree archive downloaded", {
|
|
20640
|
+
treeHash: snapshot.treeHash,
|
|
20641
|
+
snapshotBytes: binaryContent.byteLength,
|
|
20642
|
+
snapshotWireBytes: arrayBuffer.byteLength,
|
|
20643
|
+
totalBytes: binaryContent.byteLength,
|
|
20644
|
+
totalWireBytes: arrayBuffer.byteLength
|
|
20523
20645
|
});
|
|
20646
|
+
},
|
|
20647
|
+
rollback: async () => {
|
|
20648
|
+
if (this.archivePath) {
|
|
20649
|
+
await rm6(this.archivePath, { force: true }).catch(() => {
|
|
20650
|
+
});
|
|
20651
|
+
}
|
|
20524
20652
|
}
|
|
20653
|
+
});
|
|
20654
|
+
const gitApplySaga = new ApplyTreeSaga(this.log);
|
|
20655
|
+
const applyResult = await gitApplySaga.run({
|
|
20656
|
+
baseDir: repositoryPath,
|
|
20657
|
+
treeHash: snapshot.treeHash,
|
|
20658
|
+
baseCommit: snapshot.baseCommit,
|
|
20659
|
+
changes: snapshot.changes,
|
|
20660
|
+
archivePath: this.archivePath
|
|
20661
|
+
});
|
|
20662
|
+
if (!applyResult.success) {
|
|
20663
|
+
throw new Error(`Failed to apply tree: ${applyResult.error}`);
|
|
20525
20664
|
}
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20536
|
-
|
|
20665
|
+
this.log.info("Tree snapshot applied", {
|
|
20666
|
+
treeHash: snapshot.treeHash,
|
|
20667
|
+
totalChanges: snapshot.changes.length,
|
|
20668
|
+
deletedFiles: snapshot.changes.filter((c) => c.status === "D").length
|
|
20669
|
+
});
|
|
20670
|
+
return { treeHash: snapshot.treeHash };
|
|
20671
|
+
} finally {
|
|
20672
|
+
if (this.archivePath) {
|
|
20673
|
+
await rm6(this.archivePath, { force: true }).catch(() => {
|
|
20674
|
+
});
|
|
20675
|
+
}
|
|
20676
|
+
await this.removeTmpDirIfEmpty(tmpDir);
|
|
20677
|
+
this.archivePath = null;
|
|
20537
20678
|
}
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
|
|
20679
|
+
}
|
|
20680
|
+
async removeTmpDirIfEmpty(tmpDir) {
|
|
20681
|
+
const entries = await readdir2(tmpDir).catch(() => null);
|
|
20682
|
+
if (!entries || entries.length > 0) {
|
|
20683
|
+
return;
|
|
20684
|
+
}
|
|
20685
|
+
await rmdir2(tmpDir).catch(() => {
|
|
20544
20686
|
});
|
|
20545
|
-
return { treeHash: snapshot.treeHash };
|
|
20546
20687
|
}
|
|
20547
20688
|
};
|
|
20548
20689
|
|
|
20549
20690
|
// src/sagas/capture-tree-saga.ts
|
|
20550
20691
|
import { existsSync as existsSync6 } from "fs";
|
|
20551
|
-
import { readFile as readFile6, rm as rm7 } from "fs/promises";
|
|
20692
|
+
import { readdir as readdir3, readFile as readFile6, rm as rm7, rmdir as rmdir3 } from "fs/promises";
|
|
20552
20693
|
import { join as join13 } from "path";
|
|
20553
20694
|
var CaptureTreeSaga2 = class extends Saga {
|
|
20554
20695
|
sagaName = "CaptureTreeSaga";
|
|
@@ -20569,54 +20710,62 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
20569
20710
|
}
|
|
20570
20711
|
const shouldArchive = !!apiClient;
|
|
20571
20712
|
const archivePath = shouldArchive ? join13(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
20572
|
-
|
|
20573
|
-
|
|
20574
|
-
|
|
20575
|
-
|
|
20576
|
-
|
|
20577
|
-
|
|
20578
|
-
|
|
20579
|
-
|
|
20580
|
-
|
|
20581
|
-
|
|
20582
|
-
|
|
20583
|
-
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
20588
|
-
|
|
20589
|
-
|
|
20590
|
-
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20594
|
-
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
|
|
20598
|
-
|
|
20599
|
-
|
|
20600
|
-
|
|
20601
|
-
|
|
20713
|
+
try {
|
|
20714
|
+
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
20715
|
+
const captureResult = await gitCaptureSaga.run({
|
|
20716
|
+
baseDir: repositoryPath,
|
|
20717
|
+
lastTreeHash,
|
|
20718
|
+
archivePath
|
|
20719
|
+
});
|
|
20720
|
+
if (!captureResult.success) {
|
|
20721
|
+
throw new Error(`Failed to capture tree: ${captureResult.error}`);
|
|
20722
|
+
}
|
|
20723
|
+
const {
|
|
20724
|
+
snapshot: gitSnapshot,
|
|
20725
|
+
archivePath: createdArchivePath,
|
|
20726
|
+
changed
|
|
20727
|
+
} = captureResult.data;
|
|
20728
|
+
if (!changed || !gitSnapshot) {
|
|
20729
|
+
this.log.debug("No changes since last capture", { lastTreeHash });
|
|
20730
|
+
return { snapshot: null, newTreeHash: lastTreeHash };
|
|
20731
|
+
}
|
|
20732
|
+
let archiveUrl;
|
|
20733
|
+
if (apiClient && createdArchivePath) {
|
|
20734
|
+
try {
|
|
20735
|
+
archiveUrl = await this.uploadArchive(
|
|
20736
|
+
createdArchivePath,
|
|
20737
|
+
gitSnapshot.treeHash,
|
|
20738
|
+
apiClient,
|
|
20739
|
+
taskId,
|
|
20740
|
+
runId
|
|
20741
|
+
);
|
|
20742
|
+
} finally {
|
|
20743
|
+
await rm7(createdArchivePath, { force: true }).catch(() => {
|
|
20744
|
+
});
|
|
20745
|
+
}
|
|
20746
|
+
}
|
|
20747
|
+
const snapshot = {
|
|
20748
|
+
treeHash: gitSnapshot.treeHash,
|
|
20749
|
+
baseCommit: gitSnapshot.baseCommit,
|
|
20750
|
+
changes: gitSnapshot.changes,
|
|
20751
|
+
timestamp: gitSnapshot.timestamp,
|
|
20752
|
+
interrupted,
|
|
20753
|
+
archiveUrl
|
|
20754
|
+
};
|
|
20755
|
+
this.log.info("Tree captured", {
|
|
20756
|
+
treeHash: snapshot.treeHash,
|
|
20757
|
+
changes: snapshot.changes.length,
|
|
20758
|
+
interrupted,
|
|
20759
|
+
archiveUrl
|
|
20760
|
+
});
|
|
20761
|
+
return { snapshot, newTreeHash: snapshot.treeHash };
|
|
20762
|
+
} finally {
|
|
20763
|
+
if (archivePath) {
|
|
20764
|
+
await rm7(archivePath, { force: true }).catch(() => {
|
|
20602
20765
|
});
|
|
20603
20766
|
}
|
|
20767
|
+
await this.removeTmpDirIfEmpty(tmpDir);
|
|
20604
20768
|
}
|
|
20605
|
-
const snapshot = {
|
|
20606
|
-
treeHash: gitSnapshot.treeHash,
|
|
20607
|
-
baseCommit: gitSnapshot.baseCommit,
|
|
20608
|
-
changes: gitSnapshot.changes,
|
|
20609
|
-
timestamp: gitSnapshot.timestamp,
|
|
20610
|
-
interrupted,
|
|
20611
|
-
archiveUrl
|
|
20612
|
-
};
|
|
20613
|
-
this.log.info("Tree captured", {
|
|
20614
|
-
treeHash: snapshot.treeHash,
|
|
20615
|
-
changes: snapshot.changes.length,
|
|
20616
|
-
interrupted,
|
|
20617
|
-
archiveUrl
|
|
20618
|
-
});
|
|
20619
|
-
return { snapshot, newTreeHash: snapshot.treeHash };
|
|
20620
20769
|
}
|
|
20621
20770
|
async uploadArchive(archivePath, treeHash, apiClient, taskId, runId) {
|
|
20622
20771
|
const archiveUrl = await this.step({
|
|
@@ -20655,6 +20804,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
20655
20804
|
});
|
|
20656
20805
|
return archiveUrl;
|
|
20657
20806
|
}
|
|
20807
|
+
async removeTmpDirIfEmpty(tmpDir) {
|
|
20808
|
+
const entries = await readdir3(tmpDir).catch(() => null);
|
|
20809
|
+
if (!entries || entries.length > 0) {
|
|
20810
|
+
return;
|
|
20811
|
+
}
|
|
20812
|
+
await rmdir3(tmpDir).catch(() => {
|
|
20813
|
+
});
|
|
20814
|
+
}
|
|
20658
20815
|
};
|
|
20659
20816
|
|
|
20660
20817
|
// src/tree-tracker.ts
|