@promptowl/contextnest-community 1.12.0 → 1.14.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 +59 -2
- package/README.md +3 -1
- package/dist/{chunk-3M7677XW.js → chunk-I5KYGMIT.js} +36 -1
- package/dist/{chunk-ZMSU7BCC.js → chunk-IHGPZ6NY.js} +1 -1
- package/dist/{chunk-JHIU6RZU.js → chunk-JP67WXLA.js} +34 -1
- package/dist/{chunk-F7EKYNXG.js → chunk-KPK656BH.js} +1336 -829
- package/dist/{chunk-XB7T3THE.js → chunk-MZ4IR5KH.js} +471 -504
- package/dist/{client-YUOEVUFU.js → client-3MGIP57D.js} +1 -1
- package/dist/{grants-service-PS5RMMEA.js → grants-service-5ZFISCBH.js} +2 -2
- package/dist/index.js +311 -108
- package/dist/{review-service-DPN26C47.js → review-service-6G7MVTY6.js} +7 -5
- package/dist/{stewardship-service-7BHI2YXB.js → stewardship-service-G7IF7ZNP.js} +4 -2
- package/dist/{version-service-YQIIQ7FN.js → version-service-FGL47APL.js} +6 -4
- package/dist/web3/assets/index-BYmOkm4p.js +1052 -0
- package/dist/web3/assets/index-p6uQyhml.css +1 -0
- package/dist/web3/index.html +2 -2
- package/package.json +2 -1
- package/dist/web3/assets/index-BiGiarBx.css +0 -1
- package/dist/web3/assets/index-CUVRdYvH.js +0 -1026
package/CONFIGURATION.md
CHANGED
|
@@ -73,7 +73,8 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
73
73
|
| `TELEMETRY_INTERVAL_MS` | `3600000` (1 hour) | How often buffered telemetry is flushed to PromptOwl. |
|
|
74
74
|
| `TRACE_RETENTION_DAYS` | `14` | Activity-trace retention window in days (the `api_events` rows behind `GET /admin/trace` and `GET /nests/:id/trace`). Rows older than this are pruned opportunistically (every ~500 inserts). `0` = keep forever (pruning is skipped entirely). Capped at `3650`; invalid/negative values fall back to `14`. Also editable from Settings → Advanced. |
|
|
75
75
|
| `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). |
|
|
76
|
-
| `MAX_BODY_BYTES` | `10485760` (10 MB) | Reject requests whose `Content-Length` exceeds this. Prevents giant-payload DoS. |
|
|
76
|
+
| `MAX_BODY_BYTES` | `10485760` (10 MB) | Reject requests whose `Content-Length` exceeds this. Prevents giant-payload DoS. The asset-upload route (`POST /nests/:id/assets`) is exempt up to the video cap below. |
|
|
77
|
+
| `VIDEO_MAX_MB` | `30` | Max size (in MB) of a video uploaded into a doc. Default `30` keeps it under Cloud Run's ~32 MiB HTTP/1 request-body limit, so an oversized video is rejected with a clear message instead of a bare `413` from the platform. Raise only where the deployment can actually accept larger request bodies (not behind Cloud Run, or on HTTP/2 / direct-to-bucket upload). Images are fixed at 10 MB. |
|
|
77
78
|
| `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. |
|
|
78
79
|
| `PROMPTOWL_TEAMS_ENABLED` | _(unset — off)_ | Lets users who signed in with PromptOwl import their PromptOwl teams as local teams. Off by default; set `true` to enable, or toggle from Settings → Advanced. When off, `GET/POST /teams/promptowl` return `404` and the PromptOwl Teams panel is hidden. Independent of `PROMPTOWL_SIGN_IN_GATE`. |
|
|
79
80
|
| `TYPE_ARTIFACT_ENABLED` | `true` | Set `false` to disable creation of **artifact** nodes server-wide (existing artifact nodes stay readable — never data loss). Runnable types (agent/skill/tool) are gated by `FEATURE_WORKFLOW_PLANE`, not here. Also editable from Settings (`/admin/settings`). |
|
|
@@ -86,6 +87,7 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
86
87
|
| `RUN_MAX_CONCURRENT_ROOTS` | `50` | Max concurrently-running root (depth-0) runs per nest (clamped 1..1000). The subagent caps bound a single tree; this bounds how many trees can run at once, so triggers can't flood a nest. |
|
|
87
88
|
| `ANTHROPIC_API_KEY` | _(unset)_ | Server-wide default runner key for workflow-plane agent runs. Never returned by the API — the health endpoint reports presence only, and Settings shows a masked tail. |
|
|
88
89
|
| `SLACK_WEBHOOK_URL` | _(unset — connector off)_ | Slack incoming-webhook URL for governance-event notifications (review submitted/approved/rejected, collaborator added). `https://` only — the URL embeds a secret. Also editable from Settings in the UI. |
|
|
90
|
+
| `MSTEAMS_WEBHOOK_URL` | _(unset — connector off)_ | Microsoft Teams incoming-webhook URL for the same governance events, posted as Adaptive Cards. `https://` only — the URL embeds a secret. Also editable from Settings in the UI. See [Microsoft Teams notifications](#microsoft-teams-notifications). |
|
|
89
91
|
| `SMTP_URL` | _(unset — connector off)_ | SMTP connection URL for email notifications (`smtp://` or `smtps://`, credentials inline). Requires `NOTIFY_EMAIL_FROM` and `NOTIFY_EMAIL_TO`. Also editable from Settings. |
|
|
90
92
|
| `NOTIFY_EMAIL_FROM` | _(unset)_ | From address for notification emails. |
|
|
91
93
|
| `NOTIFY_EMAIL_TO` | _(unset)_ | Comma-separated recipients for notification emails. |
|
|
@@ -93,6 +95,61 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
93
95
|
|
|
94
96
|
---
|
|
95
97
|
|
|
98
|
+
## Microsoft Teams notifications
|
|
99
|
+
|
|
100
|
+
Set `MSTEAMS_WEBHOOK_URL` (or paste the URL into **Settings → Notifications →
|
|
101
|
+
Microsoft Teams notifications**) and the server posts the same governance
|
|
102
|
+
events the Slack connector covers — review requested / approved / rejected,
|
|
103
|
+
collaborator added, plus per-nest burst digests — to one Teams channel as
|
|
104
|
+
[Adaptive Cards](https://adaptivecards.io/). Leave it empty to turn the
|
|
105
|
+
connector off. Delivery is fire-and-forget with a short timeout: a Teams
|
|
106
|
+
outage or a bad URL never slows down or fails the action that triggered the
|
|
107
|
+
message.
|
|
108
|
+
|
|
109
|
+
> Naming note: the setting is `msteams_webhook_url` / `MSTEAMS_WEBHOOK_URL`
|
|
110
|
+
> because "teams" in this product means user groups (the `/teams` API), not
|
|
111
|
+
> Microsoft Teams.
|
|
112
|
+
|
|
113
|
+
### Create the incoming webhook in a Teams channel
|
|
114
|
+
|
|
115
|
+
Microsoft retired the classic Office 365 "Incoming Webhook" connectors;
|
|
116
|
+
webhooks are now created with the **Workflows** (Power Automate) app:
|
|
117
|
+
|
|
118
|
+
1. In Microsoft Teams, open the channel that should receive notifications,
|
|
119
|
+
click **⋯ (More options) → Workflows**.
|
|
120
|
+
2. Pick the template **"Post to a channel when a webhook request is
|
|
121
|
+
received"** (searching for "webhook" finds it), sign in if prompted, and
|
|
122
|
+
confirm the team + channel.
|
|
123
|
+
3. Create the flow and **copy the HTTP POST URL** it shows (a
|
|
124
|
+
`https://….logic.azure.com/…` or `https://….powerplatform.com/…` address).
|
|
125
|
+
4. Paste that URL into **Settings → Notifications → Microsoft Teams
|
|
126
|
+
notifications** (or set `MSTEAMS_WEBHOOK_URL`).
|
|
127
|
+
5. Save, then click **Send test** on the card to post a test message and
|
|
128
|
+
confirm the channel receives it. (The button tests the *saved* URL — save
|
|
129
|
+
first, then test. The Slack card has the same button.)
|
|
130
|
+
|
|
131
|
+
If your tenant still has a legacy Office 365 connector webhook, it is expected
|
|
132
|
+
to accept the same payload envelope (legacy connectors documented support for
|
|
133
|
+
Adaptive Card attachments, though we have not verified one end-to-end — the
|
|
134
|
+
card's plain-text `fallbackText` is the safety net) — but create new hooks via
|
|
135
|
+
Workflows.
|
|
136
|
+
|
|
137
|
+
The URL embeds a secret — anyone holding it can post to the channel, so treat
|
|
138
|
+
it like a password (it is why the setting requires `https://`). The server
|
|
139
|
+
sends the standard envelope Workflows expects:
|
|
140
|
+
`{"type": "message", "attachments": [{"contentType":
|
|
141
|
+
"application/vnd.microsoft.card.adaptive", "content": {…card…}}]}` with a
|
|
142
|
+
plain-text `fallbackText` for clients that cannot render cards.
|
|
143
|
+
|
|
144
|
+
Per-nest connector rows post the same Adaptive Card format to a nest-specific
|
|
145
|
+
webhook, with per-event filtering: `POST /nests/:id/connectors` with a JSON
|
|
146
|
+
body like `{"channel": "teams", "url": "https://…", "events":
|
|
147
|
+
["review_requested", "review_rejected"]}` (`events` may also be `["*"]`; the
|
|
148
|
+
`url` may be an `env:KEY` reference into the nest's env store). Manage rows
|
|
149
|
+
with `GET`/`PATCH`/`DELETE` on the same path.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
96
153
|
## Single sign-on (OIDC)
|
|
97
154
|
|
|
98
155
|
Generic OpenID Connect sign-in against any spec-compliant identity provider —
|
|
@@ -233,7 +290,7 @@ Postgres backend starts empty.
|
|
|
233
290
|
### Runtime settings persistence
|
|
234
291
|
|
|
235
292
|
Settings you change at runtime — everything on the **Settings page** (`/admin/settings`:
|
|
236
|
-
sign-in gate, logo, base URL, upload limit, feature flags, OIDC SSO, Slack/SMTP connectors)
|
|
293
|
+
sign-in gate, logo, base URL, upload limit, feature flags, OIDC SSO, Slack/Teams/SMTP connectors)
|
|
237
294
|
plus the **installed license key** — are stored in the database (`server_settings`
|
|
238
295
|
table), **not** in a `.env` file. This is deliberate: on Cloud Run the container
|
|
239
296
|
filesystem is ephemeral, so a file-based value was wiped by every rebuild, and each
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ 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
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
|
|
15
16
|
- Apply stewardship workflows — draft, pending review, approved
|
|
16
17
|
- Share nests with collaborators or publish them read-only to the public
|
|
17
18
|
- Serve approved context to AI agents via MCP, HTTP, or CLI
|
|
@@ -93,6 +94,7 @@ For redistribution, hosted-service, OEM, or regulated-industry licensing, contac
|
|
|
93
94
|
| Configurable nest storage (external / GCS volume mount) | ✅ | ✅ |
|
|
94
95
|
| Markdown + YAML frontmatter vaults | ✅ | ✅ |
|
|
95
96
|
| Import existing folder / vault | ✅ | ✅ |
|
|
97
|
+
| Export nest as portable bundle (.zip) | ✅ | ✅ |
|
|
96
98
|
| Markdown rendering + wiki cross-linking | ✅ | ✅ |
|
|
97
99
|
| External-edit detection + version diff | ✅ | ✅ |
|
|
98
100
|
| Stewardship workflow (draft/review/approve) | ✅ | ✅ |
|
|
@@ -101,7 +103,7 @@ For redistribution, hosted-service, OEM, or regulated-industry licensing, contac
|
|
|
101
103
|
| Custom logo / branding | ✅ | ✅ |
|
|
102
104
|
| Admin password reset + user removal (in-platform) | ✅ | ✅ |
|
|
103
105
|
| Wiki backlinks, outline, hover-preview, link health | ✅ | ✅ |
|
|
104
|
-
| Rich editor — tables, callouts, toggles, code highlight, find/replace | ✅ | ✅ |
|
|
106
|
+
| Rich editor — tables, callouts, toggles, code highlight, find/replace, image & video upload | ✅ | ✅ |
|
|
105
107
|
| Steward version revert | ✅ | ✅ |
|
|
106
108
|
| MCP server for AI agents | ✅ | ✅ |
|
|
107
109
|
| Centralized multi-tenant admin console | — | ✅ |
|
|
@@ -12,6 +12,7 @@ import { join, dirname } from "path";
|
|
|
12
12
|
import { existsSync } from "fs";
|
|
13
13
|
import { fileURLToPath } from "url";
|
|
14
14
|
import dotenv from "dotenv";
|
|
15
|
+
import { z } from "zod";
|
|
15
16
|
var __filename = fileURLToPath(import.meta.url);
|
|
16
17
|
var __dirname = dirname(__filename);
|
|
17
18
|
var envCandidates = [
|
|
@@ -26,11 +27,13 @@ if (envFileLoaded && !isTestRun) {
|
|
|
26
27
|
var canonicalEnvFile = process.env.ENV_FILE_PATH || join(dataRoot(), ".env");
|
|
27
28
|
var canonicalEnvLoaded = null;
|
|
28
29
|
var slackUrlWarned = false;
|
|
30
|
+
var msteamsUrlWarned = false;
|
|
29
31
|
var emailFromWarned = false;
|
|
30
32
|
var emailToWarned = false;
|
|
31
33
|
var oidcIssuerWarned = false;
|
|
34
|
+
var EMAIL_SCHEMA = z.email();
|
|
32
35
|
function isEmailish(v) {
|
|
33
|
-
return
|
|
36
|
+
return EMAIL_SCHEMA.safeParse(v.trim()).success && !/[\r\n]/.test(v);
|
|
34
37
|
}
|
|
35
38
|
function isEmailListish(v) {
|
|
36
39
|
return v.split(",").every((e) => isEmailish(e.trim()));
|
|
@@ -353,6 +356,27 @@ var config = {
|
|
|
353
356
|
}
|
|
354
357
|
return raw;
|
|
355
358
|
},
|
|
359
|
+
/**
|
|
360
|
+
* Optional Microsoft Teams incoming-webhook URL (Workflows app) for team
|
|
361
|
+
* notifications — same events as SLACK_WEBHOOK_URL, posted as Adaptive
|
|
362
|
+
* Cards. Empty/unset = connector off. https only — a webhook carries an
|
|
363
|
+
* implicit secret in its path, so it never travels plaintext. Named
|
|
364
|
+
* MSTEAMS_* deliberately: "teams" in this codebase means user groups.
|
|
365
|
+
*/
|
|
366
|
+
get MSTEAMS_WEBHOOK_URL() {
|
|
367
|
+
const raw = process.env.MSTEAMS_WEBHOOK_URL?.trim();
|
|
368
|
+
if (!raw) return null;
|
|
369
|
+
if (!/^https:\/\//i.test(raw)) {
|
|
370
|
+
if (!msteamsUrlWarned) {
|
|
371
|
+
msteamsUrlWarned = true;
|
|
372
|
+
console.warn(
|
|
373
|
+
"[config] MSTEAMS_WEBHOOK_URL rejected: must be an https:// URL. Microsoft Teams notifications disabled."
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
return raw;
|
|
379
|
+
},
|
|
356
380
|
get FEATURE_WORKFLOW_PLANE() {
|
|
357
381
|
return process.env.FEATURE_WORKFLOW_PLANE === "true";
|
|
358
382
|
},
|
|
@@ -494,6 +518,17 @@ var config = {
|
|
|
494
518
|
*/
|
|
495
519
|
get MAX_BODY_BYTES() {
|
|
496
520
|
return parseInt(process.env.MAX_BODY_BYTES || String(10 * 1024 * 1024), 10);
|
|
521
|
+
},
|
|
522
|
+
/**
|
|
523
|
+
* Max video upload size in bytes. Default 30 MB — kept under Cloud Run's
|
|
524
|
+
* 32 MiB HTTP/1 request-body limit so a too-big video is rejected cleanly by
|
|
525
|
+
* our own check (clear message) instead of a bare 413 from the platform.
|
|
526
|
+
* Raise via VIDEO_MAX_MB only when the deployment can actually accept it
|
|
527
|
+
* (e.g. not behind Cloud Run, or on HTTP/2 / direct-to-bucket upload).
|
|
528
|
+
*/
|
|
529
|
+
get VIDEO_MAX_BYTES() {
|
|
530
|
+
const mb = parseInt(process.env.VIDEO_MAX_MB || "30", 10);
|
|
531
|
+
return (Number.isFinite(mb) && mb > 0 ? mb : 30) * 1024 * 1024;
|
|
497
532
|
}
|
|
498
533
|
};
|
|
499
534
|
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-XQ46F76G.js";
|
|
5
5
|
import {
|
|
6
6
|
getDb
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-I5KYGMIT.js";
|
|
8
8
|
|
|
9
9
|
// src/governance/version-service.ts
|
|
10
10
|
import { createHash } from "crypto";
|
|
@@ -41,6 +41,38 @@ async function createVersion(params) {
|
|
|
41
41
|
status: params.status
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
async function upsertVersion(params) {
|
|
45
|
+
const db = getDb();
|
|
46
|
+
const contentHash = hashContent(params.content);
|
|
47
|
+
const sql = insertOrReplace(
|
|
48
|
+
db,
|
|
49
|
+
`INSERT INTO node_versions (nest_id, node_id, version, content_hash, author, status, change_note, tags_json)
|
|
50
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
51
|
+
["nest_id", "node_id", "version"],
|
|
52
|
+
// Reset created_at on conflict too: SQLite's INSERT OR REPLACE deletes+
|
|
53
|
+
// reinserts (created_at → now), so mirror that on Postgres — otherwise a
|
|
54
|
+
// draft edited in place would keep its original (import-time) "edited at".
|
|
55
|
+
`content_hash = excluded.content_hash, author = excluded.author, status = excluded.status, change_note = excluded.change_note, tags_json = excluded.tags_json, created_at = ${nowExpr(db)}`
|
|
56
|
+
);
|
|
57
|
+
await db.run(sql, [
|
|
58
|
+
params.nestId,
|
|
59
|
+
params.nodeId,
|
|
60
|
+
params.version,
|
|
61
|
+
contentHash,
|
|
62
|
+
params.author,
|
|
63
|
+
params.status,
|
|
64
|
+
params.changeNote || null,
|
|
65
|
+
params.tags ? JSON.stringify(params.tags) : null
|
|
66
|
+
]);
|
|
67
|
+
return {
|
|
68
|
+
version: params.version,
|
|
69
|
+
content: params.content,
|
|
70
|
+
editedBy: params.author,
|
|
71
|
+
editedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
72
|
+
changeNote: params.changeNote,
|
|
73
|
+
status: params.status
|
|
74
|
+
};
|
|
75
|
+
}
|
|
44
76
|
async function getVersions(nestId, nodeId) {
|
|
45
77
|
const db = getDb();
|
|
46
78
|
const rows = await db.all(
|
|
@@ -172,6 +204,7 @@ export {
|
|
|
172
204
|
SYSTEM_AUTHOR_PREFIX,
|
|
173
205
|
systemAuthor,
|
|
174
206
|
createVersion,
|
|
207
|
+
upsertVersion,
|
|
175
208
|
getVersions,
|
|
176
209
|
getVersion,
|
|
177
210
|
getCurrentVersion,
|