@nordbyte/nordrelay 0.5.0 → 0.5.1

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.
Files changed (38) hide show
  1. package/README.md +16 -10
  2. package/dist/access-control.js +2 -0
  3. package/dist/agent-updates.js +43 -8
  4. package/dist/bot-ui.js +1 -0
  5. package/dist/bot.js +108 -1063
  6. package/dist/channel-actions.js +8 -8
  7. package/dist/operations.js +63 -9
  8. package/dist/relay-artifact-service.js +126 -0
  9. package/dist/relay-external-activity-monitor.js +216 -0
  10. package/dist/relay-queue-service.js +66 -0
  11. package/dist/relay-runtime-types.js +1 -0
  12. package/dist/relay-runtime.js +77 -359
  13. package/dist/support-bundle.js +205 -0
  14. package/dist/telegram-agent-commands.js +212 -0
  15. package/dist/telegram-artifact-commands.js +139 -0
  16. package/dist/telegram-command-menu.js +1 -0
  17. package/dist/telegram-command-types.js +1 -0
  18. package/dist/telegram-diagnostics-command.js +102 -0
  19. package/dist/telegram-general-commands.js +52 -0
  20. package/dist/telegram-operational-commands.js +153 -0
  21. package/dist/telegram-preference-commands.js +198 -0
  22. package/dist/telegram-queue-commands.js +278 -0
  23. package/dist/telegram-support-command.js +53 -0
  24. package/dist/telegram-update-commands.js +6 -1
  25. package/dist/web-api-contract.js +79 -31
  26. package/dist/web-api-types.js +1 -0
  27. package/dist/web-dashboard-access-routes.js +163 -0
  28. package/dist/web-dashboard-artifact-routes.js +65 -0
  29. package/dist/web-dashboard-assets.js +2 -0
  30. package/dist/web-dashboard-http.js +143 -0
  31. package/dist/web-dashboard-pages.js +257 -0
  32. package/dist/web-dashboard-runtime-routes.js +92 -0
  33. package/dist/web-dashboard-session-routes.js +209 -0
  34. package/dist/web-dashboard.js +43 -882
  35. package/dist/webui-assets/dashboard.css +74 -4
  36. package/dist/webui-assets/dashboard.js +163 -24
  37. package/dist/zip-writer.js +83 -0
  38. package/package.json +10 -4
package/README.md CHANGED
@@ -37,6 +37,7 @@ Session control:
37
37
  - `/tasks` and `/progress` show the current turn status, queue length, active tool, elapsed time, and last error.
38
38
  - `/activity` shows a compact timeline of recent rollout events for the active thread, with filters and export.
39
39
  - `/diagnostics` reports redacted runtime, config, user/group authorization, Telegram rate-limit, mirror, voice, session, queue, and progress details.
40
+ - `/support` exports a redacted diagnostics ZIP with config, health, versions, agent paths, recent logs, audit events, update jobs, state backend, and OS/Node/npm info.
40
41
  - `/lock`, `/unlock`, and `/locks` provide a team write-lock for shared sessions so one user can operate while others watch.
41
42
  - `/audit` shows recent prompt, queue, lock, command, authentication, permission-denied, user, group, Telegram-link, Telegram-chat, and web-session audit events for admins.
42
43
 
@@ -183,9 +184,9 @@ Operations:
183
184
 
184
185
  - Plugin command/skill starts, stops, restarts, and inspects the connector process.
185
186
  - Manual process commands support `start`, `stop`, `restart`, `status`, and `foreground`.
