@cerefox/memory 1.10.1 → 1.11.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.
@@ -7440,7 +7440,7 @@ var exports_meta = {};
7440
7440
  __export(exports_meta, {
7441
7441
  PKG_VERSION: () => PKG_VERSION
7442
7442
  });
7443
- var PKG_VERSION = "1.10.1";
7443
+ var PKG_VERSION = "1.11.0";
7444
7444
  var init_meta = () => {};
7445
7445
 
7446
7446
  // ../../_shared/config/paths.ts
@@ -7468,12 +7468,12 @@ function resolveConfigDir(opts = {}) {
7468
7468
  }
7469
7469
  const here = opts.cwd ?? processCwd();
7470
7470
  const cwdEnv = join(here, ".env");
7471
- if (existsSync(cwdEnv) && cwdEnvHasCerefoxKey(cwdEnv)) {
7471
+ if (existsSync(cwdEnv) && envFileHasCerefoxKey(cwdEnv)) {
7472
7472
  return resolvePath(here);
7473
7473
  }
7474
7474
  return userState;
7475
7475
  }
7476
- function cwdEnvHasCerefoxKey(envPath) {
7476
+ function envFileHasCerefoxKey(envPath) {
7477
7477
  try {
7478
7478
  const contents = readFileSync(envPath, "utf8");
7479
7479
  return /^\s*CEREFOX_[A-Z0-9_]+\s*=/m.test(contents);
@@ -7494,7 +7494,7 @@ function isDevMode(opts = {}) {
7494
7494
  return false;
7495
7495
  const here = opts.cwd ?? processCwd();
7496
7496
  const cwdEnv = join(here, ".env");
7497
- return existsSync(cwdEnv) && cwdEnvHasCerefoxKey(cwdEnv);
7497
+ return existsSync(cwdEnv) && envFileHasCerefoxKey(cwdEnv);
7498
7498
  }
7499
7499
  var USER_STATE_DIR_NAME = ".cerefox";
7500
7500
  var init_paths = () => {};
@@ -7565,6 +7565,7 @@ var init_env = __esm(() => {
7565
7565
  var exports_config = {};
7566
7566
  __export(exports_config, {
7567
7567
  USER_STATE_DIR_NAME: () => USER_STATE_DIR_NAME,
7568
+ envFileHasCerefoxKey: () => envFileHasCerefoxKey,
7568
7569
  isDevMode: () => isDevMode,
7569
7570
  loadEnv: () => loadEnv,
7570
7571
  loadSettings: () => loadSettings,
@@ -26002,7 +26003,7 @@ var init_bundled_docs = __esm(() => {
26002
26003
  });
26003
26004
 
26004
26005
  // ../../_shared/ef-meta/index.ts
26005
- var EF_VERSION = "1.10.1", CEREFOX_VERSION = "1.10.1", EF_LAST_CHANGED = "1.10.1";
26006
+ var EF_VERSION = "1.11.0", CEREFOX_VERSION = "1.11.0", EF_LAST_CHANGED = "1.11.0";
26006
26007
  var init_ef_meta = () => {};
26007
26008
 
26008
26009
  // ../../_shared/compatibility/index.ts
@@ -55096,9 +55097,13 @@ var init_audit_log = __esm(() => {
55096
55097
  };
55097
55098
  });
55098
55099
 
55100
+ // ../../_shared/mcp-tools/access-paths.ts
55101
+ var init_access_paths = () => {};
55102
+
55099
55103
  // ../../_shared/mcp-tools/types.ts
55100
55104
  var McpInvalidParams;
55101
55105
  var init_types3 = __esm(() => {
55106
+ init_access_paths();
55102
55107
  McpInvalidParams = class McpInvalidParams extends Error {
55103
55108
  constructor(message) {
55104
55109
  super(message);
@@ -77982,21 +77987,22 @@ function checkMcpConfigs() {
77982
77987
  detail: found.join(", ")
77983
77988
  };
77984
77989
  }
77985
- function checkLegacyShadowEnv() {
77986
- const home = homedir6();
77987
- const homeEnv = join9(home, USER_STATE_DIR_NAME, ".env");
77988
- const cwdEnv = join9(process.cwd(), ".env");
77989
- if (!existsSync10(homeEnv) || !existsSync10(cwdEnv))
77990
+ function checkLegacyShadowEnv(opts = {}) {
77991
+ const activeEnv = resolveEnvFile(opts);
77992
+ const cwdEnv = join9(opts.cwd ?? process.cwd(), ".env");
77993
+ if (!existsSync10(activeEnv) || !existsSync10(cwdEnv))
77990
77994
  return null;
77991
77995
  try {
77992
- if (realpathSync(homeEnv) === realpathSync(cwdEnv))
77996
+ if (realpathSync(activeEnv) === realpathSync(cwdEnv))
77993
77997
  return null;
77994
77998
  } catch {}
77999
+ if (!envFileHasCerefoxKey(cwdEnv))
78000
+ return null;
77995
78001
  return {
77996
78002
  name: "legacy env",
77997
78003
  status: "skipped",
77998
- detail: `${cwdEnv} (shadowed by ~/.cerefox/.env)`,
77999
- hint: "Shadowed by ~/.cerefox/.env and no longer read by anything (Python was removed at v1.0.0). Safe to delete."
78004
+ detail: `${cwdEnv} (shadowed by ${activeEnv})`,
78005
+ hint: `Shadowed by ${activeEnv} and no longer read by anything (Python was removed at v1.0.0). Safe to delete.`
78000
78006
  };
78001
78007
  }
78002
78008
  async function checkPostgres() {
@@ -84457,7 +84463,7 @@ var CONFIG_CATALOG = [
84457
84463
  },
84458
84464
  {
84459
84465
  key: "require_requestor_identity",
84460
- description: "Require a requestor/author on MCP tool calls.",
84466
+ description: "Require a requestor/author. Enforced on the Edge Functions (incl. remote MCP) ONLY — not local MCP, /api/v1 or the CLI.",
84461
84467
  kind: "boolean",
84462
84468
  defaultValue: "false",
84463
84469
  group: "Governance",
@@ -84569,6 +84575,78 @@ function validateConfigValue(key, value) {
84569
84575
 
84570
84576
  // src/web/routes/config.ts
84571
84577
  init_config();
84578
+
84579
+ // src/web/identity.ts
84580
+ var DEFAULT_WEB_AUTHOR = "web-ui";
84581
+ var DEFAULT_WEB_AUTHOR_TYPE = "user";
84582
+ var WEB_UI_IDENTITY = {
84583
+ author: DEFAULT_WEB_AUTHOR,
84584
+ authorType: DEFAULT_WEB_AUTHOR_TYPE,
84585
+ requestor: DEFAULT_WEB_AUTHOR,
84586
+ accessPath: "webapp",
84587
+ named: false
84588
+ };
84589
+ var HEADER = {
84590
+ author: "x-cerefox-author",
84591
+ authorType: "x-cerefox-author-type",
84592
+ requestor: "x-cerefox-requestor"
84593
+ };
84594
+ function readField(c2, headerName, body, bodyKey) {
84595
+ const header = c2.req.header(headerName);
84596
+ if (header !== undefined) {
84597
+ if (header.trim() === "") {
84598
+ return { ok: false, detail: `${headerName} must not be blank.` };
84599
+ }
84600
+ return { ok: true, value: header.trim() };
84601
+ }
84602
+ if (body && bodyKey in body) {
84603
+ const raw2 = body[bodyKey];
84604
+ if (raw2 === null || raw2 === undefined)
84605
+ return { ok: true, value: undefined };
84606
+ if (typeof raw2 !== "string") {
84607
+ return { ok: false, detail: `${bodyKey} must be a string.` };
84608
+ }
84609
+ if (raw2.trim() === "") {
84610
+ return { ok: false, detail: `${bodyKey} must not be blank.` };
84611
+ }
84612
+ return { ok: true, value: raw2.trim() };
84613
+ }
84614
+ return { ok: true, value: undefined };
84615
+ }
84616
+ function resolveCallerIdentity(c2, body) {
84617
+ const author = readField(c2, HEADER.author, body, "author");
84618
+ if (!author.ok)
84619
+ return author;
84620
+ const requestor = readField(c2, HEADER.requestor, body, "requestor");
84621
+ if (!requestor.ok)
84622
+ return requestor;
84623
+ const authorType = readField(c2, HEADER.authorType, body, "author_type");
84624
+ if (!authorType.ok)
84625
+ return authorType;
84626
+ if (authorType.value !== undefined && authorType.value !== "user" && authorType.value !== "agent") {
84627
+ return {
84628
+ ok: false,
84629
+ detail: `author_type must be "user" or "agent" (got ${JSON.stringify(authorType.value)}).`
84630
+ };
84631
+ }
84632
+ const named = author.value !== undefined || requestor.value !== undefined || authorType.value !== undefined;
84633
+ if (!named)
84634
+ return { ok: true, identity: WEB_UI_IDENTITY };
84635
+ const resolvedAuthor = author.value ?? requestor.value ?? DEFAULT_WEB_AUTHOR;
84636
+ const resolvedRequestor = requestor.value ?? author.value ?? DEFAULT_WEB_AUTHOR;
84637
+ return {
84638
+ ok: true,
84639
+ identity: {
84640
+ author: resolvedAuthor,
84641
+ authorType: authorType.value ?? DEFAULT_WEB_AUTHOR_TYPE,
84642
+ requestor: resolvedRequestor,
84643
+ accessPath: "api",
84644
+ named: true
84645
+ }
84646
+ };
84647
+ }
84648
+
84649
+ // src/web/routes/config.ts
84572
84650
  init__utils();
84573
84651
  function unwrapScalarRpc(data) {
84574
84652
  if (typeof data === "string")
@@ -84640,11 +84718,14 @@ function registerConfigRoutes(app, ctx) {
84640
84718
  const invalid = validateConfigValue(key, value);
84641
84719
  if (invalid)
84642
84720
  return c2.json({ detail: invalid }, 400);
84721
+ const who = resolveCallerIdentity(c2, body);
84722
+ if (!who.ok)
84723
+ return c2.json({ detail: who.detail }, 400);
84643
84724
  const { error: error3 } = await ctx.supabase.rpc("cerefox_set_config", {
84644
84725
  p_key: key,
84645
84726
  p_value: value,
84646
- p_author: "web-ui",
84647
- p_author_type: "user"
84727
+ p_author: who.identity.author,
84728
+ p_author_type: who.identity.authorType
84648
84729
  });
84649
84730
  if (error3) {
84650
84731
  const remediation = storeWriteRemediation(error3.message ?? "", "cerefox_set_config");
@@ -84662,8 +84743,8 @@ init__utils();
84662
84743
  function logWebUsage(ctx, params) {
84663
84744
  Promise.resolve(ctx.supabase.rpc("cerefox_log_usage", {
84664
84745
  p_operation: params.operation,
84665
- p_access_path: "webapp",
84666
- p_requestor: params.requestor ?? "web-ui",
84746
+ p_access_path: params.access_path ?? "webapp",
84747
+ p_requestor: params.requestor ?? DEFAULT_WEB_AUTHOR,
84667
84748
  p_document_id: params.document_id ?? null,
84668
84749
  p_project_id: params.project_id ?? null,
84669
84750
  p_query_text: params.query_text ?? null,
@@ -84989,7 +85070,16 @@ function registerDiscoveryRoutes(app, ctx) {
84989
85070
  results = results.filter((r) => statusMap.get(r.document_id) === reviewStatus);
84990
85071
  }
84991
85072
  }
84992
- logWebUsage(ctx, { operation: "search", query_text: q, result_count: results.length });
85073
+ const who = resolveCallerIdentity(c2);
85074
+ if (!who.ok)
85075
+ return c2.json({ detail: who.detail }, 400);
85076
+ logWebUsage(ctx, {
85077
+ operation: "search",
85078
+ query_text: q,
85079
+ result_count: results.length,
85080
+ requestor: who.identity.requestor,
85081
+ access_path: who.identity.accessPath
85082
+ });
84993
85083
  return c2.json({
84994
85084
  results,
84995
85085
  query: q,
@@ -85314,7 +85404,15 @@ function registerDocumentReadRoutes(app, ctx) {
85314
85404
  const meta = await getDocumentRow(ctx, documentId);
85315
85405
  const projectIds = await listDocumentProjectIds(ctx, documentId);
85316
85406
  const versions3 = await listDocumentVersions(ctx, documentId);
85317
- logWebUsage(ctx, { operation: "get-document", document_id: documentId });
85407
+ const who = resolveCallerIdentity(c2);
85408
+ if (!who.ok)
85409
+ return c2.json({ detail: who.detail }, 400);
85410
+ logWebUsage(ctx, {
85411
+ operation: "get-document",
85412
+ document_id: documentId,
85413
+ requestor: who.identity.requestor,
85414
+ access_path: who.identity.accessPath
85415
+ });
85318
85416
  return c2.json({
85319
85417
  document_id: documentId,
85320
85418
  full_content: doc2.full_content ?? "",
@@ -85451,6 +85549,9 @@ function registerDocumentWriteRoutes(app, ctx) {
85451
85549
  } catch {
85452
85550
  return c2.json({ success: false, error: "Invalid JSON body" }, 400);
85453
85551
  }
85552
+ const who = resolveCallerIdentity(c2, body);
85553
+ if (!who.ok)
85554
+ return c2.json({ detail: who.detail }, 400);
85454
85555
  const title = String(body.title ?? "").trim();
85455
85556
  const content = String(body.content ?? "");
85456
85557
  const projectIds = Array.isArray(body.project_ids) ? body.project_ids : [];
@@ -85491,8 +85592,8 @@ function registerDocumentWriteRoutes(app, ctx) {
85491
85592
  source: "manual",
85492
85593
  projectIds: Array.isArray(body.project_ids) ? body.project_ids : undefined,
85493
85594
  metadata: body.metadata != null ? metadata : undefined,
85494
- author: "web-ui",
85495
- authorType: "user",
85595
+ author: who.identity.author,
85596
+ authorType: who.identity.authorType,
85496
85597
  expectedContentHash: typeof body.expected_content_hash === "string" ? body.expected_content_hash : null
85497
85598
  });
85498
85599
  return c2.json({ success: true, reindexed: result.reindexed });
@@ -85537,9 +85638,9 @@ function registerDocumentWriteRoutes(app, ctx) {
85537
85638
  title: body.title !== undefined && title !== doc2.title ? title : undefined,
85538
85639
  metadata: body.metadata != null ? metadata : undefined,
85539
85640
  projectIds: Array.isArray(body.project_ids) ? projectIds : undefined,
85540
- author: "web-ui",
85541
- authorType: "user",
85542
- accessPath: "webapp"
85641
+ author: who.identity.author,
85642
+ authorType: who.identity.authorType,
85643
+ accessPath: who.identity.accessPath
85543
85644
  });
85544
85645
  return c2.json({ success: true, reindexed: false, ...facets });
85545
85646
  } catch (err) {
@@ -85556,10 +85657,20 @@ function registerDocumentWriteRoutes(app, ctx) {
85556
85657
  });
85557
85658
  app.delete("/api/v1/documents/:document_id", async (c2) => {
85558
85659
  const documentId = c2.req.param("document_id");
85660
+ const who = resolveCallerIdentity(c2);
85661
+ if (!who.ok)
85662
+ return c2.json({ detail: who.detail }, 400);
85663
+ const expectedHash = (c2.req.header("x-cerefox-expected-content-hash") ?? c2.req.query("expected_content_hash") ?? "").trim();
85664
+ if (who.identity.named && expectedHash === "") {
85665
+ return c2.json({
85666
+ detail: "CEREFOX_TOKEN_REQUIRED: an identified caller must send the content_hash it read, " + "as the X-Cerefox-Expected-Content-Hash header or an expected_content_hash query " + "parameter. A delete must follow a read."
85667
+ }, 400);
85668
+ }
85559
85669
  const { data, error: error3 } = await ctx.supabase.rpc("cerefox_delete_document", {
85560
85670
  p_document_id: documentId,
85561
- p_author: "web-ui",
85562
- p_author_type: "user"
85671
+ p_author: who.identity.author,
85672
+ p_author_type: who.identity.authorType,
85673
+ p_expected_content_hash: expectedHash === "" ? null : expectedHash
85563
85674
  });
85564
85675
  if (error3) {
85565
85676
  if (isDocumentNotFoundError(error3)) {
@@ -85572,10 +85683,13 @@ function registerDocumentWriteRoutes(app, ctx) {
85572
85683
  });
85573
85684
  app.post("/api/v1/documents/:document_id/restore", async (c2) => {
85574
85685
  const documentId = c2.req.param("document_id");
85686
+ const who = resolveCallerIdentity(c2);
85687
+ if (!who.ok)
85688
+ return c2.json({ detail: who.detail }, 400);
85575
85689
  const { data, error: error3 } = await ctx.supabase.rpc("cerefox_restore_document", {
85576
85690
  p_document_id: documentId,
85577
- p_author: "web-ui",
85578
- p_author_type: "user"
85691
+ p_author: who.identity.author,
85692
+ p_author_type: who.identity.authorType
85579
85693
  });
85580
85694
  if (error3) {
85581
85695
  if (isDocumentNotFoundError(error3)) {
@@ -85588,10 +85702,13 @@ function registerDocumentWriteRoutes(app, ctx) {
85588
85702
  });
85589
85703
  app.delete("/api/v1/documents/:document_id/purge", async (c2) => {
85590
85704
  const documentId = c2.req.param("document_id");
85705
+ const who = resolveCallerIdentity(c2);
85706
+ if (!who.ok)
85707
+ return c2.json({ detail: who.detail }, 400);
85591
85708
  const { error: error3 } = await ctx.supabase.rpc("cerefox_purge_document", {
85592
85709
  p_document_id: documentId,
85593
- p_author: "web-ui",
85594
- p_author_type: "user"
85710
+ p_author: who.identity.author,
85711
+ p_author_type: who.identity.authorType
85595
85712
  });
85596
85713
  if (error3)
85597
85714
  return c2.json({ detail: error3.message }, 500);
@@ -85605,6 +85722,9 @@ function registerDocumentWriteRoutes(app, ctx) {
85605
85722
  } catch {
85606
85723
  return c2.json({ detail: "Invalid JSON body" }, 400);
85607
85724
  }
85725
+ const who = resolveCallerIdentity(c2, body);
85726
+ if (!who.ok)
85727
+ return c2.json({ detail: who.detail }, 400);
85608
85728
  const status = body.status;
85609
85729
  if (status !== "approved" && status !== "pending_review") {
85610
85730
  return c2.json({ detail: `Invalid status: ${JSON.stringify(status)}` }, 400);
@@ -85619,7 +85739,8 @@ function registerDocumentWriteRoutes(app, ctx) {
85619
85739
  return c2.json({ detail: error3.message }, 500);
85620
85740
  await createAuditEntry(ctx, {
85621
85741
  operation: "status-change",
85622
- author: "user",
85742
+ author: who.identity.author,
85743
+ authorType: who.identity.authorType,
85623
85744
  documentId,
85624
85745
  description: `Review status changed from '${oldStatus}' to '${status}'`
85625
85746
  });
@@ -85634,6 +85755,9 @@ function registerDocumentWriteRoutes(app, ctx) {
85634
85755
  } catch {
85635
85756
  return c2.json({ detail: "Invalid JSON body" }, 400);
85636
85757
  }
85758
+ const who = resolveCallerIdentity(c2, body);
85759
+ if (!who.ok)
85760
+ return c2.json({ detail: who.detail }, 400);
85637
85761
  const archived = Boolean(body.archived);
85638
85762
  const { data, error: error3 } = await ctx.supabase.from("cerefox_document_versions").update({ archived }).eq("id", versionId).select("document_id, version_number").maybeSingle();
85639
85763
  if (error3)
@@ -85642,7 +85766,8 @@ function registerDocumentWriteRoutes(app, ctx) {
85642
85766
  const op = archived ? "archive" : "unarchive";
85643
85767
  await createAuditEntry(ctx, {
85644
85768
  operation: op,
85645
- author: "user",
85769
+ author: who.identity.author,
85770
+ authorType: who.identity.authorType,
85646
85771
  documentId: ver?.document_id ?? documentId,
85647
85772
  versionId,
85648
85773
  description: `Version ${ver?.version_number ?? "?"} ${op}d`
@@ -85671,6 +85796,9 @@ function registerIngestRoutes(app, ctx) {
85671
85796
  return c2.json(notReady("Title is required."), 200);
85672
85797
  if (!content.trim())
85673
85798
  return c2.json(notReady("Content cannot be empty."), 200);
85799
+ const who = resolveCallerIdentity(c2, body);
85800
+ if (!who.ok)
85801
+ return c2.json({ detail: who.detail }, 400);
85674
85802
  try {
85675
85803
  const pipeline2 = new IngestionPipeline({
85676
85804
  supabase: ctx.supabase,
@@ -85684,8 +85812,8 @@ function registerIngestRoutes(app, ctx) {
85684
85812
  metadata: body.metadata ?? null,
85685
85813
  updateExisting: Boolean(body.update_existing),
85686
85814
  documentId: body.document_id ?? null,
85687
- author: "web-ui",
85688
- authorType: "user"
85815
+ author: who.identity.author,
85816
+ authorType: who.identity.authorType
85689
85817
  });
85690
85818
  const skipped = result.action === "skipped";
85691
85819
  const resp = {
@@ -85697,7 +85825,12 @@ function registerIngestRoutes(app, ctx) {
85697
85825
  };
85698
85826
  if (result.note)
85699
85827
  resp.note = result.note;
85700
- logWebUsage(ctx, { operation: "ingest", document_id: result.documentId });
85828
+ logWebUsage(ctx, {
85829
+ operation: "ingest",
85830
+ document_id: result.documentId,
85831
+ requestor: who.identity.requestor,
85832
+ access_path: who.identity.accessPath
85833
+ });
85701
85834
  return c2.json(resp, 200);
85702
85835
  } catch (err) {
85703
85836
  return c2.json(notReady(err instanceof Error ? err.message : String(err)), 200);
@@ -85736,6 +85869,9 @@ function registerIngestRoutes(app, ctx) {
85736
85869
  metadata = null;
85737
85870
  }
85738
85871
  }
85872
+ const who = resolveCallerIdentity(c2, form);
85873
+ if (!who.ok)
85874
+ return c2.json({ detail: who.detail }, 400);
85739
85875
  try {
85740
85876
  const pipeline2 = new IngestionPipeline({
85741
85877
  supabase: ctx.supabase,
@@ -85749,11 +85885,16 @@ function registerIngestRoutes(app, ctx) {
85749
85885
  projectIds,
85750
85886
  metadata,
85751
85887
  updateExisting,
85752
- author: "web-ui",
85753
- authorType: "user"
85888
+ author: who.identity.author,
85889
+ authorType: who.identity.authorType
85754
85890
  });
85755
85891
  const skipped = result.action === "skipped";
85756
- logWebUsage(ctx, { operation: "ingest", document_id: result.documentId });
85892
+ logWebUsage(ctx, {
85893
+ operation: "ingest",
85894
+ document_id: result.documentId,
85895
+ requestor: who.identity.requestor,
85896
+ access_path: who.identity.accessPath
85897
+ });
85757
85898
  return c2.json({
85758
85899
  success: !skipped,
85759
85900
  document_id: result.documentId,
@@ -85790,6 +85931,9 @@ function registerIngestRoutes(app, ctx) {
85790
85931
  if (!existing)
85791
85932
  return c2.json(notReady("Document not found"), 404);
85792
85933
  const title = existing.title || file.name || "Untitled";
85934
+ const who = resolveCallerIdentity(c2, form);
85935
+ if (!who.ok)
85936
+ return c2.json({ detail: who.detail }, 400);
85793
85937
  try {
85794
85938
  const pipeline2 = new IngestionPipeline({
85795
85939
  supabase: ctx.supabase,
@@ -85800,10 +85944,17 @@ function registerIngestRoutes(app, ctx) {
85800
85944
  text,
85801
85945
  title,
85802
85946
  source: "file",
85803
- author: "web-ui",
85804
- authorType: "user"
85947
+ author: who.identity.author,
85948
+ authorType: who.identity.authorType,
85949
+ expectedContentHash: typeof form.expected_content_hash === "string" && form.expected_content_hash.trim() !== "" ? form.expected_content_hash.trim() : null,
85950
+ lastWriteWins: String(form.last_write_wins ?? "") === "true"
85951
+ });
85952
+ logWebUsage(ctx, {
85953
+ operation: "ingest",
85954
+ document_id: result.documentId,
85955
+ requestor: who.identity.requestor,
85956
+ access_path: who.identity.accessPath
85805
85957
  });
85806
- logWebUsage(ctx, { operation: "ingest", document_id: result.documentId });
85807
85958
  return c2.json({
85808
85959
  success: true,
85809
85960
  document_id: result.documentId,
@@ -86154,11 +86305,14 @@ function registerProjectsRoutes(app, ctx) {
86154
86305
  if (!name)
86155
86306
  return c2.json({ detail: "Project name is required" }, 400);
86156
86307
  const description = String(body.description ?? "").trim();
86308
+ const who = resolveCallerIdentity(c2, body);
86309
+ if (!who.ok)
86310
+ return c2.json({ detail: who.detail }, 400);
86157
86311
  const { data, error: error3 } = await ctx.supabase.rpc("cerefox_create_project", {
86158
86312
  p_name: name,
86159
86313
  p_description: description,
86160
- p_author: "web-ui",
86161
- p_author_type: "user"
86314
+ p_author: who.identity.author,
86315
+ p_author_type: who.identity.authorType
86162
86316
  });
86163
86317
  if (error3) {
86164
86318
  const msg = error3.message ?? "";
@@ -86194,12 +86348,15 @@ function registerProjectsRoutes(app, ctx) {
86194
86348
  if (Object.keys(update).length === 0) {
86195
86349
  return c2.json({ detail: "Nothing to update — pass name and/or description" }, 400);
86196
86350
  }
86351
+ const who = resolveCallerIdentity(c2, body);
86352
+ if (!who.ok)
86353
+ return c2.json({ detail: who.detail }, 400);
86197
86354
  const { data, error: error3 } = await ctx.supabase.rpc("cerefox_update_project", {
86198
86355
  p_project_id: projectId,
86199
86356
  p_name: update.name ?? null,
86200
86357
  p_description: update.description ?? null,
86201
- p_author: "web-ui",
86202
- p_author_type: "user"
86358
+ p_author: who.identity.author,
86359
+ p_author_type: who.identity.authorType
86203
86360
  });
86204
86361
  if (error3) {
86205
86362
  const msg = error3.message ?? "";
@@ -86217,10 +86374,13 @@ function registerProjectsRoutes(app, ctx) {
86217
86374
  });
86218
86375
  app.delete("/api/v1/projects/:project_id", async (c2) => {
86219
86376
  const projectId = c2.req.param("project_id");
86377
+ const who = resolveCallerIdentity(c2);
86378
+ if (!who.ok)
86379
+ return c2.json({ detail: who.detail }, 400);
86220
86380
  const { data, error: error3 } = await ctx.supabase.rpc("cerefox_delete_project", {
86221
86381
  p_project_id: projectId,
86222
- p_author: "web-ui",
86223
- p_author_type: "user"
86382
+ p_author: who.identity.author,
86383
+ p_author_type: who.identity.authorType
86224
86384
  });
86225
86385
  if (error3)
86226
86386
  return c2.json({ detail: error3.message }, 500);