agent-relay-orchestrator 0.74.0 → 0.76.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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/self-upgrade.ts +16 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-orchestrator",
3
- "version": "0.74.0",
3
+ "version": "0.76.0",
4
4
  "description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "test": "bun test"
17
17
  },
18
18
  "dependencies": {
19
- "agent-relay-sdk": "0.2.49"
19
+ "agent-relay-sdk": "0.2.51"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "latest",
@@ -1,6 +1,7 @@
1
1
  import { join } from "node:path";
2
2
  import { existsSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
+ import { runInstallWithRetry } from "agent-relay-sdk";
4
5
  import type { OrchestratorConfig } from "./config";
5
6
  import type { RelayClient, RelayCommand } from "./relay";
6
7
  import { detectSelfSupervision, type SelfSupervision } from "./self-supervision";
@@ -8,16 +9,6 @@ import { detectSelfSupervision, type SelfSupervision } from "./self-supervision"
8
9
  const VALID_PROVIDERS = new Set(["auto", "all", "codex", "claude", "orchestrator"]);
9
10
  const SEMVER_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
10
11
 
11
- /**
12
- * Stale-packument cache race: right after a publish, a remote host's cached npm
13
- * metadata may not yet list the new version, so `npm install ...@X` fails with
14
- * ETARGET / "No matching version found" even though it IS published (#211). The
15
- * packument refreshes within seconds; `--prefer-online` + retry recovers it.
16
- */
17
- const CACHE_RACE_RE = /ETARGET|No matching version found|notarget/i;
18
- const DEFAULT_INSTALL_RETRIES = 4;
19
- const DEFAULT_INSTALL_RETRY_BASE_MS = 2000;
20
-
21
12
  interface SelfUpgradeOptions {
22
13
  /** Sleep between install retries (injectable for tests). */
23
14
  sleep?: (ms: number) => Promise<void>;
@@ -29,10 +20,6 @@ interface SelfUpgradeOptions {
29
20
  supervision?: SelfSupervision;
30
21
  }
31
22
 
32
- function isCacheRaceError(output: string): boolean {
33
- return CACHE_RACE_RE.test(output);
34
- }
35
-
36
23
  export interface SelfUpgradeRunner {
37
24
  run(cmd: string[]): Promise<{ exitCode: number; stdout: string; stderr: string }>;
38
25
  commandExists(name: string): boolean;
@@ -137,16 +124,21 @@ export async function handleSelfUpgrade(
137
124
  unit: plan.unit,
138
125
  });
139
126
 
140
- const install = await runInstallWithRetry(plan, runner, opts, async (attempt, delayMs) => {
141
- await relay.updateCommand(command.id, "running", {
142
- phase: "installing",
143
- targetVersion: plan.targetVersion,
144
- unit: plan.unit,
145
- retry: attempt,
146
- retryDelayMs: delayMs,
147
- note: `target ${plan.targetVersion} not yet visible to this host's npm cache; retrying (attempt ${attempt})`,
148
- });
149
- console.error(`[orchestrator] self-upgrade install hit a stale-cache race for ${plan.targetVersion}; retry ${attempt} in ${delayMs}ms`);
127
+ const install = await runInstallWithRetry(plan.installCmd, runner.run.bind(runner), {
128
+ sleep: opts.sleep,
129
+ retries: opts.installRetries,
130
+ baseDelayMs: opts.installRetryBaseMs,
131
+ onRetry: async ({ attempt, delayMs }) => {
132
+ await relay.updateCommand(command.id, "running", {
133
+ phase: "installing",
134
+ targetVersion: plan.targetVersion,
135
+ unit: plan.unit,
136
+ retry: attempt,
137
+ retryDelayMs: delayMs,
138
+ note: `target ${plan.targetVersion} not yet visible to this host's npm cache; retrying (attempt ${attempt})`,
139
+ });
140
+ console.error(`[orchestrator] self-upgrade install hit a stale-cache race for ${plan.targetVersion}; retry ${attempt} in ${delayMs}ms`);
141
+ },
150
142
  });
151
143
  if (install.exitCode !== 0) {
152
144
  throw new Error(`install failed (exit ${install.exitCode}): ${(install.stderr || install.stdout).trim().slice(-500)}`);
@@ -167,32 +159,6 @@ export async function handleSelfUpgrade(
167
159
  console.error(`[orchestrator] self-upgrade to ${plan.targetVersion} installed; restart dispatched for ${plan.unit}`);
168
160
  }
169
161
 
170
- /**
171
- * Run the install, retrying with exponential backoff when it fails on a
172
- * stale-packument cache race (#211). Non-cache-race failures return immediately
173
- * so genuine errors aren't masked by retries.
174
- */
175
- async function runInstallWithRetry(
176
- plan: SelfUpgradePlan,
177
- runner: SelfUpgradeRunner,
178
- opts: SelfUpgradeOptions,
179
- onRetry: (attempt: number, delayMs: number) => Promise<void>,
180
- ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
181
- const retries = opts.installRetries ?? DEFAULT_INSTALL_RETRIES;
182
- const baseMs = opts.installRetryBaseMs ?? DEFAULT_INSTALL_RETRY_BASE_MS;
183
- const sleep = opts.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
184
-
185
- let result = await runner.run(plan.installCmd);
186
- for (let attempt = 1; attempt <= retries; attempt++) {
187
- if (result.exitCode === 0 || !isCacheRaceError(result.stderr || result.stdout)) break;
188
- const delayMs = baseMs * 2 ** (attempt - 1);
189
- await onRetry(attempt, delayMs);
190
- await sleep(delayMs);
191
- result = await runner.run(plan.installCmd);
192
- }
193
- return result;
194
- }
195
-
196
162
  function normalizeProviders(value: unknown): string[] {
197
163
  const list = Array.isArray(value)
198
164
  ? value.filter((v): v is string => typeof v === "string").map((v) => v.trim()).filter(Boolean)