@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
|
@@ -809,10 +809,10 @@ var require_src2 = __commonJS({
|
|
|
809
809
|
var fs_1 = __require("fs");
|
|
810
810
|
var debug_1 = __importDefault(require_src());
|
|
811
811
|
var log = debug_1.default("@kwsites/file-exists");
|
|
812
|
-
function check(
|
|
813
|
-
log(`checking %s`,
|
|
812
|
+
function check(path8, isFile, isDirectory) {
|
|
813
|
+
log(`checking %s`, path8);
|
|
814
814
|
try {
|
|
815
|
-
const stat = fs_1.statSync(
|
|
815
|
+
const stat = fs_1.statSync(path8);
|
|
816
816
|
if (stat.isFile() && isFile) {
|
|
817
817
|
log(`[OK] path represents a file`);
|
|
818
818
|
return true;
|
|
@@ -832,8 +832,8 @@ var require_src2 = __commonJS({
|
|
|
832
832
|
throw e;
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
|
-
function exists2(
|
|
836
|
-
return check(
|
|
835
|
+
function exists2(path8, type = exports.READABLE) {
|
|
836
|
+
return check(path8, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
|
|
837
837
|
}
|
|
838
838
|
exports.exists = exists2;
|
|
839
839
|
exports.FILE = 1;
|
|
@@ -1178,174 +1178,12 @@ import {
|
|
|
1178
1178
|
import {
|
|
1179
1179
|
query
|
|
1180
1180
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
1181
|
-
|
|
1182
|
-
// ../shared/dist/index.js
|
|
1183
|
-
var consoleLogger = {
|
|
1184
|
-
info: (_message, _data) => {
|
|
1185
|
-
},
|
|
1186
|
-
debug: (_message, _data) => {
|
|
1187
|
-
},
|
|
1188
|
-
error: (_message, _data) => {
|
|
1189
|
-
},
|
|
1190
|
-
warn: (_message, _data) => {
|
|
1191
|
-
}
|
|
1192
|
-
};
|
|
1193
|
-
var Saga = class {
|
|
1194
|
-
completedSteps = [];
|
|
1195
|
-
currentStepName = "unknown";
|
|
1196
|
-
stepTimings = [];
|
|
1197
|
-
log;
|
|
1198
|
-
constructor(logger) {
|
|
1199
|
-
this.log = logger ?? consoleLogger;
|
|
1200
|
-
}
|
|
1201
|
-
/**
|
|
1202
|
-
* Run the saga with the given input.
|
|
1203
|
-
* Returns a discriminated union result - either success with data or failure with error details.
|
|
1204
|
-
*/
|
|
1205
|
-
async run(input) {
|
|
1206
|
-
this.completedSteps = [];
|
|
1207
|
-
this.currentStepName = "unknown";
|
|
1208
|
-
this.stepTimings = [];
|
|
1209
|
-
const sagaStart = performance.now();
|
|
1210
|
-
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
1211
|
-
try {
|
|
1212
|
-
const result = await this.execute(input);
|
|
1213
|
-
const totalDuration = performance.now() - sagaStart;
|
|
1214
|
-
this.log.debug("Saga completed successfully", {
|
|
1215
|
-
sagaName: this.constructor.name,
|
|
1216
|
-
stepsCompleted: this.completedSteps.length,
|
|
1217
|
-
totalDurationMs: Math.round(totalDuration),
|
|
1218
|
-
stepTimings: this.stepTimings
|
|
1219
|
-
});
|
|
1220
|
-
return { success: true, data: result };
|
|
1221
|
-
} catch (error) {
|
|
1222
|
-
this.log.error("Saga failed, initiating rollback", {
|
|
1223
|
-
sagaName: this.constructor.name,
|
|
1224
|
-
failedStep: this.currentStepName,
|
|
1225
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1226
|
-
});
|
|
1227
|
-
await this.rollback();
|
|
1228
|
-
return {
|
|
1229
|
-
success: false,
|
|
1230
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1231
|
-
failedStep: this.currentStepName
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
/**
|
|
1236
|
-
* Execute a step with its rollback action.
|
|
1237
|
-
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
1238
|
-
* The step name is automatically tracked for error reporting.
|
|
1239
|
-
*
|
|
1240
|
-
* @param config - Step configuration with name, execute, and rollback functions
|
|
1241
|
-
* @returns The result of the execute function
|
|
1242
|
-
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
1243
|
-
*/
|
|
1244
|
-
async step(config) {
|
|
1245
|
-
this.currentStepName = config.name;
|
|
1246
|
-
this.log.debug(`Executing step: ${config.name}`);
|
|
1247
|
-
const stepStart = performance.now();
|
|
1248
|
-
const result = await config.execute();
|
|
1249
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
1250
|
-
this.stepTimings.push({ name: config.name, durationMs });
|
|
1251
|
-
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
1252
|
-
this.completedSteps.push({
|
|
1253
|
-
name: config.name,
|
|
1254
|
-
rollback: () => config.rollback(result)
|
|
1255
|
-
});
|
|
1256
|
-
return result;
|
|
1257
|
-
}
|
|
1258
|
-
/**
|
|
1259
|
-
* Execute a step that doesn't need rollback.
|
|
1260
|
-
* Useful for read-only operations or operations that are idempotent.
|
|
1261
|
-
* The step name is automatically tracked for error reporting.
|
|
1262
|
-
*
|
|
1263
|
-
* @param name - Step name for logging and error tracking
|
|
1264
|
-
* @param execute - The action to execute
|
|
1265
|
-
* @returns The result of the execute function
|
|
1266
|
-
*/
|
|
1267
|
-
async readOnlyStep(name, execute) {
|
|
1268
|
-
this.currentStepName = name;
|
|
1269
|
-
this.log.debug(`Executing read-only step: ${name}`);
|
|
1270
|
-
const stepStart = performance.now();
|
|
1271
|
-
const result = await execute();
|
|
1272
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
1273
|
-
this.stepTimings.push({ name, durationMs });
|
|
1274
|
-
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
1275
|
-
return result;
|
|
1276
|
-
}
|
|
1277
|
-
/**
|
|
1278
|
-
* Roll back all completed steps in reverse order.
|
|
1279
|
-
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
1280
|
-
*/
|
|
1281
|
-
async rollback() {
|
|
1282
|
-
this.log.info("Rolling back saga", {
|
|
1283
|
-
stepsToRollback: this.completedSteps.length
|
|
1284
|
-
});
|
|
1285
|
-
const stepsReversed = [...this.completedSteps].reverse();
|
|
1286
|
-
for (const step of stepsReversed) {
|
|
1287
|
-
try {
|
|
1288
|
-
this.log.debug(`Rolling back step: ${step.name}`);
|
|
1289
|
-
await step.rollback();
|
|
1290
|
-
this.log.debug(`Step rolled back: ${step.name}`);
|
|
1291
|
-
} catch (error) {
|
|
1292
|
-
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
1293
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
this.log.info("Rollback completed", {
|
|
1298
|
-
stepsAttempted: this.completedSteps.length
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
/**
|
|
1302
|
-
* Get the number of completed steps (useful for testing)
|
|
1303
|
-
*/
|
|
1304
|
-
getCompletedStepCount() {
|
|
1305
|
-
return this.completedSteps.length;
|
|
1306
|
-
}
|
|
1307
|
-
};
|
|
1308
|
-
var NOOP_COLLECTOR = {
|
|
1309
|
-
time: (_label, fn) => fn(),
|
|
1310
|
-
timeSync: (_label, fn) => fn(),
|
|
1311
|
-
record: () => {
|
|
1312
|
-
},
|
|
1313
|
-
summarize: () => {
|
|
1314
|
-
}
|
|
1315
|
-
};
|
|
1316
|
-
function createTimingCollector(enabled, log) {
|
|
1317
|
-
if (!enabled) return NOOP_COLLECTOR;
|
|
1318
|
-
const steps = {};
|
|
1319
|
-
return {
|
|
1320
|
-
async time(label, fn) {
|
|
1321
|
-
const start = Date.now();
|
|
1322
|
-
const result = await fn();
|
|
1323
|
-
steps[label] = Date.now() - start;
|
|
1324
|
-
return result;
|
|
1325
|
-
},
|
|
1326
|
-
timeSync(label, fn) {
|
|
1327
|
-
const start = Date.now();
|
|
1328
|
-
const result = fn();
|
|
1329
|
-
steps[label] = Date.now() - start;
|
|
1330
|
-
return result;
|
|
1331
|
-
},
|
|
1332
|
-
record(label, ms) {
|
|
1333
|
-
steps[label] = ms;
|
|
1334
|
-
},
|
|
1335
|
-
summarize(label) {
|
|
1336
|
-
const total = Object.values(steps).reduce((a, b) => a + b, 0);
|
|
1337
|
-
log(`[timing] ${label}: ${total}ms`, steps);
|
|
1338
|
-
}
|
|
1339
|
-
};
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// src/adapters/claude/claude-agent.ts
|
|
1343
1181
|
import { v7 as uuidv7 } from "uuid";
|
|
1344
1182
|
|
|
1345
1183
|
// package.json
|
|
1346
1184
|
var package_default = {
|
|
1347
1185
|
name: "@posthog/agent",
|
|
1348
|
-
version: "2.1.
|
|
1186
|
+
version: "2.1.53",
|
|
1349
1187
|
repository: "https://github.com/PostHog/twig",
|
|
1350
1188
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
1351
1189
|
exports: {
|
|
@@ -1723,8 +1561,8 @@ var ToolContentBuilder = class {
|
|
|
1723
1561
|
this.items.push({ type: "content", content: image(data, mimeType, uri) });
|
|
1724
1562
|
return this;
|
|
1725
1563
|
}
|
|
1726
|
-
diff(
|
|
1727
|
-
this.items.push({ type: "diff", path:
|
|
1564
|
+
diff(path8, oldText, newText) {
|
|
1565
|
+
this.items.push({ type: "diff", path: path8, oldText, newText });
|
|
1728
1566
|
return this;
|
|
1729
1567
|
}
|
|
1730
1568
|
build() {
|
|
@@ -1914,13 +1752,13 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1914
1752
|
locations: []
|
|
1915
1753
|
};
|
|
1916
1754
|
case "Edit": {
|
|
1917
|
-
const
|
|
1755
|
+
const path8 = input?.file_path ? String(input.file_path) : void 0;
|
|
1918
1756
|
let oldText = input?.old_string ? String(input.old_string) : null;
|
|
1919
1757
|
let newText = input?.new_string ? String(input.new_string) : "";
|
|
1920
1758
|
let affectedLines = [];
|
|
1921
|
-
if (
|
|
1759
|
+
if (path8 && oldText) {
|
|
1922
1760
|
try {
|
|
1923
|
-
const oldContent = cachedFileContent[
|
|
1761
|
+
const oldContent = cachedFileContent[path8] || "";
|
|
1924
1762
|
const newContent = replaceAndCalculateLocation(oldContent, [
|
|
1925
1763
|
{
|
|
1926
1764
|
oldText,
|
|
@@ -1936,17 +1774,17 @@ function toolInfoFromToolUse(toolUse, cachedFileContent, logger = new Logger({ d
|
|
|
1936
1774
|
}
|
|
1937
1775
|
}
|
|
1938
1776
|
return {
|
|
1939
|
-
title:
|
|
1777
|
+
title: path8 ? `Edit \`${path8}\`` : "Edit",
|
|
1940
1778
|
kind: "edit",
|
|
1941
|
-
content: input &&
|
|
1779
|
+
content: input && path8 ? [
|
|
1942
1780
|
{
|
|
1943
1781
|
type: "diff",
|
|
1944
|
-
path:
|
|
1782
|
+
path: path8,
|
|
1945
1783
|
oldText,
|
|
1946
1784
|
newText
|
|
1947
1785
|
}
|
|
1948
1786
|
] : [],
|
|
1949
|
-
locations:
|
|
1787
|
+
locations: path8 ? affectedLines.length > 0 ? affectedLines.map((line) => ({ line, path: path8 })) : [{ path: path8 }] : []
|
|
1950
1788
|
};
|
|
1951
1789
|
}
|
|
1952
1790
|
case "Write": {
|
|
@@ -3420,12 +3258,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3420
3258
|
logWriter;
|
|
3421
3259
|
options;
|
|
3422
3260
|
lastSentConfigOptions;
|
|
3423
|
-
debug;
|
|
3424
3261
|
constructor(client, logWriter, options) {
|
|
3425
3262
|
super(client);
|
|
3426
3263
|
this.logWriter = logWriter;
|
|
3427
3264
|
this.options = options;
|
|
3428
|
-
this.debug = options?.debug ?? false;
|
|
3429
3265
|
this.toolUseCache = {};
|
|
3430
3266
|
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
3431
3267
|
}
|
|
@@ -3468,36 +3304,26 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3468
3304
|
}
|
|
3469
3305
|
async newSession(params) {
|
|
3470
3306
|
this.checkAuthStatus();
|
|
3471
|
-
const tc = createTimingCollector(
|
|
3472
|
-
this.debug,
|
|
3473
|
-
(msg, data) => this.logger.info(msg, data)
|
|
3474
|
-
);
|
|
3475
3307
|
const meta = params._meta;
|
|
3476
3308
|
const sessionId = uuidv7();
|
|
3477
3309
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3478
|
-
const mcpServers =
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
isResume: false,
|
|
3494
|
-
onModeChange: this.createOnModeChange(sessionId),
|
|
3495
|
-
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3496
|
-
onProcessExited: this.options?.onProcessExited
|
|
3497
|
-
})
|
|
3498
|
-
);
|
|
3310
|
+
const mcpServers = parseMcpServers(params);
|
|
3311
|
+
const options = buildSessionOptions({
|
|
3312
|
+
cwd: params.cwd,
|
|
3313
|
+
mcpServers,
|
|
3314
|
+
permissionMode,
|
|
3315
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
3316
|
+
logger: this.logger,
|
|
3317
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3318
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3319
|
+
sessionId,
|
|
3320
|
+
isResume: false,
|
|
3321
|
+
onModeChange: this.createOnModeChange(sessionId),
|
|
3322
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3323
|
+
onProcessExited: this.options?.onProcessExited
|
|
3324
|
+
});
|
|
3499
3325
|
const input = new Pushable();
|
|
3500
|
-
const q =
|
|
3326
|
+
const q = query({ prompt: input, options });
|
|
3501
3327
|
const session = this.createSession(
|
|
3502
3328
|
sessionId,
|
|
3503
3329
|
q,
|
|
@@ -3509,27 +3335,17 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3509
3335
|
session.taskRunId = meta?.taskRunId;
|
|
3510
3336
|
this.registerPersistence(sessionId, meta);
|
|
3511
3337
|
if (meta?.taskRunId) {
|
|
3512
|
-
await
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
adapter: "claude"
|
|
3518
|
-
})
|
|
3519
|
-
);
|
|
3338
|
+
await this.client.extNotification("_posthog/sdk_session", {
|
|
3339
|
+
taskRunId: meta.taskRunId,
|
|
3340
|
+
sessionId,
|
|
3341
|
+
adapter: "claude"
|
|
3342
|
+
});
|
|
3520
3343
|
}
|
|
3521
|
-
const modelOptions = await
|
|
3522
|
-
|
|
3523
|
-
() => this.getModelConfigOptions()
|
|
3524
|
-
);
|
|
3525
|
-
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
3344
|
+
const modelOptions = await this.getModelConfigOptions();
|
|
3345
|
+
this.deferBackgroundFetches(q, sessionId, mcpServers);
|
|
3526
3346
|
session.modelId = modelOptions.currentModelId;
|
|
3527
3347
|
await this.trySetModel(q, modelOptions.currentModelId);
|
|
3528
|
-
const configOptions = await
|
|
3529
|
-
"buildConfigOptions",
|
|
3530
|
-
() => this.buildConfigOptions(modelOptions)
|
|
3531
|
-
);
|
|
3532
|
-
tc.summarize("newSession");
|
|
3348
|
+
const configOptions = await this.buildConfigOptions(modelOptions);
|
|
3533
3349
|
return {
|
|
3534
3350
|
sessionId,
|
|
3535
3351
|
configOptions
|
|
@@ -3539,10 +3355,6 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3539
3355
|
return this.resumeSession(params);
|
|
3540
3356
|
}
|
|
3541
3357
|
async resumeSession(params) {
|
|
3542
|
-
const tc = createTimingCollector(
|
|
3543
|
-
this.debug,
|
|
3544
|
-
(msg, data) => this.logger.info(msg, data)
|
|
3545
|
-
);
|
|
3546
3358
|
const meta = params._meta;
|
|
3547
3359
|
const sessionId = meta?.sessionId;
|
|
3548
3360
|
if (!sessionId) {
|
|
@@ -3551,32 +3363,22 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3551
3363
|
if (this.sessionId === sessionId) {
|
|
3552
3364
|
return {};
|
|
3553
3365
|
}
|
|
3554
|
-
const mcpServers =
|
|
3555
|
-
"parseMcpServers",
|
|
3556
|
-
() => parseMcpServers(params)
|
|
3557
|
-
);
|
|
3366
|
+
const mcpServers = parseMcpServers(params);
|
|
3558
3367
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3559
|
-
const { query: q, session } = await
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3570
|
-
})
|
|
3571
|
-
);
|
|
3368
|
+
const { query: q, session } = await this.initializeQuery({
|
|
3369
|
+
cwd: params.cwd,
|
|
3370
|
+
permissionMode,
|
|
3371
|
+
mcpServers,
|
|
3372
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3373
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3374
|
+
sessionId,
|
|
3375
|
+
isResume: true,
|
|
3376
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3377
|
+
});
|
|
3572
3378
|
session.taskRunId = meta?.taskRunId;
|
|
3573
3379
|
this.registerPersistence(sessionId, meta);
|
|
3574
|
-
this.deferBackgroundFetches(
|
|
3575
|
-
const configOptions = await
|
|
3576
|
-
"buildConfigOptions",
|
|
3577
|
-
() => this.buildConfigOptions()
|
|
3578
|
-
);
|
|
3579
|
-
tc.summarize("resumeSession");
|
|
3380
|
+
this.deferBackgroundFetches(q, sessionId, mcpServers);
|
|
3381
|
+
const configOptions = await this.buildConfigOptions();
|
|
3580
3382
|
return { configOptions };
|
|
3581
3383
|
}
|
|
3582
3384
|
async prompt(params) {
|
|
@@ -3764,13 +3566,10 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3764
3566
|
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3765
3567
|
* Both populate caches used later — neither is needed to return configOptions.
|
|
3766
3568
|
*/
|
|
3767
|
-
deferBackgroundFetches(
|
|
3569
|
+
deferBackgroundFetches(q, sessionId, mcpServers) {
|
|
3768
3570
|
Promise.all([
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
"mcpMetadata",
|
|
3772
|
-
() => fetchMcpToolMetadata(mcpServers, this.logger)
|
|
3773
|
-
)
|
|
3571
|
+
getAvailableSlashCommands(q),
|
|
3572
|
+
fetchMcpToolMetadata(mcpServers, this.logger)
|
|
3774
3573
|
]).then(([slashCommands]) => {
|
|
3775
3574
|
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
3776
3575
|
}).catch((err) => {
|
|
@@ -4064,10 +3863,7 @@ function createClaudeConnection(config) {
|
|
|
4064
3863
|
const agentStream = ndJsonStream(agentWritable, streams.agent.readable);
|
|
4065
3864
|
let agent = null;
|
|
4066
3865
|
const agentConnection = new AgentSideConnection((client) => {
|
|
4067
|
-
agent = new ClaudeAcpAgent(client, logWriter,
|
|
4068
|
-
...config.processCallbacks,
|
|
4069
|
-
debug: config.debug
|
|
4070
|
-
});
|
|
3866
|
+
agent = new ClaudeAcpAgent(client, logWriter, config.processCallbacks);
|
|
4071
3867
|
logger.info(`Created ${agent.adapterName} agent`);
|
|
4072
3868
|
return agent;
|
|
4073
3869
|
}, agentStream);
|
|
@@ -4482,6 +4278,8 @@ var PostHogAPIClient = class {
|
|
|
4482
4278
|
};
|
|
4483
4279
|
|
|
4484
4280
|
// src/session-log-writer.ts
|
|
4281
|
+
import fs3 from "fs";
|
|
4282
|
+
import path4 from "path";
|
|
4485
4283
|
var SessionLogWriter = class _SessionLogWriter {
|
|
4486
4284
|
static FLUSH_DEBOUNCE_MS = 500;
|
|
4487
4285
|
static FLUSH_MAX_INTERVAL_MS = 5e3;
|
|
@@ -4495,8 +4293,10 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4495
4293
|
sessions = /* @__PURE__ */ new Map();
|
|
4496
4294
|
messageCounts = /* @__PURE__ */ new Map();
|
|
4497
4295
|
logger;
|
|
4296
|
+
localCachePath;
|
|
4498
4297
|
constructor(options = {}) {
|
|
4499
4298
|
this.posthogAPI = options.posthogAPI;
|
|
4299
|
+
this.localCachePath = options.localCachePath;
|
|
4500
4300
|
this.logger = options.logger ?? new Logger({ debug: false, prefix: "[SessionLogWriter]" });
|
|
4501
4301
|
}
|
|
4502
4302
|
async flushAll() {
|
|
@@ -4526,6 +4326,21 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4526
4326
|
});
|
|
4527
4327
|
this.sessions.set(sessionId, { context });
|
|
4528
4328
|
this.lastFlushAttemptTime.set(sessionId, Date.now());
|
|
4329
|
+
if (this.localCachePath) {
|
|
4330
|
+
const sessionDir = path4.join(
|
|
4331
|
+
this.localCachePath,
|
|
4332
|
+
"sessions",
|
|
4333
|
+
context.runId
|
|
4334
|
+
);
|
|
4335
|
+
try {
|
|
4336
|
+
fs3.mkdirSync(sessionDir, { recursive: true });
|
|
4337
|
+
} catch (error) {
|
|
4338
|
+
this.logger.warn("Failed to create local cache directory", {
|
|
4339
|
+
sessionDir,
|
|
4340
|
+
error
|
|
4341
|
+
});
|
|
4342
|
+
}
|
|
4343
|
+
}
|
|
4529
4344
|
}
|
|
4530
4345
|
isRegistered(sessionId) {
|
|
4531
4346
|
return this.sessions.has(sessionId);
|
|
@@ -4563,6 +4378,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4563
4378
|
timestamp,
|
|
4564
4379
|
notification: message
|
|
4565
4380
|
};
|
|
4381
|
+
this.writeToLocalCache(sessionId, entry);
|
|
4566
4382
|
if (this.posthogAPI) {
|
|
4567
4383
|
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
4568
4384
|
pending.push(entry);
|
|
@@ -4663,6 +4479,7 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4663
4479
|
}
|
|
4664
4480
|
}
|
|
4665
4481
|
};
|
|
4482
|
+
this.writeToLocalCache(sessionId, entry);
|
|
4666
4483
|
if (this.posthogAPI) {
|
|
4667
4484
|
const pending = this.pendingEntries.get(sessionId) ?? [];
|
|
4668
4485
|
pending.push(entry);
|
|
@@ -4690,11 +4507,28 @@ var SessionLogWriter = class _SessionLogWriter {
|
|
|
4690
4507
|
const timeout = setTimeout(() => this.flush(sessionId), delay2);
|
|
4691
4508
|
this.flushTimeouts.set(sessionId, timeout);
|
|
4692
4509
|
}
|
|
4510
|
+
writeToLocalCache(sessionId, entry) {
|
|
4511
|
+
if (!this.localCachePath) return;
|
|
4512
|
+
const session = this.sessions.get(sessionId);
|
|
4513
|
+
if (!session) return;
|
|
4514
|
+
const logPath = path4.join(
|
|
4515
|
+
this.localCachePath,
|
|
4516
|
+
"sessions",
|
|
4517
|
+
session.context.runId,
|
|
4518
|
+
"logs.ndjson"
|
|
4519
|
+
);
|
|
4520
|
+
try {
|
|
4521
|
+
fs3.appendFileSync(logPath, `${JSON.stringify(entry)}
|
|
4522
|
+
`);
|
|
4523
|
+
} catch (error) {
|
|
4524
|
+
this.logger.warn("Failed to write to local cache", { logPath, error });
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4693
4527
|
};
|
|
4694
4528
|
|
|
4695
4529
|
// ../git/dist/queries.js
|
|
4696
|
-
import * as
|
|
4697
|
-
import * as
|
|
4530
|
+
import * as fs5 from "fs/promises";
|
|
4531
|
+
import * as path6 from "path";
|
|
4698
4532
|
|
|
4699
4533
|
// ../../node_modules/simple-git/dist/esm/index.js
|
|
4700
4534
|
var import_file_exists = __toESM(require_dist(), 1);
|
|
@@ -4733,8 +4567,8 @@ function pathspec(...paths) {
|
|
|
4733
4567
|
cache.set(key, paths);
|
|
4734
4568
|
return key;
|
|
4735
4569
|
}
|
|
4736
|
-
function isPathSpec(
|
|
4737
|
-
return
|
|
4570
|
+
function isPathSpec(path8) {
|
|
4571
|
+
return path8 instanceof String && cache.has(path8);
|
|
4738
4572
|
}
|
|
4739
4573
|
function toPaths(pathSpec) {
|
|
4740
4574
|
return cache.get(pathSpec) || [];
|
|
@@ -4823,8 +4657,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
|
|
|
4823
4657
|
function forEachLineWithContent(input, callback) {
|
|
4824
4658
|
return toLinesWithContent(input, true).map((line) => callback(line));
|
|
4825
4659
|
}
|
|
4826
|
-
function folderExists(
|
|
4827
|
-
return (0, import_file_exists.exists)(
|
|
4660
|
+
function folderExists(path8) {
|
|
4661
|
+
return (0, import_file_exists.exists)(path8, import_file_exists.FOLDER);
|
|
4828
4662
|
}
|
|
4829
4663
|
function append(target, item) {
|
|
4830
4664
|
if (Array.isArray(target)) {
|
|
@@ -5228,8 +5062,8 @@ function checkIsRepoRootTask() {
|
|
|
5228
5062
|
commands,
|
|
5229
5063
|
format: "utf-8",
|
|
5230
5064
|
onError,
|
|
5231
|
-
parser(
|
|
5232
|
-
return /^\.(git)?$/.test(
|
|
5065
|
+
parser(path8) {
|
|
5066
|
+
return /^\.(git)?$/.test(path8.trim());
|
|
5233
5067
|
}
|
|
5234
5068
|
};
|
|
5235
5069
|
}
|
|
@@ -5663,11 +5497,11 @@ function parseGrep(grep) {
|
|
|
5663
5497
|
const paths = /* @__PURE__ */ new Set();
|
|
5664
5498
|
const results = {};
|
|
5665
5499
|
forEachLineWithContent(grep, (input) => {
|
|
5666
|
-
const [
|
|
5667
|
-
paths.add(
|
|
5668
|
-
(results[
|
|
5500
|
+
const [path8, line, preview] = input.split(NULL);
|
|
5501
|
+
paths.add(path8);
|
|
5502
|
+
(results[path8] = results[path8] || []).push({
|
|
5669
5503
|
line: asNumber(line),
|
|
5670
|
-
path:
|
|
5504
|
+
path: path8,
|
|
5671
5505
|
preview
|
|
5672
5506
|
});
|
|
5673
5507
|
});
|
|
@@ -6432,14 +6266,14 @@ var init_hash_object = __esm({
|
|
|
6432
6266
|
init_task();
|
|
6433
6267
|
}
|
|
6434
6268
|
});
|
|
6435
|
-
function parseInit(bare,
|
|
6269
|
+
function parseInit(bare, path8, text2) {
|
|
6436
6270
|
const response = String(text2).trim();
|
|
6437
6271
|
let result;
|
|
6438
6272
|
if (result = initResponseRegex.exec(response)) {
|
|
6439
|
-
return new InitSummary(bare,
|
|
6273
|
+
return new InitSummary(bare, path8, false, result[1]);
|
|
6440
6274
|
}
|
|
6441
6275
|
if (result = reInitResponseRegex.exec(response)) {
|
|
6442
|
-
return new InitSummary(bare,
|
|
6276
|
+
return new InitSummary(bare, path8, true, result[1]);
|
|
6443
6277
|
}
|
|
6444
6278
|
let gitDir = "";
|
|
6445
6279
|
const tokens = response.split(" ");
|
|
@@ -6450,7 +6284,7 @@ function parseInit(bare, path7, text2) {
|
|
|
6450
6284
|
break;
|
|
6451
6285
|
}
|
|
6452
6286
|
}
|
|
6453
|
-
return new InitSummary(bare,
|
|
6287
|
+
return new InitSummary(bare, path8, /^re/i.test(response), gitDir);
|
|
6454
6288
|
}
|
|
6455
6289
|
var InitSummary;
|
|
6456
6290
|
var initResponseRegex;
|
|
@@ -6459,9 +6293,9 @@ var init_InitSummary = __esm({
|
|
|
6459
6293
|
"src/lib/responses/InitSummary.ts"() {
|
|
6460
6294
|
"use strict";
|
|
6461
6295
|
InitSummary = class {
|
|
6462
|
-
constructor(bare,
|
|
6296
|
+
constructor(bare, path8, existing, gitDir) {
|
|
6463
6297
|
this.bare = bare;
|
|
6464
|
-
this.path =
|
|
6298
|
+
this.path = path8;
|
|
6465
6299
|
this.existing = existing;
|
|
6466
6300
|
this.gitDir = gitDir;
|
|
6467
6301
|
}
|
|
@@ -6473,7 +6307,7 @@ var init_InitSummary = __esm({
|
|
|
6473
6307
|
function hasBareCommand(command) {
|
|
6474
6308
|
return command.includes(bareCommand);
|
|
6475
6309
|
}
|
|
6476
|
-
function initTask(bare = false,
|
|
6310
|
+
function initTask(bare = false, path8, customArgs) {
|
|
6477
6311
|
const commands = ["init", ...customArgs];
|
|
6478
6312
|
if (bare && !hasBareCommand(commands)) {
|
|
6479
6313
|
commands.splice(1, 0, bareCommand);
|
|
@@ -6482,7 +6316,7 @@ function initTask(bare = false, path7, customArgs) {
|
|
|
6482
6316
|
commands,
|
|
6483
6317
|
format: "utf-8",
|
|
6484
6318
|
parser(text2) {
|
|
6485
|
-
return parseInit(commands.includes("--bare"),
|
|
6319
|
+
return parseInit(commands.includes("--bare"), path8, text2);
|
|
6486
6320
|
}
|
|
6487
6321
|
};
|
|
6488
6322
|
}
|
|
@@ -7298,12 +7132,12 @@ var init_FileStatusSummary = __esm({
|
|
|
7298
7132
|
"use strict";
|
|
7299
7133
|
fromPathRegex = /^(.+)\0(.+)$/;
|
|
7300
7134
|
FileStatusSummary = class {
|
|
7301
|
-
constructor(
|
|
7302
|
-
this.path =
|
|
7135
|
+
constructor(path8, index, working_dir) {
|
|
7136
|
+
this.path = path8;
|
|
7303
7137
|
this.index = index;
|
|
7304
7138
|
this.working_dir = working_dir;
|
|
7305
7139
|
if (index === "R" || working_dir === "R") {
|
|
7306
|
-
const detail = fromPathRegex.exec(
|
|
7140
|
+
const detail = fromPathRegex.exec(path8) || [null, path8, path8];
|
|
7307
7141
|
this.from = detail[2] || "";
|
|
7308
7142
|
this.path = detail[1] || "";
|
|
7309
7143
|
}
|
|
@@ -7334,14 +7168,14 @@ function splitLine(result, lineStr) {
|
|
|
7334
7168
|
default:
|
|
7335
7169
|
return;
|
|
7336
7170
|
}
|
|
7337
|
-
function data(index, workingDir,
|
|
7171
|
+
function data(index, workingDir, path8) {
|
|
7338
7172
|
const raw = `${index}${workingDir}`;
|
|
7339
7173
|
const handler = parsers6.get(raw);
|
|
7340
7174
|
if (handler) {
|
|
7341
|
-
handler(result,
|
|
7175
|
+
handler(result, path8);
|
|
7342
7176
|
}
|
|
7343
7177
|
if (raw !== "##" && raw !== "!!") {
|
|
7344
|
-
result.files.push(new FileStatusSummary(
|
|
7178
|
+
result.files.push(new FileStatusSummary(path8, index, workingDir));
|
|
7345
7179
|
}
|
|
7346
7180
|
}
|
|
7347
7181
|
}
|
|
@@ -7654,9 +7488,9 @@ var init_simple_git_api = __esm({
|
|
|
7654
7488
|
next
|
|
7655
7489
|
);
|
|
7656
7490
|
}
|
|
7657
|
-
hashObject(
|
|
7491
|
+
hashObject(path8, write) {
|
|
7658
7492
|
return this._runTask(
|
|
7659
|
-
hashObjectTask(
|
|
7493
|
+
hashObjectTask(path8, write === true),
|
|
7660
7494
|
trailingFunctionArgument(arguments)
|
|
7661
7495
|
);
|
|
7662
7496
|
}
|
|
@@ -8009,8 +7843,8 @@ var init_branch = __esm({
|
|
|
8009
7843
|
}
|
|
8010
7844
|
});
|
|
8011
7845
|
function toPath(input) {
|
|
8012
|
-
const
|
|
8013
|
-
return
|
|
7846
|
+
const path8 = input.trim().replace(/^["']|["']$/g, "");
|
|
7847
|
+
return path8 && normalize(path8);
|
|
8014
7848
|
}
|
|
8015
7849
|
var parseCheckIgnore;
|
|
8016
7850
|
var init_CheckIgnore = __esm({
|
|
@@ -8324,8 +8158,8 @@ __export(sub_module_exports, {
|
|
|
8324
8158
|
subModuleTask: () => subModuleTask,
|
|
8325
8159
|
updateSubModuleTask: () => updateSubModuleTask
|
|
8326
8160
|
});
|
|
8327
|
-
function addSubModuleTask(repo,
|
|
8328
|
-
return subModuleTask(["add", repo,
|
|
8161
|
+
function addSubModuleTask(repo, path8) {
|
|
8162
|
+
return subModuleTask(["add", repo, path8]);
|
|
8329
8163
|
}
|
|
8330
8164
|
function initSubModuleTask(customArgs) {
|
|
8331
8165
|
return subModuleTask(["init", ...customArgs]);
|
|
@@ -8655,8 +8489,8 @@ var require_git = __commonJS2({
|
|
|
8655
8489
|
}
|
|
8656
8490
|
return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
|
|
8657
8491
|
};
|
|
8658
|
-
Git2.prototype.submoduleAdd = function(repo,
|
|
8659
|
-
return this._runTask(addSubModuleTask2(repo,
|
|
8492
|
+
Git2.prototype.submoduleAdd = function(repo, path8, then) {
|
|
8493
|
+
return this._runTask(addSubModuleTask2(repo, path8), trailingFunctionArgument2(arguments));
|
|
8660
8494
|
};
|
|
8661
8495
|
Git2.prototype.submoduleUpdate = function(args, then) {
|
|
8662
8496
|
return this._runTask(
|
|
@@ -9257,22 +9091,22 @@ function createGitClient(baseDir, options) {
|
|
|
9257
9091
|
|
|
9258
9092
|
// ../git/dist/lock-detector.js
|
|
9259
9093
|
import { execFile } from "child_process";
|
|
9260
|
-
import
|
|
9261
|
-
import
|
|
9094
|
+
import fs4 from "fs/promises";
|
|
9095
|
+
import path5 from "path";
|
|
9262
9096
|
import { promisify } from "util";
|
|
9263
9097
|
var execFileAsync = promisify(execFile);
|
|
9264
9098
|
async function getIndexLockPath(repoPath) {
|
|
9265
9099
|
try {
|
|
9266
9100
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "index.lock"], { cwd: repoPath });
|
|
9267
|
-
return
|
|
9101
|
+
return path5.resolve(repoPath, stdout.trim());
|
|
9268
9102
|
} catch {
|
|
9269
|
-
return
|
|
9103
|
+
return path5.join(repoPath, ".git", "index.lock");
|
|
9270
9104
|
}
|
|
9271
9105
|
}
|
|
9272
9106
|
async function getLockInfo(repoPath) {
|
|
9273
9107
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9274
9108
|
try {
|
|
9275
|
-
const stat = await
|
|
9109
|
+
const stat = await fs4.stat(lockPath);
|
|
9276
9110
|
return {
|
|
9277
9111
|
path: lockPath,
|
|
9278
9112
|
ageMs: Date.now() - stat.mtimeMs
|
|
@@ -9283,7 +9117,7 @@ async function getLockInfo(repoPath) {
|
|
|
9283
9117
|
}
|
|
9284
9118
|
async function removeLock(repoPath) {
|
|
9285
9119
|
const lockPath = await getIndexLockPath(repoPath);
|
|
9286
|
-
await
|
|
9120
|
+
await fs4.rm(lockPath, { force: true });
|
|
9287
9121
|
}
|
|
9288
9122
|
async function isLocked(repoPath) {
|
|
9289
9123
|
return await getLockInfo(repoPath) !== null;
|
|
@@ -9450,10 +9284,137 @@ async function getHeadSha(baseDir, options) {
|
|
|
9450
9284
|
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
9451
9285
|
import { join as join5 } from "path";
|
|
9452
9286
|
|
|
9287
|
+
// ../shared/dist/index.js
|
|
9288
|
+
var consoleLogger = {
|
|
9289
|
+
info: (_message, _data) => {
|
|
9290
|
+
},
|
|
9291
|
+
debug: (_message, _data) => {
|
|
9292
|
+
},
|
|
9293
|
+
error: (_message, _data) => {
|
|
9294
|
+
},
|
|
9295
|
+
warn: (_message, _data) => {
|
|
9296
|
+
}
|
|
9297
|
+
};
|
|
9298
|
+
var Saga = class {
|
|
9299
|
+
completedSteps = [];
|
|
9300
|
+
currentStepName = "unknown";
|
|
9301
|
+
stepTimings = [];
|
|
9302
|
+
log;
|
|
9303
|
+
constructor(logger) {
|
|
9304
|
+
this.log = logger ?? consoleLogger;
|
|
9305
|
+
}
|
|
9306
|
+
/**
|
|
9307
|
+
* Run the saga with the given input.
|
|
9308
|
+
* Returns a discriminated union result - either success with data or failure with error details.
|
|
9309
|
+
*/
|
|
9310
|
+
async run(input) {
|
|
9311
|
+
this.completedSteps = [];
|
|
9312
|
+
this.currentStepName = "unknown";
|
|
9313
|
+
this.stepTimings = [];
|
|
9314
|
+
const sagaStart = performance.now();
|
|
9315
|
+
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
9316
|
+
try {
|
|
9317
|
+
const result = await this.execute(input);
|
|
9318
|
+
const totalDuration = performance.now() - sagaStart;
|
|
9319
|
+
this.log.debug("Saga completed successfully", {
|
|
9320
|
+
sagaName: this.constructor.name,
|
|
9321
|
+
stepsCompleted: this.completedSteps.length,
|
|
9322
|
+
totalDurationMs: Math.round(totalDuration),
|
|
9323
|
+
stepTimings: this.stepTimings
|
|
9324
|
+
});
|
|
9325
|
+
return { success: true, data: result };
|
|
9326
|
+
} catch (error) {
|
|
9327
|
+
this.log.error("Saga failed, initiating rollback", {
|
|
9328
|
+
sagaName: this.constructor.name,
|
|
9329
|
+
failedStep: this.currentStepName,
|
|
9330
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9331
|
+
});
|
|
9332
|
+
await this.rollback();
|
|
9333
|
+
return {
|
|
9334
|
+
success: false,
|
|
9335
|
+
error: error instanceof Error ? error.message : String(error),
|
|
9336
|
+
failedStep: this.currentStepName
|
|
9337
|
+
};
|
|
9338
|
+
}
|
|
9339
|
+
}
|
|
9340
|
+
/**
|
|
9341
|
+
* Execute a step with its rollback action.
|
|
9342
|
+
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
9343
|
+
* The step name is automatically tracked for error reporting.
|
|
9344
|
+
*
|
|
9345
|
+
* @param config - Step configuration with name, execute, and rollback functions
|
|
9346
|
+
* @returns The result of the execute function
|
|
9347
|
+
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
9348
|
+
*/
|
|
9349
|
+
async step(config) {
|
|
9350
|
+
this.currentStepName = config.name;
|
|
9351
|
+
this.log.debug(`Executing step: ${config.name}`);
|
|
9352
|
+
const stepStart = performance.now();
|
|
9353
|
+
const result = await config.execute();
|
|
9354
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9355
|
+
this.stepTimings.push({ name: config.name, durationMs });
|
|
9356
|
+
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
9357
|
+
this.completedSteps.push({
|
|
9358
|
+
name: config.name,
|
|
9359
|
+
rollback: () => config.rollback(result)
|
|
9360
|
+
});
|
|
9361
|
+
return result;
|
|
9362
|
+
}
|
|
9363
|
+
/**
|
|
9364
|
+
* Execute a step that doesn't need rollback.
|
|
9365
|
+
* Useful for read-only operations or operations that are idempotent.
|
|
9366
|
+
* The step name is automatically tracked for error reporting.
|
|
9367
|
+
*
|
|
9368
|
+
* @param name - Step name for logging and error tracking
|
|
9369
|
+
* @param execute - The action to execute
|
|
9370
|
+
* @returns The result of the execute function
|
|
9371
|
+
*/
|
|
9372
|
+
async readOnlyStep(name, execute) {
|
|
9373
|
+
this.currentStepName = name;
|
|
9374
|
+
this.log.debug(`Executing read-only step: ${name}`);
|
|
9375
|
+
const stepStart = performance.now();
|
|
9376
|
+
const result = await execute();
|
|
9377
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9378
|
+
this.stepTimings.push({ name, durationMs });
|
|
9379
|
+
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
9380
|
+
return result;
|
|
9381
|
+
}
|
|
9382
|
+
/**
|
|
9383
|
+
* Roll back all completed steps in reverse order.
|
|
9384
|
+
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
9385
|
+
*/
|
|
9386
|
+
async rollback() {
|
|
9387
|
+
this.log.info("Rolling back saga", {
|
|
9388
|
+
stepsToRollback: this.completedSteps.length
|
|
9389
|
+
});
|
|
9390
|
+
const stepsReversed = [...this.completedSteps].reverse();
|
|
9391
|
+
for (const step of stepsReversed) {
|
|
9392
|
+
try {
|
|
9393
|
+
this.log.debug(`Rolling back step: ${step.name}`);
|
|
9394
|
+
await step.rollback();
|
|
9395
|
+
this.log.debug(`Step rolled back: ${step.name}`);
|
|
9396
|
+
} catch (error) {
|
|
9397
|
+
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
9398
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9399
|
+
});
|
|
9400
|
+
}
|
|
9401
|
+
}
|
|
9402
|
+
this.log.info("Rollback completed", {
|
|
9403
|
+
stepsAttempted: this.completedSteps.length
|
|
9404
|
+
});
|
|
9405
|
+
}
|
|
9406
|
+
/**
|
|
9407
|
+
* Get the number of completed steps (useful for testing)
|
|
9408
|
+
*/
|
|
9409
|
+
getCompletedStepCount() {
|
|
9410
|
+
return this.completedSteps.length;
|
|
9411
|
+
}
|
|
9412
|
+
};
|
|
9413
|
+
|
|
9453
9414
|
// ../git/dist/sagas/tree.js
|
|
9454
9415
|
import { existsSync as existsSync4 } from "fs";
|
|
9455
|
-
import * as
|
|
9456
|
-
import * as
|
|
9416
|
+
import * as fs6 from "fs/promises";
|
|
9417
|
+
import * as path7 from "path";
|
|
9457
9418
|
import * as tar from "tar";
|
|
9458
9419
|
|
|
9459
9420
|
// ../git/dist/git-saga.js
|
|
@@ -9479,14 +9440,14 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9479
9440
|
tempIndexPath = null;
|
|
9480
9441
|
async executeGitOperations(input) {
|
|
9481
9442
|
const { baseDir, lastTreeHash, archivePath, signal } = input;
|
|
9482
|
-
const tmpDir =
|
|
9443
|
+
const tmpDir = path7.join(baseDir, ".git", "twig-tmp");
|
|
9483
9444
|
await this.step({
|
|
9484
9445
|
name: "create_tmp_dir",
|
|
9485
|
-
execute: () =>
|
|
9446
|
+
execute: () => fs6.mkdir(tmpDir, { recursive: true }),
|
|
9486
9447
|
rollback: async () => {
|
|
9487
9448
|
}
|
|
9488
9449
|
});
|
|
9489
|
-
this.tempIndexPath =
|
|
9450
|
+
this.tempIndexPath = path7.join(tmpDir, `index-${Date.now()}`);
|
|
9490
9451
|
const tempIndexGit = this.git.env({
|
|
9491
9452
|
...process.env,
|
|
9492
9453
|
GIT_INDEX_FILE: this.tempIndexPath
|
|
@@ -9496,7 +9457,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9496
9457
|
execute: () => tempIndexGit.raw(["read-tree", "HEAD"]),
|
|
9497
9458
|
rollback: async () => {
|
|
9498
9459
|
if (this.tempIndexPath) {
|
|
9499
|
-
await
|
|
9460
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9500
9461
|
});
|
|
9501
9462
|
}
|
|
9502
9463
|
}
|
|
@@ -9505,7 +9466,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9505
9466
|
const treeHash = await this.readOnlyStep("write_tree", () => tempIndexGit.raw(["write-tree"]));
|
|
9506
9467
|
if (lastTreeHash && treeHash === lastTreeHash) {
|
|
9507
9468
|
this.log.debug("No changes since last capture", { treeHash });
|
|
9508
|
-
await
|
|
9469
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9509
9470
|
});
|
|
9510
9471
|
return { snapshot: null, changed: false };
|
|
9511
9472
|
}
|
|
@@ -9517,7 +9478,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9517
9478
|
}
|
|
9518
9479
|
});
|
|
9519
9480
|
const changes = await this.readOnlyStep("get_changes", () => this.getChanges(this.git, baseCommit, treeHash));
|
|
9520
|
-
await
|
|
9481
|
+
await fs6.rm(this.tempIndexPath, { force: true }).catch(() => {
|
|
9521
9482
|
});
|
|
9522
9483
|
const snapshot = {
|
|
9523
9484
|
treeHash,
|
|
@@ -9541,15 +9502,15 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9541
9502
|
if (filesToArchive.length === 0) {
|
|
9542
9503
|
return void 0;
|
|
9543
9504
|
}
|
|
9544
|
-
const existingFiles = filesToArchive.filter((f) => existsSync4(
|
|
9505
|
+
const existingFiles = filesToArchive.filter((f) => existsSync4(path7.join(baseDir, f)));
|
|
9545
9506
|
if (existingFiles.length === 0) {
|
|
9546
9507
|
return void 0;
|
|
9547
9508
|
}
|
|
9548
9509
|
await this.step({
|
|
9549
9510
|
name: "create_archive",
|
|
9550
9511
|
execute: async () => {
|
|
9551
|
-
const archiveDir =
|
|
9552
|
-
await
|
|
9512
|
+
const archiveDir = path7.dirname(archivePath);
|
|
9513
|
+
await fs6.mkdir(archiveDir, { recursive: true });
|
|
9553
9514
|
await tar.create({
|
|
9554
9515
|
gzip: true,
|
|
9555
9516
|
file: archivePath,
|
|
@@ -9557,7 +9518,7 @@ var CaptureTreeSaga = class extends GitSaga {
|
|
|
9557
9518
|
}, existingFiles);
|
|
9558
9519
|
},
|
|
9559
9520
|
rollback: async () => {
|
|
9560
|
-
await
|
|
9521
|
+
await fs6.rm(archivePath, { force: true }).catch(() => {
|
|
9561
9522
|
});
|
|
9562
9523
|
}
|
|
9563
9524
|
});
|
|
@@ -9656,9 +9617,9 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9656
9617
|
const filesToExtract = changes.filter((c) => c.status !== "D").map((c) => c.path);
|
|
9657
9618
|
await this.readOnlyStep("backup_existing_files", async () => {
|
|
9658
9619
|
for (const filePath of filesToExtract) {
|
|
9659
|
-
const fullPath =
|
|
9620
|
+
const fullPath = path7.join(baseDir, filePath);
|
|
9660
9621
|
try {
|
|
9661
|
-
const content = await
|
|
9622
|
+
const content = await fs6.readFile(fullPath);
|
|
9662
9623
|
this.fileBackups.set(filePath, content);
|
|
9663
9624
|
} catch {
|
|
9664
9625
|
}
|
|
@@ -9675,16 +9636,16 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9675
9636
|
},
|
|
9676
9637
|
rollback: async () => {
|
|
9677
9638
|
for (const filePath of this.extractedFiles) {
|
|
9678
|
-
const fullPath =
|
|
9639
|
+
const fullPath = path7.join(baseDir, filePath);
|
|
9679
9640
|
const backup = this.fileBackups.get(filePath);
|
|
9680
9641
|
if (backup) {
|
|
9681
|
-
const dir =
|
|
9682
|
-
await
|
|
9642
|
+
const dir = path7.dirname(fullPath);
|
|
9643
|
+
await fs6.mkdir(dir, { recursive: true }).catch(() => {
|
|
9683
9644
|
});
|
|
9684
|
-
await
|
|
9645
|
+
await fs6.writeFile(fullPath, backup).catch(() => {
|
|
9685
9646
|
});
|
|
9686
9647
|
} else {
|
|
9687
|
-
await
|
|
9648
|
+
await fs6.rm(fullPath, { force: true }).catch(() => {
|
|
9688
9649
|
});
|
|
9689
9650
|
}
|
|
9690
9651
|
}
|
|
@@ -9692,10 +9653,10 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9692
9653
|
});
|
|
9693
9654
|
}
|
|
9694
9655
|
for (const change of changes.filter((c) => c.status === "D")) {
|
|
9695
|
-
const fullPath =
|
|
9656
|
+
const fullPath = path7.join(baseDir, change.path);
|
|
9696
9657
|
const backupContent = await this.readOnlyStep(`backup_${change.path}`, async () => {
|
|
9697
9658
|
try {
|
|
9698
|
-
return await
|
|
9659
|
+
return await fs6.readFile(fullPath);
|
|
9699
9660
|
} catch {
|
|
9700
9661
|
return null;
|
|
9701
9662
|
}
|
|
@@ -9703,15 +9664,15 @@ var ApplyTreeSaga = class extends GitSaga {
|
|
|
9703
9664
|
await this.step({
|
|
9704
9665
|
name: `delete_${change.path}`,
|
|
9705
9666
|
execute: async () => {
|
|
9706
|
-
await
|
|
9667
|
+
await fs6.rm(fullPath, { force: true });
|
|
9707
9668
|
this.log.debug(`Deleted file: ${change.path}`);
|
|
9708
9669
|
},
|
|
9709
9670
|
rollback: async () => {
|
|
9710
9671
|
if (backupContent) {
|
|
9711
|
-
const dir =
|
|
9712
|
-
await
|
|
9672
|
+
const dir = path7.dirname(fullPath);
|
|
9673
|
+
await fs6.mkdir(dir, { recursive: true }).catch(() => {
|
|
9713
9674
|
});
|
|
9714
|
-
await
|
|
9675
|
+
await fs6.writeFile(fullPath, backupContent).catch(() => {
|
|
9715
9676
|
});
|
|
9716
9677
|
}
|
|
9717
9678
|
}
|