@covibes/zeroshot 5.3.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 -385
- package/cli/index.js +108 -4615
- package/package.json +16 -125
- package/CHANGELOG.md +0 -537
- package/cli/commands/providers.js +0 -150
- 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 -253
- 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 -697
- package/cluster-templates/base-templates/single-worker.json +0 -63
- package/cluster-templates/base-templates/worker-validator.json +0 -152
- 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 -212
- package/lib/id-detector.js +0 -53
- package/lib/provider-detection.js +0 -59
- package/lib/provider-names.js +0 -56
- package/lib/settings.js +0 -405
- 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/test-install.sh +0 -40
- package/scripts/validate-templates.js +0 -100
- package/src/agent/agent-config.js +0 -193
- package/src/agent/agent-context-builder.js +0 -335
- package/src/agent/agent-hook-executor.js +0 -402
- package/src/agent/agent-lifecycle.js +0 -683
- package/src/agent/agent-stuck-detector.js +0 -256
- package/src/agent/agent-task-executor.js +0 -1431
- package/src/agent/agent-trigger-evaluator.js +0 -67
- package/src/agent/output-extraction.js +0 -200
- package/src/agent/output-reformatter.js +0 -175
- package/src/agent/schema-utils.js +0 -111
- package/src/agent-wrapper.js +0 -570
- package/src/agents/git-pusher-agent.json +0 -20
- 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 -523
- package/src/config-router.js +0 -80
- package/src/config-validator.js +0 -1660
- package/src/copy-worker.js +0 -43
- package/src/github.js +0 -139
- package/src/isolation-manager.js +0 -1381
- package/src/ledger.js +0 -614
- 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 -2519
- package/src/preflight.js +0 -485
- package/src/process-metrics.js +0 -566
- package/src/providers/anthropic/cli-builder.js +0 -45
- package/src/providers/anthropic/index.js +0 -134
- package/src/providers/anthropic/models.js +0 -23
- package/src/providers/anthropic/output-parser.js +0 -159
- package/src/providers/base-provider.js +0 -181
- package/src/providers/capabilities.js +0 -51
- 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 -92
- package/src/providers/index.js +0 -75
- package/src/providers/openai/cli-builder.js +0 -122
- package/src/providers/openai/index.js +0 -135
- package/src/providers/openai/models.js +0 -21
- package/src/providers/openai/output-parser.js +0 -129
- package/src/schemas/sub-cluster.js +0 -156
- package/src/status-footer.js +0 -980
- package/src/sub-cluster-wrapper.js +0 -560
- package/src/task-runner.js +0 -30
- package/src/template-resolver.js +0 -369
- 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 -325
- package/src/tui/demo.js +0 -208
- package/src/tui/formatters.js +0 -128
- package/src/tui/index.js +0 -193
- package/src/tui/keybindings.js +0 -383
- package/src/tui/layout.js +0 -334
- package/src/tui/renderer.js +0 -194
- package/task-lib/attachable-watcher.js +0 -272
- package/task-lib/claude-recovery.js +0 -119
- package/task-lib/commands/clean.js +0 -50
- 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 -411
- 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 -98
- 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 -10
- package/task-lib/name-generator.js +0 -230
- package/task-lib/package.json +0 -3
- package/task-lib/runner.js +0 -144
- package/task-lib/scheduler.js +0 -252
- package/task-lib/store.js +0 -230
- package/task-lib/tui/formatters.js +0 -166
- package/task-lib/tui/index.js +0 -197
- package/task-lib/tui/layout.js +0 -111
- package/task-lib/tui/renderer.js +0 -119
- package/task-lib/tui.js +0 -384
- package/task-lib/watcher.js +0 -188
package/lib/completion.js
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
const omelette = require('omelette');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
function setupCompletion() {
|
|
6
|
-
const complete = omelette('zeroshot');
|
|
7
|
-
|
|
8
|
-
complete.on('start', ({ reply }) => {
|
|
9
|
-
reply(['--issue', '--text', '--config']);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
complete.on('list', ({ reply }) => {
|
|
13
|
-
reply([]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
complete.on('status', ({ reply }) => {
|
|
17
|
-
// Complete with cluster IDs
|
|
18
|
-
try {
|
|
19
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
20
|
-
if (fs.existsSync(clustersDir)) {
|
|
21
|
-
const clusterIds = fs
|
|
22
|
-
.readdirSync(clustersDir)
|
|
23
|
-
.filter((f) => f.endsWith('.json'))
|
|
24
|
-
.map((f) => f.replace('.json', ''));
|
|
25
|
-
reply(clusterIds);
|
|
26
|
-
} else {
|
|
27
|
-
reply([]);
|
|
28
|
-
}
|
|
29
|
-
} catch {
|
|
30
|
-
reply([]);
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
complete.on('logs', ({ reply }) => {
|
|
35
|
-
// Complete with cluster IDs or flags
|
|
36
|
-
try {
|
|
37
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
38
|
-
const completions = ['--follow', '-f', '--limit', '-n'];
|
|
39
|
-
|
|
40
|
-
if (fs.existsSync(clustersDir)) {
|
|
41
|
-
const clusterIds = fs
|
|
42
|
-
.readdirSync(clustersDir)
|
|
43
|
-
.filter((f) => f.endsWith('.json'))
|
|
44
|
-
.map((f) => f.replace('.json', ''));
|
|
45
|
-
reply([...clusterIds, ...completions]);
|
|
46
|
-
} else {
|
|
47
|
-
reply(completions);
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
reply(['--follow', '-f', '--limit', '-n']);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
complete.on('stop', ({ reply }) => {
|
|
55
|
-
// Complete with active cluster IDs
|
|
56
|
-
try {
|
|
57
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
58
|
-
if (fs.existsSync(clustersDir)) {
|
|
59
|
-
const clusterIds = fs
|
|
60
|
-
.readdirSync(clustersDir)
|
|
61
|
-
.filter((f) => f.endsWith('.json'))
|
|
62
|
-
.map((f) => f.replace('.json', ''));
|
|
63
|
-
reply(clusterIds);
|
|
64
|
-
} else {
|
|
65
|
-
reply([]);
|
|
66
|
-
}
|
|
67
|
-
} catch {
|
|
68
|
-
reply([]);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
complete.on('kill', ({ reply }) => {
|
|
73
|
-
// Complete with active cluster IDs
|
|
74
|
-
try {
|
|
75
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
76
|
-
if (fs.existsSync(clustersDir)) {
|
|
77
|
-
const clusterIds = fs
|
|
78
|
-
.readdirSync(clustersDir)
|
|
79
|
-
.filter((f) => f.endsWith('.json'))
|
|
80
|
-
.map((f) => f.replace('.json', ''));
|
|
81
|
-
reply(clusterIds);
|
|
82
|
-
} else {
|
|
83
|
-
reply([]);
|
|
84
|
-
}
|
|
85
|
-
} catch {
|
|
86
|
-
reply([]);
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
complete.on('export', ({ reply }) => {
|
|
91
|
-
// Complete with cluster IDs
|
|
92
|
-
try {
|
|
93
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
94
|
-
if (fs.existsSync(clustersDir)) {
|
|
95
|
-
const clusterIds = fs
|
|
96
|
-
.readdirSync(clustersDir)
|
|
97
|
-
.filter((f) => f.endsWith('.json'))
|
|
98
|
-
.map((f) => f.replace('.json', ''));
|
|
99
|
-
reply(clusterIds);
|
|
100
|
-
} else {
|
|
101
|
-
reply([]);
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
reply([]);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
complete.on('finish', ({ reply }) => {
|
|
109
|
-
// Complete with cluster IDs and flags
|
|
110
|
-
try {
|
|
111
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
112
|
-
const completions = ['--merge', '--pr', '--push'];
|
|
113
|
-
|
|
114
|
-
if (fs.existsSync(clustersDir)) {
|
|
115
|
-
const clusterIds = fs
|
|
116
|
-
.readdirSync(clustersDir)
|
|
117
|
-
.filter((f) => f.endsWith('.json'))
|
|
118
|
-
.map((f) => f.replace('.json', ''));
|
|
119
|
-
reply([...clusterIds, ...completions]);
|
|
120
|
-
} else {
|
|
121
|
-
reply(completions);
|
|
122
|
-
}
|
|
123
|
-
} catch {
|
|
124
|
-
reply(['--merge', '--pr', '--push']);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
complete.on('resume', ({ reply }) => {
|
|
129
|
-
// Complete with cluster IDs
|
|
130
|
-
try {
|
|
131
|
-
const clustersDir = path.join(process.env.HOME, '.zeroshot', 'clusters');
|
|
132
|
-
if (fs.existsSync(clustersDir)) {
|
|
133
|
-
const clusterIds = fs
|
|
134
|
-
.readdirSync(clustersDir)
|
|
135
|
-
.filter((f) => f.endsWith('.json'))
|
|
136
|
-
.map((f) => f.replace('.json', ''));
|
|
137
|
-
reply(clusterIds);
|
|
138
|
-
} else {
|
|
139
|
-
reply([]);
|
|
140
|
-
}
|
|
141
|
-
} catch {
|
|
142
|
-
reply([]);
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
complete.on('ui', ({ reply }) => {
|
|
147
|
-
reply(['--port']);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
complete.on('watch', ({ reply }) => {
|
|
151
|
-
reply(['--filter', '--refresh-rate', 'running', 'stopped', 'all']);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Default completion - show commands
|
|
155
|
-
complete.on('', ({ reply }) => {
|
|
156
|
-
reply([
|
|
157
|
-
'start',
|
|
158
|
-
'list',
|
|
159
|
-
'status',
|
|
160
|
-
'logs',
|
|
161
|
-
'stop',
|
|
162
|
-
'kill',
|
|
163
|
-
'finish',
|
|
164
|
-
'resume',
|
|
165
|
-
'export',
|
|
166
|
-
'ui',
|
|
167
|
-
'watch',
|
|
168
|
-
]);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
complete.init();
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
module.exports = { setupCompletion };
|
package/lib/docker-config.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Docker mount configuration for isolation mode
|
|
3
|
-
* Fully generic - no hardcoded paths, all configurable
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Built-in mount presets
|
|
8
|
-
* Uses $HOME placeholder - resolved at runtime based on dockerContainerHome setting
|
|
9
|
-
* Host paths use ~ (expanded to host user's home)
|
|
10
|
-
* Container paths use $HOME (expanded to configured container home)
|
|
11
|
-
*/
|
|
12
|
-
const MOUNT_PRESETS = {
|
|
13
|
-
gh: { host: '~/.config/gh', container: '$HOME/.config/gh', readonly: false },
|
|
14
|
-
git: { host: '~/.gitconfig', container: '$HOME/.gitconfig', readonly: true },
|
|
15
|
-
ssh: { host: '~/.ssh', container: '$HOME/.ssh', readonly: true },
|
|
16
|
-
aws: { host: '~/.aws', container: '$HOME/.aws', readonly: true },
|
|
17
|
-
azure: { host: '~/.azure', container: '$HOME/.azure', readonly: true },
|
|
18
|
-
kube: { host: '~/.kube', container: '$HOME/.kube', readonly: true },
|
|
19
|
-
terraform: { host: '~/.terraform.d', container: '$HOME/.terraform.d', readonly: false },
|
|
20
|
-
gcloud: { host: '~/.config/gcloud', container: '$HOME/.config/gcloud', readonly: true },
|
|
21
|
-
claude: { host: '~/.claude', container: '$HOME/.claude', readonly: true },
|
|
22
|
-
codex: { host: '~/.config/codex', container: '$HOME/.config/codex', readonly: true },
|
|
23
|
-
gemini: { host: '~/.config/gemini', container: '$HOME/.config/gemini', readonly: true },
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Environment variables to auto-pass for each preset
|
|
28
|
-
* Supports:
|
|
29
|
-
* - Simple: 'VAR_NAME' (pass if set in host env)
|
|
30
|
-
* - Pattern: 'VAR_*' (pass all matching vars)
|
|
31
|
-
* - Forced: 'VAR=value' (always set to value)
|
|
32
|
-
* - Empty: 'VAR=' (always set to empty string)
|
|
33
|
-
*/
|
|
34
|
-
const ENV_PRESETS = {
|
|
35
|
-
aws: ['AWS_REGION', 'AWS_DEFAULT_REGION', 'AWS_PROFILE', 'AWS_PAGER='],
|
|
36
|
-
azure: ['AZURE_SUBSCRIPTION_ID', 'AZURE_TENANT_ID', 'AZURE_CLIENT_ID'],
|
|
37
|
-
gcloud: ['CLOUDSDK_CORE_PROJECT', 'GOOGLE_CLOUD_PROJECT'],
|
|
38
|
-
kube: ['KUBECONFIG'],
|
|
39
|
-
terraform: ['TF_VAR_*'],
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Resolve mount config to actual mount specs
|
|
44
|
-
* @param {Array<string|object>} config - Preset names or {host, container, readonly?} objects
|
|
45
|
-
* @param {object} options - Resolution options
|
|
46
|
-
* @param {string} [options.containerHome='/root'] - Container home directory for $HOME expansion
|
|
47
|
-
* @returns {Array<{host: string, container: string, readonly: boolean}>}
|
|
48
|
-
*/
|
|
49
|
-
function resolveMounts(config, options = {}) {
|
|
50
|
-
if (!Array.isArray(config)) {
|
|
51
|
-
throw new Error('dockerMounts must be an array');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const containerHome = options.containerHome || '/root';
|
|
55
|
-
|
|
56
|
-
return config.flatMap((item) => {
|
|
57
|
-
if (typeof item === 'string') {
|
|
58
|
-
const preset = MOUNT_PRESETS[item];
|
|
59
|
-
if (!preset) {
|
|
60
|
-
throw new Error(
|
|
61
|
-
`Unknown mount preset: "${item}". Valid presets: ${Object.keys(MOUNT_PRESETS).join(', ')}`
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
host: preset.host,
|
|
66
|
-
container: preset.container.replace(/\$HOME/g, containerHome),
|
|
67
|
-
readonly: preset.readonly,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (typeof item === 'object' && item !== null) {
|
|
72
|
-
if (!item.host || !item.container) {
|
|
73
|
-
throw new Error('Custom mount must have "host" and "container" properties');
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
host: item.host,
|
|
77
|
-
container: item.container.replace(/\$HOME/g, containerHome),
|
|
78
|
-
readonly: item.readonly !== false,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
throw new Error(
|
|
83
|
-
`Invalid mount config: ${JSON.stringify(item)}. Use preset name or {host, container, readonly?}`
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Resolve env vars to pass based on enabled presets + explicit additions
|
|
90
|
-
* @param {Array<string|object>} mountConfig - The mount config (to detect presets)
|
|
91
|
-
* @param {Array<string>} extraEnvs - Additional env vars to pass
|
|
92
|
-
* @returns {Array<string>} - List of env var specs
|
|
93
|
-
*/
|
|
94
|
-
function resolveEnvs(mountConfig, extraEnvs = []) {
|
|
95
|
-
const envs = new Set(extraEnvs);
|
|
96
|
-
|
|
97
|
-
for (const item of mountConfig) {
|
|
98
|
-
if (typeof item === 'string' && ENV_PRESETS[item]) {
|
|
99
|
-
for (const envVar of ENV_PRESETS[item]) {
|
|
100
|
-
envs.add(envVar);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return [...envs];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Expand env var patterns and resolve values
|
|
110
|
-
* Supports:
|
|
111
|
-
* - Simple: 'VAR_NAME' (use value from env if set)
|
|
112
|
-
* - Pattern: 'VAR_*' (expand to all matching vars)
|
|
113
|
-
* - Forced: 'VAR=value' (always set to value)
|
|
114
|
-
* - Empty: 'VAR=' (always set to empty string)
|
|
115
|
-
*
|
|
116
|
-
* @param {Array<string>} envVars - List of env var specs
|
|
117
|
-
* @param {object} env - Environment object (defaults to process.env)
|
|
118
|
-
* @returns {Array<{name: string, value: string|null, forced: boolean}>}
|
|
119
|
-
*/
|
|
120
|
-
function expandEnvPatterns(envVars, env = process.env) {
|
|
121
|
-
const result = [];
|
|
122
|
-
|
|
123
|
-
for (const envVar of envVars) {
|
|
124
|
-
// Forced value: VAR=value or VAR=
|
|
125
|
-
if (envVar.includes('=')) {
|
|
126
|
-
const [name, ...valueParts] = envVar.split('=');
|
|
127
|
-
const value = valueParts.join('=');
|
|
128
|
-
result.push({ name, value, forced: true });
|
|
129
|
-
}
|
|
130
|
-
// Pattern: VAR_*
|
|
131
|
-
else if (envVar.endsWith('*')) {
|
|
132
|
-
const prefix = envVar.slice(0, -1);
|
|
133
|
-
for (const key of Object.keys(env)) {
|
|
134
|
-
if (key.startsWith(prefix)) {
|
|
135
|
-
result.push({ name: key, value: null, forced: false });
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// Simple: VAR_NAME
|
|
140
|
-
else {
|
|
141
|
-
result.push({ name: envVar, value: null, forced: false });
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return result;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Validate mount config
|
|
150
|
-
* @param {unknown} value - Value to validate
|
|
151
|
-
* @returns {string|null} - Error message if invalid, null if valid
|
|
152
|
-
*/
|
|
153
|
-
function validateMountConfig(value) {
|
|
154
|
-
if (!Array.isArray(value)) {
|
|
155
|
-
return 'dockerMounts must be an array';
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
for (const item of value) {
|
|
159
|
-
if (typeof item === 'string') {
|
|
160
|
-
if (!MOUNT_PRESETS[item]) {
|
|
161
|
-
return `Unknown mount preset: "${item}". Valid: ${Object.keys(MOUNT_PRESETS).join(', ')}`;
|
|
162
|
-
}
|
|
163
|
-
} else if (typeof item === 'object' && item !== null) {
|
|
164
|
-
if (!item.host) {
|
|
165
|
-
return 'Custom mount missing "host" property';
|
|
166
|
-
}
|
|
167
|
-
if (!item.container) {
|
|
168
|
-
return 'Custom mount missing "container" property';
|
|
169
|
-
}
|
|
170
|
-
if (item.readonly !== undefined && typeof item.readonly !== 'boolean') {
|
|
171
|
-
return '"readonly" must be a boolean';
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
return `Invalid mount: ${JSON.stringify(item)}. Use preset name or {host, container, readonly?}`;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
return null;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Validate env passthrough config
|
|
183
|
-
* @param {unknown} value - Value to validate
|
|
184
|
-
* @returns {string|null} - Error message if invalid, null if valid
|
|
185
|
-
*/
|
|
186
|
-
function validateEnvPassthrough(value) {
|
|
187
|
-
if (!Array.isArray(value)) {
|
|
188
|
-
return 'dockerEnvPassthrough must be an array';
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
for (const item of value) {
|
|
192
|
-
if (typeof item !== 'string') {
|
|
193
|
-
return `Invalid env var: ${JSON.stringify(item)}. Must be a string`;
|
|
194
|
-
}
|
|
195
|
-
// Allow: VAR, VAR_*, VAR=value, VAR=
|
|
196
|
-
if (!/^[A-Z_][A-Z0-9_]*(\*|=.*)?$/i.test(item)) {
|
|
197
|
-
return `Invalid env var spec: "${item}". Use VAR, VAR_*, or VAR=value`;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
module.exports = {
|
|
205
|
-
MOUNT_PRESETS,
|
|
206
|
-
ENV_PRESETS,
|
|
207
|
-
resolveMounts,
|
|
208
|
-
resolveEnvs,
|
|
209
|
-
expandEnvPatterns,
|
|
210
|
-
validateMountConfig,
|
|
211
|
-
validateEnvPassthrough,
|
|
212
|
-
};
|
package/lib/id-detector.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ID Detector - Determines if an ID is a task or cluster
|
|
3
|
-
*
|
|
4
|
-
* Strategy:
|
|
5
|
-
* 1. Check if ID exists in cluster storage
|
|
6
|
-
* 2. If not, check if ID exists in task storage
|
|
7
|
-
* 3. Return type: 'cluster', 'task', or null
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const path = require('path');
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const os = require('os');
|
|
13
|
-
|
|
14
|
-
// Storage paths
|
|
15
|
-
const CLUSTER_DIR = path.join(os.homedir(), '.zeroshot');
|
|
16
|
-
const TASK_DIR = path.join(os.homedir(), '.claude-zeroshot');
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Detect if ID is a cluster or task
|
|
20
|
-
* @param {string} id - The ID to check
|
|
21
|
-
* @returns {'cluster'|'task'|null} - Type of ID or null if not found
|
|
22
|
-
*/
|
|
23
|
-
function detectIdType(id) {
|
|
24
|
-
// Check clusters
|
|
25
|
-
const clusterFile = path.join(CLUSTER_DIR, 'clusters.json');
|
|
26
|
-
if (fs.existsSync(clusterFile)) {
|
|
27
|
-
try {
|
|
28
|
-
const clusters = JSON.parse(fs.readFileSync(clusterFile, 'utf8'));
|
|
29
|
-
if (clusters[id]) {
|
|
30
|
-
return 'cluster';
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
// Ignore parse errors
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Check tasks
|
|
38
|
-
const taskFile = path.join(TASK_DIR, 'tasks.json');
|
|
39
|
-
if (fs.existsSync(taskFile)) {
|
|
40
|
-
try {
|
|
41
|
-
const tasks = JSON.parse(fs.readFileSync(taskFile, 'utf8'));
|
|
42
|
-
if (tasks[id]) {
|
|
43
|
-
return 'task';
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// Ignore parse errors
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = { detectIdType };
|
|
@@ -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,56 +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
|
-
};
|
|
9
|
-
|
|
10
|
-
const VALID_PROVIDERS = ['claude', 'codex', 'gemini'];
|
|
11
|
-
|
|
12
|
-
function normalizeProviderName(name) {
|
|
13
|
-
if (!name || typeof name !== 'string') return name;
|
|
14
|
-
const normalized = PROVIDER_ALIASES[name.toLowerCase()];
|
|
15
|
-
return normalized || name;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function normalizeProviderSettings(providerSettings) {
|
|
19
|
-
if (
|
|
20
|
-
!providerSettings ||
|
|
21
|
-
typeof providerSettings !== 'object' ||
|
|
22
|
-
Array.isArray(providerSettings)
|
|
23
|
-
) {
|
|
24
|
-
return providerSettings;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const normalized = {};
|
|
28
|
-
const entries = Object.entries(providerSettings);
|
|
29
|
-
const aliasFirst = entries.sort(([left], [right]) => {
|
|
30
|
-
const leftIsCanonical = normalizeProviderName(left) === left;
|
|
31
|
-
const rightIsCanonical = normalizeProviderName(right) === right;
|
|
32
|
-
if (leftIsCanonical === rightIsCanonical) return 0;
|
|
33
|
-
return leftIsCanonical ? 1 : -1;
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
for (const [key, value] of aliasFirst) {
|
|
37
|
-
const canonical = normalizeProviderName(key);
|
|
38
|
-
if (!VALID_PROVIDERS.includes(canonical)) {
|
|
39
|
-
normalized[key] = value;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
normalized[canonical] = {
|
|
43
|
-
...(normalized[canonical] || {}),
|
|
44
|
-
...(value || {}),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return normalized;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = {
|
|
52
|
-
PROVIDER_ALIASES,
|
|
53
|
-
VALID_PROVIDERS,
|
|
54
|
-
normalizeProviderName,
|
|
55
|
-
normalizeProviderSettings,
|
|
56
|
-
};
|