@posthog/agent 2.1.47 → 2.1.53
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/{agent-BJ7Uacyp.d.ts → agent-9gv5HohC.d.ts} +4 -2
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +91 -132
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +288 -327
- package/dist/index.js.map +1 -1
- package/dist/server/agent-server.js +286 -325
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +293 -332
- package/dist/server/bin.cjs.map +1 -1
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
- package/src/adapters/acp-connection.ts +1 -6
- package/src/adapters/claude/claude-agent.ts +39 -75
- package/src/agent.ts +1 -1
- package/src/session-log-writer.ts +50 -0
- package/src/types.ts +2 -0
package/dist/server/bin.cjs
CHANGED
|
@@ -805,10 +805,10 @@ var require_src2 = __commonJS({
|
|
|
805
805
|
var fs_1 = require("fs");
|
|
806
806
|
var debug_1 = __importDefault(require_src());
|
|
807
807
|
var log = debug_1.default("@kwsites/file-exists");
|
|
808
|
-
function check(
|
|
809
|
-
log(`checking %s`,
|
|
808
|
+
function check(path8, isFile, isDirectory) {
|
|
809
|
+
log(`checking %s`, path8);
|
|
810
810
|
try {
|
|
811
|
-
const stat = fs_1.statSync(
|
|
811
|
+
const stat = fs_1.statSync(path8);
|
|
812
812
|
if (stat.isFile() && isFile) {
|
|
813
813
|
log(`[OK] path represents a file`);
|
|
814
814
|
return true;
|
|
@@ -828,8 +828,8 @@ var require_src2 = __commonJS({
|
|
|
828
828
|
throw e;
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
|
-
function exists2(
|
|
832
|
-
return check(
|
|
831
|
+
function exists2(path8, type = exports2.READABLE) {
|
|
832
|
+
return check(path8, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
|
|
833
833
|
}
|
|
834
834
|
exports2.exists = exists2;
|
|
835
835
|
exports2.FILE = 1;
|
|
@@ -1170,174 +1170,12 @@ var os3 = __toESM(require("os"), 1);
|
|
|
1170
1170
|
var path3 = __toESM(require("path"), 1);
|
|
1171
1171
|
var import_sdk2 = require("@agentclientprotocol/sdk");
|
|
1172
1172
|
var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
1173
|
-
|
|
1174
|
-
// ../shared/dist/index.js
|
|
1175
|
-
var consoleLogger = {
|
|
1176
|
-
info: (_message, _data) => {
|
|
1177
|
-
},
|
|
1178
|
-
debug: (_message, _data) => {
|
|
1179
|
-
},
|
|
1180
|
-
error: (_message, _data) => {
|
|
1181
|
-
},
|
|
1182
|
-
warn: (_message, _data) => {
|
|
1183
|
-
}
|
|
1184
|
-
};
|
|
1185
|
-
var Saga = class {
|
|
1186
|
-
completedSteps = [];
|
|
1187
|
-
currentStepName = "unknown";
|
|
1188
|
-
stepTimings = [];
|
|
1189
|
-
log;
|
|
1190
|
-
constructor(logger) {
|
|
1191
|
-
this.log = logger ?? consoleLogger;
|
|
1192
|
-
}
|
|
1193
|
-
/**
|
|
1194
|
-
* Run the saga with the given input.
|
|
1195
|
-
* Returns a discriminated union result - either success with data or failure with error details.
|
|
1196
|
-
*/
|
|
1197
|
-
async run(input) {
|
|
1198
|
-
this.completedSteps = [];
|
|
1199
|
-
this.currentStepName = "unknown";
|
|
1200
|
-
this.stepTimings = [];
|
|
1201
|
-
const sagaStart = performance.now();
|
|
1202
|
-
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
1203
|
-
try {
|
|
1204
|
-
const result = await this.execute(input);
|
|
1205
|
-
const totalDuration = performance.now() - sagaStart;
|
|
1206
|
-
this.log.debug("Saga completed successfully", {
|
|
1207
|
-
sagaName: this.constructor.name,
|
|
1208
|
-
stepsCompleted: this.completedSteps.length,
|
|
1209
|
-
totalDurationMs: Math.round(totalDuration),
|
|
1210
|
-
stepTimings: this.stepTimings
|
|
1211
|
-
});
|
|
1212
|
-
return { success: true, data: result };
|
|
1213
|
-
} catch (error) {
|
|
1214
|
-
this.log.error("Saga failed, initiating rollback", {
|
|
1215
|
-
sagaName: this.constructor.name,
|
|
1216
|
-
failedStep: this.currentStepName,
|
|
1217
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1218
|
-
});
|
|
1219
|
-
await this.rollback();
|
|
1220
|
-
return {
|
|
1221
|
-
success: false,
|
|
1222
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1223
|
-
failedStep: this.currentStepName
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Execute a step with its rollback action.
|
|
1229
|
-
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
1230
|
-
* The step name is automatically tracked for error reporting.
|
|
1231
|
-
*
|
|
1232
|
-
* @param config - Step configuration with name, execute, and rollback functions
|
|
1233
|
-
* @returns The result of the execute function
|
|
1234
|
-
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
1235
|
-
*/
|
|
1236
|
-
async step(config) {
|
|
1237
|
-
this.currentStepName = config.name;
|
|
1238
|
-
this.log.debug(`Executing step: ${config.name}`);
|
|
1239
|
-
const stepStart = performance.now();
|
|
1240
|
-
const result = await config.execute();
|
|
1241
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
1242
|
-
this.stepTimings.push({ name: config.name, durationMs });
|
|
1243
|
-
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
1244
|
-
this.completedSteps.push({
|
|
1245
|
-
name: config.name,
|
|
1246
|
-
rollback: () => config.rollback(result)
|
|
1247
|
-
});
|
|
1248
|
-
return result;
|
|
1249
|
-
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Execute a step that doesn't need rollback.
|
|
1252
|
-
* Useful for read-only operations or operations that are idempotent.
|
|
1253
|
-
* The step name is automatically tracked for error reporting.
|
|
1254
|
-
*
|
|
1255
|
-
* @param name - Step name for logging and error tracking
|
|
1256
|
-
* @param execute - The action to execute
|
|
1257
|
-
* @returns The result of the execute function
|
|
1258
|
-
*/
|
|
1259
|
-
async readOnlyStep(name, execute) {
|
|
1260
|
-
this.currentStepName = name;
|
|
1261
|
-
this.log.debug(`Executing read-only step: ${name}`);
|
|
1262
|
-
const stepStart = performance.now();
|
|
1263
|
-
const result = await execute();
|
|
1264
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
1265
|
-
this.stepTimings.push({ name, durationMs });
|
|
1266
|
-
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
1267
|
-
return result;
|
|
1268
|
-
}
|
|
1269
|
-
/**
|
|
1270
|
-
* Roll back all completed steps in reverse order.
|
|
1271
|
-
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
1272
|
-
*/
|
|
1273
|
-
async rollback() {
|
|
1274
|
-
this.log.info("Rolling back saga", {
|
|
1275
|
-
stepsToRollback: this.completedSteps.length
|
|
1276
|
-
});
|
|
1277
|
-
const stepsReversed = [...this.completedSteps].reverse();
|
|
1278
|
-
for (const step of stepsReversed) {
|
|
1279
|
-
try {
|
|
1280
|
-
this.log.debug(`Rolling back step: ${step.name}`);
|
|
1281
|
-
await step.rollback();
|
|
1282
|
-
this.log.debug(`Step rolled back: ${step.name}`);
|
|
1283
|
-
} catch (error) {
|
|
1284
|
-
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
1285
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1286
|
-
});
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
this.log.info("Rollback completed", {
|
|
1290
|
-
stepsAttempted: this.completedSteps.length
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
/**
|
|
1294
|
-
* Get the number of completed steps (useful for testing)
|
|
1295
|
-
*/
|
|
1296
|
-
getCompletedStepCount() {
|
|
1297
|
-
return this.completedSteps.length;
|
|
1298
|
-
}
|
|
1299
|
-
};
|
|
1300
|
-
var NOOP_COLLECTOR = {
|
|
1301
|
-
time: (_label, fn) => fn(),
|
|
1302
|
-
timeSync: (_label, fn) => fn(),
|
|
1303
|
-
record: () => {
|
|
1304
|
-
},
|
|
1305
|
-
summarize: () => {
|
|
1306
|
-
}
|
|
1307
|
-
};
|
|
1308
|
-
function createTimingCollector(enabled, log) {
|
|
1309
|
-
if (!enabled) return NOOP_COLLECTOR;
|
|
1310
|
-
const steps = {};
|
|
1311
|
-
return {
|
|
1312
|
-
async time(label, fn) {
|
|
1313
|
-
const start = Date.now();
|
|
1314
|
-
const result = await fn();
|
|
1315
|
-
steps[label] = Date.now() - start;
|
|
1316
|
-
return result;
|
|
1317
|
-
},
|
|
1318
|
-
timeSync(label, fn) {
|
|
1319
|
-
const start = Date.now();
|
|
1320
|
-
const result = fn();
|
|
1321
|
-
steps[label] = Date.now() - start;
|
|
1322
|
-
return result;
|
|
1323
|
-
},
|
|
1324
|
-
record(label, ms) {
|
|
1325
|
-
steps[label] = ms;
|
|
1326
|
-
},
|
|
1327
|
-
summarize(label) {
|
|
1328
|
-
const total = Object.values(steps).reduce((a, b) => a + b, 0);
|
|
1329
|
-
log(`[timing] ${label}: ${total}ms`, steps);
|
|
1330
|
-
}
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
1333
|
-
|
|
1334
|
-
// src/adapters/claude/claude-agent.ts
|
|
1335
1173
|
var import_uuid = require("uuid");
|
|
1336
1174
|
|
|
1337
1175
|
// package.json
|
|
1338
1176
|
var package_default = {
|
|
1339
1177
|
name: "@posthog/agent",
|
|
1340
|
-
version: "2.1.
|
|
1178
|
+
version: "2.1.53",
|
|
1341
1179
|
repository: "https://github.com/PostHog/twig",
|
|
1342
1180
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
1343
1181
|
exports: {
|
|
@@ -1715,8 +1553,8 @@ var ToolContentBuilder = class {
|
|
|
1715
1553
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1716
1554
|
return this;
|
|
1717
1555
|
}
|
|
1718
|
-
diff(
|
|
1719
|
-
this.items.push({ type: "diff", path:
|
|
1556
|
+
diff(path8, oldText, newText) {
|
|
1557
|
+
this.items.push({ type: "diff", path: path8, oldText, newText });
|
|
1720
1558
|
return this;
|
|
1721
1559
|
}
|
|
1722
1560
|
build() {
|
|
@@ -1906,13 +1744,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1906
1744
|
locations: []
|
|
1907
1745
|
};
|
|
1908
1746
|
case "Edit": {
|
|
1909
|
-
const
|
|
1747
|
+
const path8 = input?.file_path ? String(input.file_path) : void 0;
|
|
1910
1748
|
let oldText = input?.old_string ? String(input.old_string) : null;
|
|
1911
1749
|
let newText = input?.new_string ? String(input.new_string) : "";
|
|
1912
1750
|
let affectedLines = [];
|
|
1913
|
-
if (
|
|
1751
|
+
if (path8 && oldText) {
|
|
1914
1752
|
try {
|
|
1915
|
-
const oldContent = cachedFileContent[
|
|
1753
|
+
const oldContent = cachedFileContent[path8] || "";
|
|
1916
1754
|
const newContent = replaceAndCalculateLocation(oldContent, [
|
|
1917
1755
|
{
|
|
1918
1756
|
oldText,
|
|
@@ -1928,17 +1766,17 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1928
1766
|
}
|
|
1929
1767
|
}
|
|
1930
1768
|
return {
|
|
1931
|
-
title:
|
|
1769
|
+
title: path8 ? `Edit \`${path8}\`` : "Edit",
|
|
1932
1770
|
kind: "edit",
|
|
1933
|
-
content: input &&
|
|
1771
|
+
content: input && path8 ? [
|
|
1934
1772
|
{
|
|
1935
1773
|
type: "diff",
|
|
1936
|
-
path:
|
|
1774
|
+
path: path8,
|
|
1937
1775
|
oldText,
|
|
1938
1776
|
newText
|
|
1939
1777
|
}
|
|
1940
1778
|
] : [],
|
|
1941
|
-
locations:
|
|
1779
|
+
locations: path8 ? affectedLines.length > 0 ? affectedLines.map((line) => ({ line, path: path8 })) : [{ path: path8 }] : []
|
|
1942
1780
|
};
|
|
1943
1781
|
}
|
|
1944
1782
|
case "Write": {
|
|
@@ -3412,12 +3250,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3412
3250
|
logWriter;
|
|
3413
3251
|
options;
|
|
3414
3252
|
lastSentConfigOptions;
|
|
3415
|
-
debug;
|
|
3416
3253
|
constructor(client, logWriter, options) {
|
|
3417
3254
|
super(client);
|
|
3418
3255
|
this.logWriter = logWriter;
|
|
3419
3256
|
this.options = options;
|
|
3420
|
-
this.debug = options?.debug ?? false;
|
|
3421
3257
|
this.toolUseCache = {};
|
|
3422
3258
|
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
3423
3259
|
}
|
|
@@ -3460,36 +3296,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3460
3296
|
}
|
|
3461
3297
|
async newSession(params) {
|
|
3462
3298
|
this.checkAuthStatus();
|
|
3463
|
-
const tc = createTimingCollector(
|
|
3464
|
-
this.debug,
|
|
3465
|
-
(msg, data) => this.logger.info(msg, data)
|
|
3466
|
-
);
|
|
3467
3299
|
const meta = params._meta;
|
|
3468
3300
|
const sessionId = (0, import_uuid.v7)();
|
|
3469
3301
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3470
|
-
const mcpServers =
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
isResume: false,
|
|
3486
|
-
onModeChange: this.createOnModeChange(sessionId),
|
|
3487
|
-
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3488
|
-
onProcessExited: this.options?.onProcessExited
|
|
3489
|
-
})
|
|
3490
|
-
);
|
|
3302
|
+
const mcpServers = parseMcpServers(params);
|
|
3303
|
+
const options = buildSessionOptions({
|
|
3304
|
+
cwd: params.cwd,
|
|
3305
|
+
mcpServers,
|
|
3306
|
+
permissionMode,
|
|
3307
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
3308
|
+
logger: this.logger,
|
|
3309
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3310
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3311
|
+
sessionId,
|
|
3312
|
+
isResume: false,
|
|
3313
|
+
onModeChange: this.createOnModeChange(sessionId),
|
|
3314
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3315
|
+
onProcessExited: this.options?.onProcessExited
|
|
3316
|
+
});
|
|
3491
3317
|
const input = new Pushable();
|
|
3492
|
-
const q =
|
|
3318
|
+
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3493
3319
|
const session = this.createSession(
|
|
3494
3320
|
sessionId,
|
|
3495
3321
|
q,
|
|
@@ -3501,27 +3327,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3501
3327
|
session.taskRunId = meta?.taskRunId;
|
|
3502
3328
|
this.registerPersistence(sessionId, meta);
|
|
3503
3329
|
if (meta?.taskRunId) {
|
|
3504
|
-
await
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
adapter: "claude"
|
|
3510
|
-
})
|
|
3511
|
-
);
|
|
3330
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
3331
|
+
taskRunId: meta.taskRunId,
|
|
3332
|
+
sessionId,
|
|
3333
|
+
adapter: "claude"
|
|
3334
|
+
});
|
|
3512
3335
|
}
|
|
3513
|
-
const modelOptions = await
|
|
3514
|
-
|
|
3515
|
-
() => this.getModelConfigOptions()
|
|
3516
|
-
);
|
|
3517
|
-
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
3336
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
3337
|
+
this.deferBackgroundFetches(q, sessionId, mcpServers);
|
|
3518
3338
|
session.modelId = modelOptions.currentModelId;
|
|
3519
3339
|
await this.trySetModel(q, modelOptions.currentModelId);
|
|
3520
|
-
const configOptions = await
|
|
3521
|
-
"buildConfigOptions",
|
|
3522
|
-
() => this.buildConfigOptions(modelOptions)
|
|
3523
|
-
);
|
|
3524
|
-
tc.summarize("newSession");
|
|
3340
|
+
const configOptions = await this.buildConfigOptions(modelOptions);
|
|
3525
3341
|
return {
|
|
3526
3342
|
sessionId,
|
|
3527
3343
|
configOptions
|
|
@@ -3531,10 +3347,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3531
3347
|
return this.resumeSession(params);
|
|
3532
3348
|
}
|
|
3533
3349
|
async resumeSession(params) {
|
|
3534
|
-
const tc = createTimingCollector(
|
|
3535
|
-
this.debug,
|
|
3536
|
-
(msg, data) => this.logger.info(msg, data)
|
|
3537
|
-
);
|
|
3538
3350
|
const meta = params._meta;
|
|
3539
3351
|
const sessionId = meta?.sessionId;
|
|
3540
3352
|
if (!sessionId) {
|
|
@@ -3543,32 +3355,22 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3543
3355
|
if (this.sessionId === sessionId) {
|
|
3544
3356
|
return {};
|
|
3545
3357
|
}
|
|
3546
|
-
const mcpServers =
|
|
3547
|
-
"parseMcpServers",
|
|
3548
|
-
() => parseMcpServers(params)
|
|
3549
|
-
);
|
|
3358
|
+
const mcpServers = parseMcpServers(params);
|
|
3550
3359
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3551
|
-
const { query: q, session } = await
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3562
|
-
})
|
|
3563
|
-
);
|
|
3360
|
+
const { query: q, session } = await this.initializeQuery({
|
|
3361
|
+
cwd: params.cwd,
|
|
3362
|
+
permissionMode,
|
|
3363
|
+
mcpServers,
|
|
3364
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3365
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3366
|
+
sessionId,
|
|
3367
|
+
isResume: true,
|
|
3368
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3369
|
+
});
|
|
3564
3370
|
session.taskRunId = meta?.taskRunId;
|
|
3565
3371
|
this.registerPersistence(sessionId, meta);
|
|
3566
|
-
this.deferBackgroundFetches(
|
|
3567
|
-
const configOptions = await
|
|
3568
|
-
"buildConfigOptions",
|
|
3569
|
-
() => this.buildConfigOptions()
|
|
3570
|
-
);
|
|
3571
|
-
tc.summarize("resumeSession");
|
|
3372
|
+
this.deferBackgroundFetches(q, sessionId, mcpServers);
|
|
3373
|
+
const configOptions = await this.buildConfigOptions();
|
|
3572
3374
|
return { configOptions };
|
|
3573
3375
|
}
|
|
3574
3376
|
async prompt(params) {
|
|
@@ -3756,13 +3558,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3756
3558
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3757
3559
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
3758
3560
|
*/
|
|
3759
|
-
deferBackgroundFetches(
|
|
3561
|
+
deferBackgroundFetches(q, sessionId, mcpServers) {
|
|
3760
3562
|
Promise.all([
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
"mcpMetadata",
|
|
3764
|
-
() => fetchMcpToolMetadata(mcpServers, this.logger)
|
|
3765
|
-
)
|
|
3563
|
+
getAvailableSlashCommands(q),
|
|
3564
|
+
fetchMcpToolMetadata(mcpServers, this.logger)
|
|
3766
3565
|
]).then(([slashCommands]) => {
|
|
3767
3566
|
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
3768
3567
|
}).catch((err) => {
|
|
@@ -4056,10 +3855,7 @@ function createClaudeConnection(config) {
|
|
|
4056
3855
|
const agentStream = (0, import_sdk3.ndJsonStream)(agentWritable, streams.agent.readable);
|
|
4057
3856
|
let agent = null;
|
|
4058
3857
|
const agentConnection = new import_sdk3.AgentSideConnection((client) => {
|
|
4059
|
-
agent = new ClaudeAcpAgent(client, logWriter,
|
|
4060
|
-
...config.processCallbacks,
|
|
4061
|
-
debug: config.debug
|
|
4062
|
-
});
|
|
3858
|
+
agent = new ClaudeAcpAgent(client, logWriter, config.processCallbacks);
|
|
4063
3859
|
logger.info(`Created ${agent.adapterName} agent`);
|
|
4064
3860
|
return agent;
|
|
4065
3861
|
}, agentStream);
|
|
@@ -4474,6 +4270,8 @@ var PostHogAPIClient = class {
|
|
|
4474
4270
|
};
|
|
4475
4271
|
|
|
4476
4272
|
// src/session-log-writer.ts
|
|
4273
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
4274
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
4477
4275
|
var SessionLogWriter = class _SessionLogWriter {
|
|
4478
4276
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
4479
4277
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -4487,8 +4285,10 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4487
4285
|
sessions = /* @__PURE__ */ new Map();
|
|
4488
4286
|
messageCounts = /* @__PURE__ */ new Map();
|
|
4489
4287
|
logger;
|
|
4288
|
+
localCachePath;
|
|
4490
4289
|
constructor(options = {}) {
|
|
4491
4290
|
this.posthogAPI = options.posthogAPI;
|
|
4291
|
+
this.localCachePath = options.localCachePath;
|
|
4492
4292
|
this.logger = options.logger ?? new Logger({ debug: false, prefix: "[SessionLogWriter]" });
|
|
4493
4293
|
}
|
|
4494
4294
|
async flushAll() {
|
|
@@ -4518,6 +4318,21 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4518
4318
|
});
|
|
4519
4319
|
this.sessions.set(sessionId, { context });
|
|
4520
4320
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
4321
|
+
if (this.localCachePath) {
|
|
4322
|
+
const sessionDir = import_node_path.default.join(
|
|
4323
|
+
this.localCachePath,
|
|
4324
|
+
"sessions",
|
|
4325
|
+
context.runId
|
|
4326
|
+
);
|
|
4327
|
+
try {
|
|
4328
|
+
import_node_fs2.default.mkdirSync(sessionDir, { recursive: true });
|
|
4329
|
+
} catch (error) {
|
|
4330
|
+
this.logger.warn("Failed to create local cache directory", {
|
|
4331
|
+
sessionDir,
|
|
4332
|
+
error
|
|
4333
|
+
});
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4521
4336
|
}
|
|
4522
4337
|
isRegistered(sessionId) {
|
|
4523
4338
|
return this.sessions.has(sessionId);
|
|
@@ -4555,6 +4370,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4555
4370
|
timestamp,
|
|
4556
4371
|
notification: message
|
|
4557
4372
|
};
|
|
4373
|
+
this.writeToLocalCache(sessionId, entry);
|
|
4558
4374
|
if (this.posthogAPI) {
|
|
4559
4375
|
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
4560
4376
|
pending.push(entry);
|
|
@@ -4655,6 +4471,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4655
4471
|
}
|
|
4656
4472
|
}
|
|
4657
4473
|
};
|
|
4474
|
+
this.writeToLocalCache(sessionId, entry);
|
|
4658
4475
|
if (this.posthogAPI) {
|
|
4659
4476
|
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
4660
4477
|
pending.push(entry);
|
|
@@ -4682,11 +4499,28 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4682
4499
|
const timeout = setTimeout(() => this.flush(sessionId), delay2);
|
|
4683
4500
|
this.flushTimeouts.set(sessionId, timeout);
|
|
4684
4501
|
}
|
|
4502
|
+
writeToLocalCache(sessionId, entry) {
|
|
4503
|
+
if (!this.localCachePath) return;
|
|
4504
|
+
const session = this.sessions.get(sessionId);
|
|
4505
|
+
if (!session) return;
|
|
4506
|
+
const logPath = import_node_path.default.join(
|
|
4507
|
+
this.localCachePath,
|
|
4508
|
+
"sessions",
|
|
4509
|
+
session.context.runId,
|
|
4510
|
+
"logs.ndjson"
|
|
4511
|
+
);
|
|
4512
|
+
try {
|
|
4513
|
+
import_node_fs2.default.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
4514
|
+
`);
|
|
4515
|
+
} catch (error) {
|
|
4516
|
+
this.logger.warn("Failed to write to local cache", { logPath, error });
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4685
4519
|
};
|
|
4686
4520
|
|
|
4687
4521
|
// ../git/dist/queries.js
|
|
4688
|
-
var
|
|
4689
|
-
var
|
|
4522
|
+
var fs5 = __toESM(require("fs/promises"), 1);
|
|
4523
|
+
var path6 = __toESM(require("path"), 1);
|
|
4690
4524
|
|
|
4691
4525
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4692
4526
|
var import_node_buffer = require("buffer");
|
|
@@ -4694,7 +4528,7 @@ var import_file_exists = __toESM(require_dist(), 1);
|
|
|
4694
4528
|
var import_debug = __toESM(require_src(), 1);
|
|
4695
4529
|
var import_child_process = require("child_process");
|
|
4696
4530
|
var import_promise_deferred = __toESM(require_dist2(), 1);
|
|
4697
|
-
var
|
|
4531
|
+
var import_node_path2 = require("path");
|
|
4698
4532
|
var import_promise_deferred2 = __toESM(require_dist2(), 1);
|
|
4699
4533
|
var import_node_events = require("events");
|
|
4700
4534
|
var __defProp2 = Object.defineProperty;
|
|
@@ -4725,8 +4559,8 @@ function pathspec(...paths) {
|
|
|
4725
4559
|
cache.set(key, paths);
|
|
4726
4560
|
return key;
|
|
4727
4561
|
}
|
|
4728
|
-
function isPathSpec(
|
|
4729
|
-
return
|
|
4562
|
+
function isPathSpec(path8) {
|
|
4563
|
+
return path8 instanceof String && cache.has(path8);
|
|
4730
4564
|
}
|
|
4731
4565
|
function toPaths(pathSpec) {
|
|
4732
4566
|
return cache.get(pathSpec) || [];
|
|
@@ -4815,8 +4649,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4815
4649
|
function forEachLineWithContent(input, callback) {
|
|
4816
4650
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4817
4651
|
}
|
|
4818
|
-
function folderExists(
|
|
4819
|
-
return (0, import_file_exists.exists)(
|
|
4652
|
+
function folderExists(path8) {
|
|
4653
|
+
return (0, import_file_exists.exists)(path8, import_file_exists.FOLDER);
|
|
4820
4654
|
}
|
|
4821
4655
|
function append(target, item) {
|
|
4822
4656
|
if (Array.isArray(target)) {
|
|
@@ -5220,8 +5054,8 @@ function checkIsRepoRootTask() {
|
|
|
5220
5054
|
commands,
|
|
5221
5055
|
format: "utf-8",
|
|
5222
5056
|
onError,
|
|
5223
|
-
parser(
|
|
5224
|
-
return /^\.(git)?$/.test(
|
|
5057
|
+
parser(path8) {
|
|
5058
|
+
return /^\.(git)?$/.test(path8.trim());
|
|
5225
5059
|
}
|
|
5226
5060
|
};
|
|
5227
5061
|
}
|
|
@@ -5655,11 +5489,11 @@ function parseGrep(grep) {
|
|
|
5655
5489
|
const paths = /* @__PURE__ */ new Set();
|
|
5656
5490
|
const results = {};
|
|
5657
5491
|
forEachLineWithContent(grep, (input) => {
|
|
5658
|
-
const [
|
|
5659
|
-
paths.add(
|
|
5660
|
-
(results[
|
|
5492
|
+
const [path8, line, preview] = input.split(NULL);
|
|
5493
|
+
paths.add(path8);
|
|
5494
|
+
(results[path8] = results[path8] || []).push({
|
|
5661
5495
|
line: asNumber(line),
|
|
5662
|
-
path:
|
|
5496
|
+
path: path8,
|
|
5663
5497
|
preview
|
|
5664
5498
|
});
|
|
5665
5499
|
});
|
|
@@ -6424,14 +6258,14 @@ var init_hash_object = __esm({
|
|
|
6424
6258
|
init_task();
|
|
6425
6259
|
}
|
|
6426
6260
|
});
|
|
6427
|
-
function parseInit(bare,
|
|
6261
|
+
function parseInit(bare, path8, text2) {
|
|
6428
6262
|
const response = String(text2).trim();
|
|
6429
6263
|
let result;
|
|
6430
6264
|
if (result = initResponseRegex.exec(response)) {
|
|
6431
|
-
return new InitSummary(bare,
|
|
6265
|
+
return new InitSummary(bare, path8, false, result[1]);
|
|
6432
6266
|
}
|
|
6433
6267
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6434
|
-
return new InitSummary(bare,
|
|
6268
|
+
return new InitSummary(bare, path8, true, result[1]);
|
|
6435
6269
|
}
|
|
6436
6270
|
let gitDir = "";
|
|
6437
6271
|
const tokens = response.split(" ");
|
|
@@ -6442,7 +6276,7 @@ function parseInit(bare, path7, text2) {
|
|
|
6442
6276
|
break;
|
|
6443
6277
|
}
|
|
6444
6278
|
}
|
|
6445
|
-
return new InitSummary(bare,
|
|
6279
|
+
return new InitSummary(bare, path8, /^re/i.test(response), gitDir);
|
|
6446
6280
|
}
|
|
6447
6281
|
var InitSummary;
|
|
6448
6282
|
var initResponseRegex;
|
|
@@ -6451,9 +6285,9 @@ var init_InitSummary = __esm({
|
|
|
6451
6285
|
"src/lib/responses/InitSummary.ts"() {
|
|
6452
6286
|
"use strict";
|
|
6453
6287
|
InitSummary = class {
|
|
6454
|
-
constructor(bare,
|
|
6288
|
+
constructor(bare, path8, existing, gitDir) {
|
|
6455
6289
|
this.bare = bare;
|
|
6456
|
-
this.path =
|
|
6290
|
+
this.path = path8;
|
|
6457
6291
|
this.existing = existing;
|
|
6458
6292
|
this.gitDir = gitDir;
|
|
6459
6293
|
}
|
|
@@ -6465,7 +6299,7 @@ var init_InitSummary = __esm({
|
|
|
6465
6299
|
function hasBareCommand(command) {
|
|
6466
6300
|
return command.includes(bareCommand);
|
|
6467
6301
|
}
|
|
6468
|
-
function initTask(bare = false,
|
|
6302
|
+
function initTask(bare = false, path8, customArgs) {
|
|
6469
6303
|
const commands = ["init", ...customArgs];
|
|
6470
6304
|
if (bare && !hasBareCommand(commands)) {
|
|
6471
6305
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6474,7 +6308,7 @@ function initTask(bare = false, path7, customArgs) {
|
|
|
6474
6308
|
commands,
|
|
6475
6309
|
format: "utf-8",
|
|
6476
6310
|
parser(text2) {
|
|
6477
|
-
return parseInit(commands.includes("--bare"),
|
|
6311
|
+
return parseInit(commands.includes("--bare"), path8, text2);
|
|
6478
6312
|
}
|
|
6479
6313
|
};
|
|
6480
6314
|
}
|
|
@@ -7290,12 +7124,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7290
7124
|
"use strict";
|
|
7291
7125
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7292
7126
|
FileStatusSummary = class {
|
|
7293
|
-
constructor(
|
|
7294
|
-
this.path =
|
|
7127
|
+
constructor(path8, index, working_dir) {
|
|
7128
|
+
this.path = path8;
|
|
7295
7129
|
this.index = index;
|
|
7296
7130
|
this.working_dir = working_dir;
|
|
7297
7131
|
if (index === "R" || working_dir === "R") {
|
|
7298
|
-
const detail = fromPathRegex.exec(
|
|
7132
|
+
const detail = fromPathRegex.exec(path8) || [null, path8, path8];
|
|
7299
7133
|
this.from = detail[2] || "";
|
|
7300
7134
|
this.path = detail[1] || "";
|
|
7301
7135
|
}
|
|
@@ -7326,14 +7160,14 @@ function splitLine(result, lineStr) {
|
|
|
7326
7160
|
default:
|
|
7327
7161
|
return;
|
|
7328
7162
|
}
|
|
7329
|
-
function data(index, workingDir,
|
|
7163
|
+
function data(index, workingDir, path8) {
|
|
7330
7164
|
const raw = `${index}${workingDir}`;
|
|
7331
7165
|
const handler = parsers6.get(raw);
|
|
7332
7166
|
if (handler) {
|
|
7333
|
-
handler(result,
|
|
7167
|
+
handler(result, path8);
|
|
7334
7168
|
}
|
|
7335
7169
|
if (raw !== "##" && raw !== "!!") {
|
|
7336
|
-
result.files.push(new FileStatusSummary(
|
|
7170
|
+
result.files.push(new FileStatusSummary(path8, index, workingDir));
|
|
7337
7171
|
}
|
|
7338
7172
|
}
|
|
7339
7173
|
}
|
|
@@ -7646,9 +7480,9 @@ var init_simple_git_api = __esm({
|
|
|
7646
7480
|
next
|
|
7647
7481
|
);
|
|
7648
7482
|
}
|
|
7649
|
-
hashObject(
|
|
7483
|
+
hashObject(path8, write) {
|
|
7650
7484
|
return this._runTask(
|
|
7651
|
-
hashObjectTask(
|
|
7485
|
+
hashObjectTask(path8, write === true),
|
|
7652
7486
|
trailingFunctionArgument(arguments)
|
|
7653
7487
|
);
|
|
7654
7488
|
}
|
|
@@ -8001,8 +7835,8 @@ var init_branch = __esm({
|
|
|
8001
7835
|
}
|
|
8002
7836
|
});
|
|
8003
7837
|
function toPath(input) {
|
|
8004
|
-
const
|
|
8005
|
-
return
|
|
7838
|
+
const path8 = input.trim().replace(/^["']|["']$/g, "");
|
|
7839
|
+
return path8 && (0, import_node_path2.normalize)(path8);
|
|
8006
7840
|
}
|
|
8007
7841
|
var parseCheckIgnore;
|
|
8008
7842
|
var init_CheckIgnore = __esm({
|
|
@@ -8316,8 +8150,8 @@ __export(sub_module_exports, {
|
|
|
8316
8150
|
subModuleTask: () => subModuleTask,
|
|
8317
8151
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8318
8152
|
});
|
|
8319
|
-
function addSubModuleTask(repo,
|
|
8320
|
-
return subModuleTask(["add", repo,
|
|
8153
|
+
function addSubModuleTask(repo, path8) {
|
|
8154
|
+
return subModuleTask(["add", repo, path8]);
|
|
8321
8155
|
}
|
|
8322
8156
|
function initSubModuleTask(customArgs) {
|
|
8323
8157
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8647,8 +8481,8 @@ var require_git = __commonJS2({
|
|
|
8647
8481
|
}
|
|
8648
8482
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8649
8483
|
};
|
|
8650
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8651
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
8484
|
+
Git2.prototype.submoduleAdd = function(repo, path8, then) {
|
|
8485
|
+
return this._runTask(addSubModuleTask2(repo, path8), trailingFunctionArgument2(arguments));
|
|
8652
8486
|
};
|
|
8653
8487
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8654
8488
|
return this._runTask(
|
|
@@ -9250,15 +9084,15 @@ function createGitClient(baseDir, options) {
|
|
|
9250
9084
|
// ../git/dist/lock-detector.js
|
|
9251
9085
|
var import_node_child_process3 = require("child_process");
|
|
9252
9086
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
9253
|
-
var
|
|
9087
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
9254
9088
|
var import_node_util = require("util");
|
|
9255
9089
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process3.execFile);
|
|
9256
9090
|
async function getIndexLockPath(repoPath) {
|
|
9257
9091
|
try {
|
|
9258
9092
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "index.lock"], { cwd: repoPath });
|
|
9259
|
-
return
|
|
9093
|
+
return import_node_path3.default.resolve(repoPath, stdout.trim());
|
|
9260
9094
|
} catch {
|
|
9261
|
-
return
|
|
9095
|
+
return import_node_path3.default.join(repoPath, ".git", "index.lock");
|
|
9262
9096
|
}
|
|
9263
9097
|
}
|
|
9264
9098
|
async function getLockInfo(repoPath) {
|
|
@@ -9440,12 +9274,139 @@ async function getHeadSha(baseDir, options) {
|
|
|
9440
9274
|
|
|
9441
9275
|
// src/sagas/apply-snapshot-saga.ts
|
|
9442
9276
|
var import_promises2 = require("fs/promises");
|
|
9443
|
-
var
|
|
9277
|
+
var import_node_path4 = require("path");
|
|
9278
|
+
|
|
9279
|
+
// ../shared/dist/index.js
|
|
9280
|
+
var consoleLogger = {
|
|
9281
|
+
info: (_message, _data) => {
|
|
9282
|
+
},
|
|
9283
|
+
debug: (_message, _data) => {
|
|
9284
|
+
},
|
|
9285
|
+
error: (_message, _data) => {
|
|
9286
|
+
},
|
|
9287
|
+
warn: (_message, _data) => {
|
|
9288
|
+
}
|
|
9289
|
+
};
|
|
9290
|
+
var Saga = class {
|
|
9291
|
+
completedSteps = [];
|
|
9292
|
+
currentStepName = "unknown";
|
|
9293
|
+
stepTimings = [];
|
|
9294
|
+
log;
|
|
9295
|
+
constructor(logger) {
|
|
9296
|
+
this.log = logger ?? consoleLogger;
|
|
9297
|
+
}
|
|
9298
|
+
/**
|
|
9299
|
+
* Run the saga with the given input.
|
|
9300
|
+
* Returns a discriminated union result - either success with data or failure with error details.
|
|
9301
|
+
*/
|
|
9302
|
+
async run(input) {
|
|
9303
|
+
this.completedSteps = [];
|
|
9304
|
+
this.currentStepName = "unknown";
|
|
9305
|
+
this.stepTimings = [];
|
|
9306
|
+
const sagaStart = performance.now();
|
|
9307
|
+
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
9308
|
+
try {
|
|
9309
|
+
const result = await this.execute(input);
|
|
9310
|
+
const totalDuration = performance.now() - sagaStart;
|
|
9311
|
+
this.log.debug("Saga completed successfully", {
|
|
9312
|
+
sagaName: this.constructor.name,
|
|
9313
|
+
stepsCompleted: this.completedSteps.length,
|
|
9314
|
+
totalDurationMs: Math.round(totalDuration),
|
|
9315
|
+
stepTimings: this.stepTimings
|
|
9316
|
+
});
|
|
9317
|
+
return { success: true, data: result };
|
|
9318
|
+
} catch (error) {
|
|
9319
|
+
this.log.error("Saga failed, initiating rollback", {
|
|
9320
|
+
sagaName: this.constructor.name,
|
|
9321
|
+
failedStep: this.currentStepName,
|
|
9322
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9323
|
+
});
|
|
9324
|
+
await this.rollback();
|
|
9325
|
+
return {
|
|
9326
|
+
success: false,
|
|
9327
|
+
error: error instanceof Error ? error.message : String(error),
|
|
9328
|
+
failedStep: this.currentStepName
|
|
9329
|
+
};
|
|
9330
|
+
}
|
|
9331
|
+
}
|
|
9332
|
+
/**
|
|
9333
|
+
* Execute a step with its rollback action.
|
|
9334
|
+
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
9335
|
+
* The step name is automatically tracked for error reporting.
|
|
9336
|
+
*
|
|
9337
|
+
* @param config - Step configuration with name, execute, and rollback functions
|
|
9338
|
+
* @returns The result of the execute function
|
|
9339
|
+
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
9340
|
+
*/
|
|
9341
|
+
async step(config) {
|
|
9342
|
+
this.currentStepName = config.name;
|
|
9343
|
+
this.log.debug(`Executing step: ${config.name}`);
|
|
9344
|
+
const stepStart = performance.now();
|
|
9345
|
+
const result = await config.execute();
|
|
9346
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9347
|
+
this.stepTimings.push({ name: config.name, durationMs });
|
|
9348
|
+
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
9349
|
+
this.completedSteps.push({
|
|
9350
|
+
name: config.name,
|
|
9351
|
+
rollback: () => config.rollback(result)
|
|
9352
|
+
});
|
|
9353
|
+
return result;
|
|
9354
|
+
}
|
|
9355
|
+
/**
|
|
9356
|
+
* Execute a step that doesn't need rollback.
|
|
9357
|
+
* Useful for read-only operations or operations that are idempotent.
|
|
9358
|
+
* The step name is automatically tracked for error reporting.
|
|
9359
|
+
*
|
|
9360
|
+
* @param name - Step name for logging and error tracking
|
|
9361
|
+
* @param execute - The action to execute
|
|
9362
|
+
* @returns The result of the execute function
|
|
9363
|
+
*/
|
|
9364
|
+
async readOnlyStep(name, execute) {
|
|
9365
|
+
this.currentStepName = name;
|
|
9366
|
+
this.log.debug(`Executing read-only step: ${name}`);
|
|
9367
|
+
const stepStart = performance.now();
|
|
9368
|
+
const result = await execute();
|
|
9369
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9370
|
+
this.stepTimings.push({ name, durationMs });
|
|
9371
|
+
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
9372
|
+
return result;
|
|
9373
|
+
}
|
|
9374
|
+
/**
|
|
9375
|
+
* Roll back all completed steps in reverse order.
|
|
9376
|
+
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
9377
|
+
*/
|
|
9378
|
+
async rollback() {
|
|
9379
|
+
this.log.info("Rolling back saga", {
|
|
9380
|
+
stepsToRollback: this.completedSteps.length
|
|
9381
|
+
});
|
|
9382
|
+
const stepsReversed = [...this.completedSteps].reverse();
|
|
9383
|
+
for (const step of stepsReversed) {
|
|
9384
|
+
try {
|
|
9385
|
+
this.log.debug(`Rolling back step: ${step.name}`);
|
|
9386
|
+
await step.rollback();
|
|
9387
|
+
this.log.debug(`Step rolled back: ${step.name}`);
|
|
9388
|
+
} catch (error) {
|
|
9389
|
+
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
9390
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9391
|
+
});
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
this.log.info("Rollback completed", {
|
|
9395
|
+
stepsAttempted: this.completedSteps.length
|
|
9396
|
+
});
|
|
9397
|
+
}
|
|
9398
|
+
/**
|
|
9399
|
+
* Get the number of completed steps (useful for testing)
|
|
9400
|
+
*/
|
|
9401
|
+
getCompletedStepCount() {
|
|
9402
|
+
return this.completedSteps.length;
|
|
9403
|
+
}
|
|
9404
|
+
};
|
|
9444
9405
|
|
|
9445
9406
|
// ../git/dist/sagas/tree.js
|
|
9446
|
-
var
|
|
9447
|
-
var
|
|
9448
|
-
var
|
|
9407
|
+
var import_node_fs3 = require("fs");
|
|
9408
|
+
var fs6 = __toESM(require("fs/promises"), 1);
|
|
9409
|
+
var path7 = __toESM(require("path"), 1);
|
|
9449
9410
|
var tar = __toESM(require("tar"), 1);
|
|
9450
9411
|
|
|
9451
9412
|
// ../git/dist/git-saga.js
|
|
@@ -9471,14 +9432,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9471
9432
|
tempIndexPath = null;
|
|
9472
9433
|
async executeGitOperations(input) {
|
|
9473
9434
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9474
|
-
const tmpDir =
|
|
9435
|
+
const tmpDir = path7.join(baseDir, ".git", "twig-tmp");
|
|
9475
9436
|
await this.step({
|
|
9476
9437
|
name: "create_tmp_dir",
|
|
9477
|
-
execute: () =>
|
|
9438
|
+
execute: () => fs6.mkdir(tmpDir, { recursive: true }),
|
|
9478
9439
|
rollback: async () => {
|
|
9479
9440
|
}
|
|
9480
9441
|
});
|
|
9481
|
-
this.tempIndexPath =
|
|
9442
|
+
this.tempIndexPath = path7.join(tmpDir, `index-${Date.now()}`);
|
|
9482
9443
|
const tempIndexGit = this.git.env({
|
|
9483
9444
|
...process.env,
|
|
9484
9445
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9488,7 +9449,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9488
9449
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9489
9450
|
rollback: async () => {
|
|
9490
9451
|
if (this.tempIndexPath) {
|
|
9491
|
-
await
|
|
9452
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9492
9453
|
});
|
|
9493
9454
|
}
|
|
9494
9455
|
}
|
|
@@ -9497,7 +9458,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9497
9458
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9498
9459
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9499
9460
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9500
|
-
await
|
|
9461
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9501
9462
|
});
|
|
9502
9463
|
return { snapshot: null, changed: false };
|
|
9503
9464
|
}
|
|
@@ -9509,7 +9470,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9509
9470
|
}
|
|
9510
9471
|
});
|
|
9511
9472
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9512
|
-
await
|
|
9473
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9513
9474
|
});
|
|
9514
9475
|
const snapshot = {
|
|
9515
9476
|
treeHash,
|
|
@@ -9533,15 +9494,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9533
9494
|
if (filesToArchive.length === 0) {
|
|
9534
9495
|
return void 0;
|
|
9535
9496
|
}
|
|
9536
|
-
const existingFiles = filesToArchive.filter((f) => (0,
|
|
9497
|
+
const existingFiles = filesToArchive.filter((f) => (0, import_node_fs3.existsSync)(path7.join(baseDir, f)));
|
|
9537
9498
|
if (existingFiles.length === 0) {
|
|
9538
9499
|
return void 0;
|
|
9539
9500
|
}
|
|
9540
9501
|
await this.step({
|
|
9541
9502
|
name: "create_archive",
|
|
9542
9503
|
execute: async () => {
|
|
9543
|
-
const archiveDir =
|
|
9544
|
-
await
|
|
9504
|
+
const archiveDir = path7.dirname(archivePath);
|
|
9505
|
+
await fs6.mkdir(archiveDir, { recursive: true });
|
|
9545
9506
|
await tar.create({
|
|
9546
9507
|
gzip: true,
|
|
9547
9508
|
file: archivePath,
|
|
@@ -9549,7 +9510,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9549
9510
|
}, existingFiles);
|
|
9550
9511
|
},
|
|
9551
9512
|
rollback: async () => {
|
|
9552
|
-
await
|
|
9513
|
+
await fs6.rm(archivePath, { force: true }).catch(() => {
|
|
9553
9514
|
});
|
|
9554
9515
|
}
|
|
9555
9516
|
});
|
|
@@ -9648,9 +9609,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9648
9609
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9649
9610
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9650
9611
|
for (const filePath of filesToExtract) {
|
|
9651
|
-
const fullPath =
|
|
9612
|
+
const fullPath = path7.join(baseDir, filePath);
|
|
9652
9613
|
try {
|
|
9653
|
-
const content = await
|
|
9614
|
+
const content = await fs6.readFile(fullPath);
|
|
9654
9615
|
this.fileBackups.set(filePath, content);
|
|
9655
9616
|
} catch {
|
|
9656
9617
|
}
|
|
@@ -9667,16 +9628,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9667
9628
|
},
|
|
9668
9629
|
rollback: async () => {
|
|
9669
9630
|
for (const filePath of this.extractedFiles) {
|
|
9670
|
-
const fullPath =
|
|
9631
|
+
const fullPath = path7.join(baseDir, filePath);
|
|
9671
9632
|
const backup = this.fileBackups.get(filePath);
|
|
9672
9633
|
if (backup) {
|
|
9673
|
-
const dir =
|
|
9674
|
-
await
|
|
9634
|
+
const dir = path7.dirname(fullPath);
|
|
9635
|
+
await fs6.mkdir(dir, { recursive: true }).catch(() => {
|
|
9675
9636
|
});
|
|
9676
|
-
await
|
|
9637
|
+
await fs6.writeFile(fullPath, backup).catch(() => {
|
|
9677
9638
|
});
|
|
9678
9639
|
} else {
|
|
9679
|
-
await
|
|
9640
|
+
await fs6.rm(fullPath, { force: true }).catch(() => {
|
|
9680
9641
|
});
|
|
9681
9642
|
}
|
|
9682
9643
|
}
|
|
@@ -9684,10 +9645,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9684
9645
|
});
|
|
9685
9646
|
}
|
|
9686
9647
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9687
|
-
const fullPath =
|
|
9648
|
+
const fullPath = path7.join(baseDir, change.path);
|
|
9688
9649
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9689
9650
|
try {
|
|
9690
|
-
return await
|
|
9651
|
+
return await fs6.readFile(fullPath);
|
|
9691
9652
|
} catch {
|
|
9692
9653
|
return null;
|
|
9693
9654
|
}
|
|
@@ -9695,15 +9656,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9695
9656
|
await this.step({
|
|
9696
9657
|
name: `delete_${change.path}`,
|
|
9697
9658
|
execute: async () => {
|
|
9698
|
-
await
|
|
9659
|
+
await fs6.rm(fullPath, { force: true });
|
|
9699
9660
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9700
9661
|
},
|
|
9701
9662
|
rollback: async () => {
|
|
9702
9663
|
if (backupContent) {
|
|
9703
|
-
const dir =
|
|
9704
|
-
await
|
|
9664
|
+
const dir = path7.dirname(fullPath);
|
|
9665
|
+
await fs6.mkdir(dir, { recursive: true }).catch(() => {
|
|
9705
9666
|
});
|
|
9706
|
-
await
|
|
9667
|
+
await fs6.writeFile(fullPath, backupContent).catch(() => {
|
|
9707
9668
|
});
|
|
9708
9669
|
}
|
|
9709
9670
|
}
|
|
@@ -9725,7 +9686,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9725
9686
|
archivePath = null;
|
|
9726
9687
|
async execute(input) {
|
|
9727
9688
|
const { snapshot, repositoryPath, apiClient, taskId, runId } = input;
|
|
9728
|
-
const tmpDir = (0,
|
|
9689
|
+
const tmpDir = (0, import_node_path4.join)(repositoryPath, ".posthog", "tmp");
|
|
9729
9690
|
if (!snapshot.archiveUrl) {
|
|
9730
9691
|
throw new Error("Cannot apply snapshot: no archive URL");
|
|
9731
9692
|
}
|
|
@@ -9735,7 +9696,7 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9735
9696
|
rollback: async () => {
|
|
9736
9697
|
}
|
|
9737
9698
|
});
|
|
9738
|
-
this.archivePath = (0,
|
|
9699
|
+
this.archivePath = (0, import_node_path4.join)(tmpDir, `${snapshot.treeHash}.tar.gz`);
|
|
9739
9700
|
await this.step({
|
|
9740
9701
|
name: "download_archive",
|
|
9741
9702
|
execute: async () => {
|
|
@@ -9781,9 +9742,9 @@ var ApplySnapshotSaga = class extends Saga {
|
|
|
9781
9742
|
};
|
|
9782
9743
|
|
|
9783
9744
|
// src/sagas/capture-tree-saga.ts
|
|
9784
|
-
var
|
|
9745
|
+
var import_node_fs4 = require("fs");
|
|
9785
9746
|
var import_promises3 = require("fs/promises");
|
|
9786
|
-
var
|
|
9747
|
+
var import_node_path5 = require("path");
|
|
9787
9748
|
var CaptureTreeSaga2 = class extends Saga {
|
|
9788
9749
|
async execute(input) {
|
|
9789
9750
|
const {
|
|
@@ -9794,14 +9755,14 @@ var CaptureTreeSaga2 = class extends Saga {
|
|
|
9794
9755
|
taskId,
|
|
9795
9756
|
runId
|
|
9796
9757
|
} = input;
|
|
9797
|
-
const tmpDir = (0,
|
|
9798
|
-
if ((0,
|
|
9758
|
+
const tmpDir = (0, import_node_path5.join)(repositoryPath, ".posthog", "tmp");
|
|
9759
|
+
if ((0, import_node_fs4.existsSync)((0, import_node_path5.join)(repositoryPath, ".gitmodules"))) {
|
|
9799
9760
|
this.log.warn(
|
|
9800
9761
|
"Repository has submodules - snapshot may not capture submodule state"
|
|
9801
9762
|
);
|
|
9802
9763
|
}
|
|
9803
9764
|
const shouldArchive = !!apiClient;
|
|
9804
|
-
const archivePath = shouldArchive ? (0,
|
|
9765
|
+
const archivePath = shouldArchive ? (0, import_node_path5.join)(tmpDir, `tree-${Date.now()}.tar.gz`) : void 0;
|
|
9805
9766
|
const gitCaptureSaga = new CaptureTreeSaga(this.log);
|
|
9806
9767
|
const captureResult = await gitCaptureSaga.run({
|
|
9807
9768
|
baseDir: repositoryPath,
|