@posthog/agent 2.1.47 → 2.1.48
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-DcBmoTR4.d.ts} +0 -2
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +42 -122
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +169 -247
- package/dist/index.js.map +1 -1
- package/dist/server/agent-server.js +169 -246
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +169 -246
- package/dist/server/bin.cjs.map +1 -1
- 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 +0 -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.48",
|
|
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: {
|
|
@@ -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);
|
|
@@ -9450,6 +9246,133 @@ async function getHeadSha(baseDir, options) {
|
|
|
9450
9246
|
import { mkdir as mkdir3, rm as rm3, writeFile as writeFile3 } from "fs/promises";
|
|
9451
9247
|
import { join as join5 } from "path";
|
|
9452
9248
|
|
|
9249
|
+
// ../shared/dist/index.js
|
|
9250
|
+
var consoleLogger = {
|
|
9251
|
+
info: (_message, _data) => {
|
|
9252
|
+
},
|
|
9253
|
+
debug: (_message, _data) => {
|
|
9254
|
+
},
|
|
9255
|
+
error: (_message, _data) => {
|
|
9256
|
+
},
|
|
9257
|
+
warn: (_message, _data) => {
|
|
9258
|
+
}
|
|
9259
|
+
};
|
|
9260
|
+
var Saga = class {
|
|
9261
|
+
completedSteps = [];
|
|
9262
|
+
currentStepName = "unknown";
|
|
9263
|
+
stepTimings = [];
|
|
9264
|
+
log;
|
|
9265
|
+
constructor(logger) {
|
|
9266
|
+
this.log = logger ?? consoleLogger;
|
|
9267
|
+
}
|
|
9268
|
+
/**
|
|
9269
|
+
* Run the saga with the given input.
|
|
9270
|
+
* Returns a discriminated union result - either success with data or failure with error details.
|
|
9271
|
+
*/
|
|
9272
|
+
async run(input) {
|
|
9273
|
+
this.completedSteps = [];
|
|
9274
|
+
this.currentStepName = "unknown";
|
|
9275
|
+
this.stepTimings = [];
|
|
9276
|
+
const sagaStart = performance.now();
|
|
9277
|
+
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
9278
|
+
try {
|
|
9279
|
+
const result = await this.execute(input);
|
|
9280
|
+
const totalDuration = performance.now() - sagaStart;
|
|
9281
|
+
this.log.debug("Saga completed successfully", {
|
|
9282
|
+
sagaName: this.constructor.name,
|
|
9283
|
+
stepsCompleted: this.completedSteps.length,
|
|
9284
|
+
totalDurationMs: Math.round(totalDuration),
|
|
9285
|
+
stepTimings: this.stepTimings
|
|
9286
|
+
});
|
|
9287
|
+
return { success: true, data: result };
|
|
9288
|
+
} catch (error) {
|
|
9289
|
+
this.log.error("Saga failed, initiating rollback", {
|
|
9290
|
+
sagaName: this.constructor.name,
|
|
9291
|
+
failedStep: this.currentStepName,
|
|
9292
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9293
|
+
});
|
|
9294
|
+
await this.rollback();
|
|
9295
|
+
return {
|
|
9296
|
+
success: false,
|
|
9297
|
+
error: error instanceof Error ? error.message : String(error),
|
|
9298
|
+
failedStep: this.currentStepName
|
|
9299
|
+
};
|
|
9300
|
+
}
|
|
9301
|
+
}
|
|
9302
|
+
/**
|
|
9303
|
+
* Execute a step with its rollback action.
|
|
9304
|
+
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
9305
|
+
* The step name is automatically tracked for error reporting.
|
|
9306
|
+
*
|
|
9307
|
+
* @param config - Step configuration with name, execute, and rollback functions
|
|
9308
|
+
* @returns The result of the execute function
|
|
9309
|
+
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
9310
|
+
*/
|
|
9311
|
+
async step(config) {
|
|
9312
|
+
this.currentStepName = config.name;
|
|
9313
|
+
this.log.debug(`Executing step: ${config.name}`);
|
|
9314
|
+
const stepStart = performance.now();
|
|
9315
|
+
const result = await config.execute();
|
|
9316
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9317
|
+
this.stepTimings.push({ name: config.name, durationMs });
|
|
9318
|
+
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
9319
|
+
this.completedSteps.push({
|
|
9320
|
+
name: config.name,
|
|
9321
|
+
rollback: () => config.rollback(result)
|
|
9322
|
+
});
|
|
9323
|
+
return result;
|
|
9324
|
+
}
|
|
9325
|
+
/**
|
|
9326
|
+
* Execute a step that doesn't need rollback.
|
|
9327
|
+
* Useful for read-only operations or operations that are idempotent.
|
|
9328
|
+
* The step name is automatically tracked for error reporting.
|
|
9329
|
+
*
|
|
9330
|
+
* @param name - Step name for logging and error tracking
|
|
9331
|
+
* @param execute - The action to execute
|
|
9332
|
+
* @returns The result of the execute function
|
|
9333
|
+
*/
|
|
9334
|
+
async readOnlyStep(name, execute) {
|
|
9335
|
+
this.currentStepName = name;
|
|
9336
|
+
this.log.debug(`Executing read-only step: ${name}`);
|
|
9337
|
+
const stepStart = performance.now();
|
|
9338
|
+
const result = await execute();
|
|
9339
|
+
const durationMs = Math.round(performance.now() - stepStart);
|
|
9340
|
+
this.stepTimings.push({ name, durationMs });
|
|
9341
|
+
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
9342
|
+
return result;
|
|
9343
|
+
}
|
|
9344
|
+
/**
|
|
9345
|
+
* Roll back all completed steps in reverse order.
|
|
9346
|
+
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
9347
|
+
*/
|
|
9348
|
+
async rollback() {
|
|
9349
|
+
this.log.info("Rolling back saga", {
|
|
9350
|
+
stepsToRollback: this.completedSteps.length
|
|
9351
|
+
});
|
|
9352
|
+
const stepsReversed = [...this.completedSteps].reverse();
|
|
9353
|
+
for (const step of stepsReversed) {
|
|
9354
|
+
try {
|
|
9355
|
+
this.log.debug(`Rolling back step: ${step.name}`);
|
|
9356
|
+
await step.rollback();
|
|
9357
|
+
this.log.debug(`Step rolled back: ${step.name}`);
|
|
9358
|
+
} catch (error) {
|
|
9359
|
+
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
9360
|
+
error: error instanceof Error ? error.message : String(error)
|
|
9361
|
+
});
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
this.log.info("Rollback completed", {
|
|
9365
|
+
stepsAttempted: this.completedSteps.length
|
|
9366
|
+
});
|
|
9367
|
+
}
|
|
9368
|
+
/**
|
|
9369
|
+
* Get the number of completed steps (useful for testing)
|
|
9370
|
+
*/
|
|
9371
|
+
getCompletedStepCount() {
|
|
9372
|
+
return this.completedSteps.length;
|
|
9373
|
+
}
|
|
9374
|
+
};
|
|
9375
|
+
|
|
9453
9376
|
// ../git/dist/sagas/tree.js
|
|
9454
9377
|
import { existsSync as existsSync4 } from "fs";
|
|
9455
9378
|
import * as fs5 from "fs/promises";
|