agent-relay-orchestrator 0.91.3 → 0.91.4
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/package.json +3 -2
- package/src/shared-callmux.ts +44 -5
- package/vendor/callmux/bin/callmux.js +47579 -0
- package/vendor/callmux/package.json +11 -0
- package/vendor/callmux/schema.json +868 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-orchestrator",
|
|
3
|
-
"version": "0.91.
|
|
3
|
+
"version": "0.91.4",
|
|
4
4
|
"description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"src/**/*.ts",
|
|
11
11
|
"!src/**/*.test.ts",
|
|
12
|
+
"vendor/**",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
@@ -16,7 +17,7 @@
|
|
|
16
17
|
"test": "bun test"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
|
-
"agent-relay-sdk": "0.2.
|
|
20
|
+
"agent-relay-sdk": "0.2.71"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
23
|
"@types/bun": "latest",
|
package/src/shared-callmux.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, isAbsolute, join } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
4
|
import { errMessage, isRecord } from "agent-relay-sdk";
|
|
5
5
|
import type { OrchestratorConfig } from "./config";
|
|
6
6
|
import { agentRelayHome } from "./config";
|
|
@@ -54,6 +54,15 @@ export interface SharedCallmuxSupervisorDeps {
|
|
|
54
54
|
setTimeout(fn: () => void, ms: number): Timer;
|
|
55
55
|
clearTimeout(timer: Timer): void;
|
|
56
56
|
log(message: string): void;
|
|
57
|
+
report(snapshot: SharedCallmuxHealthSnapshot): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface SharedCallmuxHealthSnapshot {
|
|
61
|
+
state: "disabled" | "missing" | "starting" | "running" | "unhealthy" | "restarting" | "stopped";
|
|
62
|
+
url: string;
|
|
63
|
+
command?: string;
|
|
64
|
+
reason?: string;
|
|
65
|
+
pid?: number;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
export function sharedCallmuxOptionsFromEnv(env: Record<string, string | undefined> = process.env): SharedCallmuxOptions {
|
|
@@ -63,14 +72,13 @@ export function sharedCallmuxOptionsFromEnv(env: Record<string, string | undefin
|
|
|
63
72
|
const port = numberEnv(env[SHARED_CALLMUX_PORT_ENV]) ?? parsed?.port ?? DEFAULT_SHARED_CALLMUX_PORT;
|
|
64
73
|
const url = explicitUrl ?? `http://${host}:${port}/mcp`;
|
|
65
74
|
return {
|
|
66
|
-
|
|
67
|
-
command: env[SHARED_CALLMUX_COMMAND_ENV] || "callmux",
|
|
75
|
+
command: env[SHARED_CALLMUX_COMMAND_ENV] || bundledCallmuxCommand() || "callmux",
|
|
68
76
|
host,
|
|
69
77
|
port,
|
|
70
78
|
url,
|
|
71
79
|
configPath: env[SHARED_CALLMUX_CONFIG_ENV] || join(agentRelayHome(), "callmux", "shared-listener.json"),
|
|
72
80
|
sourceConfigPath: env[SHARED_CALLMUX_SOURCE_CONFIG_ENV] || env.CALLMUX_CONFIG || join(homedir(), ".config", "callmux", "config.json"),
|
|
73
|
-
enabled: env[SHARED_CALLMUX_ENABLE_ENV]
|
|
81
|
+
enabled: !envOff(env[SHARED_CALLMUX_ENABLE_ENV]),
|
|
74
82
|
};
|
|
75
83
|
}
|
|
76
84
|
|
|
@@ -118,14 +126,17 @@ export class SharedCallmuxSupervisor {
|
|
|
118
126
|
|
|
119
127
|
start(): void {
|
|
120
128
|
if (!this.opts.enabled) {
|
|
121
|
-
this.deps.log("[orchestrator] shared callmux listener disabled
|
|
129
|
+
this.deps.log("[orchestrator] shared callmux listener disabled by AGENT_RELAY_SHARED_CALLMUX_ENABLE=0");
|
|
130
|
+
this.report("disabled", "global kill-switch");
|
|
122
131
|
return;
|
|
123
132
|
}
|
|
124
133
|
this.deps.log(`[orchestrator] Shared callmux listener: ${this.opts.url}`);
|
|
125
134
|
if (!this.deps.which(this.opts.command)) {
|
|
126
135
|
this.deps.log("[orchestrator] shared callmux not found — shared listener dormant");
|
|
136
|
+
this.report("missing", "command not found");
|
|
127
137
|
return;
|
|
128
138
|
}
|
|
139
|
+
this.report("starting");
|
|
129
140
|
this.spawn();
|
|
130
141
|
this.healthTimer = this.deps.setInterval(() => {
|
|
131
142
|
void this.checkHealth();
|
|
@@ -156,9 +167,11 @@ export class SharedCallmuxSupervisor {
|
|
|
156
167
|
}
|
|
157
168
|
if (ok) {
|
|
158
169
|
this.backoffMs = this.timing.restartBaseMs ?? 1_000;
|
|
170
|
+
this.report("running", undefined, this.proc?.pid);
|
|
159
171
|
return true;
|
|
160
172
|
}
|
|
161
173
|
this.deps.log(`[orchestrator] Shared callmux readiness failed at ${readyUrl}; restarting`);
|
|
174
|
+
this.report("unhealthy", "readiness failed", this.proc?.pid);
|
|
162
175
|
this.restart("readiness failed");
|
|
163
176
|
return false;
|
|
164
177
|
}
|
|
@@ -177,20 +190,24 @@ export class SharedCallmuxSupervisor {
|
|
|
177
190
|
proc = this.deps.spawn(this.opts.command, args, { env, cwd: homedir() });
|
|
178
191
|
} catch (err) {
|
|
179
192
|
this.deps.log(`[orchestrator] Shared callmux listener failed to start: ${errMessage(err)}; scheduling restart`);
|
|
193
|
+
this.report("restarting", errMessage(err));
|
|
180
194
|
this.scheduleRestart();
|
|
181
195
|
return;
|
|
182
196
|
}
|
|
183
197
|
this.proc = proc;
|
|
184
198
|
this.deps.log(`[orchestrator] Started shared callmux listener pid=${proc.pid ?? "unknown"}`);
|
|
199
|
+
this.report("running", undefined, proc.pid);
|
|
185
200
|
proc.exited.then((code) => {
|
|
186
201
|
if (this.proc !== proc || this.stopping) return;
|
|
187
202
|
this.proc = null;
|
|
188
203
|
this.deps.log(`[orchestrator] Shared callmux listener exited (${code ?? "signal"}); scheduling restart`);
|
|
204
|
+
this.report("restarting", `exited ${code ?? "signal"}`);
|
|
189
205
|
this.scheduleRestart();
|
|
190
206
|
}).catch((err) => {
|
|
191
207
|
if (this.proc !== proc || this.stopping) return;
|
|
192
208
|
this.proc = null;
|
|
193
209
|
this.deps.log(`[orchestrator] Shared callmux listener exit watcher failed: ${err}`);
|
|
210
|
+
this.report("restarting", errMessage(err));
|
|
194
211
|
this.scheduleRestart();
|
|
195
212
|
});
|
|
196
213
|
}
|
|
@@ -201,9 +218,20 @@ export class SharedCallmuxSupervisor {
|
|
|
201
218
|
this.proc.kill("SIGTERM");
|
|
202
219
|
this.proc = null;
|
|
203
220
|
}
|
|
221
|
+
this.report("restarting", reason);
|
|
204
222
|
this.scheduleRestart();
|
|
205
223
|
}
|
|
206
224
|
|
|
225
|
+
private report(state: SharedCallmuxHealthSnapshot["state"], reason?: string, pid?: number): void {
|
|
226
|
+
this.deps.report({
|
|
227
|
+
state,
|
|
228
|
+
url: this.opts.url,
|
|
229
|
+
command: this.opts.command,
|
|
230
|
+
...(reason ? { reason } : {}),
|
|
231
|
+
...(pid ? { pid } : {}),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
207
235
|
private scheduleRestart(): void {
|
|
208
236
|
if (this.stopping || this.restartTimer) return;
|
|
209
237
|
const delay = this.backoffMs;
|
|
@@ -238,15 +266,26 @@ function defaultDeps(): SharedCallmuxSupervisorDeps {
|
|
|
238
266
|
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
239
267
|
clearTimeout: (timer) => clearTimeout(timer),
|
|
240
268
|
log: (message) => console.error(message),
|
|
269
|
+
report: (snapshot) => console.error(`[orchestrator] shared callmux status ${snapshot.state}${snapshot.reason ? `: ${snapshot.reason}` : ""}`),
|
|
241
270
|
};
|
|
242
271
|
}
|
|
243
272
|
|
|
273
|
+
export function bundledCallmuxCommand(): string | null {
|
|
274
|
+
const candidate = resolve(import.meta.dir, "../vendor/callmux/bin/callmux.js");
|
|
275
|
+
return existsSync(candidate) ? candidate : null;
|
|
276
|
+
}
|
|
277
|
+
|
|
244
278
|
function resolveCommand(command: string): string | null {
|
|
245
279
|
if (isAbsolute(command)) return existsSync(command) ? command : null;
|
|
246
280
|
if (command.includes("/")) return existsSync(command) ? command : null;
|
|
247
281
|
return Bun.which(command);
|
|
248
282
|
}
|
|
249
283
|
|
|
284
|
+
function envOff(value: string | undefined): boolean {
|
|
285
|
+
if (value === undefined || value === null || value === "") return false;
|
|
286
|
+
return ["0", "false", "off", "no"].includes(value.trim().toLowerCase());
|
|
287
|
+
}
|
|
288
|
+
|
|
250
289
|
function readJsonObject(path: string): Record<string, unknown> {
|
|
251
290
|
if (!existsSync(path)) return {};
|
|
252
291
|
const parsed = JSON.parse(readFileSync(path, "utf8"));
|