@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
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
const readline = require('readline');
|
|
2
|
-
const { loadSettings, saveSettings } = require('../../lib/settings');
|
|
3
|
-
const { VALID_PROVIDERS, normalizeProviderName } = require('../../lib/provider-names');
|
|
4
|
-
const { detectProviders, getProvider } = require('../../src/providers');
|
|
5
|
-
|
|
6
|
-
function question(rl, prompt) {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
rl.question(prompt, (answer) => resolve(answer.trim()));
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async function providersCommand() {
|
|
13
|
-
const detected = await detectProviders();
|
|
14
|
-
const settings = loadSettings();
|
|
15
|
-
|
|
16
|
-
console.log('\nProvider Status Default Level Model CLI Path');
|
|
17
|
-
console.log('─'.repeat(70));
|
|
18
|
-
|
|
19
|
-
for (const [name, status] of Object.entries(detected)) {
|
|
20
|
-
const provider = getProvider(name);
|
|
21
|
-
const statusIcon = status.available ? '✓ found' : '✗ not found';
|
|
22
|
-
const providerSettings = settings.providerSettings?.[name] || {};
|
|
23
|
-
const defaultLevel = providerSettings.defaultLevel || provider.getDefaultLevel();
|
|
24
|
-
const levelOverrides = providerSettings.levelOverrides || {};
|
|
25
|
-
const modelSpec = provider.resolveModelSpec(defaultLevel, levelOverrides);
|
|
26
|
-
const modelLabel = modelSpec?.model || '-';
|
|
27
|
-
const cliPath = status.available ? await provider.getCliPath() : '-';
|
|
28
|
-
const isDefault = settings.defaultProvider === name ? ' (default)' : '';
|
|
29
|
-
|
|
30
|
-
console.log(
|
|
31
|
-
`${provider.displayName.padEnd(12)} ${statusIcon.padEnd(12)} ${defaultLevel.padEnd(
|
|
32
|
-
14
|
|
33
|
-
)} ${modelLabel.padEnd(16)} ${cliPath}${isDefault}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
console.log('\nCommands:');
|
|
38
|
-
console.log(' zeroshot providers set-default <provider> Set default provider');
|
|
39
|
-
console.log(' zeroshot providers setup <provider> Configure a provider');
|
|
40
|
-
console.log(
|
|
41
|
-
'\nNote: Authentication is managed by each CLI; zeroshot does not validate login or API keys.'
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function setDefaultCommand(args) {
|
|
46
|
-
const provider = normalizeProviderName(args[0]);
|
|
47
|
-
if (!VALID_PROVIDERS.includes(provider)) {
|
|
48
|
-
console.error(`Invalid provider: ${args[0]}`);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const settings = loadSettings();
|
|
53
|
-
settings.defaultProvider = provider;
|
|
54
|
-
saveSettings(settings);
|
|
55
|
-
|
|
56
|
-
console.log(`Default provider set to: ${provider}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function setupCommand(args) {
|
|
60
|
-
const provider = normalizeProviderName(args[0]);
|
|
61
|
-
if (!provider) {
|
|
62
|
-
console.error('Provider is required (claude, codex, gemini)');
|
|
63
|
-
process.exit(1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const providerModule = getProvider(provider);
|
|
67
|
-
|
|
68
|
-
console.log(`\n${providerModule.displayName} Setup\n`);
|
|
69
|
-
|
|
70
|
-
const available = await providerModule.isAvailable();
|
|
71
|
-
if (!available) {
|
|
72
|
-
console.log(`✗ ${providerModule.cliCommand} CLI not found`);
|
|
73
|
-
console.log('\nInstall with:');
|
|
74
|
-
console.log(providerModule.getInstallInstructions());
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
console.log(`✓ ${providerModule.cliCommand} CLI found`);
|
|
78
|
-
|
|
79
|
-
console.log('\nAuth is user-managed; run the CLI login flow if needed:');
|
|
80
|
-
console.log(providerModule.getAuthInstructions());
|
|
81
|
-
|
|
82
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const levels = providerModule.getLevelMapping();
|
|
86
|
-
const levelKeys = Object.keys(levels).sort((a, b) => levels[a].rank - levels[b].rank);
|
|
87
|
-
|
|
88
|
-
console.log('\nAvailable levels (cheapest to most capable):');
|
|
89
|
-
levelKeys.forEach((level, i) => {
|
|
90
|
-
const spec = levels[level];
|
|
91
|
-
const reasoning = spec.reasoningEffort ? ` (reasoning: ${spec.reasoningEffort})` : '';
|
|
92
|
-
console.log(` ${i + 1}) ${level} -> ${spec.model}${reasoning}`);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
const maxIdxRaw = await question(rl, `\nMax level (cost ceiling) [${levelKeys.length}]: `);
|
|
96
|
-
const minIdxRaw = await question(rl, 'Min level (floor) [1]: ');
|
|
97
|
-
const maxIdxNum = Math.min(
|
|
98
|
-
levelKeys.length,
|
|
99
|
-
Math.max(1, parseInt(maxIdxRaw) || levelKeys.length)
|
|
100
|
-
);
|
|
101
|
-
const minIdxNum = Math.min(levelKeys.length, Math.max(1, parseInt(minIdxRaw) || 1));
|
|
102
|
-
const minIdx = Math.min(minIdxNum, maxIdxNum);
|
|
103
|
-
const maxIdx = Math.max(minIdxNum, maxIdxNum);
|
|
104
|
-
|
|
105
|
-
const defaultIdxRaw = await question(
|
|
106
|
-
rl,
|
|
107
|
-
`Default level (between ${minIdx}-${maxIdx}) [${maxIdx}]: `
|
|
108
|
-
);
|
|
109
|
-
const defaultIdxNum = Math.min(maxIdx, Math.max(minIdx, parseInt(defaultIdxRaw) || maxIdx));
|
|
110
|
-
|
|
111
|
-
const levelOverrides = {};
|
|
112
|
-
const wantsOverrides = await question(rl, 'Override models per level? [y/N]: ');
|
|
113
|
-
if (wantsOverrides.toLowerCase() === 'y') {
|
|
114
|
-
const catalog = Object.keys(providerModule.getModelCatalog());
|
|
115
|
-
for (const level of levelKeys) {
|
|
116
|
-
const modelChoice = await question(rl, `Model for ${level} (${catalog.join(', ')}): `);
|
|
117
|
-
if (modelChoice) levelOverrides[level] = { model: modelChoice };
|
|
118
|
-
if (provider === 'codex') {
|
|
119
|
-
const reasoning = await question(rl, `Reasoning for ${level} (low|medium|high|xhigh): `);
|
|
120
|
-
if (reasoning) {
|
|
121
|
-
levelOverrides[level] = {
|
|
122
|
-
...(levelOverrides[level] || {}),
|
|
123
|
-
reasoningEffort: reasoning,
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const settings = loadSettings();
|
|
131
|
-
settings.providerSettings = settings.providerSettings || {};
|
|
132
|
-
settings.providerSettings[provider] = {
|
|
133
|
-
maxLevel: levelKeys[maxIdx - 1],
|
|
134
|
-
minLevel: levelKeys[minIdx - 1],
|
|
135
|
-
defaultLevel: levelKeys[defaultIdxNum - 1],
|
|
136
|
-
levelOverrides,
|
|
137
|
-
};
|
|
138
|
-
saveSettings(settings);
|
|
139
|
-
|
|
140
|
-
console.log(`\n✓ ${providerModule.displayName} configured successfully`);
|
|
141
|
-
} finally {
|
|
142
|
-
rl.close();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
module.exports = {
|
|
147
|
-
providersCommand,
|
|
148
|
-
setDefaultCommand,
|
|
149
|
-
setupCommand,
|
|
150
|
-
};
|
package/cli/lib/first-run.js
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* First-Run Setup Wizard
|
|
3
|
-
*
|
|
4
|
-
* Interactive setup on first use:
|
|
5
|
-
* - Welcome banner
|
|
6
|
-
* - Max model ceiling selection (sonnet/opus/haiku)
|
|
7
|
-
* - Auto-update preference
|
|
8
|
-
* - Marks setup as complete
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const readline = require('readline');
|
|
12
|
-
const { loadSettings, saveSettings } = require('../../lib/settings');
|
|
13
|
-
const { detectProviders } = require('../../src/providers');
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Print welcome banner
|
|
17
|
-
*/
|
|
18
|
-
function printWelcome() {
|
|
19
|
-
console.log(`
|
|
20
|
-
╔═══════════════════════════════════════════════════════════════╗
|
|
21
|
-
║ ║
|
|
22
|
-
║ Welcome to Zeroshot! ║
|
|
23
|
-
║ Multi-agent orchestration engine ║
|
|
24
|
-
║ ║
|
|
25
|
-
║ Let's configure a few settings to get started. ║
|
|
26
|
-
║ ║
|
|
27
|
-
╚═══════════════════════════════════════════════════════════════╝
|
|
28
|
-
`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Create readline interface
|
|
33
|
-
* @returns {readline.Interface}
|
|
34
|
-
*/
|
|
35
|
-
function createReadline() {
|
|
36
|
-
return readline.createInterface({
|
|
37
|
-
input: process.stdin,
|
|
38
|
-
output: process.stdout,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Prompt for provider selection
|
|
44
|
-
* @param {readline.Interface} rl
|
|
45
|
-
* @param {object} detected
|
|
46
|
-
* @returns {Promise<string>}
|
|
47
|
-
*/
|
|
48
|
-
function promptProvider(rl, detected) {
|
|
49
|
-
console.log('\nWhich AI provider would you like to use by default?\n');
|
|
50
|
-
|
|
51
|
-
const available = Object.entries(detected).filter(([_, status]) => status.available);
|
|
52
|
-
|
|
53
|
-
if (available.length === 0) {
|
|
54
|
-
console.log('No AI CLI tools detected. Please install one of:');
|
|
55
|
-
console.log(' - Claude Code: npm install -g @anthropic-ai/claude-code');
|
|
56
|
-
console.log(' - Codex CLI: npm install -g @openai/codex');
|
|
57
|
-
console.log(' - Gemini CLI: npm install -g @google/gemini-cli');
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
available.forEach(([name], i) => {
|
|
62
|
-
console.log(` ${i + 1}) ${name} (installed)`);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return new Promise((resolve) => {
|
|
66
|
-
rl.question('\nChoice [1]: ', (answer) => {
|
|
67
|
-
const idx = parseInt(answer) - 1 || 0;
|
|
68
|
-
resolve(available[idx]?.[0] || available[0][0]);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Prompt for model selection
|
|
75
|
-
* @param {readline.Interface} rl
|
|
76
|
-
* @returns {Promise<string>}
|
|
77
|
-
*/
|
|
78
|
-
function promptModel(rl) {
|
|
79
|
-
return new Promise((resolve) => {
|
|
80
|
-
console.log('What is the maximum Claude model agents can use? (cost ceiling)\n');
|
|
81
|
-
console.log(' 1) sonnet - Agents can use sonnet or haiku (recommended)');
|
|
82
|
-
console.log(' 2) opus - Agents can use opus, sonnet, or haiku');
|
|
83
|
-
console.log(' 3) haiku - Agents can only use haiku (lowest cost)\n');
|
|
84
|
-
|
|
85
|
-
rl.question('Enter 1, 2, or 3 [2]: ', (answer) => {
|
|
86
|
-
const choice = answer.trim() || '2';
|
|
87
|
-
switch (choice) {
|
|
88
|
-
case '2':
|
|
89
|
-
resolve('opus');
|
|
90
|
-
break;
|
|
91
|
-
case '3':
|
|
92
|
-
resolve('haiku');
|
|
93
|
-
break;
|
|
94
|
-
default:
|
|
95
|
-
resolve('sonnet');
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Prompt for auto-update preference
|
|
103
|
-
* @param {readline.Interface} rl
|
|
104
|
-
* @returns {Promise<boolean>}
|
|
105
|
-
*/
|
|
106
|
-
function promptAutoUpdate(rl) {
|
|
107
|
-
return new Promise((resolve) => {
|
|
108
|
-
console.log('\nWould you like zeroshot to check for updates automatically?');
|
|
109
|
-
console.log('(Checks npm registry every 24 hours)\n');
|
|
110
|
-
|
|
111
|
-
rl.question('Enable auto-update checks? [Y/n]: ', (answer) => {
|
|
112
|
-
const normalized = answer.trim().toLowerCase();
|
|
113
|
-
// Default to yes if empty or starts with 'y'
|
|
114
|
-
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Print completion message
|
|
121
|
-
* @param {object} settings - Saved settings
|
|
122
|
-
*/
|
|
123
|
-
function printComplete(settings) {
|
|
124
|
-
console.log(`
|
|
125
|
-
╔═══════════════════════════════════════════════════════════════╗
|
|
126
|
-
║ Setup complete! ║
|
|
127
|
-
╚═══════════════════════════════════════════════════════════════╝
|
|
128
|
-
|
|
129
|
-
Your settings:
|
|
130
|
-
• Provider: ${settings.defaultProvider}
|
|
131
|
-
• Max model: ${settings.maxModel} (Claude ceiling)
|
|
132
|
-
• Auto-updates: ${settings.autoCheckUpdates ? 'enabled' : 'disabled'}
|
|
133
|
-
|
|
134
|
-
Change anytime with: zeroshot settings set <key> <value>
|
|
135
|
-
|
|
136
|
-
Get started:
|
|
137
|
-
zeroshot run "Fix the bug in auth.js"
|
|
138
|
-
zeroshot run 123 (GitHub issue number)
|
|
139
|
-
zeroshot --help
|
|
140
|
-
|
|
141
|
-
`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Check if first-run setup is needed
|
|
146
|
-
* @param {object} settings - Current settings
|
|
147
|
-
* @returns {boolean}
|
|
148
|
-
*/
|
|
149
|
-
function detectFirstRun(settings) {
|
|
150
|
-
return !settings.firstRunComplete;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Main entry point - run first-time setup if needed
|
|
155
|
-
* @param {object} options
|
|
156
|
-
* @param {boolean} options.quiet - Skip interactive prompts
|
|
157
|
-
* @returns {Promise<boolean>} True if setup was run
|
|
158
|
-
*/
|
|
159
|
-
async function checkFirstRun(options = {}) {
|
|
160
|
-
const settings = loadSettings();
|
|
161
|
-
|
|
162
|
-
// Already completed setup
|
|
163
|
-
if (!detectFirstRun(settings)) {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Quiet mode - use defaults, mark complete
|
|
168
|
-
if (options.quiet) {
|
|
169
|
-
settings.firstRunComplete = true;
|
|
170
|
-
saveSettings(settings);
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Interactive setup
|
|
175
|
-
printWelcome();
|
|
176
|
-
|
|
177
|
-
const rl = createReadline();
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const detected = await detectProviders();
|
|
181
|
-
const provider = await promptProvider(rl, detected);
|
|
182
|
-
settings.defaultProvider = provider;
|
|
183
|
-
|
|
184
|
-
// Model ceiling selection
|
|
185
|
-
const model = await promptModel(rl);
|
|
186
|
-
settings.maxModel = model;
|
|
187
|
-
|
|
188
|
-
// Auto-update preference
|
|
189
|
-
const autoUpdate = await promptAutoUpdate(rl);
|
|
190
|
-
settings.autoCheckUpdates = autoUpdate;
|
|
191
|
-
|
|
192
|
-
// Mark complete
|
|
193
|
-
settings.firstRunComplete = true;
|
|
194
|
-
saveSettings(settings);
|
|
195
|
-
|
|
196
|
-
// Print completion
|
|
197
|
-
printComplete(settings);
|
|
198
|
-
|
|
199
|
-
return true;
|
|
200
|
-
} finally {
|
|
201
|
-
rl.close();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
module.exports = {
|
|
206
|
-
checkFirstRun,
|
|
207
|
-
// Exported for testing
|
|
208
|
-
detectFirstRun,
|
|
209
|
-
printWelcome,
|
|
210
|
-
printComplete,
|
|
211
|
-
};
|
|
@@ -1,281 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Update Checker - Checks npm registry for newer versions
|
|
3
|
-
*
|
|
4
|
-
* Features:
|
|
5
|
-
* - 24-hour check interval (avoids registry spam)
|
|
6
|
-
* - 5-second timeout (non-blocking if offline)
|
|
7
|
-
* - Interactive prompt for manual update
|
|
8
|
-
* - Respects quiet mode (no prompts in CI/scripts)
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const https = require('https');
|
|
12
|
-
const { spawn } = require('child_process');
|
|
13
|
-
const readline = require('readline');
|
|
14
|
-
const { loadSettings, saveSettings } = require('../../lib/settings');
|
|
15
|
-
|
|
16
|
-
// 24 hours in milliseconds
|
|
17
|
-
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
18
|
-
|
|
19
|
-
// Timeout for npm registry fetch (5 seconds)
|
|
20
|
-
const FETCH_TIMEOUT_MS = 5000;
|
|
21
|
-
|
|
22
|
-
// npm registry URL
|
|
23
|
-
const REGISTRY_URL = 'https://registry.npmjs.org/@covibes/zeroshot/latest';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Get current package version
|
|
27
|
-
* @returns {string}
|
|
28
|
-
*/
|
|
29
|
-
function getCurrentVersion() {
|
|
30
|
-
const pkg = require('../../package.json');
|
|
31
|
-
return pkg.version;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Compare semver versions
|
|
36
|
-
* @param {string} current - Current version (e.g., "1.5.0")
|
|
37
|
-
* @param {string} latest - Latest version (e.g., "1.6.0")
|
|
38
|
-
* @returns {boolean} True if latest > current
|
|
39
|
-
*/
|
|
40
|
-
function isNewerVersion(current, latest) {
|
|
41
|
-
const currentParts = current.split('.').map(Number);
|
|
42
|
-
const latestParts = latest.split('.').map(Number);
|
|
43
|
-
|
|
44
|
-
for (let i = 0; i < 3; i++) {
|
|
45
|
-
const c = currentParts[i] || 0;
|
|
46
|
-
const l = latestParts[i] || 0;
|
|
47
|
-
if (l > c) return true;
|
|
48
|
-
if (l < c) return false;
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Fetch latest version from npm registry
|
|
55
|
-
* @returns {Promise<string|null>} Latest version or null on failure
|
|
56
|
-
*/
|
|
57
|
-
function fetchLatestVersion() {
|
|
58
|
-
return new Promise((resolve) => {
|
|
59
|
-
const req = https.get(REGISTRY_URL, { timeout: FETCH_TIMEOUT_MS }, (res) => {
|
|
60
|
-
if (res.statusCode !== 200) {
|
|
61
|
-
resolve(null);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let data = '';
|
|
66
|
-
res.on('data', (chunk) => {
|
|
67
|
-
data += chunk;
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
res.on('end', () => {
|
|
71
|
-
try {
|
|
72
|
-
const json = JSON.parse(data);
|
|
73
|
-
resolve(json.version || null);
|
|
74
|
-
} catch {
|
|
75
|
-
resolve(null);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
req.on('error', () => {
|
|
81
|
-
resolve(null);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
req.on('timeout', () => {
|
|
85
|
-
req.destroy();
|
|
86
|
-
resolve(null);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
// Additional safety timeout
|
|
90
|
-
setTimeout(() => {
|
|
91
|
-
req.destroy();
|
|
92
|
-
resolve(null);
|
|
93
|
-
}, FETCH_TIMEOUT_MS + 1000);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Prompt user for update confirmation
|
|
99
|
-
* @param {string} currentVersion
|
|
100
|
-
* @param {string} latestVersion
|
|
101
|
-
* @returns {Promise<boolean>} True if user wants to update
|
|
102
|
-
*/
|
|
103
|
-
function promptForUpdate(currentVersion, latestVersion) {
|
|
104
|
-
return new Promise((resolve) => {
|
|
105
|
-
const rl = readline.createInterface({
|
|
106
|
-
input: process.stdin,
|
|
107
|
-
output: process.stdout,
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
console.log(`\n📦 Update available: ${currentVersion} → ${latestVersion}`);
|
|
111
|
-
rl.question(' Install now? [y/N] ', (answer) => {
|
|
112
|
-
rl.close();
|
|
113
|
-
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Check if we have write permission to npm global directory
|
|
120
|
-
* @returns {boolean} True if we can write to npm global prefix
|
|
121
|
-
*/
|
|
122
|
-
function canWriteToNpmGlobal() {
|
|
123
|
-
const { execSync } = require('child_process');
|
|
124
|
-
const fs = require('fs');
|
|
125
|
-
|
|
126
|
-
try {
|
|
127
|
-
// Get npm global prefix (e.g., /usr/lib or /home/user/.nvm/versions/node/...)
|
|
128
|
-
const prefix = execSync('npm config get prefix', { encoding: 'utf8' }).trim();
|
|
129
|
-
const globalModulesDir = require('path').join(prefix, 'lib', 'node_modules');
|
|
130
|
-
|
|
131
|
-
// Check if directory exists and is writable
|
|
132
|
-
fs.accessSync(globalModulesDir, fs.constants.W_OK);
|
|
133
|
-
return true;
|
|
134
|
-
} catch {
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Run npm install to update the package
|
|
141
|
-
* @returns {Promise<boolean>} True if update succeeded
|
|
142
|
-
*/
|
|
143
|
-
function runUpdate() {
|
|
144
|
-
return new Promise((resolve) => {
|
|
145
|
-
// Check permissions BEFORE attempting update
|
|
146
|
-
if (!canWriteToNpmGlobal()) {
|
|
147
|
-
console.log('\n⚠️ Cannot auto-update: no write permission to npm global directory.');
|
|
148
|
-
console.log(' Run manually with sudo:');
|
|
149
|
-
console.log(' sudo npm install -g @covibes/zeroshot@latest\n');
|
|
150
|
-
resolve(false);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
console.log('\n📥 Installing update...');
|
|
155
|
-
|
|
156
|
-
const proc = spawn('npm', ['install', '-g', '@covibes/zeroshot@latest'], {
|
|
157
|
-
stdio: 'inherit',
|
|
158
|
-
shell: true,
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
proc.on('close', (code) => {
|
|
162
|
-
if (code === 0) {
|
|
163
|
-
console.log('✅ Update installed successfully!');
|
|
164
|
-
console.log(' Restart zeroshot to use the new version.\n');
|
|
165
|
-
resolve(true);
|
|
166
|
-
} else {
|
|
167
|
-
console.log('❌ Update failed. Try manually:');
|
|
168
|
-
console.log(' sudo npm install -g @covibes/zeroshot@latest\n');
|
|
169
|
-
resolve(false);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
proc.on('error', () => {
|
|
174
|
-
console.log('❌ Update failed. Try manually:');
|
|
175
|
-
console.log(' sudo npm install -g @covibes/zeroshot@latest\n');
|
|
176
|
-
resolve(false);
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Check if update check should run
|
|
183
|
-
* @param {object} settings - Current settings
|
|
184
|
-
* @returns {boolean}
|
|
185
|
-
*/
|
|
186
|
-
function shouldCheckForUpdates(settings) {
|
|
187
|
-
// Disabled by user
|
|
188
|
-
if (!settings.autoCheckUpdates) {
|
|
189
|
-
return false;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Never checked before
|
|
193
|
-
if (!settings.lastUpdateCheckAt) {
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Check if 24 hours have passed
|
|
198
|
-
const elapsed = Date.now() - settings.lastUpdateCheckAt;
|
|
199
|
-
return elapsed >= CHECK_INTERVAL_MS;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Main entry point - check for updates
|
|
204
|
-
* @param {object} options
|
|
205
|
-
* @param {boolean} options.quiet - Skip interactive prompts
|
|
206
|
-
* @returns {Promise<void>}
|
|
207
|
-
*/
|
|
208
|
-
async function checkForUpdates(options = {}) {
|
|
209
|
-
const settings = loadSettings();
|
|
210
|
-
|
|
211
|
-
// Skip check if not due
|
|
212
|
-
if (!shouldCheckForUpdates(settings)) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const currentVersion = getCurrentVersion();
|
|
217
|
-
const latestVersion = await fetchLatestVersion();
|
|
218
|
-
|
|
219
|
-
// Update last check timestamp regardless of result
|
|
220
|
-
settings.lastUpdateCheckAt = Date.now();
|
|
221
|
-
saveSettings(settings);
|
|
222
|
-
|
|
223
|
-
// Network failure - silently skip
|
|
224
|
-
if (!latestVersion) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// No update available
|
|
229
|
-
if (!isNewerVersion(currentVersion, latestVersion)) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Already notified about this version
|
|
234
|
-
if (settings.lastSeenVersion === latestVersion) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Update lastSeenVersion so we don't nag about the same version
|
|
239
|
-
settings.lastSeenVersion = latestVersion;
|
|
240
|
-
saveSettings(settings);
|
|
241
|
-
|
|
242
|
-
// Check write permissions upfront
|
|
243
|
-
const hasWriteAccess = canWriteToNpmGlobal();
|
|
244
|
-
|
|
245
|
-
// Quiet mode - just inform, no prompt
|
|
246
|
-
if (options.quiet) {
|
|
247
|
-
console.log(`📦 Update available: ${currentVersion} → ${latestVersion}`);
|
|
248
|
-
if (hasWriteAccess) {
|
|
249
|
-
console.log(' Run: npm install -g @covibes/zeroshot@latest\n');
|
|
250
|
-
} else {
|
|
251
|
-
console.log(' Run: sudo npm install -g @covibes/zeroshot@latest\n');
|
|
252
|
-
}
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// No write permission - inform user but don't offer interactive prompt
|
|
257
|
-
// (they'd say yes then get an error, which is frustrating UX)
|
|
258
|
-
if (!hasWriteAccess) {
|
|
259
|
-
console.log(`\n📦 Update available: ${currentVersion} → ${latestVersion}`);
|
|
260
|
-
console.log(' Run: sudo npm install -g @covibes/zeroshot@latest\n');
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Interactive mode - prompt for update (only if we have write access)
|
|
265
|
-
const wantsUpdate = await promptForUpdate(currentVersion, latestVersion);
|
|
266
|
-
if (wantsUpdate) {
|
|
267
|
-
await runUpdate();
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
module.exports = {
|
|
272
|
-
checkForUpdates,
|
|
273
|
-
// Exported for testing and CLI update command
|
|
274
|
-
getCurrentVersion,
|
|
275
|
-
isNewerVersion,
|
|
276
|
-
fetchLatestVersion,
|
|
277
|
-
runUpdate,
|
|
278
|
-
shouldCheckForUpdates,
|
|
279
|
-
canWriteToNpmGlobal,
|
|
280
|
-
CHECK_INTERVAL_MS,
|
|
281
|
-
};
|