186
- - Telegram admin commands support `/logs`, `/diagnostics`, `/restart`, and `/update` for NordRelay and agent CLIs.
187
+ - Telegram admin commands support `/logs`, `/diagnostics`, `/support`, `/restart`, and `/update` for NordRelay and agent CLIs.
187
188
  - `/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.
188
- - `/update agents`, `/update <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage Codex, Pi, Hermes, OpenClaw, and Claude Code updater jobs from Telegram.
189
+ - `/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.
189
190
  - `/logs` renders redacted connector, NordRelay update, and agent update logs with local-time timestamps, levels, file path, last-modified time, and highlighted warnings/errors.
190
191
  - Logs can be emitted as timestamped plain text or JSON records with `CONNECTOR_LOG_FORMAT`.
191
192
  - Telegram sends/edits/documents are routed through a rate-limit queue that honors Telegram retry-after responses.
@@ -195,12 +196,12 @@ Operations:
195
196
  - `nordrelay init` creates a private runtime config, `nordrelay doctor` validates host prerequisites, and `nordrelay web` starts the connector plus a full local WebUI dashboard.
196
197
  - The WebUI has responsive header/sidebar/footer navigation, live chat streaming, session controls, queue/artifact/log/diagnostic views, and settings management.
197
198
  - The WebUI supports light and dark themes, tabbed settings groups, paginated session browsing, and chat uploads for images, documents, and audio transcription.
198
- - The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, and diagnostics.
199
+ - The WebUI exposes REST and SSE endpoints for chat streaming, sessions, settings, queue, artifacts, logs, health, diagnostics, and redacted diagnostics bundle export.
199
200
  - The dashboard can bind to `127.0.0.1` or `0.0.0.0`; user login and session cookies are mandatory in both modes.
200
201
  - Telegram can run with long polling or an HTTP webhook via `TELEGRAM_TRANSPORT=webhook`.
201
202
  - Version freshness checks are cached with `NORDRELAY_VERSION_CACHE_TTL_MS` to keep `/version` responsive.
202
- - CI includes typecheck, tests, package dry run, npm audit, and a separate secret-scan workflow.
203
- - `npm run dev`, `npm run build`, `npm run check`, `npm test`, `npm start`, `npm stop`, and `npm run status` are available.
203
+ - 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
+ - `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.
204
205
  - Dockerfile and `docker-compose.yml` are included for containerized operation.
205
206
  - A `launchd/start.sh` helper is included for host-managed startup.
206
207
 
@@ -424,7 +425,7 @@ The dashboard is a second NordRelay client next to Telegram. It can:
424
425
  - Edit all supported runtime settings from tabbed Settings groups with option selects, validation feedback, and restart actions.
425
426
  - View filtered connector/update/agent-update logs, structured diagnostics, enabled channels, and agent adapters.
426
427
  - Inspect a per-agent capability matrix showing model, reasoning, launch, fast mode, attachments, activity, usage, auth, login/logout, and handback support.
427
- - Check NordRelay and agent CLI versions, then start Codex, Pi, Hermes, OpenClaw, or Claude Code updates from outdated version rows with live output, cancel, delete-log, and stdin response controls for interactive updaters.
428
+ - Check NordRelay and agent CLI versions, then start Codex, Pi, Hermes, OpenClaw, or Claude Code updates from outdated rows or installs from not-installed rows with live output, cancel, delete-log, and stdin response controls.
428
429
  - Build dashboard CSS and client JavaScript from modular source assets through esbuild, then serve them as authenticated static assets instead of inline HTML.
429
430
 
430
431
  Dashboard API endpoints are served under `/api/*`. Streaming uses `GET /api/events`.
