@karmaniverous/jeeves-server 3.0.0 → 3.1.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/.tsbuildinfo +1 -1
- package/CHANGELOG.md +34 -1
- package/README.md +45 -0
- package/about.md +15 -39
- package/client/src/components/SearchModal.tsx +533 -222
- package/client/src/lib/api.ts +1 -0
- package/dist/client/assets/{CodeEditor-0XHVI8Nu.js → CodeEditor-DBeIVlfL.js} +1 -1
- package/dist/client/assets/{CodeViewer-CykMVsfX.js → CodeViewer-CbneqN2L.js} +1 -1
- package/dist/client/assets/index-D-RC7ZS6.css +1 -0
- package/dist/client/assets/index-fY6PleHE.js +62 -0
- package/dist/client/index.html +2 -2
- package/dist/src/routes/api/search.js +23 -1
- package/dist/src/routes/api/status.js +7 -1
- package/dist/src/routes/api/status.test.js +1 -1
- package/dist/src/services/eventLog.js +8 -0
- package/guides/api-integration.md +62 -155
- package/guides/deployment.md +60 -164
- package/guides/event-gateway.md +57 -50
- package/guides/exports.md +18 -20
- package/guides/index.md +21 -0
- package/guides/setup.md +211 -181
- package/guides/sharing.md +7 -7
- package/package.json +1 -1
- package/src/routes/api/search.ts +22 -1
- package/src/routes/api/status.test.ts +1 -1
- package/src/routes/api/status.ts +52 -41
- package/src/services/eventLog.ts +9 -0
- package/dist/client/assets/index-DbMebkkd.css +0 -1
- package/dist/client/assets/index-LjwgzZ7F.js +0 -62
package/guides/setup.md
CHANGED
|
@@ -2,70 +2,121 @@
|
|
|
2
2
|
|
|
3
3
|
## Prerequisites
|
|
4
4
|
|
|
5
|
-
- **Node.js** ≥
|
|
5
|
+
- **Node.js** ≥ 20
|
|
6
6
|
- **Chrome or Chromium** — required for PDF export (Puppeteer uses it headlessly)
|
|
7
|
-
- **A domain or IP** where the server will be accessible (for Google OAuth callbacks)
|
|
8
7
|
|
|
9
8
|
## Installation
|
|
10
9
|
|
|
11
10
|
```bash
|
|
12
|
-
|
|
13
|
-
cd jeeves-server
|
|
14
|
-
npm install
|
|
11
|
+
npm install -g @karmaniverous/jeeves-server
|
|
15
12
|
```
|
|
16
13
|
|
|
14
|
+
The `postinstall` script automatically downloads the PlantUML jar for local diagram rendering.
|
|
15
|
+
|
|
17
16
|
## Configuration
|
|
18
17
|
|
|
19
|
-
Jeeves Server uses
|
|
18
|
+
Jeeves Server uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) for configuration. Create a config file in any supported format:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# JSON (recommended for simplicity)
|
|
22
|
+
jeeves-server.config.json
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
# YAML
|
|
25
|
+
jeeves-server.config.yaml
|
|
26
|
+
|
|
27
|
+
# TypeScript (for type checking during authoring)
|
|
28
|
+
jeeves-server.config.ts
|
|
29
|
+
|
|
30
|
+
# Or any cosmiconfig-supported format
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The server searches for config files starting from its working directory and walking up. You can also specify an explicit path:
|
|
22
34
|
|
|
23
35
|
```bash
|
|
24
|
-
|
|
36
|
+
jeeves-server start --config /path/to/jeeves-server.config.json
|
|
25
37
|
```
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
### Config structure (JSON)
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"port": 1934,
|
|
44
|
+
"chromePath": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
45
|
+
"auth": {
|
|
46
|
+
"modes": ["keys", "google"],
|
|
47
|
+
"google": {
|
|
48
|
+
"clientId": "your-google-client-id",
|
|
49
|
+
"clientSecret": "your-google-client-secret"
|
|
50
|
+
},
|
|
51
|
+
"sessionSecret": "random-session-signing-secret"
|
|
52
|
+
},
|
|
53
|
+
"scopes": {
|
|
54
|
+
"restricted": {
|
|
55
|
+
"allow": ["/d/projects/*"],
|
|
56
|
+
"deny": ["/d/projects/secret/*"]
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"insiders": {
|
|
60
|
+
"alice@example.com": {},
|
|
61
|
+
"contractor@example.com": { "scopes": "restricted" }
|
|
62
|
+
},
|
|
63
|
+
"keys": {
|
|
64
|
+
"_internal": "random-hex-seed-for-pdf-export",
|
|
65
|
+
"_plugin": "random-hex-seed-for-openclaw-plugin",
|
|
66
|
+
"primary": "random-hex-seed-for-api-access",
|
|
67
|
+
"webhook-notion": {
|
|
68
|
+
"key": "random-hex-seed",
|
|
69
|
+
"scopes": ["/event"]
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"events": {},
|
|
73
|
+
"watcherUrl": "http://localhost:1936",
|
|
74
|
+
"runnerUrl": "http://127.0.0.1:1937"
|
|
75
|
+
}
|
|
76
|
+
```
|
|
28
77
|
|
|
29
|
-
###
|
|
78
|
+
### Environment variable substitution
|
|
30
79
|
|
|
31
|
-
|
|
32
|
-
import type { JeevesConfig } from './src/config/schema.js';
|
|
80
|
+
String values in the config support `${VAR_NAME}` substitution from `process.env`:
|
|
33
81
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"auth": {
|
|
85
|
+
"google": {
|
|
86
|
+
"clientId": "${GOOGLE_CLIENT_ID}",
|
|
87
|
+
"clientSecret": "${GOOGLE_CLIENT_SECRET}"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
42
91
|
```
|
|
43
92
|
|
|
44
|
-
|
|
93
|
+
### Validating your config
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
jeeves-server config validate [--config <path>]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This loads and validates the config against the Zod schema, reporting any errors. Use `config show` to see the fully resolved configuration with all derived keys and scope assignments.
|
|
45
100
|
|
|
46
101
|
### Platform-specific settings
|
|
47
102
|
|
|
48
103
|
**Windows** — drives are auto-discovered; no `roots` config needed:
|
|
49
|
-
```
|
|
50
|
-
chromePath:
|
|
104
|
+
```json
|
|
105
|
+
{ "chromePath": "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" }
|
|
51
106
|
```
|
|
52
107
|
|
|
53
108
|
**Linux** — configure filesystem roots for the file browser:
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
jarPath: '/opt/plantuml/plantuml.jar',
|
|
63
|
-
javaPath: '/usr/bin/java', // defaults to 'java' on PATH
|
|
64
|
-
servers: [], // private servers; community server always appended
|
|
65
|
-
},
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"chromePath": "/usr/bin/chromium-browser",
|
|
112
|
+
"roots": {
|
|
113
|
+
"home": "/home",
|
|
114
|
+
"projects": "/opt/projects"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
66
117
|
```
|
|
67
118
|
|
|
68
|
-
On Windows, `roots` is ignored. On Linux, if omitted, it defaults to `{ root:
|
|
119
|
+
On Windows, `roots` is ignored. On Linux, if omitted, it defaults to `{ "root": "/" }`.
|
|
69
120
|
|
|
70
121
|
### Config is immutable at runtime
|
|
71
122
|
|
|
@@ -77,16 +128,17 @@ Once the server starts, the config is loaded once and never written to. Mutable
|
|
|
77
128
|
|
|
78
129
|
Jeeves Server supports two authentication methods, configured via `auth.modes`:
|
|
79
130
|
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"auth": {
|
|
134
|
+
"modes": ["google", "keys"]
|
|
135
|
+
}
|
|
84
136
|
}
|
|
85
137
|
```
|
|
86
138
|
|
|
87
139
|
You can enable one or both. The order matters — modes are checked in the order listed.
|
|
88
140
|
|
|
89
|
-
### Google OAuth (`
|
|
141
|
+
### Google OAuth (`"google"`)
|
|
90
142
|
|
|
91
143
|
**Best for:** Teams where insiders log in via browser.
|
|
92
144
|
|
|
@@ -104,99 +156,106 @@ Users authenticate with their Google account. The server checks their email agai
|
|
|
104
156
|
4. Authorized redirect URI: `https://your-domain.com/auth/google/callback`
|
|
105
157
|
5. Copy the client ID and client secret into your config
|
|
106
158
|
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### Key-Based Auth (`'keys'`)
|
|
159
|
+
### Key-Based Auth (`"keys"`)
|
|
113
160
|
|
|
114
161
|
**Best for:** Headless access, bot integrations, simple setups without Google.
|
|
115
162
|
|
|
116
|
-
Users authenticate by appending `?key=<value>` to any URL. The server derives keys from configured seeds using HMAC-SHA256
|
|
163
|
+
Users authenticate by appending `?key=<value>` to any URL. The server derives keys from configured seeds using HMAC-SHA256.
|
|
117
164
|
|
|
118
165
|
**Requirements when enabled:**
|
|
119
166
|
- At least one entry in `keys`
|
|
120
167
|
|
|
121
|
-
**How keys work:** You configure a **seed** (a random secret string). The server derives the actual
|
|
168
|
+
**How keys work:** You configure a **seed** (a random secret string). The server derives the actual key from it via HMAC. You never put the derived key in the config — only the seed. To get the derived key:
|
|
122
169
|
|
|
123
170
|
```bash
|
|
124
|
-
# Via the API (requires X-API-Key header with any seed)
|
|
125
171
|
curl -H "X-API-Key: <seed>" https://your-domain.com/insider-key
|
|
126
172
|
```
|
|
127
173
|
|
|
128
174
|
### Both Modes Together
|
|
129
175
|
|
|
130
|
-
|
|
131
|
-
auth: {
|
|
132
|
-
modes: ['keys', 'google'], // Keys checked first
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
When both are active:
|
|
137
|
-
- Browser users can log in with Google for a session-based experience
|
|
138
|
-
- Bots and scripts can use `?key=` for stateless access
|
|
139
|
-
- Both methods work on every endpoint
|
|
140
|
-
|
|
141
|
-
### Choosing a Mode
|
|
176
|
+
When both are active, browser users log in with Google, and bots/scripts use `?key=` for stateless access. Both methods work on every endpoint.
|
|
142
177
|
|
|
143
178
|
| Scenario | Recommended |
|
|
144
179
|
|----------|-------------|
|
|
145
|
-
| Team of humans accessing via browser | `[
|
|
146
|
-
| Bot/script access only | `[
|
|
147
|
-
| Humans + bots on the same server | `[
|
|
148
|
-
| Quick local setup, no Google credentials | `[
|
|
180
|
+
| Team of humans accessing via browser | `["google"]` |
|
|
181
|
+
| Bot/script access only | `["keys"]` |
|
|
182
|
+
| Humans + bots on the same server | `["google", "keys"]` |
|
|
183
|
+
| Quick local setup, no Google credentials | `["keys"]` |
|
|
149
184
|
|
|
150
185
|
---
|
|
151
186
|
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
The `insiders` map defines **who** has full browsing access. Each entry is an email address with optional path scopes:
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
insiders: {
|
|
158
|
-
// Full access
|
|
159
|
-
'alice@example.com': {},
|
|
187
|
+
## Named Access Scopes
|
|
160
188
|
|
|
161
|
-
|
|
162
|
-
'contractor@example.com': {
|
|
163
|
-
scopes: ['/d/projects/client-x/*'],
|
|
164
|
-
},
|
|
189
|
+
Define reusable scope policies at the top level, then reference them by name from insiders, keys, or the outsider policy:
|
|
165
190
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"scopes": {
|
|
194
|
+
"engineering": {
|
|
195
|
+
"allow": ["/d/repos/*", "/d/docs/*"],
|
|
196
|
+
"deny": ["/d/docs/hr/*"]
|
|
171
197
|
},
|
|
198
|
+
"readonly-projects": {
|
|
199
|
+
"allow": ["/d/projects/*"]
|
|
200
|
+
}
|
|
172
201
|
},
|
|
173
|
-
|
|
202
|
+
"insiders": {
|
|
203
|
+
"dev@example.com": { "scopes": "engineering" },
|
|
204
|
+
"contractor@example.com": {
|
|
205
|
+
"scopes": "readonly-projects",
|
|
206
|
+
"deny": ["/d/projects/secret/*"]
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
174
210
|
```
|
|
175
211
|
|
|
176
|
-
|
|
212
|
+
Named scopes are **atomic** — composition happens at the point of use. An insider or key referencing a named scope can add extra `allow` and `deny` patterns that merge on top of the named scope's rules.
|
|
177
213
|
|
|
178
|
-
|
|
214
|
+
Scopes can also be specified inline (without named references) using the same formats as before:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{ "scopes": ["/d/projects/*"] }
|
|
218
|
+
{ "scopes": { "allow": ["/d/*"], "deny": ["/d/secrets/*"] } }
|
|
219
|
+
```
|
|
179
220
|
|
|
180
221
|
---
|
|
181
222
|
|
|
182
|
-
##
|
|
223
|
+
## Insiders
|
|
183
224
|
|
|
184
|
-
The `
|
|
225
|
+
The `insiders` map defines **who** has full browsing access:
|
|
226
|
+
|
|
227
|
+
```json
|
|
228
|
+
{
|
|
229
|
+
"insiders": {
|
|
230
|
+
"alice@example.com": {},
|
|
231
|
+
"contractor@example.com": { "scopes": "restricted" },
|
|
232
|
+
"team@example.com": {
|
|
233
|
+
"scopes": { "allow": ["/d/*"], "deny": ["/d/secrets/*"] }
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
185
238
|
|
|
186
|
-
|
|
187
|
-
keys: {
|
|
188
|
-
// Unscoped — full access to all paths
|
|
189
|
-
primary: 'a-random-64-char-hex-string',
|
|
239
|
+
See the [Insiders, Outsiders & Sharing](sharing.md) guide for the full access model.
|
|
190
240
|
|
|
191
|
-
|
|
192
|
-
'webhook-notion': {
|
|
193
|
-
key: 'another-random-hex-string',
|
|
194
|
-
scopes: ['/event'],
|
|
195
|
-
},
|
|
241
|
+
---
|
|
196
242
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
243
|
+
## Keys
|
|
244
|
+
|
|
245
|
+
The `keys` map defines **named API keys** for machine and human access:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"keys": {
|
|
250
|
+
"primary": "a-random-64-char-hex-string",
|
|
251
|
+
"webhook-notion": {
|
|
252
|
+
"key": "another-random-hex-string",
|
|
253
|
+
"scopes": ["/event"]
|
|
254
|
+
},
|
|
255
|
+
"_internal": "seed-for-pdf-export",
|
|
256
|
+
"_plugin": "seed-for-openclaw-plugin"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
200
259
|
```
|
|
201
260
|
|
|
202
261
|
**Generate seeds with:**
|
|
@@ -204,110 +263,81 @@ keys: {
|
|
|
204
263
|
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
205
264
|
```
|
|
206
265
|
|
|
207
|
-
###
|
|
266
|
+
### Reserved keys
|
|
208
267
|
|
|
209
|
-
|
|
268
|
+
| Key | Purpose | Scopes |
|
|
269
|
+
|-----|---------|--------|
|
|
270
|
+
| `_internal` | Puppeteer uses this to authenticate when rendering PDFs/DOCX. **Required for export.** | Must be unscoped |
|
|
271
|
+
| `_plugin` | OpenClaw plugin authentication. See [OpenClaw Integration](../../openclaw/guides/openclaw-integration.md). | Must be unscoped |
|
|
210
272
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
### Key names
|
|
214
|
-
|
|
215
|
-
Key names are used for logging and identification. Choose meaningful names: `primary`, `webhook-notion`, `ci-bot`, etc.
|
|
273
|
+
Both reserved keys are enforced unscoped by the Zod schema.
|
|
216
274
|
|
|
217
275
|
---
|
|
218
276
|
|
|
219
277
|
## Event Gateway
|
|
220
278
|
|
|
221
|
-
|
|
279
|
+
See the [Event Gateway](event-gateway.md) guide for full configuration and usage.
|
|
222
280
|
|
|
223
|
-
|
|
224
|
-
events: {
|
|
225
|
-
'notion-page-update': {
|
|
226
|
-
// JSON Schema to match against incoming body
|
|
227
|
-
schema: {
|
|
228
|
-
type: 'object',
|
|
229
|
-
properties: { type: { const: 'page.content_updated' } },
|
|
230
|
-
required: ['type'],
|
|
231
|
-
},
|
|
232
|
-
// Command to execute when matched
|
|
233
|
-
cmd: 'node /path/to/handler.js',
|
|
234
|
-
// Optional: transform body before passing to command
|
|
235
|
-
map: {
|
|
236
|
-
pageId: { '$': { method: '$.lib._.get', params: ['$.input', 'data.page_id'] } },
|
|
237
|
-
},
|
|
238
|
-
// Optional: override default timeout
|
|
239
|
-
timeoutMs: 60000,
|
|
240
|
-
},
|
|
241
|
-
},
|
|
242
|
-
```
|
|
281
|
+
---
|
|
243
282
|
|
|
244
|
-
|
|
245
|
-
```bash
|
|
246
|
-
curl -X POST "https://your-domain.com/event?key=<webhook-key>" \
|
|
247
|
-
-H "Content-Type: application/json" \
|
|
248
|
-
-d '{"type": "page.content_updated", "data": {"page_id": "abc123"}}'
|
|
249
|
-
```
|
|
283
|
+
## Optional Integrations
|
|
250
284
|
|
|
251
|
-
|
|
285
|
+
### jeeves-watcher (Semantic Search)
|
|
252
286
|
|
|
253
|
-
|
|
287
|
+
When `watcherUrl` is configured, the server proxies semantic search queries to [jeeves-watcher](https://github.com/karmaniverous/jeeves-watcher) and provides a search UI in the header, with filter facets and scope-aware result filtering.
|
|
254
288
|
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
npm run build # Compiles server → dist/
|
|
258
|
-
cd client && npx vite build --outDir ../dist/client && cd .. # Builds React SPA → dist/client/
|
|
289
|
+
```json
|
|
290
|
+
{ "watcherUrl": "http://localhost:1936" }
|
|
259
291
|
```
|
|
260
292
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
---
|
|
293
|
+
### jeeves-runner (Process Dashboard)
|
|
264
294
|
|
|
265
|
-
|
|
295
|
+
When `runnerUrl` is configured, the server proxies runner API calls for the process dashboard UI.
|
|
266
296
|
|
|
267
|
-
```
|
|
268
|
-
|
|
297
|
+
```json
|
|
298
|
+
{ "runnerUrl": "http://127.0.0.1:1937" }
|
|
269
299
|
```
|
|
270
300
|
|
|
271
|
-
###
|
|
301
|
+
### PlantUML
|
|
272
302
|
|
|
273
|
-
|
|
274
|
-
nssm install JeevesServer "node" "/path/to/dist/server.js"
|
|
275
|
-
nssm start JeevesServer
|
|
276
|
-
```
|
|
303
|
+
Mermaid is bundled as a direct dependency — no configuration needed. PlantUML uses a fallback pipeline:
|
|
277
304
|
|
|
278
|
-
|
|
305
|
+
1. **Local Java jar** (downloaded automatically via `postinstall`) — fastest, supports `!include`
|
|
306
|
+
2. **Configured PlantUML servers** — private instances, tried in order
|
|
307
|
+
3. **Public community server** (`plantuml.com`) — always appended as last resort
|
|
279
308
|
|
|
280
|
-
```
|
|
281
|
-
|
|
309
|
+
```json
|
|
310
|
+
{
|
|
311
|
+
"plantuml": {
|
|
312
|
+
"jarPath": "/opt/plantuml/plantuml.jar",
|
|
313
|
+
"javaPath": "/usr/bin/java",
|
|
314
|
+
"servers": ["https://internal.plantuml.example.com/plantuml"]
|
|
315
|
+
}
|
|
316
|
+
}
|
|
282
317
|
```
|
|
283
318
|
|
|
284
|
-
|
|
319
|
+
If `plantuml` is omitted entirely, only the community server is used.
|
|
285
320
|
|
|
286
321
|
---
|
|
287
322
|
|
|
288
|
-
##
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
├── dist/ # Compiled output (gitignored)
|
|
310
|
-
│ ├── server.js # Compiled server
|
|
311
|
-
│ └── client/ # Built React SPA
|
|
312
|
-
└── guides/ # Documentation
|
|
313
|
-
```
|
|
323
|
+
## Config Reference
|
|
324
|
+
|
|
325
|
+
| Field | Type | Default | Description |
|
|
326
|
+
|-------|------|---------|-------------|
|
|
327
|
+
| `port` | number | `1934` | Server port |
|
|
328
|
+
| `chromePath` | string | *required* | Path to Chrome/Chromium executable |
|
|
329
|
+
| `auth` | object | *required* | Authentication configuration |
|
|
330
|
+
| `scopes` | object | `{}` | Named scope definitions |
|
|
331
|
+
| `insiders` | object | `{}` | Email → insider entry map |
|
|
332
|
+
| `keys` | object | `{}` | Named key entries |
|
|
333
|
+
| `events` | object | `{}` | Event gateway schemas |
|
|
334
|
+
| `eventTimeoutMs` | number | `30000` | Default event command timeout |
|
|
335
|
+
| `eventLogPurgeMs` | number | `2592000000` | Event log retention (default: 30 days) |
|
|
336
|
+
| `maxZipSizeMb` | number | `100` | Max directory size for ZIP export |
|
|
337
|
+
| `roots` | object | — | Linux filesystem roots (ignored on Windows) |
|
|
338
|
+
| `watcherUrl` | string | — | jeeves-watcher API URL for semantic search |
|
|
339
|
+
| `runnerUrl` | string | — | jeeves-runner API URL for process dashboard |
|
|
340
|
+
| `plantuml` | object | — | PlantUML rendering config |
|
|
341
|
+
| `diagramCachePath` | string | `.diagram-cache` | Cached rendered diagram directory |
|
|
342
|
+
| `outsiderPolicy` | scopes | — | Global outsider sharing constraints |
|
|
343
|
+
| `mermaidCliPath` | string | — | **Deprecated.** Mermaid is now bundled. |
|
package/guides/sharing.md
CHANGED
|
@@ -10,11 +10,11 @@ An insider is an **authenticated user** on the server. Depending on their config
|
|
|
10
10
|
|
|
11
11
|
Anyone listed in the `insiders` map in your config:
|
|
12
12
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"alice@example.com": {},
|
|
16
|
+
"bob@example.com": { "scopes": ["/d/projects/*"] }
|
|
17
|
+
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
### How insiders authenticate
|
|
@@ -155,8 +155,8 @@ Use rotation when you need to revoke all shared links at once (e.g., a contracto
|
|
|
155
155
|
|
|
156
156
|
In addition to insider-generated keys, the server supports **named machine keys** for programmatic access:
|
|
157
157
|
|
|
158
|
-
```
|
|
159
|
-
keys: {
|
|
158
|
+
```json
|
|
159
|
+
"keys": {
|
|
160
160
|
primary: 'random-seed-string',
|
|
161
161
|
'webhook-notion': { key: 'another-seed', scopes: ['/event'] },
|
|
162
162
|
_internal: 'internal-seed',
|
package/package.json
CHANGED
package/src/routes/api/search.ts
CHANGED
|
@@ -61,6 +61,7 @@ interface GroupedResult {
|
|
|
61
61
|
title?: string;
|
|
62
62
|
author?: string;
|
|
63
63
|
participants?: string;
|
|
64
|
+
metadata: Record<string, unknown>;
|
|
64
65
|
chunks: Array<{
|
|
65
66
|
text: string;
|
|
66
67
|
index: number;
|
|
@@ -193,6 +194,25 @@ export const searchRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
193
194
|
let group = fileMap.get(key);
|
|
194
195
|
if (!group) {
|
|
195
196
|
const parts = key.split('/');
|
|
197
|
+
// Extract all non-internal payload fields as metadata
|
|
198
|
+
const meta: Record<string, unknown> = {};
|
|
199
|
+
const internalKeys = new Set([
|
|
200
|
+
'file_path',
|
|
201
|
+
'chunk_text',
|
|
202
|
+
'chunk_index',
|
|
203
|
+
'total_chunks',
|
|
204
|
+
'content_hash',
|
|
205
|
+
'embedded_at',
|
|
206
|
+
]);
|
|
207
|
+
for (const [k, v] of Object.entries(r.payload)) {
|
|
208
|
+
if (internalKeys.has(k) || v == null || v === '') continue;
|
|
209
|
+
// Normalize singular 'domain' to 'domains' array to match facet field name
|
|
210
|
+
if (k === 'domain') {
|
|
211
|
+
meta['domains'] = Array.isArray(v) ? v : [v];
|
|
212
|
+
} else {
|
|
213
|
+
meta[k] = v;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
196
216
|
group = {
|
|
197
217
|
filePath: r.payload.file_path ?? key,
|
|
198
218
|
browsePath: key,
|
|
@@ -206,6 +226,7 @@ export const searchRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
206
226
|
title: r.payload.title,
|
|
207
227
|
author: r.payload.author,
|
|
208
228
|
participants: r.payload.participants,
|
|
229
|
+
metadata: meta,
|
|
209
230
|
chunks: [],
|
|
210
231
|
};
|
|
211
232
|
fileMap.set(key, group);
|
|
@@ -296,7 +317,7 @@ export const searchRoutes: FastifyPluginAsync = async (fastify) => {
|
|
|
296
317
|
const watcherUrl = config.watcherUrl;
|
|
297
318
|
facetsFetchPromise = (async () => {
|
|
298
319
|
const watcherRes = await fetch(`${watcherUrl}/search/facets`, {
|
|
299
|
-
signal: AbortSignal.timeout(
|
|
320
|
+
signal: AbortSignal.timeout(15000),
|
|
300
321
|
});
|
|
301
322
|
if (!watcherRes.ok) {
|
|
302
323
|
throw new Error(`HTTP ${String(watcherRes.status)}`);
|
|
@@ -43,7 +43,7 @@ describe('GET /api/status', () => {
|
|
|
43
43
|
const handler = routes['/api/status'];
|
|
44
44
|
expect(handler).toBeDefined();
|
|
45
45
|
|
|
46
|
-
const result = await handler({ accessMode: 'insider' });
|
|
46
|
+
const result = await handler({ accessMode: 'insider', query: {} });
|
|
47
47
|
const status = result as Record<string, unknown>;
|
|
48
48
|
|
|
49
49
|
expect(status).toHaveProperty('version');
|