@promptowl/contextnest-community 1.23.0 → 1.25.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/API.md +2195 -0
- package/CONFIGURATION.md +8 -2
- package/README.md +62 -10
- package/STEWARDSHIP.md +253 -0
- package/dist/{chunk-2YPY2HGZ.js → chunk-2KHM5C7V.js} +40 -5
- package/dist/{chunk-XYD6V2LH.js → chunk-32SZCH36.js} +28 -33
- package/dist/{chunk-QLXC6542.js → chunk-7FROMOZM.js} +69 -27
- package/dist/{chunk-MLQU4I5J.js → chunk-DTLMBF4W.js} +10 -8
- package/dist/{chunk-QWNXWRWO.js → chunk-JDAOA5KP.js} +67 -1
- package/dist/{chunk-UOMW7TT6.js → chunk-KPGNZLZL.js} +2 -2
- package/dist/{chunk-YVMSM7LS.js → chunk-LHWOJPFE.js} +7 -0
- package/dist/{chunk-5CVZMHHB.js → chunk-TI7HMP64.js} +99 -12
- package/dist/{client-LWDMNKX3.js → client-D44ZHDTJ.js} +1 -1
- package/dist/{engine-QFF2IA3G.js → engine-TER6FDBP.js} +3 -3
- package/dist/{external-edit-service-F7D3VSA2.js → external-edit-service-CAGENHDJ.js} +4 -4
- package/dist/{grants-service-TBGTCO5K.js → grants-service-2OTFS7EH.js} +3 -3
- package/dist/index.js +1120 -331
- package/dist/{migrations.postgres-APCVSYUE.js → migrations.postgres-23VJJJJX.js} +56 -5
- package/dist/{review-service-A7PNLWG5.js → review-service-4G2XUFS6.js} +7 -7
- package/dist/{stewardship-service-32UHIRQ5.js → stewardship-service-S22V2JKG.js} +6 -4
- package/dist/{version-service-RJ6CWZ3O.js → version-service-X4FAG2OZ.js} +4 -4
- package/dist/web3/assets/ActivityTracePage-D2iIbEph.js +1 -0
- package/dist/web3/assets/AgentDocsPage-Dkhgb1hk.js +1 -0
- package/dist/web3/assets/CollaboratorManager-Bo81D--Q.js +1 -0
- package/dist/web3/assets/CollaboratorsTab-DRCitQuX.js +1 -0
- package/dist/web3/assets/DocumentEditor-DV2WHYbj.js +36 -0
- package/dist/web3/assets/DocumentsTab-BUxBCN8o.js +6 -0
- package/dist/web3/assets/ExternalEditsTab-kIBimZyE.js +1 -0
- package/dist/web3/assets/MarkdownEditor-CsVusP2d.css +1 -0
- package/dist/web3/assets/MarkdownEditor-Dfk5uIE5.js +648 -0
- package/dist/web3/assets/NestPageHeader-yOK-OIYZ.js +1 -0
- package/dist/web3/assets/NestView-DwIagxwJ.js +68 -0
- package/dist/web3/assets/OverviewTab-DFOFMVyE.js +1 -0
- package/dist/web3/assets/PersonCombobox-D350WlrJ.js +1 -0
- package/dist/web3/assets/ReasonDialog-CN3ECAnQ.js +1 -0
- package/dist/web3/assets/ReviewActions-BXDTDPp1.js +6 -0
- package/dist/web3/assets/ReviewTab-CZewAYiz.js +1 -0
- package/dist/web3/assets/StewardsTab-AagNWcWS.js +1 -0
- package/dist/web3/assets/SubmitForReviewModal-BOZJxwRN.js +1 -0
- package/dist/web3/assets/alert-dialog-4SiLKlz8.js +7 -0
- package/dist/web3/assets/arrow-left-BAgIocLn.js +6 -0
- package/dist/web3/assets/backlinks-Cb8Uf8mw.js +24 -0
- package/dist/web3/assets/card-UDyMRcps.js +1 -0
- package/dist/web3/assets/chevron-left-XR4ReJ9Z.js +6 -0
- package/dist/web3/assets/circle-check-CSkZUEFK.js +6 -0
- package/dist/web3/assets/circle-x-in2o3aHU.js +6 -0
- package/dist/web3/assets/client-attribution-BraarYaP.js +1 -0
- package/dist/web3/assets/code-xml-pPQgdctI.js +6 -0
- package/dist/web3/assets/corner-down-right-CuxZWscX.js +6 -0
- package/dist/web3/assets/count-skeleton-DBWCDhRy.js +1 -0
- package/dist/web3/assets/dates-BCxbm4_q.js +1 -0
- package/dist/web3/assets/earth-CXOcThqn.js +6 -0
- package/dist/web3/assets/file-exclamation-point-CSphs1VD.js +6 -0
- package/dist/web3/assets/folder-input-BAwK9tFL.js +11 -0
- package/dist/web3/assets/folder-target-CUSWqImF.js +1 -0
- package/dist/web3/assets/index-A0_ymcqL.js +29 -0
- package/dist/web3/assets/index-B5hLclEq.js +389 -0
- package/dist/web3/assets/index-DrUAtoQM.css +1 -0
- package/dist/web3/assets/page-BHD4beun.js +1 -0
- package/dist/web3/assets/page-BSYUXYmO.js +11 -0
- package/dist/web3/assets/page-BYc2DIux.js +45 -0
- package/dist/web3/assets/page-BfibLg8e.js +1 -0
- package/dist/web3/assets/page-CGe8MtUu.js +9 -0
- package/dist/web3/assets/page-CIgPJmG6.js +2 -0
- package/dist/web3/assets/page-CUjuSs5Q.js +24 -0
- package/dist/web3/assets/page-Ck31nx9b.js +1 -0
- package/dist/web3/assets/page-Cpdj_11Y.js +1 -0
- package/dist/web3/assets/page-D0sN3GO6.js +1 -0
- package/dist/web3/assets/page-DEpH91X2.js +1 -0
- package/dist/web3/assets/page-l8-B7CGt.js +1 -0
- package/dist/web3/assets/page-mnSHmk6A.js +6 -0
- package/dist/web3/assets/page-title-BkqlUA0j.js +1 -0
- package/dist/web3/assets/page-xzYqkBUP.js +16 -0
- package/dist/web3/assets/play-B8xE9j5f.js +6 -0
- package/dist/web3/assets/refresh-cw-CsCtmWwX.js +6 -0
- package/dist/web3/assets/scroll-area-CbcH6yrZ.js +1 -0
- package/dist/web3/assets/scroll-area-dRWncRqa.css +1 -0
- package/dist/web3/assets/select-DrL0mpRN.js +6 -0
- package/dist/web3/assets/send-CvAgOUPG.js +6 -0
- package/dist/web3/assets/settings-EY4A_DYv.js +6 -0
- package/dist/web3/assets/share-2-vGmZUl90.js +6 -0
- package/dist/web3/assets/tag-Br_y1f00.js +11 -0
- package/dist/web3/assets/trash-2-B4hRXo-p.js +6 -0
- package/dist/web3/assets/triangle-alert-CsYGyfGZ.js +6 -0
- package/dist/web3/assets/user-plus-4CTYEkd3.js +6 -0
- package/dist/web3/assets/x-qxt1WrUi.js +6 -0
- package/dist/web3/assets/zap-BcsKR0ef.js +16 -0
- package/dist/web3/index.html +2 -2
- package/package.json +5 -3
- package/dist/web3/assets/index-BJ-LRNis.js +0 -1382
- package/dist/web3/assets/index-DPMEt-A_.css +0 -1
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
ForbiddenError,
|
|
4
4
|
NotFoundError,
|
|
5
5
|
ValidationError
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LHWOJPFE.js";
|
|
7
7
|
import {
|
|
8
8
|
config,
|
|
9
9
|
getDb,
|
|
10
10
|
isEmailish
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JDAOA5KP.js";
|
|
12
12
|
import {
|
|
13
13
|
ANON_USER_ID
|
|
14
14
|
} from "./chunk-YB3LKF7U.js";
|
|
@@ -1296,8 +1296,11 @@ async function listNotifications(userEmail, opts = {}) {
|
|
|
1296
1296
|
CASE WHEN dr.target_type = 'document' THEN dr.node_id END,
|
|
1297
1297
|
-- A mention names the document directly; there's no request row.
|
|
1298
1298
|
CASE WHEN n.kind = 'mention' THEN n.subject_id END
|
|
1299
|
-
) AS node_id
|
|
1299
|
+
) AS node_id,
|
|
1300
|
+
-- Name resolved here so the inbox never fetches the nest list for it.
|
|
1301
|
+
COALESCE(ns.name, n.nest_id) AS nest_name
|
|
1300
1302
|
FROM notifications n
|
|
1303
|
+
LEFT JOIN nests ns ON ns.id = n.nest_id
|
|
1301
1304
|
LEFT JOIN review_requests rr ON rr.id = n.subject_id
|
|
1302
1305
|
LEFT JOIN deletion_requests dr ON dr.id = n.subject_id
|
|
1303
1306
|
WHERE LOWER(n.user_email) = LOWER(?)${opts.unreadOnly ? " AND n.read_at IS NULL" : ""}
|
|
@@ -2063,6 +2066,21 @@ async function setPrimeOnlyReview(nestId, enabled) {
|
|
|
2063
2066
|
nestId
|
|
2064
2067
|
]);
|
|
2065
2068
|
}
|
|
2069
|
+
async function nestCreatorIsReviewer(nestId) {
|
|
2070
|
+
const db = getDb();
|
|
2071
|
+
const row = await db.get(
|
|
2072
|
+
"SELECT creator_is_reviewer FROM nests WHERE id = ?",
|
|
2073
|
+
[nestId]
|
|
2074
|
+
);
|
|
2075
|
+
return !!row?.creator_is_reviewer;
|
|
2076
|
+
}
|
|
2077
|
+
async function setCreatorIsReviewer(nestId, enabled) {
|
|
2078
|
+
const db = getDb();
|
|
2079
|
+
await db.run("UPDATE nests SET creator_is_reviewer = ? WHERE id = ?", [
|
|
2080
|
+
enabled ? 1 : 0,
|
|
2081
|
+
nestId
|
|
2082
|
+
]);
|
|
2083
|
+
}
|
|
2066
2084
|
async function nestPrimeTags(nestId) {
|
|
2067
2085
|
const db = getDb();
|
|
2068
2086
|
const row = await db.get("SELECT prime_tags FROM nests WHERE id = ?", [
|
|
@@ -2488,6 +2506,9 @@ var FlatNestStorage = class extends NestStorage2 {
|
|
|
2488
2506
|
}
|
|
2489
2507
|
};
|
|
2490
2508
|
|
|
2509
|
+
// src/nodes/node-index.ts
|
|
2510
|
+
import { listSuggestions } from "@promptowl/contextnest-engine";
|
|
2511
|
+
|
|
2491
2512
|
// src/shared/node-id.ts
|
|
2492
2513
|
function assertSafeNodeId(rawId) {
|
|
2493
2514
|
let id = rawId;
|
|
@@ -2616,13 +2637,21 @@ async function ensureNodeIndex(nestId) {
|
|
|
2616
2637
|
return run;
|
|
2617
2638
|
}
|
|
2618
2639
|
async function rebuildNodeIndex(nestId) {
|
|
2619
|
-
const { engineCache: engineCache2 } = await import("./engine-
|
|
2620
|
-
const { documentsWithSuggestions } = await import("./external-edit-service-
|
|
2640
|
+
const { engineCache: engineCache2 } = await import("./engine-TER6FDBP.js");
|
|
2641
|
+
const { documentsWithSuggestions } = await import("./external-edit-service-CAGENHDJ.js");
|
|
2621
2642
|
const { storage, dropDiscoveryCache } = await engineCache2.get(nestId);
|
|
2622
2643
|
dropDiscoveryCache();
|
|
2623
2644
|
const startedAt = writeGeneration.get(nestId) ?? 0;
|
|
2624
2645
|
const documents = await storage.discoverDocuments({ includeRetired: true });
|
|
2625
2646
|
const staged = await documentsWithSuggestions(nestId);
|
|
2647
|
+
const stagedMetas = (await Promise.all(
|
|
2648
|
+
[...staged].map(
|
|
2649
|
+
(id) => listSuggestions(storage, id).catch((err) => {
|
|
2650
|
+
console.error("[node-index] listSuggestions failed", nestId, id, err);
|
|
2651
|
+
return [];
|
|
2652
|
+
})
|
|
2653
|
+
)
|
|
2654
|
+
)).flat();
|
|
2626
2655
|
const db = getDb();
|
|
2627
2656
|
const insertSql = insertOrReplace(
|
|
2628
2657
|
db,
|
|
@@ -2630,18 +2659,15 @@ async function rebuildNodeIndex(nestId) {
|
|
|
2630
2659
|
["nest_id", "node_id"],
|
|
2631
2660
|
UPSERT_SET
|
|
2632
2661
|
);
|
|
2633
|
-
const stagedSql = insertOrIgnore(
|
|
2634
|
-
db,
|
|
2635
|
-
"INSERT INTO node_suggestions (nest_id, node_id) VALUES (?, ?)"
|
|
2636
|
-
);
|
|
2662
|
+
const stagedSql = insertOrIgnore(db, SUGGESTION_INSERT);
|
|
2637
2663
|
await db.transaction(async (tx) => {
|
|
2638
2664
|
await tx.run("DELETE FROM node_index WHERE nest_id = ?", [nestId]);
|
|
2639
2665
|
for (const doc of documents) {
|
|
2640
2666
|
await tx.run(insertSql, [nestId, ...rowValues(doc)]);
|
|
2641
2667
|
}
|
|
2642
2668
|
await tx.run("DELETE FROM node_suggestions WHERE nest_id = ?", [nestId]);
|
|
2643
|
-
for (const
|
|
2644
|
-
await tx.run(stagedSql,
|
|
2669
|
+
for (const m of stagedMetas) {
|
|
2670
|
+
await tx.run(stagedSql, suggestionValues(nestId, m));
|
|
2645
2671
|
}
|
|
2646
2672
|
if ((writeGeneration.get(nestId) ?? 0) !== startedAt) return;
|
|
2647
2673
|
await tx.run(
|
|
@@ -2651,30 +2677,42 @@ async function rebuildNodeIndex(nestId) {
|
|
|
2651
2677
|
});
|
|
2652
2678
|
return documents.length;
|
|
2653
2679
|
}
|
|
2654
|
-
|
|
2680
|
+
var SUGGESTION_INSERT = "INSERT INTO node_suggestions (nest_id, node_id, suggestion_id, source, actor, detected_at, target_hash, proposed_hash, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
2681
|
+
var suggestionValues = (nestId, m) => [
|
|
2682
|
+
nestId,
|
|
2683
|
+
m.document_id,
|
|
2684
|
+
m.suggestion_id,
|
|
2685
|
+
m.source,
|
|
2686
|
+
m.actor,
|
|
2687
|
+
m.detected_at,
|
|
2688
|
+
m.target_hash,
|
|
2689
|
+
m.proposed_hash,
|
|
2690
|
+
m.note ?? null
|
|
2691
|
+
];
|
|
2692
|
+
async function syncSuggestionRows(nestId, nodeId, metas) {
|
|
2655
2693
|
try {
|
|
2656
2694
|
const db = getDb();
|
|
2657
|
-
|
|
2658
|
-
|
|
2695
|
+
const insertSql = insertOrIgnore(db, SUGGESTION_INSERT);
|
|
2696
|
+
await db.transaction(async (tx) => {
|
|
2697
|
+
await tx.run(
|
|
2659
2698
|
"DELETE FROM node_suggestions WHERE nest_id = ? AND node_id = ?",
|
|
2660
2699
|
[nestId, nodeId]
|
|
2661
2700
|
);
|
|
2662
|
-
|
|
2663
|
-
}
|
|
2664
|
-
await db.run(
|
|
2665
|
-
insertOrIgnore(
|
|
2666
|
-
db,
|
|
2667
|
-
"INSERT INTO node_suggestions (nest_id, node_id) VALUES (?, ?)"
|
|
2668
|
-
),
|
|
2669
|
-
[nestId, nodeId]
|
|
2670
|
-
);
|
|
2701
|
+
for (const m of metas) await tx.run(insertSql, suggestionValues(nestId, m));
|
|
2702
|
+
});
|
|
2671
2703
|
} catch (err) {
|
|
2672
|
-
console.error("[node-index] suggestion
|
|
2704
|
+
console.error("[node-index] suggestion rows failed", nestId, nodeId, err);
|
|
2673
2705
|
}
|
|
2674
2706
|
}
|
|
2707
|
+
async function listSuggestionRows(nestId) {
|
|
2708
|
+
return await getDb().all(
|
|
2709
|
+
"SELECT * FROM node_suggestions WHERE nest_id = ? ORDER BY detected_at DESC",
|
|
2710
|
+
[nestId]
|
|
2711
|
+
);
|
|
2712
|
+
}
|
|
2675
2713
|
async function nodesWithSuggestions(nestId) {
|
|
2676
2714
|
const rows = await getDb().all(
|
|
2677
|
-
"SELECT node_id FROM node_suggestions WHERE nest_id = ?",
|
|
2715
|
+
"SELECT DISTINCT node_id FROM node_suggestions WHERE nest_id = ?",
|
|
2678
2716
|
[nestId]
|
|
2679
2717
|
);
|
|
2680
2718
|
return new Set(rows.map((r) => r.node_id));
|
|
@@ -2846,7 +2884,8 @@ var engineCache = new NestEngineCache();
|
|
|
2846
2884
|
var engineApi = createEngineApi();
|
|
2847
2885
|
async function opContext(nestId, actor, onProgress) {
|
|
2848
2886
|
const { storage, query, versions } = await engineCache.get(nestId);
|
|
2849
|
-
|
|
2887
|
+
const limits = { pdfMaxBytes: config.PDF_MAX_BYTES };
|
|
2888
|
+
return { storage, query, versions, actor, onProgress, limits };
|
|
2850
2889
|
}
|
|
2851
2890
|
|
|
2852
2891
|
export {
|
|
@@ -2894,6 +2933,8 @@ export {
|
|
|
2894
2933
|
setAllowSelfApprove,
|
|
2895
2934
|
nestReviewsPrimeOnly,
|
|
2896
2935
|
setPrimeOnlyReview,
|
|
2936
|
+
nestCreatorIsReviewer,
|
|
2937
|
+
setCreatorIsReviewer,
|
|
2897
2938
|
nestPrimeTags,
|
|
2898
2939
|
setPrimeTags,
|
|
2899
2940
|
disableStewardshipAndWipeGovernance,
|
|
@@ -2916,7 +2957,8 @@ export {
|
|
|
2916
2957
|
markIndexStale,
|
|
2917
2958
|
ensureNodeIndex,
|
|
2918
2959
|
rebuildNodeIndex,
|
|
2919
|
-
|
|
2960
|
+
syncSuggestionRows,
|
|
2961
|
+
listSuggestionRows,
|
|
2920
2962
|
nodesWithSuggestions,
|
|
2921
2963
|
indexedNodeIds,
|
|
2922
2964
|
readIndexedNodes,
|
|
@@ -4,19 +4,19 @@ import {
|
|
|
4
4
|
resolveNestWideRoles,
|
|
5
5
|
resolveStewardsForNode,
|
|
6
6
|
stewardCoverageForUser
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2KHM5C7V.js";
|
|
8
8
|
import {
|
|
9
9
|
grantCoversNode,
|
|
10
10
|
listUserGrants,
|
|
11
11
|
resolveNodeGrant
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KPGNZLZL.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-TI7HMP64.js";
|
|
20
20
|
import {
|
|
21
21
|
buildDocContext,
|
|
22
22
|
buildTitleMap,
|
|
@@ -37,16 +37,16 @@ import {
|
|
|
37
37
|
resolveNestPermission,
|
|
38
38
|
sendEmailToRecipient,
|
|
39
39
|
titleForNode
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-7FROMOZM.js";
|
|
41
41
|
import {
|
|
42
42
|
ConflictError,
|
|
43
43
|
NotFoundError,
|
|
44
44
|
ValidationError
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-LHWOJPFE.js";
|
|
46
46
|
import {
|
|
47
47
|
config,
|
|
48
48
|
getDb
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-JDAOA5KP.js";
|
|
50
50
|
|
|
51
51
|
// src/governance/review-service.ts
|
|
52
52
|
import { v4 as uuid2 } from "uuid";
|
|
@@ -788,7 +788,8 @@ async function submitForReview(params) {
|
|
|
788
788
|
);
|
|
789
789
|
if (!alreadySealed) {
|
|
790
790
|
await versionManager.createVersion(node, params.requestedBy, {
|
|
791
|
-
note: params.note || "Submitted for review"
|
|
791
|
+
note: params.note || "Submitted for review",
|
|
792
|
+
...params.client ? { client: params.client } : {}
|
|
792
793
|
});
|
|
793
794
|
}
|
|
794
795
|
} catch (err) {
|
|
@@ -955,7 +956,8 @@ async function approve(params) {
|
|
|
955
956
|
author: params.approvedBy,
|
|
956
957
|
status: "published",
|
|
957
958
|
tags,
|
|
958
|
-
changeNote: note
|
|
959
|
+
changeNote: note,
|
|
960
|
+
client: params.client
|
|
959
961
|
});
|
|
960
962
|
await setApprovedVersion(
|
|
961
963
|
params.nestId,
|
|
@@ -356,6 +356,22 @@ var config = {
|
|
|
356
356
|
get PEOPLE_SUGGEST_ALL_USERS() {
|
|
357
357
|
return process.env.PEOPLE_SUGGEST_ALL_USERS === "true";
|
|
358
358
|
},
|
|
359
|
+
/**
|
|
360
|
+
* This server hosts several unrelated organizations (the PromptOwl-hosted
|
|
361
|
+
* deployment) rather than one company (a self-hosted install).
|
|
362
|
+
*
|
|
363
|
+
* OFF BY DEFAULT. Nest visibility 'org' means "every authenticated account
|
|
364
|
+
* on this deployment" (src/shared/access.ts) — on a single-company server
|
|
365
|
+
* that IS the organization, so the tier is safe to flip in one click. On a
|
|
366
|
+
* shared server it is every customer, so with this on: flipping a nest to
|
|
367
|
+
* 'org' needs the same explicit acknowledgement 'public' always needs
|
|
368
|
+
* (PATCH /nests/:id/visibility 409s without it — the server enforces it, not only the UI), and
|
|
369
|
+
* the UI words both tiers to say so. Exposed on /health as
|
|
370
|
+
* `shared_deployment`. Superadmin-togglable at runtime via /admin/settings.
|
|
371
|
+
*/
|
|
372
|
+
get SHARED_DEPLOYMENT() {
|
|
373
|
+
return process.env.SHARED_DEPLOYMENT === "true";
|
|
374
|
+
},
|
|
359
375
|
/**
|
|
360
376
|
* Path to the .env file the server reads its config from and the license
|
|
361
377
|
* install flow persists PROMPTOWL_KEY into. Defaults UNDER DATA_ROOT (not
|
|
@@ -640,6 +656,17 @@ var config = {
|
|
|
640
656
|
get VIDEO_MAX_BYTES() {
|
|
641
657
|
const mb = parseInt(process.env.VIDEO_MAX_MB || "30", 10);
|
|
642
658
|
return (Number.isFinite(mb) && mb > 0 ? mb : 30) * 1024 * 1024;
|
|
659
|
+
},
|
|
660
|
+
/**
|
|
661
|
+
* Max PDF upload size in bytes (POST /nests/:id/nodes/pdf). Default 25 MB —
|
|
662
|
+
* under Cloud Run's 32 MiB HTTP/1 request-body limit, with room for the
|
|
663
|
+
* multipart framing, so an oversized PDF is refused by our own check (413
|
|
664
|
+
* with a clear message) rather than by the platform. Read per request, so a
|
|
665
|
+
* change takes effect without a restart. Override via PDF_MAX_MB.
|
|
666
|
+
*/
|
|
667
|
+
get PDF_MAX_BYTES() {
|
|
668
|
+
const mb = parseInt(process.env.PDF_MAX_MB || "25", 10);
|
|
669
|
+
return (Number.isFinite(mb) && mb > 0 ? mb : 25) * 1024 * 1024;
|
|
643
670
|
}
|
|
644
671
|
};
|
|
645
672
|
|
|
@@ -828,6 +855,9 @@ function runMigrations(db) {
|
|
|
828
855
|
if (!nestCols.includes("prime_tags")) {
|
|
829
856
|
db.exec("ALTER TABLE nests ADD COLUMN prime_tags TEXT NOT NULL DEFAULT 'prime-document'");
|
|
830
857
|
}
|
|
858
|
+
if (!nestCols.includes("creator_is_reviewer")) {
|
|
859
|
+
db.exec("ALTER TABLE nests ADD COLUMN creator_is_reviewer INTEGER NOT NULL DEFAULT 0");
|
|
860
|
+
}
|
|
831
861
|
if (!nestCols.includes("index_synced_at")) {
|
|
832
862
|
db.exec("ALTER TABLE nests ADD COLUMN index_synced_at TEXT");
|
|
833
863
|
}
|
|
@@ -1757,6 +1787,42 @@ function runMigrations(db) {
|
|
|
1757
1787
|
recordMigration("042_community_sites");
|
|
1758
1788
|
})();
|
|
1759
1789
|
}
|
|
1790
|
+
if (!hasMigration("043_node_suggestion_details")) {
|
|
1791
|
+
db.transaction(() => {
|
|
1792
|
+
db.exec(`
|
|
1793
|
+
DROP TABLE IF EXISTS node_suggestions;
|
|
1794
|
+
CREATE TABLE node_suggestions (
|
|
1795
|
+
nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
|
|
1796
|
+
node_id TEXT NOT NULL,
|
|
1797
|
+
suggestion_id TEXT NOT NULL,
|
|
1798
|
+
source TEXT NOT NULL,
|
|
1799
|
+
actor TEXT NOT NULL,
|
|
1800
|
+
detected_at TEXT NOT NULL,
|
|
1801
|
+
target_hash TEXT NOT NULL,
|
|
1802
|
+
proposed_hash TEXT NOT NULL,
|
|
1803
|
+
note TEXT,
|
|
1804
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1805
|
+
PRIMARY KEY (nest_id, node_id, suggestion_id)
|
|
1806
|
+
);
|
|
1807
|
+
CREATE INDEX IF NOT EXISTS idx_node_suggestions_nest_detected
|
|
1808
|
+
ON node_suggestions(nest_id, detected_at DESC);
|
|
1809
|
+
UPDATE nests SET index_synced_at = NULL;
|
|
1810
|
+
`);
|
|
1811
|
+
recordMigration("043_node_suggestion_details");
|
|
1812
|
+
})();
|
|
1813
|
+
}
|
|
1814
|
+
if (!hasMigration("044_client_attribution")) {
|
|
1815
|
+
for (const [table, col] of [
|
|
1816
|
+
["node_versions", "client_json"],
|
|
1817
|
+
["api_events", "client_json"]
|
|
1818
|
+
]) {
|
|
1819
|
+
const cols = db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name);
|
|
1820
|
+
if (cols.length && !cols.includes(col)) {
|
|
1821
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${col} TEXT`);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
recordMigration("044_client_attribution");
|
|
1825
|
+
}
|
|
1760
1826
|
}
|
|
1761
1827
|
function fkViolationCounts(db) {
|
|
1762
1828
|
const rows = db.pragma("foreign_key_check");
|
|
@@ -2031,7 +2097,7 @@ async function initDb() {
|
|
|
2031
2097
|
if (config.DB_DRIVER === "postgres") {
|
|
2032
2098
|
const { Pool } = await import("pg");
|
|
2033
2099
|
const { PostgresAdapter } = await import("./adapter.postgres-6VZCMPQL.js");
|
|
2034
|
-
const { runPostgresMigrations } = await import("./migrations.postgres-
|
|
2100
|
+
const { runPostgresMigrations } = await import("./migrations.postgres-23VJJJJX.js");
|
|
2035
2101
|
const pool = new Pool(buildPgConfig());
|
|
2036
2102
|
adapter = new PostgresAdapter(pool);
|
|
2037
2103
|
await runPostgresMigrations(adapter);
|
|
@@ -31,6 +31,12 @@ var ValidationError = class extends AppError {
|
|
|
31
31
|
this.name = "ValidationError";
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
+
var PayloadTooLargeError = class extends AppError {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(413, message);
|
|
37
|
+
this.name = "PayloadTooLargeError";
|
|
38
|
+
}
|
|
39
|
+
};
|
|
34
40
|
var ConflictError = class extends AppError {
|
|
35
41
|
constructor(message) {
|
|
36
42
|
super(409, message);
|
|
@@ -61,6 +67,7 @@ export {
|
|
|
61
67
|
ForbiddenError,
|
|
62
68
|
UnauthorizedError,
|
|
63
69
|
ValidationError,
|
|
70
|
+
PayloadTooLargeError,
|
|
64
71
|
ConflictError,
|
|
65
72
|
LockedError,
|
|
66
73
|
VaultBusyError,
|
|
@@ -2,13 +2,88 @@ import {
|
|
|
2
2
|
buildTitleMap,
|
|
3
3
|
insertOrReplace,
|
|
4
4
|
nowExpr
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-7FROMOZM.js";
|
|
6
|
+
import {
|
|
7
|
+
ValidationError
|
|
8
|
+
} from "./chunk-LHWOJPFE.js";
|
|
6
9
|
import {
|
|
7
10
|
getDb
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JDAOA5KP.js";
|
|
9
12
|
|
|
10
13
|
// src/governance/version-service.ts
|
|
11
14
|
import { createHash } from "crypto";
|
|
15
|
+
|
|
16
|
+
// src/shared/client-metadata.ts
|
|
17
|
+
import {
|
|
18
|
+
clientField,
|
|
19
|
+
clientMetadataSchema,
|
|
20
|
+
CLIENT_METADATA_MAX_VALUE_LENGTH
|
|
21
|
+
} from "@promptowl/contextnest-engine/api";
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
var clientToolField = {
|
|
24
|
+
client: z.any().optional().describe(clientField.client.description ?? "")
|
|
25
|
+
};
|
|
26
|
+
function boundedValue(raw) {
|
|
27
|
+
const cut = raw.slice(0, CLIENT_METADATA_MAX_VALUE_LENGTH);
|
|
28
|
+
const last = cut.charCodeAt(cut.length - 1);
|
|
29
|
+
return last >= 55296 && last <= 56319 ? cut.slice(0, -1) : cut;
|
|
30
|
+
}
|
|
31
|
+
function orAbsent(value) {
|
|
32
|
+
return Object.keys(value).length > 0 ? value : void 0;
|
|
33
|
+
}
|
|
34
|
+
function parseClientMetadata(raw) {
|
|
35
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
36
|
+
const parsed = clientMetadataSchema.safeParse(raw);
|
|
37
|
+
if (!parsed.success) {
|
|
38
|
+
const detail = parsed.error.issues.map((i) => i.path.length ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
|
|
39
|
+
throw new ValidationError(`Invalid client metadata \u2014 ${detail}`);
|
|
40
|
+
}
|
|
41
|
+
return orAbsent(parsed.data);
|
|
42
|
+
}
|
|
43
|
+
function mergeClient(derived, supplied) {
|
|
44
|
+
return orAbsent({ ...derived, ...supplied });
|
|
45
|
+
}
|
|
46
|
+
function derivedClient(c, connection) {
|
|
47
|
+
if (process.env.CONTEXTNEST_NO_ATTRIBUTION === "1") return {};
|
|
48
|
+
const agent = process.env.CONTEXTNEST_AGENT || connection?.agent || c.get("apiKeyLabel") || "";
|
|
49
|
+
const session = process.env.CONTEXTNEST_SESSION_ID || connection?.session_id || c.req.header("Mcp-Session-Id") || "";
|
|
50
|
+
return {
|
|
51
|
+
// Truncated to the engine's bound, not rejected: a derived value must never
|
|
52
|
+
// be the reason an otherwise valid call fails validation.
|
|
53
|
+
...agent ? { agent: boundedValue(String(agent)) } : {},
|
|
54
|
+
...session ? { session_id: boundedValue(String(session)) } : {}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function connectionClient(server) {
|
|
58
|
+
const name = server.server.getClientVersion()?.name;
|
|
59
|
+
const sessionId = server.server.transport?.sessionId;
|
|
60
|
+
return {
|
|
61
|
+
...name ? { agent: name } : {},
|
|
62
|
+
...sessionId ? { session_id: sessionId } : {}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function attributeRequest(c, supplied, connection) {
|
|
66
|
+
const client = mergeClient(
|
|
67
|
+
derivedClient(c, connection),
|
|
68
|
+
parseClientMetadata(supplied)
|
|
69
|
+
);
|
|
70
|
+
if (client) c.set("clientMetadata", client);
|
|
71
|
+
return client;
|
|
72
|
+
}
|
|
73
|
+
function serializeClient(value) {
|
|
74
|
+
return value ? JSON.stringify(value) : null;
|
|
75
|
+
}
|
|
76
|
+
function deserializeClient(raw) {
|
|
77
|
+
if (typeof raw !== "string" || !raw) return void 0;
|
|
78
|
+
try {
|
|
79
|
+
const parsed = JSON.parse(raw);
|
|
80
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? orAbsent(parsed) : void 0;
|
|
81
|
+
} catch {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/governance/version-service.ts
|
|
12
87
|
function hashContent(content) {
|
|
13
88
|
return createHash("sha256").update(content).digest("hex");
|
|
14
89
|
}
|
|
@@ -20,8 +95,8 @@ async function createVersion(params) {
|
|
|
20
95
|
const db = getDb();
|
|
21
96
|
const contentHash = hashContent(params.content);
|
|
22
97
|
await db.run(
|
|
23
|
-
`INSERT INTO node_versions (nest_id, node_id, version, content_hash, author, status, change_note, tags_json)
|
|
24
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
98
|
+
`INSERT INTO node_versions (nest_id, node_id, version, content_hash, author, status, change_note, tags_json, client_json)
|
|
99
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
25
100
|
[
|
|
26
101
|
params.nestId,
|
|
27
102
|
params.nodeId,
|
|
@@ -30,7 +105,8 @@ async function createVersion(params) {
|
|
|
30
105
|
params.author,
|
|
31
106
|
params.status,
|
|
32
107
|
params.changeNote || null,
|
|
33
|
-
params.tags ? JSON.stringify(params.tags) : null
|
|
108
|
+
params.tags ? JSON.stringify(params.tags) : null,
|
|
109
|
+
serializeClient(params.client)
|
|
34
110
|
]
|
|
35
111
|
);
|
|
36
112
|
return {
|
|
@@ -39,7 +115,8 @@ async function createVersion(params) {
|
|
|
39
115
|
editedBy: params.author,
|
|
40
116
|
editedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41
117
|
changeNote: params.changeNote,
|
|
42
|
-
status: params.status
|
|
118
|
+
status: params.status,
|
|
119
|
+
client: params.client
|
|
43
120
|
};
|
|
44
121
|
}
|
|
45
122
|
async function upsertVersion(params) {
|
|
@@ -47,13 +124,13 @@ async function upsertVersion(params) {
|
|
|
47
124
|
const contentHash = hashContent(params.content);
|
|
48
125
|
const sql = insertOrReplace(
|
|
49
126
|
db,
|
|
50
|
-
`INSERT INTO node_versions (nest_id, node_id, version, content_hash, author, status, change_note, tags_json)
|
|
51
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
127
|
+
`INSERT INTO node_versions (nest_id, node_id, version, content_hash, author, status, change_note, tags_json, client_json)
|
|
128
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
52
129
|
["nest_id", "node_id", "version"],
|
|
53
130
|
// Reset created_at on conflict too: SQLite's INSERT OR REPLACE deletes+
|
|
54
131
|
// reinserts (created_at → now), so mirror that on Postgres — otherwise a
|
|
55
132
|
// draft edited in place would keep its original (import-time) "edited at".
|
|
56
|
-
`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)}`
|
|
133
|
+
`content_hash = excluded.content_hash, author = excluded.author, status = excluded.status, change_note = excluded.change_note, tags_json = excluded.tags_json, client_json = excluded.client_json, created_at = ${nowExpr(db)}`
|
|
57
134
|
);
|
|
58
135
|
await db.run(sql, [
|
|
59
136
|
params.nestId,
|
|
@@ -63,7 +140,8 @@ async function upsertVersion(params) {
|
|
|
63
140
|
params.author,
|
|
64
141
|
params.status,
|
|
65
142
|
params.changeNote || null,
|
|
66
|
-
params.tags ? JSON.stringify(params.tags) : null
|
|
143
|
+
params.tags ? JSON.stringify(params.tags) : null,
|
|
144
|
+
serializeClient(params.client)
|
|
67
145
|
]);
|
|
68
146
|
return {
|
|
69
147
|
version: params.version,
|
|
@@ -71,7 +149,8 @@ async function upsertVersion(params) {
|
|
|
71
149
|
editedBy: params.author,
|
|
72
150
|
editedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
73
151
|
changeNote: params.changeNote,
|
|
74
|
-
status: params.status
|
|
152
|
+
status: params.status,
|
|
153
|
+
client: params.client
|
|
75
154
|
};
|
|
76
155
|
}
|
|
77
156
|
async function getVersions(nestId, nodeId) {
|
|
@@ -354,11 +433,19 @@ function rowToVersion(row) {
|
|
|
354
433
|
editedBy: row.author,
|
|
355
434
|
editedAt: row.created_at,
|
|
356
435
|
changeNote: row.change_note || void 0,
|
|
357
|
-
status: row.status
|
|
436
|
+
status: row.status,
|
|
437
|
+
client: deserializeClient(row.client_json)
|
|
358
438
|
};
|
|
359
439
|
}
|
|
360
440
|
|
|
361
441
|
export {
|
|
442
|
+
clientToolField,
|
|
443
|
+
parseClientMetadata,
|
|
444
|
+
mergeClient,
|
|
445
|
+
connectionClient,
|
|
446
|
+
attributeRequest,
|
|
447
|
+
serializeClient,
|
|
448
|
+
deserializeClient,
|
|
362
449
|
hashContent,
|
|
363
450
|
SYSTEM_AUTHOR_PREFIX,
|
|
364
451
|
systemAuthor,
|
|
@@ -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-7FROMOZM.js";
|
|
6
|
+
import "./chunk-LHWOJPFE.js";
|
|
7
|
+
import "./chunk-JDAOA5KP.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-32SZCH36.js";
|
|
15
|
+
import "./chunk-7FROMOZM.js";
|
|
16
|
+
import "./chunk-LHWOJPFE.js";
|
|
17
|
+
import "./chunk-JDAOA5KP.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-KPGNZLZL.js";
|
|
10
|
+
import "./chunk-LHWOJPFE.js";
|
|
11
|
+
import "./chunk-JDAOA5KP.js";
|
|
12
12
|
import "./chunk-YB3LKF7U.js";
|
|
13
13
|
export {
|
|
14
14
|
createGrant,
|