@pretense/cli 0.6.20 → 0.6.22

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.
@@ -17,7 +17,7 @@ function versionFromPackageJson() {
17
17
  return void 0;
18
18
  }
19
19
  }
20
- var PRETENSE_VERSION = (true ? "0.6.20" : void 0) ?? versionFromPackageJson() ?? "0.0.0-unknown";
20
+ var PRETENSE_VERSION = (true ? "0.6.22" : void 0) ?? versionFromPackageJson() ?? "0.0.0-unknown";
21
21
 
22
22
  export {
23
23
  PRETENSE_VERSION
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRETENSE_VERSION
4
- } from "./chunk-N2GZIZAD.js";
4
+ } from "./chunk-BWS6UMKL.js";
5
5
  import {
6
6
  __commonJS,
7
7
  __toESM,
@@ -14472,14 +14472,16 @@ async function scanWithPresidio(text, presidioUrl, opts = {}) {
14472
14472
 
14473
14473
  // ../packages/mutator/dist/index.js
14474
14474
  init_esm_shims();
14475
+ import { existsSync as existsSync4, readFileSync as readFileSync32 } from "fs";
14476
+ import { join as join4 } from "path";
14475
14477
  import { createHmac, hkdfSync, randomBytes as randomBytes2 } from "crypto";
14476
14478
  import { createHash, randomBytes } from "crypto";
14477
14479
  import { readFileSync as readFileSync4, existsSync as existsSync22 } from "fs";
14478
14480
  import { homedir as homedir2 } from "os";
14479
14481
  import { join as join22, dirname, parse, resolve } from "path";
14480
- import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
14482
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
14481
14483
  import { homedir } from "os";
14482
- import { join as join4 } from "path";
14484
+ import { join as join5 } from "path";
14483
14485
  var DIR_MODE = 448;
14484
14486
  var FILE_MODE = 384;
14485
14487
  var POSIX_MODES_ENFORCED = process.platform !== "win32";
@@ -14501,10 +14503,10 @@ var PretenseHomeError = class extends Error {
14501
14503
  }
14502
14504
  };
14503
14505
  function pretenseHome() {
14504
- return join4(homedir(), ".pretense");
14506
+ return join5(homedir(), ".pretense");
14505
14507
  }
14506
14508
  function ensureDirOwnerOnly(dir) {
14507
- const preExisting = existsSync4(dir);
14509
+ const preExisting = existsSync5(dir);
14508
14510
  if (!preExisting) {
14509
14511
  try {
14510
14512
  mkdirSync2(dir, { recursive: true, mode: DIR_MODE });
@@ -15634,6 +15636,79 @@ function mutate(code, language, seed = "pretense", opts = {}) {
15634
15636
  }
15635
15637
  };
15636
15638
  }
15639
+ function mutateNamedIdentifiers(code, language, identifiers, seed = "pretense", opts = {}) {
15640
+ const t0 = performance.now();
15641
+ const lang = language ?? detectLanguage(code);
15642
+ const map = /* @__PURE__ */ new Map();
15643
+ if (!code?.trim() || identifiers.length === 0) {
15644
+ return { mutatedCode: code ?? "", map, stats: { tokensScanned: identifiers.length, tokensMutated: 0, durationMs: 0, language: lang } };
15645
+ }
15646
+ const signingKey = resolveSigningKey(opts);
15647
+ const usedSynthetics = /* @__PURE__ */ new Set();
15648
+ for (const { name, category } of identifiers) {
15649
+ if (!name || name.length <= 1 || SKIP_IDENTIFIERS2.has(name)) continue;
15650
+ if (!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name)) continue;
15651
+ if (map.has(name)) continue;
15652
+ let synthetic = generateSyntheticName(name, seed, category, signingKey);
15653
+ let attempt = 0;
15654
+ while ((usedSynthetics.has(synthetic) || synthetic === name || code.includes(synthetic)) && attempt < 100) {
15655
+ attempt++;
15656
+ synthetic = generateSyntheticName(name, `${seed}:${attempt}`, category, signingKey);
15657
+ }
15658
+ if (synthetic !== name && !usedSynthetics.has(synthetic) && !code.includes(synthetic)) {
15659
+ map.set(name, synthetic);
15660
+ usedSynthetics.add(synthetic);
15661
+ }
15662
+ }
15663
+ const entries = [...map.entries()].sort((a, b) => b[0].length - a[0].length);
15664
+ let mutatedCode = code;
15665
+ const applied = /* @__PURE__ */ new Map();
15666
+ for (const [original, synthetic] of entries) {
15667
+ const next = replaceAll(mutatedCode, [[original, synthetic]], lang, true);
15668
+ if (next !== mutatedCode) {
15669
+ applied.set(original, synthetic);
15670
+ mutatedCode = next;
15671
+ }
15672
+ }
15673
+ return {
15674
+ mutatedCode,
15675
+ map: applied,
15676
+ stats: {
15677
+ tokensScanned: identifiers.length,
15678
+ tokensMutated: applied.size,
15679
+ durationMs: Math.round((performance.now() - t0) * 100) / 100,
15680
+ language: lang
15681
+ }
15682
+ };
15683
+ }
15684
+ var FINGERPRINT_CATEGORIES = [
15685
+ ["functions", "function"],
15686
+ ["classes", "class"],
15687
+ ["constants", "constant"],
15688
+ ["envVars", "variable"]
15689
+ ];
15690
+ function loadFingerprintIdentifiers(projectRoot) {
15691
+ const path2 = join4(projectRoot, ".pretense", "fingerprint.json");
15692
+ if (!existsSync4(path2)) return [];
15693
+ let parsed;
15694
+ try {
15695
+ parsed = JSON.parse(readFileSync32(path2, "utf-8"));
15696
+ } catch {
15697
+ return [];
15698
+ }
15699
+ const out = [];
15700
+ const seen = /* @__PURE__ */ new Set();
15701
+ for (const [key, category] of FINGERPRINT_CATEGORIES) {
15702
+ const arr = parsed[key];
15703
+ if (!Array.isArray(arr)) continue;
15704
+ for (const name of arr) {
15705
+ if (typeof name !== "string" || seen.has(name)) continue;
15706
+ seen.add(name);
15707
+ out.push({ name, category });
15708
+ }
15709
+ }
15710
+ return out;
15711
+ }
15637
15712
  function reverse(mutatedCode, map) {
15638
15713
  if (!mutatedCode || map.size === 0) return mutatedCode;
15639
15714
  assertNoReplacementCollision(map.entries());
@@ -15940,7 +16015,7 @@ var RiskScorer = class {
15940
16015
  // ../packages/store/dist/index.js
15941
16016
  init_esm_shims();
15942
16017
  import { createRequire as createRequire2 } from "module";
15943
- import { mkdirSync as mkdirSync22, existsSync as existsSync5, chmodSync as chmodSync2, statSync as statSync2 } from "fs";
16018
+ import { mkdirSync as mkdirSync22, existsSync as existsSync6, chmodSync as chmodSync2, statSync as statSync2 } from "fs";
15944
16019
  import { dirname as dirname2 } from "path";
15945
16020
  import { homedir as homedir22 } from "os";
15946
16021
  var nodeRequire = createRequire2(import.meta.url);
@@ -16001,7 +16076,7 @@ function openDatabase(path2) {
16001
16076
  return sqliteBackend === "bun:sqlite" ? openBun(path2) : openBetterSqlite3(path2);
16002
16077
  }
16003
16078
  function ensureDbDirOwnerOnly(dir) {
16004
- const preExisting = existsSync5(dir);
16079
+ const preExisting = existsSync6(dir);
16005
16080
  if (!preExisting) mkdirSync22(dir, { recursive: true, mode: 448 });
16006
16081
  if (process.platform === "win32") return;
16007
16082
  try {
@@ -16051,7 +16126,7 @@ var AuditStore = class {
16051
16126
  if (!isMemory) {
16052
16127
  for (const p of [resolvedPath, `${resolvedPath}-wal`, `${resolvedPath}-shm`]) {
16053
16128
  try {
16054
- if (existsSync5(p)) chmodSync2(p, 384);
16129
+ if (existsSync6(p)) chmodSync2(p, 384);
16055
16130
  } catch {
16056
16131
  }
16057
16132
  }
@@ -16432,8 +16507,8 @@ var PERIOD_DAYS = 7;
16432
16507
  var PERIOD_MS = PERIOD_DAYS * 24 * 60 * 60 * 1e3;
16433
16508
 
16434
16509
  // ../apps/proxy/dist/server.js
16435
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
16436
- import { join as join5 } from "path";
16510
+ import { readFileSync as readFileSync5, existsSync as existsSync7 } from "fs";
16511
+ import { join as join6 } from "path";
16437
16512
  import { parse as parseYaml } from "yaml";
16438
16513
  import { readFileSync as readFileSync23 } from "fs";
16439
16514
  import { fileURLToPath as fileURLToPath3 } from "url";
@@ -16531,12 +16606,12 @@ var DEFAULTS = {
16531
16606
  };
16532
16607
  function loadConfig(configPath) {
16533
16608
  const paths = configPath ? [configPath] : [
16534
- join5(process.cwd(), "pretense.yaml"),
16535
- join5(process.cwd(), "pretense.yml"),
16536
- join5(process.env["HOME"] ?? "/tmp", ".pretense", "config.yaml")
16609
+ join6(process.cwd(), "pretense.yaml"),
16610
+ join6(process.cwd(), "pretense.yml"),
16611
+ join6(process.env["HOME"] ?? "/tmp", ".pretense", "config.yaml")
16537
16612
  ];
16538
16613
  for (const p of paths) {
16539
- if (existsSync6(p)) {
16614
+ if (existsSync7(p)) {
16540
16615
  try {
16541
16616
  const raw2 = parseYaml(readFileSync5(p, "utf-8"));
16542
16617
  return deepMerge(DEFAULTS, raw2);
@@ -16582,11 +16657,11 @@ function versionFromCliPackageJson() {
16582
16657
  return void 0;
16583
16658
  }
16584
16659
  function productVersion() {
16585
- return override ?? (true ? "0.6.20" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16660
+ return override ?? (true ? "0.6.22" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16586
16661
  }
16587
16662
  var UNKNOWN_COMMIT = "unknown";
16588
16663
  function bakedCommitSha() {
16589
- return "69ea69288cc5d8698de1a2bdb4e3c29c002596c6".length > 0 ? "69ea69288cc5d8698de1a2bdb4e3c29c002596c6" : UNKNOWN_COMMIT;
16664
+ return "6c0840ef4a9ecc06829215df3a27c5a1b1b603ab".length > 0 ? "6c0840ef4a9ecc06829215df3a27c5a1b1b603ab" : UNKNOWN_COMMIT;
16590
16665
  }
16591
16666
  var FEATURE_TIERS = {
16592
16667
  compliance_export: "pro",
@@ -17591,6 +17666,7 @@ function createProxy(opts = {}) {
17591
17666
  }
17592
17667
  const app = new Hono2();
17593
17668
  const derivation = { projectRoot: resolveProxyProjectRoot(opts.projectRoot) };
17669
+ const fingerprintIdentifiers = loadFingerprintIdentifiers(derivation.projectRoot);
17594
17670
  let auditWriteFailures = 0;
17595
17671
  let auditWriteFailureLogged = false;
17596
17672
  const safeLog = (entry) => {
@@ -17958,9 +18034,16 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
17958
18034
  continue;
17959
18035
  }
17960
18036
  const result = mutate(block.code, block.language, "pretense", derivation);
18037
+ let blockCode = result.mutatedCode;
17961
18038
  for (const [k, v] of result.map.entries()) globalMap.set(k, v);
17962
18039
  tokensMutated += result.stats.tokensMutated;
17963
- mutatedBlocks.push(result.mutatedCode);
18040
+ if (fingerprintIdentifiers.length > 0) {
18041
+ const fp = mutateNamedIdentifiers(blockCode, block.language, fingerprintIdentifiers, "pretense", derivation);
18042
+ for (const [k, v] of fp.map.entries()) globalMap.set(k, v);
18043
+ tokensMutated += fp.stats.tokensMutated;
18044
+ blockCode = fp.mutatedCode;
18045
+ }
18046
+ mutatedBlocks.push(blockCode);
17964
18047
  }
17965
18048
  return replaceCodeBlocks(text, blocks, mutatedBlocks);
17966
18049
  });
@@ -18006,7 +18089,12 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
18006
18089
  action: "blocked",
18007
18090
  secretsBlocked: secretsTokenized,
18008
18091
  piiRedacted: piiTokenized,
18009
- mutationCount: tokensMutated,
18092
+ // TRUTHFUL COUNT: every span this proxy rewrote before egress, of any
18093
+ // kind (identifier tokens + tokenized secrets/PII) — the SAME number the
18094
+ // audit row logs as `mutationsApplied`. `tokensMutated` alone counted
18095
+ // only code identifiers, so a secret-only mutation pushed mutationCount:0
18096
+ // to the live dashboard even though a secret WAS mutated on the wire.
18097
+ mutationCount: mutationsApplied,
18010
18098
  bytesScanned: fullText.length,
18011
18099
  provider,
18012
18100
  findings: riskResult.factors.map((f) => f.name),
@@ -18088,7 +18176,12 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
18088
18176
  action: "error",
18089
18177
  secretsBlocked: secretsTokenized,
18090
18178
  piiRedacted: piiTokenized,
18091
- mutationCount: tokensMutated,
18179
+ // TRUTHFUL COUNT: every span this proxy rewrote before egress, of any
18180
+ // kind (identifier tokens + tokenized secrets/PII) — the SAME number the
18181
+ // audit row logs as `mutationsApplied`. `tokensMutated` alone counted
18182
+ // only code identifiers, so a secret-only mutation pushed mutationCount:0
18183
+ // to the live dashboard even though a secret WAS mutated on the wire.
18184
+ mutationCount: mutationsApplied,
18092
18185
  bytesScanned: originalSize,
18093
18186
  provider,
18094
18187
  findings: ["upstream_unreachable"],
@@ -18127,12 +18220,23 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
18127
18220
  action: anyTokenized ? "mutated" : "allowed",
18128
18221
  secretsBlocked: secretsTokenized,
18129
18222
  piiRedacted: piiTokenized,
18130
- mutationCount: tokensMutated,
18223
+ // TRUTHFUL COUNT: every span rewritten before egress (identifier tokens +
18224
+ // tokenized secrets/PII), matching the audit row's `mutationsApplied`. Using
18225
+ // `tokensMutated` here made the live dashboard show mutationCount:0 for a
18226
+ // request whose only mutation was a tokenized secret — the exact "meter logs
18227
+ // 0" defect. severity now reflects it too (mutationCount>0 -> 3, not 1).
18228
+ mutationCount: mutationsApplied,
18131
18229
  bytesScanned: originalSize,
18132
18230
  provider,
18133
- // Surface the skip in the telemetry stream too, so a dashboard/SIEM can
18134
- // alert on unscannable egress fields rather than assume full coverage.
18135
- ...skippedUnscannable > 0 ? { findings: ["skipped_unscannable_field"] } : {},
18231
+ // Detector KINDS that fired (e.g. "aws_access_key") metadata only, the same
18232
+ // kinds the audit row records as `detectorKinds`. NEVER secret VALUES or
18233
+ // identifier NAMES (the SSE bus carries the usage-upload privacy contract).
18234
+ // Plus the unscannable-skip marker so a dashboard/SIEM can alert on coverage
18235
+ // gaps rather than assume full coverage.
18236
+ findings: [
18237
+ ...foundTypes,
18238
+ ...skippedUnscannable > 0 ? ["skipped_unscannable_field"] : []
18239
+ ],
18136
18240
  sourceIp: c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip")
18137
18241
  }));
18138
18242
  if (anyTokenized) {
@@ -18263,11 +18367,11 @@ async function findFreePort(start, opts = {}) {
18263
18367
 
18264
18368
  // src/proxy-pidfile.ts
18265
18369
  init_esm_shims();
18266
- import { existsSync as existsSync7, readFileSync as readFileSync6, unlinkSync } from "fs";
18267
- import { join as join6 } from "path";
18370
+ import { existsSync as existsSync8, readFileSync as readFileSync6, unlinkSync } from "fs";
18371
+ import { join as join7 } from "path";
18268
18372
  import { homedir as homedir3 } from "os";
18269
18373
  function pidFilePath(home = homedir3()) {
18270
- return join6(process.env.PRETENSE_PID_DIR ?? join6(home, ".pretense"), "proxy.pid");
18374
+ return join7(process.env.PRETENSE_PID_DIR ?? join7(home, ".pretense"), "proxy.pid");
18271
18375
  }
18272
18376
  function isProcessAlive(pid) {
18273
18377
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -18279,7 +18383,7 @@ function isProcessAlive(pid) {
18279
18383
  }
18280
18384
  }
18281
18385
  function readProxyRecord(path2 = pidFilePath()) {
18282
- if (!existsSync7(path2)) return null;
18386
+ if (!existsSync8(path2)) return null;
18283
18387
  try {
18284
18388
  const parsed = JSON.parse(readFileSync6(path2, "utf-8"));
18285
18389
  if (!Number.isInteger(parsed.pid) || parsed.pid <= 0) return null;
@@ -18306,7 +18410,7 @@ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
18306
18410
  async function stopProxy(path2 = pidFilePath(), graceMs = 2e3, pollMs = 50) {
18307
18411
  const rec = readProxyRecord(path2);
18308
18412
  if (!rec) {
18309
- if (existsSync7(path2)) {
18413
+ if (existsSync8(path2)) {
18310
18414
  removeProxyRecord(path2);
18311
18415
  return { kind: "stale", pid: 0, path: path2 };
18312
18416
  }
@@ -18479,17 +18583,17 @@ import chalk3 from "chalk";
18479
18583
 
18480
18584
  // src/plan-usage.ts
18481
18585
  init_esm_shims();
18482
- import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
18483
- import { join as join7 } from "path";
18586
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
18587
+ import { join as join8 } from "path";
18484
18588
  import { homedir as homedir4 } from "os";
18485
18589
  function credentialsPath() {
18486
- return join7(homedir4(), ".pretense", "credentials.json");
18590
+ return join8(homedir4(), ".pretense", "credentials.json");
18487
18591
  }
18488
18592
  function readApiKey() {
18489
18593
  const fromEnv = process.env["PRETENSE_API_KEY"]?.trim();
18490
18594
  if (fromEnv) return fromEnv;
18491
18595
  const path2 = credentialsPath();
18492
- if (!existsSync8(path2)) return null;
18596
+ if (!existsSync9(path2)) return null;
18493
18597
  try {
18494
18598
  const creds = JSON.parse(readFileSync7(path2, "utf-8"));
18495
18599
  const key = creds["apiKey"] ?? creds["api_key"];
@@ -18911,7 +19015,7 @@ import { Command as Command5, Option, InvalidArgumentError as InvalidArgumentErr
18911
19015
  import chalk5 from "chalk";
18912
19016
  import { readFileSync as readFileSync8, statSync as statSync3, readdirSync as readdirSync2, openSync, readSync, closeSync } from "fs";
18913
19017
  import { execSync as execSync3 } from "child_process";
18914
- import { resolve as resolve3, join as join8, extname as extname2, basename } from "path";
19018
+ import { resolve as resolve3, join as join9, extname as extname2, basename } from "path";
18915
19019
 
18916
19020
  // src/safe-output.ts
18917
19021
  init_esm_shims();
@@ -19101,9 +19205,9 @@ function walkDir(dir) {
19101
19205
  for (const entry of entries) {
19102
19206
  if (entry.isDirectory()) {
19103
19207
  if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
19104
- files.push(...walkDir(join8(dir, entry.name)));
19208
+ files.push(...walkDir(join9(dir, entry.name)));
19105
19209
  } else if (entry.isFile()) {
19106
- files.push(join8(dir, entry.name));
19210
+ files.push(join9(dir, entry.name));
19107
19211
  }
19108
19212
  }
19109
19213
  return files;
@@ -19827,13 +19931,13 @@ function scanCommand() {
19827
19931
  init_esm_shims();
19828
19932
  import { Command as Command6 } from "commander";
19829
19933
  import chalk6 from "chalk";
19830
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
19831
- import { join as join9 } from "path";
19934
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
19935
+ import { join as join10 } from "path";
19832
19936
  function statusCommand() {
19833
19937
  return new Command6("status").description("Show Pretense protection status").action(async () => {
19834
19938
  const dir = process.cwd();
19835
- const fingerprintPath = join9(dir, ".pretense", "fingerprint.json");
19836
- const configPath = join9(dir, "pretense.yaml");
19939
+ const fingerprintPath = join10(dir, ".pretense", "fingerprint.json");
19940
+ const configPath = join10(dir, "pretense.yaml");
19837
19941
  console.log(chalk6.cyan("\n Pretense Status\n"));
19838
19942
  const record = readProxyRecord(pidFilePath());
19839
19943
  if (record && !isProcessAlive(record.pid)) {
@@ -19855,12 +19959,12 @@ function statusCommand() {
19855
19959
  console.log(chalk6.yellow(` \u25CB Proxy not running ${chalk6.dim("(run: pretense start)")}`));
19856
19960
  }
19857
19961
  }
19858
- if (existsSync9(configPath)) {
19962
+ if (existsSync10(configPath)) {
19859
19963
  console.log(chalk6.green(` \u2713 Config: pretense.yaml`));
19860
19964
  } else {
19861
19965
  console.log(chalk6.yellow(` \u25CB No config ${chalk6.dim("(run: pretense config)")}`));
19862
19966
  }
19863
- if (existsSync9(fingerprintPath)) {
19967
+ if (existsSync10(fingerprintPath)) {
19864
19968
  try {
19865
19969
  const fp = JSON.parse(readFileSync9(fingerprintPath, "utf-8"));
19866
19970
  const age = Math.round((Date.now() - fp.scannedAt) / (60 * 1e3));
@@ -19882,8 +19986,8 @@ function statusCommand() {
19882
19986
  init_esm_shims();
19883
19987
  import { Command as Command7 } from "commander";
19884
19988
  import chalk7 from "chalk";
19885
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
19886
- import { join as join10 } from "path";
19989
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync10, existsSync as existsSync11 } from "fs";
19990
+ import { join as join11 } from "path";
19887
19991
  import YAML2 from "yaml";
19888
19992
  var DEFAULT_CONFIG = {
19889
19993
  version: 1,
@@ -19896,8 +20000,8 @@ function findConfigPath() {
19896
20000
  const cwd = process.cwd();
19897
20001
  const candidates = [".pretense.yaml", "pretense.yaml"];
19898
20002
  for (const name of candidates) {
19899
- const full = join10(cwd, name);
19900
- if (existsSync10(full)) return full;
20003
+ const full = join11(cwd, name);
20004
+ if (existsSync11(full)) return full;
19901
20005
  }
19902
20006
  return null;
19903
20007
  }
@@ -19912,7 +20016,7 @@ function loadConfig2() {
19912
20016
  }
19913
20017
  }
19914
20018
  function configFilePath() {
19915
- return findConfigPath() ?? join10(process.cwd(), "pretense.yaml");
20019
+ return findConfigPath() ?? join11(process.cwd(), "pretense.yaml");
19916
20020
  }
19917
20021
  function getNestedValue(obj, key) {
19918
20022
  const parts = key.split(".");
@@ -19983,7 +20087,7 @@ function configCommand() {
19983
20087
  });
19984
20088
  cmd.option("--force", "Overwrite existing config with defaults", false).action((opts) => {
19985
20089
  if (opts.force) {
19986
- const outPath = join10(process.cwd(), "pretense.yaml");
20090
+ const outPath = join11(process.cwd(), "pretense.yaml");
19987
20091
  writeFileSync4(outPath, YAML2.stringify(DEFAULT_CONFIG));
19988
20092
  console.log(chalk7.green(`
19989
20093
  \u2713 Created pretense.yaml
@@ -20000,14 +20104,14 @@ init_esm_shims();
20000
20104
  import { Command as Command8 } from "commander";
20001
20105
  import chalk8 from "chalk";
20002
20106
  import { homedir as homedir5 } from "os";
20003
- import { join as join11 } from "path";
20107
+ import { join as join12 } from "path";
20004
20108
  function logsCommand() {
20005
20109
  return new Command8("logs").description("Show recent audit log entries").option("-l, --limit <n>", "Number of entries to show", "50").option("--json", "Output as JSON", false).action((opts) => {
20006
20110
  const limit = parseInt(opts.limit, 10);
20007
20111
  let store;
20008
20112
  let entries;
20009
20113
  try {
20010
- store = new AuditStore(join11(homedir5(), ".pretense", "audit.db"));
20114
+ store = new AuditStore(join12(homedir5(), ".pretense", "audit.db"));
20011
20115
  entries = store.getRecentEntries(limit);
20012
20116
  store.close();
20013
20117
  } catch {
@@ -20047,7 +20151,7 @@ function logsCommand() {
20047
20151
  init_esm_shims();
20048
20152
  import { Command as Command9 } from "commander";
20049
20153
  import chalk9 from "chalk";
20050
- import { join as join12 } from "path";
20154
+ import { join as join13 } from "path";
20051
20155
  import { homedir as homedir6 } from "os";
20052
20156
  function renderBar(percent, width = 30) {
20053
20157
  const filled = Math.min(Math.round(percent / 100 * width), width);
@@ -20087,7 +20191,7 @@ function emptyUsagePayload(resolution) {
20087
20191
  };
20088
20192
  }
20089
20193
  function usageCommand() {
20090
- return new Command9("usage").description("Show plan usage vs limits for the current billing period").option("--db <path>", "Path to audit database", join12(homedir6(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
20194
+ return new Command9("usage").description("Show plan usage vs limits for the current billing period").option("--db <path>", "Path to audit database", join13(homedir6(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
20091
20195
  const resolution = await resolvePlanTier({ offline: opts.offline });
20092
20196
  const tier = resolution.tier;
20093
20197
  const collected = collectUsage(opts.db);
@@ -20238,12 +20342,12 @@ function usageCommand() {
20238
20342
  init_esm_shims();
20239
20343
  import { Command as Command10 } from "commander";
20240
20344
  import chalk10 from "chalk";
20241
- import { existsSync as existsSync11, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "fs";
20242
- import { join as join13 } from "path";
20345
+ import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "fs";
20346
+ import { join as join14 } from "path";
20243
20347
  import { homedir as homedir7 } from "os";
20244
20348
  import { execSync as execSync4 } from "child_process";
20245
- var CREDENTIALS_DIR = join13(homedir7(), ".pretense");
20246
- var CREDENTIALS_PATH = join13(CREDENTIALS_DIR, "credentials.json");
20349
+ var CREDENTIALS_DIR = join14(homedir7(), ".pretense");
20350
+ var CREDENTIALS_PATH = join14(CREDENTIALS_DIR, "credentials.json");
20247
20351
  async function verifyApiKey(apiKey) {
20248
20352
  const url = `${getConfiguredApiRoot()}/api/cli/auth/validate`;
20249
20353
  const timeoutMs = tierRequestTimeoutMs();
@@ -20290,7 +20394,7 @@ function storageNotice() {
20290
20394
  `);
20291
20395
  }
20292
20396
  function readCredentials() {
20293
- if (!existsSync11(CREDENTIALS_PATH)) return null;
20397
+ if (!existsSync12(CREDENTIALS_PATH)) return null;
20294
20398
  try {
20295
20399
  return JSON.parse(readFileSync11(CREDENTIALS_PATH, "utf-8"));
20296
20400
  } catch {
@@ -20424,7 +20528,7 @@ function authCommand() {
20424
20528
  openUrl(authUrl);
20425
20529
  });
20426
20530
  auth.command("logout").description("Remove stored credentials").action(() => {
20427
- if (!existsSync11(CREDENTIALS_PATH)) {
20531
+ if (!existsSync12(CREDENTIALS_PATH)) {
20428
20532
  console.log(chalk10.yellow("\n No credentials found. Already logged out.\n"));
20429
20533
  return;
20430
20534
  }
@@ -20479,7 +20583,7 @@ init_esm_shims();
20479
20583
  import { Command as Command11 } from "commander";
20480
20584
  import chalk11 from "chalk";
20481
20585
  import { readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
20482
- import { join as join14, extname as extname3, relative, isAbsolute } from "path";
20586
+ import { join as join15, extname as extname3, relative, isAbsolute } from "path";
20483
20587
  var SUPPORTED_EXTENSIONS2 = /* @__PURE__ */ new Set([
20484
20588
  ".ts",
20485
20589
  ".tsx",
@@ -20514,7 +20618,7 @@ function discoverFiles(dir) {
20514
20618
  }
20515
20619
  for (const entry of entries) {
20516
20620
  if (entry.startsWith(".")) continue;
20517
- const full = join14(current, entry);
20621
+ const full = join15(current, entry);
20518
20622
  let stat;
20519
20623
  try {
20520
20624
  stat = statSync4(full);
@@ -20605,7 +20709,7 @@ function formatAgent(results, basePath) {
20605
20709
  const agentResults = [];
20606
20710
  for (const r of results) {
20607
20711
  if (r.mutations.length === 0 && r.secrets.length === 0) continue;
20608
- const filePath = join14(basePath, r.file);
20712
+ const filePath = join15(basePath, r.file);
20609
20713
  let lines;
20610
20714
  try {
20611
20715
  lines = readFileSync12(filePath, "utf-8").split("\n");
@@ -20676,7 +20780,7 @@ function formatInteractive(results) {
20676
20780
  }
20677
20781
  function reviewCommand() {
20678
20782
  return new Command11("review").description("Preview mutations and secrets that would be applied").argument("[path]", "Directory to review (defaults to current directory)").option("--plain", "Plain text output", false).option("--json", "JSON array output with metadata", false).option("--agent", "Structured JSON optimized for AI agent consumption", false).option("--prompt-only", "Minimal output suitable for piping", false).option("--interactive", "Interactive formatted output (default)", false).action(async (path2, opts) => {
20679
- const targetDir = path2 ? isAbsolute(path2) ? path2 : join14(process.cwd(), path2) : process.cwd();
20783
+ const targetDir = path2 ? isAbsolute(path2) ? path2 : join15(process.cwd(), path2) : process.cwd();
20680
20784
  const files = discoverFiles(targetDir);
20681
20785
  if (files.length === 0) {
20682
20786
  if (opts?.json || opts?.agent) {
@@ -20712,13 +20816,13 @@ init_esm_shims();
20712
20816
  import { Command as Command12 } from "commander";
20713
20817
  import chalk12 from "chalk";
20714
20818
  import {
20715
- existsSync as existsSync12,
20819
+ existsSync as existsSync13,
20716
20820
  writeFileSync as writeFileSync5,
20717
20821
  chmodSync as chmodSync3,
20718
20822
  readFileSync as readFileSync13,
20719
20823
  mkdirSync as mkdirSync3
20720
20824
  } from "fs";
20721
- import { join as join15, resolve as resolve4 } from "path";
20825
+ import { join as join16, resolve as resolve4 } from "path";
20722
20826
  var HOOK_SCRIPTS = {
20723
20827
  "pre-commit": `#!/bin/sh
20724
20828
  # Pretense AI Firewall \u2014 pre-commit hook
@@ -20737,14 +20841,14 @@ exit $?
20737
20841
  };
20738
20842
  var VALID_MODES = ["pre-commit", "pre-push"];
20739
20843
  function findGitDir(cwd) {
20740
- const gitDir = join15(cwd, ".git");
20741
- if (existsSync12(gitDir)) return gitDir;
20844
+ const gitDir = join16(cwd, ".git");
20845
+ if (existsSync13(gitDir)) return gitDir;
20742
20846
  return null;
20743
20847
  }
20744
20848
  function installHook(hooksDir, mode, force) {
20745
- const hookPath = join15(hooksDir, mode);
20849
+ const hookPath = join16(hooksDir, mode);
20746
20850
  const script = HOOK_SCRIPTS[mode];
20747
- if (existsSync12(hookPath) && !force) {
20851
+ if (existsSync13(hookPath) && !force) {
20748
20852
  const existing = readFileSync13(hookPath, "utf-8");
20749
20853
  if (existing.includes("Pretense AI Firewall")) {
20750
20854
  return { installed: false, path: hookPath, skipped: true };
@@ -20786,7 +20890,7 @@ function installCommand() {
20786
20890
  );
20787
20891
  process.exit(1);
20788
20892
  }
20789
- const hooksDir = join15(gitDir, "hooks");
20893
+ const hooksDir = join16(gitDir, "hooks");
20790
20894
  mkdirSync3(hooksDir, { recursive: true });
20791
20895
  console.log(chalk12.cyan("\n Pretense hook installer\n"));
20792
20896
  let hasError = false;
@@ -20823,22 +20927,22 @@ function installCommand() {
20823
20927
  init_esm_shims();
20824
20928
  import { Command as Command13 } from "commander";
20825
20929
  import chalk13 from "chalk";
20826
- import { appendFileSync as appendFileSync3, readFileSync as readFileSync14, existsSync as existsSync13 } from "fs";
20827
- import { join as join16 } from "path";
20930
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
20931
+ import { join as join17 } from "path";
20828
20932
  import { homedir as homedir8 } from "os";
20829
20933
  var IGNORE_FILE = ".pretenseignore";
20830
- var LAST_SCAN_PATH = join16(homedir8(), ".pretense", "last-scan.json");
20934
+ var LAST_SCAN_PATH = join17(homedir8(), ".pretense", "last-scan.json");
20831
20935
  function ignorePath() {
20832
- return join16(process.cwd(), IGNORE_FILE);
20936
+ return join17(process.cwd(), IGNORE_FILE);
20833
20937
  }
20834
20938
  function readIgnoreFile() {
20835
20939
  const p = ignorePath();
20836
- if (!existsSync13(p)) return [];
20940
+ if (!existsSync14(p)) return [];
20837
20941
  return readFileSync14(p, "utf-8").split("\n").filter((l) => l.length > 0);
20838
20942
  }
20839
20943
  function appendPattern(pattern) {
20840
20944
  const p = ignorePath();
20841
- const content = existsSync13(p) ? readFileSync14(p, "utf-8") : "";
20945
+ const content = existsSync14(p) ? readFileSync14(p, "utf-8") : "";
20842
20946
  const lines = content.split("\n").filter((l) => l.length > 0);
20843
20947
  if (lines.includes(pattern)) {
20844
20948
  console.log(chalk13.yellow(`
@@ -20877,7 +20981,7 @@ function ignoreCommand() {
20877
20981
  return;
20878
20982
  }
20879
20983
  if (opts.lastFound) {
20880
- if (!existsSync13(LAST_SCAN_PATH)) {
20984
+ if (!existsSync14(LAST_SCAN_PATH)) {
20881
20985
  console.error(chalk13.red(`
20882
20986
  \u2717 No last scan results found at ${LAST_SCAN_PATH}
20883
20987
  `));
@@ -21135,8 +21239,8 @@ function completionCommand() {
21135
21239
  init_esm_shims();
21136
21240
  import { Command as Command15 } from "commander";
21137
21241
  import chalk14 from "chalk";
21138
- import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "fs";
21139
- import { join as join17, resolve as resolve5 } from "path";
21242
+ import { existsSync as existsSync15, writeFileSync as writeFileSync6 } from "fs";
21243
+ import { join as join18, resolve as resolve5 } from "path";
21140
21244
  var DEFAULT_CONFIG_YAML = `scan:
21141
21245
  secrets: block
21142
21246
  pii: redact
@@ -21176,9 +21280,9 @@ function quickstartCommand() {
21176
21280
  process.exit(1);
21177
21281
  }
21178
21282
  console.log(chalk14.dim(" [2/4] Writing config..."));
21179
- const configPath = join17(dir, "pretense.yaml");
21283
+ const configPath = join18(dir, "pretense.yaml");
21180
21284
  if (!opts.dryRun) {
21181
- if (!existsSync14(configPath)) {
21285
+ if (!existsSync15(configPath)) {
21182
21286
  writeFileSync6(configPath, DEFAULT_CONFIG_YAML);
21183
21287
  console.log(chalk14.green(" Created pretense.yaml"));
21184
21288
  } else {
@@ -21187,8 +21291,8 @@ function quickstartCommand() {
21187
21291
  }
21188
21292
  console.log(chalk14.dim(" [3/4] Updating .gitignore..."));
21189
21293
  if (!opts.dryRun) {
21190
- const gitignorePath = join17(dir, ".gitignore");
21191
- let content = existsSync14(gitignorePath) ? (await import("fs")).default.readFileSync(gitignorePath, "utf-8") : "";
21294
+ const gitignorePath = join18(dir, ".gitignore");
21295
+ let content = existsSync15(gitignorePath) ? (await import("fs")).default.readFileSync(gitignorePath, "utf-8") : "";
21192
21296
  if (!content.includes(".pretense/")) {
21193
21297
  content = content + (content.endsWith("\n") ? "" : "\n") + ".pretense/\n";
21194
21298
  writeFileSync6(gitignorePath, content);
@@ -21218,7 +21322,7 @@ function quickstartCommand() {
21218
21322
  init_esm_shims();
21219
21323
  import { Command as Command16, Option as Option2 } from "commander";
21220
21324
  import chalk16 from "chalk";
21221
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, chmodSync as chmodSync4, existsSync as existsSync15 } from "fs";
21325
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, chmodSync as chmodSync4, existsSync as existsSync16 } from "fs";
21222
21326
  import { createHash as createHash3 } from "crypto";
21223
21327
  import { resolve as resolve6, extname as extname4 } from "path";
21224
21328
 
@@ -21226,8 +21330,8 @@ import { resolve as resolve6, extname as extname4 } from "path";
21226
21330
  init_esm_shims();
21227
21331
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, hkdfSync as hkdfSync2 } from "crypto";
21228
21332
  import { homedir as homedir9 } from "os";
21229
- import { join as join18 } from "path";
21230
- var KEYS_DIR = join18(homedir9(), ".pretense", "keys");
21333
+ import { join as join19 } from "path";
21334
+ var KEYS_DIR = join19(homedir9(), ".pretense", "keys");
21231
21335
  var HKDF_SALT2 = Buffer.from("pretense-maps-v1", "utf-8");
21232
21336
  var HKDF_INFO2 = Buffer.from("secret-mapping-aes-256-gcm", "utf-8");
21233
21337
  var IV_LEN = 12;
@@ -21272,13 +21376,13 @@ import {
21272
21376
  fchmodSync
21273
21377
  } from "fs";
21274
21378
  import { randomBytes as randomBytes4 } from "crypto";
21275
- import { dirname as dirname4, basename as basename2, join as join19 } from "path";
21379
+ import { dirname as dirname4, basename as basename2, join as join20 } from "path";
21276
21380
  function writeFileAtomicSync(targetPath, data, opts = {}) {
21277
21381
  let resolved;
21278
21382
  try {
21279
21383
  resolved = realpathSync2(targetPath);
21280
21384
  } catch {
21281
- resolved = join19(realpathSync2(dirname4(targetPath)), basename2(targetPath));
21385
+ resolved = join20(realpathSync2(dirname4(targetPath)), basename2(targetPath));
21282
21386
  }
21283
21387
  let mode = opts.mode;
21284
21388
  if (mode === void 0) {
@@ -21289,7 +21393,7 @@ function writeFileAtomicSync(targetPath, data, opts = {}) {
21289
21393
  }
21290
21394
  }
21291
21395
  const dir = dirname4(resolved);
21292
- const tmp = join19(dir, `.${basename2(resolved)}.pretense-${randomBytes4(6).toString("hex")}.tmp`);
21396
+ const tmp = join20(dir, `.${basename2(resolved)}.pretense-${randomBytes4(6).toString("hex")}.tmp`);
21293
21397
  const buf = Buffer.isBuffer(data) ? data : Buffer.from(data, "utf-8");
21294
21398
  let fd = openSync2(tmp, "wx", 384);
21295
21399
  try {
@@ -21330,13 +21434,13 @@ function writeFileAtomicSync(targetPath, data, opts = {}) {
21330
21434
  // src/default-map.ts
21331
21435
  init_esm_shims();
21332
21436
  import { createHash as createHash2 } from "crypto";
21333
- import { join as join20 } from "path";
21437
+ import { join as join21 } from "path";
21334
21438
  function mapsDir(override2) {
21335
- return override2 ?? join20(pretenseHome(), "maps");
21439
+ return override2 ?? join21(pretenseHome(), "maps");
21336
21440
  }
21337
21441
  function defaultMapPath(absFilePath, override2) {
21338
21442
  const digest = createHash2("sha256").update(absFilePath, "utf8").digest("hex");
21339
- return join20(mapsDir(override2), `${digest}.json`);
21443
+ return join21(mapsDir(override2), `${digest}.json`);
21340
21444
  }
21341
21445
  function ensureDefaultMapPath(absFilePath, override2) {
21342
21446
  ensureDirOwnerOnly(mapsDir(override2));
@@ -21346,11 +21450,11 @@ function ensureDefaultMapPath(absFilePath, override2) {
21346
21450
  // src/local-audit.ts
21347
21451
  init_esm_shims();
21348
21452
  import { homedir as homedir10 } from "os";
21349
- import { join as join21 } from "path";
21453
+ import { join as join24 } from "path";
21350
21454
  import { randomUUID } from "crypto";
21351
21455
  import chalk15 from "chalk";
21352
21456
  function localAuditDbPath() {
21353
- return join21(homedir10(), ".pretense", "audit.db");
21457
+ return join24(homedir10(), ".pretense", "audit.db");
21354
21458
  }
21355
21459
  function recordLocalAudit(rec, dbPath = localAuditDbPath()) {
21356
21460
  let store = null;
@@ -21449,7 +21553,7 @@ async function uploadUsage(event) {
21449
21553
  // src/commands/mutate.ts
21450
21554
  var REVERSAL_MAP_VERSION = 3;
21451
21555
  function preflightReversalMap(mapPath, absFile, content, contentSha, force) {
21452
- if (!existsSync15(mapPath)) return { action: "write", generation: 1 };
21556
+ if (!existsSync16(mapPath)) return { action: "write", generation: 1 };
21453
21557
  let existing;
21454
21558
  try {
21455
21559
  const parsed = JSON.parse(readFileSync15(mapPath, "utf-8"));
@@ -21626,7 +21730,25 @@ async function runMutate(file, opts = {}) {
21626
21730
  opts.level,
21627
21731
  opts.presetName
21628
21732
  );
21629
- if (findings.length === 0) {
21733
+ const absFile = resolve6(file);
21734
+ const projectRoot = resolveProjectRoot(absFile);
21735
+ const secretResult = mutateSecrets(content, findings, { projectRoot, keysDir: opts.keysDir });
21736
+ const fingerprintIds = loadFingerprintIdentifiers(projectRoot);
21737
+ const idMut = fingerprintIds.length > 0 ? mutateNamedIdentifiers(
21738
+ secretResult.content,
21739
+ L2_LANGUAGE_BY_EXT2[extname4(file).toLowerCase()],
21740
+ fingerprintIds,
21741
+ "pretense",
21742
+ { projectRoot, keysDir: opts.keysDir }
21743
+ ) : { mutatedCode: secretResult.content, map: /* @__PURE__ */ new Map() };
21744
+ const finalContent = idMut.mutatedCode;
21745
+ const identifierMutations = [...idMut.map.entries()].map(([original, replacement]) => ({
21746
+ original,
21747
+ replacement,
21748
+ type: "proprietary"
21749
+ }));
21750
+ const totalMutations = secretResult.mutations.length + identifierMutations.length;
21751
+ if (totalMutations === 0) {
21630
21752
  if (unmutatable.size === 0) {
21631
21753
  console.error(chalk16.dim(` No secrets found in ${file} \u2014 nothing to mutate.`));
21632
21754
  } else {
@@ -21636,9 +21758,10 @@ async function runMutate(file, opts = {}) {
21636
21758
  return 0;
21637
21759
  }
21638
21760
  reportUnmutatable(unmutatable);
21639
- const absFile = resolve6(file);
21640
- const projectRoot = resolveProjectRoot(absFile);
21641
- const result = mutateSecrets(content, findings, { projectRoot, keysDir: opts.keysDir });
21761
+ const result = {
21762
+ content: finalContent,
21763
+ mutations: [...secretResult.mutations, ...identifierMutations]
21764
+ };
21642
21765
  let effectiveMap;
21643
21766
  if (preview) {
21644
21767
  effectiveMap = void 0;
@@ -21710,13 +21833,21 @@ async function runMutate(file, opts = {}) {
21710
21833
  );
21711
21834
  return 1;
21712
21835
  }
21836
+ const secretCount = secretResult.mutations.length;
21837
+ const idCount = identifierMutations.length;
21838
+ const summary = () => {
21839
+ const parts = [];
21840
+ if (secretCount > 0) parts.push(`${secretCount} secret${secretCount === 1 ? "" : "s"}`);
21841
+ if (idCount > 0) parts.push(`${idCount} registered identifier${idCount === 1 ? "" : "s"}`);
21842
+ return parts.join(" + ");
21843
+ };
21713
21844
  try {
21714
21845
  if (inPlace) {
21715
21846
  writeFileAtomicSync(file, result.content);
21716
- console.error(chalk16.green(` \u2713 Mutated ${result.mutations.length} secrets in ${file} (in-place)`));
21847
+ console.error(chalk16.green(` \u2713 Mutated ${summary()} in ${file} (in-place)`));
21717
21848
  } else if (opts.output) {
21718
21849
  writeFileAtomicSync(opts.output, result.content);
21719
- console.error(chalk16.green(` \u2713 Mutated ${result.mutations.length} secrets \u2192 ${opts.output}`));
21850
+ console.error(chalk16.green(` \u2713 Mutated ${summary()} \u2192 ${opts.output}`));
21720
21851
  } else {
21721
21852
  process.stdout.write(result.content);
21722
21853
  }
@@ -21733,7 +21864,9 @@ async function runMutate(file, opts = {}) {
21733
21864
  let secretsBlocked = 0;
21734
21865
  let piiRedacted = 0;
21735
21866
  for (const m of result.mutations) {
21736
- if (categoryByType.get(m.type) === "pii") piiRedacted += 1;
21867
+ const cat = categoryByType.get(m.type);
21868
+ if (cat === "pii") piiRedacted += 1;
21869
+ else if (m.type === "proprietary") continue;
21737
21870
  else secretsBlocked += 1;
21738
21871
  }
21739
21872
  recordLocalAudit({
@@ -21795,7 +21928,7 @@ function mutateCommand() {
21795
21928
  init_esm_shims();
21796
21929
  import { Command as Command17 } from "commander";
21797
21930
  import chalk17 from "chalk";
21798
- import { readFileSync as readFileSync16, existsSync as existsSync16, statSync as statSync6 } from "fs";
21931
+ import { readFileSync as readFileSync16, existsSync as existsSync17, statSync as statSync6 } from "fs";
21799
21932
  import { resolve as resolve7 } from "path";
21800
21933
  import { createHash as createHash4 } from "crypto";
21801
21934
  function sha256Hex3(s) {
@@ -21856,14 +21989,14 @@ function loadReversalMap(mapPath) {
21856
21989
  function runReverse(file, opts = {}) {
21857
21990
  const reverseStartMs = Date.now();
21858
21991
  const absPath = resolve7(file);
21859
- if (!existsSync16(absPath) || !statSync6(absPath).isFile()) {
21992
+ if (!existsSync17(absPath) || !statSync6(absPath).isFile()) {
21860
21993
  console.error(chalk17.red(`
21861
21994
  x File not found: ${file}
21862
21995
  `));
21863
21996
  return 1;
21864
21997
  }
21865
21998
  const mapPath = opts.map ?? defaultMapPath(absPath, opts.mapsDir);
21866
- if (!opts.map && !existsSync16(mapPath)) {
21999
+ if (!opts.map && !existsSync17(mapPath)) {
21867
22000
  console.error(
21868
22001
  chalk17.red(`
21869
22002
  x No reversal map found for ${absPath}`) + chalk17.dim(`
@@ -22320,7 +22453,7 @@ init_esm_shims();
22320
22453
  import { Command as Command20 } from "commander";
22321
22454
  import chalk20 from "chalk";
22322
22455
  import { homedir as homedir11 } from "os";
22323
- import { join as join24 } from "path";
22456
+ import { join as join25 } from "path";
22324
22457
  var VALID_FORMATS2 = ["table", "csv", "json", "jsonl"];
22325
22458
  function toSafeAuditRow(e) {
22326
22459
  return {
@@ -22444,7 +22577,7 @@ function auditCommand() {
22444
22577
  `));
22445
22578
  process.exit(1);
22446
22579
  }
22447
- const dbPath = opts.db ?? join24(homedir11(), ".pretense", "audit.db");
22580
+ const dbPath = opts.db ?? join25(homedir11(), ".pretense", "audit.db");
22448
22581
  let rows;
22449
22582
  try {
22450
22583
  const store = new AuditStore(dbPath);
@@ -22504,7 +22637,7 @@ function auditCommand() {
22504
22637
  init_esm_shims();
22505
22638
  import { Command as Command21 } from "commander";
22506
22639
  import chalk21 from "chalk";
22507
- import { join as join25 } from "path";
22640
+ import { join as join26 } from "path";
22508
22641
  import { homedir as homedir12 } from "os";
22509
22642
  function remaining(used, limit) {
22510
22643
  if (limit === null) return null;
@@ -22523,7 +22656,7 @@ function bar(used, limit, width = 24) {
22523
22656
  return chalk21.green(s);
22524
22657
  }
22525
22658
  function creditsCommand() {
22526
- return new Command21("credits").alias("tokens").description("Show remaining mutation budget for the current period").option("--db <path>", "Path to audit database", join25(homedir12(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
22659
+ return new Command21("credits").alias("tokens").description("Show remaining mutation budget for the current period").option("--db <path>", "Path to audit database", join26(homedir12(), ".pretense", "audit.db")).option("--json", "Output as JSON", false).option("--offline", "Do not contact the plan service to resolve the tier", false).action(async (opts) => {
22527
22660
  const resolution = await resolvePlanTier({ offline: opts.offline });
22528
22661
  const tier = resolution.tier;
22529
22662
  const collected = collectUsage(opts.db);
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRETENSE_VERSION
4
- } from "./chunk-N2GZIZAD.js";
4
+ } from "./chunk-BWS6UMKL.js";
5
5
  import {
6
6
  init_esm_shims
7
7
  } from "./chunk-TWMRHZGM.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretense/cli",
3
- "version": "0.6.20",
3
+ "version": "0.6.22",
4
4
  "description": "Local-first AI firewall that mutates proprietary code before sending to LLM APIs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,14 +24,14 @@
24
24
  "typescript": "^5.4.0",
25
25
  "vitest": "^1.0.0",
26
26
  "@pretense/billing": "0.1.0",
27
- "@pretense/protocol": "0.1.0",
27
+ "@pretense/compliance-engine": "0.1.0",
28
28
  "@pretense/learner": "0.2.0",
29
29
  "@pretense/mutator": "0.2.0",
30
- "@pretense/compliance-engine": "0.1.0",
30
+ "@pretense/proxy": "0.1.0",
31
31
  "@pretense/scanner": "0.2.0",
32
32
  "@pretense/scanner-rs": "0.2.0",
33
33
  "@pretense/store": "0.2.0",
34
- "@pretense/proxy": "0.1.0"
34
+ "@pretense/protocol": "0.1.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public",