@promptowl/contextnest-community 1.16.1 → 1.17.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.
package/README.md CHANGED
@@ -1,169 +1,164 @@
1
- # ContextNest Community Edition
2
-
3
- **Self-hosted context governance server for AI workflows.** Part of the [PromptOwl](https://promptowl.ai) platform.
4
-
5
- > ⚠️ **Commercial Software.** ContextNest Community Edition is proprietary software licensed by Promptowl LLC. A free [PromptOwl account](https://app.promptowl.ai) is required to use it. Redistribution, hosting-as-a-service, and competitive use are prohibited. See [LICENSE.md](./LICENSE.md) for full terms.
6
-
7
- ---
8
-
9
- ## What it is
10
-
11
- ContextNest Community Edition is a self-hosted server that lets you:
12
-
13
- - Store, version, and govern markdown-based context documents ("nests")
14
- - Import an existing folder or vault of markdown files in one step
15
- - Export a nest as a portable bundle and re-import it on another self-hosted host
16
- - Apply stewardship workflows — draft, pending review, approved
17
- - Share nests with collaborators or publish them read-only to the public
18
- - Serve approved context to AI agents via MCP, HTTP, or CLI
19
- - Sync with the PromptOwl hosted platform for multi-user collaboration
20
-
21
- The server runs locally or on your own infrastructure. Your PromptOwl account handles authentication, entitlement, and governance metadata.
22
-
23
- ## Quickstart
24
-
25
- ```bash
26
- # 1. Run the community server
27
- npx @promptowl/contextnest-community
28
-
29
- # 2. Open the server in your browser
30
- # http://localhost:3838
31
- # On first boot with no license, it lands on the License Setup Page.
32
-
33
- # 3. Paste your PromptOwl license key (pk_...) — see "License setup" below
34
- ```
35
-
36
- The server listens on `http://localhost:3838` by default. Without a valid license the server still runs and serves reads, but write actions return `503` until you activate. See [CONFIGURATION.md](./CONFIGURATION.md) for all environment variables (port, auth mode, storage, telemetry).
37
-
38
- > **Optional:** scaffold a local nest with the open-source CLI before connecting:
39
- > ```bash
40
- > npx @promptowl/contextnest-cli init
41
- > ```
42
-
43
- ## License setup
44
-
45
- ContextNest Community Edition requires a PromptOwl Community License key (`pk_...`). Getting and activating one:
46
-
47
- ### 1. Create the key (free)
48
-
49
- 1. Sign up or log in at <https://app.promptowl.ai>
50
- 2. Open the **Overview** menu → **Community License**
51
- 3. Click **Create a Community License key**
52
- 4. Copy the generated key — it starts with `pk_`
53
-
54
- ### 2. Activate the server
55
-
56
- Pick **one** of two ways:
57
-
58
- **A. Browser setup page (recommended for first run)**
59
-
60
- 1. Start the server: `npx @promptowl/contextnest-community`
61
- 2. Open <http://localhost:3838> — with no license installed, the server boots into **setup mode** and shows the **License Setup Page**
62
- 3. Paste your `pk_...` key and submit
63
- 4. The server validates it against PromptOwl, writes it to your `.env`, and exits setup mode — no restart needed
64
-
65
- **B. Environment variable (recommended for Docker / CI / scripted deploys)**
66
-
67
- ```bash
68
- PROMPTOWL_KEY=pk_... npx @promptowl/contextnest-community
69
- ```
70
-
71
- The key is read at boot. The server validates against PromptOwl on startup; if valid, it goes straight into licensed mode.
72
-
73
- ### 3. How licensing behaves at runtime
74
-
75
- - **Unlicensed / setup mode** — reads work; every non-GET (write) request returns `503` until a valid key is installed.
76
- - **Live revocation** — a long-poll watcher tracks license state against PromptOwl. If your key is revoked, the server blocks writes within seconds (no restart required) and returns to setup mode.
77
- - **Admin identity follows the license** — the admin user is whichever PromptOwl account owns the installed key, resolved live per request. Transferring the license to another account immediately promotes the new owner and demotes the old one.
78
-
79
- For redistribution, hosted-service, OEM, or regulated-industry licensing, contact **hoot@promptowl.ai**.
80
-
81
- ## System requirements
82
-
83
- - **Node.js** 20.x or later
84
- - **PromptOwl account** — free signup at <https://app.promptowl.ai/signup>
85
- - **OS:** Windows, macOS, or Linux
86
- - **Disk:** ~200 MB for the server, plus storage for your nests
87
-
88
- ## What you get
89
-
90
- | Feature | Community Edition | Enterprise |
91
- |---|:---:|:---:|
92
- | Self-hosted context server | ✅ | ✅ |
93
- | SQLite or PostgreSQL / Cloud SQL backend | ✅ | ✅ |
94
- | Configurable nest storage (external / GCS volume mount) | ✅ | ✅ |
95
- | Markdown + YAML frontmatter vaults | ✅ | ✅ |
96
- | Import existing folder / vault | ✅ | ✅ |
97
- | Export nest as portable bundle (.zip) | ✅ | ✅ |
98
- | Markdown rendering + wiki cross-linking | ✅ | ✅ |
99
- | External-edit detection + version diff | ✅ | ✅ |
100
- | Stewardship workflow (draft/review/approve) | ✅ | ✅ |
101
- | Per-nest sharing + collaborators | ✅ | ✅ |
102
- | Public read-only nests | ✅ | ✅ |
103
- | Custom logo / branding | ✅ | ✅ |
104
- | Admin password reset + user removal (in-platform) | ✅ | ✅ |
105
- | Wiki backlinks, outline, hover-preview, link health | ✅ | ✅ |
106
- | Rich editor — tables, callouts, toggles, code highlight, find/replace, image & video upload | ✅ | ✅ |
107
- | Steward version revert | ✅ | ✅ |
108
- | MCP server for AI agents | ✅ | ✅ |
109
- | Centralized multi-tenant admin console | — | ✅ |
110
- | Single sign-on (OIDC — Entra ID, Google, Okta, Keycloak) | ✅ | ✅ |
111
- | SAML / SCIM provisioning | — | ✅ |
112
- | Audit log streaming | — | ✅ |
113
- | Policy transforms (redaction, summarization) | — | ✅ |
114
- | Priority support and SLA | — | ✅ |
115
-
116
- For Enterprise pricing and features, contact **hoot@promptowl.ai** or visit <https://promptowl.ai/contextnest/>.
117
-
118
- ## What's new
119
-
120
- Release notes live in [CHANGELOG.md](./CHANGELOG.md).
121
-
122
- ## Licensing
123
-
124
- ContextNest Community Edition is **commercial software**. It is **not open source**.
125
-
126
- **You may:**
127
- - Install and run the Software on devices You own or control
128
- - Use the Software for internal business purposes, tied to a valid PromptOwl account
129
- - Make backup and archival copies
130
-
131
- **You may not:**
132
- - Redistribute, resell, rent, lease, or sublicense the Software
133
- - Offer the Software as a hosted, managed, or software-as-a-service product to third parties
134
- - Reverse engineer, decompile, or create derivative works
135
- - Use the Software to build a competing product or service
136
- - Remove copyright, trademark, or license notices
137
-
138
- Full license text: [LICENSE.md](./LICENSE.md)
139
-
140
- **For redistribution, hosted-service, OEM, or regulated-industry use,** contact **hoot@promptowl.ai** for a commercial license agreement.
141
-
142
- ## Platform terms
143
-
144
- Because the Software requires a PromptOwl account, the following terms also apply to Your use:
145
-
146
- - **End User License Agreement** — <https://promptowl.ai/eula/>
147
- - **Terms of Service** — <https://promptowl.ai/terms-of-service/>
148
- - **Privacy Policy** — <https://promptowl.ai/privacy-policy/>
149
- - **Acceptable Use Policy** — <https://promptowl.ai/acceptable-use/>
150
- - **Disclaimer** — <https://promptowl.ai/disclaimer/>
151
- - **Cookie Policy** — <https://promptowl.ai/cookies/>
152
-
153
- ## Support
154
-
155
- - **Documentation:** <https://promptowl.ai/contextnest/>
156
- - **Product questions:** <https://promptowl.ai/contact-us/>
157
- - **Support & bugs:** `hoot@promptowl.ai`
158
- - **Commercial licensing:** `hoot@promptowl.ai` (subject: *ContextNest Commercial License*)
159
-
160
- ## AI output disclaimer
161
-
162
- The Software injects content into large language models. AI output may be inaccurate, incomplete, or inappropriate for your use case. You are responsible for reviewing and validating any AI-generated content before relying on it, particularly in business-critical or regulated contexts. Do not deploy the Software in medical, legal, financial-advisory, or safety-critical environments without appropriate human oversight.
163
-
164
- ---
165
-
166
- **Copyright © 2026 Promptowl LLC.** All rights reserved.
167
- "ContextNest" and "PromptOwl" are trademarks of Promptowl LLC.
168
-
169
- Promptowl LLC · 3060 Mercer University Dr Ste 110 · Atlanta, GA 30341 · USA
1
+ # ContextNest Community Edition
2
+
3
+ **Self-hosted context governance server for AI workflows.** Part of the [PromptOwl](https://promptowl.ai) platform.
4
+
5
+ > ⚠️ **Commercial Software.** ContextNest Community Edition is proprietary software licensed by Promptowl LLC. A free [PromptOwl account](https://app.promptowl.ai) is required to use it. Redistribution, hosting-as-a-service, and competitive use are prohibited. See [LICENSE.md](./LICENSE.md) for full terms.
6
+
7
+ ---
8
+
9
+ ## What it is
10
+
11
+ ContextNest Community Edition is a self-hosted server that lets you:
12
+
13
+ - Store, version, and govern markdown-based context documents ("nests")
14
+ - Import an existing folder or vault of markdown files in one step
15
+ - Export a nest as a portable bundle and re-import it on another self-hosted host
16
+ - Apply stewardship workflows — draft, pending review, approved
17
+ - Share nests with collaborators or publish them read-only to the public
18
+ - Serve approved context to AI agents via MCP or HTTP
19
+ - Sync with the PromptOwl hosted platform for multi-user collaboration
20
+
21
+ The server runs locally or on your own infrastructure. Your PromptOwl account handles authentication, entitlement, and governance metadata.
22
+
23
+ ## Quickstart
24
+
25
+ ```bash
26
+ # 1. Run the community server
27
+ npx @promptowl/contextnest-community
28
+
29
+ # 2. Open the server in your browser
30
+ # http://localhost:3838
31
+ # On first boot with no license, it lands on the License Setup Page.
32
+
33
+ # 3. Paste your PromptOwl license key (pk_...) — see "License setup" below
34
+ ```
35
+
36
+ The server listens on `http://localhost:3838` by default. Without a valid license the server still runs and serves reads, but write actions return `503` until you activate. See [CONFIGURATION.md](./CONFIGURATION.md) for all environment variables (port, auth mode, storage, telemetry).
37
+
38
+ ## License setup
39
+
40
+ ContextNest Community Edition requires a PromptOwl Community License key (`pk_...`). Getting and activating one:
41
+
42
+ ### 1. Create the key (free)
43
+
44
+ 1. Sign up or log in at <https://app.promptowl.ai>
45
+ 2. Open the **Overview** menu → **Community License**
46
+ 3. Click **Create a Community License key**
47
+ 4. Copy the generated key — it starts with `pk_`
48
+
49
+ ### 2. Activate the server
50
+
51
+ Pick **one** of two ways:
52
+
53
+ **A. Browser setup page (recommended for first run)**
54
+
55
+ 1. Start the server: `npx @promptowl/contextnest-community`
56
+ 2. Open <http://localhost:3838> — with no license installed, the server boots into **setup mode** and shows the **License Setup Page**
57
+ 3. Paste your `pk_...` key and submit
58
+ 4. The server validates it against PromptOwl, writes it to your `.env`, and exits setup mode — no restart needed
59
+
60
+ **B. Environment variable (recommended for Docker / CI / scripted deploys)**
61
+
62
+ ```bash
63
+ PROMPTOWL_KEY=pk_... npx @promptowl/contextnest-community
64
+ ```
65
+
66
+ The key is read at boot. The server validates against PromptOwl on startup; if valid, it goes straight into licensed mode.
67
+
68
+ ### 3. How licensing behaves at runtime
69
+
70
+ - **Unlicensed / setup mode** — reads work; every non-GET (write) request returns `503` until a valid key is installed.
71
+ - **Live revocation** — a long-poll watcher tracks license state against PromptOwl. If your key is revoked, the server blocks writes within seconds (no restart required) and returns to setup mode.
72
+ - **Admin identity follows the license** — the admin user is whichever PromptOwl account owns the installed key, resolved live per request. Transferring the license to another account immediately promotes the new owner and demotes the old one.
73
+
74
+ For redistribution, hosted-service, OEM, or regulated-industry licensing, contact **hoot@promptowl.ai**.
75
+
76
+ ## System requirements
77
+
78
+ - **Node.js** 20.x or later
79
+ - **PromptOwl account** — free signup at <https://app.promptowl.ai/signup>
80
+ - **OS:** Windows, macOS, or Linux
81
+ - **Disk:** ~200 MB for the server, plus storage for your nests
82
+
83
+ ## What you get
84
+
85
+ | Feature | Community Edition | Enterprise |
86
+ |---|:---:|:---:|
87
+ | Self-hosted context server | ✅ | ✅ |
88
+ | SQLite or PostgreSQL / Cloud SQL backend | ✅ | ✅ |
89
+ | Configurable nest storage (external / GCS volume mount) | ✅ | ✅ |
90
+ | Markdown + YAML frontmatter vaults | ✅ | ✅ |
91
+ | Import existing folder / vault | ✅ | ✅ |
92
+ | Export nest as portable bundle (.zip) | ✅ | ✅ |
93
+ | Markdown rendering + wiki cross-linking | ✅ | ✅ |
94
+ | External-edit detection + version diff | ✅ | ✅ |
95
+ | Stewardship workflow (draft/review/approve) | ✅ | ✅ |
96
+ | Per-nest sharing + collaborators | ✅ | ✅ |
97
+ | Public read-only nests | ✅ | ✅ |
98
+ | Custom logo / branding | ✅ | ✅ |
99
+ | Admin password reset + user removal (in-platform) | ✅ | ✅ |
100
+ | Wiki backlinks, outline, hover-preview, link health | ✅ | ✅ |
101
+ | Rich editor — tables, callouts, toggles, code highlight, find/replace, image & video upload | ✅ | ✅ |
102
+ | Steward version revert | ✅ | ✅ |
103
+ | MCP server for AI agents | ✅ | ✅ |
104
+ | Centralized multi-tenant admin console | — | ✅ |
105
+ | Single sign-on (OIDC — Entra ID, Google, Okta, Keycloak) | ✅ | ✅ |
106
+ | SAML / SCIM provisioning | — | ✅ |
107
+ | Audit log streaming | — | ✅ |
108
+ | Policy transforms (redaction, summarization) | — | ✅ |
109
+ | Priority support and SLA | — | ✅ |
110
+
111
+ For Enterprise pricing and features, contact **hoot@promptowl.ai** or visit <https://promptowl.ai/contextnest/>.
112
+
113
+ ## What's new
114
+
115
+ Release notes live in [CHANGELOG.md](./CHANGELOG.md).
116
+
117
+ ## Licensing
118
+
119
+ ContextNest Community Edition is **commercial software**. It is **not open source**.
120
+
121
+ **You may:**
122
+ - Install and run the Software on devices You own or control
123
+ - Use the Software for internal business purposes, tied to a valid PromptOwl account
124
+ - Make backup and archival copies
125
+
126
+ **You may not:**
127
+ - Redistribute, resell, rent, lease, or sublicense the Software
128
+ - Offer the Software as a hosted, managed, or software-as-a-service product to third parties
129
+ - Reverse engineer, decompile, or create derivative works
130
+ - Use the Software to build a competing product or service
131
+ - Remove copyright, trademark, or license notices
132
+
133
+ Full license text: [LICENSE.md](./LICENSE.md)
134
+
135
+ **For redistribution, hosted-service, OEM, or regulated-industry use,** contact **hoot@promptowl.ai** for a commercial license agreement.
136
+
137
+ ## Platform terms
138
+
139
+ Because the Software requires a PromptOwl account, the following terms also apply to Your use:
140
+
141
+ - **End User License Agreement** — <https://promptowl.ai/eula/>
142
+ - **Terms of Service** — <https://promptowl.ai/terms-of-service/>
143
+ - **Privacy Policy** — <https://promptowl.ai/privacy-policy/>
144
+ - **Acceptable Use Policy** — <https://promptowl.ai/acceptable-use/>
145
+ - **Disclaimer** — <https://promptowl.ai/disclaimer/>
146
+ - **Cookie Policy** — <https://promptowl.ai/cookies/>
147
+
148
+ ## Support
149
+
150
+ - **Documentation:** <https://promptowl.ai/contextnest/>
151
+ - **Product questions:** <https://promptowl.ai/contact-us/>
152
+ - **Support & bugs:** `hoot@promptowl.ai`
153
+ - **Commercial licensing:** `hoot@promptowl.ai` (subject: *ContextNest Commercial License*)
154
+
155
+ ## AI output disclaimer
156
+
157
+ The Software injects content into large language models. AI output may be inaccurate, incomplete, or inappropriate for your use case. You are responsible for reviewing and validating any AI-generated content before relying on it, particularly in business-critical or regulated contexts. Do not deploy the Software in medical, legal, financial-advisory, or safety-critical environments without appropriate human oversight.
158
+
159
+ ---
160
+
161
+ **Copyright © 2026 Promptowl LLC.** All rights reserved.
162
+ "ContextNest" and "PromptOwl" are trademarks of Promptowl LLC.
163
+
164
+ Promptowl LLC · 3060 Mercer University Dr Ste 110 · Atlanta, GA 30341 · USA
@@ -2,19 +2,19 @@ import {
2
2
  grantCoversNode,
3
3
  listUserGrants,
4
4
  resolveNodeGrant
5
- } from "./chunk-OTGZQZKK.js";
5
+ } from "./chunk-HWDPCPYT.js";
6
6
  import {
7
7
  createVersion,
8
8
  getApprovedVersion,
9
9
  getApprovedVersions,
10
10
  getCurrentVersion,
11
11
  setApprovedVersion
12
- } from "./chunk-D6HICFVW.js";
12
+ } from "./chunk-UXJSO54R.js";
13
13
  import {
14
14
  canUserAccess,
15
15
  canUserApprove,
16
16
  resolveStewardsForNode
17
- } from "./chunk-CLKZZZS2.js";
17
+ } from "./chunk-YF4OFT2V.js";
18
18
  import {
19
19
  buildDocContext,
20
20
  buildTitleMap,
@@ -29,9 +29,10 @@ import {
29
29
  nowExpr,
30
30
  opContext,
31
31
  prettyFolderPath,
32
+ resolveNestPermission,
32
33
  sendEmailToRecipient,
33
34
  titleForNode
34
- } from "./chunk-L7UCMGWZ.js";
35
+ } from "./chunk-SNFGU5Q7.js";
35
36
  import {
36
37
  ConflictError,
37
38
  NotFoundError,
@@ -225,13 +226,31 @@ async function canReadNode(nestId, nodeId, userId, userEmail) {
225
226
  if ((await canUserAccess(nestId, nodeId, userEmail)).allowed) return true;
226
227
  return await resolveNodeGrant(nestId, userId, nodeId) !== null;
227
228
  }
229
+ async function canRequestDeletion(params) {
230
+ const { nestId, userId, userEmail, nodeId } = params;
231
+ const permission = params.permission ?? await resolveNestPermission(nestId, userId);
232
+ if (permission === "owner" || permission === "admin" || permission === "write") {
233
+ return true;
234
+ }
235
+ const resolved = await resolveStewardsForNode(nestId, nodeId);
236
+ const isEditorSteward = resolved.some(
237
+ (r) => r.steward.userEmail.toLowerCase() === userEmail.toLowerCase() && r.steward.role === "editor" && (nodeId !== "" || r.steward.scope === "nest")
238
+ );
239
+ if (isEditorSteward) return true;
240
+ if (nodeId === "") return false;
241
+ return await resolveNodeGrant(nestId, userId, nodeId) === "write";
242
+ }
228
243
  async function filterAccessible(nestId, userId, userEmail, nodes, approvedVersions) {
229
244
  if (await isPublicReader(nestId, userId)) {
230
245
  const approved = approvedVersions ?? await getApprovedVersions(nestId);
231
246
  return nodes.filter((n) => approved.has(n.id));
232
247
  }
233
- if (!await isStewardshipEnabled(nestId)) return nodes;
234
248
  const grants = await listUserGrants(nestId, userId);
249
+ if (!await isStewardshipEnabled(nestId)) {
250
+ if (grants.length === 0) return nodes;
251
+ if (await resolveNestPermission(nestId, userId) !== "none") return nodes;
252
+ return nodes.filter((n) => grantCoversNode(grants, n.id));
253
+ }
235
254
  const accessible = [];
236
255
  for (const n of nodes) {
237
256
  if ((await canUserAccess(nestId, n.id, userEmail)).allowed || grantCoversNode(grants, n.id)) {
@@ -1477,6 +1496,15 @@ async function listDeletionRequests(params) {
1477
1496
  async function dropDeletionRequest(id) {
1478
1497
  await getDb().run("DELETE FROM deletion_requests WHERE id = ?", [id]);
1479
1498
  }
1499
+ async function withdrawDeletionRequest(params) {
1500
+ const db = getDb();
1501
+ const pending = await getDeletionRequest(params.id);
1502
+ if (!pending || pending.nestId !== params.nestId || pending.status !== "pending" || pending.requestedBy.toLowerCase() !== params.withdrawnBy.toLowerCase()) {
1503
+ return null;
1504
+ }
1505
+ await db.run("DELETE FROM deletion_requests WHERE id = ?", [params.id]);
1506
+ return pending;
1507
+ }
1480
1508
  async function declineDeletion(params) {
1481
1509
  const db = getDb();
1482
1510
  const pending = await getDeletionRequest(params.id);
@@ -1556,6 +1584,7 @@ export {
1556
1584
  markNotificationsRead,
1557
1585
  resolveCallerEmail,
1558
1586
  canReadNode,
1587
+ canRequestDeletion,
1559
1588
  filterAccessible,
1560
1589
  safeJson,
1561
1590
  requireWorkflowPlane,
@@ -1586,5 +1615,6 @@ export {
1586
1615
  getDeletionRequest,
1587
1616
  listDeletionRequests,
1588
1617
  dropDeletionRequest,
1618
+ withdrawDeletionRequest,
1589
1619
  declineDeletion
1590
1620
  };
@@ -22,14 +22,6 @@ async function resolveNodeGrant(nestId, userId, nodeId) {
22
22
  }
23
23
  return best;
24
24
  }
25
- async function hasAnyGrant(nestId, userId) {
26
- if (!userId) return false;
27
- const row = await getDb().get(
28
- "SELECT 1 AS x FROM grants WHERE nest_id = ? AND user_id = ? LIMIT 1",
29
- [nestId, userId]
30
- );
31
- return !!row;
32
- }
33
25
  async function listUserGrants(nestId, userId) {
34
26
  if (!userId) return [];
35
27
  return await getDb().all(
@@ -42,6 +34,15 @@ function grantCoversNode(grants, nodeId) {
42
34
  (g) => nodeId === g.target || nodeId.startsWith(g.target + "/")
43
35
  );
44
36
  }
37
+ function grantRoleForNode(grants, nodeId) {
38
+ let best = null;
39
+ for (const g of grants) {
40
+ if (nodeId === g.target || nodeId.startsWith(g.target + "/")) {
41
+ if (!best || rank(g.role) > rank(best)) best = g.role;
42
+ }
43
+ }
44
+ return best;
45
+ }
45
46
  async function createGrant(params) {
46
47
  const { nestId, targetType, target, userId, role, grantedBy } = params;
47
48
  if (!["document", "folder"].includes(targetType)) {
@@ -94,9 +95,9 @@ async function deleteGrant(nestId, id) {
94
95
 
95
96
  export {
96
97
  resolveNodeGrant,
97
- hasAnyGrant,
98
98
  listUserGrants,
99
99
  grantCoversNode,
100
+ grantRoleForNode,
100
101
  createGrant,
101
102
  listGrants,
102
103
  deleteGrant