@nordbyte/nordrelay 0.5.2 → 0.7.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.
Files changed (71) hide show
  1. package/.env.example +80 -11
  2. package/README.md +154 -22
  3. package/dist/access-control.js +7 -1
  4. package/dist/activity-events.js +44 -0
  5. package/dist/audit-log.js +40 -2
  6. package/dist/bot-preferences.js +1 -0
  7. package/dist/bot-rendering.js +10 -7
  8. package/dist/bot.js +535 -11
  9. package/dist/channel-actions.js +7 -2
  10. package/dist/channel-adapter.js +40 -7
  11. package/dist/channel-command-catalog.js +88 -0
  12. package/dist/channel-command-service.js +369 -0
  13. package/dist/channel-mirror-registry.js +77 -0
  14. package/dist/channel-peer-prompt.js +95 -0
  15. package/dist/channel-runtime.js +12 -5
  16. package/dist/channel-turn-service.js +237 -0
  17. package/dist/codex-state.js +114 -78
  18. package/dist/config-metadata.js +93 -13
  19. package/dist/config.js +103 -8
  20. package/dist/context-key.js +87 -5
  21. package/dist/discord-artifacts.js +165 -0
  22. package/dist/discord-bot.js +2073 -0
  23. package/dist/discord-channel-runtime.js +133 -0
  24. package/dist/discord-command-surface.js +57 -0
  25. package/dist/discord-rate-limit.js +141 -0
  26. package/dist/index.js +36 -5
  27. package/dist/job-store.js +127 -0
  28. package/dist/metrics.js +87 -0
  29. package/dist/peer-auth.js +85 -0
  30. package/dist/peer-client.js +256 -0
  31. package/dist/peer-context.js +21 -0
  32. package/dist/peer-identity.js +127 -0
  33. package/dist/peer-runtime-service.js +636 -0
  34. package/dist/peer-server.js +220 -0
  35. package/dist/peer-store.js +294 -0
  36. package/dist/peer-types.js +52 -0
  37. package/dist/relay-external-activity-monitor.js +47 -6
  38. package/dist/relay-runtime-helpers.js +208 -0
  39. package/dist/relay-runtime.js +897 -394
  40. package/dist/remote-prompt.js +98 -0
  41. package/dist/runtime-cache.js +57 -0
  42. package/dist/session-locks.js +10 -7
  43. package/dist/support-bundle.js +1 -0
  44. package/dist/telegram-access-commands.js +15 -2
  45. package/dist/telegram-access-middleware.js +16 -3
  46. package/dist/telegram-agent-commands.js +25 -0
  47. package/dist/telegram-artifact-commands.js +46 -0
  48. package/dist/telegram-command-menu.js +3 -53
  49. package/dist/telegram-diagnostics-command.js +5 -50
  50. package/dist/telegram-general-commands.js +16 -6
  51. package/dist/telegram-operational-commands.js +14 -6
  52. package/dist/telegram-preference-commands.js +23 -127
  53. package/dist/telegram-queue-commands.js +74 -4
  54. package/dist/telegram-support-command.js +7 -0
  55. package/dist/telegram-update-commands.js +27 -0
  56. package/dist/user-management.js +208 -0
  57. package/dist/web-api-contract.js +17 -0
  58. package/dist/web-dashboard-access-routes.js +74 -1
  59. package/dist/web-dashboard-artifact-routes.js +3 -3
  60. package/dist/web-dashboard-assets.js +2 -0
  61. package/dist/web-dashboard-pages.js +109 -13
  62. package/dist/web-dashboard-peer-routes.js +204 -0
  63. package/dist/web-dashboard-runtime-routes.js +53 -8
  64. package/dist/web-dashboard-session-routes.js +27 -20
  65. package/dist/web-dashboard-ui.js +2 -0
  66. package/dist/web-dashboard.js +160 -6
  67. package/dist/web-state.js +33 -2
  68. package/dist/webui-assets/dashboard.css +75 -1
  69. package/dist/webui-assets/dashboard.js +779 -55
  70. package/package.json +5 -2
  71. package/plugins/nordrelay/scripts/nordrelay.mjs +578 -19
