@covibes/zeroshot 5.4.0 → 5.4.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/LICENSE +1 -1
- package/README.md +8 -465
- package/cli/index.js +101 -5231
- package/package.json +16 -130
- package/CHANGELOG.md +0 -537
- package/cli/commands/providers.js +0 -149
- package/cli/lib/first-run.js +0 -211
- package/cli/lib/update-checker.js +0 -281
- package/cli/message-formatter-utils.js +0 -75
- package/cli/message-formatters-normal.js +0 -275
- package/cli/message-formatters-watch.js +0 -181
- package/cluster-templates/base-templates/debug-workflow.json +0 -375
- package/cluster-templates/base-templates/full-workflow.json +0 -581
- package/cluster-templates/base-templates/single-worker.json +0 -63
- package/cluster-templates/base-templates/worker-validator.json +0 -208
- package/cluster-templates/conductor-bootstrap.json +0 -124
- package/docker/zeroshot-cluster/Dockerfile +0 -152
- package/docker/zeroshot-cluster/pre-baked-deps.json +0 -28
- package/lib/completion.js +0 -174
- package/lib/docker-config.js +0 -220
- package/lib/git-remote-utils.js +0 -165
- package/lib/id-detector.js +0 -56
- package/lib/provider-defaults.js +0 -62
- package/lib/provider-detection.js +0 -59
- package/lib/provider-names.js +0 -57
- package/lib/settings/claude-auth.js +0 -78
- package/lib/settings.js +0 -503
- package/lib/stream-json-parser.js +0 -10
- package/scripts/fix-node-pty-permissions.js +0 -75
- package/scripts/record-demo.sh +0 -279
- package/scripts/setup-merge-queue.sh +0 -170
- package/scripts/test-install.sh +0 -40
- package/scripts/validate-templates.js +0 -100
- package/src/agent/agent-config.js +0 -246
- package/src/agent/agent-context-builder.js +0 -444
- package/src/agent/agent-hook-executor.js +0 -599
- package/src/agent/agent-lifecycle.js +0 -743
- package/src/agent/agent-stuck-detector.js +0 -256
- package/src/agent/agent-task-executor.js +0 -1690
- package/src/agent/agent-trigger-evaluator.js +0 -67
- package/src/agent/output-extraction.js +0 -217
- package/src/agent/output-reformatter.js +0 -175
- package/src/agent/schema-utils.js +0 -146
- package/src/agent-wrapper.js +0 -579
- package/src/agents/git-pusher-template.js +0 -285
- package/src/attach/attach-client.js +0 -438
- package/src/attach/attach-server.js +0 -543
- package/src/attach/index.js +0 -35
- package/src/attach/protocol.js +0 -220
- package/src/attach/ring-buffer.js +0 -121
- package/src/attach/socket-discovery.js +0 -267
- package/src/claude-task-runner.js +0 -574
- package/src/config-router.js +0 -80
- package/src/config-validator.js +0 -1925
- package/src/copy-worker.js +0 -43
- package/src/input-helpers.js +0 -65
- package/src/isolation-manager.js +0 -1471
- package/src/issue-providers/README.md +0 -305
- package/src/issue-providers/azure-devops-provider.js +0 -273
- package/src/issue-providers/base-provider.js +0 -232
- package/src/issue-providers/github-provider.js +0 -179
- package/src/issue-providers/gitlab-provider.js +0 -241
- package/src/issue-providers/index.js +0 -196
- package/src/issue-providers/jira-provider.js +0 -239
- package/src/ledger.js +0 -631
- package/src/lib/safe-exec.js +0 -88
- package/src/logic-engine.js +0 -223
- package/src/message-bus-bridge.js +0 -139
- package/src/message-bus.js +0 -249
- package/src/name-generator.js +0 -232
- package/src/orchestrator.js +0 -2815
- package/src/preflight.js +0 -639
- package/src/process-metrics.js +0 -608
- package/src/providers/anthropic/cli-builder.js +0 -73
- package/src/providers/anthropic/index.js +0 -204
- package/src/providers/anthropic/models.js +0 -23
- package/src/providers/anthropic/output-parser.js +0 -177
- package/src/providers/base-provider.js +0 -251
- package/src/providers/capabilities.js +0 -60
- package/src/providers/google/cli-builder.js +0 -55
- package/src/providers/google/index.js +0 -116
- package/src/providers/google/models.js +0 -24
- package/src/providers/google/output-parser.js +0 -101
- package/src/providers/index.js +0 -91
- package/src/providers/openai/cli-builder.js +0 -133
- package/src/providers/openai/index.js +0 -136
- package/src/providers/openai/models.js +0 -21
- package/src/providers/openai/output-parser.js +0 -143
- package/src/providers/opencode/cli-builder.js +0 -42
- package/src/providers/opencode/index.js +0 -103
- package/src/providers/opencode/models.js +0 -55
- package/src/providers/opencode/output-parser.js +0 -122
- package/src/schemas/sub-cluster.js +0 -185
- package/src/status-footer.js +0 -1026
- package/src/sub-cluster-wrapper.js +0 -601
- package/src/task-runner.js +0 -30
- package/src/template-resolver.js +0 -372
- package/src/tui/CHANGES.txt +0 -133
- package/src/tui/LAYOUT.md +0 -261
- package/src/tui/README.txt +0 -192
- package/src/tui/TWO-LEVEL-NAVIGATION.md +0 -186
- package/src/tui/data-poller.js +0 -349
- package/src/tui/demo.js +0 -208
- package/src/tui/formatters.js +0 -129
- package/src/tui/index.js +0 -193
- package/src/tui/keybindings.js +0 -324
- package/src/tui/layout.js +0 -334
- package/src/tui/renderer.js +0 -184
- package/task-lib/attachable-watcher.js +0 -309
- package/task-lib/claude-recovery.js +0 -156
- package/task-lib/commands/clean.js +0 -50
- package/task-lib/commands/episodes.js +0 -105
- package/task-lib/commands/get-log-path.js +0 -23
- package/task-lib/commands/kill.js +0 -32
- package/task-lib/commands/list.js +0 -105
- package/task-lib/commands/logs.js +0 -453
- package/task-lib/commands/resume.js +0 -42
- package/task-lib/commands/run.js +0 -57
- package/task-lib/commands/schedule.js +0 -105
- package/task-lib/commands/scheduler-cmd.js +0 -96
- package/task-lib/commands/schedules.js +0 -148
- package/task-lib/commands/status.js +0 -44
- package/task-lib/commands/unschedule.js +0 -16
- package/task-lib/completion.js +0 -9
- package/task-lib/config.js +0 -8
- package/task-lib/name-generator.js +0 -230
- package/task-lib/package.json +0 -3
- package/task-lib/runner.js +0 -201
- package/task-lib/scheduler.js +0 -252
- package/task-lib/store.js +0 -529
- package/task-lib/tui/formatters.js +0 -215
- package/task-lib/tui/index.js +0 -197
- package/task-lib/tui/layout.js +0 -111
- package/task-lib/tui/renderer.js +0 -129
- package/task-lib/tui.js +0 -425
- package/task-lib/watcher.js +0 -226
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
const { execSync, spawnSync } = require('child_process');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
function commandExists(command) {
|
|
6
|
-
if (!command) return false;
|
|
7
|
-
if (command.includes(path.sep)) {
|
|
8
|
-
return fs.existsSync(command);
|
|
9
|
-
}
|
|
10
|
-
try {
|
|
11
|
-
execSync(`command -v ${command}`, { stdio: 'pipe' });
|
|
12
|
-
return true;
|
|
13
|
-
} catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getCommandPath(command) {
|
|
19
|
-
if (!command) return null;
|
|
20
|
-
if (command.includes(path.sep)) {
|
|
21
|
-
return fs.existsSync(command) ? command : null;
|
|
22
|
-
}
|
|
23
|
-
try {
|
|
24
|
-
const output = execSync(`command -v ${command}`, { encoding: 'utf8', stdio: 'pipe' });
|
|
25
|
-
return output.trim() || null;
|
|
26
|
-
} catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getHelpOutput(command, args = []) {
|
|
32
|
-
if (!commandExists(command)) return '';
|
|
33
|
-
|
|
34
|
-
const attempt = (flag) => {
|
|
35
|
-
const result = spawnSync(command, [...args, flag], { encoding: 'utf8' });
|
|
36
|
-
const output = `${result.stdout || ''}${result.stderr || ''}`;
|
|
37
|
-
return output.trim();
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const help = attempt('--help');
|
|
41
|
-
if (help) return help;
|
|
42
|
-
|
|
43
|
-
const alt = attempt('-h');
|
|
44
|
-
return alt || '';
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getVersionOutput(command, args = []) {
|
|
48
|
-
if (!commandExists(command)) return '';
|
|
49
|
-
const result = spawnSync(command, [...args, '--version'], { encoding: 'utf8' });
|
|
50
|
-
const output = `${result.stdout || ''}${result.stderr || ''}`;
|
|
51
|
-
return output.trim();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
module.exports = {
|
|
55
|
-
commandExists,
|
|
56
|
-
getCommandPath,
|
|
57
|
-
getHelpOutput,
|
|
58
|
-
getVersionOutput,
|
|
59
|
-
};
|
package/lib/provider-names.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const PROVIDER_ALIASES = {
|
|
2
|
-
anthropic: 'claude',
|
|
3
|
-
openai: 'codex',
|
|
4
|
-
google: 'gemini',
|
|
5
|
-
claude: 'claude',
|
|
6
|
-
codex: 'codex',
|
|
7
|
-
gemini: 'gemini',
|
|
8
|
-
opencode: 'opencode',
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const VALID_PROVIDERS = ['claude', 'codex', 'gemini', 'opencode'];
|
|
12
|
-
|
|
13
|
-
function normalizeProviderName(name) {
|
|
14
|
-
if (!name || typeof name !== 'string') return name;
|
|
15
|
-
const normalized = PROVIDER_ALIASES[name.toLowerCase()];
|
|
16
|
-
return normalized || name;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function normalizeProviderSettings(providerSettings) {
|
|
20
|
-
if (
|
|
21
|
-
!providerSettings ||
|
|
22
|
-
typeof providerSettings !== 'object' ||
|
|
23
|
-
Array.isArray(providerSettings)
|
|
24
|
-
) {
|
|
25
|
-
return providerSettings;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const normalized = {};
|
|
29
|
-
const entries = Object.entries(providerSettings);
|
|
30
|
-
const aliasFirst = entries.sort(([left], [right]) => {
|
|
31
|
-
const leftIsCanonical = normalizeProviderName(left) === left;
|
|
32
|
-
const rightIsCanonical = normalizeProviderName(right) === right;
|
|
33
|
-
if (leftIsCanonical === rightIsCanonical) return 0;
|
|
34
|
-
return leftIsCanonical ? 1 : -1;
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
for (const [key, value] of aliasFirst) {
|
|
38
|
-
const canonical = normalizeProviderName(key);
|
|
39
|
-
if (!VALID_PROVIDERS.includes(canonical)) {
|
|
40
|
-
normalized[key] = value;
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
normalized[canonical] = {
|
|
44
|
-
...(normalized[canonical] || {}),
|
|
45
|
-
...(value || {}),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return normalized;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
module.exports = {
|
|
53
|
-
PROVIDER_ALIASES,
|
|
54
|
-
VALID_PROVIDERS,
|
|
55
|
-
normalizeProviderName,
|
|
56
|
-
normalizeProviderSettings,
|
|
57
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Claude authentication settings and helpers
|
|
3
|
-
* Extracted from settings.js for provider-specific isolation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Anthropic API key prefix for validation
|
|
8
|
-
*/
|
|
9
|
-
const ANTHROPIC_KEY_PREFIX = 'sk-ant-';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Environment variables used for Claude authentication
|
|
13
|
-
*/
|
|
14
|
-
const CLAUDE_AUTH_ENV_VARS = [
|
|
15
|
-
'ANTHROPIC_API_KEY',
|
|
16
|
-
'AWS_BEARER_TOKEN_BEDROCK',
|
|
17
|
-
'AWS_REGION',
|
|
18
|
-
'CLAUDE_CODE_USE_BEDROCK',
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Validate an Anthropic API key format
|
|
23
|
-
* @param {string|null|undefined} key - API key to validate
|
|
24
|
-
* @returns {boolean} True if key is falsy OR starts with valid prefix
|
|
25
|
-
*/
|
|
26
|
-
function isValidAnthropicKey(key) {
|
|
27
|
-
return !key || key.startsWith(ANTHROPIC_KEY_PREFIX);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Check if Bedrock mode is active based on env and overrides
|
|
32
|
-
* @param {Object} [envOverrides={}] - Environment variable overrides to check
|
|
33
|
-
* @returns {boolean} True if Bedrock mode is active
|
|
34
|
-
*/
|
|
35
|
-
function isBedrockMode(envOverrides = {}) {
|
|
36
|
-
return (
|
|
37
|
-
envOverrides.CLAUDE_CODE_USE_BEDROCK === '1' || process.env.CLAUDE_CODE_USE_BEDROCK === '1'
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Resolve Claude authentication environment variables from settings
|
|
43
|
-
* Bedrock takes priority over direct Anthropic API key AND OAuth session
|
|
44
|
-
* @param {Object} settings - Settings object from loadSettings()
|
|
45
|
-
* @returns {Object} Environment variables to set for Claude auth
|
|
46
|
-
*/
|
|
47
|
-
function resolveClaudeAuth(settings) {
|
|
48
|
-
const claudeSettings = settings.providerSettings?.claude || {};
|
|
49
|
-
const env = {};
|
|
50
|
-
|
|
51
|
-
// Bedrock takes priority over everything (including OAuth)
|
|
52
|
-
if (!process.env.AWS_BEARER_TOKEN_BEDROCK && claudeSettings.bedrockApiKey) {
|
|
53
|
-
env.AWS_BEARER_TOKEN_BEDROCK = claudeSettings.bedrockApiKey;
|
|
54
|
-
env.CLAUDE_CODE_USE_BEDROCK = '1';
|
|
55
|
-
if (claudeSettings.bedrockRegion && !process.env.AWS_REGION) {
|
|
56
|
-
env.AWS_REGION = claudeSettings.bedrockRegion;
|
|
57
|
-
}
|
|
58
|
-
} else if (process.env.AWS_BEARER_TOKEN_BEDROCK && !process.env.CLAUDE_CODE_USE_BEDROCK) {
|
|
59
|
-
// Auto-set CLAUDE_CODE_USE_BEDROCK if token present in env
|
|
60
|
-
env.CLAUDE_CODE_USE_BEDROCK = '1';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Anthropic API key only if no Bedrock
|
|
64
|
-
const hasBedrock = env.AWS_BEARER_TOKEN_BEDROCK || process.env.AWS_BEARER_TOKEN_BEDROCK;
|
|
65
|
-
if (!process.env.ANTHROPIC_API_KEY && !hasBedrock && claudeSettings.anthropicApiKey) {
|
|
66
|
-
env.ANTHROPIC_API_KEY = claudeSettings.anthropicApiKey;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return env;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
module.exports = {
|
|
73
|
-
ANTHROPIC_KEY_PREFIX,
|
|
74
|
-
CLAUDE_AUTH_ENV_VARS,
|
|
75
|
-
isValidAnthropicKey,
|
|
76
|
-
isBedrockMode,
|
|
77
|
-
resolveClaudeAuth,
|
|
78
|
-
};
|
package/lib/settings.js
DELETED
|
@@ -1,503 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settings management for zeroshot
|
|
3
|
-
* Persistent user preferences stored in ~/.zeroshot/settings.json
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const os = require('os');
|
|
9
|
-
const { validateMountConfig, validateEnvPassthrough } = require('./docker-config');
|
|
10
|
-
const {
|
|
11
|
-
VALID_PROVIDERS,
|
|
12
|
-
normalizeProviderName,
|
|
13
|
-
normalizeProviderSettings,
|
|
14
|
-
} = require('./provider-names');
|
|
15
|
-
|
|
16
|
-
// Lazy-loaded to avoid circular dependency (issue-providers → settings → issue-providers)
|
|
17
|
-
let _issueProviderFns = null;
|
|
18
|
-
function getIssueProviderFns() {
|
|
19
|
-
if (!_issueProviderFns) {
|
|
20
|
-
_issueProviderFns = require('../src/issue-providers');
|
|
21
|
-
}
|
|
22
|
-
return _issueProviderFns;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get settings file path (dynamically reads env var for testing)
|
|
27
|
-
* Using a getter ensures tests can override the path at runtime
|
|
28
|
-
* @returns {string}
|
|
29
|
-
*/
|
|
30
|
-
function getSettingsFile() {
|
|
31
|
-
return (
|
|
32
|
-
process.env.ZEROSHOT_SETTINGS_FILE || path.join(os.homedir(), '.zeroshot', 'settings.json')
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Import provider defaults from separate module to avoid circular dependency
|
|
37
|
-
const { getProviderDefaults, clearProviderDefaultsCache } = require('./provider-defaults');
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Model hierarchy for cost ceiling validation
|
|
41
|
-
* Higher number = more expensive/capable model
|
|
42
|
-
*/
|
|
43
|
-
const MODEL_HIERARCHY = {
|
|
44
|
-
opus: 3,
|
|
45
|
-
sonnet: 2,
|
|
46
|
-
haiku: 1,
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const VALID_MODELS = Object.keys(MODEL_HIERARCHY);
|
|
50
|
-
const LEVEL_RANKS = { level1: 1, level2: 2, level3: 3 };
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Validate a requested model against the maxModel ceiling and minModel floor
|
|
54
|
-
* @param {string} requestedModel - Model the agent wants to use
|
|
55
|
-
* @param {string} maxModel - Maximum allowed model (cost ceiling)
|
|
56
|
-
* @param {string|null} minModel - Minimum required model (cost floor)
|
|
57
|
-
* @returns {string} The validated model
|
|
58
|
-
* @throws {Error} If requested model exceeds ceiling or falls below floor
|
|
59
|
-
*/
|
|
60
|
-
function validateModelAgainstMax(requestedModel, maxModel, minModel = null) {
|
|
61
|
-
if (!requestedModel) return maxModel; // Default to ceiling if unspecified
|
|
62
|
-
|
|
63
|
-
if (!VALID_MODELS.includes(requestedModel)) {
|
|
64
|
-
throw new Error(`Invalid model "${requestedModel}". Valid: ${VALID_MODELS.join(', ')}`);
|
|
65
|
-
}
|
|
66
|
-
if (!VALID_MODELS.includes(maxModel)) {
|
|
67
|
-
throw new Error(`Invalid maxModel "${maxModel}". Valid: ${VALID_MODELS.join(', ')}`);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (MODEL_HIERARCHY[requestedModel] > MODEL_HIERARCHY[maxModel]) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Agent requests "${requestedModel}" but maxModel is "${maxModel}". ` +
|
|
73
|
-
`Either lower agent's model or raise maxModel.`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (minModel) {
|
|
78
|
-
if (!VALID_MODELS.includes(minModel)) {
|
|
79
|
-
throw new Error(`Invalid minModel "${minModel}". Valid: ${VALID_MODELS.join(', ')}`);
|
|
80
|
-
}
|
|
81
|
-
if (MODEL_HIERARCHY[minModel] > MODEL_HIERARCHY[maxModel]) {
|
|
82
|
-
throw new Error(`minModel "${minModel}" cannot be higher than maxModel "${maxModel}".`);
|
|
83
|
-
}
|
|
84
|
-
if (MODEL_HIERARCHY[requestedModel] < MODEL_HIERARCHY[minModel]) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`Agent requests "${requestedModel}" but minModel is "${minModel}". ` +
|
|
87
|
-
`Either raise agent's model or lower minModel.`
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return requestedModel;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Default settings (base - without issue provider settings which are added dynamically)
|
|
96
|
-
const DEFAULT_SETTINGS_BASE = {
|
|
97
|
-
maxModel: 'opus', // Cost ceiling - agents cannot use models above this
|
|
98
|
-
minModel: null, // Cost floor - agents cannot use models below this (null = no minimum)
|
|
99
|
-
defaultProvider: 'claude',
|
|
100
|
-
get providerSettings() {
|
|
101
|
-
// Dynamically build from providers on first access
|
|
102
|
-
return getProviderDefaults();
|
|
103
|
-
},
|
|
104
|
-
defaultConfig: 'conductor-bootstrap',
|
|
105
|
-
defaultDocker: false,
|
|
106
|
-
strictSchema: true, // true = reliable json output (default), false = live streaming (may crash - see bold-meadow-11)
|
|
107
|
-
logLevel: 'normal',
|
|
108
|
-
// Auto-update settings
|
|
109
|
-
autoCheckUpdates: true, // Check npm registry for newer versions
|
|
110
|
-
lastUpdateCheckAt: null, // Unix timestamp of last check (null = never checked)
|
|
111
|
-
lastSeenVersion: null, // Don't re-prompt for same version
|
|
112
|
-
// Claude command - customize how to invoke Claude CLI (default: 'claude')
|
|
113
|
-
// Example: 'ccr code' for claude-code-router integration
|
|
114
|
-
claudeCommand: 'claude',
|
|
115
|
-
// Docker isolation mounts - preset names or {host, container, readonly?} objects
|
|
116
|
-
// Valid presets: gh, git, ssh, aws, azure, kube, terraform, gcloud, claude, codex, gemini, opencode
|
|
117
|
-
dockerMounts: ['gh', 'git', 'ssh'],
|
|
118
|
-
// Extra env vars to pass to Docker container (in addition to preset-implied ones)
|
|
119
|
-
// Supports: VAR (if set), VAR_* (pattern), VAR=value (forced), VAR= (empty)
|
|
120
|
-
dockerEnvPassthrough: [],
|
|
121
|
-
// Container home directory - where $HOME resolves in container paths
|
|
122
|
-
// Default: /home/node (matches zeroshot-cluster-base image)
|
|
123
|
-
dockerContainerHome: '/home/node',
|
|
124
|
-
// Issue provider settings - defaultIssueSource is here, others come from providers
|
|
125
|
-
defaultIssueSource: 'github', // 'github' | 'gitlab' | 'jira' | 'azure-devops'
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
// Cache for merged defaults (base + issue provider settings)
|
|
129
|
-
let _defaultSettingsCache = null;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Get DEFAULT_SETTINGS with issue provider settings merged in
|
|
133
|
-
* Lazy-loaded to avoid circular dependency at module load time
|
|
134
|
-
*/
|
|
135
|
-
function getDefaultSettings() {
|
|
136
|
-
if (!_defaultSettingsCache) {
|
|
137
|
-
const issueProviderDefaults = getIssueProviderFns().getIssueProviderSettingsDefaults();
|
|
138
|
-
_defaultSettingsCache = {
|
|
139
|
-
...DEFAULT_SETTINGS_BASE,
|
|
140
|
-
...issueProviderDefaults,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
return _defaultSettingsCache;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// For backward compatibility, export DEFAULT_SETTINGS as a getter
|
|
147
|
-
const DEFAULT_SETTINGS = new Proxy(DEFAULT_SETTINGS_BASE, {
|
|
148
|
-
get(target, prop) {
|
|
149
|
-
// For known base properties, return from base
|
|
150
|
-
if (prop in target) {
|
|
151
|
-
return target[prop];
|
|
152
|
-
}
|
|
153
|
-
// For issue provider settings, get from merged defaults
|
|
154
|
-
const merged = getDefaultSettings();
|
|
155
|
-
return merged[prop];
|
|
156
|
-
},
|
|
157
|
-
has(target, prop) {
|
|
158
|
-
if (prop in target) return true;
|
|
159
|
-
const merged = getDefaultSettings();
|
|
160
|
-
return prop in merged;
|
|
161
|
-
},
|
|
162
|
-
ownKeys() {
|
|
163
|
-
const merged = getDefaultSettings();
|
|
164
|
-
return Object.keys(merged);
|
|
165
|
-
},
|
|
166
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
167
|
-
const merged = getDefaultSettings();
|
|
168
|
-
if (prop in merged) {
|
|
169
|
-
return { enumerable: true, configurable: true, value: merged[prop] };
|
|
170
|
-
}
|
|
171
|
-
return undefined;
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
function mapLegacyModelToLevel(model) {
|
|
176
|
-
switch (model) {
|
|
177
|
-
case 'haiku':
|
|
178
|
-
return 'level1';
|
|
179
|
-
case 'sonnet':
|
|
180
|
-
return 'level2';
|
|
181
|
-
case 'opus':
|
|
182
|
-
return 'level3';
|
|
183
|
-
default:
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function mergeProviderSettings(current, overrides) {
|
|
189
|
-
// Ensure current has all providers with their defaults
|
|
190
|
-
const providerDefaults = getProviderDefaults();
|
|
191
|
-
const merged = {};
|
|
192
|
-
|
|
193
|
-
for (const provider of VALID_PROVIDERS) {
|
|
194
|
-
merged[provider] = {
|
|
195
|
-
...(providerDefaults[provider] || {}),
|
|
196
|
-
...(current[provider] || {}),
|
|
197
|
-
...(overrides?.[provider] || {}),
|
|
198
|
-
};
|
|
199
|
-
if (!merged[provider].levelOverrides) {
|
|
200
|
-
merged[provider].levelOverrides = {};
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
return merged;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function applyLegacyModelBounds(settings) {
|
|
207
|
-
if (!settings.providerSettings) return settings;
|
|
208
|
-
const claude = settings.providerSettings.claude || {};
|
|
209
|
-
const legacyMaxLevel = mapLegacyModelToLevel(settings.maxModel);
|
|
210
|
-
const legacyMinLevel = mapLegacyModelToLevel(settings.minModel);
|
|
211
|
-
|
|
212
|
-
if (legacyMaxLevel) {
|
|
213
|
-
claude.maxLevel = legacyMaxLevel;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (legacyMinLevel) {
|
|
217
|
-
claude.minLevel = legacyMinLevel;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const minRank = LEVEL_RANKS[claude.minLevel] || LEVEL_RANKS.level1;
|
|
221
|
-
const maxRank = LEVEL_RANKS[claude.maxLevel] || LEVEL_RANKS.level3;
|
|
222
|
-
const defaultRank = LEVEL_RANKS[claude.defaultLevel] || LEVEL_RANKS.level2;
|
|
223
|
-
|
|
224
|
-
if (minRank > maxRank) {
|
|
225
|
-
claude.minLevel = 'level1';
|
|
226
|
-
claude.maxLevel = 'level3';
|
|
227
|
-
} else if (defaultRank < minRank) {
|
|
228
|
-
claude.defaultLevel = claude.minLevel;
|
|
229
|
-
} else if (defaultRank > maxRank) {
|
|
230
|
-
claude.defaultLevel = claude.maxLevel;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
settings.providerSettings.claude = claude;
|
|
234
|
-
return settings;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function normalizeLoadedSettings(parsed) {
|
|
238
|
-
const normalized = { ...parsed };
|
|
239
|
-
if (parsed.defaultProvider) {
|
|
240
|
-
normalized.defaultProvider = normalizeProviderName(parsed.defaultProvider);
|
|
241
|
-
}
|
|
242
|
-
if (parsed.providerSettings) {
|
|
243
|
-
normalized.providerSettings = normalizeProviderSettings(parsed.providerSettings);
|
|
244
|
-
}
|
|
245
|
-
return normalized;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Load settings from disk, merging with defaults
|
|
250
|
-
*/
|
|
251
|
-
function loadSettings() {
|
|
252
|
-
const settingsFile = getSettingsFile();
|
|
253
|
-
if (!fs.existsSync(settingsFile)) {
|
|
254
|
-
// Return a copy with resolved providerSettings
|
|
255
|
-
return {
|
|
256
|
-
...DEFAULT_SETTINGS,
|
|
257
|
-
providerSettings: getProviderDefaults(),
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
try {
|
|
261
|
-
const data = fs.readFileSync(settingsFile, 'utf8');
|
|
262
|
-
const parsed = normalizeLoadedSettings(JSON.parse(data));
|
|
263
|
-
const merged = { ...DEFAULT_SETTINGS, ...parsed };
|
|
264
|
-
merged.defaultProvider =
|
|
265
|
-
normalizeProviderName(merged.defaultProvider) || DEFAULT_SETTINGS.defaultProvider;
|
|
266
|
-
merged.providerSettings = mergeProviderSettings(getProviderDefaults(), parsed.providerSettings);
|
|
267
|
-
return applyLegacyModelBounds(merged);
|
|
268
|
-
} catch {
|
|
269
|
-
console.error('Warning: Could not load settings, using defaults');
|
|
270
|
-
return {
|
|
271
|
-
...DEFAULT_SETTINGS,
|
|
272
|
-
providerSettings: getProviderDefaults(),
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Save settings to disk
|
|
279
|
-
*/
|
|
280
|
-
function saveSettings(settings) {
|
|
281
|
-
const settingsFile = getSettingsFile();
|
|
282
|
-
const dir = path.dirname(settingsFile);
|
|
283
|
-
if (!fs.existsSync(dir)) {
|
|
284
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
285
|
-
}
|
|
286
|
-
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, 2), 'utf8');
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Validate claudeCommand setting
|
|
291
|
-
* @returns {string|null} Error message if invalid, null if valid
|
|
292
|
-
*/
|
|
293
|
-
function validateClaudeCommand(value) {
|
|
294
|
-
if (typeof value !== 'string') {
|
|
295
|
-
return 'claudeCommand must be a string';
|
|
296
|
-
}
|
|
297
|
-
if (value.trim().length === 0) {
|
|
298
|
-
return 'claudeCommand cannot be empty';
|
|
299
|
-
}
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Validate providerSettings structure by delegating to provider implementations
|
|
305
|
-
* @returns {string|null} Error message if invalid, null if valid
|
|
306
|
-
*/
|
|
307
|
-
function validateProviderSettings(value) {
|
|
308
|
-
const normalizedSettings = normalizeProviderSettings(value);
|
|
309
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
310
|
-
return 'providerSettings must be an object';
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Lazy require to avoid circular dependency
|
|
314
|
-
const { getProvider } = require('../src/providers');
|
|
315
|
-
|
|
316
|
-
for (const [providerName, settings] of Object.entries(normalizedSettings || {})) {
|
|
317
|
-
if (!VALID_PROVIDERS.includes(providerName)) {
|
|
318
|
-
return `Unknown provider in providerSettings: ${providerName}`;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Delegate validation to the provider
|
|
322
|
-
try {
|
|
323
|
-
const provider = getProvider(providerName);
|
|
324
|
-
const error = provider.validateSettings(settings);
|
|
325
|
-
if (error) return error;
|
|
326
|
-
} catch (err) {
|
|
327
|
-
return `Failed to validate ${providerName} settings: ${err.message}`;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Validate a setting value
|
|
336
|
-
* @returns {string|null} Error message if invalid, null if valid
|
|
337
|
-
*/
|
|
338
|
-
function validateSetting(key, value) {
|
|
339
|
-
if (!(key in DEFAULT_SETTINGS)) {
|
|
340
|
-
return `Unknown setting: ${key}`;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (key === 'maxModel' && !VALID_MODELS.includes(value)) {
|
|
344
|
-
return `Invalid model: ${value}. Valid models: ${VALID_MODELS.join(', ')}`;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (key === 'minModel' && value !== null && !VALID_MODELS.includes(value)) {
|
|
348
|
-
return `Invalid model: ${value}. Valid models: ${VALID_MODELS.join(', ')}, null`;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (key === 'logLevel' && !['quiet', 'normal', 'verbose'].includes(value)) {
|
|
352
|
-
return `Invalid log level: ${value}. Valid levels: quiet, normal, verbose`;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (key === 'claudeCommand') {
|
|
356
|
-
return validateClaudeCommand(value);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
if (key === 'defaultProvider') {
|
|
360
|
-
const normalized = normalizeProviderName(value);
|
|
361
|
-
if (!VALID_PROVIDERS.includes(normalized)) {
|
|
362
|
-
return `Invalid provider: ${value}. Valid providers: ${VALID_PROVIDERS.join(', ')}`;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (key === 'providerSettings') {
|
|
367
|
-
return validateProviderSettings(value);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (key === 'dockerMounts') {
|
|
371
|
-
return validateMountConfig(value);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (key === 'dockerEnvPassthrough') {
|
|
375
|
-
return validateEnvPassthrough(value);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Issue source settings validation (grouped for maintainability)
|
|
379
|
-
const issueSourceError = validateIssueSourceSetting(key, value);
|
|
380
|
-
if (issueSourceError !== undefined) {
|
|
381
|
-
return issueSourceError;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
return null;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Validate issue source related settings
|
|
389
|
-
* Delegates to issue providers for provider-specific settings
|
|
390
|
-
* @param {string} key - Setting key
|
|
391
|
-
* @param {any} value - Setting value
|
|
392
|
-
* @returns {string|null|undefined} Error message, null if valid, undefined if not an issue source setting
|
|
393
|
-
*/
|
|
394
|
-
function validateIssueSourceSetting(key, value) {
|
|
395
|
-
// defaultIssueSource is handled here (not provider-specific)
|
|
396
|
-
if (key === 'defaultIssueSource') {
|
|
397
|
-
const { listProviders } = getIssueProviderFns();
|
|
398
|
-
const validSources = listProviders();
|
|
399
|
-
if (!validSources.includes(value)) {
|
|
400
|
-
return `Invalid issue source: ${value}. Valid: ${validSources.join(', ')}`;
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Delegate to issue providers for provider-specific settings
|
|
406
|
-
const { validateIssueProviderSetting } = getIssueProviderFns();
|
|
407
|
-
return validateIssueProviderSetting(key, value);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Coerce value to correct type based on default value type
|
|
412
|
-
*/
|
|
413
|
-
function coerceValue(key, value) {
|
|
414
|
-
const defaultValue = DEFAULT_SETTINGS[key];
|
|
415
|
-
|
|
416
|
-
// Handle null values for minModel
|
|
417
|
-
if (key === 'minModel' && (value === 'null' || value === null)) {
|
|
418
|
-
return null;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (typeof defaultValue === 'boolean') {
|
|
422
|
-
return value === 'true' || value === '1' || value === 'yes' || value === true;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (typeof defaultValue === 'number') {
|
|
426
|
-
const parsed = parseInt(value);
|
|
427
|
-
if (isNaN(parsed)) {
|
|
428
|
-
throw new Error(`Invalid number: ${value}`);
|
|
429
|
-
}
|
|
430
|
-
return parsed;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Handle array settings (dockerMounts, dockerEnvPassthrough)
|
|
434
|
-
if (Array.isArray(defaultValue)) {
|
|
435
|
-
if (typeof value === 'string') {
|
|
436
|
-
try {
|
|
437
|
-
const parsed = JSON.parse(value);
|
|
438
|
-
if (!Array.isArray(parsed)) {
|
|
439
|
-
throw new Error(`${key} must be an array`);
|
|
440
|
-
}
|
|
441
|
-
return parsed;
|
|
442
|
-
} catch (e) {
|
|
443
|
-
if (e instanceof SyntaxError) {
|
|
444
|
-
throw new Error(`Invalid JSON for ${key}: ${value}`);
|
|
445
|
-
}
|
|
446
|
-
throw e;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return value;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
if (key === 'providerSettings') {
|
|
453
|
-
if (typeof value === 'string') {
|
|
454
|
-
try {
|
|
455
|
-
return normalizeProviderSettings(JSON.parse(value));
|
|
456
|
-
} catch {
|
|
457
|
-
throw new Error(`Invalid JSON for providerSettings: ${value}`);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return normalizeProviderSettings(value);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if (key === 'defaultProvider') {
|
|
464
|
-
return normalizeProviderName(value);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
return value;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Get parsed Claude command from settings/env
|
|
472
|
-
* Supports space-separated commands like 'ccr code'
|
|
473
|
-
* @returns {{ command: string, args: string[] }}
|
|
474
|
-
*/
|
|
475
|
-
function getClaudeCommand() {
|
|
476
|
-
const settings = loadSettings();
|
|
477
|
-
const raw = process.env.ZEROSHOT_CLAUDE_COMMAND || settings.claudeCommand || 'claude';
|
|
478
|
-
const parts = raw.trim().split(/\s+/);
|
|
479
|
-
return {
|
|
480
|
-
command: parts[0],
|
|
481
|
-
args: parts.slice(1),
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
module.exports = {
|
|
486
|
-
loadSettings,
|
|
487
|
-
saveSettings,
|
|
488
|
-
validateSetting,
|
|
489
|
-
coerceValue,
|
|
490
|
-
DEFAULT_SETTINGS,
|
|
491
|
-
getSettingsFile,
|
|
492
|
-
getClaudeCommand,
|
|
493
|
-
// Model validation exports
|
|
494
|
-
MODEL_HIERARCHY,
|
|
495
|
-
VALID_MODELS,
|
|
496
|
-
validateModelAgainstMax,
|
|
497
|
-
// Provider defaults exports
|
|
498
|
-
clearProviderDefaultsCache,
|
|
499
|
-
// Backward compatibility: SETTINGS_FILE as getter (reads env var dynamically)
|
|
500
|
-
get SETTINGS_FILE() {
|
|
501
|
-
return getSettingsFile();
|
|
502
|
-
},
|
|
503
|
-
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compatibility wrapper for Claude stream-json parsing.
|
|
3
|
-
* Prefer provider-specific parsers in src/providers.
|
|
4
|
-
*/
|
|
5
|
-
const { parseEvent, parseChunk } = require('../src/providers/anthropic/output-parser');
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
parseEvent,
|
|
9
|
-
parseChunk,
|
|
10
|
-
};
|