@promptowl/contextnest-community 1.18.0 → 1.20.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 +83 -1
- package/dist/{chunk-QUZXRLUA.js → chunk-3LUUJUWS.js} +2 -2
- package/dist/{chunk-FUUJG6XP.js → chunk-7CPP6FIU.js} +256 -40
- package/dist/{chunk-OOBZG6BW.js → chunk-EY7WOKJR.js} +3 -3
- package/dist/{chunk-HGN45I5P.js → chunk-GVUBZTI3.js} +27 -185
- package/dist/{chunk-34UXRZXD.js → chunk-KHMSDTJJ.js} +2 -2
- package/dist/{chunk-TQACHVVA.js → chunk-O5DYL7ZG.js} +3 -3
- package/dist/{chunk-HYSTFMFG.js → chunk-XCFKS65Y.js} +107 -1
- package/dist/{chunk-GUNJTORH.js → chunk-YVMSM7LS.js} +14 -1
- package/dist/{client-SW5I6ZBK.js → client-7Z2IVCPW.js} +1 -1
- package/dist/{engine-34E3YNX7.js → engine-VZTSDUPB.js} +3 -3
- package/dist/{external-edit-service-6Q6SHEOF.js → external-edit-service-3FVK3OD7.js} +4 -4
- package/dist/{grants-service-ME7HIVHU.js → grants-service-WC27TRY3.js} +3 -3
- package/dist/index.js +2294 -1106
- package/dist/{migrations.postgres-5VI4RDTS.js → migrations.postgres-L42OXR7K.js} +47 -1
- package/dist/{review-service-H2M5ACPQ.js → review-service-TJRHRMMZ.js} +7 -7
- package/dist/{stewardship-service-U4CKL7Y5.js → stewardship-service-HRVD7SIS.js} +4 -4
- package/dist/{version-service-JWEN5UUW.js → version-service-5PIFT2OY.js} +4 -4
- package/dist/web3/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/dist/web3/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/dist/web3/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/dist/web3/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/dist/web3/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/dist/web3/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/dist/web3/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/dist/web3/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/dist/web3/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/dist/web3/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/dist/web3/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/dist/web3/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/dist/web3/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/dist/web3/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/dist/web3/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/dist/web3/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/dist/web3/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/dist/web3/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/dist/web3/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/dist/web3/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/dist/web3/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/dist/web3/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/dist/web3/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/dist/web3/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/dist/web3/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/dist/web3/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/dist/web3/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/dist/web3/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/dist/web3/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/dist/web3/assets/index-D4UG3T04.js +1365 -0
- package/dist/web3/assets/index-Dw1UTDIk.css +1 -0
- package/dist/web3/index.html +2 -2
- package/package.json +7 -2
- package/dist/web3/assets/index-DONv5_Ni.css +0 -1
- package/dist/web3/assets/index-jSGrnboh.js +0 -1065
|
@@ -4,19 +4,19 @@ import {
|
|
|
4
4
|
resolveNestWideRoles,
|
|
5
5
|
resolveStewardsForNode,
|
|
6
6
|
stewardCoverageForUser
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-O5DYL7ZG.js";
|
|
8
8
|
import {
|
|
9
9
|
grantCoversNode,
|
|
10
10
|
listUserGrants,
|
|
11
11
|
resolveNodeGrant
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3LUUJUWS.js";
|
|
13
13
|
import {
|
|
14
14
|
createVersion,
|
|
15
15
|
getApprovedVersion,
|
|
16
16
|
getApprovedVersions,
|
|
17
17
|
getCurrentVersion,
|
|
18
18
|
setApprovedVersion
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-KHMSDTJJ.js";
|
|
20
20
|
import {
|
|
21
21
|
buildDocContext,
|
|
22
22
|
buildTitleMap,
|
|
@@ -28,191 +28,27 @@ import {
|
|
|
28
28
|
isStewardshipEnabled,
|
|
29
29
|
nestName,
|
|
30
30
|
notifyEmailForNest,
|
|
31
|
+
notifyReviewRequested,
|
|
32
|
+
notifyReviewResolved,
|
|
31
33
|
nowExpr,
|
|
32
34
|
opContext,
|
|
33
|
-
permissionLabel,
|
|
34
35
|
prettyFolderPath,
|
|
35
36
|
resolveNestPermission,
|
|
36
37
|
sendEmailToRecipient,
|
|
37
38
|
titleForNode
|
|
38
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-7CPP6FIU.js";
|
|
39
40
|
import {
|
|
40
41
|
ConflictError,
|
|
41
42
|
NotFoundError,
|
|
42
43
|
ValidationError
|
|
43
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-YVMSM7LS.js";
|
|
44
45
|
import {
|
|
45
46
|
config,
|
|
46
47
|
getDb
|
|
47
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-XCFKS65Y.js";
|
|
48
49
|
|
|
49
50
|
// src/governance/review-service.ts
|
|
50
|
-
import { v4 as
|
|
51
|
-
|
|
52
|
-
// src/governance/notify-service.ts
|
|
53
|
-
import { v4 as uuid } from "uuid";
|
|
54
|
-
async function listWatchers(nestId, nodeId) {
|
|
55
|
-
return await getDb().all(
|
|
56
|
-
`SELECT id, node_id, user_email, created_by, created_at
|
|
57
|
-
FROM watchers WHERE nest_id = ? AND node_id = ? ORDER BY created_at`,
|
|
58
|
-
[nestId, nodeId]
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
async function addWatcher(nestId, nodeId, userEmail, createdBy) {
|
|
62
|
-
await getDb().run(
|
|
63
|
-
`INSERT INTO watchers (id, nest_id, node_id, user_email, created_by, created_at)
|
|
64
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
65
|
-
ON CONFLICT(nest_id, node_id, user_email) DO NOTHING`,
|
|
66
|
-
[uuid(), nestId, nodeId, userEmail.toLowerCase(), createdBy, (/* @__PURE__ */ new Date()).toISOString()]
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
async function removeWatcher(nestId, nodeId, userEmail) {
|
|
70
|
-
const db = getDb();
|
|
71
|
-
const row = await db.get(
|
|
72
|
-
"SELECT id FROM watchers WHERE nest_id = ? AND node_id = ? AND LOWER(user_email) = LOWER(?)",
|
|
73
|
-
[nestId, nodeId, userEmail]
|
|
74
|
-
);
|
|
75
|
-
if (!row) return false;
|
|
76
|
-
await db.run("DELETE FROM watchers WHERE id = ?", [row.id]);
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
async function notifyReviewRequested(params) {
|
|
80
|
-
const { nestId, nodeId, requestedBy, reviewId } = params;
|
|
81
|
-
const db = getDb();
|
|
82
|
-
try {
|
|
83
|
-
const rows = await db.all(
|
|
84
|
-
`SELECT DISTINCT w.user_email, w.node_id AS via
|
|
85
|
-
FROM watchers w
|
|
86
|
-
WHERE w.nest_id = ?
|
|
87
|
-
AND (w.node_id = ?
|
|
88
|
-
OR w.node_id IN (
|
|
89
|
-
SELECT e.from_node FROM edges e
|
|
90
|
-
JOIN edge_types t ON t.id = e.type_id
|
|
91
|
-
WHERE e.nest_id = ? AND e.to_node = ? AND t.is_flow = 1
|
|
92
|
-
))`,
|
|
93
|
-
[nestId, nodeId, nestId, nodeId]
|
|
94
|
-
);
|
|
95
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
96
|
-
const label = await describeNode(nestId, nodeId);
|
|
97
|
-
let created = 0;
|
|
98
|
-
for (const r of rows) {
|
|
99
|
-
if (r.user_email.toLowerCase() === requestedBy.toLowerCase()) continue;
|
|
100
|
-
await db.run(
|
|
101
|
-
`INSERT INTO notifications (id, nest_id, user_email, kind, subject_id, message, created_at)
|
|
102
|
-
VALUES (?, ?, ?, 'review_requested', ?, ?, ?)`,
|
|
103
|
-
[
|
|
104
|
-
uuid(),
|
|
105
|
-
nestId,
|
|
106
|
-
r.user_email.toLowerCase(),
|
|
107
|
-
reviewId,
|
|
108
|
-
r.via === nodeId ? `"${label}" has a new version awaiting review (submitted by ${requestedBy})` : `Agent "${await describeNode(nestId, r.via)}" produced output on "${label}" \u2014 awaiting review`,
|
|
109
|
-
now
|
|
110
|
-
]
|
|
111
|
-
);
|
|
112
|
-
created++;
|
|
113
|
-
}
|
|
114
|
-
return created;
|
|
115
|
-
} catch (err) {
|
|
116
|
-
console.error("[notify] review fan-out failed", nestId, nodeId, err);
|
|
117
|
-
return 0;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function notifyReviewResolved(params) {
|
|
121
|
-
const { nestId, nodeId, status, resolvedBy, requestedBy, reviewId } = params;
|
|
122
|
-
const db = getDb();
|
|
123
|
-
try {
|
|
124
|
-
const rows = await db.all(
|
|
125
|
-
`SELECT DISTINCT w.user_email
|
|
126
|
-
FROM watchers w
|
|
127
|
-
WHERE w.nest_id = ?
|
|
128
|
-
AND (w.node_id = ?
|
|
129
|
-
OR w.node_id IN (
|
|
130
|
-
SELECT e.from_node FROM edges e
|
|
131
|
-
JOIN edge_types t ON t.id = e.type_id
|
|
132
|
-
WHERE e.nest_id = ? AND e.to_node = ? AND t.is_flow = 1
|
|
133
|
-
))`,
|
|
134
|
-
[nestId, nodeId, nestId, nodeId]
|
|
135
|
-
);
|
|
136
|
-
const recipients = new Set(rows.map((r) => r.user_email.toLowerCase()));
|
|
137
|
-
recipients.add(requestedBy.toLowerCase());
|
|
138
|
-
recipients.delete(resolvedBy.toLowerCase());
|
|
139
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
140
|
-
const label = await describeNode(nestId, nodeId);
|
|
141
|
-
let created = 0;
|
|
142
|
-
for (const email of recipients) {
|
|
143
|
-
await db.run(
|
|
144
|
-
`INSERT INTO notifications (id, nest_id, user_email, kind, subject_id, message, created_at)
|
|
145
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
146
|
-
[
|
|
147
|
-
uuid(),
|
|
148
|
-
nestId,
|
|
149
|
-
email,
|
|
150
|
-
`review_${status}`,
|
|
151
|
-
reviewId,
|
|
152
|
-
`"${label}" was ${status} by ${resolvedBy}`,
|
|
153
|
-
now
|
|
154
|
-
]
|
|
155
|
-
);
|
|
156
|
-
created++;
|
|
157
|
-
}
|
|
158
|
-
return created;
|
|
159
|
-
} catch (err) {
|
|
160
|
-
console.error("[notify] resolution fan-out failed", nestId, nodeId, err);
|
|
161
|
-
return 0;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
async function notifyNestInvite(params) {
|
|
165
|
-
const { nestId, inviteeEmail, permission, nestName: nestName2 } = params;
|
|
166
|
-
try {
|
|
167
|
-
await getDb().run(
|
|
168
|
-
`INSERT INTO notifications (id, nest_id, user_email, kind, subject_id, message, created_at)
|
|
169
|
-
VALUES (?, ?, ?, 'nest_invite', ?, ?, ?)`,
|
|
170
|
-
[
|
|
171
|
-
uuid(),
|
|
172
|
-
nestId,
|
|
173
|
-
inviteeEmail.toLowerCase(),
|
|
174
|
-
nestId,
|
|
175
|
-
`You were added to "${nestName2}" as ${permissionLabel(permission)}`,
|
|
176
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
177
|
-
]
|
|
178
|
-
);
|
|
179
|
-
} catch (err) {
|
|
180
|
-
console.error("[notify] invite inbox insert failed", nestId, inviteeEmail, err);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
async function listNotifications(userEmail, opts = {}) {
|
|
184
|
-
const limit = Math.min(opts.limit ?? 50, 200);
|
|
185
|
-
return await getDb().all(
|
|
186
|
-
`SELECT n.id, n.nest_id, n.kind, n.subject_id, n.message, n.created_at, n.read_at,
|
|
187
|
-
COALESCE(
|
|
188
|
-
rr.node_id,
|
|
189
|
-
CASE WHEN dr.target_type = 'document' THEN dr.node_id END
|
|
190
|
-
) AS node_id
|
|
191
|
-
FROM notifications n
|
|
192
|
-
LEFT JOIN review_requests rr ON rr.id = n.subject_id
|
|
193
|
-
LEFT JOIN deletion_requests dr ON dr.id = n.subject_id
|
|
194
|
-
WHERE LOWER(n.user_email) = LOWER(?)${opts.unreadOnly ? " AND n.read_at IS NULL" : ""}
|
|
195
|
-
ORDER BY n.created_at DESC LIMIT ${limit}`,
|
|
196
|
-
[userEmail]
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
async function markNotificationsRead(userEmail, ids) {
|
|
200
|
-
const db = getDb();
|
|
201
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
202
|
-
if (ids === "all") {
|
|
203
|
-
await db.run(
|
|
204
|
-
"UPDATE notifications SET read_at = ? WHERE LOWER(user_email) = LOWER(?) AND read_at IS NULL",
|
|
205
|
-
[now, userEmail]
|
|
206
|
-
);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
for (const id of ids.slice(0, 200)) {
|
|
210
|
-
await db.run(
|
|
211
|
-
"UPDATE notifications SET read_at = ? WHERE id = ? AND LOWER(user_email) = LOWER(?)",
|
|
212
|
-
[now, id, userEmail]
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
51
|
+
import { v4 as uuid2 } from "uuid";
|
|
216
52
|
|
|
217
53
|
// src/workflow/env-routes.ts
|
|
218
54
|
import { Hono as Hono2 } from "hono";
|
|
@@ -270,7 +106,7 @@ async function filterAccessible(nestId, userId, userEmail, nodes, approvedVersio
|
|
|
270
106
|
|
|
271
107
|
// src/workflow/edge-type-routes.ts
|
|
272
108
|
import { Hono } from "hono";
|
|
273
|
-
import { v4 as
|
|
109
|
+
import { v4 as uuid } from "uuid";
|
|
274
110
|
|
|
275
111
|
// src/shared/json.ts
|
|
276
112
|
var safeJson = (s) => {
|
|
@@ -363,7 +199,7 @@ async function seedDefaultEdgeTypes(nestId) {
|
|
|
363
199
|
(id, nest_id, name, description, direction, is_flow, condition_schema, color, created_by, created_at, updated_at)
|
|
364
200
|
VALUES (?, ?, ?, ?, 'directed', ?, NULL, ?, ?, ?, ?)
|
|
365
201
|
ON CONFLICT DO NOTHING`,
|
|
366
|
-
[
|
|
202
|
+
[uuid(), nestId, name, description, isFlow, color, createdBy, now, now]
|
|
367
203
|
);
|
|
368
204
|
}
|
|
369
205
|
}
|
|
@@ -451,7 +287,7 @@ edgeTypeRoutes.post("/", requireWorkflowPlane, async (c) => {
|
|
|
451
287
|
`INSERT INTO edge_types
|
|
452
288
|
(id, nest_id, name, description, direction, is_flow, condition_schema, color, created_by, created_at, updated_at)
|
|
453
289
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
454
|
-
[
|
|
290
|
+
[uuid(), nestId, name, body.description.trim(), direction, isFlow, conditionSchema, color, definedBy, now, now]
|
|
455
291
|
);
|
|
456
292
|
}
|
|
457
293
|
});
|
|
@@ -869,7 +705,7 @@ async function submitForReview(params) {
|
|
|
869
705
|
if (existing) {
|
|
870
706
|
throw new Error("A review is already pending for this node");
|
|
871
707
|
}
|
|
872
|
-
const id =
|
|
708
|
+
const id = uuid2();
|
|
873
709
|
await db.run(
|
|
874
710
|
`INSERT INTO review_requests
|
|
875
711
|
(id, nest_id, node_id, version, requested_by, request_note, priority)
|
|
@@ -1168,6 +1004,18 @@ async function cancelReview(params) {
|
|
|
1168
1004
|
[params.nestId, params.nodeId]
|
|
1169
1005
|
);
|
|
1170
1006
|
if (!pending) return null;
|
|
1007
|
+
if (String(pending.requested_by).toLowerCase() !== params.cancelledBy.toLowerCase()) {
|
|
1008
|
+
const verdict = await canUserApprove(
|
|
1009
|
+
params.nestId,
|
|
1010
|
+
params.nodeId,
|
|
1011
|
+
params.cancelledBy
|
|
1012
|
+
);
|
|
1013
|
+
if (!verdict.allowed) {
|
|
1014
|
+
throw new Error(
|
|
1015
|
+
"Only the submitter or a reviewer can withdraw this review"
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1171
1019
|
await db.run(
|
|
1172
1020
|
`UPDATE review_requests
|
|
1173
1021
|
SET status = 'cancelled', resolved_by = ?, resolved_at = ${nowExpr(db)}
|
|
@@ -1282,7 +1130,7 @@ async function inboxInsert(nestId, recipients, kind, subjectId, message) {
|
|
|
1282
1130
|
await db.run(
|
|
1283
1131
|
`INSERT INTO notifications (id, nest_id, user_email, kind, subject_id, message, created_at)
|
|
1284
1132
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
1285
|
-
[
|
|
1133
|
+
[uuid2(), nestId, email.toLowerCase(), kind, subjectId, message, now]
|
|
1286
1134
|
);
|
|
1287
1135
|
} catch (err) {
|
|
1288
1136
|
console.error("[notify] deletion inbox insert failed", nestId, email, err);
|
|
@@ -1420,7 +1268,7 @@ async function requestDeletion(params) {
|
|
|
1420
1268
|
if (existing) {
|
|
1421
1269
|
throw new ConflictError(`A deletion request is already pending for this ${noun}`);
|
|
1422
1270
|
}
|
|
1423
|
-
const id =
|
|
1271
|
+
const id = uuid2();
|
|
1424
1272
|
try {
|
|
1425
1273
|
await db.run(
|
|
1426
1274
|
`INSERT INTO deletion_requests (id, nest_id, node_id, target_type, requested_by, reason)
|
|
@@ -1584,12 +1432,6 @@ function rowToReviewRequest(row) {
|
|
|
1584
1432
|
}
|
|
1585
1433
|
|
|
1586
1434
|
export {
|
|
1587
|
-
listWatchers,
|
|
1588
|
-
addWatcher,
|
|
1589
|
-
removeWatcher,
|
|
1590
|
-
notifyNestInvite,
|
|
1591
|
-
listNotifications,
|
|
1592
|
-
markNotificationsRead,
|
|
1593
1435
|
resolveCallerEmail,
|
|
1594
1436
|
canReadNode,
|
|
1595
1437
|
canRequestDeletion,
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
buildTitleMap,
|
|
3
3
|
insertOrReplace,
|
|
4
4
|
nowExpr
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7CPP6FIU.js";
|
|
6
6
|
import {
|
|
7
7
|
getDb
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-XCFKS65Y.js";
|
|
9
9
|
|
|
10
10
|
// src/governance/version-service.ts
|
|
11
11
|
import { createHash } from "crypto";
|
|
@@ -11,16 +11,16 @@ import {
|
|
|
11
11
|
resolveNestPermission,
|
|
12
12
|
resolveTeamRolesForUser,
|
|
13
13
|
sendEmailToRecipient
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-7CPP6FIU.js";
|
|
15
15
|
import {
|
|
16
16
|
ConflictError,
|
|
17
17
|
ValidationError
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-YVMSM7LS.js";
|
|
19
19
|
import {
|
|
20
20
|
config,
|
|
21
21
|
getDb,
|
|
22
22
|
isEmailish
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-XCFKS65Y.js";
|
|
24
24
|
|
|
25
25
|
// src/governance/stewardship-service.ts
|
|
26
26
|
import { v4 as uuid } from "uuid";
|
|
@@ -176,6 +176,58 @@ var config = {
|
|
|
176
176
|
get PUBLIC_BASE_URL() {
|
|
177
177
|
return (process.env.PUBLIC_BASE_URL || "").replace(/\/$/, "");
|
|
178
178
|
},
|
|
179
|
+
/**
|
|
180
|
+
* Dedicated secret this deployment uses to SIGN the short-lived MCP tickets
|
|
181
|
+
* minted by the SSO token-exchange (POST /auth/token-exchange). Kept distinct
|
|
182
|
+
* from OFFICIAL_COMMUNITY_SSO_SECRET so a SELF-HOSTED server can issue its own
|
|
183
|
+
* MCP bearers without the official PromptOwl trust relationship. The MCP
|
|
184
|
+
* acceptor verifies a ticket signed with EITHER secret. Unset ⇒ token-exchange
|
|
185
|
+
* cannot mint (and the acceptor simply doesn't offer this key).
|
|
186
|
+
*/
|
|
187
|
+
get MCP_SIGNING_SECRET() {
|
|
188
|
+
return process.env.MCP_SIGNING_SECRET || "";
|
|
189
|
+
},
|
|
190
|
+
/**
|
|
191
|
+
* Master switch for the SSO token-exchange endpoint. Default OFF — a
|
|
192
|
+
* deployment must deliberately enable it (and set MCP_SIGNING_SECRET + at
|
|
193
|
+
* least one provider) before /auth/token-exchange will mint anything.
|
|
194
|
+
*/
|
|
195
|
+
get SSO_TOKEN_EXCHANGE_ENABLED() {
|
|
196
|
+
return process.env.SSO_TOKEN_EXCHANGE_ENABLED === "true";
|
|
197
|
+
},
|
|
198
|
+
get MS_TOKEN_EXCHANGE_ENABLED() {
|
|
199
|
+
return process.env.MS_TOKEN_EXCHANGE_ENABLED === "true";
|
|
200
|
+
},
|
|
201
|
+
get GOOGLE_TOKEN_EXCHANGE_ENABLED() {
|
|
202
|
+
return process.env.GOOGLE_TOKEN_EXCHANGE_ENABLED === "true";
|
|
203
|
+
},
|
|
204
|
+
/**
|
|
205
|
+
* Microsoft Entra: the app (client) id inbound tokens must be issued for
|
|
206
|
+
* (audience), and the tenant id used to build the expected issuer
|
|
207
|
+
* https://login.microsoftonline.com/<tenant>/v2.0.
|
|
208
|
+
*/
|
|
209
|
+
get MS_CLIENT_ID() {
|
|
210
|
+
return (process.env.MS_CLIENT_ID || "").trim();
|
|
211
|
+
},
|
|
212
|
+
get MS_TENANT_ID() {
|
|
213
|
+
return (process.env.MS_TENANT_ID || "").trim();
|
|
214
|
+
},
|
|
215
|
+
/**
|
|
216
|
+
* Google: the OAuth client id inbound tokens must be issued for (audience).
|
|
217
|
+
* Issuer is fixed at https://accounts.google.com.
|
|
218
|
+
*/
|
|
219
|
+
get GOOGLE_CLIENT_ID() {
|
|
220
|
+
return (process.env.GOOGLE_CLIENT_ID || "").trim();
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
* Lifetime of a minted MCP ticket, in seconds. Clamped to [30, 3600]; short
|
|
224
|
+
* by design so a leaked exchange token expires quickly. Default 300.
|
|
225
|
+
*/
|
|
226
|
+
get MCP_TOKEN_TTL_SECONDS() {
|
|
227
|
+
const raw = parseInt(process.env.MCP_TOKEN_TTL_SECONDS || "", 10);
|
|
228
|
+
if (!Number.isFinite(raw)) return 300;
|
|
229
|
+
return Math.max(30, Math.min(3600, raw));
|
|
230
|
+
},
|
|
179
231
|
/**
|
|
180
232
|
* Restrict "Sign in with PromptOwl":
|
|
181
233
|
* "open" — anyone may sign in with PromptOwl (default)
|
|
@@ -744,6 +796,12 @@ function runMigrations(db) {
|
|
|
744
796
|
if (!nestCols.includes("index_synced_at")) {
|
|
745
797
|
db.exec("ALTER TABLE nests ADD COLUMN index_synced_at TEXT");
|
|
746
798
|
}
|
|
799
|
+
if (!nestCols.includes("reader_mode")) {
|
|
800
|
+
db.exec("ALTER TABLE nests ADD COLUMN reader_mode INTEGER NOT NULL DEFAULT 0");
|
|
801
|
+
}
|
|
802
|
+
if (!nestCols.includes("reader_home_node")) {
|
|
803
|
+
db.exec("ALTER TABLE nests ADD COLUMN reader_home_node TEXT");
|
|
804
|
+
}
|
|
747
805
|
const userCols = db.prepare("PRAGMA table_info(users)").all().map((c) => c.name);
|
|
748
806
|
if (!userCols.includes("is_admin")) {
|
|
749
807
|
db.exec("ALTER TABLE users ADD COLUMN is_admin INTEGER NOT NULL DEFAULT 0");
|
|
@@ -1572,6 +1630,54 @@ function runMigrations(db) {
|
|
|
1572
1630
|
})();
|
|
1573
1631
|
recordMigration("036_node_index");
|
|
1574
1632
|
}
|
|
1633
|
+
if (!hasMigration("037_publish")) {
|
|
1634
|
+
db.transaction(() => {
|
|
1635
|
+
db.exec(`
|
|
1636
|
+
CREATE TABLE IF NOT EXISTS publish (
|
|
1637
|
+
id TEXT PRIMARY KEY,
|
|
1638
|
+
nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
|
|
1639
|
+
node_id TEXT NOT NULL,
|
|
1640
|
+
slug TEXT NOT NULL,
|
|
1641
|
+
access_mode TEXT NOT NULL DEFAULT 'public'
|
|
1642
|
+
CHECK(access_mode IN ('public','email','code','invite')),
|
|
1643
|
+
code_hash TEXT,
|
|
1644
|
+
comments_enabled INTEGER NOT NULL DEFAULT 0,
|
|
1645
|
+
watermark INTEGER NOT NULL DEFAULT 0,
|
|
1646
|
+
no_download INTEGER NOT NULL DEFAULT 0,
|
|
1647
|
+
created_by TEXT NOT NULL,
|
|
1648
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1649
|
+
expires_at TEXT,
|
|
1650
|
+
revoked_at TEXT
|
|
1651
|
+
);
|
|
1652
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_publish_slug ON publish(slug);
|
|
1653
|
+
CREATE INDEX IF NOT EXISTS idx_publish_nest ON publish(nest_id, node_id);
|
|
1654
|
+
|
|
1655
|
+
CREATE TABLE IF NOT EXISTS access_grant (
|
|
1656
|
+
id TEXT PRIMARY KEY,
|
|
1657
|
+
publish_id TEXT NOT NULL REFERENCES publish(id) ON DELETE CASCADE,
|
|
1658
|
+
kind TEXT NOT NULL
|
|
1659
|
+
CHECK(kind IN ('invite','link','session','view')),
|
|
1660
|
+
email TEXT,
|
|
1661
|
+
token_hash TEXT,
|
|
1662
|
+
expires_at TEXT,
|
|
1663
|
+
used_at TEXT,
|
|
1664
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1665
|
+
);
|
|
1666
|
+
CREATE INDEX IF NOT EXISTS idx_access_grant_publish ON access_grant(publish_id, kind);
|
|
1667
|
+
CREATE INDEX IF NOT EXISTS idx_access_grant_token ON access_grant(token_hash);
|
|
1668
|
+
`);
|
|
1669
|
+
})();
|
|
1670
|
+
recordMigration("037_publish");
|
|
1671
|
+
}
|
|
1672
|
+
{
|
|
1673
|
+
const publishCols = db.prepare("PRAGMA table_info(publish)").all().map((c) => c.name);
|
|
1674
|
+
if (publishCols.length && !publishCols.includes("watermark")) {
|
|
1675
|
+
db.exec("ALTER TABLE publish ADD COLUMN watermark INTEGER NOT NULL DEFAULT 0");
|
|
1676
|
+
}
|
|
1677
|
+
if (publishCols.length && !publishCols.includes("no_download")) {
|
|
1678
|
+
db.exec("ALTER TABLE publish ADD COLUMN no_download INTEGER NOT NULL DEFAULT 0");
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1575
1681
|
}
|
|
1576
1682
|
function mergeCaseCollidingUsers(db) {
|
|
1577
1683
|
const groups = db.prepare(
|
|
@@ -1789,7 +1895,7 @@ async function initDb() {
|
|
|
1789
1895
|
if (config.DB_DRIVER === "postgres") {
|
|
1790
1896
|
const { Pool } = await import("pg");
|
|
1791
1897
|
const { PostgresAdapter } = await import("./adapter.postgres-YOODX2BI.js");
|
|
1792
|
-
const { runPostgresMigrations } = await import("./migrations.postgres-
|
|
1898
|
+
const { runPostgresMigrations } = await import("./migrations.postgres-L42OXR7K.js");
|
|
1793
1899
|
const pool = new Pool(buildPgConfig());
|
|
1794
1900
|
adapter = new PostgresAdapter(pool);
|
|
1795
1901
|
await runPostgresMigrations(adapter);
|
|
@@ -43,6 +43,17 @@ var LockedError = class extends AppError {
|
|
|
43
43
|
this.name = "LockedError";
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
+
var VaultBusyError = class extends AppError {
|
|
47
|
+
retryAfterSeconds = 5;
|
|
48
|
+
constructor(message = "The vault is busy with another write. Retry shortly.") {
|
|
49
|
+
super(503, message);
|
|
50
|
+
this.name = "VaultBusyError";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function isVaultLockTimeout(err) {
|
|
54
|
+
const e = err;
|
|
55
|
+
return e?.name === "VaultLockTimeoutError" || e?.code === "VAULT_LOCK_TIMEOUT";
|
|
56
|
+
}
|
|
46
57
|
|
|
47
58
|
export {
|
|
48
59
|
AppError,
|
|
@@ -51,5 +62,7 @@ export {
|
|
|
51
62
|
UnauthorizedError,
|
|
52
63
|
ValidationError,
|
|
53
64
|
ConflictError,
|
|
54
|
-
LockedError
|
|
65
|
+
LockedError,
|
|
66
|
+
VaultBusyError,
|
|
67
|
+
isVaultLockTimeout
|
|
55
68
|
};
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
engineApi,
|
|
3
3
|
engineCache,
|
|
4
4
|
opContext
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-7CPP6FIU.js";
|
|
6
|
+
import "./chunk-YVMSM7LS.js";
|
|
7
|
+
import "./chunk-XCFKS65Y.js";
|
|
8
8
|
import "./chunk-YB3LKF7U.js";
|
|
9
9
|
import "./chunk-FRQJWGN3.js";
|
|
10
10
|
export {
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
scanNestForDrift,
|
|
12
12
|
startDriftScanner,
|
|
13
13
|
stopDriftScanner
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
17
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-EY7WOKJR.js";
|
|
15
|
+
import "./chunk-7CPP6FIU.js";
|
|
16
|
+
import "./chunk-YVMSM7LS.js";
|
|
17
|
+
import "./chunk-XCFKS65Y.js";
|
|
18
18
|
import "./chunk-YB3LKF7U.js";
|
|
19
19
|
import "./chunk-FRQJWGN3.js";
|
|
20
20
|
export {
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
listGrants,
|
|
7
7
|
listUserGrants,
|
|
8
8
|
resolveNodeGrant
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-3LUUJUWS.js";
|
|
10
|
+
import "./chunk-YVMSM7LS.js";
|
|
11
|
+
import "./chunk-XCFKS65Y.js";
|
|
12
12
|
import "./chunk-YB3LKF7U.js";
|
|
13
13
|
export {
|
|
14
14
|
createGrant,
|