agent-trace 0.1.0 → 0.2.0
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/agent-trace.cjs +1216 -17
- package/package.json +1 -1
package/agent-trace.cjs
CHANGED
|
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// packages/runtime/src/standalone-entry.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var
|
|
27
|
+
var import_node_path5 = __toESM(require("node:path"));
|
|
28
|
+
var import_node_os3 = __toESM(require("node:os"));
|
|
29
|
+
var import_node_fs5 = __toESM(require("node:fs"));
|
|
30
30
|
|
|
31
31
|
// packages/dashboard/src/web-server.ts
|
|
32
32
|
var import_node_http = __toESM(require("node:http"));
|
|
@@ -2202,32 +2202,32 @@ function isEventSource(value) {
|
|
|
2202
2202
|
function isPrivacyTier(value) {
|
|
2203
2203
|
return PRIVACY_TIERS.some((tier) => tier === value);
|
|
2204
2204
|
}
|
|
2205
|
-
function addError(errors,
|
|
2206
|
-
errors.push(`${
|
|
2205
|
+
function addError(errors, path6, message) {
|
|
2206
|
+
errors.push(`${path6}: ${message}`);
|
|
2207
2207
|
}
|
|
2208
|
-
function readRequiredString(source, key, errors,
|
|
2208
|
+
function readRequiredString(source, key, errors, path6) {
|
|
2209
2209
|
const value = source[key];
|
|
2210
2210
|
if (!isNonEmptyString(value)) {
|
|
2211
|
-
addError(errors,
|
|
2211
|
+
addError(errors, path6, "must be a non-empty string");
|
|
2212
2212
|
return void 0;
|
|
2213
2213
|
}
|
|
2214
2214
|
return value;
|
|
2215
2215
|
}
|
|
2216
|
-
function readOptionalString(source, key, errors,
|
|
2216
|
+
function readOptionalString(source, key, errors, path6) {
|
|
2217
2217
|
const value = source[key];
|
|
2218
2218
|
if (value === void 0) {
|
|
2219
2219
|
return void 0;
|
|
2220
2220
|
}
|
|
2221
2221
|
if (!isNonEmptyString(value)) {
|
|
2222
|
-
addError(errors,
|
|
2222
|
+
addError(errors, path6, "must be a non-empty string when provided");
|
|
2223
2223
|
return void 0;
|
|
2224
2224
|
}
|
|
2225
2225
|
return value;
|
|
2226
2226
|
}
|
|
2227
|
-
function readRequiredIsoDate(source, key, errors,
|
|
2227
|
+
function readRequiredIsoDate(source, key, errors, path6) {
|
|
2228
2228
|
const value = source[key];
|
|
2229
2229
|
if (!isValidIsoDate(value)) {
|
|
2230
|
-
addError(errors,
|
|
2230
|
+
addError(errors, path6, "must be a valid ISO-8601 date");
|
|
2231
2231
|
return void 0;
|
|
2232
2232
|
}
|
|
2233
2233
|
return value;
|
|
@@ -3021,9 +3021,9 @@ function readStringArray(record, key) {
|
|
|
3021
3021
|
}
|
|
3022
3022
|
return output;
|
|
3023
3023
|
}
|
|
3024
|
-
function readNestedString(record,
|
|
3024
|
+
function readNestedString(record, path6) {
|
|
3025
3025
|
let current = record;
|
|
3026
|
-
for (const key of
|
|
3026
|
+
for (const key of path6) {
|
|
3027
3027
|
const asObject = asRecord3(current);
|
|
3028
3028
|
if (asObject === void 0) {
|
|
3029
3029
|
return void 0;
|
|
@@ -4343,6 +4343,1126 @@ function createSqliteBackedRuntime(options) {
|
|
|
4343
4343
|
};
|
|
4344
4344
|
}
|
|
4345
4345
|
|
|
4346
|
+
// packages/cli/src/args.ts
|
|
4347
|
+
function isCliCommand(value) {
|
|
4348
|
+
return value === "init" || value === "status" || value === "hook-handler";
|
|
4349
|
+
}
|
|
4350
|
+
function parsePrivacyTier(value) {
|
|
4351
|
+
if (value === void 0) {
|
|
4352
|
+
return void 0;
|
|
4353
|
+
}
|
|
4354
|
+
if (value === "1" || value === "2" || value === "3") {
|
|
4355
|
+
return Number(value);
|
|
4356
|
+
}
|
|
4357
|
+
return void 0;
|
|
4358
|
+
}
|
|
4359
|
+
function parseArgs(argv) {
|
|
4360
|
+
const commandCandidate = argv[2];
|
|
4361
|
+
const command = isCliCommand(commandCandidate) ? commandCandidate : void 0;
|
|
4362
|
+
let configDir;
|
|
4363
|
+
let collectorUrl;
|
|
4364
|
+
let privacyTier;
|
|
4365
|
+
let installHooks;
|
|
4366
|
+
let forward = false;
|
|
4367
|
+
for (let i = 3; i < argv.length; i += 1) {
|
|
4368
|
+
const token = argv[i];
|
|
4369
|
+
if (token === "--forward") {
|
|
4370
|
+
forward = true;
|
|
4371
|
+
continue;
|
|
4372
|
+
}
|
|
4373
|
+
if (token === "--install-hooks") {
|
|
4374
|
+
installHooks = true;
|
|
4375
|
+
continue;
|
|
4376
|
+
}
|
|
4377
|
+
if (token === "--no-install-hooks") {
|
|
4378
|
+
installHooks = false;
|
|
4379
|
+
continue;
|
|
4380
|
+
}
|
|
4381
|
+
if (token === "--config-dir") {
|
|
4382
|
+
const value = argv[i + 1];
|
|
4383
|
+
if (typeof value === "string" && value.length > 0) {
|
|
4384
|
+
configDir = value;
|
|
4385
|
+
}
|
|
4386
|
+
i += 1;
|
|
4387
|
+
continue;
|
|
4388
|
+
}
|
|
4389
|
+
if (token === "--collector-url") {
|
|
4390
|
+
const value = argv[i + 1];
|
|
4391
|
+
if (typeof value === "string" && value.length > 0) {
|
|
4392
|
+
collectorUrl = value;
|
|
4393
|
+
}
|
|
4394
|
+
i += 1;
|
|
4395
|
+
continue;
|
|
4396
|
+
}
|
|
4397
|
+
if (token === "--privacy-tier") {
|
|
4398
|
+
const value = argv[i + 1];
|
|
4399
|
+
privacyTier = parsePrivacyTier(value);
|
|
4400
|
+
i += 1;
|
|
4401
|
+
continue;
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
return {
|
|
4405
|
+
command,
|
|
4406
|
+
...configDir !== void 0 ? { configDir } : {},
|
|
4407
|
+
...collectorUrl !== void 0 ? { collectorUrl } : {},
|
|
4408
|
+
...privacyTier !== void 0 ? { privacyTier } : {},
|
|
4409
|
+
...installHooks !== void 0 ? { installHooks } : {},
|
|
4410
|
+
...forward ? { forward: true } : {}
|
|
4411
|
+
};
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
// packages/cli/src/claude-hooks.ts
|
|
4415
|
+
var HOOK_EVENTS = [
|
|
4416
|
+
"SessionStart",
|
|
4417
|
+
"SessionEnd",
|
|
4418
|
+
"PostToolUse",
|
|
4419
|
+
"Stop",
|
|
4420
|
+
"TaskCompleted"
|
|
4421
|
+
];
|
|
4422
|
+
function buildClaudeHookConfig(hookCommand, generatedAt) {
|
|
4423
|
+
return {
|
|
4424
|
+
version: "1.0",
|
|
4425
|
+
generatedAt,
|
|
4426
|
+
hooks: HOOK_EVENTS.map((event) => ({
|
|
4427
|
+
event,
|
|
4428
|
+
command: hookCommand
|
|
4429
|
+
}))
|
|
4430
|
+
};
|
|
4431
|
+
}
|
|
4432
|
+
|
|
4433
|
+
// packages/cli/src/config-store.ts
|
|
4434
|
+
var import_node_fs2 = __toESM(require("node:fs"));
|
|
4435
|
+
var import_node_os2 = __toESM(require("node:os"));
|
|
4436
|
+
var import_node_path3 = __toESM(require("node:path"));
|
|
4437
|
+
var CONFIG_FILE_NAME = "agent-trace.json";
|
|
4438
|
+
var CLAUDE_HOOKS_FILE_NAME = "agent-trace-claude-hooks.json";
|
|
4439
|
+
var CLAUDE_GLOBAL_SETTINGS_FILE_NAME = "settings.json";
|
|
4440
|
+
var CLAUDE_LOCAL_SETTINGS_FILE_NAME = "settings.local.json";
|
|
4441
|
+
function ensurePrivacyTier(value) {
|
|
4442
|
+
return value === 1 || value === 2 || value === 3;
|
|
4443
|
+
}
|
|
4444
|
+
function parseConfig(raw) {
|
|
4445
|
+
const parsedUnknown = JSON.parse(raw);
|
|
4446
|
+
if (typeof parsedUnknown !== "object" || parsedUnknown === null) {
|
|
4447
|
+
return void 0;
|
|
4448
|
+
}
|
|
4449
|
+
const parsed = parsedUnknown;
|
|
4450
|
+
if (parsed["version"] !== "1.0") {
|
|
4451
|
+
return void 0;
|
|
4452
|
+
}
|
|
4453
|
+
if (typeof parsed["collectorUrl"] !== "string" || parsed["collectorUrl"].length === 0) {
|
|
4454
|
+
return void 0;
|
|
4455
|
+
}
|
|
4456
|
+
if (!ensurePrivacyTier(parsed["privacyTier"])) {
|
|
4457
|
+
return void 0;
|
|
4458
|
+
}
|
|
4459
|
+
if (typeof parsed["hookCommand"] !== "string" || parsed["hookCommand"].length === 0) {
|
|
4460
|
+
return void 0;
|
|
4461
|
+
}
|
|
4462
|
+
if (typeof parsed["updatedAt"] !== "string" || parsed["updatedAt"].length === 0) {
|
|
4463
|
+
return void 0;
|
|
4464
|
+
}
|
|
4465
|
+
return {
|
|
4466
|
+
version: "1.0",
|
|
4467
|
+
collectorUrl: parsed["collectorUrl"],
|
|
4468
|
+
privacyTier: parsed["privacyTier"],
|
|
4469
|
+
hookCommand: parsed["hookCommand"],
|
|
4470
|
+
updatedAt: parsed["updatedAt"]
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
function isRecord2(value) {
|
|
4474
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4475
|
+
}
|
|
4476
|
+
function isGlobalClaudeConfigDir(configDir) {
|
|
4477
|
+
const globalClaudeDir = import_node_path3.default.join(import_node_os2.default.homedir(), ".claude");
|
|
4478
|
+
return import_node_path3.default.resolve(configDir) === import_node_path3.default.resolve(globalClaudeDir);
|
|
4479
|
+
}
|
|
4480
|
+
function readSettingsDocument(filePath) {
|
|
4481
|
+
if (!import_node_fs2.default.existsSync(filePath)) {
|
|
4482
|
+
return {
|
|
4483
|
+
exists: false,
|
|
4484
|
+
settings: {}
|
|
4485
|
+
};
|
|
4486
|
+
}
|
|
4487
|
+
try {
|
|
4488
|
+
const parsed = JSON.parse(import_node_fs2.default.readFileSync(filePath, "utf8"));
|
|
4489
|
+
if (!isRecord2(parsed)) {
|
|
4490
|
+
return {
|
|
4491
|
+
exists: true,
|
|
4492
|
+
settings: {}
|
|
4493
|
+
};
|
|
4494
|
+
}
|
|
4495
|
+
return {
|
|
4496
|
+
exists: true,
|
|
4497
|
+
settings: parsed
|
|
4498
|
+
};
|
|
4499
|
+
} catch {
|
|
4500
|
+
return {
|
|
4501
|
+
exists: true,
|
|
4502
|
+
settings: {}
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
function toMutableHooks(settings) {
|
|
4507
|
+
const mutableHooks = {};
|
|
4508
|
+
const hooks = settings["hooks"];
|
|
4509
|
+
if (!isRecord2(hooks)) {
|
|
4510
|
+
return mutableHooks;
|
|
4511
|
+
}
|
|
4512
|
+
for (const [eventName, value] of Object.entries(hooks)) {
|
|
4513
|
+
if (Array.isArray(value)) {
|
|
4514
|
+
mutableHooks[eventName] = [...value];
|
|
4515
|
+
continue;
|
|
4516
|
+
}
|
|
4517
|
+
mutableHooks[eventName] = [];
|
|
4518
|
+
}
|
|
4519
|
+
return mutableHooks;
|
|
4520
|
+
}
|
|
4521
|
+
function toMutableEnv(settings) {
|
|
4522
|
+
const mutableEnv = {};
|
|
4523
|
+
const env = settings["env"];
|
|
4524
|
+
if (!isRecord2(env)) {
|
|
4525
|
+
return mutableEnv;
|
|
4526
|
+
}
|
|
4527
|
+
for (const [key, value] of Object.entries(env)) {
|
|
4528
|
+
if (typeof value === "string") {
|
|
4529
|
+
mutableEnv[key] = value;
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
return mutableEnv;
|
|
4533
|
+
}
|
|
4534
|
+
function hasCommandOnEvent(entries, expectedCommand) {
|
|
4535
|
+
return entries.some((entry) => {
|
|
4536
|
+
if (!isRecord2(entry)) {
|
|
4537
|
+
return false;
|
|
4538
|
+
}
|
|
4539
|
+
const hooks = entry["hooks"];
|
|
4540
|
+
if (!Array.isArray(hooks)) {
|
|
4541
|
+
return false;
|
|
4542
|
+
}
|
|
4543
|
+
return hooks.some((hook) => {
|
|
4544
|
+
if (!isRecord2(hook)) {
|
|
4545
|
+
return false;
|
|
4546
|
+
}
|
|
4547
|
+
return hook["type"] === "command" && hook["command"] === expectedCommand;
|
|
4548
|
+
});
|
|
4549
|
+
});
|
|
4550
|
+
}
|
|
4551
|
+
function createCommandEntry(command) {
|
|
4552
|
+
const commandHook = {
|
|
4553
|
+
type: "command",
|
|
4554
|
+
command,
|
|
4555
|
+
timeout: 10
|
|
4556
|
+
};
|
|
4557
|
+
return {
|
|
4558
|
+
hooks: [commandHook]
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function areHooksInstalled(hooksMap, config) {
|
|
4562
|
+
return config.hooks.every((entry) => {
|
|
4563
|
+
const entries = hooksMap[entry.event] ?? [];
|
|
4564
|
+
return hasCommandOnEvent(entries, entry.command);
|
|
4565
|
+
});
|
|
4566
|
+
}
|
|
4567
|
+
function toSettingsDocument(settings, hooksMap, envMap) {
|
|
4568
|
+
const hooks = hooksMap;
|
|
4569
|
+
const env = envMap;
|
|
4570
|
+
return {
|
|
4571
|
+
...settings,
|
|
4572
|
+
hooks,
|
|
4573
|
+
...Object.keys(env).length > 0 ? { env } : {}
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
var FileCliConfigStore = class {
|
|
4577
|
+
resolveConfigDir(configDirOverride) {
|
|
4578
|
+
if (configDirOverride !== void 0 && configDirOverride.length > 0) {
|
|
4579
|
+
return configDirOverride;
|
|
4580
|
+
}
|
|
4581
|
+
const fromEnv = process.env["AGENT_TRACE_CONFIG_DIR"];
|
|
4582
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) {
|
|
4583
|
+
return fromEnv;
|
|
4584
|
+
}
|
|
4585
|
+
return import_node_path3.default.join(import_node_os2.default.homedir(), ".claude");
|
|
4586
|
+
}
|
|
4587
|
+
resolveConfigPath(configDirOverride) {
|
|
4588
|
+
return import_node_path3.default.join(this.resolveConfigDir(configDirOverride), CONFIG_FILE_NAME);
|
|
4589
|
+
}
|
|
4590
|
+
resolveHooksPath(configDirOverride) {
|
|
4591
|
+
return import_node_path3.default.join(this.resolveConfigDir(configDirOverride), CLAUDE_HOOKS_FILE_NAME);
|
|
4592
|
+
}
|
|
4593
|
+
resolveClaudeSettingsPath(configDirOverride) {
|
|
4594
|
+
const configDir = this.resolveConfigDir(configDirOverride);
|
|
4595
|
+
const settingsFileName = isGlobalClaudeConfigDir(configDir) ? CLAUDE_GLOBAL_SETTINGS_FILE_NAME : CLAUDE_LOCAL_SETTINGS_FILE_NAME;
|
|
4596
|
+
return import_node_path3.default.join(configDir, settingsFileName);
|
|
4597
|
+
}
|
|
4598
|
+
readConfig(configDirOverride) {
|
|
4599
|
+
const configPath = this.resolveConfigPath(configDirOverride);
|
|
4600
|
+
if (!import_node_fs2.default.existsSync(configPath)) {
|
|
4601
|
+
return void 0;
|
|
4602
|
+
}
|
|
4603
|
+
const raw = import_node_fs2.default.readFileSync(configPath, "utf8");
|
|
4604
|
+
return parseConfig(raw);
|
|
4605
|
+
}
|
|
4606
|
+
writeConfig(config, configDirOverride) {
|
|
4607
|
+
const configDir = this.resolveConfigDir(configDirOverride);
|
|
4608
|
+
import_node_fs2.default.mkdirSync(configDir, { recursive: true });
|
|
4609
|
+
const configPath = this.resolveConfigPath(configDirOverride);
|
|
4610
|
+
import_node_fs2.default.writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
4611
|
+
`, "utf8");
|
|
4612
|
+
return configPath;
|
|
4613
|
+
}
|
|
4614
|
+
writeClaudeHooks(config, configDirOverride) {
|
|
4615
|
+
const configDir = this.resolveConfigDir(configDirOverride);
|
|
4616
|
+
import_node_fs2.default.mkdirSync(configDir, { recursive: true });
|
|
4617
|
+
const hooksPath = this.resolveHooksPath(configDirOverride);
|
|
4618
|
+
import_node_fs2.default.writeFileSync(hooksPath, `${JSON.stringify(config, null, 2)}
|
|
4619
|
+
`, "utf8");
|
|
4620
|
+
return hooksPath;
|
|
4621
|
+
}
|
|
4622
|
+
installClaudeHooks(config, configDirOverride, settingsEnv = {}) {
|
|
4623
|
+
const configDir = this.resolveConfigDir(configDirOverride);
|
|
4624
|
+
import_node_fs2.default.mkdirSync(configDir, { recursive: true });
|
|
4625
|
+
const settingsPath = this.resolveClaudeSettingsPath(configDirOverride);
|
|
4626
|
+
const read = readSettingsDocument(settingsPath);
|
|
4627
|
+
const hooksMap = toMutableHooks(read.settings);
|
|
4628
|
+
const envMap = toMutableEnv(read.settings);
|
|
4629
|
+
let changed = !read.exists;
|
|
4630
|
+
for (const hook of config.hooks) {
|
|
4631
|
+
const eventEntries = hooksMap[hook.event] ?? [];
|
|
4632
|
+
if (!hasCommandOnEvent(eventEntries, hook.command)) {
|
|
4633
|
+
eventEntries.push(createCommandEntry(hook.command));
|
|
4634
|
+
hooksMap[hook.event] = eventEntries;
|
|
4635
|
+
changed = true;
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
Object.entries(settingsEnv).forEach(([key, value]) => {
|
|
4639
|
+
if (envMap[key] !== value) {
|
|
4640
|
+
envMap[key] = value;
|
|
4641
|
+
changed = true;
|
|
4642
|
+
}
|
|
4643
|
+
});
|
|
4644
|
+
const installed = areHooksInstalled(hooksMap, config);
|
|
4645
|
+
if (changed) {
|
|
4646
|
+
const updatedSettings = toSettingsDocument(read.settings, hooksMap, envMap);
|
|
4647
|
+
import_node_fs2.default.writeFileSync(settingsPath, `${JSON.stringify(updatedSettings, null, 2)}
|
|
4648
|
+
`, "utf8");
|
|
4649
|
+
}
|
|
4650
|
+
return {
|
|
4651
|
+
settingsPath,
|
|
4652
|
+
installed
|
|
4653
|
+
};
|
|
4654
|
+
}
|
|
4655
|
+
isClaudeHooksInstalled(config, configDirOverride) {
|
|
4656
|
+
const settingsPath = this.resolveClaudeSettingsPath(configDirOverride);
|
|
4657
|
+
const read = readSettingsDocument(settingsPath);
|
|
4658
|
+
if (!read.exists) {
|
|
4659
|
+
return false;
|
|
4660
|
+
}
|
|
4661
|
+
const hooksMap = toMutableHooks(read.settings);
|
|
4662
|
+
return areHooksInstalled(hooksMap, config);
|
|
4663
|
+
}
|
|
4664
|
+
};
|
|
4665
|
+
|
|
4666
|
+
// packages/cli/src/init.ts
|
|
4667
|
+
function ensurePrivacyTierOrDefault(value) {
|
|
4668
|
+
if (value === void 0) {
|
|
4669
|
+
return 2;
|
|
4670
|
+
}
|
|
4671
|
+
return value;
|
|
4672
|
+
}
|
|
4673
|
+
function nowIso(inputNowIso) {
|
|
4674
|
+
if (inputNowIso !== void 0) {
|
|
4675
|
+
return inputNowIso;
|
|
4676
|
+
}
|
|
4677
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
4678
|
+
}
|
|
4679
|
+
function shouldInstallHooks(value) {
|
|
4680
|
+
if (value === void 0) {
|
|
4681
|
+
return true;
|
|
4682
|
+
}
|
|
4683
|
+
return value;
|
|
4684
|
+
}
|
|
4685
|
+
function deriveOtelLogsEndpoint(collectorUrl) {
|
|
4686
|
+
try {
|
|
4687
|
+
const parsed = new URL(collectorUrl);
|
|
4688
|
+
parsed.port = "4717";
|
|
4689
|
+
parsed.pathname = "";
|
|
4690
|
+
parsed.search = "";
|
|
4691
|
+
parsed.hash = "";
|
|
4692
|
+
const value = parsed.toString();
|
|
4693
|
+
return value.endsWith("/") ? value.slice(0, -1) : value;
|
|
4694
|
+
} catch {
|
|
4695
|
+
return "http://127.0.0.1:4717";
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4698
|
+
function buildTelemetryEnv(collectorUrl, privacyTier) {
|
|
4699
|
+
const enablePromptAndToolDetail = privacyTier >= 2;
|
|
4700
|
+
const otelLogsEndpoint = deriveOtelLogsEndpoint(collectorUrl);
|
|
4701
|
+
return {
|
|
4702
|
+
CLAUDE_CODE_ENABLE_TELEMETRY: "1",
|
|
4703
|
+
OTEL_METRICS_EXPORTER: "none",
|
|
4704
|
+
OTEL_LOGS_EXPORTER: "otlp",
|
|
4705
|
+
OTEL_EXPORTER_OTLP_PROTOCOL: "grpc",
|
|
4706
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: otelLogsEndpoint,
|
|
4707
|
+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: otelLogsEndpoint,
|
|
4708
|
+
OTEL_LOG_USER_PROMPTS: enablePromptAndToolDetail ? "1" : "0",
|
|
4709
|
+
OTEL_LOG_TOOL_DETAILS: enablePromptAndToolDetail ? "1" : "0"
|
|
4710
|
+
};
|
|
4711
|
+
}
|
|
4712
|
+
function runInit(input, store = new FileCliConfigStore()) {
|
|
4713
|
+
const timestamp = nowIso(input.nowIso);
|
|
4714
|
+
const config = {
|
|
4715
|
+
version: "1.0",
|
|
4716
|
+
collectorUrl: input.collectorUrl ?? "http://127.0.0.1:8317/v1/hooks",
|
|
4717
|
+
privacyTier: ensurePrivacyTierOrDefault(input.privacyTier),
|
|
4718
|
+
hookCommand: "agent-trace hook-handler --forward",
|
|
4719
|
+
updatedAt: timestamp
|
|
4720
|
+
};
|
|
4721
|
+
const telemetryEnv = buildTelemetryEnv(config.collectorUrl, config.privacyTier);
|
|
4722
|
+
const hooks = buildClaudeHookConfig(config.hookCommand, timestamp);
|
|
4723
|
+
const configPath = store.writeConfig(config, input.configDir);
|
|
4724
|
+
const hooksPath = store.writeClaudeHooks(hooks, input.configDir);
|
|
4725
|
+
const installResult = shouldInstallHooks(input.installHooks) ? store.installClaudeHooks(hooks, input.configDir, telemetryEnv) : {
|
|
4726
|
+
settingsPath: store.resolveClaudeSettingsPath(input.configDir),
|
|
4727
|
+
installed: store.isClaudeHooksInstalled(hooks, input.configDir)
|
|
4728
|
+
};
|
|
4729
|
+
return {
|
|
4730
|
+
ok: true,
|
|
4731
|
+
configPath,
|
|
4732
|
+
hooksPath,
|
|
4733
|
+
settingsPath: installResult.settingsPath,
|
|
4734
|
+
settingsHooksInstalled: installResult.installed,
|
|
4735
|
+
config,
|
|
4736
|
+
hooks
|
|
4737
|
+
};
|
|
4738
|
+
}
|
|
4739
|
+
|
|
4740
|
+
// packages/cli/src/status.ts
|
|
4741
|
+
var import_node_fs3 = __toESM(require("node:fs"));
|
|
4742
|
+
function runStatus(configDir, store = new FileCliConfigStore()) {
|
|
4743
|
+
const configPath = store.resolveConfigPath(configDir);
|
|
4744
|
+
const hooksPath = store.resolveHooksPath(configDir);
|
|
4745
|
+
const config = store.readConfig(configDir);
|
|
4746
|
+
if (config === void 0) {
|
|
4747
|
+
return {
|
|
4748
|
+
ok: false,
|
|
4749
|
+
message: "agent-trace config not found",
|
|
4750
|
+
configPath
|
|
4751
|
+
};
|
|
4752
|
+
}
|
|
4753
|
+
const settingsPath = store.resolveClaudeSettingsPath(configDir);
|
|
4754
|
+
const expectedHooks = buildClaudeHookConfig(config.hookCommand, config.updatedAt);
|
|
4755
|
+
return {
|
|
4756
|
+
ok: true,
|
|
4757
|
+
message: "agent-trace config found",
|
|
4758
|
+
configPath,
|
|
4759
|
+
hooksPath,
|
|
4760
|
+
hooksConfigured: import_node_fs3.default.existsSync(hooksPath),
|
|
4761
|
+
settingsPath,
|
|
4762
|
+
settingsHooksInstalled: store.isClaudeHooksInstalled(expectedHooks, configDir),
|
|
4763
|
+
config
|
|
4764
|
+
};
|
|
4765
|
+
}
|
|
4766
|
+
|
|
4767
|
+
// packages/cli/src/hook-handler.ts
|
|
4768
|
+
var import_node_crypto4 = __toESM(require("node:crypto"));
|
|
4769
|
+
var import_node_child_process = require("node:child_process");
|
|
4770
|
+
var import_node_fs4 = __toESM(require("node:fs"));
|
|
4771
|
+
var import_node_path4 = __toESM(require("node:path"));
|
|
4772
|
+
function isIsoDate2(value) {
|
|
4773
|
+
if (Number.isNaN(Date.parse(value))) {
|
|
4774
|
+
return false;
|
|
4775
|
+
}
|
|
4776
|
+
return value.endsWith("Z") || /[+-]\d{2}:\d{2}$/.test(value);
|
|
4777
|
+
}
|
|
4778
|
+
function parseHookPayload(rawStdin) {
|
|
4779
|
+
const trimmed = rawStdin.trim();
|
|
4780
|
+
if (trimmed.length === 0) {
|
|
4781
|
+
return void 0;
|
|
4782
|
+
}
|
|
4783
|
+
const parsed = JSON.parse(trimmed);
|
|
4784
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4785
|
+
return void 0;
|
|
4786
|
+
}
|
|
4787
|
+
return parsed;
|
|
4788
|
+
}
|
|
4789
|
+
function readString5(record, key) {
|
|
4790
|
+
const value = record[key];
|
|
4791
|
+
if (typeof value === "string" && value.length > 0) {
|
|
4792
|
+
return value;
|
|
4793
|
+
}
|
|
4794
|
+
return void 0;
|
|
4795
|
+
}
|
|
4796
|
+
function readNumber4(record, key) {
|
|
4797
|
+
const value = record[key];
|
|
4798
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
4799
|
+
return value;
|
|
4800
|
+
}
|
|
4801
|
+
return void 0;
|
|
4802
|
+
}
|
|
4803
|
+
function readStringArray3(record, key) {
|
|
4804
|
+
const value = record[key];
|
|
4805
|
+
if (!Array.isArray(value)) {
|
|
4806
|
+
return void 0;
|
|
4807
|
+
}
|
|
4808
|
+
const output = [];
|
|
4809
|
+
value.forEach((item) => {
|
|
4810
|
+
if (typeof item === "string" && item.length > 0) {
|
|
4811
|
+
output.push(item);
|
|
4812
|
+
}
|
|
4813
|
+
});
|
|
4814
|
+
if (output.length === 0) {
|
|
4815
|
+
return void 0;
|
|
4816
|
+
}
|
|
4817
|
+
return output;
|
|
4818
|
+
}
|
|
4819
|
+
function readNestedString2(record, path6) {
|
|
4820
|
+
let current = record;
|
|
4821
|
+
for (const key of path6) {
|
|
4822
|
+
if (typeof current !== "object" || current === null || Array.isArray(current)) {
|
|
4823
|
+
return void 0;
|
|
4824
|
+
}
|
|
4825
|
+
current = current[key];
|
|
4826
|
+
}
|
|
4827
|
+
if (typeof current === "string" && current.length > 0) {
|
|
4828
|
+
return current;
|
|
4829
|
+
}
|
|
4830
|
+
return void 0;
|
|
4831
|
+
}
|
|
4832
|
+
function pickCommand2(payload) {
|
|
4833
|
+
const record = payload;
|
|
4834
|
+
return readString5(record, "command") ?? readString5(record, "bash_command") ?? readString5(record, "bashCommand") ?? readNestedString2(record, ["tool_input", "command"]);
|
|
4835
|
+
}
|
|
4836
|
+
function pickToolName2(payload) {
|
|
4837
|
+
const record = payload;
|
|
4838
|
+
return readString5(record, "tool_name") ?? readString5(record, "toolName");
|
|
4839
|
+
}
|
|
4840
|
+
function isGitBashPayload(payload) {
|
|
4841
|
+
const toolName = pickToolName2(payload);
|
|
4842
|
+
const command = pickCommand2(payload);
|
|
4843
|
+
if (toolName === void 0 || command === void 0) {
|
|
4844
|
+
return false;
|
|
4845
|
+
}
|
|
4846
|
+
const cmd = command.trim();
|
|
4847
|
+
return toolName.toLowerCase() === "bash" && (cmd.startsWith("git ") || cmd.startsWith("gh pr ") || cmd.includes("gh pr create"));
|
|
4848
|
+
}
|
|
4849
|
+
function isSessionEndEvent(payload) {
|
|
4850
|
+
const eventType = pickEventType(payload).toLowerCase();
|
|
4851
|
+
return eventType === "session_end" || eventType === "sessionend" || eventType === "stop" || eventType === "task_completed" || eventType === "taskcompleted";
|
|
4852
|
+
}
|
|
4853
|
+
function isSessionStartEvent(payload) {
|
|
4854
|
+
const eventType = pickEventType(payload).toLowerCase();
|
|
4855
|
+
return eventType === "session_start" || eventType === "sessionstart" || eventType === "startup";
|
|
4856
|
+
}
|
|
4857
|
+
function shouldAttemptGitEnrichment(payload) {
|
|
4858
|
+
return isGitBashPayload(payload) || isSessionStartEvent(payload) || isSessionEndEvent(payload);
|
|
4859
|
+
}
|
|
4860
|
+
function isGitCommitCommand(command) {
|
|
4861
|
+
return /\bgit\s+commit\b/.test(command);
|
|
4862
|
+
}
|
|
4863
|
+
function parseCommitMessage2(command) {
|
|
4864
|
+
const regex = /(?:^|\s)-m\s+["']([^"']+)["']/;
|
|
4865
|
+
const match = command.match(regex);
|
|
4866
|
+
if (match?.[1] === void 0 || match[1].length === 0) {
|
|
4867
|
+
return void 0;
|
|
4868
|
+
}
|
|
4869
|
+
const message = match[1];
|
|
4870
|
+
if (message.startsWith("$(") || message.startsWith("`")) {
|
|
4871
|
+
return void 0;
|
|
4872
|
+
}
|
|
4873
|
+
return message;
|
|
4874
|
+
}
|
|
4875
|
+
function extractPrUrl(payload) {
|
|
4876
|
+
const record = payload;
|
|
4877
|
+
const output = readString5(record, "tool_response") ?? readString5(record, "toolResponse") ?? readString5(record, "stdout") ?? readString5(record, "output");
|
|
4878
|
+
const command = pickCommand2(payload);
|
|
4879
|
+
const combined = [command, output].filter((s) => s !== void 0).join("\n");
|
|
4880
|
+
if (combined.length === 0) return void 0;
|
|
4881
|
+
const prUrlMatch = combined.match(/https:\/\/github\.com\/[^\s"']+\/pull\/\d+/);
|
|
4882
|
+
if (prUrlMatch !== null && prUrlMatch[0] !== void 0) return prUrlMatch[0];
|
|
4883
|
+
return void 0;
|
|
4884
|
+
}
|
|
4885
|
+
function parsePrFromUrl(url) {
|
|
4886
|
+
const match = url.match(/https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
|
|
4887
|
+
if (match === null || match[1] === void 0 || match[2] === void 0) return void 0;
|
|
4888
|
+
const prNumber = Number.parseInt(match[2], 10);
|
|
4889
|
+
if (!Number.isFinite(prNumber)) return void 0;
|
|
4890
|
+
return { repo: match[1], prNumber };
|
|
4891
|
+
}
|
|
4892
|
+
function pickRepositoryPath(payload) {
|
|
4893
|
+
const record = payload;
|
|
4894
|
+
return readString5(record, "project_path") ?? readString5(record, "projectPath") ?? readString5(record, "cwd") ?? readString5(record, "working_directory") ?? readString5(record, "workingDirectory");
|
|
4895
|
+
}
|
|
4896
|
+
function toUniqueStrings3(values) {
|
|
4897
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4898
|
+
const output = [];
|
|
4899
|
+
values.forEach((value) => {
|
|
4900
|
+
if (value.length === 0 || seen.has(value)) {
|
|
4901
|
+
return;
|
|
4902
|
+
}
|
|
4903
|
+
seen.add(value);
|
|
4904
|
+
output.push(value);
|
|
4905
|
+
});
|
|
4906
|
+
return output;
|
|
4907
|
+
}
|
|
4908
|
+
function parseIntSafe(raw) {
|
|
4909
|
+
const parsed = Number.parseInt(raw, 10);
|
|
4910
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
4911
|
+
return void 0;
|
|
4912
|
+
}
|
|
4913
|
+
return parsed;
|
|
4914
|
+
}
|
|
4915
|
+
function parseNumstatOutput(raw) {
|
|
4916
|
+
const lines = raw.split(/\r?\n/);
|
|
4917
|
+
let linesAdded = 0;
|
|
4918
|
+
let linesRemoved = 0;
|
|
4919
|
+
const filesChanged = [];
|
|
4920
|
+
let matched = false;
|
|
4921
|
+
lines.forEach((line) => {
|
|
4922
|
+
const match = line.trim().match(/^(\d+|-)\s+(\d+|-)\s+(.+)$/);
|
|
4923
|
+
if (match === null) {
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
const addedRaw = match[1];
|
|
4927
|
+
const removedRaw = match[2];
|
|
4928
|
+
const filePath = match[3]?.trim();
|
|
4929
|
+
if (filePath === void 0 || filePath.length === 0) {
|
|
4930
|
+
return;
|
|
4931
|
+
}
|
|
4932
|
+
if (addedRaw !== void 0 && addedRaw !== "-") {
|
|
4933
|
+
linesAdded += parseIntSafe(addedRaw) ?? 0;
|
|
4934
|
+
}
|
|
4935
|
+
if (removedRaw !== void 0 && removedRaw !== "-") {
|
|
4936
|
+
linesRemoved += parseIntSafe(removedRaw) ?? 0;
|
|
4937
|
+
}
|
|
4938
|
+
filesChanged.push(filePath);
|
|
4939
|
+
matched = true;
|
|
4940
|
+
});
|
|
4941
|
+
if (!matched) {
|
|
4942
|
+
return void 0;
|
|
4943
|
+
}
|
|
4944
|
+
return {
|
|
4945
|
+
linesAdded,
|
|
4946
|
+
linesRemoved,
|
|
4947
|
+
filesChanged: toUniqueStrings3(filesChanged)
|
|
4948
|
+
};
|
|
4949
|
+
}
|
|
4950
|
+
function stableStringify(payload) {
|
|
4951
|
+
const keys = Object.keys(payload).sort();
|
|
4952
|
+
const record = {};
|
|
4953
|
+
keys.forEach((key) => {
|
|
4954
|
+
record[key] = payload[key];
|
|
4955
|
+
});
|
|
4956
|
+
return JSON.stringify(record);
|
|
4957
|
+
}
|
|
4958
|
+
function buildEventId3(payload, now) {
|
|
4959
|
+
const material = `${now}:${stableStringify(payload)}`;
|
|
4960
|
+
return import_node_crypto4.default.createHash("sha256").update(material).digest("hex");
|
|
4961
|
+
}
|
|
4962
|
+
function pickSessionId(payload) {
|
|
4963
|
+
const fromSnake = payload["session_id"];
|
|
4964
|
+
if (typeof fromSnake === "string" && fromSnake.length > 0) {
|
|
4965
|
+
return fromSnake;
|
|
4966
|
+
}
|
|
4967
|
+
const fromCamel = payload["sessionId"];
|
|
4968
|
+
if (typeof fromCamel === "string" && fromCamel.length > 0) {
|
|
4969
|
+
return fromCamel;
|
|
4970
|
+
}
|
|
4971
|
+
return "unknown_session";
|
|
4972
|
+
}
|
|
4973
|
+
function pickPromptId2(payload) {
|
|
4974
|
+
const fromSnake = payload["prompt_id"];
|
|
4975
|
+
if (typeof fromSnake === "string" && fromSnake.length > 0) {
|
|
4976
|
+
return fromSnake;
|
|
4977
|
+
}
|
|
4978
|
+
const fromCamel = payload["promptId"];
|
|
4979
|
+
if (typeof fromCamel === "string" && fromCamel.length > 0) {
|
|
4980
|
+
return fromCamel;
|
|
4981
|
+
}
|
|
4982
|
+
const fromMessageId = payload["message_id"];
|
|
4983
|
+
if (typeof fromMessageId === "string" && fromMessageId.length > 0) {
|
|
4984
|
+
return fromMessageId;
|
|
4985
|
+
}
|
|
4986
|
+
const fromMessageIdCamel = payload["messageId"];
|
|
4987
|
+
if (typeof fromMessageIdCamel === "string" && fromMessageIdCamel.length > 0) {
|
|
4988
|
+
return fromMessageIdCamel;
|
|
4989
|
+
}
|
|
4990
|
+
return void 0;
|
|
4991
|
+
}
|
|
4992
|
+
function pickEventType(payload) {
|
|
4993
|
+
const event = payload["event"];
|
|
4994
|
+
if (typeof event === "string" && event.length > 0) {
|
|
4995
|
+
return event;
|
|
4996
|
+
}
|
|
4997
|
+
const type = payload["type"];
|
|
4998
|
+
if (typeof type === "string" && type.length > 0) {
|
|
4999
|
+
return type;
|
|
5000
|
+
}
|
|
5001
|
+
const hookEventNameSnake = payload["hook_event_name"];
|
|
5002
|
+
if (typeof hookEventNameSnake === "string" && hookEventNameSnake.length > 0) {
|
|
5003
|
+
return hookEventNameSnake;
|
|
5004
|
+
}
|
|
5005
|
+
const hookEventNameCamel = payload["hookEventName"];
|
|
5006
|
+
if (typeof hookEventNameCamel === "string" && hookEventNameCamel.length > 0) {
|
|
5007
|
+
return hookEventNameCamel;
|
|
5008
|
+
}
|
|
5009
|
+
const hookNameSnake = payload["hook_name"];
|
|
5010
|
+
if (typeof hookNameSnake === "string" && hookNameSnake.length > 0) {
|
|
5011
|
+
return hookNameSnake;
|
|
5012
|
+
}
|
|
5013
|
+
const hookNameCamel = payload["hookName"];
|
|
5014
|
+
if (typeof hookNameCamel === "string" && hookNameCamel.length > 0) {
|
|
5015
|
+
return hookNameCamel;
|
|
5016
|
+
}
|
|
5017
|
+
const hook = payload["hook"];
|
|
5018
|
+
if (typeof hook === "string" && hook.length > 0) {
|
|
5019
|
+
return hook;
|
|
5020
|
+
}
|
|
5021
|
+
return "hook_event";
|
|
5022
|
+
}
|
|
5023
|
+
function pickTimestamp(payload, now) {
|
|
5024
|
+
const value = payload["timestamp"];
|
|
5025
|
+
if (typeof value === "string" && value.length > 0) {
|
|
5026
|
+
return value;
|
|
5027
|
+
}
|
|
5028
|
+
return now;
|
|
5029
|
+
}
|
|
5030
|
+
function getPrivacyTier(store, configDir) {
|
|
5031
|
+
const config = store.readConfig(configDir);
|
|
5032
|
+
if (config === void 0) {
|
|
5033
|
+
return 2;
|
|
5034
|
+
}
|
|
5035
|
+
return config.privacyTier;
|
|
5036
|
+
}
|
|
5037
|
+
function getCollectorUrl(store, configDir, collectorUrlOverride) {
|
|
5038
|
+
if (collectorUrlOverride !== void 0 && collectorUrlOverride.length > 0) {
|
|
5039
|
+
return collectorUrlOverride;
|
|
5040
|
+
}
|
|
5041
|
+
const config = store.readConfig(configDir);
|
|
5042
|
+
if (config !== void 0) {
|
|
5043
|
+
return config.collectorUrl;
|
|
5044
|
+
}
|
|
5045
|
+
return "http://127.0.0.1:8317/v1/hooks";
|
|
5046
|
+
}
|
|
5047
|
+
function normalizeBaseline(baseline) {
|
|
5048
|
+
return {
|
|
5049
|
+
...baseline.repositoryPath !== void 0 ? { repositoryPath: baseline.repositoryPath } : {},
|
|
5050
|
+
linesAdded: Math.max(0, Math.trunc(baseline.linesAdded)),
|
|
5051
|
+
linesRemoved: Math.max(0, Math.trunc(baseline.linesRemoved)),
|
|
5052
|
+
filesChanged: toUniqueStrings3(baseline.filesChanged),
|
|
5053
|
+
capturedAt: baseline.capturedAt
|
|
5054
|
+
};
|
|
5055
|
+
}
|
|
5056
|
+
function isHookSessionBaseline(value) {
|
|
5057
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
5058
|
+
return false;
|
|
5059
|
+
}
|
|
5060
|
+
const record = value;
|
|
5061
|
+
if (typeof record["linesAdded"] !== "number" || typeof record["linesRemoved"] !== "number") {
|
|
5062
|
+
return false;
|
|
5063
|
+
}
|
|
5064
|
+
if (typeof record["capturedAt"] !== "string") {
|
|
5065
|
+
return false;
|
|
5066
|
+
}
|
|
5067
|
+
if (!Array.isArray(record["filesChanged"])) {
|
|
5068
|
+
return false;
|
|
5069
|
+
}
|
|
5070
|
+
return record["filesChanged"].every((entry) => typeof entry === "string");
|
|
5071
|
+
}
|
|
5072
|
+
function parseBaselineFile(raw) {
|
|
5073
|
+
const parsed = JSON.parse(raw);
|
|
5074
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
5075
|
+
return {
|
|
5076
|
+
sessions: {}
|
|
5077
|
+
};
|
|
5078
|
+
}
|
|
5079
|
+
const root = parsed;
|
|
5080
|
+
const sessionsRaw = root["sessions"];
|
|
5081
|
+
if (typeof sessionsRaw !== "object" || sessionsRaw === null || Array.isArray(sessionsRaw)) {
|
|
5082
|
+
return {
|
|
5083
|
+
sessions: {}
|
|
5084
|
+
};
|
|
5085
|
+
}
|
|
5086
|
+
const sessionsRecord = sessionsRaw;
|
|
5087
|
+
const sessions = {};
|
|
5088
|
+
Object.keys(sessionsRecord).forEach((sessionId) => {
|
|
5089
|
+
const candidate = sessionsRecord[sessionId];
|
|
5090
|
+
if (!isHookSessionBaseline(candidate)) {
|
|
5091
|
+
return;
|
|
5092
|
+
}
|
|
5093
|
+
sessions[sessionId] = normalizeBaseline(candidate);
|
|
5094
|
+
});
|
|
5095
|
+
return {
|
|
5096
|
+
sessions
|
|
5097
|
+
};
|
|
5098
|
+
}
|
|
5099
|
+
var FileHookSessionBaselineStore = class {
|
|
5100
|
+
baselinePath;
|
|
5101
|
+
constructor(store, configDir) {
|
|
5102
|
+
this.baselinePath = import_node_path4.default.join(store.resolveConfigDir(configDir), "hook-session-baselines.json");
|
|
5103
|
+
}
|
|
5104
|
+
readState() {
|
|
5105
|
+
try {
|
|
5106
|
+
if (!import_node_fs4.default.existsSync(this.baselinePath)) {
|
|
5107
|
+
return {
|
|
5108
|
+
sessions: {}
|
|
5109
|
+
};
|
|
5110
|
+
}
|
|
5111
|
+
const raw = import_node_fs4.default.readFileSync(this.baselinePath, "utf8");
|
|
5112
|
+
return parseBaselineFile(raw);
|
|
5113
|
+
} catch {
|
|
5114
|
+
return {
|
|
5115
|
+
sessions: {}
|
|
5116
|
+
};
|
|
5117
|
+
}
|
|
5118
|
+
}
|
|
5119
|
+
writeState(state) {
|
|
5120
|
+
const dir = import_node_path4.default.dirname(this.baselinePath);
|
|
5121
|
+
import_node_fs4.default.mkdirSync(dir, { recursive: true });
|
|
5122
|
+
import_node_fs4.default.writeFileSync(this.baselinePath, JSON.stringify(state, null, 2), "utf8");
|
|
5123
|
+
}
|
|
5124
|
+
read(sessionId) {
|
|
5125
|
+
const state = this.readState();
|
|
5126
|
+
return state.sessions[sessionId];
|
|
5127
|
+
}
|
|
5128
|
+
write(sessionId, baseline) {
|
|
5129
|
+
const state = this.readState();
|
|
5130
|
+
const next = {
|
|
5131
|
+
sessions: {
|
|
5132
|
+
...state.sessions,
|
|
5133
|
+
[sessionId]: normalizeBaseline(baseline)
|
|
5134
|
+
}
|
|
5135
|
+
};
|
|
5136
|
+
this.writeState(next);
|
|
5137
|
+
}
|
|
5138
|
+
delete(sessionId) {
|
|
5139
|
+
const state = this.readState();
|
|
5140
|
+
if (!(sessionId in state.sessions)) {
|
|
5141
|
+
return;
|
|
5142
|
+
}
|
|
5143
|
+
const sessions = {
|
|
5144
|
+
...state.sessions
|
|
5145
|
+
};
|
|
5146
|
+
delete sessions[sessionId];
|
|
5147
|
+
this.writeState({
|
|
5148
|
+
sessions
|
|
5149
|
+
});
|
|
5150
|
+
}
|
|
5151
|
+
};
|
|
5152
|
+
function runGitCommand(args, repositoryPath) {
|
|
5153
|
+
try {
|
|
5154
|
+
const output = (0, import_node_child_process.execFileSync)("git", [...args], {
|
|
5155
|
+
...repositoryPath !== void 0 ? { cwd: repositoryPath } : {},
|
|
5156
|
+
encoding: "utf8",
|
|
5157
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5158
|
+
});
|
|
5159
|
+
const trimmed = output.trim();
|
|
5160
|
+
if (trimmed.length === 0) {
|
|
5161
|
+
return void 0;
|
|
5162
|
+
}
|
|
5163
|
+
return trimmed;
|
|
5164
|
+
} catch {
|
|
5165
|
+
return void 0;
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
var ShellHookGitContextProvider = class {
|
|
5169
|
+
readContext(request) {
|
|
5170
|
+
const branch = runGitCommand(["rev-parse", "--abbrev-ref", "HEAD"], request.repositoryPath);
|
|
5171
|
+
const headSha = runGitCommand(["rev-parse", "HEAD"], request.repositoryPath);
|
|
5172
|
+
const diffStatsRaw = request.includeDiffStats && request.diffSource === "working_tree" ? runGitCommand(["diff", "--numstat", "HEAD"], request.repositoryPath) : request.includeDiffStats ? runGitCommand(["show", "--numstat", "--format="], request.repositoryPath) : void 0;
|
|
5173
|
+
const diffStats = diffStatsRaw === void 0 ? void 0 : parseNumstatOutput(diffStatsRaw);
|
|
5174
|
+
const commitMessage = request.includeCommitMessage === true ? runGitCommand(["log", "-1", "--format=%B"], request.repositoryPath) : void 0;
|
|
5175
|
+
if (branch === void 0 && headSha === void 0 && commitMessage === void 0 && diffStats?.linesAdded === void 0 && diffStats?.linesRemoved === void 0 && diffStats?.filesChanged === void 0) {
|
|
5176
|
+
return void 0;
|
|
5177
|
+
}
|
|
5178
|
+
return {
|
|
5179
|
+
...branch !== void 0 ? { branch } : {},
|
|
5180
|
+
...headSha !== void 0 ? { headSha } : {},
|
|
5181
|
+
...commitMessage !== void 0 ? { commitMessage } : {},
|
|
5182
|
+
...diffStats?.linesAdded !== void 0 ? { linesAdded: diffStats.linesAdded } : {},
|
|
5183
|
+
...diffStats?.linesRemoved !== void 0 ? { linesRemoved: diffStats.linesRemoved } : {},
|
|
5184
|
+
...diffStats?.filesChanged !== void 0 ? { filesChanged: diffStats.filesChanged } : {}
|
|
5185
|
+
};
|
|
5186
|
+
}
|
|
5187
|
+
};
|
|
5188
|
+
function enrichHookPayloadWithGitContext(payload, provider, baselineStore, now) {
|
|
5189
|
+
if (!shouldAttemptGitEnrichment(payload)) {
|
|
5190
|
+
return {
|
|
5191
|
+
payload,
|
|
5192
|
+
enriched: false,
|
|
5193
|
+
usedSessionBaselineDelta: false
|
|
5194
|
+
};
|
|
5195
|
+
}
|
|
5196
|
+
const record = payload;
|
|
5197
|
+
const command = pickCommand2(payload);
|
|
5198
|
+
const sessionStartEvent = isSessionStartEvent(payload);
|
|
5199
|
+
const sessionEndEvent = isSessionEndEvent(payload);
|
|
5200
|
+
const isCommit = command !== void 0 && isGitCommitCommand(command) && !sessionStartEvent && !sessionEndEvent;
|
|
5201
|
+
const includeDiffStats = isCommit || sessionStartEvent || sessionEndEvent;
|
|
5202
|
+
const diffSource = sessionStartEvent || sessionEndEvent ? "working_tree" : "head_commit";
|
|
5203
|
+
const repositoryPath = pickRepositoryPath(payload);
|
|
5204
|
+
const sessionId = pickSessionId(payload);
|
|
5205
|
+
const contextRequest = {
|
|
5206
|
+
includeDiffStats,
|
|
5207
|
+
...includeDiffStats ? { diffSource } : {},
|
|
5208
|
+
...isCommit ? { includeCommitMessage: true } : {},
|
|
5209
|
+
...repositoryPath !== void 0 ? { repositoryPath } : {}
|
|
5210
|
+
};
|
|
5211
|
+
const gitContext = provider.readContext(contextRequest);
|
|
5212
|
+
let usedSessionBaselineDelta = false;
|
|
5213
|
+
if (sessionStartEvent && sessionId !== "unknown_session" && gitContext !== void 0) {
|
|
5214
|
+
baselineStore.write(sessionId, {
|
|
5215
|
+
...repositoryPath !== void 0 ? { repositoryPath } : {},
|
|
5216
|
+
linesAdded: gitContext.linesAdded ?? 0,
|
|
5217
|
+
linesRemoved: gitContext.linesRemoved ?? 0,
|
|
5218
|
+
filesChanged: gitContext.filesChanged ?? [],
|
|
5219
|
+
capturedAt: now
|
|
5220
|
+
});
|
|
5221
|
+
}
|
|
5222
|
+
let linesAdded = gitContext?.linesAdded;
|
|
5223
|
+
let linesRemoved = gitContext?.linesRemoved;
|
|
5224
|
+
let filesChanged = gitContext?.filesChanged;
|
|
5225
|
+
if (sessionStartEvent) {
|
|
5226
|
+
linesAdded = void 0;
|
|
5227
|
+
linesRemoved = void 0;
|
|
5228
|
+
filesChanged = void 0;
|
|
5229
|
+
}
|
|
5230
|
+
if (sessionEndEvent && sessionId !== "unknown_session") {
|
|
5231
|
+
const baseline = baselineStore.read(sessionId);
|
|
5232
|
+
if (baseline !== void 0 && gitContext !== void 0) {
|
|
5233
|
+
const currentLinesAdded = gitContext.linesAdded ?? 0;
|
|
5234
|
+
const currentLinesRemoved = gitContext.linesRemoved ?? 0;
|
|
5235
|
+
linesAdded = Math.max(0, currentLinesAdded - baseline.linesAdded);
|
|
5236
|
+
linesRemoved = Math.max(0, currentLinesRemoved - baseline.linesRemoved);
|
|
5237
|
+
const currentFiles = gitContext.filesChanged ?? [];
|
|
5238
|
+
if (currentFiles.length > 0) {
|
|
5239
|
+
const baselineFiles = new Set(baseline.filesChanged);
|
|
5240
|
+
const deltaFiles = currentFiles.filter((filePath) => !baselineFiles.has(filePath));
|
|
5241
|
+
filesChanged = deltaFiles.length > 0 ? deltaFiles : currentFiles;
|
|
5242
|
+
}
|
|
5243
|
+
usedSessionBaselineDelta = true;
|
|
5244
|
+
}
|
|
5245
|
+
baselineStore.delete(sessionId);
|
|
5246
|
+
}
|
|
5247
|
+
const patch = {};
|
|
5248
|
+
if (isCommit) {
|
|
5249
|
+
patch["is_commit"] = true;
|
|
5250
|
+
}
|
|
5251
|
+
const commitMessageFromCommand = command === void 0 ? void 0 : parseCommitMessage2(command);
|
|
5252
|
+
const commitMessageFromGit = gitContext?.commitMessage;
|
|
5253
|
+
const commitMessage = commitMessageFromCommand ?? commitMessageFromGit;
|
|
5254
|
+
const existingCommitMessage = readString5(record, "commit_message") ?? readString5(record, "commitMessage");
|
|
5255
|
+
if (existingCommitMessage === void 0 && commitMessage !== void 0) {
|
|
5256
|
+
patch["commit_message"] = commitMessage;
|
|
5257
|
+
}
|
|
5258
|
+
const existingBranch = readString5(record, "git_branch") ?? readString5(record, "gitBranch");
|
|
5259
|
+
if (existingBranch === void 0 && gitContext?.branch !== void 0) {
|
|
5260
|
+
patch["git_branch"] = gitContext.branch;
|
|
5261
|
+
}
|
|
5262
|
+
const existingCommitSha = readString5(record, "commit_sha") ?? readString5(record, "commitSha");
|
|
5263
|
+
if (existingCommitSha === void 0 && gitContext?.headSha !== void 0) {
|
|
5264
|
+
patch["commit_sha"] = gitContext.headSha;
|
|
5265
|
+
}
|
|
5266
|
+
const existingLinesAdded = readNumber4(record, "lines_added") ?? readNumber4(record, "linesAdded");
|
|
5267
|
+
if (existingLinesAdded === void 0 && linesAdded !== void 0) {
|
|
5268
|
+
patch["lines_added"] = linesAdded;
|
|
5269
|
+
}
|
|
5270
|
+
const existingLinesRemoved = readNumber4(record, "lines_removed") ?? readNumber4(record, "linesRemoved");
|
|
5271
|
+
if (existingLinesRemoved === void 0 && linesRemoved !== void 0) {
|
|
5272
|
+
patch["lines_removed"] = linesRemoved;
|
|
5273
|
+
}
|
|
5274
|
+
const existingFilesChanged = readStringArray3(record, "files_changed") ?? readStringArray3(record, "filesChanged");
|
|
5275
|
+
if (existingFilesChanged === void 0 && filesChanged !== void 0) {
|
|
5276
|
+
patch["files_changed"] = filesChanged;
|
|
5277
|
+
}
|
|
5278
|
+
const prUrl = extractPrUrl(payload);
|
|
5279
|
+
if (prUrl !== void 0) {
|
|
5280
|
+
const existingPrUrl = readString5(record, "pr_url") ?? readString5(record, "prUrl");
|
|
5281
|
+
if (existingPrUrl === void 0) {
|
|
5282
|
+
patch["pr_url"] = prUrl;
|
|
5283
|
+
const parsed = parsePrFromUrl(prUrl);
|
|
5284
|
+
if (parsed !== void 0) {
|
|
5285
|
+
patch["pr_repo"] = parsed.repo;
|
|
5286
|
+
patch["pr_number"] = parsed.prNumber;
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
if (Object.keys(patch).length === 0) {
|
|
5291
|
+
return {
|
|
5292
|
+
payload,
|
|
5293
|
+
enriched: false,
|
|
5294
|
+
usedSessionBaselineDelta
|
|
5295
|
+
};
|
|
5296
|
+
}
|
|
5297
|
+
return {
|
|
5298
|
+
payload: {
|
|
5299
|
+
...payload,
|
|
5300
|
+
...patch
|
|
5301
|
+
},
|
|
5302
|
+
enriched: true,
|
|
5303
|
+
usedSessionBaselineDelta
|
|
5304
|
+
};
|
|
5305
|
+
}
|
|
5306
|
+
function toEnvelope(payload, privacyTier, now, extraAttributes = {}) {
|
|
5307
|
+
const promptId = pickPromptId2(payload);
|
|
5308
|
+
const eventType = pickEventType(payload);
|
|
5309
|
+
const envelope = {
|
|
5310
|
+
schemaVersion: "1.0",
|
|
5311
|
+
source: "hook",
|
|
5312
|
+
sourceVersion: "agent-trace-cli-v0.1",
|
|
5313
|
+
eventId: buildEventId3(payload, now),
|
|
5314
|
+
sessionId: pickSessionId(payload),
|
|
5315
|
+
...promptId !== void 0 ? { promptId } : {},
|
|
5316
|
+
eventType,
|
|
5317
|
+
eventTimestamp: pickTimestamp(payload, now),
|
|
5318
|
+
ingestedAt: now,
|
|
5319
|
+
privacyTier,
|
|
5320
|
+
payload,
|
|
5321
|
+
attributes: {
|
|
5322
|
+
hook_name: eventType,
|
|
5323
|
+
...extraAttributes
|
|
5324
|
+
}
|
|
5325
|
+
};
|
|
5326
|
+
return envelope;
|
|
5327
|
+
}
|
|
5328
|
+
function validateEnvelope(envelope) {
|
|
5329
|
+
const errors = [];
|
|
5330
|
+
if (envelope.schemaVersion !== "1.0") {
|
|
5331
|
+
errors.push("schemaVersion must equal 1.0");
|
|
5332
|
+
}
|
|
5333
|
+
if (envelope.source !== "hook") {
|
|
5334
|
+
errors.push("source must equal hook");
|
|
5335
|
+
}
|
|
5336
|
+
if (envelope.eventId.length === 0) {
|
|
5337
|
+
errors.push("eventId must be non-empty");
|
|
5338
|
+
}
|
|
5339
|
+
if (envelope.sessionId.length === 0) {
|
|
5340
|
+
errors.push("sessionId must be non-empty");
|
|
5341
|
+
}
|
|
5342
|
+
if (envelope.eventType.length === 0) {
|
|
5343
|
+
errors.push("eventType must be non-empty");
|
|
5344
|
+
}
|
|
5345
|
+
if (!isIsoDate2(envelope.eventTimestamp)) {
|
|
5346
|
+
errors.push("eventTimestamp must be ISO-8601");
|
|
5347
|
+
}
|
|
5348
|
+
if (!isIsoDate2(envelope.ingestedAt)) {
|
|
5349
|
+
errors.push("ingestedAt must be ISO-8601");
|
|
5350
|
+
}
|
|
5351
|
+
if (envelope.privacyTier !== 1 && envelope.privacyTier !== 2 && envelope.privacyTier !== 3) {
|
|
5352
|
+
errors.push("privacyTier must be 1, 2, or 3");
|
|
5353
|
+
}
|
|
5354
|
+
return errors;
|
|
5355
|
+
}
|
|
5356
|
+
function runHookHandler(input, store = new FileCliConfigStore(), gitContextProvider = new ShellHookGitContextProvider()) {
|
|
5357
|
+
let payload;
|
|
5358
|
+
try {
|
|
5359
|
+
payload = parseHookPayload(input.rawStdin);
|
|
5360
|
+
} catch {
|
|
5361
|
+
return {
|
|
5362
|
+
ok: false,
|
|
5363
|
+
errors: ["hook payload is not valid JSON"]
|
|
5364
|
+
};
|
|
5365
|
+
}
|
|
5366
|
+
if (payload === void 0) {
|
|
5367
|
+
return {
|
|
5368
|
+
ok: false,
|
|
5369
|
+
errors: ["hook payload is empty or invalid"]
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5372
|
+
const now = input.nowIso ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
5373
|
+
const privacyTier = getPrivacyTier(store, input.configDir);
|
|
5374
|
+
const baselineStore = new FileHookSessionBaselineStore(store, input.configDir);
|
|
5375
|
+
const enrichment = enrichHookPayloadWithGitContext(payload, gitContextProvider, baselineStore, now);
|
|
5376
|
+
const envelope = toEnvelope(enrichment.payload, privacyTier, now, {
|
|
5377
|
+
...enrichment.enriched ? { git_enriched: "1" } : {},
|
|
5378
|
+
...enrichment.usedSessionBaselineDelta ? { git_session_delta: "1" } : {}
|
|
5379
|
+
});
|
|
5380
|
+
const errors = validateEnvelope(envelope);
|
|
5381
|
+
if (errors.length > 0) {
|
|
5382
|
+
return {
|
|
5383
|
+
ok: false,
|
|
5384
|
+
errors
|
|
5385
|
+
};
|
|
5386
|
+
}
|
|
5387
|
+
return {
|
|
5388
|
+
ok: true,
|
|
5389
|
+
envelope
|
|
5390
|
+
};
|
|
5391
|
+
}
|
|
5392
|
+
var FetchCollectorHttpClient = class {
|
|
5393
|
+
async postJson(url, payload) {
|
|
5394
|
+
try {
|
|
5395
|
+
const response = await fetch(url, {
|
|
5396
|
+
method: "POST",
|
|
5397
|
+
headers: {
|
|
5398
|
+
"Content-Type": "application/json"
|
|
5399
|
+
},
|
|
5400
|
+
body: JSON.stringify(payload)
|
|
5401
|
+
});
|
|
5402
|
+
const body = await response.text();
|
|
5403
|
+
return {
|
|
5404
|
+
ok: true,
|
|
5405
|
+
statusCode: response.status,
|
|
5406
|
+
body
|
|
5407
|
+
};
|
|
5408
|
+
} catch (error) {
|
|
5409
|
+
return {
|
|
5410
|
+
ok: false,
|
|
5411
|
+
statusCode: 0,
|
|
5412
|
+
body: "",
|
|
5413
|
+
error: String(error)
|
|
5414
|
+
};
|
|
5415
|
+
}
|
|
5416
|
+
}
|
|
5417
|
+
};
|
|
5418
|
+
function isSuccessStatus(statusCode) {
|
|
5419
|
+
return statusCode >= 200 && statusCode < 300;
|
|
5420
|
+
}
|
|
5421
|
+
async function runHookHandlerAndForward(input, client = new FetchCollectorHttpClient(), store = new FileCliConfigStore(), gitContextProvider = new ShellHookGitContextProvider()) {
|
|
5422
|
+
const hookResult = runHookHandler(
|
|
5423
|
+
{
|
|
5424
|
+
rawStdin: input.rawStdin,
|
|
5425
|
+
...input.configDir !== void 0 ? { configDir: input.configDir } : {},
|
|
5426
|
+
...input.nowIso !== void 0 ? { nowIso: input.nowIso } : {}
|
|
5427
|
+
},
|
|
5428
|
+
store,
|
|
5429
|
+
gitContextProvider
|
|
5430
|
+
);
|
|
5431
|
+
if (!hookResult.ok) {
|
|
5432
|
+
return {
|
|
5433
|
+
ok: false,
|
|
5434
|
+
errors: hookResult.errors
|
|
5435
|
+
};
|
|
5436
|
+
}
|
|
5437
|
+
const collectorUrl = getCollectorUrl(store, input.configDir, input.collectorUrl);
|
|
5438
|
+
const postResult = await client.postJson(collectorUrl, hookResult.envelope);
|
|
5439
|
+
if (!postResult.ok) {
|
|
5440
|
+
return {
|
|
5441
|
+
ok: false,
|
|
5442
|
+
envelope: hookResult.envelope,
|
|
5443
|
+
errors: [postResult.error ?? "failed to send hook event to collector"]
|
|
5444
|
+
};
|
|
5445
|
+
}
|
|
5446
|
+
if (!isSuccessStatus(postResult.statusCode)) {
|
|
5447
|
+
return {
|
|
5448
|
+
ok: false,
|
|
5449
|
+
envelope: hookResult.envelope,
|
|
5450
|
+
statusCode: postResult.statusCode,
|
|
5451
|
+
errors: [
|
|
5452
|
+
`collector returned status ${String(postResult.statusCode)}`,
|
|
5453
|
+
...postResult.body.length > 0 ? [postResult.body] : []
|
|
5454
|
+
]
|
|
5455
|
+
};
|
|
5456
|
+
}
|
|
5457
|
+
return {
|
|
5458
|
+
ok: true,
|
|
5459
|
+
envelope: hookResult.envelope,
|
|
5460
|
+
collectorUrl,
|
|
5461
|
+
statusCode: postResult.statusCode,
|
|
5462
|
+
body: postResult.body
|
|
5463
|
+
};
|
|
5464
|
+
}
|
|
5465
|
+
|
|
4346
5466
|
// packages/runtime/src/standalone-entry.ts
|
|
4347
5467
|
function readNumberEnv(name, fallback) {
|
|
4348
5468
|
const raw = process.env[name];
|
|
@@ -4358,11 +5478,77 @@ function readPrivacyTierEnv(name) {
|
|
|
4358
5478
|
return 2;
|
|
4359
5479
|
}
|
|
4360
5480
|
function resolveDefaultSqlitePath() {
|
|
4361
|
-
const dataDir =
|
|
4362
|
-
|
|
4363
|
-
return
|
|
5481
|
+
const dataDir = import_node_path5.default.join(import_node_os3.default.homedir(), ".agent-trace");
|
|
5482
|
+
import_node_fs5.default.mkdirSync(dataDir, { recursive: true });
|
|
5483
|
+
return import_node_path5.default.join(dataDir, "data.db");
|
|
4364
5484
|
}
|
|
4365
|
-
async function
|
|
5485
|
+
async function readStdin() {
|
|
5486
|
+
return new Promise((resolve, reject) => {
|
|
5487
|
+
let buffer = "";
|
|
5488
|
+
process.stdin.setEncoding("utf8");
|
|
5489
|
+
process.stdin.on("data", (chunk) => {
|
|
5490
|
+
buffer += chunk;
|
|
5491
|
+
});
|
|
5492
|
+
process.stdin.on("end", () => resolve(buffer));
|
|
5493
|
+
process.stdin.on("error", (error) => reject(error));
|
|
5494
|
+
});
|
|
5495
|
+
}
|
|
5496
|
+
function printUsage() {
|
|
5497
|
+
process.stdout.write(
|
|
5498
|
+
"\nusage: agent-trace <command>\n\ncommands:\n (none) start the server (collector + api + dashboard)\n init configure Claude Code hooks\n status check if hooks are installed\n hook-handler process a hook event from stdin\n\noptions:\n --collector-url <url> collector endpoint (default: http://127.0.0.1:8317/v1/hooks)\n --privacy-tier <1|2|3> privacy tier (default: 2)\n --install-hooks install hooks into Claude settings (default for init)\n --no-install-hooks skip hook installation\n --forward forward hook event to collector (hook-handler)\n --config-dir <path> config directory (default: ~/.claude)\n\n"
|
|
5499
|
+
);
|
|
5500
|
+
}
|
|
5501
|
+
async function handleCliCommand(args) {
|
|
5502
|
+
const command = args.command;
|
|
5503
|
+
if (command === "init") {
|
|
5504
|
+
const result2 = runInit({
|
|
5505
|
+
...args.configDir !== void 0 ? { configDir: args.configDir } : {},
|
|
5506
|
+
...args.collectorUrl !== void 0 ? { collectorUrl: args.collectorUrl } : {},
|
|
5507
|
+
...args.privacyTier !== void 0 ? { privacyTier: args.privacyTier } : {},
|
|
5508
|
+
...args.installHooks !== void 0 ? { installHooks: args.installHooks } : {}
|
|
5509
|
+
});
|
|
5510
|
+
process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
5511
|
+
`);
|
|
5512
|
+
return;
|
|
5513
|
+
}
|
|
5514
|
+
if (command === "status") {
|
|
5515
|
+
const result2 = runStatus(args.configDir);
|
|
5516
|
+
process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
5517
|
+
`);
|
|
5518
|
+
process.exitCode = result2.ok ? 0 : 1;
|
|
5519
|
+
return;
|
|
5520
|
+
}
|
|
5521
|
+
const rawStdin = await readStdin();
|
|
5522
|
+
if (args.forward === true) {
|
|
5523
|
+
const result2 = await runHookHandlerAndForward({
|
|
5524
|
+
rawStdin,
|
|
5525
|
+
...args.configDir !== void 0 ? { configDir: args.configDir } : {},
|
|
5526
|
+
...args.collectorUrl !== void 0 ? { collectorUrl: args.collectorUrl } : {}
|
|
5527
|
+
});
|
|
5528
|
+
if (!result2.ok) {
|
|
5529
|
+
process.stderr.write(`${JSON.stringify(result2, null, 2)}
|
|
5530
|
+
`);
|
|
5531
|
+
process.exitCode = 1;
|
|
5532
|
+
return;
|
|
5533
|
+
}
|
|
5534
|
+
process.stdout.write(`${JSON.stringify(result2, null, 2)}
|
|
5535
|
+
`);
|
|
5536
|
+
return;
|
|
5537
|
+
}
|
|
5538
|
+
const result = runHookHandler({
|
|
5539
|
+
rawStdin,
|
|
5540
|
+
...args.configDir !== void 0 ? { configDir: args.configDir } : {}
|
|
5541
|
+
});
|
|
5542
|
+
if (!result.ok) {
|
|
5543
|
+
process.stderr.write(`${JSON.stringify(result, null, 2)}
|
|
5544
|
+
`);
|
|
5545
|
+
process.exitCode = 1;
|
|
5546
|
+
return;
|
|
5547
|
+
}
|
|
5548
|
+
process.stdout.write(`${JSON.stringify(result.envelope, null, 2)}
|
|
5549
|
+
`);
|
|
5550
|
+
}
|
|
5551
|
+
async function startServer() {
|
|
4366
5552
|
const host = process.env["RUNTIME_HOST"] ?? "127.0.0.1";
|
|
4367
5553
|
const collectorPort = readNumberEnv("COLLECTOR_PORT", 8317);
|
|
4368
5554
|
const apiPort = readNumberEnv("API_PORT", 8318);
|
|
@@ -4455,6 +5641,19 @@ async function main() {
|
|
|
4455
5641
|
void shutdown();
|
|
4456
5642
|
});
|
|
4457
5643
|
}
|
|
5644
|
+
async function main() {
|
|
5645
|
+
const args = parseArgs(process.argv);
|
|
5646
|
+
const command = args.command;
|
|
5647
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
5648
|
+
printUsage();
|
|
5649
|
+
return;
|
|
5650
|
+
}
|
|
5651
|
+
if (command === "init" || command === "status" || command === "hook-handler") {
|
|
5652
|
+
await handleCliCommand(args);
|
|
5653
|
+
return;
|
|
5654
|
+
}
|
|
5655
|
+
await startServer();
|
|
5656
|
+
}
|
|
4458
5657
|
void main().catch((error) => {
|
|
4459
5658
|
process.stderr.write(`${String(error)}
|
|
4460
5659
|
`);
|