@cerefox/memory 0.7.2 → 0.8.0

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 (47) hide show
  1. package/dist/bin/cerefox.js +1096 -344
  2. package/dist/frontend/assets/{index-BzAPcCXA.js → index-CAp2_lFX.js} +2 -2
  3. package/dist/frontend/assets/index-CAp2_lFX.js.map +1 -0
  4. package/dist/frontend/index.html +1 -1
  5. package/dist/server-assets/_shared/ef-meta/index.ts +97 -0
  6. package/dist/server-assets/_shared/embeddings/index.ts +175 -0
  7. package/dist/server-assets/_shared/mcp-tools/_chunker.ts +187 -0
  8. package/dist/server-assets/_shared/mcp-tools/_projects.ts +121 -0
  9. package/dist/server-assets/_shared/mcp-tools/_utils.ts +73 -0
  10. package/dist/server-assets/_shared/mcp-tools/audit-log.ts +95 -0
  11. package/dist/server-assets/_shared/mcp-tools/get-document.ts +73 -0
  12. package/dist/server-assets/_shared/mcp-tools/get-help-content.ts +26 -0
  13. package/dist/server-assets/_shared/mcp-tools/get-help.ts +90 -0
  14. package/dist/server-assets/_shared/mcp-tools/index.ts +67 -0
  15. package/dist/server-assets/_shared/mcp-tools/ingest.ts +315 -0
  16. package/dist/server-assets/_shared/mcp-tools/list-metadata-keys.ts +55 -0
  17. package/dist/server-assets/_shared/mcp-tools/list-projects.ts +59 -0
  18. package/dist/server-assets/_shared/mcp-tools/list-versions.ts +72 -0
  19. package/dist/server-assets/_shared/mcp-tools/metadata-search.ts +154 -0
  20. package/dist/server-assets/_shared/mcp-tools/search.ts +193 -0
  21. package/dist/server-assets/_shared/mcp-tools/set-document-projects.ts +163 -0
  22. package/dist/server-assets/_shared/mcp-tools/types.ts +92 -0
  23. package/dist/server-assets/db/migrations/0003_add_document_versions.sql +91 -0
  24. package/dist/server-assets/db/migrations/0004_add_audit_log_review_status_archived.sql +71 -0
  25. package/dist/server-assets/db/migrations/0005_metadata_search.sql +628 -0
  26. package/dist/server-assets/db/migrations/0006_usage_log.sql +255 -0
  27. package/dist/server-assets/db/migrations/0007_usage_log_requestor.sql +178 -0
  28. package/dist/server-assets/db/migrations/0008_soft_delete.sql +130 -0
  29. package/dist/server-assets/db/migrations/0009_audit_log_restore_operation.sql +20 -0
  30. package/dist/server-assets/db/migrations/0010_requestor_enforcement_config.sql +12 -0
  31. package/dist/server-assets/db/migrations/0011_title_boosting.sql +48 -0
  32. package/dist/server-assets/db/rpcs.sql +1723 -0
  33. package/dist/server-assets/db/schema.sql +380 -0
  34. package/dist/server-assets/supabase/functions/cerefox-get-audit-log/index.ts +117 -0
  35. package/dist/server-assets/supabase/functions/cerefox-get-document/index.ts +138 -0
  36. package/dist/server-assets/supabase/functions/cerefox-ingest/index.ts +819 -0
  37. package/dist/server-assets/supabase/functions/cerefox-list-projects/index.ts +96 -0
  38. package/dist/server-assets/supabase/functions/cerefox-list-versions/index.ts +113 -0
  39. package/dist/server-assets/supabase/functions/cerefox-mcp/index.ts +294 -0
  40. package/dist/server-assets/supabase/functions/cerefox-mcp/shared.ts +42 -0
  41. package/dist/server-assets/supabase/functions/cerefox-metadata/index.ts +99 -0
  42. package/dist/server-assets/supabase/functions/cerefox-metadata-search/index.ts +146 -0
  43. package/dist/server-assets/supabase/functions/cerefox-search/index.ts +382 -0
  44. package/docs/guides/connect-agents.md +58 -3
  45. package/docs/guides/migration-v0.5.md +50 -0
  46. package/package.json +3 -2
  47. package/dist/frontend/assets/index-BzAPcCXA.js.map +0 -1
@@ -7179,7 +7179,7 @@ var exports_meta = {};
7179
7179
  __export(exports_meta, {
7180
7180
  PKG_VERSION: () => PKG_VERSION
7181
7181
  });
7182
- var PKG_VERSION = "0.7.2";
7182
+ var PKG_VERSION = "0.8.0";
7183
7183
  var init_meta = () => {};
7184
7184
 
7185
7185
  // ../../node_modules/.bun/tslib@2.8.1/node_modules/tslib/tslib.js
