agent-relay-server 0.4.39 → 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/README.md +8 -8
- package/package.json +1 -1
- package/public/dashboard.js +233 -17
- package/public/icons/agent-relay-192.png +0 -0
- package/public/icons/agent-relay-512.png +0 -0
- package/public/icons/agent-relay.svg +14 -0
- package/public/index.html +276 -4
- package/public/manifest.webmanifest +33 -0
- package/public/sw.js +58 -0
- package/src/cli.ts +80 -17
- package/src/connectors.ts +256 -0
- package/src/db.ts +544 -25
- package/src/index.ts +25 -1
- package/src/routes.ts +632 -26
- package/src/security.ts +2 -1
- package/src/sse.ts +21 -1
- package/src/types.ts +152 -3
package/src/security.ts
CHANGED
|
@@ -95,7 +95,8 @@ export function requiredScopeFor(method: string, pathname: string): string | nul
|
|
|
95
95
|
if (pathname === "/api/stats") return "stats:read";
|
|
96
96
|
if (pathname === "/api/health") return "health:read";
|
|
97
97
|
if (pathname === "/api/events") return "events:read";
|
|
98
|
-
if (pathname === "/api/
|
|
98
|
+
if (pathname === "/api/connectors" || pathname.startsWith("/api/connectors/")) return method === "GET" ? "connectors:read" : "system:write";
|
|
99
|
+
if (pathname.startsWith("/api/channels") || pathname.startsWith("/api/channel-bindings")) return method === "GET" ? "channels:read" : "channels:write";
|
|
99
100
|
if (pathname === "/api/integrations" || pathname.startsWith("/api/integrations/")) return method === "GET" ? "integrations:read" : "integrations:write";
|
|
100
101
|
if (pathname.startsWith("/api/agents")) return method === "GET" ? "agents:read" : "agents:write";
|
|
101
102
|
if (pathname.startsWith("/api/activity")) return method === "GET" ? "activity:read" : "activity:write";
|
package/src/sse.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getAgent } from "./db";
|
|
1
|
+
import { getAgent, getOrchestrator } from "./db";
|
|
2
2
|
import type { Message, Task } from "./types";
|
|
3
3
|
|
|
4
4
|
interface Connection {
|
|
@@ -122,6 +122,12 @@ export function emitTaskChanged(task: Task, eventType = "task.updated") {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
export function emitChannelActivity(activity: Record<string, unknown>) {
|
|
126
|
+
for (const conn of connections.values()) {
|
|
127
|
+
send(conn, "channel.activity", activity);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
125
131
|
export function getConnectionCount(): number {
|
|
126
132
|
return connections.size;
|
|
127
133
|
}
|
|
@@ -135,3 +141,17 @@ function targetMatchesAgent(target: string, agentId: string): boolean {
|
|
|
135
141
|
if (target.startsWith("label:") && agent.label === target.slice(6)) return true;
|
|
136
142
|
return false;
|
|
137
143
|
}
|
|
144
|
+
|
|
145
|
+
export function emitOrchestratorStatus(orchestratorId: string) {
|
|
146
|
+
const orch = getOrchestrator(orchestratorId);
|
|
147
|
+
const data = orch ?? { id: orchestratorId, status: "offline" };
|
|
148
|
+
for (const conn of connections.values()) {
|
|
149
|
+
send(conn, "orchestrator.status", data);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function emitOrchestratorRemoved(orchestratorId: string) {
|
|
154
|
+
for (const conn of connections.values()) {
|
|
155
|
+
send(conn, "orchestrator.removed", { id: orchestratorId });
|
|
156
|
+
}
|
|
157
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
export type AgentKind = "provider" | "channel" | "orchestrator" | "system" | "user";
|
|
2
|
+
|
|
1
3
|
export interface AgentCard {
|
|
2
4
|
id: string;
|
|
3
5
|
name: string;
|
|
6
|
+
kind: AgentKind;
|
|
4
7
|
label?: string; // human-friendly alias; acts as a fan-out target ("label:foo")
|
|
5
8
|
tags: string[];
|
|
6
9
|
machine?: string;
|
|
@@ -15,13 +18,19 @@ export interface AgentCard {
|
|
|
15
18
|
createdAt: number;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
|
-
export type
|
|
21
|
+
export type MessageKind =
|
|
22
|
+
| "chat"
|
|
23
|
+
| "channel.event"
|
|
24
|
+
| "task"
|
|
25
|
+
| "pair"
|
|
26
|
+
| "control"
|
|
27
|
+
| "system";
|
|
19
28
|
|
|
20
29
|
export interface Message {
|
|
21
30
|
id: number;
|
|
22
31
|
from: string;
|
|
23
32
|
to: string; // agent-id | "tag:<name>" | "broadcast" | "cap:<name>"
|
|
24
|
-
|
|
33
|
+
kind: MessageKind;
|
|
25
34
|
channel?: string;
|
|
26
35
|
subject?: string;
|
|
27
36
|
body: string;
|
|
@@ -32,6 +41,7 @@ export interface Message {
|
|
|
32
41
|
claimedAt?: number;
|
|
33
42
|
claimExpiresAt?: number;
|
|
34
43
|
idempotencyKey?: string;
|
|
44
|
+
payload: Record<string, unknown>;
|
|
35
45
|
meta?: Record<string, unknown>;
|
|
36
46
|
readBy: string[];
|
|
37
47
|
createdAt: number;
|
|
@@ -40,16 +50,71 @@ export interface Message {
|
|
|
40
50
|
export interface SendMessageInput {
|
|
41
51
|
from: string;
|
|
42
52
|
to: string;
|
|
43
|
-
|
|
53
|
+
kind?: MessageKind;
|
|
44
54
|
channel?: string;
|
|
45
55
|
subject?: string;
|
|
46
56
|
body: string;
|
|
47
57
|
replyTo?: number;
|
|
48
58
|
claimable?: boolean;
|
|
49
59
|
idempotencyKey?: string;
|
|
60
|
+
payload?: Record<string, unknown>;
|
|
50
61
|
meta?: Record<string, unknown>;
|
|
51
62
|
}
|
|
52
63
|
|
|
64
|
+
export type ConnectorKind = "channel" | "event" | "provider" | "orchestrator";
|
|
65
|
+
export type ConnectorAction = "install" | "uninstall" | "enable" | "disable" | "start" | "stop" | "restart" | "status" | "doctor";
|
|
66
|
+
|
|
67
|
+
export interface ConnectorManifest {
|
|
68
|
+
schema: "agent-relay.connector.v1";
|
|
69
|
+
id: string;
|
|
70
|
+
kind: ConnectorKind;
|
|
71
|
+
packageName?: string;
|
|
72
|
+
binary: string;
|
|
73
|
+
displayName: string;
|
|
74
|
+
description?: string;
|
|
75
|
+
version: string;
|
|
76
|
+
capabilities: string[];
|
|
77
|
+
commands: Partial<Record<ConnectorAction, string[]>>;
|
|
78
|
+
configSchema?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface ConnectorRuntime {
|
|
82
|
+
installed: boolean;
|
|
83
|
+
enabled?: boolean;
|
|
84
|
+
running?: boolean;
|
|
85
|
+
status?: "ok" | "warn" | "error" | "unknown";
|
|
86
|
+
detail?: string;
|
|
87
|
+
updatedAt?: string;
|
|
88
|
+
raw?: unknown;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface ConnectorSummary {
|
|
92
|
+
id: string;
|
|
93
|
+
kind: ConnectorKind;
|
|
94
|
+
displayName: string;
|
|
95
|
+
description?: string;
|
|
96
|
+
version: string;
|
|
97
|
+
packageName?: string;
|
|
98
|
+
binary: string;
|
|
99
|
+
capabilities: string[];
|
|
100
|
+
registryPath: string;
|
|
101
|
+
manifest: ConnectorManifest;
|
|
102
|
+
config?: Record<string, unknown>;
|
|
103
|
+
state?: Record<string, unknown>;
|
|
104
|
+
runtime: ConnectorRuntime;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface ConnectorActionResult {
|
|
108
|
+
connectorId: string;
|
|
109
|
+
action: ConnectorAction;
|
|
110
|
+
command?: string[];
|
|
111
|
+
ok: boolean;
|
|
112
|
+
exitCode?: number | null;
|
|
113
|
+
stdout?: string;
|
|
114
|
+
stderr?: string;
|
|
115
|
+
parsed?: unknown;
|
|
116
|
+
}
|
|
117
|
+
|
|
53
118
|
export type PairStatus = "pending" | "active" | "ended" | "rejected" | "expired";
|
|
54
119
|
|
|
55
120
|
export interface PairSession {
|
|
@@ -99,6 +164,7 @@ export interface PollQuery {
|
|
|
99
164
|
export interface RegisterAgentInput {
|
|
100
165
|
id: string;
|
|
101
166
|
name: string;
|
|
167
|
+
kind?: AgentKind;
|
|
102
168
|
label?: string | null;
|
|
103
169
|
tags?: string[];
|
|
104
170
|
machine?: string;
|
|
@@ -203,16 +269,39 @@ export interface IntegrationSummary {
|
|
|
203
269
|
|
|
204
270
|
export type ChannelDirection = "inbound" | "outbound" | "bidirectional";
|
|
205
271
|
|
|
272
|
+
export type ChannelRouteTarget =
|
|
273
|
+
| { type: "agent"; id: string }
|
|
274
|
+
| { type: "label"; id: string }
|
|
275
|
+
| { type: "tag"; id: string }
|
|
276
|
+
| { type: "capability"; id: string }
|
|
277
|
+
| { type: "broadcast" }
|
|
278
|
+
| { type: "orchestrator"; id: string };
|
|
279
|
+
|
|
280
|
+
export type ChannelBindingMode = "exclusive" | "claimable" | "broadcast";
|
|
281
|
+
|
|
282
|
+
export interface ChannelBinding {
|
|
283
|
+
id: string;
|
|
284
|
+
channelId: string;
|
|
285
|
+
conversationId?: string;
|
|
286
|
+
target: ChannelRouteTarget;
|
|
287
|
+
mode: ChannelBindingMode;
|
|
288
|
+
priority: number;
|
|
289
|
+
createdAt: number;
|
|
290
|
+
updatedAt: number;
|
|
291
|
+
}
|
|
292
|
+
|
|
206
293
|
export interface ChannelSummary {
|
|
207
294
|
id: string;
|
|
208
295
|
name: string;
|
|
209
296
|
type: string;
|
|
210
297
|
transport: string;
|
|
211
298
|
agentId: string;
|
|
299
|
+
accountId: string;
|
|
212
300
|
status: AgentCard["status"];
|
|
213
301
|
ready: boolean;
|
|
214
302
|
direction: ChannelDirection;
|
|
215
303
|
target?: string;
|
|
304
|
+
binding?: ChannelBinding;
|
|
216
305
|
topicChannels: string[];
|
|
217
306
|
capabilities: string[];
|
|
218
307
|
tags: string[];
|
|
@@ -291,6 +380,66 @@ export interface ActivityEventInput {
|
|
|
291
380
|
metadata?: Record<string, unknown>;
|
|
292
381
|
}
|
|
293
382
|
|
|
383
|
+
// --- Orchestrators ---
|
|
384
|
+
|
|
385
|
+
export type OrchestratorStatus = "online" | "offline";
|
|
386
|
+
export type SpawnProvider = "claude" | "codex";
|
|
387
|
+
export type SpawnApprovalMode = "open" | "guarded" | "read-only";
|
|
388
|
+
|
|
389
|
+
export interface Orchestrator {
|
|
390
|
+
id: string;
|
|
391
|
+
hostname: string;
|
|
392
|
+
status: OrchestratorStatus;
|
|
393
|
+
agentId: string; // relay agent id for messaging
|
|
394
|
+
providers: SpawnProvider[];
|
|
395
|
+
baseDir: string;
|
|
396
|
+
envKeys: string[]; // names only, never values
|
|
397
|
+
meta: Record<string, unknown>;
|
|
398
|
+
managedAgents: ManagedAgent[];
|
|
399
|
+
lastSeen: number;
|
|
400
|
+
createdAt: number;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export interface ManagedAgent {
|
|
404
|
+
agentId: string;
|
|
405
|
+
provider: SpawnProvider;
|
|
406
|
+
tmuxSession: string;
|
|
407
|
+
cwd: string;
|
|
408
|
+
label?: string;
|
|
409
|
+
approvalMode: SpawnApprovalMode;
|
|
410
|
+
pid?: number;
|
|
411
|
+
startedAt: number;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export interface RegisterOrchestratorInput {
|
|
415
|
+
id: string;
|
|
416
|
+
hostname: string;
|
|
417
|
+
providers: SpawnProvider[];
|
|
418
|
+
baseDir: string;
|
|
419
|
+
envKeys?: string[];
|
|
420
|
+
meta?: Record<string, unknown>;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export interface OrchestratorSpawnInput {
|
|
424
|
+
provider: SpawnProvider;
|
|
425
|
+
cwd?: string;
|
|
426
|
+
label?: string;
|
|
427
|
+
approvalMode?: SpawnApprovalMode;
|
|
428
|
+
prompt?: string;
|
|
429
|
+
env?: Record<string, string>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export interface OrchestratorSpawnResult {
|
|
433
|
+
orchestratorId: string;
|
|
434
|
+
provider: SpawnProvider;
|
|
435
|
+
tmuxSession: string;
|
|
436
|
+
cwd: string;
|
|
437
|
+
label?: string;
|
|
438
|
+
approvalMode: SpawnApprovalMode;
|
|
439
|
+
pid?: number;
|
|
440
|
+
startedAt: number;
|
|
441
|
+
}
|
|
442
|
+
|
|
294
443
|
export interface HealthCheck {
|
|
295
444
|
name: string;
|
|
296
445
|
status: "ok" | "warn" | "error";
|