@darkhunt-security/endpoint-codex 0.9.12 → 0.9.14

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "darkhunt-guard",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "hooks": "./hooks/hooks.json",
5
5
  "description": "Darkhunt endpoint plugin for Codex CLI \u2014 full session trace capture and PreToolUse guardrails.",
6
6
  "author": {
@@ -5674,8 +5674,8 @@ var require_getMachineId_linux = __commonJS({
5674
5674
  var fs_1 = __require("fs");
5675
5675
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
5676
5676
  async function getMachineId() {
5677
- const paths2 = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
5678
- for (const path of paths2) {
5677
+ const paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
5678
+ for (const path of paths) {
5679
5679
  try {
5680
5680
  const result2 = await fs_1.promises.readFile(path, { encoding: "utf8" });
5681
5681
  return result2.trim();
@@ -13248,8 +13248,8 @@ var require_getMachineId_linux2 = __commonJS({
13248
13248
  var fs_1 = __require("fs");
13249
13249
  var api_1 = (init_esm(), __toCommonJS(esm_exports));
13250
13250
  async function getMachineId() {
13251
- const paths2 = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
13252
- for (const path of paths2) {
13251
+ const paths = ["/etc/machine-id", "/var/lib/dbus/machine-id"];
13252
+ for (const path of paths) {
13253
13253
  try {
13254
13254
  const result2 = await fs_1.promises.readFile(path, { encoding: "utf8" });
13255
13255
  return result2.trim();
@@ -18096,8 +18096,8 @@ var DEFAULT_PROFILE = "default";
18096
18096
  var CREDENTIALS_BASENAME = "endpoint-credentials";
18097
18097
  var DEFAULT_CREDENTIALS_PATH = join2(CONFIG_DIR, `${CREDENTIALS_BASENAME}.json`);
18098
18098
  var CREDENTIALS_ENV = "DARKHUNT_ENDPOINT_CREDENTIALS";
18099
- function resolveProfile(explicit2, fromConfig) {
18100
- return explicit2 ?? process.env["DARKHUNT_PROFILE"] ?? fromConfig ?? DEFAULT_PROFILE;
18099
+ function resolveProfile(explicit, fromConfig) {
18100
+ return explicit ?? process.env["DARKHUNT_PROFILE"] ?? fromConfig ?? DEFAULT_PROFILE;
18101
18101
  }
18102
18102
  function expandPath(path) {
18103
18103
  if (path.startsWith("~/"))
@@ -18109,13 +18109,13 @@ function credentialsSelection(sources = {}) {
18109
18109
  const pointer = s.explicitPath ?? process.env[CREDENTIALS_ENV] ?? s.configuredPath;
18110
18110
  if (pointer)
18111
18111
  return { path: expandPath(pointer), explicit: true };
18112
- const profile2 = s.profile ?? DEFAULT_PROFILE;
18113
- if (profile2 === DEFAULT_PROFILE)
18112
+ const profile = s.profile ?? DEFAULT_PROFILE;
18113
+ if (profile === DEFAULT_PROFILE)
18114
18114
  return { path: DEFAULT_CREDENTIALS_PATH, explicit: false };
18115
- return { path: join2(CONFIG_DIR, `${CREDENTIALS_BASENAME}.${profile2}.json`), explicit: true };
18115
+ return { path: join2(CONFIG_DIR, `${CREDENTIALS_BASENAME}.${profile}.json`), explicit: true };
18116
18116
  }
18117
- function scopeKey(vendor, profile2) {
18118
- return profile2 === DEFAULT_PROFILE ? vendor : `${vendor}.${profile2}`;
18117
+ function scopeKey(vendor, profile) {
18118
+ return profile === DEFAULT_PROFILE ? vendor : `${vendor}.${profile}`;
18119
18119
  }
18120
18120
 
18121
18121
  // packages/core/dist/config/local.js
@@ -18175,11 +18175,11 @@ function readConfigFile(vendor) {
18175
18175
  }
18176
18176
  function loadLocalConfig(vendor, options = {}) {
18177
18177
  const vendorFile = readConfigFile(vendor);
18178
- const profile2 = resolveProfile(options.profile, vendorFile.profile);
18178
+ const profile = resolveProfile(options.profile, vendorFile.profile);
18179
18179
  const selection = credentialsSelection({
18180
18180
  ...options.credentialsPath !== void 0 ? { explicitPath: options.credentialsPath } : {},
18181
18181
  ...vendorFile.credentials !== void 0 ? { configuredPath: vendorFile.credentials } : {},
18182
- profile: profile2
18182
+ profile
18183
18183
  });
18184
18184
  const inlineComplete = REQUIRED_FIELDS.every((name) => typeof vendorFile[name] === "string" && vendorFile[name] !== "");
18185
18185
  const useInline = inlineComplete && !selection.explicit;
@@ -18198,13 +18198,13 @@ function loadLocalConfig(vendor, options = {}) {
18198
18198
  ["applicationId", applicationId]
18199
18199
  ]) {
18200
18200
  if (typeof value !== "string" || value === "") {
18201
- throw new ConfigError(useInline ? `missing ${name} in ${configPath()}` : `missing ${name} for profile '${profile2}' (looked in ${credsPath}, then ${configPath()})`);
18201
+ throw new ConfigError(useInline ? `missing ${name} in ${configPath()}` : `missing ${name} for profile '${profile}' (looked in ${credsPath}, then ${configPath()})`);
18202
18202
  }
18203
18203
  }
18204
18204
  return {
18205
- profile: profile2,
18205
+ profile,
18206
18206
  credentialsPath: credsPath,
18207
- scope: scopeKey(vendor, profile2),
18207
+ scope: scopeKey(vendor, profile),
18208
18208
  baseUrl,
18209
18209
  apiKey,
18210
18210
  tenantId,
@@ -18245,16 +18245,16 @@ function loadBeat(scope) {
18245
18245
  function liveTranscripts(beat, now = Date.now()) {
18246
18246
  if (!beat)
18247
18247
  return [];
18248
- const paths2 = /* @__PURE__ */ new Set();
18248
+ const paths = /* @__PURE__ */ new Set();
18249
18249
  for (const session of Object.values(beat.sessions ?? {})) {
18250
18250
  if (!session.transcriptPath)
18251
18251
  continue;
18252
18252
  const at = Date.parse(session.at);
18253
18253
  if (Number.isNaN(at) || now - at > BEAT_LIVE_MS)
18254
18254
  continue;
18255
- paths2.add(session.transcriptPath);
18255
+ paths.add(session.transcriptPath);
18256
18256
  }
18257
- return [...paths2];
18257
+ return [...paths];
18258
18258
  }
18259
18259
 
18260
18260
  // packages/core/dist/emit/ids.js
@@ -21270,16 +21270,16 @@ var Generation = class extends Span {
21270
21270
  this.otelSpan.setAttribute(ATTR.MODEL_NAME, model);
21271
21271
  this.otelSpan.setAttribute(GEN_AI.REQUEST_MODEL, model);
21272
21272
  }
21273
- setUsage(usage) {
21274
- this.otelSpan.setAttribute(ATTR.USAGE_DETAILS, safeJsonStringify(usage));
21275
- if (usage.input_tokens !== void 0)
21276
- this.otelSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, usage.input_tokens);
21277
- if (usage.output_tokens !== void 0)
21278
- this.otelSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, usage.output_tokens);
21279
- if (usage.cache_read_tokens !== void 0)
21280
- this.otelSpan.setAttribute(GEN_AI.USAGE_CACHE_READ_INPUT_TOKENS, usage.cache_read_tokens);
21281
- if (usage.cache_creation_tokens !== void 0)
21282
- this.otelSpan.setAttribute(GEN_AI.USAGE_CACHE_CREATION_INPUT_TOKENS, usage.cache_creation_tokens);
21273
+ setUsage(usage2) {
21274
+ this.otelSpan.setAttribute(ATTR.USAGE_DETAILS, safeJsonStringify(usage2));
21275
+ if (usage2.input_tokens !== void 0)
21276
+ this.otelSpan.setAttribute(GEN_AI.USAGE_INPUT_TOKENS, usage2.input_tokens);
21277
+ if (usage2.output_tokens !== void 0)
21278
+ this.otelSpan.setAttribute(GEN_AI.USAGE_OUTPUT_TOKENS, usage2.output_tokens);
21279
+ if (usage2.cache_read_tokens !== void 0)
21280
+ this.otelSpan.setAttribute(GEN_AI.USAGE_CACHE_READ_INPUT_TOKENS, usage2.cache_read_tokens);
21281
+ if (usage2.cache_creation_tokens !== void 0)
21282
+ this.otelSpan.setAttribute(GEN_AI.USAGE_CACHE_CREATION_INPUT_TOKENS, usage2.cache_creation_tokens);
21283
21283
  }
21284
21284
  setCost(cost) {
21285
21285
  this.otelSpan.setAttribute(ATTR.COST_DETAILS, safeJsonStringify(cost));
@@ -21683,19 +21683,19 @@ var SPOOL_KEY = "::spool::";
21683
21683
  var DARKHUNT_SOURCE_HINT = "darkhunt-telemetry";
21684
21684
  function drainSpool(mapper, checkpoints) {
21685
21685
  const path = spoolPath(mapper.vendor);
21686
- const paths2 = /* @__PURE__ */ new Set();
21686
+ const paths = /* @__PURE__ */ new Set();
21687
21687
  try {
21688
21688
  const { lines, offset, size } = readNewLines(path, resumeOffset(checkpoints, SPOOL_KEY));
21689
21689
  for (const line of lines) {
21690
21690
  try {
21691
21691
  const event = JSON.parse(line.text);
21692
21692
  if (event.transcriptPath)
21693
- paths2.add(event.transcriptPath);
21693
+ paths.add(event.transcriptPath);
21694
21694
  } catch {
21695
21695
  continue;
21696
21696
  }
21697
21697
  }
21698
- return { paths: [...paths2], checkpoint: { offset, size } };
21698
+ return { paths: [...paths], checkpoint: { offset, size } };
21699
21699
  } catch {
21700
21700
  return { paths: [] };
21701
21701
  }
@@ -21719,6 +21719,7 @@ async function runForwarder(mapper, options = {}) {
21719
21719
  skipped: ["another forwarder holds the lock"],
21720
21720
  kinds: {},
21721
21721
  warnings: [],
21722
+ emitted: { transcripts: 0, records: 0 },
21722
21723
  ok: true
21723
21724
  };
21724
21725
  }
@@ -21728,6 +21729,7 @@ async function runForwarder(mapper, options = {}) {
21728
21729
  skipped: [],
21729
21730
  kinds: {},
21730
21731
  warnings: [],
21732
+ emitted: { transcripts: 0, records: 0 },
21731
21733
  ok: true
21732
21734
  };
21733
21735
  try {
@@ -21784,8 +21786,8 @@ async function runForwarder(mapper, options = {}) {
21784
21786
  }
21785
21787
  const drain = options.paths ? { paths: options.paths } : drainSpool(mapper, checkpoints);
21786
21788
  const named = options.paths ? drain.paths : [.../* @__PURE__ */ new Set([...drain.paths, ...liveTranscripts(loadBeat(config.scope))])];
21787
- const paths2 = [...new Set(named.flatMap((path) => [path, ...related(mapper, path)]))];
21788
- if (paths2.length === 0) {
21789
+ const paths = [...new Set(named.flatMap((path) => [path, ...related(mapper, path)]))];
21790
+ if (paths.length === 0) {
21789
21791
  if (drain.checkpoint)
21790
21792
  checkpoints[SPOOL_KEY] = drain.checkpoint;
21791
21793
  saveCheckpoints(config.scope, checkpoints);
@@ -21815,7 +21817,7 @@ async function runForwarder(mapper, options = {}) {
21815
21817
  const watch = watchExports();
21816
21818
  const staged = { ...checkpoints };
21817
21819
  try {
21818
- for (const path of paths2) {
21820
+ for (const path of paths) {
21819
21821
  try {
21820
21822
  const from = resumeOffset(checkpoints, path);
21821
21823
  const carried = resumeTurn(checkpoints, path, from);
@@ -21877,6 +21879,7 @@ async function runForwarder(mapper, options = {}) {
21877
21879
  await client.shutdown();
21878
21880
  watch.stop();
21879
21881
  }
21882
+ result2.emitted = { transcripts: result2.transcripts, records: result2.records };
21880
21883
  if (watch.failures() > 0) {
21881
21884
  result2.ok = false;
21882
21885
  result2.error = watch.reason() ?? "span export failed";
@@ -21901,12 +21904,12 @@ async function runForwarder(mapper, options = {}) {
21901
21904
  release();
21902
21905
  }
21903
21906
  }
21904
- function withinDays(paths2, days) {
21907
+ function withinDays(paths, days) {
21905
21908
  if (!Number.isFinite(days) || days <= 0) {
21906
21909
  throw new RangeError(`--days must be a positive number of days, got ${days}`);
21907
21910
  }
21908
21911
  const cutoff = Date.now() - days * 24 * 60 * 60 * 1e3;
21909
- return paths2.filter((path) => {
21912
+ return paths.filter((path) => {
21910
21913
  try {
21911
21914
  return statSync4(path).mtimeMs >= cutoff;
21912
21915
  } catch {
@@ -21958,6 +21961,142 @@ import { homedir as homedir3 } from "node:os";
21958
21961
  import { join as join9 } from "node:path";
21959
21962
  var CLI_CREDENTIALS_PATH = join9(homedir3(), ".darkhunt", "credentials.json");
21960
21963
 
21964
+ // packages/core/dist/cli/backfill.js
21965
+ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["dry-run"]);
21966
+ var VALUE_FLAGS = /* @__PURE__ */ new Set(["profile", "credentials", "days", "batch"]);
21967
+ var DEFAULT_BATCH = 25;
21968
+ function usage(command) {
21969
+ return [
21970
+ `usage: ${command} [options] [paths...]`,
21971
+ "",
21972
+ " --dry-run map only: no network, no checkpoints",
21973
+ " --days=N only transcripts written to in the last N days",
21974
+ " --batch=N transcripts per pass (default " + String(DEFAULT_BATCH) + ")",
21975
+ " --profile=NAME endpoint profile to ship to",
21976
+ " --credentials=PATH explicit credentials file",
21977
+ " -h, --help this message",
21978
+ "",
21979
+ " With no paths this ships EVERY transcript on disk. Narrow it with --days",
21980
+ " or by naming transcripts, and confirm the destination with --dry-run first."
21981
+ ].join("\n");
21982
+ }
21983
+ function parseBackfillArgs(argv) {
21984
+ const paths = [];
21985
+ const values = /* @__PURE__ */ new Map();
21986
+ let dryRun = false;
21987
+ for (const arg of argv) {
21988
+ if (arg === "-h" || arg === "--help")
21989
+ return { kind: "help" };
21990
+ if (!arg.startsWith("--")) {
21991
+ paths.push(arg);
21992
+ continue;
21993
+ }
21994
+ const body = arg.slice(2);
21995
+ const eq = body.indexOf("=");
21996
+ const name = eq === -1 ? body : body.slice(0, eq);
21997
+ if (BOOLEAN_FLAGS.has(name)) {
21998
+ if (eq !== -1)
21999
+ return { kind: "error", message: `--${name} takes no value` };
22000
+ dryRun = true;
22001
+ continue;
22002
+ }
22003
+ if (VALUE_FLAGS.has(name)) {
22004
+ if (eq === -1)
22005
+ return { kind: "error", message: `--${name} needs a value, as --${name}=VALUE` };
22006
+ values.set(name, body.slice(eq + 1));
22007
+ continue;
22008
+ }
22009
+ return {
22010
+ kind: "error",
22011
+ message: `unknown option '${arg}'. Known: ${[...BOOLEAN_FLAGS, ...VALUE_FLAGS].map((f) => `--${f}`).sort().join(", ")}, --help`
22012
+ };
22013
+ }
22014
+ const positive = (name) => {
22015
+ const raw = values.get(name);
22016
+ if (raw === void 0)
22017
+ return void 0;
22018
+ const n = Number(raw);
22019
+ if (!Number.isFinite(n) || n <= 0) {
22020
+ return { kind: "error", message: `--${name} must be a positive number, got '${raw}'` };
22021
+ }
22022
+ return n;
22023
+ };
22024
+ const sinceDays = positive("days");
22025
+ if (typeof sinceDays === "object")
22026
+ return sinceDays;
22027
+ const batch = positive("batch");
22028
+ if (typeof batch === "object")
22029
+ return batch;
22030
+ return {
22031
+ kind: "run",
22032
+ options: {
22033
+ dryRun,
22034
+ paths,
22035
+ ...values.get("profile") !== void 0 ? { profile: values.get("profile") } : {},
22036
+ ...values.get("credentials") !== void 0 ? { credentialsPath: values.get("credentials") } : {},
22037
+ ...sinceDays !== void 0 ? { sinceDays } : {},
22038
+ ...batch !== void 0 ? { batch } : {}
22039
+ }
22040
+ };
22041
+ }
22042
+ function batches(items, size) {
22043
+ const out = [];
22044
+ for (let i = 0; i < items.length; i += size)
22045
+ out.push(items.slice(i, i + size));
22046
+ return out;
22047
+ }
22048
+ async function runBackfill(mapper, options, say2) {
22049
+ const discovered = options.paths.length > 0 ? options.paths : discoverTranscripts(mapper);
22050
+ const paths = options.sinceDays === void 0 ? discovered : withinDays(discovered, options.sinceDays);
22051
+ const window2 = options.sinceDays === void 0 ? "" : ` written to in the last ${options.sinceDays} day(s)`;
22052
+ say2(`${options.dryRun ? "DRY RUN \u2014 nothing will be sent. " : ""}${paths.length} transcript(s)${window2}`);
22053
+ const pass = (subset) => runForwarder(mapper, {
22054
+ paths: subset,
22055
+ dryRun: options.dryRun,
22056
+ ...options.profile !== void 0 ? { profile: options.profile } : {},
22057
+ ...options.credentialsPath !== void 0 ? { credentialsPath: options.credentialsPath } : {}
22058
+ });
22059
+ if (options.dryRun)
22060
+ return pass(paths);
22061
+ if (paths.length === 0)
22062
+ return pass([]);
22063
+ return runPasses(batches(paths, options.batch ?? DEFAULT_BATCH), pass, say2);
22064
+ }
22065
+ async function runPasses(groups, pass, say2) {
22066
+ const total = {
22067
+ transcripts: 0,
22068
+ records: 0,
22069
+ skipped: [],
22070
+ kinds: {},
22071
+ warnings: [],
22072
+ emitted: { transcripts: 0, records: 0 },
22073
+ ok: true
22074
+ };
22075
+ for (const [index, group] of groups.entries()) {
22076
+ const result2 = await pass(group);
22077
+ total.transcripts += result2.transcripts;
22078
+ total.records += result2.records;
22079
+ total.emitted.transcripts += result2.emitted.transcripts;
22080
+ total.emitted.records += result2.emitted.records;
22081
+ total.skipped.push(...result2.skipped);
22082
+ total.warnings.push(...result2.warnings);
22083
+ for (const [kind, n] of Object.entries(result2.kinds)) {
22084
+ total.kinds[kind] = (total.kinds[kind] ?? 0) + n;
22085
+ }
22086
+ if (result2.ok === false) {
22087
+ total.ok = false;
22088
+ if (result2.error !== void 0)
22089
+ total.error = result2.error;
22090
+ say2(`stopped in batch ${index + 1} of ${groups.length}; earlier batches are checkpointed`);
22091
+ break;
22092
+ }
22093
+ if (groups.length > 1) {
22094
+ say2(`batch ${index + 1}/${groups.length}: ${result2.records} record(s)`);
22095
+ }
22096
+ }
22097
+ return total;
22098
+ }
22099
+
21961
22100
  // adapters/codex/dist/transcript.js
21962
22101
  import { readFileSync as readFileSync6 } from "node:fs";
21963
22102
  import { basename, join as join10 } from "node:path";
@@ -22018,15 +22157,15 @@ function budgeted(steps) {
22018
22157
  return kept;
22019
22158
  }
22020
22159
  function execEnvelope(text) {
22021
- let parsed;
22160
+ let parsed2;
22022
22161
  try {
22023
- parsed = JSON.parse(text);
22162
+ parsed2 = JSON.parse(text);
22024
22163
  } catch {
22025
22164
  return void 0;
22026
22165
  }
22027
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
22166
+ if (!parsed2 || typeof parsed2 !== "object" || Array.isArray(parsed2))
22028
22167
  return void 0;
22029
- const bag = parsed;
22168
+ const bag = parsed2;
22030
22169
  if (!Object.prototype.hasOwnProperty.call(bag, "output"))
22031
22170
  return void 0;
22032
22171
  const metadata = bag["metadata"];
@@ -22065,13 +22204,13 @@ function jwtClaims(token) {
22065
22204
  }
22066
22205
  }
22067
22206
  function emailFromAuth(raw) {
22068
- let parsed;
22207
+ let parsed2;
22069
22208
  try {
22070
- parsed = JSON.parse(raw);
22209
+ parsed2 = JSON.parse(raw);
22071
22210
  } catch {
22072
22211
  return void 0;
22073
22212
  }
22074
- const tokens = parsed?.tokens;
22213
+ const tokens = parsed2?.tokens;
22075
22214
  const token = tokens?.id_token;
22076
22215
  if (typeof token !== "string" || token === "")
22077
22216
  return void 0;
@@ -22089,11 +22228,11 @@ function repositoryIdentity(url) {
22089
22228
  return `${scp[1]}/${stripSuffix(scp[2])}`;
22090
22229
  const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `ssh://${trimmed}`;
22091
22230
  try {
22092
- const parsed = new URL(withScheme);
22093
- if (!parsed.hostname)
22231
+ const parsed2 = new URL(withScheme);
22232
+ if (!parsed2.hostname)
22094
22233
  return trimmed;
22095
- const path = stripSuffix(parsed.pathname.replace(/^\/+/, ""));
22096
- return path === "" ? parsed.hostname : `${parsed.hostname}/${path}`;
22234
+ const path = stripSuffix(parsed2.pathname.replace(/^\/+/, ""));
22235
+ return path === "" ? parsed2.hostname : `${parsed2.hostname}/${path}`;
22097
22236
  } catch {
22098
22237
  return trimmed;
22099
22238
  }
@@ -22201,8 +22340,8 @@ var codexTranscript = {
22201
22340
  }
22202
22341
  const kind = str(payload["type"]);
22203
22342
  if (raw.type === "event_msg" && kind === "token_count") {
22204
- const usage = mapTokenCount(payload["info"]);
22205
- return usage ? [{ ...base, kind: "usage", usage }] : [];
22343
+ const usage2 = mapTokenCount(payload["info"]);
22344
+ return usage2 ? [{ ...base, kind: "usage", usage: usage2 }] : [];
22206
22345
  }
22207
22346
  if (raw.type === "compacted") {
22208
22347
  const note = str(payload["message"]);
@@ -22337,33 +22476,21 @@ var codexTranscript = {
22337
22476
  };
22338
22477
 
22339
22478
  // adapters/codex/bin/backfill.mjs
22340
- var argv = process.argv.slice(2);
22341
- var dryRun = argv.includes("--dry-run");
22342
- var explicit = argv.filter((a) => !a.startsWith("--"));
22343
- var flag = (n) => {
22344
- const a = argv.find((x) => x.startsWith(`--${n}=`));
22345
- return a ? a.slice(n.length + 3) : void 0;
22346
- };
22347
- var profile = flag("profile");
22348
- var credentialsPath2 = flag("credentials");
22349
- var sinceDays;
22350
- if (flag("days") !== void 0) {
22351
- sinceDays = Number(flag("days"));
22352
- if (!Number.isFinite(sinceDays) || sinceDays <= 0) {
22353
- process.stderr.write(`darkhunt: --days must be a positive number, got '${flag("days")}'
22354
- `);
22355
- process.exit(1);
22356
- }
22357
- }
22358
- var discovered = explicit.length > 0 ? explicit : discoverTranscripts(codexTranscript);
22359
- var paths = sinceDays === void 0 ? discovered : withinDays(discovered, sinceDays);
22360
- var window2 = sinceDays === void 0 ? "" : ` written to in the last ${sinceDays} day(s)`;
22361
22479
  var say = (m) => process.stderr.write(`darkhunt: ${m}
22362
22480
  `);
22363
- say(`${dryRun ? "DRY RUN \u2014 nothing will be sent. " : ""}${paths.length} transcript(s)${window2}`);
22364
- var result = await runForwarder(codexTranscript, { paths, dryRun, profile, credentialsPath: credentialsPath2 });
22481
+ var parsed = parseBackfillArgs(process.argv.slice(2));
22482
+ if (parsed.kind === "help") {
22483
+ process.stdout.write(usage("backfill.mjs") + "\n");
22484
+ process.exit(0);
22485
+ }
22486
+ if (parsed.kind === "error") {
22487
+ say(parsed.message);
22488
+ process.stderr.write(usage("backfill.mjs") + "\n");
22489
+ process.exit(2);
22490
+ }
22491
+ var result = await runBackfill(codexTranscript, parsed.options, say);
22365
22492
  say(
22366
- `${dryRun ? "mapped" : "shipped"} ${result.records} record(s) from ${result.transcripts} transcript(s)`
22493
+ `${parsed.options.dryRun ? "mapped" : "shipped"} ${result.records} record(s) from ${result.transcripts} transcript(s)`
22367
22494
  );
22368
22495
  for (const [kind, n] of Object.entries(result.kinds).sort((a, b) => b[1] - a[1])) {
22369
22496
  say(` ${kind.padEnd(18)} ${n}`);
@@ -22372,6 +22499,11 @@ for (const skip of result.skipped) say(`skipped ${skip}`);
22372
22499
  for (const warning of result.warnings ?? []) say(`WARNING ${warning}`);
22373
22500
  if (result.ok === false) {
22374
22501
  say(`EXPORT FAILED \u2014 ${result.error}`);
22502
+ if (result.emitted.records > 0) {
22503
+ say(
22504
+ `${result.emitted.records} record(s) from ${result.emitted.transcripts} transcript(s) were sent before the failure and may have been stored`
22505
+ );
22506
+ }
22375
22507
  say("checkpoints were NOT advanced; re-run once the cause is fixed");
22376
22508
  process.exit(1);
22377
22509
  }
@@ -21719,6 +21719,7 @@ async function runForwarder(mapper, options = {}) {
21719
21719
  skipped: ["another forwarder holds the lock"],
21720
21720
  kinds: {},
21721
21721
  warnings: [],
21722
+ emitted: { transcripts: 0, records: 0 },
21722
21723
  ok: true
21723
21724
  };
21724
21725
  }
@@ -21728,6 +21729,7 @@ async function runForwarder(mapper, options = {}) {
21728
21729
  skipped: [],
21729
21730
  kinds: {},
21730
21731
  warnings: [],
21732
+ emitted: { transcripts: 0, records: 0 },
21731
21733
  ok: true
21732
21734
  };
21733
21735
  try {
@@ -21877,6 +21879,7 @@ async function runForwarder(mapper, options = {}) {
21877
21879
  await client.shutdown();
21878
21880
  watch.stop();
21879
21881
  }
21882
+ result.emitted = { transcripts: result.transcripts, records: result.records };
21880
21883
  if (watch.failures() > 0) {
21881
21884
  result.ok = false;
21882
21885
  result.error = watch.reason() ?? "span export failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@darkhunt-security/endpoint-codex",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "type": "module",
5
5
  "description": "Darkhunt endpoint adapter for Codex CLI: hook codec, rollout transcript mapper, plugin manifest.",
6
6
  "bin": {