@@ -22585,6 +22585,7 @@ function loadSettings(opts = {}) {
22585
22585
  return {
22586
22586
  supabaseUrl: env2.CEREFOX_SUPABASE_URL ?? "",
22587
22587
  supabaseKey: env2.CEREFOX_SUPABASE_KEY ?? "",
22588
+ supabaseAnonKey: env2.CEREFOX_SUPABASE_ANON_KEY ?? "",
22588
22589
  databaseUrl: env2.CEREFOX_DATABASE_URL ?? "",
22589
22590
  openaiApiKey: env2.CEREFOX_OPENAI_API_KEY ?? env2.OPENAI_API_KEY ?? "",
22590
22591
  fireworksApiKey: env2.CEREFOX_FIREWORKS_API_KEY ?? ""
@@ -22631,99 +22632,6 @@ var init_client = __esm(() => {
22631
22632
  init_cli_core();
22632
22633
  });
22633
22634
 
22634
- // src/cli/util/bundled-docs.ts
22635
- import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
22636
- import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
22637
- import { fileURLToPath } from "node:url";
22638
- function findPackageRoot() {
22639
- let dir = dirname2(fileURLToPath(import.meta.url));
22640
- for (let i = 0;i < 10; i++) {
22641
- const pkgJson = join4(dir, "package.json");
22642
- if (existsSync4(pkgJson)) {
22643
- try {
22644
- const parsed = JSON.parse(readFileSync4(pkgJson, "utf8"));
22645
- if (parsed.name === "@cerefox/memory")
22646
- return dir;
22647
- } catch {}
22648
- }
22649
- const parent = dirname2(dir);
22650
- if (parent === dir)
22651
- break;
22652
- dir = parent;
22653
- }
22654
- return resolve2(dirname2(fileURLToPath(import.meta.url)), "..", "..", "..");
22655
- }
22656
- function bundledDocsDir() {
22657
- const inPackage = join4(PACKAGE_ROOT, "docs");
22658
- if (existsSync4(inPackage))
22659
- return inPackage;
22660
- return resolve2(PACKAGE_ROOT, "..", "..", "docs");
22661
- }
22662
- function agentGuidePath() {
22663
- const candidates = [
22664
- join4(PACKAGE_ROOT, "AGENT_GUIDE.md"),
22665
- resolve2(PACKAGE_ROOT, "..", "..", "AGENT_GUIDE.md")
22666
- ];
22667
- for (const c2 of candidates)
22668
- if (existsSync4(c2))
22669
- return c2;
22670
- return null;
22671
- }
22672
- function agentQuickReferencePath() {
22673
- const candidates = [
22674
- join4(PACKAGE_ROOT, "AGENT_QUICK_REFERENCE.md"),
22675
- resolve2(PACKAGE_ROOT, "..", "..", "AGENT_QUICK_REFERENCE.md")
22676
- ];
22677
- for (const c2 of candidates)
22678
- if (existsSync4(c2))
22679
- return c2;
22680
- return null;
22681
- }
22682
- function listBundledDocs() {
22683
- const entries = [];
22684
- const docsDir = bundledDocsDir();
22685
- const guidesDir = join4(docsDir, "guides");
22686
- if (existsSync4(guidesDir) && statSync(guidesDir).isDirectory()) {
22687
- for (const name of readdirSync(guidesDir)) {
22688
- if (!name.endsWith(".md"))
22689
- continue;
22690
- const full = join4(guidesDir, name);
22691
- entries.push({
22692
- topic: name.replace(/\.md$/, ""),
22693
- path: full,
22694
- size: statSync(full).size
22695
- });
22696
- }
22697
- }
22698
- const ag = agentGuidePath();
22699
- if (ag)
22700
- entries.push({ topic: "agent-guide", path: ag, size: statSync(ag).size });
22701
- const aqr = agentQuickReferencePath();
22702
- if (aqr) {
22703
- entries.push({
22704
- topic: "agent-quick-reference",
22705
- path: aqr,
22706
- size: statSync(aqr).size
22707
- });
22708
- }
22709
- entries.sort((a, b) => a.topic.localeCompare(b.topic));
22710
- return entries;
22711
- }
22712
- function readBundledDoc(topic) {
22713
- const entry = listBundledDocs().find((d) => d.topic === topic);
22714
- if (!entry)
22715
- return null;
22716
- return {
22717
- topic: entry.topic,
22718
- path: entry.path,
22719
- content: readFileSync4(entry.path, "utf8")
22720
- };
22721
- }
22722
- var PACKAGE_ROOT;
22723
- var init_bundled_docs = __esm(() => {
22724
- PACKAGE_ROOT = findPackageRoot();
22725
- });
22726
-
22727
22635
  // ../../node_modules/.bun/postgres@3.4.9/node_modules/postgres/src/query.js
22728
22636
  function cachedError(xs) {
22729
22637
  if (originCache.has(xs))
@@ -22742,9 +22650,9 @@ var init_query = __esm(() => {
22742
22650
  CLOSE = {};
22743
22651
  Query = class Query extends Promise {
22744
22652
  constructor(strings, args, handler, canceller, options = {}) {
22745
- let resolve3, reject;
22653
+ let resolve2, reject;
22746
22654
  super((a, b) => {
22747
- resolve3 = a;
22655
+ resolve2 = a;
22748
22656
  reject = b;
22749
22657
  });
22750
22658
  this.tagged = Array.isArray(strings.raw);
@@ -22755,7 +22663,7 @@ var init_query = __esm(() => {
22755
22663
  this.options = options;
22756
22664
  this.state = null;
22757
22665
  this.statement = null;
22758
- this.resolve = (x) => (this.active = false, resolve3(x));
22666
+ this.resolve = (x) => (this.active = false, resolve2(x));
22759
22667
  this.reject = (x) => (this.active = false, reject(x));
22760
22668
  this.active = false;
22761
22669
  this.cancelled = null;
@@ -22803,12 +22711,12 @@ var init_query = __esm(() => {
22803
22711
  if (this.executed && !this.active)
22804
22712
  return { done: true };
22805
22713
  prev && prev();
22806
- const promise = new Promise((resolve3, reject) => {
22714
+ const promise = new Promise((resolve2, reject) => {
22807
22715
  this.cursorFn = (value) => {
22808
- resolve3({ value, done: false });
22716
+ resolve2({ value, done: false });
22809
22717
  return new Promise((r) => prev = r);
22810
22718
  };
22811
- this.resolve = () => (this.active = false, resolve3({ done: true }));
22719
+ this.resolve = () => (this.active = false, resolve2({ done: true }));
22812
22720
  this.reject = (x) => (this.active = false, reject(x));
22813
22721
  });
22814
22722
  this.execute();
@@ -22867,31 +22775,31 @@ var init_query = __esm(() => {
22867
22775
  // ../../node_modules/.bun/postgres@3.4.9/node_modules/postgres/src/errors.js
22868
22776
  function connection(x, options, socket) {
22869
22777
  const { host, port } = socket || options;
22870
- const error2 = Object.assign(new Error("write " + x + " " + (options.path || host + ":" + port)), {
22778
+ const error = Object.assign(new Error("write " + x + " " + (options.path || host + ":" + port)), {
22871
22779
  code: x,
22872
22780
  errno: x,
22873
22781
  address: options.path || host
22874
22782
  }, options.path ? {} : { port });
22875
- Error.captureStackTrace(error2, connection);
22876
- return error2;
22783
+ Error.captureStackTrace(error, connection);
22784
+ return error;
22877
22785
  }
22878
22786
  function postgres(x) {
22879
- const error2 = new PostgresError(x);
22880
- Error.captureStackTrace(error2, postgres);
22881
- return error2;
22787
+ const error = new PostgresError(x);
22788
+ Error.captureStackTrace(error, postgres);
22789
+ return error;
22882
22790
  }
22883
22791
  function generic(code, message) {
22884
- const error2 = Object.assign(new Error(code + ": " + message), { code });
22885
- Error.captureStackTrace(error2, generic);
22886
- return error2;
22792
+ const error = Object.assign(new Error(code + ": " + message), { code });
22793
+ Error.captureStackTrace(error, generic);
22794
+ return error;
22887
22795
  }
22888
22796
  function notSupported(x) {
22889
- const error2 = Object.assign(new Error(x + " (B) is not supported"), {
22797
+ const error = Object.assign(new Error(x + " (B) is not supported"), {
22890
22798
  code: "MESSAGE_NOT_SUPPORTED",
22891
22799
  name: x
22892
22800
  });
22893
- Error.captureStackTrace(error2, notSupported);
22894
- return error2;
22801
+ Error.captureStackTrace(error, notSupported);
22802
+ return error;
22895
22803
  }
22896
22804
  var PostgresError, Errors;
22897
22805
  var init_errors2 = __esm(() => {
@@ -23239,7 +23147,7 @@ function fit(x) {
23239
23147
  prev.copy(buffer);
23240
23148
  }
23241
23149
  }
23242
- function reset2() {
23150
+ function reset() {
23243
23151
  b.i = 0;
23244
23152
  return b;
23245
23153
  }
@@ -23255,7 +23163,7 @@ var init_bytes = __esm(() => {
23255
23163
  };
23256
23164
  return acc;
23257
23165
  }, {});
23258
- b = Object.assign(reset2, messages, {
23166
+ b = Object.assign(reset, messages, {
23259
23167
  N: String.fromCharCode(0),
23260
23168
  i: 0,
23261
23169
  inc(x) {
@@ -23354,22 +23262,22 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23354
23262
  try {
23355
23263
  x = options.socket ? await Promise.resolve(options.socket(options)) : new net.Socket;
23356
23264
  } catch (e) {
23357
- error2(e);
23265
+ error(e);
23358
23266
  return;
23359
23267
  }
23360
- x.on("error", error2);
23268
+ x.on("error", error);
23361
23269
  x.on("close", closed);
23362
23270
  x.on("drain", drain);
23363
23271
  return x;
23364
23272
  }
23365
- async function cancel({ pid, secret }, resolve3, reject) {
23273
+ async function cancel({ pid, secret }, resolve2, reject) {
23366
23274
  try {
23367
23275
  cancelMessage = bytes_default().i32(16).i32(80877102).i32(pid).i32(secret).end(16);
23368
23276
  await connect();
23369
23277
  socket.once("error", reject);
23370
- socket.once("close", resolve3);
23371
- } catch (error3) {
23372
- reject(error3);
23278
+ socket.once("close", resolve2);
23279
+ } catch (error2) {
23280
+ reject(error2);
23373
23281
  }
23374
23282
  }
23375
23283
  function execute(q) {
@@ -23384,9 +23292,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23384
23292
  query ? sent.push(q) : (query = q, query.active = true);
23385
23293
  build(q);
23386
23294
  return write(toBuffer(q)) && !q.describeFirst && !q.cursorFn && sent.length < max_pipeline && (!q.options.onexecute || q.options.onexecute(connection2));
23387
- } catch (error3) {
23295
+ } catch (error2) {
23388
23296
  sent.length === 0 && write(Sync);
23389
- errored(error3);
23297
+ errored(error2);
23390
23298
  return true;
23391
23299
  }
23392
23300
  }
@@ -23465,7 +23373,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23465
23373
  socket.removeAllListeners();
23466
23374
  socket = tls.connect(options2);
23467
23375
  socket.on("secureConnect", connected);
23468
- socket.on("error", error2);
23376
+ socket.on("error", error);
23469
23377
  socket.on("close", closed);
23470
23378
  socket.on("drain", drain);
23471
23379
  }
@@ -23531,10 +23439,10 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23531
23439
  const s = StartupMessage();
23532
23440
  write(s);
23533
23441
  } catch (err) {
23534
- error2(err);
23442
+ error(err);
23535
23443
  }
23536
23444
  }
23537
- function error2(err) {
23445
+ function error(err) {
23538
23446
  if (connection2.queue === queues.connecting && options.host[retries + 1])
23539
23447
  return;
23540
23448
  errored(err);
@@ -23567,7 +23475,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23567
23475
  function terminate() {
23568
23476
  terminated = true;
23569
23477
  if (stream || query || initial || sent.length)
23570
- error2(Errors.connection("CONNECTION_DESTROYED", options));
23478
+ error(Errors.connection("CONNECTION_DESTROYED", options));
23571
23479
  clearImmediate(nextWriteTimer);
23572
23480
  if (socket) {
23573
23481
  socket.removeListener("data", data);
@@ -23590,7 +23498,7 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23590
23498
  socket = null;
23591
23499
  if (initial)
23592
23500
  return reconnect();
23593
- !hadError && (query || sent.length) && error2(Errors.connection("CONNECTION_CLOSED", options, socket));
23501
+ !hadError && (query || sent.length) && error(Errors.connection("CONNECTION_CLOSED", options, socket));
23594
23502
  closedTime = performance.now();
23595
23503
  hadError && options.shared.retries++;
23596
23504
  delay = (typeof backoff === "function" ? backoff(options.shared.retries) : backoff) * 1000;
@@ -23814,9 +23722,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23814
23722
  errored(Errors.postgres(parseError(x)));
23815
23723
  }
23816
23724
  }
23817
- function retry(q, error3) {
23725
+ function retry(q, error2) {
23818
23726
  delete statements[q.signature];
23819
- q.retried = error3;
23727
+ q.retried = error2;
23820
23728
  execute(q);
23821
23729
  }
23822
23730
  function NotificationResponse(x) {
@@ -23847,9 +23755,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23847
23755
  write(chunk2, encoding, callback) {
23848
23756
  socket.write(bytes_default().d().raw(chunk2).end(), callback);
23849
23757
  },
23850
- destroy(error3, callback) {
23851
- callback(error3);
23852
- socket.write(bytes_default().f().str(error3 + bytes_default.N).end());
23758
+ destroy(error2, callback) {
23759
+ callback(error2);
23760
+ socket.write(bytes_default().f().str(error2 + bytes_default.N).end());
23853
23761
  stream = null;
23854
23762
  },
23855
23763
  final(callback) {
@@ -23877,9 +23785,9 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23877
23785
  write(chunk2, encoding, callback) {
23878
23786
  socket.write(bytes_default().d().raw(chunk2).end(), callback);
23879
23787
  },
23880
- destroy(error3, callback) {
23881
- callback(error3);
23882
- socket.write(bytes_default().f().str(error3 + bytes_default.N).end());
23788
+ destroy(error2, callback) {
23789
+ callback(error2);
23790
+ socket.write(bytes_default().f().str(error2 + bytes_default.N).end());
23883
23791
  stream = null;
23884
23792
  },
23885
23793
  final(callback) {
@@ -23955,15 +23863,15 @@ function Connection(options, queues = {}, { onopen = noop, onend = noop, onclose
23955
23863
  }
23956
23864
  }
23957
23865
  function parseError(x) {
23958
- const error2 = {};
23866
+ const error = {};
23959
23867
  let start = 5;
23960
23868
  for (let i = 5;i < x.length - 1; i++) {
23961
23869
  if (x[i] === 0) {
23962
- error2[errorFields[x[start]]] = x.toString("utf8", start + 1, i);
23870
+ error[errorFields[x[start]]] = x.toString("utf8", start + 1, i);
23963
23871
  start = i + 1;
23964
23872
  }
23965
23873
  }
23966
- return error2;
23874
+ return error;
23967
23875
  }
23968
23876
  function md5(x) {
23969
23877
  return crypto2.createHash("md5").update(x).digest("hex");
@@ -24109,10 +24017,10 @@ function Subscribe(postgres2, options) {
24109
24017
  lsn: Buffer.concat(x.consistent_point.split("/").map((x2) => Buffer.from(("00000000" + x2).slice(-8), "hex")))
24110
24018
  };
24111
24019
  stream2.on("data", data);
24112
- stream2.on("error", error2);
24020
+ stream2.on("error", error);
24113
24021
  stream2.on("close", sql2.close);
24114
24022
  return { stream: stream2, state: xs.state };
24115
- function error2(e) {
24023
+ function error(e) {
24116
24024
  console.error("Unexpected error during logical streaming - reconnecting", e);
24117
24025
  }
24118
24026
  function data(x2) {
@@ -24240,7 +24148,7 @@ var noop2 = () => {};
24240
24148
  // ../../node_modules/.bun/postgres@3.4.9/node_modules/postgres/src/large.js
24241
24149
  import Stream2 from "stream";
24242
24150
  function largeObject(sql, oid, mode = 131072 | 262144) {
24243
- return new Promise(async (resolve3, reject) => {
24151
+ return new Promise(async (resolve2, reject) => {
24244
24152
  await sql.begin(async (sql2) => {
24245
24153
  let finish;
24246
24154
  !oid && ([{ oid }] = await sql2`select lo_creat(-1) as oid`);
@@ -24266,7 +24174,7 @@ function largeObject(sql, oid, mode = 131072 | 262144) {
24266
24174
  ) seek
24267
24175
  `
24268
24176
  };
24269
- resolve3(lo);
24177
+ resolve2(lo);
24270
24178
  return new Promise(async (r) => finish = r);
24271
24179
  async function readable({
24272
24180
  highWaterMark = 2048 * 8,
@@ -24309,7 +24217,7 @@ var exports_src = {};
24309
24217
  __export(exports_src, {
24310
24218
  default: () => src_default
24311
24219
  });
24312
- import os2 from "os";
24220
+ import os from "os";
24313
24221
  import fs from "fs";
24314
24222
  function Postgres(a, b2) {
24315
24223
  const options = parseOptions(a, b2), subscribe = options.no_subscribe || Subscribe(Postgres, { ...options });
@@ -24426,8 +24334,8 @@ function Postgres(a, b2) {
24426
24334
  }
24427
24335
  async function reserve() {
24428
24336
  const queue = queue_default();
24429
- const c2 = open.length ? open.shift() : await new Promise((resolve3, reject) => {
24430
- const query = { reserve: resolve3, reject };
24337
+ const c2 = open.length ? open.shift() : await new Promise((resolve2, reject) => {
24338
+ const query = { reserve: resolve2, reject };
24431
24339
  queries.push(query);
24432
24340
  closed.length && connect(closed.shift(), query);
24433
24341
  });
@@ -24454,8 +24362,8 @@ function Postgres(a, b2) {
24454
24362
  scope(connection2, fn),
24455
24363
  new Promise((_, reject) => connection2.onclose = reject)
24456
24364
  ]);
24457
- } catch (error2) {
24458
- throw error2;
24365
+ } catch (error) {
24366
+ throw error;
24459
24367
  }
24460
24368
  async function scope(c2, fn2, name) {
24461
24369
  const sql2 = Sql(handler2);
@@ -24464,9 +24372,9 @@ function Postgres(a, b2) {
24464
24372
  let uncaughtError, result;
24465
24373
  name && await sql2`savepoint ${sql2(name)}`;
24466
24374
  try {
24467
- result = await new Promise((resolve3, reject) => {
24375
+ result = await new Promise((resolve2, reject) => {
24468
24376
  const x = fn2(sql2);
24469
- Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve3, reject);
24377
+ Promise.resolve(Array.isArray(x) ? Promise.all(x) : x).then(resolve2, reject);
24470
24378
  });
24471
24379
  if (uncaughtError)
24472
24380
  throw uncaughtError;
@@ -24523,8 +24431,8 @@ function Postgres(a, b2) {
24523
24431
  return c2.execute(query) ? move(c2, busy) : move(c2, full);
24524
24432
  }
24525
24433
  function cancel(query) {
24526
- return new Promise((resolve3, reject) => {
24527
- query.state ? query.active ? connection_default(options).cancel(query.state, resolve3, reject) : query.cancelled = { resolve: resolve3, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve3());
24434
+ return new Promise((resolve2, reject) => {
24435
+ query.state ? query.active ? connection_default(options).cancel(query.state, resolve2, reject) : query.cancelled = { resolve: resolve2, reject } : (queries.remove(query), query.cancelled = true, query.reject(Errors.generic("57014", "canceling statement due to user request")), resolve2());
24528
24436
  });
24529
24437
  }
24530
24438
  async function end({ timeout = null } = {}) {
@@ -24540,11 +24448,11 @@ function Postgres(a, b2) {
24540
24448
  async function close() {
24541
24449
  await Promise.all(connections.map((c2) => c2.end()));
24542
24450
  }
24543
- async function destroy(resolve3) {
24451
+ async function destroy(resolve2) {
24544
24452
  await Promise.all(connections.map((c2) => c2.terminate()));
24545
24453
  while (queries.length)
24546
24454
  queries.shift().reject(Errors.connection("CONNECTION_DESTROYED", options));
24547
- resolve3();
24455
+ resolve2();
24548
24456
  }
24549
24457
  function connect(c2, query) {
24550
24458
  move(c2, connecting);
@@ -24577,7 +24485,7 @@ function Postgres(a, b2) {
24577
24485
  function parseOptions(a, b2) {
24578
24486
  if (a && a.shared)
24579
24487
  return a;
24580
- const env4 = process.env, o = (!a || typeof a === "string" ? b2 : a) || {}, { url, multihost } = parseUrl(a), query = [...url.searchParams].reduce((a2, [b3, c2]) => (a2[b3] = c2, a2), {}), host = o.hostname || o.host || multihost || url.hostname || env4.PGHOST || "localhost", port = o.port || url.port || env4.PGPORT || 5432, user = o.user || o.username || url.username || env4.PGUSERNAME || env4.PGUSER || osUsername();
24488
+ const env3 = process.env, o = (!a || typeof a === "string" ? b2 : a) || {}, { url, multihost } = parseUrl(a), query = [...url.searchParams].reduce((a2, [b3, c2]) => (a2[b3] = c2, a2), {}), host = o.hostname || o.host || multihost || url.hostname || env3.PGHOST || "localhost", port = o.port || url.port || env3.PGPORT || 5432, user = o.user || o.username || url.username || env3.PGUSERNAME || env3.PGUSER || osUsername();
24581
24489
  o.no_prepare && (o.prepare = false);
24582
24490
  query.sslmode && (query.ssl = query.sslmode, delete query.sslmode);
24583
24491
  "timeout" in o && (console.log("The timeout option is deprecated, use idle_timeout instead"), o.idle_timeout = o.timeout);
@@ -24603,21 +24511,21 @@ function parseOptions(a, b2) {
24603
24511
  host: Array.isArray(host) ? host : host.split(",").map((x) => x.split(":")[0]),
24604
24512
  port: Array.isArray(port) ? port : host.split(",").map((x) => parseInt(x.split(":")[1] || port)),
24605
24513
  path: o.path || host.indexOf("/") > -1 && host + "/.s.PGSQL." + port,
24606
- database: o.database || o.db || (url.pathname || "").slice(1) || env4.PGDATABASE || user,
24514
+ database: o.database || o.db || (url.pathname || "").slice(1) || env3.PGDATABASE || user,
24607
24515
  user,
24608
- pass: o.pass || o.password || url.password || env4.PGPASSWORD || "",
24516
+ pass: o.pass || o.password || url.password || env3.PGPASSWORD || "",
24609
24517
  ...Object.entries(defaults).reduce((acc, [k, d]) => {
24610
- const value = k in o ? o[k] : (k in query) ? query[k] === "disable" || query[k] === "false" ? false : query[k] : env4["PG" + k.toUpperCase()] || d;
24518
+ const value = k in o ? o[k] : (k in query) ? query[k] === "disable" || query[k] === "false" ? false : query[k] : env3["PG" + k.toUpperCase()] || d;
24611
24519
  acc[k] = typeof value === "string" && ints.includes(k) ? +value : value;
24612
24520
  return acc;
24613
24521
  }, {}),
24614
24522
  connection: {
24615
- application_name: env4.PGAPPNAME || "postgres.js",
24523
+ application_name: env3.PGAPPNAME || "postgres.js",
24616
24524
  ...o.connection,
24617
24525
  ...Object.entries(query).reduce((acc, [k, v]) => ((k in defaults) || (acc[k] = v), acc), {})
24618
24526
  },
24619
24527
  types: o.types || {},
24620
- target_session_attrs: tsa(o, url, env4),
24528
+ target_session_attrs: tsa(o, url, env3),
24621
24529
  onnotice: o.onnotice,
24622
24530
  onnotify: o.onnotify,
24623
24531
  onclose: o.onclose,
@@ -24629,8 +24537,8 @@ function parseOptions(a, b2) {
24629
24537
  ...mergeUserTypes(o.types)
24630
24538
  };
24631
24539
  }
24632
- function tsa(o, url, env4) {
24633
- const x = o.target_session_attrs || url.searchParams.get("target_session_attrs") || env4.PGTARGETSESSIONATTRS;
24540
+ function tsa(o, url, env3) {
24541
+ const x = o.target_session_attrs || url.searchParams.get("target_session_attrs") || env3.PGTARGETSESSIONATTRS;
24634
24542
  if (!x || ["read-write", "read-only", "primary", "standby", "prefer-standby"].includes(x))
24635
24543
  return x;
24636
24544
  throw new Error("target_session_attrs " + x + " is not supported");
@@ -24680,7 +24588,7 @@ function parseUrl(url) {
24680
24588
  }
24681
24589
  function osUsername() {
24682
24590
  try {
24683
- return os2.userInfo().username;
24591
+ return os.userInfo().username;
24684
24592
  } catch (_) {
24685
24593
  return process.env.USERNAME || process.env.USER || process.env.LOGNAME;
24686
24594
  }
@@ -24714,6 +24622,99 @@ var init_src = __esm(() => {
24714
24622
  src_default = Postgres;
24715
24623
  });
24716
24624
 
24625
+ // src/cli/util/bundled-docs.ts
24626
+ import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync } from "node:fs";
24627
+ import { dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
24628
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
24629
+ function findPackageRoot() {
24630
+ let dir = dirname3(fileURLToPath2(import.meta.url));
24631
+ for (let i = 0;i < 10; i++) {
24632
+ const pkgJson = join5(dir, "package.json");
24633
+ if (existsSync7(pkgJson)) {
24634
+ try {
24635
+ const parsed = JSON.parse(readFileSync5(pkgJson, "utf8"));
24636
+ if (parsed.name === "@cerefox/memory")
24637
+ return dir;
24638
+ } catch {}
24639
+ }
24640
+ const parent = dirname3(dir);
24641
+ if (parent === dir)
24642
+ break;
24643
+ dir = parent;
24644
+ }
24645
+ return resolve2(dirname3(fileURLToPath2(import.meta.url)), "..", "..", "..");
24646
+ }
24647
+ function bundledDocsDir() {
24648
+ const inPackage = join5(PACKAGE_ROOT, "docs");
24649
+ if (existsSync7(inPackage))
24650
+ return inPackage;
24651
+ return resolve2(PACKAGE_ROOT, "..", "..", "docs");
24652
+ }
24653
+ function agentGuidePath() {
24654
+ const candidates = [
24655
+ join5(PACKAGE_ROOT, "AGENT_GUIDE.md"),
24656
+ resolve2(PACKAGE_ROOT, "..", "..", "AGENT_GUIDE.md")
24657
+ ];
24658
+ for (const c2 of candidates)
24659
+ if (existsSync7(c2))
24660
+ return c2;
24661
+ return null;
24662
+ }
24663
+ function agentQuickReferencePath() {
24664
+ const candidates = [
24665
+ join5(PACKAGE_ROOT, "AGENT_QUICK_REFERENCE.md"),
24666
+ resolve2(PACKAGE_ROOT, "..", "..", "AGENT_QUICK_REFERENCE.md")
24667
+ ];
24668
+ for (const c2 of candidates)
24669
+ if (existsSync7(c2))
24670
+ return c2;
24671
+ return null;
24672
+ }
24673
+ function listBundledDocs() {
24674
+ const entries = [];
24675
+ const docsDir = bundledDocsDir();
24676
+ const guidesDir = join5(docsDir, "guides");
24677
+ if (existsSync7(guidesDir) && statSync(guidesDir).isDirectory()) {
24678
+ for (const name of readdirSync3(guidesDir)) {
24679
+ if (!name.endsWith(".md"))
24680
+ continue;
24681
+ const full = join5(guidesDir, name);
24682
+ entries.push({
24683
+ topic: name.replace(/\.md$/, ""),
24684
+ path: full,
24685
+ size: statSync(full).size
24686
+ });
24687
+ }
24688
+ }
24689
+ const ag = agentGuidePath();
24690
+ if (ag)
24691
+ entries.push({ topic: "agent-guide", path: ag, size: statSync(ag).size });
24692
+ const aqr = agentQuickReferencePath();
24693
+ if (aqr) {
24694
+ entries.push({
24695
+ topic: "agent-quick-reference",
24696
+ path: aqr,
24697
+ size: statSync(aqr).size
24698
+ });
24699
+ }
24700
+ entries.sort((a, b2) => a.topic.localeCompare(b2.topic));
24701
+ return entries;
24702
+ }
24703
+ function readBundledDoc(topic) {
24704
+ const entry = listBundledDocs().find((d) => d.topic === topic);
24705
+ if (!entry)
24706
+ return null;
24707
+ return {
24708
+ topic: entry.topic,
24709
+ path: entry.path,
24710
+ content: readFileSync5(entry.path, "utf8")
24711
+ };
24712
+ }
24713
+ var PACKAGE_ROOT;
24714
+ var init_bundled_docs = __esm(() => {
24715
+ PACKAGE_ROOT = findPackageRoot();
24716
+ });
24717
+
24717
24718
  // ../../_shared/embeddings/index.ts
24718
24719
  async function getEmbedding(text, apiKey) {
24719
24720
  let lastError = null;
@@ -26752,7 +26753,7 @@ __export(exports_sync_self_docs, {
26752
26753
  runSyncSelfDocs: () => runSyncSelfDocs,
26753
26754
  registerSyncSelfDocs: () => registerSyncSelfDocs
26754
26755
  });
26755
- import { readFileSync as readFileSync8 } from "node:fs";
26756
+ import { readFileSync as readFileSync9 } from "node:fs";
26756
26757
  import { basename as basename4, extname as extname4 } from "node:path";
26757
26758
  async function runSyncSelfDocs(options = {}) {
26758
26759
  const project = options.project ?? "_cerefox-self-docs";
@@ -26779,7 +26780,7 @@ async function runSyncSelfDocs(options = {}) {
26779
26780
  const authorType = resolveAuthorType("agent");
26780
26781
  const outcomes = [];
26781
26782
  for (const doc of docs) {
26782
- const content = readFileSync8(doc.path, "utf8");
26783
+ const content = readFileSync9(doc.path, "utf8");
26783
26784
  const m = content.match(/^#\s+(.+)$/m);
26784
26785
  const title = m ? m[1].trim() : basename4(doc.path, extname4(doc.path));
26785
26786
  try {
@@ -26813,11 +26814,11 @@ async function runSyncSelfDocs(options = {}) {
26813
26814
  printTable(outcomes.filter((o) => o.status === "error").map((o) => ({ topic: o.topic, error: o.detail.slice(0, 100) })));
26814
26815
  }
26815
26816
  }
26816
- async function action13(options) {
26817
+ async function action14(options) {
26817
26818
  await runSyncSelfDocs(options);
26818
26819
  }
26819
26820
  function registerSyncSelfDocs(program2) {
26820
- program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action13);
26821
+ program2.command("sync-self-docs").description("Ingest bundled Cerefox docs under the _cerefox-self-docs project.").option("--dry-run", "List what would be ingested without writing.").option("--project <name>", "Override the target project name.", "_cerefox-self-docs").action(action14);
26821
26822
  }
26822
26823
  var init_sync_self_docs = __esm(() => {
26823
26824
  init_cli_core();
@@ -41527,18 +41528,287 @@ function registerDeleteDoc(program2) {
41527
41528
  program2.command("delete-doc").description("Soft-delete a document (recoverable via the web UI trash).").argument("<document-id>", "UUID of the document to delete.").option("--reason <text>", "Optional reason recorded in the audit log.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("--yes", "Skip the confirmation prompt.").action(action6);
41528
41529
  }
41529
41530
 
41531
+ // src/cli/commands/deploy-server.ts
41532
+ init_cli_core();
41533
+ init_config();
41534
+ import { spawnSync as spawnSync2 } from "node:child_process";
41535
+ import { existsSync as existsSync6 } from "node:fs";
41536
+ import { readdirSync as readdirSync2 } from "node:fs";
41537
+
41538
+ // ../../_shared/server-assets/index.ts
41539
+ import { existsSync as existsSync4 } from "node:fs";
41540
+ import { dirname as dirname2, join as join4 } from "node:path";
41541
+ import { fileURLToPath } from "node:url";
41542
+ import { cwd as processCwd2 } from "node:process";
41543
+ function moduleDir() {
41544
+ return dirname2(fileURLToPath(import.meta.url));
41545
+ }
41546
+ function bundledServerAssets(serverAssetsRoot) {
41547
+ return {
41548
+ schemaFile: join4(serverAssetsRoot, "db", "schema.sql"),
41549
+ rpcsFile: join4(serverAssetsRoot, "db", "rpcs.sql"),
41550
+ migrationsDir: join4(serverAssetsRoot, "db", "migrations"),
41551
+ functionsDir: join4(serverAssetsRoot, "supabase", "functions"),
41552
+ layout: "bundled"
41553
+ };
41554
+ }
41555
+ function sourceServerAssets(repoRoot) {
41556
+ const dbDir = join4(repoRoot, "src", "cerefox", "db");
41557
+ return {
41558
+ schemaFile: join4(dbDir, "schema.sql"),
41559
+ rpcsFile: join4(dbDir, "rpcs.sql"),
41560
+ migrationsDir: join4(dbDir, "migrations"),
41561
+ functionsDir: join4(repoRoot, "supabase", "functions"),
41562
+ layout: "source"
41563
+ };
41564
+ }
41565
+ function serverAssetsUsable(p) {
41566
+ return existsSync4(p.schemaFile) && existsSync4(p.rpcsFile);
41567
+ }
41568
+ function resolveServerAssets(opts = {}) {
41569
+ if (opts.assetsDir) {
41570
+ return { ...bundledServerAssets(opts.assetsDir), layout: "explicit" };
41571
+ }
41572
+ const here = opts.moduleDirOverride ?? moduleDir();
41573
+ const cwd = opts.cwd ?? processCwd2();
41574
+ const candidates = [
41575
+ bundledServerAssets(join4(here, "..", "server-assets")),
41576
+ sourceServerAssets(join4(here, "..", "..")),
41577
+ sourceServerAssets(cwd)
41578
+ ];
41579
+ for (const candidate of candidates) {
41580
+ if (serverAssetsUsable(candidate))
41581
+ return candidate;
41582
+ }
41583
+ return sourceServerAssets(join4(here, "..", ".."));
41584
+ }
41585
+
41586
+ // ../../_shared/db-deploy/index.ts
41587
+ import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync } from "node:fs";
41588
+ init_src();
41589
+ var RESET_SQL = `
41590
+ DROP TABLE IF EXISTS cerefox_chunks CASCADE;
41591
+ DROP TABLE IF EXISTS cerefox_documents CASCADE;
41592
+ DROP TABLE IF EXISTS cerefox_projects CASCADE;
41593
+ DROP TABLE IF EXISTS cerefox_migrations CASCADE;
41594
+ DROP FUNCTION IF EXISTS cerefox_set_updated_at CASCADE;
41595
+ DROP FUNCTION IF EXISTS cerefox_hybrid_search CASCADE;
41596
+ DROP FUNCTION IF EXISTS cerefox_fts_search CASCADE;
41597
+ DROP FUNCTION IF EXISTS cerefox_semantic_search CASCADE;
41598
+ DROP FUNCTION IF EXISTS cerefox_reconstruct_doc CASCADE;
41599
+ `;
41600
+ var EXTENSIONS_SQL = `
41601
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
41602
+ CREATE EXTENSION IF NOT EXISTS "vector";
41603
+ `;
41604
+ function listMigrationFiles(migrationsDir) {
41605
+ if (!existsSync5(migrationsDir))
41606
+ return [];
41607
+ return readdirSync(migrationsDir).filter((n) => n.endsWith(".sql")).sort();
41608
+ }
41609
+ function buildDeploySteps(assets, opts = {}) {
41610
+ const schemaSql = readFileSync4(assets.schemaFile, "utf8");
41611
+ const rpcsSql = readFileSync4(assets.rpcsFile, "utf8");
41612
+ const steps = [];
41613
+ if (opts.reset) {
41614
+ steps.push({ label: "Reset: drop existing Cerefox objects", sql: RESET_SQL });
41615
+ }
41616
+ steps.push({ label: "Enable extensions (uuid-ossp, vector/pgvector)", sql: EXTENSIONS_SQL }, { label: "Apply schema (tables, indexes, triggers)", sql: schemaSql }, { label: "Apply RPCs (search functions)", sql: rpcsSql });
41617
+ const migrationFiles = listMigrationFiles(assets.migrationsDir);
41618
+ if (migrationFiles.length > 0) {
41619
+ const values2 = migrationFiles.map((n) => `('${n.replace(/'/g, "''")}')`).join(", ");
41620
+ steps.push({
41621
+ label: "Stamp migration files as already applied",
41622
+ sql: `INSERT INTO cerefox_migrations (filename) VALUES ${values2} ON CONFLICT (filename) DO NOTHING;`
41623
+ });
41624
+ }
41625
+ return steps;
41626
+ }
41627
+ async function runDbDeploy(opts) {
41628
+ const log = opts.log ?? (() => {});
41629
+ const steps = buildDeploySteps(opts.assets, { reset: opts.reset });
41630
+ if (opts.dryRun) {
41631
+ for (const step of steps) {
41632
+ log(`▶ ${step.label}… (dry-run, not executed)`);
41633
+ }
41634
+ return { ok: true, stepsRun: steps.length };
41635
+ }
41636
+ const sql = src_default(opts.dbUrl, { prepare: false, onnotice: () => {} });
41637
+ let stepsRun = 0;
41638
+ try {
41639
+ for (const step of steps) {
41640
+ log(`▶ ${step.label}…`);
41641
+ try {
41642
+ await sql.unsafe(step.sql);
41643
+ stepsRun++;
41644
+ } catch (err) {
41645
+ const message = err instanceof Error ? err.message : String(err);
41646
+ return { ok: false, stepsRun, failedStep: step.label, error: message };
41647
+ }
41648
+ }
41649
+ return { ok: true, stepsRun };
41650
+ } finally {
41651
+ await sql.end({ timeout: 5 }).catch(() => {});
41652
+ }
41653
+ }
41654
+
41655
+ // src/cli/commands/deploy-server.ts
41656
+ function commandSucceeds(cmd, args) {
41657
+ try {
41658
+ const r = spawnSync2(cmd, args, { encoding: "utf8", timeout: 15000 });
41659
+ return r.status === 0;
41660
+ } catch {
41661
+ return false;
41662
+ }
41663
+ }
41664
+ function listEdgeFunctions(functionsDir) {
41665
+ if (!existsSync6(functionsDir))
41666
+ return [];
41667
+ return readdirSync2(functionsDir, { withFileTypes: true }).filter((e) => e.isDirectory() && e.name.startsWith("cerefox-")).map((e) => e.name).sort();
41668
+ }
41669
+ async function action7(options) {
41670
+ const settings = loadSettings();
41671
+ const assets = resolveServerAssets();
41672
+ const doSchema = !options.functionsOnly;
41673
+ const doFunctions = !options.schemaOnly;
41674
+ const checks = [];
41675
+ if (doSchema) {
41676
+ checks.push({
41677
+ label: "CEREFOX_DATABASE_URL is set (Session Pooler, port 5432)",
41678
+ ok: Boolean(settings.databaseUrl),
41679
+ remediation: "Set CEREFOX_DATABASE_URL in your .env. Use the Session Pooler URI " + "(port 5432, username postgres.<project-ref>, ?sslmode=require). " + "See docs/guides/setup-supabase.md → Connection pooling."
41680
+ });
41681
+ checks.push({
41682
+ label: "Bundled schema assets present",
41683
+ ok: existsSync6(assets.schemaFile) && existsSync6(assets.rpcsFile),
41684
+ remediation: "Schema files not found. Reinstall the package, or run from a repo " + "clone (src/cerefox/db/)."
41685
+ });
41686
+ }
41687
+ let efNames = [];
41688
+ if (doFunctions) {
41689
+ efNames = listEdgeFunctions(assets.functionsDir);
41690
+ checks.push({
41691
+ label: "Bundled Edge Function sources present",
41692
+ ok: efNames.length > 0,
41693
+ remediation: "Edge Function sources not found under the bundled assets. Reinstall " + "the package, or run from a repo clone (supabase/functions/)."
41694
+ });
41695
+ checks.push({
41696
+ label: "Supabase CLI reachable (`npx supabase --version`)",
41697
+ ok: commandSucceeds("npx", ["--yes", "supabase", "--version"]),
41698
+ remediation: "Install Node 20+ (npx ships with it) from nodejs.org, then re-run. " + "`cerefox deploy-server` shells out to the Supabase CLI via npx."
41699
+ });
41700
+ checks.push({
41701
+ label: "Supabase project linked (`npx supabase link`)",
41702
+ ok: existsSync6("supabase/config.toml") || existsSync6(".supabase/config.toml"),
41703
+ remediation: `Authenticate + link your project (one-time, from any working dir):
41704
+ ` + ` npx supabase login
41705
+ ` + ` npx supabase link --project-ref <your-project-ref>
41706
+ ` + " Your project ref is in the dashboard URL: " + "https://supabase.com/dashboard/project/<project-ref>"
41707
+ });
41708
+ }
41709
+ const failed = checks.filter((ch) => !ch.ok);
41710
+ println(c.bold("Cerefox deploy-server — pre-flight"));
41711
+ for (const ch of checks) {
41712
+ println(` ${ch.ok ? c.green("✓") : c.red("✗")} ${ch.label}`);
41713
+ }
41714
+ if (failed.length > 0) {
41715
+ eprintln("");
41716
+ eprintln(c.red(`Cannot deploy yet — ${failed.length} prerequisite(s) missing:`));
41717
+ for (const ch of failed) {
41718
+ eprintln(`
41719
+ ${c.red("✗")} ${ch.label}`);
41720
+ eprintln(c.dim(` → ${ch.remediation}`));
41721
+ }
41722
+ eprintln("\nFix the above and re-run `cerefox deploy-server` (it's idempotent).");
41723
+ process.exit(1);
41724
+ }
41725
+ println(c.green(`
41726
+ ✓ All prerequisites satisfied.`));
41727
+ const planLines = [];
41728
+ if (doSchema) {
41729
+ planLines.push(` • ${options.reset ? "RESET + redeploy" : "Deploy"} schema + RPCs to ${settings.supabaseUrl || "your Supabase database"}`);
41730
+ }
41731
+ if (doFunctions) {
41732
+ planLines.push(` • Deploy ${efNames.length} Edge Function(s): ${efNames.join(", ")}`);
41733
+ }
41734
+ println(c.bold(`
41735
+ Plan:`));
41736
+ for (const line of planLines)
41737
+ println(line);
41738
+ if (options.dryRun) {
41739
+ println(c.yellow(`
41740
+ ⚠ --dry-run: nothing was deployed.`));
41741
+ process.exit(0);
41742
+ }
41743
+ const proceed = await confirm(`
41744
+ Proceed with deployment to Supabase${options.reset ? " (--reset DROPS existing data!)" : ""}?`, true);
41745
+ if (!proceed) {
41746
+ println(c.dim("Aborted."));
41747
+ process.exit(0);
41748
+ }
41749
+ if (doSchema) {
41750
+ println(c.bold(`
41751
+ ▶ Deploying schema + RPCs…`));
41752
+ const result = await runDbDeploy({
41753
+ dbUrl: settings.databaseUrl,
41754
+ assets,
41755
+ reset: options.reset,
41756
+ log: (line) => println(c.dim(` ${line}`))
41757
+ });
41758
+ if (!result.ok) {
41759
+ eprintln(c.red(`
41760
+ ✗ Schema deploy failed at "${result.failedStep}": ${result.error}`));
41761
+ process.exit(1);
41762
+ }
41763
+ println(c.green(` ✓ Schema deployed (${result.stepsRun} steps).`));
41764
+ }
41765
+ if (doFunctions) {
41766
+ println(c.bold(`
41767
+ ▶ Deploying ${efNames.length} Edge Function(s)…`));
41768
+ let efOk = 0;
41769
+ const efFailed = [];
41770
+ for (const ef of efNames) {
41771
+ info(` deploying ${ef}…`);
41772
+ const workdir = assets.functionsDir.replace(/\/functions$/, "").replace(/\/supabase$/, "");
41773
+ const r = spawnSync2("npx", ["--yes", "supabase", "functions", "deploy", ef], {
41774
+ encoding: "utf8",
41775
+ stdio: "inherit",
41776
+ cwd: workdir,
41777
+ timeout: 120000
41778
+ });
41779
+ if (r.status === 0)
41780
+ efOk++;
41781
+ else
41782
+ efFailed.push(ef);
41783
+ }
41784
+ if (efFailed.length > 0) {
41785
+ eprintln(c.red(`
41786
+ ✗ ${efFailed.length} Edge Function(s) failed: ${efFailed.join(", ")}`));
41787
+ eprintln(c.dim(" Re-run `cerefox deploy-server --functions-only` after fixing the cause."));
41788
+ process.exit(1);
41789
+ }
41790
+ println(c.green(` ✓ Deployed ${efOk} Edge Function(s).`));
41791
+ }
41792
+ println(c.green(`
41793
+ ✓ Server deploy complete.`));
41794
+ println(c.dim("Verify with: cerefox doctor"));
41795
+ }
41796
+ function registerDeployServer(program2) {
41797
+ program2.command("deploy-server").description("Deploy the Cerefox server side (schema + RPCs + Edge Functions) to Supabase.").option("--dry-run", "Print the plan + pre-flight without deploying.").option("--reset", "DROP existing Cerefox tables before redeploying the schema (DESTRUCTIVE).").option("--schema-only", "Deploy only the schema + RPCs (skip Edge Functions).").option("--functions-only", "Deploy only the Edge Functions (skip the schema).").action(action7);
41798
+ }
41799
+
41530
41800
  // src/cli/commands/docs.ts
41531
41801
  init_cli_core();
41532
41802
  init_bundled_docs();
41533
- import { spawnSync as spawnSync2 } from "node:child_process";
41803
+ import { spawnSync as spawnSync3 } from "node:child_process";
41534
41804
  function openInBrowser(path) {
41535
41805
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
41536
- const result = spawnSync2(cmd, [path], { stdio: "ignore" });
41806
+ const result = spawnSync3(cmd, [path], { stdio: "ignore" });
41537
41807
  if (result.status !== 0) {
41538
41808
  println(c.dim(`(could not auto-open; the file is at: ${path})`));
41539
41809
  }
41540
41810
  }
41541
- function action7(topic, options) {
41811
+ function action8(topic, options) {
41542
41812
  const docs = listBundledDocs();
41543
41813
  if (options.list || !topic && !options.print) {
41544
41814
  if (docs.length === 0) {
@@ -41567,7 +41837,7 @@ function action7(topic, options) {
41567
41837
  openInBrowser(doc.path);
41568
41838
  }
41569
41839
  function registerDocs(program2) {
41570
- program2.command("docs").description("Open bundled Cerefox docs in your browser (or print to stdout).").argument("[topic]", "Doc topic (e.g. quickstart, connect-agents). Omit for the index.").option("--print", "Print to stdout instead of opening a browser.").option("--list", "List available topics.").action(action7);
41840
+ program2.command("docs").description("Open bundled Cerefox docs in your browser (or print to stdout).").argument("[topic]", "Doc topic (e.g. quickstart, connect-agents). Omit for the index.").option("--print", "Print to stdout instead of opening a browser.").option("--list", "List available topics.").action(action8);
41571
41841
  }
41572
41842
 
41573
41843
  // ../../node_modules/.bun/ora@9.4.0/node_modules/ora/index.js
@@ -41753,7 +42023,7 @@ var ansi_styles_default = ansiStyles;
41753
42023
 
41754
42024
  // ../../node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
41755
42025
  import process2 from "node:process";
41756
- import os from "node:os";
42026
+ import os2 from "node:os";
41757
42027
  import tty from "node:tty";
41758
42028
  function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
41759
42029
  const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
@@ -41818,7 +42088,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
41818
42088
  return min;
41819
42089
  }
41820
42090
  if (process2.platform === "win32") {
41821
- const osRelease = os.release().split(".");
42091
+ const osRelease = os2.release().split(".");
41822
42092
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
41823
42093
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
41824
42094
  }
@@ -44132,7 +44402,7 @@ var format = (open, close) => {
44132
44402
  return result;
44133
44403
  };
44134
44404
  };
44135
- var reset = format(0, 0);
44405
+ var reset2 = format(0, 0);
44136
44406
  var bold = format(1, 22);
44137
44407
  var dim = format(2, 22);
44138
44408
  var italic = format(3, 23);
@@ -44959,9 +45229,105 @@ init_cli_core();
44959
45229
  init_meta();
44960
45230
  init_config();
44961
45231
  init_config();
44962
- import { existsSync as existsSync5, readFileSync as readFileSync5, realpathSync, statSync as statSync2 } from "node:fs";
45232
+ import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync, statSync as statSync2 } from "node:fs";
44963
45233
  import { homedir as homedir4 } from "node:os";
44964
- import { join as join5 } from "node:path";
45234
+ import { join as join6 } from "node:path";
45235
+
45236
+ // ../../_shared/compatibility/index.ts
45237
+ var COMPATIBILITY = {
45238
+ minSchema: "0.3.1",
45239
+ minEdgeFunctions: "0.6.0"
45240
+ };
45241
+ function compareSemver(a, b2) {
45242
+ const norm = (v) => v.split(/[.-]/).slice(0, 3).map((p) => Number.parseInt(p, 10)).map((n) => Number.isFinite(n) ? n : 0);
45243
+ const pa = norm(a);
45244
+ const pb = norm(b2);
45245
+ for (let i = 0;i < 3; i++) {
45246
+ const x = pa[i] ?? 0;
45247
+ const y = pb[i] ?? 0;
45248
+ if (x !== y)
45249
+ return x < y ? -1 : 1;
45250
+ }
45251
+ return 0;
45252
+ }
45253
+ function classifyCompat(deployed, min, bundled) {
45254
+ if (!deployed)
45255
+ return "unknown";
45256
+ if (compareSemver(deployed, min) < 0)
45257
+ return "below-min";
45258
+ if (bundled && compareSemver(deployed, bundled) < 0)
45259
+ return "above-min-but-old";
45260
+ return "ok";
45261
+ }
45262
+ function aggregatorUrlFor(supabaseUrl) {
45263
+ const base = supabaseUrl.replace(/\/$/, "");
45264
+ return `${base}/functions/v1/cerefox-mcp/version?peers=true`;
45265
+ }
45266
+ async function checkServerCompatibility(opts) {
45267
+ const fetchImpl = opts.fetchImpl ?? fetch;
45268
+ const result = {
45269
+ schema: { deployed: null, min: COMPATIBILITY.minSchema, level: "unknown" },
45270
+ edgeFunctions: {
45271
+ deployed: null,
45272
+ min: COMPATIBILITY.minEdgeFunctions,
45273
+ level: "unknown",
45274
+ errors: []
45275
+ },
45276
+ blocking: false,
45277
+ efProbeSkipped: false
45278
+ };
45279
+ if (!opts.bearer) {
45280
+ result.efProbeSkipped = true;
45281
+ result.efSkipReason = "No anon JWT (CEREFOX_SUPABASE_ANON_KEY) configured; Edge Function version check skipped.";
45282
+ return result;
45283
+ }
45284
+ let agg = null;
45285
+ try {
45286
+ const ctrl = new AbortController;
45287
+ const timer2 = setTimeout(() => ctrl.abort(), opts.timeoutMs ?? 6000);
45288
+ try {
45289
+ const resp = await fetchImpl(opts.aggregatorUrl, {
45290
+ method: "GET",
45291
+ headers: { Authorization: `Bearer ${opts.bearer}`, apikey: opts.bearer },
45292
+ signal: ctrl.signal
45293
+ });
45294
+ if (resp.ok) {
45295
+ agg = await resp.json();
45296
+ } else {
45297
+ result.efProbeSkipped = true;
45298
+ result.efSkipReason = resp.status === 404 || resp.status === 405 ? "Edge Functions predate v0.8 (no /version route). Redeploy with `cerefox deploy-server --functions-only` to enable version checks." : `Aggregator returned HTTP ${resp.status}; Edge Function version check skipped.`;
45299
+ }
45300
+ } finally {
45301
+ clearTimeout(timer2);
45302
+ }
45303
+ } catch (err) {
45304
+ result.efProbeSkipped = true;
45305
+ result.efSkipReason = `Could not reach the version aggregator: ${err instanceof Error ? err.message : String(err)}`;
45306
+ }
45307
+ if (!agg)
45308
+ return result;
45309
+ result.schema.deployed = agg.schema ?? null;
45310
+ result.schema.level = classifyCompat(result.schema.deployed, COMPATIBILITY.minSchema, opts.bundledSchema);
45311
+ const versions = [];
45312
+ if (agg.version)
45313
+ versions.push(agg.version);
45314
+ for (const ef of agg.efs ?? [])
45315
+ versions.push(ef.version);
45316
+ result.edgeFunctions.errors = agg.errors ?? [];
45317
+ if (versions.length > 0) {
45318
+ const weakest = versions.reduce((lo, v) => compareSemver(v, lo) < 0 ? v : lo);
45319
+ result.edgeFunctions.deployed = weakest;
45320
+ result.edgeFunctions.level = classifyCompat(weakest, COMPATIBILITY.minEdgeFunctions, opts.bundledEf);
45321
+ } else {
45322
+ result.edgeFunctions.level = "unknown";
45323
+ result.efProbeSkipped = true;
45324
+ result.efSkipReason = "Aggregator reported no Edge Function versions; check skipped.";
45325
+ }
45326
+ result.blocking = result.schema.level === "below-min" || result.edgeFunctions.level === "below-min";
45327
+ return result;
45328
+ }
45329
+
45330
+ // src/cli/util/checks.ts
44965
45331
  function checkBinary() {
44966
45332
  return {
44967
45333
  name: "binary",
@@ -45012,7 +45378,7 @@ function checkConfig() {
45012
45378
  hint: "Run `cerefox init` to bootstrap."
45013
45379
  };
45014
45380
  }
45015
- if (!existsSync5(envPath)) {
45381
+ if (!existsSync8(envPath)) {
45016
45382
  return {
45017
45383
  name: "config",
45018
45384
  status: "error",
@@ -45179,10 +45545,10 @@ async function checkSchemaVersion() {
45179
45545
  }
45180
45546
  }
45181
45547
  function hasCerefoxInJsonFile(path) {
45182
- if (!existsSync5(path))
45548
+ if (!existsSync8(path))
45183
45549
  return false;
45184
45550
  try {
45185
- const parsed = JSON.parse(readFileSync5(path, "utf8"));
45551
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
45186
45552
  const mcpServers = parsed.mcpServers;
45187
45553
  return Boolean(mcpServers && typeof mcpServers === "object" && "cerefox" in mcpServers);
45188
45554
  } catch {
@@ -45191,9 +45557,9 @@ function hasCerefoxInJsonFile(path) {
45191
45557
  }
45192
45558
  function checkMcpConfigs() {
45193
45559
  const home = homedir4();
45194
- const claudeCodeUser = join5(home, ".claude.json");
45195
- const claudeCodeProj = join5(process.cwd(), ".mcp.json");
45196
- const claudeDesktop = process.platform === "darwin" ? join5(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join5(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : join5(home, ".config", "Claude", "claude_desktop_config.json");
45560
+ const claudeCodeUser = join6(home, ".claude.json");
45561
+ const claudeCodeProj = join6(process.cwd(), ".mcp.json");
45562
+ const claudeDesktop = process.platform === "darwin" ? join6(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : process.platform === "win32" ? join6(process.env.APPDATA ?? "", "Claude", "claude_desktop_config.json") : join6(home, ".config", "Claude", "claude_desktop_config.json");
45197
45563
  const found = [];
45198
45564
  if (hasCerefoxInJsonFile(claudeCodeUser))
45199
45565
  found.push("Claude Code (user)");
@@ -45217,9 +45583,9 @@ function checkMcpConfigs() {
45217
45583
  }
45218
45584
  function checkLegacyShadowEnv() {
45219
45585
  const home = homedir4();
45220
- const homeEnv = join5(home, USER_STATE_DIR_NAME, ".env");
45221
- const cwdEnv = join5(process.cwd(), ".env");
45222
- if (!existsSync5(homeEnv) || !existsSync5(cwdEnv))
45586
+ const homeEnv = join6(home, USER_STATE_DIR_NAME, ".env");
45587
+ const cwdEnv = join6(process.cwd(), ".env");
45588
+ if (!existsSync8(homeEnv) || !existsSync8(cwdEnv))
45223
45589
  return null;
45224
45590
  try {
45225
45591
  if (realpathSync(homeEnv) === realpathSync(cwdEnv))
@@ -45278,6 +45644,68 @@ async function checkPostgres() {
45278
45644
  await sql.end({ timeout: 1 }).catch(() => {});
45279
45645
  }
45280
45646
  }
45647
+ async function checkEdgeFunctionsCompat() {
45648
+ const settings = loadSettings();
45649
+ if (!settings.supabaseUrl) {
45650
+ return {
45651
+ name: "edge functions",
45652
+ status: "skipped",
45653
+ detail: "Supabase URL not set; EF version check skipped."
45654
+ };
45655
+ }
45656
+ if (!settings.supabaseAnonKey) {
45657
+ return {
45658
+ name: "edge functions",
45659
+ status: "skipped",
45660
+ detail: "No CEREFOX_SUPABASE_ANON_KEY set; EF version check skipped.",
45661
+ hint: "Set the legacy anon JWT (eyJ…) to enable client↔server version checks."
45662
+ };
45663
+ }
45664
+ let compat;
45665
+ try {
45666
+ compat = await checkServerCompatibility({
45667
+ aggregatorUrl: aggregatorUrlFor(settings.supabaseUrl),
45668
+ bearer: settings.supabaseAnonKey,
45669
+ bundledEf: PKG_VERSION
45670
+ });
45671
+ } catch (err) {
45672
+ return {
45673
+ name: "edge functions",
45674
+ status: "skipped",
45675
+ detail: `Version probe failed: ${err instanceof Error ? err.message : String(err)}`
45676
+ };
45677
+ }
45678
+ if (compat.efProbeSkipped) {
45679
+ return {
45680
+ name: "edge functions",
45681
+ status: "skipped",
45682
+ detail: compat.efSkipReason ?? "EF version check skipped."
45683
+ };
45684
+ }
45685
+ const deployed = compat.edgeFunctions.deployed ?? "unknown";
45686
+ switch (compat.edgeFunctions.level) {
45687
+ case "below-min":
45688
+ return {
45689
+ name: "edge functions",
45690
+ status: "error",
45691
+ detail: `Deployed EF v${deployed} is below the required minimum v${compat.edgeFunctions.min}.`,
45692
+ hint: "Redeploy the Edge Functions: `cerefox deploy-server --functions-only`."
45693
+ };
45694
+ case "above-min-but-old":
45695
+ return {
45696
+ name: "edge functions",
45697
+ status: "warn",
45698
+ detail: `Deployed EF v${deployed} works but is older than this client (v${PKG_VERSION}).`,
45699
+ hint: "Consider redeploying: `cerefox deploy-server --functions-only`."
45700
+ };
45701
+ default:
45702
+ return {
45703
+ name: "edge functions",
45704
+ status: "ok",
45705
+ detail: `Deployed EF v${deployed} (≥ required v${compat.edgeFunctions.min}).`
45706
+ };
45707
+ }
45708
+ }
45281
45709
  async function runSteps(steps, opts) {
45282
45710
  const results = [];
45283
45711
  for (let i = 0;i < steps.length; i++) {
@@ -45304,6 +45732,7 @@ async function runAllChecks(opts = {}) {
45304
45732
  { name: "supabase", phase: "Probing Supabase Data API", run: () => checkSupabase() },
45305
45733
  { name: "openai", phase: "Probing OpenAI embeddings", run: () => checkOpenAI() },
45306
45734
  { name: "schema", phase: "Reading schema version", run: () => checkSchemaVersion() },
45735
+ { name: "edge functions", phase: "Probing Edge Function versions", run: () => checkEdgeFunctionsCompat() },
45307
45736
  { name: "postgres", phase: "Probing Postgres DDL endpoint", run: () => checkPostgres() },
45308
45737
  { name: "mcp clients", phase: "Scanning MCP client configs", run: () => checkMcpConfigs() }
45309
45738
  ];
@@ -45331,7 +45760,7 @@ function symbol(status) {
45331
45760
  return cErr.dim("ℹ");
45332
45761
  }
45333
45762
  }
45334
- async function action8(options) {
45763
+ async function action9(options) {
45335
45764
  const useSpinner = !options.json && process.stderr.isTTY;
45336
45765
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
45337
45766
  const results = await runAllChecks({
@@ -45368,13 +45797,13 @@ async function action8(options) {
45368
45797
  }
45369
45798
  }
45370
45799
  function registerDoctor(program2) {
45371
- program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action8);
45800
+ program2.command("doctor").description("Run diagnostic checks against the installed Cerefox.").option("--json", "Emit machine-readable JSON (no colours, structured output).").action(action9);
45372
45801
  }
45373
45802
 
45374
45803
  // src/cli/commands/get-audit-log.ts
45375
45804
  init_cli_core();
45376
45805
  init_client();
45377
- async function action9(options) {
45806
+ async function action10(options) {
45378
45807
  const limit = parsePositiveInt(options.limit, "--limit", 50);
45379
45808
  const client = getClient();
45380
45809
  const data = await client.rpc("cerefox_list_audit_entries", {
@@ -45412,14 +45841,14 @@ async function action9(options) {
45412
45841
  })));
45413
45842
  }
45414
45843
  function registerGetAuditLog(program2) {
45415
- program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action9);
45844
+ program2.command("get-audit-log").description("Query the audit log with optional filters.").option("-d, --document-id <uuid>", "Filter by document.").option("-a, --author <name>", "Filter by author.").option("-o, --operation <type>", "Filter by operation: create, update-content, update-metadata, delete, restore.").option("--since <iso>", "Lower-bound ISO timestamp.").option("--until <iso>", "Upper-bound ISO timestamp.").option("-l, --limit <n>", "Maximum entries (max 200).", "50").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action10);
45416
45845
  }
45417
45846
 
45418
45847
  // src/cli/commands/get-doc.ts
45419
45848
  init_cli_core();
45420
45849
  init_cli_core();
45421
45850
  init_client();
45422
- async function action10(documentId, options) {
45851
+ async function action11(documentId, options) {
45423
45852
  const client = getClient();
45424
45853
  const rows = await client.rpc("cerefox_get_document", {
45425
45854
  p_document_id: documentId,
@@ -45449,18 +45878,18 @@ async function action10(documentId, options) {
45449
45878
  println(doc.full_content);
45450
45879
  }
45451
45880
  function registerGetDoc(program2) {
45452
- program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action10);
45881
+ program2.command("get-doc").description("Retrieve the full content of a document by ID.").argument("<document-id>", "UUID of the document.").option("--version-id <uuid>", "Specific archived version (default: current).").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action11);
45453
45882
  }
45454
45883
 
45455
45884
  // src/cli/commands/ingest.ts
45456
45885
  init_dist4();
45457
45886
  init_cli_core();
45458
45887
  init_config();
45459
- import { readFileSync as readFileSync7 } from "node:fs";
45888
+ import { readFileSync as readFileSync8 } from "node:fs";
45460
45889
  import { basename as basename2, extname as extname2 } from "node:path";
45461
45890
 
45462
45891
  // src/ingestion/pipeline.ts
45463
- import { readFileSync as readFileSync6 } from "node:fs";
45892
+ import { readFileSync as readFileSync7 } from "node:fs";
45464
45893
  import { basename, extname, resolve as resolve3 } from "node:path";
45465
45894
 
45466
45895
  // ../../_shared/ingest/chunker.ts
@@ -46144,7 +46573,7 @@ ${c2.content}`);
46144
46573
  };
46145
46574
  }
46146
46575
  async ingestFile(path, opts = {}) {
46147
- const text = readFileSync6(path, "utf8");
46576
+ const text = readFileSync7(path, "utf8");
46148
46577
  const absPath = resolve3(path);
46149
46578
  const stem = basename(absPath, extname(absPath));
46150
46579
  return this.ingestText({
@@ -46178,7 +46607,7 @@ async function readContent(path, paste) {
46178
46607
  }
46179
46608
  let content;
46180
46609
  try {
46181
- content = readFileSync7(path, "utf8");
46610
+ content = readFileSync8(path, "utf8");
46182
46611
  } catch (err) {
46183
46612
  const msg = err instanceof Error ? err.message : String(err);
46184
46613
  throw userError(`Cannot read ${path}: ${msg}`);
@@ -46186,7 +46615,7 @@ async function readContent(path, paste) {
46186
46615
  const titleFromPath = basename2(path, extname2(path));
46187
46616
  return { content, titleFromPath };
46188
46617
  }
46189
- async function action11(path, options) {
46618
+ async function action12(path, options) {
46190
46619
  const { content, titleFromPath } = await readContent(path, Boolean(options.paste));
46191
46620
  const title = options.title ?? titleFromPath;
46192
46621
  if (!title || title.trim() === "") {
@@ -46258,7 +46687,7 @@ async function action11(path, options) {
46258
46687
  }
46259
46688
  }
46260
46689
  function registerIngest(program2) {
46261
- program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action11);
46690
+ program2.command("ingest").description("Ingest a file (or stdin paste) into the knowledge base.").argument("[path]", "Path to the file to ingest. Omit when using --paste.").option("--paste", "Read content from stdin instead of a file.").option("-t, --title <title>", "Document title (required with --paste; defaults to filename without extension).").option("-p, --project-name <name>", "Single project membership (non-destructive on update).").option("-P, --project-names <names>", "Comma-separated full project membership set (destructive replace on update).").option("-m, --metadata <json>", "JSON metadata object.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-i, --document-id <uuid>", "Update a specific document by UUID (overrides --update-if-exists).").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").action(action12);
46262
46691
  }
46263
46692
 
46264
46693
  // src/cli/commands/ingest-dir.ts
@@ -46266,18 +46695,18 @@ init_dist4();
46266
46695
  init_cli_core();
46267
46696
  init_config();
46268
46697
  var import_cli_progress = __toESM(require_cli_progress(), 1);
46269
- import { readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
46270
- import { basename as basename3, extname as extname3, join as join6 } from "node:path";
46698
+ import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
46699
+ import { basename as basename3, extname as extname3, join as join7 } from "node:path";
46271
46700
  function walk(dir, extensions) {
46272
46701
  let entries;
46273
46702
  try {
46274
- entries = readdirSync2(dir);
46703
+ entries = readdirSync4(dir);
46275
46704
  } catch (err) {
46276
46705
  throw userError(`Cannot read directory ${dir}: ${err instanceof Error ? err.message : String(err)}`);
46277
46706
  }
46278
46707
  const files = [];
46279
46708
  for (const name of entries) {
46280
- const full = join6(dir, name);
46709
+ const full = join7(dir, name);
46281
46710
  let stat;
46282
46711
  try {
46283
46712
  stat = statSync3(full);
@@ -46294,7 +46723,7 @@ function walk(dir, extensions) {
46294
46723
  }
46295
46724
  return files;
46296
46725
  }
46297
- async function action12(dir, options) {
46726
+ async function action13(dir, options) {
46298
46727
  const extensions = new Set((options.extensions ?? ".md,.txt").split(",").map((e) => e.trim().toLowerCase()).map((e) => e.startsWith(".") ? e : "." + e).filter((e) => e.length > 0));
46299
46728
  const files = walk(dir, extensions);
46300
46729
  if (files.length === 0) {
@@ -46367,29 +46796,30 @@ async function action12(dir, options) {
46367
46796
  }
46368
46797
  }
46369
46798
  function registerIngestDir(program2) {
46370
- program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action12);
46799
+ program2.command("ingest-dir").description("Recursively ingest a directory of markdown / text files.").argument("<dir>", "Root directory to walk.").option("-p, --project-name <name>", "Project membership for all ingested docs.").option("-m, --metadata <json>", "JSON metadata applied to every doc.").option("--source <label>", "Origin label (default: cli).", "cli").option("-u, --update-if-exists", "Update an existing doc with the same title.").option("-a, --author <name>", "Caller identity (audit log).").option("--author-type <type>", "'user' or 'agent' (default: user).", "user").option("-e, --extensions <list>", "Comma-separated file extensions to ingest.", ".md,.txt").action(action13);
46371
46800
  }
46372
46801
 
46373
46802
  // src/cli/commands/init.ts
46374
46803
  init_cli_core();
46375
46804
  init_config();
46805
+ import { spawnSync as spawnSync4 } from "node:child_process";
46376
46806
  import {
46377
46807
  chmodSync,
46378
46808
  copyFileSync as copyFileSync2,
46379
- existsSync as existsSync6,
46809
+ existsSync as existsSync9,
46380
46810
  mkdirSync as mkdirSync3,
46381
- readFileSync as readFileSync9,
46811
+ readFileSync as readFileSync10,
46382
46812
  writeFileSync as writeFileSync3
46383
46813
  } from "node:fs";
46384
46814
  import { homedir as homedir5 } from "node:os";
46385
- import { dirname as dirname3, join as join7 } from "node:path";
46815
+ import { dirname as dirname4, join as join8 } from "node:path";
46386
46816
  async function readConfigFile(path) {
46387
- if (!existsSync6(path)) {
46817
+ if (!existsSync9(path)) {
46388
46818
  throw userError(`--config file not found: ${path}`);
46389
46819
  }
46390
46820
  let parsed;
46391
46821
  try {
46392
- parsed = JSON.parse(readFileSync9(path, "utf8"));
46822
+ parsed = JSON.parse(readFileSync10(path, "utf8"));
46393
46823
  } catch (err) {
46394
46824
  throw userError(`--config: invalid JSON in ${path}: ${err instanceof Error ? err.message : String(err)}`);
46395
46825
  }
@@ -46431,7 +46861,7 @@ function parseDotEnvFile(content) {
46431
46861
  return map;
46432
46862
  }
46433
46863
  function answersFromEnvFile(path) {
46434
- const parsed = parseDotEnvFile(readFileSync9(path, "utf8"));
46864
+ const parsed = parseDotEnvFile(readFileSync10(path, "utf8"));
46435
46865
  const required = ["CEREFOX_SUPABASE_URL", "CEREFOX_SUPABASE_KEY", "OPENAI_API_KEY"];
46436
46866
  for (const key of required) {
46437
46867
  if (!parsed[key] || parsed[key].trim() === "") {
@@ -46592,14 +47022,60 @@ async function promptMigrationChoice() {
46592
47022
  const ch = choice.trim().toLowerCase() || "c";
46593
47023
  return ch;
46594
47024
  }
46595
- async function postWriteLifecycle(envPath, options) {
46596
- if (!options.skipSchema) {
47025
+ async function probeSchemaVersion(url, key) {
47026
+ try {
47027
+ const resp = await fetch(`${url.replace(/\/$/, "")}/rest/v1/rpc/cerefox_schema_version`, {
47028
+ method: "POST",
47029
+ headers: { "Content-Type": "application/json", apikey: key, Authorization: `Bearer ${key}` },
47030
+ body: "{}"
47031
+ });
47032
+ if (!resp.ok)
47033
+ return null;
47034
+ const data = await resp.json();
47035
+ return typeof data === "string" ? data : null;
47036
+ } catch {
47037
+ return null;
47038
+ }
47039
+ }
47040
+ function launchDeployServer(extraArgs = []) {
47041
+ const r = spawnSync4(process.execPath, [process.argv[1], "deploy-server", ...extraArgs], {
47042
+ stdio: "inherit"
47043
+ });
47044
+ return r.status ?? 1;
47045
+ }
47046
+ async function maybeOfferServerDeploy() {
47047
+ const settings = loadSettings();
47048
+ if (!settings.supabaseUrl || !settings.supabaseKey)
47049
+ return;
47050
+ const deployed = await probeSchemaVersion(settings.supabaseUrl, settings.supabaseKey);
47051
+ if (deployed === null) {
46597
47052
  println(c.bold("Schema deploy"));
46598
- println(c.dim(` v0.5 doesn't yet bundle the schema-deploy path (it needs the direct
46599
- ` + " Postgres connection ported, scheduled for v0.6). For now:"));
46600
- println(c.cyan(" uv run python scripts/db_deploy.py"));
46601
- println(c.dim(" Skip this if your Supabase already has the schema."));
47053
+ println(c.dim(" No Cerefox schema detected on this Supabase project."));
47054
+ const yes = await confirm(" Deploy the server now (schema + RPCs + Edge Functions)?", false);
47055
+ if (yes)
47056
+ launchDeployServer();
47057
+ else
47058
+ println(c.dim(" Skipped. Run `cerefox deploy-server` later (or `cerefox doctor` to recheck)."));
46602
47059
  println("");
47060
+ return;
47061
+ }
47062
+ if (compareSemver(deployed, COMPATIBILITY.minSchema) < 0) {
47063
+ println(c.bold("Schema deploy"));
47064
+ println(c.yellow(` Deployed schema v${deployed} is below the required v${COMPATIBILITY.minSchema}.`));
47065
+ const yes = await confirm(" Redeploy the server now (--reset DROPS existing data)?", true);
47066
+ if (yes)
47067
+ launchDeployServer(["--reset"]);
47068
+ else
47069
+ println(c.dim(" Skipped. Run `cerefox deploy-server --reset` when ready."));
47070
+ println("");
47071
+ return;
47072
+ }
47073
+ println(c.dim(`Schema v${deployed} already deployed (≥ required v${COMPATIBILITY.minSchema}).`));
47074
+ println("");
47075
+ }
47076
+ async function postWriteLifecycle(envPath, options) {
47077
+ if (!options.skipSchema) {
47078
+ await maybeOfferServerDeploy();
46603
47079
  }
46604
47080
  if (!options.skipSelfDocs) {
46605
47081
  println(c.bold("Self-doc ingest"));
@@ -46640,7 +47116,7 @@ async function postWriteLifecycle(envPath, options) {
46640
47116
  println(c.dim(` Config in effect: ${envPath}`));
46641
47117
  }
46642
47118
  function writeAnswersTo(target, answers) {
46643
- mkdirSync3(dirname3(target), { recursive: true });
47119
+ mkdirSync3(dirname4(target), { recursive: true });
46644
47120
  writeFileSync3(target, buildEnvFile(answers), "utf8");
46645
47121
  if (process.platform !== "win32") {
46646
47122
  try {
@@ -46650,13 +47126,13 @@ function writeAnswersTo(target, answers) {
46650
47126
  }
46651
47127
  }
46652
47128
  }
46653
- async function action14(options) {
46654
- const homeEnv = join7(homedir5(), USER_STATE_DIR_NAME, ".env");
46655
- const cwdEnv = join7(process.cwd(), ".env");
47129
+ async function action15(options) {
47130
+ const homeEnv = join8(homedir5(), USER_STATE_DIR_NAME, ".env");
47131
+ const cwdEnv = join8(process.cwd(), ".env");
46656
47132
  const explicitDir = (process.env.CEREFOX_CONFIG_DIR ?? "").trim();
46657
47133
  if (explicitDir) {
46658
47134
  const target2 = resolveEnvFile();
46659
- if (existsSync6(target2) && !options.force) {
47135
+ if (existsSync9(target2) && !options.force) {
46660
47136
  println(c.yellow(`⚠ Config already exists at ${target2}.`));
46661
47137
  const ok2 = await confirm("Overwrite?", true);
46662
47138
  if (!ok2) {
@@ -46678,7 +47154,7 @@ async function action14(options) {
46678
47154
  await postWriteLifecycle(target2, options);
46679
47155
  return;
46680
47156
  }
46681
- if (existsSync6(homeEnv) && !options.force) {
47157
+ if (existsSync9(homeEnv) && !options.force) {
46682
47158
  println(c.yellow(`⚠ Config already exists at ${homeEnv}.`));
46683
47159
  const ok2 = await confirm("Overwrite?", true);
46684
47160
  if (!ok2) {
@@ -46699,12 +47175,12 @@ async function action14(options) {
46699
47175
  await postWriteLifecycle(homeEnv, options);
46700
47176
  return;
46701
47177
  }
46702
- if (existsSync6(cwdEnv) && !options.force && !options.config) {
47178
+ if (existsSync9(cwdEnv) && !options.force && !options.config) {
46703
47179
  printMigrationMenu(cwdEnv, homeEnv);
46704
47180
  const ch = await promptMigrationChoice();
46705
47181
  println("");
46706
47182
  if (ch === "c") {
46707
- mkdirSync3(dirname3(homeEnv), { recursive: true });
47183
+ mkdirSync3(dirname4(homeEnv), { recursive: true });
46708
47184
  copyFileSync2(cwdEnv, homeEnv);
46709
47185
  if (process.platform !== "win32") {
46710
47186
  try {
@@ -46759,13 +47235,13 @@ async function action14(options) {
46759
47235
  await postWriteLifecycle(target, options);
46760
47236
  }
46761
47237
  function registerInit(program2) {
46762
- program2.command("init").description("Interactive first-run setup (config, schema deploy stub, optional MCP wiring).").option("-c, --config <file>", "Non-interactive mode: read answers from a JSON file.").option("--force", "Overwrite existing configuration without prompting.").option("--skip-schema", "Skip the schema deploy step.").option("--skip-self-docs", "Skip the bundled self-doc ingest.").option("--skip-agent-config", "Skip the optional MCP agent wiring.").action(action14);
47238
+ program2.command("init").description("Interactive first-run setup (config, schema deploy stub, optional MCP wiring).").option("-c, --config <file>", "Non-interactive mode: read answers from a JSON file.").option("--force", "Overwrite existing configuration without prompting.").option("--skip-schema", "Skip the schema deploy step.").option("--skip-self-docs", "Skip the bundled self-doc ingest.").option("--skip-agent-config", "Skip the optional MCP agent wiring.").action(action15);
46763
47239
  }
46764
47240
 
46765
47241
  // src/cli/commands/list-docs.ts
46766
47242
  init_cli_core();
46767
47243
  init_client();
46768
- async function action15(options) {
47244
+ async function action16(options) {
46769
47245
  const limit = parsePositiveInt(options.limit, "--limit", 100);
46770
47246
  const client = getClient();
46771
47247
  let projectId;
@@ -46820,13 +47296,13 @@ async function action15(options) {
46820
47296
  })));
46821
47297
  }
46822
47298
  function registerListDocs(program2) {
46823
- program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--json", "Emit machine-readable JSON.").action(action15);
47299
+ program2.command("list-docs").description("List documents in the knowledge base.").option("-p, --project <name>", "Filter to a specific project.").option("-l, --limit <n>", "Maximum docs to return.", "100").option("--json", "Emit machine-readable JSON.").action(action16);
46824
47300
  }
46825
47301
 
46826
47302
  // src/cli/commands/list-metadata-keys.ts
46827
47303
  init_cli_core();
46828
47304
  init_client();
46829
- async function action16(options) {
47305
+ async function action17(options) {
46830
47306
  const client = getClient();
46831
47307
  const data = await client.rpc("cerefox_list_metadata_keys");
46832
47308
  if (data === null) {
@@ -46854,13 +47330,13 @@ async function action16(options) {
46854
47330
  })));
46855
47331
  }
46856
47332
  function registerListMetadataKeys(program2) {
46857
- program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action16);
47333
+ program2.command("list-metadata-keys").description("List all metadata keys with document counts and example values.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action17);
46858
47334
  }
46859
47335
 
46860
47336
  // src/cli/commands/list-projects.ts
46861
47337
  init_cli_core();
46862
47338
  init_client();
46863
- async function action17(options) {
47339
+ async function action18(options) {
46864
47340
  const client = getClient();
46865
47341
  const { data, error: error2 } = await client.raw.from("cerefox_projects").select("id, name, description, created_at").order("name", { ascending: true });
46866
47342
  if (error2) {
@@ -46889,13 +47365,13 @@ async function action17(options) {
46889
47365
  })), "(no projects)");
46890
47366
  }
46891
47367
  function registerListProjects(program2) {
46892
- program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action17);
47368
+ program2.command("list-projects").description("List all projects in the knowledge base.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action18);
46893
47369
  }
46894
47370
 
46895
47371
  // src/cli/commands/list-versions.ts
46896
47372
  init_cli_core();
46897
47373
  init_client();
46898
- async function action18(documentId, options) {
47374
+ async function action19(documentId, options) {
46899
47375
  const client = getClient();
46900
47376
  const data = await client.rpc("cerefox_list_document_versions", {
46901
47377
  p_document_id: documentId
@@ -46936,7 +47412,7 @@ async function action18(documentId, options) {
46936
47412
  })));
46937
47413
  }
46938
47414
  function registerListVersions(program2) {
46939
- program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action18);
47415
+ program2.command("list-versions").description("List archived versions of a document.").argument("<document-id>", "UUID of the document.").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action19);
46940
47416
  }
46941
47417
 
46942
47418
  // src/cli/commands/mcp.ts
@@ -46951,7 +47427,7 @@ function registerMcp(program2) {
46951
47427
  // src/cli/commands/metadata-search.ts
46952
47428
  init_cli_core();
46953
47429
  init_client();
46954
- async function action19(options) {
47430
+ async function action20(options) {
46955
47431
  const metadataFilter = parseJsonObjectArg(options.metadataFilter, "--metadata-filter");
46956
47432
  if (!metadataFilter || Object.keys(metadataFilter).length === 0) {
46957
47433
  throw userError("--metadata-filter is required and must be a non-empty JSON object.", `Example: --metadata-filter '{"type":"decision-log"}'.`);
@@ -47014,7 +47490,7 @@ async function action19(options) {
47014
47490
  }
47015
47491
  }
47016
47492
  function registerMetadataSearch(program2) {
47017
- program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action19);
47493
+ program2.command("metadata-search").description("Find documents by metadata criteria (no text query).").requiredOption("-f, --metadata-filter <json>", "JSON object; only docs whose metadata contains ALL pairs are returned.").option("-p, --project-name <name>", "Filter to a specific project.").option("--updated-since <iso>", "Only docs updated on/after this ISO timestamp.").option("--created-since <iso>", "Only docs created on/after this ISO timestamp.").option("--include-content", "Include full document text in results.").option("-l, --limit <n>", "Maximum docs to return.", "10").option("--max-bytes <n>", "Response size budget in bytes (with --include-content).", "200000").option("-r, --requestor <name>", "Agent / user name (usage log).").option("--json", "Emit machine-readable JSON.").action(action20);
47018
47494
  }
47019
47495
 
47020
47496
  // src/cli/commands/reindex.ts
@@ -47022,7 +47498,7 @@ init_dist4();
47022
47498
  init_cli_core();
47023
47499
  init_config();
47024
47500
  var DEFAULT_MODEL = "text-embedding-3-small";
47025
- async function action20(options) {
47501
+ async function action21(options) {
47026
47502
  const settings = loadSettings();
47027
47503
  if (!settings.supabaseUrl || !settings.supabaseKey) {
47028
47504
  throw userError("Supabase credentials not configured — run `cerefox init` first.");
@@ -47101,41 +47577,41 @@ ${c2.content}`;
47101
47577
  }
47102
47578
  }
47103
47579
  function registerReindex(program2) {
47104
- program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action20);
47580
+ program2.command("reindex").description("Re-embed existing document chunks (v0.7+).").option("--all", "Reindex every chunk regardless of embedder.").option("--batch <n>", "Chunks per OpenAI batch call. Capped at 96 internally.", "32").option("--dry-run", "Show counts without re-embedding.").option("-i, --document-id <uuid>", "Limit reindex to a single document.").action(action21);
47105
47581
  }
47106
47582
 
47107
47583
  // src/cli/commands/restore.ts
47108
47584
  init_cli_core();
47109
47585
  init_client();
47110
- import { existsSync as existsSync7, readFileSync as readFileSync10, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
47586
+ import { existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, statSync as statSync4 } from "node:fs";
47111
47587
  import { homedir as homedir6 } from "node:os";
47112
- import { join as join8, resolve as resolve4 } from "node:path";
47588
+ import { join as join9, resolve as resolve4 } from "node:path";
47113
47589
  function expandHome2(path) {
47114
47590
  if (path === "~")
47115
47591
  return homedir6();
47116
47592
  if (path.startsWith("~/"))
47117
- return join8(homedir6(), path.slice(2));
47593
+ return join9(homedir6(), path.slice(2));
47118
47594
  return path;
47119
47595
  }
47120
47596
  function resolveBackupFile(target) {
47121
47597
  const path = resolve4(expandHome2(target));
47122
- if (!existsSync7(path)) {
47598
+ if (!existsSync10(path)) {
47123
47599
  throw userError(`Backup path not found: ${target}`);
47124
47600
  }
47125
47601
  const stat = statSync4(path);
47126
47602
  if (stat.isFile())
47127
47603
  return path;
47128
- const candidates = readdirSync3(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join8(path, n)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
47604
+ const candidates = readdirSync5(path).filter((n) => n.endsWith(".json") && n.startsWith("cerefox-")).map((n) => ({ name: n, mtime: statSync4(join9(path, n)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
47129
47605
  if (candidates.length === 0) {
47130
47606
  throw userError(`No cerefox-*.json files in ${path}`);
47131
47607
  }
47132
- return join8(path, candidates[0].name);
47608
+ return join9(path, candidates[0].name);
47133
47609
  }
47134
- async function action21(target, options) {
47610
+ async function action22(target, options) {
47135
47611
  const file = resolveBackupFile(target);
47136
47612
  let payload;
47137
47613
  try {
47138
- payload = JSON.parse(readFileSync10(file, "utf8"));
47614
+ payload = JSON.parse(readFileSync11(file, "utf8"));
47139
47615
  } catch (err) {
47140
47616
  throw userError(`Could not parse backup file ${file}: ${err instanceof Error ? err.message : String(err)}`);
47141
47617
  }
@@ -47187,7 +47663,7 @@ async function action21(target, options) {
47187
47663
  }
47188
47664
  }
47189
47665
  function registerRestore(program2) {
47190
- program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action21);
47666
+ program2.command("restore").description("Restore a JSON-snapshot backup into the knowledge base.").argument("<snapshot>", "Backup file (or directory; most recent is picked) produced by `cerefox backup`.").option("--dry-run", "Print what would be restored without writing.").option("-p, --project-name <name>", "Reserved for future use; currently ignored (project memberships ride along with each doc's metadata).").action(action22);
47191
47667
  }
47192
47668
 
47193
47669
  // src/cli/commands/search.ts
@@ -47212,7 +47688,7 @@ async function embedQuery(query) {
47212
47688
  }
47213
47689
 
47214
47690
  // src/cli/commands/search.ts
47215
- async function action22(query, options) {
47691
+ async function action23(query, options) {
47216
47692
  if (!query || query.trim() === "") {
47217
47693
  throw userError("Empty query.");
47218
47694
  }
@@ -47346,13 +47822,13 @@ async function action22(query, options) {
47346
47822
  }
47347
47823
  }
47348
47824
  function registerSearch(program2) {
47349
- program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold.", "0.5").option("--max-bytes <n>", "Response size budget in bytes.", "200000").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").action(action22);
47825
+ program2.command("search").description("Search the knowledge base (hybrid FTS + semantic).").argument("<query>", "Natural-language search query.").option("-c, --match-count <n>", "Maximum number of documents to return.", "5").option("-p, --project-name <name>", "Filter results to a specific project.").option("-f, --metadata-filter <json>", "JSON containment filter; only docs whose metadata contains ALL pairs are returned.").option("--mode <mode>", "Search mode: docs (default), hybrid, fts.", "docs").option("--alpha <float>", "Semantic weight 0..1 (default: 0.7).", "0.7").option("--min-score <float>", "Minimum cosine similarity threshold.", "0.5").option("--max-bytes <n>", "Response size budget in bytes.", "200000").option("-r, --requestor <name>", "Agent / user name (recorded in usage log).").option("--json", "Emit machine-readable JSON instead of the default text.").action(action23);
47350
47826
  }
47351
47827
 
47352
47828
  // src/cli/commands/self-update.ts
47353
47829
  init_cli_core();
47354
47830
  init_meta();
47355
- import { spawnSync as spawnSync3 } from "node:child_process";
47831
+ import { spawnSync as spawnSync5 } from "node:child_process";
47356
47832
  function detectRuntime() {
47357
47833
  const bin = (process.argv[1] ?? "").toLowerCase();
47358
47834
  if (bin.includes(".bun") || bin.includes("/bun/")) {
@@ -47393,7 +47869,7 @@ async function fetchLatestVersion() {
47393
47869
  }
47394
47870
  return body.version;
47395
47871
  }
47396
- async function action23(options) {
47872
+ async function action24(options) {
47397
47873
  let target;
47398
47874
  try {
47399
47875
  target = options.version ?? await fetchLatestVersion();
@@ -47425,7 +47901,7 @@ async function action23(options) {
47425
47901
  return;
47426
47902
  }
47427
47903
  }
47428
- const result = spawnSync3(runtime.command, runtime.args(target), {
47904
+ const result = spawnSync5(runtime.command, runtime.args(target), {
47429
47905
  stdio: "inherit"
47430
47906
  });
47431
47907
  if (result.status !== 0) {
@@ -47445,7 +47921,7 @@ async function action23(options) {
47445
47921
  }
47446
47922
  function registerSelfUpdate(program2) {
47447
47923
  const desc = "Upgrade Cerefox in place. Alias: `cerefox upgrade`.";
47448
- const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action23);
47924
+ const declaration = (cmd) => cmd.description(desc).option("--check", "Print current vs latest; do nothing.").option("--yes", "Non-interactive (skip confirmation).").option("--version <version>", "Pin a specific version (e.g. 0.5.1 or 0.6.0-rc.1).").action(action24);
47449
47925
  declaration(program2.command("self-update"));
47450
47926
  declaration(program2.command("upgrade"));
47451
47927
  }
@@ -47464,7 +47940,7 @@ function symbol2(status) {
47464
47940
  return cErr.dim("ℹ");
47465
47941
  }
47466
47942
  }
47467
- async function action24(options) {
47943
+ async function action25(options) {
47468
47944
  const useSpinner = !options.json && process.stderr.isTTY;
47469
47945
  const spinner = useSpinner ? ora({ text: "Starting checks…", spinner: "dots", stream: process.stderr }).start() : null;
47470
47946
  const results = await runFastChecks({
@@ -47485,7 +47961,7 @@ async function action24(options) {
47485
47961
  }
47486
47962
  }
47487
47963
  function registerStatus(program2) {
47488
- program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action24);
47964
+ program2.command("status").description("Quick sanity check (fast subset of `cerefox doctor`).").option("--json", "Emit machine-readable JSON.").action(action25);
47489
47965
  }
47490
47966
 
47491
47967
  // src/cli/commands/sync-docs.ts
@@ -47494,19 +47970,19 @@ init_mcp_tools();
47494
47970
  init_config();
47495
47971
  init_client();
47496
47972
  import {
47497
- existsSync as existsSync8,
47498
- readFileSync as readFileSync11,
47499
- readdirSync as readdirSync4,
47973
+ existsSync as existsSync11,
47974
+ readFileSync as readFileSync12,
47975
+ readdirSync as readdirSync6,
47500
47976
  statSync as statSync5
47501
47977
  } from "node:fs";
47502
- import { basename as basename5, extname as extname5, join as join9, relative } from "node:path";
47978
+ import { basename as basename5, extname as extname5, join as join10, relative } from "node:path";
47503
47979
  var ROOT_LEVEL_DOCS = ["README.md", "AGENT_GUIDE.md", "AGENT_QUICK_REFERENCE.md"];
47504
47980
  function walkMarkdown(dir) {
47505
47981
  const out = [];
47506
- if (!existsSync8(dir))
47982
+ if (!existsSync11(dir))
47507
47983
  return out;
47508
- for (const name of readdirSync4(dir)) {
47509
- const full = join9(dir, name);
47984
+ for (const name of readdirSync6(dir)) {
47985
+ const full = join10(dir, name);
47510
47986
  let stat;
47511
47987
  try {
47512
47988
  stat = statSync5(full);
@@ -47521,16 +47997,16 @@ function walkMarkdown(dir) {
47521
47997
  }
47522
47998
  return out;
47523
47999
  }
47524
- async function action25(options) {
48000
+ async function action26(options) {
47525
48001
  const cwd = process.cwd();
47526
48002
  const project = options.project ?? "cerefox";
47527
48003
  const targets = [];
47528
48004
  for (const rel of ROOT_LEVEL_DOCS) {
47529
- const abs = join9(cwd, rel);
47530
- if (existsSync8(abs))
48005
+ const abs = join10(cwd, rel);
48006
+ if (existsSync11(abs))
47531
48007
  targets.push({ abs, rel });
47532
48008
  }
47533
- for (const abs of walkMarkdown(join9(cwd, "docs"))) {
48009
+ for (const abs of walkMarkdown(join10(cwd, "docs"))) {
47534
48010
  targets.push({ abs, rel: relative(cwd, abs) });
47535
48011
  }
47536
48012
  if (targets.length === 0) {
@@ -47554,7 +48030,7 @@ async function action25(options) {
47554
48030
  const authorType = resolveAuthorType("agent");
47555
48031
  const outcomes = [];
47556
48032
  for (const t of targets) {
47557
- const content = readFileSync11(t.abs, "utf8");
48033
+ const content = readFileSync12(t.abs, "utf8");
47558
48034
  const title = basename5(t.abs, extname5(t.abs));
47559
48035
  try {
47560
48036
  const message = await ingestTool.handler(client.raw, {
@@ -47583,7 +48059,7 @@ async function action25(options) {
47583
48059
  }
47584
48060
  }
47585
48061
  function registerSyncDocs(program2) {
47586
- program2.command("sync-docs").description("Sync repo docs (README, AGENT_*, docs/) into a Cerefox project.").option("--dry-run", "Print what would be synced without writing.").option("-p, --project <name>", "Target project for the sync.", "cerefox").action(action25);
48062
+ program2.command("sync-docs").description("Sync repo docs (README, AGENT_*, docs/) into a Cerefox project.").option("--dry-run", "Print what would be synced without writing.").option("-p, --project <name>", "Target project for the sync.", "cerefox").action(action26);
47587
48063
  }
47588
48064
 
47589
48065
  // src/cli/program.ts
@@ -48918,8 +49394,8 @@ var _baseMimes = {
48918
49394
  var baseMimes = _baseMimes;
48919
49395
 
48920
49396
  // ../../node_modules/.bun/@hono+node-server@2.0.4+1bbe96acb4c5ebf1/node_modules/@hono/node-server/dist/serve-static.mjs
48921
- import { createReadStream, existsSync as existsSync9, statSync as statSync6 } from "node:fs";
48922
- import { join as join10 } from "node:path";
49397
+ import { createReadStream, existsSync as existsSync12, statSync as statSync6 } from "node:fs";
49398
+ import { join as join11 } from "node:path";
48923
49399
  var COMPRESSIBLE_CONTENT_TYPE_REGEX = /^\s*(?:text\/[^;\s]+|application\/(?:javascript|json|xml|xml-dtd|ecmascript|dart|postscript|rtf|tar|toml|vnd\.dart|vnd\.ms-fontobject|vnd\.ms-opentype|wasm|x-httpd-php|x-javascript|x-ns-proxy-autoconfig|x-sh|x-tar|x-virtualbox-hdd|x-virtualbox-ova|x-virtualbox-ovf|x-virtualbox-vbox|x-virtualbox-vdi|x-virtualbox-vhd|x-virtualbox-vmdk|x-www-form-urlencoded)|font\/(?:otf|ttf)|image\/(?:bmp|vnd\.adobe\.photoshop|vnd\.microsoft\.icon|vnd\.ms-dds|x-icon|x-ms-bmp)|message\/rfc822|model\/gltf-binary|x-shader\/x-fragment|x-shader\/x-vertex|[^;\s]+?\+(?:json|text|xml|yaml))(?:[;\s]|$)/i;
48924
49400
  var ENCODINGS = {
48925
49401
  br: ".br",
@@ -48951,7 +49427,7 @@ var tryDecodeURI = (str) => tryDecode(str, decodeURI);
48951
49427
  var serveStatic = (options = { root: "" }) => {
48952
49428
  const root = options.root || "";
48953
49429
  const optionPath = options.path;
48954
- if (root !== "" && !existsSync9(root))
49430
+ if (root !== "" && !existsSync12(root))
48955
49431
  console.error(`serveStatic: root path '${root}' is not found, are you sure it's correct?`);
48956
49432
  return async (c2, next) => {
48957
49433
  if (c2.finalized)
@@ -48968,11 +49444,11 @@ var serveStatic = (options = { root: "" }) => {
48968
49444
  await options.onNotFound?.(c2.req.path, c2);
48969
49445
  return next();
48970
49446
  }
48971
- let path = join10(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
49447
+ let path = join11(root, !optionPath && options.rewriteRequestPath ? options.rewriteRequestPath(filename, c2) : filename);
48972
49448
  let stats = getStats(path);
48973
49449
  if (stats && stats.isDirectory()) {
48974
49450
  const indexFile = options.index ?? "index.html";
48975
- path = join10(path, indexFile);
49451
+ path = join11(path, indexFile);
48976
49452
  stats = getStats(path);
48977
49453
  }
48978
49454
  if (!stats) {
@@ -49029,9 +49505,9 @@ var serveStatic = (options = { root: "" }) => {
49029
49505
  };
49030
49506
 
49031
49507
  // src/web/server.ts
49032
- import { existsSync as existsSync12 } from "node:fs";
49033
- import { readFileSync as readFileSync13 } from "node:fs";
49034
- import { join as join13 } from "node:path";
49508
+ import { existsSync as existsSync15 } from "node:fs";
49509
+ import { readFileSync as readFileSync14 } from "node:fs";
49510
+ import { join as join14 } from "node:path";
49035
49511
 
49036
49512
  // ../../node_modules/.bun/hono@4.12.23/node_modules/hono/dist/compose.js
49037
49513
  var compose = (middleware, onError, onNotFound) => {
@@ -51880,13 +52356,13 @@ import { execFileSync } from "node:child_process";
51880
52356
 
51881
52357
  // src/web/docs.ts
51882
52358
  import {
51883
- existsSync as existsSync10,
51884
- readFileSync as readFileSync12,
51885
- readdirSync as readdirSync5,
52359
+ existsSync as existsSync13,
52360
+ readFileSync as readFileSync13,
52361
+ readdirSync as readdirSync7,
51886
52362
  statSync as statSync7
51887
52363
  } from "node:fs";
51888
- import { basename as basename6, dirname as dirname4, join as join11, resolve as resolve5 } from "node:path";
51889
- import { fileURLToPath as fileURLToPath2 } from "node:url";
52364
+ import { basename as basename6, dirname as dirname5, join as join12, resolve as resolve5 } from "node:path";
52365
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
51890
52366
  var TOP_LEVEL_DOCS = [
51891
52367
  { filename: "README.md", path: "README.md", category: "readme" },
51892
52368
  {
@@ -51900,38 +52376,38 @@ var TOP_LEVEL_DOCS = [
51900
52376
  category: "agent-guide"
51901
52377
  }
51902
52378
  ];
51903
- function moduleDir() {
51904
- return dirname4(fileURLToPath2(import.meta.url));
52379
+ function moduleDir2() {
52380
+ return dirname5(fileURLToPath3(import.meta.url));
51905
52381
  }
51906
52382
  function resolveDocsRoots() {
51907
- const here = moduleDir();
52383
+ const here = moduleDir2();
51908
52384
  const pkgRootCandidates = [
51909
- join11(here, "..", ".."),
51910
- join11(here, "..", "..", "..", "..")
52385
+ join12(here, "..", ".."),
52386
+ join12(here, "..", "..", "..", "..")
51911
52387
  ];
51912
52388
  let pkgGuides = null;
51913
52389
  let pkgTopLevel = null;
51914
52390
  for (const pkg of pkgRootCandidates) {
51915
- const guides = join11(pkg, "docs", "guides");
51916
- if (existsSync10(guides) && statSync7(guides).isDirectory()) {
52391
+ const guides = join12(pkg, "docs", "guides");
52392
+ if (existsSync13(guides) && statSync7(guides).isDirectory()) {
51917
52393
  pkgGuides = guides;
51918
52394
  pkgTopLevel = pkg;
51919
52395
  break;
51920
52396
  }
51921
52397
  }
51922
- const repoCandidate = join11(here, "..", "..", "..", "..");
51923
- const repoGuides = join11(repoCandidate, "docs", "guides");
52398
+ const repoCandidate = join12(here, "..", "..", "..", "..");
52399
+ const repoGuides = join12(repoCandidate, "docs", "guides");
51924
52400
  const repoTopLevel = repoCandidate;
51925
52401
  return {
51926
52402
  pkgGuides,
51927
52403
  pkgTopLevel,
51928
- repoGuides: existsSync10(repoGuides) ? repoGuides : null,
51929
- repoTopLevel: existsSync10(join11(repoTopLevel, "README.md")) ? repoTopLevel : null
52404
+ repoGuides: existsSync13(repoGuides) ? repoGuides : null,
52405
+ repoTopLevel: existsSync13(join12(repoTopLevel, "README.md")) ? repoTopLevel : null
51930
52406
  };
51931
52407
  }
51932
52408
  function readH1(filePath) {
51933
52409
  try {
51934
- const content = readFileSync12(filePath, "utf8");
52410
+ const content = readFileSync13(filePath, "utf8");
51935
52411
  const match2 = content.match(/^#\s+(.+?)\s*$/m);
51936
52412
  return match2 ? match2[1] : null;
51937
52413
  } catch {
@@ -51951,17 +52427,17 @@ function listBundledDocs2() {
51951
52427
  const topRoot = pkgTopLevel ?? repoTopLevel;
51952
52428
  if (topRoot) {
51953
52429
  for (const t of TOP_LEVEL_DOCS) {
51954
- const abs = join11(topRoot, t.filename);
51955
- if (existsSync10(abs)) {
52430
+ const abs = join12(topRoot, t.filename);
52431
+ if (existsSync13(abs)) {
51956
52432
  entries.push(entryForFile(abs, t.path, t.category));
51957
52433
  }
51958
52434
  }
51959
52435
  }
51960
52436
  const guidesRoot = pkgGuides ?? repoGuides;
51961
52437
  if (guidesRoot) {
51962
- const names = readdirSync5(guidesRoot).filter((n) => n.endsWith(".md")).sort();
52438
+ const names = readdirSync7(guidesRoot).filter((n) => n.endsWith(".md")).sort();
51963
52439
  for (const name of names) {
51964
- const abs = join11(guidesRoot, name);
52440
+ const abs = join12(guidesRoot, name);
51965
52441
  entries.push(entryForFile(abs, `guides/${name}`, "guide"));
51966
52442
  }
51967
52443
  }
@@ -51975,9 +52451,9 @@ function readDoc(docPath) {
51975
52451
  if (!candidate.startsWith(resolve5(root) + "/") && candidate !== resolve5(root)) {
51976
52452
  continue;
51977
52453
  }
51978
- if (existsSync10(candidate) && statSync7(candidate).isFile()) {
52454
+ if (existsSync13(candidate) && statSync7(candidate).isFile()) {
51979
52455
  try {
51980
- return readFileSync12(candidate, "utf8");
52456
+ return readFileSync13(candidate, "utf8");
51981
52457
  } catch {
51982
52458
  return null;
51983
52459
  }
@@ -52023,17 +52499,17 @@ function registerMetaRoutes(app, ctx) {
52023
52499
  app.get("/api/v1/schema-version", async (c2) => {
52024
52500
  let bundled = null;
52025
52501
  try {
52026
- const { readFileSync: readFileSync13, existsSync: existsSync11 } = await import("node:fs");
52027
- const { fileURLToPath: fileURLToPath3 } = await import("node:url");
52028
- const { dirname: dirname5, join: join12 } = await import("node:path");
52029
- const here = dirname5(fileURLToPath3(import.meta.url));
52502
+ const { readFileSync: readFileSync14, existsSync: existsSync14 } = await import("node:fs");
52503
+ const { fileURLToPath: fileURLToPath4 } = await import("node:url");
52504
+ const { dirname: dirname6, join: join13 } = await import("node:path");
52505
+ const here = dirname6(fileURLToPath4(import.meta.url));
52030
52506
  const candidates = [
52031
- join12(here, "..", "..", "..", "db", "schema.sql"),
52032
- join12(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52507
+ join13(here, "..", "..", "..", "db", "schema.sql"),
52508
+ join13(here, "..", "..", "..", "..", "..", "src", "cerefox", "db", "schema.sql")
52033
52509
  ];
52034
52510
  for (const path of candidates) {
52035
- if (existsSync11(path)) {
52036
- const sql = readFileSync13(path, "utf8");
52511
+ if (existsSync14(path)) {
52512
+ const sql = readFileSync14(path, "utf8");
52037
52513
  const match2 = sql.match(SCHEMA_VERSION_RE);
52038
52514
  bundled = match2 ? match2[1] : null;
52039
52515
  break;
@@ -52071,7 +52547,14 @@ function registerMetaRoutes(app, ctx) {
52071
52547
  } catch {}
52072
52548
  }
52073
52549
  const mismatch = Boolean(bundled && deployed && bundled !== deployed);
52074
- return c2.json({ bundled, deployed, mismatch });
52550
+ const level = classifyCompat(deployed, COMPATIBILITY.minSchema, bundled);
52551
+ return c2.json({
52552
+ bundled,
52553
+ deployed,
52554
+ mismatch,
52555
+ level,
52556
+ min: COMPATIBILITY.minSchema
52557
+ });
52075
52558
  });
52076
52559
  }
52077
52560
 
@@ -52136,21 +52619,21 @@ function registerProjectsRoutes(app, ctx) {
52136
52619
  }
52137
52620
 
52138
52621
  // src/web/static.ts
52139
- import { existsSync as existsSync11, statSync as statSync8 } from "node:fs";
52140
- import { dirname as dirname5, join as join12 } from "node:path";
52141
- import { fileURLToPath as fileURLToPath3 } from "node:url";
52142
- function moduleDir2() {
52143
- return dirname5(fileURLToPath3(import.meta.url));
52622
+ import { existsSync as existsSync14, statSync as statSync8 } from "node:fs";
52623
+ import { dirname as dirname6, join as join13 } from "node:path";
52624
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
52625
+ function moduleDir3() {
52626
+ return dirname6(fileURLToPath4(import.meta.url));
52144
52627
  }
52145
52628
  function isUsableSpaDir(dir) {
52146
- return existsSync11(dir) && statSync8(dir).isDirectory() && existsSync11(join12(dir, "index.html"));
52629
+ return existsSync14(dir) && statSync8(dir).isDirectory() && existsSync14(join13(dir, "index.html"));
52147
52630
  }
52148
52631
  function resolveSpaDist() {
52149
- const here = moduleDir2();
52632
+ const here = moduleDir3();
52150
52633
  const candidates = [
52151
- join12(here, "..", "frontend"),
52152
- join12(here, "..", "..", "dist", "frontend"),
52153
- join12(here, "..", "..", "..", "..", "frontend", "dist")
52634
+ join13(here, "..", "frontend"),
52635
+ join13(here, "..", "..", "..", "..", "frontend", "dist"),
52636
+ join13(here, "..", "..", "dist", "frontend")
52154
52637
  ];
52155
52638
  for (const c2 of candidates) {
52156
52639
  if (isUsableSpaDir(c2))
@@ -52159,13 +52642,13 @@ function resolveSpaDist() {
52159
52642
  return null;
52160
52643
  }
52161
52644
  function resolveStaticDir() {
52162
- const here = moduleDir2();
52645
+ const here = moduleDir3();
52163
52646
  const candidates = [
52164
- join12(here, "..", "static"),
52165
- join12(here, "..", "..", "..", "..", "web", "static")
52647
+ join13(here, "..", "static"),
52648
+ join13(here, "..", "..", "..", "..", "web", "static")
52166
52649
  ];
52167
52650
  for (const c2 of candidates) {
52168
- if (existsSync11(c2) && statSync8(c2).isDirectory())
52651
+ if (existsSync14(c2) && statSync8(c2).isDirectory())
52169
52652
  return c2;
52170
52653
  }
52171
52654
  return null;
@@ -52207,6 +52690,8 @@ var ROOT_REDIRECT_HTML = `<!DOCTYPE html>
52207
52690
  </html>`;
52208
52691
 
52209
52692
  // src/web/server.ts
52693
+ init_meta();
52694
+ init_config();
52210
52695
  function buildApp(ctx = buildWebContext()) {
52211
52696
  const app = new Hono2;
52212
52697
  if (true) {
@@ -52246,18 +52731,52 @@ function buildApp(ctx = buildWebContext()) {
52246
52731
  root: spaDist,
52247
52732
  rewriteRequestPath: (path) => path.replace(/^\/app/, "") || "/"
52248
52733
  }));
52249
- const indexPath = join13(spaDist, "index.html");
52250
- if (existsSync12(indexPath)) {
52251
- const indexHtml = readFileSync13(indexPath, "utf8");
52734
+ const indexPath = join14(spaDist, "index.html");
52735
+ if (existsSync15(indexPath)) {
52736
+ const indexHtml = readFileSync14(indexPath, "utf8");
52252
52737
  app.get("/app/*", (c2) => c2.html(indexHtml));
52253
52738
  }
52254
52739
  }
52255
52740
  app.get("/", (c2) => c2.html(ROOT_REDIRECT_HTML));
52256
52741
  return app;
52257
52742
  }
52743
+
52744
+ class CompatibilityError extends Error {
52745
+ }
52746
+ async function assertServerCompatible() {
52747
+ const settings = loadSettings();
52748
+ if (!settings.supabaseUrl || !settings.supabaseAnonKey)
52749
+ return;
52750
+ let compat2;
52751
+ try {
52752
+ compat2 = await checkServerCompatibility({
52753
+ aggregatorUrl: aggregatorUrlFor(settings.supabaseUrl),
52754
+ bearer: settings.supabaseAnonKey,
52755
+ bundledEf: PKG_VERSION
52756
+ });
52757
+ } catch {
52758
+ return;
52759
+ }
52760
+ if (!compat2.blocking)
52761
+ return;
52762
+ const parts = [];
52763
+ if (compat2.schema.level === "below-min") {
52764
+ parts.push(` • schema v${compat2.schema.deployed} is below the required v${compat2.schema.min}`);
52765
+ }
52766
+ if (compat2.edgeFunctions.level === "below-min") {
52767
+ parts.push(` • Edge Functions v${compat2.edgeFunctions.deployed} are below the required v${compat2.edgeFunctions.min}`);
52768
+ }
52769
+ throw new CompatibilityError(`Refusing to start: the deployed Cerefox server is incompatible with this client (v${PKG_VERSION}).
52770
+ ` + parts.join(`
52771
+ `) + `
52772
+
52773
+ Redeploy your server: cerefox deploy-server
52774
+ ` + `(or downgrade the client to match the deployed server).`);
52775
+ }
52258
52776
  async function buildWebServer(options = {}) {
52259
52777
  const host = options.host ?? "127.0.0.1";
52260
52778
  const port = options.port ?? 8000;
52779
+ await assertServerCompatible();
52261
52780
  const app = buildApp();
52262
52781
  const server = serve({ fetch: app.fetch, hostname: host, port });
52263
52782
  return {
@@ -52269,31 +52788,263 @@ async function buildWebServer(options = {}) {
52269
52788
  };
52270
52789
  }
52271
52790
 
52791
+ // src/web/daemon.ts
52792
+ import { spawn } from "node:child_process";
52793
+ import {
52794
+ existsSync as existsSync16,
52795
+ mkdirSync as mkdirSync4,
52796
+ openSync,
52797
+ readFileSync as readFileSync15,
52798
+ rmSync,
52799
+ writeFileSync as writeFileSync4
52800
+ } from "node:fs";
52801
+ import { homedir as homedir7 } from "node:os";
52802
+ import { join as join15 } from "node:path";
52803
+ var STATE_DIR = join15(homedir7(), ".cerefox");
52804
+ var PID_FILE = join15(STATE_DIR, "web.pid");
52805
+ var LOG_FILE = join15(STATE_DIR, "web.log");
52806
+ var daemonPaths = { stateDir: STATE_DIR, pidFile: PID_FILE, logFile: LOG_FILE };
52807
+ function ensureStateDir() {
52808
+ if (!existsSync16(STATE_DIR))
52809
+ mkdirSync4(STATE_DIR, { recursive: true });
52810
+ }
52811
+ function readPidFile() {
52812
+ if (!existsSync16(PID_FILE))
52813
+ return null;
52814
+ try {
52815
+ const parsed = JSON.parse(readFileSync15(PID_FILE, "utf8"));
52816
+ if (typeof parsed.pid !== "number")
52817
+ return null;
52818
+ return {
52819
+ pid: parsed.pid,
52820
+ port: typeof parsed.port === "number" ? parsed.port : 8000,
52821
+ host: typeof parsed.host === "string" ? parsed.host : "127.0.0.1",
52822
+ startedAt: typeof parsed.startedAt === "string" ? parsed.startedAt : "unknown"
52823
+ };
52824
+ } catch {
52825
+ return null;
52826
+ }
52827
+ }
52828
+ function writePidFile(info3) {
52829
+ ensureStateDir();
52830
+ writeFileSync4(PID_FILE, JSON.stringify(info3, null, 2) + `
52831
+ `, "utf8");
52832
+ }
52833
+ function removePidFile() {
52834
+ rmSync(PID_FILE, { force: true });
52835
+ }
52836
+ function isProcessAlive(pid) {
52837
+ try {
52838
+ process.kill(pid, 0);
52839
+ return true;
52840
+ } catch (err) {
52841
+ return err.code === "EPERM";
52842
+ }
52843
+ }
52844
+ async function isResponding(host, port, timeoutMs = 1500) {
52845
+ const probeHost = host === "0.0.0.0" ? "127.0.0.1" : host;
52846
+ try {
52847
+ const ctrl = new AbortController;
52848
+ const timer2 = setTimeout(() => ctrl.abort(), timeoutMs);
52849
+ try {
52850
+ const resp = await fetch(`http://${probeHost}:${port}/api/v1/version`, {
52851
+ signal: ctrl.signal
52852
+ });
52853
+ return resp.ok;
52854
+ } finally {
52855
+ clearTimeout(timer2);
52856
+ }
52857
+ } catch {
52858
+ return false;
52859
+ }
52860
+ }
52861
+ var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
52862
+ function assertUnix() {
52863
+ if (process.platform === "win32") {
52864
+ throw new Error("Daemon mode (`cerefox web start/stop/status`) is not supported on Windows yet. " + "Run `cerefox web` in the foreground, or set up a Windows service manually.");
52865
+ }
52866
+ }
52867
+ async function startDaemon(opts) {
52868
+ assertUnix();
52869
+ ensureStateDir();
52870
+ const existing = readPidFile();
52871
+ if (existing && isProcessAlive(existing.pid)) {
52872
+ return existing.port === opts.port ? { kind: "already-running", info: existing } : { kind: "port-conflict", info: existing };
52873
+ }
52874
+ if (existing)
52875
+ removePidFile();
52876
+ const logFd = openSync(LOG_FILE, "a");
52877
+ const child = spawn(opts.runtime, [opts.scriptPath, "web", "--host", opts.host, "--port", String(opts.port)], { detached: true, stdio: ["ignore", logFd, logFd] });
52878
+ child.unref();
52879
+ if (typeof child.pid !== "number") {
52880
+ throw new Error("Failed to spawn the web server process (no pid).");
52881
+ }
52882
+ writePidFile({
52883
+ pid: child.pid,
52884
+ port: opts.port,
52885
+ host: opts.host,
52886
+ startedAt: new Date().toISOString()
52887
+ });
52888
+ let responding = false;
52889
+ for (let i = 0;i < 20; i++) {
52890
+ if (!isProcessAlive(child.pid))
52891
+ break;
52892
+ if (await isResponding(opts.host, opts.port)) {
52893
+ responding = true;
52894
+ break;
52895
+ }
52896
+ await sleep2(250);
52897
+ }
52898
+ return { kind: "started", pid: child.pid, responding };
52899
+ }
52900
+ async function stopDaemon() {
52901
+ assertUnix();
52902
+ const info3 = readPidFile();
52903
+ if (!info3 || !isProcessAlive(info3.pid)) {
52904
+ removePidFile();
52905
+ return { kind: "not-running" };
52906
+ }
52907
+ try {
52908
+ process.kill(info3.pid, "SIGTERM");
52909
+ } catch {}
52910
+ let forced = false;
52911
+ let alive = true;
52912
+ for (let i = 0;i < 12; i++) {
52913
+ await sleep2(250);
52914
+ if (!isProcessAlive(info3.pid)) {
52915
+ alive = false;
52916
+ break;
52917
+ }
52918
+ }
52919
+ if (alive) {
52920
+ try {
52921
+ process.kill(info3.pid, "SIGKILL");
52922
+ forced = true;
52923
+ } catch {}
52924
+ }
52925
+ removePidFile();
52926
+ return { kind: "stopped", pid: info3.pid, forced };
52927
+ }
52928
+ async function statusDaemon() {
52929
+ const info3 = readPidFile();
52930
+ if (!info3)
52931
+ return { kind: "stopped" };
52932
+ if (!isProcessAlive(info3.pid))
52933
+ return { kind: "stale", info: info3 };
52934
+ const responding = await isResponding(info3.host, info3.port);
52935
+ return { kind: "running", info: info3, responding };
52936
+ }
52937
+
52272
52938
  // src/cli/commands/web.ts
52939
+ function parsePort(raw2) {
52940
+ const port = Number.parseInt(raw2, 10);
52941
+ if (!Number.isFinite(port) || port < 1 || port > 65535) {
52942
+ eprintln(`Invalid --port: ${raw2}`);
52943
+ process.exit(2);
52944
+ }
52945
+ return port;
52946
+ }
52947
+ async function runForeground(host, port, watch) {
52948
+ if (watch) {
52949
+ info("--watch is reserved; not yet implemented.");
52950
+ }
52951
+ try {
52952
+ const handle = await buildWebServer({ host, port });
52953
+ info(`Cerefox web listening on http://${handle.host}:${handle.port}/`);
52954
+ println(` Web UI: http://${handle.host}:${handle.port}/app/`);
52955
+ println(` API: http://${handle.host}:${handle.port}/api/v1/`);
52956
+ const shutdown = async (signal) => {
52957
+ info(`Received ${signal}; shutting down.`);
52958
+ await handle.close().catch(() => {});
52959
+ process.exit(0);
52960
+ };
52961
+ process.on("SIGINT", () => void shutdown("SIGINT"));
52962
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
52963
+ } catch (err) {
52964
+ if (err instanceof CompatibilityError) {
52965
+ eprintln(err.message);
52966
+ } else {
52967
+ eprintln(`Failed to start web server: ${err instanceof Error ? err.message : String(err)}`);
52968
+ }
52969
+ process.exit(1);
52970
+ }
52971
+ }
52273
52972
  function registerWeb(program2) {
52274
- program2.command("web").description("Start the local web UI / API server.").option("--host <host>", "Bind host.", "127.0.0.1").option("--port <port>", "Bind port.", "8000").option("--watch", "Enable hot-reload (dev mode; reserved for v0.6.x).").action(async (rawOpts) => {
52275
- const port = Number.parseInt(rawOpts.port, 10);
52276
- if (!Number.isFinite(port) || port < 1 || port > 65535) {
52277
- eprintln(`Invalid --port: ${rawOpts.port}`);
52278
- process.exit(2);
52973
+ const web = program2.command("web").description("Start the local web UI / API server (foreground; see `web start` for daemon).").option("--host <host>", "Bind host.", "127.0.0.1").option("--port <port>", "Bind port.", "8000").option("--watch", "Enable hot-reload (dev mode; reserved).").action(async (rawOpts) => {
52974
+ await runForeground(rawOpts.host, parsePort(rawOpts.port), rawOpts.watch);
52975
+ });
52976
+ web.command("start").description("Start the web server in the background (detached daemon).").option("--host <host>", "Bind host.", "127.0.0.1").option("--port <port>", "Bind port.", "8000").action(async (rawOpts) => {
52977
+ const port = parsePort(rawOpts.port);
52978
+ try {
52979
+ const outcome = await startDaemon({
52980
+ host: rawOpts.host,
52981
+ port,
52982
+ scriptPath: process.argv[1],
52983
+ runtime: process.execPath
52984
+ });
52985
+ switch (outcome.kind) {
52986
+ case "already-running":
52987
+ info(`Cerefox web is already running on :${outcome.info.port} (pid ${outcome.info.pid}).`);
52988
+ process.exit(0);
52989
+ break;
52990
+ case "port-conflict":
52991
+ eprintln(`A Cerefox web daemon is already running on :${outcome.info.port} (pid ${outcome.info.pid}).
52992
+ ` + `Stop it first (\`cerefox web stop\`) or start on its port.`);
52993
+ process.exit(1);
52994
+ break;
52995
+ case "started":
52996
+ if (outcome.responding) {
52997
+ info(`Cerefox web started (pid ${outcome.pid}) on http://${rawOpts.host}:${port}/`);
52998
+ println(` Web UI: http://${rawOpts.host}:${port}/app/`);
52999
+ println(c.dim(` Logs: ${daemonPaths.logFile}`));
53000
+ println(c.dim(` Stop: cerefox web stop`));
53001
+ } else {
53002
+ eprintln(`Started (pid ${outcome.pid}) but it is not responding on :${port} yet.
53003
+ ` + `Check the log for errors: ${daemonPaths.logFile}`);
53004
+ process.exit(1);
53005
+ }
53006
+ break;
53007
+ }
53008
+ } catch (err) {
53009
+ eprintln(err instanceof Error ? err.message : String(err));
53010
+ process.exit(1);
52279
53011
  }
52280
- if (rawOpts.watch) {
52281
- info("--watch is reserved; not yet implemented in Part 24A.");
53012
+ });
53013
+ web.command("stop").description("Stop the background web server daemon.").action(async () => {
53014
+ try {
53015
+ const outcome = await stopDaemon();
53016
+ if (outcome.kind === "not-running") {
53017
+ info("No Cerefox web daemon is running.");
53018
+ } else {
53019
+ info(`Stopped Cerefox web (pid ${outcome.pid})${outcome.forced ? " — forced (SIGKILL)" : ""}.`);
53020
+ }
53021
+ } catch (err) {
53022
+ eprintln(err instanceof Error ? err.message : String(err));
53023
+ process.exit(1);
52282
53024
  }
53025
+ });
53026
+ web.command("status").description("Show the background web server daemon status.").action(async () => {
52283
53027
  try {
52284
- const handle = await buildWebServer({ host: rawOpts.host, port });
52285
- info(`Cerefox web listening on http://${handle.host}:${handle.port}/`);
52286
- println(` Web UI: http://${handle.host}:${handle.port}/app/`);
52287
- println(` API: http://${handle.host}:${handle.port}/api/v1/`);
52288
- const shutdown = async (signal) => {
52289
- info(`Received ${signal}; shutting down.`);
52290
- await handle.close().catch(() => {});
52291
- process.exit(0);
52292
- };
52293
- process.on("SIGINT", () => void shutdown("SIGINT"));
52294
- process.on("SIGTERM", () => void shutdown("SIGTERM"));
53028
+ const status = await statusDaemon();
53029
+ switch (status.kind) {
53030
+ case "stopped":
53031
+ println("Cerefox web: stopped (no daemon running).");
53032
+ break;
53033
+ case "stale":
53034
+ println(c.yellow(`Cerefox web: stale pidfile — process ${status.info.pid} is not running.`));
53035
+ println(c.dim(` Clean up: cerefox web stop (removes ${daemonPaths.pidFile})`));
53036
+ break;
53037
+ case "running":
53038
+ if (status.responding) {
53039
+ println(c.green(`Cerefox web: running on :${status.info.port} (pid ${status.info.pid}, since ${status.info.startedAt}).`));
53040
+ } else {
53041
+ println(c.yellow(`Cerefox web: process ${status.info.pid} alive but not responding on :${status.info.port}.`));
53042
+ println(c.dim(` Check the log: ${daemonPaths.logFile}`));
53043
+ }
53044
+ break;
53045
+ }
52295
53046
  } catch (err) {
52296
- eprintln(`Failed to start web server: ${err instanceof Error ? err.message : String(err)}`);
53047
+ eprintln(err instanceof Error ? err.message : String(err));
52297
53048
  process.exit(1);
52298
53049
  }
52299
53050
  });
@@ -52307,7 +53058,7 @@ Command groups (each row in the list above falls into one):
52307
53058
  ` + ` · list-metadata-keys · metadata-search · get-audit-log
52308
53059
  ` + ` WRITES ingest · ingest-dir · delete-doc
52309
53060
  ` + ` SERVERS mcp · web
52310
- ` + ` LIFECYCLE init · doctor · status · configure-agent · self-update · upgrade · sync-self-docs
53061
+ ` + ` LIFECYCLE init · doctor · status · configure-agent · self-update · upgrade · sync-self-docs · deploy-server
52311
53062
  ` + ` OPS backup · restore · sync-docs · docs · reindex · config-get · config-set · completion
52312
53063
  ` + `
52313
53064
  Exit codes:
@@ -52338,6 +53089,7 @@ Learn more:
52338
53089
  registerConfigureAgent(program2);
52339
53090
  registerSelfUpdate(program2);
52340
53091
  registerSyncSelfDocs(program2);
53092
+ registerDeployServer(program2);
52341
53093
  registerBackup(program2);
52342
53094
  registerRestore(program2);
52343
53095
  registerSyncDocs(program2);
@@ -52351,7 +53103,7 @@ Learn more:
52351
53103
 
52352
53104
  // src/bin/cerefox.ts
52353
53105
  async function bareEntryPoint() {
52354
- const { existsSync: existsSync13 } = await import("node:fs");
53106
+ const { existsSync: existsSync17 } = await import("node:fs");
52355
53107
  const { resolveEnvFile: resolveEnvFile2 } = await Promise.resolve().then(() => (init_config(), exports_config));
52356
53108
  const { c: c2, println: println2 } = await Promise.resolve().then(() => (init_cli_core(), exports_cli_core));
52357
53109
  const { PKG_VERSION: PKG_VERSION2 } = await Promise.resolve().then(() => (init_meta(), exports_meta));
@@ -52360,7 +53112,7 @@ async function bareEntryPoint() {
52360
53112
  println2("");
52361
53113
  let configExists = false;
52362
53114
  try {
52363
- configExists = existsSync13(resolveEnvFile2());
53115
+ configExists = existsSync17(resolveEnvFile2());
52364
53116
  } catch {}
52365
53117
  if (!configExists) {
52366
53118
  println2(c2.yellow("⚠ No config detected."));