@covibes/zeroshot 5.2.1 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +174 -189
- package/README.md +226 -195
- package/cli/commands/providers.js +149 -0
- package/cli/index.js +3145 -2366
- package/cli/lib/first-run.js +40 -3
- package/cli/message-formatters-normal.js +28 -6
- package/cluster-templates/base-templates/debug-workflow.json +24 -78
- package/cluster-templates/base-templates/full-workflow.json +99 -316
- package/cluster-templates/base-templates/single-worker.json +23 -15
- package/cluster-templates/base-templates/worker-validator.json +105 -36
- package/cluster-templates/conductor-bootstrap.json +9 -7
- package/lib/docker-config.js +14 -1
- package/lib/git-remote-utils.js +165 -0
- package/lib/id-detector.js +10 -7
- package/lib/provider-defaults.js +62 -0
- package/lib/provider-detection.js +59 -0
- package/lib/provider-names.js +57 -0
- package/lib/settings/claude-auth.js +78 -0
- package/lib/settings.js +298 -15
- package/lib/stream-json-parser.js +4 -238
- package/package.json +27 -6
- package/scripts/setup-merge-queue.sh +170 -0
- package/scripts/validate-templates.js +100 -0
- package/src/agent/agent-config.js +140 -63
- package/src/agent/agent-context-builder.js +336 -165
- package/src/agent/agent-hook-executor.js +337 -67
- package/src/agent/agent-lifecycle.js +386 -287
- package/src/agent/agent-stuck-detector.js +7 -7
- package/src/agent/agent-task-executor.js +944 -683
- package/src/agent/output-extraction.js +217 -0
- package/src/agent/output-reformatter.js +175 -0
- package/src/agent/schema-utils.js +146 -0
- package/src/agent-wrapper.js +112 -31
- package/src/agents/git-pusher-template.js +285 -0
- package/src/claude-task-runner.js +145 -44
- package/src/config-router.js +13 -13
- package/src/config-validator.js +1049 -563
- package/src/input-helpers.js +65 -0
- package/src/isolation-manager.js +499 -320
- package/src/issue-providers/README.md +305 -0
- package/src/issue-providers/azure-devops-provider.js +273 -0
- package/src/issue-providers/base-provider.js +232 -0
- package/src/issue-providers/github-provider.js +179 -0
- package/src/issue-providers/gitlab-provider.js +241 -0
- package/src/issue-providers/index.js +196 -0
- package/src/issue-providers/jira-provider.js +239 -0
- package/src/ledger.js +50 -11
- package/src/lib/safe-exec.js +88 -0
- package/src/orchestrator.js +1348 -757
- package/src/preflight.js +306 -149
- package/src/process-metrics.js +98 -56
- package/src/providers/anthropic/cli-builder.js +73 -0
- package/src/providers/anthropic/index.js +204 -0
- package/src/providers/anthropic/models.js +23 -0
- package/src/providers/anthropic/output-parser.js +177 -0
- package/src/providers/base-provider.js +251 -0
- package/src/providers/capabilities.js +60 -0
- package/src/providers/google/cli-builder.js +55 -0
- package/src/providers/google/index.js +116 -0
- package/src/providers/google/models.js +24 -0
- package/src/providers/google/output-parser.js +101 -0
- package/src/providers/index.js +91 -0
- package/src/providers/openai/cli-builder.js +133 -0
- package/src/providers/openai/index.js +136 -0
- package/src/providers/openai/models.js +21 -0
- package/src/providers/openai/output-parser.js +143 -0
- package/src/providers/opencode/cli-builder.js +42 -0
- package/src/providers/opencode/index.js +103 -0
- package/src/providers/opencode/models.js +55 -0
- package/src/providers/opencode/output-parser.js +122 -0
- package/src/schemas/sub-cluster.js +68 -39
- package/src/status-footer.js +94 -48
- package/src/sub-cluster-wrapper.js +92 -36
- package/src/task-runner.js +8 -6
- package/src/template-resolver.js +12 -9
- package/src/tui/data-poller.js +123 -99
- package/src/tui/formatters.js +4 -3
- package/src/tui/keybindings.js +259 -318
- package/src/tui/layout.js +20 -3
- package/src/tui/renderer.js +11 -21
- package/task-lib/attachable-watcher.js +150 -111
- package/task-lib/claude-recovery.js +156 -0
- package/task-lib/commands/episodes.js +105 -0
- package/task-lib/commands/list.js +3 -3
- package/task-lib/commands/logs.js +231 -189
- package/task-lib/commands/resume.js +3 -2
- package/task-lib/commands/run.js +12 -3
- package/task-lib/commands/schedules.js +111 -61
- package/task-lib/config.js +0 -2
- package/task-lib/runner.js +128 -50
- package/task-lib/scheduler.js +3 -3
- package/task-lib/store.js +464 -152
- package/task-lib/tui/formatters.js +94 -45
- package/task-lib/tui/renderer.js +13 -3
- package/task-lib/tui.js +73 -32
- package/task-lib/watcher.js +157 -100
- package/src/agents/git-pusher-agent.json +0 -20
- package/src/github.js +0 -103
package/src/template-resolver.js
CHANGED
|
@@ -272,25 +272,28 @@ class TemplateResolver {
|
|
|
272
272
|
* @returns {any}
|
|
273
273
|
*/
|
|
274
274
|
_parseValue(str) {
|
|
275
|
-
|
|
275
|
+
const trimmed = str.trim();
|
|
276
276
|
|
|
277
277
|
// String literals (single or double quotes)
|
|
278
|
-
if (
|
|
279
|
-
|
|
278
|
+
if (
|
|
279
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
280
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
281
|
+
) {
|
|
282
|
+
return trimmed.slice(1, -1);
|
|
280
283
|
}
|
|
281
284
|
|
|
282
285
|
// Boolean literals
|
|
283
|
-
if (
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
+
if (trimmed === 'true') return true;
|
|
287
|
+
if (trimmed === 'false') return false;
|
|
288
|
+
if (trimmed === 'undefined' || trimmed === 'null') return undefined;
|
|
286
289
|
|
|
287
290
|
// Numbers
|
|
288
|
-
if (/^-?\d+(\.\d+)?$/.test(
|
|
289
|
-
return parseFloat(
|
|
291
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
292
|
+
return parseFloat(trimmed);
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
// Return as-is for other cases
|
|
293
|
-
return
|
|
296
|
+
return trimmed;
|
|
294
297
|
}
|
|
295
298
|
|
|
296
299
|
/**
|
package/src/tui/data-poller.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const pidusage = require('pidusage');
|
|
12
12
|
const Ledger = require('../ledger');
|
|
13
|
+
const fs = require('fs');
|
|
13
14
|
const path = require('path');
|
|
14
15
|
const os = require('os');
|
|
15
16
|
|
|
@@ -138,47 +139,11 @@ class DataPoller {
|
|
|
138
139
|
try {
|
|
139
140
|
const clusters = this.orchestrator.listClusters();
|
|
140
141
|
const stats = {};
|
|
142
|
+
const pids = this._collectClusterPids(clusters);
|
|
141
143
|
|
|
142
|
-
// Collect all PIDs from all agents
|
|
143
|
-
const pids = [];
|
|
144
|
-
for (const cluster of clusters) {
|
|
145
|
-
try {
|
|
146
|
-
const status = this.orchestrator.getStatus(cluster.id);
|
|
147
|
-
for (const agent of status.agents || []) {
|
|
148
|
-
if (agent.pid) {
|
|
149
|
-
pids.push(agent.pid);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
} catch {
|
|
153
|
-
// Skip clusters that error
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Get stats for all PIDs
|
|
159
144
|
if (pids.length > 0) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// Convert to map format: pid -> { cpu, memory }
|
|
164
|
-
for (const pid of pids) {
|
|
165
|
-
if (pidStats[pid]) {
|
|
166
|
-
stats[pid] = {
|
|
167
|
-
cpu: pidStats[pid].cpu || 0,
|
|
168
|
-
memory: pidStats[pid].memory || 0,
|
|
169
|
-
};
|
|
170
|
-
} else {
|
|
171
|
-
// Process died - set to zero
|
|
172
|
-
stats[pid] = { cpu: 0, memory: 0 };
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
} catch {
|
|
176
|
-
// pidusage throws if any process is dead
|
|
177
|
-
// Set all to zero and continue
|
|
178
|
-
for (const pid of pids) {
|
|
179
|
-
stats[pid] = { cpu: 0, memory: 0 };
|
|
180
|
-
}
|
|
181
|
-
}
|
|
145
|
+
const pidStats = await this._safePidUsage(pids);
|
|
146
|
+
this._populatePidStats(stats, pids, pidStats);
|
|
182
147
|
}
|
|
183
148
|
|
|
184
149
|
this.onUpdate({
|
|
@@ -198,54 +163,36 @@ class DataPoller {
|
|
|
198
163
|
*/
|
|
199
164
|
_watchForNewClusters() {
|
|
200
165
|
this.watchForNewClustersStopFn = this.orchestrator.watchForNewClusters((cluster) => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// Only load if database file exists
|
|
209
|
-
const fs = require('fs');
|
|
210
|
-
if (!fs.existsSync(dbPath)) {
|
|
211
|
-
return; // Skip non-existent ledgers
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const ledger = new Ledger(dbPath);
|
|
215
|
-
this.ledgers.set(cluster.id, ledger);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Start streaming messages
|
|
219
|
-
this._streamLedgerMessages(cluster.id);
|
|
166
|
+
// Lazy load ledger only when we need to stream messages
|
|
167
|
+
// This avoids loading all ledgers on startup
|
|
168
|
+
const ledger = this._loadLedgerForCluster(cluster, {
|
|
169
|
+
label: 'cluster',
|
|
170
|
+
requireExisting: true,
|
|
171
|
+
});
|
|
220
172
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
type: 'new_cluster',
|
|
224
|
-
cluster,
|
|
225
|
-
});
|
|
226
|
-
} catch (error) {
|
|
227
|
-
console.error(
|
|
228
|
-
`[DataPoller] Failed to load ledger for cluster ${cluster.id}:`,
|
|
229
|
-
error.message
|
|
230
|
-
);
|
|
173
|
+
if (!ledger) {
|
|
174
|
+
return;
|
|
231
175
|
}
|
|
176
|
+
|
|
177
|
+
// Start streaming messages
|
|
178
|
+
this._streamLedgerMessages(cluster.id);
|
|
179
|
+
|
|
180
|
+
// Emit update about new cluster
|
|
181
|
+
this.onUpdate({
|
|
182
|
+
type: 'new_cluster',
|
|
183
|
+
cluster,
|
|
184
|
+
});
|
|
232
185
|
}, 2000);
|
|
233
186
|
|
|
234
187
|
// Also load ledgers for all existing clusters
|
|
235
188
|
const existingClusters = this.orchestrator.listClusters();
|
|
236
189
|
for (const cluster of existingClusters) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
const ledger = new Ledger(dbPath);
|
|
241
|
-
this.ledgers.set(cluster.id, ledger);
|
|
242
|
-
this._streamLedgerMessages(cluster.id);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
console.error(
|
|
245
|
-
`[DataPoller] Failed to load ledger for existing cluster ${cluster.id}:`,
|
|
246
|
-
error.message
|
|
247
|
-
);
|
|
190
|
+
const ledger = this._loadLedgerForCluster(cluster, { label: 'existing cluster' });
|
|
191
|
+
if (!ledger) {
|
|
192
|
+
continue;
|
|
248
193
|
}
|
|
194
|
+
|
|
195
|
+
this._streamLedgerMessages(cluster.id);
|
|
249
196
|
}
|
|
250
197
|
}
|
|
251
198
|
|
|
@@ -295,31 +242,108 @@ class DataPoller {
|
|
|
295
242
|
const clusters = this.orchestrator.listClusters();
|
|
296
243
|
|
|
297
244
|
for (const cluster of clusters) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
for (const agent of status.agents || []) {
|
|
302
|
-
if (agent.pid) {
|
|
303
|
-
try {
|
|
304
|
-
const pidStat = await pidusage(agent.pid);
|
|
305
|
-
stats[agent.pid] = {
|
|
306
|
-
cpu: pidStat.cpu || 0,
|
|
307
|
-
memory: pidStat.memory || 0,
|
|
308
|
-
};
|
|
309
|
-
} catch {
|
|
310
|
-
// Process died - set to zero
|
|
311
|
-
stats[agent.pid] = { cpu: 0, memory: 0 };
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
} catch {
|
|
316
|
-
// Skip clusters that error
|
|
317
|
-
continue;
|
|
245
|
+
const pids = this._getClusterAgentPids(cluster);
|
|
246
|
+
for (const pid of pids) {
|
|
247
|
+
stats[pid] = await this._getSinglePidStat(pid);
|
|
318
248
|
}
|
|
319
249
|
}
|
|
320
250
|
|
|
321
251
|
return stats;
|
|
322
252
|
}
|
|
253
|
+
|
|
254
|
+
_collectClusterPids(clusters) {
|
|
255
|
+
const pids = [];
|
|
256
|
+
|
|
257
|
+
for (const cluster of clusters) {
|
|
258
|
+
pids.push(...this._getClusterAgentPids(cluster));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return pids;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_getClusterAgentPids(cluster) {
|
|
265
|
+
try {
|
|
266
|
+
const status = this.orchestrator.getStatus(cluster.id);
|
|
267
|
+
const pids = [];
|
|
268
|
+
|
|
269
|
+
for (const agent of status.agents || []) {
|
|
270
|
+
if (agent.pid) {
|
|
271
|
+
pids.push(agent.pid);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return pids;
|
|
276
|
+
} catch {
|
|
277
|
+
// Skip clusters that error
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async _getSinglePidStat(pid) {
|
|
283
|
+
try {
|
|
284
|
+
const pidStat = await pidusage(pid);
|
|
285
|
+
return {
|
|
286
|
+
cpu: pidStat.cpu || 0,
|
|
287
|
+
memory: pidStat.memory || 0,
|
|
288
|
+
};
|
|
289
|
+
} catch {
|
|
290
|
+
// Process died - set to zero
|
|
291
|
+
return { cpu: 0, memory: 0 };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async _safePidUsage(pids) {
|
|
296
|
+
try {
|
|
297
|
+
return await pidusage(pids);
|
|
298
|
+
} catch {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
_populatePidStats(stats, pids, pidStats) {
|
|
304
|
+
for (const pid of pids) {
|
|
305
|
+
const entry = pidStats?.[pid];
|
|
306
|
+
stats[pid] = {
|
|
307
|
+
cpu: entry?.cpu || 0,
|
|
308
|
+
memory: entry?.memory || 0,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
_loadLedgerForCluster(cluster, options) {
|
|
314
|
+
const { label, requireExisting = false } = options;
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
return this._ensureLedger(cluster.id, { requireExisting });
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error(
|
|
320
|
+
`[DataPoller] Failed to load ledger for ${label} ${cluster.id}:`,
|
|
321
|
+
error.message
|
|
322
|
+
);
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
_ensureLedger(clusterId, { requireExisting = false } = {}) {
|
|
328
|
+
const existing = this.ledgers.get(clusterId);
|
|
329
|
+
if (existing) {
|
|
330
|
+
return existing;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const dbPath = this._getLedgerPath(clusterId);
|
|
334
|
+
if (requireExisting && !fs.existsSync(dbPath)) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const ledger = new Ledger(dbPath);
|
|
339
|
+
this.ledgers.set(clusterId, ledger);
|
|
340
|
+
return ledger;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
_getLedgerPath(clusterId) {
|
|
344
|
+
const storageDir = this.orchestrator.storageDir || path.join(os.homedir(), '.zeroshot');
|
|
345
|
+
return path.join(storageDir, `${clusterId}.db`);
|
|
346
|
+
}
|
|
323
347
|
}
|
|
324
348
|
|
|
325
349
|
module.exports = DataPoller;
|
package/src/tui/formatters.js
CHANGED
|
@@ -64,12 +64,13 @@ const formatCPU = (percent) => {
|
|
|
64
64
|
if (typeof percent !== 'number' || percent < 0) return '0.0%';
|
|
65
65
|
|
|
66
66
|
// Assert normalized range (should never exceed 100% after per-core normalization)
|
|
67
|
-
|
|
67
|
+
let normalizedPercent = percent;
|
|
68
|
+
if (normalizedPercent > 100) {
|
|
68
69
|
console.warn(`[formatCPU] CPU percent ${percent}% exceeds 100% - normalization bug?`);
|
|
69
|
-
|
|
70
|
+
normalizedPercent = 100;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
return `${
|
|
73
|
+
return `${normalizedPercent.toFixed(1)}%`;
|
|
73
74
|
};
|
|
74
75
|
|
|
75
76
|
/**
|