@promptowl/contextnest-community 1.21.0 → 1.22.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 +2 -0
- package/dist/{adapter.postgres-YOODX2BI.js → adapter.postgres-6VZCMPQL.js} +11 -1
- package/dist/{chunk-IGPJ74O4.js → chunk-6WESEA75.js} +2 -2
- package/dist/{chunk-LPPKPEYI.js → chunk-J2OQ3MEB.js} +77 -3
- package/dist/{chunk-XWGEXQU3.js → chunk-MY4JIWQD.js} +1 -1
- package/dist/{chunk-4KJGQHWB.js → chunk-OQXZ43HG.js} +18 -8
- package/dist/{chunk-N2DTJFCD.js → chunk-PX3P4FTU.js} +2 -2
- package/dist/{chunk-6JGQX4GA.js → chunk-W5ILNGPD.js} +12 -8
- package/dist/{chunk-6PJQSKKV.js → chunk-ZAV4QUZA.js} +4 -4
- package/dist/{client-BD6PAUCZ.js → client-CSCHXUNX.js} +1 -1
- package/dist/{engine-IKZQ46P7.js → engine-XKRP7GOQ.js} +2 -2
- package/dist/{external-edit-service-LTON57HO.js → external-edit-service-TXW7SXSF.js} +3 -3
- package/dist/{grants-service-LSNZN6NG.js → grants-service-UZ2MGDO5.js} +2 -2
- package/dist/index.js +32 -15
- package/dist/{migrations.postgres-JA2YGLS3.js → migrations.postgres-KKICIH2C.js} +17 -2
- package/dist/{review-service-6JK64IRT.js → review-service-XFWTENTN.js} +6 -6
- package/dist/{stewardship-service-2YKGBP63.js → stewardship-service-5HBQGDMK.js} +3 -3
- package/dist/{version-service-PJV74PVQ.js → version-service-TKNYJMCM.js} +3 -3
- package/dist/web3/assets/{index-deyhV93k.js → index-C2hW_9j9.js} +294 -294
- package/dist/web3/assets/module-uADzTImp.js +3 -0
- package/dist/web3/index.html +1 -1
- package/package.json +2 -1
package/CONFIGURATION.md
CHANGED
|
@@ -76,6 +76,8 @@ The server prints a loud warning at startup when `AUTH_MODE=open` is active.
|
|
|
76
76
|
| `ENV_FILE_PATH` | `$DATA_ROOT/.env` | Path to an optional `.env` file the server reads at boot (in addition to `$cwd/.env`). **No longer used for persistence** — the License Setup Page and Settings page now write to the database, not this file (see [Runtime settings persistence](#runtime-settings-persistence)). Kept for operators who bootstrap config from a mounted `.env`. |
|
|
77
77
|
| `TELEMETRY_ENABLED` | `"true"` (set to `"false"` to disable) | Batched, anonymized usage events sent to PromptOwl. Off disables the loop entirely. |
|
|
78
78
|
| `TELEMETRY_INTERVAL_MS` | `3600000` (1 hour) | How often buffered telemetry is flushed to PromptOwl. |
|
|
79
|
+
| `POSTHOG_KEY` | `""` | PostHog project API key for product analytics in the UI. Empty = analytics off (the default — a self-hosted install brings its own project). Served to the browser via `/health`, but only while `TELEMETRY_ENABLED` is on, so that switch turns off everything this server sends outward. Also editable from Settings → Advanced. |
|
|
80
|
+
| `POSTHOG_HOST` | `https://us.i.posthog.com` | PostHog ingestion host. Set it to your own region or self-hosted PostHog. Also editable from Settings → Advanced. |
|
|
79
81
|
| `TRACE_RETENTION_DAYS` | `14` | Activity-trace retention window in days (the `api_events` rows behind `GET /admin/trace` and `GET /nests/:id/trace`). Rows older than this are pruned opportunistically (every ~500 inserts). `0` = keep forever (pruning is skipped entirely). Capped at `3650`; invalid/negative values fall back to `14`. Also editable from Settings → Advanced. |
|
|
80
82
|
| `CORS_ORIGINS` | `*` in open mode; `http://localhost:5173,http://localhost:3838` in key mode | Comma-separated allowlist. Set to `*` to allow any origin (**only** safe in open mode — in key mode with Bearer tokens this enables CSRF). |
|
|
81
83
|
| `FRAME_ANCESTORS` | `'self'` | Which origins may embed this server in an iframe, sent as CSP `frame-ancestors`. The default lets nothing but this origin frame the UI, which blocks clickjacking. Deployments that are meant to be embedded list the embedding origin — e.g. the PromptOwl Data Room iframes ContextNest, so that install sets `FRAME_ANCESTORS="https://app.promptowl.ai"`. Comma-separated; `'self'` is always included; `*` allows any site and disables the protection. Note the embedding page must be **same-site** (a sibling subdomain) for the session cookie to survive inside the frame — a genuinely cross-domain embed will render the login page no matter what this is set to. |
|
|
@@ -5,7 +5,17 @@ function translatePlaceholders(sql) {
|
|
|
5
5
|
let n = 0;
|
|
6
6
|
for (let i = 0; i < sql.length; i++) {
|
|
7
7
|
const ch = sql[i];
|
|
8
|
-
if (ch === "
|
|
8
|
+
if (!inStr && ch === "-" && sql[i + 1] === "-") {
|
|
9
|
+
const end = sql.indexOf("\n", i);
|
|
10
|
+
const stop = end === -1 ? sql.length : end;
|
|
11
|
+
out += sql.slice(i, stop);
|
|
12
|
+
i = stop - 1;
|
|
13
|
+
} else if (!inStr && ch === "/" && sql[i + 1] === "*") {
|
|
14
|
+
const end = sql.indexOf("*/", i + 2);
|
|
15
|
+
const stop = end === -1 ? sql.length : end + 2;
|
|
16
|
+
out += sql.slice(i, stop);
|
|
17
|
+
i = stop - 1;
|
|
18
|
+
} else if (ch === "'") {
|
|
9
19
|
inStr = !inStr;
|
|
10
20
|
out += ch;
|
|
11
21
|
} else if (ch === "?" && !inStr) {
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
buildTitleMap,
|
|
3
3
|
insertOrReplace,
|
|
4
4
|
nowExpr
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-W5ILNGPD.js";
|
|
6
6
|
import {
|
|
7
7
|
getDb
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
9
9
|
|
|
10
10
|
// src/governance/version-service.ts
|
|
11
11
|
import { createHash } from "crypto";
|
|
@@ -351,6 +351,19 @@ var config = {
|
|
|
351
351
|
get TELEMETRY_INTERVAL_MS() {
|
|
352
352
|
return parseInt(process.env.TELEMETRY_INTERVAL_MS || "3600000", 10);
|
|
353
353
|
},
|
|
354
|
+
/**
|
|
355
|
+
* PostHog product analytics. Empty key = off, which is the default: a
|
|
356
|
+
* self-hosted operator brings their own project. The key is a public client
|
|
357
|
+
* key by design (the browser sends it), so /health may hand it out — but
|
|
358
|
+
* only while TELEMETRY_ENABLED is on, so one switch stops all outbound
|
|
359
|
+
* usage data. Editable at runtime from the admin Settings page.
|
|
360
|
+
*/
|
|
361
|
+
get POSTHOG_KEY() {
|
|
362
|
+
return (process.env.POSTHOG_KEY || "").trim();
|
|
363
|
+
},
|
|
364
|
+
get POSTHOG_HOST() {
|
|
365
|
+
return (process.env.POSTHOG_HOST || "").trim() || "https://us.i.posthog.com";
|
|
366
|
+
},
|
|
354
367
|
/**
|
|
355
368
|
* Activity-trace retention window in days (the api_events table behind
|
|
356
369
|
* GET /admin/trace and GET /nests/:id/trace). 0 = keep forever (pruning is
|
|
@@ -626,7 +639,7 @@ function runMigrations(db) {
|
|
|
626
639
|
slug TEXT NOT NULL,
|
|
627
640
|
description TEXT,
|
|
628
641
|
visibility TEXT NOT NULL DEFAULT 'private'
|
|
629
|
-
CHECK(visibility IN ('private', 'public')),
|
|
642
|
+
CHECK(visibility IN ('private', 'org', 'public')),
|
|
630
643
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
631
644
|
UNIQUE(user_id, slug)
|
|
632
645
|
);
|
|
@@ -1702,6 +1715,67 @@ function runMigrations(db) {
|
|
|
1702
1715
|
})();
|
|
1703
1716
|
recordMigration("039_nest_pins");
|
|
1704
1717
|
}
|
|
1718
|
+
if (!hasMigration("041_nest_visibility_org")) {
|
|
1719
|
+
widenNestVisibilityWithGuard(db);
|
|
1720
|
+
recordMigration("041_nest_visibility_org");
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
function fkViolationCounts(db) {
|
|
1724
|
+
const rows = db.pragma("foreign_key_check");
|
|
1725
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1726
|
+
for (const r of rows) {
|
|
1727
|
+
const key = `${r.table}|${r.parent}|${r.fkid}`;
|
|
1728
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
1729
|
+
}
|
|
1730
|
+
return counts;
|
|
1731
|
+
}
|
|
1732
|
+
function widenNestVisibilityWithGuard(db) {
|
|
1733
|
+
const before = fkViolationCounts(db);
|
|
1734
|
+
const fkWasOn = db.pragma("foreign_keys", { simple: true }) === 1;
|
|
1735
|
+
if (fkWasOn) db.pragma("foreign_keys = OFF");
|
|
1736
|
+
try {
|
|
1737
|
+
db.transaction(() => {
|
|
1738
|
+
widenNestVisibilityCheck(db);
|
|
1739
|
+
})();
|
|
1740
|
+
const after = fkViolationCounts(db);
|
|
1741
|
+
const introduced = [...after].filter(
|
|
1742
|
+
([key, n]) => n > (before.get(key) ?? 0)
|
|
1743
|
+
);
|
|
1744
|
+
if (introduced.length > 0) {
|
|
1745
|
+
throw new Error(
|
|
1746
|
+
`041_nest_visibility_org introduced foreign-key violations: ${introduced.map(([key]) => key).join(", ")}`
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
} finally {
|
|
1750
|
+
if (fkWasOn) db.pragma("foreign_keys = ON");
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
function widenNestVisibilityCheck(db) {
|
|
1754
|
+
const row = db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'nests'").get();
|
|
1755
|
+
const ddl = row?.sql;
|
|
1756
|
+
if (!ddl) return;
|
|
1757
|
+
const checkRe = /CHECK\s*\(\s*visibility\s+IN\s*\([^)]*\)\s*\)/i;
|
|
1758
|
+
const current = ddl.match(checkRe);
|
|
1759
|
+
if (!current) return;
|
|
1760
|
+
if (/'org'/i.test(current[0])) return;
|
|
1761
|
+
const newDdl = ddl.replace(
|
|
1762
|
+
/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`\[]?nests["'`\]]?/i,
|
|
1763
|
+
"CREATE TABLE nests_new"
|
|
1764
|
+
).replace(checkRe, "CHECK(visibility IN ('private', 'org', 'public'))");
|
|
1765
|
+
if (!newDdl.startsWith("CREATE TABLE nests_new")) {
|
|
1766
|
+
throw new Error("041: could not rewrite the nests CREATE TABLE statement");
|
|
1767
|
+
}
|
|
1768
|
+
const cols = db.prepare("PRAGMA table_info(nests)").all().map((c) => `"${c.name}"`).join(", ");
|
|
1769
|
+
const indexes = db.prepare(
|
|
1770
|
+
"SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = 'nests' AND sql IS NOT NULL"
|
|
1771
|
+
).all().map((r) => `${r.sql};`);
|
|
1772
|
+
db.exec(`
|
|
1773
|
+
${newDdl};
|
|
1774
|
+
INSERT INTO nests_new (${cols}) SELECT ${cols} FROM nests;
|
|
1775
|
+
DROP TABLE nests;
|
|
1776
|
+
ALTER TABLE nests_new RENAME TO nests;
|
|
1777
|
+
${indexes.join("\n ")}
|
|
1778
|
+
`);
|
|
1705
1779
|
}
|
|
1706
1780
|
function mergeCaseCollidingUsers(db) {
|
|
1707
1781
|
const groups = db.prepare(
|
|
@@ -1918,8 +1992,8 @@ async function initDb() {
|
|
|
1918
1992
|
if (adapter) return adapter;
|
|
1919
1993
|
if (config.DB_DRIVER === "postgres") {
|
|
1920
1994
|
const { Pool } = await import("pg");
|
|
1921
|
-
const { PostgresAdapter } = await import("./adapter.postgres-
|
|
1922
|
-
const { runPostgresMigrations } = await import("./migrations.postgres-
|
|
1995
|
+
const { PostgresAdapter } = await import("./adapter.postgres-6VZCMPQL.js");
|
|
1996
|
+
const { runPostgresMigrations } = await import("./migrations.postgres-KKICIH2C.js");
|
|
1923
1997
|
const pool = new Pool(buildPgConfig());
|
|
1924
1998
|
adapter = new PostgresAdapter(pool);
|
|
1925
1999
|
await runPostgresMigrations(adapter);
|
|
@@ -4,19 +4,19 @@ import {
|
|
|
4
4
|
resolveNestWideRoles,
|
|
5
5
|
resolveStewardsForNode,
|
|
6
6
|
stewardCoverageForUser
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PX3P4FTU.js";
|
|
8
8
|
import {
|
|
9
9
|
grantCoversNode,
|
|
10
10
|
listUserGrants,
|
|
11
11
|
resolveNodeGrant
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-MY4JIWQD.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-6WESEA75.js";
|
|
20
20
|
import {
|
|
21
21
|
buildDocContext,
|
|
22
22
|
buildTitleMap,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
resolveNestPermission,
|
|
37
37
|
sendEmailToRecipient,
|
|
38
38
|
titleForNode
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-W5ILNGPD.js";
|
|
40
40
|
import {
|
|
41
41
|
ConflictError,
|
|
42
42
|
NotFoundError,
|
|
@@ -45,7 +45,7 @@ import {
|
|
|
45
45
|
import {
|
|
46
46
|
config,
|
|
47
47
|
getDb
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
49
49
|
|
|
50
50
|
// src/governance/review-service.ts
|
|
51
51
|
import { v4 as uuid2 } from "uuid";
|
|
@@ -131,7 +131,17 @@ async function nodeTrail(db, nestId, ids) {
|
|
|
131
131
|
for (const r of rows) if (r.title) titles.set(r.node_id, r.title);
|
|
132
132
|
} catch {
|
|
133
133
|
}
|
|
134
|
-
|
|
134
|
+
const label = (id) => titles.get(id) ?? id;
|
|
135
|
+
const idsPerLabel = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const id of unique) {
|
|
137
|
+
const l = label(id);
|
|
138
|
+
(idsPerLabel.get(l) ?? idsPerLabel.set(l, /* @__PURE__ */ new Set()).get(l)).add(id);
|
|
139
|
+
}
|
|
140
|
+
return ids.map((id) => {
|
|
141
|
+
const l = label(id);
|
|
142
|
+
const ambiguous = (idsPerLabel.get(l)?.size ?? 0) > 1;
|
|
143
|
+
return ambiguous && l !== id ? `"${l}" (${id})` : `"${l}"`;
|
|
144
|
+
}).join(" \u2192 ");
|
|
135
145
|
}
|
|
136
146
|
async function assertFlowGraphAcyclic(nestId, extraFlowTypeId) {
|
|
137
147
|
const db = getDb();
|
|
@@ -147,7 +157,7 @@ async function assertFlowGraphAcyclic(nestId, extraFlowTypeId) {
|
|
|
147
157
|
for (const r of rows) {
|
|
148
158
|
if (r.from_node === r.to_node) {
|
|
149
159
|
throw new ConflictError(
|
|
150
|
-
`
|
|
160
|
+
`This step loops back on itself: ${await nodeTrail(db, nestId, [r.from_node])}. A flow can't return to a step it already ran.`
|
|
151
161
|
);
|
|
152
162
|
}
|
|
153
163
|
nodes.add(r.from_node);
|
|
@@ -170,7 +180,7 @@ async function assertFlowGraphAcyclic(nestId, extraFlowTypeId) {
|
|
|
170
180
|
}
|
|
171
181
|
if (removed < nodes.size) {
|
|
172
182
|
throw new ConflictError(
|
|
173
|
-
"
|
|
183
|
+
"Making this connection type a flow would loop these steps back on each other. A flow can't return to a step it already ran."
|
|
174
184
|
);
|
|
175
185
|
}
|
|
176
186
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
resolveNestPermission,
|
|
12
12
|
resolveTeamRolesForUser,
|
|
13
13
|
sendEmailToRecipient
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-W5ILNGPD.js";
|
|
15
15
|
import {
|
|
16
16
|
ConflictError,
|
|
17
17
|
ValidationError
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
config,
|
|
21
21
|
getDb,
|
|
22
22
|
isEmailish
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
24
24
|
|
|
25
25
|
// src/governance/stewardship-service.ts
|
|
26
26
|
import { v4 as uuid } from "uuid";
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
config,
|
|
9
9
|
getDb,
|
|
10
10
|
isEmailish
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
12
12
|
import {
|
|
13
13
|
ANON_USER_ID
|
|
14
14
|
} from "./chunk-YB3LKF7U.js";
|
|
@@ -1879,8 +1879,9 @@ async function resolveNestAccessRaw(nestId, userId) {
|
|
|
1879
1879
|
return { ...base, permission: best, isPublicReader: false };
|
|
1880
1880
|
}
|
|
1881
1881
|
if (base.isSteward) best = higher(best, "read");
|
|
1882
|
-
|
|
1883
|
-
|
|
1882
|
+
const visibilityRead = row.visibility === "public" || row.visibility === "org" && userId !== ANON_USER_ID;
|
|
1883
|
+
if (visibilityRead) best = higher(best, "read");
|
|
1884
|
+
const isPublicReader2 = visibilityRead && !row.collab_permission && !base.isSteward;
|
|
1884
1885
|
return { ...base, permission: best, isPublicReader: isPublicReader2 };
|
|
1885
1886
|
}
|
|
1886
1887
|
async function resolveNestPermission(nestId, userId) {
|
|
@@ -2309,11 +2310,13 @@ async function listSharedNests(userId) {
|
|
|
2309
2310
|
async function listVisibleNests(userId, opts = {}) {
|
|
2310
2311
|
const db = getDb();
|
|
2311
2312
|
const includeAnon = config.AUTH_MODE === "open" || await isServerAdminUserId(userId);
|
|
2313
|
+
const visibleByVisibility = userId === ANON_USER_ID ? ["public"] : ["public", "org"];
|
|
2312
2314
|
const params = [
|
|
2313
2315
|
userId,
|
|
2314
2316
|
userId,
|
|
2315
2317
|
...opts.readable ? [userId] : [],
|
|
2316
2318
|
userId,
|
|
2319
|
+
...opts.readable ? visibleByVisibility : [],
|
|
2317
2320
|
...includeAnon ? [ANON_USER_ID] : []
|
|
2318
2321
|
];
|
|
2319
2322
|
const rows = await db.all(
|
|
@@ -2329,7 +2332,7 @@ async function listVisibleNests(userId, opts = {}) {
|
|
|
2329
2332
|
AND (n.user_id = ?
|
|
2330
2333
|
OR nc.user_id IS NOT NULL
|
|
2331
2334
|
OR s.id IS NOT NULL
|
|
2332
|
-
${opts.readable ?
|
|
2335
|
+
${opts.readable ? `OR g.id IS NOT NULL OR n.visibility IN (${visibleByVisibility.map(() => "?").join(", ")})` : ""}
|
|
2333
2336
|
${includeAnon ? "OR n.user_id = ?" : ""})
|
|
2334
2337
|
ORDER BY n.created_at DESC`,
|
|
2335
2338
|
params
|
|
@@ -2345,9 +2348,10 @@ async function listVisibleNests(userId, opts = {}) {
|
|
|
2345
2348
|
}
|
|
2346
2349
|
async function listPublicNests(userId) {
|
|
2347
2350
|
const db = getDb();
|
|
2351
|
+
const visibilities = userId === ANON_USER_ID ? ["public"] : ["public", "org"];
|
|
2348
2352
|
return await db.all(
|
|
2349
2353
|
`SELECT n.* FROM nests n
|
|
2350
|
-
WHERE n.visibility
|
|
2354
|
+
WHERE n.visibility IN (${visibilities.map(() => "?").join(", ")})
|
|
2351
2355
|
AND ${LIVE_ONLY_N}
|
|
2352
2356
|
AND n.user_id != ?
|
|
2353
2357
|
AND NOT EXISTS (
|
|
@@ -2355,7 +2359,7 @@ async function listPublicNests(userId) {
|
|
|
2355
2359
|
WHERE nc.nest_id = n.id AND nc.user_id = ?
|
|
2356
2360
|
)
|
|
2357
2361
|
ORDER BY n.created_at DESC`,
|
|
2358
|
-
[userId, userId]
|
|
2362
|
+
[...visibilities, userId, userId]
|
|
2359
2363
|
);
|
|
2360
2364
|
}
|
|
2361
2365
|
async function getNest(nestId) {
|
|
@@ -2587,8 +2591,8 @@ async function ensureNodeIndex(nestId) {
|
|
|
2587
2591
|
return run;
|
|
2588
2592
|
}
|
|
2589
2593
|
async function rebuildNodeIndex(nestId) {
|
|
2590
|
-
const { engineCache: engineCache2 } = await import("./engine-
|
|
2591
|
-
const { documentsWithSuggestions } = await import("./external-edit-service-
|
|
2594
|
+
const { engineCache: engineCache2 } = await import("./engine-XKRP7GOQ.js");
|
|
2595
|
+
const { documentsWithSuggestions } = await import("./external-edit-service-TXW7SXSF.js");
|
|
2592
2596
|
const { storage, dropDiscoveryCache } = await engineCache2.get(nestId);
|
|
2593
2597
|
dropDiscoveryCache();
|
|
2594
2598
|
const startedAt = writeGeneration.get(nestId) ?? 0;
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
markIndexStale,
|
|
6
6
|
resolveNestPath,
|
|
7
7
|
setSuggestionFlag
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-W5ILNGPD.js";
|
|
9
9
|
import {
|
|
10
10
|
getDb
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
12
12
|
|
|
13
13
|
// src/governance/external-edit-service.ts
|
|
14
14
|
import { readFile, readdir } from "fs/promises";
|
|
@@ -82,7 +82,7 @@ async function scanDocumentForDriftInternal(nestId, documentId, actor) {
|
|
|
82
82
|
if (await bodyMatchesLatestVersion(storage, documentId, drift.actualHash)) {
|
|
83
83
|
return null;
|
|
84
84
|
}
|
|
85
|
-
const { hasUnsealedDraft } = await import("./version-service-
|
|
85
|
+
const { hasUnsealedDraft } = await import("./version-service-TKNYJMCM.js");
|
|
86
86
|
if (await hasUnsealedDraft(nestId, documentId)) {
|
|
87
87
|
return null;
|
|
88
88
|
}
|
|
@@ -259,7 +259,7 @@ async function listExternalEditVerdicts(nestId, documentId) {
|
|
|
259
259
|
async function mirrorVersion(input) {
|
|
260
260
|
const { storage } = await engineCache.get(input.nestId);
|
|
261
261
|
const node = await storage.readDocument(input.documentId);
|
|
262
|
-
const { upsertVersion, setApprovedVersion } = await import("./version-service-
|
|
262
|
+
const { upsertVersion, setApprovedVersion } = await import("./version-service-TKNYJMCM.js");
|
|
263
263
|
await upsertVersion({
|
|
264
264
|
nestId: input.nestId,
|
|
265
265
|
nodeId: input.documentId,
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
engineApi,
|
|
3
3
|
engineCache,
|
|
4
4
|
opContext
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-W5ILNGPD.js";
|
|
6
6
|
import "./chunk-YVMSM7LS.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-J2OQ3MEB.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-ZAV4QUZA.js";
|
|
15
|
+
import "./chunk-W5ILNGPD.js";
|
|
16
16
|
import "./chunk-YVMSM7LS.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-J2OQ3MEB.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-MY4JIWQD.js";
|
|
10
10
|
import "./chunk-YVMSM7LS.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-J2OQ3MEB.js";
|
|
12
12
|
import "./chunk-YB3LKF7U.js";
|
|
13
13
|
export {
|
|
14
14
|
createGrant,
|
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
submitForReview,
|
|
38
38
|
withNodeWriteLock,
|
|
39
39
|
withdrawDeletionRequest
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-OQXZ43HG.js";
|
|
41
41
|
import {
|
|
42
42
|
canCreateInNest,
|
|
43
43
|
canManageStewards,
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
resolveUserRoles,
|
|
58
58
|
syncFromConfig,
|
|
59
59
|
updateSteward
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-PX3P4FTU.js";
|
|
61
61
|
import {
|
|
62
62
|
createGrant,
|
|
63
63
|
deleteGrant,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
listGrants,
|
|
67
67
|
listUserGrants,
|
|
68
68
|
resolveNodeGrant
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-MY4JIWQD.js";
|
|
70
70
|
import {
|
|
71
71
|
generateApiKey,
|
|
72
72
|
getKeyPrefix,
|
|
@@ -92,7 +92,7 @@ import {
|
|
|
92
92
|
listMyDrafts,
|
|
93
93
|
setApprovedVersion,
|
|
94
94
|
upsertVersion
|
|
95
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-6WESEA75.js";
|
|
96
96
|
import {
|
|
97
97
|
approveExternalEdit,
|
|
98
98
|
getExternalEditDetail,
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
scanDocumentForDrift,
|
|
105
105
|
scanNestForDrift,
|
|
106
106
|
startDriftScanner
|
|
107
|
-
} from "./chunk-
|
|
107
|
+
} from "./chunk-ZAV4QUZA.js";
|
|
108
108
|
import {
|
|
109
109
|
addMember,
|
|
110
110
|
addWatcher,
|
|
@@ -208,7 +208,7 @@ import {
|
|
|
208
208
|
updateMemberRole,
|
|
209
209
|
validateLicense,
|
|
210
210
|
verifySmtp
|
|
211
|
-
} from "./chunk-
|
|
211
|
+
} from "./chunk-W5ILNGPD.js";
|
|
212
212
|
import {
|
|
213
213
|
AppError,
|
|
214
214
|
ConflictError,
|
|
@@ -226,7 +226,7 @@ import {
|
|
|
226
226
|
initDb,
|
|
227
227
|
isEmailListish,
|
|
228
228
|
isEmailish
|
|
229
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-J2OQ3MEB.js";
|
|
230
230
|
import {
|
|
231
231
|
ANON_EMAIL,
|
|
232
232
|
ANON_USER_ID,
|
|
@@ -4147,8 +4147,8 @@ sharingRoutes.delete("/collaborators/:collabId", async (c) => {
|
|
|
4147
4147
|
});
|
|
4148
4148
|
sharingRoutes.patch("/visibility", async (c) => {
|
|
4149
4149
|
const body = await c.req.json();
|
|
4150
|
-
if (!body.visibility || !["private", "public"].includes(body.visibility)) {
|
|
4151
|
-
throw new ValidationError("visibility must be private or public");
|
|
4150
|
+
if (!body.visibility || !["private", "org", "public"].includes(body.visibility)) {
|
|
4151
|
+
throw new ValidationError("visibility must be private, org or public");
|
|
4152
4152
|
}
|
|
4153
4153
|
if (body.visibility === "public" && body.acknowledge_public !== true) {
|
|
4154
4154
|
return c.json(
|
|
@@ -5817,7 +5817,7 @@ nodeRoutes.post("/", async (c) => {
|
|
|
5817
5817
|
nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
|
|
5818
5818
|
const nestId = c.req.param("nestId");
|
|
5819
5819
|
const nodeId = c.req.param("nodeId");
|
|
5820
|
-
const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-
|
|
5820
|
+
const { resolveStewardsWithFallback: resolveStewardsWithFallback2 } = await import("./stewardship-service-5HBQGDMK.js");
|
|
5821
5821
|
const { stewards, fallbackToOwner, ownerEmail } = await resolveStewardsWithFallback2(
|
|
5822
5822
|
nestId,
|
|
5823
5823
|
nodeId
|
|
@@ -5838,7 +5838,7 @@ nodeRoutes.get("/:nodeId{.+}/stewards", async (c) => {
|
|
|
5838
5838
|
nodeRoutes.get("/:nodeId{.+}/reviews", async (c) => {
|
|
5839
5839
|
const nestId = c.req.param("nestId");
|
|
5840
5840
|
const nodeId = c.req.param("nodeId");
|
|
5841
|
-
const { getReviewHistory: getReviewHistory2 } = await import("./review-service-
|
|
5841
|
+
const { getReviewHistory: getReviewHistory2 } = await import("./review-service-XFWTENTN.js");
|
|
5842
5842
|
const history = await getReviewHistory2(nestId, nodeId);
|
|
5843
5843
|
return c.json({ reviews: history });
|
|
5844
5844
|
});
|
|
@@ -7859,7 +7859,7 @@ async function validateCondition(nestId, mode, condition) {
|
|
|
7859
7859
|
async function assertNoFlowCycle(db, nestId, fromNode, toNode) {
|
|
7860
7860
|
if (fromNode === toNode) {
|
|
7861
7861
|
throw new ConflictError(
|
|
7862
|
-
`
|
|
7862
|
+
`This step loops back on itself: ${await nodeTrail(db, nestId, [fromNode])}. A flow can't return to a step it already ran.`
|
|
7863
7863
|
);
|
|
7864
7864
|
}
|
|
7865
7865
|
const rows = await db.all(
|
|
@@ -7888,7 +7888,7 @@ async function assertNoFlowCycle(db, nestId, fromNode, toNode) {
|
|
|
7888
7888
|
}
|
|
7889
7889
|
path.reverse().push(toNode);
|
|
7890
7890
|
throw new ConflictError(
|
|
7891
|
-
`
|
|
7891
|
+
`These steps loop back on each other: ${await nodeTrail(db, nestId, path)}. A flow can't return to a step it already ran.`
|
|
7892
7892
|
);
|
|
7893
7893
|
}
|
|
7894
7894
|
for (const nb of adj.get(cur) ?? []) {
|
|
@@ -9927,8 +9927,8 @@ ${list}`,
|
|
|
9927
9927
|
if (!target) return "target is required (a node id or folder prefix).";
|
|
9928
9928
|
if (!["read", "write"].includes(role)) return "role must be read or write.";
|
|
9929
9929
|
try {
|
|
9930
|
-
const { createGrant: createGrant2 } = await import("./grants-service-
|
|
9931
|
-
const db = (await import("./client-
|
|
9930
|
+
const { createGrant: createGrant2 } = await import("./grants-service-UZ2MGDO5.js");
|
|
9931
|
+
const db = (await import("./client-CSCHXUNX.js")).getDb();
|
|
9932
9932
|
const { normalizeEmail: normalizeEmail2 } = await import("./email-R7DFS6E5.js");
|
|
9933
9933
|
const e = normalizeEmail2(String(args.email || ""));
|
|
9934
9934
|
if (!e) return "email is required.";
|
|
@@ -13013,6 +13013,11 @@ function createApp() {
|
|
|
13013
13013
|
// Presence only: the publish dialog hides the email/invite gates when
|
|
13014
13014
|
// this server cannot send mail, because their only way in is a mailed link.
|
|
13015
13015
|
email_configured: isEmailConfigured(),
|
|
13016
|
+
// Product analytics for the SPA. The PostHog project key is a public
|
|
13017
|
+
// client key, but it only ships while usage telemetry is on — one switch
|
|
13018
|
+
// covers everything this server sends outward.
|
|
13019
|
+
posthog_key: config.TELEMETRY_ENABLED ? config.POSTHOG_KEY || null : null,
|
|
13020
|
+
posthog_host: config.POSTHOG_HOST,
|
|
13016
13021
|
promptowl_sign_in_gate: config.PROMPTOWL_SIGN_IN_GATE,
|
|
13017
13022
|
// Manual (email/password) sign-in mode (open | invite-only | disabled).
|
|
13018
13023
|
// The login page reads this to show/hide the credential form, and to
|
|
@@ -13137,6 +13142,8 @@ function createApp() {
|
|
|
13137
13142
|
manual_sign_in: config.MANUAL_SIGN_IN,
|
|
13138
13143
|
logo_url: config.LOGO_URL,
|
|
13139
13144
|
telemetry_enabled: config.TELEMETRY_ENABLED,
|
|
13145
|
+
posthog_key: config.POSTHOG_KEY,
|
|
13146
|
+
posthog_host: config.POSTHOG_HOST,
|
|
13140
13147
|
public_base_url: config.PUBLIC_BASE_URL,
|
|
13141
13148
|
max_body_bytes: config.MAX_BODY_BYTES,
|
|
13142
13149
|
workflow_plane_enabled: config.FEATURE_WORKFLOW_PLANE,
|
|
@@ -13235,6 +13242,16 @@ function createApp() {
|
|
|
13235
13242
|
if ("telemetry_enabled" in body) {
|
|
13236
13243
|
pending.push({ name: "TELEMETRY_ENABLED", value: body.telemetry_enabled ? "true" : "false" });
|
|
13237
13244
|
}
|
|
13245
|
+
if ("posthog_key" in body) {
|
|
13246
|
+
const v = String(body.posthog_key ?? "").trim();
|
|
13247
|
+
pending.push({ name: "POSTHOG_KEY", value: v || null });
|
|
13248
|
+
}
|
|
13249
|
+
if ("posthog_host" in body) {
|
|
13250
|
+
const v = String(body.posthog_host ?? "").trim().replace(/\/$/, "");
|
|
13251
|
+
if (v && !/^https?:\/\/[^\s]+$/i.test(v))
|
|
13252
|
+
addError("posthog_host", "PostHog host must be a http(s) URL, e.g. https://us.i.posthog.com");
|
|
13253
|
+
else pending.push({ name: "POSTHOG_HOST", value: v || null });
|
|
13254
|
+
}
|
|
13238
13255
|
if ("workflow_plane_enabled" in body) {
|
|
13239
13256
|
pending.push({
|
|
13240
13257
|
name: "FEATURE_WORKFLOW_PLANE",
|
|
@@ -47,7 +47,8 @@ var HISTORICAL_MIGRATIONS = [
|
|
|
47
47
|
"036_node_index",
|
|
48
48
|
"037_publish",
|
|
49
49
|
"038_nest_archive",
|
|
50
|
-
"039_nest_pins"
|
|
50
|
+
"039_nest_pins",
|
|
51
|
+
"041_nest_visibility_org"
|
|
51
52
|
];
|
|
52
53
|
async function runPostgresMigrations(db) {
|
|
53
54
|
await db.exec(`
|
|
@@ -74,7 +75,7 @@ async function runPostgresMigrations(db) {
|
|
|
74
75
|
slug TEXT NOT NULL,
|
|
75
76
|
description TEXT,
|
|
76
77
|
visibility TEXT NOT NULL DEFAULT 'private'
|
|
77
|
-
CHECK(visibility IN ('private', 'public')),
|
|
78
|
+
CHECK(visibility IN ('private', 'org', 'public')),
|
|
78
79
|
created_at TEXT NOT NULL DEFAULT ${NOW},
|
|
79
80
|
stewardship_enabled INTEGER NOT NULL DEFAULT 0,
|
|
80
81
|
is_imported INTEGER NOT NULL DEFAULT 0,
|
|
@@ -695,6 +696,20 @@ async function runPostgresMigrations(db) {
|
|
|
695
696
|
[id]
|
|
696
697
|
);
|
|
697
698
|
}
|
|
699
|
+
const visibilityCheck = await db.get(
|
|
700
|
+
`SELECT pg_get_constraintdef(oid) AS def
|
|
701
|
+
FROM pg_constraint
|
|
702
|
+
WHERE conrelid = 'nests'::regclass
|
|
703
|
+
AND conname = 'nests_visibility_check'`
|
|
704
|
+
);
|
|
705
|
+
if (!visibilityCheck?.def?.includes("'org'")) {
|
|
706
|
+
await db.run(
|
|
707
|
+
"ALTER TABLE nests DROP CONSTRAINT IF EXISTS nests_visibility_check"
|
|
708
|
+
);
|
|
709
|
+
await db.run(
|
|
710
|
+
"ALTER TABLE nests ADD CONSTRAINT nests_visibility_check CHECK (visibility IN ('private', 'org', 'public'))"
|
|
711
|
+
);
|
|
712
|
+
}
|
|
698
713
|
await db.run(
|
|
699
714
|
"UPDATE nests SET visibility = 'public' WHERE user_id = ? AND visibility = 'private'",
|
|
700
715
|
[ANON_USER_ID]
|
|
@@ -17,15 +17,15 @@ import {
|
|
|
17
17
|
requestDeletion,
|
|
18
18
|
submitForReview,
|
|
19
19
|
withdrawDeletionRequest
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-OQXZ43HG.js";
|
|
21
21
|
import {
|
|
22
22
|
canUserApprove
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-PX3P4FTU.js";
|
|
24
|
+
import "./chunk-MY4JIWQD.js";
|
|
25
|
+
import "./chunk-6WESEA75.js";
|
|
26
|
+
import "./chunk-W5ILNGPD.js";
|
|
27
27
|
import "./chunk-YVMSM7LS.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-J2OQ3MEB.js";
|
|
29
29
|
import "./chunk-YB3LKF7U.js";
|
|
30
30
|
import "./chunk-FRQJWGN3.js";
|
|
31
31
|
export {
|
|
@@ -23,10 +23,10 @@ import {
|
|
|
23
23
|
syncFromConfig,
|
|
24
24
|
updateSteward,
|
|
25
25
|
updateStewardRole
|
|
26
|
-
} from "./chunk-
|
|
27
|
-
import "./chunk-
|
|
26
|
+
} from "./chunk-PX3P4FTU.js";
|
|
27
|
+
import "./chunk-W5ILNGPD.js";
|
|
28
28
|
import "./chunk-YVMSM7LS.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-J2OQ3MEB.js";
|
|
30
30
|
import "./chunk-YB3LKF7U.js";
|
|
31
31
|
import "./chunk-FRQJWGN3.js";
|
|
32
32
|
export {
|
|
@@ -20,10 +20,10 @@ import {
|
|
|
20
20
|
setApprovedVersion,
|
|
21
21
|
systemAuthor,
|
|
22
22
|
upsertVersion
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-6WESEA75.js";
|
|
24
|
+
import "./chunk-W5ILNGPD.js";
|
|
25
25
|
import "./chunk-YVMSM7LS.js";
|
|
26
|
-
import "./chunk-
|
|
26
|
+
import "./chunk-J2OQ3MEB.js";
|
|
27
27
|
import "./chunk-YB3LKF7U.js";
|
|
28
28
|
import "./chunk-FRQJWGN3.js";
|
|
29
29
|
export {
|