brainjar 0.3.1228 → 0.3.1230

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/brainjar.js +230 -117
  2. package/package.json +1 -1
package/brainjar.js CHANGED
@@ -107,7 +107,7 @@ var CLI_VERSION, CACHE, DAY;
107
107
  var init_version_check = __esm({
108
108
  "packages/cli/src/version-check.ts"() {
109
109
  "use strict";
110
- CLI_VERSION = "0.3.1228";
110
+ CLI_VERSION = "0.3.1230";
111
111
  CACHE = path.join(os.homedir(), ".brainjar", "version-check.json");
112
112
  DAY = 864e5;
113
113
  }
@@ -6496,8 +6496,12 @@ var init_settings = __esm({
6496
6496
  // "on" | "off": monthly contingent per workspace (default on in SaaS)
6497
6497
  "ai_free_monthly_usd",
6498
6498
  // Free workspace, USD per month
6499
+ "ai_pro_monthly_usd",
6500
+ // Pro workspace, USD per billing period (BJ-132)
6499
6501
  "ai_team_seat_monthly_usd",
6500
- // Team, USD per seat and month (applies once licenses exist)
6502
+ // Company, USD per seat and billing period, pooled (BJ-132)
6503
+ "credits_per_usd",
6504
+ // what SaaS customers see: credits per USD of inference (BJ-132)
6501
6505
  "ai_operator_exempt",
6502
6506
  // "true" | "false": operator accounts are never capped
6503
6507
  "ai_daily_budget_usd",
@@ -6524,7 +6528,9 @@ var init_settings = __esm({
6524
6528
  "stripe_price_company",
6525
6529
  "stripe_price_license_pro",
6526
6530
  "stripe_price_license_company",
6527
- "billing_consent_text"
6531
+ "billing_consent_text",
6532
+ // Credit packs (BJ-132 step 3): one per line, "<Stripe price id> <credits>".
6533
+ "credit_packs"
6528
6534
  ];
6529
6535
  PROVIDER_KEY_SETTINGS = {
6530
6536
  anthropic: "anthropic_api_key",
@@ -6547,7 +6553,7 @@ MCowBQYDK2VwAyEAKOUWx/Qgav+dKg78fIlckGby22CRKeuMWR5tkT1rYuw=
6547
6553
 
6548
6554
  // packages/db/src/plan.ts
6549
6555
  import { createPublicKey, sign, verify } from "node:crypto";
6550
- var trustedKeys;
6556
+ var trustedKeys, PAID_STATUSES;
6551
6557
  var init_plan = __esm({
6552
6558
  "packages/db/src/plan.ts"() {
6553
6559
  "use strict";
@@ -6556,6 +6562,7 @@ var init_plan = __esm({
6556
6562
  init_settings();
6557
6563
  init_license_key();
6558
6564
  trustedKeys = [createPublicKey(LICENSE_PUBLIC_KEY)];
6565
+ PAID_STATUSES = ["active", "trialing", "past_due"];
6559
6566
  }
6560
6567
  });
6561
6568
 
@@ -8189,9 +8196,6 @@ var init_health = __esm({
8189
8196
  });
8190
8197
 
8191
8198
  // packages/db/src/spend.ts
8192
- function monthKey(ownerId, at = /* @__PURE__ */ new Date()) {
8193
- return `ws:${ownerId}:${at.toISOString().slice(0, 7)}`;
8194
- }
8195
8199
  function dayKey(at = /* @__PURE__ */ new Date()) {
8196
8200
  return `day:${at.toISOString().slice(0, 10)}`;
8197
8201
  }
@@ -8203,19 +8207,59 @@ function dayRenewal(at = /* @__PURE__ */ new Date()) {
8203
8207
  Date.UTC(at.getUTCFullYear(), at.getUTCMonth(), at.getUTCDate() + 1)
8204
8208
  );
8205
8209
  }
8206
- async function addSpend(brainId, usd, at = /* @__PURE__ */ new Date(), pool2 = getPool()) {
8210
+ async function addSpend(brainId, usd, at = /* @__PURE__ */ new Date(), pool2 = getPool(), credits) {
8207
8211
  if (!(usd > 0)) return;
8208
- await pool2.query(
8212
+ const r = await pool2.query(
8209
8213
  `INSERT INTO llm_spend (key, usd)
8210
8214
  SELECT k, $2 FROM (
8211
8215
  SELECT $3::text AS k
8212
8216
  UNION ALL
8213
- SELECT 'ws:' || b.owner_id || ':' || $4 FROM brain b
8214
- WHERE b.id = $1 AND b.owner_id IS NOT NULL
8217
+ (SELECT 'ws:' || b.owner_id || ':' || $4 FROM brain b
8218
+ WHERE b.id = $1 AND b.owner_id IS NOT NULL
8219
+ UNION
8220
+ SELECT credit_key(b.owner_id, $5) FROM brain b
8221
+ WHERE b.id = $1 AND b.owner_id IS NOT NULL)
8215
8222
  ) keys
8216
- ON CONFLICT (key) DO UPDATE SET usd = llm_spend.usd + EXCLUDED.usd, updated_at = now()`,
8217
- [brainId, usd, dayKey(at), at.toISOString().slice(0, 7)]
8223
+ ON CONFLICT (key) DO UPDATE SET usd = llm_spend.usd + EXCLUDED.usd, updated_at = now()
8224
+ RETURNING key, usd`,
8225
+ [brainId, usd, dayKey(at), at.toISOString().slice(0, 7), at]
8226
+ );
8227
+ if (!credits) return;
8228
+ const row = r.rows.find((x) => x.key.startsWith("wsp:")) ?? r.rows.find((x) => x.key.startsWith("ws:"));
8229
+ if (!row) return;
8230
+ const over = Math.min(
8231
+ usd,
8232
+ Math.max(0, Number(row.usd) - credits.includedUsd)
8233
+ );
8234
+ if (over > 0)
8235
+ await pool2.query(
8236
+ `UPDATE credit_balance SET credits = greatest(0, credits - $2), updated_at = now()
8237
+ WHERE user_id = $1`,
8238
+ [row.key.split(":")[1], over * credits.creditsPerUsd]
8239
+ );
8240
+ }
8241
+ async function creditPeriod(ownerId, at = /* @__PURE__ */ new Date(), pool2 = getPool()) {
8242
+ const r = await pool2.query(
8243
+ `SELECT credit_key($1, $2) AS key, ap.period_start, ap.period_end
8244
+ FROM (SELECT 1) one
8245
+ LEFT JOIN account_plan ap ON ap.user_id = $1
8246
+ AND ap.status IN ('active', 'trialing', 'past_due')
8247
+ AND ap.period_start <= $2 AND ap.period_end > $2`,
8248
+ [ownerId, at]
8249
+ );
8250
+ const row = r.rows[0];
8251
+ return row.key.startsWith("wsp:") && row.period_start && row.period_end ? { key: row.key, start: row.period_start, end: row.period_end } : {
8252
+ key: row.key,
8253
+ start: new Date(Date.UTC(at.getUTCFullYear(), at.getUTCMonth(), 1)),
8254
+ end: monthRenewal(at)
8255
+ };
8256
+ }
8257
+ async function purchasedCredits(ownerId, pool2 = getPool()) {
8258
+ const r = await pool2.query(
8259
+ "SELECT credits FROM credit_balance WHERE user_id = $1",
8260
+ [ownerId]
8218
8261
  );
8262
+ return Number(r.rows[0]?.credits ?? 0);
8219
8263
  }
8220
8264
  async function spendUsd(key, pool2 = getPool()) {
8221
8265
  const r = await pool2.query(
@@ -8242,6 +8286,19 @@ var init_spend = __esm({
8242
8286
  });
8243
8287
 
8244
8288
  // packages/db/src/billing.ts
8289
+ async function accountPlanRow(userId, pool2 = getPool()) {
8290
+ const r = await pool2.query(
8291
+ `SELECT plan, seats, status, period_end, cancel_at_period_end, ended_at
8292
+ FROM account_plan WHERE user_id = $1`,
8293
+ [userId]
8294
+ );
8295
+ return r.rows[0] ?? null;
8296
+ }
8297
+ async function paidAccountPlan(userId, pool2 = getPool()) {
8298
+ const row = await accountPlanRow(userId, pool2);
8299
+ return row && isPaid(row.status) ? { plan: row.plan, seats: row.seats } : null;
8300
+ }
8301
+ var isPaid;
8245
8302
  var init_billing = __esm({
8246
8303
  "packages/db/src/billing.ts"() {
8247
8304
  "use strict";
@@ -8249,6 +8306,7 @@ var init_billing = __esm({
8249
8306
  init_pool();
8250
8307
  init_settings();
8251
8308
  init_plan();
8309
+ isPaid = (status) => PAID_STATUSES.includes(status);
8252
8310
  }
8253
8311
  });
8254
8312
 
@@ -8315,7 +8373,7 @@ var init_ruleset = __esm({
8315
8373
  });
8316
8374
 
8317
8375
  // packages/db/src/usage.ts
8318
- async function recordLlmUsage(u) {
8376
+ async function recordLlmUsage(u, credits) {
8319
8377
  await getPool().query(
8320
8378
  `INSERT INTO llm_usage (brain_id, workload, model, triggered_by, input_tokens, cached_input_tokens, output_tokens)
8321
8379
  VALUES ($1,$2,$3,$4,$5,$6,$7)`,
@@ -8335,7 +8393,10 @@ async function recordLlmUsage(u) {
8335
8393
  input: u.inputTokens,
8336
8394
  cachedInput: u.cachedInputTokens,
8337
8395
  output: u.outputTokens
8338
- })
8396
+ }),
8397
+ void 0,
8398
+ void 0,
8399
+ await credits?.().catch(() => null)
8339
8400
  );
8340
8401
  }
8341
8402
  var init_usage = __esm({
@@ -13346,6 +13407,145 @@ var init_provider_rate2 = __esm({
13346
13407
  }
13347
13408
  });
13348
13409
 
13410
+ // packages/ingest/src/allowance.ts
13411
+ async function aiBudgetSettings() {
13412
+ const env = loadEnv();
13413
+ const stored = await getAppSettings().catch(
13414
+ () => ({})
13415
+ );
13416
+ const pick = (key, envValue) => envValue?.trim() || stored[key];
13417
+ const contingent = pick("ai_contingent", env.BRAINJAR_AI_CONTINGENT);
13418
+ const exempt = pick("ai_operator_exempt", env.BRAINJAR_AI_OPERATOR_EXEMPT);
13419
+ const daily = amount(
13420
+ pick("ai_daily_budget_usd", env.BRAINJAR_AI_DAILY_BUDGET_USD)
13421
+ );
13422
+ return {
13423
+ contingent: contingent ? contingent === "on" : loadConfig().environment === "saas",
13424
+ freeUsd: amount(pick("ai_free_monthly_usd", env.BRAINJAR_FREE_MONTHLY_USD)) ?? DEFAULT_FREE_MONTHLY_USD,
13425
+ proUsd: amount(pick("ai_pro_monthly_usd", env.BRAINJAR_PRO_MONTHLY_USD)) ?? DEFAULT_PRO_MONTHLY_USD,
13426
+ teamSeatUsd: amount(
13427
+ pick("ai_team_seat_monthly_usd", env.BRAINJAR_TEAM_SEAT_MONTHLY_USD)
13428
+ ) ?? DEFAULT_TEAM_SEAT_MONTHLY_USD,
13429
+ creditsPerUsd: positive(pick("credits_per_usd", env.BRAINJAR_CREDITS_PER_USD)) ?? DEFAULT_CREDITS_PER_USD,
13430
+ operatorExempt: exempt ? exempt !== "false" : true,
13431
+ dailyBudgetUsd: daily && daily > 0 ? daily : null,
13432
+ releasedDay: pick("ai_budget_released_day", void 0) ?? null
13433
+ };
13434
+ }
13435
+ async function aiAllowance(brainId, now = /* @__PURE__ */ new Date()) {
13436
+ const cfg = await aiBudgetSettings();
13437
+ if (cfg.dailyBudgetUsd !== null && cfg.releasedDay !== day(now)) {
13438
+ const used2 = await spendUsd(dayKey(now));
13439
+ if (used2 >= cfg.dailyBudgetUsd) {
13440
+ if (brainId && await swapAppSetting("ai_budget_alerted_day", day(now)))
13441
+ await enqueueJob(
13442
+ brainId,
13443
+ "budget-alert",
13444
+ { usedUsd: used2, budgetUsd: cfg.dailyBudgetUsd, day: day(now) },
13445
+ { priority: JOB_PRIORITY.interactive, dedupeKey: day(now) }
13446
+ ).catch(() => {
13447
+ });
13448
+ return {
13449
+ reason: "daily_budget",
13450
+ usedUsd: used2,
13451
+ limitUsd: cfg.dailyBudgetUsd,
13452
+ renewsAt: dayRenewal(now)
13453
+ };
13454
+ }
13455
+ }
13456
+ if (!cfg.contingent || !brainId) return null;
13457
+ const ws = await brainWorkspace(brainId);
13458
+ if (!ws || ws.operator && cfg.operatorExempt) return null;
13459
+ const [limit, period] = await Promise.all([
13460
+ includedUsd(ws.ownerId, cfg),
13461
+ creditPeriod(ws.ownerId, now)
13462
+ ]);
13463
+ const used = await spendUsd(period.key);
13464
+ if (used < limit) {
13465
+ if (used >= limit * WARN_SHARE)
13466
+ await warnOwnerOnce(ws.ownerId, brainId, period, now).catch(() => {
13467
+ });
13468
+ return null;
13469
+ }
13470
+ if (await purchasedCredits(ws.ownerId) > 0) return null;
13471
+ await tellOwnerOnce(ws.ownerId, brainId, period, now).catch(() => {
13472
+ });
13473
+ return {
13474
+ reason: "contingent",
13475
+ usedUsd: used,
13476
+ limitUsd: limit,
13477
+ renewsAt: period.end
13478
+ };
13479
+ }
13480
+ async function includedUsd(ownerId, cfg) {
13481
+ const paid = await paidAccountPlan(ownerId).catch(() => null);
13482
+ if (!paid) return cfg.freeUsd;
13483
+ return paid.plan === "pro" ? cfg.proUsd : paid.seats * cfg.teamSeatUsd;
13484
+ }
13485
+ async function creditTerms(brainId) {
13486
+ const cfg = await aiBudgetSettings();
13487
+ if (!cfg.contingent || !brainId) return null;
13488
+ const ws = await brainWorkspace(brainId);
13489
+ if (!ws || ws.operator && cfg.operatorExempt) return null;
13490
+ return {
13491
+ includedUsd: await includedUsd(ws.ownerId, cfg),
13492
+ creditsPerUsd: cfg.creditsPerUsd
13493
+ };
13494
+ }
13495
+ async function tellOwnerOnce(ownerId, brainId, period, _now) {
13496
+ if (await hasUserNotificationSince(ownerId, "ai_contingent", period.start))
13497
+ return;
13498
+ await createNotification({
13499
+ kind: "ai_contingent",
13500
+ brainId,
13501
+ userId: ownerId,
13502
+ title: "This month's AI credits are used up",
13503
+ body: `BrainJar keeps working without AI until ${renewalDate(period.end)}: capture uses the basic extraction, docs are built without AI text, verification and written summaries pause. Pro and Company can buy more credits under Account, Plan.`,
13504
+ href: creditsHref(brainId)
13505
+ });
13506
+ }
13507
+ async function warnOwnerOnce(ownerId, brainId, period, _now) {
13508
+ if (await hasUserNotificationSince(ownerId, "ai_credits_low", period.start))
13509
+ return;
13510
+ await createNotification({
13511
+ kind: "ai_credits_low",
13512
+ brainId,
13513
+ userId: ownerId,
13514
+ title: "80 percent of this month's AI credits are used",
13515
+ body: `The rest lasts until ${renewalDate(period.end)}. When it is used up, BrainJar keeps working without AI until then; Pro and Company can buy more credits under Account, Plan.`,
13516
+ href: creditsHref(brainId)
13517
+ });
13518
+ }
13519
+ var DEFAULT_FREE_MONTHLY_USD, DEFAULT_PRO_MONTHLY_USD, DEFAULT_TEAM_SEAT_MONTHLY_USD, DEFAULT_CREDITS_PER_USD, amount, positive, day, WARN_SHARE, renewalDate, creditsHref;
13520
+ var init_allowance = __esm({
13521
+ "packages/ingest/src/allowance.ts"() {
13522
+ "use strict";
13523
+ init_src2();
13524
+ init_src();
13525
+ DEFAULT_FREE_MONTHLY_USD = 1;
13526
+ DEFAULT_PRO_MONTHLY_USD = 10;
13527
+ DEFAULT_TEAM_SEAT_MONTHLY_USD = 5;
13528
+ DEFAULT_CREDITS_PER_USD = 100;
13529
+ amount = (v) => {
13530
+ if (v === void 0 || v.trim() === "") return null;
13531
+ const n2 = Number(v);
13532
+ return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
13533
+ };
13534
+ positive = (v) => {
13535
+ const n2 = amount(v);
13536
+ return n2 !== null && n2 > 0 ? n2 : null;
13537
+ };
13538
+ day = (d) => d.toISOString().slice(0, 10);
13539
+ WARN_SHARE = 0.8;
13540
+ renewalDate = (d) => d.toLocaleDateString("en-GB", {
13541
+ day: "numeric",
13542
+ month: "long",
13543
+ timeZone: "UTC"
13544
+ });
13545
+ creditsHref = (brainId) => loadConfig().environment === "saas" ? "/account?tab=plan" : `/settings?brain=${encodeURIComponent(brainId)}&tab=usage`;
13546
+ }
13547
+ });
13548
+
13349
13549
  // packages/ingest/src/llm.ts
13350
13550
  function rationaleModelId(env = process.env) {
13351
13551
  return env.BRAINJAR_RATIONALE_MODEL?.trim() || "claude-opus-5";
@@ -13355,16 +13555,20 @@ function logUsage(meta, model, usage) {
13355
13555
  console.warn(
13356
13556
  `brainjar: AI call without a Brain (workload ${meta?.workload ?? "other"}); it counts only toward the daily budget`
13357
13557
  );
13358
- void recordLlmUsage({
13359
- brainId: meta?.brainId ?? null,
13360
- workload: meta?.workload ?? "other",
13361
- triggeredBy: meta?.actor ?? null,
13362
- model,
13363
- // Cache writes bill near the input rate; booked as input to keep the ledger simple.
13364
- inputTokens: (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0),
13365
- cachedInputTokens: usage.cache_read_input_tokens ?? 0,
13366
- outputTokens: usage.output_tokens ?? 0
13367
- }).catch(() => {
13558
+ void recordLlmUsage(
13559
+ {
13560
+ brainId: meta?.brainId ?? null,
13561
+ workload: meta?.workload ?? "other",
13562
+ triggeredBy: meta?.actor ?? null,
13563
+ model,
13564
+ // Cache writes bill near the input rate; booked as input to keep the ledger simple.
13565
+ inputTokens: (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0),
13566
+ cachedInputTokens: usage.cache_read_input_tokens ?? 0,
13567
+ outputTokens: usage.output_tokens ?? 0
13568
+ },
13569
+ // The workspace's credit terms decide what comes off purchased credits (BJ-132).
13570
+ () => creditTerms(meta?.brainId)
13571
+ ).catch(() => {
13368
13572
  });
13369
13573
  }
13370
13574
  function anthropicClient(apiKey, model, meta) {
@@ -13381,6 +13585,7 @@ var init_llm = __esm({
13381
13585
  init_sdk();
13382
13586
  init_src2();
13383
13587
  init_provider_rate2();
13588
+ init_allowance();
13384
13589
  cachedSystem = (system) => [
13385
13590
  {
13386
13591
  type: "text",
@@ -13619,98 +13824,6 @@ var init_openai = __esm({
13619
13824
  }
13620
13825
  });
13621
13826
 
13622
- // packages/ingest/src/allowance.ts
13623
- async function aiBudgetSettings() {
13624
- const env = loadEnv();
13625
- const stored = await getAppSettings().catch(
13626
- () => ({})
13627
- );
13628
- const pick = (key, envValue) => envValue?.trim() || stored[key];
13629
- const contingent = pick("ai_contingent", env.BRAINJAR_AI_CONTINGENT);
13630
- const exempt = pick("ai_operator_exempt", env.BRAINJAR_AI_OPERATOR_EXEMPT);
13631
- const daily = amount(
13632
- pick("ai_daily_budget_usd", env.BRAINJAR_AI_DAILY_BUDGET_USD)
13633
- );
13634
- return {
13635
- contingent: contingent ? contingent === "on" : loadConfig().environment === "saas",
13636
- freeUsd: amount(pick("ai_free_monthly_usd", env.BRAINJAR_FREE_MONTHLY_USD)) ?? DEFAULT_FREE_MONTHLY_USD,
13637
- teamSeatUsd: amount(
13638
- pick("ai_team_seat_monthly_usd", env.BRAINJAR_TEAM_SEAT_MONTHLY_USD)
13639
- ) ?? DEFAULT_TEAM_SEAT_MONTHLY_USD,
13640
- operatorExempt: exempt ? exempt !== "false" : true,
13641
- dailyBudgetUsd: daily && daily > 0 ? daily : null,
13642
- releasedDay: pick("ai_budget_released_day", void 0) ?? null
13643
- };
13644
- }
13645
- async function aiAllowance(brainId, now = /* @__PURE__ */ new Date()) {
13646
- const cfg = await aiBudgetSettings();
13647
- if (cfg.dailyBudgetUsd !== null && cfg.releasedDay !== day(now)) {
13648
- const used2 = await spendUsd(dayKey(now));
13649
- if (used2 >= cfg.dailyBudgetUsd) {
13650
- if (brainId && await swapAppSetting("ai_budget_alerted_day", day(now)))
13651
- await enqueueJob(
13652
- brainId,
13653
- "budget-alert",
13654
- { usedUsd: used2, budgetUsd: cfg.dailyBudgetUsd, day: day(now) },
13655
- { priority: JOB_PRIORITY.interactive, dedupeKey: day(now) }
13656
- ).catch(() => {
13657
- });
13658
- return {
13659
- reason: "daily_budget",
13660
- usedUsd: used2,
13661
- limitUsd: cfg.dailyBudgetUsd,
13662
- renewsAt: dayRenewal(now)
13663
- };
13664
- }
13665
- }
13666
- if (!cfg.contingent || !brainId) return null;
13667
- const ws = await brainWorkspace(brainId);
13668
- if (!ws || ws.operator && cfg.operatorExempt) return null;
13669
- const limit = cfg.freeUsd;
13670
- const used = await spendUsd(monthKey(ws.ownerId, now));
13671
- if (used < limit) return null;
13672
- const renewsAt = monthRenewal(now);
13673
- await tellOwnerOnce(ws.ownerId, brainId, renewsAt, now).catch(() => {
13674
- });
13675
- return { reason: "contingent", usedUsd: used, limitUsd: limit, renewsAt };
13676
- }
13677
- async function tellOwnerOnce(ownerId, brainId, renewsAt, now) {
13678
- const monthStart = new Date(
13679
- Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)
13680
- );
13681
- if (await hasUserNotificationSince(ownerId, "ai_contingent", monthStart))
13682
- return;
13683
- await createNotification({
13684
- kind: "ai_contingent",
13685
- brainId,
13686
- userId: ownerId,
13687
- title: "Monthly AI contingent used up",
13688
- body: `BrainJar keeps working without AI until ${renewalDate(renewsAt)}: capture uses the basic extraction, docs are built without AI text, verification and written summaries pause.`,
13689
- href: `/settings?brain=${encodeURIComponent(brainId)}&tab=usage`
13690
- });
13691
- }
13692
- var DEFAULT_FREE_MONTHLY_USD, DEFAULT_TEAM_SEAT_MONTHLY_USD, amount, day, renewalDate;
13693
- var init_allowance = __esm({
13694
- "packages/ingest/src/allowance.ts"() {
13695
- "use strict";
13696
- init_src2();
13697
- init_src();
13698
- DEFAULT_FREE_MONTHLY_USD = 1;
13699
- DEFAULT_TEAM_SEAT_MONTHLY_USD = 5;
13700
- amount = (v) => {
13701
- if (v === void 0 || v.trim() === "") return null;
13702
- const n2 = Number(v);
13703
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
13704
- };
13705
- day = (d) => d.toISOString().slice(0, 10);
13706
- renewalDate = (d) => d.toLocaleDateString("en-GB", {
13707
- day: "numeric",
13708
- month: "long",
13709
- timeZone: "UTC"
13710
- });
13711
- }
13712
- });
13713
-
13714
13827
  // packages/ingest/src/routing.ts
13715
13828
  async function tierClient(tier, meta, effort) {
13716
13829
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "brainjar",
3
- "version": "0.3.1228",
3
+ "version": "0.3.1230",
4
4
  "description": "BrainJar CLI: turn a repository into a live, self-documenting Brain.",
5
5
  "type": "module",
6
6
  "bin": {