sneakoscope 3.1.12 → 3.1.13
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 +10 -12
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/commands/doctor.js +62 -32
- package/dist/core/agents/agent-role-config.js +12 -1
- package/dist/core/codex/agent-config-file-repair.js +115 -19
- package/dist/core/codex/codex-startup-config-postcheck.js +57 -4
- package/dist/core/codex-control/codex-0140-capability.js +72 -7
- package/dist/core/codex-control/codex-0140-feature-probes.js +174 -16
- package/dist/core/codex-control/codex-0140-real-probes.js +43 -3
- package/dist/core/codex-control/codex-0140-usage-parser.js +81 -0
- package/dist/core/codex-native/native-capability-postcheck.js +5 -2
- package/dist/core/codex-native/native-capability-repair-matrix.js +4 -4
- package/dist/core/config/secret-preservation.js +107 -10
- package/dist/core/doctor/context7-mcp-repair.js +15 -0
- package/dist/core/doctor/doctor-repair-postcheck.js +9 -3
- package/dist/core/doctor/doctor-transaction.js +98 -2
- package/dist/core/doctor/supabase-mcp-repair.js +36 -6
- package/dist/core/fsx.js +1 -1
- package/dist/core/loops/loop-concurrency-budget.js +22 -0
- package/dist/core/mcp/mcp-config-preservation.js +30 -7
- package/dist/core/naruto/naruto-loop-mesh.js +5 -1
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-fake-adapter.js +8 -2
- package/dist/core/zellij/zellij-launcher.js +16 -0
- package/dist/scripts/codex-0140-feature-gate-lib.js +4 -2
- package/dist/scripts/release-3113-required-gates.js +25 -0
- package/package.json +11 -3
- package/dist/scripts/loop-directive-check-lib.js +0 -388
- package/dist/scripts/loop-hardening-check-lib.js +0 -289
- package/dist/scripts/sks-1-12-real-execution-check-lib.js +0 -27
- package/dist/scripts/sks-3-1-4-directive-check-lib.js +0 -212
- package/dist/scripts/sks-3-1-5-directive-check-lib.js +0 -318
- package/dist/scripts/sks-3-1-6-directive-check-lib.js +0 -522
- package/dist/scripts/sks-3-1-7-directive-check-lib.js +0 -58
- package/dist/scripts/sks-3-1-8-check-lib.js +0 -30
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { spawn } from 'node:child_process';
|
|
7
|
-
import { runProcess } from '../core/fsx.js';
|
|
8
|
-
import { decideLoopFixturePolicy } from '../core/loops/loop-fixture-policy.js';
|
|
9
|
-
import { writeLoopFinalArbiterGateContract } from '../core/loops/loop-final-arbiter-contract.js';
|
|
10
|
-
import { runLoopGates } from '../core/loops/loop-gate-runner.js';
|
|
11
|
-
import { runLoopMakerWorkers } from '../core/loops/loop-worker-runtime.js';
|
|
12
|
-
import { runLoopGptFinalArbiter } from '../core/loops/loop-gpt-final-arbiter.js';
|
|
13
|
-
import { mergeLoopWorktrees } from '../core/loops/loop-integration-merge.js';
|
|
14
|
-
import { mergeSingleLoopWorktree } from '../core/loops/loop-merge-strategy.js';
|
|
15
|
-
import { appendLoopMutationEvent, mutationLedgerFromLoopProofs, readLoopMutationLedger } from '../core/loops/loop-mutation-ledger.js';
|
|
16
|
-
import { buildLoopSideEffectReport } from '../core/loops/loop-side-effect-scanner.js';
|
|
17
|
-
import { interruptLoopWorkers, readLoopActiveWorkers, registerLoopActiveWorker } from '../core/loops/loop-interrupt-registry.js';
|
|
18
|
-
import { computeLoopConcurrencyBudget } from '../core/loops/loop-concurrency-budget.js';
|
|
19
|
-
import { defaultLoopBudget } from '../core/loops/loop-schema.js';
|
|
20
|
-
import { root } from './sks-1-18-gate-lib.js';
|
|
21
|
-
export async function runLoopHardeningCheck(id) {
|
|
22
|
-
const assertions = [];
|
|
23
|
-
const assert = (condition, message, detail = {}) => assertions.push({ ok: Boolean(condition), message, detail });
|
|
24
|
-
const temp = await fs.mkdtemp(path.join(os.tmpdir(), `sks-312-${safe(id)}-`));
|
|
25
|
-
await fs.mkdir(path.join(temp, '.sneakoscope', 'missions'), { recursive: true });
|
|
26
|
-
if (id === 'loop:fixture-policy') {
|
|
27
|
-
const allowed = decideLoopFixturePolicy({ root: temp, missionId: 'M-check-fixture-policy', mode: 'gate', requested: true, argv: ['/x/dist/scripts/loop-fixture-policy-check.js'], env: {} });
|
|
28
|
-
const denied = decideLoopFixturePolicy({ root, missionId: 'M-prod-fixture-policy', mode: 'gate', requested: true, argv: ['sks', 'loop', 'run'], env: { SKS_LOOP_GATE_FIXTURE: '1' } });
|
|
29
|
-
assert(allowed.allowed && allowed.reason.includes('release_check_script'), 'fixture policy allows check/blackbox temp runs', allowed);
|
|
30
|
-
assert(!denied.allowed && denied.blockers.includes('loop_gate_fixture_forbidden_in_production'), 'fixture policy blocks production command fixture', denied);
|
|
31
|
-
}
|
|
32
|
-
else if (id === 'loop:gate-fixture-guard') {
|
|
33
|
-
const node = sampleNode('loop-zellij', 'M-prod-gate-fixture');
|
|
34
|
-
const prev = process.env.SKS_LOOP_GATE_FIXTURE;
|
|
35
|
-
const argv = replaceArgv(['sks', 'loop', 'run']);
|
|
36
|
-
await fs.writeFile(path.join(temp, 'package.json'), JSON.stringify({ scripts: { 'release:version-truth': 'node -e "process.exit(0)"' } }, null, 2));
|
|
37
|
-
process.env.SKS_LOOP_GATE_FIXTURE = '1';
|
|
38
|
-
const gates = await runLoopGates({ root: temp, missionId: node.mission_id, node, gates: { triage: [], local: ['release:version-truth'], checker: [], integration: [], final: [] } });
|
|
39
|
-
restoreEnv('SKS_LOOP_GATE_FIXTURE', prev);
|
|
40
|
-
restoreArgv(argv);
|
|
41
|
-
assert(!gates.ok && gates.blockers.includes('loop_gate_fixture_forbidden_in_production'), 'production gate fixture cannot synthetic-pass');
|
|
42
|
-
}
|
|
43
|
-
else if (id === 'loop:worker-fixture-guard') {
|
|
44
|
-
const plan = samplePlan('M-prod-worker-fixture', [sampleNode('loop-zellij', 'M-prod-worker-fixture')]);
|
|
45
|
-
const prev = process.env.SKS_LOOP_RUNTIME_FIXTURE;
|
|
46
|
-
const argv = replaceArgv(['sks', 'loop', 'run']);
|
|
47
|
-
process.env.SKS_LOOP_RUNTIME_FIXTURE = '1';
|
|
48
|
-
try {
|
|
49
|
-
await runLoopMakerWorkers({ root: temp, plan, node: plan.graph.nodes[0], fixture: true });
|
|
50
|
-
assert(false, 'production worker fixture throws');
|
|
51
|
-
}
|
|
52
|
-
catch (err) {
|
|
53
|
-
assert(String(err).includes('loop_fixture_runtime_forbidden'), 'production worker fixture throws forbidden error', { message: String(err) });
|
|
54
|
-
}
|
|
55
|
-
restoreEnv('SKS_LOOP_RUNTIME_FIXTURE', prev);
|
|
56
|
-
restoreArgv(argv);
|
|
57
|
-
}
|
|
58
|
-
else if (id === 'loop:gpt-final-fixture-guard') {
|
|
59
|
-
const plan = samplePlan('M-prod-gpt-fixture', [sampleNode('loop-zellij', 'M-prod-gpt-fixture')]);
|
|
60
|
-
const argv = replaceArgv(['sks', 'loop', 'run']);
|
|
61
|
-
const arbiter = await runLoopGptFinalArbiter({ root: temp, plan, proofs: [sampleProof('loop-zellij', 'M-prod-gpt-fixture', ['src/core/zellij/a.ts'])], integrationMerge: sampleMerge(['src/core/zellij/a.ts']), forceVerdict: 'approve' });
|
|
62
|
-
restoreArgv(argv);
|
|
63
|
-
assert(!arbiter.ok && arbiter.blockers.includes('loop_gpt_final_fixture_forbidden_in_production'), 'production GPT final fixture cannot approve');
|
|
64
|
-
}
|
|
65
|
-
else if (id === 'loop:fixture-production-misuse-blackbox') {
|
|
66
|
-
const gate = decideLoopFixturePolicy({ root, missionId: 'M-normal-production', mode: 'gate', requested: true, env: { SKS_LOOP_GATE_FIXTURE: '1' }, argv: ['sks', 'loop', 'run'] });
|
|
67
|
-
const worker = decideLoopFixturePolicy({ root, missionId: 'M-normal-production', mode: 'worker', requested: true, env: { SKS_LOOP_RUNTIME_FIXTURE: '1' }, argv: ['sks', 'naruto'] });
|
|
68
|
-
const allowed = decideLoopFixturePolicy({ root: temp, missionId: 'M-check-production-misuse', mode: 'worker', requested: true, env: { SKS_TEST_RUNTIME_FIXTURE_ALLOWED: '1' }, argv: ['/x/dist/scripts/loop-fixture-production-misuse-blackbox.js'] });
|
|
69
|
-
assert(!gate.allowed && !worker.allowed, 'production gate/worker fixtures are denied');
|
|
70
|
-
assert(allowed.allowed, 'M-check temp fixture remains allowed');
|
|
71
|
-
}
|
|
72
|
-
else if (id === 'loop:final-arbiter-contract') {
|
|
73
|
-
const contract = await writeLoopFinalArbiterGateContract(temp, 'M-check-final-contract');
|
|
74
|
-
assert(contract.handled_by === 'loop-finalizer' && contract.production_fixture_allowed === false, 'final arbiter contract is finalizer-owned');
|
|
75
|
-
assert(await exists(path.join(temp, '.sneakoscope/missions/M-check-final-contract/loops/gpt-final-arbiter-gate-contract.json')), 'contract artifact written');
|
|
76
|
-
}
|
|
77
|
-
else if (id === 'loop:gpt-final-gate-contract') {
|
|
78
|
-
const node = sampleNode('loop-zellij', 'M-check-gpt-final-gate');
|
|
79
|
-
const result = await runLoopGates({ root: temp, missionId: node.mission_id, node, gates: { triage: [], local: [], checker: [], integration: [], final: ['gpt:final-arbiter'] } });
|
|
80
|
-
const artifact = await readJson(path.join(temp, '.sneakoscope/missions/M-check-gpt-final-gate/loops/loop-zellij/gates/gpt-final-arbiter.json'));
|
|
81
|
-
assert(result.ok && result.skipped_gates.includes('gpt:final-arbiter'), 'gpt final pseudo gate is skipped by gate runner');
|
|
82
|
-
assert(artifact.handled_by === 'loop-finalizer' && artifact.deferred_contract_path, 'gate artifact points to finalizer contract');
|
|
83
|
-
}
|
|
84
|
-
else if (id === 'loop:gpt-final-contract-crossref') {
|
|
85
|
-
const plan = samplePlan('M-check-gpt-crossref', [sampleNode('loop-zellij', 'M-check-gpt-crossref')]);
|
|
86
|
-
const arbiter = await runLoopGptFinalArbiter({ root: temp, plan, proofs: [sampleProof('loop-zellij', plan.mission_id, ['src/core/zellij/a.ts'])], integrationMerge: sampleMerge(['src/core/zellij/a.ts']), forceVerdict: 'approve' });
|
|
87
|
-
assert(arbiter.ok, 'check mission may use forced GPT final verdict');
|
|
88
|
-
assert(await exists(path.join(temp, '.sneakoscope/missions/M-check-gpt-crossref/loops/fixture-policy.json')), 'GPT final fixture policy artifact written');
|
|
89
|
-
}
|
|
90
|
-
else if (id === 'loop:merge-strategy' || id === 'loop:merge-strategy-blackbox') {
|
|
91
|
-
const fixture = await gitFixture('merge-strategy');
|
|
92
|
-
const proof = sampleProof('loop-zellij', 'M-check-merge-strategy', ['src/core/zellij/a.ts']);
|
|
93
|
-
proof.worktree.path = fixture.worktree;
|
|
94
|
-
proof.worktree.branch = 'loop-branch';
|
|
95
|
-
await fs.writeFile(path.join(fixture.worktree, 'src/core/zellij/a.ts'), 'changed\n');
|
|
96
|
-
const merge = await mergeSingleLoopWorktree({ root: fixture.root, proof, worktreePath: fixture.worktree, allowBranchMerge: true });
|
|
97
|
-
assert(merge.ok && ['apply', 'apply-3way', 'cherry-pick', 'already_applied'].includes(String(merge.selected_strategy)), 'merge strategy ladder applies simple patch', merge);
|
|
98
|
-
if (id === 'loop:merge-strategy-blackbox') {
|
|
99
|
-
const again = await mergeSingleLoopWorktree({ root: fixture.root, proof, worktreePath: fixture.worktree, allowBranchMerge: true });
|
|
100
|
-
assert(again.ok && again.selected_strategy === 'already_applied', 'already applied patch is handled');
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else if (id === 'loop:integration-merge-strategy') {
|
|
104
|
-
const fixture = await gitFixture('integration-merge');
|
|
105
|
-
await fs.writeFile(path.join(fixture.worktree, 'src/core/zellij/a.ts'), 'integrated\n');
|
|
106
|
-
const proof = sampleProof('loop-zellij', 'M-check-integration-merge', ['src/core/zellij/a.ts']);
|
|
107
|
-
proof.worktree.path = fixture.worktree;
|
|
108
|
-
const plan = samplePlan('M-check-integration-merge', [
|
|
109
|
-
sampleNode('loop-zellij', 'M-check-integration-merge'),
|
|
110
|
-
sampleNode('loop-integration', 'M-check-integration-merge')
|
|
111
|
-
]);
|
|
112
|
-
const result = await mergeLoopWorktrees({ root: fixture.root, plan, proofs: [proof] });
|
|
113
|
-
assert(result.ok && result.merge_attempts?.['loop-zellij'], 'integration merge records merge strategy attempts');
|
|
114
|
-
}
|
|
115
|
-
else if (id === 'loop:mutation-ledger') {
|
|
116
|
-
await appendLoopMutationEvent(temp, 'M-check-ledger', { loop_id: 'loop-zellij', event_type: 'file_changed', file_path: 'src/core/zellij/a.ts', source: 'git-diff', allowed_by_owner_scope: true, details: {} });
|
|
117
|
-
const rows = await readLoopMutationLedger(temp, 'M-check-ledger');
|
|
118
|
-
assert(rows.length === 1 && rows[0].event_type === 'file_changed', 'mutation ledger append/read works');
|
|
119
|
-
}
|
|
120
|
-
else if (id === 'loop:side-effect-scanner' || id === 'loop:side-effect-blackbox') {
|
|
121
|
-
const proofs = [sampleProof('loop-zellij', 'M-check-side-effect', ['package.json'])];
|
|
122
|
-
await mutationLedgerFromLoopProofs({ root: temp, missionId: 'M-check-side-effect', proofs, integrationMerge: sampleMerge(['package.json']) });
|
|
123
|
-
const report = await buildLoopSideEffectReport({ root: temp, missionId: 'M-check-side-effect', proofs, integrationMerge: sampleMerge(['package.json']) });
|
|
124
|
-
assert(!report.ok && report.unexpected_package_changes.includes('package.json'), 'side-effect scanner blocks non-integration package mutation', report);
|
|
125
|
-
if (id === 'loop:side-effect-blackbox')
|
|
126
|
-
assert(report.blockers.some((row) => row.includes('unexpected_package_change')), 'side-effect blackbox exposes blocker');
|
|
127
|
-
}
|
|
128
|
-
else if (id === 'loop:side-effect-final-arbiter') {
|
|
129
|
-
const plan = samplePlan('M-check-side-effect-final', [sampleNode('loop-zellij', 'M-check-side-effect-final')]);
|
|
130
|
-
const report = await buildLoopSideEffectReport({ root: temp, missionId: plan.mission_id, proofs: [sampleProof('loop-zellij', plan.mission_id, ['package.json'])], integrationMerge: sampleMerge(['package.json']) });
|
|
131
|
-
const arbiter = await runLoopGptFinalArbiter({ root: temp, plan, proofs: [sampleProof('loop-zellij', plan.mission_id, ['package.json'])], integrationMerge: sampleMerge(['package.json']), sideEffectReport: report });
|
|
132
|
-
assert(!arbiter.ok && arbiter.verdict === 'reject', 'side-effect block rejects before GPT can approve');
|
|
133
|
-
}
|
|
134
|
-
else if (id === 'loop:interrupt-registry' || id === 'loop:worker-handle-registration') {
|
|
135
|
-
await registerLoopActiveWorker(temp, { mission_id: 'M-check-interrupt', loop_id: 'loop-zellij', phase: 'maker', worker_id: 'w1', session_id: 's1', pid: null, interrupt_supported: true });
|
|
136
|
-
const handles = await readLoopActiveWorkers(temp, 'M-check-interrupt');
|
|
137
|
-
assert(handles.length === 1 && handles[0].status === 'running', 'active worker handle registers');
|
|
138
|
-
}
|
|
139
|
-
else if (id === 'loop:worker-interrupt' || id === 'loop:kill-interrupt-real-blackbox') {
|
|
140
|
-
const child = spawn(process.execPath, ['-e', 'setTimeout(() => {}, 30000)'], { stdio: 'ignore' });
|
|
141
|
-
await registerLoopActiveWorker(temp, { mission_id: 'M-check-interrupt-real', loop_id: 'loop-zellij', phase: 'maker', worker_id: 'sleepy', session_id: null, pid: child.pid || null, interrupt_supported: true });
|
|
142
|
-
const result = await interruptLoopWorkers({ root: temp, missionId: 'M-check-interrupt-real', target: 'loop-zellij', graceMs: 50 });
|
|
143
|
-
assert(result.interrupted.includes('sleepy'), 'active worker receives interrupt');
|
|
144
|
-
child.kill('SIGKILL');
|
|
145
|
-
}
|
|
146
|
-
else if (id === 'loop:concurrency-budget' || id === 'loop:concurrency-budget-runtime' || id === 'loop:concurrency-oversubscription-blackbox') {
|
|
147
|
-
const plan = samplePlan('M-check-budget', Array.from({ length: 10 }, (_, i) => sampleNode(`loop-${i}`, 'M-check-budget', 8, 8)));
|
|
148
|
-
const budget = computeLoopConcurrencyBudget({ plan, parallelism: 'extreme', env: { SKS_LOOP_MAX_ACTIVE_WORKERS: '16', SKS_LOOP_MAX_ACTIVE_LOOPS: '4', SKS_LOOP_MAX_MODEL_CALLS: '8' } });
|
|
149
|
-
assert(budget.max_active_workers === 16 && budget.max_active_loops === 4, 'env concurrency budget overrides apply', budget);
|
|
150
|
-
assert(budget.per_loop_worker_budget.reduce((sum, row) => sum + row.maker_workers + row.checker_workers, 0) <= 16, 'per-loop worker budget does not oversubscribe');
|
|
151
|
-
}
|
|
152
|
-
else if (id === 'loop:mesh-production-e2e-blackbox') {
|
|
153
|
-
const fixture = await gitFixture('mesh-e2e');
|
|
154
|
-
const proof = sampleProof('loop-zellij', 'M-check-mesh-e2e', ['src/core/zellij/a.ts']);
|
|
155
|
-
proof.worktree.path = fixture.worktree;
|
|
156
|
-
await fs.writeFile(path.join(fixture.worktree, 'src/core/zellij/a.ts'), 'mesh\n');
|
|
157
|
-
const merge = await mergeSingleLoopWorktree({ root: fixture.root, proof, worktreePath: fixture.worktree, allowBranchMerge: true });
|
|
158
|
-
const side = await buildLoopSideEffectReport({ root: fixture.root, missionId: 'M-check-mesh-e2e', proofs: [proof], integrationMerge: sampleMerge(['src/core/zellij/a.ts']) });
|
|
159
|
-
const fixturePolicy = decideLoopFixturePolicy({ root: fixture.root, missionId: 'M-check-mesh-e2e', mode: 'gpt-final', requested: true, argv: ['/x/dist/scripts/loop-mesh-production-e2e-blackbox.js'], env: {} });
|
|
160
|
-
assert(merge.ok && side.ok && fixturePolicy.allowed, 'production e2e blackbox covers merge, side effects, and check-only final fixture');
|
|
161
|
-
}
|
|
162
|
-
else if (id === 'loop:status-proof-ux') {
|
|
163
|
-
const text = await fs.readFile(path.join(root, 'src/core/commands/loop-command.ts'), 'utf8');
|
|
164
|
-
assert(['active_worker_handles', 'side_effects', 'strategy_summary', 'Final arbiter'].every((token) => text.includes(token)), 'loop status/proof UX exposes hardening fields');
|
|
165
|
-
}
|
|
166
|
-
else if (id === 'changelog:loop-productionization') {
|
|
167
|
-
const text = await fs.readFile(path.join(root, 'CHANGELOG.md'), 'utf8');
|
|
168
|
-
assert(text.includes('## [3.1.2] - 2026-06-13'), 'changelog has 3.1.2 section');
|
|
169
|
-
for (const token of ['fixture misuse guard', 'Finalizer-owned GPT final arbiter', 'merge strategy ladder', 'side-effect scanner', 'kill interrupt', 'concurrency budget', 'production e2e blackbox']) {
|
|
170
|
-
assert(text.toLowerCase().includes(token.toLowerCase()), `changelog mentions ${token}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
else if (id === 'docs:loop-productionization') {
|
|
174
|
-
const docs = await Promise.all(['docs/loop-runtime.md', 'docs/naruto-loop-mesh.md', 'docs/loop-fixture-policy.md', 'docs/loop-merge-strategy.md'].map((file) => fs.readFile(path.join(root, file), 'utf8')));
|
|
175
|
-
for (const token of ['fixture', 'gpt:final-arbiter', 'merge strategy', 'side-effect', 'interrupt', 'concurrency']) {
|
|
176
|
-
assert(docs.some((text) => text.toLowerCase().includes(token.toLowerCase())), `docs mention ${token}`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
assert(false, `unknown loop hardening check id: ${id}`);
|
|
181
|
-
}
|
|
182
|
-
const failed = assertions.filter((row) => !row.ok);
|
|
183
|
-
const report = { schema: 'sks.loop-hardening-check.v1', id, ok: failed.length === 0, assertions, temp_root: temp };
|
|
184
|
-
console.log(JSON.stringify(report, null, 2));
|
|
185
|
-
if (failed.length)
|
|
186
|
-
process.exitCode = 1;
|
|
187
|
-
}
|
|
188
|
-
function sampleNode(loopId, missionId, makerWorkers = 2, checkerWorkers = 1) {
|
|
189
|
-
return {
|
|
190
|
-
schema: 'sks.loop-node.v1',
|
|
191
|
-
loop_id: loopId,
|
|
192
|
-
mission_id: missionId,
|
|
193
|
-
title: loopId,
|
|
194
|
-
purpose: 'fixture node',
|
|
195
|
-
level: 'L2-action',
|
|
196
|
-
route: loopId.includes('integration') ? '$Integration' : '$Loop',
|
|
197
|
-
owner_scope: { files: [], directories: ['src/core/zellij'], package_scripts: [], release_gate_ids: [], exclusive: true, collision_policy: 'handoff' },
|
|
198
|
-
state_file: 'state.json',
|
|
199
|
-
run_log_file: 'run.jsonl',
|
|
200
|
-
budget: defaultLoopBudget({ max_model_calls: 8, max_subagents: makerWorkers + checkerWorkers }),
|
|
201
|
-
maker: { route: '$Naruto', role: 'implementer', worker_count: makerWorkers, backend_preference: ['codex-sdk'], local_draft_allowed: false, gpt_final_required: false },
|
|
202
|
-
checker: { route: '$QA-LOOP', worker_count: checkerWorkers, fresh_session_required: true, stronger_model_required: false, required_before_next_iteration: true },
|
|
203
|
-
gates: { triage: [], local: [], checker: [], integration: [], final: [] },
|
|
204
|
-
dependencies: [],
|
|
205
|
-
handoff_policy: { allow_handoff: true, reasons: [], artifact: null },
|
|
206
|
-
worktree: { required: false, mode: 'none', branch_prefix: 'loop', cleanup: 'keep-on-failure' },
|
|
207
|
-
risk: { level: 'medium', reasons: [], requires_worktree: false, requires_gpt_final: true, requires_human_handoff: false }
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
function samplePlan(missionId, nodes) {
|
|
211
|
-
return {
|
|
212
|
-
schema: 'sks.loop-plan.v1',
|
|
213
|
-
mission_id: missionId,
|
|
214
|
-
request: 'fixture plan',
|
|
215
|
-
generated_at: new Date().toISOString(),
|
|
216
|
-
planner: { route: '$Loop', model_policy: 'deterministic', confidence: 'high' },
|
|
217
|
-
graph: { nodes, edges: [] },
|
|
218
|
-
global_budget: defaultLoopBudget({ max_model_calls: 32, max_subagents: 32 }),
|
|
219
|
-
safety: { no_unrequested_fallback_code: true, require_owner_lease: true, require_checker_for_action: true, require_gpt_final_for_source_mutation: true },
|
|
220
|
-
integration_loop_id: nodes.at(-1)?.loop_id || 'loop-integration',
|
|
221
|
-
compatibility: { goal_compat_artifact: null, source_command: 'loop' },
|
|
222
|
-
blockers: []
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
function sampleProof(loopId, missionId, changedFiles) {
|
|
226
|
-
return {
|
|
227
|
-
schema: 'sks.loop-proof.v1',
|
|
228
|
-
mission_id: missionId,
|
|
229
|
-
loop_id: loopId,
|
|
230
|
-
status: 'completed',
|
|
231
|
-
iterations: 1,
|
|
232
|
-
owner_scope: { files: [], directories: ['src/core/zellij'], package_scripts: [], release_gate_ids: [], exclusive: true, collision_policy: 'handoff' },
|
|
233
|
-
worktree: { id: loopId, path: null, branch: null },
|
|
234
|
-
maker_result: { ok: true, worker_count: 1, artifacts: [], patch_candidates: [], backend: 'deterministic-fixture', changed_files: changedFiles, runtime_proof_path: null },
|
|
235
|
-
checker_result: { ok: true, worker_count: 1, artifacts: [], blockers: [], backend: 'deterministic-fixture', checker_findings: [], fresh_session: true, runtime_proof_path: null },
|
|
236
|
-
gate_result: { ok: true, selected_gates: [], passed_gates: [], failed_gates: [], skipped_gates: [], blockers: [] },
|
|
237
|
-
budget: { used: { wall_ms: 1, model_calls: 1, subagents: 2, iterations: 1, changed_files: changedFiles.length, patch_bytes: 1 }, max: defaultLoopBudget() },
|
|
238
|
-
changed_files: changedFiles,
|
|
239
|
-
patch_bytes: 1,
|
|
240
|
-
handoff: { required: false, reason: null, artifact: null },
|
|
241
|
-
blockers: []
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
function sampleMerge(changedFiles) {
|
|
245
|
-
return { schema: 'sks.loop-integration-merge.v1', ok: true, applied_loops: ['loop-zellij'], conflict_loops: [], changed_files: changedFiles, blockers: [] };
|
|
246
|
-
}
|
|
247
|
-
async function gitFixture(name) {
|
|
248
|
-
const repo = await fs.mkdtemp(path.join(os.tmpdir(), `sks-312-git-${safe(name)}-`));
|
|
249
|
-
await fs.mkdir(path.join(repo, 'src/core/zellij'), { recursive: true });
|
|
250
|
-
await fs.writeFile(path.join(repo, 'src/core/zellij/a.ts'), 'base\n');
|
|
251
|
-
await runProcess('git', ['init'], { cwd: repo, maxOutputBytes: 10000 });
|
|
252
|
-
await runProcess('git', ['config', 'user.email', 'sks@example.invalid'], { cwd: repo, maxOutputBytes: 10000 });
|
|
253
|
-
await runProcess('git', ['config', 'user.name', 'SKS Check'], { cwd: repo, maxOutputBytes: 10000 });
|
|
254
|
-
await runProcess('git', ['add', '.'], { cwd: repo, maxOutputBytes: 10000 });
|
|
255
|
-
await runProcess('git', ['commit', '-m', 'base'], { cwd: repo, maxOutputBytes: 20000 });
|
|
256
|
-
const worktree = `${repo}-worktree`;
|
|
257
|
-
await runProcess('git', ['worktree', 'add', '-b', 'loop-branch', worktree], { cwd: repo, maxOutputBytes: 20000 });
|
|
258
|
-
return { root: repo, worktree };
|
|
259
|
-
}
|
|
260
|
-
async function exists(file) {
|
|
261
|
-
try {
|
|
262
|
-
await fs.access(file);
|
|
263
|
-
return true;
|
|
264
|
-
}
|
|
265
|
-
catch {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
async function readJson(file) {
|
|
270
|
-
return JSON.parse(await fs.readFile(file, 'utf8'));
|
|
271
|
-
}
|
|
272
|
-
function restoreEnv(key, value) {
|
|
273
|
-
if (value === undefined)
|
|
274
|
-
delete process.env[key];
|
|
275
|
-
else
|
|
276
|
-
process.env[key] = value;
|
|
277
|
-
}
|
|
278
|
-
function replaceArgv(next) {
|
|
279
|
-
const previous = [...process.argv];
|
|
280
|
-
process.argv.splice(0, process.argv.length, ...next);
|
|
281
|
-
return previous;
|
|
282
|
-
}
|
|
283
|
-
function restoreArgv(previous) {
|
|
284
|
-
process.argv.splice(0, process.argv.length, ...previous);
|
|
285
|
-
}
|
|
286
|
-
function safe(value) {
|
|
287
|
-
return String(value).replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '').toLowerCase() || 'check';
|
|
288
|
-
}
|
|
289
|
-
//# sourceMappingURL=loop-hardening-check-lib.js.map
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { assertGate, emitGate, root, runSksJson } from './sks-1-11-gate-lib.js';
|
|
6
|
-
export { assertGate, emitGate, root, runSksJson };
|
|
7
|
-
export function read(rel) {
|
|
8
|
-
return fs.readFileSync(path.join(root, rel), 'utf8');
|
|
9
|
-
}
|
|
10
|
-
export function json(rel) {
|
|
11
|
-
return JSON.parse(read(rel));
|
|
12
|
-
}
|
|
13
|
-
export function contains(rel, needles) {
|
|
14
|
-
const text = read(rel);
|
|
15
|
-
const missing = needles.filter((needle) => !text.includes(needle));
|
|
16
|
-
return { ok: missing.length === 0, missing };
|
|
17
|
-
}
|
|
18
|
-
export function requireContains(gate, rel, needles) {
|
|
19
|
-
const result = contains(rel, needles);
|
|
20
|
-
assertGate(result.ok, `${gate} missing required wiring in ${rel}`, { missing: result.missing });
|
|
21
|
-
}
|
|
22
|
-
export function requirePackageScripts(gate, scripts) {
|
|
23
|
-
const pkg = json('package.json');
|
|
24
|
-
const missing = scripts.filter((script) => !pkg.scripts?.[script]);
|
|
25
|
-
assertGate(missing.length === 0, `${gate} package scripts missing`, { missing });
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=sks-1-12-real-execution-check-lib.js.map
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import fsp from 'node:fs/promises';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { assertGate, emitGate, importDist, root } from './sks-1-18-gate-lib.js';
|
|
7
|
-
export async function runDirective314Gate(id) {
|
|
8
|
-
if (id.startsWith('zellij:'))
|
|
9
|
-
return zellijGate(id);
|
|
10
|
-
if (id.startsWith('doctor:zellij'))
|
|
11
|
-
return doctorZellijGate(id);
|
|
12
|
-
if (id.startsWith('mad:zellij'))
|
|
13
|
-
return madZellijGate(id);
|
|
14
|
-
if (id.startsWith('codex-app:') || id === 'doctor:codex-app-harness')
|
|
15
|
-
return codexAppGate(id);
|
|
16
|
-
if (id.startsWith('loop:'))
|
|
17
|
-
return loopGate(id);
|
|
18
|
-
throw new Error(`unknown_gate:${id}`);
|
|
19
|
-
}
|
|
20
|
-
async function zellijGate(id) {
|
|
21
|
-
const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
|
|
22
|
-
const selfHeal = await importDist('core/zellij/zellij-self-heal.js');
|
|
23
|
-
const policy = await importDist('core/zellij/homebrew-policy.js');
|
|
24
|
-
if (id === 'zellij:homebrew-policy') {
|
|
25
|
-
assertGate(policy.resolveHomebrewInstallPolicy({ env: {} }).allowed === false, 'Homebrew install must not be silent by default');
|
|
26
|
-
assertGate(policy.resolveHomebrewInstallPolicy({ installHomebrew: true }).allowed === false, 'Homebrew install flag alone must still require --yes or interactive/env approval');
|
|
27
|
-
assertGate(policy.resolveHomebrewInstallPolicy({ installHomebrew: true, autoApprove: true }).allowed === true, 'Homebrew install should be allowed with explicit flag+yes');
|
|
28
|
-
assertGate(policy.homebrewMissingDoctorMessage().includes('sks doctor --fix --install-homebrew --yes'), 'Homebrew policy must expose one-shot doctor command');
|
|
29
|
-
return emitGate(id, { fixtures: 3 });
|
|
30
|
-
}
|
|
31
|
-
if (id === 'zellij:update-missing-self-heal') {
|
|
32
|
-
const update = await importDist('core/zellij/zellij-update.js');
|
|
33
|
-
const result = await update.maybePromptZellijUpdateForLaunch(['--yes'], {
|
|
34
|
-
label: 'MAD launch',
|
|
35
|
-
root: rootDir,
|
|
36
|
-
selfHealOnMissing: true,
|
|
37
|
-
autoApprove: true,
|
|
38
|
-
env: fakeZellijEnv('missing', { brew: true })
|
|
39
|
-
});
|
|
40
|
-
assertGate(result.status === 'installed', 'missing update path must self-heal when requested', result);
|
|
41
|
-
return emitGate(id, { status: result.status });
|
|
42
|
-
}
|
|
43
|
-
const result = await selfHeal.repairZellijForSks({
|
|
44
|
-
root: rootDir,
|
|
45
|
-
requestedBy: 'doctor --fix',
|
|
46
|
-
fixRequested: true,
|
|
47
|
-
autoApprove: true,
|
|
48
|
-
installHomebrew: false,
|
|
49
|
-
env: fakeZellijEnv('missing', { brew: true })
|
|
50
|
-
});
|
|
51
|
-
assertGate(result.ok === true, 'zellij self-heal must succeed with fake brew present', result);
|
|
52
|
-
assertGate(result.strategy === 'brew-install-zellij', 'missing zellij must select brew-install-zellij', result);
|
|
53
|
-
assertGate(fs.existsSync(path.join(rootDir, '.sneakoscope', 'reports', 'zellij-self-heal.json')), 'self-heal artifact missing');
|
|
54
|
-
emitGate(id, { strategy: result.strategy });
|
|
55
|
-
}
|
|
56
|
-
async function doctorZellijGate(id) {
|
|
57
|
-
const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
|
|
58
|
-
const mod = await importDist('core/doctor/doctor-zellij-repair.js');
|
|
59
|
-
const env = fakeZellijEnv(id.includes('upgrade') ? 'too_old' : 'missing', { brew: !id.includes('no-homebrew') });
|
|
60
|
-
const previous = swapEnv(env);
|
|
61
|
-
try {
|
|
62
|
-
const result = await mod.runDoctorZellijRepair({ root: rootDir, args: ['--fix', '--yes'], doctorFix: true });
|
|
63
|
-
if (id.includes('no-homebrew')) {
|
|
64
|
-
assertGate(result.strategy === 'manual-required', 'no-homebrew doctor repair must be manual-required', result);
|
|
65
|
-
assertGate(String(result.command).includes('--install-homebrew'), 'manual no-homebrew path must show install-homebrew command', result);
|
|
66
|
-
}
|
|
67
|
-
else if (id.includes('upgrade')) {
|
|
68
|
-
assertGate(result.strategy === 'brew-upgrade-zellij', 'stale zellij must upgrade', result);
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
assertGate(result.strategy === 'brew-install-zellij', 'missing zellij must install', result);
|
|
72
|
-
}
|
|
73
|
-
if (id === 'doctor:zellij-fix-output') {
|
|
74
|
-
const line = mod.doctorZellijRepairConsoleLine(result);
|
|
75
|
-
assertGate(!/optional live panes disabled/i.test(line), 'doctor repair output must not use optional/blocking wording', { line });
|
|
76
|
-
}
|
|
77
|
-
emitGate(id, { strategy: result.strategy });
|
|
78
|
-
}
|
|
79
|
-
finally {
|
|
80
|
-
restoreEnv(previous);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
async function madZellijGate(id) {
|
|
84
|
-
const source = fs.readFileSync(path.join(root, 'src/core/commands/mad-sks-command.ts'), 'utf8');
|
|
85
|
-
assertGate(source.includes("requestedBy: 'sks --mad'"), 'MAD must request zellij self-heal as sks --mad');
|
|
86
|
-
assertGate(source.includes('--headless') && source.includes('live_panes: false'), 'MAD must support headless live_panes=false');
|
|
87
|
-
assertGate(!/optional live panes disabled/.test(source), 'MAD source must not print optional live panes disabled');
|
|
88
|
-
if (id === 'mad:zellij-no-contradictory-output') {
|
|
89
|
-
const update = fs.readFileSync(path.join(root, 'src/core/zellij/zellij-update.ts'), 'utf8');
|
|
90
|
-
assertGate(!/Zellij not found \(optional live panes disabled\)/.test(update), 'Zellij missing output must not be contradictory optional wording');
|
|
91
|
-
}
|
|
92
|
-
emitGate(id, { source_checked: true });
|
|
93
|
-
}
|
|
94
|
-
async function codexAppGate(id) {
|
|
95
|
-
const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
|
|
96
|
-
const previous = swapEnv({
|
|
97
|
-
SKS_CODEX_0138_FAKE: '1',
|
|
98
|
-
SKS_CODEX_0139_FAKE: '1',
|
|
99
|
-
SKS_CODEX_PLUGIN_JSON_FAKE: '1',
|
|
100
|
-
SKS_CODEX_AGENT_TYPE_SUPPORTED: id.includes('blackbox') ? '1' : ''
|
|
101
|
-
});
|
|
102
|
-
try {
|
|
103
|
-
if (id === 'codex-app:harness-matrix' || id === 'doctor:codex-app-harness' || id === 'codex-app:harness-blackbox') {
|
|
104
|
-
const mod = await importDist('core/codex-app/codex-app-harness-matrix.js');
|
|
105
|
-
const matrix = await mod.buildCodexAppHarnessMatrix({ root: rootDir });
|
|
106
|
-
assertGate(matrix.schema === 'sks.codex-app-harness-matrix.v1', 'harness matrix schema mismatch', matrix);
|
|
107
|
-
assertGate(matrix.app_features.plugin_json === true, 'fixture should expose plugin_json', matrix);
|
|
108
|
-
if (id === 'doctor:codex-app-harness') {
|
|
109
|
-
const doctor = fs.readFileSync(path.join(root, 'src/commands/doctor.ts'), 'utf8');
|
|
110
|
-
assertGate(doctor.includes('Codex App Harness:'), 'doctor output must include Codex App Harness section');
|
|
111
|
-
assertGate(doctor.includes('codex_app_harness_matrix'), 'doctor JSON must include codex_app_harness_matrix');
|
|
112
|
-
}
|
|
113
|
-
return emitGate(id, { ok: matrix.ok, warnings: matrix.warnings.length });
|
|
114
|
-
}
|
|
115
|
-
if (id === 'codex-app:skill-sync' || id === 'codex-app:skill-agent-blackbox') {
|
|
116
|
-
const mod = await importDist('core/codex-app/codex-skill-sync.js');
|
|
117
|
-
const skillsRoot = path.join(rootDir, 'skills');
|
|
118
|
-
await fsp.mkdir(path.join(skillsRoot, 'ulw-loop'), { recursive: true });
|
|
119
|
-
const report = await mod.syncCodexSksSkills({ root: rootDir, skillsRoot, apply: true });
|
|
120
|
-
assertGate(report.interop.clobbered_external_routes === false && report.external_route_names_preserved.includes('ulw-loop'), 'skill sync must preserve existing external route skills', report);
|
|
121
|
-
return emitGate(id, { desired: report.desired_skills.length });
|
|
122
|
-
}
|
|
123
|
-
if (id === 'codex-app:agent-role-sync') {
|
|
124
|
-
const mod = await importDist('core/codex-app/codex-agent-role-sync.js');
|
|
125
|
-
const report = await mod.syncCodexAgentRoles({ root: rootDir, codexHome: path.join(rootDir, 'codex-home'), apply: true, agentTypeSupported: true });
|
|
126
|
-
assertGate(report.fallback === 'agent_type', 'agent role sync should use agent_type when supported', report);
|
|
127
|
-
return emitGate(id, { roles: report.directive_roles.length });
|
|
128
|
-
}
|
|
129
|
-
if (id === 'codex-app:init-deep') {
|
|
130
|
-
const mod = await importDist('core/codex-app/codex-init-deep.js');
|
|
131
|
-
await fsp.mkdir(path.join(rootDir, 'src/core/zellij'), { recursive: true });
|
|
132
|
-
await fsp.writeFile(path.join(rootDir, 'src/core/zellij/a.ts'), 'export {}\n');
|
|
133
|
-
const report = await mod.runCodexInitDeep({ root: rootDir, apply: true });
|
|
134
|
-
assertGate(report.root_agents_preserved === true, 'init-deep must preserve user AGENTS.md', report);
|
|
135
|
-
return emitGate(id, { guidance: report.directory_guidance.length });
|
|
136
|
-
}
|
|
137
|
-
if (id === 'codex-app:hook-lifecycle') {
|
|
138
|
-
const mod = await importDist('core/codex-app/codex-hook-lifecycle.js');
|
|
139
|
-
const report = await mod.buildCodexHookLifecycle({ root: rootDir });
|
|
140
|
-
assertGate(report.approval_state === 'unknown', 'hook lifecycle must report unknown approval when not detectable', report);
|
|
141
|
-
return emitGate(id, { lifecycle: Object.keys(report.lifecycle).length });
|
|
142
|
-
}
|
|
143
|
-
if (id === 'codex-app:execution-profile') {
|
|
144
|
-
const mod = await importDist('core/codex-app/codex-app-execution-profile.js');
|
|
145
|
-
const profile = await mod.resolveCodexAppExecutionProfile({ root: rootDir });
|
|
146
|
-
assertGate(['codex-app-native', 'codex-cli-headless', 'sks-loop-headless', 'degraded-no-app'].includes(profile.mode), 'execution profile mode invalid', profile);
|
|
147
|
-
return emitGate(id, { mode: profile.mode });
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
finally {
|
|
151
|
-
restoreEnv(previous);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
async function loopGate(id) {
|
|
155
|
-
const rootDir = await tempRoot(`sks-${id.replace(/[:/]/g, '-')}-`);
|
|
156
|
-
if (id === 'loop:planner-project-memory') {
|
|
157
|
-
const init = await importDist('core/codex-app/codex-init-deep.js');
|
|
158
|
-
const planner = await importDist('core/loops/loop-planner.js');
|
|
159
|
-
await fsp.mkdir(path.join(rootDir, 'src/core/loops'), { recursive: true });
|
|
160
|
-
await fsp.writeFile(path.join(rootDir, 'src/core/loops/a.ts'), 'export {}\n');
|
|
161
|
-
await init.runCodexInitDeep({ root: rootDir, apply: true });
|
|
162
|
-
const plan = await planner.planLoopsFromRequest({ root: rootDir, missionId: 'M-loop-memory', request: 'update loop planner project memory', sourceCommand: 'loop' });
|
|
163
|
-
assertGate(plan.project_memory?.injected === true, 'loop planner must consume init-deep memory hints', plan);
|
|
164
|
-
return emitGate(id, { injected: true });
|
|
165
|
-
}
|
|
166
|
-
const planDir = path.join(rootDir, '.sneakoscope', 'missions', 'M-loop-cont', 'loops');
|
|
167
|
-
await fsp.mkdir(planDir, { recursive: true });
|
|
168
|
-
await fsp.writeFile(path.join(rootDir, '.sneakoscope', 'missions', 'M-loop-cont', 'loops', 'loop-plan.json'), JSON.stringify({ graph: { nodes: [{ loop_id: 'loop-a' }] } }));
|
|
169
|
-
const mod = await importDist('core/loops/loop-continuation-enforcer.js');
|
|
170
|
-
const report = await mod.evaluateLoopContinuation({ root: rootDir, missionId: 'M-loop-cont' });
|
|
171
|
-
assertGate(report.should_continue === true, 'loop continuation should request resume when proof missing', report);
|
|
172
|
-
emitGate(id, { should_continue: report.should_continue });
|
|
173
|
-
}
|
|
174
|
-
function fakeZellijEnv(status, opts = {}) {
|
|
175
|
-
return {
|
|
176
|
-
...process.env,
|
|
177
|
-
SKS_ZELLIJ_CAPABILITY_FAKE_STATUS: status,
|
|
178
|
-
SKS_ZELLIJ_CAPABILITY_FAKE_VERSION: status === 'too_old' ? '0.40.0' : '0.44.0',
|
|
179
|
-
SKS_ZELLIJ_SELF_HEAL_BEFORE_STATUS: status,
|
|
180
|
-
SKS_ZELLIJ_SELF_HEAL_BEFORE_VERSION: status === 'too_old' ? '0.40.0' : '',
|
|
181
|
-
SKS_ZELLIJ_SELF_HEAL_AFTER_STATUS: 'ok',
|
|
182
|
-
SKS_ZELLIJ_SELF_HEAL_AFTER_VERSION: '0.44.3',
|
|
183
|
-
SKS_ZELLIJ_LATEST_VERSION: '0.44.3',
|
|
184
|
-
SKS_ZELLIJ_SELF_HEAL_FAKE_RUN: '1',
|
|
185
|
-
SKS_ZELLIJ_SELF_HEAL_BREW_PRESENT: opts.brew ? '1' : '0'
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
async function tempRoot(prefix) {
|
|
189
|
-
const dir = await fsp.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
190
|
-
await fsp.mkdir(path.join(dir, '.sneakoscope', 'reports'), { recursive: true });
|
|
191
|
-
return dir;
|
|
192
|
-
}
|
|
193
|
-
function swapEnv(next) {
|
|
194
|
-
const previous = {};
|
|
195
|
-
for (const [key, value] of Object.entries(next)) {
|
|
196
|
-
previous[key] = process.env[key];
|
|
197
|
-
if (value === '')
|
|
198
|
-
delete process.env[key];
|
|
199
|
-
else
|
|
200
|
-
process.env[key] = value;
|
|
201
|
-
}
|
|
202
|
-
return previous;
|
|
203
|
-
}
|
|
204
|
-
function restoreEnv(previous) {
|
|
205
|
-
for (const [key, value] of Object.entries(previous)) {
|
|
206
|
-
if (value === undefined)
|
|
207
|
-
delete process.env[key];
|
|
208
|
-
else
|
|
209
|
-
process.env[key] = value;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
//# sourceMappingURL=sks-3-1-4-directive-check-lib.js.map
|