agent-relay-runner 0.60.1 → 0.61.1
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 +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/src/config.ts +66 -2
- package/src/index.ts +11 -9
- package/src/providers.ts +18 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.61.1",
|
|
4
4
|
"description": "Unified provider lifecycle runner for Agent Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"directory": "runner"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"agent-relay-sdk": "0.2.
|
|
23
|
+
"agent-relay-sdk": "0.2.37"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/bun": "latest",
|
package/src/config.ts
CHANGED
|
@@ -2,8 +2,8 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { homedir, hostname } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
-
import { DEFAULT_RELAY_URL, stringValue } from "agent-relay-sdk";
|
|
6
|
-
import type { WorkspaceMetadata } from "agent-relay-sdk";
|
|
5
|
+
import { DEFAULT_RELAY_URL, RELAY_TOKEN_HEADER, errMessage, stringValue } from "agent-relay-sdk";
|
|
6
|
+
import type { SettingEntry, WorkspaceMetadata } from "agent-relay-sdk";
|
|
7
7
|
import { sanitizeFsName } from "agent-relay-sdk/fs-name";
|
|
8
8
|
import type { ProviderConfig } from "./adapter";
|
|
9
9
|
|
|
@@ -36,6 +36,7 @@ export function defaultProviderConfig(provider: string): ProviderConfig {
|
|
|
36
36
|
defaultApprovalMode: "guarded",
|
|
37
37
|
defaultTags: [],
|
|
38
38
|
chatCaptureMode: "final",
|
|
39
|
+
reasoningCapture: true,
|
|
39
40
|
headless: {
|
|
40
41
|
tmuxPrefix: `${provider}-relay`,
|
|
41
42
|
shutdownTimeoutMs: 10_000,
|
|
@@ -67,6 +68,7 @@ export function loadProviderConfig(provider: string, home = agentRelayHome()): L
|
|
|
67
68
|
defaultApprovalMode: stringValue(raw.defaultApprovalMode) ?? defaults.defaultApprovalMode,
|
|
68
69
|
defaultTags: stringArray(raw.defaultTags) ?? defaults.defaultTags,
|
|
69
70
|
chatCaptureMode: enumValue(raw.chatCaptureMode, ["final", "full"]) ?? defaults.chatCaptureMode,
|
|
71
|
+
reasoningCapture: booleanValue(raw.reasoningCapture) ?? defaults.reasoningCapture,
|
|
70
72
|
headless: {
|
|
71
73
|
tmuxPrefix: stringValue(recordValue(raw.headless).tmuxPrefix) ?? defaults.headless.tmuxPrefix,
|
|
72
74
|
shutdownTimeoutMs: positiveInteger(recordValue(raw.headless).shutdownTimeoutMs) ?? defaults.headless.shutdownTimeoutMs,
|
|
@@ -74,6 +76,24 @@ export function loadProviderConfig(provider: string, home = agentRelayHome()): L
|
|
|
74
76
|
};
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
export async function loadProviderConfigFromRelay(
|
|
80
|
+
provider: string,
|
|
81
|
+
opts: { relayUrl: string; token?: string; home?: string; host?: string },
|
|
82
|
+
): Promise<LoadedProviderConfig> {
|
|
83
|
+
const host = opts.host ?? hostname();
|
|
84
|
+
const home = opts.home ?? agentRelayHome();
|
|
85
|
+
const local = loadProviderConfig(provider, home);
|
|
86
|
+
const key = `${host}/${provider}`;
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const fetched = await fetchProviderConfig(provider, opts.relayUrl, opts.token, key);
|
|
90
|
+
if (fetched) return fetched;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.warn(`[agent-relay] provider config relay read failed (${errMessage(error)}); using local fallback ${local.path}.`);
|
|
93
|
+
}
|
|
94
|
+
return local;
|
|
95
|
+
}
|
|
96
|
+
|
|
77
97
|
export function writeProviderConfig(provider: string, config: ProviderConfig, home = agentRelayHome()): LoadedProviderConfig {
|
|
78
98
|
const dir = providersDir(home);
|
|
79
99
|
mkdirSync(dir, { recursive: true });
|
|
@@ -93,6 +113,7 @@ export function providerConfigPublic(config: LoadedProviderConfig): Record<strin
|
|
|
93
113
|
defaultApprovalMode: config.defaultApprovalMode,
|
|
94
114
|
defaultTags: config.defaultTags,
|
|
95
115
|
chatCaptureMode: config.chatCaptureMode,
|
|
116
|
+
reasoningCapture: config.reasoningCapture,
|
|
96
117
|
headless: config.headless,
|
|
97
118
|
};
|
|
98
119
|
}
|
|
@@ -156,6 +177,10 @@ function enumValue<T extends string>(value: unknown, allowed: T[]): T | undefine
|
|
|
156
177
|
return typeof value === "string" && allowed.includes(value as T) ? (value as T) : undefined;
|
|
157
178
|
}
|
|
158
179
|
|
|
180
|
+
function booleanValue(value: unknown): boolean | undefined {
|
|
181
|
+
return typeof value === "boolean" ? value : undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
159
184
|
function stringArray(value: unknown): string[] | undefined {
|
|
160
185
|
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : undefined;
|
|
161
186
|
}
|
|
@@ -178,3 +203,42 @@ function maskEnv(env: Record<string, string>): Record<string, string> {
|
|
|
178
203
|
}
|
|
179
204
|
return result;
|
|
180
205
|
}
|
|
206
|
+
|
|
207
|
+
async function fetchProviderConfig(
|
|
208
|
+
provider: string,
|
|
209
|
+
relayUrl: string,
|
|
210
|
+
token: string | undefined,
|
|
211
|
+
key: string,
|
|
212
|
+
): Promise<LoadedProviderConfig | null> {
|
|
213
|
+
const url = new URL(`/api/settings/provider-config/${encodeURIComponent(key)}`, relayUrl);
|
|
214
|
+
const res = await fetch(url, {
|
|
215
|
+
headers: token ? { [RELAY_TOKEN_HEADER]: token } : undefined,
|
|
216
|
+
signal: AbortSignal.timeout(5_000),
|
|
217
|
+
});
|
|
218
|
+
if (res.status === 404) return null;
|
|
219
|
+
const body = await res.json().catch(() => null) as (Partial<SettingEntry> & { error?: string }) | null;
|
|
220
|
+
if (!res.ok) throw new Error(body?.error ? `${res.status}: ${body.error}` : `HTTP ${res.status}`);
|
|
221
|
+
if (body?.version === 0) return null;
|
|
222
|
+
return providerConfigFromValue(provider, body?.value, `config:provider-config/${key}`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function providerConfigFromValue(provider: string, value: unknown, path: string): LoadedProviderConfig {
|
|
226
|
+
const raw = recordValue(value);
|
|
227
|
+
const defaults = defaultProviderConfig(provider);
|
|
228
|
+
return {
|
|
229
|
+
path,
|
|
230
|
+
command: stringValue(raw.command) ?? defaults.command,
|
|
231
|
+
defaultArgs: stringArray(raw.defaultArgs) ?? defaults.defaultArgs,
|
|
232
|
+
env: stringRecord(raw.env) ?? defaults.env,
|
|
233
|
+
pluginDirs: stringArray(raw.pluginDirs) ?? defaults.pluginDirs,
|
|
234
|
+
defaultCapabilities: stringArray(raw.defaultCapabilities) ?? defaults.defaultCapabilities,
|
|
235
|
+
defaultApprovalMode: stringValue(raw.defaultApprovalMode) ?? defaults.defaultApprovalMode,
|
|
236
|
+
defaultTags: stringArray(raw.defaultTags) ?? defaults.defaultTags,
|
|
237
|
+
chatCaptureMode: enumValue(raw.chatCaptureMode, ["final", "full"]) ?? defaults.chatCaptureMode,
|
|
238
|
+
reasoningCapture: booleanValue(raw.reasoningCapture) ?? defaults.reasoningCapture,
|
|
239
|
+
headless: {
|
|
240
|
+
tmuxPrefix: stringValue(recordValue(raw.headless).tmuxPrefix) ?? defaults.headless.tmuxPrefix,
|
|
241
|
+
shutdownTimeoutMs: positiveInteger(recordValue(raw.headless).shutdownTimeoutMs) ?? defaults.headless.shutdownTimeoutMs,
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
import { hostname } from "node:os";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
import { isatty } from "node:tty";
|
|
5
|
-
import { ClaudeAdapter } from "./adapters/claude";
|
|
6
|
-
import { CodexAdapter } from "./adapters/codex";
|
|
7
5
|
import { AgentRunner } from "./runner";
|
|
8
|
-
import { loadGlobalConfig,
|
|
6
|
+
import { loadGlobalConfig, loadProviderConfigFromRelay, resolveCwd, runnerId } from "./config";
|
|
7
|
+
import { createProviderAdapter } from "./providers";
|
|
9
8
|
import { VERSION } from "./version";
|
|
10
|
-
import type { AgentProfile, WorkspaceMetadata } from "agent-relay-sdk";
|
|
9
|
+
import type { AgentProfile, SpawnProvider, WorkspaceMetadata } from "agent-relay-sdk";
|
|
11
10
|
import { errMessage, normalizeAgentLifecycle, RELAY_TOKEN_HEADER } from "agent-relay-sdk";
|
|
12
11
|
|
|
13
12
|
interface CliOptions {
|
|
14
|
-
provider:
|
|
13
|
+
provider: SpawnProvider;
|
|
15
14
|
model?: string;
|
|
16
15
|
effort?: string;
|
|
17
16
|
headless: boolean;
|
|
@@ -38,11 +37,14 @@ export async function main(argv = process.argv): Promise<void> {
|
|
|
38
37
|
}
|
|
39
38
|
const opts = parseArgs(argv);
|
|
40
39
|
const globalConfig = loadGlobalConfig();
|
|
41
|
-
const
|
|
40
|
+
const relayUrl = opts.relayUrl ?? globalConfig.relayUrl;
|
|
41
|
+
const providerConfig = await loadProviderConfigFromRelay(opts.provider, {
|
|
42
|
+
relayUrl,
|
|
43
|
+
token: opts.token ?? globalConfig.token,
|
|
44
|
+
});
|
|
42
45
|
const cwd = resolveCwd(opts.cwd, globalConfig.defaultCwd);
|
|
43
46
|
const id = runnerId(opts.provider, cwd, opts.label);
|
|
44
|
-
const adapter = opts.provider
|
|
45
|
-
const relayUrl = opts.relayUrl ?? globalConfig.relayUrl;
|
|
47
|
+
const adapter = createProviderAdapter(opts.provider);
|
|
46
48
|
const runtimeAuth = await resolveRunnerToken({
|
|
47
49
|
interactive: opts.interactive,
|
|
48
50
|
relayUrl,
|
|
@@ -212,7 +214,7 @@ export function isVersionRequest(argv: string[]): boolean {
|
|
|
212
214
|
|
|
213
215
|
function parseArgs(argv: string[]): CliOptions {
|
|
214
216
|
const bin = [argv[1], process.env._].map((s) => basename(s || "")).join(" ");
|
|
215
|
-
let provider:
|
|
217
|
+
let provider: SpawnProvider = bin.includes("claude") ? "claude" : "codex";
|
|
216
218
|
const relayArgs = argv.slice(2);
|
|
217
219
|
const providerSep = relayArgs.indexOf("--");
|
|
218
220
|
const ownArgs = providerSep >= 0 ? relayArgs.slice(0, providerSep) : relayArgs;
|
package/src/providers.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SpawnProvider } from "agent-relay-sdk";
|
|
2
|
+
import type { ProviderAdapter } from "./adapter";
|
|
3
|
+
import { ClaudeAdapter } from "./adapters/claude";
|
|
4
|
+
import { CodexAdapter } from "./adapters/codex";
|
|
5
|
+
|
|
6
|
+
interface RunnerProviderRegistration {
|
|
7
|
+
provider: SpawnProvider;
|
|
8
|
+
createAdapter: () => ProviderAdapter;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const RUNNER_PROVIDERS: Record<SpawnProvider, RunnerProviderRegistration> = {
|
|
12
|
+
claude: { provider: "claude", createAdapter: () => new ClaudeAdapter() },
|
|
13
|
+
codex: { provider: "codex", createAdapter: () => new CodexAdapter() },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function createProviderAdapter(provider: SpawnProvider): ProviderAdapter {
|
|
17
|
+
return RUNNER_PROVIDERS[provider].createAdapter();
|
|
18
|
+
}
|