@akasecurity/ai-tc-claude-code 0.9.7 → 0.9.8

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.
@@ -50,7 +50,7 @@ var require_ignore = __commonJS({
50
50
  var REGEX_SPLITALL_CRLF = /\r?\n/g;
51
51
  var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
52
52
  var REGEX_TEST_TRAILING_SLASH = /\/$/;
53
- var SLASH = "/";
53
+ var SLASH2 = "/";
54
54
  var TMP_KEY_IGNORE = "node-ignore";
55
55
  if (typeof Symbol !== "undefined") {
56
56
  TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
@@ -422,11 +422,11 @@ var require_ignore = __commonJS({
422
422
  if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
423
423
  return this.test(path);
424
424
  }
425
- const slices = path.split(SLASH).filter(Boolean);
425
+ const slices = path.split(SLASH2).filter(Boolean);
426
426
  slices.pop();
427
427
  if (slices.length) {
428
428
  const parent = this._t(
429
- slices.join(SLASH) + SLASH,
429
+ slices.join(SLASH2) + SLASH2,
430
430
  this._testCache,
431
431
  true,
432
432
  slices
@@ -442,14 +442,14 @@ var require_ignore = __commonJS({
442
442
  return cache[path];
443
443
  }
444
444
  if (!slices) {
445
- slices = path.split(SLASH).filter(Boolean);
445
+ slices = path.split(SLASH2).filter(Boolean);
446
446
  }
447
447
  slices.pop();
448
448
  if (!slices.length) {
449
449
  return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
450
450
  }
451
451
  const parent = this._t(
452
- slices.join(SLASH) + SLASH,
452
+ slices.join(SLASH2) + SLASH2,
453
453
  cache,
454
454
  checkUnignored,
455
455
  slices
@@ -491,14 +491,31 @@ var require_ignore = __commonJS({
491
491
  }
492
492
  });
493
493
 
494
- // ../../packages/plugin-sdk/src/config.ts
495
- import { existsSync as existsSync5 } from "fs";
496
- import { join as join8 } from "path";
494
+ // ../../packages/plugin-runtime/src/attached/failure.ts
495
+ function statusOf(err) {
496
+ if (typeof err !== "object" || err === null || !("status" in err)) return null;
497
+ const { status } = err;
498
+ if (typeof status !== "number" || !Number.isInteger(status)) return null;
499
+ return status >= 100 && status <= 599 ? status : null;
500
+ }
501
+ function classifyFailure(err) {
502
+ switch (statusOf(err)) {
503
+ case 401:
504
+ return "unauthorized";
505
+ case 403:
506
+ return "forbidden";
507
+ default:
508
+ return "unreachable";
509
+ }
510
+ }
497
511
 
498
- // ../../packages/persistence/src/database.ts
499
- import { randomUUID as randomUUID10 } from "crypto";
500
- import { join as join2, sep } from "path";
501
- import { DatabaseSync } from "node:sqlite";
512
+ // ../../packages/plugin-runtime/src/attached/forward-drops.ts
513
+ import { readFileSync as readFileSync7 } from "fs";
514
+ import { join as join10 } from "path";
515
+
516
+ // ../../packages/persistence/src/control-plane-credential.ts
517
+ import { chmodSync as chmodSync2, lstatSync as lstatSync2, readFileSync, rmSync as rmSync2, statSync } from "fs";
518
+ import { join } from "path";
502
519
 
503
520
  // ../../packages/schema/src/drizzle/sqlite-ddl.ts
504
521
  var SQLITE_MIGRATIONS = [
@@ -16202,6 +16219,125 @@ var ConfigScanRecord = external_exports.object({
16202
16219
  findings: external_exports.array(ConfigPostureFindingInput).optional()
16203
16220
  });
16204
16221
 
16222
+ // ../../packages/schema/src/zod/control-plane.ts
16223
+ var ATTACHED_CREDENTIAL_FILENAME = "control-plane-credential.json";
16224
+ var ATTACHED_CREDENTIAL_SPEC_VERSION = 1;
16225
+ var AttachedCredential = external_exports.object({
16226
+ specVersion: external_exports.literal(ATTACHED_CREDENTIAL_SPEC_VERSION),
16227
+ // The control-plane endpoint this credential was minted against.
16228
+ endpoint: external_exports.string().min(1),
16229
+ // The bearer credential itself. Never logged, never rendered — status
16230
+ // surfaces show `keyPrefix` and nothing else.
16231
+ apiKey: external_exports.string().min(1),
16232
+ // First few characters of the key, safe to display so a user can match the
16233
+ // credential against their organization's key list.
16234
+ keyPrefix: external_exports.string().min(1).max(16).optional(),
16235
+ mintedAt: external_exports.iso.datetime().optional()
16236
+ });
16237
+ var MAX_DATE_MS = 253402300799999;
16238
+ var MAX_INT4 = 2147483647;
16239
+ var StorePosturePack = external_exports.object({
16240
+ packId: external_exports.string().min(1),
16241
+ // 'namespace/packId'
16242
+ version: external_exports.string().min(1),
16243
+ enabled: external_exports.boolean(),
16244
+ // Stringified pass-through of the local store's `installed_packs.updated_at`
16245
+ // — the column format is store-version-dependent (epoch millis vs ISO), so
16246
+ // the wire shape assumes neither.
16247
+ updatedAt: external_exports.string().nullable()
16248
+ }).meta({ id: "StorePosturePack" });
16249
+ var StorePosturePolicyCounts = external_exports.object({
16250
+ total: external_exports.number().int().min(0),
16251
+ disabled: external_exports.number().int().min(0),
16252
+ // Exhaustive per-action map; the builder pre-fills every action with 0.
16253
+ //
16254
+ // Spelled out member-by-member rather than `z.record(ActionTaken, …)`. Zod
16255
+ // enforces exhaustiveness either way, but z.record emits `propertyNames` +
16256
+ // `additionalProperties` into a generated schema document, and a type
16257
+ // generator renders THAT with every key optional — a sender built against
16258
+ // the generated type would typecheck and still be rejected at runtime. An
16259
+ // explicit object emits `properties` + `required`, so generated types
16260
+ // demand all five.
16261
+ //
16262
+ // `satisfies Record<ActionTaken, …>` keeps the link to the enum: adding an
16263
+ // ActionTaken member is a COMPILE error here instead of silent drift.
16264
+ // `.strict()` is load-bearing — it rejects an unknown action key, which a
16265
+ // bare object would silently STRIP, accepting a miscounted map as valid.
16266
+ byAction: external_exports.object({
16267
+ warn: external_exports.number().int().min(0),
16268
+ redact: external_exports.number().int().min(0),
16269
+ block: external_exports.number().int().min(0),
16270
+ allow: external_exports.number().int().min(0),
16271
+ log: external_exports.number().int().min(0)
16272
+ }).strict()
16273
+ }).meta({ id: "StorePosturePolicyCounts" });
16274
+ var StorePosturePlugin = external_exports.object({
16275
+ /** Package name of the reporting plugin. */
16276
+ package: external_exports.string().min(1).max(200),
16277
+ version: external_exports.string().min(1).max(64),
16278
+ /** Version of the bundled core, when the build records one separately. */
16279
+ ossVersion: external_exports.string().max(64).nullable(),
16280
+ /**
16281
+ * `version` of the policy bundle this machine last fetched. Bounded at 200
16282
+ * rather than the 64 a bare sha256 hex digest needs today, so a later
16283
+ * format with an algorithm prefix does not start rejecting the channel.
16284
+ */
16285
+ policyBundleVersion: external_exports.string().max(200).nullable(),
16286
+ /** Epoch millis, on the CLIENT clock, of that fetch. */
16287
+ policyFetchedAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable()
16288
+ }).meta({ id: "StorePosturePlugin" });
16289
+ var StorePostureSnapshot = external_exports.object({
16290
+ deviceId: external_exports.guid(),
16291
+ hostname: external_exports.string().min(1).max(253),
16292
+ // Epoch millis on the CLIENT clock. Bounded by what a receiving store
16293
+ // accepts (see MAX_DATE_MS), not by what a JavaScript Date can hold.
16294
+ capturedAt: external_exports.number().int().min(0).max(MAX_DATE_MS),
16295
+ // False is a measurement, not an error state: "no local store exists on
16296
+ // this machine".
16297
+ storePresent: external_exports.boolean(),
16298
+ schemaVersion: external_exports.number().int().min(0).max(MAX_INT4).nullable(),
16299
+ // PRAGMA user_version
16300
+ findingsTotal: external_exports.number().int().min(0).max(MAX_INT4),
16301
+ // Epoch millis, bounded like `capturedAt` — see MAX_DATE_MS on what that
16302
+ // bound does and does not do. Worth stating for these two specifically:
16303
+ // they are read from the local store's own ROWS rather than from this
16304
+ // machine's clock, so a damaged or hand-edited store is enough to produce
16305
+ // an out-of-range value with no clock skew involved.
16306
+ findingsFirstAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16307
+ findingsLastAt: external_exports.number().int().min(0).max(MAX_DATE_MS).nullable(),
16308
+ packs: external_exports.array(StorePosturePack).max(500),
16309
+ policyCounts: StorePosturePolicyCounts,
16310
+ // OPTIONAL, not nullable: a reporter that predates this member keeps
16311
+ // getting its 200 without a payload change.
16312
+ plugin: StorePosturePlugin.optional()
16313
+ }).meta({ id: "StorePostureSnapshot" });
16314
+ var CAPTURE_VERSION_PREFIX = "capture/";
16315
+ var RecordAuditEventRequest = AuditEventInput.extend({
16316
+ inspections: external_exports.array(ToolCallInspection).default([])
16317
+ }).refine((v) => v.inspections.every((i) => !i.ruleVersion.startsWith(CAPTURE_VERSION_PREFIX)), {
16318
+ message: `inspections[].ruleVersion must not start with \`${CAPTURE_VERSION_PREFIX}\` \u2014 that namespace is reserved for capture definitions the control plane mints itself`,
16319
+ path: ["inspections"]
16320
+ }).meta({ id: "RecordAuditEventRequest" });
16321
+ var IngestAck = external_exports.object({
16322
+ accepted: external_exports.number().int().nonnegative(),
16323
+ duplicates: external_exports.number().int().nonnegative()
16324
+ });
16325
+ var PRINTABLE = /^[^\p{Cc}\p{Cf}]*$/u;
16326
+ var printable = (max) => external_exports.string().max(max).regex(PRINTABLE, "must not contain control characters");
16327
+ var PluginWhoami = external_exports.object({
16328
+ tenantName: printable(200),
16329
+ userEmail: printable(320),
16330
+ role: printable(64),
16331
+ keyKind: printable(64),
16332
+ serverTime: printable(64)
16333
+ });
16334
+ var ControlPlaneErrorBody = external_exports.object({
16335
+ error: external_exports.object({
16336
+ code: external_exports.string().optional(),
16337
+ message: external_exports.string().optional()
16338
+ }).optional()
16339
+ });
16340
+
16205
16341
  // ../../packages/schema/src/zod/registry.ts
16206
16342
  var Namespace = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
16207
16343
  var PackId = external_exports.string().regex(/^[a-z][a-z0-9-]*$/);
@@ -16504,15 +16640,15 @@ function summaryToDetectionListItem(s) {
16504
16640
  }
16505
16641
  function rowToDetectionDetail(row, findingsLast30d, update) {
16506
16642
  const rules = row.rules.flatMap((r) => {
16507
- const parsed = Matcher.safeParse(r.matcher);
16508
- if (!parsed.success) return [];
16643
+ const parsed2 = Matcher.safeParse(r.matcher);
16644
+ if (!parsed2.success) return [];
16509
16645
  return [
16510
16646
  {
16511
16647
  id: r.id,
16512
16648
  name: r.name,
16513
16649
  category: r.category,
16514
16650
  severity: r.severity,
16515
- matcher: parsed.data
16651
+ matcher: parsed2.data
16516
16652
  }
16517
16653
  ];
16518
16654
  });
@@ -17158,8 +17294,8 @@ function toApiAction(dbVal) {
17158
17294
  }
17159
17295
  function toApiCategory(dbVal) {
17160
17296
  if (dbVal === "code_context") return "source_code";
17161
- const parsed = FindingCategory.safeParse(dbVal);
17162
- return parsed.success ? parsed.data : "custom";
17297
+ const parsed2 = FindingCategory.safeParse(dbVal);
17298
+ return parsed2.success ? parsed2.data : "custom";
17163
17299
  }
17164
17300
  function toApiProvider(sourceTool) {
17165
17301
  return TOOL_TO_HARNESS[sourceTool] ?? HARNESS.Api;
@@ -17786,6 +17922,9 @@ var WorkspaceSettings = external_exports.object({
17786
17922
  function defaultWorkspaceSettings() {
17787
17923
  return WorkspaceSettings.parse({});
17788
17924
  }
17925
+ function isAttached(settings) {
17926
+ return settings.runMode === "attached" && settings.controlPlane !== void 0;
17927
+ }
17789
17928
  function toInventoryRow(input, id, now) {
17790
17929
  return {
17791
17930
  id,
@@ -18053,8 +18192,8 @@ function builtinPolicyIsReversible(id) {
18053
18192
  return BUILTIN_POLICY_SPECS[id].reversible;
18054
18193
  }
18055
18194
  function policyIdIsReversible(policyId) {
18056
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18057
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18195
+ const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18196
+ const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
18058
18197
  return builtinPolicyIsReversible(id);
18059
18198
  }
18060
18199
  var DEFAULT_ACTIONS = Object.fromEntries(
@@ -18065,8 +18204,8 @@ var BUILTIN_POLICIES = Object.fromEntries(
18065
18204
  );
18066
18205
  var DEFAULT_PACK_POLICY_ID = "monitor";
18067
18206
  function policyIdToAction(policyId) {
18068
- const parsed = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18069
- const id = parsed.success ? parsed.data : DEFAULT_PACK_POLICY_ID;
18207
+ const parsed2 = BuiltinPolicyId.safeParse(policyId ?? DEFAULT_PACK_POLICY_ID);
18208
+ const id = parsed2.success ? parsed2.data : DEFAULT_PACK_POLICY_ID;
18070
18209
  return BUILTIN_POLICIES[id].action;
18071
18210
  }
18072
18211
  var UsedByItem = external_exports.object({
@@ -18509,53 +18648,6 @@ function reviewSeverityRank(reasons) {
18509
18648
  return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
18510
18649
  }
18511
18650
 
18512
- // ../../packages/persistence/src/ids.ts
18513
- import { createHash } from "crypto";
18514
- function sha256Hex(input) {
18515
- return createHash("sha256").update(input).digest("hex");
18516
- }
18517
- function inventoryId(objectType, identityKey) {
18518
- return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18519
- }
18520
- function sourceProjectId(url2) {
18521
- return sha256Hex(canonicalIdentity(["source_project", url2]));
18522
- }
18523
- function classifiedDataId(cls) {
18524
- return sha256Hex(canonicalIdentity(["classified_data", cls]));
18525
- }
18526
- function inspectionDefinitionId(ruleId, version2) {
18527
- return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18528
- }
18529
- function llmCallId(sessionId, messageId) {
18530
- return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18531
- }
18532
- function toolCallId(sessionId, toolUseId) {
18533
- return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18534
- }
18535
- function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
18536
- return sha256Hex(
18537
- canonicalIdentity([
18538
- "inspection_finding",
18539
- auditEventId,
18540
- ruleId,
18541
- String(spanStart),
18542
- String(spanEnd)
18543
- ])
18544
- );
18545
- }
18546
- var NO_SESSION = "no_session";
18547
- var NO_PATH = "no_path";
18548
- function captureId(sessionId, contentHash, filePath = null) {
18549
- return sha256Hex(
18550
- canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18551
- );
18552
- }
18553
-
18554
- // ../../packages/persistence/src/internal/snapshot.ts
18555
- import { randomUUID } from "crypto";
18556
- import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync2, statSync } from "fs";
18557
- import { basename, dirname, join } from "path";
18558
-
18559
18651
  // ../../packages/persistence/src/paths.ts
18560
18652
  import {
18561
18653
  chmodSync,
@@ -18603,8 +18695,181 @@ function tightenFile(file2) {
18603
18695
  function tightenPerms(file2) {
18604
18696
  for (const path of [file2, ...dbSidecars(file2)]) chmodBestEffort(path, DATA_FILE_MODE);
18605
18697
  }
18698
+ function writeExclusiveOwnerOnlySync(file2, data) {
18699
+ writeFileSync(file2, data, { mode: DATA_FILE_MODE, flag: "wx" });
18700
+ }
18701
+ function writeOwnerOnlyFileSync(file2, data) {
18702
+ const tmp = `${file2}.${String(process.pid)}.tmp`;
18703
+ try {
18704
+ rmSync(tmp, { force: true });
18705
+ } catch {
18706
+ }
18707
+ try {
18708
+ writeExclusiveOwnerOnlySync(tmp, data);
18709
+ renameSync(tmp, file2);
18710
+ } finally {
18711
+ try {
18712
+ rmSync(tmp, { force: true });
18713
+ } catch {
18714
+ }
18715
+ }
18716
+ tightenFile(file2);
18717
+ }
18718
+ function createOwnerOnlyFileSync(file2, data) {
18719
+ const tmp = `${file2}.${String(process.pid)}.${String(threadId)}.new`;
18720
+ try {
18721
+ rmSync(tmp, { force: true });
18722
+ } catch {
18723
+ }
18724
+ let created;
18725
+ try {
18726
+ writeExclusiveOwnerOnlySync(tmp, data);
18727
+ created = publishByLink(tmp, file2, data);
18728
+ } finally {
18729
+ try {
18730
+ rmSync(tmp, { force: true });
18731
+ } catch {
18732
+ }
18733
+ }
18734
+ if (created) tightenFile(file2);
18735
+ return created;
18736
+ }
18737
+ var LINK_UNSUPPORTED = /* @__PURE__ */ new Set(["EPERM", "ENOSYS", "ENOTSUP", "EOPNOTSUPP", "EINVAL"]);
18738
+ function publishByLink(tmp, file2, data) {
18739
+ try {
18740
+ linkSync(tmp, file2);
18741
+ return true;
18742
+ } catch (err) {
18743
+ const code = err.code;
18744
+ if (code === "EEXIST") return false;
18745
+ if (!LINK_UNSUPPORTED.has(code ?? "")) throw err;
18746
+ }
18747
+ try {
18748
+ writeExclusiveOwnerOnlySync(file2, data);
18749
+ return true;
18750
+ } catch (err) {
18751
+ if (err.code === "EEXIST") return false;
18752
+ throw err;
18753
+ }
18754
+ }
18755
+
18756
+ // ../../packages/persistence/src/control-plane-credential.ts
18757
+ function controlPlaneCredentialPath(settingsDir2) {
18758
+ return join(settingsDir2, ATTACHED_CREDENTIAL_FILENAME);
18759
+ }
18760
+ var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
18761
+ function isSafeEndpoint(endpoint) {
18762
+ let parsed2;
18763
+ try {
18764
+ parsed2 = new URL(endpoint);
18765
+ } catch {
18766
+ return false;
18767
+ }
18768
+ if (parsed2.protocol === "https:") return true;
18769
+ return parsed2.protocol === "http:" && LOOPBACK_HOSTS.has(parsed2.hostname);
18770
+ }
18771
+ function repairOrRefuseMode(file2) {
18772
+ const link = lstatSync2(file2, { throwIfNoEntry: false });
18773
+ if (link === void 0) return "absent";
18774
+ if (link.isSymbolicLink()) return "untrusted";
18775
+ const stat = statSync(file2, { throwIfNoEntry: false });
18776
+ if (stat === void 0) return "absent";
18777
+ const uid = process.getuid?.();
18778
+ if (uid !== void 0 && stat.uid !== uid) return "untrusted";
18779
+ if (process.platform !== "win32" && (stat.mode & 511) !== DATA_FILE_MODE) {
18780
+ try {
18781
+ chmodSync2(file2, DATA_FILE_MODE);
18782
+ } catch {
18783
+ return "untrusted";
18784
+ }
18785
+ }
18786
+ return "ok";
18787
+ }
18788
+ function readControlPlaneCredentialState(settingsDir2, connection) {
18789
+ const file2 = controlPlaneCredentialPath(settingsDir2);
18790
+ let raw;
18791
+ const gate = repairOrRefuseMode(file2);
18792
+ if (gate === "absent") return { usable: false, reason: "absent" };
18793
+ if (gate === "untrusted") return { usable: false, reason: "untrusted-file" };
18794
+ try {
18795
+ raw = readFileSync(file2, "utf8");
18796
+ } catch (err) {
18797
+ const code = err.code;
18798
+ return { usable: false, reason: code === "ENOENT" ? "absent" : "unreadable" };
18799
+ }
18800
+ let parsed2;
18801
+ try {
18802
+ parsed2 = JSON.parse(raw);
18803
+ } catch {
18804
+ return { usable: false, reason: "malformed" };
18805
+ }
18806
+ const result = AttachedCredential.safeParse(parsed2);
18807
+ if (!result.success) return { usable: false, reason: "malformed" };
18808
+ if (!isSafeEndpoint(result.data.endpoint)) {
18809
+ return { usable: false, reason: "unsafe-endpoint" };
18810
+ }
18811
+ if (connection !== void 0 && connection.endpoint !== result.data.endpoint) {
18812
+ return {
18813
+ usable: false,
18814
+ reason: "endpoint-mismatch",
18815
+ credentialEndpoint: result.data.endpoint,
18816
+ settingsEndpoint: connection.endpoint
18817
+ };
18818
+ }
18819
+ return { usable: true, credential: result.data };
18820
+ }
18821
+
18822
+ // ../../packages/persistence/src/database.ts
18823
+ import { randomUUID as randomUUID10 } from "crypto";
18824
+ import { join as join3, sep } from "path";
18825
+ import { DatabaseSync } from "node:sqlite";
18826
+
18827
+ // ../../packages/persistence/src/ids.ts
18828
+ import { createHash } from "crypto";
18829
+ function sha256Hex(input) {
18830
+ return createHash("sha256").update(input).digest("hex");
18831
+ }
18832
+ function inventoryId(objectType, identityKey) {
18833
+ return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
18834
+ }
18835
+ function sourceProjectId(url2) {
18836
+ return sha256Hex(canonicalIdentity(["source_project", url2]));
18837
+ }
18838
+ function classifiedDataId(cls) {
18839
+ return sha256Hex(canonicalIdentity(["classified_data", cls]));
18840
+ }
18841
+ function inspectionDefinitionId(ruleId, version2) {
18842
+ return sha256Hex(canonicalIdentity(["inspection_definition", ruleId, version2]));
18843
+ }
18844
+ function llmCallId(sessionId, messageId) {
18845
+ return sha256Hex(canonicalIdentity(["audit_event_llm_call", sessionId, messageId]));
18846
+ }
18847
+ function toolCallId(sessionId, toolUseId) {
18848
+ return sha256Hex(canonicalIdentity(["audit_event_tool_call", sessionId, toolUseId]));
18849
+ }
18850
+ function inspectionFindingId(auditEventId, ruleId, spanStart, spanEnd) {
18851
+ return sha256Hex(
18852
+ canonicalIdentity([
18853
+ "inspection_finding",
18854
+ auditEventId,
18855
+ ruleId,
18856
+ String(spanStart),
18857
+ String(spanEnd)
18858
+ ])
18859
+ );
18860
+ }
18861
+ var NO_SESSION = "no_session";
18862
+ var NO_PATH = "no_path";
18863
+ function captureId(sessionId, contentHash, filePath = null) {
18864
+ return sha256Hex(
18865
+ canonicalIdentity(["capture", sessionId ?? NO_SESSION, contentHash, filePath ?? NO_PATH])
18866
+ );
18867
+ }
18606
18868
 
18607
18869
  // ../../packages/persistence/src/internal/snapshot.ts
18870
+ import { randomUUID } from "crypto";
18871
+ import { existsSync, readdirSync, renameSync as renameSync2, rmSync as rmSync3, statSync as statSync2 } from "fs";
18872
+ import { basename, dirname, join as join2 } from "path";
18608
18873
  function backupPath(file2, tag) {
18609
18874
  return `${file2}.${tag}.${String(Date.now())}.${randomUUID().slice(0, 8)}.bak`;
18610
18875
  }
@@ -18614,15 +18879,15 @@ var STAGED_NAME_SUFFIX = `.bak${SNAPSHOT_STAGING_SUFFIX}`;
18614
18879
  var SNAPSHOT_STAGING_COPY = "copy";
18615
18880
  function createSnapshotStaging(backup) {
18616
18881
  const stage = `${backup}${SNAPSHOT_STAGING_SUFFIX}`;
18617
- rmSync2(stage, { recursive: true, force: true });
18882
+ rmSync3(stage, { recursive: true, force: true });
18618
18883
  mkdirOwnerOnlySync(stage);
18619
18884
  tightenDir(stage);
18620
- return { stage, copy: join(stage, SNAPSHOT_STAGING_COPY) };
18885
+ return { stage, copy: join2(stage, SNAPSHOT_STAGING_COPY) };
18621
18886
  }
18622
18887
  function idleMs(entry) {
18623
- for (const candidate of [join(entry, SNAPSHOT_STAGING_COPY), entry]) {
18888
+ for (const candidate of [join2(entry, SNAPSHOT_STAGING_COPY), entry]) {
18624
18889
  try {
18625
- return Date.now() - statSync(candidate).mtimeMs;
18890
+ return Date.now() - statSync2(candidate).mtimeMs;
18626
18891
  } catch {
18627
18892
  }
18628
18893
  }
@@ -18639,11 +18904,11 @@ function reapStalePartials(file2) {
18639
18904
  }
18640
18905
  for (const name of entries) {
18641
18906
  if (!name.startsWith(prefix) || !name.endsWith(STAGED_NAME_SUFFIX)) continue;
18642
- const staging = join(dir, name);
18907
+ const staging = join2(dir, name);
18643
18908
  try {
18644
18909
  const idle = idleMs(staging);
18645
18910
  if (idle !== null && idle > STALE_PARTIAL_MS) {
18646
- rmSync2(staging, { recursive: true, force: true });
18911
+ rmSync3(staging, { recursive: true, force: true });
18647
18912
  }
18648
18913
  } catch {
18649
18914
  }
@@ -18657,13 +18922,13 @@ function snapshotStore(db, backup) {
18657
18922
  renameSync2(copy, backup);
18658
18923
  } catch (error51) {
18659
18924
  try {
18660
- rmSync2(stage, { recursive: true, force: true });
18925
+ rmSync3(stage, { recursive: true, force: true });
18661
18926
  } catch {
18662
18927
  }
18663
18928
  throw error51;
18664
18929
  }
18665
18930
  try {
18666
- rmSync2(stage, { recursive: true, force: true });
18931
+ rmSync3(stage, { recursive: true, force: true });
18667
18932
  } catch {
18668
18933
  }
18669
18934
  }
@@ -18678,7 +18943,7 @@ function moveStoreAside(file2, backup) {
18678
18943
  renameSync2(sidecar, moved);
18679
18944
  undo.push([moved, sidecar]);
18680
18945
  } catch {
18681
- rmSync2(sidecar, { force: true });
18946
+ rmSync3(sidecar, { force: true });
18682
18947
  }
18683
18948
  }
18684
18949
  } catch (error51) {
@@ -18694,14 +18959,14 @@ function moveStoreAside(file2, backup) {
18694
18959
  }
18695
18960
  function discardStore(file2, backup) {
18696
18961
  try {
18697
- rmSync2(file2, { force: true });
18962
+ rmSync3(file2, { force: true });
18698
18963
  for (const sidecar of dbSidecars(file2)) {
18699
- rmSync2(sidecar, { force: true });
18964
+ rmSync3(sidecar, { force: true });
18700
18965
  }
18701
18966
  } catch (error51) {
18702
18967
  if (existsSync(file2)) {
18703
18968
  try {
18704
- rmSync2(backup, { force: true });
18969
+ rmSync3(backup, { force: true });
18705
18970
  } catch {
18706
18971
  }
18707
18972
  }
@@ -18933,10 +19198,31 @@ function applyMigrations(db, file2) {
18933
19198
  if (drained) applyLegacyDropMigration(db, file2);
18934
19199
  }
18935
19200
  }
19201
+ function readLegacyTables(db) {
19202
+ let holdsRows = false;
19203
+ const marks = [];
19204
+ for (const table2 of ["events", "findings"]) {
19205
+ try {
19206
+ const row = db.prepare(`SELECT count(*) AS n, ifnull(max(rowid), -1) AS hi FROM ${table2}`).get();
19207
+ if (row === void 0) {
19208
+ holdsRows = true;
19209
+ marks.push(`${table2}:unreadable`);
19210
+ continue;
19211
+ }
19212
+ if (row.n > 0) holdsRows = true;
19213
+ marks.push(`${table2}:${String(row.n)}:${String(row.hi)}`);
19214
+ } catch {
19215
+ holdsRows = true;
19216
+ marks.push(`${table2}:unreadable`);
19217
+ }
19218
+ }
19219
+ return { holdsRows, mark: marks.join("|") };
19220
+ }
18936
19221
  function applyLegacyDropMigration(db, file2) {
18937
19222
  const migration = SQLITE_MIGRATIONS.find((m) => m.tag === LEGACY_DROP_MIGRATION_TAG);
18938
19223
  if (!migration) return;
18939
- if (file2) {
19224
+ const before = file2 === void 0 ? void 0 : readLegacyTables(db);
19225
+ if (file2 !== void 0 && before?.holdsRows === true) {
18940
19226
  try {
18941
19227
  backupBeforeLegacyDrop(db, file2);
18942
19228
  } catch (error51) {
@@ -18950,6 +19236,12 @@ function applyLegacyDropMigration(db, file2) {
18950
19236
  () => {
18951
19237
  const alreadyDropped = db.prepare("SELECT 1 FROM migration_ledger WHERE tag = ?").get(migration.tag);
18952
19238
  if (alreadyDropped) return;
19239
+ if (before !== void 0 && readLegacyTables(db).mark !== before.mark) {
19240
+ akaWarn(
19241
+ "legacy events/findings rows changed after the pre-drop snapshot decision; deferring the drop so the next open can copy them first."
19242
+ );
19243
+ return;
19244
+ }
18953
19245
  for (const statement of splitStatements(migration.sql)) {
18954
19246
  db.exec(statement);
18955
19247
  }
@@ -19304,8 +19596,8 @@ function safeJson(s, fallback) {
19304
19596
  function parseJsonObject(s) {
19305
19597
  if (s == null) return void 0;
19306
19598
  try {
19307
- const parsed = JSON.parse(s);
19308
- if (typeof parsed === "object" && parsed !== null) return parsed;
19599
+ const parsed2 = JSON.parse(s);
19600
+ if (typeof parsed2 === "object" && parsed2 !== null) return parsed2;
19309
19601
  } catch {
19310
19602
  }
19311
19603
  return void 0;
@@ -19316,16 +19608,16 @@ function encodeKeysetCursor(payload) {
19316
19608
  return Buffer.from(JSON.stringify(payload)).toString("base64url");
19317
19609
  }
19318
19610
  function decodeKeysetCursor(cursor) {
19319
- const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
19320
- if (parsed !== void 0 && "startedAtMs" in parsed && "id" in parsed && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
19611
+ const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
19612
+ if (parsed2 !== void 0 && "startedAtMs" in parsed2 && "id" in parsed2 && // `Number.isInteger`, not `typeof === 'number'`. Every timestamp this
19321
19613
  // resumes from is epoch millis, and a payload carrying ±Infinity or a
19322
19614
  // fraction binds cleanly rather than failing — returning an EMPTY page with
19323
19615
  // a null cursor, which a caller reads as "end of list". That is the one
19324
19616
  // outcome a cursor that does not decode must never produce, since the
19325
19617
  // documented behaviour above is to restart from the top. (`1e999` is valid
19326
19618
  // JSON and parses to Infinity; a bare `NaN` is not, so it cannot arrive.)
19327
- Number.isInteger(parsed.startedAtMs) && typeof parsed.id === "string") {
19328
- return parsed;
19619
+ Number.isInteger(parsed2.startedAtMs) && typeof parsed2.id === "string") {
19620
+ return parsed2;
19329
19621
  }
19330
19622
  return null;
19331
19623
  }
@@ -19390,18 +19682,18 @@ var DB_EVENT_TYPE_TO_KIND = {
19390
19682
  };
19391
19683
  function safeParseStringArray(raw) {
19392
19684
  if (!raw) return [];
19393
- const parsed = safeJson(raw, null);
19394
- return Array.isArray(parsed) ? parsed : [];
19685
+ const parsed2 = safeJson(raw, null);
19686
+ return Array.isArray(parsed2) ? parsed2 : [];
19395
19687
  }
19396
19688
  var DEFAULT_HARNESS = HARNESS.ClaudeCode;
19397
19689
  function toHarness(raw) {
19398
- const parsed = Harness.safeParse(raw);
19399
- return parsed.success ? parsed.data : DEFAULT_HARNESS;
19690
+ const parsed2 = Harness.safeParse(raw);
19691
+ return parsed2.success ? parsed2.data : DEFAULT_HARNESS;
19400
19692
  }
19401
19693
  function resolveLifecycle(row, lastActivityMs, nowMs) {
19402
19694
  if (row.status) {
19403
- const parsed = SessionStatus.safeParse(row.status);
19404
- if (parsed.success) return { status: parsed.data, endedAtMs: row.ended_at };
19695
+ const parsed2 = SessionStatus.safeParse(row.status);
19696
+ if (parsed2.success) return { status: parsed2.data, endedAtMs: row.ended_at };
19405
19697
  }
19406
19698
  if (row.ended_at !== null) return { status: "completed", endedAtMs: row.ended_at };
19407
19699
  if (lastActivityMs >= nowMs - LIVE_ACTIVITY_WINDOW_MS) {
@@ -20360,9 +20652,9 @@ var SqliteDetectionsRepository = class {
20360
20652
  const ruleIds = /* @__PURE__ */ new Set();
20361
20653
  for (const r of rows) {
20362
20654
  if (intToBool(r.enabled)) active += 1;
20363
- const parsed = parseRules(r.rulesJson);
20364
- rules += parsed.length;
20365
- for (const rule of parsed) {
20655
+ const parsed2 = parseRules(r.rulesJson);
20656
+ rules += parsed2.length;
20657
+ for (const rule of parsed2) {
20366
20658
  if (typeof rule.id === "string") ruleIds.add(rule.id);
20367
20659
  }
20368
20660
  }
@@ -20896,12 +21188,12 @@ function encodeGroupCursor(group) {
20896
21188
  return Buffer.from(JSON.stringify(payload)).toString("base64url");
20897
21189
  }
20898
21190
  function decodeGroupCursor(cursor) {
20899
- const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
20900
- if (parsed !== void 0 && typeof parsed.sev === "string" && typeof parsed.t === "string" && typeof parsed.id === "string") {
21191
+ const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
21192
+ if (parsed2 !== void 0 && typeof parsed2.sev === "string" && typeof parsed2.t === "string" && typeof parsed2.id === "string") {
20901
21193
  return {
20902
- severity: parsed.sev,
20903
- latestDetectedAt: parsed.t,
20904
- id: parsed.id
21194
+ severity: parsed2.sev,
21195
+ latestDetectedAt: parsed2.t,
21196
+ id: parsed2.id
20905
21197
  };
20906
21198
  }
20907
21199
  return null;
@@ -22035,16 +22327,16 @@ var SqliteInstalledPacksRepository = class {
22035
22327
  continue;
22036
22328
  }
22037
22329
  for (const entry of raw) {
22038
- const parsed = Rule.safeParse(entry);
22039
- if (parsed.success) {
22040
- out.rules.push(parsed.data);
22041
- out.ruleActions.set(parsed.data.id, action);
22042
- out.ruleVersions.set(parsed.data.id, row.version);
22043
- if (reversible) out.reversibleRules.add(parsed.data.id);
22044
- else out.reversibleRules.delete(parsed.data.id);
22330
+ const parsed2 = Rule.safeParse(entry);
22331
+ if (parsed2.success) {
22332
+ out.rules.push(parsed2.data);
22333
+ out.ruleActions.set(parsed2.data.id, action);
22334
+ out.ruleVersions.set(parsed2.data.id, row.version);
22335
+ if (reversible) out.reversibleRules.add(parsed2.data.id);
22336
+ else out.reversibleRules.delete(parsed2.data.id);
22045
22337
  } else {
22046
22338
  out.invalidRules += 1;
22047
- reject(pack, printableRuleId(entry), firstIssueReason(parsed.error));
22339
+ reject(pack, printableRuleId(entry), firstIssueReason(parsed2.error));
22048
22340
  }
22049
22341
  }
22050
22342
  }
@@ -23434,15 +23726,15 @@ function encodeReuseCursor(payload) {
23434
23726
  return Buffer.from(JSON.stringify(payload)).toString("base64url");
23435
23727
  }
23436
23728
  function decodeReuseCursor(cursor) {
23437
- const parsed = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
23438
- if (parsed !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
23729
+ const parsed2 = parseJsonObject(Buffer.from(cursor, "base64url").toString("utf8"));
23730
+ if (parsed2 !== void 0 && // `Number.isInteger`, not `typeof === 'number'`: a payload carrying
23439
23731
  // ±Infinity or a fraction binds cleanly and returns an EMPTY page with a
23440
23732
  // null cursor, which the caller reads as "end of list" — the one outcome a
23441
23733
  // malformed cursor must never produce, since restarting from the top is the
23442
23734
  // documented behaviour and the only recoverable one. (`1e999` is valid JSON
23443
23735
  // and parses to Infinity; a bare `NaN` is not, so it cannot arrive here.)
23444
- Number.isInteger(parsed.occurrences) && typeof parsed.pointerId === "string") {
23445
- return { occurrences: parsed.occurrences, pointerId: parsed.pointerId };
23736
+ Number.isInteger(parsed2.occurrences) && typeof parsed2.pointerId === "string") {
23737
+ return { occurrences: parsed2.occurrences, pointerId: parsed2.pointerId };
23446
23738
  }
23447
23739
  return null;
23448
23740
  }
@@ -25171,7 +25463,7 @@ function openAndInitialize(file2) {
25171
25463
  }
25172
25464
  function openLocalDatabase(dir) {
25173
25465
  ensureDataDirSync(dir);
25174
- const file2 = join2(dir, DB_FILENAME);
25466
+ const file2 = join3(dir, DB_FILENAME);
25175
25467
  reapStalePartials(file2);
25176
25468
  const {
25177
25469
  db,
@@ -25407,9 +25699,9 @@ import {
25407
25699
  closeSync,
25408
25700
  existsSync as existsSync2,
25409
25701
  openSync,
25410
- readFileSync,
25411
- rmSync as rmSync3,
25412
- statSync as statSync2,
25702
+ readFileSync as readFileSync2,
25703
+ rmSync as rmSync4,
25704
+ statSync as statSync3,
25413
25705
  writeFileSync as writeFileSync2
25414
25706
  } from "fs";
25415
25707
  import { hostname as hostname3 } from "os";
@@ -25420,20 +25712,20 @@ import { createHash as createHash3 } from "crypto";
25420
25712
 
25421
25713
  // ../../packages/persistence/src/fingerprint.ts
25422
25714
  import { createHmac, randomBytes } from "crypto";
25423
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
25424
- import { join as join3 } from "path";
25715
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
25716
+ import { join as join4 } from "path";
25425
25717
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
25426
25718
  var EXCEPTION_KEY_FILENAME = "exception.key";
25427
25719
  var KEY_MATERIAL_BYTES = 32;
25428
25720
  function keyFilePath(dataDir2) {
25429
- return join3(dataDir2, EXCEPTION_KEY_FILENAME);
25721
+ return join4(dataDir2, EXCEPTION_KEY_FILENAME);
25430
25722
  }
25431
25723
  function parseKeyFile(raw) {
25432
- const parsed = JSON.parse(raw);
25433
- if (typeof parsed !== "object" || parsed === null) {
25724
+ const parsed2 = JSON.parse(raw);
25725
+ if (typeof parsed2 !== "object" || parsed2 === null) {
25434
25726
  throw new Error("exception key file is corrupt: not a JSON object");
25435
25727
  }
25436
- const { version: version2, material } = parsed;
25728
+ const { version: version2, material } = parsed2;
25437
25729
  if (typeof version2 !== "number" || !Number.isInteger(version2) || version2 < 1) {
25438
25730
  throw new Error("exception key file is corrupt: bad version");
25439
25731
  }
@@ -25449,7 +25741,7 @@ function parseKeyFile(raw) {
25449
25741
  function readFingerprintKey(dataDir2) {
25450
25742
  let raw;
25451
25743
  try {
25452
- raw = readFileSync2(keyFilePath(dataDir2), "utf8");
25744
+ raw = readFileSync3(keyFilePath(dataDir2), "utf8");
25453
25745
  } catch (err) {
25454
25746
  if (err.code === "ENOENT") return null;
25455
25747
  throw err instanceof Error ? err : new Error(String(err));
@@ -25461,18 +25753,22 @@ function readFingerprintKey(dataDir2) {
25461
25753
  import { renameSync as renameSync3 } from "fs";
25462
25754
  import { mkdir } from "fs/promises";
25463
25755
  import { homedir } from "os";
25464
- import { join as join4 } from "path";
25756
+ import { join as join5 } from "path";
25465
25757
  function defaultDataDir() {
25466
- return join4(homedir(), ".aka");
25758
+ return join5(homedir(), ".aka");
25467
25759
  }
25468
25760
  function settingsDir(base = defaultDataDir()) {
25469
- return join4(base, "settings");
25761
+ return join5(base, "settings");
25470
25762
  }
25471
25763
  function dataDir(base = defaultDataDir()) {
25472
- return join4(base, "data");
25764
+ return join5(base, "data");
25473
25765
  }
25474
25766
  function dbPath(base = defaultDataDir()) {
25475
- return join4(dataDir(base), "aka.db");
25767
+ return join5(dataDir(base), "aka.db");
25768
+ }
25769
+ async function ensureDataDir(dir = defaultDataDir()) {
25770
+ await mkdir(dir, { recursive: true, mode: DATA_DIR_MODE });
25771
+ tightenDir(dir);
25476
25772
  }
25477
25773
  function ensureLayoutDirSync(dir = defaultDataDir()) {
25478
25774
  ensureDataDirSync(dir);
@@ -25485,8 +25781,8 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25485
25781
  for (const { name, dest } of moves) {
25486
25782
  try {
25487
25783
  ensureDataDirSync(dest);
25488
- const moved = join4(dest, name);
25489
- renameSync3(join4(base, name), moved);
25784
+ const moved = join5(dest, name);
25785
+ renameSync3(join5(base, name), moved);
25490
25786
  tightenFile(moved);
25491
25787
  } catch {
25492
25788
  }
@@ -25494,7 +25790,7 @@ function migrateLegacyLayout(base = defaultDataDir()) {
25494
25790
  }
25495
25791
 
25496
25792
  // ../../packages/persistence/src/managed-settings.ts
25497
- import { readFileSync as readFileSync3 } from "fs";
25793
+ import { readFileSync as readFileSync4 } from "fs";
25498
25794
  import { posix, win32 } from "path";
25499
25795
  function managedSettingsPaths(platform2 = process.platform) {
25500
25796
  if (platform2 === "darwin") {
@@ -25512,14 +25808,14 @@ function readManagedSettings(paths = managedSettingsPaths()) {
25512
25808
  for (const path of paths) {
25513
25809
  let text;
25514
25810
  try {
25515
- text = readFileSync3(path, "utf8");
25811
+ text = readFileSync4(path, "utf8");
25516
25812
  } catch {
25517
25813
  continue;
25518
25814
  }
25519
25815
  const record2 = parseJsonObject(text);
25520
25816
  if (!record2) continue;
25521
- const parsed = ManagedSettings.safeParse(record2);
25522
- if (parsed.success) return parsed.data;
25817
+ const parsed2 = ManagedSettings.safeParse(record2);
25818
+ if (parsed2.success) return parsed2.data;
25523
25819
  }
25524
25820
  return null;
25525
25821
  }
@@ -25559,14 +25855,14 @@ function overlayManagedSettings(settings, managed, now = () => /* @__PURE__ */ n
25559
25855
  }
25560
25856
 
25561
25857
  // ../../packages/persistence/src/settings.ts
25562
- import { readFileSync as readFileSync4 } from "fs";
25563
- import { join as join5 } from "path";
25858
+ import { readFileSync as readFileSync5 } from "fs";
25859
+ import { join as join6 } from "path";
25564
25860
  var SETTINGS_FILENAME = "settings.json";
25565
25861
  function readWorkspaceSettings(base = defaultDataDir()) {
25566
25862
  return overlayManagedSettings(readUserSettings(base), readManagedSettings());
25567
25863
  }
25568
25864
  function readUserSettings(base) {
25569
- const record2 = readJson(join5(settingsDir(base), SETTINGS_FILENAME));
25865
+ const record2 = readJson(join6(settingsDir(base), SETTINGS_FILENAME));
25570
25866
  if (!record2) return defaultWorkspaceSettings();
25571
25867
  try {
25572
25868
  return WorkspaceSettings.parse(record2);
@@ -25577,13 +25873,17 @@ function readUserSettings(base) {
25577
25873
  function readJson(file2) {
25578
25874
  let text;
25579
25875
  try {
25580
- text = readFileSync4(file2, "utf8");
25876
+ text = readFileSync5(file2, "utf8");
25581
25877
  } catch {
25582
25878
  return null;
25583
25879
  }
25584
25880
  return parseJsonObject(text) ?? null;
25585
25881
  }
25586
25882
 
25883
+ // ../../packages/persistence/src/store-symlinks.ts
25884
+ import { existsSync as existsSync4, lstatSync as lstatSync3, readlinkSync, realpathSync, statSync as statSync4 } from "fs";
25885
+ import { dirname as dirname2, join as join7, resolve } from "path";
25886
+
25587
25887
  // ../../packages/persistence/src/vault/crypto.ts
25588
25888
  import {
25589
25889
  createCipheriv,
@@ -25596,26 +25896,73 @@ import {
25596
25896
  // ../../packages/persistence/src/vault/key-provider.ts
25597
25897
  import { execFileSync } from "child_process";
25598
25898
  import { randomBytes as randomBytes2 } from "crypto";
25599
- import { chmodSync as chmodSync2, readFileSync as readFileSync5, renameSync as renameSync4, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
25600
- import { join as join6 } from "path";
25899
+ import { chmodSync as chmodSync3, readFileSync as readFileSync6, renameSync as renameSync4, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync3 } from "fs";
25900
+ import { join as join8 } from "path";
25601
25901
 
25602
25902
  // ../../packages/persistence/src/vault/vault.ts
25603
25903
  import { randomBytes as randomBytes3, randomUUID as randomUUID12 } from "crypto";
25604
25904
 
25605
25905
  // ../../packages/persistence/src/warn-era-cap.ts
25606
- import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "fs";
25607
- import { join as join7 } from "path";
25906
+ import { existsSync as existsSync5, writeFileSync as writeFileSync4 } from "fs";
25907
+ import { join as join9 } from "path";
25608
25908
  var MARKER = "warn-era-capped";
25609
25909
  function capWarnEraEnforcementOnce(db, policyMode, dataDir2) {
25610
25910
  if (policyMode !== "warn") return { capped: 0, skipped: "not-warn" };
25611
- const marker = join7(dataDir2, MARKER);
25612
- if (existsSync4(marker)) return { capped: 0, skipped: "already-run" };
25911
+ const marker = join9(dataDir2, MARKER);
25912
+ if (existsSync5(marker)) return { capped: 0, skipped: "already-run" };
25613
25913
  const capped = db.policies.capCategoryActions();
25614
25914
  writeFileSync4(marker, `${new Date(Date.now()).toISOString()}
25615
25915
  `, { mode: DATA_FILE_MODE });
25616
25916
  return { capped };
25617
25917
  }
25618
25918
 
25919
+ // ../../packages/plugin-runtime/src/attached/forward-drops.ts
25920
+ var FORWARD_DROPS_FILENAME = "attached-forward-drops.json";
25921
+ function forwardDropsPath(dataDir2) {
25922
+ return join10(dataDir2, FORWARD_DROPS_FILENAME);
25923
+ }
25924
+ function recordForwardDrops(dataDir2, count, nowMs) {
25925
+ if (count <= 0) return;
25926
+ try {
25927
+ ensureDataDirSync(dataDir2);
25928
+ const previous = readForwardDrops(dataDir2);
25929
+ const next = {
25930
+ droppedForwards: (previous?.droppedForwards ?? 0) + count,
25931
+ lastDropAtMs: nowMs
25932
+ };
25933
+ writeOwnerOnlyFileSync(forwardDropsPath(dataDir2), `${JSON.stringify(next)}
25934
+ `);
25935
+ } catch {
25936
+ }
25937
+ }
25938
+ function readForwardDrops(dataDir2) {
25939
+ try {
25940
+ const parsed2 = JSON.parse(readFileSync7(forwardDropsPath(dataDir2), "utf8"));
25941
+ if (typeof parsed2 !== "object" || parsed2 === null) return null;
25942
+ const record2 = parsed2;
25943
+ if (typeof record2.droppedForwards !== "number" || !Number.isFinite(record2.droppedForwards)) {
25944
+ return null;
25945
+ }
25946
+ if (record2.droppedForwards <= 0) return null;
25947
+ if (typeof record2.lastDropAtMs !== "number" || !Number.isFinite(record2.lastDropAtMs)) {
25948
+ return null;
25949
+ }
25950
+ return { droppedForwards: record2.droppedForwards, lastDropAtMs: record2.lastDropAtMs };
25951
+ } catch {
25952
+ return null;
25953
+ }
25954
+ }
25955
+
25956
+ // ../../packages/plugin-runtime/src/attached/forward-policy.ts
25957
+ import { randomUUID as randomUUID15 } from "crypto";
25958
+ import { readFileSync as readFileSync12 } from "fs";
25959
+ import { readFile, rename, writeFile } from "fs/promises";
25960
+ import { join as join18 } from "path";
25961
+
25962
+ // ../../packages/plugin-sdk/src/config.ts
25963
+ import { existsSync as existsSync6 } from "fs";
25964
+ import { join as join11 } from "path";
25965
+
25619
25966
  // ../../packages/plugin-sdk/src/provider-env.ts
25620
25967
  var DEFAULT_ANTHROPIC_HOST = "api.anthropic.com";
25621
25968
  var booleanish = external_exports.string().optional().transform((v) => {
@@ -25650,8 +25997,8 @@ function hostOf(url2) {
25650
25997
  }
25651
25998
  }
25652
25999
  function resolveProvider() {
25653
- const parsed = ProviderEnvSchema.safeParse(process.env);
25654
- const env = parsed.success ? parsed.data : ProviderEnvSchema.parse({});
26000
+ const parsed2 = ProviderEnvSchema.safeParse(process.env);
26001
+ const env = parsed2.success ? parsed2.data : ProviderEnvSchema.parse({});
25655
26002
  if (env.CLAUDE_CODE_USE_BEDROCK === true) return { provider: "bedrock" };
25656
26003
  if (env.CLAUDE_CODE_USE_VERTEX === true) return { provider: "vertex" };
25657
26004
  const baseUrl = env.ANTHROPIC_BASE_URL;
@@ -25668,8 +26015,8 @@ function resolveProvider() {
25668
26015
  function loadConfig(base = defaultDataDir(), resolveProviderFn = resolveProvider) {
25669
26016
  try {
25670
26017
  ensureLayoutDirSync(base);
25671
- const settingsFile = join8(settingsDir(base), "settings.json");
25672
- if (existsSync5(settingsFile)) tightenFile(settingsFile);
26018
+ const settingsFile = join11(settingsDir(base), "settings.json");
26019
+ if (existsSync6(settingsFile)) tightenFile(settingsFile);
25673
26020
  } catch {
25674
26021
  }
25675
26022
  migrateLegacyLayout(base);
@@ -25692,9 +26039,9 @@ function resolveProviderSafe(resolveProviderFn) {
25692
26039
  }
25693
26040
 
25694
26041
  // ../../packages/plugin-sdk/src/config-inventory.ts
25695
- import { readdirSync as readdirSync2, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "fs";
26042
+ import { readdirSync as readdirSync2, readFileSync as readFileSync9, realpathSync as realpathSync2, statSync as statSync7 } from "fs";
25696
26043
  import { homedir as homedir2 } from "os";
25697
- import { basename as basename3, join as join10 } from "path";
26044
+ import { basename as basename3, join as join13 } from "path";
25698
26045
 
25699
26046
  // ../../packages/detections/src/egress/registry.ts
25700
26047
  var EXTRACTOR_VERSION = "1";
@@ -27172,10 +27519,10 @@ var localhost_ref_default = {
27172
27519
  severity: "low",
27173
27520
  matcher: {
27174
27521
  type: "regex",
27175
- pattern: "\\b(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|::1)\\b",
27522
+ pattern: "(?<![A-Za-z0-9_])(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|(?<![A-Za-z0-9_]\\[)::1(?!:|\\.[0-9]))(?![A-Za-z0-9_])",
27176
27523
  flags: "g"
27177
27524
  },
27178
- examples: ["localhost", "127.0.0.1"]
27525
+ examples: ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
27179
27526
  };
27180
27527
 
27181
27528
  // ../../rules/core-code-context/stack-trace.json
@@ -28477,36 +28824,36 @@ function bundledDetections() {
28477
28824
  }
28478
28825
 
28479
28826
  // ../../packages/plugin-sdk/src/repo.ts
28480
- import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
28481
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join9, sep as sep2 } from "path";
28827
+ import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync6 } from "fs";
28828
+ import { basename as basename2, dirname as dirname3, isAbsolute, join as join12, sep as sep2 } from "path";
28482
28829
 
28483
28830
  // ../../packages/plugin-sdk/src/events.ts
28484
28831
  import { createHash as createHash4, randomUUID as randomUUID13 } from "crypto";
28485
28832
 
28486
28833
  // ../../packages/plugin-sdk/src/isolated-scan.ts
28487
- import { existsSync as existsSync7 } from "fs";
28834
+ import { existsSync as existsSync8 } from "fs";
28488
28835
  import { fileURLToPath } from "url";
28489
28836
  import { Worker } from "worker_threads";
28490
28837
 
28491
28838
  // ../../packages/plugin-sdk/src/ignore-layers.ts
28492
28839
  var import_ignore = __toESM(require_ignore(), 1);
28493
- import { readFileSync as readFileSync8 } from "fs";
28494
- import { join as join11 } from "path";
28840
+ import { readFileSync as readFileSync10 } from "fs";
28841
+ import { join as join14 } from "path";
28495
28842
 
28496
28843
  // ../../packages/plugin-sdk/src/inventory-resolver.ts
28497
28844
  import { arch, hostname as hostname4, platform, release } from "os";
28498
28845
 
28499
28846
  // ../../packages/plugin-sdk/src/nudge.ts
28500
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
28501
- import { join as join12 } from "path";
28847
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
28848
+ import { join as join15 } from "path";
28502
28849
 
28503
28850
  // ../../packages/plugin-sdk/src/paths.ts
28504
- import { readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
28505
- import { basename as basename4, dirname as dirname3, sep as sep3 } from "path";
28851
+ import { readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
28852
+ import { basename as basename4, dirname as dirname4, sep as sep3 } from "path";
28506
28853
 
28507
28854
  // ../../packages/plugin-sdk/src/project-files.ts
28508
- import { existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
28509
- import { basename as basename5, join as join13 } from "path";
28855
+ import { existsSync as existsSync9, readdirSync as readdirSync4 } from "fs";
28856
+ import { basename as basename5, join as join16 } from "path";
28510
28857
 
28511
28858
  // ../../packages/plugin-sdk/src/provider-env-antigravity.ts
28512
28859
  var optionalBaseUrl2 = external_exports.preprocess((v) => {
@@ -28541,37 +28888,1164 @@ import { randomUUID as randomUUID14 } from "crypto";
28541
28888
  var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
28542
28889
 
28543
28890
  // ../../packages/plugin-sdk/src/throttle.ts
28544
- import { mkdirSync as mkdirSync3, statSync as statSync6, writeFileSync as writeFileSync6 } from "fs";
28545
- import { join as join14 } from "path";
28546
-
28547
- // ../../packages/plugin-runtime/src/standalone-gateway.ts
28548
- import { randomUUID as randomUUID15 } from "crypto";
28549
-
28550
- // ../../packages/plugin-runtime/src/recorder.ts
28551
- var PLUGIN_RECORDER_BINARY = "plugin";
28891
+ import { mkdirSync as mkdirSync3, statSync as statSync8, writeFileSync as writeFileSync6 } from "fs";
28892
+ import { join as join17 } from "path";
28893
+
28894
+ // ../../packages/plugin-runtime/src/attached/with-timeout.ts
28895
+ var REQUEST_TIMEOUT_MS = 2e3;
28896
+ function withTimeout(promise2, ms) {
28897
+ let timer;
28898
+ const timeout = new Promise((_, reject) => {
28899
+ timer = setTimeout(() => {
28900
+ reject(new Error("attached gateway request timed out"));
28901
+ }, ms);
28902
+ });
28903
+ promise2.catch(() => void 0);
28904
+ return Promise.race([promise2, timeout]).finally(() => {
28905
+ clearTimeout(timer);
28906
+ });
28907
+ }
28552
28908
 
28553
- // ../../packages/plugin-runtime/src/standalone-gateway.ts
28554
- var StandaloneDataGateway = class {
28555
- db;
28556
- // Kept for the fingerprint key lookup (exception.key lives beside the store).
28557
- dataDir;
28558
- // One notice per gateway — see warnRulesetDiscarded.
28559
- warnedRulesetDiscarded = false;
28560
- constructor(dataDir2, detections = [], meta3) {
28561
- this.db = openLocalDatabase(dataDir2);
28562
- this.dataDir = dataDir2;
28563
- this.db.installedPacks.recordInventory(detections, meta3);
28909
+ // ../../packages/plugin-runtime/src/attached/forward-policy.ts
28910
+ function isInvalidRequest(err) {
28911
+ return typeof err === "object" && err !== null && err.name === "RemoteRequestInvalid";
28912
+ }
28913
+ var FORWARD_BUDGET_MS = 1500;
28914
+ var DECISION_PATH_BUDGET_MS = 800;
28915
+ var BREAKER_FAILURE_THRESHOLD = 3;
28916
+ var BREAKER_COOLDOWN_MS = 3e4;
28917
+ var CLOSED = { consecutiveFailures: 0, openedAtMs: null, lastFailure: null };
28918
+ var FAILURES = /* @__PURE__ */ new Set([
28919
+ "unauthorized",
28920
+ "forbidden",
28921
+ "unreachable"
28922
+ ]);
28923
+ var FORWARD_STATE_FILENAME = "attached-state.json";
28924
+ var STATE_FILENAME = FORWARD_STATE_FILENAME;
28925
+ function parseBreakerState(raw, nowMs) {
28926
+ try {
28927
+ const parsed2 = JSON.parse(raw);
28928
+ if (typeof parsed2 !== "object" || parsed2 === null) return null;
28929
+ const record2 = parsed2;
28930
+ const failures = typeof record2.consecutiveFailures === "number" && record2.consecutiveFailures >= 0 ? record2.consecutiveFailures : 0;
28931
+ const openedAtMs = typeof record2.openedAtMs === "number" && Number.isFinite(record2.openedAtMs) && record2.openedAtMs <= nowMs ? record2.openedAtMs : null;
28932
+ const lastFailure = typeof record2.lastFailure === "string" && FAILURES.has(record2.lastFailure) ? record2.lastFailure : null;
28933
+ return { consecutiveFailures: failures, openedAtMs, lastFailure };
28934
+ } catch {
28935
+ return null;
28564
28936
  }
28565
- recordCapture(record2) {
28566
- this.db.recordCapture(record2.event, record2.findings);
28567
- return Promise.resolve();
28937
+ }
28938
+ function createForwardPolicy(deps) {
28939
+ const now = deps.now ?? (() => Date.now());
28940
+ const file2 = join18(deps.dir, STATE_FILENAME);
28941
+ let state = null;
28942
+ let loading = null;
28943
+ async function readState() {
28944
+ let raw;
28945
+ try {
28946
+ raw = await readFile(file2, "utf8");
28947
+ } catch {
28948
+ return { ...CLOSED };
28949
+ }
28950
+ return parseBreakerState(raw, now()) ?? { ...CLOSED };
28568
28951
  }
28569
- ensureInventory(ctx) {
28570
- return Promise.resolve(this.db.ensureInventory(ctx));
28952
+ async function load() {
28953
+ if (state !== null) return state;
28954
+ loading ??= readState().then((loaded) => {
28955
+ state = loaded;
28956
+ loading = null;
28957
+ return loaded;
28958
+ });
28959
+ return loading;
28571
28960
  }
28572
- recordAuditEvent(event) {
28573
- this.db.auditEvents.insertAuditEvent(event);
28574
- return Promise.resolve();
28961
+ async function persist(next) {
28962
+ state = next;
28963
+ try {
28964
+ await ensureDataDir(deps.dir);
28965
+ const tmp = `${file2}.${randomUUID15()}.tmp`;
28966
+ await writeFile(tmp, JSON.stringify(next), { encoding: "utf8", mode: DATA_FILE_MODE });
28967
+ await rename(tmp, file2);
28968
+ } catch {
28969
+ }
28970
+ }
28971
+ return {
28972
+ async run(op, opts) {
28973
+ const budget = opts?.decisionPath === true ? DECISION_PATH_BUDGET_MS : FORWARD_BUDGET_MS;
28974
+ let current;
28975
+ try {
28976
+ current = await load();
28977
+ } catch {
28978
+ current = { ...CLOSED };
28979
+ }
28980
+ const at = now();
28981
+ if (current.openedAtMs !== null) {
28982
+ if (at - current.openedAtMs < BREAKER_COOLDOWN_MS) {
28983
+ return { ok: false, reason: "breaker-open" };
28984
+ }
28985
+ await persist({
28986
+ consecutiveFailures: current.consecutiveFailures,
28987
+ openedAtMs: at,
28988
+ lastFailure: current.lastFailure
28989
+ });
28990
+ }
28991
+ try {
28992
+ const value = await withTimeout(op(), budget);
28993
+ if (current.openedAtMs !== null || current.consecutiveFailures > 0) {
28994
+ await persist({ ...CLOSED });
28995
+ }
28996
+ return { ok: true, value };
28997
+ } catch (err) {
28998
+ if (isInvalidRequest(err)) return { ok: false, reason: "invalid-request" };
28999
+ const reason = classifyFailure(err);
29000
+ const failures = current.consecutiveFailures + 1;
29001
+ const shouldOpen = current.openedAtMs !== null || failures >= BREAKER_FAILURE_THRESHOLD;
29002
+ await persist({
29003
+ consecutiveFailures: failures,
29004
+ openedAtMs: shouldOpen ? now() : null,
29005
+ lastFailure: reason
29006
+ });
29007
+ return { ok: false, reason };
29008
+ }
29009
+ }
29010
+ };
29011
+ }
29012
+
29013
+ // ../../packages/plugin-runtime/src/attached/gateway.ts
29014
+ var ACTION_STRENGTH = {
29015
+ allow: 0,
29016
+ log: 1,
29017
+ warn: 2,
29018
+ redact: 3,
29019
+ block: 4
29020
+ };
29021
+ function ruleCategoryMap(wireRules, localRules) {
29022
+ const map2 = /* @__PURE__ */ new Map();
29023
+ for (const rule of wireRules ?? []) map2.set(rule.id, rule.category);
29024
+ for (const rule of localRules ?? []) map2.set(rule.id, rule.category);
29025
+ for (const pack of bundledDetections()) {
29026
+ for (const rule of pack.rules) map2.set(rule.id, rule.category);
29027
+ }
29028
+ return map2;
29029
+ }
29030
+ function strongerOf(a, b) {
29031
+ if (a === null) return b;
29032
+ if (b === null) return a;
29033
+ return ACTION_STRENGTH[a] >= ACTION_STRENGTH[b] ? a : b;
29034
+ }
29035
+ function policyKey(policy) {
29036
+ return "ruleId" in policy.target ? `rule:${policy.target.ruleId}` : `category:${policy.target.category}`;
29037
+ }
29038
+ function floorFor(policy, categoryByRuleId) {
29039
+ const category = "category" in policy.target ? policy.target.category : categoryByRuleId.get(policy.target.ruleId);
29040
+ return category === void 0 ? null : DEFAULT_ACTIONS[category];
29041
+ }
29042
+ function mergeRaiseOnly(localPolicies, remotePolicies, categoryByRuleId) {
29043
+ const merged = /* @__PURE__ */ new Map();
29044
+ const disabled = [];
29045
+ const remoteCategoryAction = /* @__PURE__ */ new Map();
29046
+ for (const policy of remotePolicies) {
29047
+ if (!policy.enabled) continue;
29048
+ if (!("category" in policy.target)) continue;
29049
+ if (remoteCategoryAction.has(policy.target.category)) continue;
29050
+ const floor = floorFor(policy, categoryByRuleId);
29051
+ remoteCategoryAction.set(
29052
+ policy.target.category,
29053
+ floor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[floor] ? floor : policy.action
29054
+ );
29055
+ }
29056
+ for (const policy of localPolicies) {
29057
+ if (!policy.enabled) {
29058
+ disabled.push(policy);
29059
+ continue;
29060
+ }
29061
+ const key = policyKey(policy);
29062
+ if (merged.has(key)) continue;
29063
+ let remoteFloor = null;
29064
+ if ("ruleId" in policy.target) {
29065
+ const category = categoryByRuleId.get(policy.target.ruleId);
29066
+ if (category !== void 0) remoteFloor = remoteCategoryAction.get(category) ?? null;
29067
+ }
29068
+ merged.set(
29069
+ key,
29070
+ remoteFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[remoteFloor] ? { ...policy, action: remoteFloor } : policy
29071
+ );
29072
+ }
29073
+ const localCategoryAction = /* @__PURE__ */ new Map();
29074
+ for (const policy of merged.values()) {
29075
+ if ("category" in policy.target) localCategoryAction.set(policy.target.category, policy.action);
29076
+ }
29077
+ for (const policy of remotePolicies) {
29078
+ if (!policy.enabled) {
29079
+ disabled.push(policy);
29080
+ continue;
29081
+ }
29082
+ const key = policyKey(policy);
29083
+ const floor = floorFor(policy, categoryByRuleId);
29084
+ let localFloor = null;
29085
+ if ("ruleId" in policy.target) {
29086
+ const category = categoryByRuleId.get(policy.target.ruleId);
29087
+ if (category !== void 0) localFloor = localCategoryAction.get(category) ?? null;
29088
+ }
29089
+ const effectiveFloor = strongerOf(floor, localFloor);
29090
+ const clamped = effectiveFloor !== null && ACTION_STRENGTH[policy.action] < ACTION_STRENGTH[effectiveFloor] ? { ...policy, action: effectiveFloor } : policy;
29091
+ const existing = merged.get(key);
29092
+ if (existing === void 0) {
29093
+ merged.set(key, clamped);
29094
+ continue;
29095
+ }
29096
+ if (ACTION_STRENGTH[clamped.action] > ACTION_STRENGTH[existing.action]) {
29097
+ merged.set(key, clamped);
29098
+ }
29099
+ }
29100
+ return [...merged.values(), ...disabled];
29101
+ }
29102
+ var AttachedDataGateway = class {
29103
+ constructor(deps) {
29104
+ this.deps = deps;
29105
+ }
29106
+ deps;
29107
+ /**
29108
+ * The control plane's OWN resolution of this session's inventory, captured by
29109
+ * ensureInventory. Null until the first successful forward — and it stays
29110
+ * null for the whole session when the control plane is unreachable, which is fine:
29111
+ * reKeyForForward then leaves the event's ids alone and the control plane resolves
29112
+ * what it can from the descriptors it already has.
29113
+ */
29114
+ remoteInventory = null;
29115
+ // ---------------------------------------------------------------------
29116
+ // Writes: local first, then forward.
29117
+ // ---------------------------------------------------------------------
29118
+ async recordCapture(record2) {
29119
+ await this.deps.local.recordCapture(record2);
29120
+ await this.deps.forward.run(
29121
+ () => this.deps.client.ingestEvents({
29122
+ events: [record2.event],
29123
+ ...record2.dedupe ? { dedupe: record2.dedupe } : {}
29124
+ }),
29125
+ { decisionPath: true }
29126
+ );
29127
+ }
29128
+ async ensureInventory(ctx) {
29129
+ const resolved = await this.deps.local.ensureInventory(ctx);
29130
+ const remote = await this.deps.forward.run(() => this.deps.client.ingestInventory(ctx));
29131
+ this.remoteInventory = remote.ok ? remote.value : null;
29132
+ const snapshot = await (async () => {
29133
+ try {
29134
+ return await this.deps.posture?.prepare() ?? null;
29135
+ } catch {
29136
+ return null;
29137
+ }
29138
+ })();
29139
+ if (snapshot) {
29140
+ try {
29141
+ await withTimeout(
29142
+ this.deps.posture?.send(snapshot) ?? Promise.resolve(),
29143
+ REQUEST_TIMEOUT_MS
29144
+ );
29145
+ } catch {
29146
+ }
29147
+ }
29148
+ return resolved;
29149
+ }
29150
+ // The id is minted CLIENT-side and stored verbatim: the control plane does NOT
29151
+ // re-key it. `pgAuditValues` writes `id: event.id` and carries tenancy in
29152
+ // its own scoping columns, so the device and the forwarded copy
29153
+ // share one id space — which is what makes a re-post idempotent at all.
29154
+ //
29155
+ // Re-posts collapse via `onConflictDoUpdate` on the `id` PK, guarded by
29156
+ // `setWhere eventType = 'session'` (NOT onConflictDoNothing). That guard is
29157
+ // what makes an attached retry safe: a capture-stubbed session row can still
29158
+ // be HEALED by the authoritative root, while a duplicate non-session event —
29159
+ // a retried tool_call, exactly this path — can never stomp a populated row.
29160
+ async recordAuditEvent(event) {
29161
+ await this.deps.local.recordAuditEvent(event);
29162
+ await this.deps.forward.run(
29163
+ () => this.deps.client.recordAuditEvent(reKeyForForward(event, this.remoteInventory))
29164
+ );
29165
+ }
29166
+ // Attached `llm_call` is written locally by the inner gateway, then routed to
29167
+ // the control plane through the existing `recordAuditEvent` ingest (no dedicated
29168
+ // client method yet) by pre-building the audit event from the natural key.
29169
+ // The forward goes DIRECTLY to the client rather than through this.recordAuditEvent,
29170
+ // which would write the event to the local store a second time.
29171
+ async recordLlmCall(input) {
29172
+ await this.deps.local.recordLlmCall(input);
29173
+ await this.deps.forward.run(
29174
+ () => this.deps.client.recordAuditEvent(
29175
+ reKeyForForward(llmAuditEvent(input), this.remoteInventory)
29176
+ )
29177
+ );
29178
+ }
29179
+ /**
29180
+ * Forward one batch, item by item, under ONE aggregate deadline.
29181
+ *
29182
+ * Per-item budgets bound each request and nothing bounded their sum — see
29183
+ * BATCH_FORWARD_BUDGET_MS. When the deadline passes the remainder is dropped
29184
+ * rather than sent: the local write has already succeeded, so every caller
29185
+ * has a correct result to return, and a drop is the outcome this path is
29186
+ * built to accept (G8) where a blown hook timeout is not.
29187
+ *
29188
+ * Serial rather than concurrent on purpose. Firing N requests at once would
29189
+ * trade a latency problem for a burst the plane's own per-key rate limiting
29190
+ * would answer with the refusals the breaker then counts.
29191
+ *
29192
+ * WHAT IS DROPPED IS COUNTED. Every other forward failure ends in
29193
+ * `ForwardPolicy.run`'s catch and moves the breaker's file, which is what
29194
+ * lets status call the forward unhealthy; this path returns BEFORE `run` is
29195
+ * reached, so without the tally in `forward-drops.ts` a slow-but-answering
29196
+ * plane produces no failures, keeps the breaker closed, renders a healthy
29197
+ * block, and discards the tail of every batch indefinitely.
29198
+ */
29199
+ async forwardBatch(inputs, toEvent) {
29200
+ const deadline = Date.now() + BATCH_FORWARD_BUDGET_MS;
29201
+ for (let i = 0; i < inputs.length; i += 1) {
29202
+ const now = Date.now();
29203
+ if (now >= deadline) {
29204
+ recordForwardDrops(this.deps.dataDir, inputs.length - i, now);
29205
+ return;
29206
+ }
29207
+ const input = inputs[i];
29208
+ await this.deps.forward.run(
29209
+ () => this.deps.client.recordAuditEvent(reKeyForForward(toEvent(input), this.remoteInventory))
29210
+ );
29211
+ }
29212
+ }
29213
+ // Delegated as a BATCH rather than looped over recordLlmCall: the inner
29214
+ // gateway may write the whole batch in one local transaction, and looping
29215
+ // here would replace that with N separate local writes.
29216
+ async recordLlmCalls(inputs) {
29217
+ await this.deps.local.recordLlmCalls(inputs);
29218
+ await this.forwardBatch(inputs, (input) => llmAuditEvent(input));
29219
+ }
29220
+ // `input.inspections` (secrets detected client-side in the tool's masked
29221
+ // target) ride along on the request's `inspections` field — the control plane
29222
+ // persists each as an inspection_findings row linked to this audit event
29223
+ // (see RecordAuditEventRequest in @akasecurity/schema). The masked
29224
+ // `target` already rides `input.attributes`, so no raw secret leaks either
29225
+ // way — this only stops the FINDING row itself from being dropped.
29226
+ async recordToolCalls(inputs) {
29227
+ await this.deps.local.recordToolCalls(inputs);
29228
+ await this.forwardBatch(inputs, (input) => toolAuditEvent(input));
29229
+ }
29230
+ // Forwarded as a `config_scan` audit event: there is no dedicated
29231
+ // config-scan ingest endpoint, and the audit-event door is the one the
29232
+ // control plane already opens for client-minted, idempotent records.
29233
+ //
29234
+ // ONLY `scanEvent` CROSSES, and unlike `recordCapture` the plane cannot
29235
+ // re-derive the rest. A `ConfigScanRecord` is four things committed together
29236
+ // locally — the inventory `items`, this audit event, and the posture
29237
+ // `definitions`/`findings` that reference it — and three of them stay on the
29238
+ // device. Say that plainly rather than let the asymmetry with `recordCapture`
29239
+ // read as the same argument: there, findings are omitted BECAUSE the plane
29240
+ // re-derives them from `Event.content`; here there is no content to re-derive
29241
+ // from, so what is omitted is simply not sent.
29242
+ //
29243
+ // That is the wire contract as it stands rather than an oversight to patch
29244
+ // here. `items` has no route at all, and `RecordAuditEventRequest.inspections`
29245
+ // is documented as tool-call findings — widening it to carry config-scan
29246
+ // findings is an egress change (a posture finding's `maskedMatch` holds the
29247
+ // matched command) and a decision about what an attached deployment is
29248
+ // entitled to, not a bug fix. An attached machine's config posture therefore
29249
+ // reaches the plane as the event only; the dashboard's own view of it is the
29250
+ // local store.
29251
+ async recordConfigScan(record2) {
29252
+ await this.deps.local.recordConfigScan(record2);
29253
+ await this.deps.forward.run(
29254
+ () => this.deps.client.recordAuditEvent(reKeyForForward(record2.scanEvent, this.remoteInventory))
29255
+ );
29256
+ }
29257
+ async recordBlockedDetection(entry) {
29258
+ return this.deps.local.recordBlockedDetection(entry);
29259
+ }
29260
+ /**
29261
+ * LOCAL-ONLY, deliberately. The shares API is read-plus-decision-override
29262
+ * with no egress ingest endpoint, so there is nothing to forward to; adding a
29263
+ * forward here would be inventing a wire contract that does not exist. The
29264
+ * local write is the whole operation, and its summary is the real one — the
29265
+ * scanner reads a throw as a FAILED WRITE and skips its ledger commit, so
29266
+ * returning the inner gateway's result keeps the retry semantics honest.
29267
+ */
29268
+ async recordProjectEgress(input) {
29269
+ return this.deps.local.recordProjectEgress(input);
29270
+ }
29271
+ // ---------------------------------------------------------------------
29272
+ // Reads and device-local ledgers: pure delegation.
29273
+ // ---------------------------------------------------------------------
29274
+ async configInventoryReport() {
29275
+ return this.deps.local.configInventoryReport();
29276
+ }
29277
+ async readSessionProvider(sessionId) {
29278
+ return this.deps.local.readSessionProvider(sessionId);
29279
+ }
29280
+ async facets() {
29281
+ return this.deps.local.facets();
29282
+ }
29283
+ /**
29284
+ * Delegated UNMODIFIED — including its refusals.
29285
+ *
29286
+ * This is a fail-secure boundary: it decides whether an approved exception
29287
+ * lets a blocked action through. Under local-first the local store owns the
29288
+ * exception ledger, so the honest answer is whatever it says; wrapping this
29289
+ * in a fallback (`catch { return true }`, or defaulting on a timeout) would
29290
+ * turn a store error into a granted bypass. If the inner gateway rejects,
29291
+ * this rejects, and the runtime's own handling decides — which is asserted
29292
+ * end-to-end through runtime.capture rather than here.
29293
+ */
29294
+ async consumeException(id) {
29295
+ return this.deps.local.consumeException(id);
29296
+ }
29297
+ async recentFindings(opts) {
29298
+ return this.deps.local.recentFindings(opts);
29299
+ }
29300
+ async healthSummary() {
29301
+ return this.deps.local.healthSummary();
29302
+ }
29303
+ async activityByDay(days) {
29304
+ return this.deps.local.activityByDay(days);
29305
+ }
29306
+ async tokenReports() {
29307
+ return this.deps.local.tokenReports();
29308
+ }
29309
+ async knownContentHashes() {
29310
+ return this.deps.local.knownContentHashes();
29311
+ }
29312
+ async scanLedger(rulesetHash) {
29313
+ return this.deps.local.scanLedger(rulesetHash);
29314
+ }
29315
+ async recordScanned(entries) {
29316
+ return this.deps.local.recordScanned(entries);
29317
+ }
29318
+ async getRuleProbeVerdict(ruleKey) {
29319
+ return this.deps.local.getRuleProbeVerdict(ruleKey);
29320
+ }
29321
+ async setRuleProbeVerdict(ruleKey, verdict, worstProbeMs) {
29322
+ return this.deps.local.setRuleProbeVerdict(ruleKey, verdict, worstProbeMs);
29323
+ }
29324
+ async openAtRestKeysForPath(path) {
29325
+ return this.deps.local.openAtRestKeysForPath(path);
29326
+ }
29327
+ async resolvedAtRestKeysForPath(path) {
29328
+ return this.deps.local.resolvedAtRestKeysForPath(path);
29329
+ }
29330
+ async insertResolution(input) {
29331
+ return this.deps.local.insertResolution(input);
29332
+ }
29333
+ async close() {
29334
+ return this.deps.local.close();
29335
+ }
29336
+ // ---------------------------------------------------------------------
29337
+ // Policy
29338
+ // ---------------------------------------------------------------------
29339
+ async getPolicyBundle() {
29340
+ const local = await this.deps.local.getPolicyBundle();
29341
+ const cached2 = await (async () => {
29342
+ try {
29343
+ return await this.deps.readCachedBundle();
29344
+ } catch {
29345
+ return null;
29346
+ }
29347
+ })();
29348
+ if (cached2 === null) return local;
29349
+ const byRuleId = /* @__PURE__ */ new Map();
29350
+ for (const rule of [...local.rules ?? [], ...cached2.rules ?? []]) {
29351
+ if (!byRuleId.has(rule.id)) byRuleId.set(rule.id, rule);
29352
+ }
29353
+ const rules = [...byRuleId.values()];
29354
+ return {
29355
+ ...local,
29356
+ // The remote version identifies the composed bundle for the poller.
29357
+ version: cached2.version,
29358
+ rules,
29359
+ policies: mergeRaiseOnly(
29360
+ local.policies,
29361
+ cached2.policies,
29362
+ ruleCategoryMap(cached2.rules, local.rules)
29363
+ ),
29364
+ customKeywords: [...local.customKeywords, ...cached2.customKeywords]
29365
+ // `rulesComplete` is a STANDALONE-ONLY signal (the user's local installed
29366
+ // snapshot) and is taken from the LOCAL bundle only — never from the wire
29367
+ // or the on-disk cache. Honoring a cached one would hand the control plane, or
29368
+ // anything able to write policy-cache.json, a kill-switch over the
29369
+ // compiled-in bundled packs: `{ rulesComplete: true, rules: [] }` would
29370
+ // zero local detection. Spread from `local` above, and deliberately not
29371
+ // re-read from `cached` here.
29372
+ //
29373
+ // THREE MORE OF THE CACHED BUNDLE'S FIELDS ARE DROPPED, each on purpose,
29374
+ // and each named here so a reader can tell a decision from an omission:
29375
+ //
29376
+ // `exceptions` — an exception SUPPRESSES a detection, so honoring
29377
+ // one from an unsigned on-disk cache would let
29378
+ // anything able to write that file turn rules off.
29379
+ // Every other field this merge accepts can only
29380
+ // RAISE enforcement; this is the one that cannot,
29381
+ // so it stays local-only until the bundle is
29382
+ // signed. Exceptions remain a device-local ledger.
29383
+ // `reversibleRuleIds` — the Redact & Vault archetype makes a redaction
29384
+ // recoverable, which is a CUSTODY change: it puts
29385
+ // the detected value in the local vault instead of
29386
+ // destroying it. Taking that instruction from the
29387
+ // cache would let a remote party turn one-way
29388
+ // redaction into retention. Dropping it keeps the
29389
+ // one-way behaviour, which the schema itself calls
29390
+ // "the safe direction to default".
29391
+ // `ruleVersions` — remote rules fall back to their own spec version.
29392
+ // Cosmetic rather than protective: it only affects
29393
+ // how a finding is version-attributed, and the two
29394
+ // sides may therefore attribute org rules
29395
+ // differently. Worth carrying once there is a
29396
+ // reader that needs it; nothing reads it today.
29397
+ };
29398
+ }
29399
+ // ---------------------------------------------------------------------
29400
+ // LocalStoreMaintenance — by delegation (D3).
29401
+ //
29402
+ // Implementing these is what actually closes the skipped-local-maintenance
29403
+ // gap: the OSS structural guard `hasLocalStoreMaintenance()` is satisfied by
29404
+ // any object carrying all five, so the composite qualifies and SessionStart
29405
+ // runs maintenance on the device's real store.
29406
+ //
29407
+ // ⚠ Two of the five are SYNCHRONOUS and must stay that way. `handle-session-start`
29408
+ // calls `capWarnEraEnforcement` without `await` and uses `staleBinaryNotice`'s
29409
+ // return value directly; declaring them `async` here would hand those call
29410
+ // sites a Promise and silently break both.
29411
+ // ---------------------------------------------------------------------
29412
+ async sweepTerminalExceptions(retentionMs) {
29413
+ return this.deps.local.sweepTerminalExceptions(retentionMs);
29414
+ }
29415
+ capWarnEraEnforcement(policyMode) {
29416
+ return this.deps.local.capWarnEraEnforcement(policyMode);
29417
+ }
29418
+ async recordProjectFiles(projectId, scan2) {
29419
+ return this.deps.local.recordProjectFiles(projectId, scan2);
29420
+ }
29421
+ async reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
29422
+ return this.deps.local.reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot);
29423
+ }
29424
+ staleBinaryNotice(currentVersion) {
29425
+ return this.deps.local.staleBinaryNotice(currentVersion);
29426
+ }
29427
+ };
29428
+ function reKeyForForward(event, remote) {
29429
+ if (remote === null) {
29430
+ const stripped = { ...event };
29431
+ delete stripped.hostId;
29432
+ delete stripped.harnessId;
29433
+ delete stripped.sourceProjectId;
29434
+ return stripped;
29435
+ }
29436
+ const rekeyed = { ...event };
29437
+ delete rekeyed.hostId;
29438
+ delete rekeyed.harnessId;
29439
+ delete rekeyed.sourceProjectId;
29440
+ if (remote.hostId !== void 0) rekeyed.hostId = remote.hostId;
29441
+ if (remote.harnessId !== void 0) rekeyed.harnessId = remote.harnessId;
29442
+ if (remote.sourceProjectId !== void 0) rekeyed.sourceProjectId = remote.sourceProjectId;
29443
+ return rekeyed;
29444
+ }
29445
+ var BATCH_FORWARD_BUDGET_MS = 3e3;
29446
+ function llmAuditEvent(input) {
29447
+ return {
29448
+ id: llmCallId(input.sessionId, input.messageId),
29449
+ eventType: "llm_call",
29450
+ startedAt: input.startedAt,
29451
+ parentId: input.parentId,
29452
+ rootSessionId: input.rootSessionId,
29453
+ attributes: input.attributes
29454
+ };
29455
+ }
29456
+ function toolAuditEvent(input) {
29457
+ return {
29458
+ id: toolCallId(input.sessionId, input.toolUseId),
29459
+ eventType: "tool_call",
29460
+ startedAt: input.startedAt,
29461
+ parentId: input.parentId,
29462
+ rootSessionId: input.rootSessionId,
29463
+ attributes: input.attributes,
29464
+ inspections: input.inspections
29465
+ };
29466
+ }
29467
+
29468
+ // ../../packages/plugin-runtime/src/attached/policy-store.ts
29469
+ import { randomUUID as randomUUID16 } from "crypto";
29470
+ import { readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
29471
+ import { join as join19 } from "path";
29472
+
29473
+ // ../../packages/plugin-runtime/src/attached/atomic-publish.ts
29474
+ import { rename as rename2 } from "fs/promises";
29475
+ var RETRYABLE = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
29476
+ var ATTEMPTS = 5;
29477
+ var delay = (ms) => new Promise((resolve2) => {
29478
+ setTimeout(resolve2, ms);
29479
+ });
29480
+ async function publishByRename(tmp, file2, move = rename2) {
29481
+ for (let attempt = 1; ; attempt += 1) {
29482
+ try {
29483
+ await move(tmp, file2);
29484
+ return;
29485
+ } catch (err) {
29486
+ const code = err.code;
29487
+ if (attempt >= ATTEMPTS || code === void 0 || !RETRYABLE.has(code)) throw err;
29488
+ await delay(attempt * 10);
29489
+ }
29490
+ }
29491
+ }
29492
+
29493
+ // ../../packages/plugin-runtime/src/attached/policy-store.ts
29494
+ function createPolicyStore(dir = dataDir()) {
29495
+ const file2 = join19(dir, "policy-cache.json");
29496
+ async function read() {
29497
+ try {
29498
+ const raw = await readFile2(file2, "utf8");
29499
+ const parsed2 = JSON.parse(raw);
29500
+ if (typeof parsed2 !== "object" || parsed2 === null) return null;
29501
+ const record2 = parsed2;
29502
+ const bundle = PolicyBundle.parse(record2.bundle);
29503
+ const fetchedAtMs = typeof record2.fetchedAtMs === "number" ? record2.fetchedAtMs : 0;
29504
+ const etag = typeof record2.etag === "string" ? record2.etag : void 0;
29505
+ return { bundle, fetchedAtMs, ...etag === void 0 ? {} : { etag } };
29506
+ } catch {
29507
+ return null;
29508
+ }
29509
+ }
29510
+ async function write(bundle, etag) {
29511
+ await ensureDataDir(dir);
29512
+ const stored = {
29513
+ bundle,
29514
+ fetchedAtMs: Date.now(),
29515
+ ...etag === void 0 ? {} : { etag }
29516
+ };
29517
+ const tmp = `${file2}.${randomUUID16()}.tmp`;
29518
+ try {
29519
+ await writeFile2(tmp, JSON.stringify(stored), {
29520
+ encoding: "utf8",
29521
+ mode: DATA_FILE_MODE,
29522
+ flag: "wx"
29523
+ });
29524
+ await publishByRename(tmp, file2);
29525
+ } catch (err) {
29526
+ await rm(tmp, { force: true }).catch(() => void 0);
29527
+ throw err;
29528
+ }
29529
+ }
29530
+ return { read, write, file: file2 };
29531
+ }
29532
+
29533
+ // ../../packages/remote/src/http.ts
29534
+ import { request as httpRequest } from "http";
29535
+ import { request as httpsRequest } from "https";
29536
+ var DEFAULT_TIMEOUT_MS = 1e4;
29537
+ var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
29538
+ var RemoteRequestError = class extends Error {
29539
+ constructor(status) {
29540
+ super(`control-plane request failed with status ${String(status)}`);
29541
+ this.status = status;
29542
+ this.name = "RemoteRequestError";
29543
+ }
29544
+ status;
29545
+ };
29546
+ var RemoteRequestInvalid = class extends Error {
29547
+ constructor(route, cause) {
29548
+ super(`refusing to send a malformed body to ${route}`);
29549
+ this.cause = cause;
29550
+ this.name = "RemoteRequestInvalid";
29551
+ }
29552
+ cause;
29553
+ };
29554
+ var RemoteResponseInvalid = class extends Error {
29555
+ constructor(route, detail) {
29556
+ super(`control plane answered ${route} with ${detail}`);
29557
+ this.name = "RemoteResponseInvalid";
29558
+ }
29559
+ };
29560
+ var RemoteTransportError = class extends Error {
29561
+ /**
29562
+ * The status the peer sent, when headers arrived and only the BODY was
29563
+ * refused.
29564
+ *
29565
+ * Undefined for the ordinary case this class was written for — no answer at
29566
+ * all. It exists because two paths reject after a status has already been
29567
+ * delivered: an oversized body and an aborted response. Discarding it there
29568
+ * reported a deployment answering 401 with a verbose body as a network
29569
+ * outage, which sends the reader to look at their network instead of their
29570
+ * credential.
29571
+ */
29572
+ constructor(reason, status) {
29573
+ super(`control-plane request did not complete: ${reason}`);
29574
+ this.status = status;
29575
+ this.name = "RemoteTransportError";
29576
+ }
29577
+ status;
29578
+ };
29579
+ async function send(options) {
29580
+ const url2 = new URL(options.url);
29581
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
29582
+ const send_ = url2.protocol === "http:" ? httpRequest : httpsRequest;
29583
+ const requestOptions = {
29584
+ method: options.method,
29585
+ headers: {
29586
+ // CALLER HEADERS FIRST, so this module's own are not overridable. Spread
29587
+ // last they win, and two of the values below are ones no caller may
29588
+ // replace: `x-api-key` is the credential, and `content-length` is the
29589
+ // byte count that stops a multi-byte body being truncated by the
29590
+ // receiver. `SendOptions.headers` is a free-form record on an exported
29591
+ // function, so "no caller does that today" is not the guarantee to rely
29592
+ // on. The one header any caller actually passes — `if-none-match` on the
29593
+ // conditional GET — is untouched by this order.
29594
+ ...options.headers,
29595
+ // The credential. One header, matching what the deployment authenticates
29596
+ // on; a second copy in an `Authorization` header would be one more place
29597
+ // it can be logged by an intermediary for no gain.
29598
+ "x-api-key": options.apiKey,
29599
+ accept: "application/json",
29600
+ ...options.body === void 0 ? {} : {
29601
+ "content-type": "application/json",
29602
+ // Byte length, not string length: a multi-byte body sent with a
29603
+ // character count is truncated by the receiver.
29604
+ "content-length": String(Buffer.byteLength(options.body))
29605
+ }
29606
+ }
29607
+ };
29608
+ return new Promise((resolve2, reject) => {
29609
+ let settled = false;
29610
+ const fail = (reason, status) => {
29611
+ if (settled) return;
29612
+ settled = true;
29613
+ reject(new RemoteTransportError(reason, status));
29614
+ };
29615
+ const req = send_(url2, requestOptions, (res) => {
29616
+ const chunks = [];
29617
+ let size = 0;
29618
+ res.on("data", (chunk) => {
29619
+ size += chunk.length;
29620
+ if (size > MAX_RESPONSE_BYTES) {
29621
+ fail(`response exceeded ${String(MAX_RESPONSE_BYTES)} bytes`, res.statusCode);
29622
+ res.destroy();
29623
+ req.destroy();
29624
+ return;
29625
+ }
29626
+ chunks.push(chunk);
29627
+ });
29628
+ res.on("aborted", () => {
29629
+ fail("the response was aborted", res.statusCode);
29630
+ });
29631
+ res.on("end", () => {
29632
+ if (settled) return;
29633
+ settled = true;
29634
+ resolve2({
29635
+ status: res.statusCode ?? 0,
29636
+ headers: res.headers,
29637
+ body: Buffer.concat(chunks).toString("utf8")
29638
+ });
29639
+ });
29640
+ });
29641
+ const deadline = setTimeout(() => {
29642
+ fail(`no response within ${String(timeoutMs)}ms`);
29643
+ req.destroy();
29644
+ }, timeoutMs);
29645
+ deadline.unref();
29646
+ req.on("upgrade", (_res, socket) => {
29647
+ fail("the deployment answered with a protocol upgrade");
29648
+ socket.destroy();
29649
+ });
29650
+ req.on("close", () => {
29651
+ fail("the connection closed before a response was read");
29652
+ clearTimeout(deadline);
29653
+ });
29654
+ req.on("error", (err) => {
29655
+ fail(err.message);
29656
+ });
29657
+ if (options.body !== void 0) req.write(options.body);
29658
+ req.end();
29659
+ });
29660
+ }
29661
+
29662
+ // ../../packages/remote/src/client.ts
29663
+ var ROUTES = {
29664
+ events: "/v1/events",
29665
+ auditEvents: "/v1/audit-events",
29666
+ inventory: "/v1/inventory",
29667
+ storePosture: "/v1/store-posture",
29668
+ policyBundle: "/v1/policy-bundle",
29669
+ whoami: "/v1/plugin/whoami"
29670
+ };
29671
+ function headerValue(response, name) {
29672
+ const raw = response.headers[name];
29673
+ if (raw === void 0) return void 0;
29674
+ return Array.isArray(raw) ? raw[0] : raw;
29675
+ }
29676
+ function okBody(response) {
29677
+ if (response.status < 200 || response.status >= 300) {
29678
+ throw new RemoteRequestError(response.status);
29679
+ }
29680
+ return response.body;
29681
+ }
29682
+ function parsed(schema, body, route) {
29683
+ let json2;
29684
+ try {
29685
+ json2 = JSON.parse(body);
29686
+ } catch {
29687
+ throw new RemoteResponseInvalid(route, "a body that is not JSON");
29688
+ }
29689
+ const result = schema.safeParse(json2);
29690
+ if (!result.success) {
29691
+ throw new RemoteResponseInvalid(route, "a body this client cannot read");
29692
+ }
29693
+ return result.data;
29694
+ }
29695
+ function withoutTrailingSlashes(endpoint) {
29696
+ let end = endpoint.length;
29697
+ while (end > 0 && endpoint.charCodeAt(end - 1) === SLASH) end -= 1;
29698
+ return endpoint.slice(0, end);
29699
+ }
29700
+ var SLASH = "/".charCodeAt(0);
29701
+ function createRemoteClient(options) {
29702
+ const base = withoutTrailingSlashes(options.endpoint);
29703
+ const url2 = (route) => `${base}${route}`;
29704
+ const common = { apiKey: options.apiKey, timeoutMs: options.timeoutMs };
29705
+ return {
29706
+ async ingestEvents(batch) {
29707
+ const response = await send({
29708
+ ...common,
29709
+ method: "POST",
29710
+ url: url2(ROUTES.events),
29711
+ body: JSON.stringify(batch)
29712
+ });
29713
+ return parsed(IngestAck, okBody(response), ROUTES.events);
29714
+ },
29715
+ async ingestInventory(context) {
29716
+ const response = await send({
29717
+ ...common,
29718
+ method: "POST",
29719
+ url: url2(ROUTES.inventory),
29720
+ body: JSON.stringify(context)
29721
+ });
29722
+ return parsed(ResolvedInventory, okBody(response), ROUTES.inventory);
29723
+ },
29724
+ async recordAuditEvent(event) {
29725
+ const validated = RecordAuditEventRequest.safeParse(event);
29726
+ if (!validated.success) throw new RemoteRequestInvalid(ROUTES.auditEvents, validated.error);
29727
+ const submission = validated.data;
29728
+ const response = await send({
29729
+ ...common,
29730
+ method: "POST",
29731
+ url: url2(ROUTES.auditEvents),
29732
+ body: JSON.stringify(submission)
29733
+ });
29734
+ okBody(response);
29735
+ },
29736
+ async reportStorePosture(snapshot) {
29737
+ const response = await send({
29738
+ ...common,
29739
+ method: "POST",
29740
+ url: url2(ROUTES.storePosture),
29741
+ body: JSON.stringify(snapshot)
29742
+ });
29743
+ okBody(response);
29744
+ },
29745
+ async getPolicyBundle(etag) {
29746
+ const response = await send({
29747
+ ...common,
29748
+ method: "GET",
29749
+ url: url2(ROUTES.policyBundle),
29750
+ ...etag === void 0 ? {} : { headers: { "if-none-match": etag } }
29751
+ });
29752
+ if (response.status === 304) {
29753
+ return { changed: false, etag: headerValue(response, "etag") ?? etag };
29754
+ }
29755
+ const bundle = parsed(PolicyBundle, okBody(response), ROUTES.policyBundle);
29756
+ return { changed: true, bundle, etag: headerValue(response, "etag") };
29757
+ },
29758
+ async whoami() {
29759
+ const response = await send({ ...common, method: "GET", url: url2(ROUTES.whoami) });
29760
+ return parsed(PluginWhoami, okBody(response), ROUTES.whoami);
29761
+ }
29762
+ };
29763
+ }
29764
+
29765
+ // ../../packages/plugin-runtime/src/attached/posture-reporter.ts
29766
+ var POSTURE_REPORT_INTERVAL_MS = 60 * 60 * 1e3;
29767
+ function createPostureReporter(deps) {
29768
+ async function prepare() {
29769
+ try {
29770
+ const state = await withTimeout(deps.store.read(), REQUEST_TIMEOUT_MS);
29771
+ if (state === null) return null;
29772
+ const nowMs = deps.now();
29773
+ const elapsed = nowMs - state.lastAttemptedAtMs;
29774
+ if (elapsed >= 0 && elapsed < POSTURE_REPORT_INTERVAL_MS) return null;
29775
+ try {
29776
+ await withTimeout(deps.store.markAttempted(state.deviceId, nowMs), REQUEST_TIMEOUT_MS);
29777
+ } catch {
29778
+ }
29779
+ const { readError, ...measurement } = deps.readStore();
29780
+ if (readError) return null;
29781
+ let plugin;
29782
+ try {
29783
+ plugin = await deps.pluginBlock?.();
29784
+ } catch {
29785
+ plugin = void 0;
29786
+ }
29787
+ return {
29788
+ deviceId: state.deviceId,
29789
+ hostname: deps.hostname(),
29790
+ capturedAt: nowMs,
29791
+ ...measurement,
29792
+ // Omit the key rather than spread an explicit `undefined` —
29793
+ // exactOptionalPropertyTypes distinguishes the two, and the bridge in
29794
+ // factory.ts keys on presence.
29795
+ ...plugin === void 0 ? {} : { plugin }
29796
+ };
29797
+ } catch {
29798
+ return null;
29799
+ }
29800
+ }
29801
+ async function send2(snapshot) {
29802
+ try {
29803
+ await deps.report(snapshot);
29804
+ } catch {
29805
+ }
29806
+ }
29807
+ return { prepare, send: send2 };
29808
+ }
29809
+
29810
+ // ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
29811
+ import { statSync as statSync9 } from "fs";
29812
+ import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
29813
+
29814
+ // ../../packages/plugin-runtime/src/attached/action-counts.ts
29815
+ function emptyActionCounts() {
29816
+ return Object.fromEntries(ACTION_TAKEN_KEYS.map((a) => [a, 0]));
29817
+ }
29818
+ function isActionTaken(value) {
29819
+ return ACTION_TAKEN_KEYS.includes(value);
29820
+ }
29821
+
29822
+ // ../../packages/plugin-runtime/src/attached/posture-snapshot.ts
29823
+ var CAPTURE_EVENT_TYPES_SQL2 = `('prompt','response','code_change','tool_use')`;
29824
+ function isSchemaAbsent(err) {
29825
+ return err instanceof Error && /no such table/i.test(err.message);
29826
+ }
29827
+ function emptyReadout(readError = false) {
29828
+ const byAction = emptyActionCounts();
29829
+ return {
29830
+ storePresent: false,
29831
+ schemaVersion: null,
29832
+ findingsTotal: 0,
29833
+ findingsFirstAt: null,
29834
+ findingsLastAt: null,
29835
+ packs: [],
29836
+ policyCounts: { total: 0, disabled: 0, byAction },
29837
+ readError
29838
+ };
29839
+ }
29840
+ function readStorePosture(dbPath2) {
29841
+ try {
29842
+ statSync9(dbPath2);
29843
+ } catch (err) {
29844
+ const code = err.code;
29845
+ if (code === "ENOENT" || code === "ENOTDIR") return emptyReadout();
29846
+ return emptyReadout(true);
29847
+ }
29848
+ let db = null;
29849
+ let version2 = null;
29850
+ let packs = [];
29851
+ let policyCounts = {
29852
+ total: 0,
29853
+ disabled: 0,
29854
+ byAction: emptyActionCounts()
29855
+ };
29856
+ let findingsTotal = 0;
29857
+ let findingsFirstAt = null;
29858
+ let findingsLastAt = null;
29859
+ const currentReadout = () => ({
29860
+ storePresent: true,
29861
+ schemaVersion: version2,
29862
+ findingsTotal,
29863
+ findingsFirstAt,
29864
+ findingsLastAt,
29865
+ packs,
29866
+ policyCounts,
29867
+ readError: false
29868
+ });
29869
+ try {
29870
+ db = new DatabaseSync3(dbPath2, { readOnly: true });
29871
+ db.exec("PRAGMA busy_timeout = 2000");
29872
+ version2 = db.prepare("PRAGMA user_version").get().user_version;
29873
+ try {
29874
+ const packRows = db.prepare(
29875
+ `SELECT namespace, pack_id, version, enabled, updated_at FROM installed_packs ORDER BY namespace, pack_id`
29876
+ ).all();
29877
+ packs = packRows.map((r) => ({
29878
+ packId: `${r.namespace}/${r.pack_id}`,
29879
+ version: r.version,
29880
+ enabled: r.enabled !== 0,
29881
+ updatedAt: r.updated_at == null ? null : String(r.updated_at)
29882
+ }));
29883
+ } catch (err) {
29884
+ if (!isSchemaAbsent(err)) throw err;
29885
+ }
29886
+ try {
29887
+ const policyRows = db.prepare(`SELECT action, enabled FROM policies`).all();
29888
+ const byAction = emptyActionCounts();
29889
+ let disabled = 0;
29890
+ for (const row of policyRows) {
29891
+ if (row.enabled === 0) disabled += 1;
29892
+ if (isActionTaken(row.action)) byAction[row.action] += 1;
29893
+ }
29894
+ policyCounts = { total: policyRows.length, disabled, byAction };
29895
+ } catch (err) {
29896
+ if (!isSchemaAbsent(err)) throw err;
29897
+ }
29898
+ try {
29899
+ const agg = db.prepare(
29900
+ `SELECT count(*) AS n, min(f.first_detected_at) AS firstAt, max(f.first_detected_at) AS lastAt
29901
+ FROM inspection_findings f JOIN audit_events e ON e.id = f.audit_event_id
29902
+ WHERE e.event_type IN ${CAPTURE_EVENT_TYPES_SQL2}`
29903
+ ).get();
29904
+ findingsTotal = agg.n;
29905
+ findingsFirstAt = agg.firstAt;
29906
+ findingsLastAt = agg.lastAt;
29907
+ } catch (err) {
29908
+ if (!isSchemaAbsent(err)) throw err;
29909
+ }
29910
+ return currentReadout();
29911
+ } catch {
29912
+ return emptyReadout(true);
29913
+ } finally {
29914
+ try {
29915
+ db?.close();
29916
+ } catch {
29917
+ }
29918
+ }
29919
+ }
29920
+
29921
+ // ../../packages/plugin-runtime/src/attached/posture-store.ts
29922
+ import { randomUUID as randomUUID17 } from "crypto";
29923
+ import { readFile as readFile3, rm as rm2, writeFile as writeFile3 } from "fs/promises";
29924
+ import { join as join20 } from "path";
29925
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
29926
+ function createPostureStore(dir = settingsDir(), legacyDir) {
29927
+ const file2 = join20(dir, "posture-state.json");
29928
+ const legacyFile = legacyDir === void 0 ? null : join20(legacyDir, "posture-state.json");
29929
+ async function persist(state) {
29930
+ await ensureDataDir(dir);
29931
+ const tmp = `${file2}.${randomUUID17()}.tmp`;
29932
+ try {
29933
+ await writeFile3(tmp, JSON.stringify(state), { encoding: "utf8", mode: DATA_FILE_MODE });
29934
+ await publishByRename(tmp, file2);
29935
+ } catch (err) {
29936
+ await rm2(tmp, { force: true }).catch(() => void 0);
29937
+ throw err;
29938
+ }
29939
+ }
29940
+ async function readFrom(path) {
29941
+ let raw;
29942
+ try {
29943
+ raw = await readFile3(path, "utf8");
29944
+ } catch (err) {
29945
+ const code = err.code;
29946
+ if (code === "ENOENT" || code === "ENOTDIR") return null;
29947
+ throw err;
29948
+ }
29949
+ try {
29950
+ const parsed2 = JSON.parse(raw);
29951
+ if (typeof parsed2 === "object" && parsed2 !== null) {
29952
+ const record2 = parsed2;
29953
+ if (typeof record2.deviceId === "string" && UUID_RE.test(record2.deviceId)) {
29954
+ const stamp = typeof record2.lastAttemptedAtMs === "number" ? record2.lastAttemptedAtMs : typeof record2.lastReportedAtMs === "number" ? record2.lastReportedAtMs : 0;
29955
+ return { deviceId: record2.deviceId, lastAttemptedAtMs: stamp };
29956
+ }
29957
+ }
29958
+ } catch {
29959
+ }
29960
+ return null;
29961
+ }
29962
+ async function read() {
29963
+ const current = await readFrom(file2);
29964
+ if (current) return current;
29965
+ const legacy = legacyFile === null || legacyFile === file2 ? null : await readFrom(legacyFile).catch(() => null);
29966
+ if (legacy) {
29967
+ try {
29968
+ await persist(legacy);
29969
+ } catch {
29970
+ }
29971
+ return legacy;
29972
+ }
29973
+ const fresh = { deviceId: randomUUID17(), lastAttemptedAtMs: 0 };
29974
+ try {
29975
+ await ensureDataDir(dir);
29976
+ if (createOwnerOnlyFileSync(file2, JSON.stringify(fresh))) return fresh;
29977
+ } catch {
29978
+ return null;
29979
+ }
29980
+ const winner = await readFrom(file2).catch(() => null);
29981
+ if (winner) return winner;
29982
+ try {
29983
+ await persist(fresh);
29984
+ } catch {
29985
+ return null;
29986
+ }
29987
+ return fresh;
29988
+ }
29989
+ async function markAttempted(deviceId, atMs) {
29990
+ await persist({ deviceId, lastAttemptedAtMs: atMs });
29991
+ }
29992
+ return { read, markAttempted, file: file2 };
29993
+ }
29994
+
29995
+ // ../../packages/plugin-runtime/src/attached/sync-state.ts
29996
+ import { readFileSync as readFileSync13 } from "fs";
29997
+ import { join as join21 } from "path";
29998
+
29999
+ // ../../packages/plugin-runtime/src/attached/status.ts
30000
+ var REFUSAL_LINES = {
30001
+ unauthorized: "KEY REJECTED \u2014 re-attach with a valid plugin key",
30002
+ forbidden: "ACCESS REFUSED \u2014 key is valid but not permitted; ask your org admin"
30003
+ };
30004
+ var OUTCOME_LINES = {
30005
+ ok: "policy synced",
30006
+ "not-modified": "policy up to date",
30007
+ unauthorized: REFUSAL_LINES.unauthorized,
30008
+ forbidden: REFUSAL_LINES.forbidden,
30009
+ unreachable: "control plane unreachable at last attempt",
30010
+ "invalid-bundle": "control plane sent a policy bundle this build cannot read"
30011
+ };
30012
+
30013
+ // ../../packages/plugin-runtime/src/attached/sync-trigger.ts
30014
+ import { spawn } from "child_process";
30015
+ import { fileURLToPath as fileURLToPath2 } from "url";
30016
+ var SYNC_THROTTLE_MS = 15 * 60 * 1e3;
30017
+
30018
+ // ../../packages/plugin-runtime/src/attached/factory.ts
30019
+ import { hostname as hostname5 } from "os";
30020
+
30021
+ // ../../packages/plugin-runtime/src/standalone-gateway.ts
30022
+ import { randomUUID as randomUUID18 } from "crypto";
30023
+
30024
+ // ../../packages/plugin-runtime/src/recorder.ts
30025
+ var PLUGIN_RECORDER_BINARY = "plugin";
30026
+
30027
+ // ../../packages/plugin-runtime/src/standalone-gateway.ts
30028
+ var StandaloneDataGateway = class {
30029
+ db;
30030
+ // Kept for the fingerprint key lookup (exception.key lives beside the store).
30031
+ dataDir;
30032
+ // One notice per gateway — see warnRulesetDiscarded.
30033
+ warnedRulesetDiscarded = false;
30034
+ constructor(dataDir2, detections = [], meta3) {
30035
+ this.db = openLocalDatabase(dataDir2);
30036
+ this.dataDir = dataDir2;
30037
+ this.db.installedPacks.recordInventory(detections, meta3);
30038
+ }
30039
+ recordCapture(record2) {
30040
+ this.db.recordCapture(record2.event, record2.findings);
30041
+ return Promise.resolve();
30042
+ }
30043
+ ensureInventory(ctx) {
30044
+ return Promise.resolve(this.db.ensureInventory(ctx));
30045
+ }
30046
+ recordAuditEvent(event) {
30047
+ this.db.auditEvents.insertAuditEvent(event);
30048
+ return Promise.resolve();
28575
30049
  }
28576
30050
  // The id is minted inside the repository from the natural key — the plugin can't
28577
30051
  // import @akasecurity/persistence to compute it, so the gateway is the boundary that
@@ -28586,12 +30060,12 @@ var StandaloneDataGateway = class {
28586
30060
  // reconciler drops the whole pass and recovers it idempotently on the next read.
28587
30061
  recordLlmCalls(inputs) {
28588
30062
  if (inputs.length === 0) return Promise.resolve();
28589
- return new Promise((resolve, reject) => {
30063
+ return new Promise((resolve2, reject) => {
28590
30064
  try {
28591
30065
  this.db.auditEvents.runInTransaction(() => {
28592
30066
  for (const input of inputs) this.db.auditEvents.insertLlmCall(input);
28593
30067
  });
28594
- resolve();
30068
+ resolve2();
28595
30069
  } catch (err) {
28596
30070
  reject(err instanceof Error ? err : new Error(String(err)));
28597
30071
  }
@@ -28603,12 +30077,12 @@ var StandaloneDataGateway = class {
28603
30077
  // drops the whole pass and recovers it idempotently next time.
28604
30078
  recordToolCalls(inputs) {
28605
30079
  if (inputs.length === 0) return Promise.resolve();
28606
- return new Promise((resolve, reject) => {
30080
+ return new Promise((resolve2, reject) => {
28607
30081
  try {
28608
30082
  this.db.auditEvents.runInTransaction(() => {
28609
30083
  for (const input of inputs) this.writeToolCall(input);
28610
30084
  });
28611
- resolve();
30085
+ resolve2();
28612
30086
  } catch (err) {
28613
30087
  reject(err instanceof Error ? err : new Error(String(err)));
28614
30088
  }
@@ -28750,7 +30224,7 @@ var StandaloneDataGateway = class {
28750
30224
  const customKeywords = [...new Set(policies.flatMap((p) => p.customKeywords ?? []))];
28751
30225
  const installed = this.installedScanRules();
28752
30226
  const rulePolicies = installed ? [...installed.ruleActions].map(([ruleId, action]) => ({
28753
- id: randomUUID15(),
30227
+ id: randomUUID18(),
28754
30228
  scope: "global",
28755
30229
  target: { ruleId },
28756
30230
  action,
@@ -28904,20 +30378,66 @@ var StandaloneDataGateway = class {
28904
30378
  }
28905
30379
  };
28906
30380
 
30381
+ // ../../packages/plugin-runtime/src/attached/factory.ts
30382
+ function resolveGatewayForConfig(config2, meta3) {
30383
+ const local = new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
30384
+ try {
30385
+ if (!isAttached(config2.settings)) return local;
30386
+ const connection = config2.settings.controlPlane;
30387
+ if (connection === void 0) return local;
30388
+ const state = readControlPlaneCredentialState(config2.settingsDir, connection);
30389
+ if (!state.usable) return local;
30390
+ const client = createRemoteClient({
30391
+ endpoint: connection.endpoint,
30392
+ apiKey: state.credential.apiKey
30393
+ });
30394
+ const store = createPolicyStore(config2.dataDir);
30395
+ const postureStore = createPostureStore(config2.settingsDir, config2.dataDir);
30396
+ const forward = createForwardPolicy({ dir: config2.dataDir });
30397
+ return new AttachedDataGateway({
30398
+ local,
30399
+ client,
30400
+ dataDir: config2.dataDir,
30401
+ readCachedBundle: () => store.read().then((cached2) => cached2?.bundle ?? null),
30402
+ forward,
30403
+ posture: createPostureReporter({
30404
+ // THROUGH THE BREAKER, and wrapped HERE rather than around
30405
+ // `PostureReporter.send`. The reporter swallows every error by
30406
+ // contract, so a wrap outside it would hand `forward.run` a resolved
30407
+ // promise for a send that failed — recording a SUCCESS, clearing
30408
+ // `consecutiveFailures` and `lastFailure`, and telling `aka status` the
30409
+ // forward recovered when nothing did. Wrapping the raw client call puts
30410
+ // the breaker above the swallow, where it can see the truth.
30411
+ //
30412
+ // What it buys: once the breaker is open — the plane already confirmed
30413
+ // down by the gateway's own writes — this stops paying a request
30414
+ // timeout per throttle interval to re-learn it.
30415
+ report: (snapshot) => forward.run(() => client.reportStorePosture(snapshot)).then(() => void 0),
30416
+ store: postureStore,
30417
+ readStore: () => readStorePosture(config2.dbPath),
30418
+ hostname: () => hostname5(),
30419
+ now: () => Date.now()
30420
+ })
30421
+ });
30422
+ } catch {
30423
+ return local;
30424
+ }
30425
+ }
30426
+
28907
30427
  // ../../packages/plugin-runtime/src/resolve.ts
28908
- var standaloneGatewayFactory = (config2, meta3) => new StandaloneDataGateway(config2.dataDir, bundledDetections(), meta3);
28909
- var defaultGatewayFactory = standaloneGatewayFactory;
30428
+ var configuredGatewayFactory = (config2, meta3) => resolveGatewayForConfig(config2, meta3);
30429
+ var defaultGatewayFactory = configuredGatewayFactory;
28910
30430
  function resolveDataGateway(config2, meta3, gatewayFactory = defaultGatewayFactory) {
28911
30431
  return gatewayFactory(config2, meta3);
28912
30432
  }
28913
30433
 
28914
30434
  // ../../packages/plugin-runtime/src/handle-session-start.ts
28915
- import { randomUUID as randomUUID16 } from "crypto";
30435
+ import { randomUUID as randomUUID19 } from "crypto";
28916
30436
  var EXCEPTION_RETENTION_MS = 90 * 24 * 60 * 60 * 1e3;
28917
30437
 
28918
30438
  // src/hooks/shared.ts
28919
30439
  async function readStdin() {
28920
- return new Promise((resolve) => {
30440
+ return new Promise((resolve2) => {
28921
30441
  let data = "";
28922
30442
  let settled = false;
28923
30443
  const finish = () => {
@@ -28926,7 +30446,7 @@ async function readStdin() {
28926
30446
  clearTimeout(timer);
28927
30447
  process.stdin.removeListener("data", onData);
28928
30448
  process.stdin.removeListener("end", finish);
28929
- resolve(data);
30449
+ resolve2(data);
28930
30450
  };
28931
30451
  const onData = (chunk) => {
28932
30452
  data += chunk;
@@ -28941,7 +30461,7 @@ async function readStdin() {
28941
30461
 
28942
30462
  // ../../packages/setup-wizard/src/remediation/rotation-checklist.ts
28943
30463
  import { writeFileSync as writeFileSync7 } from "fs";
28944
- import { join as join15 } from "path";
30464
+ import { join as join22 } from "path";
28945
30465
 
28946
30466
  // ../../packages/setup-wizard/src/triage/merge.ts
28947
30467
  var RANK = Object.fromEntries(
@@ -28949,9 +30469,9 @@ var RANK = Object.fromEntries(
28949
30469
  );
28950
30470
 
28951
30471
  // ../../packages/setup-wizard/src/triage/plan-file.ts
28952
- import { mkdtempSync, readFileSync as readFileSync10, rmdirSync, rmSync as rmSync5, writeFileSync as writeFileSync8 } from "fs";
30472
+ import { mkdtempSync, readFileSync as readFileSync14, rmdirSync, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
28953
30473
  import { tmpdir } from "os";
28954
- import { basename as basename6, dirname as dirname4, join as join16 } from "path";
30474
+ import { basename as basename6, dirname as dirname5, join as join23 } from "path";
28955
30475
  var SuppressionEntrySchema = external_exports.object({
28956
30476
  ruleId: external_exports.string(),
28957
30477
  category: DetectionCategory,
@@ -28994,8 +30514,8 @@ var PersistedPlanSchema = external_exports.object({
28994
30514
 
28995
30515
  // src/command-registry.ts
28996
30516
  import { readdirSync as readdirSync5 } from "fs";
28997
- import { fileURLToPath as fileURLToPath2 } from "url";
28998
- var COMMANDS_DIR = fileURLToPath2(new URL("../commands", import.meta.url));
30517
+ import { fileURLToPath as fileURLToPath3 } from "url";
30518
+ var COMMANDS_DIR = fileURLToPath3(new URL("../commands", import.meta.url));
28999
30519
 
29000
30520
  // src/present.ts
29001
30521
  var SHADE = {