@nordbyte/nordrelay 0.2.1 → 0.3.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/.env.example +22 -0
- package/CHANGELOG.md +17 -0
- package/README.md +130 -8
- package/dist/access-control.js +6 -0
- package/dist/agent-adapter.js +60 -0
- package/dist/audit-log.js +54 -0
- package/dist/bot-preferences.js +13 -9
- package/dist/bot-ui.js +6 -0
- package/dist/bot.js +521 -24
- package/dist/channel-adapter.js +58 -0
- package/dist/config.js +47 -0
- package/dist/index.js +47 -2
- package/dist/logger.js +24 -1
- package/dist/operations.js +339 -14
- package/dist/prompt-store.js +33 -11
- package/dist/relay-runtime.js +479 -0
- package/dist/session-locks.js +81 -0
- package/dist/session-registry.js +10 -6
- package/dist/settings-service.js +230 -0
- package/dist/state-backend.js +74 -0
- package/dist/web-dashboard.js +764 -0
- package/package.json +4 -1
- package/plugins/nordrelay/.codex-plugin/plugin.json +1 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +199 -2
package/.env.example
CHANGED
|
@@ -56,6 +56,12 @@ ENABLE_TELEGRAM_LOGIN=true
|
|
|
56
56
|
ENABLE_TELEGRAM_REACTIONS=false
|
|
57
57
|
TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS=80
|
|
58
58
|
TELEGRAM_EDIT_MIN_INTERVAL_MS=1200
|
|
59
|
+
TELEGRAM_TRANSPORT=polling
|
|
60
|
+
TELEGRAM_WEBHOOK_URL=
|
|
61
|
+
TELEGRAM_WEBHOOK_HOST=127.0.0.1
|
|
62
|
+
TELEGRAM_WEBHOOK_PORT=8080
|
|
63
|
+
TELEGRAM_WEBHOOK_PATH=/telegram/webhook
|
|
64
|
+
TELEGRAM_WEBHOOK_SECRET=
|
|
59
65
|
TELEGRAM_CLI_MIRROR_MODE=status
|
|
60
66
|
TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS=4000
|
|
61
67
|
TELEGRAM_NOTIFY_MODE=minimal
|
|
@@ -69,6 +75,22 @@ ARTIFACT_IGNORE_DIRS=
|
|
|
69
75
|
ARTIFACT_IGNORE_GLOBS=
|
|
70
76
|
TELEGRAM_AUTO_SEND_ARTIFACTS=false
|
|
71
77
|
|
|
78
|
+
# State and team controls. Use sqlite for a single-file state database when
|
|
79
|
+
# better-sqlite3 is available; json keeps separate human-readable files.
|
|
80
|
+
NORDRELAY_STATE_BACKEND=json
|
|
81
|
+
NORDRELAY_AUDIT_MAX_EVENTS=1000
|
|
82
|
+
NORDRELAY_SESSION_LOCK_TTL_MS=1800000
|
|
83
|
+
NORDRELAY_VERSION_CACHE_TTL_MS=3600000
|
|
84
|
+
|
|
85
|
+
# Local WebUI dashboard. Binding to 0.0.0.0 requires either token auth or
|
|
86
|
+
# basic auth; startup fails without one of these credentials.
|
|
87
|
+
NORDRELAY_DASHBOARD_HOST=127.0.0.1
|
|
88
|
+
NORDRELAY_DASHBOARD_PORT=31878
|
|
89
|
+
NORDRELAY_DASHBOARD_TOKEN=
|
|
90
|
+
NORDRELAY_DASHBOARD_USER=
|
|
91
|
+
NORDRELAY_DASHBOARD_PASSWORD=
|
|
92
|
+
NORDRELAY_ENV_FILE=
|
|
93
|
+
|
|
72
94
|
# Optional workspace guardrails. Leave WORKSPACE_ALLOWED_ROOTS empty to allow
|
|
73
95
|
# all workspaces discovered from enabled agent state.
|
|
74
96
|
WORKSPACE_ALLOWED_ROOTS=
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.3.0 - 2026-05-12
|
|
4
|
+
|
|
5
|
+
Commits since `v0.2.1`:
|
|
6
|
+
|
|
7
|
+
- Prepare v0.3.0 release with copyable WebUI thread IDs and aligned session controls.
|
|
8
|
+
- Improve dashboard usability and uploads.
|
|
9
|
+
- Expand WebUI dashboard.
|
|
10
|
+
- Keep dashboard server running.
|
|
11
|
+
- Expand NordRelay platform features.
|
|
12
|
+
- Hide missing timestamp marker in logs.
|
|
13
|
+
- Show CLI paths directly in version output.
|
|
14
|
+
- Add version freshness checks.
|
|
15
|
+
- Render log messages as normal text.
|
|
16
|
+
- Improve version and log display.
|
|
17
|
+
- Improve logs and self-update behavior.
|
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Session control:
|
|
|
24
24
|
- `/handback` returns a ready-to-run CLI command for continuing in the native agent CLI.
|
|
25
25
|
- `/retry` resends the last prompt for the current Telegram context.
|
|
26
26
|
- `/queue`, inline run/top/up/down/cancel buttons, `/cancel <queue-id>`, and `/clearqueue` manage queued prompts for a busy Telegram context.
|
|
27
|
+
- `/queue later <minutes> <prompt>` schedules a prompt for later execution, and `/queue inspect <queue-id>` shows full queue metadata.
|
|
27
28
|
- `/abort`, `/stop`, and the inline Abort button cancel the active agent turn.
|
|
28
29
|
- Busy prompts are queued per Telegram context instead of being dropped.
|
|
29
30
|
- If the attached thread is currently active in the local Codex CLI, Telegram prompts are queued until that CLI task finishes.
|
|
@@ -36,6 +37,15 @@ Session control:
|
|
|
36
37
|
- `/tasks` and `/progress` show the current turn status, queue length, active tool, elapsed time, and last error.
|
|
37
38
|
- `/activity` shows a compact timeline of recent rollout events for the active thread, with filters and export.
|
|
38
39
|
- `/diagnostics` reports redacted runtime, config, role, Telegram rate-limit, mirror, voice, session, queue, and progress details for admins.
|
|
40
|
+
- `/lock`, `/unlock`, and `/locks` provide a team write-lock for shared sessions so one user can operate while others watch.
|
|
41
|
+
- `/audit` shows recent prompt, queue, lock, and command audit events for admins.
|
|
42
|
+
|
|
43
|
+
Adapter architecture:
|
|
44
|
+
|
|
45
|
+
- Telegram is implemented as the first channel adapter with text, typing, streaming edits, inline buttons, files, photos, voice, topics, and webhook capability metadata.
|
|
46
|
+
- `/channels` shows available and planned messaging adapters for Discord, WhatsApp, Slack, and Matrix.
|
|
47
|
+
- Codex and Pi are implemented as agent adapters, with descriptors for future Claude Code, OpenClaw, and Hermes support.
|
|
48
|
+
- `/agents` shows available/planned agent adapters and whether Codex/Pi are enabled.
|
|
39
49
|
|
|
40
50
|
Codex runtime:
|
|
41
51
|
|
|
@@ -95,6 +105,7 @@ Telegram output:
|
|
|
95
105
|
- Image artifacts are sent with Telegram previews; large multi-file outputs are bundled into one ZIP when possible.
|
|
96
106
|
- `/artifacts` lists recent generated files and can resend the latest or a specific artifact turn.
|
|
97
107
|
- `/artifacts` includes inline actions to resend, ZIP, or delete artifact turns.
|
|
108
|
+
- `/artifacts images`, `/artifacts docs`, `/artifacts search <text>`, and `/artifacts delete <turn-id>` filter, find, and clean up artifacts from Telegram.
|
|
98
109
|
- Old artifact and inbox turn directories are pruned automatically with configurable retention.
|
|
99
110
|
- Optional Telegram message reactions can acknowledge work start and completion with `ENABLE_TELEGRAM_REACTIONS=true`.
|
|
100
111
|
|
|
@@ -120,24 +131,43 @@ Operations:
|
|
|
120
131
|
- Plugin command/skill starts, stops, restarts, and inspects the connector process.
|
|
121
132
|
- Manual process commands support `start`, `stop`, `restart`, `status`, and `foreground`.
|
|
122
133
|
- Telegram admin commands support `/logs`, `/diagnostics`, `/restart`, and `/update`.
|
|
123
|
-
- `/update`
|
|
124
|
-
-
|
|
134
|
+
- `/update` detects the install type: npm installs update with `npm install -g @nordbyte/nordrelay@latest`; source checkouts pull `origin/main`, install dependencies, run check, tests, and build, then restart.
|
|
135
|
+
- `/logs` renders redacted connector and update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
|
|
136
|
+
- Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
|
|
125
137
|
- Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
|
|
126
138
|
- Context metadata, queues, and preferences are written atomically with backup recovery.
|
|
139
|
+
- Context metadata, queues, preferences, audit events, and locks can use JSON files or the optional SQLite state backend with `NORDRELAY_STATE_BACKEND=sqlite`.
|
|
127
140
|
- Runtime state and logs are written under `~/.codex/nordrelay/`.
|
|
141
|
+
- `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts a full local WebUI dashboard.
|
|
142
|
+
- The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
|
|
143
|
+
- The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
|
|
144
|
+
- The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, and diagnostics.
|
|
145
|
+
- Binding the dashboard to `0.0.0.0` is refused unless `NORDRELAY_DASHBOARD_TOKEN` or `NORDRELAY_DASHBOARD_USER` plus `NORDRELAY_DASHBOARD_PASSWORD` is configured.
|
|
146
|
+
- Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
|
|
147
|
+
- Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS` to keep `/version` responsive.
|
|
148
|
+
- CI includes typecheck, tests, package dry run, npm audit, and a separate secret-scan workflow.
|
|
128
149
|
- `npm run dev`, `npm run build`, `npm run check`, `npm test`, `npm start`, `npm stop`, and `npm run status` are available.
|
|
129
150
|
- Dockerfile and `docker-compose.yml` are included for containerized operation.
|
|
130
151
|
- A `launchd/start.sh` helper is included for host-managed startup.
|
|
131
152
|
|
|
132
153
|
## First Run Setup
|
|
133
154
|
|
|
134
|
-
|
|
155
|
+
Recommended npm setup:
|
|
135
156
|
|
|
136
157
|
```bash
|
|
137
158
|
npm install -g @nordbyte/nordrelay
|
|
159
|
+
nordrelay init
|
|
160
|
+
nordrelay doctor
|
|
161
|
+
nordrelay start
|
|
138
162
|
```
|
|
139
163
|
|
|
140
|
-
For package installs, put runtime configuration in a directory-local `.env` before running `nordrelay`, or in `~/.codex/nordrelay/nordrelay.env`.
|
|
164
|
+
npm is the fastest install path and is the recommended default for normal use. For package installs, put runtime configuration in a directory-local `.env` before running `nordrelay`, or in `~/.codex/nordrelay/nordrelay.env`.
|
|
165
|
+
|
|
166
|
+
Non-interactive setup is also supported:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
nordrelay init --token 123456789:replace-me --admin-id 123456789
|
|
170
|
+
```
|
|
141
171
|
|
|
142
172
|
Source checkout setup:
|
|
143
173
|
|
|
@@ -219,11 +249,14 @@ The old unnamespaced `/remote` command is no longer required and current Codex T
|
|
|
219
249
|
Manual process commands:
|
|
220
250
|
|
|
221
251
|
```bash
|
|
252
|
+
nordrelay init
|
|
253
|
+
nordrelay doctor
|
|
222
254
|
nordrelay start
|
|
223
255
|
nordrelay status
|
|
224
256
|
nordrelay restart
|
|
225
257
|
nordrelay stop
|
|
226
258
|
nordrelay foreground
|
|
259
|
+
nordrelay web
|
|
227
260
|
```
|
|
228
261
|
|
|
229
262
|
Source checkout process commands:
|
|
@@ -234,6 +267,8 @@ node plugins/nordrelay/scripts/nordrelay.mjs status
|
|
|
234
267
|
node plugins/nordrelay/scripts/nordrelay.mjs restart
|
|
235
268
|
node plugins/nordrelay/scripts/nordrelay.mjs stop
|
|
236
269
|
node plugins/nordrelay/scripts/nordrelay.mjs foreground
|
|
270
|
+
node plugins/nordrelay/scripts/nordrelay.mjs doctor
|
|
271
|
+
node plugins/nordrelay/scripts/nordrelay.mjs web
|
|
237
272
|
```
|
|
238
273
|
|
|
239
274
|
NPM shortcuts:
|
|
@@ -251,11 +286,68 @@ Runtime files:
|
|
|
251
286
|
- State file: `~/.codex/nordrelay/state.json`
|
|
252
287
|
- Log file: `~/.codex/nordrelay/nordrelay.log`
|
|
253
288
|
- Home override: `NORDRELAY_HOME=/custom/path`
|
|
289
|
+
- Local dashboard: `nordrelay web --host 127.0.0.1 --port 31878`
|
|
290
|
+
|
|
291
|
+
## WebUI Dashboard
|
|
292
|
+
|
|
293
|
+
Start the local WebUI:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
nordrelay web
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Open:
|
|
300
|
+
|
|
301
|
+
```text
|
|
302
|
+
http://127.0.0.1:31878/
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The dashboard is a second NordRelay client next to Telegram. It can:
|
|
306
|
+
|
|
307
|
+
- Start a new Codex or Pi session.
|
|
308
|
+
- Switch or attach existing sessions.
|
|
309
|
+
- Send prompts and receive streamed text/tool/plan updates through Server-Sent Events.
|
|
310
|
+
- Upload images, documents, and audio files from the chat composer. Images are passed as image inputs, documents are staged for the agent, and audio is transcribed through the configured voice backend.
|
|
311
|
+
- Abort turns, hand sessions back to the native CLI, and inspect the active session.
|
|
312
|
+
- Manage queued prompts.
|
|
313
|
+
- Browse, download, ZIP, and delete artifacts.
|
|
314
|
+
- Edit all supported runtime settings from tabbed Settings groups.
|
|
315
|
+
- View logs, diagnostics, enabled channels, and agent adapters.
|
|
316
|
+
|
|
317
|
+
Dashboard API endpoints are served under `/api/*`. Streaming uses `GET /api/events`.
|
|
318
|
+
|
|
319
|
+
Dashboard auth:
|
|
320
|
+
|
|
321
|
+
```dotenv
|
|
322
|
+
NORDRELAY_DASHBOARD_HOST=127.0.0.1
|
|
323
|
+
NORDRELAY_DASHBOARD_PORT=31878
|
|
324
|
+
NORDRELAY_DASHBOARD_TOKEN=replace-with-random-token
|
|
325
|
+
# or:
|
|
326
|
+
NORDRELAY_DASHBOARD_USER=admin
|
|
327
|
+
NORDRELAY_DASHBOARD_PASSWORD=replace-with-random-password
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
When `NORDRELAY_DASHBOARD_HOST=0.0.0.0`, NordRelay refuses to start the dashboard unless token or basic auth is configured. Login cookies use `SameSite=Strict`, and every dashboard route, API endpoint, SSE stream, artifact download, and health endpoint requires the same auth.
|
|
331
|
+
|
|
332
|
+
Webhook mode:
|
|
333
|
+
|
|
334
|
+
```dotenv
|
|
335
|
+
TELEGRAM_TRANSPORT=webhook
|
|
336
|
+
TELEGRAM_WEBHOOK_URL=https://relay.example
|
|
337
|
+
TELEGRAM_WEBHOOK_HOST=127.0.0.1
|
|
338
|
+
TELEGRAM_WEBHOOK_PORT=8080
|
|
339
|
+
TELEGRAM_WEBHOOK_PATH=/telegram/webhook
|
|
340
|
+
TELEGRAM_WEBHOOK_SECRET=replace-with-random-secret
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Run NordRelay behind your reverse proxy so the public URL forwards to `http://127.0.0.1:8080/telegram/webhook`. `GET /healthz` returns a simple health check.
|
|
254
344
|
|
|
255
345
|
## Telegram Commands
|
|
256
346
|
|
|
257
347
|
- `/start` shows welcome text and the selected launch profile.
|
|
258
348
|
- `/help` shows the grouped command reference.
|
|
349
|
+
- `/channels` shows available and planned messaging adapters.
|
|
350
|
+
- `/agents` shows available and planned coding-agent adapters.
|
|
259
351
|
- `/agent` selects the active agent for this Telegram context.
|
|
260
352
|
- `/new` starts a new thread. If the selected agent knows multiple workspaces, Telegram shows a workspace picker.
|
|
261
353
|
- `/session` shows current thread details.
|
|
@@ -272,13 +364,19 @@ Runtime files:
|
|
|
272
364
|
- `/queue` shows queued prompts for this Telegram context with inline run/top/up/down/cancel buttons.
|
|
273
365
|
- `/queue pause` pauses automatic queued prompt execution.
|
|
274
366
|
- `/queue resume` resumes automatic queued prompt execution.
|
|
367
|
+
- `/queue later <minutes> <prompt>` schedules a prompt for later execution.
|
|
368
|
+
- `/queue inspect <queue-id>` shows one queued prompt with created time, schedule time, attempts, and last error.
|
|
275
369
|
- `/queue move <queue-id> top|up|down` changes queued prompt priority.
|
|
276
370
|
- `/queue run <queue-id>` resumes the queue and runs that prompt next when the session is idle.
|
|
277
371
|
- Queued prompt replies include a cancel button while the prompt is still waiting.
|
|
278
372
|
- `/cancel <queue-id>` removes one queued prompt; the queue id is the short code shown in messages such as `Queued prompt 332kmt`.
|
|
279
373
|
- `/clearqueue` clears queued prompts for this Telegram context.
|
|
280
374
|
- `/activity [all|tools|errors|user|agent|tasks] [limit] [since 1h] [export]` shows or exports rollout activity for the active thread.
|
|
281
|
-
- `/
|
|
375
|
+
- `/audit [limit]` shows recent audit events. Admin only.
|
|
376
|
+
- `/lock` locks writes for this Telegram session to the current user.
|
|
377
|
+
- `/unlock` releases the current session write lock.
|
|
378
|
+
- `/locks` lists active write locks.
|
|
379
|
+
- `/artifacts [latest|zip latest|turn-id|images|docs|search <text>|delete <turn-id>]` lists, filters, resends, zips, searches, or deletes generated artifacts for the current workspace.
|
|
282
380
|
- `/workspaces` lists workspaces known to the selected agent and allowed by the workspace policy.
|
|
283
381
|
- `/abort` cancels the current operation.
|
|
284
382
|
- `/stop` is an alias for `/abort`.
|
|
@@ -300,11 +398,13 @@ Runtime files:
|
|
|
300
398
|
- `/tasks` or `/progress` reports the current turn and queue progress.
|
|
301
399
|
- `/status` reports connector runtime status.
|
|
302
400
|
- `/health` reports runtime health, auth, PIDs, Codex CLI, Pi CLI, and state DB.
|
|
303
|
-
- `/version` reports connector, Codex CLI, and Pi CLI
|
|
304
|
-
- `/logs [lines]` shows a redacted connector log tail. Admin only.
|
|
401
|
+
- `/version` reports connector, Codex CLI, and Pi CLI paths plus installed/latest NordRelay, Codex, and Pi versions with status icons.
|
|
402
|
+
- `/logs [lines]` shows a redacted, timestamped connector log tail. Admin only.
|
|
403
|
+
- `/logs update [lines]` shows the self-update log. Admin only.
|
|
404
|
+
- `/logs all [lines]` shows connector and self-update logs together. Admin only.
|
|
305
405
|
- `/diagnostics` shows redacted connector diagnostics. Admin only.
|
|
306
406
|
- `/restart` restarts the connector process. Admin only.
|
|
307
|
-
- `/update`
|
|
407
|
+
- `/update` updates through npm or git depending on the detected install type, then restarts only on success. Admin only.
|
|
308
408
|
|
|
309
409
|
## Command Examples
|
|
310
410
|
|
|
@@ -422,6 +522,8 @@ Artifacts:
|
|
|
422
522
|
- When more than five artifacts are sent, the connector tries to send one ZIP bundle instead of many separate files.
|
|
423
523
|
- Use `/artifacts` to list recent artifact turns with inline Send/ZIP/Delete actions.
|
|
424
524
|
- Use `/artifacts latest`, `/artifacts zip latest`, or `/artifacts <turn-id>` from text commands.
|
|
525
|
+
- Use `/artifacts images`, `/artifacts docs`, or `/artifacts search <text>` to narrow large artifact histories.
|
|
526
|
+
- Use `/artifacts delete <turn-id>` to delete an artifact turn without opening the inline confirmation flow.
|
|
425
527
|
- Telegram file delivery is capped at the configured `MAX_FILE_SIZE` per artifact or ZIP bundle.
|
|
426
528
|
- Old turn and inbox directories are pruned automatically to keep workspace state compact.
|
|
427
529
|
|
|
@@ -475,6 +577,12 @@ Telegram:
|
|
|
475
577
|
- `TELEGRAM_ROLE_POLICIES_JSON`: optional JSON object mapping roles to permissions. Permissions are `inspect`, `sessions`, `prompt`, `files`, `settings`, `auth`, and `admin`.
|
|
476
578
|
- `TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS`: minimum interval for normal Telegram API sends. Defaults to `80`.
|
|
477
579
|
- `TELEGRAM_EDIT_MIN_INTERVAL_MS`: minimum interval for Telegram message edits. Defaults to `1200`.
|
|
580
|
+
- `TELEGRAM_TRANSPORT`: `polling` or `webhook`. Defaults to `polling`.
|
|
581
|
+
- `TELEGRAM_WEBHOOK_URL`: public base URL for webhook mode, for example `https://relay.example`.
|
|
582
|
+
- `TELEGRAM_WEBHOOK_HOST`: local bind host for webhook mode. Defaults to `127.0.0.1`.
|
|
583
|
+
- `TELEGRAM_WEBHOOK_PORT`: local bind port for webhook mode. Defaults to `8080`.
|
|
584
|
+
- `TELEGRAM_WEBHOOK_PATH`: webhook request path. Defaults to `/telegram/webhook`.
|
|
585
|
+
- `TELEGRAM_WEBHOOK_SECRET`: optional Telegram webhook secret token.
|
|
478
586
|
- `TELEGRAM_CLI_MIRROR_MODE`: default CLI mirror mode: `off`, `status`, `final`, or `full`. Defaults to `status`.
|
|
479
587
|
- `TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS`: minimum interval for mirrored CLI status edits. Defaults to `4000`.
|
|
480
588
|
- `TELEGRAM_NOTIFY_MODE`: default notification mode: `off`, `minimal`, or `all`. Defaults to `minimal`.
|
|
@@ -492,6 +600,19 @@ Agent selection:
|
|
|
492
600
|
- `NORDRELAY_CODEX_ENABLED`: enables Codex contexts. Defaults to `true`.
|
|
493
601
|
- `NORDRELAY_PI_ENABLED`: enables Pi contexts. Defaults to `false`.
|
|
494
602
|
- `NORDRELAY_DEFAULT_AGENT`: `codex` or `pi`, used for new Telegram contexts. Defaults to the first enabled agent.
|
|
603
|
+
- `NORDRELAY_STATE_BACKEND`: `json` or `sqlite`. JSON is the default; SQLite requires `better-sqlite3`.
|
|
604
|
+
- `NORDRELAY_AUDIT_MAX_EVENTS`: maximum audit events retained. Defaults to `1000`.
|
|
605
|
+
- `NORDRELAY_SESSION_LOCK_TTL_MS`: session write-lock TTL. Defaults to `1800000`.
|
|
606
|
+
- `NORDRELAY_VERSION_CACHE_TTL_MS`: npm version freshness cache TTL. Defaults to `3600000`; set `0` to disable.
|
|
607
|
+
|
|
608
|
+
Dashboard:
|
|
609
|
+
|
|
610
|
+
- `NORDRELAY_DASHBOARD_HOST`: dashboard bind host. Defaults to `127.0.0.1`.
|
|
611
|
+
- `NORDRELAY_DASHBOARD_PORT`: dashboard bind port. Defaults to `31878`.
|
|
612
|
+
- `NORDRELAY_DASHBOARD_TOKEN`: optional dashboard bearer/login token. Required when binding to `0.0.0.0` unless basic auth is configured.
|
|
613
|
+
- `NORDRELAY_DASHBOARD_USER`: optional dashboard basic-auth user.
|
|
614
|
+
- `NORDRELAY_DASHBOARD_PASSWORD`: optional dashboard basic-auth password. Required with `NORDRELAY_DASHBOARD_USER`.
|
|
615
|
+
- `NORDRELAY_ENV_FILE`: optional env file edited by the dashboard settings page. Defaults to `~/.codex/nordrelay/nordrelay.env` when present, otherwise `.env` in the runtime root.
|
|
495
616
|
|
|
496
617
|
Codex:
|
|
497
618
|
|
|
@@ -552,6 +673,7 @@ NordRelay wrapper:
|
|
|
552
673
|
|
|
553
674
|
- `NORDRELAY_HOME`: state/log directory override. Defaults to `~/.codex/nordrelay`.
|
|
554
675
|
- `NORDRELAY_SOURCE_ROOT`: runtime source root override. Useful when the plugin is launched from Codex cache.
|
|
676
|
+
- `NORDRELAY_UPDATE_METHOD`: optional `auto`, `npm`, or `git` self-update method override. Auto uses git when the runtime root has a `.git` directory and npm otherwise.
|
|
555
677
|
- `NORDRELAY_KEEP_PENDING_UPDATES`: set true to avoid dropping pending Telegram updates on start.
|
|
556
678
|
- `NORDRELAY_FORWARD_TOOL_OUTPUT`: backward-compatible alias that sets `TOOL_VERBOSITY=all` when `TOOL_VERBOSITY` is unset.
|
|
557
679
|
- `NORDRELAY_STATE_FILE`: internal state-file path passed by the wrapper.
|
package/dist/access-control.js
CHANGED
|
@@ -13,10 +13,13 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
13
13
|
["status", "inspect"],
|
|
14
14
|
["health", "inspect"],
|
|
15
15
|
["version", "inspect"],
|
|
16
|
+
["channels", "inspect"],
|
|
17
|
+
["agents", "inspect"],
|
|
16
18
|
["diagnostics", "admin"],
|
|
17
19
|
["tasks", "inspect"],
|
|
18
20
|
["progress", "inspect"],
|
|
19
21
|
["activity", "inspect"],
|
|
22
|
+
["audit", "admin"],
|
|
20
23
|
["mirror", "settings"],
|
|
21
24
|
["notify", "settings"],
|
|
22
25
|
["workspaces", "sessions"],
|
|
@@ -32,6 +35,9 @@ const COMMAND_PERMISSIONS = new Map([
|
|
|
32
35
|
["handback", "sessions"],
|
|
33
36
|
["new", "sessions"],
|
|
34
37
|
["sync", "sessions"],
|
|
38
|
+
["lock", "sessions"],
|
|
39
|
+
["unlock", "sessions"],
|
|
40
|
+
["locks", "sessions"],
|
|
35
41
|
["queue", "inspect"],
|
|
36
42
|
["cancel", "prompt"],
|
|
37
43
|
["clearqueue", "prompt"],
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CODEX_AGENT_CAPABILITIES, PI_AGENT_CAPABILITIES, } from "./agent.js";
|
|
2
|
+
export const BUILTIN_AGENT_ADAPTERS = [
|
|
3
|
+
{
|
|
4
|
+
id: "codex",
|
|
5
|
+
label: "Codex",
|
|
6
|
+
status: "available",
|
|
7
|
+
capabilities: CODEX_AGENT_CAPABILITIES,
|
|
8
|
+
envFlag: "NORDRELAY_CODEX_ENABLED",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "pi",
|
|
12
|
+
label: "Pi",
|
|
13
|
+
status: "available",
|
|
14
|
+
capabilities: PI_AGENT_CAPABILITIES,
|
|
15
|
+
envFlag: "NORDRELAY_PI_ENABLED",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "claude-code",
|
|
19
|
+
label: "Claude Code",
|
|
20
|
+
status: "planned",
|
|
21
|
+
capabilities: plannedCapabilities(),
|
|
22
|
+
notes: "Use this descriptor as the target contract for a future Claude Code session service.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "openclaw",
|
|
26
|
+
label: "OpenClaw",
|
|
27
|
+
status: "planned",
|
|
28
|
+
capabilities: plannedCapabilities(),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "hermes",
|
|
32
|
+
label: "Hermes",
|
|
33
|
+
status: "planned",
|
|
34
|
+
capabilities: plannedCapabilities(),
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
export function listAgentAdapterDescriptors() {
|
|
38
|
+
return BUILTIN_AGENT_ADAPTERS.map((descriptor) => ({
|
|
39
|
+
...descriptor,
|
|
40
|
+
capabilities: { ...descriptor.capabilities },
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
function plannedCapabilities() {
|
|
44
|
+
return {
|
|
45
|
+
launchProfiles: false,
|
|
46
|
+
fastMode: false,
|
|
47
|
+
externalActivity: false,
|
|
48
|
+
cliMirror: false,
|
|
49
|
+
activityLog: false,
|
|
50
|
+
auth: false,
|
|
51
|
+
login: false,
|
|
52
|
+
logout: false,
|
|
53
|
+
usageLimits: false,
|
|
54
|
+
workspaces: true,
|
|
55
|
+
attachments: true,
|
|
56
|
+
modelSelection: true,
|
|
57
|
+
reasoningSelection: true,
|
|
58
|
+
handback: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createDocumentStore } from "./state-backend.js";
|
|
3
|
+
export class AuditLogStore {
|
|
4
|
+
store;
|
|
5
|
+
maxEvents;
|
|
6
|
+
constructor(workspace, backend = "json", maxEvents = 1000) {
|
|
7
|
+
this.store = createDocumentStore({
|
|
8
|
+
workspace,
|
|
9
|
+
fileName: "audit.json",
|
|
10
|
+
sqliteKey: "audit",
|
|
11
|
+
backend,
|
|
12
|
+
});
|
|
13
|
+
this.maxEvents = maxEvents;
|
|
14
|
+
}
|
|
15
|
+
append(event) {
|
|
16
|
+
const payload = this.readPayload();
|
|
17
|
+
const next = {
|
|
18
|
+
id: randomUUID().replace(/-/g, "").slice(0, 12),
|
|
19
|
+
timestamp: new Date().toISOString(),
|
|
20
|
+
channelId: "telegram",
|
|
21
|
+
...event,
|
|
22
|
+
};
|
|
23
|
+
payload.events.push(next);
|
|
24
|
+
if (payload.events.length > this.maxEvents) {
|
|
25
|
+
payload.events.splice(0, payload.events.length - this.maxEvents);
|
|
26
|
+
}
|
|
27
|
+
this.store.write(payload);
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
list(limit = 20) {
|
|
31
|
+
return this.readPayload().events.slice(-Math.max(1, Math.min(200, limit))).reverse();
|
|
32
|
+
}
|
|
33
|
+
readPayload() {
|
|
34
|
+
const payload = this.store.read();
|
|
35
|
+
if (!payload || payload.version !== 1 || !Array.isArray(payload.events)) {
|
|
36
|
+
return { version: 1, events: [] };
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
version: 1,
|
|
40
|
+
events: payload.events.filter(isAuditEvent),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function isAuditEvent(value) {
|
|
45
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const candidate = value;
|
|
49
|
+
return typeof candidate.id === "string" &&
|
|
50
|
+
typeof candidate.timestamp === "string" &&
|
|
51
|
+
typeof candidate.action === "string" &&
|
|
52
|
+
typeof candidate.status === "string" &&
|
|
53
|
+
typeof candidate.contextKey === "string";
|
|
54
|
+
}
|
package/dist/bot-preferences.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { readJsonFileWithBackup, writeJsonFileAtomic } from "./persistence.js";
|
|
1
|
+
import { createDocumentStore } from "./state-backend.js";
|
|
3
2
|
export class BotPreferencesStore {
|
|
4
|
-
|
|
3
|
+
store;
|
|
5
4
|
contexts = new Map();
|
|
6
|
-
constructor(workspace) {
|
|
7
|
-
this.
|
|
5
|
+
constructor(workspace, backend = "json") {
|
|
6
|
+
this.store = createDocumentStore({
|
|
7
|
+
workspace,
|
|
8
|
+
fileName: "preferences.json",
|
|
9
|
+
sqliteKey: "preferences",
|
|
10
|
+
backend,
|
|
11
|
+
});
|
|
8
12
|
this.load();
|
|
9
13
|
}
|
|
10
14
|
get(contextKey) {
|
|
@@ -29,14 +33,14 @@ export class BotPreferencesStore {
|
|
|
29
33
|
version: 1,
|
|
30
34
|
contexts: Object.fromEntries(this.contexts.entries()),
|
|
31
35
|
};
|
|
32
|
-
|
|
36
|
+
this.store.write(payload);
|
|
33
37
|
}
|
|
34
38
|
load() {
|
|
35
|
-
const
|
|
36
|
-
if (!
|
|
39
|
+
const payload = this.store.read();
|
|
40
|
+
if (!payload?.contexts || typeof payload.contexts !== "object") {
|
|
37
41
|
return;
|
|
38
42
|
}
|
|
39
|
-
for (const [contextKey, rawPreferences] of Object.entries(
|
|
43
|
+
for (const [contextKey, rawPreferences] of Object.entries(payload.contexts)) {
|
|
40
44
|
const preferences = normalizePreferences(rawPreferences);
|
|
41
45
|
if (preferences) {
|
|
42
46
|
this.contexts.set(contextKey, preferences);
|
package/dist/bot-ui.js
CHANGED
|
@@ -51,12 +51,15 @@ export function renderHelpMessage() {
|
|
|
51
51
|
commands: [
|
|
52
52
|
["/start", "Welcome & status"],
|
|
53
53
|
["/help", "This reference"],
|
|
54
|
+
["/channels", "Messaging adapter status"],
|
|
55
|
+
["/agents", "Agent adapter status"],
|
|
54
56
|
["/voice", "Voice transcription status"],
|
|
55
57
|
["/status", "Connector runtime status"],
|
|
56
58
|
["/health", "Connector health report"],
|
|
57
59
|
["/version", "Connector version"],
|
|
58
60
|
["/tasks", "Current turn progress"],
|
|
59
61
|
["/activity", "Thread activity timeline"],
|
|
62
|
+
["/audit", "Recent audit events"],
|
|
60
63
|
],
|
|
61
64
|
},
|
|
62
65
|
{
|
|
@@ -64,6 +67,9 @@ export function renderHelpMessage() {
|
|
|
64
67
|
commands: [
|
|
65
68
|
["/logs", "Show connector log tail"],
|
|
66
69
|
["/diagnostics", "Connector diagnostics"],
|
|
70
|
+
["/lock", "Lock session writes to you"],
|
|
71
|
+
["/unlock", "Release session write lock"],
|
|
72
|
+
["/locks", "List active write locks"],
|
|
67
73
|
["/restart", "Restart connector"],
|
|
68
74
|
["/update", "Pull, build, and restart"],
|
|
69
75
|
],
|