@the-open-engine/zeroshot 6.1.0 → 6.3.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/README.md +141 -474
- package/cli/commands/cmdproof.js +180 -6
- package/cli/event-copy.js +12 -0
- package/cli/index.js +311 -96
- package/cli/message-formatters-normal.js +14 -2
- package/cli/message-formatters-watch.js +9 -2
- package/cluster-hooks/block-ask-user-question.py +53 -0
- package/cluster-hooks/block-dangerous-git.py +145 -0
- package/lib/clusters-registry.js +48 -0
- package/lib/compose-utils.js +101 -0
- package/lib/detached-startup.js +9 -0
- package/lib/id-detector.js +8 -9
- package/lib/path-check.js +63 -0
- package/lib/run-mode.js +22 -0
- package/lib/start-cluster.js +39 -23
- package/package.json +3 -4
- package/scripts/check-path.js +19 -0
- package/scripts/validate-templates.js +11 -29
- package/src/agent/agent-hook-executor.js +1 -1
- package/src/agent/agent-task-executor.js +4 -3
- package/src/agent/pr-verification.js +27 -1
- package/src/agents/git-pusher-template.js +121 -4
- package/src/attach/socket-discovery.js +2 -3
- package/src/claude-credentials.js +75 -0
- package/src/claude-task-runner.js +1 -0
- package/src/isolation-manager.js +12 -9
- package/src/issue-providers/README.md +45 -15
- package/src/issue-providers/index.js +2 -0
- package/src/issue-providers/jira-provider.js +8 -1
- package/src/issue-providers/linear-provider.js +405 -0
- package/src/ledger.js +42 -3
- package/src/lib/gc.js +2 -3
- package/src/message-bus.js +10 -0
- package/src/orchestrator.js +264 -144
- package/src/preflight.js +12 -16
- package/src/providers/base-provider.js +7 -6
- package/src/status-footer.js +13 -1
- package/src/template-validation/index.js +3 -0
- package/src/template-validation/report-formatter.js +55 -0
- package/src/worktree-claude-config.js +2 -13
- package/task-lib/runner.js +1 -0
- package/task-lib/watcher.js +1 -0
package/cli/commands/cmdproof.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const os = require('os');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const crypto = require('crypto');
|
|
4
5
|
const { spawnSync } = require('child_process');
|
|
5
6
|
|
|
6
7
|
const {
|
|
@@ -153,6 +154,7 @@ function resolvePaths(env) {
|
|
|
153
154
|
return {
|
|
154
155
|
cacheDir,
|
|
155
156
|
keyDir,
|
|
157
|
+
lockDir: env.ZEROSHOT_CMDPROOF_LOCK_DIR || path.join(root, 'locks'),
|
|
156
158
|
privateKeyPath: path.join(keyDir, 'private-key.json'),
|
|
157
159
|
publicKeyPath: path.join(keyDir, 'public-key.json'),
|
|
158
160
|
};
|
|
@@ -180,6 +182,114 @@ function spawnCmdproof(args, options) {
|
|
|
180
182
|
return result;
|
|
181
183
|
}
|
|
182
184
|
|
|
185
|
+
function parsePositiveInt(value, fallback) {
|
|
186
|
+
const parsed = Number.parseInt(String(value || ''), 10);
|
|
187
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function sleepMs(ms) {
|
|
191
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function proofLockName(proof, verifyReport = null) {
|
|
195
|
+
const actionKey = getVerifyActionKey(verifyReport);
|
|
196
|
+
const digest = crypto
|
|
197
|
+
.createHash('sha256')
|
|
198
|
+
.update(`${proof.id}\0${proof.profile}\0${proof.command}\0${actionKey}`)
|
|
199
|
+
.digest('hex')
|
|
200
|
+
.slice(0, 16);
|
|
201
|
+
const safeId = proof.id.replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
202
|
+
return `${safeId}-${digest}.lock`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function getVerifyActionKey(verifyReport) {
|
|
206
|
+
return verifyReport?.actionKey || verifyReport?.action_key || '';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function proofLockPath(proof, paths, verifyReport = null) {
|
|
210
|
+
return path.join(paths.lockDir, proofLockName(proof, verifyReport));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function removeLock(lockPath) {
|
|
214
|
+
fs.rmSync(lockPath, { recursive: true, force: true });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function lockOwnerState(lockPath) {
|
|
218
|
+
try {
|
|
219
|
+
const metadata = JSON.parse(fs.readFileSync(path.join(lockPath, 'owner.json'), 'utf8'));
|
|
220
|
+
if (!Number.isInteger(metadata.pid) || metadata.pid <= 0) {
|
|
221
|
+
return 'unknown';
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
process.kill(metadata.pid, 0);
|
|
225
|
+
return 'alive';
|
|
226
|
+
} catch (error) {
|
|
227
|
+
return error?.code === 'ESRCH' ? 'gone' : 'unknown';
|
|
228
|
+
}
|
|
229
|
+
} catch {
|
|
230
|
+
return 'unknown';
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function lockIsStale(lockPath, staleMs) {
|
|
235
|
+
try {
|
|
236
|
+
const stat = fs.statSync(lockPath);
|
|
237
|
+
const ownerState = lockOwnerState(lockPath);
|
|
238
|
+
if (ownerState === 'alive') {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
if (ownerState === 'gone') {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return Date.now() - stat.mtimeMs > staleMs;
|
|
245
|
+
} catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function writeLockMetadata(lockPath, proof) {
|
|
251
|
+
const metadata = {
|
|
252
|
+
pid: process.pid,
|
|
253
|
+
proofId: proof.id,
|
|
254
|
+
profile: proof.profile,
|
|
255
|
+
command: proof.command,
|
|
256
|
+
createdAt: new Date().toISOString(),
|
|
257
|
+
};
|
|
258
|
+
fs.writeFileSync(path.join(lockPath, 'owner.json'), `${JSON.stringify(metadata, null, 2)}\n`, {
|
|
259
|
+
mode: 0o600,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function tryAcquireProofLock(proof, paths, env, verifyReport = null) {
|
|
264
|
+
const lockPath = proofLockPath(proof, paths, verifyReport);
|
|
265
|
+
const staleMs = parsePositiveInt(env.ZEROSHOT_CMDPROOF_LOCK_STALE_MS, 6 * 60 * 60 * 1000);
|
|
266
|
+
fs.mkdirSync(paths.lockDir, { recursive: true, mode: 0o700 });
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
fs.mkdirSync(lockPath, { mode: 0o700 });
|
|
270
|
+
writeLockMetadata(lockPath, proof);
|
|
271
|
+
return { acquired: true, lockPath };
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (error?.code !== 'EEXIST') {
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
if (!lockIsStale(lockPath, staleMs)) {
|
|
277
|
+
return { acquired: false, lockPath };
|
|
278
|
+
}
|
|
279
|
+
removeLock(lockPath);
|
|
280
|
+
try {
|
|
281
|
+
fs.mkdirSync(lockPath, { mode: 0o700 });
|
|
282
|
+
writeLockMetadata(lockPath, proof);
|
|
283
|
+
return { acquired: true, lockPath };
|
|
284
|
+
} catch (retryError) {
|
|
285
|
+
if (retryError?.code === 'EEXIST') {
|
|
286
|
+
return { acquired: false, lockPath };
|
|
287
|
+
}
|
|
288
|
+
throw retryError;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
183
293
|
function ensureKeypair(paths, options) {
|
|
184
294
|
fs.mkdirSync(paths.cacheDir, { recursive: true, mode: 0o700 });
|
|
185
295
|
fs.mkdirSync(paths.keyDir, { recursive: true, mode: 0o700 });
|
|
@@ -231,6 +341,74 @@ function runMode(mode, proof, paths, options) {
|
|
|
231
341
|
return statusOf(result);
|
|
232
342
|
}
|
|
233
343
|
|
|
344
|
+
function runProveWithLock(proof, paths, options, lockPath) {
|
|
345
|
+
try {
|
|
346
|
+
return runMode('prove', proof, paths, options);
|
|
347
|
+
} finally {
|
|
348
|
+
removeLock(lockPath);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function verifyProof(proof, paths, options, { write = true } = {}) {
|
|
353
|
+
const result = spawnCmdproof(buildCmdproofArgs('verify', proof, paths), options);
|
|
354
|
+
if (write) {
|
|
355
|
+
writeResult(result, options.stdout, options.stderr);
|
|
356
|
+
}
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function waitForProofOrProve(proof, paths, options, verifyReport) {
|
|
361
|
+
const startedAt = Date.now();
|
|
362
|
+
const waitMs = parsePositiveInt(options.env.ZEROSHOT_CMDPROOF_WAIT_MS, 30 * 60 * 1000);
|
|
363
|
+
const pollMs = parsePositiveInt(options.env.ZEROSHOT_CMDPROOF_POLL_MS, 2500);
|
|
364
|
+
|
|
365
|
+
while (Date.now() - startedAt < waitMs) {
|
|
366
|
+
sleepMs(pollMs);
|
|
367
|
+
const verifyResult = verifyProof(proof, paths, options, { write: false });
|
|
368
|
+
const latestReport = parseJsonObject(verifyResult.stdout) || verifyReport;
|
|
369
|
+
if (!shouldFallbackFromVerify(verifyResult)) {
|
|
370
|
+
writeResult(verifyResult, options.stdout, options.stderr);
|
|
371
|
+
return statusOf(verifyResult);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const lockReport = getVerifyActionKey(latestReport) ? latestReport : verifyReport;
|
|
375
|
+
const lock = tryAcquireProofLock(proof, paths, options.env, lockReport);
|
|
376
|
+
if (lock.acquired) {
|
|
377
|
+
options.stderr.write(
|
|
378
|
+
`zeroshot cmdproof check ${proof.id}: no reusable proof appeared; acquired proof lock after wait.\n`
|
|
379
|
+
);
|
|
380
|
+
return runProveWithLock(proof, paths, options, lock.lockPath);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
options.stderr.write(
|
|
385
|
+
`zeroshot cmdproof check ${proof.id}: timed out waiting ${waitMs}ms for in-flight proof; leaving miss for caller.\n`
|
|
386
|
+
);
|
|
387
|
+
const finalVerify = verifyProof(proof, paths, options, { write: true });
|
|
388
|
+
return statusOf(finalVerify);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function runCheck(proof, paths, options) {
|
|
392
|
+
const verifyResult = verifyProof(proof, paths, options);
|
|
393
|
+
if (!shouldFallbackFromVerify(verifyResult)) {
|
|
394
|
+
return statusOf(verifyResult);
|
|
395
|
+
}
|
|
396
|
+
const verifyReport = parseJsonObject(verifyResult.stdout);
|
|
397
|
+
if (!getVerifyActionKey(verifyReport)) {
|
|
398
|
+
return runMode('prove', proof, paths, options);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const lock = tryAcquireProofLock(proof, paths, options.env, verifyReport);
|
|
402
|
+
if (lock.acquired) {
|
|
403
|
+
return runProveWithLock(proof, paths, options, lock.lockPath);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
options.stderr.write(
|
|
407
|
+
`zeroshot cmdproof check ${proof.id}: proof miss; waiting for in-flight proof from another agent.\n`
|
|
408
|
+
);
|
|
409
|
+
return waitForProofOrProve(proof, paths, options, verifyReport);
|
|
410
|
+
}
|
|
411
|
+
|
|
234
412
|
function runCmdproof({
|
|
235
413
|
mode,
|
|
236
414
|
id,
|
|
@@ -250,12 +428,7 @@ function runCmdproof({
|
|
|
250
428
|
ensureKeypair(paths, options);
|
|
251
429
|
|
|
252
430
|
if (mode === 'check') {
|
|
253
|
-
|
|
254
|
-
writeResult(verifyResult, stdout, stderr);
|
|
255
|
-
if (!shouldFallbackFromVerify(verifyResult)) {
|
|
256
|
-
return statusOf(verifyResult);
|
|
257
|
-
}
|
|
258
|
-
return runMode('prove', proof, paths, options);
|
|
431
|
+
return runCheck(proof, paths, options);
|
|
259
432
|
}
|
|
260
433
|
|
|
261
434
|
return runMode(mode, proof, paths, options);
|
|
@@ -265,4 +438,5 @@ module.exports = {
|
|
|
265
438
|
buildCmdproofArgs,
|
|
266
439
|
parseCommandToArgv,
|
|
267
440
|
runCmdproof,
|
|
441
|
+
proofLockName,
|
|
268
442
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const EVENT_COPY = {
|
|
2
|
+
IMPLEMENTATION_READY: 'Implementation ready',
|
|
3
|
+
PR_CREATED: 'Pull request created',
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
function formatMergeStatus(merged) {
|
|
7
|
+
if (merged === true || merged === 'true') return 'merged';
|
|
8
|
+
if (merged === false || merged === 'false') return 'auto-merge pending approval';
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { EVENT_COPY, formatMergeStatus };
|