@a5c-ai/genty-runtime 5.1.1-staging.0007199a1cb2
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/README.md +69 -0
- package/dist/apiResult.d.ts +19 -0
- package/dist/apiResult.d.ts.map +1 -0
- package/dist/apiResult.js +16 -0
- package/dist/background/state.d.ts +20 -0
- package/dist/background/state.d.ts.map +1 -0
- package/dist/background/state.js +52 -0
- package/dist/backgroundProcessRegistry.d.ts +124 -0
- package/dist/backgroundProcessRegistry.d.ts.map +1 -0
- package/dist/backgroundProcessRegistry.js +427 -0
- package/dist/cost/claudeCodeParser.d.ts +81 -0
- package/dist/cost/claudeCodeParser.d.ts.map +1 -0
- package/dist/cost/claudeCodeParser.js +232 -0
- package/dist/cost/collector.d.ts +42 -0
- package/dist/cost/collector.d.ts.map +1 -0
- package/dist/cost/collector.js +105 -0
- package/dist/cost/effectCost.d.ts +23 -0
- package/dist/cost/effectCost.d.ts.map +1 -0
- package/dist/cost/effectCost.js +26 -0
- package/dist/cost/index.d.ts +19 -0
- package/dist/cost/index.d.ts.map +1 -0
- package/dist/cost/index.js +39 -0
- package/dist/cost/journal.d.ts +40 -0
- package/dist/cost/journal.d.ts.map +1 -0
- package/dist/cost/journal.js +137 -0
- package/dist/cost/types.d.ts +164 -0
- package/dist/cost/types.d.ts.map +1 -0
- package/dist/cost/types.js +228 -0
- package/dist/daemon/automationExecutor.d.ts +16 -0
- package/dist/daemon/automationExecutor.d.ts.map +1 -0
- package/dist/daemon/automationExecutor.js +222 -0
- package/dist/daemon/config.d.ts +8 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +245 -0
- package/dist/daemon/daemonLog.d.ts +30 -0
- package/dist/daemon/daemonLog.d.ts.map +1 -0
- package/dist/daemon/daemonLog.js +140 -0
- package/dist/daemon/durableQueue.d.ts +41 -0
- package/dist/daemon/durableQueue.d.ts.map +1 -0
- package/dist/daemon/durableQueue.js +183 -0
- package/dist/daemon/fileWatcher.d.ts +9 -0
- package/dist/daemon/fileWatcher.d.ts.map +1 -0
- package/dist/daemon/fileWatcher.js +144 -0
- package/dist/daemon/index.d.ts +15 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +25 -0
- package/dist/daemon/lifecycle.d.ts +13 -0
- package/dist/daemon/lifecycle.d.ts.map +1 -0
- package/dist/daemon/lifecycle.js +320 -0
- package/dist/daemon/loop.d.ts +27 -0
- package/dist/daemon/loop.d.ts.map +1 -0
- package/dist/daemon/loop.js +387 -0
- package/dist/daemon/timerScheduler.d.ts +13 -0
- package/dist/daemon/timerScheduler.d.ts.map +1 -0
- package/dist/daemon/timerScheduler.js +212 -0
- package/dist/daemon/types.d.ts +122 -0
- package/dist/daemon/types.d.ts.map +1 -0
- package/dist/daemon/types.js +25 -0
- package/dist/daemon/webhookListener.d.ts +6 -0
- package/dist/daemon/webhookListener.d.ts.map +1 -0
- package/dist/daemon/webhookListener.js +132 -0
- package/dist/execution/index.d.ts +10 -0
- package/dist/execution/index.d.ts.map +1 -0
- package/dist/execution/index.js +20 -0
- package/dist/execution/modes/docker.d.ts +26 -0
- package/dist/execution/modes/docker.d.ts.map +1 -0
- package/dist/execution/modes/docker.js +183 -0
- package/dist/execution/modes/index.d.ts +10 -0
- package/dist/execution/modes/index.d.ts.map +1 -0
- package/dist/execution/modes/index.js +14 -0
- package/dist/execution/modes/kubernetes.d.ts +46 -0
- package/dist/execution/modes/kubernetes.d.ts.map +1 -0
- package/dist/execution/modes/kubernetes.js +334 -0
- package/dist/execution/modes/local.d.ts +23 -0
- package/dist/execution/modes/local.d.ts.map +1 -0
- package/dist/execution/modes/local.js +117 -0
- package/dist/execution/modes/ssh.d.ts +23 -0
- package/dist/execution/modes/ssh.d.ts.map +1 -0
- package/dist/execution/modes/ssh.js +144 -0
- package/dist/execution/policy.d.ts +15 -0
- package/dist/execution/policy.d.ts.map +1 -0
- package/dist/execution/policy.js +121 -0
- package/dist/execution/provider.d.ts +32 -0
- package/dist/execution/provider.d.ts.map +1 -0
- package/dist/execution/provider.js +90 -0
- package/dist/execution/types.d.ts +189 -0
- package/dist/execution/types.d.ts.map +1 -0
- package/dist/execution/types.js +9 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/observability/diagnostics.d.ts +25 -0
- package/dist/observability/diagnostics.d.ts.map +1 -0
- package/dist/observability/diagnostics.js +98 -0
- package/dist/observability/health.d.ts +19 -0
- package/dist/observability/health.d.ts.map +1 -0
- package/dist/observability/health.js +145 -0
- package/dist/observability/index.d.ts +7 -0
- package/dist/observability/index.d.ts.map +1 -0
- package/dist/observability/index.js +25 -0
- package/dist/observability/runStatus.d.ts +44 -0
- package/dist/observability/runStatus.d.ts.map +1 -0
- package/dist/observability/runStatus.js +170 -0
- package/dist/observability/timeline.d.ts +11 -0
- package/dist/observability/timeline.d.ts.map +1 -0
- package/dist/observability/timeline.js +176 -0
- package/dist/observability/types.d.ts +65 -0
- package/dist/observability/types.d.ts.map +1 -0
- package/dist/observability/types.js +8 -0
- package/dist/observability/webhooks.d.ts +68 -0
- package/dist/observability/webhooks.d.ts.map +1 -0
- package/dist/observability/webhooks.js +132 -0
- package/dist/resources/budget-tracker.d.ts +56 -0
- package/dist/resources/budget-tracker.d.ts.map +1 -0
- package/dist/resources/budget-tracker.js +131 -0
- package/dist/resources/concurrency-guard.d.ts +55 -0
- package/dist/resources/concurrency-guard.d.ts.map +1 -0
- package/dist/resources/concurrency-guard.js +132 -0
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +20 -0
- package/dist/resources/manager.d.ts +52 -0
- package/dist/resources/manager.d.ts.map +1 -0
- package/dist/resources/manager.js +150 -0
- package/dist/resources/timeout-cascade.d.ts +56 -0
- package/dist/resources/timeout-cascade.d.ts.map +1 -0
- package/dist/resources/timeout-cascade.js +145 -0
- package/dist/resources/types.d.ts +130 -0
- package/dist/resources/types.d.ts.map +1 -0
- package/dist/resources/types.js +9 -0
- package/dist/rpc/index.d.ts +5 -0
- package/dist/rpc/index.d.ts.map +1 -0
- package/dist/rpc/index.js +7 -0
- package/dist/rpc/server.d.ts +13 -0
- package/dist/rpc/server.d.ts.map +1 -0
- package/dist/rpc/server.js +64 -0
- package/dist/rpc/server.test.d.ts +2 -0
- package/dist/rpc/server.test.d.ts.map +1 -0
- package/dist/rpc/server.test.js +35 -0
- package/dist/rpc/types.d.ts +23 -0
- package/dist/rpc/types.d.ts.map +1 -0
- package/dist/rpc/types.js +20 -0
- package/dist/session/context.d.ts +22 -0
- package/dist/session/context.d.ts.map +1 -0
- package/dist/session/context.js +113 -0
- package/dist/session/continuityState.d.ts +39 -0
- package/dist/session/continuityState.d.ts.map +1 -0
- package/dist/session/continuityState.js +164 -0
- package/dist/session/cost.d.ts +63 -0
- package/dist/session/cost.d.ts.map +1 -0
- package/dist/session/cost.js +194 -0
- package/dist/session/discovery.d.ts +24 -0
- package/dist/session/discovery.d.ts.map +1 -0
- package/dist/session/discovery.js +43 -0
- package/dist/session/export.d.ts +4 -0
- package/dist/session/export.d.ts.map +1 -0
- package/dist/session/export.js +56 -0
- package/dist/session/export.test.d.ts +2 -0
- package/dist/session/export.test.d.ts.map +1 -0
- package/dist/session/export.test.js +42 -0
- package/dist/session/history.d.ts +30 -0
- package/dist/session/history.d.ts.map +1 -0
- package/dist/session/history.js +143 -0
- package/dist/session/index.d.ts +20 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +78 -0
- package/dist/session/memoryExtraction.d.ts +65 -0
- package/dist/session/memoryExtraction.d.ts.map +1 -0
- package/dist/session/memoryExtraction.js +201 -0
- package/dist/session/parse.d.ts +45 -0
- package/dist/session/parse.d.ts.map +1 -0
- package/dist/session/parse.js +170 -0
- package/dist/session/persistence.d.ts +46 -0
- package/dist/session/persistence.d.ts.map +1 -0
- package/dist/session/persistence.js +180 -0
- package/dist/session/rewind.d.ts +45 -0
- package/dist/session/rewind.d.ts.map +1 -0
- package/dist/session/rewind.js +68 -0
- package/dist/session/rewind.test.d.ts +2 -0
- package/dist/session/rewind.test.d.ts.map +1 -0
- package/dist/session/rewind.test.js +96 -0
- package/dist/session/tree.d.ts +29 -0
- package/dist/session/tree.d.ts.map +1 -0
- package/dist/session/tree.js +115 -0
- package/dist/session/tree.test.d.ts +2 -0
- package/dist/session/tree.test.d.ts.map +1 -0
- package/dist/session/tree.test.js +75 -0
- package/dist/session/types.d.ts +267 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +45 -0
- package/dist/session/write.d.ts +61 -0
- package/dist/session/write.d.ts.map +1 -0
- package/dist/session/write.js +213 -0
- package/dist/shellInvocation.d.ts +6 -0
- package/dist/shellInvocation.d.ts.map +1 -0
- package/dist/shellInvocation.js +8 -0
- package/dist/shellInvocation.test.d.ts +2 -0
- package/dist/shellInvocation.test.d.ts.map +1 -0
- package/dist/shellInvocation.test.js +18 -0
- package/dist/storage/journal.d.ts +17 -0
- package/dist/storage/journal.d.ts.map +1 -0
- package/dist/storage/journal.js +165 -0
- package/dist/storage/runFiles.d.ts +10 -0
- package/dist/storage/runFiles.d.ts.map +1 -0
- package/dist/storage/runFiles.js +54 -0
- package/dist/telemetry/audit-log.d.ts +56 -0
- package/dist/telemetry/audit-log.d.ts.map +1 -0
- package/dist/telemetry/audit-log.js +59 -0
- package/dist/telemetry/exporters.d.ts +35 -0
- package/dist/telemetry/exporters.d.ts.map +1 -0
- package/dist/telemetry/exporters.js +141 -0
- package/dist/telemetry/index.d.ts +12 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +25 -0
- package/dist/telemetry/provider.d.ts +57 -0
- package/dist/telemetry/provider.d.ts.map +1 -0
- package/dist/telemetry/provider.js +261 -0
- package/dist/telemetry/span-tree.d.ts +46 -0
- package/dist/telemetry/span-tree.d.ts.map +1 -0
- package/dist/telemetry/span-tree.js +93 -0
- package/dist/telemetry/traceContext.d.ts +10 -0
- package/dist/telemetry/traceContext.d.ts.map +1 -0
- package/dist/telemetry/traceContext.js +43 -0
- package/dist/telemetry/types.d.ts +109 -0
- package/dist/telemetry/types.d.ts.map +1 -0
- package/dist/telemetry/types.js +21 -0
- package/dist/types/sdk.d.ts +66 -0
- package/dist/types/sdk.d.ts.map +1 -0
- package/dist/types/sdk.js +9 -0
- package/dist/utils/ulid.d.ts +11 -0
- package/dist/utils/ulid.d.ts.map +1 -0
- package/dist/utils/ulid.js +62 -0
- package/package.json +137 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Background process lifecycle management for the bash agentic tool (GAP-TOOLS-036).
|
|
4
|
+
*
|
|
5
|
+
* Tracks child processes spawned with `run_in_background: true`, collects
|
|
6
|
+
* stdout/stderr, fires completion callbacks, and enforces a concurrency limit.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.BackgroundProcessRegistry = void 0;
|
|
43
|
+
const childProcess = __importStar(require("node:child_process"));
|
|
44
|
+
const ulid_1 = require("./utils/ulid");
|
|
45
|
+
const execution_1 = require("./execution");
|
|
46
|
+
const shellInvocation_1 = require("./shellInvocation");
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Registry
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
const DEFAULT_MAX_CONCURRENT = 16;
|
|
51
|
+
const DEFAULT_TERMINATION_GRACE_MS = 3_000;
|
|
52
|
+
const TERMINAL_STATUSES = new Set([
|
|
53
|
+
"completed",
|
|
54
|
+
"exited",
|
|
55
|
+
"cancelled",
|
|
56
|
+
"killed",
|
|
57
|
+
"timed_out",
|
|
58
|
+
"skipped",
|
|
59
|
+
"failed",
|
|
60
|
+
]);
|
|
61
|
+
class BackgroundProcessRegistry {
|
|
62
|
+
processes = new Map();
|
|
63
|
+
maxConcurrent;
|
|
64
|
+
spawnFn;
|
|
65
|
+
processKillFn;
|
|
66
|
+
platform;
|
|
67
|
+
constructor(options) {
|
|
68
|
+
this.maxConcurrent = options?.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
|
|
69
|
+
this.spawnFn = options?.spawnFn ?? childProcess.spawn;
|
|
70
|
+
this.processKillFn = options?.processKillFn ?? process.kill;
|
|
71
|
+
this.platform = options?.platform ?? process.platform;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Spawn a command in the background and begin tracking it.
|
|
75
|
+
* Returns a snapshot of the initial record.
|
|
76
|
+
*/
|
|
77
|
+
spawn(opts) {
|
|
78
|
+
(0, execution_1.validateFilesystemPolicy)(opts.cwd, opts.executionPolicy);
|
|
79
|
+
const dependsOn = [...(opts.dependsOn ?? [])];
|
|
80
|
+
validateDependencies(dependsOn);
|
|
81
|
+
for (const dependencyId of dependsOn) {
|
|
82
|
+
const dependency = this.processes.get(dependencyId);
|
|
83
|
+
if (!dependency) {
|
|
84
|
+
throw new Error(`Unknown background process dependency "${dependencyId}"`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const runningCount = [...this.processes.values()].filter((p) => p.status === "running" || p.status === "paused").length;
|
|
88
|
+
if (runningCount >= this.maxConcurrent && dependsOn.length === 0) {
|
|
89
|
+
throw new Error(`Max concurrent background processes limit reached (${this.maxConcurrent}). ` +
|
|
90
|
+
`Cancel or wait for existing processes before spawning new ones.`);
|
|
91
|
+
}
|
|
92
|
+
const backgroundTaskId = (0, ulid_1.nextUlid)();
|
|
93
|
+
const startMs = Date.now();
|
|
94
|
+
const resources = (0, execution_1.normalizeResourceLimits)(opts.executionPolicy);
|
|
95
|
+
const tracked = {
|
|
96
|
+
backgroundTaskId,
|
|
97
|
+
pid: -1,
|
|
98
|
+
command: opts.command,
|
|
99
|
+
description: opts.description,
|
|
100
|
+
startedAt: new Date(startMs).toISOString(),
|
|
101
|
+
startMs,
|
|
102
|
+
status: dependsOn.length === 0 ? "running" : "queued",
|
|
103
|
+
exitCode: null,
|
|
104
|
+
stdout: createRetainedStream(),
|
|
105
|
+
stderr: createRetainedStream(),
|
|
106
|
+
durationMs: null,
|
|
107
|
+
maxOutputBytes: resources.maxOutputBytes,
|
|
108
|
+
opts,
|
|
109
|
+
dependsOn,
|
|
110
|
+
hookErrors: [],
|
|
111
|
+
onComplete: opts.onComplete,
|
|
112
|
+
};
|
|
113
|
+
this.processes.set(backgroundTaskId, tracked);
|
|
114
|
+
this.runHook(tracked, "preSpawn");
|
|
115
|
+
if (tracked.status === "failed") {
|
|
116
|
+
tracked.durationMs = Date.now() - tracked.startMs;
|
|
117
|
+
return this.snapshot(tracked);
|
|
118
|
+
}
|
|
119
|
+
if (dependsOn.length === 0) {
|
|
120
|
+
this.startTrackedProcess(tracked);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
this.resolveQueuedProcess(tracked);
|
|
124
|
+
}
|
|
125
|
+
return this.snapshot(tracked);
|
|
126
|
+
}
|
|
127
|
+
startTrackedProcess(tracked) {
|
|
128
|
+
if (tracked.status !== "queued" && tracked.status !== "running") {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const runningCount = [...this.processes.values()].filter((p) => p !== tracked && (p.status === "running" || p.status === "paused")).length;
|
|
132
|
+
if (runningCount >= this.maxConcurrent) {
|
|
133
|
+
tracked.status = "queued";
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const opts = tracked.opts;
|
|
137
|
+
const shellInvocation = (0, shellInvocation_1.buildShellInvocation)(opts.command);
|
|
138
|
+
const child = this.spawnFn(shellInvocation.command, shellInvocation.args, {
|
|
139
|
+
cwd: opts.cwd,
|
|
140
|
+
env: (0, execution_1.resolveExecutionEnvironment)(opts.env, opts.executionPolicy),
|
|
141
|
+
shell: false,
|
|
142
|
+
windowsHide: true,
|
|
143
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
144
|
+
detached: opts.terminateProcessGroup === true && this.platform !== "win32",
|
|
145
|
+
});
|
|
146
|
+
tracked.child = child;
|
|
147
|
+
tracked.pid = child.pid ?? -1;
|
|
148
|
+
tracked.status = "running";
|
|
149
|
+
child.stdout?.on("data", (chunk) => {
|
|
150
|
+
appendCapped(tracked.stdout, chunk, tracked.maxOutputBytes);
|
|
151
|
+
});
|
|
152
|
+
child.stderr?.on("data", (chunk) => {
|
|
153
|
+
appendCapped(tracked.stderr, chunk, tracked.maxOutputBytes);
|
|
154
|
+
});
|
|
155
|
+
const resources = (0, execution_1.normalizeResourceLimits)(opts.executionPolicy);
|
|
156
|
+
if (resources.timeoutMs !== undefined) {
|
|
157
|
+
tracked.timeout = setTimeout(() => {
|
|
158
|
+
if (tracked.status === "running" || tracked.status === "paused") {
|
|
159
|
+
this.terminate(tracked, "timed_out");
|
|
160
|
+
}
|
|
161
|
+
}, resources.timeoutMs);
|
|
162
|
+
}
|
|
163
|
+
child.on("close", (code) => {
|
|
164
|
+
if (tracked.timeout)
|
|
165
|
+
clearTimeout(tracked.timeout);
|
|
166
|
+
if (tracked.terminationTimer)
|
|
167
|
+
clearTimeout(tracked.terminationTimer);
|
|
168
|
+
if (tracked.status === "running" || tracked.status === "paused") {
|
|
169
|
+
tracked.status = code === 0 ? "completed" : "exited";
|
|
170
|
+
}
|
|
171
|
+
tracked.exitCode = code ?? 1;
|
|
172
|
+
tracked.durationMs = Date.now() - tracked.startMs;
|
|
173
|
+
this.runHook(tracked, "postDestroy");
|
|
174
|
+
if (tracked.onComplete) {
|
|
175
|
+
try {
|
|
176
|
+
tracked.onComplete(this.completionEvent(tracked));
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Fire-and-forget — callback errors must not crash.
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
this.resolveDependents(tracked.backgroundTaskId);
|
|
183
|
+
});
|
|
184
|
+
child.on("error", () => {
|
|
185
|
+
if (tracked.timeout)
|
|
186
|
+
clearTimeout(tracked.timeout);
|
|
187
|
+
if (tracked.terminationTimer)
|
|
188
|
+
clearTimeout(tracked.terminationTimer);
|
|
189
|
+
if (tracked.status === "running" || tracked.status === "paused") {
|
|
190
|
+
tracked.status = "exited";
|
|
191
|
+
}
|
|
192
|
+
tracked.exitCode = 1;
|
|
193
|
+
tracked.durationMs = Date.now() - tracked.startMs;
|
|
194
|
+
this.runHook(tracked, "postDestroy");
|
|
195
|
+
this.resolveDependents(tracked.backgroundTaskId);
|
|
196
|
+
});
|
|
197
|
+
this.runHook(tracked, "postSpawn");
|
|
198
|
+
}
|
|
199
|
+
/** Get a snapshot of a tracked process by ID, or undefined. */
|
|
200
|
+
get(backgroundTaskId) {
|
|
201
|
+
const tracked = this.processes.get(backgroundTaskId);
|
|
202
|
+
if (!tracked)
|
|
203
|
+
return undefined;
|
|
204
|
+
return this.snapshot(tracked);
|
|
205
|
+
}
|
|
206
|
+
/** List snapshots of all tracked processes. */
|
|
207
|
+
list() {
|
|
208
|
+
return [...this.processes.values()].map((t) => this.snapshot(t));
|
|
209
|
+
}
|
|
210
|
+
/** Cancel (SIGTERM) a running process. Returns true if found and killed. */
|
|
211
|
+
cancel(backgroundTaskId) {
|
|
212
|
+
const tracked = this.processes.get(backgroundTaskId);
|
|
213
|
+
if (!tracked)
|
|
214
|
+
return false;
|
|
215
|
+
if (isTerminal(tracked.status))
|
|
216
|
+
return true;
|
|
217
|
+
this.terminate(tracked, "cancelled");
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
/** Kill all running processes. */
|
|
221
|
+
killAll() {
|
|
222
|
+
for (const tracked of this.processes.values()) {
|
|
223
|
+
if (!isTerminal(tracked.status)) {
|
|
224
|
+
this.terminate(tracked, "killed");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/** Kill all processes and clear tracking state. */
|
|
229
|
+
dispose() {
|
|
230
|
+
this.killAll();
|
|
231
|
+
this.processes.clear();
|
|
232
|
+
}
|
|
233
|
+
pause(backgroundTaskId) {
|
|
234
|
+
const tracked = this.processes.get(backgroundTaskId);
|
|
235
|
+
if (!tracked || tracked.status !== "running" || !this.canSignal(tracked)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (!this.sendSignal(tracked, "SIGSTOP")) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
tracked.status = "paused";
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
resume(backgroundTaskId) {
|
|
245
|
+
const tracked = this.processes.get(backgroundTaskId);
|
|
246
|
+
if (!tracked || tracked.status !== "paused" || !this.canSignal(tracked)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
if (!this.sendSignal(tracked, "SIGCONT")) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
tracked.status = "running";
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
// -------------------------------------------------------------------------
|
|
256
|
+
// Internals
|
|
257
|
+
// -------------------------------------------------------------------------
|
|
258
|
+
snapshot(t) {
|
|
259
|
+
const stdoutTruncated = t.stdout.droppedBytes > 0;
|
|
260
|
+
const stderrTruncated = t.stderr.droppedBytes > 0;
|
|
261
|
+
return {
|
|
262
|
+
backgroundTaskId: t.backgroundTaskId,
|
|
263
|
+
pid: t.pid,
|
|
264
|
+
command: t.command,
|
|
265
|
+
description: t.description,
|
|
266
|
+
startedAt: t.startedAt,
|
|
267
|
+
status: t.status,
|
|
268
|
+
exitCode: t.exitCode,
|
|
269
|
+
stdout: Buffer.concat(t.stdout.chunks).toString("utf8"),
|
|
270
|
+
stderr: Buffer.concat(t.stderr.chunks).toString("utf8"),
|
|
271
|
+
stdoutTruncated: stdoutTruncated || undefined,
|
|
272
|
+
stderrTruncated: stderrTruncated || undefined,
|
|
273
|
+
stdoutRetainedBytes: t.stdout.retainedBytes,
|
|
274
|
+
stderrRetainedBytes: t.stderr.retainedBytes,
|
|
275
|
+
stdoutDroppedBytes: t.stdout.droppedBytes,
|
|
276
|
+
stderrDroppedBytes: t.stderr.droppedBytes,
|
|
277
|
+
dependsOn: t.dependsOn.length > 0 ? [...t.dependsOn] : undefined,
|
|
278
|
+
dependencyFailure: t.dependencyFailure,
|
|
279
|
+
hookErrors: t.hookErrors.length > 0 ? [...t.hookErrors] : undefined,
|
|
280
|
+
durationMs: t.durationMs,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
completionEvent(t) {
|
|
284
|
+
return {
|
|
285
|
+
...this.snapshot(t),
|
|
286
|
+
exitCode: t.exitCode ?? 1,
|
|
287
|
+
durationMs: t.durationMs ?? Date.now() - t.startMs,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
terminate(tracked, intent) {
|
|
291
|
+
if (tracked.terminating || isTerminal(tracked.status)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
const wasPaused = tracked.status === "paused";
|
|
295
|
+
tracked.terminating = true;
|
|
296
|
+
tracked.terminalIntent = intent;
|
|
297
|
+
tracked.status = intent;
|
|
298
|
+
tracked.durationMs = Date.now() - tracked.startMs;
|
|
299
|
+
if (intent === "timed_out") {
|
|
300
|
+
this.runHook(tracked, "onTimeout");
|
|
301
|
+
}
|
|
302
|
+
this.runHook(tracked, "preDestroy");
|
|
303
|
+
if (wasPaused) {
|
|
304
|
+
this.sendSignal(tracked, "SIGCONT");
|
|
305
|
+
}
|
|
306
|
+
this.sendSignal(tracked, "SIGTERM");
|
|
307
|
+
tracked.terminationTimer = setTimeout(() => {
|
|
308
|
+
if (this.isStillRunning(tracked)) {
|
|
309
|
+
this.sendSignal(tracked, "SIGKILL");
|
|
310
|
+
}
|
|
311
|
+
}, tracked.opts.terminationGraceMs ?? DEFAULT_TERMINATION_GRACE_MS);
|
|
312
|
+
}
|
|
313
|
+
canSignal(tracked) {
|
|
314
|
+
return this.platform !== "win32" && tracked.pid > 0 && tracked.child !== undefined;
|
|
315
|
+
}
|
|
316
|
+
sendSignal(tracked, signal) {
|
|
317
|
+
if (!tracked.child || tracked.pid <= 0) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
if (this.shouldSignalProcessGroup(tracked)) {
|
|
322
|
+
this.processKillFn(-tracked.pid, signal);
|
|
323
|
+
}
|
|
324
|
+
else if (this.processKillFn !== process.kill) {
|
|
325
|
+
this.processKillFn(tracked.pid, signal);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
tracked.child.kill(signal);
|
|
329
|
+
}
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
shouldSignalProcessGroup(tracked) {
|
|
337
|
+
return tracked.opts.terminateProcessGroup === true
|
|
338
|
+
&& this.platform !== "win32"
|
|
339
|
+
&& tracked.pid > 0;
|
|
340
|
+
}
|
|
341
|
+
isStillRunning(tracked) {
|
|
342
|
+
const child = tracked.child;
|
|
343
|
+
if (!child)
|
|
344
|
+
return false;
|
|
345
|
+
return child.exitCode === null && child.killed !== true;
|
|
346
|
+
}
|
|
347
|
+
resolveDependents(backgroundTaskId) {
|
|
348
|
+
for (const candidate of this.processes.values()) {
|
|
349
|
+
if (candidate.status !== "queued" || !candidate.dependsOn.includes(backgroundTaskId)) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
this.resolveQueuedProcess(candidate);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
resolveQueuedProcess(tracked) {
|
|
356
|
+
const dependencies = tracked.dependsOn.map((id) => this.processes.get(id));
|
|
357
|
+
const failed = dependencies.find((dependency) => dependency && dependency.status !== "completed");
|
|
358
|
+
if (failed && isTerminal(failed.status)) {
|
|
359
|
+
tracked.status = "skipped";
|
|
360
|
+
tracked.dependencyFailure = failed.backgroundTaskId;
|
|
361
|
+
tracked.durationMs = Date.now() - tracked.startMs;
|
|
362
|
+
this.resolveDependents(tracked.backgroundTaskId);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (dependencies.every((dependency) => dependency?.status === "completed")) {
|
|
366
|
+
this.startTrackedProcess(tracked);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
runHook(tracked, hook) {
|
|
370
|
+
const fn = tracked.opts.hooks?.[hook];
|
|
371
|
+
if (!fn)
|
|
372
|
+
return;
|
|
373
|
+
try {
|
|
374
|
+
const result = fn(this.snapshot(tracked));
|
|
375
|
+
if (result != null && typeof result.then === "function") {
|
|
376
|
+
tracked.hookErrors.push({
|
|
377
|
+
hook,
|
|
378
|
+
message: "Async lifecycle hooks are not supported by synchronous spawn",
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
384
|
+
tracked.hookErrors.push({ hook, message });
|
|
385
|
+
if (hook === "preSpawn") {
|
|
386
|
+
tracked.status = "failed";
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
exports.BackgroundProcessRegistry = BackgroundProcessRegistry;
|
|
392
|
+
function createRetainedStream() {
|
|
393
|
+
return {
|
|
394
|
+
chunks: [],
|
|
395
|
+
retainedBytes: 0,
|
|
396
|
+
droppedBytes: 0,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function appendCapped(stream, chunk, maxBytes) {
|
|
400
|
+
if (maxBytes === undefined) {
|
|
401
|
+
stream.chunks.push(chunk);
|
|
402
|
+
stream.retainedBytes += chunk.byteLength;
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
const remaining = maxBytes - stream.retainedBytes;
|
|
406
|
+
if (remaining <= 0) {
|
|
407
|
+
stream.droppedBytes += chunk.byteLength;
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (chunk.byteLength <= remaining) {
|
|
411
|
+
stream.chunks.push(chunk);
|
|
412
|
+
stream.retainedBytes += chunk.byteLength;
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
stream.chunks.push(chunk.subarray(0, remaining));
|
|
416
|
+
stream.retainedBytes += remaining;
|
|
417
|
+
stream.droppedBytes += chunk.byteLength - remaining;
|
|
418
|
+
}
|
|
419
|
+
function validateDependencies(dependsOn) {
|
|
420
|
+
const unique = new Set(dependsOn);
|
|
421
|
+
if (unique.size !== dependsOn.length) {
|
|
422
|
+
throw new Error("Background process dependency cycle or duplicate dependency detected");
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
function isTerminal(status) {
|
|
426
|
+
return TERMINAL_STATUSES.has(status);
|
|
427
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code JSONL session file parser for cost data collection.
|
|
3
|
+
*
|
|
4
|
+
* Parses Claude Code session JSONL files (main session and subagent files)
|
|
5
|
+
* to extract token usage data and compute costs. Each assistant message with
|
|
6
|
+
* a `usage` block becomes a {@link CostEventData} entry.
|
|
7
|
+
*
|
|
8
|
+
* JSONL format (one JSON object per line):
|
|
9
|
+
* ```json
|
|
10
|
+
* {
|
|
11
|
+
* "type": "assistant",
|
|
12
|
+
* "message": {
|
|
13
|
+
* "model": "claude-opus-4-6",
|
|
14
|
+
* "usage": {
|
|
15
|
+
* "input_tokens": 3,
|
|
16
|
+
* "cache_creation_input_tokens": 26928,
|
|
17
|
+
* "cache_read_input_tokens": 0,
|
|
18
|
+
* "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 26928 },
|
|
19
|
+
* "output_tokens": 28,
|
|
20
|
+
* "service_tier": "standard"
|
|
21
|
+
* }
|
|
22
|
+
* },
|
|
23
|
+
* "timestamp": "2026-04-05T..."
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { CostEventData, ModelCostStats } from "./types";
|
|
28
|
+
/**
|
|
29
|
+
* Parse a single Claude Code session JSONL file into cost events.
|
|
30
|
+
*
|
|
31
|
+
* Reads the file, splits by newline, and for each line that represents an
|
|
32
|
+
* assistant message with usage data, extracts token counts and computes
|
|
33
|
+
* the USD cost using the extended pricing model.
|
|
34
|
+
*
|
|
35
|
+
* @param sessionJsonlPath - Absolute path to the `.jsonl` file.
|
|
36
|
+
* @returns Array of cost events extracted from assistant messages.
|
|
37
|
+
*/
|
|
38
|
+
export declare function parseClaudeCodeSession(sessionJsonlPath: string): Promise<CostEventData[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Parse a Claude Code session JSONL file and all associated subagent files.
|
|
41
|
+
*
|
|
42
|
+
* Subagent files live at `<sessionDir>/subagents/agent-*.jsonl` where
|
|
43
|
+
* `<sessionDir>` is derived by stripping the `.jsonl` extension from the
|
|
44
|
+
* main session file path.
|
|
45
|
+
*
|
|
46
|
+
* @param sessionJsonlPath - Absolute path to the main session `.jsonl` file.
|
|
47
|
+
* @returns Merged array of cost events from the main session and all subagents.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseClaudeCodeSessionWithSubagents(sessionJsonlPath: string): Promise<CostEventData[]>;
|
|
50
|
+
/** Aggregated usage summary returned by {@link aggregateUsageData}. */
|
|
51
|
+
export interface AggregatedUsage {
|
|
52
|
+
/** Total number of cost events. */
|
|
53
|
+
eventCount: number;
|
|
54
|
+
/** Total base input tokens across all events. */
|
|
55
|
+
totalInputTokens: number;
|
|
56
|
+
/** Total output tokens across all events. */
|
|
57
|
+
totalOutputTokens: number;
|
|
58
|
+
/** Total cache-creation tokens (top-level) across all events. */
|
|
59
|
+
totalCacheCreationTokens: number;
|
|
60
|
+
/** Total cache-read tokens across all events. */
|
|
61
|
+
totalCacheReadTokens: number;
|
|
62
|
+
/** Total 5-minute cache-creation tokens. */
|
|
63
|
+
totalCacheCreation5mTokens: number;
|
|
64
|
+
/** Total 1-hour cache-creation tokens. */
|
|
65
|
+
totalCacheCreation1hTokens: number;
|
|
66
|
+
/** Total computed cost in USD. */
|
|
67
|
+
totalCostUsd: number;
|
|
68
|
+
/** Per-model breakdown of usage statistics. */
|
|
69
|
+
byModel: Record<string, ModelCostStats>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Aggregate usage data across an array of cost events.
|
|
73
|
+
*
|
|
74
|
+
* Sums token counts and costs, producing both overall totals and a
|
|
75
|
+
* per-model breakdown.
|
|
76
|
+
*
|
|
77
|
+
* @param events - Array of cost events (e.g. from {@link parseClaudeCodeSessionWithSubagents}).
|
|
78
|
+
* @returns Aggregated usage summary with per-model breakdown.
|
|
79
|
+
*/
|
|
80
|
+
export declare function aggregateUsageData(events: CostEventData[]): AggregatedUsage;
|
|
81
|
+
//# sourceMappingURL=claudeCodeParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeCodeParser.d.ts","sourceRoot":"","sources":["../../src/cost/claudeCodeParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAqC7D;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,CAC1C,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+D1B;AAMD;;;;;;;;;GASG;AACH,wBAAsB,mCAAmC,CACvD,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+B1B;AAMD,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,gBAAgB,EAAE,MAAM,CAAC;IACzB,6CAA6C;IAC7C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,wBAAwB,EAAE,MAAM,CAAC;IACjC,iDAAiD;IACjD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,0BAA0B,EAAE,MAAM,CAAC;IACnC,0CAA0C;IAC1C,0BAA0B,EAAE,MAAM,CAAC;IACnC,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACzC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,eAAe,CAqD3E"}
|