@pretense/cli 0.6.2 → 0.6.4

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.
Files changed (2) hide show
  1. package/dist/index.js +94 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15862,6 +15862,30 @@ function extractDeltas(line, provider) {
15862
15862
  delta[prop] = v;
15863
15863
  }
15864
15864
  });
15865
+ } else if (provider === "google") {
15866
+ const candidates = json["candidates"];
15867
+ if (!Array.isArray(candidates)) return null;
15868
+ for (let ci = 0; ci < candidates.length; ci++) {
15869
+ const cand = candidates[ci];
15870
+ const candIndex = typeof cand["index"] === "number" ? cand["index"] : ci;
15871
+ const content = cand["content"];
15872
+ if (!content) continue;
15873
+ const parts = content["parts"];
15874
+ if (!Array.isArray(parts)) continue;
15875
+ for (const part of parts) {
15876
+ if (typeof part["text"] !== "string") continue;
15877
+ const isThought = part["thought"] === true;
15878
+ refs.push({
15879
+ key: `g:${candIndex}:${isThought ? "thought" : "text"}`,
15880
+ index: candIndex,
15881
+ kind: isThought ? "google_thought" : "google_text",
15882
+ text: part["text"],
15883
+ set: (v) => {
15884
+ part["text"] = v;
15885
+ }
15886
+ });
15887
+ }
15888
+ }
15865
15889
  } else {
15866
15890
  const choices = json["choices"];
15867
15891
  if (!Array.isArray(choices) || choices.length === 0) return null;
@@ -15907,6 +15931,14 @@ function toJsonStringContextMap(map) {
15907
15931
  }
15908
15932
  return out;
15909
15933
  }
