@promptowl/contextnest-community 1.25.0 → 1.27.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 (121) hide show
  1. package/API.md +102 -9
  2. package/CONFIGURATION.md +604 -117
  3. package/README.md +3 -2
  4. package/dist/{chunk-JDAOA5KP.js → chunk-H3W72HN3.js} +67 -14
  5. package/dist/{chunk-2KHM5C7V.js → chunk-MB52DWWP.js} +13 -15
  6. package/dist/{chunk-TI7HMP64.js → chunk-NFFUZF2B.js} +23 -2
  7. package/dist/{chunk-F66VCBLA.js → chunk-O3JEI33N.js} +51 -2
  8. package/dist/{chunk-7FROMOZM.js → chunk-QMQDWRHN.js} +180 -550
  9. package/dist/chunk-RXCVXG34.js +523 -0
  10. package/dist/{chunk-32SZCH36.js → chunk-SE5U4J4T.js} +4 -4
  11. package/dist/{chunk-KPGNZLZL.js → chunk-VHEMGQ3I.js} +1 -1
  12. package/dist/{chunk-DTLMBF4W.js → chunk-VRLAP4HB.js} +42 -17
  13. package/dist/{engine-TER6FDBP.js → engine-HLYQLP5T.js} +3 -2
  14. package/dist/{external-edit-service-CAGENHDJ.js → external-edit-service-3DFQ4EZZ.js} +4 -3
  15. package/dist/{grants-service-2OTFS7EH.js → grants-service-WICV4Y2J.js} +2 -2
  16. package/dist/index.js +1669 -521
  17. package/dist/invited-user-XEBX6BM4.js +12 -0
  18. package/dist/{migrations.postgres-23VJJJJX.js → migrations.postgres-HGSQ7IA5.js} +4 -0
  19. package/dist/{review-service-4G2XUFS6.js → review-service-GB43UQVH.js} +7 -6
  20. package/dist/{stewardship-service-S22V2JKG.js → stewardship-service-KDBPWRZI.js} +4 -3
  21. package/dist/{version-service-X4FAG2OZ.js → version-service-JYHFPSYP.js} +8 -3
  22. package/dist/web3/assets/ActivityTracePage-CkRoyIc-.js +1 -0
  23. package/dist/web3/assets/AgentDocsPage-BTRXmliv.js +1 -0
  24. package/dist/web3/assets/CollaboratorManager-CqJIkvs-.js +1 -0
  25. package/dist/web3/assets/CollaboratorsTab-JcO1P-qm.js +1 -0
  26. package/dist/web3/assets/DocumentEditor-D1GLeeEP.js +36 -0
  27. package/dist/web3/assets/DocumentsTab-BAgSxTX1.js +6 -0
  28. package/dist/web3/assets/ExternalEditsTab-DuuTXZap.js +1 -0
  29. package/dist/web3/assets/{MarkdownEditor-Dfk5uIE5.js → MarkdownEditor-BELFww9q.js} +130 -135
  30. package/dist/web3/assets/MarkdownEditor-ndtIx5x0.css +1 -0
  31. package/dist/web3/assets/NestPageHeader-Bbje2ncY.js +1 -0
  32. package/dist/web3/assets/NestView-D4OMXPQ7.js +73 -0
  33. package/dist/web3/assets/OverviewTab-FIDGjR3Q.js +6 -0
  34. package/dist/web3/assets/PersonCombobox-D52m_jhc.js +1 -0
  35. package/dist/web3/assets/{ReviewActions-BXDTDPp1.js → ReviewActions-DYqBcWlv.js} +2 -2
  36. package/dist/web3/assets/ReviewTab-DoxfygOb.js +1 -0
  37. package/dist/web3/assets/StewardsTab-CFsYjbEn.js +1 -0
  38. package/dist/web3/assets/SubmitForReviewModal-AW2OLNAE.js +1 -0
  39. package/dist/web3/assets/{alert-dialog-4SiLKlz8.js → alert-dialog-p-fBLSWx.js} +2 -2
  40. package/dist/web3/assets/{arrow-left-BAgIocLn.js → arrow-left-CiErCCTG.js} +1 -1
  41. package/dist/web3/assets/backlinks-DaVSYvhV.js +19 -0
  42. package/dist/web3/assets/boxes-VWrCCMzW.js +6 -0
  43. package/dist/web3/assets/{chevron-left-XR4ReJ9Z.js → chevron-left-CsD8gZwL.js} +1 -1
  44. package/dist/web3/assets/{circle-check-CSkZUEFK.js → circle-check-BBCXd9LI.js} +1 -1
  45. package/dist/web3/assets/{circle-x-in2o3aHU.js → circle-x-C9LsnORQ.js} +1 -1
  46. package/dist/web3/assets/{code-xml-pPQgdctI.js → code-xml-BaoV7HiQ.js} +1 -1
  47. package/dist/web3/assets/{corner-down-right-CuxZWscX.js → corner-down-right-DgiTXrtw.js} +1 -1
  48. package/dist/web3/assets/count-skeleton-CnEE4uF-.js +1 -0
  49. package/dist/web3/assets/{earth-CXOcThqn.js → earth-BmMbVt9k.js} +1 -1
  50. package/dist/web3/assets/{file-exclamation-point-CSphs1VD.js → file-exclamation-point-qJeca9-A.js} +1 -1
  51. package/dist/web3/assets/{folder-input-BAwK9tFL.js → folder-input-D7R93LJ6.js} +1 -1
  52. package/dist/web3/assets/index-DNmWTxN7.css +1 -0
  53. package/dist/web3/assets/index-DvuUIUP6.js +451 -0
  54. package/dist/web3/assets/{page-CUjuSs5Q.js → page-BJQuaTRW.js} +3 -3
  55. package/dist/web3/assets/page-Bb8xGOPQ.js +2 -0
  56. package/dist/web3/assets/page-BtM-qDzr.js +6 -0
  57. package/dist/web3/assets/page-Bxd0EG25.js +1 -0
  58. package/dist/web3/assets/page-C3QCO6IR.js +1 -0
  59. package/dist/web3/assets/page-CfoR0khy.js +6 -0
  60. package/dist/web3/assets/page-D7Zr5tAI.js +1 -0
  61. package/dist/web3/assets/page-DATSlHCW.js +6 -0
  62. package/dist/web3/assets/page-DTjPQizZ.js +45 -0
  63. package/dist/web3/assets/page-DspptlWr.js +1 -0
  64. package/dist/web3/assets/{page-xzYqkBUP.js → page-I6vAFJOj.js} +5 -5
  65. package/dist/web3/assets/page-QxI4Ud2s.js +9 -0
  66. package/dist/web3/assets/page-YcNNg-dX.js +1 -0
  67. package/dist/web3/assets/page-kM7eO5Kj.js +1 -0
  68. package/dist/web3/assets/{page-title-BkqlUA0j.js → page-title-Dn5jANiU.js} +1 -1
  69. package/dist/web3/assets/{play-B8xE9j5f.js → play-B2k_Ix-L.js} +1 -1
  70. package/dist/web3/assets/{send-CvAgOUPG.js → send-DPNxv3As.js} +1 -1
  71. package/dist/web3/assets/{settings-EY4A_DYv.js → settings-DZjq-OQA.js} +1 -1
  72. package/dist/web3/assets/{share-2-vGmZUl90.js → share-2-C-OnE2ru.js} +1 -1
  73. package/dist/web3/assets/{tag-Br_y1f00.js → tag-CwJl7FdI.js} +1 -1
  74. package/dist/web3/assets/{trash-2-B4hRXo-p.js → trash-2-aljmIff4.js} +1 -1
  75. package/dist/web3/assets/{user-plus-4CTYEkd3.js → user-plus-DPerZexX.js} +1 -1
  76. package/dist/web3/assets/{zap-BcsKR0ef.js → zap-CHZtiN7P.js} +1 -1
  77. package/dist/web3/index.html +2 -2
  78. package/package.json +8 -3
  79. package/dist/client-D44ZHDTJ.js +0 -11
  80. package/dist/keys-CTOGAG3W.js +0 -20
  81. package/dist/web3/assets/ActivityTracePage-D2iIbEph.js +0 -1
  82. package/dist/web3/assets/AgentDocsPage-Dkhgb1hk.js +0 -1
  83. package/dist/web3/assets/CollaboratorManager-Bo81D--Q.js +0 -1
  84. package/dist/web3/assets/CollaboratorsTab-DRCitQuX.js +0 -1
  85. package/dist/web3/assets/DocumentEditor-DV2WHYbj.js +0 -36
  86. package/dist/web3/assets/DocumentsTab-BUxBCN8o.js +0 -6
  87. package/dist/web3/assets/ExternalEditsTab-kIBimZyE.js +0 -1
  88. package/dist/web3/assets/MarkdownEditor-CsVusP2d.css +0 -1
  89. package/dist/web3/assets/NestPageHeader-yOK-OIYZ.js +0 -1
  90. package/dist/web3/assets/NestView-DwIagxwJ.js +0 -68
  91. package/dist/web3/assets/OverviewTab-DFOFMVyE.js +0 -1
  92. package/dist/web3/assets/PersonCombobox-D350WlrJ.js +0 -1
  93. package/dist/web3/assets/ReasonDialog-CN3ECAnQ.js +0 -1
  94. package/dist/web3/assets/ReviewTab-CZewAYiz.js +0 -1
  95. package/dist/web3/assets/StewardsTab-AagNWcWS.js +0 -1
  96. package/dist/web3/assets/SubmitForReviewModal-BOZJxwRN.js +0 -1
  97. package/dist/web3/assets/backlinks-Cb8Uf8mw.js +0 -24
  98. package/dist/web3/assets/card-UDyMRcps.js +0 -1
  99. package/dist/web3/assets/count-skeleton-DBWCDhRy.js +0 -1
  100. package/dist/web3/assets/dates-BCxbm4_q.js +0 -1
  101. package/dist/web3/assets/index-A0_ymcqL.js +0 -29
  102. package/dist/web3/assets/index-B5hLclEq.js +0 -389
  103. package/dist/web3/assets/index-DrUAtoQM.css +0 -1
  104. package/dist/web3/assets/page-BHD4beun.js +0 -1
  105. package/dist/web3/assets/page-BSYUXYmO.js +0 -11
  106. package/dist/web3/assets/page-BYc2DIux.js +0 -45
  107. package/dist/web3/assets/page-BfibLg8e.js +0 -1
  108. package/dist/web3/assets/page-CGe8MtUu.js +0 -9
  109. package/dist/web3/assets/page-CIgPJmG6.js +0 -2
  110. package/dist/web3/assets/page-Ck31nx9b.js +0 -1
  111. package/dist/web3/assets/page-Cpdj_11Y.js +0 -1
  112. package/dist/web3/assets/page-D0sN3GO6.js +0 -1
  113. package/dist/web3/assets/page-DEpH91X2.js +0 -1
  114. package/dist/web3/assets/page-l8-B7CGt.js +0 -1
  115. package/dist/web3/assets/page-mnSHmk6A.js +0 -6
  116. package/dist/web3/assets/refresh-cw-CsCtmWwX.js +0 -6
  117. package/dist/web3/assets/scroll-area-CbcH6yrZ.js +0 -1
  118. package/dist/web3/assets/scroll-area-dRWncRqa.css +0 -1
  119. package/dist/web3/assets/select-DrL0mpRN.js +0 -6
  120. package/dist/web3/assets/triangle-alert-CsYGyfGZ.js +0 -6
  121. package/dist/web3/assets/x-qxt1WrUi.js +0 -6
