@promptowl/contextnest-community 1.7.0 → 1.9.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 +191 -181
- package/LICENSE.md +142 -142
- package/README.md +224 -224
- package/dist/chunk-3JTODC3Y.js +48 -0
- package/dist/{chunk-43DOX4LH.js → chunk-BYS4HDME.js} +45 -49
- package/dist/{chunk-QMLAXQES.js → chunk-DPHV6Q26.js} +311 -19
- package/dist/chunk-FRQJWGN3.js +8 -0
- package/dist/{chunk-VD5QX2ZQ.js → chunk-HRQWNRVI.js} +4 -2
- package/dist/chunk-TIO5XOFD.js +103 -0
- package/dist/chunk-VC3QNHHB.js +664 -0
- package/dist/chunk-XQ46F76G.js +16 -0
- package/dist/client-GI74NPIW.js +11 -0
- package/dist/email-R7DFS6E5.js +6 -0
- package/dist/grants-service-MDLRPMZR.js +21 -0
- package/dist/index.js +2579 -230
- package/dist/{migrations.postgres-3HMGLKOV.js → migrations.postgres-ORSV7UFJ.js} +139 -1
- package/dist/{review-service-2FSKW425.js → review-service-A3XFQTJH.js} +6 -4
- package/dist/{stewardship-service-I2JAFYJU.js → stewardship-service-3KCVVXCW.js} +4 -2
- package/dist/{version-service-FJWVTZKR.js → version-service-6J4OWQL6.js} +3 -2
- package/dist/web3/assets/{hootie-_U3ECslt.svg → hootie-C2ocYkn4.svg} +8 -8
- package/dist/web3/assets/index-CHavHPIT.css +1 -0
- package/dist/web3/assets/index-DyvOBH92.js +934 -0
- package/dist/web3/index.html +14 -14
- package/package.json +161 -152
- package/dist/chunk-MOXICJPD.js +0 -303
- package/dist/web3/assets/index-24dBhQSQ.css +0 -1
- package/dist/web3/assets/index-DpM8J4RN.js +0 -889
package/dist/web3/index.html
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>ContextNest</title>
|
|
7
|
-
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🪺</text></svg>" />
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
10
|
-
</head>
|
|
11
|
-
<body class="bg-background text-foreground antialiased">
|
|
12
|
-
<div id="root"></div>
|
|
13
|
-
</body>
|
|
14
|
-
</html>
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>ContextNest</title>
|
|
7
|
+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🪺</text></svg>" />
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DyvOBH92.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CHavHPIT.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body class="bg-background text-foreground antialiased">
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,152 +1,161 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@promptowl/contextnest-community",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "ContextNest Community Edition — self-hosted context governance server for AI workflows. Commercial software by Promptowl LLC.",
|
|
5
|
-
"private": false,
|
|
6
|
-
"type": "module",
|
|
7
|
-
"license": "SEE LICENSE IN LICENSE.md",
|
|
8
|
-
"author": {
|
|
9
|
-
"name": "Promptowl LLC",
|
|
10
|
-
"email": "hoot@promptowl.ai",
|
|
11
|
-
"url": "https://promptowl.ai"
|
|
12
|
-
},
|
|
13
|
-
"homepage": "https://promptowl.ai/contextnest/",
|
|
14
|
-
"bugs": {
|
|
15
|
-
"email": "hoot@promptowl.ai",
|
|
16
|
-
"url": "https://promptowl.ai/contact-us/"
|
|
17
|
-
},
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "https://github.com/PromptOwl/contextnest-community.git"
|
|
21
|
-
},
|
|
22
|
-
"keywords": [
|
|
23
|
-
"contextnest",
|
|
24
|
-
"promptowl",
|
|
25
|
-
"context-governance",
|
|
26
|
-
"llm",
|
|
27
|
-
"mcp",
|
|
28
|
-
"ai-workflows",
|
|
29
|
-
"commercial"
|
|
30
|
-
],
|
|
31
|
-
"main": "dist/index.js",
|
|
32
|
-
"bin": {
|
|
33
|
-
"contextnest-community": "dist/index.js"
|
|
34
|
-
},
|
|
35
|
-
"files": [
|
|
36
|
-
"dist",
|
|
37
|
-
"LICENSE.md",
|
|
38
|
-
"README.md",
|
|
39
|
-
"CONFIGURATION.md"
|
|
40
|
-
],
|
|
41
|
-
"publishConfig": {
|
|
42
|
-
"access": "public"
|
|
43
|
-
},
|
|
44
|
-
"engines": {
|
|
45
|
-
"node": ">=20.0.0"
|
|
46
|
-
},
|
|
47
|
-
"scripts": {
|
|
48
|
-
"dev": "concurrently -k -n server,ui -c blue,magenta \"npm run dev:server\" \"npm run dev:ui\"",
|
|
49
|
-
"dev:server": "tsx watch src/index.ts",
|
|
50
|
-
"dev:ui": "vite build --config ui/vite.config.ts --watch",
|
|
51
|
-
"build": "tsup src/index.ts --format esm --dts --clean && npm run build:ui",
|
|
52
|
-
"build:server": "tsup src/index.ts --format esm --dts --clean",
|
|
53
|
-
"build:ui": "vite build --config ui/vite.config.ts",
|
|
54
|
-
"start": "node dist/index.js",
|
|
55
|
-
"typecheck": "tsc --noEmit",
|
|
56
|
-
"test": "vitest run",
|
|
57
|
-
"test:watch": "vitest",
|
|
58
|
-
"prepublishOnly": "npm run build",
|
|
59
|
-
"version-packages": "changeset version && npm install --package-lock-only",
|
|
60
|
-
"release": "npm run build && changeset publish"
|
|
61
|
-
},
|
|
62
|
-
"pnpm": {
|
|
63
|
-
"onlyBuiltDependencies": [
|
|
64
|
-
"better-sqlite3",
|
|
65
|
-
"esbuild"
|
|
66
|
-
]
|
|
67
|
-
},
|
|
68
|
-
"dependencies": {
|
|
69
|
-
"@hono/node-server": "^1.13.8",
|
|
70
|
-
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
71
|
-
"@promptowl/contextnest-engine": "^1.0.0",
|
|
72
|
-
"bcryptjs": "^3.0.3",
|
|
73
|
-
"better-sqlite3": "^11.8.2",
|
|
74
|
-
"dotenv": "^16.6.1",
|
|
75
|
-
"hono": "
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"@changesets/
|
|
83
|
-
"@
|
|
84
|
-
"@radix-ui/react-
|
|
85
|
-
"@radix-ui/react-
|
|
86
|
-
"@radix-ui/react-
|
|
87
|
-
"@radix-ui/react-
|
|
88
|
-
"@radix-ui/react-
|
|
89
|
-
"@radix-ui/react-
|
|
90
|
-
"@radix-ui/react-
|
|
91
|
-
"@radix-ui/react-
|
|
92
|
-
"@radix-ui/react-
|
|
93
|
-
"@radix-ui/react-
|
|
94
|
-
"@radix-ui/react-
|
|
95
|
-
"@radix-ui/react-
|
|
96
|
-
"@radix-ui/react-
|
|
97
|
-
"@radix-ui/react-
|
|
98
|
-
"@radix-ui/react-
|
|
99
|
-
"@
|
|
100
|
-
"@
|
|
101
|
-
"@tiptap/
|
|
102
|
-
"@tiptap/extension-
|
|
103
|
-
"@tiptap/extension-
|
|
104
|
-
"@tiptap/extension-
|
|
105
|
-
"@tiptap/extension-
|
|
106
|
-
"@tiptap/extension-
|
|
107
|
-
"@tiptap/extension-
|
|
108
|
-
"@tiptap/extension-
|
|
109
|
-
"@tiptap/extension-
|
|
110
|
-
"@tiptap/extension-
|
|
111
|
-
"@tiptap/extension-
|
|
112
|
-
"@tiptap/extension-
|
|
113
|
-
"@tiptap/extension-
|
|
114
|
-
"@tiptap/extension-
|
|
115
|
-
"@tiptap/
|
|
116
|
-
"@tiptap/
|
|
117
|
-
"@tiptap/
|
|
118
|
-
"@
|
|
119
|
-
"@
|
|
120
|
-
"@types/
|
|
121
|
-
"@types/
|
|
122
|
-
"@types/
|
|
123
|
-
"@types/
|
|
124
|
-
"@types/
|
|
125
|
-
"@
|
|
126
|
-
"@
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"react": "^
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
|
|
152
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@promptowl/contextnest-community",
|
|
3
|
+
"version": "1.9.0",
|
|
4
|
+
"description": "ContextNest Community Edition — self-hosted context governance server for AI workflows. Commercial software by Promptowl LLC.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Promptowl LLC",
|
|
10
|
+
"email": "hoot@promptowl.ai",
|
|
11
|
+
"url": "https://promptowl.ai"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://promptowl.ai/contextnest/",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"email": "hoot@promptowl.ai",
|
|
16
|
+
"url": "https://promptowl.ai/contact-us/"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/PromptOwl/contextnest-community.git"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"contextnest",
|
|
24
|
+
"promptowl",
|
|
25
|
+
"context-governance",
|
|
26
|
+
"llm",
|
|
27
|
+
"mcp",
|
|
28
|
+
"ai-workflows",
|
|
29
|
+
"commercial"
|
|
30
|
+
],
|
|
31
|
+
"main": "dist/index.js",
|
|
32
|
+
"bin": {
|
|
33
|
+
"contextnest-community": "dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"LICENSE.md",
|
|
38
|
+
"README.md",
|
|
39
|
+
"CONFIGURATION.md"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.0.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"dev": "concurrently -k -n server,ui -c blue,magenta \"npm run dev:server\" \"npm run dev:ui\"",
|
|
49
|
+
"dev:server": "tsx watch src/index.ts",
|
|
50
|
+
"dev:ui": "vite build --config ui/vite.config.ts --watch",
|
|
51
|
+
"build": "tsup src/index.ts --format esm --dts --clean && npm run build:ui",
|
|
52
|
+
"build:server": "tsup src/index.ts --format esm --dts --clean",
|
|
53
|
+
"build:ui": "vite build --config ui/vite.config.ts",
|
|
54
|
+
"start": "node dist/index.js",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"prepublishOnly": "npm run build",
|
|
59
|
+
"version-packages": "changeset version && npm install --package-lock-only",
|
|
60
|
+
"release": "npm run build && changeset publish"
|
|
61
|
+
},
|
|
62
|
+
"pnpm": {
|
|
63
|
+
"onlyBuiltDependencies": [
|
|
64
|
+
"better-sqlite3",
|
|
65
|
+
"esbuild"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"dependencies": {
|
|
69
|
+
"@hono/node-server": "^1.13.8",
|
|
70
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
71
|
+
"@promptowl/contextnest-engine": "^1.0.0",
|
|
72
|
+
"bcryptjs": "^3.0.3",
|
|
73
|
+
"better-sqlite3": "^11.8.2",
|
|
74
|
+
"dotenv": "^16.6.1",
|
|
75
|
+
"hono": "4.12.25",
|
|
76
|
+
"nodemailer": "9.0.3",
|
|
77
|
+
"pg": "^8.13.1",
|
|
78
|
+
"uuid": "^14.0.0",
|
|
79
|
+
"zod": "^4.3.6"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@changesets/changelog-github": "^0.7.0",
|
|
83
|
+
"@changesets/cli": "^2.31.0",
|
|
84
|
+
"@radix-ui/react-alert-dialog": "^1.1.11",
|
|
85
|
+
"@radix-ui/react-avatar": "^1.1.0",
|
|
86
|
+
"@radix-ui/react-checkbox": "^1.1.1",
|
|
87
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
88
|
+
"@radix-ui/react-dropdown-menu": "^2.1.12",
|
|
89
|
+
"@radix-ui/react-icons": "^1.3.0",
|
|
90
|
+
"@radix-ui/react-label": "^2.1.0",
|
|
91
|
+
"@radix-ui/react-popover": "^1.1.11",
|
|
92
|
+
"@radix-ui/react-progress": "^1.1.0",
|
|
93
|
+
"@radix-ui/react-scroll-area": "^1.2.3",
|
|
94
|
+
"@radix-ui/react-select": "^2.1.1",
|
|
95
|
+
"@radix-ui/react-separator": "^1.1.0",
|
|
96
|
+
"@radix-ui/react-slot": "^1.2.3",
|
|
97
|
+
"@radix-ui/react-switch": "^1.1.2",
|
|
98
|
+
"@radix-ui/react-tabs": "^1.1.9",
|
|
99
|
+
"@radix-ui/react-tooltip": "^1.1.8",
|
|
100
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
101
|
+
"@tiptap/core": "^3.23.2",
|
|
102
|
+
"@tiptap/extension-character-count": "^3.23.2",
|
|
103
|
+
"@tiptap/extension-code-block-lowlight": "^3.23.2",
|
|
104
|
+
"@tiptap/extension-color": "^3.23.2",
|
|
105
|
+
"@tiptap/extension-details": "^3.23.2",
|
|
106
|
+
"@tiptap/extension-drag-handle-react": "^3.23.2",
|
|
107
|
+
"@tiptap/extension-highlight": "^3.23.2",
|
|
108
|
+
"@tiptap/extension-image": "3.23.2",
|
|
109
|
+
"@tiptap/extension-link": "^3.23.2",
|
|
110
|
+
"@tiptap/extension-node-range": "^3.23.2",
|
|
111
|
+
"@tiptap/extension-placeholder": "^3.23.2",
|
|
112
|
+
"@tiptap/extension-table": "^3.23.2",
|
|
113
|
+
"@tiptap/extension-task-item": "^3.23.2",
|
|
114
|
+
"@tiptap/extension-task-list": "^3.23.2",
|
|
115
|
+
"@tiptap/extension-text-style": "^3.23.2",
|
|
116
|
+
"@tiptap/extension-typography": "^3.23.2",
|
|
117
|
+
"@tiptap/pm": "^3.23.2",
|
|
118
|
+
"@tiptap/react": "^3.23.2",
|
|
119
|
+
"@tiptap/starter-kit": "^3.23.2",
|
|
120
|
+
"@types/bcryptjs": "^3.0.0",
|
|
121
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
122
|
+
"@types/d3-force": "^3.0.10",
|
|
123
|
+
"@types/nodemailer": "8.0.1",
|
|
124
|
+
"@types/pg": "^8.11.10",
|
|
125
|
+
"@types/react": "^18.3.0",
|
|
126
|
+
"@types/react-dom": "^18.3.0",
|
|
127
|
+
"@types/uuid": "^10.0.0",
|
|
128
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
129
|
+
"@vitest/coverage-v8": "3.2.6",
|
|
130
|
+
"autoprefixer": "^10.4.20",
|
|
131
|
+
"class-variance-authority": "^0.7.0",
|
|
132
|
+
"clsx": "^2.1.1",
|
|
133
|
+
"cmdk": "^1.0.0",
|
|
134
|
+
"concurrently": "^9.2.1",
|
|
135
|
+
"d3-force": "^3.0.0",
|
|
136
|
+
"lowlight": "^3.3.0",
|
|
137
|
+
"lucide-react": "^0.577.0",
|
|
138
|
+
"markdown-it-container": "^4.0.0",
|
|
139
|
+
"postcss": "^8.4.47",
|
|
140
|
+
"react": "^18.3.1",
|
|
141
|
+
"react-dom": "^18.3.1",
|
|
142
|
+
"react-markdown": "^9.1.0",
|
|
143
|
+
"remark-gfm": "^4.0.1",
|
|
144
|
+
"sonner": "^2.0.7",
|
|
145
|
+
"tailwind-merge": "^3.5.0",
|
|
146
|
+
"tailwindcss": "^3.4.14",
|
|
147
|
+
"tailwindcss-animate": "^1.0.7",
|
|
148
|
+
"tiptap-markdown": "^0.9.0",
|
|
149
|
+
"tsup": "^8.4.0",
|
|
150
|
+
"tsx": "^4.19.4",
|
|
151
|
+
"typescript": "^5.7.3",
|
|
152
|
+
"vite": "6.4.3",
|
|
153
|
+
"vitest": "3.2.6"
|
|
154
|
+
},
|
|
155
|
+
"overrides": {
|
|
156
|
+
"shell-quote": "1.8.4",
|
|
157
|
+
"qs": "6.15.2",
|
|
158
|
+
"@babel/core": "7.29.6",
|
|
159
|
+
"brace-expansion": "5.0.6"
|
|
160
|
+
}
|
|
161
|
+
}
|
package/dist/chunk-MOXICJPD.js
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createVersion,
|
|
3
|
-
setApprovedVersion
|
|
4
|
-
} from "./chunk-VD5QX2ZQ.js";
|
|
5
|
-
import {
|
|
6
|
-
buildTitleMap,
|
|
7
|
-
canUserApprove,
|
|
8
|
-
engineCache,
|
|
9
|
-
resolveStewardsForNode
|
|
10
|
-
} from "./chunk-43DOX4LH.js";
|
|
11
|
-
import {
|
|
12
|
-
getDb,
|
|
13
|
-
insertOrReplace,
|
|
14
|
-
nowExpr
|
|
15
|
-
} from "./chunk-QMLAXQES.js";
|
|
16
|
-
|
|
17
|
-
// src/governance/review-service.ts
|
|
18
|
-
import { v4 as uuid } from "uuid";
|
|
19
|
-
|
|
20
|
-
// src/governance/safe-publish.ts
|
|
21
|
-
import {
|
|
22
|
-
publishDocument,
|
|
23
|
-
serializeDocument
|
|
24
|
-
} from "@promptowl/contextnest-engine";
|
|
25
|
-
async function safePublishDocument(storage, docId, options) {
|
|
26
|
-
const node = await storage.readDocument(docId);
|
|
27
|
-
const cleanedFrontmatter = stripUndefinedDeep(node.frontmatter);
|
|
28
|
-
const cleanedNode = { ...node, frontmatter: cleanedFrontmatter };
|
|
29
|
-
await storage.writeDocument(docId, serializeDocument(cleanedNode));
|
|
30
|
-
return publishDocument(storage, docId, options);
|
|
31
|
-
}
|
|
32
|
-
function stripUndefinedDeep(value) {
|
|
33
|
-
if (Array.isArray(value)) {
|
|
34
|
-
return value.filter((v) => v !== void 0).map((v) => stripUndefinedDeep(v));
|
|
35
|
-
}
|
|
36
|
-
if (value && typeof value === "object") {
|
|
37
|
-
const out = {};
|
|
38
|
-
for (const [k, v] of Object.entries(value)) {
|
|
39
|
-
if (v === void 0) continue;
|
|
40
|
-
out[k] = stripUndefinedDeep(v);
|
|
41
|
-
}
|
|
42
|
-
return out;
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/governance/review-service.ts
|
|
48
|
-
async function submitForReview(params) {
|
|
49
|
-
const db = getDb();
|
|
50
|
-
const existing = await db.get(
|
|
51
|
-
"SELECT id FROM review_requests WHERE nest_id = ? AND node_id = ? AND status = 'pending'",
|
|
52
|
-
[params.nestId, params.nodeId]
|
|
53
|
-
);
|
|
54
|
-
if (existing) {
|
|
55
|
-
throw new Error("A review is already pending for this node");
|
|
56
|
-
}
|
|
57
|
-
const id = uuid();
|
|
58
|
-
await db.run(
|
|
59
|
-
`INSERT INTO review_requests
|
|
60
|
-
(id, nest_id, node_id, version, requested_by, request_note, priority)
|
|
61
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
62
|
-
[
|
|
63
|
-
id,
|
|
64
|
-
params.nestId,
|
|
65
|
-
params.nodeId,
|
|
66
|
-
params.version,
|
|
67
|
-
params.requestedBy,
|
|
68
|
-
params.note || null,
|
|
69
|
-
params.priority || "normal"
|
|
70
|
-
]
|
|
71
|
-
);
|
|
72
|
-
await db.run(
|
|
73
|
-
"UPDATE node_versions SET status = 'pending_review' WHERE nest_id = ? AND node_id = ? AND version = ?",
|
|
74
|
-
[params.nestId, params.nodeId, params.version]
|
|
75
|
-
);
|
|
76
|
-
return await getReviewRequest(id);
|
|
77
|
-
}
|
|
78
|
-
async function approve(params) {
|
|
79
|
-
const db = getDb();
|
|
80
|
-
const pending = await db.get(
|
|
81
|
-
"SELECT * FROM review_requests WHERE nest_id = ? AND node_id = ? AND status = 'pending' ORDER BY requested_at DESC LIMIT 1",
|
|
82
|
-
[params.nestId, params.nodeId]
|
|
83
|
-
);
|
|
84
|
-
if (!pending) {
|
|
85
|
-
throw new Error("No pending review for this node");
|
|
86
|
-
}
|
|
87
|
-
if (!params.override) {
|
|
88
|
-
const check = await canUserApprove(
|
|
89
|
-
params.nestId,
|
|
90
|
-
params.nodeId,
|
|
91
|
-
params.approvedBy
|
|
92
|
-
);
|
|
93
|
-
if (!check.allowed) {
|
|
94
|
-
throw new Error(check.reason);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
await db.run(
|
|
98
|
-
`UPDATE review_requests
|
|
99
|
-
SET status = 'approved', resolved_by = ?, resolved_at = ${nowExpr(db)},
|
|
100
|
-
resolution_note = ?, is_override = ?
|
|
101
|
-
WHERE id = ?`,
|
|
102
|
-
[params.approvedBy, params.note || null, params.override ? 1 : 0, pending.id]
|
|
103
|
-
);
|
|
104
|
-
await db.run(
|
|
105
|
-
"UPDATE node_versions SET status = 'published' WHERE nest_id = ? AND node_id = ? AND version = ?",
|
|
106
|
-
[params.nestId, params.nodeId, params.version]
|
|
107
|
-
);
|
|
108
|
-
await db.run(
|
|
109
|
-
insertOrReplace(
|
|
110
|
-
db,
|
|
111
|
-
`INSERT INTO approved_versions (nest_id, node_id, approved_version, approved_by)
|
|
112
|
-
VALUES (?, ?, ?, ?)`,
|
|
113
|
-
["nest_id", "node_id"],
|
|
114
|
-
`approved_version = excluded.approved_version, approved_by = excluded.approved_by, approved_at = ${nowExpr(db)}`
|
|
115
|
-
),
|
|
116
|
-
[params.nestId, params.nodeId, params.version, params.approvedBy]
|
|
117
|
-
);
|
|
118
|
-
try {
|
|
119
|
-
const { storage } = await engineCache.get(params.nestId);
|
|
120
|
-
const result = await safePublishDocument(storage, params.nodeId, {
|
|
121
|
-
editedBy: params.approvedBy,
|
|
122
|
-
note: params.note || `Approved review request ${pending.id}`
|
|
123
|
-
});
|
|
124
|
-
const engineVersion = result.versionEntry.version;
|
|
125
|
-
if (engineVersion !== params.version) {
|
|
126
|
-
const node = result.node;
|
|
127
|
-
const tags = node.frontmatter.tags || [];
|
|
128
|
-
await createVersion({
|
|
129
|
-
nestId: params.nestId,
|
|
130
|
-
nodeId: params.nodeId,
|
|
131
|
-
version: engineVersion,
|
|
132
|
-
content: node.body || "",
|
|
133
|
-
author: params.approvedBy,
|
|
134
|
-
status: "published",
|
|
135
|
-
tags,
|
|
136
|
-
changeNote: params.note || `Approved review request ${pending.id}`
|
|
137
|
-
});
|
|
138
|
-
await setApprovedVersion(
|
|
139
|
-
params.nestId,
|
|
140
|
-
params.nodeId,
|
|
141
|
-
engineVersion,
|
|
142
|
-
params.approvedBy
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
} catch (err) {
|
|
146
|
-
console.error(
|
|
147
|
-
`publishDocument failed for ${params.nestId}/${params.nodeId} on approve:`,
|
|
148
|
-
err
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
return await getReviewRequest(pending.id);
|
|
152
|
-
}
|
|
153
|
-
async function reject(params) {
|
|
154
|
-
const db = getDb();
|
|
155
|
-
const pending = await db.get(
|
|
156
|
-
"SELECT * FROM review_requests WHERE nest_id = ? AND node_id = ? AND status = 'pending' ORDER BY requested_at DESC LIMIT 1",
|
|
157
|
-
[params.nestId, params.nodeId]
|
|
158
|
-
);
|
|
159
|
-
if (!pending) {
|
|
160
|
-
throw new Error("No pending review for this node");
|
|
161
|
-
}
|
|
162
|
-
const check = await canUserApprove(
|
|
163
|
-
params.nestId,
|
|
164
|
-
params.nodeId,
|
|
165
|
-
params.rejectedBy
|
|
166
|
-
);
|
|
167
|
-
if (!check.allowed) {
|
|
168
|
-
throw new Error(check.reason);
|
|
169
|
-
}
|
|
170
|
-
await db.run(
|
|
171
|
-
`UPDATE review_requests
|
|
172
|
-
SET status = 'rejected', resolved_by = ?, resolved_at = ${nowExpr(db)}, resolution_note = ?
|
|
173
|
-
WHERE id = ?`,
|
|
174
|
-
[params.rejectedBy, params.note, pending.id]
|
|
175
|
-
);
|
|
176
|
-
await db.run(
|
|
177
|
-
"UPDATE node_versions SET status = 'rejected' WHERE nest_id = ? AND node_id = ? AND version = ?",
|
|
178
|
-
[params.nestId, params.nodeId, params.version]
|
|
179
|
-
);
|
|
180
|
-
return await getReviewRequest(pending.id);
|
|
181
|
-
}
|
|
182
|
-
async function cancelReview(params) {
|
|
183
|
-
const db = getDb();
|
|
184
|
-
const pending = await db.get(
|
|
185
|
-
"SELECT * FROM review_requests WHERE nest_id = ? AND node_id = ? AND status = 'pending' ORDER BY requested_at DESC LIMIT 1",
|
|
186
|
-
[params.nestId, params.nodeId]
|
|
187
|
-
);
|
|
188
|
-
if (!pending) return null;
|
|
189
|
-
await db.run(
|
|
190
|
-
`UPDATE review_requests
|
|
191
|
-
SET status = 'cancelled', resolved_by = ?, resolved_at = ${nowExpr(db)}
|
|
192
|
-
WHERE id = ?`,
|
|
193
|
-
[params.cancelledBy, pending.id]
|
|
194
|
-
);
|
|
195
|
-
await db.run(
|
|
196
|
-
"UPDATE node_versions SET status = 'draft' WHERE nest_id = ? AND node_id = ? AND version = ?",
|
|
197
|
-
[params.nestId, params.nodeId, pending.version]
|
|
198
|
-
);
|
|
199
|
-
return await getReviewRequest(pending.id);
|
|
200
|
-
}
|
|
201
|
-
async function getReviewQueue(params) {
|
|
202
|
-
const db = getDb();
|
|
203
|
-
let whereClauses = [];
|
|
204
|
-
const args = [];
|
|
205
|
-
if (params.nestId) {
|
|
206
|
-
whereClauses.push("nest_id = ?");
|
|
207
|
-
args.push(params.nestId);
|
|
208
|
-
}
|
|
209
|
-
if (params.status) {
|
|
210
|
-
const statuses = Array.isArray(params.status) ? params.status : [params.status];
|
|
211
|
-
whereClauses.push(
|
|
212
|
-
`status IN (${statuses.map(() => "?").join(",")})`
|
|
213
|
-
);
|
|
214
|
-
args.push(...statuses);
|
|
215
|
-
}
|
|
216
|
-
const where = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
217
|
-
const total = (await db.get(
|
|
218
|
-
`SELECT COUNT(*) as c FROM review_requests ${where}`,
|
|
219
|
-
args
|
|
220
|
-
)).c;
|
|
221
|
-
const limit = params.limit || 50;
|
|
222
|
-
const offset = params.offset || 0;
|
|
223
|
-
const rows = await db.all(
|
|
224
|
-
`SELECT * FROM review_requests ${where}
|
|
225
|
-
ORDER BY
|
|
226
|
-
CASE priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 WHEN 'normal' THEN 2 ELSE 3 END,
|
|
227
|
-
requested_at DESC
|
|
228
|
-
LIMIT ? OFFSET ?`,
|
|
229
|
-
[...args, limit, offset]
|
|
230
|
-
);
|
|
231
|
-
let requests = rows.map(rowToReviewRequest);
|
|
232
|
-
if (params.stewardEmail) {
|
|
233
|
-
const filtered = [];
|
|
234
|
-
for (const r of requests) {
|
|
235
|
-
const stewards = await resolveStewardsForNode(r.nestId, r.nodeId);
|
|
236
|
-
if (stewards.some((s) => s.steward.userEmail === params.stewardEmail)) {
|
|
237
|
-
filtered.push(r);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
requests = filtered;
|
|
241
|
-
}
|
|
242
|
-
const titleMapsByNest = /* @__PURE__ */ new Map();
|
|
243
|
-
const nestIds = new Set(requests.map((r) => r.nestId));
|
|
244
|
-
for (const nid of nestIds) {
|
|
245
|
-
titleMapsByNest.set(nid, await buildTitleMap(nid));
|
|
246
|
-
}
|
|
247
|
-
requests = requests.map((r) => ({
|
|
248
|
-
...r,
|
|
249
|
-
title: titleMapsByNest.get(r.nestId)?.get(r.nodeId)
|
|
250
|
-
}));
|
|
251
|
-
return { requests, total };
|
|
252
|
-
}
|
|
253
|
-
async function getReviewHistory(nestId, nodeId) {
|
|
254
|
-
const db = getDb();
|
|
255
|
-
const rows = await db.all(
|
|
256
|
-
"SELECT * FROM review_requests WHERE nest_id = ? AND node_id = ? ORDER BY requested_at DESC",
|
|
257
|
-
[nestId, nodeId]
|
|
258
|
-
);
|
|
259
|
-
return rows.map(rowToReviewRequest);
|
|
260
|
-
}
|
|
261
|
-
async function getPendingReview(nestId, nodeId) {
|
|
262
|
-
const db = getDb();
|
|
263
|
-
const row = await db.get(
|
|
264
|
-
"SELECT * FROM review_requests WHERE nest_id = ? AND node_id = ? AND status = 'pending' ORDER BY requested_at DESC LIMIT 1",
|
|
265
|
-
[nestId, nodeId]
|
|
266
|
-
);
|
|
267
|
-
return row ? rowToReviewRequest(row) : null;
|
|
268
|
-
}
|
|
269
|
-
async function getReviewRequest(id) {
|
|
270
|
-
const db = getDb();
|
|
271
|
-
const row = await db.get(
|
|
272
|
-
"SELECT * FROM review_requests WHERE id = ?",
|
|
273
|
-
[id]
|
|
274
|
-
);
|
|
275
|
-
return row ? rowToReviewRequest(row) : null;
|
|
276
|
-
}
|
|
277
|
-
function rowToReviewRequest(row) {
|
|
278
|
-
return {
|
|
279
|
-
id: row.id,
|
|
280
|
-
nestId: row.nest_id,
|
|
281
|
-
nodeId: row.node_id,
|
|
282
|
-
version: row.version,
|
|
283
|
-
requestedBy: row.requested_by,
|
|
284
|
-
requestedAt: row.requested_at,
|
|
285
|
-
requestNote: row.request_note || void 0,
|
|
286
|
-
status: row.status,
|
|
287
|
-
resolvedBy: row.resolved_by || void 0,
|
|
288
|
-
resolvedAt: row.resolved_at || void 0,
|
|
289
|
-
resolutionNote: row.resolution_note || void 0,
|
|
290
|
-
priority: row.priority
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export {
|
|
295
|
-
safePublishDocument,
|
|
296
|
-
submitForReview,
|
|
297
|
-
approve,
|
|
298
|
-
reject,
|
|
299
|
-
cancelReview,
|
|
300
|
-
getReviewQueue,
|
|
301
|
-
getReviewHistory,
|
|
302
|
-
getPendingReview
|
|
303
|
-
};
|