@the-open-engine/zeroshot 6.0.0 → 6.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/cli/index.js +8 -6
- package/cli/lib/update-checker.js +205 -24
- package/package.json +1 -1
- package/src/config-validator.js +39 -3
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -503,6 +503,6 @@ The TUI is not included in this release. Use `zeroshot logs -f`, `zeroshot logs
|
|
|
503
503
|
|
|
504
504
|
---
|
|
505
505
|
|
|
506
|
-
MIT - [
|
|
506
|
+
MIT - [The Open Engine Company](https://github.com/the-open-engine)
|
|
507
507
|
|
|
508
508
|
Built on [Claude Code](https://claude.com/product/claude-code) by Anthropic.
|
package/cli/index.js
CHANGED
|
@@ -67,7 +67,7 @@ const {
|
|
|
67
67
|
registerDetachedSetupCluster,
|
|
68
68
|
} = require('../lib/detached-startup');
|
|
69
69
|
// Setup wizard removed - use: zeroshot settings set <key> <value>
|
|
70
|
-
const { checkForUpdates } = require('./lib/update-checker');
|
|
70
|
+
const { checkForUpdates, printLegacyDistroNotice } = require('./lib/update-checker');
|
|
71
71
|
const { StatusFooter, AGENT_STATE, ACTIVE_STATES } = require('../src/status-footer');
|
|
72
72
|
|
|
73
73
|
// =============================================================================
|
|
@@ -5387,13 +5387,15 @@ function printMessage(msg, showClusterId = false, watchMode = false, isActive =
|
|
|
5387
5387
|
|
|
5388
5388
|
// Main async entry point
|
|
5389
5389
|
async function main() {
|
|
5390
|
-
const
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5390
|
+
const isTest = process.env.NODE_ENV === 'test';
|
|
5391
|
+
const isQuiet = process.argv.includes('-q') || process.argv.includes('--quiet') || isTest;
|
|
5392
|
+
|
|
5393
|
+
printLegacyDistroNotice();
|
|
5394
5394
|
|
|
5395
5395
|
// Check for updates (non-blocking if offline)
|
|
5396
|
-
|
|
5396
|
+
if (!isTest) {
|
|
5397
|
+
await checkForUpdates({ quiet: isQuiet });
|
|
5398
|
+
}
|
|
5397
5399
|
|
|
5398
5400
|
let args = process.argv.slice(2);
|
|
5399
5401
|
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const https = require('https');
|
|
12
|
-
const
|
|
12
|
+
const childProcess = require('child_process');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
13
15
|
const readline = require('readline');
|
|
14
16
|
const { loadSettings, saveSettings } = require('../../lib/settings');
|
|
15
17
|
|
|
18
|
+
const NEW_PACKAGE_NAME = '@the-open-engine/zeroshot';
|
|
19
|
+
const LEGACY_PACKAGE_NAME = '@covibes/zeroshot';
|
|
20
|
+
const NEW_PACKAGE_SPEC = `${NEW_PACKAGE_NAME}@latest`;
|
|
21
|
+
|
|
16
22
|
// 24 hours in milliseconds
|
|
17
23
|
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
18
24
|
|
|
@@ -20,15 +26,177 @@ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
|
20
26
|
const FETCH_TIMEOUT_MS = 5000;
|
|
21
27
|
|
|
22
28
|
// npm registry URL
|
|
23
|
-
const REGISTRY_URL =
|
|
29
|
+
const REGISTRY_URL = `https://registry.npmjs.org/${NEW_PACKAGE_NAME}/latest`;
|
|
30
|
+
|
|
31
|
+
function getPackageMetadata() {
|
|
32
|
+
return require('../../package.json');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getCurrentPackageName() {
|
|
36
|
+
return getPackageMetadata().name || NEW_PACKAGE_NAME;
|
|
37
|
+
}
|
|
24
38
|
|
|
25
39
|
/**
|
|
26
40
|
* Get current package version
|
|
27
41
|
* @returns {string}
|
|
28
42
|
*/
|
|
29
43
|
function getCurrentVersion() {
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
return getPackageMetadata().version;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isLegacyDistro(packageName = getCurrentPackageName()) {
|
|
48
|
+
return packageName === LEGACY_PACKAGE_NAME;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function printLegacyDistroNotice(packageName = getCurrentPackageName()) {
|
|
52
|
+
if (!isLegacyDistro(packageName)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.error(
|
|
57
|
+
`\n⚠️ ${LEGACY_PACKAGE_NAME} has moved to ${NEW_PACKAGE_NAME}. ` +
|
|
58
|
+
'Run `zeroshot update` to switch this installation.\n'
|
|
59
|
+
);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getPackageRoot() {
|
|
64
|
+
return path.dirname(require.resolve('../../package.json'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function hasPathSuffix(parts, suffix) {
|
|
68
|
+
if (suffix.length > parts.length) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const start = parts.length - suffix.length;
|
|
73
|
+
return suffix.every((part, index) => parts[start + index] === part);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function joinPathParts(parts) {
|
|
77
|
+
const joined = parts.join(path.sep);
|
|
78
|
+
return joined === '' ? path.parse(process.cwd()).root : joined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function deriveInstallPrefixFromPackageRoot(packageRoot, packageName) {
|
|
82
|
+
const parts = path.resolve(packageRoot).split(path.sep);
|
|
83
|
+
const packageParts = packageName.split('/');
|
|
84
|
+
|
|
85
|
+
if (!hasPathSuffix(parts, packageParts)) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const nodeModulesIndex = parts.length - packageParts.length - 1;
|
|
90
|
+
if (nodeModulesIndex < 0 || parts[nodeModulesIndex] !== 'node_modules') {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (parts[nodeModulesIndex - 1] === 'lib') {
|
|
95
|
+
return joinPathParts(parts.slice(0, nodeModulesIndex - 1));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return joinPathParts(parts.slice(0, nodeModulesIndex));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveNpmCommand(installPrefix = null) {
|
|
102
|
+
const npmName = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
103
|
+
const candidates = [];
|
|
104
|
+
|
|
105
|
+
if (installPrefix) {
|
|
106
|
+
candidates.push(path.join(installPrefix, 'bin', npmName));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
candidates.push(path.join(path.dirname(process.execPath), npmName));
|
|
110
|
+
|
|
111
|
+
for (const candidate of candidates) {
|
|
112
|
+
if (fs.existsSync(candidate)) {
|
|
113
|
+
return candidate;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return npmName;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function getNpmConfiguredPrefix(npmCommand = resolveNpmCommand()) {
|
|
121
|
+
return childProcess
|
|
122
|
+
.execFileSync(npmCommand, ['config', 'get', 'prefix'], {
|
|
123
|
+
encoding: 'utf8',
|
|
124
|
+
})
|
|
125
|
+
.trim();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getInstallPrefix(options = {}) {
|
|
129
|
+
if (options.installPrefix) {
|
|
130
|
+
return options.installPrefix;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const packageName = options.packageName || getCurrentPackageName();
|
|
134
|
+
const packageRoot = options.packageRoot || getPackageRoot();
|
|
135
|
+
const derivedPrefix = deriveInstallPrefixFromPackageRoot(packageRoot, packageName);
|
|
136
|
+
|
|
137
|
+
if (derivedPrefix) {
|
|
138
|
+
return derivedPrefix;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return getNpmConfiguredPrefix(options.npmCommand);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getGlobalModulesDir(installPrefix) {
|
|
145
|
+
const unixGlobalModulesDir = path.join(installPrefix, 'lib', 'node_modules');
|
|
146
|
+
if (fs.existsSync(unixGlobalModulesDir)) {
|
|
147
|
+
return unixGlobalModulesDir;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return path.join(installPrefix, 'node_modules');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function shellQuote(value) {
|
|
154
|
+
if (/^[A-Za-z0-9_./:@+-]+$/.test(value)) {
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function buildManualInstallCommand(installPrefix = null, useSudo = false) {
|
|
162
|
+
const command = [
|
|
163
|
+
useSudo ? 'sudo' : null,
|
|
164
|
+
'npm',
|
|
165
|
+
'install',
|
|
166
|
+
'-g',
|
|
167
|
+
installPrefix ? '--prefix' : null,
|
|
168
|
+
installPrefix ? shellQuote(installPrefix) : null,
|
|
169
|
+
NEW_PACKAGE_SPEC,
|
|
170
|
+
].filter(Boolean);
|
|
171
|
+
|
|
172
|
+
return command.join(' ');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function getUpdateTarget(options = {}) {
|
|
176
|
+
const packageName = options.packageName || getCurrentPackageName();
|
|
177
|
+
const legacy = isLegacyDistro(packageName);
|
|
178
|
+
const installPrefix = getInstallPrefix(options);
|
|
179
|
+
const npmCommand = options.npmCommand || resolveNpmCommand(installPrefix);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
packageName,
|
|
183
|
+
legacy,
|
|
184
|
+
installPrefix,
|
|
185
|
+
npmCommand,
|
|
186
|
+
globalModulesDir: getGlobalModulesDir(installPrefix),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildInstallArgs(updateTarget) {
|
|
191
|
+
const args = ['install', '-g', '--prefix', updateTarget.installPrefix];
|
|
192
|
+
|
|
193
|
+
if (updateTarget.legacy) {
|
|
194
|
+
// npm refuses to replace the legacy package's `zeroshot` bin without this.
|
|
195
|
+
args.push('--force');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
args.push(NEW_PACKAGE_SPEC);
|
|
199
|
+
return args;
|
|
32
200
|
}
|
|
33
201
|
|
|
34
202
|
/**
|
|
@@ -119,17 +287,10 @@ function promptForUpdate(currentVersion, latestVersion) {
|
|
|
119
287
|
* Check if we have write permission to npm global directory
|
|
120
288
|
* @returns {boolean} True if we can write to npm global prefix
|
|
121
289
|
*/
|
|
122
|
-
function canWriteToNpmGlobal() {
|
|
123
|
-
const { execSync } = require('child_process');
|
|
124
|
-
const fs = require('fs');
|
|
125
|
-
|
|
290
|
+
function canWriteToNpmGlobal(options = {}) {
|
|
126
291
|
try {
|
|
127
|
-
|
|
128
|
-
|
|
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);
|
|
292
|
+
const updateTarget = getUpdateTarget(options);
|
|
293
|
+
fs.accessSync(updateTarget.globalModulesDir, fs.constants.W_OK);
|
|
133
294
|
return true;
|
|
134
295
|
} catch {
|
|
135
296
|
return false;
|
|
@@ -140,22 +301,32 @@ function canWriteToNpmGlobal() {
|
|
|
140
301
|
* Run npm install to update the package
|
|
141
302
|
* @returns {Promise<boolean>} True if update succeeded
|
|
142
303
|
*/
|
|
143
|
-
function runUpdate() {
|
|
304
|
+
function runUpdate(options = {}) {
|
|
144
305
|
return new Promise((resolve) => {
|
|
306
|
+
let updateTarget;
|
|
307
|
+
try {
|
|
308
|
+
updateTarget = getUpdateTarget(options);
|
|
309
|
+
} catch {
|
|
310
|
+
console.log('❌ Update failed. Try manually:');
|
|
311
|
+
console.log(` ${buildManualInstallCommand()}\n`);
|
|
312
|
+
resolve(false);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
145
316
|
// Check permissions BEFORE attempting update
|
|
146
|
-
if (!canWriteToNpmGlobal()) {
|
|
317
|
+
if (!canWriteToNpmGlobal(options)) {
|
|
147
318
|
console.log('\n⚠️ Cannot auto-update: no write permission to npm global directory.');
|
|
148
319
|
console.log(' Run manually with sudo:');
|
|
149
|
-
console.log(
|
|
320
|
+
console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
|
|
150
321
|
resolve(false);
|
|
151
322
|
return;
|
|
152
323
|
}
|
|
153
324
|
|
|
154
325
|
console.log('\n📥 Installing update...');
|
|
155
326
|
|
|
156
|
-
const proc = spawn(
|
|
327
|
+
const proc = childProcess.spawn(updateTarget.npmCommand, buildInstallArgs(updateTarget), {
|
|
157
328
|
stdio: 'inherit',
|
|
158
|
-
shell:
|
|
329
|
+
shell: false,
|
|
159
330
|
});
|
|
160
331
|
|
|
161
332
|
proc.on('close', (code) => {
|
|
@@ -165,14 +336,14 @@ function runUpdate() {
|
|
|
165
336
|
resolve(true);
|
|
166
337
|
} else {
|
|
167
338
|
console.log('❌ Update failed. Try manually:');
|
|
168
|
-
console.log(
|
|
339
|
+
console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
|
|
169
340
|
resolve(false);
|
|
170
341
|
}
|
|
171
342
|
});
|
|
172
343
|
|
|
173
344
|
proc.on('error', () => {
|
|
174
345
|
console.log('❌ Update failed. Try manually:');
|
|
175
|
-
console.log(
|
|
346
|
+
console.log(` ${buildManualInstallCommand(updateTarget.installPrefix, true)}\n`);
|
|
176
347
|
resolve(false);
|
|
177
348
|
});
|
|
178
349
|
});
|
|
@@ -246,9 +417,9 @@ async function checkForUpdates(options = {}) {
|
|
|
246
417
|
if (options.quiet) {
|
|
247
418
|
console.log(`📦 Update available: ${currentVersion} → ${latestVersion}`);
|
|
248
419
|
if (hasWriteAccess) {
|
|
249
|
-
console.log(
|
|
420
|
+
console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), false)}\n`);
|
|
250
421
|
} else {
|
|
251
|
-
console.log(
|
|
422
|
+
console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), true)}\n`);
|
|
252
423
|
}
|
|
253
424
|
return;
|
|
254
425
|
}
|
|
@@ -257,7 +428,7 @@ async function checkForUpdates(options = {}) {
|
|
|
257
428
|
// (they'd say yes then get an error, which is frustrating UX)
|
|
258
429
|
if (!hasWriteAccess) {
|
|
259
430
|
console.log(`\n📦 Update available: ${currentVersion} → ${latestVersion}`);
|
|
260
|
-
console.log(
|
|
431
|
+
console.log(` Run: ${buildManualInstallCommand(getInstallPrefix(), true)}\n`);
|
|
261
432
|
return;
|
|
262
433
|
}
|
|
263
434
|
|
|
@@ -271,7 +442,17 @@ async function checkForUpdates(options = {}) {
|
|
|
271
442
|
module.exports = {
|
|
272
443
|
checkForUpdates,
|
|
273
444
|
// Exported for testing and CLI update command
|
|
445
|
+
NEW_PACKAGE_NAME,
|
|
446
|
+
LEGACY_PACKAGE_NAME,
|
|
274
447
|
getCurrentVersion,
|
|
448
|
+
getCurrentPackageName,
|
|
449
|
+
isLegacyDistro,
|
|
450
|
+
printLegacyDistroNotice,
|
|
451
|
+
deriveInstallPrefixFromPackageRoot,
|
|
452
|
+
getInstallPrefix,
|
|
453
|
+
resolveNpmCommand,
|
|
454
|
+
getUpdateTarget,
|
|
455
|
+
buildInstallArgs,
|
|
275
456
|
isNewerVersion,
|
|
276
457
|
fetchLatestVersion,
|
|
277
458
|
runUpdate,
|
package/package.json
CHANGED
package/src/config-validator.js
CHANGED
|
@@ -17,6 +17,13 @@ const { getProvider } = require('./providers');
|
|
|
17
17
|
const { CAPABILITIES } = require('./providers/capabilities');
|
|
18
18
|
const { GUIDANCE_TOPICS } = require('./guidance-topics');
|
|
19
19
|
|
|
20
|
+
const HOOK_ACTION_TOPIC_CONTRACTS = Object.freeze({
|
|
21
|
+
verify_pull_request: Object.freeze([
|
|
22
|
+
{ topic: 'CLUSTER_COMPLETE', keys: null },
|
|
23
|
+
{ topic: 'PUSH_BLOCKED', keys: null },
|
|
24
|
+
]),
|
|
25
|
+
});
|
|
26
|
+
|
|
20
27
|
/**
|
|
21
28
|
* Check if config is a conductor-bootstrap style config
|
|
22
29
|
* Conductor configs dynamically spawn agents via CLUSTER_OPERATIONS
|
|
@@ -288,6 +295,25 @@ function ensureTopicList(map, topic) {
|
|
|
288
295
|
return map.get(topic);
|
|
289
296
|
}
|
|
290
297
|
|
|
298
|
+
function getHookActionTopicContracts(hook) {
|
|
299
|
+
if (!hook?.action) {
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
return HOOK_ACTION_TOPIC_CONTRACTS[hook.action] || [];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function recordOutputTopic(agent, topic, topicProducers, agentOutputTopics, producerId = agent.id) {
|
|
306
|
+
const producers = ensureTopicList(topicProducers, topic);
|
|
307
|
+
if (!producers.includes(producerId)) {
|
|
308
|
+
producers.push(producerId);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const outputs = agentOutputTopics.get(agent.id);
|
|
312
|
+
if (!outputs.includes(topic)) {
|
|
313
|
+
outputs.push(topic);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
291
317
|
function recordAgentTriggers(agent, topicConsumers, agentInputTopics) {
|
|
292
318
|
for (const trigger of agent.triggers || []) {
|
|
293
319
|
const topic = trigger.topic;
|
|
@@ -321,16 +347,19 @@ function extractDynamicTopicsFromScript(script, ctx) {
|
|
|
321
347
|
}
|
|
322
348
|
|
|
323
349
|
function recordAgentOutputs(agent, topicProducers, agentOutputTopics) {
|
|
350
|
+
const hook = agent.hooks?.onComplete;
|
|
324
351
|
const outputTopic = agent.hooks?.onComplete?.config?.topic;
|
|
325
352
|
if (outputTopic) {
|
|
326
|
-
|
|
327
|
-
agentOutputTopics.get(agent.id).push(outputTopic);
|
|
353
|
+
recordOutputTopic(agent, outputTopic, topicProducers, agentOutputTopics);
|
|
328
354
|
}
|
|
329
355
|
|
|
330
356
|
const ctx = { outputTopic, agentId: agent.id, topicProducers, agentOutputTopics };
|
|
331
|
-
const hook = agent.hooks?.onComplete;
|
|
332
357
|
extractDynamicTopicsFromScript(hook?.logic?.script, ctx);
|
|
333
358
|
extractDynamicTopicsFromScript(hook?.transform?.script, ctx);
|
|
359
|
+
|
|
360
|
+
for (const contract of getHookActionTopicContracts(hook)) {
|
|
361
|
+
recordOutputTopic(agent, contract.topic, topicProducers, agentOutputTopics);
|
|
362
|
+
}
|
|
334
363
|
}
|
|
335
364
|
|
|
336
365
|
function buildMessageFlowGraph(config) {
|
|
@@ -683,6 +712,13 @@ function collectTopicContracts(config) {
|
|
|
683
712
|
addContract(topic, { agentId: agent.id, keys: null });
|
|
684
713
|
}
|
|
685
714
|
}
|
|
715
|
+
|
|
716
|
+
for (const contract of getHookActionTopicContracts(hook)) {
|
|
717
|
+
addContract(contract.topic, {
|
|
718
|
+
agentId: agent.id,
|
|
719
|
+
keys: contract.keys instanceof Set ? new Set(contract.keys) : contract.keys,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
686
722
|
}
|
|
687
723
|
|
|
688
724
|
return contracts;
|