@promptowl/contextnest-community 1.13.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 +57 -1
- package/README.md +2 -0
- package/dist/{chunk-5QQ7WKAI.js → chunk-I5KYGMIT.js} +22 -0
- package/dist/{chunk-ZQS5W45U.js → chunk-IHGPZ6NY.js} +1 -1
- package/dist/{chunk-EG77SQHO.js → chunk-JP67WXLA.js} +1 -1
- package/dist/{chunk-Q5NRCDD4.js → chunk-KPK656BH.js} +456 -27
- package/dist/{chunk-PAIP5KHB.js → chunk-MZ4IR5KH.js} +458 -500
- package/dist/{client-AAMF22YJ.js → client-3MGIP57D.js} +1 -1
- package/dist/{grants-service-FZD6QM6V.js → grants-service-5ZFISCBH.js} +2 -2
- package/dist/index.js +189 -60
- package/dist/{review-service-Q3NDK5TX.js → review-service-6G7MVTY6.js} +7 -5
- package/dist/{stewardship-service-B6L7UACX.js → stewardship-service-G7IF7ZNP.js} +2 -2
- package/dist/{version-service-L52F4WVZ.js → version-service-FGL47APL.js} +2 -2
- 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-DcmDj50q.css +0 -1
- package/dist/web3/assets/index-EGPZ6MAy.js +0 -1023
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
listGrants,
|
|
7
7
|
listUserGrants,
|
|
8
8
|
resolveNodeGrant
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-IHGPZ6NY.js";
|
|
10
10
|
import "./chunk-GUNJTORH.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-I5KYGMIT.js";
|
|
12
12
|
import "./chunk-SLTQACJW.js";
|
|
13
13
|
export {
|
|
14
14
|
createGrant,
|
package/dist/index.js
CHANGED
|
@@ -17,18 +17,23 @@ import {
|
|
|
17
17
|
listNotifications,
|
|
18
18
|
listWatchers,
|
|
19
19
|
markNotificationsRead,
|
|
20
|
+
nodeWriteKey,
|
|
20
21
|
notifyNestEvent,
|
|
22
|
+
notifyNestInvite,
|
|
21
23
|
parseConditionSchema,
|
|
22
24
|
reject,
|
|
23
25
|
removeWatcher,
|
|
26
|
+
repointPendingReview,
|
|
24
27
|
requireWorkflowPlane,
|
|
25
28
|
resolveCallerEmail,
|
|
26
29
|
safeJson,
|
|
27
30
|
safePublishDocument,
|
|
28
31
|
seedDefaultEdgeTypes,
|
|
32
|
+
sendTestNotification,
|
|
33
|
+
stripUndefinedDeep,
|
|
29
34
|
submitForReview,
|
|
30
|
-
|
|
31
|
-
} from "./chunk-
|
|
35
|
+
withNodeWriteLock
|
|
36
|
+
} from "./chunk-MZ4IR5KH.js";
|
|
32
37
|
import {
|
|
33
38
|
createGrant,
|
|
34
39
|
deleteGrant,
|
|
@@ -37,7 +42,7 @@ import {
|
|
|
37
42
|
listGrants,
|
|
38
43
|
listUserGrants,
|
|
39
44
|
resolveNodeGrant
|
|
40
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-IHGPZ6NY.js";
|
|
41
46
|
import {
|
|
42
47
|
generateApiKey,
|
|
43
48
|
getKeyPrefix,
|
|
@@ -55,7 +60,7 @@ import {
|
|
|
55
60
|
getVersions,
|
|
56
61
|
setApprovedVersion,
|
|
57
62
|
upsertVersion
|
|
58
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-JP67WXLA.js";
|
|
59
64
|
import {
|
|
60
65
|
addMember,
|
|
61
66
|
buildTitleMap,
|
|
@@ -119,6 +124,7 @@ import {
|
|
|
119
124
|
resolveStewardsForNode,
|
|
120
125
|
resolveStewardsWithFallback,
|
|
121
126
|
resolveUserRoles,
|
|
127
|
+
sendEmailToRecipient,
|
|
122
128
|
setAllowSelfApprove,
|
|
123
129
|
setStewardshipEnabled,
|
|
124
130
|
shareTeamWithNest,
|
|
@@ -130,8 +136,9 @@ import {
|
|
|
130
136
|
unshareTeamFromNest,
|
|
131
137
|
updateMemberRole,
|
|
132
138
|
updateSteward,
|
|
133
|
-
validateLicense
|
|
134
|
-
|
|
139
|
+
validateLicense,
|
|
140
|
+
verifySmtp
|
|
141
|
+
} from "./chunk-KPK656BH.js";
|
|
135
142
|
import {
|
|
136
143
|
AppError,
|
|
137
144
|
ConflictError,
|
|
@@ -154,7 +161,7 @@ import {
|
|
|
154
161
|
initDb,
|
|
155
162
|
isEmailListish,
|
|
156
163
|
isEmailish
|
|
157
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-I5KYGMIT.js";
|
|
158
165
|
import {
|
|
159
166
|
ANON_EMAIL,
|
|
160
167
|
ANON_USER_ID
|
|
@@ -1476,6 +1483,18 @@ authRoutes.post("/invite", async (c) => {
|
|
|
1476
1483
|
);
|
|
1477
1484
|
}
|
|
1478
1485
|
trackEvent("admin.invite", { adminId: callerId, email });
|
|
1486
|
+
const inviter = await db.get("SELECT email FROM users WHERE id = ?", [
|
|
1487
|
+
callerId
|
|
1488
|
+
]);
|
|
1489
|
+
void sendEmailToRecipient(email, {
|
|
1490
|
+
status: "invited",
|
|
1491
|
+
docTitle: "ContextNest",
|
|
1492
|
+
path: "",
|
|
1493
|
+
actor: email,
|
|
1494
|
+
by: inviter?.email || void 0,
|
|
1495
|
+
tempPassword,
|
|
1496
|
+
link: requestBaseUrl(c.req.url)
|
|
1497
|
+
});
|
|
1479
1498
|
return c.json(
|
|
1480
1499
|
{
|
|
1481
1500
|
temporary_password: tempPassword,
|
|
@@ -1765,7 +1784,7 @@ async function approveExternalEdit(input) {
|
|
|
1765
1784
|
const node = await storage.readDocument(input.documentId);
|
|
1766
1785
|
const versionNum = result.versionEntry.version;
|
|
1767
1786
|
const tags = node.frontmatter.tags || [];
|
|
1768
|
-
const { createVersion: createVersion2, setApprovedVersion: setApprovedVersion2 } = await import("./version-service-
|
|
1787
|
+
const { createVersion: createVersion2, setApprovedVersion: setApprovedVersion2 } = await import("./version-service-FGL47APL.js");
|
|
1769
1788
|
await createVersion2({
|
|
1770
1789
|
nestId: input.nestId,
|
|
1771
1790
|
nodeId: input.documentId,
|
|
@@ -1874,18 +1893,6 @@ async function departmentTagFor(userEmail) {
|
|
|
1874
1893
|
return null;
|
|
1875
1894
|
}
|
|
1876
1895
|
}
|
|
1877
|
-
var nodeWriteChains = /* @__PURE__ */ new Map();
|
|
1878
|
-
function withNodeWriteLock(key, fn) {
|
|
1879
|
-
const prev = (nodeWriteChains.get(key) ?? Promise.resolve()).catch(() => {
|
|
1880
|
-
});
|
|
1881
|
-
const run = prev.then(fn);
|
|
1882
|
-
nodeWriteChains.set(key, run);
|
|
1883
|
-
void run.catch(() => {
|
|
1884
|
-
}).finally(() => {
|
|
1885
|
-
if (nodeWriteChains.get(key) === run) nodeWriteChains.delete(key);
|
|
1886
|
-
});
|
|
1887
|
-
return run;
|
|
1888
|
-
}
|
|
1889
1896
|
function toSafeError(err, context, message) {
|
|
1890
1897
|
if (err instanceof AppError) return err;
|
|
1891
1898
|
console.error(`${context}:`, err);
|
|
@@ -2300,7 +2307,7 @@ async function registerImportedDocuments(nestId, userEmail, onProgress) {
|
|
|
2300
2307
|
return registered;
|
|
2301
2308
|
}
|
|
2302
2309
|
async function updateNode(nestId, nodeId, patch, userEmail) {
|
|
2303
|
-
return withNodeWriteLock(
|
|
2310
|
+
return withNodeWriteLock(nodeWriteKey(nestId, nodeId), async () => {
|
|
2304
2311
|
try {
|
|
2305
2312
|
const { storage, versions: versionManager } = await engineCache.get(
|
|
2306
2313
|
nestId
|
|
@@ -2352,13 +2359,15 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
|
|
|
2352
2359
|
const hasStewards = await isStewardshipEnabled(nestId);
|
|
2353
2360
|
const autoPublish = !hasStewards || await actorAutoPublishes(nestId, userEmail);
|
|
2354
2361
|
const currentTags = node.frontmatter.tags || [];
|
|
2355
|
-
|
|
2362
|
+
const pendingReview = hasStewards ? await getPendingReview(nestId, nodeId) : null;
|
|
2363
|
+
const isAuthorEditingOwnReview = !!pendingReview && pendingReview.requestedBy.toLowerCase() === userEmail.toLowerCase();
|
|
2364
|
+
if (pendingReview && !isAuthorEditingOwnReview) {
|
|
2356
2365
|
throw new LockedError(
|
|
2357
2366
|
"This document is awaiting steward review and is locked. Approve or reject the pending review before editing."
|
|
2358
2367
|
);
|
|
2359
2368
|
}
|
|
2360
2369
|
let responseVersion;
|
|
2361
|
-
if (!autoPublish) {
|
|
2370
|
+
if (!autoPublish || isAuthorEditingOwnReview) {
|
|
2362
2371
|
const currentVersion = await getCurrentVersion(nestId, nodeId);
|
|
2363
2372
|
const newVersion = currentVersion + 1;
|
|
2364
2373
|
node = {
|
|
@@ -2396,6 +2405,9 @@ async function updateNode(nestId, nodeId, patch, userEmail) {
|
|
|
2396
2405
|
tags: currentTags,
|
|
2397
2406
|
changeNote: patch.changeNote
|
|
2398
2407
|
});
|
|
2408
|
+
if (isAuthorEditingOwnReview) {
|
|
2409
|
+
await repointPendingReview({ nestId, nodeId, newVersion });
|
|
2410
|
+
}
|
|
2399
2411
|
responseVersion = newVersion;
|
|
2400
2412
|
} else {
|
|
2401
2413
|
node = {
|
|
@@ -3143,8 +3155,30 @@ async function addCollaborator(params) {
|
|
|
3143
3155
|
actor: params.email || userId,
|
|
3144
3156
|
permission: params.permission,
|
|
3145
3157
|
by: params.grantedByEmail || void 0
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3158
|
+
},
|
|
3159
|
+
// Invite email goes to the invitee directly (below), NOT the shared
|
|
3160
|
+
// NOTIFY_EMAIL_TO team inbox — so skip the team-channel email for this
|
|
3161
|
+
// event. Slack/Teams still fire.
|
|
3162
|
+
{ skipEmail: true }
|
|
3163
|
+
);
|
|
3164
|
+
const inviteeEmail = params.email || (await db.get("SELECT email FROM users WHERE id = ?", [userId]))?.email;
|
|
3165
|
+
if (inviteeEmail) {
|
|
3166
|
+
void sendEmailToRecipient(inviteeEmail, {
|
|
3167
|
+
status: "shared",
|
|
3168
|
+
docTitle: shareNestName,
|
|
3169
|
+
path: shareNestName,
|
|
3170
|
+
link: docLink(nestId, void 0, params.baseUrl),
|
|
3171
|
+
actor: inviteeEmail,
|
|
3172
|
+
permission: params.permission,
|
|
3173
|
+
by: params.grantedByEmail || void 0
|
|
3174
|
+
});
|
|
3175
|
+
void notifyNestInvite({
|
|
3176
|
+
nestId,
|
|
3177
|
+
inviteeEmail,
|
|
3178
|
+
permission: params.permission,
|
|
3179
|
+
nestName: shareNestName
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3148
3182
|
return await db.get(
|
|
3149
3183
|
"SELECT * FROM nest_collaborators WHERE id = ?",
|
|
3150
3184
|
[collabId]
|
|
@@ -3336,7 +3370,8 @@ nestTeamRoutes.post("/", async (c) => {
|
|
|
3336
3370
|
nestId: c.req.param("nestId"),
|
|
3337
3371
|
teamId: body.team_id,
|
|
3338
3372
|
callerUserId: userId,
|
|
3339
|
-
allowAdmin: await isLicenseAdminUserId(userId)
|
|
3373
|
+
allowAdmin: await isLicenseAdminUserId(userId),
|
|
3374
|
+
baseUrl: requestBaseUrl(c.req.url)
|
|
3340
3375
|
});
|
|
3341
3376
|
return c.json({ teams }, 201);
|
|
3342
3377
|
});
|
|
@@ -4050,7 +4085,7 @@ nodeRoutes.post("/", async (c) => {
|
|
|
4050
4085
|
nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
|
|
4051
4086
|
const nestId = c.req.param("nestId");
|
|
4052
4087
|
const nodeId = c.req.param("nodeId");
|
|
4053
|
-
const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-
|
|
4088
|
+
const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-G7IF7ZNP.js");
|
|
4054
4089
|
const { stewards, fallbackToOwner, ownerEmail } = await resolveStewardsWithFallback2(
|
|
4055
4090
|
nestId,
|
|
4056
4091
|
nodeId
|
|
@@ -4071,7 +4106,7 @@ nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
|
|
|
4071
4106
|
nodeRoutes.get("/:nodeId{.+}/versions", async (c) => {
|
|
4072
4107
|
const nestId = c.req.param("nestId");
|
|
4073
4108
|
const nodeId = c.req.param("nodeId");
|
|
4074
|
-
const { getVersions: getVersions2, getApprovedVersion: getApprovedVersion2 } = await import("./version-service-
|
|
4109
|
+
const { getVersions: getVersions2, getApprovedVersion: getApprovedVersion2 } = await import("./version-service-FGL47APL.js");
|
|
4075
4110
|
const allVersions = await getVersions2(nestId, nodeId);
|
|
4076
4111
|
const approved = await getApprovedVersion2(nestId, nodeId);
|
|
4077
4112
|
const db = getDb();
|
|
@@ -4103,7 +4138,7 @@ nodeRoutes.get("/:nodeId{.+}/versions", async (c) => {
|
|
|
4103
4138
|
nodeRoutes.get("/:nodeId{.+}/reviews", async (c) => {
|
|
4104
4139
|
const nestId = c.req.param("nestId");
|
|
4105
4140
|
const nodeId = c.req.param("nodeId");
|
|
4106
|
-
const { getReviewHistory: getReviewHistory2 } = await import("./review-service-
|
|
4141
|
+
const { getReviewHistory: getReviewHistory2 } = await import("./review-service-6G7MVTY6.js");
|
|
4107
4142
|
const history = await getReviewHistory2(nestId, nodeId);
|
|
4108
4143
|
return c.json({ reviews: history });
|
|
4109
4144
|
});
|
|
@@ -4518,7 +4553,7 @@ annotationRoutes.get("/:nodeId{.+}/hosted", async (c) => {
|
|
|
4518
4553
|
|
|
4519
4554
|
// src/nodes/query-routes.ts
|
|
4520
4555
|
import { Hono as Hono10 } from "hono";
|
|
4521
|
-
import { serializeDocument as
|
|
4556
|
+
import { serializeDocument as serializeDocument3 } from "@promptowl/contextnest-engine";
|
|
4522
4557
|
|
|
4523
4558
|
// src/tables/service.ts
|
|
4524
4559
|
function splitCsvLine(line) {
|
|
@@ -4937,6 +4972,68 @@ async function resolveExportBody(nestId, nodeId, workingBody) {
|
|
|
4937
4972
|
}
|
|
4938
4973
|
}
|
|
4939
4974
|
|
|
4975
|
+
// src/nests/bundle-service.ts
|
|
4976
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
4977
|
+
import { join as join4, sep } from "path";
|
|
4978
|
+
import JSZip from "jszip";
|
|
4979
|
+
import { serializeDocument as serializeDocument2 } from "@promptowl/contextnest-engine";
|
|
4980
|
+
function walkFiles(root) {
|
|
4981
|
+
const out = [];
|
|
4982
|
+
let entries;
|
|
4983
|
+
try {
|
|
4984
|
+
entries = readdirSync2(root, { recursive: true });
|
|
4985
|
+
} catch {
|
|
4986
|
+
return out;
|
|
4987
|
+
}
|
|
4988
|
+
for (const rel of entries) {
|
|
4989
|
+
try {
|
|
4990
|
+
if (statSync2(join4(root, rel)).isFile()) out.push(rel.split(sep).join("/"));
|
|
4991
|
+
} catch {
|
|
4992
|
+
}
|
|
4993
|
+
}
|
|
4994
|
+
return out;
|
|
4995
|
+
}
|
|
4996
|
+
async function exportNestBundle(nestId, includeDrafts) {
|
|
4997
|
+
const db = getDb();
|
|
4998
|
+
const approvedRows = await db.all(
|
|
4999
|
+
"SELECT node_id, approved_version FROM approved_versions WHERE nest_id = ?",
|
|
5000
|
+
[nestId]
|
|
5001
|
+
);
|
|
5002
|
+
const approvedByNode = new Map(
|
|
5003
|
+
approvedRows.map((r) => [r.node_id, r.approved_version])
|
|
5004
|
+
);
|
|
5005
|
+
const { storage } = await engineCache.get(nestId);
|
|
5006
|
+
const docs = await storage.discoverDocuments();
|
|
5007
|
+
const zip = new JSZip();
|
|
5008
|
+
let count = 0;
|
|
5009
|
+
for (const doc of docs) {
|
|
5010
|
+
const approved = approvedByNode.get(doc.id);
|
|
5011
|
+
const frontmatter = stripUndefinedDeep({ ...doc.frontmatter });
|
|
5012
|
+
let body = doc.body || "";
|
|
5013
|
+
if (approved != null) {
|
|
5014
|
+
const approvedBody = await resolveExportBody(nestId, doc.id, body);
|
|
5015
|
+
if (approvedBody == null) continue;
|
|
5016
|
+
body = approvedBody;
|
|
5017
|
+
frontmatter.status = "published";
|
|
5018
|
+
frontmatter.version = approved;
|
|
5019
|
+
} else {
|
|
5020
|
+
if (!includeDrafts) continue;
|
|
5021
|
+
frontmatter.status = "draft";
|
|
5022
|
+
}
|
|
5023
|
+
zip.file(`${doc.id}.md`, serializeDocument2({ ...doc, frontmatter, body }));
|
|
5024
|
+
count++;
|
|
5025
|
+
}
|
|
5026
|
+
const nestPath = resolveNestPath(nestId);
|
|
5027
|
+
for (const rel of walkFiles(nestPath)) {
|
|
5028
|
+
const segs = rel.split("/");
|
|
5029
|
+
const keep = segs.includes(".versions") || rel === ".context/config.yaml" || rel === "CONTEXT.md";
|
|
5030
|
+
if (!keep) continue;
|
|
5031
|
+
zip.file(rel, readFileSync2(join4(nestPath, rel)));
|
|
5032
|
+
}
|
|
5033
|
+
trackEvent("nest.export.bundle", { nestId, documents: count, includeDrafts });
|
|
5034
|
+
return zip.generateAsync({ type: "arraybuffer" });
|
|
5035
|
+
}
|
|
5036
|
+
|
|
4940
5037
|
// src/nodes/graph-service.ts
|
|
4941
5038
|
var MAX_GRAPH_NODES = 150;
|
|
4942
5039
|
var WIKILINK_RE = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g;
|
|
@@ -5490,10 +5587,24 @@ queryRoutes.get("/context", async (c) => {
|
|
|
5490
5587
|
return c.json({ content: content || "" });
|
|
5491
5588
|
});
|
|
5492
5589
|
queryRoutes.get("/export", async (c) => {
|
|
5590
|
+
const nestId = c.req.param("nestId");
|
|
5591
|
+
if (c.req.query("format") === "bundle") {
|
|
5592
|
+
const perm = await resolveNestPermission(nestId, c.get("userId"));
|
|
5593
|
+
if (perm !== "owner" && perm !== "admin") {
|
|
5594
|
+
throw new ForbiddenError("Only the nest owner or an admin can export a bundle");
|
|
5595
|
+
}
|
|
5596
|
+
const draftsParam = c.req.query("includeDrafts");
|
|
5597
|
+
const includeDrafts = draftsParam === "1" || draftsParam === "true";
|
|
5598
|
+
const zip = await exportNestBundle(nestId, includeDrafts);
|
|
5599
|
+
const filename = `nest-${nestId}.zip`;
|
|
5600
|
+
return c.body(zip, 200, {
|
|
5601
|
+
"Content-Type": "application/zip",
|
|
5602
|
+
"Content-Disposition": `attachment; filename="${filename}"`
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5493
5605
|
if (!isMarkdownFormat(c)) {
|
|
5494
|
-
throw new ValidationError("format=markdown is required");
|
|
5606
|
+
throw new ValidationError("format=markdown or format=bundle is required");
|
|
5495
5607
|
}
|
|
5496
|
-
const nestId = c.req.param("nestId");
|
|
5497
5608
|
const { storage, query: queryEngine } = await engineCache.get(nestId);
|
|
5498
5609
|
const selector = c.req.query("selector")?.trim() || null;
|
|
5499
5610
|
let documents;
|
|
@@ -5592,7 +5703,7 @@ queryRoutes.post("/publish", async (c) => {
|
|
|
5592
5703
|
body: doc.content,
|
|
5593
5704
|
rawContent: ""
|
|
5594
5705
|
};
|
|
5595
|
-
const serialized =
|
|
5706
|
+
const serialized = serializeDocument3(node);
|
|
5596
5707
|
await storage.writeDocument(id, serialized);
|
|
5597
5708
|
await syncNodeTags(nestId, id, tags);
|
|
5598
5709
|
created.push(id);
|
|
@@ -7397,17 +7508,15 @@ ${resolved.map((r) => `- ${r.steward.userEmail} (${r.source})`).join("\n")}` : "
|
|
|
7397
7508
|
(n) => n.frontmatter.title.toLowerCase() === args.title.toLowerCase()
|
|
7398
7509
|
);
|
|
7399
7510
|
if (!node) return `Node not found: ${args.title}`;
|
|
7400
|
-
const currentVersion = await getCurrentVersion(ctx.nestId, node.id);
|
|
7401
7511
|
try {
|
|
7402
7512
|
const request = await approve({
|
|
7403
7513
|
nestId: ctx.nestId,
|
|
7404
7514
|
nodeId: node.id,
|
|
7405
|
-
version: currentVersion,
|
|
7406
7515
|
approvedBy: ctx.userEmail,
|
|
7407
7516
|
note: args.note,
|
|
7408
7517
|
baseUrl: ctx.baseUrl
|
|
7409
7518
|
});
|
|
7410
|
-
return `Approved "${args.title}" v${
|
|
7519
|
+
return `Approved "${args.title}" v${request.version}. This version is now live for AI queries.${args.note ? `
|
|
7411
7520
|
Note: ${args.note}` : ""}`;
|
|
7412
7521
|
} catch (err) {
|
|
7413
7522
|
return `Cannot approve: ${err.message}`;
|
|
@@ -7419,17 +7528,15 @@ Note: ${args.note}` : ""}`;
|
|
|
7419
7528
|
(n) => n.frontmatter.title.toLowerCase() === args.title.toLowerCase()
|
|
7420
7529
|
);
|
|
7421
7530
|
if (!node) return `Node not found: ${args.title}`;
|
|
7422
|
-
const currentVersion = await getCurrentVersion(ctx.nestId, node.id);
|
|
7423
7531
|
try {
|
|
7424
7532
|
const request = await reject({
|
|
7425
7533
|
nestId: ctx.nestId,
|
|
7426
7534
|
nodeId: node.id,
|
|
7427
|
-
version: currentVersion,
|
|
7428
7535
|
rejectedBy: ctx.userEmail,
|
|
7429
7536
|
note: args.note,
|
|
7430
7537
|
baseUrl: ctx.baseUrl
|
|
7431
7538
|
});
|
|
7432
|
-
return `Rejected "${args.title}" v${
|
|
7539
|
+
return `Rejected "${args.title}" v${request.version}.
|
|
7433
7540
|
Reason: ${args.note}`;
|
|
7434
7541
|
} catch (err) {
|
|
7435
7542
|
return `Cannot reject: ${err.message}`;
|
|
@@ -7510,8 +7617,8 @@ ${list}`;
|
|
|
7510
7617
|
if (!target) return "target is required (a node id or folder prefix).";
|
|
7511
7618
|
if (!["read", "write"].includes(role)) return "role must be read or write.";
|
|
7512
7619
|
try {
|
|
7513
|
-
const { createGrant: createGrant2 } = await import("./grants-service-
|
|
7514
|
-
const db = (await import("./client-
|
|
7620
|
+
const { createGrant: createGrant2 } = await import("./grants-service-5ZFISCBH.js");
|
|
7621
|
+
const db = (await import("./client-3MGIP57D.js")).getDb();
|
|
7515
7622
|
const { normalizeEmail: normalizeEmail2 } = await import("./email-R7DFS6E5.js");
|
|
7516
7623
|
const e = normalizeEmail2(String(args.email || ""));
|
|
7517
7624
|
if (!e) return "email is required.";
|
|
@@ -8598,7 +8705,7 @@ definitionRoutes.delete("/:id", async (c) => {
|
|
|
8598
8705
|
import { Hono as Hono18 } from "hono";
|
|
8599
8706
|
import { v4 as uuid13 } from "uuid";
|
|
8600
8707
|
import { mkdir as mkdir2, readFile as readFile2, writeFile } from "fs/promises";
|
|
8601
|
-
import { join as
|
|
8708
|
+
import { join as join5 } from "path";
|
|
8602
8709
|
var assetRoutes = new Hono18();
|
|
8603
8710
|
var CONTENT_TYPES = {
|
|
8604
8711
|
png: "image/png",
|
|
@@ -8646,9 +8753,9 @@ assetRoutes.post("/", async (c) => {
|
|
|
8646
8753
|
);
|
|
8647
8754
|
}
|
|
8648
8755
|
const name = `${uuid13()}.${ext === "jpeg" ? "jpg" : ext}`;
|
|
8649
|
-
const dir =
|
|
8756
|
+
const dir = join5(resolveNestPath(nestId), "assets");
|
|
8650
8757
|
await mkdir2(dir, { recursive: true });
|
|
8651
|
-
await writeFile(
|
|
8758
|
+
await writeFile(join5(dir, name), Buffer.from(await file.arrayBuffer()));
|
|
8652
8759
|
const url = `/nests/${nestId}/assets/${name}`;
|
|
8653
8760
|
return c.json(
|
|
8654
8761
|
{
|
|
@@ -8670,7 +8777,7 @@ assetRoutes.get("/:file", async (c) => {
|
|
|
8670
8777
|
}
|
|
8671
8778
|
let bytes;
|
|
8672
8779
|
try {
|
|
8673
|
-
bytes = await readFile2(
|
|
8780
|
+
bytes = await readFile2(join5(resolveNestPath(nestId), "assets", name));
|
|
8674
8781
|
} catch {
|
|
8675
8782
|
throw new NotFoundError("Asset not found");
|
|
8676
8783
|
}
|
|
@@ -9425,8 +9532,8 @@ function rowToComment(row) {
|
|
|
9425
9532
|
}
|
|
9426
9533
|
|
|
9427
9534
|
// src/governance/stewards-parser.ts
|
|
9428
|
-
import { readFileSync as
|
|
9429
|
-
import { join as
|
|
9535
|
+
import { readFileSync as readFileSync3, existsSync } from "fs";
|
|
9536
|
+
import { join as join6 } from "path";
|
|
9430
9537
|
function parseStewardsYaml(content) {
|
|
9431
9538
|
const result = { version: 1 };
|
|
9432
9539
|
const lines = content.split("\n");
|
|
@@ -9492,13 +9599,13 @@ function parseEntry(str) {
|
|
|
9492
9599
|
function loadStewardsConfig(nestId) {
|
|
9493
9600
|
const nestPath = resolveNestPath(nestId);
|
|
9494
9601
|
const candidates = [
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9602
|
+
join6(nestPath, "stewards.yaml"),
|
|
9603
|
+
join6(nestPath, "stewards.yml"),
|
|
9604
|
+
join6(nestPath, ".context", "stewards.yaml")
|
|
9498
9605
|
];
|
|
9499
9606
|
for (const candidatePath of candidates) {
|
|
9500
9607
|
if (existsSync(candidatePath)) {
|
|
9501
|
-
const content =
|
|
9608
|
+
const content = readFileSync3(candidatePath, "utf-8");
|
|
9502
9609
|
return parseStewardsYaml(content);
|
|
9503
9610
|
}
|
|
9504
9611
|
}
|
|
@@ -9554,7 +9661,8 @@ governanceRoutes.post("/stewards", async (c) => {
|
|
|
9554
9661
|
documentId: body.documentId,
|
|
9555
9662
|
tagName: body.tagName,
|
|
9556
9663
|
users: body.users,
|
|
9557
|
-
assignedBy
|
|
9664
|
+
assignedBy,
|
|
9665
|
+
baseUrl: requestBaseUrl(c.req.url)
|
|
9558
9666
|
});
|
|
9559
9667
|
return c.json({ stewards: created2 }, 201);
|
|
9560
9668
|
}
|
|
@@ -9572,7 +9680,8 @@ governanceRoutes.post("/stewards", async (c) => {
|
|
|
9572
9680
|
role: body.role
|
|
9573
9681
|
}
|
|
9574
9682
|
],
|
|
9575
|
-
assignedBy
|
|
9683
|
+
assignedBy,
|
|
9684
|
+
baseUrl: requestBaseUrl(c.req.url)
|
|
9576
9685
|
});
|
|
9577
9686
|
return c.json({ steward: created[0] }, 201);
|
|
9578
9687
|
});
|
|
@@ -9891,7 +10000,6 @@ governanceNodeRoutes.post("/:nodeId{.+}/approve", async (c) => {
|
|
|
9891
10000
|
const request = await approve({
|
|
9892
10001
|
nestId,
|
|
9893
10002
|
nodeId,
|
|
9894
|
-
version: await getCurrentVersion(nestId, nodeId),
|
|
9895
10003
|
approvedBy: userEmail,
|
|
9896
10004
|
note: body.note,
|
|
9897
10005
|
override: body.override && isAdmin,
|
|
@@ -9914,7 +10022,6 @@ governanceNodeRoutes.post("/:nodeId{.+}/reject", async (c) => {
|
|
|
9914
10022
|
const request = await reject({
|
|
9915
10023
|
nestId,
|
|
9916
10024
|
nodeId,
|
|
9917
|
-
version: await getCurrentVersion(nestId, nodeId),
|
|
9918
10025
|
rejectedBy: userEmail,
|
|
9919
10026
|
note: body.note,
|
|
9920
10027
|
baseUrl: requestBaseUrl(c.req.url)
|
|
@@ -10007,19 +10114,19 @@ async function ensureAnonymousUser() {
|
|
|
10007
10114
|
// src/app.ts
|
|
10008
10115
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
10009
10116
|
import { fileURLToPath } from "url";
|
|
10010
|
-
import { dirname as dirname2, join as
|
|
10011
|
-
import { existsSync as existsSync2, readFileSync as
|
|
10117
|
+
import { dirname as dirname2, join as join7, relative as relative2 } from "path";
|
|
10118
|
+
import { existsSync as existsSync2, readFileSync as readFileSync4 } from "fs";
|
|
10012
10119
|
var HERE = dirname2(fileURLToPath(import.meta.url));
|
|
10013
10120
|
var SERVICE_VERSION = (() => {
|
|
10014
10121
|
try {
|
|
10015
|
-
return JSON.parse(
|
|
10122
|
+
return JSON.parse(readFileSync4(join7(HERE, "..", "package.json"), "utf8")).version;
|
|
10016
10123
|
} catch {
|
|
10017
10124
|
return "unknown";
|
|
10018
10125
|
}
|
|
10019
10126
|
})();
|
|
10020
10127
|
var UI_DIR_CANDIDATES = [
|
|
10021
|
-
|
|
10022
|
-
|
|
10128
|
+
join7(HERE, "web3"),
|
|
10129
|
+
join7(process.cwd(), "dist", "web3")
|
|
10023
10130
|
];
|
|
10024
10131
|
var UI_DIR_ABS = UI_DIR_CANDIDATES.find((p) => existsSync2(p)) || UI_DIR_CANDIDATES[0];
|
|
10025
10132
|
var UI_DIR_REL = relative2(process.cwd(), UI_DIR_ABS) || ".";
|
|
@@ -10240,6 +10347,7 @@ function createApp() {
|
|
|
10240
10347
|
});
|
|
10241
10348
|
const serverAdminAllowed = async (c) => config.AUTH_MODE === "open" || await isServerAdminUserId(c.get("userId"));
|
|
10242
10349
|
app.use("/admin/settings", flexAuthMiddleware);
|
|
10350
|
+
app.use("/admin/settings/*", flexAuthMiddleware);
|
|
10243
10351
|
const currentServerSettings = () => ({
|
|
10244
10352
|
promptowl_sign_in_gate: config.PROMPTOWL_SIGN_IN_GATE,
|
|
10245
10353
|
manual_sign_in: config.MANUAL_SIGN_IN,
|
|
@@ -10272,6 +10380,7 @@ function createApp() {
|
|
|
10272
10380
|
// Server-wide runner default. NEVER the value — masked tail only.
|
|
10273
10381
|
anthropic_api_key_masked: process.env.ANTHROPIC_API_KEY ? `\u2022\u2022\u2022\u2022${process.env.ANTHROPIC_API_KEY.slice(-4)}` : null,
|
|
10274
10382
|
slack_webhook_url: config.SLACK_WEBHOOK_URL ?? "",
|
|
10383
|
+
msteams_webhook_url: config.MSTEAMS_WEBHOOK_URL ?? "",
|
|
10275
10384
|
smtp_url: config.SMTP_URL ?? "",
|
|
10276
10385
|
notify_email_from: config.NOTIFY_EMAIL_FROM ?? "",
|
|
10277
10386
|
notify_email_to: config.NOTIFY_EMAIL_TO ?? ""
|
|
@@ -10281,6 +10390,20 @@ function createApp() {
|
|
|
10281
10390
|
return c.json({ error: "Only the server admin can view this." }, 403);
|
|
10282
10391
|
return c.json(currentServerSettings());
|
|
10283
10392
|
});
|
|
10393
|
+
app.post("/admin/settings/test-notification", async (c) => {
|
|
10394
|
+
if (!await serverAdminAllowed(c))
|
|
10395
|
+
return c.json({ error: "Only the server admin can do this." }, 403);
|
|
10396
|
+
let body;
|
|
10397
|
+
try {
|
|
10398
|
+
body = await c.req.json();
|
|
10399
|
+
} catch {
|
|
10400
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
10401
|
+
}
|
|
10402
|
+
if (body.channel !== "slack" && body.channel !== "teams")
|
|
10403
|
+
return c.json({ error: "channel must be 'slack' or 'teams'" }, 400);
|
|
10404
|
+
const result = await sendTestNotification(body.channel);
|
|
10405
|
+
return result.ok ? c.json({ ok: true }) : c.json({ error: result.error }, 502);
|
|
10406
|
+
});
|
|
10284
10407
|
app.patch("/admin/settings", async (c) => {
|
|
10285
10408
|
if (!await serverAdminAllowed(c))
|
|
10286
10409
|
return c.json({ error: "Only the server admin can change this." }, 403);
|
|
@@ -10473,6 +10596,12 @@ function createApp() {
|
|
|
10473
10596
|
addError("slack_webhook_url", "Slack webhook must be an https:// URL (or leave it empty to turn Slack notifications off).");
|
|
10474
10597
|
else pending.push({ name: "SLACK_WEBHOOK_URL", value: v || null });
|
|
10475
10598
|
}
|
|
10599
|
+
if ("msteams_webhook_url" in body) {
|
|
10600
|
+
const v = String(body.msteams_webhook_url ?? "").trim();
|
|
10601
|
+
if (v && !/^https:\/\//i.test(v))
|
|
10602
|
+
addError("msteams_webhook_url", "Microsoft Teams webhook must be an https:// URL (or leave it empty to turn Teams notifications off).");
|
|
10603
|
+
else pending.push({ name: "MSTEAMS_WEBHOOK_URL", value: v || null });
|
|
10604
|
+
}
|
|
10476
10605
|
if ("smtp_url" in body) {
|
|
10477
10606
|
const v = String(body.smtp_url ?? "").trim();
|
|
10478
10607
|
if (v && !/^smtps?:\/\//i.test(v))
|
|
@@ -5,17 +5,18 @@ import {
|
|
|
5
5
|
getReviewHistory,
|
|
6
6
|
getReviewQueue,
|
|
7
7
|
reject,
|
|
8
|
+
repointPendingReview,
|
|
8
9
|
submitForReview
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-MZ4IR5KH.js";
|
|
11
|
+
import "./chunk-IHGPZ6NY.js";
|
|
12
|
+
import "./chunk-JP67WXLA.js";
|
|
12
13
|
import {
|
|
13
14
|
canUserApprove
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KPK656BH.js";
|
|
15
16
|
import "./chunk-GUNJTORH.js";
|
|
16
17
|
import "./chunk-FRQJWGN3.js";
|
|
17
18
|
import "./chunk-XQ46F76G.js";
|
|
18
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-I5KYGMIT.js";
|
|
19
20
|
import "./chunk-SLTQACJW.js";
|
|
20
21
|
export {
|
|
21
22
|
approve,
|
|
@@ -25,5 +26,6 @@ export {
|
|
|
25
26
|
getReviewHistory,
|
|
26
27
|
getReviewQueue,
|
|
27
28
|
reject,
|
|
29
|
+
repointPendingReview,
|
|
28
30
|
submitForReview
|
|
29
31
|
};
|
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
syncFromConfig,
|
|
22
22
|
updateSteward,
|
|
23
23
|
updateStewardRole
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-KPK656BH.js";
|
|
25
25
|
import "./chunk-GUNJTORH.js";
|
|
26
26
|
import "./chunk-FRQJWGN3.js";
|
|
27
27
|
import "./chunk-XQ46F76G.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-I5KYGMIT.js";
|
|
29
29
|
import "./chunk-SLTQACJW.js";
|
|
30
30
|
export {
|
|
31
31
|
assignSteward,
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
setApprovedVersion,
|
|
13
13
|
systemAuthor,
|
|
14
14
|
upsertVersion
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-JP67WXLA.js";
|
|
16
16
|
import "./chunk-XQ46F76G.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-I5KYGMIT.js";
|
|
18
18
|
import "./chunk-SLTQACJW.js";
|
|
19
19
|
export {
|
|
20
20
|
SYSTEM_AUTHOR_PREFIX,
|