package/README.md CHANGED
@@ -150,7 +150,7 @@ Keys are minted in the app (Workspace → API keys, or the Connect dialog) — o
150
150
  | Activity trace — every governance action, per nest and server-wide | ✅ | ✅ |
151
151
  | Notifications — Slack, Microsoft Teams, email | ✅ | ✅ |
152
152
  | Centralized multi-tenant admin console | — | ✅ |
153
- | Single sign-on (OIDC — Entra ID, Google, Okta, Keycloak) | ✅ | ✅ |
153
+ | Single sign-on (OIDC — Entra ID, Google, Okta, Keycloak) — [admin guide](./CONFIGURATION.md#single-sign-on-oidc) | ✅ | ✅ |
154
154
  | SAML / SCIM provisioning | — | ✅ |
155
155
  | Audit log streaming | — | ✅ |
156
156
  | Policy transforms (redaction, summarization) | — | ✅ |
@@ -165,6 +165,7 @@ Release notes live in [CHANGELOG.md](./CHANGELOG.md).
165
165
  ## Documentation
166
166
 
167
167
  - [CONFIGURATION.md](./CONFIGURATION.md) — every environment variable (port, auth mode, storage, notifications, telemetry)
168
+ - [Single sign-on admin guide](./CONFIGURATION.md#single-sign-on-oidc) — connect Microsoft Entra ID, Google, Okta, Keycloak or any OIDC provider; what happens to existing accounts, API keys and agents; every `sso_error` code with its fix
168
169
  - [API.md](./API.md) — complete REST reference with request and response bodies
169
170
  - [STEWARDSHIP.md](./STEWARDSHIP.md) — the governance model: modes, stewards, scopes, prime documents, teams, super-admins
170
171
  - `<server>/llms.txt` — the agent manual the running server publishes; rendered as **Docs** in the app
@@ -217,4 +218,4 @@ The Software injects content into large language models. AI output may be inaccu
217
218
  **Copyright © 2026 Promptowl LLC.** All rights reserved.
218
219
  "ContextNest" and "PromptOwl" are trademarks of Promptowl LLC.
219
220
 
220
- Promptowl LLC · 3060 Mercer University Dr Ste 110 · Atlanta, GA 30341 · USA
221
+ Promptowl LLC · 3060 Mercer University Dr Ste 110 · Atlanta, GA 30341 · USA
@@ -2,11 +2,6 @@ import {
2
2
  ANON_USER_ID
3
3
  } from "./chunk-YB3LKF7U.js";
4
4
 
5
- // src/db/client.ts
6
- import Database from "better-sqlite3";
7
- import { mkdirSync, readFileSync } from "fs";
8
- import { dirname as dirname2 } from "path";
9
-
10
5
  // src/config.ts
11
6
  import { join, dirname } from "path";
12
7
  import { existsSync } from "fs";
@@ -356,6 +351,14 @@ var config = {
356
351
  get PEOPLE_SUGGEST_ALL_USERS() {
357
352
  return process.env.PEOPLE_SUGGEST_ALL_USERS === "true";
358
353
  },
354
+ /**
355
+ * Who may create nests. "admins" (default): server admins only, until the
356
+ * server is on a paid plan. "everyone": any signed-in user. Open mode is
357
+ * unaffected (everyone is admin there).
358
+ */
359
+ get NEST_CREATION() {
360
+ return process.env.NEST_CREATION?.trim().toLowerCase() === "everyone" ? "everyone" : "admins";
361
+ },
359
362
  /**
360
363
  * This server hosts several unrelated organizations (the PromptOwl-hosted
361
364
  * deployment) rather than one company (a self-hosted install).
@@ -498,6 +501,34 @@ var config = {
498
501
  get TYPE_TABLE_ENABLED() {
499
502
  return process.env.TYPE_TABLE_ENABLED !== "false";
500
503
  },
504
+ /**
505
+ * Hootie in the nest: the floating assistant on every page of the UI and
506
+ * its POST /nests/:id/chat route (default ON). Superadmin-editable at
507
+ * runtime via /admin/settings (hootie_enabled). Off → the route 404s and
508
+ * the launcher is hidden. Answering additionally needs the server-wide
509
+ * ANTHROPIC_API_KEY; without one the route answers 503, not 404 — the
510
+ * feature is on, it just isn't configured yet.
511
+ */
512
+ get HOOTIE_ENABLED() {
513
+ return process.env.HOOTIE_ENABLED !== "false";
514
+ },
515
+ /** Model Hootie answers with. Superadmin-editable at runtime via
516
+ * /admin/settings (hootie_model). A gateway-side name (`ollama/…`,
517
+ * `gpt-…`) is fine when ANTHROPIC_BASE_URL points at one. */
518
+ get HOOTIE_MODEL() {
519
+ return process.env.HOOTIE_MODEL || "claude-opus-5";
520
+ },
521
+ /**
522
+ * Where the Anthropic-shaped model calls go. Empty = Anthropic directly.
523
+ * Set to a LiteLLM (or PromptOwl) gateway to route Hootie AND agent runs
524
+ * through it — one setting: the runner honours the same variable via the
525
+ * run bundle's env floor. Superadmin-editable (anthropic_base_url).
526
+ * Never a per-user vendor key: that is the shadow-AI hole the gateway
527
+ * exists to close.
528
+ */
529
+ get ANTHROPIC_BASE_URL() {
530
+ return (process.env.ANTHROPIC_BASE_URL || "").trim().replace(/\/$/, "");
531
+ },
501
532
  /**
502
533
  * Sub-agent runs: lets a run spawn nested runs (an external runner calling
503
534
  * back into POST /run for a child agent). Gated SEPARATELY from the base
@@ -553,6 +584,27 @@ var config = {
553
584
  }
554
585
  return raw;
555
586
  },
587
+ /**
588
+ * Email transport: "smtp" (SMTP_URL, default) or "ses" (AWS SES via the SDK,
589
+ * for deployments that can't use SMTP). Anything else falls back to smtp.
590
+ */
591
+ get EMAIL_PROVIDER() {
592
+ return process.env.EMAIL_PROVIDER?.trim().toLowerCase() === "ses" ? "ses" : "smtp";
593
+ },
594
+ /** AWS region for SES, e.g. us-east-1. Required when EMAIL_PROVIDER=ses. */
595
+ get SES_REGION() {
596
+ return process.env.SES_REGION?.trim() || null;
597
+ },
598
+ /**
599
+ * Optional static SES credentials. Both unset → the AWS default credential
600
+ * chain (IAM role, AWS_* env, shared profile). The secret is write-only.
601
+ */
602
+ get SES_ACCESS_KEY_ID() {
603
+ return process.env.SES_ACCESS_KEY_ID?.trim() || null;
604
+ },
605
+ get SES_SECRET_ACCESS_KEY() {
606
+ return process.env.SES_SECRET_ACCESS_KEY?.trim() || null;
607
+ },
556
608
  /**
557
609
  * From address for notification emails. Shape-validated here (not only in
558
610
  * the /admin/settings PATCH) so a value set directly via env/Docker can't
@@ -670,6 +722,11 @@ var config = {
670
722
  }
671
723
  };
672
724
 
725
+ // src/db/client.ts
726
+ import Database from "better-sqlite3";
727
+ import { mkdirSync, readFileSync } from "fs";
728
+ import { dirname as dirname2 } from "path";
729
+
673
730
  // src/db/migrations.ts
674
731
  function runMigrations(db) {
675
732
  db.exec(`
@@ -867,6 +924,9 @@ function runMigrations(db) {
867
924
  if (!nestCols.includes("reader_home_node")) {
868
925
  db.exec("ALTER TABLE nests ADD COLUMN reader_home_node TEXT");
869
926
  }
927
+ if (!nestCols.includes("branding_json")) {
928
+ db.exec("ALTER TABLE nests ADD COLUMN branding_json TEXT");
929
+ }
870
930
  const userCols = db.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
871
931
  if (!userCols.includes("is_admin")) {
872
932
  db.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
@@ -2097,7 +2157,7 @@ async function initDb() {
2097
2157
  if (config.DB_DRIVER === "postgres") {
2098
2158
  const { Pool } = await import("pg");
2099
2159
  const { PostgresAdapter } = await import("./adapter.postgres-6VZCMPQL.js");
2100
- const { runPostgresMigrations } = await import("./migrations.postgres-23VJJJJX.js");
2160
+ const { runPostgresMigrations } = await import("./migrations.postgres-HGSQ7IA5.js");
2101
2161
  const pool = new Pool(buildPgConfig());
2102
2162
  adapter = new PostgresAdapter(pool);
2103
2163
  await runPostgresMigrations(adapter);
@@ -2117,18 +2177,11 @@ function getDb() {
2117
2177
  }
2118
2178
  return adapter;
2119
2179
  }
2120
- async function resetDb() {
2121
- if (adapter) {
2122
- await adapter.close();
2123
- adapter = null;
2124
- }
2125
- }
2126
2180
 
2127
2181
  export {
2128
2182
  isEmailish,
2129
2183
  isEmailListish,
2130
2184
  config,
2131
2185
  initDb,
2132
- getDb,
2133
- resetDb
2186
+ getDb
2134
2187
  };
@@ -1,28 +1,30 @@
1
1
  import {
2
2
  buildTitleMap,
3
- canApproveWith,
4
- canEditWith,
5
- canViewWith,
6
- collabPermToRole,
7
3
  docLink,
8
4
  isSuperAdmin,
9
5
  nestAllowsSelfApprove,
10
6
  nestName,
11
- primaryRole,
12
7
  resolveNestAccess,
13
8
  resolveNestPermission,
14
- resolveTeamRolesForUser,
15
- sendEmailToRecipient
16
- } from "./chunk-7FROMOZM.js";
9
+ resolveTeamRolesForUser
10
+ } from "./chunk-QMQDWRHN.js";
17
11
  import {
18
12
  ConflictError,
19
13
  ValidationError
20
14
  } from "./chunk-LHWOJPFE.js";
15
+ import {
16
+ canApproveWith,
17
+ canEditWith,
18
+ canViewWith,
19
+ collabPermToRole,
20
+ primaryRole,
21
+ sendEmailToRecipient
22
+ } from "./chunk-RXCVXG34.js";
21
23
  import {
22
24
  config,
23
25
  getDb,
24
26
  isEmailish
25
- } from "./chunk-JDAOA5KP.js";
27
+ } from "./chunk-H3W72HN3.js";
26
28
 
27
29
  // src/governance/stewardship-service.ts
28
30
  import { v4 as uuid } from "uuid";
@@ -231,12 +233,8 @@ async function createStewardRecord(params) {
231
233
  );
232
234
  let userId = userRow?.id;
233
235
  if (!userId) {
234
- const { hashPassword } = await import("./keys-CTOGAG3W.js");
235
- userId = uuid();
236
- await db.run(
237
- "INSERT INTO users (id, email, name, password_hash, is_invited) VALUES (?, ?, ?, ?, 1)",
238
- [userId, email, null, await hashPassword(uuid())]
239
- );
236
+ const { findOrCreateInvitedUser } = await import("./invited-user-XEBX6BM4.js");
237
+ userId = (await findOrCreateInvitedUser(email)).id;
240
238
  }
241
239
  const created = await assignSteward({
242
240
  nestId: params.nestId,
@@ -2,13 +2,13 @@ import {
2
2
  buildTitleMap,
3
3
  insertOrReplace,
4
4
  nowExpr
5
- } from "./chunk-7FROMOZM.js";
5
+ } from "./chunk-QMQDWRHN.js";
6
6
  import {
7
7
  ValidationError
8
8
  } from "./chunk-LHWOJPFE.js";
9
9
  import {
10
10
  getDb
11
- } from "./chunk-JDAOA5KP.js";
11
+ } from "./chunk-H3W72HN3.js";
12
12
 
13
13
  // src/governance/version-service.ts
14
14
  import { createHash } from "crypto";
@@ -215,6 +215,25 @@ async function setApprovedVersion(nestId, nodeId, version, approvedBy) {
215
215
  );
216
216
  await db.run(sql, [nestId, nodeId, version, approvedBy]);
217
217
  }
218
+ var APPROVED_HEAL_AUTHOR = "system:approved-heal";
219
+ async function moveApprovedVersion(nestId, nodeId, from, to) {
220
+ const db = getDb();
221
+ const moved = await db.run(
222
+ `UPDATE approved_versions
223
+ SET approved_version = ?, approved_by = ?, approved_at = ${nowExpr(db)}
224
+ WHERE nest_id = ? AND node_id = ? AND approved_version = ?`,
225
+ [to, APPROVED_HEAL_AUTHOR, nestId, nodeId, from]
226
+ );
227
+ if (moved.changes === 0) return false;
228
+ const note = `Approved version moved from v${from} to v${to} \u2014 v${from} could not be rebuilt`;
229
+ await db.run(
230
+ `UPDATE node_versions
231
+ SET change_note = CASE WHEN change_note IS NULL OR change_note = '' THEN ? ELSE change_note || ' \xB7 ' || ? END
232
+ WHERE nest_id = ? AND node_id = ? AND version = ?`,
233
+ [note, note, nestId, nodeId, to]
234
+ );
235
+ return true;
236
+ }
218
237
  async function getSealedVersion(nestId, nodeId) {
219
238
  const approved = await getApprovedVersion(nestId, nodeId);
220
239
  if (approved != null) return approved;
@@ -458,6 +477,8 @@ export {
458
477
  getApprovedVersion,
459
478
  getApprovedVersions,
460
479
  setApprovedVersion,
480
+ APPROVED_HEAL_AUTHOR,
481
+ moveApprovedVersion,
461
482
  getSealedVersion,
462
483
  deleteSupersededDrafts,
463
484
  deleteUnsealedVersions,
@@ -1,3 +1,17 @@
1
+ import {
2
+ sendEmailToRecipient
3
+ } from "./chunk-RXCVXG34.js";
4
+ import {
5
+ config,
6
+ getDb
7
+ } from "./chunk-H3W72HN3.js";
8
+ import {
9
+ normalizeEmail
10
+ } from "./chunk-FRQJWGN3.js";
11
+
12
+ // src/auth/invited-user.ts
13
+ import { v4 as uuid } from "uuid";
14
+
1
15
  // src/auth/keys.ts
2
16
  import { randomBytes, createHash, timingSafeEqual } from "crypto";
3
17
  import bcrypt from "bcryptjs";
@@ -124,6 +138,40 @@ function normalizeKeyLabel(raw) {
124
138
  return trimmed;
125
139
  }
126
140
 
141
+ // src/auth/invited-user.ts
142
+ async function createInvitedUser(rawEmail, opts = {}) {
143
+ const email = normalizeEmail(rawEmail);
144
+ const id = uuid();
145
+ const tempPassword = uuid().replace(/-/g, "").slice(0, 16);
146
+ await getDb().run(
147
+ "INSERT INTO users (id, email, name, password_hash, is_invited) VALUES (?, ?, ?, ?, 1)",
148
+ [id, email, null, await hashPassword(tempPassword)]
149
+ );
150
+ const base = (opts.baseUrl || config.PUBLIC_BASE_URL || "").replace(/\/$/, "");
151
+ void sendEmailToRecipient(email, {
152
+ status: "invited",
153
+ docTitle: "ContextNest",
154
+ path: "",
155
+ actor: email,
156
+ by: opts.invitedBy || void 0,
157
+ tempPassword,
158
+ promptowlSignIn: config.PROMPTOWL_SIGN_IN_GATE === "open",
159
+ // ?invite= prefills the login page and guides them to sign in.
160
+ link: base ? `${base}/?invite=${encodeURIComponent(email)}` : ""
161
+ });
162
+ return { id, tempPassword };
163
+ }
164
+ async function findOrCreateInvitedUser(rawEmail, opts = {}) {
165
+ const email = normalizeEmail(rawEmail);
166
+ const existing = await getDb().get(
167
+ "SELECT id FROM users WHERE LOWER(email) = ?",
168
+ [email]
169
+ );
170
+ if (existing) return { id: existing.id, created: false };
171
+ const { id } = await createInvitedUser(email, opts);
172
+ return { id, created: true };
173
+ }
174
+
127
175
  export {
128
176
  generateApiKey,
129
177
  hashApiKey,
@@ -131,6 +179,7 @@ export {
131
179
  parseBearerToken,
132
180
  hashPassword,
133
181
  verifyPassword,
134
- MAX_API_KEYS_PER_USER,
135
- verifyKeyRequest
182
+ verifyKeyRequest,
183
+ createInvitedUser,
184
+ findOrCreateInvitedUser
136
185
  };