agent-relay 2.1.23-beta.0 → 2.1.23-beta.2
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/dist/src/cli/index.js +157 -34
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +18 -18
- package/packages/acp-bridge/package.json +2 -2
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +5 -5
- package/packages/bridge/package.json +7 -7
- 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/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +5 -5
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/package.json +3 -3
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- 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/package.json +3 -3
- package/packages/wrapper/package.json +6 -6
package/dist/src/cli/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { RelayPtyOrchestrator, getTmuxPath } from '@agent-relay/wrapper';
|
|
|
24
24
|
import { AgentSpawner, readWorkersMetadata, getWorkerLogsDir, selectShadowCli, ensureMcpPermissions } from '@agent-relay/bridge';
|
|
25
25
|
import { generateAgentName, checkForUpdatesInBackground, checkForUpdates } from '@agent-relay/utils';
|
|
26
26
|
import { getShadowForAgent, getProjectPaths, loadRuntimeConfig } from '@agent-relay/config';
|
|
27
|
-
import { CLI_AUTH_CONFIG } from '@agent-relay/config/cli-auth-config';
|
|
27
|
+
import { CLI_AUTH_CONFIG, stripAnsiCodes } from '@agent-relay/config/cli-auth-config';
|
|
28
28
|
import { createStorageAdapter } from '@agent-relay/storage/adapter';
|
|
29
29
|
import { initTelemetry, track, enableTelemetry, disableTelemetry, getStatus, isDisabledByEnv, } from '@agent-relay/telemetry';
|
|
30
30
|
import { installMcpConfig } from '@agent-relay/mcp';
|
|
@@ -3818,6 +3818,9 @@ program
|
|
|
3818
3818
|
sshClient.end();
|
|
3819
3819
|
process.exit(1);
|
|
3820
3820
|
}
|
|
3821
|
+
// Get success/error patterns from CLI_AUTH_CONFIG for auto-detection
|
|
3822
|
+
const successPatterns = providerConfig.successPatterns || [];
|
|
3823
|
+
const errorPatterns = providerConfig.errorPatterns || [];
|
|
3821
3824
|
const execInteractive = async (cmd, timeoutMs) => {
|
|
3822
3825
|
return await new Promise((resolve, reject) => {
|
|
3823
3826
|
const cols = process.stdout.columns || 80;
|
|
@@ -3828,6 +3831,8 @@ program
|
|
|
3828
3831
|
return reject(err);
|
|
3829
3832
|
let exitCode = null;
|
|
3830
3833
|
let exitSignal = null;
|
|
3834
|
+
let authDetected = false;
|
|
3835
|
+
let outputBuffer = ''; // Rolling buffer for pattern matching
|
|
3831
3836
|
const stdin = process.stdin;
|
|
3832
3837
|
const stdout = process.stdout;
|
|
3833
3838
|
const stderr = process.stderr;
|
|
@@ -3843,8 +3848,50 @@ program
|
|
|
3843
3848
|
stream.write(data);
|
|
3844
3849
|
};
|
|
3845
3850
|
stdin.on('data', onStdinData);
|
|
3851
|
+
const cleanup = () => {
|
|
3852
|
+
stdin.off('data', onStdinData);
|
|
3853
|
+
stdout.off('resize', onResize);
|
|
3854
|
+
try {
|
|
3855
|
+
stdin.setRawMode?.(wasRaw);
|
|
3856
|
+
}
|
|
3857
|
+
catch {
|
|
3858
|
+
// ignore
|
|
3859
|
+
}
|
|
3860
|
+
stdin.pause();
|
|
3861
|
+
};
|
|
3862
|
+
// Auto-close the session when auth success is detected
|
|
3863
|
+
const closeOnAuthSuccess = () => {
|
|
3864
|
+
authDetected = true;
|
|
3865
|
+
// Brief delay so the user sees the success message
|
|
3866
|
+
setTimeout(() => {
|
|
3867
|
+
cleanup();
|
|
3868
|
+
clearTimeout(timer);
|
|
3869
|
+
try {
|
|
3870
|
+
stream.close();
|
|
3871
|
+
}
|
|
3872
|
+
catch {
|
|
3873
|
+
// ignore
|
|
3874
|
+
}
|
|
3875
|
+
}, 1500);
|
|
3876
|
+
};
|
|
3846
3877
|
stream.on('data', (data) => {
|
|
3847
3878
|
stdout.write(data);
|
|
3879
|
+
// Accumulate output for pattern matching (keep last 2KB to avoid memory growth)
|
|
3880
|
+
const text = data.toString();
|
|
3881
|
+
outputBuffer += text;
|
|
3882
|
+
if (outputBuffer.length > 2048) {
|
|
3883
|
+
outputBuffer = outputBuffer.slice(-2048);
|
|
3884
|
+
}
|
|
3885
|
+
// Check for auth success patterns
|
|
3886
|
+
if (!authDetected && successPatterns.length > 0) {
|
|
3887
|
+
const clean = stripAnsiCodes(outputBuffer);
|
|
3888
|
+
for (const pattern of successPatterns) {
|
|
3889
|
+
if (pattern.test(clean)) {
|
|
3890
|
+
closeOnAuthSuccess();
|
|
3891
|
+
break;
|
|
3892
|
+
}
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3848
3895
|
});
|
|
3849
3896
|
stream.stderr.on('data', (data) => {
|
|
3850
3897
|
stderr.write(data);
|
|
@@ -3858,16 +3905,6 @@ program
|
|
|
3858
3905
|
}
|
|
3859
3906
|
};
|
|
3860
3907
|
stdout.on('resize', onResize);
|
|
3861
|
-
const cleanup = () => {
|
|
3862
|
-
stdin.off('data', onStdinData);
|
|
3863
|
-
stdout.off('resize', onResize);
|
|
3864
|
-
try {
|
|
3865
|
-
stdin.setRawMode?.(wasRaw);
|
|
3866
|
-
}
|
|
3867
|
-
catch {
|
|
3868
|
-
// ignore
|
|
3869
|
-
}
|
|
3870
|
-
};
|
|
3871
3908
|
const timer = setTimeout(() => {
|
|
3872
3909
|
cleanup();
|
|
3873
3910
|
try {
|
|
@@ -3887,7 +3924,7 @@ program
|
|
|
3887
3924
|
stream.on('close', () => {
|
|
3888
3925
|
clearTimeout(timer);
|
|
3889
3926
|
cleanup();
|
|
3890
|
-
resolve({ exitCode, exitSignal });
|
|
3927
|
+
resolve({ exitCode, exitSignal, authDetected });
|
|
3891
3928
|
});
|
|
3892
3929
|
stream.on('error', (e) => {
|
|
3893
3930
|
clearTimeout(timer);
|
|
@@ -3901,7 +3938,7 @@ program
|
|
|
3901
3938
|
let execError = null;
|
|
3902
3939
|
try {
|
|
3903
3940
|
console.log(yellow('Starting interactive authentication...'));
|
|
3904
|
-
console.log(dim('Follow the prompts below.
|
|
3941
|
+
console.log(dim('Follow the prompts below. The session will close automatically when auth completes.'));
|
|
3905
3942
|
console.log('');
|
|
3906
3943
|
execResult = await execInteractive(remoteCommand, TIMEOUT_MS);
|
|
3907
3944
|
}
|
|
@@ -3918,7 +3955,8 @@ program
|
|
|
3918
3955
|
// Step 2: Notify cloud completion (cloud will verify and persist credentials).
|
|
3919
3956
|
console.log('');
|
|
3920
3957
|
console.log('Finalizing authentication with cloud...');
|
|
3921
|
-
|
|
3958
|
+
// Auth is successful if: success patterns were detected, OR the process exited cleanly (code 0)
|
|
3959
|
+
const success = execError === null && (execResult?.authDetected === true || execResult?.exitCode === 0);
|
|
3922
3960
|
const providerForComplete = (typeof start.provider === 'string' && start.provider.trim().length > 0)
|
|
3923
3961
|
? start.provider.trim()
|
|
3924
3962
|
: provider;
|
|
@@ -3970,17 +4008,42 @@ program
|
|
|
3970
4008
|
console.log('');
|
|
3971
4009
|
});
|
|
3972
4010
|
// ============================================================================
|
|
3973
|
-
//
|
|
4011
|
+
// cli-auth - SSH tunnel helper for provider authentication (Claude, Codex, Cursor, etc.)
|
|
3974
4012
|
// ============================================================================
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
4013
|
+
// Provider display names for CLI output
|
|
4014
|
+
const CLI_AUTH_DISPLAY_NAMES = {
|
|
4015
|
+
anthropic: 'Claude',
|
|
4016
|
+
openai: 'Codex',
|
|
4017
|
+
google: 'Gemini',
|
|
4018
|
+
cursor: 'Cursor',
|
|
4019
|
+
copilot: 'GitHub Copilot',
|
|
4020
|
+
opencode: 'OpenCode',
|
|
4021
|
+
droid: 'Droid',
|
|
4022
|
+
};
|
|
4023
|
+
// CLI command names per provider (used in help text)
|
|
4024
|
+
const CLI_AUTH_COMMAND_NAMES = {
|
|
4025
|
+
anthropic: 'claude',
|
|
4026
|
+
openai: 'codex',
|
|
4027
|
+
google: 'gemini',
|
|
4028
|
+
cursor: 'cursor',
|
|
4029
|
+
copilot: 'copilot',
|
|
4030
|
+
opencode: 'opencode',
|
|
4031
|
+
droid: 'droid',
|
|
4032
|
+
};
|
|
4033
|
+
// Provider alias mapping (CLI name → config key)
|
|
4034
|
+
const CLI_AUTH_PROVIDER_MAP = {
|
|
4035
|
+
claude: 'anthropic',
|
|
4036
|
+
codex: 'openai',
|
|
4037
|
+
gemini: 'google',
|
|
4038
|
+
};
|
|
4039
|
+
/**
|
|
4040
|
+
* Shared action handler for cli-auth and its provider-specific aliases.
|
|
4041
|
+
*/
|
|
4042
|
+
async function runCliAuth(providerArg, options) {
|
|
4043
|
+
// Resolve provider alias
|
|
4044
|
+
const provider = CLI_AUTH_PROVIDER_MAP[providerArg.toLowerCase()] || providerArg.toLowerCase();
|
|
4045
|
+
const displayName = CLI_AUTH_DISPLAY_NAMES[provider] || provider;
|
|
4046
|
+
const cliName = CLI_AUTH_COMMAND_NAMES[provider] || provider;
|
|
3984
4047
|
const TIMEOUT_MS = parseInt(options.timeout, 10) * 1000;
|
|
3985
4048
|
const CLOUD_URL = options.cloudUrl.replace(/\/$/, '');
|
|
3986
4049
|
const TUNNEL_PORT = 1455;
|
|
@@ -3992,25 +4055,26 @@ program
|
|
|
3992
4055
|
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
3993
4056
|
console.log('');
|
|
3994
4057
|
console.log(cyan('═══════════════════════════════════════════════════'));
|
|
3995
|
-
console.log(cyan(
|
|
4058
|
+
console.log(cyan(` ${displayName} Authentication Helper`));
|
|
3996
4059
|
console.log(cyan('═══════════════════════════════════════════════════'));
|
|
3997
4060
|
console.log('');
|
|
3998
4061
|
if (!options.workspace) {
|
|
3999
4062
|
console.log(red('Missing --workspace parameter.'));
|
|
4000
4063
|
console.log('');
|
|
4001
|
-
console.log(
|
|
4064
|
+
console.log(`To connect ${displayName}, follow these steps:`);
|
|
4002
4065
|
console.log('');
|
|
4003
4066
|
console.log(' 1. Go to the Agent Relay dashboard');
|
|
4004
|
-
console.log(
|
|
4067
|
+
console.log(` 2. Click "Connect with ${displayName}" (Settings → AI Providers)`);
|
|
4005
4068
|
console.log(' 3. Copy the command shown (it includes the workspace ID and token)');
|
|
4006
4069
|
console.log(' 4. Run the command in your terminal');
|
|
4007
4070
|
console.log('');
|
|
4008
4071
|
console.log('The command will look like:');
|
|
4009
|
-
console.log(cyan(
|
|
4072
|
+
console.log(cyan(` npx agent-relay cli-auth ${cliName} --workspace=<ID> --token=<TOKEN>`));
|
|
4010
4073
|
console.log('');
|
|
4011
4074
|
process.exit(1);
|
|
4012
4075
|
}
|
|
4013
4076
|
const workspaceId = options.workspace;
|
|
4077
|
+
console.log(`Provider: ${displayName}`);
|
|
4014
4078
|
console.log(`Workspace: ${workspaceId.slice(0, 8)}...`);
|
|
4015
4079
|
// Get tunnel info from cloud API
|
|
4016
4080
|
console.log('Getting workspace connection info...');
|
|
@@ -4022,18 +4086,19 @@ program
|
|
|
4022
4086
|
if (!options.token && !options.sessionCookie) {
|
|
4023
4087
|
console.log(red('Missing --token parameter.'));
|
|
4024
4088
|
console.log('');
|
|
4025
|
-
console.log(
|
|
4089
|
+
console.log(`The token is provided by the dashboard when you click "Connect with ${displayName}".`);
|
|
4026
4090
|
console.log('Copy the complete command from the dashboard and paste it here.');
|
|
4027
4091
|
console.log('');
|
|
4028
4092
|
process.exit(1);
|
|
4029
4093
|
}
|
|
4030
4094
|
let tunnelInfo;
|
|
4031
4095
|
try {
|
|
4032
|
-
// Build URL with token query
|
|
4096
|
+
// Build URL with token and provider query parameters
|
|
4033
4097
|
const tunnelInfoUrl = new URL(`${CLOUD_URL}/api/auth/codex-helper/tunnel-info/${workspaceId}`);
|
|
4034
4098
|
if (options.token) {
|
|
4035
4099
|
tunnelInfoUrl.searchParams.set('token', options.token);
|
|
4036
4100
|
}
|
|
4101
|
+
tunnelInfoUrl.searchParams.set('provider', provider);
|
|
4037
4102
|
const response = await fetch(tunnelInfoUrl.toString(), {
|
|
4038
4103
|
method: 'GET',
|
|
4039
4104
|
headers,
|
|
@@ -4162,8 +4227,8 @@ program
|
|
|
4162
4227
|
console.log('');
|
|
4163
4228
|
console.log(cyan(tunnelInfo.authUrl));
|
|
4164
4229
|
console.log('');
|
|
4165
|
-
console.log(dim(
|
|
4166
|
-
console.log(dim(
|
|
4230
|
+
console.log(dim(`The browser will redirect to localhost:${TUNNEL_PORT}, which tunnels to the workspace.`));
|
|
4231
|
+
console.log(dim(`The ${displayName} CLI in the workspace will receive the callback and complete auth.`));
|
|
4167
4232
|
console.log('');
|
|
4168
4233
|
}
|
|
4169
4234
|
else {
|
|
@@ -4178,11 +4243,12 @@ program
|
|
|
4178
4243
|
while (!authenticated && (Date.now() - startTime) < TIMEOUT_MS) {
|
|
4179
4244
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
4180
4245
|
try {
|
|
4181
|
-
// Build URL with token for authentication
|
|
4246
|
+
// Build URL with token and provider for authentication
|
|
4182
4247
|
const authStatusUrl = new URL(`${CLOUD_URL}/api/auth/codex-helper/auth-status/${workspaceId}`);
|
|
4183
4248
|
if (options.token) {
|
|
4184
4249
|
authStatusUrl.searchParams.set('token', options.token);
|
|
4185
4250
|
}
|
|
4251
|
+
authStatusUrl.searchParams.set('provider', provider);
|
|
4186
4252
|
const statusResponse = await fetch(authStatusUrl.toString(), { method: 'GET', headers, credentials: 'include' });
|
|
4187
4253
|
if (statusResponse.ok) {
|
|
4188
4254
|
const statusData = await statusResponse.json();
|
|
@@ -4210,7 +4276,7 @@ program
|
|
|
4210
4276
|
console.log(green(' Authentication Complete!'));
|
|
4211
4277
|
console.log(green('═══════════════════════════════════════════════════'));
|
|
4212
4278
|
console.log('');
|
|
4213
|
-
console.log(
|
|
4279
|
+
console.log(`Your ${displayName} account is now connected to the workspace.`);
|
|
4214
4280
|
console.log('You can close this terminal and return to the dashboard.');
|
|
4215
4281
|
console.log('');
|
|
4216
4282
|
}
|
|
@@ -4222,6 +4288,63 @@ program
|
|
|
4222
4288
|
console.log('the callback. Check if the SSH tunnel was working correctly.');
|
|
4223
4289
|
process.exit(1);
|
|
4224
4290
|
}
|
|
4291
|
+
}
|
|
4292
|
+
// Shared options for all cli-auth commands
|
|
4293
|
+
const cliAuthOptions = {
|
|
4294
|
+
workspace: '--workspace <id>',
|
|
4295
|
+
cloudUrl: '--cloud-url <url>',
|
|
4296
|
+
token: '--token <token>',
|
|
4297
|
+
sessionCookie: '--session-cookie <cookie>',
|
|
4298
|
+
timeout: '--timeout <seconds>',
|
|
4299
|
+
};
|
|
4300
|
+
const defaultCloudUrl = process.env.AGENT_RELAY_CLOUD_URL || 'https://agent-relay.com';
|
|
4301
|
+
// cli-auth <provider> - Generic command
|
|
4302
|
+
program
|
|
4303
|
+
.command('cli-auth <provider>')
|
|
4304
|
+
.description('Connect a provider via SSH tunnel to workspace (Claude, Codex, Cursor, etc.)')
|
|
4305
|
+
.option(cliAuthOptions.workspace, 'Workspace ID to connect to')
|
|
4306
|
+
.option(cliAuthOptions.cloudUrl, 'Cloud API URL', defaultCloudUrl)
|
|
4307
|
+
.option(cliAuthOptions.token, 'CLI authentication token (from dashboard)')
|
|
4308
|
+
.option(cliAuthOptions.sessionCookie, 'Session cookie for authentication (deprecated, use --token)')
|
|
4309
|
+
.option(cliAuthOptions.timeout, 'Timeout in seconds (default: 300)', '300')
|
|
4310
|
+
.action(async (providerArg, options) => {
|
|
4311
|
+
await runCliAuth(providerArg, options);
|
|
4312
|
+
});
|
|
4313
|
+
// codex-auth - Backward-compatible alias
|
|
4314
|
+
program
|
|
4315
|
+
.command('codex-auth')
|
|
4316
|
+
.description('Connect Codex via SSH tunnel to workspace (alias for cli-auth codex)')
|
|
4317
|
+
.option(cliAuthOptions.workspace, 'Workspace ID to connect to')
|
|
4318
|
+
.option(cliAuthOptions.cloudUrl, 'Cloud API URL', defaultCloudUrl)
|
|
4319
|
+
.option(cliAuthOptions.token, 'CLI authentication token (from dashboard)')
|
|
4320
|
+
.option(cliAuthOptions.sessionCookie, 'Session cookie for authentication (deprecated, use --token)')
|
|
4321
|
+
.option(cliAuthOptions.timeout, 'Timeout in seconds (default: 300)', '300')
|
|
4322
|
+
.action(async (options) => {
|
|
4323
|
+
await runCliAuth('codex', options);
|
|
4324
|
+
});
|
|
4325
|
+
// claude-auth - Alias for Claude/Anthropic
|
|
4326
|
+
program
|
|
4327
|
+
.command('claude-auth')
|
|
4328
|
+
.description('Connect Claude via SSH tunnel to workspace (alias for cli-auth claude)')
|
|
4329
|
+
.option(cliAuthOptions.workspace, 'Workspace ID to connect to')
|
|
4330
|
+
.option(cliAuthOptions.cloudUrl, 'Cloud API URL', defaultCloudUrl)
|
|
4331
|
+
.option(cliAuthOptions.token, 'CLI authentication token (from dashboard)')
|
|
4332
|
+
.option(cliAuthOptions.sessionCookie, 'Session cookie for authentication (deprecated, use --token)')
|
|
4333
|
+
.option(cliAuthOptions.timeout, 'Timeout in seconds (default: 300)', '300')
|
|
4334
|
+
.action(async (options) => {
|
|
4335
|
+
await runCliAuth('claude', options);
|
|
4336
|
+
});
|
|
4337
|
+
// cursor-auth - Alias for Cursor
|
|
4338
|
+
program
|
|
4339
|
+
.command('cursor-auth')
|
|
4340
|
+
.description('Connect Cursor via SSH tunnel to workspace (alias for cli-auth cursor)')
|
|
4341
|
+
.option(cliAuthOptions.workspace, 'Workspace ID to connect to')
|
|
4342
|
+
.option(cliAuthOptions.cloudUrl, 'Cloud API URL', defaultCloudUrl)
|
|
4343
|
+
.option(cliAuthOptions.token, 'CLI authentication token (from dashboard)')
|
|
4344
|
+
.option(cliAuthOptions.sessionCookie, 'Session cookie for authentication (deprecated, use --token)')
|
|
4345
|
+
.option(cliAuthOptions.timeout, 'Timeout in seconds (default: 300)', '300')
|
|
4346
|
+
.action(async (options) => {
|
|
4347
|
+
await runCliAuth('cursor', options);
|
|
4225
4348
|
});
|
|
4226
4349
|
// init - First-time setup wizard for Agent Relay
|
|
4227
4350
|
async function runInit(options) {
|