@nordbyte/nordrelay 0.8.0 → 0.8.2
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 +9 -0
- package/README.md +81 -1197
- package/dist/{access-control.js → access/access-control.js} +1 -1
- package/dist/{audit-log.js → access/audit-log.js} +2 -2
- package/dist/{session-locks.js → access/session-locks.js} +1 -1
- package/dist/{user-management.js → access/user-management.js} +1 -1
- package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
- package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
- package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
- package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
- package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
- package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
- package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
- package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
- package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
- package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
- package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
- package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
- package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
- package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
- package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
- package/dist/agents/shared/agent-auth-commands.js +30 -0
- package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
- package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
- package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
- package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
- package/dist/{discord-bot.js → channels/discord/discord-bot.js} +164 -424
- package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
- package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
- package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
- package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
- package/dist/channels/shared/channel-bridge-controller.js +69 -0
- package/dist/channels/shared/channel-cli-artifacts.js +51 -0
- package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
- package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
- package/dist/channels/shared/channel-external-monitor.js +52 -0
- package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
- package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
- package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +2 -2
- package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
- package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
- package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
- package/dist/{slack-bot.js → channels/slack/slack-bot.js} +159 -294
- package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
- package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
- package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
- package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
- package/dist/{bot.js → channels/telegram/bot.js} +178 -427
- package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
- package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
- package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
- package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
- package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
- package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
- package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
- package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
- package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
- package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
- package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
- package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
- package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
- package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
- package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
- package/dist/{config.js → core/config.js} +11 -3
- package/dist/index.js +27 -23
- package/dist/{peer-client.js → peers/peer-client.js} +57 -1
- package/dist/peers/peer-discovery-jobs.js +206 -0
- package/dist/peers/peer-discovery.js +223 -0
- package/dist/peers/peer-health-monitor.js +49 -0
- package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
- package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
- package/dist/{peer-server.js → peers/peer-server.js} +23 -6
- package/dist/{peer-store.js → peers/peer-store.js} +84 -11
- package/dist/{peer-types.js → peers/peer-types.js} +9 -0
- package/dist/peers/peer-web-proxy-contract.js +127 -0
- package/dist/{metrics.js → runtime/metrics.js} +5 -3
- package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
- package/dist/runtime/relay-auth-service.js +63 -0
- package/dist/runtime/relay-dashboard-service.js +139 -0
- package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +140 -53
- package/dist/runtime/relay-runtime-active-sessions.js +387 -0
- package/dist/runtime/relay-runtime-dashboard.js +201 -0
- package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +307 -0
- package/dist/runtime/relay-runtime-sessions.js +623 -0
- package/dist/runtime/relay-runtime-types.js +1 -0
- package/dist/runtime/relay-runtime-updates-jobs.js +360 -0
- package/dist/runtime/relay-runtime.js +451 -0
- package/dist/runtime/runtime-cache.js +117 -0
- package/dist/{session-registry.js → state/session-registry.js} +3 -3
- package/dist/{operations.js → support/operations.js} +7 -7
- package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
- package/dist/{web-api-contract.js → web/web-api-contract.js} +17 -3
- package/dist/web/web-api-types.js +1 -0
- package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +2 -2
- package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +24 -2
- package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
- package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +37 -10
- package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +102 -7
- package/dist/web/web-dashboard-security.js +14 -0
- package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +12 -1
- package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
- package/dist/web/web-performance.js +60 -0
- package/dist/web/web-rate-limit.js +19 -0
- package/dist/{web-state.js → web/web-state.js} +74 -5
- package/dist/webui-assets/dashboard.css +171 -10
- package/dist/webui-assets/dashboard.js +515 -48
- package/dist/webui-assets/favicon.ico +0 -0
- package/dist/webui-assets/favicon.png +0 -0
- package/dist/webui-assets/logo.png +0 -0
- package/package.json +4 -3
- package/plugins/nordrelay/scripts/nordrelay.mjs +17 -5
- package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
- package/dist/relay-runtime.js +0 -1916
- package/dist/runtime-cache.js +0 -57
- /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
- /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
- /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
- /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
- /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
- /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
- /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
- /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
- /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
- /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
- /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
- /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
- /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
- /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
- /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
- /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
- /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
- /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
- /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
- /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
- /package/dist/{agent.js → agents/shared/agent.js} +0 -0
- /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
- /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
- /package/dist/{voice.js → artifacts/voice.js} +0 -0
- /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
- /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
- /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
- /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
- /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
- /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
- /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
- /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
- /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
- /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
- /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
- /package/dist/{activity-events.js → core/activity-events.js} +0 -0
- /package/dist/{error-messages.js → core/error-messages.js} +0 -0
- /package/dist/{format.js → core/format.js} +0 -0
- /package/dist/{logger.js → core/logger.js} +0 -0
- /package/dist/{redaction.js → core/redaction.js} +0 -0
- /package/dist/{settings-service.js → core/settings-service.js} +0 -0
- /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
- /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
- /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
- /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
- /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
- /package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +0 -0
- /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
- /package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +0 -0
- /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
- /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
- /package/dist/{job-store.js → state/job-store.js} +0 -0
- /package/dist/{persistence.js → state/persistence.js} +0 -0
- /package/dist/{prompt-store.js → state/prompt-store.js} +0 -0
- /package/dist/{state-backend.js → state/state-backend.js} +0 -0
- /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
- /package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +0 -0
- /package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +0 -0
- /package/dist/{web-dashboard-ui.js → web/web-dashboard-ui.js} +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { ensureOutDir } from "../artifacts/artifacts.js";
|
|
3
|
+
import { buildFileInstructions, outboxPath, stageFile, } from "../artifacts/attachments.js";
|
|
4
|
+
import { CODEX_AGENT_CAPABILITIES, agentLabel, agentReasoningLabel, agentReasoningOptions, isAgentId, } from "../agents/shared/agent.js";
|
|
5
|
+
import { getExternalSnapshotForSession } from "../agents/shared/agent-activity.js";
|
|
6
|
+
import { listAgentAdapterDescriptors } from "../agents/shared/agent-adapter.js";
|
|
7
|
+
import { AgentUpdateManager } from "../agents/shared/agent-updates.js";
|
|
8
|
+
import { createAgentSessionService, enabledAgents } from "../agents/shared/agent-factory.js";
|
|
9
|
+
import { AuditLogStore } from "../access/audit-log.js";
|
|
10
|
+
import { BotPreferencesStore } from "../state/bot-preferences.js";
|
|
11
|
+
import { ChannelCommandService } from "../channels/shared/channel-command-service.js";
|
|
12
|
+
import { ChannelTurnService } from "../channels/shared/channel-turn-service.js";
|
|
13
|
+
import { activeSessionSourceForContextKey, ChannelMirrorRegistry } from "../channels/shared/channel-mirror-registry.js";
|
|
14
|
+
import { listThreads as listCodexThreads } from "../agents/codex/codex-state.js";
|
|
15
|
+
import { friendlyErrorText } from "../core/error-messages.js";
|
|
16
|
+
import { clearLogFile, getAgentUpdateLogPath, getConnectorHealth, getConnectorLogPath, getPackageVersion, getUpdateLogPath, getVersionChecks, readConnectorState, readFormattedLogTail, spawnConnectorRestart, spawnSelfUpdate } from "../support/operations.js";
|
|
17
|
+
import { PromptStore, toPromptEnvelope } from "../state/prompt-store.js";
|
|
18
|
+
import { UnifiedJobStore } from "../state/job-store.js";
|
|
19
|
+
import { buildRuntimeMetrics } from "./metrics.js";
|
|
20
|
+
import { RelayArtifactService } from "./relay-artifact-service.js";
|
|
21
|
+
import { RelayAuthService } from "./relay-auth-service.js";
|
|
22
|
+
import { RelayExternalActivityMonitor } from "./relay-external-activity-monitor.js";
|
|
23
|
+
import { RelayQueueService } from "./relay-queue-service.js";
|
|
24
|
+
import { RuntimeSnapshotCache } from "./runtime-cache.js";
|
|
25
|
+
import { activeSessionPriority, activityToUnifiedJob, agentUpdateStatusToUnified, dedupeJobs, hostLoginCommand, hostLogoutCommand, isPromptTerminalActivity, normalizeMimeType, promptActivityToUnifiedJob, shouldRefreshActiveSessions, taskToUnifiedJob, uploadFileDtos, } from "./relay-runtime-helpers.js";
|
|
26
|
+
import { RelayDashboardService } from "./relay-dashboard-service.js";
|
|
27
|
+
import { capabilitiesOf } from "../channels/shared/bot-rendering.js";
|
|
28
|
+
import { renderSessionInfoPlain, renderSessionUsageRows } from "../channels/shared/session-format.js";
|
|
29
|
+
import { SessionLockStore } from "../access/session-locks.js";
|
|
30
|
+
import { SessionRegistry } from "../state/session-registry.js";
|
|
31
|
+
import { createSupportBundle } from "../support/support-bundle.js";
|
|
32
|
+
import { transcribeAudio } from "../artifacts/voice.js";
|
|
33
|
+
import { WebActivityStore, WebChatStore, } from "../web/web-state.js";
|
|
34
|
+
import { evaluateWorkspacePolicy, filterAllowedWorkspaces } from "../core/workspace-policy.js";
|
|
35
|
+
export const WEB_CONTEXT_KEY = "web:dashboard";
|
|
36
|
+
const ACTIVE_CODEX_DISCOVERY_LIMIT = 200;
|
|
37
|
+
const ACTIVE_ACTIVITY_TTL_MS = 6 * 60 * 60 * 1000;
|
|
38
|
+
const MAX_WEB_SESSION_PAGE_SIZE = 50;
|
|
39
|
+
const MAX_CHAT_HISTORY = 250;
|
|
40
|
+
export function relayRuntimeSubscribe(runtime, callback) {
|
|
41
|
+
runtime.subscribers.add(callback);
|
|
42
|
+
void runtime.snapshot().then((data) => callback({ type: "snapshot", data })).catch(() => { });
|
|
43
|
+
void runtime.chatHistory().then((messages) => callback({ type: "chat_history", messages })).catch(() => { });
|
|
44
|
+
void runtime.activeSessions().then((active) => callback({ type: "active_sessions_update", active })).catch(() => { });
|
|
45
|
+
callback({ type: "activity_update", events: runtime.activity({ limit: 50 }) });
|
|
46
|
+
return () => runtime.subscribers.delete(callback);
|
|
47
|
+
}
|
|
48
|
+
export async function relayRuntimeSnapshot(runtime) {
|
|
49
|
+
const session = await runtime.getSession(true);
|
|
50
|
+
const info = runtime.publicInfo(session);
|
|
51
|
+
return {
|
|
52
|
+
session: info,
|
|
53
|
+
sessionText: renderSessionInfoPlain(info),
|
|
54
|
+
queue: runtime.queue(),
|
|
55
|
+
queuePaused: runtime.queuePaused(),
|
|
56
|
+
processing: session.isProcessing(),
|
|
57
|
+
enabledAgents: enabledAgents(runtime.config),
|
|
58
|
+
workspaces: filterAllowedWorkspaces(session.listWorkspaces(), runtime.config),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function relayRuntimeStatus(runtime) {
|
|
62
|
+
const cliOptions = runtime.cliPathOptions();
|
|
63
|
+
const [health, versionChecks, snapshot] = await Promise.all([
|
|
64
|
+
getConnectorHealth(cliOptions),
|
|
65
|
+
getVersionChecks(cliOptions),
|
|
66
|
+
runtime.snapshot(),
|
|
67
|
+
]);
|
|
68
|
+
return {
|
|
69
|
+
health,
|
|
70
|
+
versionChecks,
|
|
71
|
+
snapshot,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export async function relayRuntimeBootstrapStatus(runtime) {
|
|
75
|
+
return {
|
|
76
|
+
health: {
|
|
77
|
+
version: await getPackageVersion(),
|
|
78
|
+
state: await readConnectorState(),
|
|
79
|
+
},
|
|
80
|
+
snapshot: await runtime.snapshot(),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export async function relayRuntimeVersion(runtime) {
|
|
84
|
+
return runtime.dashboardService.version();
|
|
85
|
+
}
|
|
86
|
+
export async function relayRuntimeDiagnostics(runtime) {
|
|
87
|
+
return runtime.dashboardService.diagnostics();
|
|
88
|
+
}
|
|
89
|
+
export async function relayRuntimeAdapterHealth(runtime) {
|
|
90
|
+
return runtime.dashboardService.adapterHealth();
|
|
91
|
+
}
|
|
92
|
+
export function relayRuntimePermissions(runtime) {
|
|
93
|
+
return {
|
|
94
|
+
mode: "users",
|
|
95
|
+
message: "Access is managed by NordRelay users, groups, Telegram identities, Telegram chat access records, Discord identities, and Discord channel access records.",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export async function relayRuntimeMetrics(runtime) {
|
|
99
|
+
const [active, jobs] = await Promise.all([
|
|
100
|
+
runtime.activeSessions(),
|
|
101
|
+
runtime.jobs(),
|
|
102
|
+
]);
|
|
103
|
+
return buildRuntimeMetrics({
|
|
104
|
+
queueLength: runtime.queueService.length(),
|
|
105
|
+
queuePaused: runtime.queueService.isPaused(),
|
|
106
|
+
activeTurnCount: active.sessions.length,
|
|
107
|
+
jobs: jobs.jobs,
|
|
108
|
+
activity: runtime.activity({ limit: 500 }),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
export function relayRuntimeAudit(runtime, options = 50) {
|
|
112
|
+
return runtime.auditStore.list(options);
|
|
113
|
+
}
|
|
114
|
+
export async function relayRuntimeSupportBundle(runtime, actor) {
|
|
115
|
+
const bundle = await createSupportBundle({
|
|
116
|
+
config: runtime.config,
|
|
117
|
+
diagnostics: await runtime.diagnostics(),
|
|
118
|
+
adapterHealth: await runtime.adapterHealth(),
|
|
119
|
+
auditEvents: runtime.auditStore.list(100),
|
|
120
|
+
agentUpdateJobs: runtime.agentUpdates.list(),
|
|
121
|
+
source: "web",
|
|
122
|
+
});
|
|
123
|
+
runtime.appendActivity({
|
|
124
|
+
source: "web",
|
|
125
|
+
status: "info",
|
|
126
|
+
type: "diagnostics_bundle_exported",
|
|
127
|
+
threadId: null,
|
|
128
|
+
workspace: runtime.config.workspace,
|
|
129
|
+
actor,
|
|
130
|
+
detail: bundle.path,
|
|
131
|
+
});
|
|
132
|
+
runtime.appendAudit({
|
|
133
|
+
action: "command",
|
|
134
|
+
status: "ok",
|
|
135
|
+
contextKey: runtime.contextKey,
|
|
136
|
+
actor,
|
|
137
|
+
description: "export diagnostics bundle",
|
|
138
|
+
detail: bundle.path,
|
|
139
|
+
});
|
|
140
|
+
return bundle;
|
|
141
|
+
}
|
|
142
|
+
export async function relayRuntimeLogs(runtime, target = "connector", lines = 100) {
|
|
143
|
+
if (target === "update") {
|
|
144
|
+
return readFormattedLogTail(lines, getUpdateLogPath());
|
|
145
|
+
}
|
|
146
|
+
if (target === "agent-updates") {
|
|
147
|
+
return readFormattedLogTail(lines, getAgentUpdateLogPath());
|
|
148
|
+
}
|
|
149
|
+
return readFormattedLogTail(lines);
|
|
150
|
+
}
|
|
151
|
+
export function relayRuntimeClearLogs(runtime, target = "connector", actor) {
|
|
152
|
+
const result = clearLogFile(target === "update" ? getUpdateLogPath() : target === "agent-updates" ? getAgentUpdateLogPath() : getConnectorLogPath());
|
|
153
|
+
runtime.appendActivity({
|
|
154
|
+
source: "web",
|
|
155
|
+
status: "info",
|
|
156
|
+
type: "logs_cleared",
|
|
157
|
+
threadId: null,
|
|
158
|
+
workspace: runtime.config.workspace,
|
|
159
|
+
actor,
|
|
160
|
+
detail: `Cleared ${target} log.`,
|
|
161
|
+
});
|
|
162
|
+
runtime.appendAudit({
|
|
163
|
+
action: "command",
|
|
164
|
+
status: "ok",
|
|
165
|
+
contextKey: runtime.contextKey,
|
|
166
|
+
actor,
|
|
167
|
+
description: `clear ${target} log`,
|
|
168
|
+
detail: result.filePath,
|
|
169
|
+
});
|
|
170
|
+
return { ok: true, filePath: result.filePath, clearedAt: result.clearedAt.toISOString() };
|
|
171
|
+
}
|
|
172
|
+
export function relayRuntimeRestartConnector(runtime, actor) {
|
|
173
|
+
spawnConnectorRestart();
|
|
174
|
+
runtime.broadcastStatus("Restart requested. The dashboard may disconnect briefly.", "warn");
|
|
175
|
+
runtime.appendActivity({
|
|
176
|
+
source: "web",
|
|
177
|
+
status: "info",
|
|
178
|
+
type: "restart_requested",
|
|
179
|
+
threadId: null,
|
|
180
|
+
workspace: runtime.config.workspace,
|
|
181
|
+
actor,
|
|
182
|
+
detail: "Dashboard requested a connector restart.",
|
|
183
|
+
});
|
|
184
|
+
runtime.appendAudit({
|
|
185
|
+
action: "command",
|
|
186
|
+
status: "ok",
|
|
187
|
+
contextKey: runtime.contextKey,
|
|
188
|
+
actor,
|
|
189
|
+
description: "restart connector",
|
|
190
|
+
});
|
|
191
|
+
return { ok: true, message: "Restart requested." };
|
|
192
|
+
}
|
|
193
|
+
export function relayRuntimeDispose(runtime) {
|
|
194
|
+
if (runtime.externalMonitor) {
|
|
195
|
+
clearInterval(runtime.externalMonitor);
|
|
196
|
+
}
|
|
197
|
+
runtime.dashboardService.stopBackgroundRefresh();
|
|
198
|
+
runtime.agentUpdates.cancelAll();
|
|
199
|
+
runtime.registry.disposeAll();
|
|
200
|
+
runtime.subscribers.clear();
|
|
201
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { ensureOutDir } from "../artifacts/artifacts.js";
|
|
3
|
+
import { buildFileInstructions, outboxPath, stageFile, } from "../artifacts/attachments.js";
|
|
4
|
+
import { CODEX_AGENT_CAPABILITIES, agentLabel, agentReasoningLabel, agentReasoningOptions, isAgentId, } from "../agents/shared/agent.js";
|
|
5
|
+
import { getExternalSnapshotForSession } from "../agents/shared/agent-activity.js";
|
|
6
|
+
import { listAgentAdapterDescriptors } from "../agents/shared/agent-adapter.js";
|
|
7
|
+
import { AgentUpdateManager } from "../agents/shared/agent-updates.js";
|
|
8
|
+
import { createAgentSessionService, enabledAgents } from "../agents/shared/agent-factory.js";
|
|
9
|
+
import { AuditLogStore } from "../access/audit-log.js";
|
|
10
|
+
import { BotPreferencesStore } from "../state/bot-preferences.js";
|
|
11
|
+
import { ChannelCommandService } from "../channels/shared/channel-command-service.js";
|
|
12
|
+
import { ChannelTurnService } from "../channels/shared/channel-turn-service.js";
|
|
13
|
+
import { activeSessionSourceForContextKey, ChannelMirrorRegistry } from "../channels/shared/channel-mirror-registry.js";
|
|
14
|
+
import { listThreads as listCodexThreads } from "../agents/codex/codex-state.js";
|
|
15
|
+
import { friendlyErrorText } from "../core/error-messages.js";
|
|
16
|
+
import { clearLogFile, getAgentUpdateLogPath, getConnectorHealth, getConnectorLogPath, getPackageVersion, getUpdateLogPath, getVersionChecks, readConnectorState, readFormattedLogTail, spawnConnectorRestart, spawnSelfUpdate } from "../support/operations.js";
|
|
17
|
+
import { PromptStore, toPromptEnvelope } from "../state/prompt-store.js";
|
|
18
|
+
import { UnifiedJobStore } from "../state/job-store.js";
|
|
19
|
+
import { buildRuntimeMetrics } from "./metrics.js";
|
|
20
|
+
import { RelayArtifactService } from "./relay-artifact-service.js";
|
|
21
|
+
import { RelayAuthService } from "./relay-auth-service.js";
|
|
22
|
+
import { RelayExternalActivityMonitor } from "./relay-external-activity-monitor.js";
|
|
23
|
+
import { RelayQueueService } from "./relay-queue-service.js";
|
|
24
|
+
import { RuntimeSnapshotCache } from "./runtime-cache.js";
|
|
25
|
+
import { activeSessionPriority, activityToUnifiedJob, agentUpdateStatusToUnified, dedupeJobs, hostLoginCommand, hostLogoutCommand, isPromptTerminalActivity, normalizeMimeType, promptActivityToUnifiedJob, shouldRefreshActiveSessions, taskToUnifiedJob, uploadFileDtos, } from "./relay-runtime-helpers.js";
|
|
26
|
+
import { RelayDashboardService } from "./relay-dashboard-service.js";
|
|
27
|
+
import { capabilitiesOf } from "../channels/shared/bot-rendering.js";
|
|
28
|
+
import { renderSessionInfoPlain, renderSessionUsageRows } from "../channels/shared/session-format.js";
|
|
29
|
+
import { SessionLockStore } from "../access/session-locks.js";
|
|
30
|
+
import { SessionRegistry } from "../state/session-registry.js";
|
|
31
|
+
import { createSupportBundle } from "../support/support-bundle.js";
|
|
32
|
+
import { transcribeAudio } from "../artifacts/voice.js";
|
|
33
|
+
import { WebActivityStore, WebChatStore, } from "../web/web-state.js";
|
|
34
|
+
import { evaluateWorkspacePolicy, filterAllowedWorkspaces } from "../core/workspace-policy.js";
|
|
35
|
+
export const WEB_CONTEXT_KEY = "web:dashboard";
|
|
36
|
+
const ACTIVE_CODEX_DISCOVERY_LIMIT = 200;
|
|
37
|
+
const ACTIVE_ACTIVITY_TTL_MS = 6 * 60 * 60 * 1000;
|
|
38
|
+
const MAX_WEB_SESSION_PAGE_SIZE = 50;
|
|
39
|
+
const MAX_CHAT_HISTORY = 250;
|
|
40
|
+
export async function relayRuntimeSendPrompt(runtime, text, actor) {
|
|
41
|
+
const trimmed = text.trim();
|
|
42
|
+
if (!trimmed) {
|
|
43
|
+
throw new Error("Prompt is empty.");
|
|
44
|
+
}
|
|
45
|
+
return runtime.sendEnvelope({ ...toPromptEnvelope(trimmed), activityActor: actor }, actor);
|
|
46
|
+
}
|
|
47
|
+
export async function relayRuntimeSendUploadPrompt(runtime, options, actor) {
|
|
48
|
+
const text = options.text?.trim() ?? "";
|
|
49
|
+
const files = options.files.filter((file) => file.data.byteLength > 0);
|
|
50
|
+
if (!text && files.length === 0) {
|
|
51
|
+
throw new Error("Prompt is empty.");
|
|
52
|
+
}
|
|
53
|
+
const session = await runtime.getSession(false);
|
|
54
|
+
const workspace = session.getInfo().workspace;
|
|
55
|
+
const turnId = randomUUID().slice(0, 12);
|
|
56
|
+
const outDir = outboxPath(workspace, turnId);
|
|
57
|
+
await ensureOutDir(outDir);
|
|
58
|
+
const stagedFiles = [];
|
|
59
|
+
const imagePaths = [];
|
|
60
|
+
const transcriptParts = [];
|
|
61
|
+
for (const [index, file] of files.entries()) {
|
|
62
|
+
const mimeType = normalizeMimeType(file.mimeType, file.name);
|
|
63
|
+
const staged = await stageFile(file.data, file.name || `upload-${index + 1}`, mimeType, {
|
|
64
|
+
workspace,
|
|
65
|
+
turnId,
|
|
66
|
+
maxFileSize: runtime.config.maxFileSize,
|
|
67
|
+
});
|
|
68
|
+
stagedFiles.push(staged);
|
|
69
|
+
if (mimeType.startsWith("image/")) {
|
|
70
|
+
imagePaths.push(staged.localPath);
|
|
71
|
+
}
|
|
72
|
+
if (mimeType.startsWith("audio/")) {
|
|
73
|
+
const result = await transcribeAudio(staged.localPath, {
|
|
74
|
+
preferredBackend: runtime.config.voicePreferredBackend === "auto"
|
|
75
|
+
? undefined
|
|
76
|
+
: runtime.config.voicePreferredBackend,
|
|
77
|
+
language: runtime.config.voiceDefaultLanguage,
|
|
78
|
+
});
|
|
79
|
+
const transcript = result.text.trim();
|
|
80
|
+
if (transcript) {
|
|
81
|
+
transcriptParts.push(`Audio transcript (${staged.safeName}, via ${result.backend}):\n${transcript}`);
|
|
82
|
+
runtime.appendActivity({
|
|
83
|
+
source: "web",
|
|
84
|
+
status: "completed",
|
|
85
|
+
type: "voice_transcribed",
|
|
86
|
+
threadId: session.getInfo().threadId,
|
|
87
|
+
workspace,
|
|
88
|
+
agentId: session.getInfo().agentId,
|
|
89
|
+
actor,
|
|
90
|
+
detail: `${staged.safeName} via ${result.backend}`,
|
|
91
|
+
durationMs: result.durationMs,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (stagedFiles.length > 0) {
|
|
97
|
+
runtime.appendActivity({
|
|
98
|
+
source: "web",
|
|
99
|
+
status: "info",
|
|
100
|
+
type: "attachment_staged",
|
|
101
|
+
threadId: session.getInfo().threadId,
|
|
102
|
+
workspace,
|
|
103
|
+
agentId: session.getInfo().agentId,
|
|
104
|
+
actor,
|
|
105
|
+
detail: `${stagedFiles.length} file(s): ${stagedFiles.map((file) => file.safeName).join(", ")}`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const audioOnly = stagedFiles.length > 0 && stagedFiles.every((file) => file.mimeType.startsWith("audio/"));
|
|
109
|
+
if (runtime.config.voiceTranscribeOnly && audioOnly && !text) {
|
|
110
|
+
return {
|
|
111
|
+
queued: false,
|
|
112
|
+
transcript: transcriptParts.join("\n\n"),
|
|
113
|
+
transcribeOnly: true,
|
|
114
|
+
files: uploadFileDtos(stagedFiles),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const promptInput = {};
|
|
118
|
+
const textParts = [text, ...transcriptParts].filter(Boolean);
|
|
119
|
+
if (textParts.length > 0) {
|
|
120
|
+
promptInput.text = textParts.join("\n\n");
|
|
121
|
+
}
|
|
122
|
+
if (imagePaths.length > 0) {
|
|
123
|
+
promptInput.imagePaths = imagePaths;
|
|
124
|
+
}
|
|
125
|
+
if (stagedFiles.length > 0) {
|
|
126
|
+
promptInput.stagedFileInstructions = buildFileInstructions(stagedFiles, outDir);
|
|
127
|
+
}
|
|
128
|
+
const result = await runtime.sendEnvelope({ ...toPromptEnvelope(promptInput, outDir), activityActor: actor }, actor);
|
|
129
|
+
return {
|
|
130
|
+
...result,
|
|
131
|
+
transcript: transcriptParts.join("\n\n") || undefined,
|
|
132
|
+
files: uploadFileDtos(stagedFiles),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
export async function relayRuntimeSendEnvelope(runtime, envelope, actor) {
|
|
136
|
+
const activityActor = envelope.activityActor ?? actor;
|
|
137
|
+
const session = await runtime.getSession(false);
|
|
138
|
+
const external = getExternalSnapshotForSession(session, runtime.config, { maxEvents: 0 });
|
|
139
|
+
if (session.isProcessing() || external?.activity.active) {
|
|
140
|
+
const queued = runtime.queueService.enqueue(envelope);
|
|
141
|
+
const info = runtime.publicInfo(session);
|
|
142
|
+
runtime.appendActivity({
|
|
143
|
+
source: "web",
|
|
144
|
+
status: "queued",
|
|
145
|
+
type: "prompt_queued",
|
|
146
|
+
threadId: info.threadId,
|
|
147
|
+
workspace: info.workspace,
|
|
148
|
+
agentId: info.agentId,
|
|
149
|
+
actor: activityActor,
|
|
150
|
+
prompt: envelope.description,
|
|
151
|
+
detail: external?.activity.active
|
|
152
|
+
? `Queued because ${external.agentLabel} CLI is still processing another task.`
|
|
153
|
+
: `Queued at position ${runtime.queueService.length()}.`,
|
|
154
|
+
});
|
|
155
|
+
runtime.appendAudit({
|
|
156
|
+
action: "prompt_queued",
|
|
157
|
+
status: "ok",
|
|
158
|
+
contextKey: runtime.contextKey,
|
|
159
|
+
agentId: info.agentId,
|
|
160
|
+
threadId: info.threadId,
|
|
161
|
+
workspace: info.workspace,
|
|
162
|
+
promptId: queued.id,
|
|
163
|
+
actor: activityActor,
|
|
164
|
+
description: envelope.description,
|
|
165
|
+
});
|
|
166
|
+
if (external?.activity.active) {
|
|
167
|
+
runtime.broadcastStatus(`Waiting for ${external.agentLabel} CLI task... ${runtime.queueService.length()} queued.`, "info");
|
|
168
|
+
}
|
|
169
|
+
runtime.broadcastQueue();
|
|
170
|
+
return { queued: true, queueId: queued.id };
|
|
171
|
+
}
|
|
172
|
+
void runtime.runPrompt(session, { ...envelope, activityActor }).catch((error) => {
|
|
173
|
+
runtime.broadcast({ type: "turn_error", id: runtime.currentTurnId ?? "turn", error: friendlyErrorText(error), at: new Date().toISOString() });
|
|
174
|
+
});
|
|
175
|
+
return { queued: false };
|
|
176
|
+
}
|
|
177
|
+
export function relayRuntimeQueue(runtime) {
|
|
178
|
+
return runtime.queueService.list();
|
|
179
|
+
}
|
|
180
|
+
export function relayRuntimeQueuePaused(runtime) {
|
|
181
|
+
return runtime.queueService.isPaused();
|
|
182
|
+
}
|
|
183
|
+
export function relayRuntimeQueueAction(runtime, action, id, actor) {
|
|
184
|
+
const before = runtime.queueService.rawList();
|
|
185
|
+
const affected = id ? before.find((item) => item.id === id) : undefined;
|
|
186
|
+
runtime.queueService.apply(action, id);
|
|
187
|
+
if (id && action === "run") {
|
|
188
|
+
void runtime.drainQueue().catch((error) => runtime.broadcastStatus(friendlyErrorText(error), "error"));
|
|
189
|
+
}
|
|
190
|
+
runtime.appendActivity({
|
|
191
|
+
source: "web",
|
|
192
|
+
status: "info",
|
|
193
|
+
type: `queue_${action}`,
|
|
194
|
+
threadId: null,
|
|
195
|
+
workspace: runtime.config.workspace,
|
|
196
|
+
actor,
|
|
197
|
+
prompt: affected?.description,
|
|
198
|
+
detail: id ? `${action}: ${id}` : `${action}: ${before.length} queued`,
|
|
199
|
+
});
|
|
200
|
+
runtime.appendAudit({
|
|
201
|
+
action: "queue_updated",
|
|
202
|
+
status: "ok",
|
|
203
|
+
contextKey: runtime.contextKey,
|
|
204
|
+
actor,
|
|
205
|
+
description: id ? `${action}: ${id}` : action,
|
|
206
|
+
});
|
|
207
|
+
runtime.broadcastQueue();
|
|
208
|
+
return runtime.queue();
|
|
209
|
+
}
|
|
210
|
+
export async function relayRuntimeArtifacts(runtime) {
|
|
211
|
+
const session = await runtime.getSession(true);
|
|
212
|
+
return runtime.artifactService.list(session.getInfo().workspace, 20);
|
|
213
|
+
}
|
|
214
|
+
export async function relayRuntimeArtifact(runtime, turnId) {
|
|
215
|
+
const session = await runtime.getSession(true);
|
|
216
|
+
return runtime.artifactService.get(session.getInfo().workspace, turnId);
|
|
217
|
+
}
|
|
218
|
+
export async function relayRuntimeDeleteArtifact(runtime, turnId, actor) {
|
|
219
|
+
const session = await runtime.getSession(true);
|
|
220
|
+
const info = runtime.publicInfo(session);
|
|
221
|
+
const removed = await runtime.artifactService.delete(info.workspace, turnId);
|
|
222
|
+
runtime.appendActivity({
|
|
223
|
+
source: "web",
|
|
224
|
+
status: removed ? "info" : "failed",
|
|
225
|
+
type: "artifact_deleted",
|
|
226
|
+
threadId: info.threadId,
|
|
227
|
+
workspace: info.workspace,
|
|
228
|
+
agentId: info.agentId,
|
|
229
|
+
actor,
|
|
230
|
+
detail: turnId,
|
|
231
|
+
});
|
|
232
|
+
return removed;
|
|
233
|
+
}
|
|
234
|
+
export async function relayRuntimeCreateArtifactZip(runtime, turnId, actor) {
|
|
235
|
+
const session = await runtime.getSession(true);
|
|
236
|
+
const info = runtime.publicInfo(session);
|
|
237
|
+
const zip = await runtime.artifactService.createZip(info.workspace, turnId);
|
|
238
|
+
if (zip) {
|
|
239
|
+
runtime.appendActivity({
|
|
240
|
+
source: "web",
|
|
241
|
+
status: "info",
|
|
242
|
+
type: "artifact_zip_created",
|
|
243
|
+
threadId: info.threadId,
|
|
244
|
+
workspace: info.workspace,
|
|
245
|
+
agentId: info.agentId,
|
|
246
|
+
actor,
|
|
247
|
+
detail: zip.name,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
return zip;
|
|
251
|
+
}
|
|
252
|
+
export async function relayRuntimeArtifactPreview(runtime, turnId, relativePath) {
|
|
253
|
+
const session = await runtime.getSession(true);
|
|
254
|
+
return runtime.artifactService.preview(session.getInfo().workspace, turnId, relativePath);
|
|
255
|
+
}
|
|
256
|
+
export async function relayRuntimeEnsureActiveThread(runtime, session) {
|
|
257
|
+
if (!session.hasActiveThread()) {
|
|
258
|
+
await session.newThread();
|
|
259
|
+
runtime.updateSession(session);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export function relayRuntimeEnsureIdle(runtime, session) {
|
|
263
|
+
if (session.isProcessing()) {
|
|
264
|
+
throw new Error("The active session is still processing a turn.");
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
export async function relayRuntimeRunPrompt(runtime, session, envelope) {
|
|
268
|
+
const workspacePolicy = evaluateWorkspacePolicy(session.getInfo().workspace, runtime.config);
|
|
269
|
+
if (!workspacePolicy.allowed) {
|
|
270
|
+
throw new Error(workspacePolicy.warning ?? "Current workspace is blocked by policy.");
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
await runtime.turnService.run(session, envelope);
|
|
274
|
+
}
|
|
275
|
+
finally {
|
|
276
|
+
await runtime.drainQueue();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
export async function relayRuntimeDrainQueue(runtime) {
|
|
280
|
+
if (runtime.draining || runtime.queueService.isPaused()) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
runtime.draining = true;
|
|
284
|
+
try {
|
|
285
|
+
const session = await runtime.getSession(false);
|
|
286
|
+
while (!session.isProcessing()) {
|
|
287
|
+
const external = getExternalSnapshotForSession(session, runtime.config, { maxEvents: 0 });
|
|
288
|
+
if (external?.activity.active) {
|
|
289
|
+
runtime.broadcastStatus(`Waiting for ${external.agentLabel} CLI task... ${runtime.queueService.length()} queued.`, "info");
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const next = runtime.queueService.dequeue();
|
|
293
|
+
runtime.broadcastQueue();
|
|
294
|
+
if (!next) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await runtime.runPrompt(session, next);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
finally {
|
|
301
|
+
runtime.draining = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
export function relayRuntimeUpdateSession(runtime, session) {
|
|
305
|
+
runtime.registry.updateMetadata(runtime.contextKey, session);
|
|
306
|
+
runtime.broadcast({ type: "session_update", session: runtime.publicInfo(session) });
|
|
307
|
+
}
|