15934
+ function classifyStreamProvider(upstream) {
15935
+ const u = upstream.toLowerCase();
15936
+ if (u.includes("anthropic")) return "anthropic";
15937
+ if (u.includes("generativelanguage") || u.includes("googleapis") || u.includes("gemini")) {
15938
+ return "google";
15939
+ }
15940
+ return "openai";
15941
+ }
15910
15942
  async function streamWithRollingBuffer(upstreamResp, map, provider, requestId) {
15911
15943
  const buffers = /* @__PURE__ */ new Map();
15912
15944
  const bufferMeta = /* @__PURE__ */ new Map();
@@ -16021,6 +16053,12 @@ function buildFlushEvent(text, provider, kind, index) {
16021
16053
  const delta2 = kind === "input_json" ? { type: "input_json_delta", partial_json: text } : kind === "thinking" ? { type: "thinking_delta", thinking: text } : { type: "text_delta", text };
16022
16054
  return "data: " + JSON.stringify({ type: "content_block_delta", index, delta: delta2 });
16023
16055
  }
16056
+ if (provider === "google") {
16057
+ const part = kind === "google_thought" ? { text, thought: true } : { text };
16058
+ return "data: " + JSON.stringify({
16059
+ candidates: [{ content: { parts: [part], role: "model" }, index }]
16060
+ });
16061
+ }
16024
16062
  const delta = kind === "openai_tool_args" ? { tool_calls: [{ index, function: { arguments: text } }] } : { content: text };
16025
16063
  return "data: " + JSON.stringify({
16026
16064
  id: "pretense-flush",
@@ -16276,7 +16314,7 @@ function tokenizeBodyDeep(body, scanText) {
16276
16314
  };
16277
16315
  }
16278
16316
  async function handleStreaming(upstreamResp, map, requestId, upstream) {
16279
- const provider = upstream.includes("anthropic") ? "anthropic" : "openai";
16317
+ const provider = classifyStreamProvider(upstream);
16280
16318
  return streamWithRollingBuffer(upstreamResp, map, provider, requestId);
16281
16319
  }
16282
16320
  function createProxy(opts = {}) {
@@ -16750,7 +16788,10 @@ Upgrade to ${_upgradeTier === "enterprise" ? "Enterprise" : "Pro"} for ${_upgrad
16750
16788
  for (const [orig, syn] of globalMap.entries()) {
16751
16789
  if (!secretMap.has(syn)) reverseMap.set(orig, syn);
16752
16790
  }
16753
- if (body["stream"] === true) {
16791
+ const upstreamIsSse = (upstreamResp.headers.get("content-type") ?? "").includes(
16792
+ "text/event-stream"
16793
+ );
16794
+ if (body["stream"] === true || upstreamIsSse) {
16754
16795
  return handleStreaming(upstreamResp, reverseMap, requestId, upstream);
16755
16796
  }
16756
16797
  let respBody;
@@ -17567,8 +17608,12 @@ function logsCommand() {
17567
17608
  entries = store.getRecentEntries(limit);
17568
17609
  store.close();
17569
17610
  } catch {
17570
- console.log(chalk6.dim("\n No audit data available. The audit database has not been initialized yet."));
17571
- console.log(chalk6.dim(" Run " + chalk6.cyan("pretense start") + " and make some requests first.\n"));
17611
+ if (opts.json) {
17612
+ console.log("[]");
17613
+ return;
17614
+ }
17615
+ console.error(chalk6.dim("\n No audit data available. The audit database has not been initialized yet."));
17616
+ console.error(chalk6.dim(" Run " + chalk6.cyan("pretense start") + " and make some requests first.\n"));
17572
17617
  return;
17573
17618
  }
17574
17619
  if (opts.json) {
@@ -18962,15 +19007,26 @@ async function collectMutationFindings(content, file, level) {
18962
19007
  }
18963
19008
  async function runMutate(file, opts = {}) {
18964
19009
  const level = resolveMutateLevel(opts);
18965
- let content;
19010
+ let raw2;
18966
19011
  try {
18967
- content = readFileSync14(file, "utf-8");
19012
+ raw2 = readFileSync14(file);
18968
19013
  } catch {
18969
19014
  console.error(chalk13.red(`
18970
19015
  x Cannot read file: ${file}
18971
19016
  `));
18972
19017
  return 1;
18973
19018
  }
19019
+ const content = raw2.toString("utf-8");
19020
+ if (!Buffer.from(content, "utf-8").equals(raw2)) {
19021
+ console.error(
19022
+ chalk13.red(`
19023
+ x Not valid UTF-8: ${file}`) + chalk13.dim(`
19024
+ Mutating would replace the undecodable bytes with U+FFFD and the`) + chalk13.dim(`
19025
+ original content could not be restored. File left untouched.
19026
+ `)
19027
+ );
19028
+ return 1;
19029
+ }
18974
19030
  const findings = await collectMutationFindings(content, file, level);
18975
19031
  if (findings.length === 0) {
18976
19032
  console.error(chalk13.dim(` No secrets found in ${file} \u2014 nothing to mutate.`));
@@ -18978,15 +19034,6 @@ async function runMutate(file, opts = {}) {
18978
19034
  return 0;
18979
19035
  }
18980
19036
  const result = mutateSecrets(content, findings);
18981
- if (opts.inPlace) {
18982
- writeFileSync9(file, result.content);
18983
- console.error(chalk13.green(` \u2713 Mutated ${result.mutations.length} secrets in ${file} (in-place)`));
18984
- } else if (opts.output) {
18985
- writeFileSync9(opts.output, result.content);
18986
- console.error(chalk13.green(` \u2713 Mutated ${result.mutations.length} secrets \u2192 ${opts.output}`));
18987
- } else {
18988
- process.stdout.write(result.content);
18989
- }
18990
19037
  if (opts.map) {
18991
19038
  const absFile = resolve4(file);
18992
19039
  const projectKey = getOrCreateProjectKey(findProjectRoot(absFile), opts.keysDir);
@@ -19001,7 +19048,18 @@ async function runMutate(file, opts = {}) {
19001
19048
  type: m.type
19002
19049
  }))
19003
19050
  };
19004
- writeFileSync9(opts.map, JSON.stringify(mapFile, null, 2), { mode: 384 });
19051
+ try {
19052
+ writeFileSync9(opts.map, JSON.stringify(mapFile, null, 2), { mode: 384 });
19053
+ } catch (e) {
19054
+ console.error(
19055
+ chalk13.red(`
19056
+ x Cannot write reversal map: ${opts.map}`) + chalk13.dim(`
19057
+ ${e.message}`) + chalk13.dim(`
19058
+ Nothing was mutated \u2014 without a map the original could not be restored.
19059
+ `)
19060
+ );
19061
+ return 1;
19062
+ }
19005
19063
  try {
19006
19064
  chmodSync6(opts.map, 384);
19007
19065
  } catch {
@@ -19010,6 +19068,25 @@ async function runMutate(file, opts = {}) {
19010
19068
  chalk13.green(` \u2713 Wrote encrypted reversal map \u2192 ${opts.map}`) + chalk13.dim(" (AES-256-GCM, mode 0600)")
19011
19069
  );
19012
19070
  }
19071
+ try {
19072
+ if (opts.inPlace) {
19073
+ writeFileSync9(file, result.content);
19074
+ console.error(chalk13.green(` \u2713 Mutated ${result.mutations.length} secrets in ${file} (in-place)`));
19075
+ } else if (opts.output) {
19076
+ writeFileSync9(opts.output, result.content);
19077
+ console.error(chalk13.green(` \u2713 Mutated ${result.mutations.length} secrets \u2192 ${opts.output}`));
19078
+ } else {
19079
+ process.stdout.write(result.content);
19080
+ }
19081
+ } catch (e) {
19082
+ console.error(
19083
+ chalk13.red(`
19084
+ x Cannot write mutated content: ${opts.output ?? file}`) + chalk13.dim(`
19085
+ ${e.message}
19086
+ `)
19087
+ );
19088
+ return 1;
19089
+ }
19013
19090
  if (opts.showMap) {
19014
19091
  console.error(JSON.stringify(result.mutations, null, 2));
19015
19092
  }
@@ -19527,7 +19604,7 @@ function findClosestCommand(input, knownCommands) {
19527
19604
  return bestDist <= 3 ? best : null;
19528
19605
  }
19529
19606
  var program = new Command20();
19530
- program.name("pretense").description(chalk19.bold("AI firewall") + " \u2014 mutates proprietary code before LLM API calls").version("0.6.2", "-v, --version").addHelpText("after", `
19607
+ program.name("pretense").description(chalk19.bold("AI firewall") + " \u2014 mutates proprietary code before LLM API calls").version("0.6.4", "-v, --version").addHelpText("after", `
19531
19608
  ${chalk19.bold("Quick start:")}
19532
19609
  ${chalk19.cyan("pretense scan")} ${chalk19.dim("<file|dir>")} Scan for secrets and PII
19533
19610
  ${chalk19.cyan("pretense mutate")} ${chalk19.dim("<file>")} Preview code mutations
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pretense/cli",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Local-first AI firewall that mutates proprietary code before sending to LLM APIs",
5
5
  "type": "module",
6
6
  "bin": {