sneakoscope 4.1.1 → 4.2.1
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 +13 -10
- 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/cli/command-registry.js +1 -1
- package/dist/core/auto-review.js +1 -1
- package/dist/core/codex-control/codex-app-server-v2-client.js +86 -2
- package/dist/core/codex-control/codex-reliability-shield.js +26 -5
- package/dist/core/codex-control/codex-task-runner.js +7 -1
- package/dist/core/codex-control/model-call-concurrency.js +1 -1
- package/dist/core/commands/mad-db-command.js +146 -51
- package/dist/core/commands/mad-sks-command.js +15 -31
- package/dist/core/commands/qa-loop-command.js +23 -7
- package/dist/core/db-safety.js +35 -37
- package/dist/core/doctor/supabase-mcp-repair.js +2 -2
- package/dist/core/feature-registry.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +1 -1
- package/dist/core/init.js +5 -4
- package/dist/core/mad-db/mad-db-capability.js +203 -74
- package/dist/core/mad-db/mad-db-coordinator.js +287 -0
- package/dist/core/mad-db/mad-db-executor.js +156 -0
- package/dist/core/mad-db/mad-db-ledger.js +1 -1
- package/dist/core/mad-db/mad-db-lock.js +40 -0
- package/dist/core/mad-db/mad-db-operation-store.js +140 -0
- package/dist/core/mad-db/mad-db-policy-resolver.js +42 -22
- package/dist/core/mad-db/mad-db-policy.js +195 -0
- package/dist/core/mad-db/mad-db-postconditions.js +30 -0
- package/dist/core/mad-db/mad-db-recovery.js +27 -0
- package/dist/core/mad-db/mad-db-result-lifecycle.js +31 -102
- package/dist/core/mad-db/mad-db-runtime-profile.js +121 -0
- package/dist/core/mad-db/mad-db-target.js +64 -0
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/pipeline-internals/runtime-core.js +40 -0
- package/dist/core/providers/glm/bench/glm-benchmark-types.js +1 -1
- package/dist/core/qa-loop/qa-app-server-driver.js +134 -0
- package/dist/core/qa-loop/qa-contract-v2.js +231 -0
- package/dist/core/qa-loop/qa-gate-v2.js +132 -0
- package/dist/core/qa-loop/qa-runtime-artifacts.js +53 -0
- package/dist/core/qa-loop/qa-surface-router.js +114 -0
- package/dist/core/qa-loop/qa-types.js +18 -0
- package/dist/core/qa-loop.js +83 -26
- package/dist/core/release/gate-manifest.js +1 -0
- package/dist/core/release/release-gate-dag.js +6 -5
- package/dist/core/release/sla-scheduler.js +1 -1
- package/dist/core/routes.js +42 -12
- package/dist/core/triwiki/triwiki-affected-graph.js +3 -2
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-slot-column-anchor.js +5 -1
- package/dist/scripts/check-dist-runtime.js +3 -2
- package/dist/scripts/codex-0142-manifest-check.js +2 -1
- package/dist/scripts/codex-control-all-pipelines-check.js +1 -0
- package/dist/scripts/codex-control-model-capacity-fallback-check.js +53 -0
- package/dist/scripts/config-managed-merge-callsite-coverage-check.js +7 -1
- package/dist/scripts/loop-directive-check-lib.js +78 -1
- package/dist/scripts/mad-db-capability-check.js +13 -2
- package/dist/scripts/mad-db-command-check.js +7 -5
- package/dist/scripts/mad-db-hook-idempotency-check.js +21 -0
- package/dist/scripts/mad-db-ledger-check.js +2 -1
- package/dist/scripts/mad-db-lifecycle-hook-decision-check.js +5 -4
- package/dist/scripts/mad-db-mad-command-check.js +29 -16
- package/dist/scripts/mad-db-mcp-result-lifecycle-check.js +11 -10
- package/dist/scripts/mad-db-one-cycle-bounded-check.js +15 -18
- package/dist/scripts/mad-db-one-cycle-consumption-check.js +3 -3
- package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +9 -9
- package/dist/scripts/mad-db-operation-lifecycle-ledger-check.js +6 -6
- package/dist/scripts/mad-db-parallel-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-policy-v2-check.js +20 -0
- package/dist/scripts/mad-db-priority-resolver-check.js +5 -5
- package/dist/scripts/mad-db-real-supabase-e2e.js +166 -0
- package/dist/scripts/mad-db-route-identity-check.js +28 -0
- package/dist/scripts/mad-db-runtime-profile-lifecycle-check.js +24 -0
- package/dist/scripts/mad-db-safety-conflict-matrix-check.js +3 -3
- package/dist/scripts/mad-db-skill-policy-snapshot-check.js +15 -0
- package/dist/scripts/qa-loop-app-server-driver-check.js +74 -0
- package/dist/scripts/qa-loop-surface-router-check.js +49 -0
- package/dist/scripts/release-check-dynamic-execute.js +1 -1
- package/dist/scripts/release-dag-full-coverage-check.js +6 -0
- package/dist/scripts/release-triwiki-first-runner-blackbox.js +5 -1
- package/dist/scripts/runtime-ts-rust-boundary-check.js +1 -1
- package/dist/scripts/triwiki-affected-graph-check.js +2 -2
- package/package.json +18 -5
- package/schemas/mad-db/mad-db-capability.schema.json +92 -19
package/README.md
CHANGED
|
@@ -35,15 +35,18 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
|
|
|
35
35
|
|
|
36
36
|
## 🚀 Current Release
|
|
37
37
|
|
|
38
|
-
SKS **4.
|
|
38
|
+
SKS **4.2.1** stabilizes MadDB SQL-plane execution so explicit `$MAD-DB` and `sks mad-db run|exec|apply-migration` invocations use a first-class, mission-bound break-glass route instead of inheriting `$MAD-SKS` state.
|
|
39
39
|
|
|
40
|
-
What changed in 4.
|
|
40
|
+
What changed in 4.2.0:
|
|
41
|
+
|
|
42
|
+
- **First-class MadDB route.** `$MAD-DB` no longer aliases `$MAD-SKS`; it creates one authoritative mission, capability, runtime profile, inventory check, execution, read-back, and closeout cycle.
|
|
43
|
+
- **Capability v2 binding.** MadDB capabilities bind project root, project ref hash, mission/cycle/session identity, runtime profile hash, TTL, operator intent, and SQL-plane operation classes.
|
|
44
|
+
- **Ephemeral Supabase write profile.** Persistent Supabase MCP config stays read-only; write-capable MCP settings exist only inside the active MadDB mission and are removed in `finally`.
|
|
45
|
+
- **Exact lifecycle correlation.** Hook/result handling is keyed by canonical `tool_call_id`, uses idempotent operation state, and avoids unsafe tool-name result matching under parallel calls.
|
|
46
|
+
- **Policy/docs/test SSOT.** MadDB route metadata, generated skill guidance, DB safety wording, Doctor guidance, release gates, docs, scanner coverage, and local regression tests share the typed MadDB policy surface.
|
|
47
|
+
- **Release metadata truth.** Package, CLI version constants, Rust crate metadata, README, changelog, and release checks all point at 4.2.0.
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
- **Migration receipt v2.** First-command migration writes project receipts with installation epoch, project hash, required blockers, and optional warnings so stale locks or optional capabilities do not block normal commands.
|
|
44
|
-
- **Fast migration Doctor.** `sks doctor --fix --profile migration --machine-only` skips optional Codex App, Zellij, provider, native, and deep diagnostic work while preserving core readiness checks.
|
|
45
|
-
- **MAD bootstrap latency.** MAD defers update prompts, provider setup, UI snapshots, pane proof, and native swarm proof until the route actually needs them.
|
|
46
|
-
- **Release metadata truth.** Package, CLI version constants, Rust crate metadata, README, changelog, and release checks all point at 4.1.1.
|
|
49
|
+
What changed in 4.1.1:
|
|
47
50
|
|
|
48
51
|
What changed in 4.1.0:
|
|
49
52
|
|
|
@@ -504,7 +507,7 @@ sks --mad
|
|
|
504
507
|
sks --mad --allow-package-install --allow-service-control --allow-network --yes
|
|
505
508
|
```
|
|
506
509
|
|
|
507
|
-
This syncs existing codex-lb provider auth, creates/uses the `sks-mad-high` xhigh maintenance profile, opens the MAD-SKS permission gate for that Zellij run, starts a same-mission read-only native agent swarm, and launches a Codex CLI layout whose right-side lanes read that MAD ledger. Bare `sks --mad` grants target-project file and shell scope only; add explicit `--allow-*` flags for packages, services, network, Computer Use, browser use, generated assets, file permissions, DB writes, or other high-risk scopes. MAD-SKS is not a DB-only unlock
|
|
510
|
+
This syncs existing codex-lb provider auth, creates/uses the `sks-mad-high` xhigh maintenance profile, opens the MAD-SKS permission gate for that Zellij run, starts a same-mission read-only native agent swarm, and launches a Codex CLI layout whose right-side lanes read that MAD ledger. Bare `sks --mad` grants target-project file and shell scope only; add explicit `--allow-*` flags for packages, services, network, Computer Use, browser use, generated assets, file permissions, DB writes, or other high-risk scopes. MAD-SKS is not a DB-only unlock and does not create a MadDB capability. Catastrophic database wipe/all-row/project-management safeguards remain active outside the first-class MadDB route, and the pipeline contract still forbids unrequested fallback implementation code.
|
|
508
511
|
|
|
509
512
|
Before launching, SKS checks npm for a newer `sneakoscope` and prints a non-blocking update notice when one is available; use `sks update now` or `sks doctor --fix` when you want SKS to update itself. Use `--yes` to approve missing dependency installs automatically. Tune MAD swarm startup with `--mad-agents <n>`, `--mad-swarm-work-items <n>`, and `--mad-swarm-backend <backend>`; `--no-mad-swarm` keeps only the cockpit UI if you need a temporary fallback.
|
|
510
513
|
|
|
@@ -717,7 +720,7 @@ Use these inside Codex App or another agent prompt. They are prompt commands, no
|
|
|
717
720
|
|
|
718
721
|
Common prompts: `$Team`, `$From-Chat-IMG`, `$with-local-llm-on`, `$with-local-llm-off`, `$DFix`, `$Answer`, `$SKS`, `$QA-LOOP`, `$PPT`, `$Computer-Use`/`$CU`, `$Goal`, `$Research`, `$AutoResearch`, `$DB`, `$MAD-SKS`, `$MAD-DB`, `$GX`, `$Wiki`, and `$Help`.
|
|
719
722
|
|
|
720
|
-
`$MAD-DB` is the
|
|
723
|
+
`$MAD-DB` is the first-class MadDB SQL-plane execution route. `sks mad-db run|exec|apply-migration` creates the bound mission/capability/runtime profile, verifies Supabase `execute_sql` and `apply_migration`, executes the requested SQL-plane mutation, reads back postconditions, and then closes the write profile while proving normal read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied. See `docs/mad-db.md`.
|
|
721
724
|
|
|
722
725
|
## 🔁 Common Workflows
|
|
723
726
|
|
|
@@ -863,7 +866,7 @@ npm run release:check
|
|
|
863
866
|
npm run publish:dry
|
|
864
867
|
```
|
|
865
868
|
|
|
866
|
-
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. `npm run publish:dry` runs `release:check:full`, verifies the fresh stamp, and then performs provenance/registry and npm dry-run checks. npm's `prepublishOnly` uses `prepublish-release-check-or-fast` to accept that current stamp before the real publish; if the stamp is missing or stale, it runs `release:check:full` once before continuing.
|
|
869
|
+
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. `npm run publish:dry` runs `release:check:full`, verifies the fresh stamp, and then performs provenance/registry and npm dry-run checks. `npm run publish:npm` and `npm run release:publish` run the same prepublish gate and then `npm publish --ignore-scripts`, so the real publish path stays strict even when lifecycle scripts are skipped. npm's `prepublishOnly` uses `prepublish-release-check-or-fast` to accept that current stamp before the real publish; if the stamp is missing or stale, it runs `release:check:full` once before continuing.
|
|
867
870
|
|
|
868
871
|
Version bumps are manual. Run `sks versioning bump` only when preparing release metadata; SKS will not create `.git/hooks/pre-commit` or auto-bump during ordinary commits.
|
|
869
872
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 4.
|
|
7
|
+
Some("--version") => println!("sks-rs 4.2.1"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
|
@@ -112,7 +112,7 @@ export const COMMANDS = {
|
|
|
112
112
|
zellij: entry('beta', 'Inspect Zellij runtime status and explain repair (no auto-install)', 'dist/commands/zellij.js', directCommand(() => import('../commands/zellij.js'), 'dist/commands/zellij.js')),
|
|
113
113
|
'mad-sks': entry('beta', 'MAD-SKS scoped permission modifier', 'dist/commands/mad-sks.js', directCommand(() => import('../commands/mad-sks.js'), 'dist/commands/mad-sks.js')),
|
|
114
114
|
glm: entry('beta', 'Run GLM 5.2 MAD mode through OpenRouter', 'dist/core/commands/glm-command.js', argsCommand(() => import('../core/commands/glm-command.js'), 'glmCommand', 'dist/core/commands/glm-command.js')),
|
|
115
|
-
'mad-db': entry('beta', '
|
|
115
|
+
'mad-db': entry('beta', 'Run first-class MadDB SQL-plane execution cycles with mission-local Supabase write transport', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
|
|
116
116
|
'auto-review': entry('beta', 'Manage auto-review profile', 'dist/commands/auto-review.js', directCommand(() => import('../commands/auto-review.js'), 'dist/commands/auto-review.js')),
|
|
117
117
|
'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
|
|
118
118
|
'fast-mode': entry('stable', 'Toggle SKS Fast mode default for dollar-command routes', 'dist/core/commands/fast-mode-command.js', argsCommand(() => import('../core/commands/fast-mode-command.js'), 'fastModeCommand', 'dist/core/commands/fast-mode-command.js')),
|
package/dist/core/auto-review.js
CHANGED
|
@@ -314,7 +314,7 @@ function removeLegacyProfileConfig(text, profile) {
|
|
|
314
314
|
function upsertAutoReviewPolicy(text) {
|
|
315
315
|
const policy = [
|
|
316
316
|
'[auto_review]',
|
|
317
|
-
'policy = "In MAD launches, allow
|
|
317
|
+
'policy = "In MAD-SKS launches, allow only scoped non-MadDB high-risk work approved for the active invocation and keep catastrophic DB wipe/all-row safeguards active. In first-class MAD-DB cycles, the explicit $MAD-DB or sks mad-db run|exec|apply-migration invocation is the SQL-plane approval boundary: execute requested execute_sql/apply_migration mutations with mission-local write transport, read-back proof, and final read-only restoration. Supabase project/account/billing/credential control-plane actions remain denied."'
|
|
318
318
|
].join('\n');
|
|
319
319
|
const existing = readTableString(text, 'auto_review', 'policy');
|
|
320
320
|
if (existing && /unrequested fallback implementation code/i.test(existing))
|
|
@@ -8,10 +8,12 @@ export class CodexAppServerV2Client {
|
|
|
8
8
|
cwd;
|
|
9
9
|
timeoutMs;
|
|
10
10
|
currentTimeProvider;
|
|
11
|
+
approvalPolicy;
|
|
11
12
|
child = null;
|
|
12
13
|
nextId = 1;
|
|
13
14
|
pending = new Map();
|
|
14
15
|
notifications = [];
|
|
16
|
+
listeners = new Set();
|
|
15
17
|
stdoutBuffer = '';
|
|
16
18
|
stderr = '';
|
|
17
19
|
constructor(options) {
|
|
@@ -21,6 +23,7 @@ export class CodexAppServerV2Client {
|
|
|
21
23
|
this.cwd = options.cwd || process.cwd();
|
|
22
24
|
this.timeoutMs = Number(options.timeoutMs || 20_000);
|
|
23
25
|
this.currentTimeProvider = options.currentTimeProvider || (() => new Date());
|
|
26
|
+
this.approvalPolicy = options.approvalPolicy || {};
|
|
24
27
|
}
|
|
25
28
|
async initialize() {
|
|
26
29
|
this.start();
|
|
@@ -36,18 +39,58 @@ export class CodexAppServerV2Client {
|
|
|
36
39
|
optOutNotificationMethods: []
|
|
37
40
|
}
|
|
38
41
|
});
|
|
39
|
-
this.notify('
|
|
42
|
+
this.notify('initialized', {});
|
|
40
43
|
return result;
|
|
41
44
|
}
|
|
42
45
|
async listThreads(params = {}) {
|
|
43
46
|
return await this.request('thread/list', normalizeThreadListParams(params));
|
|
44
47
|
}
|
|
48
|
+
async startThread(params = {}) {
|
|
49
|
+
return await this.request('thread/start', params);
|
|
50
|
+
}
|
|
51
|
+
async resumeThread(params = {}) {
|
|
52
|
+
return await this.request('thread/resume', params);
|
|
53
|
+
}
|
|
45
54
|
async searchThreads(searchTerm, params = {}) {
|
|
46
55
|
return await this.listThreads({ ...params, searchTerm });
|
|
47
56
|
}
|
|
48
57
|
async readThread(threadId, includeTurns = false) {
|
|
49
58
|
return await this.request('thread/read', { threadId, includeTurns });
|
|
50
59
|
}
|
|
60
|
+
async startTurn(params = {}) {
|
|
61
|
+
return await this.request('turn/start', params);
|
|
62
|
+
}
|
|
63
|
+
async steerTurn(params = {}) {
|
|
64
|
+
return await this.request('turn/steer', params);
|
|
65
|
+
}
|
|
66
|
+
async interruptTurn(params = {}) {
|
|
67
|
+
return await this.request('turn/interrupt', params);
|
|
68
|
+
}
|
|
69
|
+
onEvent(listener) {
|
|
70
|
+
this.listeners.add(listener);
|
|
71
|
+
return () => this.listeners.delete(listener);
|
|
72
|
+
}
|
|
73
|
+
waitForNotification(methods, timeoutMs = this.timeoutMs) {
|
|
74
|
+
const expected = new Set(Array.isArray(methods) ? methods.map(String) : [String(methods)]);
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const timer = setTimeout(() => {
|
|
77
|
+
dispose();
|
|
78
|
+
reject(new Error(`Timed out waiting for app-server notification: ${Array.from(expected).join(', ')}`));
|
|
79
|
+
}, timeoutMs);
|
|
80
|
+
timer.unref?.();
|
|
81
|
+
const dispose = this.onEvent((event) => {
|
|
82
|
+
if (event && expected.has(String(event.method || ''))) {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
dispose();
|
|
85
|
+
resolve(event);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async waitForTurnCompletion(threadId, turnId, timeoutMs = this.timeoutMs) {
|
|
91
|
+
const expected = turnId ? ['turn/completed', 'thread/closed', 'thread/status/changed'] : ['turn/completed', 'thread/closed'];
|
|
92
|
+
return await this.waitForNotification(expected, timeoutMs);
|
|
93
|
+
}
|
|
51
94
|
start() {
|
|
52
95
|
if (this.child)
|
|
53
96
|
return;
|
|
@@ -107,7 +150,14 @@ export class CodexAppServerV2Client {
|
|
|
107
150
|
void this.respondToServerRequest(message);
|
|
108
151
|
}
|
|
109
152
|
else {
|
|
110
|
-
|
|
153
|
+
const event = { ...message, received_at: nowIso() };
|
|
154
|
+
this.notifications.push(event);
|
|
155
|
+
for (const listener of this.listeners) {
|
|
156
|
+
try {
|
|
157
|
+
listener(event);
|
|
158
|
+
}
|
|
159
|
+
catch { }
|
|
160
|
+
}
|
|
111
161
|
}
|
|
112
162
|
}
|
|
113
163
|
}
|
|
@@ -119,6 +169,38 @@ export class CodexAppServerV2Client {
|
|
|
119
169
|
this.write({ jsonrpc: '2.0', id, result: currentTimeResponse(this.currentTimeProvider()) });
|
|
120
170
|
return;
|
|
121
171
|
}
|
|
172
|
+
if (method === 'item/commandExecution/requestApproval' || method === 'commandExecution/requestApproval') {
|
|
173
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.commandExecution?.(message.params) || { decision: 'cancel' } });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (method === 'item/fileChange/requestApproval' || method === 'fileChange/requestApproval') {
|
|
177
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.fileChange?.(message.params) || { decision: 'cancel' } });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (method === 'item/permissions/requestApproval' || method === 'permissions/requestApproval') {
|
|
181
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.permissions?.(message.params) || { permissions: { network: { enabled: false }, fileSystem: { read: [], write: [], entries: [] } }, scope: 'turn', strictAutoReview: true } });
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (method === 'item/tool/requestUserInput') {
|
|
185
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.toolRequestUserInput?.(message.params) || { answers: {} } });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (method === 'item/tool/call') {
|
|
189
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.dynamicToolCall?.(message.params) || { contentItems: [], success: false } });
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (method === 'mcpServer/elicitation/request') {
|
|
193
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.mcpElicitation?.(message.params) || { contentItems: [], success: false } });
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (method === 'attestation/generate') {
|
|
197
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.attestation?.(message.params) || { decision: 'cancel' } });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (method === 'account/chatgptAuthTokens/refresh') {
|
|
201
|
+
this.write({ jsonrpc: '2.0', id, result: this.approvalPolicy.chatgptAuthTokensRefresh?.(message.params) || { ok: false } });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
122
204
|
this.write({
|
|
123
205
|
jsonrpc: '2.0',
|
|
124
206
|
id,
|
|
@@ -182,6 +264,8 @@ export async function createCodexAppServerV2Client(options = {}) {
|
|
|
182
264
|
clientOptions.timeoutMs = options.timeoutMs;
|
|
183
265
|
if (options.currentTimeProvider !== undefined)
|
|
184
266
|
clientOptions.currentTimeProvider = options.currentTimeProvider;
|
|
267
|
+
if (options.approvalPolicy !== undefined)
|
|
268
|
+
clientOptions.approvalPolicy = options.approvalPolicy;
|
|
185
269
|
return {
|
|
186
270
|
client: new CodexAppServerV2Client(clientOptions),
|
|
187
271
|
runtimeIdentity: runtime.identity
|
|
@@ -37,6 +37,7 @@ export async function runWithCodexReliabilityShield(input, runAttempt) {
|
|
|
37
37
|
break;
|
|
38
38
|
}
|
|
39
39
|
const blockers = attempts.flatMap((attempt) => attempt.blockers);
|
|
40
|
+
const modelCapacityRetryCount = attempts.filter((attempt) => attempt.model_capacity_error && attempt.retryable).length;
|
|
40
41
|
const report = {
|
|
41
42
|
schema: CODEX_RELIABILITY_SHIELD_SCHEMA,
|
|
42
43
|
generated_at: nowIso(),
|
|
@@ -52,6 +53,8 @@ export async function runWithCodexReliabilityShield(input, runAttempt) {
|
|
|
52
53
|
heartbeat_count: attempts.reduce((sum, attempt) => sum + attempt.heartbeat_count, 0),
|
|
53
54
|
repaired_tool_result_count: attempts.reduce((sum, attempt) => sum + attempt.repaired_tool_result_count, 0),
|
|
54
55
|
no_duplicate_streamed_output: attempts.slice(0, -1).every((attempt) => attempt.meaningful_event_count === 0),
|
|
56
|
+
model_capacity_retry_count: modelCapacityRetryCount,
|
|
57
|
+
selected_model_capacity_fallback: selectedAttempt > 1 && modelCapacityRetryCount > 0,
|
|
55
58
|
blockers
|
|
56
59
|
};
|
|
57
60
|
return {
|
|
@@ -61,24 +64,27 @@ export async function runWithCodexReliabilityShield(input, runAttempt) {
|
|
|
61
64
|
}
|
|
62
65
|
export function evaluateCodexReliabilityAttempt(result, events, policy, attempt) {
|
|
63
66
|
const meaningful = events.filter(isMeaningfulEvent);
|
|
64
|
-
const
|
|
67
|
+
const modelCapacity = isCodexModelCapacityError(result, events);
|
|
68
|
+
const fatal = !modelCapacity && hasFatalError(result, events);
|
|
65
69
|
const idle = hasIdleTimeout(events, policy.idleTimeoutMs);
|
|
66
70
|
const empty = events.length === 0 || (!String(result.finalResponse || '').trim() && meaningful.length === 0);
|
|
67
71
|
const partial = meaningful.length > 0 && !result.structuredOutput;
|
|
68
72
|
const blockers = [];
|
|
69
73
|
let retryable = false;
|
|
70
74
|
let retryReason = null;
|
|
71
|
-
if (
|
|
75
|
+
if (modelCapacity)
|
|
76
|
+
blockers.push('codex_model_capacity_unavailable');
|
|
77
|
+
if (!modelCapacity && idle && partial)
|
|
72
78
|
blockers.push('codex_reliability_idle_after_partial_output');
|
|
73
|
-
if (partial && !idle)
|
|
79
|
+
if (!modelCapacity && partial && !idle)
|
|
74
80
|
blockers.push('codex_reliability_partial_output_without_structured_result');
|
|
75
81
|
if (fatal)
|
|
76
82
|
blockers.push('codex_reliability_fatal_error_no_retry');
|
|
77
|
-
if (!fatal && idle && meaningful.length === 0) {
|
|
83
|
+
if (!modelCapacity && !fatal && idle && meaningful.length === 0) {
|
|
78
84
|
retryable = true;
|
|
79
85
|
retryReason = 'stream_idle_before_meaningful_event';
|
|
80
86
|
}
|
|
81
|
-
else if (!fatal && empty) {
|
|
87
|
+
else if (!modelCapacity && !fatal && empty) {
|
|
82
88
|
retryable = true;
|
|
83
89
|
retryReason = 'empty_sdk_result_before_meaningful_event';
|
|
84
90
|
}
|
|
@@ -92,11 +98,26 @@ export function evaluateCodexReliabilityAttempt(result, events, policy, attempt)
|
|
|
92
98
|
retry_reason: retryReason,
|
|
93
99
|
idle_timeout: idle,
|
|
94
100
|
fatal_error: fatal,
|
|
101
|
+
model_capacity_error: modelCapacity,
|
|
102
|
+
capacity_fallback_hint: null,
|
|
95
103
|
repaired_tool_result_count: 0,
|
|
96
104
|
heartbeat_count: 0,
|
|
97
105
|
blockers
|
|
98
106
|
};
|
|
99
107
|
}
|
|
108
|
+
export function isCodexModelCapacityError(result, events) {
|
|
109
|
+
const text = [
|
|
110
|
+
String(result.finalResponse || ''),
|
|
111
|
+
...(Array.isArray(result.blockers) ? result.blockers : []),
|
|
112
|
+
...events.map((event) => [
|
|
113
|
+
event?.error?.message,
|
|
114
|
+
event?.message,
|
|
115
|
+
event?.item?.text,
|
|
116
|
+
event?.raw?.failed_event?.error?.message
|
|
117
|
+
].filter(Boolean).join('\n'))
|
|
118
|
+
].join('\n');
|
|
119
|
+
return /selected model is at capacity|model(?:\s+[\w.-]+)?\s+is\s+at\s+capacity|try a different model|capacity(?:\s+is)?\s+exhausted|temporarily at capacity/i.test(text);
|
|
120
|
+
}
|
|
100
121
|
export function repairToolCallSequence(events) {
|
|
101
122
|
const repaired = [...events];
|
|
102
123
|
const openToolCalls = new Set();
|
|
@@ -118,6 +118,8 @@ export async function runCodexTask(input) {
|
|
|
118
118
|
patchEnvelopePath,
|
|
119
119
|
blockers: finalBlockers,
|
|
120
120
|
reliabilityShield: adapterResult?.reliabilityShield || null,
|
|
121
|
+
capacityFallback: adapterResult?.reliabilityShield?.selected_model_capacity_fallback === true,
|
|
122
|
+
modelCapacityRetryCount: Number(adapterResult?.reliabilityShield?.model_capacity_retry_count || 0),
|
|
121
123
|
ultraRouterDecision: routerDecision,
|
|
122
124
|
outputSchemaId: task.outputSchemaId,
|
|
123
125
|
finalResponse: adapterResult?.finalResponse || '',
|
|
@@ -146,7 +148,11 @@ export async function runCodexTask(input) {
|
|
|
146
148
|
result,
|
|
147
149
|
capability: capability,
|
|
148
150
|
sandbox,
|
|
149
|
-
envProof:
|
|
151
|
+
envProof: {
|
|
152
|
+
...runtime.env.proof,
|
|
153
|
+
capacity_fallback_selected: result.capacityFallback === true,
|
|
154
|
+
model_capacity_retry_count: result.modelCapacityRetryCount
|
|
155
|
+
},
|
|
150
156
|
config: runtime.config,
|
|
151
157
|
reliabilityShield: adapterResult?.reliabilityShield || null,
|
|
152
158
|
routerDecision: routerDecision,
|
|
@@ -56,7 +56,7 @@ export function defaultModelCallBudget(provider) {
|
|
|
56
56
|
const text = String(provider || '');
|
|
57
57
|
if (text === 'local-llm' || text === 'ollama')
|
|
58
58
|
return envInt('SKS_LOCAL_LLM_MAX_PARALLEL_REQUESTS', 4);
|
|
59
|
-
return envInt('SKS_REMOTE_API_PARALLEL_BUDGET',
|
|
59
|
+
return envInt('SKS_REMOTE_API_PARALLEL_BUDGET', 3);
|
|
60
60
|
}
|
|
61
61
|
class ModelCallSemaphoreImpl {
|
|
62
62
|
provider;
|
|
@@ -1,98 +1,178 @@
|
|
|
1
|
-
import { initProject } from '../init.js';
|
|
2
|
-
import { createMission, findLatestMission, setCurrent } from '../mission.js';
|
|
3
|
-
import { exists, sksRoot } from '../fsx.js';
|
|
4
1
|
import path from 'node:path';
|
|
5
|
-
import {
|
|
2
|
+
import { initProject } from '../init.js';
|
|
3
|
+
import { findLatestMission, setCurrent } from '../mission.js';
|
|
4
|
+
import { exists, readText, sksRoot } from '../fsx.js';
|
|
5
|
+
import { closeMadDbCycle, isMadDbCapabilityActive, MAD_DB_ACK, readMadDbCapability, resolveMadDbMissionId, revokeMadDbCapability } from '../mad-db/mad-db-capability.js';
|
|
6
|
+
import { closeMadDbRuntimeProfile, verifyReadOnlyRestored } from '../mad-db/mad-db-runtime-profile.js';
|
|
7
|
+
import { runMadDbCycle } from '../mad-db/mad-db-coordinator.js';
|
|
8
|
+
import { resolveMadDbTarget } from '../mad-db/mad-db-target.js';
|
|
9
|
+
import { quarantineStaleMadDbRuntimeProfiles } from '../mad-db/mad-db-recovery.js';
|
|
10
|
+
import { sha256 } from '../fsx.js';
|
|
6
11
|
export async function madDbCommand(args = []) {
|
|
7
12
|
const action = String(args[0] && !String(args[0]).startsWith('--') ? args[0] : 'status');
|
|
8
13
|
const rest = action === args[0] ? args.slice(1) : args;
|
|
9
14
|
const root = await sksRoot();
|
|
10
15
|
if (!(await exists(path.join(root, '.sneakoscope'))))
|
|
11
16
|
await initProject(root, {});
|
|
17
|
+
if (action === 'run')
|
|
18
|
+
return runMadDb(root, rest);
|
|
19
|
+
if (action === 'exec')
|
|
20
|
+
return execMadDb(root, rest);
|
|
21
|
+
if (action === 'apply-migration')
|
|
22
|
+
return applyMigrationMadDb(root, rest);
|
|
23
|
+
if (action === 'doctor')
|
|
24
|
+
return doctorMadDb(root, rest);
|
|
25
|
+
if (action === 'close')
|
|
26
|
+
return closeMadDb(root, rest);
|
|
12
27
|
if (action === 'enable')
|
|
13
28
|
return enableMadDb(root, rest);
|
|
14
29
|
if (action === 'revoke')
|
|
15
30
|
return revokeMadDb(root, rest);
|
|
16
31
|
if (action === 'status')
|
|
17
32
|
return statusMadDb(root, rest);
|
|
18
|
-
console.error('Usage: sks mad-db
|
|
33
|
+
console.error('Usage: sks mad-db run "<task-or-sql>" | exec --sql "<SQL>" | apply-migration --name <name> --file <sql-file> | doctor|status|close|revoke [--json]');
|
|
19
34
|
process.exitCode = 1;
|
|
20
35
|
}
|
|
36
|
+
async function runMadDb(root, args) {
|
|
37
|
+
const task = positionalText(args) || readOption(args, '--task', '');
|
|
38
|
+
const sql = readOption(args, '--sql', '');
|
|
39
|
+
const result = await runMadDbCycle({
|
|
40
|
+
root,
|
|
41
|
+
action: 'run',
|
|
42
|
+
task,
|
|
43
|
+
sql: sql || null,
|
|
44
|
+
verifySql: readOption(args, '--verify-sql', '') || null,
|
|
45
|
+
args
|
|
46
|
+
});
|
|
47
|
+
return printResult(result, args);
|
|
48
|
+
}
|
|
49
|
+
async function execMadDb(root, args) {
|
|
50
|
+
const sql = readOption(args, '--sql', '') || positionalText(args);
|
|
51
|
+
const result = await runMadDbCycle({
|
|
52
|
+
root,
|
|
53
|
+
action: 'exec',
|
|
54
|
+
task: sql || 'sks mad-db exec',
|
|
55
|
+
sql: sql || null,
|
|
56
|
+
verifySql: readOption(args, '--verify-sql', '') || null,
|
|
57
|
+
args
|
|
58
|
+
});
|
|
59
|
+
return printResult(result, args);
|
|
60
|
+
}
|
|
61
|
+
async function applyMigrationMadDb(root, args) {
|
|
62
|
+
const file = readOption(args, '--file', '');
|
|
63
|
+
const sql = readOption(args, '--sql', '') || (file ? await readText(path.resolve(file), '') : '');
|
|
64
|
+
const result = await runMadDbCycle({
|
|
65
|
+
root,
|
|
66
|
+
action: 'apply-migration',
|
|
67
|
+
task: `apply migration ${readOption(args, '--name', 'mad_db_migration')}`,
|
|
68
|
+
sql: sql || null,
|
|
69
|
+
migrationName: readOption(args, '--name', `mad_db_${Date.now()}`),
|
|
70
|
+
migrationFile: file || null,
|
|
71
|
+
verifySql: readOption(args, '--verify-sql', '') || null,
|
|
72
|
+
args
|
|
73
|
+
});
|
|
74
|
+
return printResult(result, args);
|
|
75
|
+
}
|
|
76
|
+
async function doctorMadDb(root, args) {
|
|
77
|
+
const target = await resolveMadDbTarget(root, { args });
|
|
78
|
+
const recovery = await quarantineStaleMadDbRuntimeProfiles(root);
|
|
79
|
+
const restoration = await verifyReadOnlyRestored(root, null);
|
|
80
|
+
const result = {
|
|
81
|
+
schema: 'sks.mad-db-doctor.v1',
|
|
82
|
+
ok: target.blockers.length === 0 && restoration.persistent_supabase_read_only,
|
|
83
|
+
target: { ...target, project_ref: target.project_ref ? `<hash:${target.project_ref_hash}>` : null },
|
|
84
|
+
stale_recovery: recovery,
|
|
85
|
+
read_only_restoration: restoration,
|
|
86
|
+
execute_sql_apply_migration_inventory_checked: false,
|
|
87
|
+
note: 'doctor does not open write transport; run/exec/apply-migration verifies tool inventory inside a bound cycle'
|
|
88
|
+
};
|
|
89
|
+
return printJsonOrText(result, args, result.ok ? 'MadDB doctor passed local checks.' : `MadDB doctor found blockers: ${[...target.blockers, ...restoration.blockers].join(', ')}`);
|
|
90
|
+
}
|
|
21
91
|
async function enableMadDb(root, args) {
|
|
22
92
|
const json = hasFlag(args, '--json');
|
|
23
93
|
const ack = readOption(args, '--ack', '');
|
|
24
94
|
if (ack !== MAD_DB_ACK) {
|
|
25
|
-
const result = { schema: 'sks.mad-db-command.
|
|
95
|
+
const result = { schema: 'sks.mad-db-command.v2', ok: false, action: 'enable', reason: 'deprecated_enable_no_capability', required_ack: MAD_DB_ACK, token_only: true };
|
|
26
96
|
if (json)
|
|
27
97
|
return console.log(JSON.stringify(result, null, 2));
|
|
28
|
-
console.error(`
|
|
98
|
+
console.error(`MadDB enable is deprecated and does not create a capability. Use sks mad-db run|exec|apply-migration for an executable cycle. Legacy ack was ${JSON.stringify(MAD_DB_ACK)}.`);
|
|
29
99
|
process.exitCode = 2;
|
|
30
100
|
return result;
|
|
31
101
|
}
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
ack,
|
|
41
|
-
cwd: process.cwd(),
|
|
42
|
-
ttlMs: Number(readOption(args, '--ttl-ms', String(2 * 60 * 60 * 1000)))
|
|
43
|
-
});
|
|
44
|
-
await setCurrent(root, {
|
|
45
|
-
mission_id: missionId,
|
|
46
|
-
route: 'MadDB',
|
|
47
|
-
route_command: '$MAD-DB',
|
|
48
|
-
mode: 'MADDB',
|
|
49
|
-
phase: 'MADDB_ONE_CYCLE_CAPABILITY_ACTIVE',
|
|
50
|
-
mad_db_active: true,
|
|
51
|
-
mad_db_cycle_id: capability.cycle_id,
|
|
52
|
-
mad_db_capability_file: 'mad-db-capability.json',
|
|
53
|
-
mad_db_ack_phrase: 'accepted',
|
|
54
|
-
stop_gate: 'mad-db-capability.json'
|
|
55
|
-
});
|
|
56
|
-
const result = { schema: 'sks.mad-db-command.v1', ok: true, action: 'enable', mission_id: missionId, capability };
|
|
102
|
+
const result = {
|
|
103
|
+
schema: 'sks.mad-db-command.v2',
|
|
104
|
+
ok: false,
|
|
105
|
+
action: 'enable',
|
|
106
|
+
reason: 'deprecated_enable_no_capability',
|
|
107
|
+
token_only: true,
|
|
108
|
+
executable_commands: ['sks mad-db run', 'sks mad-db exec', 'sks mad-db apply-migration']
|
|
109
|
+
};
|
|
57
110
|
if (json)
|
|
58
111
|
return console.log(JSON.stringify(result, null, 2));
|
|
59
|
-
console.
|
|
112
|
+
console.error('MadDB enable no longer creates a capability. Use sks mad-db run|exec|apply-migration to create the bound mission/profile/capability and execute SQL.');
|
|
113
|
+
process.exitCode = 2;
|
|
60
114
|
return result;
|
|
61
115
|
}
|
|
62
116
|
async function statusMadDb(root, args) {
|
|
63
|
-
const json = hasFlag(args, '--json');
|
|
64
117
|
const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest'));
|
|
65
118
|
const capability = missionId ? await readMadDbCapability(root, missionId) : null;
|
|
66
119
|
const result = {
|
|
67
|
-
schema: 'sks.mad-db-
|
|
120
|
+
schema: 'sks.mad-db-status.v2',
|
|
68
121
|
ok: true,
|
|
69
122
|
action: 'status',
|
|
70
123
|
mission_id: missionId,
|
|
71
124
|
active: isMadDbCapabilityActive(capability),
|
|
72
|
-
capability
|
|
125
|
+
capability: capability ? redactCapability(capability) : null
|
|
73
126
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
127
|
+
return printJsonOrText(result, args, !missionId || !capability ? 'MadDB: no capability found.' : `MadDB: ${result.active ? 'active' : 'inactive'} for ${missionId}; status=${capability.status}; expires=${capability.expires_at}.`);
|
|
128
|
+
}
|
|
129
|
+
async function closeMadDb(root, args) {
|
|
130
|
+
const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest')) || await findLatestMission(root);
|
|
131
|
+
const capability = missionId ? await readMadDbCapability(root, missionId) : null;
|
|
132
|
+
const restoration = missionId ? await closeMadDbRuntimeProfile({ root, missionId, reason: 'operator_close' }) : null;
|
|
133
|
+
const closed = missionId && capability ? await closeMadDbCycle(root, missionId, capability.cycle_id, 'operator_close') : null;
|
|
134
|
+
if (missionId)
|
|
135
|
+
await setCurrent(root, { mad_db_active: false, phase: 'MADDB_CLOSED' });
|
|
136
|
+
const result = { schema: 'sks.mad-db-close.v2', ok: Boolean(closed), action: 'close', mission_id: missionId, capability: closed ? redactCapability(closed) : null, read_only_restoration: restoration };
|
|
137
|
+
return printJsonOrText(result, args, closed ? `MadDB cycle closed for ${missionId}.` : 'MadDB: no capability to close.');
|
|
81
138
|
}
|
|
82
139
|
async function revokeMadDb(root, args) {
|
|
83
|
-
const json = hasFlag(args, '--json');
|
|
84
140
|
const missionId = await resolveMadDbMissionId(root, {}, readOption(args, '--mission', 'latest')) || await findLatestMission(root);
|
|
85
141
|
const revoked = missionId ? await revokeMadDbCapability(root, missionId, readOption(args, '--reason', 'operator_revoked')) : null;
|
|
142
|
+
const restoration = missionId ? await closeMadDbRuntimeProfile({ root, missionId, reason: 'operator_revoke' }) : null;
|
|
86
143
|
await setCurrent(root, { mad_db_active: false, phase: 'MADDB_REVOKED' });
|
|
87
|
-
const result = { schema: 'sks.mad-db-command.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
144
|
+
const result = { schema: 'sks.mad-db-command.v2', ok: Boolean(revoked), action: 'revoke', mission_id: missionId, capability: revoked ? redactCapability(revoked) : null, read_only_restoration: restoration };
|
|
145
|
+
return printJsonOrText(result, args, revoked ? `MadDB capability revoked for ${missionId}.` : 'MadDB: no capability to revoke.');
|
|
146
|
+
}
|
|
147
|
+
function printResult(result, args) {
|
|
148
|
+
if (hasFlag(args, '--json')) {
|
|
149
|
+
console.log(JSON.stringify(result, null, 2));
|
|
150
|
+
}
|
|
151
|
+
else if (result.ok) {
|
|
152
|
+
console.log(`MadDB complete: mission=${result.mission_id} cycle=${result.cycle_id} execution=${result.execution?.ok ? 'succeeded' : 'unknown'} verification=${result.read_back?.ok === true ? 'passed' : 'not-requested'} read-only-restored=${result.read_only_restoration?.ok === true}`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.error(`MadDB failed: mission=${result.mission_id} blockers=${(result.blockers || []).join(', ') || 'unknown'} read-only-restored=${result.read_only_restoration?.ok === true}`);
|
|
156
|
+
process.exitCode = 1;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
function printJsonOrText(result, args, text) {
|
|
161
|
+
if (hasFlag(args, '--json'))
|
|
162
|
+
console.log(JSON.stringify(result, null, 2));
|
|
92
163
|
else
|
|
93
|
-
console.log(
|
|
164
|
+
console.log(text);
|
|
165
|
+
if (result.ok === false)
|
|
166
|
+
process.exitCode = process.exitCode || 1;
|
|
94
167
|
return result;
|
|
95
168
|
}
|
|
169
|
+
function redactCapability(capability) {
|
|
170
|
+
return {
|
|
171
|
+
...capability,
|
|
172
|
+
project_ref: capability.project_ref ? `<hash:${sha256(capability.project_ref).slice(0, 16)}>` : null,
|
|
173
|
+
transport: capability.transport ? { ...capability.transport, server_url_redacted: capability.transport.server_url_redacted || '<redacted>' } : null
|
|
174
|
+
};
|
|
175
|
+
}
|
|
96
176
|
function hasFlag(args, flag) {
|
|
97
177
|
return args.includes(flag);
|
|
98
178
|
}
|
|
@@ -100,7 +180,22 @@ function readOption(args, name, fallback) {
|
|
|
100
180
|
const index = args.indexOf(name);
|
|
101
181
|
if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
|
|
102
182
|
return String(args[index + 1]);
|
|
103
|
-
const prefixed = args.find((arg) => String(arg).startsWith(name
|
|
183
|
+
const prefixed = args.find((arg) => String(arg).startsWith(`${name}=`));
|
|
104
184
|
return prefixed ? prefixed.slice(name.length + 1) : fallback;
|
|
105
185
|
}
|
|
186
|
+
function positionalText(args) {
|
|
187
|
+
const out = [];
|
|
188
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
189
|
+
const arg = args[index];
|
|
190
|
+
if (!arg)
|
|
191
|
+
continue;
|
|
192
|
+
if (arg.startsWith('--')) {
|
|
193
|
+
if (!arg.includes('=') && args[index + 1] && !String(args[index + 1]).startsWith('--'))
|
|
194
|
+
index += 1;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
out.push(arg);
|
|
198
|
+
}
|
|
199
|
+
return out.join(' ').trim();
|
|
200
|
+
}
|
|
106
201
|
//# sourceMappingURL=mad-db-command.js.map
|