agent-relay 2.1.21 → 2.1.23-beta.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/bin/relay-pty-darwin-arm64 +0 -0
- package/bin/relay-pty-darwin-x64 +0 -0
- package/bin/relay-pty-linux-arm64 +0 -0
- package/bin/relay-pty-linux-x64 +0 -0
- package/dist/index.cjs +344 -65
- package/dist/src/cli/commands/doctor.d.ts.map +1 -1
- package/dist/src/cli/commands/doctor.js +14 -2
- package/dist/src/cli/commands/doctor.js.map +1 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +425 -0
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +18 -18
- package/packages/acp-bridge/dist/acp-agent.d.ts +18 -0
- package/packages/acp-bridge/dist/acp-agent.d.ts.map +1 -1
- package/packages/acp-bridge/dist/acp-agent.js +126 -38
- package/packages/acp-bridge/dist/acp-agent.js.map +1 -1
- package/packages/acp-bridge/package.json +2 -2
- package/packages/acp-bridge/src/acp-agent.ts +146 -46
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +5 -5
- package/packages/bridge/dist/spawner.d.ts +19 -0
- package/packages/bridge/dist/spawner.d.ts.map +1 -1
- package/packages/bridge/dist/spawner.js +86 -3
- package/packages/bridge/dist/spawner.js.map +1 -1
- package/packages/bridge/dist/types.d.ts +6 -0
- package/packages/bridge/dist/types.d.ts.map +1 -1
- package/packages/bridge/package.json +7 -7
- package/packages/bridge/src/spawner.ts +103 -3
- package/packages/bridge/src/types.ts +6 -0
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +2 -2
- package/packages/daemon/dist/server.d.ts.map +1 -1
- package/packages/daemon/dist/server.js +10 -0
- package/packages/daemon/dist/server.js.map +1 -1
- package/packages/daemon/dist/spawn-manager.d.ts +15 -1
- package/packages/daemon/dist/spawn-manager.d.ts.map +1 -1
- package/packages/daemon/dist/spawn-manager.js +40 -0
- package/packages/daemon/dist/spawn-manager.js.map +1 -1
- package/packages/daemon/package.json +12 -12
- package/packages/daemon/src/server.ts +12 -0
- package/packages/daemon/src/spawn-manager-set-model.test.ts +144 -0
- package/packages/daemon/src/spawn-manager.ts +53 -0
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/dist/client-adapter.d.ts +9 -0
- package/packages/mcp/dist/client-adapter.d.ts.map +1 -1
- package/packages/mcp/dist/client-adapter.js +4 -0
- package/packages/mcp/dist/client-adapter.js.map +1 -1
- package/packages/mcp/dist/server.d.ts.map +1 -1
- package/packages/mcp/dist/server.js +7 -1
- package/packages/mcp/dist/server.js.map +1 -1
- package/packages/mcp/dist/tools/index.d.ts +1 -0
- package/packages/mcp/dist/tools/index.d.ts.map +1 -1
- package/packages/mcp/dist/tools/index.js +1 -0
- package/packages/mcp/dist/tools/index.js.map +1 -1
- package/packages/mcp/dist/tools/relay-set-model.d.ts +23 -0
- package/packages/mcp/dist/tools/relay-set-model.d.ts.map +1 -0
- package/packages/mcp/dist/tools/relay-set-model.js +52 -0
- package/packages/mcp/dist/tools/relay-set-model.js.map +1 -0
- package/packages/mcp/package.json +5 -5
- package/packages/mcp/src/client-adapter.ts +12 -0
- package/packages/mcp/src/server.ts +10 -0
- package/packages/mcp/src/tools/index.ts +7 -0
- package/packages/mcp/src/tools/relay-set-model.ts +62 -0
- package/packages/mcp/tests/tools.test.ts +120 -0
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/dist/types.d.ts +34 -1
- package/packages/protocol/dist/types.d.ts.map +1 -1
- package/packages/protocol/package.json +1 -1
- package/packages/protocol/src/types.ts +37 -0
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/dist/client.d.ts +18 -1
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +63 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/package.json +3 -3
- package/packages/sdk/src/client.ts +87 -0
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/storage/src/dlq-adapter.test.ts +33 -16
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/dist/cjs/index.js +2 -0
- package/packages/utils/dist/cjs/model-commands.js +98 -0
- package/packages/utils/dist/cjs/relay-pty-path.js +39 -2
- package/packages/utils/dist/index.d.ts +1 -0
- package/packages/utils/dist/index.d.ts.map +1 -1
- package/packages/utils/dist/index.js +1 -0
- package/packages/utils/dist/index.js.map +1 -1
- package/packages/utils/dist/model-commands.d.ts +42 -0
- package/packages/utils/dist/model-commands.d.ts.map +1 -0
- package/packages/utils/dist/model-commands.js +90 -0
- package/packages/utils/dist/model-commands.js.map +1 -0
- package/packages/utils/dist/relay-pty-path.d.ts.map +1 -1
- package/packages/utils/dist/relay-pty-path.js +54 -2
- package/packages/utils/dist/relay-pty-path.js.map +1 -1
- package/packages/utils/package.json +9 -3
- package/packages/utils/src/index.ts +1 -0
- package/packages/utils/src/model-commands.test.ts +269 -0
- package/packages/utils/src/model-commands.ts +118 -0
- package/packages/utils/src/relay-pty-path.test.ts +136 -12
- package/packages/utils/src/relay-pty-path.ts +55 -2
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts +5 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.js +32 -9
- package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -1
- package/packages/wrapper/package.json +6 -6
- package/packages/wrapper/src/relay-pty-orchestrator.ts +35 -10
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Command Registry
|
|
3
|
+
*
|
|
4
|
+
* Maps CLI types to their mid-session model-switch commands.
|
|
5
|
+
* Used by the spawner to send model switch commands to running agents.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Model command configuration for a specific CLI type.
|
|
10
|
+
*/
|
|
11
|
+
export interface ModelCommandConfig {
|
|
12
|
+
/** Whether this CLI supports mid-session model switching */
|
|
13
|
+
supported: boolean;
|
|
14
|
+
/** Function to generate the command string for switching to a given model */
|
|
15
|
+
buildCommand?: (model: string) => string;
|
|
16
|
+
/** Valid model names for this CLI */
|
|
17
|
+
validModels?: string[];
|
|
18
|
+
/** Normalize a model name to the CLI's expected format */
|
|
19
|
+
normalizeModel?: (model: string) => string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Model name aliases for Claude Code */
|
|
23
|
+
const CLAUDE_MODEL_ALIASES: Record<string, string> = {
|
|
24
|
+
'claude-opus-4': 'opus',
|
|
25
|
+
'claude-opus-4.5': 'opus',
|
|
26
|
+
'claude-opus-4-6': 'opus',
|
|
27
|
+
'claude-sonnet-4': 'sonnet',
|
|
28
|
+
'claude-sonnet-4-5': 'sonnet',
|
|
29
|
+
'claude-haiku-3.5': 'haiku',
|
|
30
|
+
'claude-haiku-4.5': 'haiku',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const CLAUDE_VALID_MODELS = ['opus', 'sonnet', 'haiku'];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Registry of CLI model switch commands.
|
|
37
|
+
* Claude-first; other CLIs can be added as they gain support.
|
|
38
|
+
*/
|
|
39
|
+
const CLI_MODEL_COMMANDS: Record<string, ModelCommandConfig> = {
|
|
40
|
+
claude: {
|
|
41
|
+
supported: true,
|
|
42
|
+
buildCommand: (model: string) => `/model ${model}\n`,
|
|
43
|
+
validModels: [
|
|
44
|
+
...CLAUDE_VALID_MODELS,
|
|
45
|
+
...Object.keys(CLAUDE_MODEL_ALIASES),
|
|
46
|
+
],
|
|
47
|
+
normalizeModel: (model: string) => {
|
|
48
|
+
const normalized = model.trim().toLowerCase();
|
|
49
|
+
return CLAUDE_MODEL_ALIASES[normalized] ?? normalized;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
codex: { supported: false },
|
|
53
|
+
gemini: { supported: false },
|
|
54
|
+
droid: { supported: false },
|
|
55
|
+
opencode: { supported: false },
|
|
56
|
+
aider: { supported: false },
|
|
57
|
+
goose: { supported: false },
|
|
58
|
+
cursor: { supported: false },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get the model command configuration for a CLI type.
|
|
63
|
+
*/
|
|
64
|
+
export function getModelCommandConfig(cli: string): ModelCommandConfig {
|
|
65
|
+
const baseCli = cli.split(':')[0].toLowerCase();
|
|
66
|
+
return CLI_MODEL_COMMANDS[baseCli] ?? { supported: false };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if a CLI type supports mid-session model switching.
|
|
71
|
+
*/
|
|
72
|
+
export function isModelSwitchSupported(cli: string): boolean {
|
|
73
|
+
return getModelCommandConfig(cli).supported;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Build the command string to switch models for a given CLI.
|
|
78
|
+
* Returns null if the CLI doesn't support model switching.
|
|
79
|
+
*/
|
|
80
|
+
export function buildModelSwitchCommand(cli: string, model: string): string | null {
|
|
81
|
+
const config = getModelCommandConfig(cli);
|
|
82
|
+
if (!config.supported || !config.buildCommand) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const normalizedModel = config.normalizeModel ? config.normalizeModel(model) : model;
|
|
87
|
+
return config.buildCommand(normalizedModel);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Validate a model name for a given CLI.
|
|
92
|
+
* Returns the normalized model name if valid.
|
|
93
|
+
*/
|
|
94
|
+
export function validateModelForCli(
|
|
95
|
+
cli: string,
|
|
96
|
+
model: string,
|
|
97
|
+
): { valid: boolean; error?: string; normalizedModel?: string } {
|
|
98
|
+
const config = getModelCommandConfig(cli);
|
|
99
|
+
|
|
100
|
+
if (!config.supported) {
|
|
101
|
+
return {
|
|
102
|
+
valid: false,
|
|
103
|
+
error: `CLI "${cli}" does not support mid-session model switching`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const normalizedModel = config.normalizeModel ? config.normalizeModel(model) : model;
|
|
108
|
+
|
|
109
|
+
if (config.validModels && !config.validModels.includes(normalizedModel)) {
|
|
110
|
+
const displayModels = config.validModels.filter(m => !m.includes('-')); // Show short names only
|
|
111
|
+
return {
|
|
112
|
+
valid: false,
|
|
113
|
+
error: `Invalid model "${model}" for CLI "${cli}". Valid models: ${displayModels.join(', ')}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return { valid: true, normalizedModel };
|
|
118
|
+
}
|
|
@@ -14,8 +14,10 @@
|
|
|
14
14
|
* 10. Environment variable override
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
17
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
18
18
|
import nodePath from 'node:path';
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import os from 'node:os';
|
|
19
21
|
import {
|
|
20
22
|
findRelayPtyBinary,
|
|
21
23
|
getLastSearchPaths,
|
|
@@ -332,20 +334,42 @@ describe('findRelayPtyBinary - search path verification', () => {
|
|
|
332
334
|
});
|
|
333
335
|
|
|
334
336
|
describe('Environment variable override', () => {
|
|
335
|
-
|
|
336
|
-
// Use an actual executable that exists on all Unix systems
|
|
337
|
-
// /bin/ls is guaranteed to exist and be executable
|
|
338
|
-
const executableFile = '/bin/ls';
|
|
339
|
-
process.env.RELAY_PTY_BINARY = executableFile;
|
|
337
|
+
let tmpDir: string;
|
|
340
338
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
expect(result).toBe(executableFile);
|
|
339
|
+
beforeEach(() => {
|
|
340
|
+
tmpDir = fs.mkdtempSync(nodePath.join(os.tmpdir(), 'relay-pty-env-test-'));
|
|
341
|
+
});
|
|
345
342
|
|
|
343
|
+
afterEach(() => {
|
|
344
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
346
345
|
delete process.env.RELAY_PTY_BINARY;
|
|
347
346
|
});
|
|
348
347
|
|
|
348
|
+
it('should use RELAY_PTY_BINARY when file is executable and platform-compatible', () => {
|
|
349
|
+
// Create a fake binary with correct magic bytes for the current platform
|
|
350
|
+
const binPath = nodePath.join(tmpDir, 'relay-pty');
|
|
351
|
+
const buf = Buffer.alloc(64);
|
|
352
|
+
if (process.platform === 'darwin') {
|
|
353
|
+
// Mach-O 64-bit little-endian magic
|
|
354
|
+
buf.writeUInt8(0xcf, 0);
|
|
355
|
+
buf.writeUInt8(0xfa, 1);
|
|
356
|
+
buf.writeUInt8(0xed, 2);
|
|
357
|
+
buf.writeUInt8(0xfe, 3);
|
|
358
|
+
} else {
|
|
359
|
+
// ELF magic
|
|
360
|
+
buf.writeUInt8(0x7f, 0);
|
|
361
|
+
buf.writeUInt8(0x45, 1);
|
|
362
|
+
buf.writeUInt8(0x4c, 2);
|
|
363
|
+
buf.writeUInt8(0x46, 3);
|
|
364
|
+
}
|
|
365
|
+
fs.writeFileSync(binPath, buf);
|
|
366
|
+
fs.chmodSync(binPath, 0o755);
|
|
367
|
+
|
|
368
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
369
|
+
const result = findRelayPtyBinary('/any/path');
|
|
370
|
+
expect(result).toBe(binPath);
|
|
371
|
+
});
|
|
372
|
+
|
|
349
373
|
it('should fall back to normal search when RELAY_PTY_BINARY file does not exist', () => {
|
|
350
374
|
process.env.RELAY_PTY_BINARY = '/nonexistent/path/relay-pty';
|
|
351
375
|
|
|
@@ -354,8 +378,6 @@ describe('findRelayPtyBinary - search path verification', () => {
|
|
|
354
378
|
|
|
355
379
|
// Should have searched multiple paths (not just the env var)
|
|
356
380
|
expect(paths.length).toBeGreaterThan(1);
|
|
357
|
-
|
|
358
|
-
delete process.env.RELAY_PTY_BINARY;
|
|
359
381
|
});
|
|
360
382
|
});
|
|
361
383
|
|
|
@@ -375,6 +397,108 @@ describe('findRelayPtyBinary - search path verification', () => {
|
|
|
375
397
|
});
|
|
376
398
|
});
|
|
377
399
|
|
|
400
|
+
describe('isPlatformCompatibleBinary - cross-platform binary validation', () => {
|
|
401
|
+
let tmpDir: string;
|
|
402
|
+
|
|
403
|
+
beforeEach(() => {
|
|
404
|
+
clearBinaryCache();
|
|
405
|
+
tmpDir = fs.mkdtempSync(nodePath.join(os.tmpdir(), 'relay-pty-test-'));
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
afterEach(() => {
|
|
409
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
410
|
+
delete process.env.RELAY_PTY_BINARY;
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
function createFakeBinary(name: string, magicBytes: number[]): string {
|
|
414
|
+
const filePath = nodePath.join(tmpDir, name);
|
|
415
|
+
const buf = Buffer.alloc(64);
|
|
416
|
+
for (let i = 0; i < magicBytes.length; i++) {
|
|
417
|
+
buf[i] = magicBytes[i];
|
|
418
|
+
}
|
|
419
|
+
fs.writeFileSync(filePath, buf);
|
|
420
|
+
fs.chmodSync(filePath, 0o755);
|
|
421
|
+
return filePath;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Mach-O 64-bit (as it appears on arm64/x64 little-endian systems)
|
|
425
|
+
const MACHO_CIGAM_64 = [0xcf, 0xfa, 0xed, 0xfe];
|
|
426
|
+
// Mach-O 64-bit native byte order
|
|
427
|
+
const MACHO_MAGIC_64 = [0xfe, 0xed, 0xfa, 0xcf];
|
|
428
|
+
// Universal/fat binary
|
|
429
|
+
const FAT_MAGIC = [0xca, 0xfe, 0xba, 0xbe];
|
|
430
|
+
// ELF magic: 7F 45 4C 46
|
|
431
|
+
const ELF_MAGIC = [0x7f, 0x45, 0x4c, 0x46];
|
|
432
|
+
|
|
433
|
+
it('should accept correct binary for current platform via RELAY_PTY_BINARY', () => {
|
|
434
|
+
const platform = process.platform;
|
|
435
|
+
const magic = platform === 'darwin' ? MACHO_CIGAM_64 : ELF_MAGIC;
|
|
436
|
+
const binPath = createFakeBinary('relay-pty', magic);
|
|
437
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
438
|
+
|
|
439
|
+
const result = findRelayPtyBinary('/any/path');
|
|
440
|
+
expect(result).toBe(binPath);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('should reject wrong-platform binary via RELAY_PTY_BINARY', () => {
|
|
444
|
+
const platform = process.platform;
|
|
445
|
+
// Use the opposite platform's magic bytes
|
|
446
|
+
const magic = platform === 'darwin' ? ELF_MAGIC : MACHO_CIGAM_64;
|
|
447
|
+
const binPath = createFakeBinary('relay-pty', magic);
|
|
448
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
449
|
+
|
|
450
|
+
const result = findRelayPtyBinary('/any/path');
|
|
451
|
+
// Should NOT return the env override since binary is for wrong platform
|
|
452
|
+
expect(result).not.toBe(binPath);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('should reject file too small to be a valid binary', () => {
|
|
456
|
+
const binPath = nodePath.join(tmpDir, 'relay-pty');
|
|
457
|
+
fs.writeFileSync(binPath, Buffer.from([0x00, 0x00])); // Only 2 bytes
|
|
458
|
+
fs.chmodSync(binPath, 0o755);
|
|
459
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
460
|
+
|
|
461
|
+
const result = findRelayPtyBinary('/any/path');
|
|
462
|
+
expect(result).not.toBe(binPath);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
if (process.platform === 'darwin') {
|
|
466
|
+
it('should accept MH_MAGIC_64 (native byte order) on macOS', () => {
|
|
467
|
+
const binPath = createFakeBinary('relay-pty', MACHO_MAGIC_64);
|
|
468
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
469
|
+
|
|
470
|
+
const result = findRelayPtyBinary('/any/path');
|
|
471
|
+
expect(result).toBe(binPath);
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('should accept fat/universal binaries on macOS', () => {
|
|
475
|
+
const binPath = createFakeBinary('relay-pty', FAT_MAGIC);
|
|
476
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
477
|
+
|
|
478
|
+
const result = findRelayPtyBinary('/any/path');
|
|
479
|
+
expect(result).toBe(binPath);
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (process.platform === 'linux') {
|
|
484
|
+
it('should reject Mach-O binaries on Linux', () => {
|
|
485
|
+
const binPath = createFakeBinary('relay-pty', MACHO_CIGAM_64);
|
|
486
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
487
|
+
|
|
488
|
+
const result = findRelayPtyBinary('/any/path');
|
|
489
|
+
expect(result).not.toBe(binPath);
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
it('should reject fat/universal binaries on Linux', () => {
|
|
493
|
+
const binPath = createFakeBinary('relay-pty', FAT_MAGIC);
|
|
494
|
+
process.env.RELAY_PTY_BINARY = binPath;
|
|
495
|
+
|
|
496
|
+
const result = findRelayPtyBinary('/any/path');
|
|
497
|
+
expect(result).not.toBe(binPath);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
|
|
378
502
|
describe('isPlatformSupported', () => {
|
|
379
503
|
it('should return true for current platform (darwin/linux arm64/x64)', () => {
|
|
380
504
|
const platform = process.platform;
|
|
@@ -93,7 +93,7 @@ export function getLastSearchPaths(): string[] {
|
|
|
93
93
|
export function findRelayPtyBinary(callerDirname: string): string | null {
|
|
94
94
|
// Check for explicit environment variable override first
|
|
95
95
|
const envOverride = process.env.RELAY_PTY_BINARY;
|
|
96
|
-
if (envOverride && isExecutable(envOverride)) {
|
|
96
|
+
if (envOverride && isExecutable(envOverride) && isPlatformCompatibleBinary(envOverride)) {
|
|
97
97
|
lastSearchPaths = [envOverride];
|
|
98
98
|
return envOverride;
|
|
99
99
|
}
|
|
@@ -266,7 +266,7 @@ export function findRelayPtyBinary(callerDirname: string): string | null {
|
|
|
266
266
|
lastSearchPaths = candidates;
|
|
267
267
|
|
|
268
268
|
for (const candidate of candidates) {
|
|
269
|
-
if (isExecutable(candidate)) {
|
|
269
|
+
if (isExecutable(candidate) && isPlatformCompatibleBinary(candidate)) {
|
|
270
270
|
return candidate;
|
|
271
271
|
}
|
|
272
272
|
}
|
|
@@ -287,6 +287,59 @@ function isExecutable(filePath: string): boolean {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Check if a binary is compatible with the current platform by reading its magic bytes.
|
|
292
|
+
* Prevents using a macOS binary on Linux or vice versa, which would fail at runtime
|
|
293
|
+
* with cryptic errors like "Syntax error: word unexpected".
|
|
294
|
+
*/
|
|
295
|
+
function isPlatformCompatibleBinary(filePath: string): boolean {
|
|
296
|
+
let fd: number | undefined;
|
|
297
|
+
try {
|
|
298
|
+
fd = fs.openSync(filePath, 'r');
|
|
299
|
+
const header = Buffer.alloc(4);
|
|
300
|
+
const bytesRead = fs.readSync(fd, header, 0, 4, 0);
|
|
301
|
+
if (bytesRead < 4) {
|
|
302
|
+
return false; // Too small to be a valid binary
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const magic = header.readUInt32BE(0);
|
|
306
|
+
const platform = os.platform();
|
|
307
|
+
|
|
308
|
+
if (platform === 'darwin') {
|
|
309
|
+
return isMachOBinary(magic);
|
|
310
|
+
}
|
|
311
|
+
if (platform === 'linux') {
|
|
312
|
+
// ELF magic: 0x7f 'E' 'L' 'F'
|
|
313
|
+
return magic === 0x7f454c46;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Unknown platform — don't block
|
|
317
|
+
return true;
|
|
318
|
+
} catch {
|
|
319
|
+
// Can't read file (e.g. execute-only permissions) — let execution attempt proceed
|
|
320
|
+
return true;
|
|
321
|
+
} finally {
|
|
322
|
+
if (fd !== undefined) {
|
|
323
|
+
try { fs.closeSync(fd); } catch { /* ignore close errors */ }
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Check if a magic value corresponds to any valid Mach-O format.
|
|
330
|
+
* Handles all variants: 32/64-bit, native/byte-swapped, and fat/universal.
|
|
331
|
+
*/
|
|
332
|
+
function isMachOBinary(magic: number): boolean {
|
|
333
|
+
return (
|
|
334
|
+
magic === 0xcffaedfe || // MH_CIGAM_64 — 64-bit, byte-swapped (arm64/x64 LE files read as BE)
|
|
335
|
+
magic === 0xfeedfacf || // MH_MAGIC_64 — 64-bit, native byte order
|
|
336
|
+
magic === 0xcefaedfe || // MH_CIGAM — 32-bit, byte-swapped
|
|
337
|
+
magic === 0xfeedface || // MH_MAGIC — 32-bit, native byte order
|
|
338
|
+
magic === 0xcafebabe || // FAT_MAGIC — universal/fat binary
|
|
339
|
+
magic === 0xbebafeca // FAT_CIGAM — universal/fat binary, byte-swapped
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
290
343
|
/**
|
|
291
344
|
* Check if relay-pty binary is available (cached).
|
|
292
345
|
* Returns true if the binary exists, false otherwise.
|
|
@@ -220,6 +220,11 @@ export declare class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
220
220
|
* Order: daemon socket → dashboard API → onRelease callback
|
|
221
221
|
*/
|
|
222
222
|
private executeReleaseWithFallbacks;
|
|
223
|
+
/**
|
|
224
|
+
* Register an agent's cwd with the dashboard API.
|
|
225
|
+
* Used after daemon socket spawns which bypass /api/spawn and its agentCwdMap.
|
|
226
|
+
*/
|
|
227
|
+
private registerCwdWithDashboard;
|
|
223
228
|
/**
|
|
224
229
|
* Spawn agent via dashboard API
|
|
225
230
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-pty-orchestrator.d.ts","sourceRoot":"","sources":["../src/relay-pty-orchestrator.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"relay-pty-orchestrator.d.ts","sourceRoot":"","sources":["../src/relay-pty-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -976,8 +976,8 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
976
976
|
switch (parsed.kind) {
|
|
977
977
|
case 'spawn':
|
|
978
978
|
if (parsed.spawn_name && parsed.spawn_cli) {
|
|
979
|
-
this.log(` Spawn detected: ${parsed.spawn_name} (${parsed.spawn_cli})`);
|
|
980
|
-
this.handleSpawnCommand(parsed.spawn_name, parsed.spawn_cli, parsed.spawn_task || '');
|
|
979
|
+
this.log(` Spawn detected: ${parsed.spawn_name} (${parsed.spawn_cli})${parsed.spawn_cwd ? ` in ${parsed.spawn_cwd}` : ''}`);
|
|
980
|
+
this.handleSpawnCommand(parsed.spawn_name, parsed.spawn_cli, parsed.spawn_task || '', parsed.spawn_cwd);
|
|
981
981
|
}
|
|
982
982
|
break;
|
|
983
983
|
case 'release':
|
|
@@ -1072,7 +1072,7 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
1072
1072
|
* now handles it after waitUntilCliReady(). Sending it here would cause
|
|
1073
1073
|
* duplicate task delivery.
|
|
1074
1074
|
*/
|
|
1075
|
-
handleSpawnCommand(name, cli, task) {
|
|
1075
|
+
handleSpawnCommand(name, cli, task, cwd) {
|
|
1076
1076
|
const key = `spawn:${name}:${cli}`;
|
|
1077
1077
|
if (this.processedSpawnCommands.has(key)) {
|
|
1078
1078
|
this.log(`Spawn already processed: ${key}`);
|
|
@@ -1080,11 +1080,11 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
1080
1080
|
}
|
|
1081
1081
|
this.processedSpawnCommands.add(key);
|
|
1082
1082
|
// Log spawn attempts (only in debug mode to avoid TUI pollution)
|
|
1083
|
-
this.log(`SPAWN REQUEST: ${name} (${cli})`);
|
|
1083
|
+
this.log(`SPAWN REQUEST: ${name} (${cli})${cwd ? ` cwd=${cwd}` : ''}`);
|
|
1084
1084
|
this.log(` client=${this.client.state}, dashboardPort=${this.config.dashboardPort}, onSpawn=${!!this.config.onSpawn}`);
|
|
1085
1085
|
// Try daemon socket first (most reliable - daemon has relay-pty binary),
|
|
1086
1086
|
// then dashboard API, then onSpawn callback as final fallback.
|
|
1087
|
-
this.executeSpawnWithFallbacks(name, cli, task).catch(err => {
|
|
1087
|
+
this.executeSpawnWithFallbacks(name, cli, task, cwd).catch(err => {
|
|
1088
1088
|
this.logError(`SPAWN FAILED: ${name} - all methods exhausted: ${err.message}`);
|
|
1089
1089
|
});
|
|
1090
1090
|
}
|
|
@@ -1092,14 +1092,21 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
1092
1092
|
* Execute spawn with daemon-first fallback chain.
|
|
1093
1093
|
* Order: daemon socket → dashboard API → onSpawn callback
|
|
1094
1094
|
*/
|
|
1095
|
-
async executeSpawnWithFallbacks(name, cli, task) {
|
|
1095
|
+
async executeSpawnWithFallbacks(name, cli, task, cwd) {
|
|
1096
1096
|
// 1. Try daemon socket spawn first (daemon always has access to relay-pty)
|
|
1097
1097
|
if (this.client.state === 'READY') {
|
|
1098
1098
|
try {
|
|
1099
1099
|
this.log(`Spawning ${name} via daemon socket`);
|
|
1100
|
-
const result = await this.client.spawn({ name, cli, task });
|
|
1100
|
+
const result = await this.client.spawn({ name, cli, task, cwd });
|
|
1101
1101
|
if (result.success) {
|
|
1102
1102
|
this.log(`SPAWN SUCCESS: ${name} via daemon socket`);
|
|
1103
|
+
// Also register cwd with dashboard API so agentCwdMap is populated
|
|
1104
|
+
// (daemon socket spawn bypasses /api/spawn which normally sets this)
|
|
1105
|
+
if (cwd && this.config.dashboardPort) {
|
|
1106
|
+
this.registerCwdWithDashboard(name, cwd).catch(err => {
|
|
1107
|
+
this.log(`Failed to register cwd with dashboard: ${err.message}`);
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1103
1110
|
}
|
|
1104
1111
|
else {
|
|
1105
1112
|
// Daemon explicitly rejected - respect its decision (policy, duplicate, etc.)
|
|
@@ -1116,7 +1123,7 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
1116
1123
|
if (this.config.dashboardPort) {
|
|
1117
1124
|
try {
|
|
1118
1125
|
this.log(`Spawning ${name} via dashboard API at port ${this.config.dashboardPort}`);
|
|
1119
|
-
await this.spawnViaDashboardApi(name, cli, task);
|
|
1126
|
+
await this.spawnViaDashboardApi(name, cli, task, cwd);
|
|
1120
1127
|
this.log(`SPAWN SUCCESS: ${name} via dashboard API`);
|
|
1121
1128
|
return;
|
|
1122
1129
|
}
|
|
@@ -1185,16 +1192,32 @@ export class RelayPtyOrchestrator extends BaseWrapper {
|
|
|
1185
1192
|
}
|
|
1186
1193
|
throw new Error(`No release mechanism available (client=${this.client.state}, dashboardPort=${this.config.dashboardPort}, onRelease=${!!this.config.onRelease})`);
|
|
1187
1194
|
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Register an agent's cwd with the dashboard API.
|
|
1197
|
+
* Used after daemon socket spawns which bypass /api/spawn and its agentCwdMap.
|
|
1198
|
+
*/
|
|
1199
|
+
async registerCwdWithDashboard(name, cwd) {
|
|
1200
|
+
const url = `http://localhost:${this.config.dashboardPort}/api/agents/${encodeURIComponent(name)}/cwd`;
|
|
1201
|
+
const response = await fetch(url, {
|
|
1202
|
+
method: 'PUT',
|
|
1203
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1204
|
+
body: JSON.stringify({ cwd }),
|
|
1205
|
+
});
|
|
1206
|
+
if (!response.ok) {
|
|
1207
|
+
throw new Error(`HTTP ${response.status}`);
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1188
1210
|
/**
|
|
1189
1211
|
* Spawn agent via dashboard API
|
|
1190
1212
|
*/
|
|
1191
|
-
async spawnViaDashboardApi(name, cli, task) {
|
|
1213
|
+
async spawnViaDashboardApi(name, cli, task, cwd) {
|
|
1192
1214
|
const url = `http://localhost:${this.config.dashboardPort}/api/spawn`;
|
|
1193
1215
|
const body = {
|
|
1194
1216
|
name,
|
|
1195
1217
|
cli,
|
|
1196
1218
|
task,
|
|
1197
1219
|
spawnerName: this.config.name, // Include spawner name so task appears from correct agent
|
|
1220
|
+
cwd,
|
|
1198
1221
|
};
|
|
1199
1222
|
try {
|
|
1200
1223
|
const response = await fetch(url, {
|