great-cto 3.75.9 → 3.75.10
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "great-cto",
|
|
3
3
|
"description": "You already have the agent. This is everything around it. great_cto runs Claude Code as a pipeline of 72 specialist agents \u2014 an independent model checks each stage before the next builds on it, spending caps refuse rather than warn, and three decisions stay yours: what gets built, how, and whether it ships.",
|
|
4
|
-
"version": "3.75.
|
|
4
|
+
"version": "3.75.10",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Alexander Velikiy",
|
|
7
7
|
"url": "https://hashnode.com/@Greatcto"
|
|
@@ -200,6 +200,29 @@ function expectedCandidate(files) {
|
|
|
200
200
|
const inventory = files.map(file => ({ path: file.path, size: file.bytes.length, sha256: hash(file.bytes), mode: file.mode })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
201
201
|
return hash(JSON.stringify(inventory));
|
|
202
202
|
}
|
|
203
|
+
/** What npm wrote against what was admitted: names the first difference, keeps no bytes. */
|
|
204
|
+
function candidateDifference(files, packageRoot) {
|
|
205
|
+
const actual = new Map(packageSnapshot(packageRoot).files.map(file => [file.path, file]));
|
|
206
|
+
const differing = files.filter(file => { const found = actual.get(file.path); return !found || found.sha256 !== hash(file.bytes) || found.mode !== file.mode; });
|
|
207
|
+
const first = differing[0], found = first && actual.get(first.path);
|
|
208
|
+
return { differing: differing.length, unexpected: [...actual.keys()].filter(name => !files.some(file => file.path === name)).length, ...(first ? { path: first.path, expectedMode: first.mode.toString(8), actualMode: found ? found.mode.toString(8) : null, sameBytes: !!found && found.sha256 === hash(first.bytes) } : {}) };
|
|
209
|
+
}
|
|
210
|
+
/** The result of a failed step, bounded: the journal explains the rollback and stays small. */
|
|
211
|
+
function stepFailure(step, result) {
|
|
212
|
+
return { step, status: result?.status ?? null, signal: result?.signal ?? null, error: result?.error?.code || result?.error?.message || null,
|
|
213
|
+
stdout: String(result?.stdout || '').slice(-500), stderr: String(result?.stderr || '').slice(-2000) };
|
|
214
|
+
}
|
|
215
|
+
/** npm applies the caller's umask to the files it unpacks, and the admitted inventory
|
|
216
|
+
* includes modes. A private umask must not turn identical bytes into a failed activation. */
|
|
217
|
+
function withPackageUmask(action) {
|
|
218
|
+
const previous = process.umask(0o022);
|
|
219
|
+
try {
|
|
220
|
+
return action();
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
process.umask(previous);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
203
226
|
function privateInstallationState(stateRoot, packageRoot, { recovering = false } = {}) {
|
|
204
227
|
const root = canonicalDirectory(stateRoot, { create: true, privateMode: true });
|
|
205
228
|
const installationId = hash(packageRoot), dir = path.join(root, installationId);
|
|
@@ -303,9 +326,13 @@ function validJournal(journal, installation, state) {
|
|
|
303
326
|
fail('invalid-recovery-selection');
|
|
304
327
|
return journal;
|
|
305
328
|
}
|
|
306
|
-
function rollback(installation, state, journal, context) {
|
|
329
|
+
function rollback(installation, state, journal, context, reason = null, detail = null) {
|
|
307
330
|
try {
|
|
308
331
|
validJournal(journal, installation, state);
|
|
332
|
+
if (reason) {
|
|
333
|
+
journal.failure = reason;
|
|
334
|
+
journal.failureDetail = detail;
|
|
335
|
+
}
|
|
309
336
|
const backup = path.join(state.dir, 'transactions', journal.transactionId, 'previous/package');
|
|
310
337
|
const snapshot = packageSnapshot(backup);
|
|
311
338
|
if (snapshot.digest !== journal.previous.packageDigest)
|
|
@@ -314,7 +341,7 @@ function rollback(installation, state, journal, context) {
|
|
|
314
341
|
if (stateMatches(current, journal.previous)) {
|
|
315
342
|
journal.state = 'ROLLED_BACK';
|
|
316
343
|
writeRecord(state.journalPath, journal);
|
|
317
|
-
return outcome('ROLLED_BACK', context);
|
|
344
|
+
return outcome('ROLLED_BACK', context, reason || journal.failure);
|
|
318
345
|
}
|
|
319
346
|
if (!journal.written || !stateMatches(current, journal.written))
|
|
320
347
|
fail('recovery-target-drift-or-unobserved-write');
|
|
@@ -358,7 +385,7 @@ function rollback(installation, state, journal, context) {
|
|
|
358
385
|
journal.state = 'ROLLED_BACK';
|
|
359
386
|
journal.recovery = 'RESTORED_PREEXISTING_OPERATOR_STATE';
|
|
360
387
|
writeRecord(state.journalPath, journal);
|
|
361
|
-
return outcome('ROLLED_BACK', context);
|
|
388
|
+
return outcome('ROLLED_BACK', context, reason || journal.failure);
|
|
362
389
|
}
|
|
363
390
|
catch (error) {
|
|
364
391
|
journal.state = 'RECOVERY_REQUIRED';
|
|
@@ -467,7 +494,7 @@ export function activateNpmRelease(admission, options) {
|
|
|
467
494
|
if (finalArtifact.sha256 !== artifact.sha256 || hash(readRegular(activationArchive, { max: 128 * 1024 * 1024 })) !== artifact.sha256)
|
|
468
495
|
fail('artifact-changed-at-activation');
|
|
469
496
|
const args = ['install', '--global', activationArchive, '--ignore-scripts', '--offline', '--prefix', installation.prefix, '--package-lock=false', '--no-save', '--audit=false', '--fund=false'];
|
|
470
|
-
const result = run(npmPath, args, { cwd: tx, env, encoding: 'utf8', timeout: 180000, maxBuffer: 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
497
|
+
const result = withPackageUmask(() => run(npmPath, args, { cwd: tx, env, encoding: 'utf8', timeout: 180000, maxBuffer: 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] }));
|
|
471
498
|
try {
|
|
472
499
|
journal.written = targetState(installation);
|
|
473
500
|
}
|
|
@@ -476,15 +503,20 @@ export function activateNpmRelease(admission, options) {
|
|
|
476
503
|
}
|
|
477
504
|
phase('ACTIVE');
|
|
478
505
|
if (result?.error || result?.signal || result?.status !== 0)
|
|
479
|
-
return rollback(installation, state, journal, context);
|
|
480
|
-
if (!journal.written
|
|
481
|
-
return rollback(installation, state, journal, context);
|
|
506
|
+
return rollback(installation, state, journal, context, 'npm-install-failed', stepFailure('npm-install', result));
|
|
507
|
+
if (!journal.written)
|
|
508
|
+
return rollback(installation, state, journal, context, 'installed-package-unreadable');
|
|
509
|
+
if (journal.written.filesDigest !== expectedCandidate(payload.files))
|
|
510
|
+
return rollback(installation, state, journal, context, 'installed-files-differ-from-admitted', candidateDifference(payload.files, installation.packageRoot));
|
|
482
511
|
if (!stateMatches(targetState(installation), journal.written))
|
|
483
512
|
return outcome('RECOVERY_REQUIRED', context, 'target-drift-after-activation');
|
|
484
513
|
for (const smokeArgs of [[installation.binaryPath, '--version'], [installation.binaryPath, 'codex-host', 'list']]) {
|
|
485
514
|
const smoke = run(process.execPath, smokeArgs, { cwd: tx, env, encoding: 'utf8', timeout: 30000, maxBuffer: 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
486
|
-
|
|
487
|
-
|
|
515
|
+
const step = 'smoke-' + (smokeArgs.includes('--version') ? 'version' : 'codex-host-list');
|
|
516
|
+
if (smoke?.error || smoke?.signal || smoke?.status !== 0)
|
|
517
|
+
return rollback(installation, state, journal, context, step + '-failed', stepFailure(step, smoke));
|
|
518
|
+
if (smokeArgs.includes('--version') && String(smoke.stdout || '').trim() !== info.version)
|
|
519
|
+
return rollback(installation, state, journal, context, 'smoke-version-mismatch', stepFailure(step, smoke));
|
|
488
520
|
if (!stateMatches(targetState(installation), journal.written))
|
|
489
521
|
return outcome('RECOVERY_REQUIRED', context, 'target-drift-during-smoke');
|
|
490
522
|
}
|
|
@@ -496,7 +528,7 @@ export function activateNpmRelease(admission, options) {
|
|
|
496
528
|
if (journal && error.activationCrash)
|
|
497
529
|
return outcome('RECOVERY_REQUIRED', context, 'interrupted-activation');
|
|
498
530
|
if (journal && ['ACTIVATING', 'ACTIVE', 'RECOVERY_REQUIRED'].includes(journal.state)) {
|
|
499
|
-
return rollback(installation, state, journal, context);
|
|
531
|
+
return rollback(installation, state, journal, context, error.activationReason || 'activation-check-failed');
|
|
500
532
|
}
|
|
501
533
|
if (journal) {
|
|
502
534
|
journal.state = 'REFUSED';
|
package/package.json
CHANGED