@@ -515,10 +516,11 @@ Run NordRelay behind your reverse proxy so the public URL forwards to `http://12
515
516
  - `/logs update [lines]` shows the self-update log. Requires `logs.read`.
516
517
  - `/logs agent [lines]` shows the aggregate agent updater log. Requires `logs.read`.
517
518
  - `/logs all [lines]` shows connector, self-update, and agent update logs together. Requires `logs.read`.
518
- - `/diagnostics` shows redacted connector diagnostics. Requires `logs.read`.
519
+ - `/diagnostics` shows redacted connector diagnostics. Requires `diagnostics.read`.
520
+ - `/support` exports a redacted diagnostics ZIP. Requires `diagnostics.read`.
519
521
  - `/restart` restarts the connector process. Requires `system.restart`.
520
522
  - `/update` updates through npm or git depending on the detected install type, then restarts only on success. Requires `updates.run`.
521
- - `/update agents`, `/update <agent>`, `/update jobs`, `/update log <id>`, `/update cancel <id>`, and `/update input <id> <text>` manage agent CLI update jobs. Requires `updates.run`.
523
+ - `/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`.
522
524
 
523
525
  ## Command Examples
524
526
 
@@ -836,7 +838,7 @@ NordRelay wrapper:
836
838
  - `NORDRELAY_HOME`: config/state/log directory override. Defaults to `~/.nordrelay`.
837
839
  - `NORDRELAY_SOURCE_ROOT`: runtime source root override. Useful when the plugin is launched from Codex cache.
838
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.
839
- - 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`.
841
+ - 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.
840
842
  - `NORDRELAY_KEEP_PENDING_UPDATES`: set true to avoid dropping pending Telegram updates on start.
841
843
  - `NORDRELAY_FORWARD_TOOL_OUTPUT`: backward-compatible alias that sets `TOOL_VERBOSITY=all` when `TOOL_VERBOSITY` is unset.
842
844
  - `NORDRELAY_STATE_FILE`: internal state-file path passed by the wrapper.
@@ -1041,9 +1043,13 @@ npm run build
1041
1043
  - `plugins/nordrelay/scripts/nordrelay.mjs`: process manager for `start`, `stop`, `restart`, `status`, and `foreground`.
1042
1044
  - `src/index.ts`: runtime entrypoint, config load, auth check, state-file writes, polling lifecycle, shutdown.
1043
1045
  - `src/bot.ts`: Telegram prompt/session runtime, streaming, file/photo/voice handling, artifacts, and error handling.
1044
- - `src/telegram-access-commands.ts`, `src/telegram-update-commands.ts`, and `src/telegram-command-menu.ts`: focused Telegram command groups for access linking, update jobs, and command menu registration.
1046
+ - `src/telegram-general-commands.ts`, `src/telegram-agent-commands.ts`, `src/telegram-preference-commands.ts`, `src/telegram-access-commands.ts`, `src/telegram-diagnostics-command.ts`, `src/telegram-update-commands.ts`, `src/telegram-support-command.ts`, and `src/telegram-command-menu.ts`: focused Telegram command groups for start/help/adapters, agent/auth controls, per-chat preferences, access linking, diagnostics/log/version commands, update jobs, diagnostics bundle export, and command menu registration.
1045
1047
  - `src/channel-adapter.ts`, `src/channel-runtime.ts`, and `src/channel-actions.ts`: channel descriptors, generic command routing, outbound delivery contracts, and channel-neutral command responses.
1046
1048
  - `src/config-metadata.ts`: shared setting metadata used by the WebUI settings page and generated `.env.example`.
1049
+ - `src/support-bundle.ts` and `src/zip-writer.ts`: redacted diagnostics bundle creation with a dependency-free ZIP writer.
1050
+ - `src/relay-queue-service.ts`, `src/relay-artifact-service.ts`, and `src/relay-external-activity-monitor.ts`: Web runtime queue operations, artifact preview/export/persistence, and external CLI activity mirroring.
1051
+ - `src/relay-runtime-types.ts`: shared Runtime/WebUI DTO types used by runtime, API contracts, and dashboard code.
1052
+ - `src/web-dashboard-http.ts`, `src/web-dashboard-pages.ts`, and `src/web-dashboard-runtime-routes.ts`: dashboard HTTP helpers, HTML shell rendering, and operational runtime API routes.
1047
1053
  - `src/webui/`: focused WebUI source assets for core runtime state/API helpers, overview rendering, live events, chat/session workflows, admin pages, and CSS sections.
1048
1054
  - `src/bot-preferences.ts`: per-context mirror, notification, quiet-hour, and voice preference persistence.
1049
1055
  - `src/telegram-rate-limit.ts`: centralized Telegram API send/edit/document rate limiting and retry-after tracking.
