@nordbyte/nordrelay 0.5.1 → 0.6.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 +65 -11
- package/README.md +97 -23
- package/dist/access-control.js +1 -0
- package/dist/activity-events.js +44 -0
- package/dist/agent-updates.js +18 -2
- package/dist/audit-log.js +40 -2
- package/dist/bot-rendering.js +10 -7
- package/dist/bot.js +492 -7
- package/dist/channel-actions.js +7 -2
- package/dist/channel-adapter.js +34 -7
- package/dist/channel-command-service.js +156 -0
- package/dist/channel-turn-service.js +237 -0
- package/dist/codex-cli.js +1 -1
- package/dist/config-metadata.js +80 -13
- package/dist/config.js +77 -7
- package/dist/context-key.js +77 -5
- package/dist/discord-artifacts.js +165 -0
- package/dist/discord-bot.js +2014 -0
- package/dist/discord-channel-runtime.js +133 -0
- package/dist/discord-command-surface.js +119 -0
- package/dist/discord-rate-limit.js +141 -0
- package/dist/index.js +16 -5
- package/dist/job-store.js +127 -0
- package/dist/metrics.js +41 -0
- package/dist/operations.js +176 -119
- package/dist/relay-external-activity-monitor.js +47 -6
- package/dist/relay-runtime.js +1003 -268
- package/dist/runtime-cache.js +57 -0
- package/dist/session-locks.js +10 -7
- package/dist/state-backend.js +3 -0
- package/dist/support-bundle.js +18 -1
- package/dist/telegram-access-commands.js +15 -2
- package/dist/telegram-access-middleware.js +16 -3
- package/dist/telegram-agent-commands.js +25 -0
- package/dist/telegram-artifact-commands.js +46 -0
- package/dist/telegram-diagnostics-command.js +5 -50
- package/dist/telegram-general-commands.js +2 -6
- package/dist/telegram-operational-commands.js +14 -6
- package/dist/telegram-queue-commands.js +74 -4
- package/dist/telegram-support-command.js +7 -0
- package/dist/telegram-update-commands.js +27 -0
- package/dist/user-management.js +208 -0
- package/dist/web-api-contract.js +9 -0
- package/dist/web-dashboard-access-routes.js +74 -1
- package/dist/web-dashboard-artifact-routes.js +3 -3
- package/dist/web-dashboard-assets.js +2 -0
- package/dist/web-dashboard-pages.js +97 -13
- package/dist/web-dashboard-runtime-routes.js +53 -8
- package/dist/web-dashboard-session-routes.js +27 -20
- package/dist/web-dashboard-ui.js +1 -0
- package/dist/web-dashboard.js +149 -6
- package/dist/web-state.js +33 -2
- package/dist/webui-assets/dashboard.css +75 -1
- package/dist/webui-assets/dashboard.js +358 -47
- package/package.json +3 -1
- package/plugins/nordrelay/.codex-plugin/plugin.json +1 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +468 -22
package/.env.example
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# NordRelay runtime config example.
|
|
2
|
-
# Access is managed with NordRelay users, groups, linked
|
|
2
|
+
# Access is managed with NordRelay users, groups, linked chat identities, and enabled group/guild channels.
|
|
3
3
|
# Create the first admin with `nordrelay init` or `nordrelay user create-admin`.
|
|
4
4
|
|
|
5
5
|
# Telegram
|
|
6
|
-
#
|
|
6
|
+
# Telegram bot and transport settings.
|
|
7
|
+
# Start the Telegram bot adapter.
|
|
8
|
+
TELEGRAM_ENABLED=true
|
|
7
9
|
# BotFather token.
|
|
8
10
|
TELEGRAM_BOT_TOKEN=123456789:replace-me
|
|
9
11
|
# polling or webhook.
|
|
@@ -20,6 +22,40 @@ TELEGRAM_WEBHOOK_PATH=/telegram/webhook
|
|
|
20
22
|
# Optional Telegram webhook secret token.
|
|
21
23
|
TELEGRAM_WEBHOOK_SECRET=
|
|
22
24
|
|
|
25
|
+
# Discord
|
|
26
|
+
# Discord bot settings. Discord is opt-in and uses the same NordRelay users, groups, and permissions as Telegram.
|
|
27
|
+
# Start the Discord bot adapter.
|
|
28
|
+
DISCORD_ENABLED=false
|
|
29
|
+
# Discord bot token.
|
|
30
|
+
DISCORD_BOT_TOKEN=
|
|
31
|
+
# Discord application/client id used for slash command registration.
|
|
32
|
+
DISCORD_CLIENT_ID=
|
|
33
|
+
# Comma-separated guild ids for instant guild slash-command registration.
|
|
34
|
+
DISCORD_GUILD_IDS=
|
|
35
|
+
# Optional comma-separated guild allow-list.
|
|
36
|
+
DISCORD_ALLOWED_GUILD_IDS=
|
|
37
|
+
# Optional comma-separated channel allow-list before user/group checks.
|
|
38
|
+
DISCORD_ALLOWED_CHANNEL_IDS=
|
|
39
|
+
# Read regular Discord text messages as prompts. Requires enabling the privileged intent in Discord.
|
|
40
|
+
DISCORD_MESSAGE_CONTENT_ENABLED=true
|
|
41
|
+
# slash, message, or both.
|
|
42
|
+
# Options: slash, message, both
|
|
43
|
+
DISCORD_COMMAND_MODE=both
|
|
44
|
+
# Register Discord slash commands on startup when client id is configured.
|
|
45
|
+
DISCORD_AUTO_REGISTER_COMMANDS=true
|
|
46
|
+
# Optional Discord override for CLI mirror mode. Uses the NordRelay default when unset.
|
|
47
|
+
# Options: off, status, final, full
|
|
48
|
+
DISCORD_CLI_MIRROR_MODE=
|
|
49
|
+
# Optional Discord override for mirrored edit interval.
|
|
50
|
+
DISCORD_CLI_MIRROR_MIN_UPDATE_MS=
|
|
51
|
+
# Optional Discord override for completion notifications.
|
|
52
|
+
# Options: off, minimal, all
|
|
53
|
+
DISCORD_NOTIFY_MODE=
|
|
54
|
+
# Optional Discord quiet hours override. Use HH-HH, off, or leave blank for default.
|
|
55
|
+
DISCORD_QUIET_HOURS=
|
|
56
|
+
# Optional Discord override for automatic artifact summaries/uploads.
|
|
57
|
+
DISCORD_AUTO_SEND_ARTIFACTS=
|
|
58
|
+
|
|
23
59
|
# Agents
|
|
24
60
|
# Agent access. Codex is enabled by default; Pi, Hermes, OpenClaw, and Claude Code are opt-in.
|
|
25
61
|
# Allow Codex sessions.
|
|
@@ -161,15 +197,27 @@ ENABLE_TELEGRAM_REACTIONS=false
|
|
|
161
197
|
TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS=80
|
|
162
198
|
# Minimum edit interval.
|
|
163
199
|
TELEGRAM_EDIT_MIN_INTERVAL_MS=1200
|
|
164
|
-
# off, status, final, or full.
|
|
200
|
+
# Default mirror mode for chat adapters: off, status, final, or full.
|
|
201
|
+
# Options: off, status, final, full
|
|
202
|
+
NORDRELAY_CLI_MIRROR_MODE=status
|
|
203
|
+
# Default minimum mirrored edit interval.
|
|
204
|
+
NORDRELAY_CLI_MIRROR_MIN_UPDATE_MS=4000
|
|
205
|
+
# Default completion notifications: off, minimal, or all.
|
|
206
|
+
# Options: off, minimal, all
|
|
207
|
+
NORDRELAY_NOTIFY_MODE=minimal
|
|
208
|
+
# Default quiet hours. Use HH-HH, off, or leave blank.
|
|
209
|
+
NORDRELAY_QUIET_HOURS=
|
|
210
|
+
# Default automatic artifact summaries/uploads for chat adapters.
|
|
211
|
+
NORDRELAY_AUTO_SEND_ARTIFACTS=false
|
|
212
|
+
# Optional Telegram override for CLI mirror mode. Uses the NordRelay default when unset.
|
|
165
213
|
# Options: off, status, final, full
|
|
166
|
-
TELEGRAM_CLI_MIRROR_MODE=
|
|
167
|
-
#
|
|
168
|
-
TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS=
|
|
169
|
-
#
|
|
214
|
+
TELEGRAM_CLI_MIRROR_MODE=
|
|
215
|
+
# Optional Telegram override for mirrored edit interval.
|
|
216
|
+
TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS=
|
|
217
|
+
# Optional Telegram override for completion notifications.
|
|
170
218
|
# Options: off, minimal, all
|
|
171
|
-
TELEGRAM_NOTIFY_MODE=
|
|
172
|
-
# HH-HH or blank.
|
|
219
|
+
TELEGRAM_NOTIFY_MODE=
|
|
220
|
+
# Optional Telegram quiet hours override. Use HH-HH, off, or leave blank for default.
|
|
173
221
|
TELEGRAM_QUIET_HOURS=
|
|
174
222
|
# Additional comma-separated regex patterns.
|
|
175
223
|
TELEGRAM_REDACT_PATTERNS=
|
|
@@ -191,8 +239,8 @@ ARTIFACT_MAX_INBOX_DIRS=30
|
|
|
191
239
|
ARTIFACT_IGNORE_DIRS=
|
|
192
240
|
# Extra ignored glob patterns.
|
|
193
241
|
ARTIFACT_IGNORE_GLOBS=
|
|
194
|
-
#
|
|
195
|
-
TELEGRAM_AUTO_SEND_ARTIFACTS=
|
|
242
|
+
# Optional Telegram override for automatic artifact summaries/uploads.
|
|
243
|
+
TELEGRAM_AUTO_SEND_ARTIFACTS=
|
|
196
244
|
|
|
197
245
|
# Workspace
|
|
198
246
|
# State and workspace guardrails.
|
|
@@ -207,8 +255,14 @@ NORDRELAY_STATE_BACKEND=json
|
|
|
207
255
|
NORDRELAY_AUDIT_MAX_EVENTS=1000
|
|
208
256
|
# Write-lock TTL.
|
|
209
257
|
NORDRELAY_SESSION_LOCK_TTL_MS=1800000
|
|
258
|
+
# Stale-while-refresh TTL for expensive dashboard API snapshots.
|
|
259
|
+
NORDRELAY_DASHBOARD_CACHE_TTL_MS=10000
|
|
260
|
+
# Maximum persisted unified jobs retained for the WebUI jobs view.
|
|
261
|
+
NORDRELAY_UNIFIED_JOB_MAX_ITEMS=1000
|
|
210
262
|
# NPM version cache TTL.
|
|
211
263
|
NORDRELAY_VERSION_CACHE_TTL_MS=3600000
|
|
264
|
+
# Installed agent CLI version cache TTL.
|
|
265
|
+
NORDRELAY_CLI_VERSION_CACHE_TTL_MS=60000
|
|
212
266
|
|
|
213
267
|
# Voice
|
|
214
268
|
# Optional voice transcription settings.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# NordRelay
|
|
2
2
|
|
|
3
|
-
NordRelay is a remote control plane for coding agents across messaging channels. The current implementation connects Codex, Pi, Hermes, OpenClaw, and Claude Code coding-agent sessions to Telegram, keeps independent sessions per chat
|
|
3
|
+
NordRelay is a remote control plane for coding agents across messaging channels. The current implementation connects Codex, Pi, Hermes, OpenClaw, and Claude Code coding-agent sessions to Telegram and Discord, keeps independent sessions per chat, thread, forum topic, or DM, streams replies and tool activity back to the active channel, supports files, photos, voice input, model controls, session browsing, retry/abort, and CLI handback.
|
|
4
4
|
|
|
5
5
|
The repo is both a local Codex marketplace and a standalone Node app. The plugin lives in `plugins/nordrelay/`; the full bot runtime lives in `src/` and uses `@openai/codex-sdk` for Codex, Pi RPC mode for Pi, the Hermes API Server for Hermes, the OpenClaw Gateway WebSocket RPC surface for OpenClaw, and the Claude Agent SDK for Claude Code.
|
|
6
6
|
|
|
@@ -43,11 +43,12 @@ Session control:
|
|
|
43
43
|
|
|
44
44
|
Adapter architecture:
|
|
45
45
|
|
|
46
|
-
- Telegram
|
|
47
|
-
-
|
|
46
|
+
- Telegram supports text, typing, streaming edits, inline buttons, files, photos, voice, forum topics, and polling/webhook transport.
|
|
47
|
+
- Discord supports text, typing, streaming edits, buttons, files, photos, voice/audio transcription, guild channels, threads, DMs, message commands, and slash commands.
|
|
48
|
+
- `/channels` shows available and planned messaging adapters for Telegram, Discord, WhatsApp, Slack, and Matrix.
|
|
48
49
|
- Codex, Pi, Hermes, OpenClaw, and Claude Code are implemented as agent adapters.
|
|
49
50
|
- `/agents` shows available/planned agent adapters and whether Codex, Pi, Hermes, OpenClaw, and Claude Code are enabled.
|
|
50
|
-
- Shared command-action renderers and a channel runtime contract keep inbound commands, outbound messages, typing, files, inline actions, and streaming-ready delivery separate from
|
|
51
|
+
- Shared command-action renderers and a channel runtime contract keep inbound commands, outbound messages, typing, files, inline actions, and streaming-ready delivery separate from channel-specific API calls.
|
|
51
52
|
|
|
52
53
|
Codex runtime:
|
|
53
54
|
|
|
@@ -159,19 +160,33 @@ Telegram output:
|
|
|
159
160
|
- Old artifact and inbox turn directories are pruned automatically with configurable retention.
|
|
160
161
|
- Optional Telegram message reactions can acknowledge work start and completion with `ENABLE_TELEGRAM_REACTIONS=true`.
|
|
161
162
|
|
|
163
|
+
Discord input and output:
|
|
164
|
+
|
|
165
|
+
- Enable Discord with `DISCORD_ENABLED=true` and `DISCORD_BOT_TOKEN`.
|
|
166
|
+
- Set `DISCORD_CLIENT_ID` to let NordRelay register slash commands automatically.
|
|
167
|
+
- `DISCORD_COMMAND_MODE=both` supports slash commands and `/command` text messages. Set it to `slash` if the bot should not read message commands.
|
|
168
|
+
- `DISCORD_MESSAGE_CONTENT_ENABLED=true` lets regular Discord messages become prompts. The matching privileged intent must also be enabled in the Discord Developer Portal.
|
|
169
|
+
- Discord DMs, guild channels, and threads get independent NordRelay contexts.
|
|
170
|
+
- Discord attachments are staged like Telegram uploads; images are passed as image inputs and audio files are transcribed before prompting.
|
|
171
|
+
- Discord buttons cover session picks, model/reasoning/launch picks, queue actions, artifact actions, update jobs, and abort where Discord component limits allow.
|
|
172
|
+
- Discord slash commands mirror the Telegram command surface where Discord supports it: `/agent`, `/auth`, `/login`, `/logout`, `/session`, `/sessions`, `/new`, `/switch`, `/attach`, `/handback`, `/workspaces`, `/pin`, `/unpin`, `/pinned`, `/model`, `/reasoning`, `/fast`, `/launch`, `/launch_profiles`, `/queue`, `/stop`, `/retry`, `/sync`, `/progress`, `/activity`, `/audit`, `/artifacts`, `/logs`, `/version`, `/diagnostics`, `/restart`, `/update`, `/lock`, `/unlock`, `/mirror`, `/notify`, `/voice`, `/link`, `/whoami`, and `/register_channel`.
|
|
173
|
+
|
|
162
174
|
Authentication and safety:
|
|
163
175
|
|
|
164
176
|
- WebUI login is required for every dashboard page, API route, SSE stream, artifact download, and health endpoint.
|
|
165
|
-
- Access is managed through NordRelay users, groups, permissions, web sessions, and linked
|
|
166
|
-
- Built-in groups are `Admin`, `User`, and `Read Only`; custom groups can be created in the WebUI and can restrict allowed agents, workspace roots, and
|
|
177
|
+
- Access is managed through NordRelay users, groups, permissions, web sessions, linked Telegram identities, and linked Discord identities.
|
|
178
|
+
- Built-in groups are `Admin`, `User`, and `Read Only`; custom groups can be created in the WebUI and can restrict allowed agents, workspace roots, Telegram chats, and Discord channels.
|
|
167
179
|
- The last active admin cannot be disabled or demoted, and web sessions are revoked when passwords or group memberships change.
|
|
168
180
|
- Admins can review and revoke active WebUI sessions from the Users page.
|
|
169
181
|
- Telegram private chats require a linked active NordRelay user.
|
|
170
182
|
- Telegram group and forum chats must be registered before use; admins can run `/register_chat` in the chat or enable chats in the WebUI.
|
|
183
|
+
- Discord DMs require a linked active NordRelay user.
|
|
184
|
+
- Discord guild channels and threads must be registered before use; admins can run `/register_channel` in the channel or enable channels in the WebUI.
|
|
171
185
|
- `/whoami` shows the linked NordRelay account and groups.
|
|
172
186
|
- `/link <code>` links a Telegram account to a NordRelay user after a link code is created in the WebUI or with `nordrelay user link-code`.
|
|
173
|
-
-
|
|
174
|
-
-
|
|
187
|
+
- `/link <code>` also links a Discord account when the code was created as a Discord link code.
|
|
188
|
+
- WebUI login and chat-account link attempts are rate-limited to reduce brute-force risk.
|
|
189
|
+
- User, group, Telegram-link, Telegram-chat, Discord-link, Discord-channel, web-session, login, and permission-denied events are written to the audit log.
|
|
175
190
|
- `/auth` reports Codex authentication, Pi provider environment health, Hermes API Server reachability, OpenClaw Gateway reachability, or Claude Code CLI auth for the selected agent.
|
|
176
191
|
- `/login` starts Telegram-managed CLI auth for Codex, Hermes, or Claude Code when enabled.
|
|
177
192
|
- `/logout` signs out of CLI auth for Codex, Hermes, or Claude Code; Codex logout is disabled while `CODEX_API_KEY` is in use.
|
|
@@ -183,23 +198,29 @@ Authentication and safety:
|
|
|
183
198
|
Operations:
|
|
184
199
|
|
|
185
200
|
- Plugin command/skill starts, stops, restarts, and inspects the connector process.
|
|
186
|
-
- Manual process commands support `start`, `stop`, `restart`, `status`, and `foreground`.
|
|
201
|
+
- Manual process commands support `start`, `stop`, `restart`, `status`, `update`, and `foreground`.
|
|
187
202
|
- Telegram admin commands support `/logs`, `/diagnostics`, `/support`, `/restart`, and `/update` for NordRelay and agent CLIs.
|
|
188
|
-
- `/update
|
|
203
|
+
- `nordrelay update`, `/update`, and the WebUI update button detect 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 if the connector is running.
|
|
189
204
|
- `/update agents`, `/update <agent>`, `/update install <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage Codex, Pi, Hermes, OpenClaw, and Claude Code updater or installer jobs from Telegram.
|
|
190
205
|
- `/logs` renders redacted connector, NordRelay update, and agent update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
|
|
191
206
|
- Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
|
|
192
207
|
- Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
|
|
208
|
+
- Mirror, notification, quiet-hour, and automatic artifact-delivery defaults are configured through channel-neutral `NORDRELAY_*` settings, with Telegram and Discord override keys when a channel should differ.
|
|
209
|
+
- The WebUI Tasks page includes a unified Jobs view for active WebUI turns, external CLI turns, queued prompts, agent update/install jobs, self-updates, and diagnostics bundle exports, with log, cancel, and retry actions where supported.
|
|
210
|
+
- Unified Jobs are persisted across restarts and retain recent prompt, queue, update, connector-update, and support-bundle history for WebUI inspection.
|
|
211
|
+
- The WebUI Metrics page reports queue state, active/completed/failed turns, job counts, average prompt duration, and Telegram/Discord rate-limit counters.
|
|
212
|
+
- Expensive dashboard views such as version checks, adapter health, and diagnostics use a short stale-while-refresh server cache so the UI can render recent data while fresh checks run in the background.
|
|
193
213
|
- Context metadata, queues, and preferences are written atomically with backup recovery.
|
|
194
214
|
- Context metadata, queues, preferences, audit events, and locks can use JSON files or the optional SQLite state backend with `NORDRELAY_STATE_BACKEND=sqlite`.
|
|
195
215
|
- Runtime config, state, and logs are written under `~/.nordrelay/`.
|
|
196
216
|
- `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts the connector plus a full local WebUI dashboard.
|
|
217
|
+
- On first WebUI startup without an admin account, NordRelay shows a setup wizard for creating the first admin; remote setup requires the one-time token printed in the server console.
|
|
197
218
|
- The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
|
|
198
219
|
- The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
|
|
199
220
|
- The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, and redacted diagnostics bundle export.
|
|
200
221
|
- The dashboard can bind to `127.0.0.1` or `0.0.0.0`; user login and session cookies are mandatory in both modes.
|
|
201
222
|
- Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
|
|
202
|
-
- Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS` to keep `/version` responsive.
|
|
223
|
+
- Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS`, and installed agent CLI version checks are cached with `NORDRELAY_CLI_VERSION_CACHE_TTL_MS`, to keep `/version` and adapter health responsive.
|
|
203
224
|
- CI runs on Ubuntu, Windows, and macOS with typecheck, Vitest, Playwright WebUI browser tests, package dry run, npm audit, and a separate secret-scan workflow.
|
|
204
225
|
- `npm run dev`, `npm run build`, `npm run check`, `npm test`, `npm run test:e2e`, `npm start`, `npm stop`, and `npm run status` are available.
|
|
205
226
|
- Dockerfile and `docker-compose.yml` are included for containerized operation.
|
|
@@ -218,12 +239,16 @@ nordrelay start
|
|
|
218
239
|
```
|
|
219
240
|
|
|
220
241
|
npm is the fastest install path and is the recommended default for normal use. `nordrelay init` writes the private runtime config to `~/.nordrelay/nordrelay.env`.
|
|
242
|
+
If you start `nordrelay web` before creating an admin, the dashboard opens a first-run setup wizard. Remote setup requires the one-time setup token printed in the NordRelay console.
|
|
221
243
|
|
|
222
244
|
Non-interactive setup is also supported:
|
|
223
245
|
|
|
224
246
|
```bash
|
|
225
247
|
nordrelay init \
|
|
226
248
|
--token 123456789:replace-me \
|
|
249
|
+
--enable-discord \
|
|
250
|
+
--discord-token "discord-bot-token" \
|
|
251
|
+
--discord-client-id "discord-client-id" \
|
|
227
252
|
--admin-email you@example.com \
|
|
228
253
|
--admin-name "Your Name" \
|
|
229
254
|
--admin-password "replace-with-a-long-password" \
|
|
@@ -231,6 +256,7 @@ nordrelay init \
|
|
|
231
256
|
```
|
|
232
257
|
|
|
233
258
|
`--telegram-user-id` is optional, but linking the first admin during setup is the fastest way to use Telegram immediately.
|
|
259
|
+
Use `--discord-user-id <id>` with `nordrelay user create-admin` or `nordrelay user link-discord` to link Discord directly.
|
|
234
260
|
|
|
235
261
|
Source checkout setup:
|
|
236
262
|
|
|
@@ -253,10 +279,23 @@ Create the Telegram bot:
|
|
|
253
279
|
5. Create the first admin user with `nordrelay init` or `nordrelay user create-admin`.
|
|
254
280
|
6. Link Telegram from the WebUI, with `nordrelay user link-telegram`, or by creating a link code and sending `/link <code>` to the bot.
|
|
255
281
|
|
|
282
|
+
Create the Discord bot:
|
|
283
|
+
|
|
284
|
+
1. Open the Discord Developer Portal and create an application.
|
|
285
|
+
2. Add a bot user and copy the bot token into `DISCORD_BOT_TOKEN`.
|
|
286
|
+
3. Copy the application client ID into `DISCORD_CLIENT_ID`.
|
|
287
|
+
4. Enable the Message Content intent if you want regular Discord messages to become prompts.
|
|
288
|
+
5. Invite the bot with application-command, message-send, message-read, attachment, and thread permissions.
|
|
289
|
+
6. Link Discord from the WebUI, with `nordrelay user link-discord`, or by creating a Discord link code and sending `/link <code>` to the bot.
|
|
290
|
+
7. In guild channels, run `/register_channel` once from an admin-linked Discord account.
|
|
291
|
+
|
|
256
292
|
Minimal private-bot `~/.nordrelay/nordrelay.env`:
|
|
257
293
|
|
|
258
294
|
```dotenv
|
|
259
295
|
TELEGRAM_BOT_TOKEN=123456789:replace-me
|
|
296
|
+
DISCORD_ENABLED=false
|
|
297
|
+
DISCORD_BOT_TOKEN=
|
|
298
|
+
DISCORD_CLIENT_ID=
|
|
260
299
|
NORDRELAY_CODEX_ENABLED=true
|
|
261
300
|
NORDRELAY_PI_ENABLED=false
|
|
262
301
|
NORDRELAY_HERMES_ENABLED=false
|
|
@@ -267,14 +306,17 @@ CODEX_SANDBOX_MODE=workspace-write
|
|
|
267
306
|
CODEX_APPROVAL_POLICY=never
|
|
268
307
|
```
|
|
269
308
|
|
|
270
|
-
User and
|
|
309
|
+
User and chat access management:
|
|
271
310
|
|
|
272
311
|
- `nordrelay init` creates the first admin user and writes `~/.nordrelay/users.json`.
|
|
273
312
|
- `nordrelay user create-admin --email you@example.com --name "Your Name"` creates another admin.
|
|
274
313
|
- `nordrelay user create --email dev@example.com --name "Dev" --group user` creates a normal user.
|
|
275
314
|
- `nordrelay user link-telegram --email you@example.com --telegram-user-id 123456789` links a Telegram account directly.
|
|
276
|
-
- `nordrelay user link-
|
|
277
|
-
-
|
|
315
|
+
- `nordrelay user link-discord --email you@example.com --discord-user-id 123456789012345678` links a Discord account directly.
|
|
316
|
+
- `nordrelay user link-code --email you@example.com` creates a short-lived Telegram code that the user sends as `/link <code>` to the Telegram bot.
|
|
317
|
+
- `nordrelay user discord-link-code --email you@example.com` creates a short-lived Discord code that the user sends as `/link <code>` to the Discord bot.
|
|
318
|
+
- Telegram group chats are disabled until an admin enables them from the WebUI or runs `/register_chat` inside the group.
|
|
319
|
+
- Discord guild channels are disabled until an admin enables them from the WebUI or runs `/register_channel` inside the channel.
|
|
278
320
|
|
|
279
321
|
Codex authentication:
|
|
280
322
|
|
|
@@ -356,6 +398,7 @@ nordrelay init
|
|
|
356
398
|
nordrelay doctor
|
|
357
399
|
nordrelay start
|
|
358
400
|
nordrelay status
|
|
401
|
+
nordrelay update
|
|
359
402
|
nordrelay restart
|
|
360
403
|
nordrelay stop
|
|
361
404
|
nordrelay foreground
|
|
@@ -367,6 +410,7 @@ Source checkout process commands:
|
|
|
367
410
|
```bash
|
|
368
411
|
node plugins/nordrelay/scripts/nordrelay.mjs start
|
|
369
412
|
node plugins/nordrelay/scripts/nordrelay.mjs status
|
|
413
|
+
node plugins/nordrelay/scripts/nordrelay.mjs update
|
|
370
414
|
node plugins/nordrelay/scripts/nordrelay.mjs restart
|
|
371
415
|
node plugins/nordrelay/scripts/nordrelay.mjs stop
|
|
372
416
|
node plugins/nordrelay/scripts/nordrelay.mjs foreground
|
|
@@ -420,6 +464,7 @@ The dashboard is a second NordRelay client next to Telegram. It can:
|
|
|
420
464
|
- Control the active session model, reasoning/thinking, fast mode, and launch profile directly from the chat view.
|
|
421
465
|
- Abort turns, hand sessions back to the native CLI, and inspect the active session.
|
|
422
466
|
- Manage queued prompts with pause/resume, run, cancel, reorder buttons, and drag-and-drop prioritization.
|
|
467
|
+
- Inspect unified jobs across queued prompts, active turns, mirrored CLI work, agent updates, self-updates, and support-bundle exports.
|
|
423
468
|
- Browse, preview, download, ZIP, and delete artifacts.
|
|
424
469
|
- Inspect the activity timeline for WebUI and mirrored CLI turns.
|
|
425
470
|
- Edit all supported runtime settings from tabbed Settings groups with option selects, validation feedback, and restart actions.
|
|
@@ -522,6 +567,18 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
|
|
|
522
567
|
- `/update` updates through npm or git depending on the detected install type, then restarts only on success. Requires `updates.run`.
|
|
523
568
|
- `/update agents`, `/update <agent>`, `/update install <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage agent CLI update and install jobs. Requires `updates.run`.
|
|
524
569
|
|
|
570
|
+
## Discord Commands
|
|
571
|
+
|
|
572
|
+
Discord supports slash commands and `/command` text messages for the shared command set. The primary differences from Telegram are:
|
|
573
|
+
|
|
574
|
+
- `/register_channel` replaces `/register_chat` for guild channels and threads.
|
|
575
|
+
- `/prompt <text>` is available for slash-command-only deployments where regular message content is disabled.
|
|
576
|
+
- `/link <code>` consumes Discord link codes created in the WebUI or with `nordrelay user discord-link-code`.
|
|
577
|
+
- `/queue`, `/sessions`, `/agent`, `/model`, `/reasoning`, `/launch`, `/artifacts`, `/update`, and `/stop` use Discord buttons where component limits allow.
|
|
578
|
+
- `/artifacts latest`, `/artifacts zip latest`, `/artifacts images`, `/artifacts docs`, `/artifacts search <text>`, and `/artifacts delete <turn-id>` are available in Discord.
|
|
579
|
+
- Unsafe launch profiles require explicit confirmation with `/launch <profile-id> confirm`.
|
|
580
|
+
- Discord does not support Telegram reactions or Telegram webhook transport; typing, message edits, attachments, files, DMs, guild channels, and threads are supported.
|
|
581
|
+
|
|
525
582
|
## Command Examples
|
|
526
583
|
|
|
527
584
|
Switching to an existing thread:
|
|
@@ -702,6 +759,7 @@ Voice transcription uses `OPENAI_API_KEY`, not `CODEX_API_KEY`.
|
|
|
702
759
|
|
|
703
760
|
Telegram:
|
|
704
761
|
|
|
762
|
+
- `TELEGRAM_ENABLED`: starts the Telegram adapter. Defaults to `true`.
|
|
705
763
|
- `TELEGRAM_BOT_TOKEN`: required BotFather token.
|
|
706
764
|
- `TELEGRAM_RATE_LIMIT_MIN_INTERVAL_MS`: minimum interval for normal Telegram API sends. Defaults to `80`.
|
|
707
765
|
- `TELEGRAM_EDIT_MIN_INTERVAL_MS`: minimum interval for Telegram message edits. Defaults to `1200`.
|
|
@@ -711,19 +769,34 @@ Telegram:
|
|
|
711
769
|
- `TELEGRAM_WEBHOOK_PORT`: local bind port for webhook mode. Defaults to `8080`.
|
|
712
770
|
- `TELEGRAM_WEBHOOK_PATH`: webhook request path. Defaults to `/telegram/webhook`.
|
|
713
771
|
- `TELEGRAM_WEBHOOK_SECRET`: optional Telegram webhook secret token.
|
|
714
|
-
- `
|
|
715
|
-
- `
|
|
716
|
-
- `
|
|
717
|
-
- `
|
|
772
|
+
- `NORDRELAY_CLI_MIRROR_MODE`: default CLI mirror mode for chat adapters: `off`, `status`, `final`, or `full`. Defaults to `status`.
|
|
773
|
+
- `NORDRELAY_CLI_MIRROR_MIN_UPDATE_MS`: default minimum interval for mirrored CLI status edits. Defaults to `4000`.
|
|
774
|
+
- `NORDRELAY_NOTIFY_MODE`: default notification mode for chat adapters: `off`, `minimal`, or `all`. Defaults to `minimal`.
|
|
775
|
+
- `NORDRELAY_QUIET_HOURS`: optional default quiet-hour range in `HH-HH` format, for example `22-7`; use `off` in a channel override to disable inherited quiet hours.
|
|
776
|
+
- `NORDRELAY_AUTO_SEND_ARTIFACTS`: default automatic artifact summaries/uploads for chat adapters. Defaults to `false`.
|
|
777
|
+
- `TELEGRAM_CLI_MIRROR_MODE`, `TELEGRAM_CLI_MIRROR_MIN_UPDATE_MS`, `TELEGRAM_NOTIFY_MODE`, `TELEGRAM_QUIET_HOURS`, and `TELEGRAM_AUTO_SEND_ARTIFACTS`: optional Telegram-specific overrides.
|
|
718
778
|
- `TELEGRAM_REDACT_PATTERNS`: comma-separated regular expressions for additional Telegram/log redaction.
|
|
719
779
|
|
|
780
|
+
Discord:
|
|
781
|
+
|
|
782
|
+
- `DISCORD_ENABLED`: starts the Discord adapter. Defaults to `false`.
|
|
783
|
+
- `DISCORD_BOT_TOKEN`: required Discord bot token when Discord is enabled.
|
|
784
|
+
- `DISCORD_CLIENT_ID`: Discord application/client id used for slash-command registration.
|
|
785
|
+
- `DISCORD_GUILD_IDS`: optional comma-separated guild ids for instant guild slash-command registration.
|
|
786
|
+
- `DISCORD_ALLOWED_GUILD_IDS`: optional guild allow-list before user/group permissions are checked.
|
|
787
|
+
- `DISCORD_ALLOWED_CHANNEL_IDS`: optional channel allow-list before user/group permissions are checked.
|
|
788
|
+
- `DISCORD_MESSAGE_CONTENT_ENABLED`: reads regular Discord text messages as prompts. Defaults to `true`.
|
|
789
|
+
- `DISCORD_COMMAND_MODE`: `slash`, `message`, or `both`. Defaults to `both`.
|
|
790
|
+
- `DISCORD_AUTO_REGISTER_COMMANDS`: registers slash commands on startup when `DISCORD_CLIENT_ID` is set. Defaults to `true`.
|
|
791
|
+
- `DISCORD_CLI_MIRROR_MODE`, `DISCORD_CLI_MIRROR_MIN_UPDATE_MS`, `DISCORD_NOTIFY_MODE`, `DISCORD_QUIET_HOURS`, and `DISCORD_AUTO_SEND_ARTIFACTS`: optional Discord-specific overrides for the channel-neutral defaults.
|
|
792
|
+
|
|
720
793
|
User management:
|
|
721
794
|
|
|
722
|
-
- Users, groups, Telegram identities, Telegram group-chat access, and web sessions are stored in `~/.nordrelay/users.json`.
|
|
723
|
-
- Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, and `link-code`.
|
|
795
|
+
- Users, groups, Telegram identities, Telegram group-chat access, Discord identities, Discord channel access, and web sessions are stored in `~/.nordrelay/users.json`.
|
|
796
|
+
- Manage users in the WebUI Users page or with `nordrelay user list`, `create-admin`, `create`, `reset-password`, `link-telegram`, `link-discord`, `link-code`, and `discord-link-code`.
|
|
724
797
|
- Built-in groups are `admin`, `user`, and `readonly`.
|
|
725
798
|
- Group permissions include `inspect`, `sessions.read`, `sessions.write`, `prompt.send`, `prompt.abort`, `files.read`, `files.write`, `settings.read`, `settings.write`, `auth.manage`, `diagnostics.read`, `logs.read`, `logs.clear`, `queue.read`, `queue.write`, `updates.run`, `system.restart`, `users.read`, `users.write`, and `audit.read`.
|
|
726
|
-
- Custom groups can also restrict access to specific agent ids, workspace roots,
|
|
799
|
+
- Custom groups can also restrict access to specific agent ids, workspace roots, Telegram chat ids, and Discord channel ids.
|
|
727
800
|
|
|
728
801
|
Agent selection:
|
|
729
802
|
|
|
@@ -732,11 +805,12 @@ Agent selection:
|
|
|
732
805
|
- `NORDRELAY_HERMES_ENABLED`: enables Hermes contexts through the Hermes API Server. Defaults to `false`.
|
|
733
806
|
- `NORDRELAY_OPENCLAW_ENABLED`: enables OpenClaw contexts through the OpenClaw Gateway. Defaults to `false`.
|
|
734
807
|
- `NORDRELAY_CLAUDE_CODE_ENABLED`: enables Claude Code contexts through the Claude Agent SDK. Defaults to `false`.
|
|
735
|
-
- `NORDRELAY_DEFAULT_AGENT`: `codex`, `pi`, `hermes`, `openclaw`, or `claude-code`, used for new
|
|
808
|
+
- `NORDRELAY_DEFAULT_AGENT`: `codex`, `pi`, `hermes`, `openclaw`, or `claude-code`, used for new chat contexts. Defaults to the first enabled agent.
|
|
736
809
|
- `NORDRELAY_STATE_BACKEND`: `json` or `sqlite`. JSON is the default; SQLite requires `better-sqlite3`.
|
|
737
810
|
- `NORDRELAY_AUDIT_MAX_EVENTS`: maximum audit events retained. Defaults to `1000`.
|
|
738
811
|
- `NORDRELAY_SESSION_LOCK_TTL_MS`: session write-lock TTL. Defaults to `1800000`.
|
|
739
812
|
- `NORDRELAY_VERSION_CACHE_TTL_MS`: npm version freshness cache TTL. Defaults to `3600000`; set `0` to disable.
|
|
813
|
+
- `NORDRELAY_CLI_VERSION_CACHE_TTL_MS`: installed agent CLI version cache TTL. Defaults to `60000`; set `0` to disable.
|
|
740
814
|
|
|
741
815
|
Dashboard:
|
|
742
816
|
|
|
@@ -837,7 +911,7 @@ NordRelay wrapper:
|
|
|
837
911
|
|
|
838
912
|
- `NORDRELAY_HOME`: config/state/log directory override. Defaults to `~/.nordrelay`.
|
|
839
913
|
- `NORDRELAY_SOURCE_ROOT`: runtime source root override. Useful when the plugin is launched from Codex cache.
|
|
840
|
-
- `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.
|
|
914
|
+
- `NORDRELAY_UPDATE_METHOD`: optional `auto`, `npm`, or `git` self-update method override used by `nordrelay update`, `/update`, and the WebUI update button. Auto uses git when the runtime root has a `.git` directory and npm otherwise.
|
|
841
915
|
- Agent updates from the dashboard and Telegram use each agent's native updater where possible: `codex update`, `pi update pi`, `hermes update --yes`, `openclaw update --yes`, and `claude update`. Not-installed agents can be installed from the dashboard or with `/update install <agent>` using npm global installs.
|
|
842
916
|
- `NORDRELAY_KEEP_PENDING_UPDATES`: set true to avoid dropping pending Telegram updates on start.
|
|
843
917
|
- `NORDRELAY_FORWARD_TOOL_OUTPUT`: backward-compatible alias that sets `TOOL_VERBOSITY=all` when `TOOL_VERBOSITY` is unset.
|
package/dist/access-control.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function activityCategoryForType(type) {
|
|
2
|
+
if (/^(prompt|cli_turn|voice|upload|attachment)/.test(type))
|
|
3
|
+
return "prompt";
|
|
4
|
+
if (/^(session|agent_switch|handback|model_|reasoning_|fast_|launch_)/.test(type))
|
|
5
|
+
return "session";
|
|
6
|
+
if (/^queue_/.test(type))
|
|
7
|
+
return "queue";
|
|
8
|
+
if (/^agent_(install|update)/.test(type))
|
|
9
|
+
return "agent-update";
|
|
10
|
+
if (/^(artifact|artifacts)/.test(type))
|
|
11
|
+
return "artifact";
|
|
12
|
+
if (/^(auth|login|logout)/.test(type))
|
|
13
|
+
return "auth";
|
|
14
|
+
if (/^(user_|group_|telegram_chat_|telegram_link|discord_channel_|discord_link|permission_|access_|lock_)/.test(type))
|
|
15
|
+
return "security";
|
|
16
|
+
if (/^(tool_|cli_tool)/.test(type))
|
|
17
|
+
return "tool";
|
|
18
|
+
return "system";
|
|
19
|
+
}
|
|
20
|
+
export function activityActorLabel(actor) {
|
|
21
|
+
if (!actor) {
|
|
22
|
+
return "system";
|
|
23
|
+
}
|
|
24
|
+
return actor.label || actor.username || actor.id || actor.channelUserId || actor.channel;
|
|
25
|
+
}
|
|
26
|
+
export function auditCategoryForAction(action) {
|
|
27
|
+
if (/^prompt_/.test(action))
|
|
28
|
+
return "prompt";
|
|
29
|
+
if (/^queue_/.test(action))
|
|
30
|
+
return "queue";
|
|
31
|
+
if (/^lock_/.test(action))
|
|
32
|
+
return "security";
|
|
33
|
+
if (/^auth_/.test(action))
|
|
34
|
+
return "auth";
|
|
35
|
+
if (/^(permission_|user_|group_|telegram_|discord_)/.test(action))
|
|
36
|
+
return "security";
|
|
37
|
+
if (/^(artifact|file)/.test(action))
|
|
38
|
+
return "artifact";
|
|
39
|
+
if (/^(model_|reasoning_|fast_|launch_|session_|handback)/.test(action))
|
|
40
|
+
return "session";
|
|
41
|
+
if (/^(tool_)/.test(action))
|
|
42
|
+
return "tool";
|
|
43
|
+
return "system";
|
|
44
|
+
}
|
package/dist/agent-updates.js
CHANGED
|
@@ -105,10 +105,11 @@ export class AgentUpdateManager {
|
|
|
105
105
|
`Working directory: ${job.cwd}`,
|
|
106
106
|
"",
|
|
107
107
|
].join("\n"));
|
|
108
|
-
const
|
|
108
|
+
const useShell = process.platform === "win32";
|
|
109
|
+
const child = spawn(useShell ? formatShellCommand(plan.command, plan.args) : plan.command, useShell ? [] : plan.args, {
|
|
109
110
|
cwd: plan.cwd,
|
|
110
111
|
env: { ...process.env, ...(this.options.env ?? {}), ...(context.env ?? {}) },
|
|
111
|
-
shell:
|
|
112
|
+
shell: useShell,
|
|
112
113
|
windowsHide: true,
|
|
113
114
|
stdio: "pipe",
|
|
114
115
|
});
|
|
@@ -342,6 +343,21 @@ function looksLikePrompt(text) {
|
|
|
342
343
|
const tail = text.split(/\r?\n/).slice(-4).join("\n");
|
|
343
344
|
return /\b(y\/n|yes\/no|continue|proceed|confirm|password|passphrase|token|api key|enter|select)\b|[?>]\s*$/i.test(tail);
|
|
344
345
|
}
|
|
346
|
+
function formatShellCommand(command, args) {
|
|
347
|
+
return [command, ...args].map(quoteWindowsCmdArg).join(" ");
|
|
348
|
+
}
|
|
349
|
+
function quoteWindowsCmdArg(value) {
|
|
350
|
+
if (value.length === 0) {
|
|
351
|
+
return "\"\"";
|
|
352
|
+
}
|
|
353
|
+
if (!/[\s"&|<>()^%]/.test(value)) {
|
|
354
|
+
return value;
|
|
355
|
+
}
|
|
356
|
+
return `"${value
|
|
357
|
+
.replace(/%/g, "%%")
|
|
358
|
+
.replace(/(\\*)"/g, '$1$1\\"')
|
|
359
|
+
.replace(/(\\+)$/g, "$1$1")}"`;
|
|
360
|
+
}
|
|
345
361
|
function capitalize(value) {
|
|
346
362
|
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
347
363
|
}
|
package/dist/audit-log.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { auditCategoryForAction, } from "./activity-events.js";
|
|
2
3
|
import { createDocumentStore } from "./state-backend.js";
|
|
3
4
|
export class AuditLogStore {
|
|
4
5
|
store;
|
|
@@ -18,6 +19,7 @@ export class AuditLogStore {
|
|
|
18
19
|
id: randomUUID().replace(/-/g, "").slice(0, 12),
|
|
19
20
|
timestamp: new Date().toISOString(),
|
|
20
21
|
...event,
|
|
22
|
+
category: event.category ?? auditCategoryForAction(event.action),
|
|
21
23
|
channelId: event.channelId ?? "telegram",
|
|
22
24
|
};
|
|
23
25
|
payload.events.push(next);
|
|
@@ -27,8 +29,22 @@ export class AuditLogStore {
|
|
|
27
29
|
this.store.write(payload);
|
|
28
30
|
return next;
|
|
29
31
|
}
|
|
30
|
-
list(
|
|
31
|
-
|
|
32
|
+
list(options = 20) {
|
|
33
|
+
const resolved = typeof options === "number" ? { limit: options } : options;
|
|
34
|
+
const limit = Math.max(1, Math.min(500, resolved.limit ?? 20));
|
|
35
|
+
const since = normalizeSince(resolved.since);
|
|
36
|
+
return this.readPayload().events
|
|
37
|
+
.filter((event) => !resolved.channelId || resolved.channelId === "all" || event.channelId === resolved.channelId)
|
|
38
|
+
.filter((event) => !resolved.status || resolved.status === "all" || event.status === resolved.status)
|
|
39
|
+
.filter((event) => !resolved.action || resolved.action === "all" || event.action === resolved.action)
|
|
40
|
+
.filter((event) => !resolved.category || resolved.category === "all" || (event.category ?? auditCategoryForAction(event.action)) === resolved.category)
|
|
41
|
+
.filter((event) => !resolved.agentId || resolved.agentId === "all" || event.agentId === resolved.agentId)
|
|
42
|
+
.filter((event) => !resolved.threadId || event.threadId === resolved.threadId)
|
|
43
|
+
.filter((event) => !resolved.workspace || event.workspace === resolved.workspace)
|
|
44
|
+
.filter((event) => !resolved.actor || auditActorMatches(event, resolved.actor))
|
|
45
|
+
.filter((event) => !since || Date.parse(event.timestamp) >= since)
|
|
46
|
+
.slice(-limit)
|
|
47
|
+
.reverse();
|
|
32
48
|
}
|
|
33
49
|
readPayload() {
|
|
34
50
|
const payload = this.store.read();
|
|
@@ -41,6 +57,28 @@ export class AuditLogStore {
|
|
|
41
57
|
};
|
|
42
58
|
}
|
|
43
59
|
}
|
|
60
|
+
function normalizeSince(value) {
|
|
61
|
+
if (value === undefined || value === null || value === "") {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const time = typeof value === "number" ? value : Date.parse(value);
|
|
65
|
+
return Number.isFinite(time) ? time : null;
|
|
66
|
+
}
|
|
67
|
+
function auditActorMatches(event, query) {
|
|
68
|
+
const needle = query.trim().toLowerCase();
|
|
69
|
+
if (!needle) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return [
|
|
73
|
+
event.actor?.id,
|
|
74
|
+
event.actor?.label,
|
|
75
|
+
event.actor?.username,
|
|
76
|
+
event.actor?.channelUserId,
|
|
77
|
+
event.actor?.channel,
|
|
78
|
+
event.actorId,
|
|
79
|
+
event.actorRole,
|
|
80
|
+
].some((value) => String(value ?? "").toLowerCase().includes(needle));
|
|
81
|
+
}
|
|
44
82
|
function isAuditEvent(value) {
|
|
45
83
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
46
84
|
return false;
|
package/dist/bot-rendering.js
CHANGED
|
@@ -61,11 +61,12 @@ export function renderAuditEvents(events) {
|
|
|
61
61
|
}
|
|
62
62
|
const lines = events.map((event) => {
|
|
63
63
|
const time = formatLocalDateTime(new Date(event.timestamp));
|
|
64
|
-
const actor = event.actorId ? `user ${event.actorId}` : "system";
|
|
64
|
+
const actor = event.actor?.label || event.actor?.username || event.actor?.id || (event.actorId ? `user ${event.actorId}` : "system");
|
|
65
65
|
const prompt = event.promptId ? ` · ${event.promptId}` : "";
|
|
66
66
|
const detail = event.detail ? ` · ${trimLine(event.detail, 90)}` : "";
|
|
67
67
|
const description = event.description ? ` · ${trimLine(event.description, 90)}` : "";
|
|
68
|
-
|
|
68
|
+
const category = event.category ? ` · ${event.category}` : "";
|
|
69
|
+
return `${time} · ${event.status.toUpperCase()} · ${event.action}${category} · ${actor}${prompt}${description}${detail}`;
|
|
69
70
|
});
|
|
70
71
|
return {
|
|
71
72
|
plain: ["Audit:", ...lines].join("\n"),
|
|
@@ -95,7 +96,9 @@ export function formatLockOwner(lock) {
|
|
|
95
96
|
if (!lock) {
|
|
96
97
|
return "nobody";
|
|
97
98
|
}
|
|
98
|
-
|
|
99
|
+
const label = lock.ownerLabel || lock.ownerUserId;
|
|
100
|
+
const channel = lock.ownerChannel ? ` via ${lock.ownerChannel}` : "";
|
|
101
|
+
return `${label} (${lock.ownerUserId})${channel}`;
|
|
99
102
|
}
|
|
100
103
|
export function formatTelegramName(ctx) {
|
|
101
104
|
const firstName = ctx.from?.first_name?.trim();
|
|
@@ -420,13 +423,13 @@ export function renderDiagnosticsPlain(config, registry, health, authenticated,
|
|
|
420
423
|
`Tool verbosity: ${config.toolVerbosity}`,
|
|
421
424
|
`Telegram rate limit queued/running/retries/429: ${runtime.rateLimit.queued}/${runtime.rateLimit.running}/${runtime.rateLimit.retries}/${runtime.rateLimit.rateLimitHits}`,
|
|
422
425
|
`Telegram last retry_after: ${runtime.rateLimit.lastRetryAfterSeconds ?? "-"}s`,
|
|
423
|
-
`CLI mirror mode/update: ${runtime.mirrorMode} / ${config.telegramMirrorMinUpdateMs} ms`,
|
|
426
|
+
`CLI mirror mode/update: ${runtime.mirrorMode} / ${config.telegramMirrorMinUpdateMs} ms (default ${config.mirrorMode} / ${config.mirrorMinUpdateMs} ms)`,
|
|
424
427
|
`Notify/quiet: ${runtime.notifyMode} / ${runtime.quietHours}`,
|
|
425
428
|
`Voice: ${runtime.voiceBackend} / ${runtime.voiceLanguage} / transcribe-only ${runtime.voiceTranscribeOnly ? "on" : "off"}`,
|
|
426
429
|
`Sync interval: ${config.codexSyncIntervalMs} ms`,
|
|
427
430
|
`External busy check/stale: ${config.codexExternalBusyCheckMs} ms / ${config.codexExternalBusyStaleMs} ms`,
|
|
428
431
|
`External mirrors/timers/status messages: ${runtime.externalMirrors}/${runtime.externalQueueTimers}/${runtime.queueStatusMessages}`,
|
|
429
|
-
`Auto-send artifacts: ${config.telegramAutoSendArtifacts ? "yes" : "no"}`,
|
|
432
|
+
`Auto-send artifacts: ${config.telegramAutoSendArtifacts ? "yes" : "no"} (default ${config.autoSendArtifacts ? "yes" : "no"})`,
|
|
430
433
|
`Artifact ignore dirs/globs: ${config.artifactIgnoreDirs.length}/${config.artifactIgnoreGlobs.length}`,
|
|
431
434
|
`Artifact retention: ${config.artifactRetentionDays}d / ${config.artifactMaxTurnDirs} turns / ${config.artifactMaxInboxDirs} inbox dirs`,
|
|
432
435
|
`Workspace allowed/warn roots: ${config.workspaceAllowedRoots.length}/${config.workspaceWarnRoots.length}`,
|
|
@@ -465,13 +468,13 @@ export function renderDiagnosticsHTML(config, registry, health, authenticated, r
|
|
|
465
468
|
`<b>Tool verbosity:</b> <code>${escapeHTML(config.toolVerbosity)}</code>`,
|
|
466
469
|
`<b>Telegram rate limit queued/running/retries/429:</b> <code>${runtime.rateLimit.queued}/${runtime.rateLimit.running}/${runtime.rateLimit.retries}/${runtime.rateLimit.rateLimitHits}</code>`,
|
|
467
470
|
`<b>Telegram last retry_after:</b> <code>${escapeHTML(String(runtime.rateLimit.lastRetryAfterSeconds ?? "-"))}s</code>`,
|
|
468
|
-
`<b>CLI mirror mode/update:</b> <code>${escapeHTML(runtime.mirrorMode)} / ${config.telegramMirrorMinUpdateMs} ms</code>`,
|
|
471
|
+
`<b>CLI mirror mode/update:</b> <code>${escapeHTML(runtime.mirrorMode)} / ${config.telegramMirrorMinUpdateMs} ms (default ${config.mirrorMode} / ${config.mirrorMinUpdateMs} ms)</code>`,
|
|
469
472
|
`<b>Notify/quiet:</b> <code>${escapeHTML(runtime.notifyMode)} / ${escapeHTML(runtime.quietHours)}</code>`,
|
|
470
473
|
`<b>Voice:</b> <code>${escapeHTML(runtime.voiceBackend)} / ${escapeHTML(runtime.voiceLanguage)} / transcribe-only ${runtime.voiceTranscribeOnly ? "on" : "off"}</code>`,
|
|
471
474
|
`<b>Sync interval:</b> <code>${config.codexSyncIntervalMs} ms</code>`,
|
|
472
475
|
`<b>External busy check/stale:</b> <code>${config.codexExternalBusyCheckMs} ms / ${config.codexExternalBusyStaleMs} ms</code>`,
|
|
473
476
|
`<b>External mirrors/timers/status messages:</b> <code>${runtime.externalMirrors}/${runtime.externalQueueTimers}/${runtime.queueStatusMessages}</code>`,
|
|
474
|
-
`<b>Auto-send artifacts:</b> <code>${config.telegramAutoSendArtifacts ? "yes" : "no"}</code>`,
|
|
477
|
+
`<b>Auto-send artifacts:</b> <code>${config.telegramAutoSendArtifacts ? "yes" : "no"} (default ${config.autoSendArtifacts ? "yes" : "no"})</code>`,
|
|
475
478
|
`<b>Artifact ignore dirs/globs:</b> <code>${config.artifactIgnoreDirs.length}/${config.artifactIgnoreGlobs.length}</code>`,
|
|
476
479
|
`<b>Artifact retention:</b> <code>${config.artifactRetentionDays}d / ${config.artifactMaxTurnDirs} turns / ${config.artifactMaxInboxDirs} inbox dirs</code>`,
|
|
477
480
|
`<b>Workspace allowed/warn roots:</b> <code>${config.workspaceAllowedRoots.length}/${config.workspaceWarnRoots.length}</code>`,
|