@@ -0,0 +1,208 @@
1
+ import path from "node:path";
2
+ export function cliHealthForAgent(agentId, health) {
3
+ if (agentId === "pi") {
4
+ return { path: health.piCliPath, label: health.piCli, version: health.piCliVersion };
5
+ }
6
+ if (agentId === "hermes") {
7
+ return { path: health.hermesCliPath, label: health.hermesCli, version: health.hermesCliVersion };
8
+ }
9
+ if (agentId === "openclaw") {
10
+ return { path: health.openClawCliPath, label: health.openClawCli, version: health.openClawCliVersion };
11
+ }
12
+ if (agentId === "claude-code") {
13
+ return { path: health.claudeCodeCliPath, label: health.claudeCodeCli, version: health.claudeCodeCliVersion };
14
+ }
15
+ return { path: health.codexCliPath, label: health.codexCli, version: health.codexCliVersion };
16
+ }
17
+ export function versionCheckForAgent(agentId, versions) {
18
+ if (agentId === "pi")
19
+ return versions.pi;
20
+ if (agentId === "hermes")
21
+ return versions.hermes;
22
+ if (agentId === "openclaw")
23
+ return versions.openclaw;
24
+ if (agentId === "claude-code")
25
+ return versions.claudeCode;
26
+ return versions.codex;
27
+ }
28
+ export function hostLoginCommand(info, config) {
29
+ if (info.agentId === "hermes") {
30
+ return `${config.hermesCliPath ?? "hermes"} login --no-browser`;
31
+ }
32
+ if (info.agentId === "claude-code") {
33
+ return `${config.claudeCodeCliPath ?? "claude"} auth login`;
34
+ }
35
+ if (info.agentId === "pi") {
36
+ return `${config.piCliPath ?? "pi"} auth login`;
37
+ }
38
+ if (info.agentId === "openclaw") {
39
+ return `${config.openClawCliPath ?? "openclaw"} login`;
40
+ }
41
+ return "codex login --device-auth";
42
+ }
43
+ export function hostLogoutCommand(info, config) {
44
+ if (info.agentId === "hermes") {
45
+ return `${config.hermesCliPath ?? "hermes"} logout`;
46
+ }
47
+ if (info.agentId === "claude-code") {
48
+ return `${config.claudeCodeCliPath ?? "claude"} auth logout`;
49
+ }
50
+ if (info.agentId === "pi") {
51
+ return `${config.piCliPath ?? "pi"} auth logout`;
52
+ }
53
+ if (info.agentId === "openclaw") {
54
+ return `${config.openClawCliPath ?? "openclaw"} logout`;
55
+ }
56
+ return "codex logout";
57
+ }
58
+ export function activeSessionPriority(session) {
59
+ if (session.status === "running") {
60
+ return 3;
61
+ }
62
+ return session.contextKey.startsWith("cli:") ? 1 : 2;
63
+ }
64
+ export function shouldRefreshActiveSessions(event) {
65
+ return event.type === "activity_update" ||
66
+ event.type === "queue_update" ||
67
+ event.type === "session_update" ||
68
+ event.type === "status" ||
69
+ event.type === "turn_start" ||
70
+ event.type === "text_delta" ||
71
+ event.type === "tool_start" ||
72
+ event.type === "tool_update" ||
73
+ event.type === "tool_end" ||
74
+ event.type === "todo_update" ||
75
+ event.type === "turn_complete" ||
76
+ event.type === "turn_error";
77
+ }
78
+ export function isPromptTerminalActivity(event) {
79
+ return event.status === "completed" ||
80
+ event.status === "failed" ||
81
+ event.status === "aborted" ||
82
+ event.type === "prompt_completed" ||
83
+ event.type === "prompt_failed" ||
84
+ event.type === "prompt_aborted";
85
+ }
86
+ export function taskToUnifiedJob(id, kind, title, task, options) {
87
+ return {
88
+ id,
89
+ kind,
90
+ title,
91
+ status: task.status,
92
+ source: task.source,
93
+ agentId: task.agentId,
94
+ agentLabel: task.agentLabel,
95
+ threadId: task.threadId,
96
+ workspace: task.workspace,
97
+ startedAt: task.startedAt,
98
+ updatedAt: task.updatedAt,
99
+ durationMs: task.durationMs,
100
+ summary: task.prompt || task.detail,
101
+ logTail: task.currentTool || task.lastTool ? `Current tool: ${task.currentTool ?? "-"}\nLast tool: ${task.lastTool ?? "-"}` : undefined,
102
+ ...options,
103
+ };
104
+ }
105
+ export function activityToUnifiedJob(event, kind, title, options) {
106
+ return {
107
+ id: `${kind}:${event.id}`,
108
+ kind,
109
+ title,
110
+ status: event.status,
111
+ source: event.source,
112
+ agentId: event.agentId,
113
+ threadId: event.threadId,
114
+ workspace: event.workspace,
115
+ owner: event.actor,
116
+ startedAt: event.timestamp,
117
+ updatedAt: event.timestamp,
118
+ finishedAt: event.timestamp,
119
+ durationMs: event.durationMs,
120
+ summary: event.prompt || event.detail,
121
+ logPath: event.detail,
122
+ logTail: event.detail,
123
+ ...options,
124
+ };
125
+ }
126
+ export function promptActivityToUnifiedJob(event) {
127
+ const status = event.status === "info" ? "completed" : event.status;
128
+ const sourceLabel = event.source === "web"
129
+ ? "WebUI"
130
+ : event.source === "telegram"
131
+ ? "Telegram"
132
+ : event.source === "discord"
133
+ ? "Discord"
134
+ : "CLI";
135
+ const promptKey = event.threadId ?? event.contextKey ?? event.id;
136
+ return {
137
+ id: `prompt:${event.source}:${promptKey}:${event.id}`,
138
+ kind: event.source === "cli" ? "external-turn" : "web-turn",
139
+ title: `${sourceLabel} prompt`,
140
+ status,
141
+ source: event.source,
142
+ agentId: event.agentId,
143
+ threadId: event.threadId,
144
+ workspace: event.workspace,
145
+ owner: event.actor,
146
+ startedAt: event.timestamp,
147
+ updatedAt: event.timestamp,
148
+ finishedAt: status === "running" || status === "queued" ? undefined : event.timestamp,
149
+ durationMs: event.durationMs,
150
+ summary: event.prompt || event.detail,
151
+ logTail: event.detail,
152
+ canCancel: status === "running" && event.source === "web",
153
+ canRetry: status !== "running",
154
+ canReadLog: Boolean(event.detail || event.prompt),
155
+ };
156
+ }
157
+ export function agentUpdateStatusToUnified(status) {
158
+ if (status === "cancelled")
159
+ return "aborted";
160
+ if (status === "running")
161
+ return "running";
162
+ if (status === "completed")
163
+ return "completed";
164
+ return "failed";
165
+ }
166
+ export function dedupeJobs(jobs) {
167
+ const seen = new Set();
168
+ return jobs.filter((job) => {
169
+ if (seen.has(job.id)) {
170
+ return false;
171
+ }
172
+ seen.add(job.id);
173
+ return true;
174
+ });
175
+ }
176
+ export function normalizeMimeType(value, name) {
177
+ const configured = value?.trim();
178
+ if (configured) {
179
+ return configured;
180
+ }
181
+ const extension = path.extname(name).toLowerCase();
182
+ if ([".jpg", ".jpeg"].includes(extension))
183
+ return "image/jpeg";
184
+ if (extension === ".png")
185
+ return "image/png";
186
+ if (extension === ".gif")
187
+ return "image/gif";
188
+ if (extension === ".webp")
189
+ return "image/webp";
190
+ if (extension === ".mp3")
191
+ return "audio/mpeg";
192
+ if (extension === ".wav")
193
+ return "audio/wav";
194
+ if (extension === ".ogg" || extension === ".oga")
195
+ return "audio/ogg";
196
+ if (extension === ".m4a")
197
+ return "audio/mp4";
198
+ if (extension === ".webm")
199
+ return "audio/webm";
200
+ return "application/octet-stream";
201
+ }
202
+ export function uploadFileDtos(files) {
203
+ return files.map((file) => ({
204
+ name: file.safeName,
205
+ mimeType: file.mimeType,
206
+ sizeBytes: file.sizeBytes,
207
+ }));
208
+ }