@promptowl/contextnest-community 1.0.1 → 1.1.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/CONFIGURATION.md +6 -4
- package/README.md +80 -7
- package/dist/{chunk-JMZ75ZCD.js → chunk-7UTMBL6Z.js} +1 -1
- package/dist/{chunk-5VHKEIAW.js → chunk-S2EWN2VA.js} +18 -36
- package/dist/{chunk-KQCWNHDM.js → chunk-TDAX3JOT.js} +25 -0
- package/dist/chunk-WCOUCBDJ.js +1406 -0
- package/dist/{chunk-7K2LLJXK.js → chunk-XRK6SQSC.js} +1 -1
- package/dist/index.js +1022 -1230
- package/dist/{keys-YV33AJK3.js → keys-73STFJJB.js} +1 -1
- package/dist/{review-service-4WS3XL6K.js → review-service-3OJIPYNV.js} +4 -4
- package/dist/{stewardship-service-C5D2O7ZE.js → stewardship-service-3XGX7QIN.js} +20 -4
- package/dist/{version-service-TFEYNPH7.js → version-service-UODXLAOJ.js} +2 -2
- package/dist/web3/assets/index-BLxRS7jD.js +673 -0
- package/dist/web3/assets/index-DszK6Vkc.css +1 -0
- package/dist/web3/index.html +2 -2
- package/package.json +136 -134
- package/dist/chunk-K22GWPT4.js +0 -498
- package/dist/web3/assets/index-DkLevP7k.js +0 -624
- package/dist/web3/assets/index-DpoBdKrd.css +0 -1
package/CONFIGURATION.md
CHANGED
|
@@ -48,11 +48,13 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
48
48
|
| `DATABASE_PATH` | `$DATA_ROOT/community.db` | Override the SQLite file location explicitly. |
|
|
49
49
|
| `AUTH_MODE` | `key` | `key` or `open`. See above. |
|
|
50
50
|
| `PROMPTOWL_API_URL` | `https://app.promptowl.ai` | PromptOwl's API origin — used for device auth, license validation, telemetry. Override for air-gapped or test setups. |
|
|
51
|
-
| `PROMPTOWL_KEY` | `""` | Your PromptOwl Community
|
|
51
|
+
| `PROMPTOWL_KEY` | `""` | Your PromptOwl Community License key (`pk_...`). Unlicensed instances still run and serve reads, but every write returns `503` until a valid key is installed. Can also be set via the browser License Setup Page, which persists it to `ENV_FILE_PATH`. |
|
|
52
|
+
| `ENV_FILE_PATH` | `$cwd/.env` | Path to the `.env` file the license install flow writes `PROMPTOWL_KEY` into (alongside existing vars). Override when your `.env` lives outside the working directory. |
|
|
52
53
|
| `TELEMETRY_ENABLED` | `"true"` (set to `"false"` to disable) | Batched, anonymized usage events sent to PromptOwl. Off disables the loop entirely. |
|
|
53
54
|
| `TELEMETRY_INTERVAL_MS` | `3600000` (1 hour) | How often buffered telemetry is flushed to PromptOwl. |
|
|
54
55
|
| `CORS_ORIGINS` | `*` in open mode; `http://localhost:5173,http://localhost:3838` in key mode | Comma-separated allowlist. Set to `*` to allow any origin (**only** safe in open mode — in key mode with Bearer tokens this enables CSRF). |
|
|
55
56
|
| `MAX_BODY_BYTES` | `10485760` (10 MB) | Reject requests whose `Content-Length` exceeds this. Prevents giant-payload DoS. |
|
|
57
|
+
| `LOGO_URL` | _(unset)_ | Custom logo shown in the UI header + login screen. Must start with `https://`, `http://`, or `data:image/` — other schemes (`file://`, relative, `javascript:`) are rejected with a warning and the bundled icon is used. |
|
|
56
58
|
|
|
57
59
|
---
|
|
58
60
|
|
|
@@ -61,10 +63,10 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
61
63
|
### Local dev / single user
|
|
62
64
|
|
|
63
65
|
```bash
|
|
64
|
-
AUTH_MODE=open DATA_ROOT=./my-data
|
|
66
|
+
AUTH_MODE=open DATA_ROOT=./my-data npm run dev
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
Or the default dev mode if `
|
|
69
|
+
Or the default dev mode if `npm run dev` already sets `AUTH_MODE=open` in your scripts.
|
|
68
70
|
|
|
69
71
|
### Team / multi-user behind a reverse proxy
|
|
70
72
|
|
|
@@ -73,7 +75,7 @@ AUTH_MODE=key \
|
|
|
73
75
|
CORS_ORIGINS="https://team.example.com,https://admin.example.com" \
|
|
74
76
|
DATA_ROOT=/var/lib/contextnest \
|
|
75
77
|
PROMPTOWL_KEY=pk_... \
|
|
76
|
-
|
|
78
|
+
npm start
|
|
77
79
|
```
|
|
78
80
|
|
|
79
81
|
Terminate TLS at the proxy, forward `X-Forwarded-For` and `X-Real-IP` headers (the rate limiter reads them), and bind the server to `127.0.0.1` so only the proxy can reach it.
|
package/README.md
CHANGED
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
ContextNest Community Edition is a self-hosted server that lets you:
|
|
12
12
|
|
|
13
13
|
- Store, version, and govern markdown-based context documents ("nests")
|
|
14
|
+
- Import an existing folder or vault of markdown files in one step
|
|
14
15
|
- Apply stewardship workflows — draft, pending review, approved
|
|
16
|
+
- Share nests with collaborators or publish them read-only to the public
|
|
15
17
|
- Serve approved context to AI agents via MCP, HTTP, or CLI
|
|
16
18
|
- Sync with the PromptOwl hosted platform for multi-user collaboration
|
|
17
19
|
|
|
@@ -20,17 +22,60 @@ The server runs locally or on your own infrastructure. Your PromptOwl account ha
|
|
|
20
22
|
## Quickstart
|
|
21
23
|
|
|
22
24
|
```bash
|
|
23
|
-
# 1.
|
|
24
|
-
|
|
25
|
+
# 1. Run the community server
|
|
26
|
+
npx @promptowl/contextnest-community
|
|
25
27
|
|
|
26
|
-
# 2.
|
|
27
|
-
|
|
28
|
+
# 2. Open the server in your browser
|
|
29
|
+
# http://localhost:3838
|
|
30
|
+
# On first boot with no license, it lands on the License Setup Page.
|
|
28
31
|
|
|
29
|
-
# 3.
|
|
32
|
+
# 3. Paste your PromptOwl license key (pk_...) — see "License setup" below
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
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).
|
|
36
|
+
|
|
37
|
+
> **Optional:** scaffold a local nest with the open-source CLI before connecting:
|
|
38
|
+
> ```bash
|
|
39
|
+
> npx @promptowl/contextnest-cli init
|
|
40
|
+
> ```
|
|
41
|
+
|
|
42
|
+
## License setup
|
|
43
|
+
|
|
44
|
+
ContextNest Community Edition requires a PromptOwl Community License key (`pk_...`). Getting and activating one:
|
|
45
|
+
|
|
46
|
+
### 1. Create the key (free)
|
|
47
|
+
|
|
48
|
+
1. Sign up or log in at <https://app.promptowl.ai>
|
|
49
|
+
2. Open the **Overview** menu → **Community License**
|
|
50
|
+
3. Click **Create a Community License key**
|
|
51
|
+
4. Copy the generated key — it starts with `pk_`
|
|
52
|
+
|
|
53
|
+
### 2. Activate the server
|
|
54
|
+
|
|
55
|
+
Pick **one** of two ways:
|
|
56
|
+
|
|
57
|
+
**A. Browser setup page (recommended for first run)**
|
|
58
|
+
|
|
59
|
+
1. Start the server: `npx @promptowl/contextnest-community`
|
|
60
|
+
2. Open <http://localhost:3838> — with no license installed, the server boots into **setup mode** and shows the **License Setup Page**
|
|
61
|
+
3. Paste your `pk_...` key and submit
|
|
62
|
+
4. The server validates it against PromptOwl, writes it to your `.env`, and exits setup mode — no restart needed
|
|
63
|
+
|
|
64
|
+
**B. Environment variable (recommended for Docker / CI / scripted deploys)**
|
|
65
|
+
|
|
66
|
+
```bash
|
|
30
67
|
PROMPTOWL_KEY=pk_... npx @promptowl/contextnest-community
|
|
31
68
|
```
|
|
32
69
|
|
|
33
|
-
The
|
|
70
|
+
The key is read at boot. The server validates against PromptOwl on startup; if valid, it goes straight into licensed mode.
|
|
71
|
+
|
|
72
|
+
### 3. How licensing behaves at runtime
|
|
73
|
+
|
|
74
|
+
- **Unlicensed / setup mode** — reads work; every non-GET (write) request returns `503` until a valid key is installed.
|
|
75
|
+
- **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.
|
|
76
|
+
- **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.
|
|
77
|
+
|
|
78
|
+
For redistribution, hosted-service, OEM, or regulated-industry licensing, contact **hoot@promptowl.ai**.
|
|
34
79
|
|
|
35
80
|
## System requirements
|
|
36
81
|
|
|
@@ -45,9 +90,15 @@ The server listens on `http://localhost:3000` by default. Without a valid `PROMP
|
|
|
45
90
|
|---|:---:|:---:|
|
|
46
91
|
| Self-hosted context server | ✅ | ✅ |
|
|
47
92
|
| Markdown + YAML frontmatter vaults | ✅ | ✅ |
|
|
93
|
+
| Import existing folder / vault | ✅ | ✅ |
|
|
94
|
+
| Markdown rendering + wiki cross-linking | ✅ | ✅ |
|
|
95
|
+
| External-edit detection + version diff | ✅ | ✅ |
|
|
48
96
|
| Stewardship workflow (draft/review/approve) | ✅ | ✅ |
|
|
97
|
+
| Per-nest sharing + collaborators | ✅ | ✅ |
|
|
98
|
+
| Public read-only nests | ✅ | ✅ |
|
|
99
|
+
| Custom logo / branding | ✅ | ✅ |
|
|
49
100
|
| MCP server for AI agents | ✅ | ✅ |
|
|
50
|
-
|
|
|
101
|
+
| Centralized multi-tenant admin console | — | ✅ |
|
|
51
102
|
| SSO / SAML / SCIM | — | ✅ |
|
|
52
103
|
| Audit log streaming | — | ✅ |
|
|
53
104
|
| Policy transforms (redaction, summarization) | — | ✅ |
|
|
@@ -55,6 +106,28 @@ The server listens on `http://localhost:3000` by default. Without a valid `PROMP
|
|
|
55
106
|
|
|
56
107
|
For Enterprise pricing and features, contact **hoot@promptowl.ai** or visit <https://promptowl.ai/contextnest/>.
|
|
57
108
|
|
|
109
|
+
## What's new in 1.1.0
|
|
110
|
+
|
|
111
|
+
- **Vault import** — import an existing folder of markdown files into a new nest in one step, from the dashboard ("Import folder") or via the API. Frontmatter, wiki links, and folder structure are preserved.
|
|
112
|
+
- **Nest sharing + collaborators** — set per-nest visibility and add collaborators with read or write access. A Share affordance is now inline in the document view.
|
|
113
|
+
- **Public read-only nests** — flip a nest to `visibility=public` to serve it to unauthenticated readers (bypasses auth for GETs only); writes still require a key.
|
|
114
|
+
- **Custom logo / branding** — set `LOGO_URL` to show your own logo in the UI header and login screen. See [CONFIGURATION.md](./CONFIGURATION.md).
|
|
115
|
+
- **Governance** — nest owners can self-approve their own changes; new per-nest auto-approve toggle; role-based action gating (buttons reflect the viewer's role); stewards can edit scope during a role change.
|
|
116
|
+
- **Editor** — `[[wikilink]]` autocomplete, broken-link click creates a new doc with the title pre-filled, tag chips filter the nest list, Notion-style H1 title, click-to-edit, and an unsaved-changes warning.
|
|
117
|
+
- **Stability** — added an error boundary so a render error in one view no longer blanks the whole app; bumped `uuid` to 14.
|
|
118
|
+
|
|
119
|
+
Full history in [CHANGELOG.md](./CHANGELOG.md).
|
|
120
|
+
|
|
121
|
+
## What's new in 1.0.1
|
|
122
|
+
|
|
123
|
+
- **Document hashing pipeline** — external-edit detection, conflict-aware safe-publish, and inline version diffs powered by `@promptowl/contextnest-engine`. When a file is edited outside the UI, the editor shows an "External edit detected" banner with a side-by-side diff and an adopt / keep choice.
|
|
124
|
+
- **Markdown rendering** — new `DocumentViewer` (react-markdown + remark-gfm + wikilink support) renders CLI/MCP-authored markdown correctly, with a view/edit toggle.
|
|
125
|
+
- **License revocation now blocks writes synchronously** — revoke flips an in-process "writes blocked" flag immediately; the next write returns `503`.
|
|
126
|
+
- **Dashboard stats** — new `GET /stats` endpoint surfaces nest / document / user counts.
|
|
127
|
+
- **Docker fix** — Dockerfile now installs via npm against the shipped `package-lock.json`; base image bumped to `node:22-slim`.
|
|
128
|
+
|
|
129
|
+
Full history in [CHANGELOG.md](./CHANGELOG.md).
|
|
130
|
+
|
|
58
131
|
## Licensing
|
|
59
132
|
|
|
60
133
|
ContextNest Community Edition is **commercial software**. It is **not open source**.
|
|
@@ -1,46 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
canUserApprove,
|
|
3
|
-
resolveStewardsForNode
|
|
4
|
-
} from "./chunk-K22GWPT4.js";
|
|
5
1
|
import {
|
|
6
2
|
createVersion,
|
|
7
3
|
setApprovedVersion
|
|
8
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-7UTMBL6Z.js";
|
|
5
|
+
import {
|
|
6
|
+
buildTitleMap,
|
|
7
|
+
canUserApprove,
|
|
8
|
+
engineCache,
|
|
9
|
+
resolveStewardsForNode
|
|
10
|
+
} from "./chunk-WCOUCBDJ.js";
|
|
9
11
|
import {
|
|
10
|
-
config,
|
|
11
12
|
getDb
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TDAX3JOT.js";
|
|
13
14
|
|
|
14
15
|
// src/governance/review-service.ts
|
|
15
16
|
import { v4 as uuid } from "uuid";
|
|
16
17
|
|
|
17
|
-
// src/nodes/engine.ts
|
|
18
|
-
import { join } from "path";
|
|
19
|
-
import {
|
|
20
|
-
NestStorage,
|
|
21
|
-
GraphQueryEngine,
|
|
22
|
-
VersionManager
|
|
23
|
-
} from "@promptowl/contextnest-engine";
|
|
24
|
-
var NestEngineCache = class {
|
|
25
|
-
cache = /* @__PURE__ */ new Map();
|
|
26
|
-
get(nestId) {
|
|
27
|
-
let engine = this.cache.get(nestId);
|
|
28
|
-
if (!engine) {
|
|
29
|
-
const nestPath = join(config.DATA_ROOT, "nests", nestId);
|
|
30
|
-
const storage = new NestStorage(nestPath);
|
|
31
|
-
const query = new GraphQueryEngine(storage);
|
|
32
|
-
const versions = new VersionManager(storage);
|
|
33
|
-
engine = { storage, query, versions };
|
|
34
|
-
this.cache.set(nestId, engine);
|
|
35
|
-
}
|
|
36
|
-
return engine;
|
|
37
|
-
}
|
|
38
|
-
evict(nestId) {
|
|
39
|
-
this.cache.delete(nestId);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
var engineCache = new NestEngineCache();
|
|
43
|
-
|
|
44
18
|
// src/governance/safe-publish.ts
|
|
45
19
|
import {
|
|
46
20
|
publishDocument,
|
|
@@ -209,7 +183,7 @@ function cancelReview(params) {
|
|
|
209
183
|
).run(params.nestId, params.nodeId, pending.version);
|
|
210
184
|
return getReviewRequest(pending.id);
|
|
211
185
|
}
|
|
212
|
-
function getReviewQueue(params) {
|
|
186
|
+
async function getReviewQueue(params) {
|
|
213
187
|
const db = getDb();
|
|
214
188
|
let whereClauses = [];
|
|
215
189
|
const args = [];
|
|
@@ -244,6 +218,15 @@ function getReviewQueue(params) {
|
|
|
244
218
|
);
|
|
245
219
|
});
|
|
246
220
|
}
|
|
221
|
+
const titleMapsByNest = /* @__PURE__ */ new Map();
|
|
222
|
+
const nestIds = new Set(requests.map((r) => r.nestId));
|
|
223
|
+
for (const nid of nestIds) {
|
|
224
|
+
titleMapsByNest.set(nid, await buildTitleMap(nid));
|
|
225
|
+
}
|
|
226
|
+
requests = requests.map((r) => ({
|
|
227
|
+
...r,
|
|
228
|
+
title: titleMapsByNest.get(r.nestId)?.get(r.nodeId)
|
|
229
|
+
}));
|
|
247
230
|
return { requests, total };
|
|
248
231
|
}
|
|
249
232
|
function getReviewHistory(nestId, nodeId) {
|
|
@@ -283,7 +266,6 @@ function rowToReviewRequest(row) {
|
|
|
283
266
|
}
|
|
284
267
|
|
|
285
268
|
export {
|
|
286
|
-
engineCache,
|
|
287
269
|
safePublishDocument,
|
|
288
270
|
submitForReview,
|
|
289
271
|
approve,
|
|
@@ -52,6 +52,25 @@ var config = {
|
|
|
52
52
|
get TELEMETRY_INTERVAL_MS() {
|
|
53
53
|
return parseInt(process.env.TELEMETRY_INTERVAL_MS || "3600000", 10);
|
|
54
54
|
},
|
|
55
|
+
/**
|
|
56
|
+
* Optional custom logo URL shown in UI header + login screen.
|
|
57
|
+
* Must be an absolute https://, http://, or data:image/… URL. Other
|
|
58
|
+
* schemes (file://, javascript:, relative paths) are rejected with a
|
|
59
|
+
* warning so an operator typo doesn't silently break the favicon.
|
|
60
|
+
* When unset or invalid, the UI falls back to the bundled icon.
|
|
61
|
+
*/
|
|
62
|
+
get LOGO_URL() {
|
|
63
|
+
const raw = process.env.LOGO_URL?.trim();
|
|
64
|
+
if (!raw) return null;
|
|
65
|
+
const ok = /^(https?:\/\/|data:image\/)/i.test(raw);
|
|
66
|
+
if (!ok) {
|
|
67
|
+
console.warn(
|
|
68
|
+
`[config] LOGO_URL rejected: must start with https://, http://, or data:image/ (got "${raw}"). Falling back to default logo.`
|
|
69
|
+
);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return raw;
|
|
73
|
+
},
|
|
55
74
|
get AUTH_MODE() {
|
|
56
75
|
return process.env.AUTH_MODE || "key";
|
|
57
76
|
},
|
|
@@ -229,6 +248,12 @@ function runMigrations(db2) {
|
|
|
229
248
|
if (!nestCols.includes("stewardship_enabled")) {
|
|
230
249
|
db2.exec("ALTER TABLE nests ADD COLUMN stewardship_enabled INTEGER NOT NULL DEFAULT 0");
|
|
231
250
|
}
|
|
251
|
+
if (!nestCols.includes("is_imported")) {
|
|
252
|
+
db2.exec("ALTER TABLE nests ADD COLUMN is_imported INTEGER NOT NULL DEFAULT 0");
|
|
253
|
+
}
|
|
254
|
+
if (!nestCols.includes("allow_self_approve")) {
|
|
255
|
+
db2.exec("ALTER TABLE nests ADD COLUMN allow_self_approve INTEGER NOT NULL DEFAULT 0");
|
|
256
|
+
}
|
|
232
257
|
const userCols = db2.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
|
|
233
258
|
if (!userCols.includes("is_admin")) {
|
|
234
259
|
db2.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
|