@promptowl/contextnest-community 1.14.0 → 1.16.1
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 +15 -0
- package/dist/chunk-CLKZZZS2.js +624 -0
- package/dist/{chunk-JP67WXLA.js → chunk-D6HICFVW.js} +77 -3
- package/dist/{chunk-I5KYGMIT.js → chunk-JMJLNEXE.js} +78 -1
- package/dist/{chunk-KPK656BH.js → chunk-L7UCMGWZ.js} +478 -924
- package/dist/{chunk-IHGPZ6NY.js → chunk-OTGZQZKK.js} +1 -1
- package/dist/{chunk-MZ4IR5KH.js → chunk-QJVRZT6M.js} +356 -76
- package/dist/{client-3MGIP57D.js → client-OBS5HOTA.js} +1 -1
- package/dist/{grants-service-5ZFISCBH.js → grants-service-I535QVTW.js} +2 -2
- package/dist/index.js +2419 -1061
- package/dist/{migrations.postgres-BRXZY2GE.js → migrations.postgres-CYKO5CFV.js} +41 -1
- package/dist/review-service-V2GR6VM3.js +47 -0
- package/dist/{stewardship-service-G7IF7ZNP.js → stewardship-service-LTREGVSJ.js} +4 -4
- package/dist/{version-service-FGL47APL.js → version-service-V6K5SZ3P.js} +11 -3
- package/dist/web3/assets/index-DnQARUL3.css +1 -0
- package/dist/web3/assets/index-_m7WyozM.js +1051 -0
- package/dist/web3/index.html +2 -2
- package/package.json +4 -3
- package/dist/chunk-XQ46F76G.js +0 -16
- package/dist/review-service-6G7MVTY6.js +0 -31
- package/dist/web3/assets/index-BYmOkm4p.js +0 -1052
- package/dist/web3/assets/index-p6uQyhml.css +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
buildTitleMap,
|
|
2
3
|
insertOrReplace,
|
|
3
4
|
nowExpr
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-L7UCMGWZ.js";
|
|
5
6
|
import {
|
|
6
7
|
getDb
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JMJLNEXE.js";
|
|
8
9
|
|
|
9
10
|
// src/governance/version-service.ts
|
|
10
11
|
import { createHash } from "crypto";
|
|
@@ -99,6 +100,15 @@ async function getCurrentVersion(nestId, nodeId) {
|
|
|
99
100
|
);
|
|
100
101
|
return row?.v || 0;
|
|
101
102
|
}
|
|
103
|
+
async function getTrackedNodeIds(nestId) {
|
|
104
|
+
const db = getDb();
|
|
105
|
+
const rows = await db.all(
|
|
106
|
+
`SELECT DISTINCT node_id FROM node_versions
|
|
107
|
+
WHERE nest_id = ? AND author NOT LIKE ?`,
|
|
108
|
+
[nestId, `${SYSTEM_AUTHOR_PREFIX}%`]
|
|
109
|
+
);
|
|
110
|
+
return new Set(rows.map((r) => r.node_id));
|
|
111
|
+
}
|
|
102
112
|
async function getApprovedVersion(nestId, nodeId) {
|
|
103
113
|
const db = getDb();
|
|
104
114
|
const row = await db.get(
|
|
@@ -107,6 +117,14 @@ async function getApprovedVersion(nestId, nodeId) {
|
|
|
107
117
|
);
|
|
108
118
|
return row?.approved_version ?? null;
|
|
109
119
|
}
|
|
120
|
+
async function getApprovedVersions(nestId) {
|
|
121
|
+
const db = getDb();
|
|
122
|
+
const rows = await db.all(
|
|
123
|
+
"SELECT node_id, approved_version FROM approved_versions WHERE nest_id = ?",
|
|
124
|
+
[nestId]
|
|
125
|
+
);
|
|
126
|
+
return new Map(rows.map((r) => [r.node_id, r.approved_version]));
|
|
127
|
+
}
|
|
110
128
|
async function setApprovedVersion(nestId, nodeId, version, approvedBy) {
|
|
111
129
|
const db = getDb();
|
|
112
130
|
const sql = insertOrReplace(
|
|
@@ -187,6 +205,59 @@ async function getDisplayStatus(nestId, nodeId) {
|
|
|
187
205
|
if (current.status === "rejected") return "rejected";
|
|
188
206
|
return "draft";
|
|
189
207
|
}
|
|
208
|
+
async function listMyDrafts(params) {
|
|
209
|
+
if (params.nestIds.length === 0) return { drafts: [], total: 0 };
|
|
210
|
+
const db = getDb();
|
|
211
|
+
const nestPlaceholders = params.nestIds.map(() => "?").join(",");
|
|
212
|
+
const where = `
|
|
213
|
+
WHERE v.author = ?
|
|
214
|
+
AND v.status IN ('draft', 'rejected')
|
|
215
|
+
AND v.nest_id IN (${nestPlaceholders})
|
|
216
|
+
AND v.version = (
|
|
217
|
+
SELECT MAX(v2.version) FROM node_versions v2
|
|
218
|
+
WHERE v2.nest_id = v.nest_id AND v2.node_id = v.node_id)
|
|
219
|
+
AND NOT EXISTS (
|
|
220
|
+
SELECT 1 FROM review_requests r
|
|
221
|
+
WHERE r.nest_id = v.nest_id AND r.node_id = v.node_id
|
|
222
|
+
AND r.status = 'pending')`;
|
|
223
|
+
const args = [params.authorEmail.toLowerCase(), ...params.nestIds];
|
|
224
|
+
const total = Number(
|
|
225
|
+
(await db.get(
|
|
226
|
+
`SELECT COUNT(*) as c FROM node_versions v ${where}`,
|
|
227
|
+
args
|
|
228
|
+
)).c
|
|
229
|
+
);
|
|
230
|
+
const limit = Math.min(Math.max(params.limit ?? 25, 1), 100);
|
|
231
|
+
const offset = Math.max(params.offset ?? 0, 0);
|
|
232
|
+
const rows = await db.all(
|
|
233
|
+
`SELECT v.nest_id, v.node_id, v.status, v.version, v.created_at
|
|
234
|
+
FROM node_versions v ${where}
|
|
235
|
+
ORDER BY v.created_at DESC
|
|
236
|
+
LIMIT ? OFFSET ?`,
|
|
237
|
+
[...args, limit, offset]
|
|
238
|
+
);
|
|
239
|
+
const titlesByNest = new Map(
|
|
240
|
+
await Promise.all(
|
|
241
|
+
[...new Set(rows.map((r) => r.nest_id))].map(
|
|
242
|
+
async (nestId) => [
|
|
243
|
+
nestId,
|
|
244
|
+
await buildTitleMap(nestId)
|
|
245
|
+
]
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
const drafts = rows.filter((r) => titlesByNest.get(r.nest_id)?.has(r.node_id)).map((r) => ({
|
|
250
|
+
nestId: r.nest_id,
|
|
251
|
+
nodeId: r.node_id,
|
|
252
|
+
title: titlesByNest.get(r.nest_id).get(r.node_id) || r.node_id,
|
|
253
|
+
status: r.status,
|
|
254
|
+
// Number(): Postgres returns INTEGER columns fine, but stay explicit —
|
|
255
|
+
// the modal renders this straight into copy.
|
|
256
|
+
version: Number(r.version),
|
|
257
|
+
updatedAt: r.created_at
|
|
258
|
+
}));
|
|
259
|
+
return { drafts, total };
|
|
260
|
+
}
|
|
190
261
|
function rowToVersion(row) {
|
|
191
262
|
return {
|
|
192
263
|
version: row.version,
|
|
@@ -208,9 +279,12 @@ export {
|
|
|
208
279
|
getVersions,
|
|
209
280
|
getVersion,
|
|
210
281
|
getCurrentVersion,
|
|
282
|
+
getTrackedNodeIds,
|
|
211
283
|
getApprovedVersion,
|
|
284
|
+
getApprovedVersions,
|
|
212
285
|
setApprovedVersion,
|
|
213
286
|
checkConflict,
|
|
214
287
|
getNodeTags,
|
|
215
|
-
getDisplayStatus
|
|
288
|
+
getDisplayStatus,
|
|
289
|
+
listMyDrafts
|
|
216
290
|
};
|
|
@@ -512,6 +512,30 @@ var config = {
|
|
|
512
512
|
if ((process.env.AUTH_MODE || "key") === "open") return "*";
|
|
513
513
|
return ["http://localhost:5173", "http://localhost:3838"];
|
|
514
514
|
},
|
|
515
|
+
/**
|
|
516
|
+
* Who may frame this server, as a CSP `frame-ancestors` source list.
|
|
517
|
+
*
|
|
518
|
+
* Defaults to `'self'`: no other site can embed the UI, which is what stops
|
|
519
|
+
* a hostile page from framing it invisibly and stealing clicks from a
|
|
520
|
+
* signed-in operator. Deployments that are *meant* to be embedded name the
|
|
521
|
+
* embedding origin — e.g. the PromptOwl Data Room iframes this server, so
|
|
522
|
+
* that install sets `FRAME_ANCESTORS="https://app.promptowl.ai"`.
|
|
523
|
+
*
|
|
524
|
+
* Comma-separated; `'self'` is always included. `*` allows any site to frame
|
|
525
|
+
* this server and disables the protection.
|
|
526
|
+
*
|
|
527
|
+
* Note we deliberately do NOT also send `X-Frame-Options` — it only
|
|
528
|
+
* understands "same origin", so it would block a sibling subdomain that
|
|
529
|
+
* `frame-ancestors` is configured to allow. Every browser that matters
|
|
530
|
+
* honours `frame-ancestors`, and it wins where both are present.
|
|
531
|
+
*/
|
|
532
|
+
get FRAME_ANCESTORS() {
|
|
533
|
+
const raw = process.env.FRAME_ANCESTORS?.trim();
|
|
534
|
+
if (!raw) return "'self'";
|
|
535
|
+
if (raw === "*") return "*";
|
|
536
|
+
const sources = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
537
|
+
return [.../* @__PURE__ */ new Set(["'self'", ...sources])].join(" ");
|
|
538
|
+
},
|
|
515
539
|
/**
|
|
516
540
|
* Max JSON body size in bytes. Prevents DoS via giant payloads.
|
|
517
541
|
* Default: 10 MB. Override via MAX_BODY_BYTES env.
|
|
@@ -711,6 +735,12 @@ function runMigrations(db) {
|
|
|
711
735
|
if (!nestCols.includes("allow_self_approve")) {
|
|
712
736
|
db.exec("ALTER TABLE nests ADD COLUMN allow_self_approve INTEGER NOT NULL DEFAULT 0");
|
|
713
737
|
}
|
|
738
|
+
if (!nestCols.includes("prime_only_review")) {
|
|
739
|
+
db.exec("ALTER TABLE nests ADD COLUMN prime_only_review INTEGER NOT NULL DEFAULT 0");
|
|
740
|
+
}
|
|
741
|
+
if (!nestCols.includes("prime_tags")) {
|
|
742
|
+
db.exec("ALTER TABLE nests ADD COLUMN prime_tags TEXT NOT NULL DEFAULT 'prime-document'");
|
|
743
|
+
}
|
|
714
744
|
const userCols = db.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
|
|
715
745
|
if (!userCols.includes("is_admin")) {
|
|
716
746
|
db.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
|
|
@@ -1454,6 +1484,53 @@ function runMigrations(db) {
|
|
|
1454
1484
|
})();
|
|
1455
1485
|
recordMigration("031_team_source");
|
|
1456
1486
|
}
|
|
1487
|
+
if (!hasMigration("032_deletion_requests")) {
|
|
1488
|
+
db.transaction(() => {
|
|
1489
|
+
db.exec(`
|
|
1490
|
+
CREATE TABLE IF NOT EXISTS deletion_requests (
|
|
1491
|
+
id TEXT PRIMARY KEY,
|
|
1492
|
+
nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
|
|
1493
|
+
node_id TEXT NOT NULL,
|
|
1494
|
+
requested_by TEXT NOT NULL, -- email
|
|
1495
|
+
reason TEXT NOT NULL,
|
|
1496
|
+
status TEXT NOT NULL DEFAULT 'pending'
|
|
1497
|
+
CHECK(status IN ('pending', 'declined')),
|
|
1498
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1499
|
+
resolved_by TEXT, -- email
|
|
1500
|
+
resolved_at TEXT,
|
|
1501
|
+
resolution_note TEXT
|
|
1502
|
+
);
|
|
1503
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_deletion_requests_pending
|
|
1504
|
+
ON deletion_requests(nest_id, node_id) WHERE status = 'pending';
|
|
1505
|
+
CREATE INDEX IF NOT EXISTS idx_deletion_requests_nest
|
|
1506
|
+
ON deletion_requests(nest_id, status);
|
|
1507
|
+
`);
|
|
1508
|
+
})();
|
|
1509
|
+
recordMigration("032_deletion_requests");
|
|
1510
|
+
}
|
|
1511
|
+
if (!hasMigration("033_deletion_request_targets")) {
|
|
1512
|
+
db.transaction(() => {
|
|
1513
|
+
const cols = db.prepare("PRAGMA table_info(deletion_requests)").all().map((c) => c.name);
|
|
1514
|
+
if (!cols.includes("target_type")) {
|
|
1515
|
+
db.exec(
|
|
1516
|
+
"ALTER TABLE deletion_requests ADD COLUMN target_type TEXT NOT NULL DEFAULT 'document'"
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
db.exec(`
|
|
1520
|
+
DROP INDEX IF EXISTS idx_deletion_requests_pending;
|
|
1521
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_deletion_requests_pending
|
|
1522
|
+
ON deletion_requests(nest_id, target_type, node_id) WHERE status = 'pending';
|
|
1523
|
+
`);
|
|
1524
|
+
})();
|
|
1525
|
+
recordMigration("033_deletion_request_targets");
|
|
1526
|
+
}
|
|
1527
|
+
if (!hasMigration("034_versions_author_status_index")) {
|
|
1528
|
+
db.exec(`
|
|
1529
|
+
CREATE INDEX IF NOT EXISTS idx_versions_author_status
|
|
1530
|
+
ON node_versions(author, status);
|
|
1531
|
+
`);
|
|
1532
|
+
recordMigration("034_versions_author_status_index");
|
|
1533
|
+
}
|
|
1457
1534
|
}
|
|
1458
1535
|
function mergeCaseCollidingUsers(db) {
|
|
1459
1536
|
const groups = db.prepare(
|
|
@@ -1671,7 +1748,7 @@ async function initDb() {
|
|
|
1671
1748
|
if (config.DB_DRIVER === "postgres") {
|
|
1672
1749
|
const { Pool } = await import("pg");
|
|
1673
1750
|
const { PostgresAdapter } = await import("./adapter.postgres-YOODX2BI.js");
|
|
1674
|
-
const { runPostgresMigrations } = await import("./migrations.postgres-
|
|
1751
|
+
const { runPostgresMigrations } = await import("./migrations.postgres-CYKO5CFV.js");
|
|
1675
1752
|
const pool = new Pool(buildPgConfig());
|
|
1676
1753
|
adapter = new PostgresAdapter(pool);
|
|
1677
1754
|
await runPostgresMigrations(adapter);
|