@useorgx/wizard 0.1.56 → 0.1.58
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 +5 -0
- package/dist/cli.js +1890 -130
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
|
|
5
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
5
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="66b240e6-4f09-5817-86f1-bac0012af8cb")}catch(e){}}();
|
|
6
6
|
import * as clack from "@clack/prompts";
|
|
7
7
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
8
|
-
import { readFileSync as
|
|
8
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
9
9
|
import { hostname } from "os";
|
|
10
|
-
import { resolve as
|
|
10
|
+
import { resolve as resolve4 } from "path";
|
|
11
11
|
import { Command } from "commander";
|
|
12
12
|
import pc3 from "picocolors";
|
|
13
13
|
|
|
@@ -32,6 +32,7 @@ import { join } from "path";
|
|
|
32
32
|
var ORGX_HOSTED_MCP_KEY = "orgx";
|
|
33
33
|
var ORGX_LOCAL_MCP_KEY = "orgx-openclaw";
|
|
34
34
|
var ORGX_HOSTED_MCP_URL = "https://mcp.useorgx.com/mcp";
|
|
35
|
+
var ORGX_CODEX_MCP_URL = `${ORGX_HOSTED_MCP_URL}?profile=commander`;
|
|
35
36
|
var ORGX_HOSTED_MCP_HEALTH_URL = "https://mcp.useorgx.com/health";
|
|
36
37
|
var ORGX_HOSTED_OAUTH_BASE_URL = "https://mcp.useorgx.com";
|
|
37
38
|
var ORGX_HOSTED_OAUTH_AUTHORIZE_URL = `${ORGX_HOSTED_OAUTH_BASE_URL}/authorize`;
|
|
@@ -187,6 +188,9 @@ import {
|
|
|
187
188
|
writeFileSync
|
|
188
189
|
} from "fs";
|
|
189
190
|
import { dirname } from "path";
|
|
191
|
+
function fileExists(path) {
|
|
192
|
+
return existsSync(path);
|
|
193
|
+
}
|
|
190
194
|
function readTextIfExists(path) {
|
|
191
195
|
if (!existsSync(path)) return null;
|
|
192
196
|
try {
|
|
@@ -831,7 +835,7 @@ function parsePairingPollResult(value) {
|
|
|
831
835
|
};
|
|
832
836
|
}
|
|
833
837
|
function sleep(ms) {
|
|
834
|
-
return new Promise((
|
|
838
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
835
839
|
}
|
|
836
840
|
async function startBrowserPairing(options, fetchImpl) {
|
|
837
841
|
const data = await fetchJson({
|
|
@@ -944,12 +948,12 @@ h1{font-size:1.5rem;color:#b91c1c}p{color:#555;margin-top:.5rem}</style>
|
|
|
944
948
|
<p>Return to your terminal and try again.</p>
|
|
945
949
|
</div></body></html>`;
|
|
946
950
|
function tryListen(port, hostname2) {
|
|
947
|
-
return new Promise((
|
|
951
|
+
return new Promise((resolve5, reject) => {
|
|
948
952
|
const server = createServer();
|
|
949
953
|
server.once("error", reject);
|
|
950
954
|
server.listen(port, hostname2, () => {
|
|
951
955
|
server.removeListener("error", reject);
|
|
952
|
-
|
|
956
|
+
resolve5(server);
|
|
953
957
|
});
|
|
954
958
|
});
|
|
955
959
|
}
|
|
@@ -978,7 +982,7 @@ async function startLocalAuthServer(options) {
|
|
|
978
982
|
const successHtml = options.successHtml ?? DEFAULT_SUCCESS_HTML;
|
|
979
983
|
const errorHtml = options.errorHtml ?? DEFAULT_ERROR_HTML;
|
|
980
984
|
const { server, port } = await bindServer(options.preferredPort, hostname2);
|
|
981
|
-
const result = new Promise((
|
|
985
|
+
const result = new Promise((resolve5, reject) => {
|
|
982
986
|
const timer = setTimeout(() => {
|
|
983
987
|
server.close();
|
|
984
988
|
reject(new Error("Timed out waiting for browser authorization."));
|
|
@@ -1021,7 +1025,7 @@ async function startLocalAuthServer(options) {
|
|
|
1021
1025
|
res.writeHead(200, { "Content-Type": "text/html" }).end(successHtml);
|
|
1022
1026
|
clearTimeout(timer);
|
|
1023
1027
|
server.close();
|
|
1024
|
-
|
|
1028
|
+
resolve5({ code, state });
|
|
1025
1029
|
});
|
|
1026
1030
|
});
|
|
1027
1031
|
return { port, result };
|
|
@@ -2462,7 +2466,7 @@ function removeLegacyCodexEntries(servers) {
|
|
|
2462
2466
|
function patchCodexConfigToml(input, localMcpUrl) {
|
|
2463
2467
|
const current = parseTomlDocument(input);
|
|
2464
2468
|
const servers = readObject(current.mcp_servers);
|
|
2465
|
-
upsertCodexEntry(servers, ORGX_HOSTED_MCP_KEY,
|
|
2469
|
+
upsertCodexEntry(servers, ORGX_HOSTED_MCP_KEY, ORGX_CODEX_MCP_URL);
|
|
2466
2470
|
if (localMcpUrl) {
|
|
2467
2471
|
upsertCodexEntry(servers, ORGX_LOCAL_MCP_KEY, localMcpUrl);
|
|
2468
2472
|
}
|
|
@@ -2470,6 +2474,28 @@ function patchCodexConfigToml(input, localMcpUrl) {
|
|
|
2470
2474
|
current.mcp_servers = servers;
|
|
2471
2475
|
return stringifyTomlDocument(current);
|
|
2472
2476
|
}
|
|
2477
|
+
function migrateCodexHostedMcpProfile(input) {
|
|
2478
|
+
if (!input) return input;
|
|
2479
|
+
const current = parseTomlDocument(input);
|
|
2480
|
+
const servers = readObject(current.mcp_servers);
|
|
2481
|
+
const hosted = readObject(servers[ORGX_HOSTED_MCP_KEY]);
|
|
2482
|
+
if (hosted.url !== ORGX_HOSTED_MCP_URL) return input;
|
|
2483
|
+
const lines = input.match(/.*(?:\r\n|\n|$)/g)?.filter((line) => line.length > 0) ?? [];
|
|
2484
|
+
let inOrgxSection = false;
|
|
2485
|
+
let changed = false;
|
|
2486
|
+
const nextLines = lines.map((line) => {
|
|
2487
|
+
if (/^\s*\[/.test(line)) {
|
|
2488
|
+
inOrgxSection = /^\s*\[mcp_servers\.(?:orgx|"orgx")\]\s*(?:#.*)?(?:\r?\n)?$/.test(line);
|
|
2489
|
+
return line;
|
|
2490
|
+
}
|
|
2491
|
+
if (!inOrgxSection || changed) return line;
|
|
2492
|
+
const match = line.match(/^(\s*url\s*=\s*)(["'])(https:\/\/mcp\.useorgx\.com\/mcp)\2([ \t]*(?:#.*)?)(\r?\n)?$/);
|
|
2493
|
+
if (!match) return line;
|
|
2494
|
+
changed = true;
|
|
2495
|
+
return `${match[1]}${match[2]}${ORGX_CODEX_MCP_URL}${match[2]}${match[4]}${match[5] ?? ""}`;
|
|
2496
|
+
});
|
|
2497
|
+
return changed ? nextLines.join("") : input;
|
|
2498
|
+
}
|
|
2473
2499
|
function removeCodexConfigToml(input) {
|
|
2474
2500
|
const current = parseTomlDocument(input);
|
|
2475
2501
|
const servers = readObject(current.mcp_servers);
|
|
@@ -2477,7 +2503,7 @@ function removeCodexConfigToml(input) {
|
|
|
2477
2503
|
if (isRecord(servers[ORGX_LOCAL_MCP_KEY])) {
|
|
2478
2504
|
delete servers[ORGX_LOCAL_MCP_KEY];
|
|
2479
2505
|
}
|
|
2480
|
-
if (hosted.url === ORGX_HOSTED_MCP_URL) {
|
|
2506
|
+
if (hosted.url === ORGX_HOSTED_MCP_URL || hosted.url === ORGX_CODEX_MCP_URL) {
|
|
2481
2507
|
delete servers[ORGX_HOSTED_MCP_KEY];
|
|
2482
2508
|
}
|
|
2483
2509
|
removeLegacyCodexEntries(servers);
|
|
@@ -2489,7 +2515,7 @@ function inspectCodexConfigToml(input) {
|
|
|
2489
2515
|
const servers = readObject(current.mcp_servers);
|
|
2490
2516
|
const hosted = readObject(servers[ORGX_HOSTED_MCP_KEY]);
|
|
2491
2517
|
const local = readObject(servers[ORGX_LOCAL_MCP_KEY]);
|
|
2492
|
-
const hostedConfigured = hosted.url ===
|
|
2518
|
+
const hostedConfigured = hosted.url === ORGX_CODEX_MCP_URL;
|
|
2493
2519
|
const localConfigured = typeof local.url === "string";
|
|
2494
2520
|
return {
|
|
2495
2521
|
configured: hostedConfigured && localConfigured,
|
|
@@ -3582,6 +3608,7 @@ function defaultPluginPaths() {
|
|
|
3582
3608
|
claudeMarketplaceDir: CLAUDE_MANAGED_MARKETPLACE_DIR,
|
|
3583
3609
|
claudeMarketplaceManifestPath: CLAUDE_MANAGED_MARKETPLACE_MANIFEST_PATH,
|
|
3584
3610
|
claudePluginDir: CLAUDE_MANAGED_PLUGIN_DIR,
|
|
3611
|
+
codexConfigPath: CODEX_CONFIG_PATH ?? join4(CODEX_DIR, "config.toml"),
|
|
3585
3612
|
codexMarketplacePath: CODEX_MARKETPLACE_PATH,
|
|
3586
3613
|
codexPluginDir: CODEX_ORGX_PLUGIN_DIR,
|
|
3587
3614
|
cursorPluginDir: CURSOR_ORGX_PLUGIN_DIR,
|
|
@@ -3604,8 +3631,8 @@ function encodeRepoPath2(value) {
|
|
|
3604
3631
|
return value.split("/").filter((segment) => segment.length > 0).map((segment) => encodeURIComponent(segment)).join("/");
|
|
3605
3632
|
}
|
|
3606
3633
|
function isLikelyRepoFilePath(path) {
|
|
3607
|
-
const
|
|
3608
|
-
return
|
|
3634
|
+
const basename6 = path.split("/").pop() ?? path;
|
|
3635
|
+
return basename6.includes(".") && !/^\.[^./]+$/.test(basename6);
|
|
3609
3636
|
}
|
|
3610
3637
|
function buildContentsUrl2(spec, path) {
|
|
3611
3638
|
const encodedPath = encodeRepoPath2(path);
|
|
@@ -3881,7 +3908,7 @@ function formatCommandFailure(command, args, result) {
|
|
|
3881
3908
|
return `${command} ${args.join(" ")} failed${result.exitCode >= 0 ? ` with exit code ${result.exitCode}` : ""}${detail ? `: ${detail}` : "."}`;
|
|
3882
3909
|
}
|
|
3883
3910
|
async function defaultCommandRunner(command, args) {
|
|
3884
|
-
return await new Promise((
|
|
3911
|
+
return await new Promise((resolve5) => {
|
|
3885
3912
|
const child = spawn(command, [...args], {
|
|
3886
3913
|
env: process.env,
|
|
3887
3914
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -3895,16 +3922,16 @@ async function defaultCommandRunner(command, args) {
|
|
|
3895
3922
|
stderr += chunk.toString();
|
|
3896
3923
|
});
|
|
3897
3924
|
child.on("error", (error) => {
|
|
3898
|
-
const
|
|
3899
|
-
|
|
3925
|
+
const errorCode2 = typeof error === "object" && error && "code" in error ? String(error.code) : void 0;
|
|
3926
|
+
resolve5({
|
|
3900
3927
|
exitCode: -1,
|
|
3901
3928
|
stdout,
|
|
3902
3929
|
stderr,
|
|
3903
|
-
...
|
|
3930
|
+
...errorCode2 ? { errorCode: errorCode2 } : {}
|
|
3904
3931
|
});
|
|
3905
3932
|
});
|
|
3906
3933
|
child.on("close", (code) => {
|
|
3907
|
-
|
|
3934
|
+
resolve5({
|
|
3908
3935
|
exitCode: code ?? -1,
|
|
3909
3936
|
stdout,
|
|
3910
3937
|
stderr
|
|
@@ -4224,11 +4251,22 @@ async function installCodexPlugin(paths, fetchImpl, runner) {
|
|
|
4224
4251
|
}
|
|
4225
4252
|
const syncResult = await syncManagedRepoTree(CODEX_PLUGIN_SYNC_SPEC, paths.codexPluginDir, fetchImpl);
|
|
4226
4253
|
const marketplaceChanged = upsertCodexMarketplaceEntry(paths.codexMarketplacePath);
|
|
4227
|
-
const
|
|
4254
|
+
const previousConfig = readTextIfExists(paths.codexConfigPath);
|
|
4255
|
+
const nextConfig = migrateCodexHostedMcpProfile(previousConfig);
|
|
4256
|
+
const configChanged = nextConfig !== previousConfig;
|
|
4257
|
+
if (configChanged && nextConfig !== null) {
|
|
4258
|
+
writeTextFile(paths.codexConfigPath, nextConfig);
|
|
4259
|
+
}
|
|
4260
|
+
const changed = syncResult.changed || marketplaceChanged || configChanged;
|
|
4261
|
+
const changes = [
|
|
4262
|
+
...syncResult.changed ? [`Synced ${syncResult.fileCount} Codex plugin files.`] : [],
|
|
4263
|
+
...marketplaceChanged ? ["Updated the Codex marketplace entry."] : [],
|
|
4264
|
+
...configChanged ? ["Updated the existing OrgX MCP connection to the commander profile."] : []
|
|
4265
|
+
];
|
|
4228
4266
|
return {
|
|
4229
4267
|
target: "codex",
|
|
4230
4268
|
changed,
|
|
4231
|
-
message: changed ?
|
|
4269
|
+
message: changed ? changes.join(" ") : "Codex plugin files and marketplace entry already match the managed OrgX plugin."
|
|
4232
4270
|
};
|
|
4233
4271
|
}
|
|
4234
4272
|
async function installOpenclawPlugin(fetchImpl, runner) {
|
|
@@ -6211,7 +6249,7 @@ function initializeWizardSentry() {
|
|
|
6211
6249
|
Sentry.init({
|
|
6212
6250
|
dsn,
|
|
6213
6251
|
environment: process.env.ORGX_SENTRY_ENVIRONMENT || "production",
|
|
6214
|
-
release: "useorgx-wizard@0.1.
|
|
6252
|
+
release: "useorgx-wizard@0.1.58",
|
|
6215
6253
|
tracesSampleRate: sampleRate(process.env.ORGX_SENTRY_TRACES_SAMPLE_RATE),
|
|
6216
6254
|
enableLogs: true,
|
|
6217
6255
|
sendDefaultPii: false,
|
|
@@ -13623,13 +13661,758 @@ function buildWorkGraphHookReplayPatch(readResult) {
|
|
|
13623
13661
|
}
|
|
13624
13662
|
|
|
13625
13663
|
// src/lib/runtime-hooks.ts
|
|
13626
|
-
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
13664
|
+
import { copyFileSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync6 } from "fs";
|
|
13627
13665
|
import { homedir as homedir3 } from "os";
|
|
13628
|
-
import { dirname as dirname5, join as join8 } from "path";
|
|
13666
|
+
import { dirname as dirname5, join as join8, resolve as resolve2 } from "path";
|
|
13667
|
+
|
|
13668
|
+
// src/lib/session-summary-hook.ts
|
|
13669
|
+
var SESSION_SUMMARY_HOOK_MARKER = "orgx-session-summary.mjs";
|
|
13670
|
+
var SESSION_SUMMARY_ENDPOINT_PATH = "/api/v1/sessions/summary";
|
|
13671
|
+
var SESSION_SUMMARY_FALLBACK_ENDPOINT_PATH = "/api/v1/work-graph/reports";
|
|
13672
|
+
var SESSION_SUMMARY_SCHEMA_VERSION = "2026-08-07";
|
|
13673
|
+
var HOOK_OUTBOX_MAX_BYTES = 8 * 1024 * 1024;
|
|
13674
|
+
function buildSessionSummaryHookScriptContent() {
|
|
13675
|
+
return `#!/usr/bin/env node
|
|
13676
|
+
// OrgX lean session-summary hook. Generated by @useorgx/wizard.
|
|
13677
|
+
//
|
|
13678
|
+
// Hot path (PostToolUse and friends): ONE small read-modify-write. No network.
|
|
13679
|
+
// Terminal (Stop / SessionEnd): ONE compact cumulative summary queue write.
|
|
13680
|
+
// A separate orgx-wizard hooks flush process owns all network delivery.
|
|
13681
|
+
//
|
|
13682
|
+
// NOTE ON Stop SEMANTICS: in Claude Code, Stop fires at every assistant turn
|
|
13683
|
+
// boundary, not only once at session end. Stop therefore writes a cumulative
|
|
13684
|
+
// snapshot and retains state. The ingest endpoint merges repeated snapshots by
|
|
13685
|
+
// session_id; SessionEnd, when the client sends it, queues the final snapshot
|
|
13686
|
+
// and clears local state.
|
|
13687
|
+
|
|
13688
|
+
import { spawn } from "node:child_process";
|
|
13689
|
+
import { closeSync, fsyncSync, mkdirSync, openSync, readFileSync, writeFileSync, writeSync, rmSync, renameSync } from "node:fs";
|
|
13690
|
+
import { homedir } from "node:os";
|
|
13691
|
+
import { join, dirname, basename } from "node:path";
|
|
13692
|
+
|
|
13693
|
+
var SCHEMA_VERSION = "${SESSION_SUMMARY_SCHEMA_VERSION}";
|
|
13694
|
+
|
|
13695
|
+
export function parseArgs(argv) {
|
|
13696
|
+
var args = {};
|
|
13697
|
+
for (var i = 0; i < argv.length; i++) {
|
|
13698
|
+
var arg = argv[i];
|
|
13699
|
+
if (arg.indexOf("--") !== 0) continue;
|
|
13700
|
+
var eq = arg.indexOf("=");
|
|
13701
|
+
if (eq < 0) args[arg.slice(2)] = "true";
|
|
13702
|
+
else args[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
13703
|
+
}
|
|
13704
|
+
return args;
|
|
13705
|
+
}
|
|
13706
|
+
|
|
13707
|
+
export function pickString() {
|
|
13708
|
+
for (var i = 0; i < arguments.length; i++) {
|
|
13709
|
+
var value = arguments[i];
|
|
13710
|
+
if (typeof value !== "string") continue;
|
|
13711
|
+
var trimmed = value.trim();
|
|
13712
|
+
if (trimmed) return trimmed;
|
|
13713
|
+
}
|
|
13714
|
+
return undefined;
|
|
13715
|
+
}
|
|
13716
|
+
|
|
13717
|
+
function configHome() {
|
|
13718
|
+
var xdg = pickString(process.env.XDG_CONFIG_HOME);
|
|
13719
|
+
return xdg ? xdg : join(homedir(), ".config");
|
|
13720
|
+
}
|
|
13721
|
+
|
|
13722
|
+
function defaultStateDir() {
|
|
13723
|
+
var configured = pickString(process.env.ORGX_WIZARD_CONFIG_HOME);
|
|
13724
|
+
var root = configured ? configured : join(configHome(), "useorgx", "wizard");
|
|
13725
|
+
return join(root, "sessions");
|
|
13726
|
+
}
|
|
13727
|
+
|
|
13728
|
+
function defaultQueueDir() {
|
|
13729
|
+
var configured = pickString(process.env.ORGX_WIZARD_CONFIG_HOME);
|
|
13730
|
+
var root = configured ? configured : join(configHome(), "useorgx", "wizard");
|
|
13731
|
+
return join(root, "session-summary-captures");
|
|
13732
|
+
}
|
|
13733
|
+
|
|
13734
|
+
// Mirrors the backfill distiller so live and historical records agree on repo.
|
|
13735
|
+
export function repoOf(cwd) {
|
|
13736
|
+
if (!cwd) return "unknown";
|
|
13737
|
+
var marker = "/Code/";
|
|
13738
|
+
var at = cwd.indexOf(marker);
|
|
13739
|
+
if (at >= 0) {
|
|
13740
|
+
var rest = cwd.slice(at + marker.length);
|
|
13741
|
+
var slash = rest.indexOf("/");
|
|
13742
|
+
var name = slash < 0 ? rest : rest.slice(0, slash);
|
|
13743
|
+
if (name) return name;
|
|
13744
|
+
}
|
|
13745
|
+
return basename(cwd) || "unknown";
|
|
13746
|
+
}
|
|
13747
|
+
|
|
13748
|
+
export function statePath(sessionId, dir) {
|
|
13749
|
+
var root = dir ? dir : defaultStateDir();
|
|
13750
|
+
// Session ids come from the harness; keep them filesystem-safe regardless.
|
|
13751
|
+
var safe = String(sessionId).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
|
|
13752
|
+
return join(root, safe + ".json");
|
|
13753
|
+
}
|
|
13754
|
+
|
|
13755
|
+
function safeFilePart(value) {
|
|
13756
|
+
return String(value).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 128);
|
|
13757
|
+
}
|
|
13758
|
+
|
|
13759
|
+
export function enqueueSummaryCapture(queueDir, summary, initiativeId, nowIso, captureKind) {
|
|
13760
|
+
var root = queueDir ? queueDir : defaultQueueDir();
|
|
13761
|
+
var stamp = String(nowIso).replace(/[^A-Za-z0-9]/g, "_");
|
|
13762
|
+
var captureId = [summary.source_client, summary.session_id, summary.ended_at, summary.events].join(":");
|
|
13763
|
+
var nonce = Date.now().toString(36) + "-" + Math.random().toString(36).slice(2, 10);
|
|
13764
|
+
var name = "summary-" + safeFilePart(summary.source_client) + "-" + safeFilePart(summary.session_id) + "-" + stamp + "-" + process.pid + "-" + nonce + ".capture.json";
|
|
13765
|
+
var target = join(root, name);
|
|
13766
|
+
var temporary = target + ".tmp";
|
|
13767
|
+
var capture = {
|
|
13768
|
+
schema_version: "orgx-session-summary-capture/v1",
|
|
13769
|
+
capture_id: captureId,
|
|
13770
|
+
capture_kind: captureKind,
|
|
13771
|
+
queued_at: nowIso,
|
|
13772
|
+
session: summary,
|
|
13773
|
+
};
|
|
13774
|
+
if (initiativeId) capture.initiative_id = initiativeId;
|
|
13775
|
+
try {
|
|
13776
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
13777
|
+
var descriptor = openSync(temporary, "w", 0o600);
|
|
13778
|
+
try {
|
|
13779
|
+
writeSync(descriptor, JSON.stringify(capture), undefined, "utf8");
|
|
13780
|
+
fsyncSync(descriptor);
|
|
13781
|
+
} finally {
|
|
13782
|
+
closeSync(descriptor);
|
|
13783
|
+
}
|
|
13784
|
+
renameSync(temporary, target);
|
|
13785
|
+
return { path: target, capture: capture };
|
|
13786
|
+
} catch (e) {
|
|
13787
|
+
try { rmSync(temporary, { force: true }); } catch (ignored) {}
|
|
13788
|
+
return null;
|
|
13789
|
+
}
|
|
13790
|
+
}
|
|
13791
|
+
|
|
13792
|
+
export function emptyState(sessionId, sourceClient, cwd, nowIso) {
|
|
13793
|
+
return {
|
|
13794
|
+
session_id: sessionId,
|
|
13795
|
+
source_client: sourceClient,
|
|
13796
|
+
cwd: cwd === undefined ? null : cwd,
|
|
13797
|
+
repo: repoOf(cwd),
|
|
13798
|
+
first_ts: nowIso,
|
|
13799
|
+
last_ts: nowIso,
|
|
13800
|
+
events: 0,
|
|
13801
|
+
prompts: 0,
|
|
13802
|
+
tool_calls: 0,
|
|
13803
|
+
tools: {},
|
|
13804
|
+
edits: 0,
|
|
13805
|
+
bash: 0,
|
|
13806
|
+
action_seq: 0,
|
|
13807
|
+
actions: [],
|
|
13808
|
+
actions_omitted: 0,
|
|
13809
|
+
pending_actions: [],
|
|
13810
|
+
pending_starts_omitted: 0,
|
|
13811
|
+
permission_requests: 0,
|
|
13812
|
+
permission_modes: [],
|
|
13813
|
+
work_context: null,
|
|
13814
|
+
};
|
|
13815
|
+
}
|
|
13816
|
+
|
|
13817
|
+
// One bounded read-modify-write. No spool, no network, no unbounded growth:
|
|
13818
|
+
// the tool map is capped so a pathological session cannot inflate the file.
|
|
13819
|
+
var MAX_TOOL_KEYS = 40;
|
|
13820
|
+
var MAX_CAPTURE_ACTIONS = 32;
|
|
13821
|
+
var MAX_PENDING_ACTIONS = 32;
|
|
13822
|
+
|
|
13823
|
+
function isPromptEvent(event) {
|
|
13824
|
+
return event === "UserPromptSubmit" || String(event).indexOf("user_prompt") >= 0;
|
|
13825
|
+
}
|
|
13826
|
+
|
|
13827
|
+
function isToolCompletionEvent(event) {
|
|
13828
|
+
var normalized = String(event).toLowerCase();
|
|
13829
|
+
return event === "PostToolUse" ||
|
|
13830
|
+
event === "PostToolUseFailure" ||
|
|
13831
|
+
normalized.indexOf("post_tool_use") >= 0;
|
|
13832
|
+
}
|
|
13833
|
+
|
|
13834
|
+
function ensureExecutionState(state) {
|
|
13835
|
+
if (!Number.isFinite(state.action_seq)) state.action_seq = 0;
|
|
13836
|
+
if (!Array.isArray(state.actions)) state.actions = [];
|
|
13837
|
+
if (!Number.isFinite(state.actions_omitted)) state.actions_omitted = 0;
|
|
13838
|
+
if (!Array.isArray(state.pending_actions)) state.pending_actions = [];
|
|
13839
|
+
if (!Number.isFinite(state.pending_starts_omitted)) state.pending_starts_omitted = 0;
|
|
13840
|
+
if (!Number.isFinite(state.permission_requests)) state.permission_requests = 0;
|
|
13841
|
+
if (!Array.isArray(state.permission_modes)) state.permission_modes = [];
|
|
13842
|
+
return state;
|
|
13843
|
+
}
|
|
13844
|
+
|
|
13845
|
+
function nextActionId(state) {
|
|
13846
|
+
state.action_seq += 1;
|
|
13847
|
+
return "action-" + String(state.action_seq).padStart(6, "0");
|
|
13848
|
+
}
|
|
13849
|
+
|
|
13850
|
+
function boundedString(value, max) {
|
|
13851
|
+
var text = pickString(value);
|
|
13852
|
+
return text ? text.slice(0, max) : undefined;
|
|
13853
|
+
}
|
|
13854
|
+
|
|
13855
|
+
function safeDuration(value) {
|
|
13856
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return undefined;
|
|
13857
|
+
return Math.min(Math.round(value), 86400000);
|
|
13858
|
+
}
|
|
13859
|
+
|
|
13860
|
+
function actionResult(event, sourceClient) {
|
|
13861
|
+
if (event === "PostToolUseFailure" || String(event).toLowerCase().indexOf("failure") >= 0) {
|
|
13862
|
+
return "failed";
|
|
13863
|
+
}
|
|
13864
|
+
// Claude's PostToolUse contract fires only after success. Codex documents
|
|
13865
|
+
// PostToolUse for completed tools, including non-zero Bash exits, so its
|
|
13866
|
+
// outcome must remain explicitly unclassified without reading tool output.
|
|
13867
|
+
return sourceClient === "claude-code" ? "succeeded" : "completed_unknown";
|
|
13868
|
+
}
|
|
13869
|
+
|
|
13870
|
+
function pendingIndex(state, toolUseId) {
|
|
13871
|
+
if (!toolUseId) return -1;
|
|
13872
|
+
for (var i = 0; i < state.pending_actions.length; i++) {
|
|
13873
|
+
if (state.pending_actions[i].correlation_key === toolUseId) return i;
|
|
13874
|
+
}
|
|
13875
|
+
return -1;
|
|
13876
|
+
}
|
|
13877
|
+
|
|
13878
|
+
function observeAction(state, input) {
|
|
13879
|
+
var event = input.event;
|
|
13880
|
+
var toolName = boundedString(input.toolName, 120);
|
|
13881
|
+
if (!toolName) return;
|
|
13882
|
+
var toolUseId = boundedString(input.toolUseId, 200);
|
|
13883
|
+
|
|
13884
|
+
if (event === "PreToolUse" || String(event).toLowerCase().indexOf("pre_tool_use") >= 0) {
|
|
13885
|
+
if (pendingIndex(state, toolUseId) >= 0) return;
|
|
13886
|
+
if (state.pending_actions.length >= MAX_PENDING_ACTIONS) {
|
|
13887
|
+
state.pending_starts_omitted += 1;
|
|
13888
|
+
return;
|
|
13889
|
+
}
|
|
13890
|
+
state.pending_actions.push({
|
|
13891
|
+
id: nextActionId(state),
|
|
13892
|
+
correlation_key: toolUseId,
|
|
13893
|
+
tool_name: normalizeToolName(toolName),
|
|
13894
|
+
started_at: input.nowIso,
|
|
13895
|
+
turn_id: boundedString(input.turnId, 120),
|
|
13896
|
+
});
|
|
13897
|
+
return;
|
|
13898
|
+
}
|
|
13899
|
+
|
|
13900
|
+
if (!isToolCompletionEvent(event)) return;
|
|
13901
|
+
var index = pendingIndex(state, toolUseId);
|
|
13902
|
+
var pending = index >= 0 ? state.pending_actions.splice(index, 1)[0] : null;
|
|
13903
|
+
if (!pending) state.pending_starts_omitted += 1;
|
|
13904
|
+
var action = {
|
|
13905
|
+
id: pending ? pending.id : nextActionId(state),
|
|
13906
|
+
type: "tool.invoke",
|
|
13907
|
+
tool_name: normalizeToolName(toolName),
|
|
13908
|
+
status: actionResult(event, state.source_client),
|
|
13909
|
+
started_at: pending ? pending.started_at : undefined,
|
|
13910
|
+
completed_at: input.nowIso,
|
|
13911
|
+
duration_ms: safeDuration(input.durationMs),
|
|
13912
|
+
turn_id: boundedString(input.turnId, 120) || (pending ? pending.turn_id : undefined),
|
|
13913
|
+
provenance: "runtime_observed",
|
|
13914
|
+
};
|
|
13915
|
+
if (state.actions.length < MAX_CAPTURE_ACTIONS) state.actions.push(action);
|
|
13916
|
+
else state.actions_omitted += 1;
|
|
13917
|
+
}
|
|
13918
|
+
|
|
13919
|
+
export function applyEvent(state, input) {
|
|
13920
|
+
ensureExecutionState(state);
|
|
13921
|
+
var event = input.event;
|
|
13922
|
+
var toolName = input.toolName;
|
|
13923
|
+
state.events += 1;
|
|
13924
|
+
state.last_ts = input.nowIso;
|
|
13925
|
+
|
|
13926
|
+
if (isPromptEvent(event)) state.prompts += 1;
|
|
13927
|
+
|
|
13928
|
+
if (event === "PermissionRequest") state.permission_requests += 1;
|
|
13929
|
+
var permissionMode = boundedString(input.permissionMode, 40);
|
|
13930
|
+
if (
|
|
13931
|
+
permissionMode &&
|
|
13932
|
+
state.permission_modes.indexOf(permissionMode) < 0 &&
|
|
13933
|
+
state.permission_modes.length < 8
|
|
13934
|
+
) {
|
|
13935
|
+
state.permission_modes.push(permissionMode);
|
|
13936
|
+
}
|
|
13937
|
+
|
|
13938
|
+
observeAction(state, input);
|
|
13939
|
+
|
|
13940
|
+
if (toolName && isToolCompletionEvent(event)) {
|
|
13941
|
+
state.tool_calls += 1;
|
|
13942
|
+
var known = state.tools[toolName] !== undefined;
|
|
13943
|
+
if (known || Object.keys(state.tools).length < MAX_TOOL_KEYS) {
|
|
13944
|
+
state.tools[toolName] = (state.tools[toolName] || 0) + 1;
|
|
13945
|
+
}
|
|
13946
|
+
if (toolName === "Write" || toolName === "Edit" || toolName === "NotebookEdit") state.edits += 1;
|
|
13947
|
+
if (toolName === "Bash") state.bash += 1;
|
|
13948
|
+
}
|
|
13949
|
+
return state;
|
|
13950
|
+
}
|
|
13951
|
+
|
|
13952
|
+
// Long MCP tool names ("mcp__<uuid>__<tool>") can make a raw tool map dominate
|
|
13953
|
+
// the payload, so keep the heaviest tools and bucket the tail. This bounds the
|
|
13954
|
+
// summary regardless of how varied or long a session gets.
|
|
13955
|
+
var MAX_REPORTED_TOOLS = 12;
|
|
13956
|
+
var MAX_TOOL_NAME = 48;
|
|
13957
|
+
|
|
13958
|
+
// "mcp__c2ed4428-8c64-4586-9c2a-52eac05637b8__search-contacts" is mostly an
|
|
13959
|
+
// opaque server uuid. The tool suffix is the part with analytic value, so
|
|
13960
|
+
// collapse the uuid rather than truncating the informative end.
|
|
13961
|
+
export function normalizeToolName(name) {
|
|
13962
|
+
var mcp = /^mcp__[0-9a-fA-F-]{16,}__(.+)$/.exec(name);
|
|
13963
|
+
var base = mcp ? "mcp__" + mcp[1] : name;
|
|
13964
|
+
return base.length > MAX_TOOL_NAME ? base.slice(0, MAX_TOOL_NAME) : base;
|
|
13965
|
+
}
|
|
13966
|
+
|
|
13967
|
+
export function boundTools(tools) {
|
|
13968
|
+
// Collapse first so two variants of the same tool merge before ranking.
|
|
13969
|
+
var merged = {};
|
|
13970
|
+
var names = Object.keys(tools);
|
|
13971
|
+
for (var i = 0; i < names.length; i++) {
|
|
13972
|
+
var key = normalizeToolName(names[i]);
|
|
13973
|
+
merged[key] = (merged[key] || 0) + tools[names[i]];
|
|
13974
|
+
}
|
|
13975
|
+
var entries = Object.keys(merged).map(function (k) { return [k, merged[k]]; });
|
|
13976
|
+
entries.sort(function (a, b) { return b[1] - a[1]; });
|
|
13977
|
+
var out = {};
|
|
13978
|
+
if (entries.length <= MAX_REPORTED_TOOLS) {
|
|
13979
|
+
for (var j = 0; j < entries.length; j++) out[entries[j][0]] = entries[j][1];
|
|
13980
|
+
return out;
|
|
13981
|
+
}
|
|
13982
|
+
for (var k = 0; k < MAX_REPORTED_TOOLS; k++) out[entries[k][0]] = entries[k][1];
|
|
13983
|
+
var other = 0;
|
|
13984
|
+
for (var m = MAX_REPORTED_TOOLS; m < entries.length; m++) other += entries[m][1];
|
|
13985
|
+
out.__other__ = other;
|
|
13986
|
+
out.__other_tools__ = entries.length - MAX_REPORTED_TOOLS;
|
|
13987
|
+
return out;
|
|
13988
|
+
}
|
|
13989
|
+
|
|
13990
|
+
function buildExecutionObservation(state, captureKind) {
|
|
13991
|
+
ensureExecutionState(state);
|
|
13992
|
+
var remaining = Math.max(0, MAX_CAPTURE_ACTIONS - state.actions.length);
|
|
13993
|
+
var includedPending = state.pending_actions.slice(0, remaining).map(function (pending) {
|
|
13994
|
+
return {
|
|
13995
|
+
id: pending.id,
|
|
13996
|
+
type: "tool.invoke",
|
|
13997
|
+
tool_name: pending.tool_name,
|
|
13998
|
+
status: "running",
|
|
13999
|
+
started_at: pending.started_at,
|
|
14000
|
+
turn_id: pending.turn_id,
|
|
14001
|
+
provenance: "runtime_observed",
|
|
14002
|
+
};
|
|
14003
|
+
});
|
|
14004
|
+
var pendingOmitted = Math.max(0, state.pending_actions.length - includedPending.length);
|
|
14005
|
+
var actions = state.actions.concat(includedPending).map(function (action) {
|
|
14006
|
+
var out = {
|
|
14007
|
+
id: boundedString(action.id, 80),
|
|
14008
|
+
type: "tool.invoke",
|
|
14009
|
+
tool_name: boundedString(action.tool_name, 48),
|
|
14010
|
+
status: action.status,
|
|
14011
|
+
provenance: "runtime_observed",
|
|
14012
|
+
};
|
|
14013
|
+
if (action.started_at) out.started_at = action.started_at;
|
|
14014
|
+
if (action.completed_at) out.completed_at = action.completed_at;
|
|
14015
|
+
if (action.duration_ms !== undefined) out.duration_ms = action.duration_ms;
|
|
14016
|
+
if (action.turn_id) out.turn_id = action.turn_id;
|
|
14017
|
+
return out;
|
|
14018
|
+
});
|
|
14019
|
+
var omitted = state.actions_omitted + pendingOmitted;
|
|
14020
|
+
var workContext = state.work_context && typeof state.work_context === "object"
|
|
14021
|
+
? state.work_context
|
|
14022
|
+
: null;
|
|
14023
|
+
var unavailable = ["verification"];
|
|
14024
|
+
if (!workContext || !workContext.intent) unavailable.push("intent");
|
|
14025
|
+
if (!workContext || !workContext.authority) unavailable.push("authority_decision");
|
|
14026
|
+
if (!workContext || !workContext.cost) unavailable.push("cost");
|
|
14027
|
+
if (!workContext || !Array.isArray(workContext.artifact_refs) || workContext.artifact_refs.length === 0) {
|
|
14028
|
+
unavailable.push("artifact_refs");
|
|
14029
|
+
}
|
|
14030
|
+
return {
|
|
14031
|
+
schema_version: "orgx-session-execution-observation/v1",
|
|
14032
|
+
boundary: captureKind || "legacy_unspecified",
|
|
14033
|
+
terminal_observed: captureKind === "session_end",
|
|
14034
|
+
actions: actions,
|
|
14035
|
+
actions_completed_observed: state.tool_calls,
|
|
14036
|
+
actions_omitted: omitted,
|
|
14037
|
+
start_times_omitted: state.pending_starts_omitted,
|
|
14038
|
+
pending_actions: state.pending_actions.length,
|
|
14039
|
+
permission_requests_observed: state.permission_requests,
|
|
14040
|
+
permission_modes_observed: state.permission_modes.slice(0, 8),
|
|
14041
|
+
action_capture_complete:
|
|
14042
|
+
omitted === 0 &&
|
|
14043
|
+
state.pending_starts_omitted === 0 &&
|
|
14044
|
+
state.pending_actions.length === 0,
|
|
14045
|
+
unavailable_fields: unavailable,
|
|
14046
|
+
};
|
|
14047
|
+
}
|
|
14048
|
+
|
|
14049
|
+
export function buildSummary(state, nowIso, captureKind) {
|
|
14050
|
+
var first = Date.parse(state.first_ts);
|
|
14051
|
+
var last = Date.parse(state.last_ts || nowIso);
|
|
14052
|
+
var spanOk = isFinite(first) && isFinite(last);
|
|
14053
|
+
var metadata = { execution: buildExecutionObservation(state, captureKind) };
|
|
14054
|
+
if (state.work_context && typeof state.work_context === "object") {
|
|
14055
|
+
metadata.work_context = state.work_context;
|
|
14056
|
+
}
|
|
14057
|
+
return {
|
|
14058
|
+
schema_version: SCHEMA_VERSION,
|
|
14059
|
+
source: "orgx_session_summary",
|
|
14060
|
+
session_id: state.session_id,
|
|
14061
|
+
source_client: state.source_client,
|
|
14062
|
+
repo: state.repo,
|
|
14063
|
+
cwd: state.cwd,
|
|
14064
|
+
day: isFinite(first) ? new Date(first).toISOString().slice(0, 10) : "unknown",
|
|
14065
|
+
started_at: state.first_ts,
|
|
14066
|
+
ended_at: state.last_ts,
|
|
14067
|
+
duration_min: spanOk ? Math.round((last - first) / 60000) : 0,
|
|
14068
|
+
events: state.events,
|
|
14069
|
+
prompts: state.prompts,
|
|
14070
|
+
tool_calls: state.tool_calls,
|
|
14071
|
+
tools: boundTools(state.tools),
|
|
14072
|
+
edits: state.edits,
|
|
14073
|
+
bash: state.bash,
|
|
14074
|
+
metadata: metadata,
|
|
14075
|
+
};
|
|
14076
|
+
}
|
|
14077
|
+
|
|
14078
|
+
export function readState(path) {
|
|
14079
|
+
try {
|
|
14080
|
+
var parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
14081
|
+
var ok = parsed && typeof parsed === "object" && !Array.isArray(parsed);
|
|
14082
|
+
return ok ? parsed : null;
|
|
14083
|
+
} catch (e) {
|
|
14084
|
+
return null;
|
|
14085
|
+
}
|
|
14086
|
+
}
|
|
14087
|
+
|
|
14088
|
+
export function writeState(path, state) {
|
|
14089
|
+
try {
|
|
14090
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
14091
|
+
writeFileSync(path, JSON.stringify(state), { encoding: "utf8", mode: 0o600 });
|
|
14092
|
+
return true;
|
|
14093
|
+
} catch (e) {
|
|
14094
|
+
return false;
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
|
|
14098
|
+
async function readStdin() {
|
|
14099
|
+
try {
|
|
14100
|
+
var chunks = [];
|
|
14101
|
+
for await (var chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
14102
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
14103
|
+
} catch (e) {
|
|
14104
|
+
return "";
|
|
14105
|
+
}
|
|
14106
|
+
}
|
|
14107
|
+
|
|
14108
|
+
export function parseJson(value) {
|
|
14109
|
+
try {
|
|
14110
|
+
var parsed = JSON.parse(value || "{}");
|
|
14111
|
+
var ok = parsed && typeof parsed === "object" && !Array.isArray(parsed);
|
|
14112
|
+
return ok ? parsed : {};
|
|
14113
|
+
} catch (e) {
|
|
14114
|
+
return {};
|
|
14115
|
+
}
|
|
14116
|
+
}
|
|
14117
|
+
|
|
14118
|
+
function workString(value, max) {
|
|
14119
|
+
if (typeof value !== "string") return null;
|
|
14120
|
+
var trimmed = value.trim();
|
|
14121
|
+
return trimmed && trimmed.length <= max ? trimmed : null;
|
|
14122
|
+
}
|
|
14123
|
+
|
|
14124
|
+
function workObject(value) {
|
|
14125
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
14126
|
+
}
|
|
14127
|
+
|
|
14128
|
+
function workStrings(value, max) {
|
|
14129
|
+
if (value === undefined) return [];
|
|
14130
|
+
if (!Array.isArray(value) || value.length > 20) return null;
|
|
14131
|
+
var output = [];
|
|
14132
|
+
for (var i = 0; i < value.length; i++) {
|
|
14133
|
+
var item = workString(value[i], max);
|
|
14134
|
+
if (!item) return null;
|
|
14135
|
+
if (output.indexOf(item) < 0) output.push(item);
|
|
14136
|
+
}
|
|
14137
|
+
return output;
|
|
14138
|
+
}
|
|
14139
|
+
|
|
14140
|
+
function workRef(value) {
|
|
14141
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
14142
|
+
var system = workString(value.system, 120);
|
|
14143
|
+
var type = workString(value.type, 120);
|
|
14144
|
+
var id = workString(value.id, 500);
|
|
14145
|
+
var uri = value.uri === undefined ? undefined : workString(value.uri, 1000);
|
|
14146
|
+
var version = value.version === undefined ? undefined : workString(value.version, 120);
|
|
14147
|
+
if (!system || !type || !id || uri === null || version === null) return null;
|
|
14148
|
+
if (uri && /[\0- ]/.test(uri)) return null;
|
|
14149
|
+
var ref = { system: system, type: type, id: id };
|
|
14150
|
+
if (uri) ref.uri = uri;
|
|
14151
|
+
if (version) ref.version = version;
|
|
14152
|
+
return ref;
|
|
14153
|
+
}
|
|
14154
|
+
|
|
14155
|
+
function workRefs(value) {
|
|
14156
|
+
if (value === undefined) return [];
|
|
14157
|
+
if (!Array.isArray(value) || value.length > 20) return null;
|
|
14158
|
+
var refs = [];
|
|
14159
|
+
var seen = {};
|
|
14160
|
+
for (var i = 0; i < value.length; i++) {
|
|
14161
|
+
var ref = workRef(value[i]);
|
|
14162
|
+
if (!ref) return null;
|
|
14163
|
+
var key = ref.system + "\0" + ref.type + "\0" + ref.id;
|
|
14164
|
+
if (!seen[key]) {
|
|
14165
|
+
seen[key] = true;
|
|
14166
|
+
refs.push(ref);
|
|
14167
|
+
}
|
|
14168
|
+
}
|
|
14169
|
+
refs.sort(function (a, b) {
|
|
14170
|
+
var left = a.system + "\0" + a.type + "\0" + a.id;
|
|
14171
|
+
var right = b.system + "\0" + b.type + "\0" + b.id;
|
|
14172
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
14173
|
+
});
|
|
14174
|
+
return refs;
|
|
14175
|
+
}
|
|
14176
|
+
|
|
14177
|
+
function workIso(value) {
|
|
14178
|
+
var text = workString(value, 100);
|
|
14179
|
+
return text && /^d{4}-d{2}-d{2}Td{2}:d{2}:d{2}(?:.d+)?(?:Z|[+-]d{2}:d{2})$/.test(text) && !Number.isNaN(Date.parse(text))
|
|
14180
|
+
? text
|
|
14181
|
+
: null;
|
|
14182
|
+
}
|
|
14183
|
+
|
|
14184
|
+
export function parseExplicitWorkContext(value) {
|
|
14185
|
+
if (!value || Buffer.byteLength(String(value), "utf8") > 4096) return null;
|
|
14186
|
+
var parsed = parseJson(value);
|
|
14187
|
+
if (parsed.schema_version !== "orgx-session-work-context/v1") return null;
|
|
14188
|
+
var intent = workObject(parsed.intent);
|
|
14189
|
+
var authority = workObject(parsed.authority);
|
|
14190
|
+
var scope = workObject(authority && authority.scope);
|
|
14191
|
+
var cost = workObject(parsed.cost);
|
|
14192
|
+
if (!intent || !authority || !scope || !cost) return null;
|
|
14193
|
+
var summary = workString(intent.summary, 2000);
|
|
14194
|
+
var objective = intent.objective === undefined ? undefined : workString(intent.objective, 4000);
|
|
14195
|
+
var acceptance = workStrings(intent.acceptance_criteria, 1000);
|
|
14196
|
+
var intentConstraints = workStrings(intent.constraints, 1000);
|
|
14197
|
+
var requestRef = intent.request_ref === undefined ? undefined : workRef(intent.request_ref);
|
|
14198
|
+
var modes = ["explicit", "delegated", "inherited", "policy", "none", "unknown"];
|
|
14199
|
+
var statuses = ["granted", "restricted", "denied", "expired", "unknown"];
|
|
14200
|
+
var mode = workString(authority.mode, 40);
|
|
14201
|
+
var status = workString(authority.status, 40);
|
|
14202
|
+
var actions = workStrings(scope.actions, 120);
|
|
14203
|
+
var resources = workRefs(scope.resources);
|
|
14204
|
+
var systems = workStrings(scope.systems, 120);
|
|
14205
|
+
var spend = scope.spend_limit === undefined ? undefined : workObject(scope.spend_limit);
|
|
14206
|
+
var spendCurrency = spend === undefined ? undefined : workString(spend && spend.currency, 12);
|
|
14207
|
+
var spendAmount = spend && spend.amount;
|
|
14208
|
+
var authorityConstraints = workStrings(authority.constraints, 1000);
|
|
14209
|
+
var authorizationRef = authority.authorization_ref === undefined ? undefined : workRef(authority.authorization_ref);
|
|
14210
|
+
var validFrom = authority.valid_from === undefined ? undefined : workIso(authority.valid_from);
|
|
14211
|
+
var validUntil = authority.valid_until === undefined ? undefined : workIso(authority.valid_until);
|
|
14212
|
+
var artifactRefs = workRefs(parsed.artifact_refs);
|
|
14213
|
+
var evidenceRefs = workRefs(parsed.evidence_refs);
|
|
14214
|
+
var currency = workString(cost.currency, 12);
|
|
14215
|
+
var source = cost.source === undefined ? undefined : workString(cost.source, 120);
|
|
14216
|
+
if (!summary || objective === null || acceptance === null || intentConstraints === null || requestRef === null) return null;
|
|
14217
|
+
if (!mode || modes.indexOf(mode) < 0 || !status || statuses.indexOf(status) < 0) return null;
|
|
14218
|
+
if (actions === null || resources === null || systems === null || authorityConstraints === null || authorizationRef === null) return null;
|
|
14219
|
+
if (spend !== undefined && (!spend || !spendCurrency || !/^[A-Z][A-Z0-9_-]{1,11}$/.test(spendCurrency) || typeof spendAmount !== "number" || !Number.isFinite(spendAmount) || spendAmount < 0 || spendAmount > 100000)) return null;
|
|
14220
|
+
if (validFrom === null || validUntil === null || (validFrom && validUntil && Date.parse(validUntil) < Date.parse(validFrom))) return null;
|
|
14221
|
+
if (!currency || !/^[A-Z][A-Z0-9_-]{1,11}$/.test(currency) || typeof cost.total !== "number" || !Number.isFinite(cost.total) || cost.total < 0 || cost.total > 100000 || source === null) return null;
|
|
14222
|
+
if (cost.estimated !== undefined && typeof cost.estimated !== "boolean") return null;
|
|
14223
|
+
if (artifactRefs === null || evidenceRefs === null) return null;
|
|
14224
|
+
var normalizedIntent = { summary: summary, acceptance_criteria: acceptance, constraints: intentConstraints };
|
|
14225
|
+
if (objective) normalizedIntent.objective = objective;
|
|
14226
|
+
if (requestRef) normalizedIntent.request_ref = requestRef;
|
|
14227
|
+
var normalizedAuthority = {
|
|
14228
|
+
mode: mode,
|
|
14229
|
+
status: status,
|
|
14230
|
+
scope: { actions: actions, resources: resources, systems: systems },
|
|
14231
|
+
constraints: authorityConstraints,
|
|
14232
|
+
};
|
|
14233
|
+
if (spend !== undefined) normalizedAuthority.scope.spend_limit = { currency: spendCurrency, amount: spendAmount };
|
|
14234
|
+
if (authorizationRef) normalizedAuthority.authorization_ref = authorizationRef;
|
|
14235
|
+
if (validFrom) normalizedAuthority.valid_from = validFrom;
|
|
14236
|
+
if (validUntil) normalizedAuthority.valid_until = validUntil;
|
|
14237
|
+
var normalizedCost = { currency: currency, total: cost.total, estimated: cost.estimated !== false };
|
|
14238
|
+
if (source) normalizedCost.source = source;
|
|
14239
|
+
return {
|
|
14240
|
+
schema_version: "orgx-session-work-context/v1",
|
|
14241
|
+
provenance: "producer_asserted",
|
|
14242
|
+
intent: normalizedIntent,
|
|
14243
|
+
authority: normalizedAuthority,
|
|
14244
|
+
cost: normalizedCost,
|
|
14245
|
+
artifact_refs: artifactRefs,
|
|
14246
|
+
evidence_refs: evidenceRefs,
|
|
14247
|
+
};
|
|
14248
|
+
}
|
|
14249
|
+
|
|
14250
|
+
function autoFlushDisabled(value) {
|
|
14251
|
+
var normalized = String(value || "").trim().toLowerCase();
|
|
14252
|
+
return normalized === "off" || normalized === "false" || normalized === "0";
|
|
14253
|
+
}
|
|
14254
|
+
|
|
14255
|
+
/**
|
|
14256
|
+
* Ask a detached Wizard process to deliver the durable queue. The hook itself
|
|
14257
|
+
* performs no network or credential work and never waits for delivery.
|
|
14258
|
+
*/
|
|
14259
|
+
export function triggerQueueDelivery(args, env, queueDir, spawnImpl) {
|
|
14260
|
+
if (autoFlushDisabled(env.ORGX_SESSION_SUMMARY_AUTO_FLUSH)) return false;
|
|
14261
|
+
var nodePath = pickString(args.delivery_node);
|
|
14262
|
+
var cliPath = pickString(args.delivery_cli);
|
|
14263
|
+
if (!nodePath || !cliPath || !queueDir) return false;
|
|
14264
|
+
var configuredLimit = parseInt(
|
|
14265
|
+
pickString(args.auto_flush_limit, env.ORGX_SESSION_SUMMARY_AUTO_FLUSH_LIMIT) || "",
|
|
14266
|
+
10
|
|
14267
|
+
);
|
|
14268
|
+
var limit = Number.isFinite(configuredLimit)
|
|
14269
|
+
? Math.max(1, Math.min(configuredLimit, 100))
|
|
14270
|
+
: 25;
|
|
14271
|
+
try {
|
|
14272
|
+
var child = spawnImpl(
|
|
14273
|
+
nodePath,
|
|
14274
|
+
[
|
|
14275
|
+
cliPath,
|
|
14276
|
+
"hooks",
|
|
14277
|
+
"flush",
|
|
14278
|
+
"--background",
|
|
14279
|
+
"--limit=" + String(limit),
|
|
14280
|
+
"--queue=" + queueDir,
|
|
14281
|
+
],
|
|
14282
|
+
{ detached: true, stdio: "ignore" }
|
|
14283
|
+
);
|
|
14284
|
+
if (child && typeof child.on === "function") {
|
|
14285
|
+
child.on("error", function () {});
|
|
14286
|
+
}
|
|
14287
|
+
if (child && typeof child.unref === "function") child.unref();
|
|
14288
|
+
return true;
|
|
14289
|
+
} catch (e) {
|
|
14290
|
+
return false;
|
|
14291
|
+
}
|
|
14292
|
+
}
|
|
14293
|
+
|
|
14294
|
+
export async function main(options) {
|
|
14295
|
+
var opts = options || {};
|
|
14296
|
+
var argv = opts.argv ? opts.argv : process.argv.slice(2);
|
|
14297
|
+
var env = opts.env ? opts.env : process.env;
|
|
14298
|
+
var stdinText = opts.stdinText === undefined ? "" : opts.stdinText;
|
|
14299
|
+
var now = opts.now ? opts.now : function () { return new Date().toISOString(); };
|
|
14300
|
+
var dir = opts.dir;
|
|
14301
|
+
var spawnImpl = opts.spawnImpl ? opts.spawnImpl : spawn;
|
|
14302
|
+
|
|
14303
|
+
var args = parseArgs(argv);
|
|
14304
|
+
var payload = parseJson(stdinText);
|
|
14305
|
+
var nowIso = now();
|
|
14306
|
+
var explicitWorkContext = parseExplicitWorkContext(env.ORGX_SESSION_WORK_CONTEXT);
|
|
14307
|
+
|
|
14308
|
+
var event = pickString(args.event, payload.hook_event_name, payload.hookEventName, "unknown");
|
|
14309
|
+
var sessionId = pickString(payload.session_id, payload.sessionId, args.session_id);
|
|
14310
|
+
if (!sessionId) return { ok: true, skipped: "missing_session_id" };
|
|
14311
|
+
|
|
14312
|
+
var sourceClient = pickString(args.source_client, env.ORGX_SOURCE_CLIENT, "claude-code");
|
|
14313
|
+
var cwd = pickString(payload.cwd, args.cwd);
|
|
14314
|
+
var stateDir = dir ? dir : pickString(args.state_dir, env.ORGX_SESSION_STATE_DIR);
|
|
14315
|
+
var path = statePath(sessionId, stateDir);
|
|
14316
|
+
|
|
14317
|
+
var terminal = event === "Stop" || event === "SessionEnd";
|
|
14318
|
+
|
|
14319
|
+
if (!terminal) {
|
|
14320
|
+
// Hot path: one small file read + write. Never a network call.
|
|
14321
|
+
var existing = readState(path);
|
|
14322
|
+
var state = existing ? existing : emptyState(sessionId, sourceClient, cwd, nowIso);
|
|
14323
|
+
if (!state.cwd && cwd) {
|
|
14324
|
+
state.cwd = cwd;
|
|
14325
|
+
state.repo = repoOf(cwd);
|
|
14326
|
+
}
|
|
14327
|
+
if (explicitWorkContext) state.work_context = explicitWorkContext;
|
|
14328
|
+
var toolName = pickString(
|
|
14329
|
+
payload.tool_name,
|
|
14330
|
+
payload.toolName,
|
|
14331
|
+
payload.tool ? payload.tool.name : undefined
|
|
14332
|
+
);
|
|
14333
|
+
applyEvent(state, {
|
|
14334
|
+
event: event,
|
|
14335
|
+
toolName: toolName,
|
|
14336
|
+
toolUseId: pickString(payload.tool_use_id, payload.toolUseId),
|
|
14337
|
+
turnId: pickString(payload.turn_id, payload.turnId),
|
|
14338
|
+
durationMs: payload.duration_ms,
|
|
14339
|
+
permissionMode: pickString(payload.permission_mode, payload.permissionMode),
|
|
14340
|
+
nowIso: nowIso,
|
|
14341
|
+
});
|
|
14342
|
+
writeState(path, state);
|
|
14343
|
+
return { ok: true, counted: true, event: event };
|
|
14344
|
+
}
|
|
14345
|
+
|
|
14346
|
+
// Terminal: persist one cumulative snapshot, then ask a detached Wizard
|
|
14347
|
+
// worker to flush. The hook never owns credentials, retries, or network I/O.
|
|
14348
|
+
var finalState = readState(path);
|
|
14349
|
+
if (!finalState) return { ok: true, skipped: "no_session_state" };
|
|
14350
|
+
if (explicitWorkContext) finalState.work_context = explicitWorkContext;
|
|
14351
|
+
var initiativeId = pickString(env.ORGX_INITIATIVE_ID, args.initiative);
|
|
14352
|
+
var queueDir = pickString(args.queue_dir, env.ORGX_SESSION_SUMMARY_QUEUE_DIR) || defaultQueueDir();
|
|
14353
|
+
var captureKind = event === "SessionEnd" ? "session_end" : "turn_boundary";
|
|
14354
|
+
var summary = buildSummary(finalState, nowIso, captureKind);
|
|
14355
|
+
var queued = enqueueSummaryCapture(queueDir, summary, initiativeId, nowIso, captureKind);
|
|
14356
|
+
if (!queued) return { ok: true, skipped: "queue_write_failed", summary: summary };
|
|
14357
|
+
var deliveryTriggered = triggerQueueDelivery(args, env, queueDir, spawnImpl);
|
|
14358
|
+
|
|
14359
|
+
if (event === "SessionEnd") {
|
|
14360
|
+
try {
|
|
14361
|
+
rmSync(path, { force: true });
|
|
14362
|
+
} catch (e) {
|
|
14363
|
+
// keep going
|
|
14364
|
+
}
|
|
14365
|
+
}
|
|
14366
|
+
|
|
14367
|
+
return {
|
|
14368
|
+
ok: true,
|
|
14369
|
+
queued: true,
|
|
14370
|
+
queue_path: queued.path,
|
|
14371
|
+
delivery_triggered: deliveryTriggered,
|
|
14372
|
+
final: event === "SessionEnd",
|
|
14373
|
+
summary: summary,
|
|
14374
|
+
};
|
|
14375
|
+
}
|
|
14376
|
+
|
|
14377
|
+
var invokedDirectly = process.argv[1] && import.meta.url === "file://" + process.argv[1];
|
|
14378
|
+
if (invokedDirectly) {
|
|
14379
|
+
readStdin()
|
|
14380
|
+
.then(function (text) { return main({ stdinText: text }); })
|
|
14381
|
+
.catch(function () {})
|
|
14382
|
+
.finally(function () { process.exit(0); });
|
|
14383
|
+
}
|
|
14384
|
+
`;
|
|
14385
|
+
}
|
|
14386
|
+
|
|
14387
|
+
// src/lib/runtime-hooks.ts
|
|
13629
14388
|
var HOOK_MARKER = "orgx-session-hook.mjs";
|
|
13630
14389
|
var EMIT_HOOK_MARKER = "orgx-emit-execution-graph.mjs";
|
|
13631
|
-
var
|
|
13632
|
-
var
|
|
14390
|
+
var SUMMARY_HOOK_MARKER = SESSION_SUMMARY_HOOK_MARKER;
|
|
14391
|
+
var HOOK_EVENTS = [
|
|
14392
|
+
"SessionStart",
|
|
14393
|
+
"UserPromptSubmit",
|
|
14394
|
+
"PreToolUse",
|
|
14395
|
+
"PostToolUse",
|
|
14396
|
+
"PermissionRequest",
|
|
14397
|
+
"Stop",
|
|
14398
|
+
"SessionEnd"
|
|
14399
|
+
];
|
|
14400
|
+
var CLAUDE_HOOK_EVENTS = [
|
|
14401
|
+
"SessionStart",
|
|
14402
|
+
"UserPromptSubmit",
|
|
14403
|
+
"PreToolUse",
|
|
14404
|
+
"PostToolUse",
|
|
14405
|
+
"PostToolUseFailure",
|
|
14406
|
+
"PermissionRequest",
|
|
14407
|
+
"SubagentStop",
|
|
14408
|
+
"Stop",
|
|
14409
|
+
"SessionEnd"
|
|
14410
|
+
];
|
|
14411
|
+
function currentPackagedCliPath() {
|
|
14412
|
+
if (!process.argv[1]) return "";
|
|
14413
|
+
const candidate = resolve2(process.argv[1]);
|
|
14414
|
+
return /\.(?:cts|mts|ts|tsx)$/i.test(candidate) ? "" : candidate;
|
|
14415
|
+
}
|
|
13633
14416
|
function defaultPaths(options = {}) {
|
|
13634
14417
|
const hookDir = join8(ORGX_WIZARD_CONFIG_HOME, "hooks");
|
|
13635
14418
|
return {
|
|
@@ -13638,6 +14421,11 @@ function defaultPaths(options = {}) {
|
|
|
13638
14421
|
codexHooksPath: options.codexHooksPath ?? join8(CODEX_DIR, "hooks.json"),
|
|
13639
14422
|
hookScriptPath: options.hookScriptPath ?? join8(hookDir, HOOK_MARKER),
|
|
13640
14423
|
emitHookScriptPath: options.emitHookScriptPath ?? join8(hookDir, EMIT_HOOK_MARKER),
|
|
14424
|
+
summaryHookScriptPath: options.summaryHookScriptPath ?? join8(hookDir, SUMMARY_HOOK_MARKER),
|
|
14425
|
+
sessionStateDir: options.sessionStateDir ?? join8(ORGX_WIZARD_CONFIG_HOME, "sessions"),
|
|
14426
|
+
sessionSummaryQueueDir: options.sessionSummaryQueueDir ?? join8(ORGX_WIZARD_CONFIG_HOME, "session-summary-captures"),
|
|
14427
|
+
deliveryNodePath: options.deliveryNodePath ?? process.execPath,
|
|
14428
|
+
deliveryCliPath: options.deliveryCliPath ?? currentPackagedCliPath(),
|
|
13641
14429
|
outboxPath: options.outboxPath ?? join8(hookDir, "events.jsonl")
|
|
13642
14430
|
};
|
|
13643
14431
|
}
|
|
@@ -13646,6 +14434,13 @@ function countJsonlLines(path) {
|
|
|
13646
14434
|
if (!raw) return 0;
|
|
13647
14435
|
return raw.split(/\r?\n/).filter((line) => line.trim().length > 0).length;
|
|
13648
14436
|
}
|
|
14437
|
+
function countSessionSummaryCaptures(path) {
|
|
14438
|
+
try {
|
|
14439
|
+
return readdirSync6(path).filter((name) => name.endsWith(".capture.json")).length;
|
|
14440
|
+
} catch {
|
|
14441
|
+
return 0;
|
|
14442
|
+
}
|
|
14443
|
+
}
|
|
13649
14444
|
function backupPath(path, now) {
|
|
13650
14445
|
const timestamp = now.toISOString().replace(/[:.]/g, "-");
|
|
13651
14446
|
return `${path}.bak.${timestamp}`;
|
|
@@ -13667,10 +14462,29 @@ function codexHasNotify(raw) {
|
|
|
13667
14462
|
}
|
|
13668
14463
|
function buildRuntimeHookScriptContent() {
|
|
13669
14464
|
return `#!/usr/bin/env node
|
|
13670
|
-
import { appendFileSync, mkdirSync } from "node:fs";
|
|
14465
|
+
import { appendFileSync, mkdirSync, renameSync, statSync } from "node:fs";
|
|
13671
14466
|
import { dirname, join } from "node:path";
|
|
13672
14467
|
import { homedir } from "node:os";
|
|
13673
14468
|
|
|
14469
|
+
const MAX_OUTBOX_BYTES = ${HOOK_OUTBOX_MAX_BYTES};
|
|
14470
|
+
|
|
14471
|
+
function outboxLimit() {
|
|
14472
|
+
const raw = parseInt(process.env.ORGX_WIZARD_HOOK_OUTBOX_MAX_BYTES || "", 10);
|
|
14473
|
+
return Number.isFinite(raw) && raw > 0 ? raw : MAX_OUTBOX_BYTES;
|
|
14474
|
+
}
|
|
14475
|
+
|
|
14476
|
+
function spoolDisabled() {
|
|
14477
|
+
return String(process.env.ORGX_WIZARD_HOOK_SPOOL || "").trim().toLowerCase() === "off";
|
|
14478
|
+
}
|
|
14479
|
+
|
|
14480
|
+
function rotateIfOversized(path) {
|
|
14481
|
+
try {
|
|
14482
|
+
if (statSync(path).size > outboxLimit()) renameSync(path, path + ".1");
|
|
14483
|
+
} catch (error) {
|
|
14484
|
+
// No spool yet, or rotation is not possible. Either way, keep going.
|
|
14485
|
+
}
|
|
14486
|
+
}
|
|
14487
|
+
|
|
13674
14488
|
function parseArgs(argv) {
|
|
13675
14489
|
const args = {};
|
|
13676
14490
|
for (const arg of argv) {
|
|
@@ -13740,8 +14554,11 @@ const record = {
|
|
|
13740
14554
|
};
|
|
13741
14555
|
|
|
13742
14556
|
try {
|
|
13743
|
-
|
|
13744
|
-
|
|
14557
|
+
if (!spoolDisabled()) {
|
|
14558
|
+
mkdirSync(dirname(outbox), { recursive: true, mode: 0o700 });
|
|
14559
|
+
rotateIfOversized(outbox);
|
|
14560
|
+
appendFileSync(outbox, JSON.stringify(record) + "\\n", { encoding: "utf8", mode: 0o600 });
|
|
14561
|
+
}
|
|
13745
14562
|
} catch {
|
|
13746
14563
|
// Hooks must never break the user's agent runtime.
|
|
13747
14564
|
}
|
|
@@ -13840,7 +14657,7 @@ function authHeaders(env) {
|
|
|
13840
14657
|
try {
|
|
13841
14658
|
const args = parseArgs(process.argv.slice(2));
|
|
13842
14659
|
const env = process.env;
|
|
13843
|
-
if (!
|
|
14660
|
+
if (!truthy(env.ORGX_EMIT_EXECUTION_GRAPH)) return;
|
|
13844
14661
|
const initiative = pick(env.ORGX_INITIATIVE_ID, args.initiative);
|
|
13845
14662
|
if (!initiative) return;
|
|
13846
14663
|
const auth = authHeaders(env);
|
|
@@ -13872,7 +14689,7 @@ function authHeaders(env) {
|
|
|
13872
14689
|
const ctrl = new AbortController();
|
|
13873
14690
|
const timer = setTimeout(() => ctrl.abort(), parseInt(env.ORGX_EMIT_TIMEOUT_MS || "", 10) || 4000);
|
|
13874
14691
|
try {
|
|
13875
|
-
await fetch(base + "/api/
|
|
14692
|
+
await fetch(base + "/api/v1/live/execution-graph", {
|
|
13876
14693
|
method: "POST",
|
|
13877
14694
|
headers: Object.assign({ "Content-Type": "application/json" }, auth),
|
|
13878
14695
|
body: JSON.stringify(event),
|
|
@@ -13888,10 +14705,69 @@ function buildEmitHookCommand(params) {
|
|
|
13888
14705
|
return [
|
|
13889
14706
|
"node",
|
|
13890
14707
|
JSON.stringify(params.emitHookScriptPath),
|
|
13891
|
-
"--enabled=true",
|
|
13892
14708
|
`--source_client=${params.sourceClient}`
|
|
13893
14709
|
].join(" ");
|
|
13894
14710
|
}
|
|
14711
|
+
function buildSummaryHookCommand(params) {
|
|
14712
|
+
const terminal = params.event === "Stop" || params.event === "SessionEnd";
|
|
14713
|
+
return [
|
|
14714
|
+
"node",
|
|
14715
|
+
JSON.stringify(params.summaryHookScriptPath),
|
|
14716
|
+
`--event=${params.event}`,
|
|
14717
|
+
`--source_client=${params.sourceClient}`,
|
|
14718
|
+
`--state_dir=${JSON.stringify(params.sessionStateDir)}`,
|
|
14719
|
+
`--queue_dir=${JSON.stringify(params.sessionSummaryQueueDir)}`,
|
|
14720
|
+
...terminal && params.deliveryNodePath && params.deliveryCliPath ? [
|
|
14721
|
+
`--delivery_node=${JSON.stringify(params.deliveryNodePath)}`,
|
|
14722
|
+
`--delivery_cli=${JSON.stringify(params.deliveryCliPath)}`
|
|
14723
|
+
] : []
|
|
14724
|
+
].join(" ");
|
|
14725
|
+
}
|
|
14726
|
+
function ensureClaudeSummaryHook(rules, event, paths) {
|
|
14727
|
+
let changed = false;
|
|
14728
|
+
let universal = rules.find((entry) => isRecord(entry) && entry.matcher === "");
|
|
14729
|
+
if (!universal) {
|
|
14730
|
+
universal = { matcher: "", hooks: [] };
|
|
14731
|
+
rules.push(universal);
|
|
14732
|
+
changed = true;
|
|
14733
|
+
}
|
|
14734
|
+
const desiredCommand = buildSummaryHookCommand({
|
|
14735
|
+
deliveryCliPath: paths.deliveryCliPath,
|
|
14736
|
+
deliveryNodePath: paths.deliveryNodePath,
|
|
14737
|
+
event,
|
|
14738
|
+
sessionStateDir: paths.sessionStateDir,
|
|
14739
|
+
sessionSummaryQueueDir: paths.sessionSummaryQueueDir,
|
|
14740
|
+
sourceClient: "claude-code",
|
|
14741
|
+
summaryHookScriptPath: paths.summaryHookScriptPath
|
|
14742
|
+
});
|
|
14743
|
+
let desiredKept = false;
|
|
14744
|
+
for (const rule of rules) {
|
|
14745
|
+
if (!isRecord(rule)) continue;
|
|
14746
|
+
const hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
|
|
14747
|
+
const next = [];
|
|
14748
|
+
for (const hook of hooks) {
|
|
14749
|
+
const isSummary = isRecord(hook) && typeof hook.command === "string" && hook.command.includes(SUMMARY_HOOK_MARKER);
|
|
14750
|
+
if (!isSummary) {
|
|
14751
|
+
next.push(hook);
|
|
14752
|
+
continue;
|
|
14753
|
+
}
|
|
14754
|
+
if (rule === universal && hook.type === "command" && hook.command === desiredCommand && !desiredKept) {
|
|
14755
|
+
next.push(hook);
|
|
14756
|
+
desiredKept = true;
|
|
14757
|
+
} else {
|
|
14758
|
+
changed = true;
|
|
14759
|
+
}
|
|
14760
|
+
}
|
|
14761
|
+
if (next.length !== hooks.length) rule.hooks = next;
|
|
14762
|
+
}
|
|
14763
|
+
if (!desiredKept) {
|
|
14764
|
+
const hooks = Array.isArray(universal.hooks) ? universal.hooks : [];
|
|
14765
|
+
hooks.push({ type: "command", command: desiredCommand });
|
|
14766
|
+
universal.hooks = hooks;
|
|
14767
|
+
changed = true;
|
|
14768
|
+
}
|
|
14769
|
+
return changed;
|
|
14770
|
+
}
|
|
13895
14771
|
function mergeCodexHooks(raw, paths) {
|
|
13896
14772
|
const value = parseJsonObject(raw);
|
|
13897
14773
|
const hooks = isRecord(value.hooks) ? value.hooks : {};
|
|
@@ -13907,10 +14783,31 @@ function mergeCodexHooks(raw, paths) {
|
|
|
13907
14783
|
const already = existing.some(
|
|
13908
14784
|
(entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
|
|
13909
14785
|
);
|
|
13910
|
-
|
|
13911
|
-
|
|
14786
|
+
const next = already ? [...existing] : [...existing, { command }];
|
|
14787
|
+
if (!already) changed = true;
|
|
14788
|
+
const desiredSummaryCommand = buildSummaryHookCommand({
|
|
14789
|
+
deliveryCliPath: paths.deliveryCliPath,
|
|
14790
|
+
deliveryNodePath: paths.deliveryNodePath,
|
|
14791
|
+
event,
|
|
14792
|
+
sessionStateDir: paths.sessionStateDir,
|
|
14793
|
+
sessionSummaryQueueDir: paths.sessionSummaryQueueDir,
|
|
14794
|
+
sourceClient: "codex",
|
|
14795
|
+
summaryHookScriptPath: paths.summaryHookScriptPath
|
|
14796
|
+
});
|
|
14797
|
+
const summaryEntries = next.filter(
|
|
14798
|
+
(entry) => isRecord(entry) && typeof entry.command === "string" && entry.command.includes(SUMMARY_HOOK_MARKER)
|
|
14799
|
+
);
|
|
14800
|
+
const summaryCurrent = summaryEntries.length === 1 && isRecord(summaryEntries[0]) && summaryEntries[0].command === desiredSummaryCommand;
|
|
14801
|
+
if (!summaryCurrent) {
|
|
14802
|
+
const withoutStaleSummary = next.filter(
|
|
14803
|
+
(entry) => !(isRecord(entry) && typeof entry.command === "string" && entry.command.includes(SUMMARY_HOOK_MARKER))
|
|
14804
|
+
);
|
|
14805
|
+
withoutStaleSummary.push({ command: desiredSummaryCommand });
|
|
14806
|
+
hooks[event] = withoutStaleSummary;
|
|
13912
14807
|
changed = true;
|
|
14808
|
+
continue;
|
|
13913
14809
|
}
|
|
14810
|
+
hooks[event] = next;
|
|
13914
14811
|
}
|
|
13915
14812
|
value.hooks = hooks;
|
|
13916
14813
|
return { changed: changed || !raw, value };
|
|
@@ -13921,7 +14818,7 @@ function mergeClaudeHooks(raw, paths) {
|
|
|
13921
14818
|
let changed = false;
|
|
13922
14819
|
for (const event of CLAUDE_HOOK_EVENTS) {
|
|
13923
14820
|
const list = Array.isArray(hooksRoot[event]) ? hooksRoot[event] : [];
|
|
13924
|
-
const matcher = event === "PreToolUse" || event === "PostToolUse" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
|
|
14821
|
+
const matcher = event === "PreToolUse" || event === "PostToolUse" || event === "PostToolUseFailure" ? "Bash|Write|Edit|MultiEdit|mcp__.*" : "";
|
|
13925
14822
|
const command = buildHookCommand({
|
|
13926
14823
|
event,
|
|
13927
14824
|
hookScriptPath: paths.hookScriptPath,
|
|
@@ -13934,7 +14831,7 @@ function mergeClaudeHooks(raw, paths) {
|
|
|
13934
14831
|
list.push(rule);
|
|
13935
14832
|
changed = true;
|
|
13936
14833
|
}
|
|
13937
|
-
|
|
14834
|
+
let hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
|
|
13938
14835
|
const already = hooks.some(
|
|
13939
14836
|
(entry) => isRecord(entry) && entry.type === "command" && typeof entry.command === "string" && entry.command.includes(HOOK_MARKER)
|
|
13940
14837
|
);
|
|
@@ -13943,6 +14840,10 @@ function mergeClaudeHooks(raw, paths) {
|
|
|
13943
14840
|
rule.hooks = hooks;
|
|
13944
14841
|
changed = true;
|
|
13945
14842
|
}
|
|
14843
|
+
if (ensureClaudeSummaryHook(list, event, paths)) {
|
|
14844
|
+
changed = true;
|
|
14845
|
+
}
|
|
14846
|
+
hooks = Array.isArray(rule.hooks) ? rule.hooks : [];
|
|
13946
14847
|
if (event === "Stop") {
|
|
13947
14848
|
const emitCommand = buildEmitHookCommand({
|
|
13948
14849
|
emitHookScriptPath: paths.emitHookScriptPath,
|
|
@@ -13992,13 +14893,18 @@ function inspectRuntimeHooks(options = {}) {
|
|
|
13992
14893
|
const codexConfigRaw = readTextIfExists(paths.codexConfigPath);
|
|
13993
14894
|
const codexHooksRaw = readTextIfExists(paths.codexHooksPath);
|
|
13994
14895
|
const claudeSettingsRaw = readTextIfExists(paths.claudeSettingsPath);
|
|
14896
|
+
const hasAutomaticDelivery = (raw) => Boolean(
|
|
14897
|
+
raw && raw.includes("--delivery_node=") && raw.includes("--delivery_cli=") && raw.includes(paths.deliveryNodePath) && raw.includes(paths.deliveryCliPath) && existsSync9(paths.deliveryNodePath) && existsSync9(paths.deliveryCliPath)
|
|
14898
|
+
);
|
|
13995
14899
|
return {
|
|
13996
14900
|
paths,
|
|
13997
14901
|
installed: {
|
|
13998
14902
|
claudeCode: hasOrgxHook(claudeSettingsRaw),
|
|
13999
14903
|
codex: hasOrgxHook(codexHooksRaw),
|
|
14000
14904
|
hookScript: existsSync9(paths.hookScriptPath),
|
|
14001
|
-
emitHookScript: existsSync9(paths.emitHookScriptPath)
|
|
14905
|
+
emitHookScript: existsSync9(paths.emitHookScriptPath),
|
|
14906
|
+
summaryHookScript: existsSync9(paths.summaryHookScriptPath),
|
|
14907
|
+
automaticDelivery: hasAutomaticDelivery(codexHooksRaw) || hasAutomaticDelivery(claudeSettingsRaw)
|
|
14002
14908
|
},
|
|
14003
14909
|
codex: {
|
|
14004
14910
|
configExists: Boolean(codexConfigRaw),
|
|
@@ -14006,7 +14912,8 @@ function inspectRuntimeHooks(options = {}) {
|
|
|
14006
14912
|
hasNotify: codexHasNotify(codexConfigRaw),
|
|
14007
14913
|
notifyPreserved: !codexHasNotify(codexConfigRaw) || !hasOrgxHook(codexConfigRaw)
|
|
14008
14914
|
},
|
|
14009
|
-
outboxEvents: countJsonlLines(paths.outboxPath)
|
|
14915
|
+
outboxEvents: countJsonlLines(paths.outboxPath),
|
|
14916
|
+
sessionSummaryCaptures: countSessionSummaryCaptures(paths.sessionSummaryQueueDir)
|
|
14010
14917
|
};
|
|
14011
14918
|
}
|
|
14012
14919
|
function installRuntimeHooks(targets, options = {}) {
|
|
@@ -14018,7 +14925,8 @@ function installRuntimeHooks(targets, options = {}) {
|
|
|
14018
14925
|
codex: false,
|
|
14019
14926
|
codexConfig: false,
|
|
14020
14927
|
hookScript: false,
|
|
14021
|
-
emitHookScript: false
|
|
14928
|
+
emitHookScript: false,
|
|
14929
|
+
summaryHookScript: false
|
|
14022
14930
|
};
|
|
14023
14931
|
mkdirSync4(dirname5(paths.hookScriptPath), { recursive: true, mode: 448 });
|
|
14024
14932
|
const scriptContent = buildRuntimeHookScriptContent();
|
|
@@ -14036,6 +14944,16 @@ function installRuntimeHooks(targets, options = {}) {
|
|
|
14036
14944
|
writeTextFile(paths.emitHookScriptPath, emitScriptContent, { mode: 448 });
|
|
14037
14945
|
changed.emitHookScript = true;
|
|
14038
14946
|
}
|
|
14947
|
+
mkdirSync4(dirname5(paths.summaryHookScriptPath), { recursive: true, mode: 448 });
|
|
14948
|
+
const summaryScriptContent = buildSessionSummaryHookScriptContent();
|
|
14949
|
+
if (readTextIfExists(paths.summaryHookScriptPath) !== summaryScriptContent) {
|
|
14950
|
+
const backup = backupExisting(paths.summaryHookScriptPath, now);
|
|
14951
|
+
if (backup) backups.push(backup);
|
|
14952
|
+
writeTextFile(paths.summaryHookScriptPath, summaryScriptContent, { mode: 448 });
|
|
14953
|
+
changed.summaryHookScript = true;
|
|
14954
|
+
}
|
|
14955
|
+
mkdirSync4(paths.sessionStateDir, { recursive: true, mode: 448 });
|
|
14956
|
+
mkdirSync4(paths.sessionSummaryQueueDir, { recursive: true, mode: 448 });
|
|
14039
14957
|
if (targets.includes("codex")) {
|
|
14040
14958
|
const rawConfig = readTextIfExists(paths.codexConfigPath);
|
|
14041
14959
|
const nextConfig = ensureCodexHooksFeature(rawConfig);
|
|
@@ -14085,6 +15003,679 @@ function parseRuntimeHookTargets(value) {
|
|
|
14085
15003
|
return [...new Set(normalized)];
|
|
14086
15004
|
}
|
|
14087
15005
|
|
|
15006
|
+
// src/lib/session-summary-queue.ts
|
|
15007
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync8, unlinkSync as unlinkSync2 } from "fs";
|
|
15008
|
+
import { join as join9 } from "path";
|
|
15009
|
+
|
|
15010
|
+
// src/lib/session-summary-backfill.ts
|
|
15011
|
+
import { createReadStream, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
15012
|
+
import { basename as basename5 } from "path";
|
|
15013
|
+
import { createInterface } from "readline";
|
|
15014
|
+
|
|
15015
|
+
// src/lib/session-work-context.ts
|
|
15016
|
+
import { z } from "zod";
|
|
15017
|
+
var SESSION_WORK_CONTEXT_VERSION = "orgx-session-work-context/v1";
|
|
15018
|
+
var SESSION_WORK_CONTEXT_MAX_JSON_BYTES = 4 * 1024;
|
|
15019
|
+
var boundedText = (max) => z.string().trim().min(1).max(max);
|
|
15020
|
+
var currency = boundedText(12).regex(/^[A-Z][A-Z0-9_-]{1,11}$/);
|
|
15021
|
+
var externalReferenceSchema = z.object({
|
|
15022
|
+
system: boundedText(120),
|
|
15023
|
+
type: boundedText(120),
|
|
15024
|
+
id: boundedText(500),
|
|
15025
|
+
uri: boundedText(1e3).refine((value) => !/[\u0000-\u0020]/.test(value)).optional(),
|
|
15026
|
+
version: boundedText(120).optional()
|
|
15027
|
+
});
|
|
15028
|
+
var stringList = (max) => z.array(boundedText(max)).max(20).default([]).transform((values) => [
|
|
15029
|
+
...new Set(values)
|
|
15030
|
+
]);
|
|
15031
|
+
var referenceList = z.array(externalReferenceSchema).max(20).default([]).transform((refs) => {
|
|
15032
|
+
const unique = /* @__PURE__ */ new Map();
|
|
15033
|
+
for (const ref of refs) {
|
|
15034
|
+
unique.set(`${ref.system}\0${ref.type}\0${ref.id}`, ref);
|
|
15035
|
+
}
|
|
15036
|
+
return [...unique.values()].sort((left, right) => {
|
|
15037
|
+
const a = `${left.system}\0${left.type}\0${left.id}`;
|
|
15038
|
+
const b = `${right.system}\0${right.type}\0${right.id}`;
|
|
15039
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
15040
|
+
});
|
|
15041
|
+
});
|
|
15042
|
+
var authoritySchema = z.object({
|
|
15043
|
+
mode: z.enum([
|
|
15044
|
+
"explicit",
|
|
15045
|
+
"delegated",
|
|
15046
|
+
"inherited",
|
|
15047
|
+
"policy",
|
|
15048
|
+
"none",
|
|
15049
|
+
"unknown"
|
|
15050
|
+
]),
|
|
15051
|
+
status: z.enum(["granted", "restricted", "denied", "expired", "unknown"]),
|
|
15052
|
+
scope: z.object({
|
|
15053
|
+
actions: stringList(120),
|
|
15054
|
+
resources: referenceList,
|
|
15055
|
+
systems: stringList(120),
|
|
15056
|
+
spend_limit: z.object({
|
|
15057
|
+
currency,
|
|
15058
|
+
amount: z.number().finite().min(0).max(1e5)
|
|
15059
|
+
}).optional()
|
|
15060
|
+
}),
|
|
15061
|
+
authorization_ref: externalReferenceSchema.optional(),
|
|
15062
|
+
constraints: stringList(1e3),
|
|
15063
|
+
valid_from: z.string().datetime({ offset: true }).optional(),
|
|
15064
|
+
valid_until: z.string().datetime({ offset: true }).optional()
|
|
15065
|
+
}).refine(
|
|
15066
|
+
(value) => !value.valid_from || !value.valid_until || Date.parse(value.valid_until) >= Date.parse(value.valid_from),
|
|
15067
|
+
{ message: "valid_until must not precede valid_from" }
|
|
15068
|
+
);
|
|
15069
|
+
var sessionWorkContextInputSchema = z.object({
|
|
15070
|
+
schema_version: z.literal(SESSION_WORK_CONTEXT_VERSION),
|
|
15071
|
+
intent: z.object({
|
|
15072
|
+
summary: boundedText(2e3),
|
|
15073
|
+
objective: boundedText(4e3).optional(),
|
|
15074
|
+
acceptance_criteria: stringList(1e3),
|
|
15075
|
+
constraints: stringList(1e3),
|
|
15076
|
+
request_ref: externalReferenceSchema.optional()
|
|
15077
|
+
}),
|
|
15078
|
+
authority: authoritySchema,
|
|
15079
|
+
cost: z.object({
|
|
15080
|
+
currency,
|
|
15081
|
+
total: z.number().finite().min(0).max(1e5),
|
|
15082
|
+
estimated: z.boolean().default(true),
|
|
15083
|
+
source: boundedText(120).optional()
|
|
15084
|
+
}),
|
|
15085
|
+
artifact_refs: referenceList,
|
|
15086
|
+
evidence_refs: referenceList
|
|
15087
|
+
});
|
|
15088
|
+
function normalizeSessionWorkContext(value) {
|
|
15089
|
+
const parsed = sessionWorkContextInputSchema.safeParse(value);
|
|
15090
|
+
return parsed.success ? { ...parsed.data, provenance: "producer_asserted" } : null;
|
|
15091
|
+
}
|
|
15092
|
+
|
|
15093
|
+
// src/lib/session-summary-backfill.ts
|
|
15094
|
+
var SESSION_EXECUTION_OBSERVATION_VERSION = "orgx-session-execution-observation/v1";
|
|
15095
|
+
var SESSION_EXECUTION_MAX_ACTIONS = 32;
|
|
15096
|
+
var SESSION_EXECUTION_UNAVAILABLE_FIELDS = /* @__PURE__ */ new Set([
|
|
15097
|
+
"intent",
|
|
15098
|
+
"authority_decision",
|
|
15099
|
+
"artifact_refs",
|
|
15100
|
+
"cost",
|
|
15101
|
+
"verification"
|
|
15102
|
+
]);
|
|
15103
|
+
var SESSION_EXECUTION_PERMISSION_MODES = /* @__PURE__ */ new Set([
|
|
15104
|
+
"default",
|
|
15105
|
+
"acceptEdits",
|
|
15106
|
+
"plan",
|
|
15107
|
+
"dontAsk",
|
|
15108
|
+
"bypassPermissions"
|
|
15109
|
+
]);
|
|
15110
|
+
function isRecord3(value) {
|
|
15111
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
15112
|
+
}
|
|
15113
|
+
function boundedString(value, maxLength) {
|
|
15114
|
+
if (typeof value !== "string") return void 0;
|
|
15115
|
+
const trimmed = value.trim();
|
|
15116
|
+
return trimmed ? trimmed.slice(0, maxLength) : void 0;
|
|
15117
|
+
}
|
|
15118
|
+
function boundedCount(value) {
|
|
15119
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? Math.min(value, 1e7) : 0;
|
|
15120
|
+
}
|
|
15121
|
+
function validTimestamp(value) {
|
|
15122
|
+
const text2 = boundedString(value, 100);
|
|
15123
|
+
return text2 && !Number.isNaN(Date.parse(text2)) ? text2 : void 0;
|
|
15124
|
+
}
|
|
15125
|
+
function normalizeObservedAction(value) {
|
|
15126
|
+
if (!isRecord3(value)) return null;
|
|
15127
|
+
const id = boundedString(value.id, 80);
|
|
15128
|
+
const toolName = boundedString(value.tool_name, 48);
|
|
15129
|
+
const status = boundedString(value.status, 40);
|
|
15130
|
+
if (!id || !toolName || !status || !["running", "succeeded", "failed", "completed_unknown"].includes(status)) {
|
|
15131
|
+
return null;
|
|
15132
|
+
}
|
|
15133
|
+
const startedAt = validTimestamp(value.started_at);
|
|
15134
|
+
const completedAt = validTimestamp(value.completed_at);
|
|
15135
|
+
const durationMs = typeof value.duration_ms === "number" && Number.isInteger(value.duration_ms) && value.duration_ms >= 0 ? Math.min(value.duration_ms, 864e5) : void 0;
|
|
15136
|
+
const turnId = boundedString(value.turn_id, 120);
|
|
15137
|
+
return {
|
|
15138
|
+
id,
|
|
15139
|
+
type: "tool.invoke",
|
|
15140
|
+
tool_name: toolName,
|
|
15141
|
+
status,
|
|
15142
|
+
provenance: "runtime_observed",
|
|
15143
|
+
...startedAt ? { started_at: startedAt } : {},
|
|
15144
|
+
...completedAt ? { completed_at: completedAt } : {},
|
|
15145
|
+
...durationMs !== void 0 ? { duration_ms: durationMs } : {},
|
|
15146
|
+
...turnId ? { turn_id: turnId } : {}
|
|
15147
|
+
};
|
|
15148
|
+
}
|
|
15149
|
+
function normalizeSessionExecutionObservation(value) {
|
|
15150
|
+
if (!isRecord3(value) || value.schema_version !== SESSION_EXECUTION_OBSERVATION_VERSION) {
|
|
15151
|
+
return null;
|
|
15152
|
+
}
|
|
15153
|
+
const boundary = boundedString(value.boundary, 40);
|
|
15154
|
+
if (!boundary || ![
|
|
15155
|
+
"turn_boundary",
|
|
15156
|
+
"session_end",
|
|
15157
|
+
"historical_backfill",
|
|
15158
|
+
"legacy_unspecified"
|
|
15159
|
+
].includes(boundary)) {
|
|
15160
|
+
return null;
|
|
15161
|
+
}
|
|
15162
|
+
const actions = (Array.isArray(value.actions) ? value.actions : []).slice(0, SESSION_EXECUTION_MAX_ACTIONS).map(normalizeObservedAction).filter((action) => action !== null);
|
|
15163
|
+
const unavailableFields = (Array.isArray(value.unavailable_fields) ? value.unavailable_fields : []).filter(
|
|
15164
|
+
(field) => typeof field === "string" && SESSION_EXECUTION_UNAVAILABLE_FIELDS.has(field)
|
|
15165
|
+
);
|
|
15166
|
+
const permissionModes = (Array.isArray(value.permission_modes_observed) ? value.permission_modes_observed : []).filter(
|
|
15167
|
+
(mode) => typeof mode === "string" && SESSION_EXECUTION_PERMISSION_MODES.has(mode)
|
|
15168
|
+
);
|
|
15169
|
+
return {
|
|
15170
|
+
schema_version: SESSION_EXECUTION_OBSERVATION_VERSION,
|
|
15171
|
+
boundary,
|
|
15172
|
+
terminal_observed: value.terminal_observed === true,
|
|
15173
|
+
actions,
|
|
15174
|
+
actions_completed_observed: boundedCount(value.actions_completed_observed),
|
|
15175
|
+
actions_omitted: boundedCount(value.actions_omitted),
|
|
15176
|
+
start_times_omitted: boundedCount(value.start_times_omitted),
|
|
15177
|
+
pending_actions: boundedCount(value.pending_actions),
|
|
15178
|
+
permission_requests_observed: boundedCount(value.permission_requests_observed),
|
|
15179
|
+
permission_modes_observed: [...new Set(permissionModes)].slice(0, 8),
|
|
15180
|
+
action_capture_complete: value.action_capture_complete === true,
|
|
15181
|
+
unavailable_fields: [...new Set(unavailableFields)]
|
|
15182
|
+
};
|
|
15183
|
+
}
|
|
15184
|
+
function buildMinimalCloudSummary(summary, metadata) {
|
|
15185
|
+
const execution = normalizeSessionExecutionObservation(summary.metadata?.execution);
|
|
15186
|
+
const workContext = normalizeSessionWorkContext(summary.metadata?.work_context);
|
|
15187
|
+
const { cwd: _localOnlyCwd, metadata: _localOnlyMetadata, ...minimal } = summary;
|
|
15188
|
+
return {
|
|
15189
|
+
...minimal,
|
|
15190
|
+
metadata: {
|
|
15191
|
+
capture: {
|
|
15192
|
+
...metadata.captureId ? { id: metadata.captureId } : {},
|
|
15193
|
+
kind: metadata.captureKind,
|
|
15194
|
+
...metadata.queuedAt ? { queued_at: metadata.queuedAt } : {}
|
|
15195
|
+
},
|
|
15196
|
+
privacy: {
|
|
15197
|
+
profile: "minimal",
|
|
15198
|
+
raw_content_included: false,
|
|
15199
|
+
cwd_included: false
|
|
15200
|
+
},
|
|
15201
|
+
...execution ? { execution } : {},
|
|
15202
|
+
...workContext ? { work_context: workContext } : {}
|
|
15203
|
+
}
|
|
15204
|
+
};
|
|
15205
|
+
}
|
|
15206
|
+
var MAX_REPORTED_TOOLS = 12;
|
|
15207
|
+
var MAX_TOOL_NAME = 48;
|
|
15208
|
+
function repoOf(cwd) {
|
|
15209
|
+
if (!cwd) return "unknown";
|
|
15210
|
+
const marker = "/Code/";
|
|
15211
|
+
const at = cwd.indexOf(marker);
|
|
15212
|
+
if (at >= 0) {
|
|
15213
|
+
const rest = cwd.slice(at + marker.length);
|
|
15214
|
+
const slash = rest.indexOf("/");
|
|
15215
|
+
const name = slash < 0 ? rest : rest.slice(0, slash);
|
|
15216
|
+
if (name) return name;
|
|
15217
|
+
}
|
|
15218
|
+
return basename5(cwd) || "unknown";
|
|
15219
|
+
}
|
|
15220
|
+
function normalizeToolName(name) {
|
|
15221
|
+
const mcp = /^mcp__[0-9a-fA-F-]{16,}__(.+)$/.exec(name);
|
|
15222
|
+
const base = mcp ? `mcp__${mcp[1]}` : name;
|
|
15223
|
+
return base.length > MAX_TOOL_NAME ? base.slice(0, MAX_TOOL_NAME) : base;
|
|
15224
|
+
}
|
|
15225
|
+
function boundTools(tools) {
|
|
15226
|
+
const merged = {};
|
|
15227
|
+
for (const [name, count] of Object.entries(tools)) {
|
|
15228
|
+
const key = normalizeToolName(name);
|
|
15229
|
+
merged[key] = (merged[key] ?? 0) + count;
|
|
15230
|
+
}
|
|
15231
|
+
const entries = Object.entries(merged).sort((a, b) => b[1] - a[1]);
|
|
15232
|
+
if (entries.length <= MAX_REPORTED_TOOLS) return Object.fromEntries(entries);
|
|
15233
|
+
const out = Object.fromEntries(entries.slice(0, MAX_REPORTED_TOOLS));
|
|
15234
|
+
const tail = entries.slice(MAX_REPORTED_TOOLS);
|
|
15235
|
+
out.__other__ = tail.reduce((sum, [, count]) => sum + count, 0);
|
|
15236
|
+
out.__other_tools__ = tail.length;
|
|
15237
|
+
return out;
|
|
15238
|
+
}
|
|
15239
|
+
function toSummary(state) {
|
|
15240
|
+
const { firstTs, lastTs } = state;
|
|
15241
|
+
return {
|
|
15242
|
+
schema_version: SESSION_SUMMARY_SCHEMA_VERSION,
|
|
15243
|
+
source: "orgx_session_summary",
|
|
15244
|
+
session_id: state.session_id,
|
|
15245
|
+
source_client: state.source_client,
|
|
15246
|
+
repo: state.repo,
|
|
15247
|
+
cwd: state.cwd,
|
|
15248
|
+
day: firstTs === null ? "unknown" : new Date(firstTs).toISOString().slice(0, 10),
|
|
15249
|
+
started_at: firstTs === null ? null : new Date(firstTs).toISOString(),
|
|
15250
|
+
ended_at: lastTs === null ? null : new Date(lastTs).toISOString(),
|
|
15251
|
+
duration_min: firstTs !== null && lastTs !== null ? Math.round((lastTs - firstTs) / 6e4) : 0,
|
|
15252
|
+
events: state.events,
|
|
15253
|
+
prompts: state.prompts,
|
|
15254
|
+
tool_calls: state.tool_calls,
|
|
15255
|
+
tools: boundTools(state.tools),
|
|
15256
|
+
edits: state.edits,
|
|
15257
|
+
bash: state.bash
|
|
15258
|
+
};
|
|
15259
|
+
}
|
|
15260
|
+
function percentile(sorted, p) {
|
|
15261
|
+
if (sorted.length === 0) return 0;
|
|
15262
|
+
return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * p))] ?? 0;
|
|
15263
|
+
}
|
|
15264
|
+
async function distillSpool(spoolPath) {
|
|
15265
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
15266
|
+
let lines = 0;
|
|
15267
|
+
let badLines = 0;
|
|
15268
|
+
const reader = createInterface({
|
|
15269
|
+
input: createReadStream(spoolPath),
|
|
15270
|
+
crlfDelay: Infinity
|
|
15271
|
+
});
|
|
15272
|
+
for await (const line of reader) {
|
|
15273
|
+
lines += 1;
|
|
15274
|
+
if (!line.trim()) continue;
|
|
15275
|
+
let record;
|
|
15276
|
+
try {
|
|
15277
|
+
record = JSON.parse(line);
|
|
15278
|
+
} catch {
|
|
15279
|
+
badLines += 1;
|
|
15280
|
+
continue;
|
|
15281
|
+
}
|
|
15282
|
+
const sessionId = typeof record.session_id === "string" ? record.session_id : "unknown";
|
|
15283
|
+
const cwd = typeof record.cwd === "string" ? record.cwd : null;
|
|
15284
|
+
const timestamp = typeof record.timestamp === "string" ? Date.parse(record.timestamp) : NaN;
|
|
15285
|
+
const ts = Number.isFinite(timestamp) ? timestamp : null;
|
|
15286
|
+
let state = sessions.get(sessionId);
|
|
15287
|
+
if (!state) {
|
|
15288
|
+
state = {
|
|
15289
|
+
session_id: sessionId,
|
|
15290
|
+
source_client: typeof record.source_client === "string" ? record.source_client : "unknown",
|
|
15291
|
+
repo: repoOf(cwd),
|
|
15292
|
+
cwd,
|
|
15293
|
+
firstTs: ts,
|
|
15294
|
+
lastTs: ts,
|
|
15295
|
+
events: 0,
|
|
15296
|
+
prompts: 0,
|
|
15297
|
+
tool_calls: 0,
|
|
15298
|
+
tools: {},
|
|
15299
|
+
edits: 0,
|
|
15300
|
+
bash: 0
|
|
15301
|
+
};
|
|
15302
|
+
sessions.set(sessionId, state);
|
|
15303
|
+
}
|
|
15304
|
+
state.events += 1;
|
|
15305
|
+
if (ts !== null) {
|
|
15306
|
+
if (state.firstTs === null || ts < state.firstTs) state.firstTs = ts;
|
|
15307
|
+
if (state.lastTs === null || ts > state.lastTs) state.lastTs = ts;
|
|
15308
|
+
}
|
|
15309
|
+
if (cwd && state.cwd === null) {
|
|
15310
|
+
state.cwd = cwd;
|
|
15311
|
+
state.repo = repoOf(cwd);
|
|
15312
|
+
}
|
|
15313
|
+
if (typeof record.source_client === "string" && state.source_client === "unknown") {
|
|
15314
|
+
state.source_client = record.source_client;
|
|
15315
|
+
}
|
|
15316
|
+
const event = typeof record.event === "string" ? record.event : "unknown";
|
|
15317
|
+
if (event === "UserPromptSubmit" || event.includes("user_prompt")) state.prompts += 1;
|
|
15318
|
+
const summary = record.summary;
|
|
15319
|
+
const tool = summary && typeof summary.tool_name === "string" ? summary.tool_name : null;
|
|
15320
|
+
if (tool && (event === "PostToolUse" || event.includes("post_tool_use"))) {
|
|
15321
|
+
state.tool_calls += 1;
|
|
15322
|
+
state.tools[tool] = (state.tools[tool] ?? 0) + 1;
|
|
15323
|
+
if (tool === "Write" || tool === "Edit" || tool === "NotebookEdit") state.edits += 1;
|
|
15324
|
+
if (tool === "Bash") state.bash += 1;
|
|
15325
|
+
}
|
|
15326
|
+
}
|
|
15327
|
+
const summaries = [...sessions.values()].map(toSummary).sort((a, b) => (a.started_at ?? "").localeCompare(b.started_at ?? ""));
|
|
15328
|
+
const sizes = summaries.map((summary) => Buffer.byteLength(JSON.stringify(summary), "utf8")).sort((a, b) => a - b);
|
|
15329
|
+
const total = sizes.reduce((sum, value) => sum + value, 0);
|
|
15330
|
+
let spoolBytes = 0;
|
|
15331
|
+
try {
|
|
15332
|
+
spoolBytes = statSync5(spoolPath).size;
|
|
15333
|
+
} catch {
|
|
15334
|
+
spoolBytes = 0;
|
|
15335
|
+
}
|
|
15336
|
+
return {
|
|
15337
|
+
spoolPath,
|
|
15338
|
+
spoolBytes,
|
|
15339
|
+
lines,
|
|
15340
|
+
badLines,
|
|
15341
|
+
summaries,
|
|
15342
|
+
bytes: {
|
|
15343
|
+
total,
|
|
15344
|
+
max: sizes.at(-1) ?? 0,
|
|
15345
|
+
p50: percentile(sizes, 0.5),
|
|
15346
|
+
p90: percentile(sizes, 0.9),
|
|
15347
|
+
mean: sizes.length > 0 ? Math.round(total / sizes.length) : 0
|
|
15348
|
+
}
|
|
15349
|
+
};
|
|
15350
|
+
}
|
|
15351
|
+
async function postSummaries(summaries, post, options = {}) {
|
|
15352
|
+
const maxConsecutiveFailures = options.maxConsecutiveFailures ?? 5;
|
|
15353
|
+
const result = { attempted: 0, posted: 0, failed: 0 };
|
|
15354
|
+
let consecutiveFailures = 0;
|
|
15355
|
+
for (const summary of summaries) {
|
|
15356
|
+
result.attempted += 1;
|
|
15357
|
+
const response = await post(summary);
|
|
15358
|
+
if (response.ok) {
|
|
15359
|
+
result.posted += 1;
|
|
15360
|
+
consecutiveFailures = 0;
|
|
15361
|
+
continue;
|
|
15362
|
+
}
|
|
15363
|
+
result.failed += 1;
|
|
15364
|
+
consecutiveFailures += 1;
|
|
15365
|
+
result.firstError ??= `HTTP ${response.status} for session ${summary.session_id}`;
|
|
15366
|
+
if (consecutiveFailures >= maxConsecutiveFailures) break;
|
|
15367
|
+
}
|
|
15368
|
+
return result;
|
|
15369
|
+
}
|
|
15370
|
+
function truncateSpool(spoolPath, post, options = {}) {
|
|
15371
|
+
if (!options.force && (post.failed > 0 || post.attempted === 0)) {
|
|
15372
|
+
return {
|
|
15373
|
+
truncated: false,
|
|
15374
|
+
reason: post.attempted === 0 ? "nothing was posted, so there is nothing safe to truncate" : `${post.failed} session(s) failed to post`,
|
|
15375
|
+
bytesReleased: 0
|
|
15376
|
+
};
|
|
15377
|
+
}
|
|
15378
|
+
let bytesReleased = 0;
|
|
15379
|
+
try {
|
|
15380
|
+
bytesReleased = statSync5(spoolPath).size;
|
|
15381
|
+
} catch {
|
|
15382
|
+
return { truncated: false, reason: "spool not found", bytesReleased: 0 };
|
|
15383
|
+
}
|
|
15384
|
+
const stamp = (options.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
15385
|
+
const archivePath = `${spoolPath}.backfilled-${stamp}`;
|
|
15386
|
+
renameSync2(spoolPath, archivePath);
|
|
15387
|
+
writeFileSync4(spoolPath, "", { encoding: "utf8", mode: 384 });
|
|
15388
|
+
return { truncated: true, archivePath, bytesReleased };
|
|
15389
|
+
}
|
|
15390
|
+
|
|
15391
|
+
// src/lib/session-summary-queue.ts
|
|
15392
|
+
var SESSION_SUMMARY_CAPTURE_VERSION = "orgx-session-summary-capture/v1";
|
|
15393
|
+
var SESSION_SUMMARY_CAPTURE_FILE_SUFFIX = ".capture.json";
|
|
15394
|
+
var SESSION_SUMMARY_CAPTURE_MAX_BYTES = 16 * 1024;
|
|
15395
|
+
function sessionSummaryCaptureFiles(queueDir) {
|
|
15396
|
+
if (!existsSync10(queueDir)) return [];
|
|
15397
|
+
try {
|
|
15398
|
+
return readdirSync7(queueDir).filter((name) => name.endsWith(SESSION_SUMMARY_CAPTURE_FILE_SUFFIX)).sort().map((name) => join9(queueDir, name));
|
|
15399
|
+
} catch {
|
|
15400
|
+
return [];
|
|
15401
|
+
}
|
|
15402
|
+
}
|
|
15403
|
+
function isRecord4(value) {
|
|
15404
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
15405
|
+
}
|
|
15406
|
+
function isSessionSummary(value) {
|
|
15407
|
+
if (!isRecord4(value)) return false;
|
|
15408
|
+
return typeof value.schema_version === "string" && typeof value.session_id === "string" && typeof value.source_client === "string";
|
|
15409
|
+
}
|
|
15410
|
+
function workGraphFingerprint(sessionId) {
|
|
15411
|
+
let left = 2166136261;
|
|
15412
|
+
let right = 16777619;
|
|
15413
|
+
for (let index = 0; index < sessionId.length; index += 1) {
|
|
15414
|
+
left = Math.imul(left ^ sessionId.charCodeAt(index), 16777619) >>> 0;
|
|
15415
|
+
right = Math.imul(
|
|
15416
|
+
right ^ sessionId.charCodeAt(sessionId.length - 1 - index),
|
|
15417
|
+
2246822507
|
|
15418
|
+
) >>> 0;
|
|
15419
|
+
}
|
|
15420
|
+
const seed = `${left.toString(16).padStart(8, "0")}${right.toString(16).padStart(8, "0")}`;
|
|
15421
|
+
return `wgf_${(seed + seed).slice(0, 24)}`;
|
|
15422
|
+
}
|
|
15423
|
+
function parseSessionSummaryCapture(raw) {
|
|
15424
|
+
if (Buffer.byteLength(raw, "utf8") > SESSION_SUMMARY_CAPTURE_MAX_BYTES) return null;
|
|
15425
|
+
try {
|
|
15426
|
+
const value = JSON.parse(raw);
|
|
15427
|
+
if (!isRecord4(value) || value.schema_version !== SESSION_SUMMARY_CAPTURE_VERSION) return null;
|
|
15428
|
+
if (typeof value.capture_id !== "string" || typeof value.queued_at !== "string" || !isSessionSummary(value.session)) {
|
|
15429
|
+
return null;
|
|
15430
|
+
}
|
|
15431
|
+
if (value.initiative_id !== void 0 && typeof value.initiative_id !== "string") {
|
|
15432
|
+
return null;
|
|
15433
|
+
}
|
|
15434
|
+
return {
|
|
15435
|
+
schema_version: SESSION_SUMMARY_CAPTURE_VERSION,
|
|
15436
|
+
capture_id: value.capture_id,
|
|
15437
|
+
// Captures created by wizard 0.1.56 before this field existed must remain
|
|
15438
|
+
// deliverable. Preserve the uncertainty instead of guessing that an old
|
|
15439
|
+
// snapshot represented a real SessionEnd.
|
|
15440
|
+
capture_kind: value.capture_kind === "turn_boundary" || value.capture_kind === "session_end" ? value.capture_kind : "legacy_unspecified",
|
|
15441
|
+
queued_at: value.queued_at,
|
|
15442
|
+
session: value.session,
|
|
15443
|
+
...typeof value.initiative_id === "string" ? { initiative_id: value.initiative_id } : {}
|
|
15444
|
+
};
|
|
15445
|
+
} catch {
|
|
15446
|
+
return null;
|
|
15447
|
+
}
|
|
15448
|
+
}
|
|
15449
|
+
function readSessionSummaryCapture(path) {
|
|
15450
|
+
try {
|
|
15451
|
+
return parseSessionSummaryCapture(readFileSync8(path, "utf8"));
|
|
15452
|
+
} catch {
|
|
15453
|
+
return null;
|
|
15454
|
+
}
|
|
15455
|
+
}
|
|
15456
|
+
function buildCaptureEndpoint(path, baseUrl) {
|
|
15457
|
+
return buildOrgxApiUrl(path.replace(/^\/api/, ""), baseUrl);
|
|
15458
|
+
}
|
|
15459
|
+
function buildWorkGraphFallback(capture) {
|
|
15460
|
+
const summary = buildMinimalCloudSummary(capture.session, {
|
|
15461
|
+
captureId: capture.capture_id,
|
|
15462
|
+
captureKind: capture.capture_kind,
|
|
15463
|
+
queuedAt: capture.queued_at
|
|
15464
|
+
});
|
|
15465
|
+
return {
|
|
15466
|
+
report: {
|
|
15467
|
+
schema_version: "2.0.0",
|
|
15468
|
+
work_graph_fingerprint: workGraphFingerprint(summary.session_id),
|
|
15469
|
+
session_id: summary.session_id,
|
|
15470
|
+
investigation: { schema_version: "2.0.0" },
|
|
15471
|
+
raw_transcripts_sent: false,
|
|
15472
|
+
events: [
|
|
15473
|
+
{
|
|
15474
|
+
source_client: summary.source_client,
|
|
15475
|
+
session_id: summary.session_id,
|
|
15476
|
+
occurred_at: summary.ended_at,
|
|
15477
|
+
metadata: summary
|
|
15478
|
+
}
|
|
15479
|
+
]
|
|
15480
|
+
}
|
|
15481
|
+
};
|
|
15482
|
+
}
|
|
15483
|
+
function deliveryBody(capture) {
|
|
15484
|
+
return {
|
|
15485
|
+
session: buildMinimalCloudSummary(capture.session, {
|
|
15486
|
+
captureId: capture.capture_id,
|
|
15487
|
+
captureKind: capture.capture_kind,
|
|
15488
|
+
queuedAt: capture.queued_at
|
|
15489
|
+
}),
|
|
15490
|
+
...capture.initiative_id ? { initiative_id: capture.initiative_id } : {}
|
|
15491
|
+
};
|
|
15492
|
+
}
|
|
15493
|
+
async function flushSessionSummaryCaptures(options) {
|
|
15494
|
+
const files = sessionSummaryCaptureFiles(options.queueDir);
|
|
15495
|
+
const limit = Math.max(1, Math.min(options.limit ?? 100, 1e3));
|
|
15496
|
+
const selected = files.slice(0, limit);
|
|
15497
|
+
const headers = {
|
|
15498
|
+
Authorization: `Bearer ${options.auth.apiKey}`,
|
|
15499
|
+
"Content-Type": "application/json"
|
|
15500
|
+
};
|
|
15501
|
+
const primaryUrl = buildCaptureEndpoint(
|
|
15502
|
+
SESSION_SUMMARY_ENDPOINT_PATH,
|
|
15503
|
+
options.auth.baseUrl
|
|
15504
|
+
);
|
|
15505
|
+
const fallbackUrl = buildCaptureEndpoint(
|
|
15506
|
+
SESSION_SUMMARY_FALLBACK_ENDPOINT_PATH,
|
|
15507
|
+
options.auth.baseUrl
|
|
15508
|
+
);
|
|
15509
|
+
const result = {
|
|
15510
|
+
found: files.length,
|
|
15511
|
+
attempted: 0,
|
|
15512
|
+
acknowledged: 0,
|
|
15513
|
+
retained: 0,
|
|
15514
|
+
malformed: 0,
|
|
15515
|
+
fallbackAcknowledged: 0
|
|
15516
|
+
};
|
|
15517
|
+
for (const path of selected) {
|
|
15518
|
+
const capture = readSessionSummaryCapture(path);
|
|
15519
|
+
if (!capture) {
|
|
15520
|
+
result.malformed += 1;
|
|
15521
|
+
result.retained += 1;
|
|
15522
|
+
result.firstError ??= `Malformed capture retained: ${path}`;
|
|
15523
|
+
continue;
|
|
15524
|
+
}
|
|
15525
|
+
result.attempted += 1;
|
|
15526
|
+
let response;
|
|
15527
|
+
let usedFallback = false;
|
|
15528
|
+
try {
|
|
15529
|
+
response = await options.send(primaryUrl, deliveryBody(capture), headers);
|
|
15530
|
+
if (!response.ok && (response.status === 404 || response.status === 405)) {
|
|
15531
|
+
response = await options.send(fallbackUrl, buildWorkGraphFallback(capture), headers);
|
|
15532
|
+
usedFallback = true;
|
|
15533
|
+
}
|
|
15534
|
+
} catch {
|
|
15535
|
+
response = { ok: false, status: 0 };
|
|
15536
|
+
}
|
|
15537
|
+
if (!response.ok) {
|
|
15538
|
+
result.retained += 1;
|
|
15539
|
+
result.firstError ??= `HTTP ${response.status} for ${capture.capture_id}`;
|
|
15540
|
+
if (options.stopOnFailure) break;
|
|
15541
|
+
continue;
|
|
15542
|
+
}
|
|
15543
|
+
try {
|
|
15544
|
+
unlinkSync2(path);
|
|
15545
|
+
result.acknowledged += 1;
|
|
15546
|
+
if (usedFallback) result.fallbackAcknowledged += 1;
|
|
15547
|
+
} catch {
|
|
15548
|
+
result.retained += 1;
|
|
15549
|
+
result.firstError ??= `Acknowledged capture could not be removed: ${path}`;
|
|
15550
|
+
}
|
|
15551
|
+
}
|
|
15552
|
+
return result;
|
|
15553
|
+
}
|
|
15554
|
+
|
|
15555
|
+
// src/lib/session-summary-flush-lease.ts
|
|
15556
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
15557
|
+
import {
|
|
15558
|
+
closeSync as closeSync2,
|
|
15559
|
+
fsyncSync,
|
|
15560
|
+
mkdirSync as mkdirSync5,
|
|
15561
|
+
openSync as openSync2,
|
|
15562
|
+
readFileSync as readFileSync9,
|
|
15563
|
+
renameSync as renameSync3,
|
|
15564
|
+
statSync as statSync6,
|
|
15565
|
+
unlinkSync as unlinkSync3,
|
|
15566
|
+
writeSync
|
|
15567
|
+
} from "fs";
|
|
15568
|
+
import { join as join10 } from "path";
|
|
15569
|
+
var SESSION_SUMMARY_FLUSH_LOCK_NAME = ".flush.lock";
|
|
15570
|
+
var SESSION_SUMMARY_FLUSH_MAX_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
15571
|
+
var MALFORMED_LOCK_GRACE_MS = 5 * 60 * 1e3;
|
|
15572
|
+
function errorCode(error) {
|
|
15573
|
+
return error && typeof error === "object" && "code" in error ? String(error.code) : null;
|
|
15574
|
+
}
|
|
15575
|
+
function readLock(path) {
|
|
15576
|
+
try {
|
|
15577
|
+
const value = JSON.parse(readFileSync9(path, "utf8"));
|
|
15578
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
15579
|
+
const record = value;
|
|
15580
|
+
return typeof record.token === "string" && typeof record.pid === "number" && Number.isInteger(record.pid) && typeof record.claimed_at === "string" ? {
|
|
15581
|
+
token: record.token,
|
|
15582
|
+
pid: record.pid,
|
|
15583
|
+
claimed_at: record.claimed_at
|
|
15584
|
+
} : null;
|
|
15585
|
+
} catch {
|
|
15586
|
+
return null;
|
|
15587
|
+
}
|
|
15588
|
+
}
|
|
15589
|
+
function processIsAlive(pid) {
|
|
15590
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
15591
|
+
try {
|
|
15592
|
+
process.kill(pid, 0);
|
|
15593
|
+
return true;
|
|
15594
|
+
} catch (error) {
|
|
15595
|
+
return errorCode(error) === "EPERM";
|
|
15596
|
+
}
|
|
15597
|
+
}
|
|
15598
|
+
function lockAgeMs(path, now) {
|
|
15599
|
+
try {
|
|
15600
|
+
return Math.max(0, now.getTime() - statSync6(path).mtimeMs);
|
|
15601
|
+
} catch {
|
|
15602
|
+
return null;
|
|
15603
|
+
}
|
|
15604
|
+
}
|
|
15605
|
+
function shouldReclaimLock(path, now, isProcessAlive) {
|
|
15606
|
+
const age = lockAgeMs(path, now);
|
|
15607
|
+
if (age === null) return true;
|
|
15608
|
+
if (age >= SESSION_SUMMARY_FLUSH_MAX_AGE_MS) return true;
|
|
15609
|
+
const record = readLock(path);
|
|
15610
|
+
if (!record) return age >= MALFORMED_LOCK_GRACE_MS;
|
|
15611
|
+
return !isProcessAlive(record.pid);
|
|
15612
|
+
}
|
|
15613
|
+
function writeLock(path, record) {
|
|
15614
|
+
let descriptor = null;
|
|
15615
|
+
try {
|
|
15616
|
+
descriptor = openSync2(path, "wx", 384);
|
|
15617
|
+
writeSync(descriptor, JSON.stringify(record), void 0, "utf8");
|
|
15618
|
+
fsyncSync(descriptor);
|
|
15619
|
+
return "acquired";
|
|
15620
|
+
} catch (error) {
|
|
15621
|
+
return errorCode(error) === "EEXIST" ? "exists" : "unavailable";
|
|
15622
|
+
} finally {
|
|
15623
|
+
if (descriptor !== null) closeSync2(descriptor);
|
|
15624
|
+
}
|
|
15625
|
+
}
|
|
15626
|
+
function releaseOwnedLock(path, token) {
|
|
15627
|
+
try {
|
|
15628
|
+
if (readLock(path)?.token === token) unlinkSync3(path);
|
|
15629
|
+
} catch {
|
|
15630
|
+
}
|
|
15631
|
+
}
|
|
15632
|
+
function claimSessionSummaryFlushLease(queueDir, options = {}) {
|
|
15633
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
15634
|
+
const pid = options.pid ?? process.pid;
|
|
15635
|
+
const token = options.token ?? randomUUID3();
|
|
15636
|
+
const isProcessAlive = options.isProcessAlive ?? processIsAlive;
|
|
15637
|
+
const path = join10(queueDir, SESSION_SUMMARY_FLUSH_LOCK_NAME);
|
|
15638
|
+
const record = { token, pid, claimed_at: now.toISOString() };
|
|
15639
|
+
try {
|
|
15640
|
+
mkdirSync5(queueDir, { recursive: true, mode: 448 });
|
|
15641
|
+
} catch {
|
|
15642
|
+
return { acquired: false, reason: "unavailable" };
|
|
15643
|
+
}
|
|
15644
|
+
const firstAttempt = writeLock(path, record);
|
|
15645
|
+
if (firstAttempt === "acquired") {
|
|
15646
|
+
return {
|
|
15647
|
+
acquired: true,
|
|
15648
|
+
path,
|
|
15649
|
+
release: () => releaseOwnedLock(path, token)
|
|
15650
|
+
};
|
|
15651
|
+
}
|
|
15652
|
+
if (firstAttempt === "unavailable") {
|
|
15653
|
+
return { acquired: false, reason: "unavailable" };
|
|
15654
|
+
}
|
|
15655
|
+
if (!shouldReclaimLock(path, now, isProcessAlive)) {
|
|
15656
|
+
return { acquired: false, reason: "busy" };
|
|
15657
|
+
}
|
|
15658
|
+
const stalePath = `${path}.stale-${token}`;
|
|
15659
|
+
try {
|
|
15660
|
+
renameSync3(path, stalePath);
|
|
15661
|
+
unlinkSync3(stalePath);
|
|
15662
|
+
} catch {
|
|
15663
|
+
return { acquired: false, reason: "busy" };
|
|
15664
|
+
}
|
|
15665
|
+
const retry = writeLock(path, record);
|
|
15666
|
+
if (retry !== "acquired") {
|
|
15667
|
+
return {
|
|
15668
|
+
acquired: false,
|
|
15669
|
+
reason: retry === "exists" ? "busy" : "unavailable"
|
|
15670
|
+
};
|
|
15671
|
+
}
|
|
15672
|
+
return {
|
|
15673
|
+
acquired: true,
|
|
15674
|
+
path,
|
|
15675
|
+
release: () => releaseOwnedLock(path, token)
|
|
15676
|
+
};
|
|
15677
|
+
}
|
|
15678
|
+
|
|
14088
15679
|
// src/spinner.ts
|
|
14089
15680
|
import ora from "ora";
|
|
14090
15681
|
import pc2 from "picocolors";
|
|
@@ -14100,40 +15691,40 @@ function createOrgxSpinner(text2) {
|
|
|
14100
15691
|
}
|
|
14101
15692
|
|
|
14102
15693
|
// src/lib/workload-diagnosis.ts
|
|
14103
|
-
import { closeSync as
|
|
14104
|
-
import { resolve as
|
|
15694
|
+
import { closeSync as closeSync3, openSync as openSync3, readSync as readSync2 } from "fs";
|
|
15695
|
+
import { resolve as resolve3 } from "path";
|
|
14105
15696
|
|
|
14106
15697
|
// src/lib/workload-diagnosis-schema.ts
|
|
14107
|
-
import { z } from "zod";
|
|
15698
|
+
import { z as z2 } from "zod";
|
|
14108
15699
|
var WORKLOAD_DIAGNOSIS_SCHEMA_VERSION = "workload-diagnosis/0.1";
|
|
14109
15700
|
var SECRET_PATTERN = /(?:\b(?:api[_-]?key|authorization|cookie|access[_-]?token|refresh[_-]?token|client[_-]?secret|password|secret|session|token)\s*[:=]\s*\S+|\bbearer\s+[a-z0-9._~+/=-]{8,}|\b(?:oxk_[a-z0-9_-]{8,}|sk-(?:live|test|proj)?[_-]?[a-z0-9_-]{8,}|[sr]k_(?:live|test)_[a-z0-9_-]{8,}|gh[pousr]_[a-z0-9]{8,}|github_pat_[a-z0-9_]{8,}|glpat-[a-z0-9_-]{16,}|xox[baprs]-[a-z0-9-]{8,}|npm_[a-z0-9]{8,}|sntrys_[a-z0-9_-]{8,}|whsec_[a-z0-9_-]{8,}|SG\.[a-z0-9_-]{16,}\.[a-z0-9_-]{16,}|pypi-[a-z0-9_-]{24,}|dop_v1_[a-f0-9]{16,}|hf_[a-z0-9]{20,}|A(?:KI|SI)A[A-Z0-9]{16}|AIza[a-z0-9_-]{20,})\b|\beyJ[a-z0-9_-]{8,}\.eyJ[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}|[a-z][a-z0-9+.-]*:\/\/[^\s/@:]+:[^\s/@]+@|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
|
|
14110
15701
|
var BASIC_AUTH_SECRET_PATTERN = /\bbasic\s+(?:[a-z0-9+/]{4})*(?:[a-z0-9+/]{4}|[a-z0-9+/]{2}==|[a-z0-9+/]{3}=)(?=$|[^a-z0-9+/=])/i;
|
|
14111
15702
|
function containsCredentialPattern(value) {
|
|
14112
15703
|
return SECRET_PATTERN.test(value) || BASIC_AUTH_SECRET_PATTERN.test(value);
|
|
14113
15704
|
}
|
|
14114
|
-
var SafeSummarySchema =
|
|
15705
|
+
var SafeSummarySchema = z2.string().trim().min(1).max(500).refine(
|
|
14115
15706
|
(value) => !/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/.test(value),
|
|
14116
15707
|
{ message: "Control characters are not allowed" }
|
|
14117
15708
|
).refine((value) => !containsCredentialPattern(value), {
|
|
14118
15709
|
message: "Do not include credentials, tokens, passwords, or private keys"
|
|
14119
15710
|
});
|
|
14120
|
-
var SafeResponseText = (max) =>
|
|
15711
|
+
var SafeResponseText = (max) => z2.string().min(1).max(max).refine((value) => !/[\u0000-\u001f\u007f-\u009f]/.test(value), {
|
|
14121
15712
|
message: "Control characters are not allowed"
|
|
14122
15713
|
});
|
|
14123
|
-
var WorkloadTimeHorizonSchema =
|
|
15714
|
+
var WorkloadTimeHorizonSchema = z2.enum([
|
|
14124
15715
|
"single_turn",
|
|
14125
15716
|
"single_session",
|
|
14126
15717
|
"multi_day",
|
|
14127
15718
|
"recurring",
|
|
14128
15719
|
"continuous"
|
|
14129
15720
|
]);
|
|
14130
|
-
var WorkloadCoordinationSchema =
|
|
15721
|
+
var WorkloadCoordinationSchema = z2.enum([
|
|
14131
15722
|
"none",
|
|
14132
15723
|
"handoff",
|
|
14133
15724
|
"parallel",
|
|
14134
15725
|
"hierarchical"
|
|
14135
15726
|
]);
|
|
14136
|
-
var WorkloadSystemCategorySchema =
|
|
15727
|
+
var WorkloadSystemCategorySchema = z2.enum([
|
|
14137
15728
|
"code_repository",
|
|
14138
15729
|
"issue_tracker",
|
|
14139
15730
|
"document_store",
|
|
@@ -14147,20 +15738,20 @@ var WorkloadSystemCategorySchema = z.enum([
|
|
|
14147
15738
|
"identity",
|
|
14148
15739
|
"other"
|
|
14149
15740
|
]);
|
|
14150
|
-
var WorkloadAccessModeSchema =
|
|
14151
|
-
var WorkloadSideEffectSchema =
|
|
15741
|
+
var WorkloadAccessModeSchema = z2.enum(["read", "write", "admin"]);
|
|
15742
|
+
var WorkloadSideEffectSchema = z2.enum([
|
|
14152
15743
|
"none",
|
|
14153
15744
|
"reversible",
|
|
14154
15745
|
"external",
|
|
14155
15746
|
"irreversible"
|
|
14156
15747
|
]);
|
|
14157
|
-
var WorkloadDataClassSchema =
|
|
15748
|
+
var WorkloadDataClassSchema = z2.enum([
|
|
14158
15749
|
"public",
|
|
14159
15750
|
"internal",
|
|
14160
15751
|
"confidential",
|
|
14161
15752
|
"regulated"
|
|
14162
15753
|
]);
|
|
14163
|
-
var WorkloadActionSchema =
|
|
15754
|
+
var WorkloadActionSchema = z2.enum([
|
|
14164
15755
|
"research",
|
|
14165
15756
|
"draft",
|
|
14166
15757
|
"read_internal_data",
|
|
@@ -14173,28 +15764,28 @@ var WorkloadActionSchema = z.enum([
|
|
|
14173
15764
|
"change_permissions",
|
|
14174
15765
|
"delete_data"
|
|
14175
15766
|
]);
|
|
14176
|
-
var WorkloadApprovalPolicySchema =
|
|
15767
|
+
var WorkloadApprovalPolicySchema = z2.enum([
|
|
14177
15768
|
"not_applicable",
|
|
14178
15769
|
"per_action",
|
|
14179
15770
|
"sensitive_actions",
|
|
14180
15771
|
"exceptions_only",
|
|
14181
15772
|
"undefined"
|
|
14182
15773
|
]);
|
|
14183
|
-
var WorkloadBudgetControlSchema =
|
|
15774
|
+
var WorkloadBudgetControlSchema = z2.enum([
|
|
14184
15775
|
"not_applicable",
|
|
14185
15776
|
"fixed_limit",
|
|
14186
15777
|
"dynamic_limit",
|
|
14187
15778
|
"unbounded",
|
|
14188
15779
|
"undefined"
|
|
14189
15780
|
]);
|
|
14190
|
-
var SystemAccessSchema =
|
|
15781
|
+
var SystemAccessSchema = z2.object({
|
|
14191
15782
|
category: WorkloadSystemCategorySchema,
|
|
14192
15783
|
access: WorkloadAccessModeSchema,
|
|
14193
15784
|
side_effect: WorkloadSideEffectSchema,
|
|
14194
15785
|
data_class: WorkloadDataClassSchema
|
|
14195
15786
|
}).strict();
|
|
14196
|
-
var AuthoritySchema =
|
|
14197
|
-
actions:
|
|
15787
|
+
var AuthoritySchema = z2.object({
|
|
15788
|
+
actions: z2.array(WorkloadActionSchema).max(11).superRefine((actions, context) => {
|
|
14198
15789
|
if (new Set(actions).size !== actions.length) {
|
|
14199
15790
|
context.addIssue({
|
|
14200
15791
|
code: "custom",
|
|
@@ -14231,21 +15822,21 @@ var AuthoritySchema = z.object({
|
|
|
14231
15822
|
});
|
|
14232
15823
|
}
|
|
14233
15824
|
});
|
|
14234
|
-
var AccountabilitySchema =
|
|
14235
|
-
evidence:
|
|
14236
|
-
acceptance:
|
|
14237
|
-
consequence:
|
|
14238
|
-
retention:
|
|
15825
|
+
var AccountabilitySchema = z2.object({
|
|
15826
|
+
evidence: z2.enum(["none", "activity_log", "artifact", "verified_outcome"]),
|
|
15827
|
+
acceptance: z2.enum(["none", "agent", "human", "downstream_system"]),
|
|
15828
|
+
consequence: z2.enum(["low", "moderate", "high", "regulated"]),
|
|
15829
|
+
retention: z2.enum(["none", "short_term", "long_term", "regulated"])
|
|
14239
15830
|
}).strict();
|
|
14240
|
-
var WorkloadDiagnosisRequestSchema =
|
|
14241
|
-
schema_version:
|
|
14242
|
-
workload:
|
|
15831
|
+
var WorkloadDiagnosisRequestSchema = z2.object({
|
|
15832
|
+
schema_version: z2.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
|
|
15833
|
+
workload: z2.object({
|
|
14243
15834
|
name: SafeSummarySchema.max(120),
|
|
14244
15835
|
outcome: SafeSummarySchema,
|
|
14245
15836
|
time_horizon: WorkloadTimeHorizonSchema,
|
|
14246
|
-
agent_count:
|
|
15837
|
+
agent_count: z2.number().int().min(1).max(64),
|
|
14247
15838
|
coordination: WorkloadCoordinationSchema,
|
|
14248
|
-
systems:
|
|
15839
|
+
systems: z2.array(SystemAccessSchema).max(12).superRefine((systems, context) => {
|
|
14249
15840
|
const seen = /* @__PURE__ */ new Set();
|
|
14250
15841
|
systems.forEach((system, index) => {
|
|
14251
15842
|
if (seen.has(system.category)) {
|
|
@@ -14325,28 +15916,28 @@ var WorkloadDiagnosisRequestSchema = z.object({
|
|
|
14325
15916
|
}
|
|
14326
15917
|
});
|
|
14327
15918
|
});
|
|
14328
|
-
var BoundaryNameSchema =
|
|
15919
|
+
var BoundaryNameSchema = z2.enum([
|
|
14329
15920
|
"time",
|
|
14330
15921
|
"agents",
|
|
14331
15922
|
"systems",
|
|
14332
15923
|
"authority",
|
|
14333
15924
|
"accountability"
|
|
14334
15925
|
]);
|
|
14335
|
-
var BoundaryFindingSchema =
|
|
14336
|
-
state:
|
|
14337
|
-
score:
|
|
15926
|
+
var BoundaryFindingSchema = z2.object({
|
|
15927
|
+
state: z2.enum(["absent", "present", "critical"]),
|
|
15928
|
+
score: z2.number().int().min(0).max(2),
|
|
14338
15929
|
reason: SafeResponseText(300)
|
|
14339
15930
|
}).strict();
|
|
14340
|
-
var ProposedResourceSchema =
|
|
15931
|
+
var ProposedResourceSchema = z2.object({
|
|
14341
15932
|
resource: SafeResponseText(80),
|
|
14342
15933
|
requested_access: WorkloadAccessModeSchema,
|
|
14343
|
-
scope_intents:
|
|
15934
|
+
scope_intents: z2.array(SafeResponseText(80)).min(1).max(12),
|
|
14344
15935
|
purpose: SafeResponseText(300),
|
|
14345
|
-
credential_input_required:
|
|
15936
|
+
credential_input_required: z2.literal(false)
|
|
14346
15937
|
}).strict();
|
|
14347
|
-
var HumanApprovalSchema =
|
|
14348
|
-
id:
|
|
14349
|
-
owner_role:
|
|
15938
|
+
var HumanApprovalSchema = z2.object({
|
|
15939
|
+
id: z2.string().regex(/^[a-z0-9_-]{1,80}$/),
|
|
15940
|
+
owner_role: z2.enum([
|
|
14350
15941
|
"workload_owner",
|
|
14351
15942
|
"system_owner",
|
|
14352
15943
|
"code_owner",
|
|
@@ -14355,63 +15946,63 @@ var HumanApprovalSchema = z.object({
|
|
|
14355
15946
|
"identity_admin",
|
|
14356
15947
|
"data_owner"
|
|
14357
15948
|
]),
|
|
14358
|
-
timing:
|
|
15949
|
+
timing: z2.enum([
|
|
14359
15950
|
"before_installation",
|
|
14360
15951
|
"before_first_use",
|
|
14361
15952
|
"per_action",
|
|
14362
15953
|
"when_threshold_exceeded"
|
|
14363
15954
|
]),
|
|
14364
15955
|
decision: SafeResponseText(300),
|
|
14365
|
-
scope:
|
|
15956
|
+
scope: z2.array(SafeResponseText(100)).min(1).max(16)
|
|
14366
15957
|
}).strict();
|
|
14367
|
-
var WorkloadDiagnosisResponseSchema =
|
|
14368
|
-
schema_version:
|
|
14369
|
-
diagnosis_id:
|
|
14370
|
-
recommendation:
|
|
14371
|
-
verdict:
|
|
14372
|
-
mode:
|
|
15958
|
+
var WorkloadDiagnosisResponseSchema = z2.object({
|
|
15959
|
+
schema_version: z2.literal(WORKLOAD_DIAGNOSIS_SCHEMA_VERSION),
|
|
15960
|
+
diagnosis_id: z2.string().regex(/^wdg_[a-f0-9]{24}$/),
|
|
15961
|
+
recommendation: z2.object({
|
|
15962
|
+
verdict: z2.enum(["needed", "conditional", "not_needed"]),
|
|
15963
|
+
mode: z2.enum(["none", "receipt_only", "governed_workspace"]),
|
|
14373
15964
|
summary: SafeResponseText(400),
|
|
14374
|
-
rationale:
|
|
14375
|
-
active_boundary_count:
|
|
15965
|
+
rationale: z2.array(SafeResponseText(300)).max(5),
|
|
15966
|
+
active_boundary_count: z2.number().int().min(0).max(5)
|
|
14376
15967
|
}).strict(),
|
|
14377
|
-
boundaries:
|
|
15968
|
+
boundaries: z2.object({
|
|
14378
15969
|
time: BoundaryFindingSchema,
|
|
14379
15970
|
agents: BoundaryFindingSchema,
|
|
14380
15971
|
systems: BoundaryFindingSchema,
|
|
14381
15972
|
authority: BoundaryFindingSchema,
|
|
14382
15973
|
accountability: BoundaryFindingSchema
|
|
14383
15974
|
}).strict(),
|
|
14384
|
-
missing_capabilities:
|
|
14385
|
-
|
|
14386
|
-
id:
|
|
15975
|
+
missing_capabilities: z2.array(
|
|
15976
|
+
z2.object({
|
|
15977
|
+
id: z2.string().regex(/^[a-z0-9_]{1,60}$/),
|
|
14387
15978
|
boundary: BoundaryNameSchema,
|
|
14388
15979
|
reason: SafeResponseText(300)
|
|
14389
15980
|
}).strict()
|
|
14390
15981
|
).max(15),
|
|
14391
|
-
proposed_resources:
|
|
14392
|
-
what_remains_local:
|
|
14393
|
-
|
|
15982
|
+
proposed_resources: z2.array(ProposedResourceSchema).max(13),
|
|
15983
|
+
what_remains_local: z2.array(
|
|
15984
|
+
z2.object({
|
|
14394
15985
|
item: SafeResponseText(100),
|
|
14395
15986
|
reason: SafeResponseText(300)
|
|
14396
15987
|
}).strict()
|
|
14397
15988
|
).min(1).max(6),
|
|
14398
|
-
risks:
|
|
14399
|
-
|
|
14400
|
-
id:
|
|
14401
|
-
severity:
|
|
15989
|
+
risks: z2.array(
|
|
15990
|
+
z2.object({
|
|
15991
|
+
id: z2.string().regex(/^[a-z0-9_]{1,60}$/),
|
|
15992
|
+
severity: z2.enum(["low", "medium", "high", "critical"]),
|
|
14402
15993
|
boundary: BoundaryNameSchema,
|
|
14403
15994
|
description: SafeResponseText(300),
|
|
14404
15995
|
mitigation: SafeResponseText(300)
|
|
14405
15996
|
}).strict()
|
|
14406
15997
|
).max(15),
|
|
14407
|
-
capabilities_after_installation:
|
|
14408
|
-
human_approvals:
|
|
14409
|
-
approval_handoff:
|
|
14410
|
-
kind:
|
|
14411
|
-
url:
|
|
14412
|
-
mutates_state:
|
|
14413
|
-
carries_sensitive_data:
|
|
14414
|
-
approval_ids:
|
|
15998
|
+
capabilities_after_installation: z2.array(SafeResponseText(300)).max(10),
|
|
15999
|
+
human_approvals: z2.array(HumanApprovalSchema).max(32),
|
|
16000
|
+
approval_handoff: z2.object({
|
|
16001
|
+
kind: z2.enum(["none", "browser_review"]),
|
|
16002
|
+
url: z2.string().url().nullable(),
|
|
16003
|
+
mutates_state: z2.literal(false),
|
|
16004
|
+
carries_sensitive_data: z2.boolean(),
|
|
16005
|
+
approval_ids: z2.array(z2.string().regex(/^[a-z0-9_-]{1,80}$/)).max(32)
|
|
14415
16006
|
}).strict()
|
|
14416
16007
|
}).strict().superRefine((response, context) => {
|
|
14417
16008
|
const activeCount = Object.values(response.boundaries).filter(
|
|
@@ -14454,7 +16045,7 @@ var MAX_HANDOFF_TOKEN_CHARACTERS = 7e3;
|
|
|
14454
16045
|
var SENSITIVE_KEY2 = /(?:^|[_-])(?:authorization|cookie|credentials?|password|secrets?|session|tokens?|api[_-]?keys?|access[_-]?tokens?|refresh[_-]?tokens?|client[_-]?secrets?|private[_-]?keys?|database[_-]?(?:url|uri)|db[_-]?(?:url|uri))(?:$|[_-])/i;
|
|
14455
16046
|
function readBoundedUtf8(source) {
|
|
14456
16047
|
const shouldClose = source !== "-";
|
|
14457
|
-
const fd = shouldClose ?
|
|
16048
|
+
const fd = shouldClose ? openSync3(resolve3(source), "r") : 0;
|
|
14458
16049
|
const buffer = Buffer.alloc(MAX_WORKLOAD_DIAGNOSIS_REQUEST_BYTES + 1);
|
|
14459
16050
|
let offset = 0;
|
|
14460
16051
|
try {
|
|
@@ -14470,7 +16061,7 @@ function readBoundedUtf8(source) {
|
|
|
14470
16061
|
offset += bytesRead;
|
|
14471
16062
|
}
|
|
14472
16063
|
} finally {
|
|
14473
|
-
if (shouldClose)
|
|
16064
|
+
if (shouldClose) closeSync3(fd);
|
|
14474
16065
|
}
|
|
14475
16066
|
if (offset > MAX_WORKLOAD_DIAGNOSIS_REQUEST_BYTES) {
|
|
14476
16067
|
throw new Error(
|
|
@@ -14825,8 +16416,171 @@ function printRuntimeHookInspection(report) {
|
|
|
14825
16416
|
console.log(` ${report.codex.hooksEnabled ? ICON.ok : ICON.warn} ${pc3.bold("Codex flag ")} ${report.codex.hooksEnabled ? pc3.green("enabled") : pc3.yellow("not enabled")} ${pc3.dim(report.paths.codexConfigPath)}`);
|
|
14826
16417
|
console.log(` ${report.codex.notifyPreserved ? ICON.ok : ICON.warn} ${pc3.bold("notify ")} ${report.codex.notifyPreserved ? pc3.green("preserved") : pc3.yellow("check config")} ${pc3.dim(report.codex.hasNotify ? "existing notify detected" : "no notify entry")}`);
|
|
14827
16418
|
console.log(` ${report.installed.claudeCode ? ICON.ok : ICON.warn} ${pc3.bold("Claude Code ")} ${report.installed.claudeCode ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.claudeSettingsPath)}`);
|
|
16419
|
+
console.log(` ${report.installed.summaryHookScript ? ICON.ok : ICON.warn} ${pc3.bold("summary hook")} ${report.installed.summaryHookScript ? pc3.green("installed") : pc3.yellow("missing")} ${pc3.dim(report.paths.summaryHookScriptPath)}`);
|
|
16420
|
+
console.log(` ${report.installed.automaticDelivery ? ICON.ok : ICON.warn} ${pc3.bold("auto delivery")} ${report.installed.automaticDelivery ? pc3.green("configured") : pc3.yellow("not configured")} ${pc3.dim("detached, ACK-gated worker")}`);
|
|
16421
|
+
console.log(` ${ICON.skip} ${pc3.bold("capture queue")} ${pc3.dim(`${report.sessionSummaryCaptures} capture${report.sessionSummaryCaptures === 1 ? "" : "s"} at ${report.paths.sessionSummaryQueueDir}`)}`);
|
|
14828
16422
|
console.log(` ${ICON.skip} ${pc3.bold("outbox ")} ${pc3.dim(`${report.outboxEvents} event${report.outboxEvents === 1 ? "" : "s"} at ${report.paths.outboxPath}`)}`);
|
|
14829
16423
|
}
|
|
16424
|
+
function printSessionSummaryFlush(result) {
|
|
16425
|
+
if (result.skipped) {
|
|
16426
|
+
console.log(` ${ICON.skip} ${pc3.bold("captures ")} ${pc3.dim(result.skipped.replaceAll("_", " "))}`);
|
|
16427
|
+
return;
|
|
16428
|
+
}
|
|
16429
|
+
const delivered = `${result.acknowledged}/${result.attempted} acknowledged`;
|
|
16430
|
+
console.log(` ${result.retained === 0 ? ICON.ok : ICON.warn} ${pc3.bold("captures ")} ${pc3.dim(`${delivered}, ${result.retained} retained, ${result.malformed} malformed`)}`);
|
|
16431
|
+
if (result.fallbackAcknowledged > 0) {
|
|
16432
|
+
console.log(` ${ICON.warn} ${pc3.bold("fallback ")} ${pc3.dim(`${result.fallbackAcknowledged} capture(s) delivered through the legacy Work Graph endpoint`)}`);
|
|
16433
|
+
}
|
|
16434
|
+
if (result.firstError) console.log(` ${ICON.warn} ${pc3.dim(result.firstError)}`);
|
|
16435
|
+
}
|
|
16436
|
+
async function runSessionSummaryFlushCommand(options) {
|
|
16437
|
+
const queueDir = resolve4(options.queue?.trim() || inspectRuntimeHooks().paths.sessionSummaryQueueDir);
|
|
16438
|
+
const found = sessionSummaryCaptureFiles(queueDir).length;
|
|
16439
|
+
let result;
|
|
16440
|
+
if (found === 0) {
|
|
16441
|
+
result = {
|
|
16442
|
+
found: 0,
|
|
16443
|
+
attempted: 0,
|
|
16444
|
+
acknowledged: 0,
|
|
16445
|
+
retained: 0,
|
|
16446
|
+
malformed: 0,
|
|
16447
|
+
fallbackAcknowledged: 0,
|
|
16448
|
+
skipped: "empty_queue"
|
|
16449
|
+
};
|
|
16450
|
+
} else {
|
|
16451
|
+
const lease = claimSessionSummaryFlushLease(queueDir);
|
|
16452
|
+
if (!lease.acquired) {
|
|
16453
|
+
if (lease.reason === "unavailable") {
|
|
16454
|
+
if (options.background) return;
|
|
16455
|
+
throw new Error(`Could not acquire the session-summary flush lease at ${queueDir}.`);
|
|
16456
|
+
}
|
|
16457
|
+
result = {
|
|
16458
|
+
found,
|
|
16459
|
+
attempted: 0,
|
|
16460
|
+
acknowledged: 0,
|
|
16461
|
+
retained: 0,
|
|
16462
|
+
malformed: 0,
|
|
16463
|
+
fallbackAcknowledged: 0,
|
|
16464
|
+
skipped: "already_running"
|
|
16465
|
+
};
|
|
16466
|
+
} else {
|
|
16467
|
+
try {
|
|
16468
|
+
const auth = await resolveOrgxAuth();
|
|
16469
|
+
if (!auth) {
|
|
16470
|
+
if (options.background) return;
|
|
16471
|
+
throw new Error("No OrgX credential found. Run `orgx-wizard login` first.");
|
|
16472
|
+
}
|
|
16473
|
+
result = await flushSessionSummaryCaptures({
|
|
16474
|
+
queueDir,
|
|
16475
|
+
auth,
|
|
16476
|
+
limit: parsePositiveInt(options.limit, 100),
|
|
16477
|
+
stopOnFailure: options.background === true,
|
|
16478
|
+
send: async (url, body, headers) => {
|
|
16479
|
+
try {
|
|
16480
|
+
const response = await fetchWithRetry(
|
|
16481
|
+
url,
|
|
16482
|
+
{
|
|
16483
|
+
method: "POST",
|
|
16484
|
+
headers,
|
|
16485
|
+
body: JSON.stringify(body)
|
|
16486
|
+
},
|
|
16487
|
+
options.background ? { timeoutMs: 8e3, retries: 0 } : { timeoutMs: 12e3, retries: 1 }
|
|
16488
|
+
);
|
|
16489
|
+
return { ok: response.ok, status: response.status };
|
|
16490
|
+
} catch {
|
|
16491
|
+
return { ok: false, status: 0 };
|
|
16492
|
+
}
|
|
16493
|
+
}
|
|
16494
|
+
});
|
|
16495
|
+
} finally {
|
|
16496
|
+
lease.release();
|
|
16497
|
+
}
|
|
16498
|
+
}
|
|
16499
|
+
}
|
|
16500
|
+
if (options.background) return;
|
|
16501
|
+
await safeTrackWizardTelemetry("hooks_session_summary_flush_ran", {
|
|
16502
|
+
command: "hooks flush",
|
|
16503
|
+
attempted: String(result.attempted),
|
|
16504
|
+
acknowledged: String(result.acknowledged),
|
|
16505
|
+
retained: String(result.retained)
|
|
16506
|
+
});
|
|
16507
|
+
if (result.retained > 0) process.exitCode = 1;
|
|
16508
|
+
if (options.json) {
|
|
16509
|
+
console.log(JSON.stringify({ ok: result.retained === 0, queueDir, ...result }, null, 2));
|
|
16510
|
+
return;
|
|
16511
|
+
}
|
|
16512
|
+
printSessionSummaryFlush(result);
|
|
16513
|
+
}
|
|
16514
|
+
async function runHookBackfillCommand(options) {
|
|
16515
|
+
const paths = inspectRuntimeHooks().paths;
|
|
16516
|
+
const spoolPath = resolve4(options.spool?.trim() || paths.outboxPath);
|
|
16517
|
+
if (!fileExists(spoolPath)) {
|
|
16518
|
+
const message = `No hook spool found at ${spoolPath}`;
|
|
16519
|
+
if (options.json) {
|
|
16520
|
+
console.log(JSON.stringify({ ok: true, distilled: 0, reason: "missing_spool", spoolPath }, null, 2));
|
|
16521
|
+
return;
|
|
16522
|
+
}
|
|
16523
|
+
console.log(` ${ICON.skip} ${pc3.dim(message)}`);
|
|
16524
|
+
return;
|
|
16525
|
+
}
|
|
16526
|
+
if ((options.post || options.truncate) && !options.yes) {
|
|
16527
|
+
throw new Error("Posting or truncating the hook spool requires --yes. Run without --post to preview.");
|
|
16528
|
+
}
|
|
16529
|
+
if (options.truncate && !options.post) {
|
|
16530
|
+
throw new Error("--truncate is only valid together with --post; nothing may be discarded unsent.");
|
|
16531
|
+
}
|
|
16532
|
+
const distilled = await distillSpool(spoolPath);
|
|
16533
|
+
const reduction = distilled.bytes.total > 0 ? Math.round(distilled.spoolBytes / distilled.bytes.total) : 0;
|
|
16534
|
+
let post = { attempted: 0, posted: 0, failed: 0 };
|
|
16535
|
+
let truncation = { truncated: false, bytesReleased: 0 };
|
|
16536
|
+
if (options.post) {
|
|
16537
|
+
const auth = await resolveOrgxAuth();
|
|
16538
|
+
if (!auth) {
|
|
16539
|
+
throw new Error("No OrgX credential found. Run `orgx-wizard login` first.");
|
|
16540
|
+
}
|
|
16541
|
+
const url = `${auth.baseUrl.replace(/\/+$/, "")}${SESSION_SUMMARY_ENDPOINT_PATH}`;
|
|
16542
|
+
post = await postSummaries(distilled.summaries, async (summary) => {
|
|
16543
|
+
const minimalSummary = buildMinimalCloudSummary(summary, {
|
|
16544
|
+
captureKind: "historical_backfill"
|
|
16545
|
+
});
|
|
16546
|
+
const response = await fetchWithRetry(url, {
|
|
16547
|
+
method: "POST",
|
|
16548
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${auth.apiKey}` },
|
|
16549
|
+
body: JSON.stringify({ session: minimalSummary, backfill: true })
|
|
16550
|
+
});
|
|
16551
|
+
return { ok: response.ok, status: response.status };
|
|
16552
|
+
});
|
|
16553
|
+
if (options.truncate) {
|
|
16554
|
+
truncation = truncateSpool(spoolPath, post);
|
|
16555
|
+
}
|
|
16556
|
+
}
|
|
16557
|
+
const result = {
|
|
16558
|
+
ok: true,
|
|
16559
|
+
spoolPath,
|
|
16560
|
+
spoolBytes: distilled.spoolBytes,
|
|
16561
|
+
lines: distilled.lines,
|
|
16562
|
+
badLines: distilled.badLines,
|
|
16563
|
+
sessions: distilled.summaries.length,
|
|
16564
|
+
payloadBytes: distilled.bytes,
|
|
16565
|
+
reductionFactor: reduction,
|
|
16566
|
+
posted: options.post ? post : null,
|
|
16567
|
+
truncated: options.truncate ? truncation : null
|
|
16568
|
+
};
|
|
16569
|
+
if (options.json) {
|
|
16570
|
+
console.log(JSON.stringify(result, null, 2));
|
|
16571
|
+
return;
|
|
16572
|
+
}
|
|
16573
|
+
console.log(` ${ICON.ok} ${pc3.bold("spool ")} ${pc3.dim(`${(distilled.spoolBytes / 1048576).toFixed(1)}MB, ${distilled.lines} lines, ${distilled.badLines} unparsable`)}`);
|
|
16574
|
+
console.log(` ${ICON.ok} ${pc3.bold("distilled ")} ${pc3.dim(`${distilled.summaries.length} sessions, ${(distilled.bytes.total / 1024).toFixed(0)}KB total (p50 ${distilled.bytes.p50}B, max ${distilled.bytes.max}B), ${reduction}x smaller`)}`);
|
|
16575
|
+
if (!options.post) {
|
|
16576
|
+
console.log(` ${ICON.skip} ${pc3.dim("preview only. Re-run with --post --yes to upload, and add --truncate to archive the spool.")}`);
|
|
16577
|
+
return;
|
|
16578
|
+
}
|
|
16579
|
+
console.log(` ${post.failed === 0 ? ICON.ok : ICON.warn} ${pc3.bold("posted ")} ${pc3.dim(`${post.posted}/${post.attempted} sessions${post.firstError ? ` (${post.firstError})` : ""}`)}`);
|
|
16580
|
+
if (options.truncate) {
|
|
16581
|
+
console.log(` ${truncation.truncated ? ICON.ok : ICON.warn} ${pc3.bold("truncated ")} ${pc3.dim(truncation.truncated ? `${(truncation.bytesReleased / 1048576).toFixed(1)}MB archived to ${truncation.archivePath}` : `skipped: ${truncation.reason}`)}`);
|
|
16582
|
+
}
|
|
16583
|
+
}
|
|
14830
16584
|
function requireHookReplayApproval(options, interactive) {
|
|
14831
16585
|
if (options.yes) return true;
|
|
14832
16586
|
if (!interactive) {
|
|
@@ -14857,7 +16611,7 @@ async function runHookReplayCommand(options) {
|
|
|
14857
16611
|
}
|
|
14858
16612
|
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
14859
16613
|
const paths = inspectRuntimeHooks().paths;
|
|
14860
|
-
const outboxPath =
|
|
16614
|
+
const outboxPath = resolve4(options.outbox?.trim() || paths.outboxPath);
|
|
14861
16615
|
const readResult = readRuntimeHookOutbox(outboxPath, parsePositiveInt(options.limit, 200));
|
|
14862
16616
|
const replay = buildWorkGraphHookReplayPatch(readResult);
|
|
14863
16617
|
if (replay.records === 0) {
|
|
@@ -14889,10 +16643,10 @@ async function runHookReplayCommand(options) {
|
|
|
14889
16643
|
}
|
|
14890
16644
|
function readAuditInput(options, interactive) {
|
|
14891
16645
|
if (options.input?.trim()) {
|
|
14892
|
-
return
|
|
16646
|
+
return readFileSync10(resolve4(options.input.trim()), "utf8");
|
|
14893
16647
|
}
|
|
14894
16648
|
if (!process.stdin.isTTY) {
|
|
14895
|
-
return
|
|
16649
|
+
return readFileSync10(0, "utf8");
|
|
14896
16650
|
}
|
|
14897
16651
|
if (!interactive) {
|
|
14898
16652
|
throw new Error("Audit input is required. Pass --input <file> or pipe text into wizard audit.");
|
|
@@ -14924,8 +16678,8 @@ function collectPathOption(value, previous = []) {
|
|
|
14924
16678
|
];
|
|
14925
16679
|
}
|
|
14926
16680
|
function parseClientExtractionFile(path) {
|
|
14927
|
-
const resolvedPath =
|
|
14928
|
-
const parsed = JSON.parse(
|
|
16681
|
+
const resolvedPath = resolve4(path);
|
|
16682
|
+
const parsed = JSON.parse(readFileSync10(resolvedPath, "utf8"));
|
|
14929
16683
|
if (!isRecord(parsed)) {
|
|
14930
16684
|
throw new Error(`AI-client extraction must be a JSON object: ${resolvedPath}`);
|
|
14931
16685
|
}
|
|
@@ -14947,8 +16701,8 @@ async function readAuditImports(options, interactive) {
|
|
|
14947
16701
|
const missingSources = [];
|
|
14948
16702
|
if (sources.length > 0) {
|
|
14949
16703
|
const imported = loadAiSessionImports({
|
|
14950
|
-
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir:
|
|
14951
|
-
...options.codexSessionsDir?.trim() ? { codexSessionsDir:
|
|
16704
|
+
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve4(options.claudeProjectsDir.trim()) } : {},
|
|
16705
|
+
...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve4(options.codexSessionsDir.trim()) } : {},
|
|
14952
16706
|
limitPerSource: parsePositiveInteger(options.sessionLimit, 3, "--session-limit"),
|
|
14953
16707
|
sinceDays: parsePositiveInteger(options.sessionDays, 30, "--session-days"),
|
|
14954
16708
|
sources
|
|
@@ -14988,8 +16742,8 @@ async function readWorkGraphInputs(options, interactive) {
|
|
|
14988
16742
|
const clientExtractions = readClientExtractions(options);
|
|
14989
16743
|
const investigationSources = parseInvestigationSourceList(options.from);
|
|
14990
16744
|
const investigationSourceData = investigationSources.length > 0 ? loadWorkGraphInvestigationSourceData({
|
|
14991
|
-
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir:
|
|
14992
|
-
...options.codexSessionsDir?.trim() ? { codexSessionsDir:
|
|
16745
|
+
...options.claudeProjectsDir?.trim() ? { claudeProjectsDir: resolve4(options.claudeProjectsDir.trim()) } : {},
|
|
16746
|
+
...options.codexSessionsDir?.trim() ? { codexSessionsDir: resolve4(options.codexSessionsDir.trim()) } : {},
|
|
14993
16747
|
cwd: process.cwd(),
|
|
14994
16748
|
limitPerSource: parsePositiveInteger(options.sessionLimit, 8, "--session-limit"),
|
|
14995
16749
|
sinceDays: parsePositiveInteger(options.sessionDays, 45, "--session-days"),
|
|
@@ -15106,10 +16860,10 @@ async function runAuditCommand(options) {
|
|
|
15106
16860
|
workspace
|
|
15107
16861
|
});
|
|
15108
16862
|
const markdown = renderSelfAuditMarkdown(plan);
|
|
15109
|
-
const outputDir =
|
|
16863
|
+
const outputDir = resolve4(options.outputDir?.trim() || ".orgx/audits");
|
|
15110
16864
|
const timestamp = plan.generated_at.replace(/[:.]/g, "-");
|
|
15111
|
-
const jsonPath =
|
|
15112
|
-
const markdownPath =
|
|
16865
|
+
const jsonPath = resolve4(outputDir, `ai-native-self-audit-${timestamp}.json`);
|
|
16866
|
+
const markdownPath = resolve4(outputDir, `ai-native-self-audit-${timestamp}.md`);
|
|
15113
16867
|
writeJsonFile(jsonPath, plan);
|
|
15114
16868
|
writeTextFile(markdownPath, markdown);
|
|
15115
16869
|
if (options.json) {
|
|
@@ -15240,7 +16994,7 @@ async function runOperatingMapCommand(queryParts, options) {
|
|
|
15240
16994
|
}
|
|
15241
16995
|
function runWorkGraphExtractionSchemaCommand(options) {
|
|
15242
16996
|
const protocol = buildWorkGraphExtractionProtocol();
|
|
15243
|
-
const outputPath = options.output?.trim() ?
|
|
16997
|
+
const outputPath = options.output?.trim() ? resolve4(options.output.trim()) : "";
|
|
15244
16998
|
if (outputPath) {
|
|
15245
16999
|
if (options.json) {
|
|
15246
17000
|
writeJsonFile(outputPath, protocol);
|
|
@@ -15270,8 +17024,8 @@ function normalizeRuntimePacketRole(role) {
|
|
|
15270
17024
|
}
|
|
15271
17025
|
function runWorkGraphRuntimeEventCommand(options) {
|
|
15272
17026
|
const source = normalizeRuntimePacketSource(options.source);
|
|
15273
|
-
const cwd =
|
|
15274
|
-
const outputRoot =
|
|
17027
|
+
const cwd = resolve4(options.cwd?.trim() || process.cwd());
|
|
17028
|
+
const outputRoot = resolve4(cwd, options.outputDir?.trim() || ".orgx/work-graph/runtime-events");
|
|
15275
17029
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
15276
17030
|
const timestamp = generatedAt.replace(/[:.]/g, "-");
|
|
15277
17031
|
const summary = options.summary?.trim() || options.message?.trim();
|
|
@@ -15292,7 +17046,7 @@ function runWorkGraphRuntimeEventCommand(options) {
|
|
|
15292
17046
|
collection_method: "runtime_packet",
|
|
15293
17047
|
redaction_state: "agent_redacted"
|
|
15294
17048
|
};
|
|
15295
|
-
const path =
|
|
17049
|
+
const path = resolve4(outputRoot, source, `${timestamp}-${process.pid}.jsonl`);
|
|
15296
17050
|
writeTextFile(path, `${JSON.stringify(packet)}
|
|
15297
17051
|
`, { mode: 384 });
|
|
15298
17052
|
if (options.json) {
|
|
@@ -15335,11 +17089,11 @@ async function runWorkGraphCommand(options, defaults = {}) {
|
|
|
15335
17089
|
workspace
|
|
15336
17090
|
});
|
|
15337
17091
|
const markdown = renderWorkGraphMarkdown(report);
|
|
15338
|
-
const outputDir =
|
|
17092
|
+
const outputDir = resolve4(commandOptions.outputDir?.trim() || ".orgx/work-graph");
|
|
15339
17093
|
const timestamp = report.generated_at.replace(/[:.]/g, "-");
|
|
15340
|
-
const jsonPath =
|
|
15341
|
-
const markdownPath =
|
|
15342
|
-
const agentBriefPath =
|
|
17094
|
+
const jsonPath = resolve4(outputDir, `work-graph-report-${timestamp}.json`);
|
|
17095
|
+
const markdownPath = resolve4(outputDir, `work-graph-report-${timestamp}.md`);
|
|
17096
|
+
const agentBriefPath = resolve4(outputDir, `work-graph-agent-brief-${timestamp}.md`);
|
|
15343
17097
|
writeJsonFile(jsonPath, report);
|
|
15344
17098
|
writeTextFile(markdownPath, markdown);
|
|
15345
17099
|
let published = null;
|
|
@@ -15754,14 +17508,14 @@ async function readSingleKey() {
|
|
|
15754
17508
|
const stdin = process.stdin;
|
|
15755
17509
|
if (!stdin.isTTY) return null;
|
|
15756
17510
|
const previousRawMode = stdin.isRaw === true;
|
|
15757
|
-
return await new Promise((
|
|
17511
|
+
return await new Promise((resolve5) => {
|
|
15758
17512
|
const cleanup = (result) => {
|
|
15759
17513
|
stdin.off("data", onData);
|
|
15760
17514
|
if (stdin.isTTY) {
|
|
15761
17515
|
stdin.setRawMode(previousRawMode);
|
|
15762
17516
|
}
|
|
15763
17517
|
stdin.pause();
|
|
15764
|
-
|
|
17518
|
+
resolve5(result);
|
|
15765
17519
|
};
|
|
15766
17520
|
const onData = (chunk) => {
|
|
15767
17521
|
const text2 = chunk.toString("utf8");
|
|
@@ -16526,7 +18280,7 @@ async function main() {
|
|
|
16526
18280
|
initializeWizardSentry();
|
|
16527
18281
|
const program = new Command();
|
|
16528
18282
|
program.name("orgx-wizard").description("Add OrgX MCP configs, skills/rules, and companion plugins to your local AI tools.").showHelpAfterError();
|
|
16529
|
-
const pkgVersion = true ? "0.1.
|
|
18283
|
+
const pkgVersion = true ? "0.1.58" : void 0;
|
|
16530
18284
|
program.version(pkgVersion ?? "unknown", "-V, --version");
|
|
16531
18285
|
program.hook("preAction", (_thisCommand, actionCommand) => {
|
|
16532
18286
|
if (Boolean(actionCommand.optsWithGlobals().json)) return;
|
|
@@ -17326,9 +19080,15 @@ async function main() {
|
|
|
17326
19080
|
}
|
|
17327
19081
|
}
|
|
17328
19082
|
});
|
|
19083
|
+
hooks.command("flush").description("Deliver queued session summaries and retain every unacknowledged capture.").option("--queue <path>", "session-summary capture queue path").option("--limit <count>", "maximum queued captures to attempt", "100").option("--background", "run quietly as a hook-triggered delivery worker").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
19084
|
+
await runSessionSummaryFlushCommand(options);
|
|
19085
|
+
});
|
|
17329
19086
|
hooks.command("replay").description("Replay passive hook outbox events into a claimed Work Graph profile.").requiredOption("--fingerprint <fingerprint>", "target Work Graph fingerprint, for example wgf_0123...").option("--outbox <path>", "runtime hook JSONL outbox path").option("--limit <count>", "maximum recent hook events to replay", "200").option("--yes", "approve publishing hook evidence without prompting").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
17330
19087
|
await runHookReplayCommand(options);
|
|
17331
19088
|
});
|
|
19089
|
+
hooks.command("backfill").description("Distill the historical hook spool into lean session summaries (preview by default).").option("--spool <path>", "runtime hook JSONL spool path").option("--post", "upload the distilled summaries to OrgX (requires --yes)").option("--truncate", "archive and empty the spool after a fully successful --post (requires --yes)").option("--yes", "confirm uploading or truncating your local work history").option("--json", "emit a JSON command summary").action(async (options) => {
|
|
19090
|
+
await runHookBackfillCommand(options);
|
|
19091
|
+
});
|
|
17332
19092
|
program.command("doctor").description("Verify OrgX health, or diagnose a credential-free workload shape.").option("--workload <path>", "diagnose a sanitized workload-shape JSON file; use - for stdin").option("--base-url <url>", "explicit workload endpoint override for testing or self-hosting").option("--json", "emit the selected doctor result as JSON").action(async (options) => {
|
|
17333
19093
|
if (options.baseUrl && !options.workload) {
|
|
17334
19094
|
const message = "--base-url is only valid together with --workload.";
|
|
@@ -17564,4 +19324,4 @@ main().catch(async (error) => {
|
|
|
17564
19324
|
process.exitCode = 1;
|
|
17565
19325
|
});
|
|
17566
19326
|
//# sourceMappingURL=cli.js.map
|
|
17567
|
-
//# debugId=
|
|
19327
|
+
//# debugId=66b240e6-4f09-5817-86f1-bac0012af8cb
|