@posthog/agent 2.1.45 → 2.1.47
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-DcBmoTR4.d.ts → agent-BJ7Uacyp.d.ts} +2 -0
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +140 -56
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +265 -183
- package/dist/index.js.map +1 -1
- package/dist/server/agent-server.js +264 -183
- package/dist/server/agent-server.js.map +1 -1
- package/dist/server/bin.cjs +264 -183
- package/dist/server/bin.cjs.map +1 -1
- package/package.json +3 -3
- package/src/adapters/acp-connection.ts +6 -1
- package/src/adapters/claude/claude-agent.ts +105 -60
- package/src/agent.ts +1 -0
package/dist/server/bin.cjs
CHANGED
|
@@ -1170,12 +1170,174 @@ 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
|
|
1173
1335
|
var import_uuid = require("uuid");
|
|
1174
1336
|
|
|
1175
1337
|
// package.json
|
|
1176
1338
|
var package_default = {
|
|
1177
1339
|
name: "@posthog/agent",
|
|
1178
|
-
version: "2.1.
|
|
1340
|
+
version: "2.1.47",
|
|
1179
1341
|
repository: "https://github.com/PostHog/twig",
|
|
1180
1342
|
description: "TypeScript agent framework wrapping Claude Agent SDK with Git-based task execution for PostHog",
|
|
1181
1343
|
exports: {
|
|
@@ -3248,12 +3410,14 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3248
3410
|
backgroundTerminals = {};
|
|
3249
3411
|
clientCapabilities;
|
|
3250
3412
|
logWriter;
|
|
3251
|
-
|
|
3413
|
+
options;
|
|
3252
3414
|
lastSentConfigOptions;
|
|
3253
|
-
|
|
3415
|
+
debug;
|
|
3416
|
+
constructor(client, logWriter, options) {
|
|
3254
3417
|
super(client);
|
|
3255
3418
|
this.logWriter = logWriter;
|
|
3256
|
-
this.
|
|
3419
|
+
this.options = options;
|
|
3420
|
+
this.debug = options?.debug ?? false;
|
|
3257
3421
|
this.toolUseCache = {};
|
|
3258
3422
|
this.logger = new Logger({ debug: true, prefix: "[ClaudeAcpAgent]" });
|
|
3259
3423
|
}
|
|
@@ -3296,27 +3460,36 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3296
3460
|
}
|
|
3297
3461
|
async newSession(params) {
|
|
3298
3462
|
this.checkAuthStatus();
|
|
3463
|
+
const tc = createTimingCollector(
|
|
3464
|
+
this.debug,
|
|
3465
|
+
(msg, data) => this.logger.info(msg, data)
|
|
3466
|
+
);
|
|
3299
3467
|
const meta = params._meta;
|
|
3300
3468
|
const sessionId = (0, import_uuid.v7)();
|
|
3301
3469
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3302
|
-
const mcpServers =
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3470
|
+
const mcpServers = tc.timeSync(
|
|
3471
|
+
"parseMcpServers",
|
|
3472
|
+
() => parseMcpServers(params)
|
|
3473
|
+
);
|
|
3474
|
+
const options = tc.timeSync(
|
|
3475
|
+
"buildSessionOptions",
|
|
3476
|
+
() => buildSessionOptions({
|
|
3477
|
+
cwd: params.cwd,
|
|
3478
|
+
mcpServers,
|
|
3479
|
+
permissionMode,
|
|
3480
|
+
canUseTool: this.createCanUseTool(sessionId),
|
|
3481
|
+
logger: this.logger,
|
|
3482
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3483
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3484
|
+
sessionId,
|
|
3485
|
+
isResume: false,
|
|
3486
|
+
onModeChange: this.createOnModeChange(sessionId),
|
|
3487
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3488
|
+
onProcessExited: this.options?.onProcessExited
|
|
3489
|
+
})
|
|
3490
|
+
);
|
|
3318
3491
|
const input = new Pushable();
|
|
3319
|
-
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3492
|
+
const q = tc.timeSync("sdkQuery", () => (0, import_claude_agent_sdk.query)({ prompt: input, options }));
|
|
3320
3493
|
const session = this.createSession(
|
|
3321
3494
|
sessionId,
|
|
3322
3495
|
q,
|
|
@@ -3328,29 +3501,40 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3328
3501
|
session.taskRunId = meta?.taskRunId;
|
|
3329
3502
|
this.registerPersistence(sessionId, meta);
|
|
3330
3503
|
if (meta?.taskRunId) {
|
|
3331
|
-
await
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3504
|
+
await tc.time(
|
|
3505
|
+
"extNotification",
|
|
3506
|
+
() => this.client.extNotification("_posthog/sdk_session", {
|
|
3507
|
+
taskRunId: meta.taskRunId,
|
|
3508
|
+
sessionId,
|
|
3509
|
+
adapter: "claude"
|
|
3510
|
+
})
|
|
3511
|
+
);
|
|
3336
3512
|
}
|
|
3337
|
-
const
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3513
|
+
const modelOptions = await tc.time(
|
|
3514
|
+
"fetchModels",
|
|
3515
|
+
() => this.getModelConfigOptions()
|
|
3516
|
+
);
|
|
3517
|
+
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
3342
3518
|
session.modelId = modelOptions.currentModelId;
|
|
3343
3519
|
await this.trySetModel(q, modelOptions.currentModelId);
|
|
3344
|
-
|
|
3520
|
+
const configOptions = await tc.time(
|
|
3521
|
+
"buildConfigOptions",
|
|
3522
|
+
() => this.buildConfigOptions(modelOptions)
|
|
3523
|
+
);
|
|
3524
|
+
tc.summarize("newSession");
|
|
3345
3525
|
return {
|
|
3346
3526
|
sessionId,
|
|
3347
|
-
configOptions
|
|
3527
|
+
configOptions
|
|
3348
3528
|
};
|
|
3349
3529
|
}
|
|
3350
3530
|
async loadSession(params) {
|
|
3351
3531
|
return this.resumeSession(params);
|
|
3352
3532
|
}
|
|
3353
3533
|
async resumeSession(params) {
|
|
3534
|
+
const tc = createTimingCollector(
|
|
3535
|
+
this.debug,
|
|
3536
|
+
(msg, data) => this.logger.info(msg, data)
|
|
3537
|
+
);
|
|
3354
3538
|
const meta = params._meta;
|
|
3355
3539
|
const sessionId = meta?.sessionId;
|
|
3356
3540
|
if (!sessionId) {
|
|
@@ -3359,29 +3543,33 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3359
3543
|
if (this.sessionId === sessionId) {
|
|
3360
3544
|
return {};
|
|
3361
3545
|
}
|
|
3362
|
-
const mcpServers =
|
|
3363
|
-
|
|
3546
|
+
const mcpServers = tc.timeSync(
|
|
3547
|
+
"parseMcpServers",
|
|
3548
|
+
() => parseMcpServers(params)
|
|
3549
|
+
);
|
|
3364
3550
|
const permissionMode = meta?.permissionMode && TWIG_EXECUTION_MODES.includes(meta.permissionMode) ? meta.permissionMode : "default";
|
|
3365
|
-
const { query: q, session } = await
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3551
|
+
const { query: q, session } = await tc.time(
|
|
3552
|
+
"initializeQuery",
|
|
3553
|
+
() => this.initializeQuery({
|
|
3554
|
+
cwd: params.cwd,
|
|
3555
|
+
permissionMode,
|
|
3556
|
+
mcpServers,
|
|
3557
|
+
systemPrompt: buildSystemPrompt(meta?.systemPrompt),
|
|
3558
|
+
userProvidedOptions: meta?.claudeCode?.options,
|
|
3559
|
+
sessionId,
|
|
3560
|
+
isResume: true,
|
|
3561
|
+
additionalDirectories: meta?.claudeCode?.options?.additionalDirectories
|
|
3562
|
+
})
|
|
3563
|
+
);
|
|
3375
3564
|
session.taskRunId = meta?.taskRunId;
|
|
3376
3565
|
this.registerPersistence(sessionId, meta);
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
};
|
|
3566
|
+
this.deferBackgroundFetches(tc, q, sessionId, mcpServers);
|
|
3567
|
+
const configOptions = await tc.time(
|
|
3568
|
+
"buildConfigOptions",
|
|
3569
|
+
() => this.buildConfigOptions()
|
|
3570
|
+
);
|
|
3571
|
+
tc.summarize("resumeSession");
|
|
3572
|
+
return { configOptions };
|
|
3385
3573
|
}
|
|
3386
3574
|
async prompt(params) {
|
|
3387
3575
|
this.session.cancelled = false;
|
|
@@ -3453,8 +3641,8 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3453
3641
|
isResume: config.isResume,
|
|
3454
3642
|
additionalDirectories: config.additionalDirectories,
|
|
3455
3643
|
onModeChange: this.createOnModeChange(config.sessionId),
|
|
3456
|
-
onProcessSpawned: this.
|
|
3457
|
-
onProcessExited: this.
|
|
3644
|
+
onProcessSpawned: this.options?.onProcessSpawned,
|
|
3645
|
+
onProcessExited: this.options?.onProcessExited
|
|
3458
3646
|
});
|
|
3459
3647
|
const q = (0, import_claude_agent_sdk.query)({ prompt: input, options });
|
|
3460
3648
|
const abortController = options.abortController;
|
|
@@ -3564,6 +3752,23 @@ var ClaudeAcpAgent = class extends BaseAcpAgent {
|
|
|
3564
3752
|
await q.setModel(fallback);
|
|
3565
3753
|
}
|
|
3566
3754
|
}
|
|
3755
|
+
/**
|
|
3756
|
+
* Fire-and-forget: fetch slash commands and MCP tool metadata in parallel.
|
|
3757
|
+
* Both populate caches used later — neither is needed to return configOptions.
|
|
3758
|
+
*/
|
|
3759
|
+
deferBackgroundFetches(tc, q, sessionId, mcpServers) {
|
|
3760
|
+
Promise.all([
|
|
3761
|
+
tc.time("slashCommands", () => getAvailableSlashCommands(q)),
|
|
3762
|
+
tc.time(
|
|
3763
|
+
"mcpMetadata",
|
|
3764
|
+
() => fetchMcpToolMetadata(mcpServers, this.logger)
|
|
3765
|
+
)
|
|
3766
|
+
]).then(([slashCommands]) => {
|
|
3767
|
+
this.sendAvailableCommandsUpdate(sessionId, slashCommands);
|
|
3768
|
+
}).catch((err) => {
|
|
3769
|
+
this.logger.warn("Failed to fetch deferred session data", { err });
|
|
3770
|
+
});
|
|
3771
|
+
}
|
|
3567
3772
|
registerPersistence(sessionId, meta) {
|
|
3568
3773
|
const persistence = meta?.persistence;
|
|
3569
3774
|
if (persistence && this.logWriter) {
|
|
@@ -3851,7 +4056,10 @@ function createClaudeConnection(config) {
|
|
|
3851
4056
|
const agentStream = (0, import_sdk3.ndJsonStream)(agentWritable, streams.agent.readable);
|
|
3852
4057
|
let agent = null;
|
|
3853
4058
|
const agentConnection = new import_sdk3.AgentSideConnection((client) => {
|
|
3854
|
-
agent = new ClaudeAcpAgent(client, logWriter,
|
|
4059
|
+
agent = new ClaudeAcpAgent(client, logWriter, {
|
|
4060
|
+
...config.processCallbacks,
|
|
4061
|
+
debug: config.debug
|
|
4062
|
+
});
|
|
3855
4063
|
logger.info(`Created ${agent.adapterName} agent`);
|
|
3856
4064
|
return agent;
|
|
3857
4065
|
}, agentStream);
|
|
@@ -9234,133 +9442,6 @@ async function getHeadSha(baseDir, options) {
|
|
|
9234
9442
|
var import_promises2 = require("fs/promises");
|
|
9235
9443
|
var import_node_path3 = require("path");
|
|
9236
9444
|
|
|
9237
|
-
// ../shared/dist/index.js
|
|
9238
|
-
var consoleLogger = {
|
|
9239
|
-
info: (_message, _data) => {
|
|
9240
|
-
},
|
|
9241
|
-
debug: (_message, _data) => {
|
|
9242
|
-
},
|
|
9243
|
-
error: (_message, _data) => {
|
|
9244
|
-
},
|
|
9245
|
-
warn: (_message, _data) => {
|
|
9246
|
-
}
|
|
9247
|
-
};
|
|
9248
|
-
var Saga = class {
|
|
9249
|
-
completedSteps = [];
|
|
9250
|
-
currentStepName = "unknown";
|
|
9251
|
-
stepTimings = [];
|
|
9252
|
-
log;
|
|
9253
|
-
constructor(logger) {
|
|
9254
|
-
this.log = logger ?? consoleLogger;
|
|
9255
|
-
}
|
|
9256
|
-
/**
|
|
9257
|
-
* Run the saga with the given input.
|
|
9258
|
-
* Returns a discriminated union result - either success with data or failure with error details.
|
|
9259
|
-
*/
|
|
9260
|
-
async run(input) {
|
|
9261
|
-
this.completedSteps = [];
|
|
9262
|
-
this.currentStepName = "unknown";
|
|
9263
|
-
this.stepTimings = [];
|
|
9264
|
-
const sagaStart = performance.now();
|
|
9265
|
-
this.log.info("Starting saga", { sagaName: this.constructor.name });
|
|
9266
|
-
try {
|
|
9267
|
-
const result = await this.execute(input);
|
|
9268
|
-
const totalDuration = performance.now() - sagaStart;
|
|
9269
|
-
this.log.debug("Saga completed successfully", {
|
|
9270
|
-
sagaName: this.constructor.name,
|
|
9271
|
-
stepsCompleted: this.completedSteps.length,
|
|
9272
|
-
totalDurationMs: Math.round(totalDuration),
|
|
9273
|
-
stepTimings: this.stepTimings
|
|
9274
|
-
});
|
|
9275
|
-
return { success: true, data: result };
|
|
9276
|
-
} catch (error) {
|
|
9277
|
-
this.log.error("Saga failed, initiating rollback", {
|
|
9278
|
-
sagaName: this.constructor.name,
|
|
9279
|
-
failedStep: this.currentStepName,
|
|
9280
|
-
error: error instanceof Error ? error.message : String(error)
|
|
9281
|
-
});
|
|
9282
|
-
await this.rollback();
|
|
9283
|
-
return {
|
|
9284
|
-
success: false,
|
|
9285
|
-
error: error instanceof Error ? error.message : String(error),
|
|
9286
|
-
failedStep: this.currentStepName
|
|
9287
|
-
};
|
|
9288
|
-
}
|
|
9289
|
-
}
|
|
9290
|
-
/**
|
|
9291
|
-
* Execute a step with its rollback action.
|
|
9292
|
-
* If the step succeeds, its rollback action is stored for potential rollback.
|
|
9293
|
-
* The step name is automatically tracked for error reporting.
|
|
9294
|
-
*
|
|
9295
|
-
* @param config - Step configuration with name, execute, and rollback functions
|
|
9296
|
-
* @returns The result of the execute function
|
|
9297
|
-
* @throws Re-throws any error from the execute function (triggers rollback)
|
|
9298
|
-
*/
|
|
9299
|
-
async step(config) {
|
|
9300
|
-
this.currentStepName = config.name;
|
|
9301
|
-
this.log.debug(`Executing step: ${config.name}`);
|
|
9302
|
-
const stepStart = performance.now();
|
|
9303
|
-
const result = await config.execute();
|
|
9304
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
9305
|
-
this.stepTimings.push({ name: config.name, durationMs });
|
|
9306
|
-
this.log.debug(`Step completed: ${config.name}`, { durationMs });
|
|
9307
|
-
this.completedSteps.push({
|
|
9308
|
-
name: config.name,
|
|
9309
|
-
rollback: () => config.rollback(result)
|
|
9310
|
-
});
|
|
9311
|
-
return result;
|
|
9312
|
-
}
|
|
9313
|
-
/**
|
|
9314
|
-
* Execute a step that doesn't need rollback.
|
|
9315
|
-
* Useful for read-only operations or operations that are idempotent.
|
|
9316
|
-
* The step name is automatically tracked for error reporting.
|
|
9317
|
-
*
|
|
9318
|
-
* @param name - Step name for logging and error tracking
|
|
9319
|
-
* @param execute - The action to execute
|
|
9320
|
-
* @returns The result of the execute function
|
|
9321
|
-
*/
|
|
9322
|
-
async readOnlyStep(name, execute) {
|
|
9323
|
-
this.currentStepName = name;
|
|
9324
|
-
this.log.debug(`Executing read-only step: ${name}`);
|
|
9325
|
-
const stepStart = performance.now();
|
|
9326
|
-
const result = await execute();
|
|
9327
|
-
const durationMs = Math.round(performance.now() - stepStart);
|
|
9328
|
-
this.stepTimings.push({ name, durationMs });
|
|
9329
|
-
this.log.debug(`Read-only step completed: ${name}`, { durationMs });
|
|
9330
|
-
return result;
|
|
9331
|
-
}
|
|
9332
|
-
/**
|
|
9333
|
-
* Roll back all completed steps in reverse order.
|
|
9334
|
-
* Rollback errors are logged but don't stop the rollback of other steps.
|
|
9335
|
-
*/
|
|
9336
|
-
async rollback() {
|
|
9337
|
-
this.log.info("Rolling back saga", {
|
|
9338
|
-
stepsToRollback: this.completedSteps.length
|
|
9339
|
-
});
|
|
9340
|
-
const stepsReversed = [...this.completedSteps].reverse();
|
|
9341
|
-
for (const step of stepsReversed) {
|
|
9342
|
-
try {
|
|
9343
|
-
this.log.debug(`Rolling back step: ${step.name}`);
|
|
9344
|
-
await step.rollback();
|
|
9345
|
-
this.log.debug(`Step rolled back: ${step.name}`);
|
|
9346
|
-
} catch (error) {
|
|
9347
|
-
this.log.error(`Failed to rollback step: ${step.name}`, {
|
|
9348
|
-
error: error instanceof Error ? error.message : String(error)
|
|
9349
|
-
});
|
|
9350
|
-
}
|
|
9351
|
-
}
|
|
9352
|
-
this.log.info("Rollback completed", {
|
|
9353
|
-
stepsAttempted: this.completedSteps.length
|
|
9354
|
-
});
|
|
9355
|
-
}
|
|
9356
|
-
/**
|
|
9357
|
-
* Get the number of completed steps (useful for testing)
|
|
9358
|
-
*/
|
|
9359
|
-
getCompletedStepCount() {
|
|
9360
|
-
return this.completedSteps.length;
|
|
9361
|
-
}
|
|
9362
|
-
};
|
|
9363
|
-
|
|
9364
9445
|
// ../git/dist/sagas/tree.js
|
|
9365
9446
|
var import_node_fs2 = require("fs");
|
|
9366
9447
|
var fs5 = __toESM(require("fs/promises"), 1);
|