@pretense/cli 0.6.25 → 0.6.27

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.25" : void 0) ?? versionFromPackageJson() ?? "0.0.0-unknown";
20
+ var PRETENSE_VERSION = (true ? "0.6.27" : 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-XYBJ63JG.js";
4
+ } from "./chunk-6L7NU7KI.js";
5
5
  import {
6
6
  __commonJS,
7
7
  __toESM,
@@ -7837,7 +7837,9 @@ import { execSync } from "child_process";
7837
7837
 
7838
7838
  // ../apps/proxy/dist/server.js
7839
7839
  init_esm_shims();
7840
- import { resolve as resolvePath } from "path";
7840
+ import { resolve as resolvePath, join as joinPath } from "path";
7841
+ import { existsSync as existsSync24, readFileSync as readFileSync33 } from "fs";
7842
+ import { homedir as homedir3 } from "os";
7841
7843
 
7842
7844
  // ../node_modules/.pnpm/hono@4.12.9/node_modules/hono/dist/index.js
7843
7845
  init_esm_shims();
@@ -16657,11 +16659,11 @@ function versionFromCliPackageJson() {
16657
16659
  return void 0;
16658
16660
  }
16659
16661
  function productVersion() {
16660
- return override ?? (true ? "0.6.25" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16662
+ return override ?? (true ? "0.6.27" : void 0) ?? versionFromCliPackageJson() ?? "0.0.0-unknown";
16661
16663
  }
16662
16664
  var UNKNOWN_COMMIT = "unknown";
16663
16665
  function bakedCommitSha() {
16664
- return "24595f8f691656d511f75ed2d8b59f98069779ac".length > 0 ? "24595f8f691656d511f75ed2d8b59f98069779ac" : UNKNOWN_COMMIT;
16666
+ return "2899946100b26294eb8ef8dbde2d50e63a5247a9".length > 0 ? "2899946100b26294eb8ef8dbde2d50e63a5247a9" : UNKNOWN_COMMIT;
16665
16667
  }
16666
16668
  var FEATURE_TIERS = {
16667
16669
  compliance_export: "pro",
@@ -17104,7 +17106,13 @@ async function streamWithRollingBuffer(upstreamResp, map, provider, requestId) {
17104
17106
  if (provider === "anthropic" && line.includes("content_block_stop")) {
17105
17107
  const stopIndex = blockStopIndex(line);
17106
17108
  if (stopIndex !== null) {
17107
- outputLines.push(...flushBlock(stopIndex), line);
17109
+ const flushed = flushBlock(stopIndex);
17110
+ if (flushed.length > 0) {
17111
+ const header = outputLines.length > 0 && outputLines[outputLines.length - 1].startsWith("event:") ? outputLines.pop() : null;
17112
+ outputLines.push(...flushed);
17113
+ if (header !== null) outputLines.push(header);
17114
+ }
17115
+ outputLines.push(line);
17108
17116
  continue;
17109
17117
  }
17110
17118
  }
@@ -17165,7 +17173,7 @@ async function streamWithRollingBuffer(upstreamResp, map, provider, requestId) {
17165
17173
  function buildFlushEvent(text, provider, kind, index) {
17166
17174
  if (provider === "anthropic") {
17167
17175
  const delta2 = kind === "input_json" ? { type: "input_json_delta", partial_json: text } : kind === "thinking" ? { type: "thinking_delta", thinking: text } : { type: "text_delta", text };
17168
- return "data: " + JSON.stringify({ type: "content_block_delta", index, delta: delta2 });
17176
+ return "event: content_block_delta\ndata: " + JSON.stringify({ type: "content_block_delta", index, delta: delta2 });
17169
17177
  }
17170
17178
  if (provider === "google") {
17171
17179
  const part = kind === "google_thought" ? { text, thought: true } : { text };
@@ -17357,6 +17365,37 @@ var _tierCache = /* @__PURE__ */ new Map();
17357
17365
  var TIER_CACHE_OK_MS = 6e4;
17358
17366
  var TIER_CACHE_FAIL_MS = 1e4;
17359
17367
  var TIER_VALIDATE_TIMEOUT_MS = 3e3;
17368
+ var LOCAL_KEY_CACHE_MS = 3e4;
17369
+ var _localKeyCache = null;
17370
+ function underTestRunner() {
17371
+ return Boolean(process.env["VITEST"]) || process.env["NODE_ENV"] === "test";
17372
+ }
17373
+ function readLocalPretenseApiKey() {
17374
+ const now = Date.now();
17375
+ if (_localKeyCache && _localKeyCache.expiresAt > now) return _localKeyCache.key;
17376
+ let key = null;
17377
+ const fromEnv = process.env["PRETENSE_API_KEY"]?.trim();
17378
+ if (fromEnv) {
17379
+ key = fromEnv;
17380
+ } else if (!underTestRunner()) {
17381
+ const path2 = joinPath(homedir3(), ".pretense", "credentials.json");
17382
+ try {
17383
+ if (existsSync24(path2)) {
17384
+ const creds = JSON.parse(readFileSync33(path2, "utf-8"));
17385
+ const raw2 = creds["apiKey"] ?? creds["api_key"];
17386
+ if (typeof raw2 === "string" && raw2.trim().length > 0) key = raw2.trim();
17387
+ }
17388
+ } catch {
17389
+ }
17390
+ }
17391
+ _localKeyCache = { key, expiresAt: now + LOCAL_KEY_CACHE_MS };
17392
+ return key;
17393
+ }
17394
+ function resolvePretenseKeyForRequest(headerKey) {
17395
+ const explicit = headerKey?.trim();
17396
+ if (explicit) return explicit;
17397
+ return readLocalPretenseApiKey() ?? void 0;
17398
+ }
17360
17399
  function tierApiRoot() {
17361
17400
  const raw2 = (process.env["PRETENSE_API_URL"] ?? "https://api.pretense.ai").trim();
17362
17401
  const normalized = /^https?:\/\//i.test(raw2) ? raw2 : `https://${raw2}`;
@@ -17832,7 +17871,7 @@ function createProxy(opts = {}) {
17832
17871
  return c.json({ error: { type: "invalid_request", message: "Empty request body" } }, 400);
17833
17872
  }
17834
17873
  {
17835
- const _apiKey = c.req.header("x-pretense-api-key") ?? c.req.header("authorization")?.replace(/^Bearer\s+/i, "");
17874
+ const _apiKey = resolvePretenseKeyForRequest(c.req.header("x-pretense-api-key"));
17836
17875
  const _effectiveTier = await resolveKeyTier(_apiKey);
17837
17876
  const _requestEnforcer = new PlanEnforcer(_effectiveTier, planCfg.tenantId);
17838
17877
  const QUOTA_PERIOD_DAYS3 = 7;
@@ -18240,7 +18279,7 @@ ${_upgradeTier === "enterprise" ? "Enterprise offers unlimited mutations \u2014
18240
18279
  sourceIp: c.req.header("x-forwarded-for") ?? c.req.header("x-real-ip")
18241
18280
  }));
18242
18281
  if (anyTokenized) {
18243
- const usageKey = c.req.header("x-pretense-api-key") ?? c.req.header("authorization")?.replace(/^Bearer\s+/i, "");
18282
+ const usageKey = resolvePretenseKeyForRequest(c.req.header("x-pretense-api-key"));
18244
18283
  uploadProxyUsage({
18245
18284
  apiKey: usageKey,
18246
18285
  identifiersMutated: mutationsApplied,
@@ -18369,8 +18408,8 @@ async function findFreePort(start, opts = {}) {
18369
18408
  init_esm_shims();
18370
18409
  import { existsSync as existsSync8, readFileSync as readFileSync6, unlinkSync } from "fs";
18371
18410
  import { join as join7 } from "path";
18372
- import { homedir as homedir3 } from "os";
18373
- function pidFilePath(home = homedir3()) {
18411
+ import { homedir as homedir4 } from "os";
18412
+ function pidFilePath(home = homedir4()) {
18374
18413
  return join7(process.env.PRETENSE_PID_DIR ?? join7(home, ".pretense"), "proxy.pid");
18375
18414
  }
18376
18415
  function isProcessAlive(pid) {
@@ -18585,9 +18624,9 @@ import chalk3 from "chalk";
18585
18624
  init_esm_shims();
18586
18625
  import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
18587
18626
  import { join as join8 } from "path";
18588
- import { homedir as homedir4 } from "os";
18627
+ import { homedir as homedir5 } from "os";
18589
18628
  function credentialsPath() {
18590
- return join8(homedir4(), ".pretense", "credentials.json");
18629
+ return join8(homedir5(), ".pretense", "credentials.json");
18591
18630
  }
18592
18631
  function readApiKey() {
18593
18632
  const fromEnv = process.env["PRETENSE_API_KEY"]?.trim();
@@ -18657,6 +18696,39 @@ function parseEntitlement(body) {
18657
18696
  ...subscriptionStatus !== void 0 ? { subscriptionStatus } : {}
18658
18697
  };
18659
18698
  }
18699
+ function parseCounter(raw2) {
18700
+ if (typeof raw2 !== "number" || !Number.isFinite(raw2)) return null;
18701
+ if (!Number.isInteger(raw2) || raw2 < 0) return null;
18702
+ return raw2;
18703
+ }
18704
+ function parseAllowance(raw2) {
18705
+ if (typeof raw2 !== "number" || !Number.isFinite(raw2) || !Number.isInteger(raw2)) return null;
18706
+ if (raw2 === -1) return { limit: null, unlimited: true };
18707
+ if (raw2 < 0) return null;
18708
+ return { limit: raw2, unlimited: false };
18709
+ }
18710
+ function parseResetInstant(raw2) {
18711
+ if (typeof raw2 !== "string" || raw2.trim().length === 0) return null;
18712
+ const ms = Date.parse(raw2.trim());
18713
+ if (!Number.isFinite(ms)) return null;
18714
+ try {
18715
+ return new Date(ms).toISOString();
18716
+ } catch {
18717
+ return null;
18718
+ }
18719
+ }
18720
+ function parseRemoteQuota(body) {
18721
+ const used = parseCounter(body["codeProtectionsUsed"]);
18722
+ const allowance = parseAllowance(body["monthlyCodeProtectionLimit"]);
18723
+ if (used === null || allowance === null) return void 0;
18724
+ const resetsAt = parseResetInstant(body["periodResetsAt"]);
18725
+ if (allowance.unlimited) {
18726
+ return { used, limit: null, remaining: null, unlimited: true, resetsAt };
18727
+ }
18728
+ const reported = parseCounter(body["codeProtectionsRemaining"]);
18729
+ const remaining2 = reported ?? Math.max(0, allowance.limit - used);
18730
+ return { used, limit: allowance.limit, remaining: remaining2, unlimited: false, resetsAt };
18731
+ }
18660
18732
  async function tierFromBackend(apiKey) {
18661
18733
  const url = `${getConfiguredApiRoot()}/api/cli/auth/validate`;
18662
18734
  const controller = new AbortController();
@@ -18691,7 +18763,13 @@ async function tierFromBackend(apiKey) {
18691
18763
  detail: "the plan service did not report a recognizable plan"
18692
18764
  };
18693
18765
  }
18694
- return { tier, source: "backend", entitlement: parseEntitlement(body) };
18766
+ const quota2 = parseRemoteQuota(body);
18767
+ return {
18768
+ tier,
18769
+ source: "backend",
18770
+ entitlement: parseEntitlement(body),
18771
+ ...quota2 ? { quota: quota2 } : {}
18772
+ };
18695
18773
  } catch (err) {
18696
18774
  const aborted = err?.name === "AbortError";
18697
18775
  return {
@@ -18753,6 +18831,87 @@ function collectUsage(dbPath) {
18753
18831
  function resetCadenceLabel() {
18754
18832
  return `every ${QUOTA_PERIOD_DAYS2} days`;
18755
18833
  }
18834
+ function formatResetInstant(iso) {
18835
+ const ms = Date.parse(iso);
18836
+ if (!Number.isFinite(ms)) return iso;
18837
+ return new Date(ms).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, " UTC");
18838
+ }
18839
+ function resetLabel(quota2) {
18840
+ return quota2?.resetsAt ? formatResetInstant(quota2.resetsAt) : resetCadenceLabel();
18841
+ }
18842
+ function percentOf(used, limit) {
18843
+ if (limit <= 0) return used > 0 ? 100 : 0;
18844
+ return Math.round(used / limit * 100);
18845
+ }
18846
+ function resolveMutationBudget(resolution, localMutationsApplied) {
18847
+ const quota2 = resolution.quota;
18848
+ if (quota2) {
18849
+ return {
18850
+ source: "server",
18851
+ used: quota2.used,
18852
+ limit: quota2.limit,
18853
+ remaining: quota2.remaining,
18854
+ percent: quota2.unlimited ? null : percentOf(quota2.used, quota2.limit ?? 0),
18855
+ unlimited: quota2.unlimited,
18856
+ limitUnknown: false,
18857
+ resetsAt: quota2.resetsAt
18858
+ };
18859
+ }
18860
+ const tier = resolution.tier;
18861
+ if (!tier) {
18862
+ return {
18863
+ source: "local-audit",
18864
+ used: localMutationsApplied,
18865
+ limit: null,
18866
+ remaining: null,
18867
+ percent: null,
18868
+ unlimited: false,
18869
+ limitUnknown: true,
18870
+ resetsAt: null
18871
+ };
18872
+ }
18873
+ const planLimit = PLAN_CONFIGS[tier].mutationsPerPeriod;
18874
+ if (planLimit === null) {
18875
+ return {
18876
+ source: "local-audit",
18877
+ used: localMutationsApplied,
18878
+ limit: null,
18879
+ remaining: null,
18880
+ percent: null,
18881
+ unlimited: true,
18882
+ limitUnknown: false,
18883
+ resetsAt: null
18884
+ };
18885
+ }
18886
+ return {
18887
+ source: "local-audit",
18888
+ used: localMutationsApplied,
18889
+ limit: planLimit,
18890
+ remaining: Math.max(0, planLimit - localMutationsApplied),
18891
+ percent: percentOf(localMutationsApplied, planLimit),
18892
+ unlimited: false,
18893
+ limitUnknown: false,
18894
+ resetsAt: null
18895
+ };
18896
+ }
18897
+ function budgetSourceLines(budget, resolution, localMutationsApplied) {
18898
+ if (budget.source === "server") {
18899
+ const lines = [
18900
+ "Mutations: counted by the Pretense API \u2014 this is the figure your quota is enforced against."
18901
+ ];
18902
+ if (localMutationsApplied !== budget.used) {
18903
+ lines.push(
18904
+ `This machine's local audit log records ${localMutationsApplied.toLocaleString("en-US")} \u2014 a different machine set and window, not your quota.`
18905
+ );
18906
+ }
18907
+ return lines;
18908
+ }
18909
+ const why = resolution.source === "no-credentials" ? "you are not signed in, so no server was asked" : resolution.source === "key-prefix" ? "a legacy pt- key names its plan offline, so no server was asked for your usage" : resolution.source === "unverified" ? resolution.detail ?? "the plan service could not be reached" : "the plan service did not report usage figures";
18910
+ return [
18911
+ "Mutations: counted LOCALLY on this machine (~/.pretense/audit.db) \u2014 this is NOT",
18912
+ `the usage your quota is enforced against, because ${why}.`
18913
+ ];
18914
+ }
18756
18915
  function priceLabel(tier) {
18757
18916
  const cents = PLAN_CONFIGS[tier].pricePerSeatCents;
18758
18917
  if (cents === null) return "custom \u2014 contact sales";
@@ -20174,7 +20333,7 @@ function configCommand() {
20174
20333
  init_esm_shims();
20175
20334
  import { Command as Command8 } from "commander";
20176
20335
  import chalk8 from "chalk";
20177
- import { homedir as homedir5 } from "os";
20336
+ import { homedir as homedir6 } from "os";
20178
20337
  import { join as join12 } from "path";
20179
20338
  function logsCommand() {
20180
20339
  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) => {
@@ -20182,7 +20341,7 @@ function logsCommand() {
20182
20341
  let store;
20183
20342
  let entries;
20184
20343
  try {
20185
- store = new AuditStore(join12(homedir5(), ".pretense", "audit.db"));
20344
+ store = new AuditStore(join12(homedir6(), ".pretense", "audit.db"));
20186
20345
  entries = store.getRecentEntries(limit);
20187
20346
  store.close();
20188
20347
  } catch {
@@ -20223,7 +20382,7 @@ init_esm_shims();
20223
20382
  import { Command as Command9 } from "commander";
20224
20383
  import chalk9 from "chalk";
20225
20384
  import { join as join13 } from "path";
20226
- import { homedir as homedir6 } from "os";
20385
+ import { homedir as homedir7 } from "os";
20227
20386
  function renderBar(percent, width = 30) {
20228
20387
  const filled = Math.min(Math.round(percent / 100 * width), width);
20229
20388
  const empty = width - filled;
@@ -20242,27 +20401,43 @@ function percentLabel(percent) {
20242
20401
  if (percent >= 80) return chalk9.yellow(`${percent}%`);
20243
20402
  return chalk9.green(`${percent}%`);
20244
20403
  }
20404
+ function mutationsPayload(budget) {
20405
+ return {
20406
+ source: budget.source,
20407
+ current: budget.used,
20408
+ limit: budget.limit,
20409
+ percent: budget.percent,
20410
+ remaining: budget.remaining,
20411
+ // `allowed` is only knowable with an allowance. `limitUnknown` must not
20412
+ // read as `true` — "we could not check" is not "you are fine".
20413
+ allowed: budget.limitUnknown ? null : budget.unlimited || (budget.remaining ?? 0) > 0,
20414
+ unlimited: budget.unlimited
20415
+ };
20416
+ }
20245
20417
  function emptyUsagePayload(resolution) {
20246
20418
  const tier = resolution.tier;
20247
20419
  const planConfig = tier ? PLAN_CONFIGS[tier] : null;
20420
+ const budget = resolveMutationBudget(resolution, 0);
20248
20421
  return {
20249
20422
  tier,
20250
20423
  tierSource: resolution.source,
20251
20424
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
20252
20425
  periodStart: null,
20253
20426
  periodEnd: null,
20254
- resetDate: null,
20427
+ resetDate: budget.resetsAt,
20255
20428
  resetCadenceDays: QUOTA_PERIOD_DAYS2,
20429
+ quotaSource: budget.source,
20256
20430
  usage: {
20257
- mutations: { current: 0, limit: planConfig ? planConfig.mutationsPerPeriod : null, percent: 0, allowed: true },
20258
- scans: { current: 0, limit: planConfig ? planConfig.scansPerPeriod : null, percent: 0, allowed: true }
20431
+ mutations: mutationsPayload(budget),
20432
+ scans: { source: "local-audit", current: 0, limit: planConfig ? planConfig.scansPerPeriod : null, percent: 0, allowed: true }
20259
20433
  },
20260
- overallPercent: 0,
20434
+ localAudit: { mutations: 0, scans: 0, secretsBlocked: 0 },
20435
+ overallPercent: budget.percent ?? 0,
20261
20436
  recommendedUpgrade: null
20262
20437
  };
20263
20438
  }
20264
20439
  function usageCommand() {
20265
- 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) => {
20440
+ return new Command9("usage").description("Show plan usage vs limits for the current billing period").option("--db <path>", "Path to audit database", join13(homedir7(), ".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) => {
20266
20441
  const resolution = await resolvePlanTier({ offline: opts.offline });
20267
20442
  const tier = resolution.tier;
20268
20443
  const collected = collectUsage(opts.db);
@@ -20278,6 +20453,7 @@ function usageCommand() {
20278
20453
  return;
20279
20454
  }
20280
20455
  const usageMetrics = collected.metrics;
20456
+ const budget = resolveMutationBudget(resolution, usageMetrics.mutationsApplied);
20281
20457
  if (opts.json) {
20282
20458
  const limits = tier ? checkAllLimits(usageMetrics, tier) : null;
20283
20459
  const planConfig2 = tier ? PLAN_CONFIGS[tier] : null;
@@ -20289,23 +20465,33 @@ function usageCommand() {
20289
20465
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
20290
20466
  periodStart: usageMetrics.periodStart,
20291
20467
  periodEnd: usageMetrics.periodEnd,
20292
- resetDate: null,
20468
+ // The server's own instant when it reported one. `null` means the
20469
+ // CLI was not told — it does NOT mean "no reset exists", and it is
20470
+ // not an invitation to compute one.
20471
+ resetDate: budget.resetsAt,
20293
20472
  resetCadenceDays: QUOTA_PERIOD_DAYS2,
20473
+ quotaSource: budget.source,
20294
20474
  usage: {
20295
- mutations: {
20296
- current: usageMetrics.mutationsApplied,
20297
- limit: planConfig2 ? planConfig2.mutationsPerPeriod : null,
20298
- percent: limits ? limits.mutations.usagePercent ?? 0 : null,
20299
- allowed: limits ? limits.mutations.allowed : null
20300
- },
20475
+ mutations: mutationsPayload(budget),
20476
+ // Scans have no server-side counterpart on the validate
20477
+ // response, so this half is local and says so.
20301
20478
  scans: {
20479
+ source: "local-audit",
20302
20480
  current: usageMetrics.totalScans,
20303
20481
  limit: planConfig2 ? planConfig2.scansPerPeriod : null,
20304
20482
  percent: limits ? limits.scans.usagePercent ?? 0 : null,
20305
20483
  allowed: limits ? limits.scans.allowed : null
20306
20484
  }
20307
20485
  },
20308
- overallPercent: tier ? maxUsagePercent(usageMetrics, tier) : null,
20486
+ // The raw local counters, always under a key that names them as
20487
+ // local, so a consumer that wants this machine's view can have it
20488
+ // without it ever being mistaken for the quota.
20489
+ localAudit: {
20490
+ mutations: usageMetrics.mutationsApplied,
20491
+ scans: usageMetrics.totalScans,
20492
+ secretsBlocked: usageMetrics.secretsBlocked
20493
+ },
20494
+ overallPercent: budget.percent ?? (tier ? maxUsagePercent(usageMetrics, tier) : null),
20309
20495
  recommendedUpgrade: tier ? recommendedTier(usageMetrics, tier) : null
20310
20496
  },
20311
20497
  null,
@@ -20314,11 +20500,11 @@ function usageCommand() {
20314
20500
  );
20315
20501
  return;
20316
20502
  }
20317
- const resetDate = resetCadenceLabel();
20503
+ const resetDate = resetLabel(resolution.quota);
20318
20504
  console.log(
20319
20505
  chalk9.cyan(
20320
- "\n Pretense Usage \u2014 " + // Header shows today's date, NOT a billing month: quotas roll on a
20321
- // 7-day window anchored to signup, so a month name would imply a
20506
+ "\n Pretense Usage \u2014 " + // Header shows today's date, NOT a billing month: the quota window
20507
+ // trails the account's own activity, so a month name would imply a
20322
20508
  // reset boundary that does not exist.
20323
20509
  (/* @__PURE__ */ new Date()).toLocaleDateString("en-US", {
20324
20510
  month: "long",
@@ -20337,35 +20523,60 @@ function usageCommand() {
20337
20523
  console.log(chalk9.yellow(` ${line}`));
20338
20524
  }
20339
20525
  console.log();
20340
- console.log(" " + "Mutations".padEnd(20) + usageMetrics.mutationsApplied.toLocaleString("en-US"));
20526
+ console.log(" " + "Mutations".padEnd(20) + budget.used.toLocaleString("en-US"));
20341
20527
  console.log(" " + "API Requests".padEnd(20) + usageMetrics.totalScans.toLocaleString("en-US"));
20342
20528
  console.log(" " + "Secrets Blocked".padEnd(20) + usageMetrics.secretsBlocked.toLocaleString("en-US"));
20343
20529
  console.log();
20530
+ for (const line of budgetSourceLines(budget, resolution, usageMetrics.mutationsApplied)) {
20531
+ console.log(chalk9.dim(` ${line}`));
20532
+ }
20533
+ console.log();
20344
20534
  return;
20345
20535
  }
20346
20536
  const planConfig = PLAN_CONFIGS[tier];
20347
- const overall = maxUsagePercent(usageMetrics, tier);
20537
+ const overall = budget.source === "server" ? budget.percent ?? 0 : maxUsagePercent(usageMetrics, tier);
20348
20538
  const nextTier = recommendedTier(usageMetrics, tier);
20349
20539
  console.log(
20350
20540
  " " + chalk9.bold("Metric".padEnd(20)) + chalk9.bold("Used / Limit".padEnd(30)) + chalk9.bold("Progress")
20351
20541
  );
20352
20542
  console.log(" " + chalk9.dim("\u2500".repeat(72)));
20353
20543
  const rows = [
20354
- { label: "Mutations", value: usageMetrics.mutationsApplied, limit: planConfig.mutationsPerPeriod },
20355
- { label: "API Requests", value: usageMetrics.totalScans, limit: planConfig.scansPerPeriod }
20544
+ {
20545
+ label: "Mutations",
20546
+ value: budget.used,
20547
+ // `limitUnknown` must render as unknown, NOT as unlimited. That
20548
+ // conflation is the original defect: an org at 100% of its quota read
20549
+ // every missing field as "no limit" and was told it had none.
20550
+ limit: budget.limitUnknown ? null : budget.limit,
20551
+ percent: budget.percent
20552
+ },
20553
+ {
20554
+ label: "API Requests",
20555
+ value: usageMetrics.totalScans,
20556
+ limit: planConfig.scansPerPeriod,
20557
+ percent: null
20558
+ }
20356
20559
  ];
20357
20560
  for (const row of rows) {
20358
- const percent = row.limit === null ? 0 : Math.round(row.value / row.limit * 100);
20359
- const bar2 = row.limit === null ? chalk9.dim(" unlimited ".padEnd(32)) : renderBar(percent);
20561
+ const percent = row.percent ?? (row.limit === null ? 0 : Math.round(row.value / row.limit * 100));
20562
+ const unknown = row.label === "Mutations" && budget.limitUnknown;
20563
+ const bar2 = unknown ? chalk9.dim(" not reported ".padEnd(32)) : row.limit === null ? chalk9.dim(" unlimited ".padEnd(32)) : renderBar(percent);
20564
+ const used = unknown ? `${row.value.toLocaleString("en-US")} / not reported` : formatMetric(row.value, row.limit);
20360
20565
  console.log(
20361
- " " + row.label.padEnd(20) + formatMetric(row.value, row.limit).padEnd(30) + bar2 + " " + (row.limit === null ? "" : percentLabel(percent))
20566
+ " " + row.label.padEnd(20) + used.padEnd(30) + bar2 + " " + (row.limit === null || unknown ? "" : percentLabel(percent))
20362
20567
  );
20363
20568
  }
20364
20569
  console.log(
20365
20570
  " " + "Secrets Blocked".padEnd(20) + String(usageMetrics.secretsBlocked.toLocaleString("en-US")).padEnd(30) + chalk9.dim("(no limit)")
20366
20571
  );
20367
20572
  console.log(" " + chalk9.dim("\u2500".repeat(72)));
20368
- if (tier === "enterprise") {
20573
+ for (const line of budgetSourceLines(budget, resolution, usageMetrics.mutationsApplied)) {
20574
+ console.log(" " + chalk9.dim(line));
20575
+ }
20576
+ console.log(
20577
+ " " + chalk9.dim("API Requests / Secrets Blocked: counted locally on this machine.")
20578
+ );
20579
+ if (tier === "enterprise" && budget.unlimited) {
20369
20580
  console.log(chalk9.green("\n Enterprise plan \u2014 all limits are unlimited.\n"));
20370
20581
  } else if (overall >= 100) {
20371
20582
  console.log(chalk9.red(`
@@ -20415,9 +20626,9 @@ import { Command as Command10 } from "commander";
20415
20626
  import chalk10 from "chalk";
20416
20627
  import { existsSync as existsSync12, readFileSync as readFileSync11, unlinkSync as unlinkSync2 } from "fs";
20417
20628
  import { join as join14 } from "path";
20418
- import { homedir as homedir7 } from "os";
20629
+ import { homedir as homedir8 } from "os";
20419
20630
  import { execSync as execSync4 } from "child_process";
20420
- var CREDENTIALS_DIR = join14(homedir7(), ".pretense");
20631
+ var CREDENTIALS_DIR = join14(homedir8(), ".pretense");
20421
20632
  var CREDENTIALS_PATH = join14(CREDENTIALS_DIR, "credentials.json");
20422
20633
  async function verifyApiKey(apiKey) {
20423
20634
  const url = `${getConfiguredApiRoot()}/api/cli/auth/validate`;
@@ -21001,9 +21212,9 @@ import { Command as Command13 } from "commander";
21001
21212
  import chalk13 from "chalk";
21002
21213
  import { appendFileSync as appendFileSync3, readFileSync as readFileSync14, existsSync as existsSync14 } from "fs";
21003
21214
  import { join as join17 } from "path";
21004
- import { homedir as homedir8 } from "os";
21215
+ import { homedir as homedir9 } from "os";
21005
21216
  var IGNORE_FILE = ".pretenseignore";
21006
- var LAST_SCAN_PATH = join17(homedir8(), ".pretense", "last-scan.json");
21217
+ var LAST_SCAN_PATH = join17(homedir9(), ".pretense", "last-scan.json");
21007
21218
  function ignorePath() {
21008
21219
  return join17(process.cwd(), IGNORE_FILE);
21009
21220
  }
@@ -21401,9 +21612,9 @@ import { resolve as resolve6, extname as extname4 } from "path";
21401
21612
  // src/secret-crypto.ts
21402
21613
  init_esm_shims();
21403
21614
  import { createCipheriv, createDecipheriv, randomBytes as randomBytes3, hkdfSync as hkdfSync2 } from "crypto";
21404
- import { homedir as homedir9 } from "os";
21615
+ import { homedir as homedir10 } from "os";
21405
21616
  import { join as join19 } from "path";
21406
- var KEYS_DIR = join19(homedir9(), ".pretense", "keys");
21617
+ var KEYS_DIR = join19(homedir10(), ".pretense", "keys");
21407
21618
  var HKDF_SALT2 = Buffer.from("pretense-maps-v1", "utf-8");
21408
21619
  var HKDF_INFO2 = Buffer.from("secret-mapping-aes-256-gcm", "utf-8");
21409
21620
  var IV_LEN = 12;
@@ -21521,12 +21732,12 @@ function ensureDefaultMapPath(absFilePath, override2) {
21521
21732
 
21522
21733
  // src/local-audit.ts
21523
21734
  init_esm_shims();
21524
- import { homedir as homedir10 } from "os";
21735
+ import { homedir as homedir11 } from "os";
21525
21736
  import { join as join24 } from "path";
21526
21737
  import { randomUUID } from "crypto";
21527
21738
  import chalk15 from "chalk";
21528
21739
  function localAuditDbPath() {
21529
- return join24(homedir10(), ".pretense", "audit.db");
21740
+ return join24(homedir11(), ".pretense", "audit.db");
21530
21741
  }
21531
21742
  function recordLocalAudit(rec, dbPath = localAuditDbPath()) {
21532
21743
  let store = null;
@@ -22524,7 +22735,7 @@ function policyCommand() {
22524
22735
  init_esm_shims();
22525
22736
  import { Command as Command20 } from "commander";
22526
22737
  import chalk20 from "chalk";
22527
- import { homedir as homedir11 } from "os";
22738
+ import { homedir as homedir12 } from "os";
22528
22739
  import { join as join25 } from "path";
22529
22740
  var VALID_FORMATS2 = ["table", "csv", "json", "jsonl"];
22530
22741
  function toSafeAuditRow(e) {
@@ -22649,7 +22860,7 @@ function auditCommand() {
22649
22860
  `));
22650
22861
  process.exit(1);
22651
22862
  }
22652
- const dbPath = opts.db ?? join25(homedir11(), ".pretense", "audit.db");
22863
+ const dbPath = opts.db ?? join25(homedir12(), ".pretense", "audit.db");
22653
22864
  let rows;
22654
22865
  try {
22655
22866
  const store = new AuditStore(dbPath);
@@ -22710,7 +22921,7 @@ init_esm_shims();
22710
22921
  import { Command as Command21 } from "commander";
22711
22922
  import chalk21 from "chalk";
22712
22923
  import { join as join26 } from "path";
22713
- import { homedir as homedir12 } from "os";
22924
+ import { homedir as homedir13 } from "os";
22714
22925
  function remaining(used, limit) {
22715
22926
  if (limit === null) return null;
22716
22927
  return Math.max(0, limit - used);
@@ -22728,7 +22939,7 @@ function bar(used, limit, width = 24) {
22728
22939
  return chalk21.green(s);
22729
22940
  }
22730
22941
  function creditsCommand() {
22731
- 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) => {
22942
+ return new Command21("credits").alias("tokens").description("Show remaining mutation budget for the current period").option("--db <path>", "Path to audit database", join26(homedir13(), ".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) => {
22732
22943
  const resolution = await resolvePlanTier({ offline: opts.offline });
22733
22944
  const tier = resolution.tier;
22734
22945
  const collected = collectUsage(opts.db);
@@ -22743,9 +22954,10 @@ function creditsCommand() {
22743
22954
  if (!collected && !opts.json) {
22744
22955
  console.log(chalk21.dim("\n No usage recorded yet \u2014 showing your full allowance.\n"));
22745
22956
  }
22746
- const mutationLimit = tier ? PLAN_CONFIGS[tier].mutationsPerPeriod : null;
22957
+ const budget = resolveMutationBudget(resolution, m.mutationsApplied);
22958
+ const mutationLimit = budget.limitUnknown ? null : budget.limit;
22959
+ const mutationsLeft = budget.limitUnknown ? null : budget.remaining;
22747
22960
  const scanLimit = tier ? PLAN_CONFIGS[tier].scansPerPeriod : null;
22748
- const mutationsLeft = tier ? remaining(m.mutationsApplied, mutationLimit) : null;
22749
22961
  const scansLeft = tier ? remaining(m.totalScans, scanLimit) : null;
22750
22962
  if (opts.json) {
22751
22963
  console.log(
@@ -22754,18 +22966,26 @@ function creditsCommand() {
22754
22966
  tier,
22755
22967
  tierSource: resolution.source,
22756
22968
  ...resolution.detail ? { tierDetail: resolution.detail } : {},
22757
- resetDate: null,
22969
+ // The server's instant when it sent one. It sends `periodResetsAt`
22970
+ // on every validation; this used to be hardcoded `null` while a
22971
+ // hardcoded cadence string was printed beside it.
22972
+ resetDate: budget.resetsAt,
22758
22973
  resetCadenceDays: QUOTA_PERIOD_DAYS2,
22974
+ quotaSource: budget.source,
22759
22975
  mutations: {
22760
- used: m.mutationsApplied,
22761
- limit: tier ? mutationLimit : null,
22762
- remaining: tier ? mutationsLeft : null
22976
+ source: budget.source,
22977
+ used: budget.used,
22978
+ limit: mutationLimit,
22979
+ remaining: mutationsLeft,
22980
+ unlimited: budget.unlimited
22763
22981
  },
22764
22982
  scans: {
22983
+ source: "local-audit",
22765
22984
  used: m.totalScans,
22766
22985
  limit: tier ? scanLimit : null,
22767
22986
  remaining: tier ? scansLeft : null
22768
- }
22987
+ },
22988
+ localAudit: { mutations: m.mutationsApplied, scans: m.totalScans }
22769
22989
  },
22770
22990
  null,
22771
22991
  2
@@ -22774,29 +22994,39 @@ function creditsCommand() {
22774
22994
  return;
22775
22995
  }
22776
22996
  console.log(chalk21.cyan("\n Pretense Credits"));
22777
- console.log(chalk21.dim(` Plan: ${tierLabel(tier)} | Resets: ${resetCadenceLabel()}
22778
- `));
22997
+ console.log(
22998
+ chalk21.dim(` Plan: ${tierLabel(tier)} | Resets: ${resetLabel(resolution.quota)}
22999
+ `)
23000
+ );
22779
23001
  if (!tier) {
22780
23002
  for (const line of unverifiedNote(resolution.detail)) {
22781
23003
  console.log(chalk21.yellow(` ${line}`));
22782
23004
  }
22783
23005
  console.log();
22784
- console.log(" " + "Mutations used".padEnd(20) + m.mutationsApplied.toLocaleString("en-US"));
23006
+ console.log(" " + "Mutations used".padEnd(20) + budget.used.toLocaleString("en-US"));
22785
23007
  console.log(" " + "Requests used".padEnd(20) + m.totalScans.toLocaleString("en-US"));
22786
23008
  console.log();
23009
+ for (const line of budgetSourceLines(budget, resolution, m.mutationsApplied)) {
23010
+ console.log(chalk21.dim(` ${line}`));
23011
+ }
23012
+ console.log();
22787
23013
  return;
22788
23014
  }
22789
23015
  console.log(
22790
- " " + "Mutations".padEnd(14) + bar(m.mutationsApplied, mutationLimit) + " " + chalk21.bold(fmt(mutationsLeft)) + chalk21.dim(` left of ${fmt(mutationLimit)}`)
23016
+ " " + "Mutations".padEnd(14) + bar(budget.used, mutationLimit) + " " + chalk21.bold(fmt(mutationsLeft)) + chalk21.dim(` left of ${fmt(mutationLimit)}`)
22791
23017
  );
22792
23018
  console.log(
22793
23019
  " " + "Requests".padEnd(14) + bar(m.totalScans, scanLimit) + " " + chalk21.bold(fmt(scansLeft)) + chalk21.dim(` left of ${fmt(scanLimit)}`)
22794
23020
  );
23021
+ console.log();
23022
+ for (const line of budgetSourceLines(budget, resolution, m.mutationsApplied)) {
23023
+ console.log(chalk21.dim(` ${line}`));
23024
+ }
22795
23025
  if (tier !== "enterprise" && mutationLimit !== null && mutationsLeft === 0) {
22796
23026
  console.log(
22797
23027
  chalk21.red("\n Mutation budget exhausted for this period.") + chalk21.dim(" Run ") + chalk21.cyan("pretense upgrade") + chalk21.dim(" to compare plans.")
22798
23028
  );
22799
- } else if (tier !== "enterprise" && mutationLimit !== null && m.mutationsApplied >= mutationLimit * 0.8) {
23029
+ } else if (tier !== "enterprise" && mutationLimit !== null && budget.used >= mutationLimit * 0.8) {
22800
23030
  console.log(
22801
23031
  chalk21.yellow("\n Running low.") + chalk21.dim(" Run ") + chalk21.cyan("pretense upgrade") + chalk21.dim(" to compare plans.")
22802
23032
  );
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  PRETENSE_VERSION
4
- } from "./chunk-XYBJ63JG.js";
4
+ } from "./chunk-6L7NU7KI.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.25",
3
+ "version": "0.6.27",
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/learner": "0.2.0",
28
27
  "@pretense/compliance-engine": "0.1.0",
29
- "@pretense/mutator": "0.2.0",
28
+ "@pretense/protocol": "0.1.0",
29
+ "@pretense/learner": "0.2.0",
30
30
  "@pretense/proxy": "0.1.0",
31
+ "@pretense/mutator": "0.2.0",
31
32
  "@pretense/scanner": "0.2.0",
32
33
  "@pretense/scanner-rs": "0.2.0",
33
- "@pretense/store": "0.2.0",
34
- "@pretense/protocol": "0.1.0"
34
+ "@pretense/store": "0.2.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public",