@pretense/cli 0.6.16 → 0.6.17

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.16" : void 0) ?? versionFromPackageJson() ?? "0.0.0-unknown";
20
+ var PRETENSE_VERSION = (true ? "0.6.17" : 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-MH6CXHTM.js";
4
+ } from "./chunk-EFDPBFXJ.js";
5
5
  import {
6
6
  __commonJS,
7
7
  __toESM,
@@ -3892,10 +3892,10 @@ var Minipass = class extends EventEmitter {
3892
3892
  * Return a void Promise that resolves once the stream ends.
3893
3893
  */
3894
3894
  async promise() {
3895
- return new Promise((resolve7, reject) => {
3895
+ return new Promise((resolve8, reject) => {
3896
3896
  this.on(DESTROYED, () => reject(new Error("stream destroyed")));
3897
3897
  this.on("error", (er) => reject(er));
3898
- this.on("end", () => resolve7());
3898
+ this.on("end", () => resolve8());
3899
3899
  });
3900
3900
  }
3901
3901
  /**
@@ -3919,7 +3919,7 @@ var Minipass = class extends EventEmitter {
3919
3919
  return Promise.resolve({ done: false, value: res });
3920
3920
  if (this[EOF])
3921
3921
  return stop();
3922
- let resolve7;
3922
+ let resolve8;
3923
3923
  let reject;
3924
3924
  const onerr = (er) => {
3925
3925
  this.off("data", ondata);
@@ -3933,19 +3933,19 @@ var Minipass = class extends EventEmitter {
3933
3933
  this.off("end", onend);
3934
3934
  this.off(DESTROYED, ondestroy);
3935
3935
  this.pause();
3936
- resolve7({ value, done: !!this[EOF] });
3936
+ resolve8({ value, done: !!this[EOF] });
3937
3937
  };
3938
3938
  const onend = () => {
3939
3939
  this.off("error", onerr);
3940
3940
  this.off("data", ondata);
3941
3941
  this.off(DESTROYED, ondestroy);
3942
3942
  stop();
3943
- resolve7({ done: true, value: void 0 });
3943
+ resolve8({ done: true, value: void 0 });
3944
3944
  };
3945
3945
  const ondestroy = () => onerr(new Error("stream destroyed"));
3946
3946
  return new Promise((res2, rej) => {
3947
3947
  reject = rej;
3948
- resolve7 = res2;
3948
+ resolve8 = res2;
3949
3949
  this.once(DESTROYED, ondestroy);
3950
3950
  this.once("error", onerr);
3951
3951
  this.once("end", onend);
@@ -4917,9 +4917,9 @@ var PathBase = class {
4917
4917
  if (this.#asyncReaddirInFlight) {
4918
4918
  await this.#asyncReaddirInFlight;
4919
4919
  } else {
4920
- let resolve7 = () => {
4920
+ let resolve8 = () => {
4921
4921
  };
4922
- this.#asyncReaddirInFlight = new Promise((res) => resolve7 = res);
4922
+ this.#asyncReaddirInFlight = new Promise((res) => resolve8 = res);
4923
4923
  try {
4924
4924
  for (const e of await this.#fs.promises.readdir(fullpath, {
4925
4925
  withFileTypes: true
@@ -4932,7 +4932,7 @@ var PathBase = class {
4932
4932
  children.provisional = 0;
4933
4933
  }
4934
4934
  this.#asyncReaddirInFlight = void 0;
4935
- resolve7();
4935
+ resolve8();
4936
4936
  }
4937
4937
  return children.slice(0, children.provisional);
4938
4938
  }
@@ -10413,7 +10413,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
10413
10413
  });
10414
10414
  if (!chunk) {
10415
10415
  if (i === 1) {
10416
- await new Promise((resolve7) => setTimeout(resolve7));
10416
+ await new Promise((resolve8) => setTimeout(resolve8));
10417
10417
  maxReadCount = 3;
10418
10418
  continue;
10419
10419
  }
@@ -14408,7 +14408,7 @@ import { createHmac, hkdfSync, randomBytes as randomBytes2 } from "crypto";
14408
14408
  import { createHash, randomBytes } from "crypto";
14409
14409
  import { readFileSync as readFileSync4, existsSync as existsSync22 } from "fs";
14410
14410
  import { homedir as homedir2 } from "os";
14411
- import { join as join22, dirname, parse } from "path";
14411
+ import { join as join22, dirname, parse, resolve } from "path";
14412
14412
  import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync2, statSync, writeFileSync as writeFileSync3 } from "fs";
14413
14413
  import { homedir } from "os";
14414
14414
  import { join as join4 } from "path";
@@ -14481,6 +14481,7 @@ function tighten(path2, exact, kind) {
14481
14481
  );
14482
14482
  }
14483
14483
  }
14484
+ var PROJECT_ROOT_ENV = "PRETENSE_PROJECT_ROOT";
14484
14485
  var KEYS_DIR_NAME = join22(".pretense", "keys");
14485
14486
  function keysDir() {
14486
14487
  return join22(homedir2(), KEYS_DIR_NAME);
@@ -14503,7 +14504,25 @@ var PROJECT_MARKERS = [
14503
14504
  ".hg",
14504
14505
  ".svn"
14505
14506
  ];
14507
+ function markersAt(dir, home) {
14508
+ return dir === home ? PROJECT_MARKERS.filter((m) => m !== ".pretense") : PROJECT_MARKERS;
14509
+ }
14506
14510
  function findProjectRoot(filePath) {
14511
+ let dir = dirname(filePath);
14512
+ const { root } = parse(dir);
14513
+ const startDir = dir;
14514
+ const home = homedir2();
14515
+ while (true) {
14516
+ if (markersAt(dir, home).some((m) => existsSync22(join22(dir, m)))) {
14517
+ return dir;
14518
+ }
14519
+ if (dir === root) return startDir;
14520
+ const parent = dirname(dir);
14521
+ if (parent === dir) return startDir;
14522
+ dir = parent;
14523
+ }
14524
+ }
14525
+ function legacyFindProjectRoot(filePath) {
14507
14526
  let dir = dirname(filePath);
14508
14527
  const { root } = parse(dir);
14509
14528
  const startDir = dir;
@@ -14517,6 +14536,21 @@ function findProjectRoot(filePath) {
14517
14536
  dir = parent;
14518
14537
  }
14519
14538
  }
14539
+ function resolveProjectRoot(filePath) {
14540
+ const override2 = process.env[PROJECT_ROOT_ENV];
14541
+ if (override2 && override2.trim()) return resolve(override2.trim());
14542
+ return findProjectRoot(filePath);
14543
+ }
14544
+ function candidateProjectRoots(filePath) {
14545
+ const out = [];
14546
+ const override2 = process.env[PROJECT_ROOT_ENV];
14547
+ if (override2 && override2.trim()) out.push(resolve(override2.trim()));
14548
+ out.push(findProjectRoot(filePath));
14549
+ out.push(dirname(filePath));
14550
+ out.push(legacyFindProjectRoot(filePath));
14551
+ const seen = /* @__PURE__ */ new Set();
14552
+ return out.filter((r) => seen.has(r) ? false : (seen.add(r), true));
14553
+ }
14520
14554
  function findProjectRootForDir(dir) {
14521
14555
  return findProjectRoot(join22(dir, "__pretense_probe__"));
14522
14556
  }
@@ -16335,7 +16369,7 @@ import { join as join5 } from "path";
16335
16369
  import { parse as parseYaml } from "yaml";
16336
16370
  import { readFileSync as readFileSync23 } from "fs";
16337
16371
  import { fileURLToPath as fileURLToPath3 } from "url";
16338
- import { dirname as dirname3, join as join23, resolve } from "path";
16372
+ import { dirname as dirname3, join as join23, resolve as resolve2 } from "path";
16339
16373
  import https from "https";
16340
16374
 
16341
16375
  // ../packages/tls/dist/index.js
@@ -16471,7 +16505,7 @@ function versionFromCliPackageJson() {
16471
16505
  }
16472
16506
  } catch {
16473
16507
  }
16474
- const parent = resolve(dir, "..");
16508
+ const parent = resolve2(dir, "..");
16475
16509
  if (parent === dir) break;
16476
16510
  dir = parent;
16477
16511
  }
@@ -16480,11 +16514,11 @@ function versionFromCliPackageJson() {
16480
16514
  return void 0;
16481
16515
  }
16482
16516
  function productVersion() {
16483
- return override ?? (true ? "0.6.16" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16517
+ return override ?? (true ? "0.6.17" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16484
16518
  }
16485
16519
  var UNKNOWN_COMMIT = "unknown";
16486
16520
  function bakedCommitSha() {
16487
- return "65c9faca2983b357369c6f91aa0cad0cf3a70b82".length > 0 ? "65c9faca2983b357369c6f91aa0cad0cf3a70b82" : UNKNOWN_COMMIT;
16521
+ return "6f467e565f6f035a63fe420acef1019ddf2455f0".length > 0 ? "6f467e565f6f035a63fe420acef1019ddf2455f0" : UNKNOWN_COMMIT;
16488
16522
  }
16489
16523
  var FEATURE_TIERS = {
16490
16524
  compliance_export: "pro",
@@ -17989,14 +18023,14 @@ if (isMain) {
17989
18023
  init_esm_shims();
17990
18024
  import net from "net";
17991
18025
  function isPortFree(port) {
17992
- return new Promise((resolve7) => {
18026
+ return new Promise((resolve8) => {
17993
18027
  const srv = net.createServer();
17994
18028
  srv.once("error", () => {
17995
- srv.close(() => resolve7(false));
17996
- resolve7(false);
18029
+ srv.close(() => resolve8(false));
18030
+ resolve8(false);
17997
18031
  });
17998
18032
  srv.once("listening", () => {
17999
- srv.close(() => resolve7(true));
18033
+ srv.close(() => resolve8(true));
18000
18034
  });
18001
18035
  srv.listen(port);
18002
18036
  });
@@ -18662,7 +18696,7 @@ import { Command as Command5, Option, InvalidArgumentError as InvalidArgumentErr
18662
18696
  import chalk5 from "chalk";
18663
18697
  import { readFileSync as readFileSync8, statSync as statSync3, readdirSync as readdirSync2, openSync, readSync, closeSync } from "fs";
18664
18698
  import { execSync as execSync3 } from "child_process";
18665
- import { resolve as resolve2, join as join8, extname as extname2, basename } from "path";
18699
+ import { resolve as resolve3, join as join8, extname as extname2, basename } from "path";
18666
18700
 
18667
18701
  // src/safe-output.ts
18668
18702
  init_esm_shims();
@@ -18789,7 +18823,7 @@ function isBinaryFile(filePath) {
18789
18823
  }
18790
18824
  var SEVERITY_ORDER = { low: 0, medium: 1, high: 2, critical: 3 };
18791
18825
  function collectFiles(target) {
18792
- const abs = resolve2(target);
18826
+ const abs = resolve3(target);
18793
18827
  let stat;
18794
18828
  try {
18795
18829
  stat = statSync3(abs);
@@ -20469,7 +20503,7 @@ import {
20469
20503
  readFileSync as readFileSync13,
20470
20504
  mkdirSync as mkdirSync3
20471
20505
  } from "fs";
20472
- import { join as join15, resolve as resolve3 } from "path";
20506
+ import { join as join15, resolve as resolve4 } from "path";
20473
20507
  var HOOK_SCRIPTS = {
20474
20508
  "pre-commit": `#!/bin/sh
20475
20509
  # Pretense AI Firewall \u2014 pre-commit hook
@@ -20511,7 +20545,7 @@ function installCommand() {
20511
20545
  "-m, --mode <mode>",
20512
20546
  "Hook mode: pre-commit, pre-push (omit to install both)"
20513
20547
  ).option("-f, --force", "Overwrite existing hooks without prompting", false).option("-d, --dir <path>", "Project directory (defaults to cwd)", ".").action((opts) => {
20514
- const dir = resolve3(opts.dir);
20548
+ const dir = resolve4(opts.dir);
20515
20549
  const force = opts.force;
20516
20550
  const modeArg = opts.mode;
20517
20551
  if (modeArg && !VALID_MODES.includes(modeArg)) {
@@ -20887,7 +20921,7 @@ init_esm_shims();
20887
20921
  import { Command as Command15 } from "commander";
20888
20922
  import chalk14 from "chalk";
20889
20923
  import { existsSync as existsSync14, writeFileSync as writeFileSync6 } from "fs";
20890
- import { join as join17, resolve as resolve4 } from "path";
20924
+ import { join as join17, resolve as resolve5 } from "path";
20891
20925
  var DEFAULT_CONFIG_YAML = `scan:
20892
20926
  secrets: block
20893
20927
  pii: redact
@@ -20914,7 +20948,7 @@ verbose: false
20914
20948
  `;
20915
20949
  function quickstartCommand() {
20916
20950
  return new Command15("quickstart").description("One-command setup: init + install hooks + show next steps").option("-d, --dir <path>", "Directory to scan", ".").option("--dry-run", "Print what would be written without writing", false).action(async (opts) => {
20917
- const dir = resolve4(opts.dir);
20951
+ const dir = resolve5(opts.dir);
20918
20952
  console.log(chalk14.bold.cyan("\n Pretense Quickstart\n"));
20919
20953
  console.log(chalk14.dim(" Setting up AI firewall for this project...\n"));
20920
20954
  console.log(chalk14.dim(" [1/4] Scanning codebase..."));
@@ -20971,7 +21005,7 @@ import { Command as Command16, Option as Option2 } from "commander";
20971
21005
  import chalk16 from "chalk";
20972
21006
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync7, chmodSync as chmodSync4, existsSync as existsSync15 } from "fs";
20973
21007
  import { createHash as createHash3 } from "crypto";
20974
- import { resolve as resolve5, extname as extname4 } from "path";
21008
+ import { resolve as resolve6, extname as extname4 } from "path";
20975
21009
 
20976
21010
  // src/secret-crypto.ts
20977
21011
  init_esm_shims();
@@ -21156,7 +21190,7 @@ function preflightReversalMap(mapPath, absFile, content, contentSha, force) {
21156
21190
  to overwrite it deliberately.`
21157
21191
  };
21158
21192
  }
21159
- const existingFile = existing.file ? resolve5(existing.file) : void 0;
21193
+ const existingFile = existing.file ? resolve6(existing.file) : void 0;
21160
21194
  if (existingFile && existingFile !== absFile) {
21161
21195
  if (force) {
21162
21196
  return { action: "write", generation: 1, supersededSha: existing.originalSha };
@@ -21326,8 +21360,8 @@ async function runMutate(file, opts = {}) {
21326
21360
  return 0;
21327
21361
  }
21328
21362
  reportUnmutatable(unmutatable);
21329
- const absFile = resolve5(file);
21330
- const projectRoot = findProjectRoot(absFile);
21363
+ const absFile = resolve6(file);
21364
+ const projectRoot = resolveProjectRoot(absFile);
21331
21365
  const result = mutateSecrets(content, findings, { projectRoot, keysDir: opts.keysDir });
21332
21366
  let effectiveMap;
21333
21367
  if (preview) {
@@ -21480,7 +21514,7 @@ init_esm_shims();
21480
21514
  import { Command as Command17 } from "commander";
21481
21515
  import chalk17 from "chalk";
21482
21516
  import { readFileSync as readFileSync16, existsSync as existsSync16, statSync as statSync6 } from "fs";
21483
- import { resolve as resolve6 } from "path";
21517
+ import { resolve as resolve7 } from "path";
21484
21518
  import { createHash as createHash4 } from "crypto";
21485
21519
  function sha256Hex3(s) {
21486
21520
  return createHash4("sha256").update(s, "utf8").digest("hex");
@@ -21539,7 +21573,7 @@ function loadReversalMap(mapPath) {
21539
21573
  }
21540
21574
  function runReverse(file, opts = {}) {
21541
21575
  const reverseStartMs = Date.now();
21542
- const absPath = resolve6(file);
21576
+ const absPath = resolve7(file);
21543
21577
  if (!existsSync16(absPath) || !statSync6(absPath).isFile()) {
21544
21578
  console.error(chalk17.red(`
21545
21579
  x File not found: ${file}
@@ -21561,30 +21595,41 @@ function runReverse(file, opts = {}) {
21561
21595
  const mapFile = loadReversalMap(mapPath);
21562
21596
  let plainMutations;
21563
21597
  if (mapFile.encrypted) {
21564
- const projectRoot = findProjectRoot(mapFile.file ?? absPath);
21565
- const key = readProjectKey(projectRoot, opts.keysDir);
21566
- if (!key) {
21567
- console.error(
21568
- chalk17.red(`
21569
- x Encryption key not found for this project \u2014 cannot decrypt the reversal map.`) + chalk17.dim(`
21570
- Expected a key derived from ${projectRoot}. Reverse must run where the map was created.
21571
- `)
21572
- );
21573
- return 1;
21598
+ const keySource = mapFile.file ?? absPath;
21599
+ const roots = candidateProjectRoots(keySource);
21600
+ let decrypted;
21601
+ let anyKeyPresent = false;
21602
+ for (const root of roots) {
21603
+ const key = readProjectKey(root, opts.keysDir);
21604
+ if (!key) continue;
21605
+ anyKeyPresent = true;
21606
+ try {
21607
+ decrypted = mapFile.mutations.map((m) => ({
21608
+ original: decryptSecret(m.original, key),
21609
+ replacement: m.replacement
21610
+ }));
21611
+ break;
21612
+ } catch {
21613
+ decrypted = void 0;
21614
+ }
21574
21615
  }
21575
- try {
21576
- plainMutations = mapFile.mutations.map((m) => ({
21577
- original: decryptSecret(m.original, key),
21578
- replacement: m.replacement
21579
- }));
21580
- } catch {
21616
+ if (!decrypted) {
21617
+ const looked = roots.join("\n ");
21581
21618
  console.error(
21582
21619
  chalk17.red(`
21583
- x Failed to decrypt the reversal map (wrong key or corrupt data). File left untouched.
21620
+ x Encryption key not found for this file \u2014 cannot decrypt the reversal map.`) + (anyKeyPresent ? chalk17.dim(`
21621
+ A key was present but did not decrypt this map (wrong key or corrupt data).`) : chalk17.dim(`
21622
+ No key was found under any known project location for this file.`)) + chalk17.dim(`
21623
+ Looked under these project roots (keys dir ${opts.keysDir ?? "~/.pretense/keys"}):`) + chalk17.dim(`
21624
+ ${looked}`) + chalk17.dim(`
21625
+ Run \`reverse\` on the SAME machine and HOME where you mutated the file,`) + chalk17.dim(`
21626
+ or set ${chalk17.cyan("PRETENSE_PROJECT_ROOT")} to the project root used at mutate time.`) + chalk17.dim(`
21627
+ File left untouched.
21584
21628
  `)
21585
21629
  );
21586
21630
  return 1;
21587
21631
  }
21632
+ plainMutations = decrypted;
21588
21633
  } else {
21589
21634
  plainMutations = mapFile.mutations.map((m) => ({ original: m.original, replacement: m.replacement }));
21590
21635
  }
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRETENSE_VERSION
4
- } from "./chunk-MH6CXHTM.js";
4
+ } from "./chunk-EFDPBFXJ.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.16",
3
+ "version": "0.6.17",
4
4
  "description": "Local-first AI firewall that mutates proprietary code before sending to LLM APIs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,15 +23,15 @@
23
23
  "tsx": "^4.0.0",
24
24
  "typescript": "^5.4.0",
25
25
  "vitest": "^1.0.0",
26
- "@pretense/protocol": "0.1.0",
27
26
  "@pretense/compliance-engine": "0.1.0",
28
- "@pretense/proxy": "0.1.0",
27
+ "@pretense/protocol": "0.1.0",
28
+ "@pretense/billing": "0.1.0",
29
+ "@pretense/learner": "0.2.0",
29
30
  "@pretense/mutator": "0.2.0",
30
- "@pretense/scanner": "0.2.0",
31
+ "@pretense/proxy": "0.1.0",
31
32
  "@pretense/scanner-rs": "0.2.0",
32
33
  "@pretense/store": "0.2.0",
33
- "@pretense/billing": "0.1.0",
34
- "@pretense/learner": "0.2.0"
34
+ "@pretense/scanner": "0.2.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public",