@the-open-engine/zeroshot 6.4.0 → 6.5.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/cli/commands/providers.js +11 -5
- package/cli/index.js +79 -16
- package/cli/lib/first-run.js +14 -3
- package/docker/zeroshot-cluster/Dockerfile +11 -1
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts +4 -0
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js +259 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts +23 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.js +373 -0
- package/lib/agent-cli-provider/adapters/acp.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +2 -29
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js +154 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.js +176 -0
- package/lib/agent-cli-provider/adapters/copilot.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts +6 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.js +161 -0
- package/lib/agent-cli-provider/adapters/gateway.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/gemini.js +9 -2
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +6 -34
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +5 -1
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/adapters/pi.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/pi.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/pi.js +331 -0
- package/lib/agent-cli-provider/adapters/pi.js.map +1 -0
- package/lib/agent-cli-provider/claude-command.d.ts +6 -0
- package/lib/agent-cli-provider/claude-command.d.ts.map +1 -0
- package/lib/agent-cli-provider/claude-command.js +43 -0
- package/lib/agent-cli-provider/claude-command.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +13 -17
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +10 -3
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +57 -2
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-support.js +25 -3
- package/lib/agent-cli-provider/contract-support.js.map +1 -1
- package/lib/agent-cli-provider/errors.d.ts +1 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -1
- package/lib/agent-cli-provider/errors.js +14 -5
- package/lib/agent-cli-provider/errors.js.map +1 -1
- package/lib/agent-cli-provider/gateway-client.d.ts +32 -0
- package/lib/agent-cli-provider/gateway-client.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-client.js +135 -0
- package/lib/agent-cli-provider/gateway-client.js.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts +9 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.js +294 -0
- package/lib/agent-cli-provider/gateway-runner.js.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts +14 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.js +495 -0
- package/lib/agent-cli-provider/gateway-tools.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +3 -2
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +14 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.d.ts +2 -2
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.js +17 -3
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +437 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -0
- package/lib/agent-cli-provider/provider-registry.js +411 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +7 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +139 -19
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +106 -4
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/docker-config.js +24 -13
- package/lib/provider-defaults.js +15 -4
- package/lib/provider-detection.js +2 -0
- package/lib/provider-names.js +32 -12
- package/lib/repo-settings.js +15 -1
- package/lib/settings/claude-auth.js +3 -6
- package/lib/settings.js +17 -2
- package/lib/setup-apply.js +300 -0
- package/lib/setup-journal.js +109 -0
- package/lib/setup-plan.js +406 -0
- package/lib/setup-undo.js +88 -0
- package/lib/start-cluster.js +12 -1
- package/lib/stream-json-parser.js +7 -5
- package/package.json +29 -1
- package/scripts/live-provider-smoke.js +221 -0
- package/src/agent/agent-lifecycle.js +5 -4
- package/src/agent/agent-quality-gate-schema.js +2 -2
- package/src/agent/agent-task-executor.js +41 -2
- package/src/agent-cli-provider/acp-stdio-runner.ts +314 -0
- package/src/agent-cli-provider/adapters/acp.ts +493 -0
- package/src/agent-cli-provider/adapters/claude.ts +2 -32
- package/src/agent-cli-provider/adapters/copilot-parser.ts +166 -0
- package/src/agent-cli-provider/adapters/copilot.ts +231 -0
- package/src/agent-cli-provider/adapters/gateway.ts +186 -0
- package/src/agent-cli-provider/adapters/gemini.ts +9 -2
- package/src/agent-cli-provider/adapters/index.ts +5 -36
- package/src/agent-cli-provider/adapters/opencode.ts +4 -1
- package/src/agent-cli-provider/adapters/pi.ts +410 -0
- package/src/agent-cli-provider/claude-command.ts +47 -0
- package/src/agent-cli-provider/contract-actions.ts +14 -17
- package/src/agent-cli-provider/contract-invoke.ts +12 -3
- package/src/agent-cli-provider/contract-options.ts +72 -3
- package/src/agent-cli-provider/contract-support.ts +30 -3
- package/src/agent-cli-provider/errors.ts +14 -4
- package/src/agent-cli-provider/gateway-client.ts +170 -0
- package/src/agent-cli-provider/gateway-runner.ts +353 -0
- package/src/agent-cli-provider/gateway-tools.ts +616 -0
- package/src/agent-cli-provider/index.ts +28 -0
- package/src/agent-cli-provider/invoke-evidence.ts +24 -3
- package/src/agent-cli-provider/provider-registry.ts +515 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +192 -18
- package/src/agent-cli-provider/types.ts +145 -4
- package/src/config-validator.js +33 -15
- package/src/isolation-manager.js +124 -19
- package/src/orchestrator.js +23 -10
- package/src/preflight.js +83 -30
- package/src/providers/capabilities.js +15 -42
- package/src/providers/index.js +52 -72
- package/src/worktree-claude-config.js +17 -0
- package/task-lib/commands/run.js +1 -0
- package/task-lib/provider-helper-runtime.js +11 -0
- package/task-lib/runner.js +7 -0
package/src/isolation-manager.js
CHANGED
|
@@ -16,8 +16,13 @@ const os = require('os');
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const { loadSettings } = require('../lib/settings');
|
|
18
18
|
const { CLAUDE_AUTH_ENV_VARS, resolveClaudeAuth } = require('../lib/settings/claude-auth');
|
|
19
|
-
const { normalizeProviderName } = require('../lib/provider-names');
|
|
20
|
-
const {
|
|
19
|
+
const { normalizeProviderName, getProviderMetadata } = require('../lib/provider-names');
|
|
20
|
+
const {
|
|
21
|
+
MOUNT_PRESETS,
|
|
22
|
+
resolveMounts,
|
|
23
|
+
resolveEnvs,
|
|
24
|
+
expandEnvPatterns,
|
|
25
|
+
} = require('../lib/docker-config');
|
|
21
26
|
const { getProvider } = require('./providers');
|
|
22
27
|
const { readRepoSettings } = require('../lib/repo-settings');
|
|
23
28
|
const { provisionClaudeCredentials } = require('./claude-credentials');
|
|
@@ -98,6 +103,24 @@ function resolveWorktreeSetupTimeoutMs(repoSettings = {}, options = {}) {
|
|
|
98
103
|
|
|
99
104
|
const DEFAULT_IMAGE = 'zeroshot-cluster-base';
|
|
100
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Shell command that installs a provider's CLI inside the cluster image, or null when the
|
|
108
|
+
* provider is baked into the base image (e.g. Claude) or has no single-command installer.
|
|
109
|
+
* Sourced from the provider registry (docker.install) so nothing here is provider-specific.
|
|
110
|
+
* @param {string} providerName
|
|
111
|
+
* @returns {string|null}
|
|
112
|
+
*/
|
|
113
|
+
function providerDockerInstall(providerName) {
|
|
114
|
+
if (!providerName) return null;
|
|
115
|
+
try {
|
|
116
|
+
const metadata = getProviderMetadata(providerName);
|
|
117
|
+
const install = metadata && metadata.docker && metadata.docker.install;
|
|
118
|
+
return typeof install === 'string' && install.trim() ? install.trim() : null;
|
|
119
|
+
} catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
101
124
|
class IsolationManager {
|
|
102
125
|
constructor(options = {}) {
|
|
103
126
|
this.image = options.image || DEFAULT_IMAGE;
|
|
@@ -171,7 +194,13 @@ class IsolationManager {
|
|
|
171
194
|
clusterConfigDir,
|
|
172
195
|
});
|
|
173
196
|
|
|
174
|
-
const mountedHosts = this._applyCredentialMounts(
|
|
197
|
+
const mountedHosts = this._applyCredentialMounts(
|
|
198
|
+
args,
|
|
199
|
+
config,
|
|
200
|
+
settings,
|
|
201
|
+
containerHome,
|
|
202
|
+
providerName
|
|
203
|
+
);
|
|
175
204
|
this._warnMissingProviderCredentials(providerName, mountedHosts, config, containerHome);
|
|
176
205
|
|
|
177
206
|
args.push('-w', '/workspace', image, 'tail', '-f', '/dev/null');
|
|
@@ -285,13 +314,25 @@ class IsolationManager {
|
|
|
285
314
|
return settings.dockerMounts;
|
|
286
315
|
}
|
|
287
316
|
|
|
288
|
-
|
|
317
|
+
// Auto-activate the running provider's own credential preset (mount + env) so `--docker` works
|
|
318
|
+
// without listing it in dockerMounts. Claude is mounted separately, so skip it.
|
|
319
|
+
_withActiveProviderPreset(mountConfig, providerName) {
|
|
320
|
+
if (!providerName || providerName === 'claude') return mountConfig;
|
|
321
|
+
if (!MOUNT_PRESETS[providerName]) return mountConfig;
|
|
322
|
+
if (mountConfig.some((item) => item === providerName)) return mountConfig;
|
|
323
|
+
return [...mountConfig, providerName];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
_applyCredentialMounts(args, config, settings, containerHome, providerName) {
|
|
289
327
|
const mountedHosts = [];
|
|
290
328
|
if (config.noMounts) {
|
|
291
329
|
return mountedHosts;
|
|
292
330
|
}
|
|
293
331
|
|
|
294
|
-
const mountConfig = this.
|
|
332
|
+
const mountConfig = this._withActiveProviderPreset(
|
|
333
|
+
this._resolveMountConfig(config, settings),
|
|
334
|
+
providerName
|
|
335
|
+
);
|
|
295
336
|
const mounts = resolveMounts(mountConfig, { containerHome });
|
|
296
337
|
const claudeContainerPath = path.posix.join(containerHome, '.claude');
|
|
297
338
|
|
|
@@ -363,16 +404,40 @@ class IsolationManager {
|
|
|
363
404
|
return;
|
|
364
405
|
}
|
|
365
406
|
|
|
407
|
+
const metadata = getProviderMetadata(providerName);
|
|
366
408
|
const provider = getProvider(providerName);
|
|
409
|
+
|
|
410
|
+
// An env token (e.g. COPILOT_GITHUB_TOKEN) is a complete credential on its own.
|
|
411
|
+
const credentialEnvKeys = metadata.credentialEnvKeys || [];
|
|
412
|
+
if (credentialEnvKeys.some((key) => process.env[key])) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// A mount only counts if it carries the secret (credentialInMount !== false).
|
|
417
|
+
const credentialInMount = metadata.docker && metadata.docker.credentialInMount === false;
|
|
367
418
|
const credentialPaths = provider.getCredentialPaths ? provider.getCredentialPaths() : [];
|
|
368
419
|
const expandedCreds = credentialPaths.map((cred) => expandHomePath(cred));
|
|
369
|
-
const hasCredentialMount =
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
420
|
+
const hasCredentialMount =
|
|
421
|
+
!credentialInMount &&
|
|
422
|
+
mountedHosts.some((hostPath) =>
|
|
423
|
+
expandedCreds.some(
|
|
424
|
+
(credPath) => pathContains(hostPath, credPath) || pathContains(credPath, hostPath)
|
|
425
|
+
)
|
|
426
|
+
);
|
|
427
|
+
if (hasCredentialMount) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (credentialInMount && credentialEnvKeys.length > 0) {
|
|
432
|
+
console.warn(
|
|
433
|
+
`[IsolationManager] ⚠️ ${provider.displayName} could not find credentials for Docker. ` +
|
|
434
|
+
`Its login token is not stored in a mountable file — export one of ` +
|
|
435
|
+
`${credentialEnvKeys.join(', ')} before running with --docker.`
|
|
436
|
+
);
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
374
439
|
|
|
375
|
-
if (
|
|
440
|
+
if (expandedCreds.length > 0) {
|
|
376
441
|
const exampleHost = credentialPaths[0];
|
|
377
442
|
const exampleContainer = exampleHost.replace(/^~(?=\/|$)/, containerHome);
|
|
378
443
|
const mountNote = config.noMounts ? 'Credential mounts are disabled. ' : '';
|
|
@@ -1230,13 +1295,40 @@ class IsolationManager {
|
|
|
1230
1295
|
}
|
|
1231
1296
|
}
|
|
1232
1297
|
|
|
1298
|
+
/**
|
|
1299
|
+
* Resolve the cluster image tag for a provider. Providers baked into the base image (e.g.
|
|
1300
|
+
* Claude) run on the base image directly; providers with a `docker.install` command get a
|
|
1301
|
+
* per-provider image variant `<baseImage>-<providerId>` whose install step is a Docker-cached
|
|
1302
|
+
* layer (built once, reused thereafter).
|
|
1303
|
+
* @param {string} providerName
|
|
1304
|
+
* @param {string} [baseImage]
|
|
1305
|
+
* @returns {string}
|
|
1306
|
+
*/
|
|
1307
|
+
static imageForProvider(providerName, baseImage = DEFAULT_IMAGE) {
|
|
1308
|
+
if (!providerDockerInstall(providerName)) {
|
|
1309
|
+
return baseImage;
|
|
1310
|
+
}
|
|
1311
|
+
return `${baseImage}-${normalizeProviderName(providerName)}`;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* Docker `--build-arg` values that install a provider's CLI into its image variant, or [] when
|
|
1316
|
+
* the provider is baked into the base image or has no installer.
|
|
1317
|
+
* @param {string} providerName
|
|
1318
|
+
* @returns {string[]}
|
|
1319
|
+
*/
|
|
1320
|
+
static providerBuildArgs(providerName) {
|
|
1321
|
+
const install = providerDockerInstall(providerName);
|
|
1322
|
+
return install ? [`PROVIDER_INSTALL=${install}`] : [];
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1233
1325
|
/**
|
|
1234
1326
|
* Build the Docker image with retry logic
|
|
1235
1327
|
* @param {string} [image] - Image name to build
|
|
1236
1328
|
* @param {number} [maxRetries=3] - Maximum retry attempts
|
|
1237
1329
|
* @returns {Promise<void>}
|
|
1238
1330
|
*/
|
|
1239
|
-
static async buildImage(image = DEFAULT_IMAGE, maxRetries = 3) {
|
|
1331
|
+
static async buildImage(image = DEFAULT_IMAGE, maxRetries = 3, buildArgs = []) {
|
|
1240
1332
|
// Repository root is one level up from src/
|
|
1241
1333
|
const repoRoot = path.join(__dirname, '..');
|
|
1242
1334
|
const dockerfilePath = path.join(repoRoot, 'docker', 'zeroshot-cluster', 'Dockerfile');
|
|
@@ -1245,6 +1337,12 @@ class IsolationManager {
|
|
|
1245
1337
|
throw new Error(`Dockerfile not found at ${dockerfilePath}`);
|
|
1246
1338
|
}
|
|
1247
1339
|
|
|
1340
|
+
// Each buildArg becomes a `--build-arg KEY=VALUE` pair (e.g. the per-provider install command).
|
|
1341
|
+
const buildArgFlags = [];
|
|
1342
|
+
for (const arg of buildArgs) {
|
|
1343
|
+
buildArgFlags.push('--build-arg', arg);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1248
1346
|
console.log(`[IsolationManager] Building Docker image '${image}'...`);
|
|
1249
1347
|
|
|
1250
1348
|
const baseDelay = 3000; // 3 seconds
|
|
@@ -1253,11 +1351,18 @@ class IsolationManager {
|
|
|
1253
1351
|
try {
|
|
1254
1352
|
// CRITICAL: Run from repo root so build context includes package.json and src/
|
|
1255
1353
|
// Use -f flag to specify Dockerfile location
|
|
1256
|
-
runSync(
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1354
|
+
runSync(
|
|
1355
|
+
'docker',
|
|
1356
|
+
['build', '-f', 'docker/zeroshot-cluster/Dockerfile', ...buildArgFlags, '-t', image, '.'],
|
|
1357
|
+
{
|
|
1358
|
+
cwd: repoRoot,
|
|
1359
|
+
encoding: 'utf8',
|
|
1360
|
+
stdio: 'inherit',
|
|
1361
|
+
// No timeout: image builds legitimately take many minutes (apt, tool downloads,
|
|
1362
|
+
// provider install). runSync's 30s default would kill every build (ETIMEDOUT).
|
|
1363
|
+
timeout: 0,
|
|
1364
|
+
}
|
|
1365
|
+
);
|
|
1261
1366
|
|
|
1262
1367
|
console.log(`[IsolationManager] ✓ Image '${image}' built successfully`);
|
|
1263
1368
|
return;
|
|
@@ -1284,7 +1389,7 @@ class IsolationManager {
|
|
|
1284
1389
|
* @param {boolean} [autoBuild=true] - Auto-build if missing
|
|
1285
1390
|
* @returns {Promise<void>}
|
|
1286
1391
|
*/
|
|
1287
|
-
static async ensureImage(image = DEFAULT_IMAGE, autoBuild = true) {
|
|
1392
|
+
static async ensureImage(image = DEFAULT_IMAGE, autoBuild = true, buildArgs = []) {
|
|
1288
1393
|
if (this.imageExists(image)) {
|
|
1289
1394
|
return;
|
|
1290
1395
|
}
|
|
@@ -1297,7 +1402,7 @@ class IsolationManager {
|
|
|
1297
1402
|
}
|
|
1298
1403
|
|
|
1299
1404
|
console.log(`[IsolationManager] Image '${image}' not found, building automatically...`);
|
|
1300
|
-
await this.buildImage(image);
|
|
1405
|
+
await this.buildImage(image, 3, buildArgs);
|
|
1301
1406
|
}
|
|
1302
1407
|
|
|
1303
1408
|
/**
|
package/src/orchestrator.js
CHANGED
|
@@ -1138,11 +1138,12 @@ class Orchestrator {
|
|
|
1138
1138
|
const messageBus = new MessageBus(ledger);
|
|
1139
1139
|
|
|
1140
1140
|
// Handle isolation mode (Docker container OR git worktree)
|
|
1141
|
-
const {
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1141
|
+
const {
|
|
1142
|
+
isolationManager,
|
|
1143
|
+
containerId,
|
|
1144
|
+
worktreeInfo,
|
|
1145
|
+
image: resolvedIsolationImage,
|
|
1146
|
+
} = await this._initializeIsolation(options, config, clusterId);
|
|
1146
1147
|
let requiredQualityGates = resolveClusterRequiredQualityGates(config, options);
|
|
1147
1148
|
let commandProofs = resolveClusterCommandProofs(config, options);
|
|
1148
1149
|
options.requiredQualityGates = requiredQualityGates;
|
|
@@ -1188,7 +1189,9 @@ class Orchestrator {
|
|
|
1188
1189
|
? {
|
|
1189
1190
|
enabled: true,
|
|
1190
1191
|
containerId,
|
|
1191
|
-
image
|
|
1192
|
+
// Provider-specific image variant (resolved in _initializeIsolation) so resume
|
|
1193
|
+
// recreates the container on the same image that has the provider CLI installed.
|
|
1194
|
+
image: resolvedIsolationImage || options.isolationImage || 'zeroshot-cluster-base',
|
|
1192
1195
|
manager: isolationManager,
|
|
1193
1196
|
workDir: options.cwd || process.cwd(), // Persisted for resume
|
|
1194
1197
|
}
|
|
@@ -1729,20 +1732,30 @@ class Orchestrator {
|
|
|
1729
1732
|
let isolationManager = null;
|
|
1730
1733
|
let containerId = null;
|
|
1731
1734
|
let worktreeInfo = null;
|
|
1735
|
+
let isolationImage = null;
|
|
1732
1736
|
|
|
1733
1737
|
if (options.isolation) {
|
|
1734
1738
|
if (!IsolationManager.isDockerAvailable()) {
|
|
1735
1739
|
throw new Error('Docker is not available. Install Docker to use --docker mode.');
|
|
1736
1740
|
}
|
|
1737
1741
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1742
|
+
// Resolve the provider first so the cluster runs on that provider's image variant, which
|
|
1743
|
+
// installs the provider CLI as a Docker-cached layer. Providers baked into the base image
|
|
1744
|
+
// (e.g. Claude) resolve back to the base image unchanged.
|
|
1745
|
+
const providerName = this._resolveClusterProvider(config);
|
|
1746
|
+
const baseImage = options.isolationImage || 'zeroshot-cluster-base';
|
|
1747
|
+
const image = IsolationManager.imageForProvider(providerName, baseImage);
|
|
1748
|
+
await IsolationManager.ensureImage(
|
|
1749
|
+
image,
|
|
1750
|
+
true,
|
|
1751
|
+
IsolationManager.providerBuildArgs(providerName)
|
|
1752
|
+
);
|
|
1753
|
+
isolationImage = image;
|
|
1740
1754
|
|
|
1741
1755
|
isolationManager = new IsolationManager({ image });
|
|
1742
1756
|
this._log(`[Orchestrator] Starting cluster in isolation mode (image: ${image})`);
|
|
1743
1757
|
|
|
1744
1758
|
const workDir = options.cwd || process.cwd();
|
|
1745
|
-
const providerName = this._resolveClusterProvider(config);
|
|
1746
1759
|
containerId = await isolationManager.createContainer(clusterId, {
|
|
1747
1760
|
workDir,
|
|
1748
1761
|
image,
|
|
@@ -1770,7 +1783,7 @@ class Orchestrator {
|
|
|
1770
1783
|
this._log(`[Orchestrator] Branch: ${worktreeInfo.branch}`);
|
|
1771
1784
|
}
|
|
1772
1785
|
|
|
1773
|
-
return { isolationManager, containerId, worktreeInfo };
|
|
1786
|
+
return { isolationManager, containerId, worktreeInfo, image: isolationImage };
|
|
1774
1787
|
}
|
|
1775
1788
|
|
|
1776
1789
|
_applyAutoPrConfig(config, inputData, options) {
|
package/src/preflight.js
CHANGED
|
@@ -19,7 +19,12 @@ const {
|
|
|
19
19
|
resolveClaudeAuth,
|
|
20
20
|
} = require('../lib/settings/claude-auth.js');
|
|
21
21
|
const { loadSettings, getClaudeCommand } = require('../lib/settings.js');
|
|
22
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
VALID_PROVIDERS,
|
|
24
|
+
getProviderMetadata,
|
|
25
|
+
normalizeProviderName,
|
|
26
|
+
resolveProviderCommand,
|
|
27
|
+
} = require('../lib/provider-names');
|
|
23
28
|
const { detectGitContext } = require('../lib/git-remote-utils');
|
|
24
29
|
const { readKeychainCredentials } = require('./claude-credentials');
|
|
25
30
|
|
|
@@ -343,10 +348,21 @@ function buildClaudeCommand(options) {
|
|
|
343
348
|
return options.claudeCommand || [command, ...args].join(' ');
|
|
344
349
|
}
|
|
345
350
|
|
|
351
|
+
function buildRegistryInstallRecovery(metadata, command) {
|
|
352
|
+
return [
|
|
353
|
+
...metadata.installInstructions.split('\n').filter(Boolean),
|
|
354
|
+
`Then run: ${command} --version`,
|
|
355
|
+
];
|
|
356
|
+
}
|
|
357
|
+
|
|
346
358
|
function validateClaudeProvider(options) {
|
|
347
359
|
const errors = [];
|
|
348
360
|
const warnings = [];
|
|
361
|
+
const metadata = getProviderMetadata('claude');
|
|
349
362
|
const claudeCommand = buildClaudeCommand(options);
|
|
363
|
+
const claudeCommandParts = claudeCommand.trim().split(/\s+/);
|
|
364
|
+
const usesDefaultClaudeCommand =
|
|
365
|
+
claudeCommandParts.length === 1 && claudeCommandParts[0] === metadata.binary;
|
|
350
366
|
|
|
351
367
|
const claude = getClaudeVersion(claudeCommand);
|
|
352
368
|
if (!claude.installed) {
|
|
@@ -354,12 +370,8 @@ function validateClaudeProvider(options) {
|
|
|
354
370
|
formatError(
|
|
355
371
|
'Claude command not available',
|
|
356
372
|
claude.error,
|
|
357
|
-
|
|
358
|
-
?
|
|
359
|
-
'Install Claude CLI: npm install -g @anthropic-ai/claude-code',
|
|
360
|
-
'Or: brew install claude (macOS)',
|
|
361
|
-
'Then run: claude --version',
|
|
362
|
-
]
|
|
373
|
+
usesDefaultClaudeCommand
|
|
374
|
+
? buildRegistryInstallRecovery(metadata, metadata.binary)
|
|
363
375
|
: [
|
|
364
376
|
`Command '${claudeCommand}' not found`,
|
|
365
377
|
'Check settings: zeroshot settings',
|
|
@@ -404,41 +416,82 @@ function validateCliProvider(command, title, detail, recovery) {
|
|
|
404
416
|
return { errors, warnings: [] };
|
|
405
417
|
}
|
|
406
418
|
|
|
407
|
-
function
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
'
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
'Command "opencode" not installed',
|
|
425
|
-
['Install Opencode CLI: see https://opencode.ai', 'Then run: opencode --version']
|
|
419
|
+
function validateGatewayProvider() {
|
|
420
|
+
const { getProvider } = require('./providers');
|
|
421
|
+
const provider = getProvider('gateway');
|
|
422
|
+
if (provider.isAvailable()) {
|
|
423
|
+
return { errors: [], warnings: [] };
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
errors: [
|
|
427
|
+
formatError(
|
|
428
|
+
'Gateway provider not configured',
|
|
429
|
+
'providerSettings.gateway must define baseUrl, apiKey, model, and toolPolicy before gateway can run.',
|
|
430
|
+
[
|
|
431
|
+
'Run: zeroshot settings',
|
|
432
|
+
'Set providerSettings.gateway.baseUrl to your OpenAI-compatible endpoint base URL',
|
|
433
|
+
'Set providerSettings.gateway.apiKey and providerSettings.gateway.model',
|
|
434
|
+
'Set providerSettings.gateway.toolPolicy.roots and toolPolicy.commands explicitly',
|
|
435
|
+
]
|
|
426
436
|
),
|
|
437
|
+
],
|
|
438
|
+
warnings: [],
|
|
427
439
|
};
|
|
440
|
+
}
|
|
428
441
|
|
|
429
|
-
|
|
430
|
-
|
|
442
|
+
function validateProvider(providerName, options) {
|
|
443
|
+
let metadata;
|
|
444
|
+
try {
|
|
445
|
+
metadata = getProviderMetadata(providerName);
|
|
446
|
+
} catch {
|
|
431
447
|
return {
|
|
432
448
|
errors: [
|
|
433
449
|
formatError('Unknown provider', `Provider "${providerName}" is not supported`, [
|
|
434
|
-
|
|
450
|
+
`Use ${VALID_PROVIDERS.join(', ')}`,
|
|
435
451
|
]),
|
|
436
452
|
],
|
|
437
453
|
warnings: [],
|
|
438
454
|
};
|
|
439
455
|
}
|
|
440
456
|
|
|
441
|
-
|
|
457
|
+
if (metadata.command.kind === 'configured-claude') {
|
|
458
|
+
return validateClaudeProvider(options);
|
|
459
|
+
}
|
|
460
|
+
if (providerName === 'gateway') {
|
|
461
|
+
return validateGatewayProvider();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return validateRegistryCliProvider(providerName);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function validateRegistryCliProvider(providerName) {
|
|
468
|
+
const metadata = getProviderMetadata(providerName);
|
|
469
|
+
const { command } = resolveProviderCommand(providerName);
|
|
470
|
+
const installSteps = metadata.installInstructions.split('\n').filter(Boolean);
|
|
471
|
+
if (!commandExists(command)) {
|
|
472
|
+
return validateCliProvider(
|
|
473
|
+
command,
|
|
474
|
+
`${metadata.displayName} CLI not available`,
|
|
475
|
+
`Command "${command}" not installed`,
|
|
476
|
+
[...installSteps, `Then run: ${command} --version`]
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const { getProvider } = require('./providers');
|
|
481
|
+
if (getProvider(providerName).isAvailable()) {
|
|
482
|
+
return { errors: [], warnings: [] };
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
errors: [
|
|
487
|
+
formatError(
|
|
488
|
+
`${metadata.displayName} CLI not available`,
|
|
489
|
+
`Command "${command}" is installed but did not produce usable --help/--version output`,
|
|
490
|
+
[...installSteps, `Then run: ${command} --version`]
|
|
491
|
+
),
|
|
492
|
+
],
|
|
493
|
+
warnings: [],
|
|
494
|
+
};
|
|
442
495
|
}
|
|
443
496
|
|
|
444
497
|
function validateGhRequirement() {
|
|
@@ -1,48 +1,21 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
PROVIDER_CAPABILITIES,
|
|
3
|
+
normalizeProviderName,
|
|
4
|
+
providerSupportsCapability,
|
|
5
|
+
} = require('../../lib/provider-names');
|
|
2
6
|
|
|
3
|
-
const CAPABILITIES =
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
reasoningEffort: false,
|
|
12
|
-
},
|
|
13
|
-
codex: {
|
|
14
|
-
dockerIsolation: true,
|
|
15
|
-
worktreeIsolation: true,
|
|
16
|
-
mcpServers: true,
|
|
17
|
-
jsonSchema: true,
|
|
18
|
-
streamJson: true,
|
|
19
|
-
thinkingMode: true,
|
|
20
|
-
reasoningEffort: true,
|
|
21
|
-
},
|
|
22
|
-
gemini: {
|
|
23
|
-
dockerIsolation: true,
|
|
24
|
-
worktreeIsolation: true,
|
|
25
|
-
mcpServers: true,
|
|
26
|
-
jsonSchema: 'experimental',
|
|
27
|
-
streamJson: true,
|
|
28
|
-
thinkingMode: true,
|
|
29
|
-
reasoningEffort: false,
|
|
30
|
-
},
|
|
31
|
-
opencode: {
|
|
32
|
-
dockerIsolation: true,
|
|
33
|
-
worktreeIsolation: true,
|
|
34
|
-
mcpServers: true,
|
|
35
|
-
jsonSchema: 'experimental',
|
|
36
|
-
streamJson: true,
|
|
37
|
-
thinkingMode: true,
|
|
38
|
-
reasoningEffort: true,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
7
|
+
const CAPABILITIES = Object.freeze(
|
|
8
|
+
Object.fromEntries(
|
|
9
|
+
Object.entries(PROVIDER_CAPABILITIES).map(([provider, capabilities]) => [
|
|
10
|
+
provider,
|
|
11
|
+
Object.freeze({ ...capabilities }),
|
|
12
|
+
])
|
|
13
|
+
)
|
|
14
|
+
);
|
|
41
15
|
|
|
42
16
|
function checkCapability(provider, capability) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return caps[capability] === true;
|
|
17
|
+
if (!provider) return false;
|
|
18
|
+
return providerSupportsCapability(provider, capability);
|
|
46
19
|
}
|
|
47
20
|
|
|
48
21
|
function warnIfExperimental(provider, capability) {
|