@@ -79,6 +79,8 @@ const COMMAND_PERMISSIONS = new Map([
79
79
  ["voice", "inspect"],
80
80
  ["whoami", "inspect"],
81
81
  ["diagnostics", "diagnostics.read"],
82
+ ["support", "diagnostics.read"],
83
+ ["diagnostics_bundle", "diagnostics.read"],
82
84
  ["logs", "logs.read"],
83
85
  ["audit", "audit.read"],
84
86
  ["restart", "system.restart"],
@@ -7,9 +7,16 @@ import { resolveClaudeCodeCli } from "./claude-code-cli.js";
7
7
  import { resolveCodexCli } from "./codex-cli.js";
8
8
  import { resolveHermesCli } from "./hermes-cli.js";
9
9
  import { resolveOpenClawCli } from "./openclaw-cli.js";
10
- import { getAgentUpdateLogPath, getConnectorHome } from "./operations.js";
10
+ import { getAgentUpdateLogPath, getConnectorHome, resolveNpmSpawnCommand } from "./operations.js";
11
11
  import { resolvePiCli } from "./pi-cli.js";
12
12
  import { redactText } from "./redaction.js";
13
+ const AGENT_INSTALL_PACKAGES = {
14
+ codex: "@openai/codex",
15
+ pi: "@earendil-works/pi-coding-agent",
16
+ hermes: "hermes-agent",
17
+ openclaw: "openclaw",
18
+ "claude-code": "@anthropic-ai/claude-code",
19
+ };
13
20
  export class AgentUpdateManager {
14
21
  options;
15
22
  jobs = new Map();
@@ -58,12 +65,12 @@ export class AgentUpdateManager {
58
65
  this.persistJobs();
59
66
  return snapshot;
60
67
  }
61
- start(agentId, context = {}) {
68
+ start(agentId, context = {}, operation = "update") {
62
69
  const running = [...this.jobs.values()].find((job) => job.agentId === agentId && job.status === "running");
63
70
  if (running) {
64
- throw new Error(`${agentLabel(agentId)} update is already running.`);
71
+ throw new Error(`${agentLabel(agentId)} update/install job is already running.`);
65
72
  }
66
- const plan = resolveAgentUpdatePlan(agentId, { ...context, env: context.env ?? this.options.env });
73
+ const plan = resolveAgentUpdatePlan(agentId, { ...context, env: context.env ?? this.options.env }, operation);
67
74
  const now = new Date().toISOString();
68
75
  const id = `${agentId.replace(/[^a-z0-9]/gi, "")}-${Date.now().toString(36)}`;
69
76
  const logPath = path.join(this.home, "updates", `${id}.log`);
@@ -72,6 +79,7 @@ export class AgentUpdateManager {
72
79
  id,
73
80
  agentId,
74
81
  agentLabel: plan.agentLabel,
82
+ operation,
75
83
  status: "running",
76
84
  method: plan.method,
77
85
  command: plan.command,
@@ -91,7 +99,7 @@ export class AgentUpdateManager {
91
99
  this.jobs.set(id, job);
92
100
  this.persistJobs();
93
101
  this.append(job, [
94
- `[${now}] Starting ${job.agentLabel} update`,
102
+ `[${now}] Starting ${job.agentLabel} ${job.operation}`,
95
103
  `Method: ${job.method}`,
96
104
  `Command: ${[job.command, ...job.args].join(" ")}`,
97
105
  `Working directory: ${job.cwd}`,
@@ -115,7 +123,7 @@ export class AgentUpdateManager {
115
123
  if (job.status !== "running") {
116
124
  return;
117
125
  }
118
- this.finish(job, code === 0 ? "completed" : "failed", code, signal, code === 0 ? undefined : `Update exited with code ${code ?? "unknown"}`);
126
+ this.finish(job, code === 0 ? "completed" : "failed", code, signal, code === 0 ? undefined : `${capitalize(job.operation)} exited with code ${code ?? "unknown"}`);
119
127
  });
120
128
  this.emit(job);
121
129
  return this.snapshot(job);
@@ -178,7 +186,7 @@ export class AgentUpdateManager {
178
186
  job.finishedAt = new Date().toISOString();
179
187
  job.updatedAt = job.finishedAt;
180
188
  job.child = undefined;
181
- this.append(job, `\n[${job.finishedAt}] ${job.agentLabel} update ${status}${error ? `: ${job.error}` : ""}\n`);
189
+ this.append(job, `\n[${job.finishedAt}] ${job.agentLabel} ${job.operation} ${status}${error ? `: ${job.error}` : ""}\n`);
182
190
  }
183
191
  emit(job) {
184
192
  this.options.onUpdate?.(this.snapshot(job));
@@ -205,6 +213,7 @@ export class AgentUpdateManager {
205
213
  }
206
214
  const job = {
207
215
  ...snapshot,
216
+ operation: snapshot.operation ?? "update",
208
217
  status: staleRunning ? "failed" : snapshot.status,
209
218
  canInput: false,
210
219
  needsInput: false,
@@ -254,7 +263,10 @@ function isProcessRunning(pid) {
254
263
  return false;
255
264
  }
256
265
  }
257
- export function resolveAgentUpdatePlan(agentId, context = {}) {
266
+ export function resolveAgentUpdatePlan(agentId, context = {}, operation = "update") {
267
+ if (operation === "install") {
268
+ return resolveAgentInstallPlan(agentId, context);
269
+ }
258
270
  const env = context.env ?? process.env;
259
271
  switch (agentId) {
260
272
  case "codex": {
@@ -294,10 +306,30 @@ export function resolveAgentUpdatePlan(agentId, context = {}) {
294
306
  }
295
307
  }
296
308
  }
309
+ function resolveAgentInstallPlan(agentId, context = {}) {
310
+ const env = context.env ?? process.env;
311
+ const npm = resolveNpmSpawnCommand(env);
312
+ if (!npm) {
313
+ throw new Error(`Cannot install ${agentLabel(agentId)} because npm was not found on PATH.`);
314
+ }
315
+ const packageName = AGENT_INSTALL_PACKAGES[agentId];
316
+ return {
317
+ agentId,
318
+ agentLabel: agentLabel(agentId),
319
+ operation: "install",
320
+ method: `npm install -g ${packageName}@latest`,
321
+ command: npm.command,
322
+ args: [...npm.argsPrefix, "install", "-g", `${packageName}@latest`],
323
+ cwd: env.HOME || os.homedir(),
324
+ summary: `Installs the latest ${agentLabel(agentId)} CLI with npm. Restart NordRelay after installation if the agent was previously unavailable.`,
325
+ interactive: true,
326
+ };
327
+ }
297
328
  function plan(agentId, method, command, args, summary, env) {
298
329
  return {
299
330
  agentId,
300
331
  agentLabel: agentLabel(agentId),
332
+ operation: "update",
301
333
  method,
302
334
  command,
303
335
  args,
@@ -310,3 +342,6 @@ function looksLikePrompt(text) {
310
342
  const tail = text.split(/\r?\n/).slice(-4).join("\n");
311
343
  return /\b(y\/n|yes\/no|continue|proceed|confirm|password|passphrase|token|api key|enter|select)\b|[?>]\s*$/i.test(tail);
312
344
  }
345
+ function capitalize(value) {
346
+ return value.charAt(0).toUpperCase() + value.slice(1);
347
+ }
package/dist/bot-ui.js CHANGED
@@ -67,6 +67,7 @@ export function renderHelpMessage() {
67
67
  commands: [
68
68
  ["/logs", "Show connector log tail"],
69
69
  ["/diagnostics", "Connector diagnostics"],
70
+ ["/support", "Export diagnostics bundle"],
70
71
  ["/lock", "Lock session writes to you"],
71
72
  ["/unlock", "Release session write lock"],
72
73
  ["/locks", "List active write locks"],