@promptowl/contextnest-community 1.22.0 → 1.24.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 +2092 -0
- package/CONFIGURATION.md +5 -2
- package/README.md +62 -10
- package/STEWARDSHIP.md +229 -0
- package/dist/{chunk-PX3P4FTU.js → chunk-2TPQTN4Y.js} +5 -4
- package/dist/{chunk-OQXZ43HG.js → chunk-5EZOPA47.js} +40 -10
- package/dist/{chunk-MY4JIWQD.js → chunk-I3CSD6CK.js} +1 -1
- package/dist/{chunk-W5ILNGPD.js → chunk-KIAAEHWL.js} +80 -28
- package/dist/{chunk-J2OQ3MEB.js → chunk-LA3VTQ22.js} +63 -1
- package/dist/{chunk-ZAV4QUZA.js → chunk-XUIWAWDO.js} +28 -33
- package/dist/{chunk-6WESEA75.js → chunk-ZTT4U4NE.js} +2 -2
- package/dist/{client-CSCHXUNX.js → client-KEY4PYJH.js} +1 -1
- package/dist/{engine-XKRP7GOQ.js → engine-S3QBQ7LH.js} +2 -2
- package/dist/{external-edit-service-TXW7SXSF.js → external-edit-service-6FNFPOJJ.js} +3 -3
- package/dist/{grants-service-UZ2MGDO5.js → grants-service-UT3EQ3R7.js} +2 -2
- package/dist/index.js +1261 -305
- package/dist/{migrations.postgres-KKICIH2C.js → migrations.postgres-AIQ7WSU7.js} +60 -4
- package/dist/{review-service-XFWTENTN.js → review-service-HHGOTO6P.js} +6 -6
- package/dist/{stewardship-service-5HBQGDMK.js → stewardship-service-4DHNRULG.js} +3 -3
- package/dist/{version-service-TKNYJMCM.js → version-service-KIOQME6U.js} +3 -3
- package/dist/web3/assets/ActivityTracePage-BOZHgJ8S.js +1 -0
- package/dist/web3/assets/AgentDocsPage-CIiaBqMy.js +1 -0
- package/dist/web3/assets/CollaboratorManager-By91wrIr.js +1 -0
- package/dist/web3/assets/CollaboratorsTab-D1EE64Fs.js +1 -0
- package/dist/web3/assets/DocumentEditor-DrQfrW3d.js +36 -0
- package/dist/web3/assets/DocumentsTab-Vba-AzBv.js +6 -0
- package/dist/web3/assets/ExternalEditsTab-BSzAQIGM.js +1 -0
- package/dist/web3/assets/MarkdownEditor-74N_naNQ.css +1 -0
- package/dist/web3/assets/MarkdownEditor-C9zqZjC5.js +643 -0
- package/dist/web3/assets/NestPageHeader-CoLUV1Oz.js +1 -0
- package/dist/web3/assets/NestView-CTZ55tXD.js +63 -0
- package/dist/web3/assets/OverviewTab-Dxi-Hu-N.js +1 -0
- package/dist/web3/assets/PersonCombobox-CPOlNs6G.js +1 -0
- package/dist/web3/assets/ReasonDialog-DgLxfRWv.js +1 -0
- package/dist/web3/assets/ReviewActions-OfOdqzCn.js +6 -0
- package/dist/web3/assets/ReviewTab-CyX8yxd3.js +1 -0
- package/dist/web3/assets/StewardsTab-DBEEfAQI.js +1 -0
- package/dist/web3/assets/SubmitForReviewModal-Ci2DdWs0.js +1 -0
- package/dist/web3/assets/alert-dialog-DTozKlmV.js +7 -0
- package/dist/web3/assets/arrow-left-BCKt4CwQ.js +6 -0
- package/dist/web3/assets/backlinks-CYd4xENL.js +24 -0
- package/dist/web3/assets/card-XG2dP5Xf.js +1 -0
- package/dist/web3/assets/chevron-left-BPwUT9DS.js +6 -0
- package/dist/web3/assets/circle-check-DI8IBGeo.js +6 -0
- package/dist/web3/assets/circle-x-VKVhC88W.js +6 -0
- package/dist/web3/assets/code-xml-CMabYZvH.js +6 -0
- package/dist/web3/assets/corner-down-right-BGxwK6wH.js +6 -0
- package/dist/web3/assets/count-skeleton-DMbdpscX.js +1 -0
- package/dist/web3/assets/dates-BCxbm4_q.js +1 -0
- package/dist/web3/assets/earth-BEp1EKTo.js +6 -0
- package/dist/web3/assets/file-exclamation-point-r8qZGIjP.js +6 -0
- package/dist/web3/assets/folder-input-Cmtx1Jhk.js +11 -0
- package/dist/web3/assets/folder-target-CUSWqImF.js +1 -0
- package/dist/web3/assets/index-BM-h3DwI.css +1 -0
- package/dist/web3/assets/index-C1wTSjfP.js +29 -0
- package/dist/web3/assets/index-EaX2yql0.js +389 -0
- package/dist/web3/assets/page-B3yvQvHy.js +1 -0
- package/dist/web3/assets/page-BExMvttJ.js +1 -0
- package/dist/web3/assets/page-BUOADv2X.js +1 -0
- package/dist/web3/assets/page-BYUFhYWZ.js +1 -0
- package/dist/web3/assets/page-Bicf02Cz.js +1 -0
- package/dist/web3/assets/page-BsPMDm5d.js +45 -0
- package/dist/web3/assets/page-ByyVLDVo.js +16 -0
- package/dist/web3/assets/page-CC0NJi8v.js +1 -0
- package/dist/web3/assets/page-CHb0F5hV.js +24 -0
- package/dist/web3/assets/page-CPjWQNKx.js +11 -0
- package/dist/web3/assets/page-CQt3ZGbf.js +2 -0
- package/dist/web3/assets/page-Cj1NetQ-.js +1 -0
- package/dist/web3/assets/page-CtaW65El.js +1 -0
- package/dist/web3/assets/page-DGOL9l8i.js +6 -0
- package/dist/web3/assets/page-title-ClvvBsIo.js +1 -0
- package/dist/web3/assets/play-C5YNKpAH.js +6 -0
- package/dist/web3/assets/refresh-cw-BhHzSH9m.js +6 -0
- package/dist/web3/assets/scroll-area-dRWncRqa.css +1 -0
- package/dist/web3/assets/scroll-area-hz-xtayP.js +1 -0
- package/dist/web3/assets/select-DBFTrUmI.js +6 -0
- package/dist/web3/assets/send-0CHvmluT.js +6 -0
- package/dist/web3/assets/settings-CeeF4LEb.js +6 -0
- package/dist/web3/assets/share-2-CMnOlOt-.js +6 -0
- package/dist/web3/assets/tag-DQ_6J5Gv.js +11 -0
- package/dist/web3/assets/trash-2-HBi-Pslz.js +6 -0
- package/dist/web3/assets/triangle-alert-CDy8-7sv.js +6 -0
- package/dist/web3/assets/user-plus-CvBNcpn4.js +6 -0
- package/dist/web3/assets/x-By7piikG.js +6 -0
- package/dist/web3/assets/zap-C2T8riBp.js +11 -0
- package/dist/web3/index.html +2 -2
- package/package.json +4 -2
- package/dist/web3/assets/index-C2hW_9j9.js +0 -1382
- package/dist/web3/assets/index-T4pilFNL.css +0 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
config,
|
|
9
9
|
getDb,
|
|
10
10
|
isEmailish
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-LA3VTQ22.js";
|
|
12
12
|
import {
|
|
13
13
|
ANON_USER_ID
|
|
14
14
|
} from "./chunk-YB3LKF7U.js";
|
|
@@ -138,6 +138,12 @@ function startTelemetryLoop() {
|
|
|
138
138
|
function nowExpr(db) {
|
|
139
139
|
return db.dialect === "sqlite" ? "datetime('now')" : "to_char((now() AT TIME ZONE 'utc'), 'YYYY-MM-DD HH24:MI:SS')";
|
|
140
140
|
}
|
|
141
|
+
function suffixOf(db, column, length) {
|
|
142
|
+
return db.dialect === "sqlite" ? `substr(${column}, -${length})` : `right(${column}, ${length})`;
|
|
143
|
+
}
|
|
144
|
+
function nowMsExpr(db) {
|
|
145
|
+
return db.dialect === "sqlite" ? "strftime('%Y-%m-%d %H:%M:%f','now')" : "to_char((now() AT TIME ZONE 'utc'), 'YYYY-MM-DD HH24:MI:SS.MS')";
|
|
146
|
+
}
|
|
141
147
|
function insertOrIgnore(db, insertSql) {
|
|
142
148
|
return db.dialect === "sqlite" ? insertSql.replace(/^\s*INSERT\s+INTO/i, "INSERT OR IGNORE INTO") : `${insertSql} ON CONFLICT DO NOTHING`;
|
|
143
149
|
}
|
|
@@ -657,6 +663,7 @@ function renderText(message) {
|
|
|
657
663
|
}
|
|
658
664
|
var STATUS_META = {
|
|
659
665
|
approved: { subjectWord: "approved", label: "Approved", color: "#16a34a" },
|
|
666
|
+
mention: { subjectWord: "mentioned you", label: "Mention", color: "#7c3aed" },
|
|
660
667
|
rejected: { subjectWord: "rejected", label: "Rejected", color: "#dc2626" },
|
|
661
668
|
pending_review: {
|
|
662
669
|
subjectWord: "awaiting review",
|
|
@@ -715,6 +722,17 @@ var TEMPLATES = {
|
|
|
715
722
|
lineH: (x) => `${x.doc}${x.vH} has been approved${x.byH}${x.noteH}.`,
|
|
716
723
|
rows: governanceRows
|
|
717
724
|
},
|
|
725
|
+
// A mention names one person, so this is the one template addressed to
|
|
726
|
+
// "you". `note` carries the comment's own words when there are any — a
|
|
727
|
+
// mention with no context reads as a summons.
|
|
728
|
+
mention: {
|
|
729
|
+
heading: (x) => `${x.d.actor || "Someone"} mentioned you in ${x.title}`,
|
|
730
|
+
detailsHeader: "Document details",
|
|
731
|
+
linkLabel: "Open document",
|
|
732
|
+
lineT: (x) => `You were mentioned in ${x.title}${x.byT}${x.noteT}.`,
|
|
733
|
+
lineH: (x) => `You were mentioned in ${x.doc}${x.byH}${x.noteH}.`,
|
|
734
|
+
rows: (x) => [plainRow("Nest", x.d.path)]
|
|
735
|
+
},
|
|
718
736
|
rejected: {
|
|
719
737
|
heading: (x) => `${x.title} is ${x.meta.subjectWord}`,
|
|
720
738
|
detailsHeader: "Document details",
|
|
@@ -1278,8 +1296,11 @@ async function listNotifications(userEmail, opts = {}) {
|
|
|
1278
1296
|
CASE WHEN dr.target_type = 'document' THEN dr.node_id END,
|
|
1279
1297
|
-- A mention names the document directly; there's no request row.
|
|
1280
1298
|
CASE WHEN n.kind = 'mention' THEN n.subject_id END
|
|
1281
|
-
) 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
|
|
1282
1302
|
FROM notifications n
|
|
1303
|
+
LEFT JOIN nests ns ON ns.id = n.nest_id
|
|
1283
1304
|
LEFT JOIN review_requests rr ON rr.id = n.subject_id
|
|
1284
1305
|
LEFT JOIN deletion_requests dr ON dr.id = n.subject_id
|
|
1285
1306
|
WHERE LOWER(n.user_email) = LOWER(?)${opts.unreadOnly ? " AND n.read_at IS NULL" : ""}
|
|
@@ -1304,6 +1325,12 @@ async function markNotificationsRead(userEmail, ids) {
|
|
|
1304
1325
|
);
|
|
1305
1326
|
}
|
|
1306
1327
|
}
|
|
1328
|
+
async function markNotificationsReadBySubject(subjectId, kind) {
|
|
1329
|
+
await getDb().run(
|
|
1330
|
+
"UPDATE notifications SET read_at = ? WHERE subject_id = ? AND kind = ? AND read_at IS NULL",
|
|
1331
|
+
[(/* @__PURE__ */ new Date()).toISOString(), subjectId, kind]
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1307
1334
|
|
|
1308
1335
|
// src/governance/teams-service.ts
|
|
1309
1336
|
var VALID_TEAM_ROLES = ["admin", "editor", "viewer"];
|
|
@@ -2195,22 +2222,23 @@ var LIVE_ONLY_N = "n.archived_at IS NULL";
|
|
|
2195
2222
|
async function listNests(userId, opts = {}) {
|
|
2196
2223
|
const db = getDb();
|
|
2197
2224
|
const scope = opts.archived ? "archived_at IS NOT NULL" : LIVE_ONLY;
|
|
2225
|
+
const orderBy = opts.archived ? "archived_at DESC" : "created_at DESC";
|
|
2198
2226
|
if (userId === ANON_USER_ID) {
|
|
2199
2227
|
return await db.all(
|
|
2200
|
-
`SELECT * FROM nests WHERE user_id = ? AND ${scope} ORDER BY
|
|
2228
|
+
`SELECT * FROM nests WHERE user_id = ? AND ${scope} ORDER BY ${orderBy}`,
|
|
2201
2229
|
[ANON_USER_ID]
|
|
2202
2230
|
);
|
|
2203
2231
|
}
|
|
2204
2232
|
const includeAnon = config.AUTH_MODE === "open" || await isLicenseAdminUserId(userId);
|
|
2205
2233
|
if (!includeAnon) {
|
|
2206
2234
|
return await db.all(
|
|
2207
|
-
`SELECT * FROM nests WHERE user_id = ? AND ${scope} ORDER BY
|
|
2235
|
+
`SELECT * FROM nests WHERE user_id = ? AND ${scope} ORDER BY ${orderBy}`,
|
|
2208
2236
|
[userId]
|
|
2209
2237
|
);
|
|
2210
2238
|
}
|
|
2211
2239
|
return await db.all(
|
|
2212
2240
|
`SELECT * FROM nests WHERE (user_id = ? OR user_id = ?) AND ${scope}
|
|
2213
|
-
ORDER BY
|
|
2241
|
+
ORDER BY ${orderBy}`,
|
|
2214
2242
|
[userId, ANON_USER_ID]
|
|
2215
2243
|
);
|
|
2216
2244
|
}
|
|
@@ -2463,6 +2491,9 @@ var FlatNestStorage = class extends NestStorage2 {
|
|
|
2463
2491
|
}
|
|
2464
2492
|
};
|
|
2465
2493
|
|
|
2494
|
+
// src/nodes/node-index.ts
|
|
2495
|
+
import { listSuggestions } from "@promptowl/contextnest-engine";
|
|
2496
|
+
|
|
2466
2497
|
// src/shared/node-id.ts
|
|
2467
2498
|
function assertSafeNodeId(rawId) {
|
|
2468
2499
|
let id = rawId;
|
|
@@ -2591,13 +2622,21 @@ async function ensureNodeIndex(nestId) {
|
|
|
2591
2622
|
return run;
|
|
2592
2623
|
}
|
|
2593
2624
|
async function rebuildNodeIndex(nestId) {
|
|
2594
|
-
const { engineCache: engineCache2 } = await import("./engine-
|
|
2595
|
-
const { documentsWithSuggestions } = await import("./external-edit-service-
|
|
2625
|
+
const { engineCache: engineCache2 } = await import("./engine-S3QBQ7LH.js");
|
|
2626
|
+
const { documentsWithSuggestions } = await import("./external-edit-service-6FNFPOJJ.js");
|
|
2596
2627
|
const { storage, dropDiscoveryCache } = await engineCache2.get(nestId);
|
|
2597
2628
|
dropDiscoveryCache();
|
|
2598
2629
|
const startedAt = writeGeneration.get(nestId) ?? 0;
|
|
2599
2630
|
const documents = await storage.discoverDocuments({ includeRetired: true });
|
|
2600
2631
|
const staged = await documentsWithSuggestions(nestId);
|
|
2632
|
+
const stagedMetas = (await Promise.all(
|
|
2633
|
+
[...staged].map(
|
|
2634
|
+
(id) => listSuggestions(storage, id).catch((err) => {
|
|
2635
|
+
console.error("[node-index] listSuggestions failed", nestId, id, err);
|
|
2636
|
+
return [];
|
|
2637
|
+
})
|
|
2638
|
+
)
|
|
2639
|
+
)).flat();
|
|
2601
2640
|
const db = getDb();
|
|
2602
2641
|
const insertSql = insertOrReplace(
|
|
2603
2642
|
db,
|
|
@@ -2605,18 +2644,15 @@ async function rebuildNodeIndex(nestId) {
|
|
|
2605
2644
|
["nest_id", "node_id"],
|
|
2606
2645
|
UPSERT_SET
|
|
2607
2646
|
);
|
|
2608
|
-
const stagedSql = insertOrIgnore(
|
|
2609
|
-
db,
|
|
2610
|
-
"INSERT INTO node_suggestions (nest_id, node_id) VALUES (?, ?)"
|
|
2611
|
-
);
|
|
2647
|
+
const stagedSql = insertOrIgnore(db, SUGGESTION_INSERT);
|
|
2612
2648
|
await db.transaction(async (tx) => {
|
|
2613
2649
|
await tx.run("DELETE FROM node_index WHERE nest_id = ?", [nestId]);
|
|
2614
2650
|
for (const doc of documents) {
|
|
2615
2651
|
await tx.run(insertSql, [nestId, ...rowValues(doc)]);
|
|
2616
2652
|
}
|
|
2617
2653
|
await tx.run("DELETE FROM node_suggestions WHERE nest_id = ?", [nestId]);
|
|
2618
|
-
for (const
|
|
2619
|
-
await tx.run(stagedSql,
|
|
2654
|
+
for (const m of stagedMetas) {
|
|
2655
|
+
await tx.run(stagedSql, suggestionValues(nestId, m));
|
|
2620
2656
|
}
|
|
2621
2657
|
if ((writeGeneration.get(nestId) ?? 0) !== startedAt) return;
|
|
2622
2658
|
await tx.run(
|
|
@@ -2626,30 +2662,42 @@ async function rebuildNodeIndex(nestId) {
|
|
|
2626
2662
|
});
|
|
2627
2663
|
return documents.length;
|
|
2628
2664
|
}
|
|
2629
|
-
|
|
2665
|
+
var SUGGESTION_INSERT = "INSERT INTO node_suggestions (nest_id, node_id, suggestion_id, source, actor, detected_at, target_hash, proposed_hash, note) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
2666
|
+
var suggestionValues = (nestId, m) => [
|
|
2667
|
+
nestId,
|
|
2668
|
+
m.document_id,
|
|
2669
|
+
m.suggestion_id,
|
|
2670
|
+
m.source,
|
|
2671
|
+
m.actor,
|
|
2672
|
+
m.detected_at,
|
|
2673
|
+
m.target_hash,
|
|
2674
|
+
m.proposed_hash,
|
|
2675
|
+
m.note ?? null
|
|
2676
|
+
];
|
|
2677
|
+
async function syncSuggestionRows(nestId, nodeId, metas) {
|
|
2630
2678
|
try {
|
|
2631
2679
|
const db = getDb();
|
|
2632
|
-
|
|
2633
|
-
|
|
2680
|
+
const insertSql = insertOrIgnore(db, SUGGESTION_INSERT);
|
|
2681
|
+
await db.transaction(async (tx) => {
|
|
2682
|
+
await tx.run(
|
|
2634
2683
|
"DELETE FROM node_suggestions WHERE nest_id = ? AND node_id = ?",
|
|
2635
2684
|
[nestId, nodeId]
|
|
2636
2685
|
);
|
|
2637
|
-
|
|
2638
|
-
}
|
|
2639
|
-
await db.run(
|
|
2640
|
-
insertOrIgnore(
|
|
2641
|
-
db,
|
|
2642
|
-
"INSERT INTO node_suggestions (nest_id, node_id) VALUES (?, ?)"
|
|
2643
|
-
),
|
|
2644
|
-
[nestId, nodeId]
|
|
2645
|
-
);
|
|
2686
|
+
for (const m of metas) await tx.run(insertSql, suggestionValues(nestId, m));
|
|
2687
|
+
});
|
|
2646
2688
|
} catch (err) {
|
|
2647
|
-
console.error("[node-index] suggestion
|
|
2689
|
+
console.error("[node-index] suggestion rows failed", nestId, nodeId, err);
|
|
2648
2690
|
}
|
|
2649
2691
|
}
|
|
2692
|
+
async function listSuggestionRows(nestId) {
|
|
2693
|
+
return await getDb().all(
|
|
2694
|
+
"SELECT * FROM node_suggestions WHERE nest_id = ? ORDER BY detected_at DESC",
|
|
2695
|
+
[nestId]
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2650
2698
|
async function nodesWithSuggestions(nestId) {
|
|
2651
2699
|
const rows = await getDb().all(
|
|
2652
|
-
"SELECT node_id FROM node_suggestions WHERE nest_id = ?",
|
|
2700
|
+
"SELECT DISTINCT node_id FROM node_suggestions WHERE nest_id = ?",
|
|
2653
2701
|
[nestId]
|
|
2654
2702
|
);
|
|
2655
2703
|
return new Set(rows.map((r) => r.node_id));
|
|
@@ -2826,6 +2874,8 @@ async function opContext(nestId, actor, onProgress) {
|
|
|
2826
2874
|
|
|
2827
2875
|
export {
|
|
2828
2876
|
nowExpr,
|
|
2877
|
+
suffixOf,
|
|
2878
|
+
nowMsExpr,
|
|
2829
2879
|
insertOrIgnore,
|
|
2830
2880
|
insertOrReplace,
|
|
2831
2881
|
trackEvent,
|
|
@@ -2889,7 +2939,8 @@ export {
|
|
|
2889
2939
|
markIndexStale,
|
|
2890
2940
|
ensureNodeIndex,
|
|
2891
2941
|
rebuildNodeIndex,
|
|
2892
|
-
|
|
2942
|
+
syncSuggestionRows,
|
|
2943
|
+
listSuggestionRows,
|
|
2893
2944
|
nodesWithSuggestions,
|
|
2894
2945
|
indexedNodeIds,
|
|
2895
2946
|
readIndexedNodes,
|
|
@@ -2914,6 +2965,7 @@ export {
|
|
|
2914
2965
|
notifyNestInvite,
|
|
2915
2966
|
listNotifications,
|
|
2916
2967
|
markNotificationsRead,
|
|
2968
|
+
markNotificationsReadBySubject,
|
|
2917
2969
|
createTeam,
|
|
2918
2970
|
findTeamByExternalId,
|
|
2919
2971
|
listTeamsForUser,
|
|
@@ -334,6 +334,28 @@ var config = {
|
|
|
334
334
|
get OIDC_DEPARTMENT_TAGGING() {
|
|
335
335
|
return process.env.OIDC_DEPARTMENT_TAGGING === "true";
|
|
336
336
|
},
|
|
337
|
+
/**
|
|
338
|
+
* Widen the add-a-person suggestions (GET /people/suggest) from "people you
|
|
339
|
+
* already work with" to every registered account on this server.
|
|
340
|
+
*
|
|
341
|
+
* OFF BY DEFAULT, and the default is the point. On a single-company
|
|
342
|
+
* self-hosted server every account is a colleague, so the whole directory is
|
|
343
|
+
* the useful answer and this saves asking someone for their address. On the
|
|
344
|
+
* PromptOwl-hosted deployment, one server holds many unrelated customers —
|
|
345
|
+
* so the same list is one customer's staff being offered another customer's
|
|
346
|
+
* email addresses. The hosted deployment leaves this unset; a self-hosted
|
|
347
|
+
* operator who knows their user table is their own staff turns it on.
|
|
348
|
+
*
|
|
349
|
+
* Narrows nothing when off: the default scoping (shared nests and teams,
|
|
350
|
+
* plus everything for a server admin, who administers the accounts anyway)
|
|
351
|
+
* is unchanged. Suggestions never gate the add either way — an address that
|
|
352
|
+
* appears in no list is still valid to type, so this only ever affects what
|
|
353
|
+
* is *offered*, never who can be invited. Superadmin-togglable at runtime
|
|
354
|
+
* via /admin/settings.
|
|
355
|
+
*/
|
|
356
|
+
get PEOPLE_SUGGEST_ALL_USERS() {
|
|
357
|
+
return process.env.PEOPLE_SUGGEST_ALL_USERS === "true";
|
|
358
|
+
},
|
|
337
359
|
/**
|
|
338
360
|
* Path to the .env file the server reads its config from and the license
|
|
339
361
|
* install flow persists PROMPTOWL_KEY into. Defaults UNDER DATA_ROOT (not
|
|
@@ -1719,6 +1741,46 @@ function runMigrations(db) {
|
|
|
1719
1741
|
widenNestVisibilityWithGuard(db);
|
|
1720
1742
|
recordMigration("041_nest_visibility_org");
|
|
1721
1743
|
}
|
|
1744
|
+
if (!hasMigration("042_community_sites")) {
|
|
1745
|
+
db.transaction(() => {
|
|
1746
|
+
db.exec(`
|
|
1747
|
+
CREATE TABLE IF NOT EXISTS community_sites (
|
|
1748
|
+
id TEXT PRIMARY KEY,
|
|
1749
|
+
name TEXT NOT NULL,
|
|
1750
|
+
url TEXT NOT NULL UNIQUE,
|
|
1751
|
+
secret TEXT NOT NULL,
|
|
1752
|
+
is_active INTEGER NOT NULL DEFAULT 1,
|
|
1753
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1754
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
1755
|
+
);
|
|
1756
|
+
`);
|
|
1757
|
+
recordMigration("042_community_sites");
|
|
1758
|
+
})();
|
|
1759
|
+
}
|
|
1760
|
+
if (!hasMigration("043_node_suggestion_details")) {
|
|
1761
|
+
db.transaction(() => {
|
|
1762
|
+
db.exec(`
|
|
1763
|
+
DROP TABLE IF EXISTS node_suggestions;
|
|
1764
|
+
CREATE TABLE node_suggestions (
|
|
1765
|
+
nest_id TEXT NOT NULL REFERENCES nests(id) ON DELETE CASCADE,
|
|
1766
|
+
node_id TEXT NOT NULL,
|
|
1767
|
+
suggestion_id TEXT NOT NULL,
|
|
1768
|
+
source TEXT NOT NULL,
|
|
1769
|
+
actor TEXT NOT NULL,
|
|
1770
|
+
detected_at TEXT NOT NULL,
|
|
1771
|
+
target_hash TEXT NOT NULL,
|
|
1772
|
+
proposed_hash TEXT NOT NULL,
|
|
1773
|
+
note TEXT,
|
|
1774
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
1775
|
+
PRIMARY KEY (nest_id, node_id, suggestion_id)
|
|
1776
|
+
);
|
|
1777
|
+
CREATE INDEX IF NOT EXISTS idx_node_suggestions_nest_detected
|
|
1778
|
+
ON node_suggestions(nest_id, detected_at DESC);
|
|
1779
|
+
UPDATE nests SET index_synced_at = NULL;
|
|
1780
|
+
`);
|
|
1781
|
+
recordMigration("043_node_suggestion_details");
|
|
1782
|
+
})();
|
|
1783
|
+
}
|
|
1722
1784
|
}
|
|
1723
1785
|
function fkViolationCounts(db) {
|
|
1724
1786
|
const rows = db.pragma("foreign_key_check");
|
|
@@ -1993,7 +2055,7 @@ async function initDb() {
|
|
|
1993
2055
|
if (config.DB_DRIVER === "postgres") {
|
|
1994
2056
|
const { Pool } = await import("pg");
|
|
1995
2057
|
const { PostgresAdapter } = await import("./adapter.postgres-6VZCMPQL.js");
|
|
1996
|
-
const { runPostgresMigrations } = await import("./migrations.postgres-
|
|
2058
|
+
const { runPostgresMigrations } = await import("./migrations.postgres-AIQ7WSU7.js");
|
|
1997
2059
|
const pool = new Pool(buildPgConfig());
|
|
1998
2060
|
adapter = new PostgresAdapter(pool);
|
|
1999
2061
|
await runPostgresMigrations(adapter);
|
|
@@ -2,13 +2,14 @@ import {
|
|
|
2
2
|
engineCache,
|
|
3
3
|
indexedNodeIds,
|
|
4
4
|
isNestArchived,
|
|
5
|
+
listSuggestionRows,
|
|
5
6
|
markIndexStale,
|
|
6
7
|
resolveNestPath,
|
|
7
|
-
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
syncSuggestionRows
|
|
9
|
+
} from "./chunk-KIAAEHWL.js";
|
|
9
10
|
import {
|
|
10
11
|
getDb
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LA3VTQ22.js";
|
|
12
13
|
|
|
13
14
|
// src/governance/external-edit-service.ts
|
|
14
15
|
import { readFile, readdir } from "fs/promises";
|
|
@@ -82,7 +83,7 @@ async function scanDocumentForDriftInternal(nestId, documentId, actor) {
|
|
|
82
83
|
if (await bodyMatchesLatestVersion(storage, documentId, drift.actualHash)) {
|
|
83
84
|
return null;
|
|
84
85
|
}
|
|
85
|
-
const { hasUnsealedDraft } = await import("./version-service-
|
|
86
|
+
const { hasUnsealedDraft } = await import("./version-service-KIOQME6U.js");
|
|
86
87
|
if (await hasUnsealedDraft(nestId, documentId)) {
|
|
87
88
|
return null;
|
|
88
89
|
}
|
|
@@ -91,7 +92,7 @@ async function scanDocumentForDriftInternal(nestId, documentId, actor) {
|
|
|
91
92
|
const existing = await listSuggestions(storage, documentId);
|
|
92
93
|
const dup = existing.find((s) => s.proposed_hash === drift.actualHash);
|
|
93
94
|
if (dup) {
|
|
94
|
-
await
|
|
95
|
+
await syncSuggestionRows(nestId, documentId, existing);
|
|
95
96
|
return { meta: dup, created: false };
|
|
96
97
|
}
|
|
97
98
|
const result = await stageSuggestion({
|
|
@@ -103,13 +104,13 @@ async function scanDocumentForDriftInternal(nestId, documentId, actor) {
|
|
|
103
104
|
actor,
|
|
104
105
|
docTier: "standard"
|
|
105
106
|
});
|
|
106
|
-
await
|
|
107
|
+
await syncSuggestionRows(nestId, documentId, [...existing, result.meta]);
|
|
107
108
|
return { meta: result.meta, created: true };
|
|
108
109
|
}
|
|
109
110
|
async function refreshSuggestionFlag(nestId, storage, documentId) {
|
|
110
111
|
try {
|
|
111
112
|
const remaining = await listSuggestions(storage, documentId);
|
|
112
|
-
await
|
|
113
|
+
await syncSuggestionRows(nestId, documentId, remaining);
|
|
113
114
|
} catch (err) {
|
|
114
115
|
console.error("[external-edit] flag refresh failed", nestId, documentId, err);
|
|
115
116
|
}
|
|
@@ -170,33 +171,27 @@ async function getPendingChange(nestId, documentId) {
|
|
|
170
171
|
return null;
|
|
171
172
|
}
|
|
172
173
|
async function listNestExternalEdits(nestId) {
|
|
174
|
+
const rows = await listSuggestionRows(nestId);
|
|
175
|
+
if (rows.length === 0) return [];
|
|
173
176
|
const { storage } = await engineCache.get(nestId);
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
const kept = await Promise.all(
|
|
178
|
+
rows.map(
|
|
179
|
+
async (r) => await bodyMatchesLatestVersion(storage, r.node_id, r.proposed_hash) ? null : r
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
return kept.flatMap(
|
|
183
|
+
(r) => r ? [{
|
|
184
|
+
suggestion_id: r.suggestion_id,
|
|
185
|
+
nest_id: nestId,
|
|
186
|
+
document_id: r.node_id,
|
|
187
|
+
source: r.source,
|
|
188
|
+
detected_at: r.detected_at,
|
|
189
|
+
actor: r.actor,
|
|
190
|
+
target_hash: r.target_hash,
|
|
191
|
+
proposed_hash: r.proposed_hash,
|
|
192
|
+
note: r.note ?? void 0
|
|
193
|
+
}] : []
|
|
187
194
|
);
|
|
188
|
-
const entries = lists.flat().map((meta) => ({
|
|
189
|
-
suggestion_id: meta.suggestion_id,
|
|
190
|
-
nest_id: nestId,
|
|
191
|
-
document_id: meta.document_id,
|
|
192
|
-
source: meta.source,
|
|
193
|
-
detected_at: meta.detected_at,
|
|
194
|
-
actor: meta.actor,
|
|
195
|
-
target_hash: meta.target_hash,
|
|
196
|
-
proposed_hash: meta.proposed_hash,
|
|
197
|
-
note: meta.note
|
|
198
|
-
}));
|
|
199
|
-
return entries.sort((a, b) => b.detected_at.localeCompare(a.detected_at));
|
|
200
195
|
}
|
|
201
196
|
async function getExternalEditDetail(nestId, documentId, suggestionId) {
|
|
202
197
|
const { storage } = await engineCache.get(nestId);
|
|
@@ -259,7 +254,7 @@ async function listExternalEditVerdicts(nestId, documentId) {
|
|
|
259
254
|
async function mirrorVersion(input) {
|
|
260
255
|
const { storage } = await engineCache.get(input.nestId);
|
|
261
256
|
const node = await storage.readDocument(input.documentId);
|
|
262
|
-
const { upsertVersion, setApprovedVersion } = await import("./version-service-
|
|
257
|
+
const { upsertVersion, setApprovedVersion } = await import("./version-service-KIOQME6U.js");
|
|
263
258
|
await upsertVersion({
|
|
264
259
|
nestId: input.nestId,
|
|
265
260
|
nodeId: input.documentId,
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
buildTitleMap,
|
|
3
3
|
insertOrReplace,
|
|
4
4
|
nowExpr
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KIAAEHWL.js";
|
|
6
6
|
import {
|
|
7
7
|
getDb
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LA3VTQ22.js";
|
|
9
9
|
|
|
10
10
|
// src/governance/version-service.ts
|
|
11
11
|
import { createHash } from "crypto";
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
engineApi,
|
|
3
3
|
engineCache,
|
|
4
4
|
opContext
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KIAAEHWL.js";
|
|
6
6
|
import "./chunk-YVMSM7LS.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-LA3VTQ22.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-
|
|
14
|
+
} from "./chunk-XUIWAWDO.js";
|
|
15
|
+
import "./chunk-KIAAEHWL.js";
|
|
16
16
|
import "./chunk-YVMSM7LS.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-LA3VTQ22.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-
|
|
9
|
+
} from "./chunk-I3CSD6CK.js";
|
|
10
10
|
import "./chunk-YVMSM7LS.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-LA3VTQ22.js";
|
|
12
12
|
import "./chunk-YB3LKF7U.js";
|
|
13
13
|
export {
|
|
14
14
|
createGrant,
|