sneakoscope 1.0.5 → 1.0.7
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 +60 -2
- 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/build-manifest.json +7 -1
- package/dist/cli/install-helpers.d.ts +5 -0
- package/dist/cli/install-helpers.js +160 -12
- package/dist/commands/codex-lb.js +138 -6
- package/dist/commands/wiki.d.ts +1 -1
- package/dist/core/codex-compat/codex-compat-report.d.ts +7 -0
- package/dist/core/codex-compat/codex-compat-report.js +1 -0
- package/dist/core/codex-compat/codex-hook-issues.d.ts +20 -0
- package/dist/core/codex-compat/codex-hook-issues.js +93 -0
- package/dist/core/codex-compat/codex-hook-schema.d.ts +3 -0
- package/dist/core/codex-compat/codex-hook-schema.js +7 -4
- package/dist/core/codex-compat/codex-hook-semantic-validator.d.ts +5 -1
- package/dist/core/codex-compat/codex-hook-semantic-validator.js +84 -71
- package/dist/core/codex-compat/codex-hook-warning-detector.d.ts +6 -0
- package/dist/core/codex-compat/codex-hook-warning-detector.js +46 -27
- package/dist/core/codex-lb/codex-lb-setup.d.ts +65 -0
- package/dist/core/codex-lb/codex-lb-setup.js +170 -0
- package/dist/core/commands/computer-use-command.js +39 -2
- package/dist/core/commands/wiki-command.d.ts +2 -2
- package/dist/core/computer-use-live-evidence.d.ts +109 -0
- package/dist/core/computer-use-live-evidence.js +276 -0
- package/dist/core/computer-use-status.d.ts +31 -0
- package/dist/core/computer-use-status.js +65 -0
- package/dist/core/evidence/evidence-router.js +10 -0
- package/dist/core/fsx.d.ts +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/proof/evidence-collector.d.ts +1 -1
- package/dist/core/proof/route-finalizer.js +29 -2
- package/dist/core/triwiki-wrongness/wrongness-cli.d.ts +2 -2
- package/dist/core/triwiki-wrongness/wrongness-ledger.js +3 -3
- package/dist/core/triwiki-wrongness/wrongness-proof-linker.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-retrieval.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-schema.d.ts +1 -1
- package/dist/core/triwiki-wrongness/wrongness-schema.js +17 -2
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -4,7 +4,11 @@ Fast legacy-free proof-first Codex trust layer with image-based Voxel TriWiki.
|
|
|
4
4
|
|
|
5
5
|
Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable.
|
|
6
6
|
|
|
7
|
-
SKS **1.0.
|
|
7
|
+
SKS **1.0.7** is the Ultimate Final Completion seal for the Codex trust harness: Computer Use live evidence is an opt-in, local-only macOS evidence path with explicit `probe_only`, `live_capture_attempted`, `live_capture_success`, and `live_capture_blocked` modes; `codex-lb setup` reports durable persistence versus `process_only_ephemeral` honestly; and docs/release readiness checks block mock/probe/live overclaims.
|
|
8
|
+
|
|
9
|
+
SKS **1.0.6** is the final precision polish for the Codex trust harness: hook compatibility is classified as upstream schema plus an SKS zero-warning strict subset, `sks codex-lb setup` previews and applies the exact choices the user selected, and Computer Use has an optional live smoke surface for macOS capability/evidence status.
|
|
10
|
+
|
|
11
|
+
SKS **1.0.5** sealed the prior trust harness: hook outputs were validated against both vendored OpenAI Codex CLI `rust-v0.131.0` schemas and runtime semantic parser rules, codex-lb setup survived macOS user-session launches through env-file/Keychain/launchctl-aware repair surfaces, and Computer Use became the preferred macOS visual evidence capability when available.
|
|
8
12
|
|
|
9
13
|
SKS **1.0.4** introduced the `rust-v0.131.0` schema snapshot, guided codex-lb setup path, and Computer Use/MAD-SKS separation that 1.0.5 now hardens into release gates.
|
|
10
14
|
|
|
@@ -18,8 +22,62 @@ Hybrid-free **`1.0.1`** already delivered the CLI entrypoint/router/registry/Tru
|
|
|
18
22
|
|
|
19
23
|
SKS does not try to clone every other harness. It focuses on one thing: making Codex work auditable, visual-evidence-bound, safety-gated, and reproducible through Completion Proof.
|
|
20
24
|
|
|
21
|
-

|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## 1.0.7 Ultimate Final Completion
|
|
29
|
+
|
|
30
|
+
1.0.7 does not add a new route, skill, or competing harness. It closes the last trust gaps around live visual evidence, persistence truth, and release documentation.
|
|
31
|
+
|
|
32
|
+
Computer Use live evidence stays optional and explicit:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
sks computer-use smoke --json
|
|
36
|
+
sks computer-use smoke --real --capture-screenshot --json
|
|
37
|
+
sks computer-use smoke --real --require-real --json
|
|
38
|
+
npm run computer-use:live-evidence
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Default smoke is `probe_only` and never attempts screen capture. Real mode records `live_capture_attempted`, `live_capture_success`, or `live_capture_blocked`; if Codex App, macOS permission, or the official Computer Use capture surface is unavailable, SKS writes a structured blocker instead of fabricated evidence. Browser Use evidence and manual screenshots remain separate sources. Computer Use screenshots are local-only by default, carry SHA-256 metadata, and link to Image Voxel only when a mission-local anchor can be made.
|
|
42
|
+
|
|
43
|
+
codex-lb setup now reports the exact persistence truth:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --plan --json
|
|
47
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --yes --no-env-file --no-keychain --no-launchctl --shell-profile skip --json
|
|
48
|
+
npm run codex-lb:persistence-truth
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Durable modes are `durable_env_file`, `durable_keychain`, `durable_launchctl`, and `shell_profile`. If all durable modes are disabled, setup is classified as `process_only_ephemeral`, emits `next_shell_requires_setup_or_env`, and warns that Codex App GUI launches may not see credentials. Use `sks codex-lb setup --write-env-file --keychain --launchctl` to recover durable persistence.
|
|
22
52
|
|
|
53
|
+
Documentation truthfulness is now a release invariant:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run docs:truthfulness
|
|
57
|
+
npm run release:readiness
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The hook compatibility surface remains the upstream schema plus the SKS zero-warning strict subset; SKS does not claim to mirror every upstream runtime parser rule or guarantee universal Computer Use availability.
|
|
61
|
+
|
|
62
|
+
## 1.0.6 Final Precision Polish
|
|
63
|
+
|
|
64
|
+
SKS validates Codex hooks against the OpenAI Codex `rust-v0.131.0` schema and enforces a stricter SKS zero-warning subset. Some fields may be accepted by upstream but are intentionally disallowed by SKS to avoid user-facing hook warnings and release drift. `sks hooks warning-check --json` now reports `schema_violation`, `upstream_semantic_unsupported`, `sks_zero_warning_disallowed`, `legacy_shape`, and `policy_disallowed` category counts.
|
|
65
|
+
|
|
66
|
+
`sks codex-lb setup` is now a two-phase plan/apply wizard. Every question maps to an actual action: provider selection, env file writing, Keychain storage, launchctl sync, shell profile snippets, and health checks.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --plan --json
|
|
70
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --yes --no-default-provider --no-env-file --json
|
|
71
|
+
npm run codex-lb:setup-truthfulness
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Computer Use live validation is optional and opt-in. On macOS, `SKS_TEST_REAL_COMPUTER_USE=1 sks computer-use smoke --real --json` attempts a non-destructive capability/evidence check. If Codex App or macOS denies the capability, SKS records a structured blocker and does not fabricate visual evidence.
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
sks computer-use smoke --json
|
|
78
|
+
SKS_TEST_REAL_COMPUTER_USE=1 sks computer-use smoke --real --json
|
|
79
|
+
npm run computer-use:live-optional
|
|
80
|
+
```
|
|
23
81
|
|
|
24
82
|
## 1.0.5 Ultimate Harness Seal
|
|
25
83
|
|
|
@@ -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 1.0.
|
|
7
|
+
Some("--version") => println!("sks-rs 1.0.7"),
|
|
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
package/dist/build-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.dist-build.v2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.7",
|
|
4
4
|
"typescript": true,
|
|
5
5
|
"mjs_runtime_files": 0,
|
|
6
6
|
"files": [
|
|
@@ -188,6 +188,8 @@
|
|
|
188
188
|
"core/codex-compat/codex-compat-report.js",
|
|
189
189
|
"core/codex-compat/codex-config-policy.d.ts",
|
|
190
190
|
"core/codex-compat/codex-config-policy.js",
|
|
191
|
+
"core/codex-compat/codex-hook-issues.d.ts",
|
|
192
|
+
"core/codex-compat/codex-hook-issues.js",
|
|
191
193
|
"core/codex-compat/codex-hook-output-builders.d.ts",
|
|
192
194
|
"core/codex-compat/codex-hook-output-builders.js",
|
|
193
195
|
"core/codex-compat/codex-hook-output-normalizer.d.ts",
|
|
@@ -208,6 +210,8 @@
|
|
|
208
210
|
"core/codex-lb-circuit.js",
|
|
209
211
|
"core/codex-lb/codex-lb-env.d.ts",
|
|
210
212
|
"core/codex-lb/codex-lb-env.js",
|
|
213
|
+
"core/codex-lb/codex-lb-setup.d.ts",
|
|
214
|
+
"core/codex-lb/codex-lb-setup.js",
|
|
211
215
|
"core/codex-model-guard.d.ts",
|
|
212
216
|
"core/codex-model-guard.js",
|
|
213
217
|
"core/commands/autoresearch-command.d.ts",
|
|
@@ -282,6 +286,8 @@
|
|
|
282
286
|
"core/commands/wiki-command.js",
|
|
283
287
|
"core/commands/wrongness-command.d.ts",
|
|
284
288
|
"core/commands/wrongness-command.js",
|
|
289
|
+
"core/computer-use-live-evidence.d.ts",
|
|
290
|
+
"core/computer-use-live-evidence.js",
|
|
285
291
|
"core/computer-use-status.d.ts",
|
|
286
292
|
"core/computer-use-status.js",
|
|
287
293
|
"core/context7-client.d.ts",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CodexLbPersistenceSummary } from '../core/codex-lb/codex-lb-setup.js';
|
|
1
2
|
type CodexLbStatusSnapshot = Awaited<ReturnType<typeof codexLbStatus>>;
|
|
2
3
|
/** Install-time shim reconciliation; fields vary by `status`. */
|
|
3
4
|
export type SksPostinstallShimResult = {
|
|
@@ -56,6 +57,10 @@ export type CodexLbAuthInstallResult = {
|
|
|
56
57
|
export type ConfigureCodexLbResult = {
|
|
57
58
|
ok?: boolean;
|
|
58
59
|
status: string;
|
|
60
|
+
plan?: Record<string, unknown>;
|
|
61
|
+
applied_actions?: Array<Record<string, unknown>>;
|
|
62
|
+
drift?: string[];
|
|
63
|
+
persistence?: CodexLbPersistenceSummary;
|
|
59
64
|
config_path?: string;
|
|
60
65
|
env_path?: string;
|
|
61
66
|
metadata_path?: string;
|
|
@@ -12,6 +12,7 @@ import { codexLaunchCommand, platformTmuxInstallHint, tmuxReadiness, tmuxReadine
|
|
|
12
12
|
import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
|
|
13
13
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
14
14
|
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
15
|
+
import { buildCodexLbSetupPlan, codexLbPersistenceSummary, installCodexLbShellProfileSnippet, selectedCodexLbPersistenceModes } from '../core/codex-lb/codex-lb-setup.js';
|
|
15
16
|
const DEFAULT_CODEX_APP_PLUGINS = [
|
|
16
17
|
['browser', 'openai-bundled'],
|
|
17
18
|
['chrome', 'openai-bundled'],
|
|
@@ -307,8 +308,33 @@ export async function configureCodexLb(opts = {}) {
|
|
|
307
308
|
const rawHost = String(opts.host || opts.baseUrl || '');
|
|
308
309
|
const baseUrl = normalizeCodexLbBaseUrl(rawHost);
|
|
309
310
|
const apiKey = String(opts.apiKey || '').trim();
|
|
311
|
+
const useDefaultProvider = opts.useDefaultProvider !== false;
|
|
312
|
+
const writeEnvFile = opts.writeEnvFile !== false;
|
|
313
|
+
const storeKeychain = opts.storeKeychain === true || opts.keychain === true;
|
|
314
|
+
const syncLaunchctl = opts.syncLaunchctl !== false && opts.syncLaunchEnv !== false;
|
|
315
|
+
const shellProfile = opts.shellProfile || 'skip';
|
|
316
|
+
const setupAnswers = {
|
|
317
|
+
host_or_base_url: rawHost,
|
|
318
|
+
api_key_source: opts.apiKeySource || 'cli_option',
|
|
319
|
+
use_as_default_provider: useDefaultProvider,
|
|
320
|
+
write_env_file: writeEnvFile,
|
|
321
|
+
store_keychain: storeKeychain,
|
|
322
|
+
sync_launchctl: syncLaunchctl,
|
|
323
|
+
install_shell_profile: shellProfile,
|
|
324
|
+
run_health_check: opts.runHealth === true,
|
|
325
|
+
allow_insecure_localhost: opts.allowInsecureHttp === true || opts.allowInsecureLocalhost === true
|
|
326
|
+
};
|
|
327
|
+
const selectedPersistenceModes = selectedCodexLbPersistenceModes(setupAnswers);
|
|
328
|
+
const plan = buildCodexLbSetupPlan(setupAnswers, {
|
|
329
|
+
home,
|
|
330
|
+
configPath,
|
|
331
|
+
envPath,
|
|
332
|
+
metadataPath: opts.metadataPath || codexLbMetadataPath(home)
|
|
333
|
+
});
|
|
310
334
|
if (!baseUrl)
|
|
311
335
|
return { ok: false, status: 'missing_host_or_base_url', config_path: configPath, env_path: envPath };
|
|
336
|
+
if (plan.blockers.length)
|
|
337
|
+
return { ok: false, status: 'plan_blocked', plan: plan, drift: plan.blockers, config_path: configPath, env_path: envPath };
|
|
312
338
|
if (/[\u0000-\u001f\u007f\s]/.test(rawHost.trim()))
|
|
313
339
|
return { ok: false, status: 'invalid_host_or_base_url', config_path: configPath, env_path: envPath, error: 'host_or_base_url_contains_whitespace_or_control_character' };
|
|
314
340
|
if (!apiKey)
|
|
@@ -316,12 +342,22 @@ export async function configureCodexLb(opts = {}) {
|
|
|
316
342
|
const insecureLocalWarning = /^http:\/\//i.test(baseUrl) && !/^http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/i.test(baseUrl) && !opts.allowInsecureHttp
|
|
317
343
|
? ['codex-lb base URL uses http outside localhost; prefer https or pass an explicit allow flag in the calling surface.']
|
|
318
344
|
: [];
|
|
345
|
+
const beforeState = await captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile });
|
|
346
|
+
const appliedActions = [];
|
|
319
347
|
await ensureDir(path.dirname(configPath));
|
|
320
348
|
const current = await readText(configPath, '');
|
|
321
|
-
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl));
|
|
349
|
+
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl, useDefaultProvider));
|
|
322
350
|
await writeTextAtomic(configPath, next);
|
|
323
|
-
|
|
324
|
-
|
|
351
|
+
appliedActions.push({ type: 'write_config_provider', target: configPath, ok: true });
|
|
352
|
+
if (useDefaultProvider)
|
|
353
|
+
appliedActions.push({ type: 'select_default_provider', target: configPath, ok: true });
|
|
354
|
+
if (writeEnvFile) {
|
|
355
|
+
await writeTextAtomic(envPath, `export CODEX_LB_BASE_URL=${shellSingleQuote(baseUrl)}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`);
|
|
356
|
+
await fsp.chmod(envPath, 0o600).catch(() => { });
|
|
357
|
+
appliedActions.push({ type: 'write_env_file', target: envPath, ok: true });
|
|
358
|
+
}
|
|
359
|
+
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
360
|
+
process.env.CODEX_LB_API_KEY = apiKey;
|
|
325
361
|
const keyFingerprint = await sha256Text(apiKey);
|
|
326
362
|
const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
|
|
327
363
|
await writeTextAtomic(metadataPath, `${JSON.stringify({
|
|
@@ -332,23 +368,67 @@ export async function configureCodexLb(opts = {}) {
|
|
|
332
368
|
api_key: { redacted: true, sha256: keyFingerprint }
|
|
333
369
|
}, null, 2)}\n`);
|
|
334
370
|
await fsp.chmod(metadataPath, 0o600).catch(() => { });
|
|
335
|
-
|
|
336
|
-
const
|
|
371
|
+
appliedActions.push({ type: 'write_metadata', target: metadataPath, ok: true });
|
|
372
|
+
const keychain = storeKeychain ? await writeCodexLbKeychain(apiKey, opts).catch((err) => ({ ok: false, status: 'keychain_store_failed', error: err.message })) : { ok: false, status: 'skipped' };
|
|
373
|
+
if (storeKeychain)
|
|
374
|
+
appliedActions.push({ type: 'store_keychain', target: 'macOS Keychain service sks-codex-lb', ok: keychain.ok === true, status: keychain.status });
|
|
375
|
+
const codexEnvironment = await syncCodexLbProviderEnvironment({ env_path: envPath, base_url: baseUrl }, { ...opts, home, apiKey, baseUrl, syncLaunchEnv: syncLaunchctl });
|
|
376
|
+
if (syncLaunchctl)
|
|
377
|
+
appliedActions.push({ type: 'sync_launchctl', target: 'macOS launchctl user environment', ok: codexEnvironment.ok === true, status: codexEnvironment.status });
|
|
378
|
+
const shellProfileResult = await installCodexLbShellProfileSnippet({ home, envPath, shellProfile }).catch((err) => ({ ok: false, status: 'failed', files: [], error: err.message }));
|
|
379
|
+
if (shellProfile !== 'skip')
|
|
380
|
+
appliedActions.push({ type: 'install_shell_profile_snippet', target: shellProfileResult.files?.join(', ') || shellProfile, ok: shellProfileResult.ok === true, status: shellProfileResult.status });
|
|
337
381
|
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home, force: true });
|
|
338
382
|
const codexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
339
383
|
const authReconcile = await reconcileCodexLbAuthConflict({ ...opts, home, status: codexLb }).catch((err) => ({ status: 'failed', reason: 'exception', error: err.message }));
|
|
340
384
|
const finalCodexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
341
385
|
const ok = Boolean(codexEnvironment.ok && codexLogin.ok);
|
|
386
|
+
const afterState = await captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile });
|
|
387
|
+
const drift = detectCodexLbSetupDrift({
|
|
388
|
+
useDefaultProvider,
|
|
389
|
+
writeEnvFile,
|
|
390
|
+
storeKeychain,
|
|
391
|
+
syncLaunchctl,
|
|
392
|
+
shellProfile,
|
|
393
|
+
selected: finalCodexLb.selected,
|
|
394
|
+
envFile: finalCodexLb.env_file,
|
|
395
|
+
keychain,
|
|
396
|
+
codexEnvironment,
|
|
397
|
+
shellProfileResult,
|
|
398
|
+
beforeState,
|
|
399
|
+
afterState
|
|
400
|
+
});
|
|
401
|
+
const appliedPersistenceModes = appliedCodexLbPersistenceModes({
|
|
402
|
+
writeEnvFile,
|
|
403
|
+
storeKeychain,
|
|
404
|
+
syncLaunchctl,
|
|
405
|
+
shellProfile,
|
|
406
|
+
envFile: finalCodexLb.env_file,
|
|
407
|
+
keychain,
|
|
408
|
+
codexEnvironment,
|
|
409
|
+
shellProfileResult,
|
|
410
|
+
apiKeySource: finalCodexLb.env_loader?.api_key?.source || null
|
|
411
|
+
});
|
|
412
|
+
const persistence = codexLbPersistenceSummary({
|
|
413
|
+
selectedModes: selectedPersistenceModes,
|
|
414
|
+
appliedModes: appliedPersistenceModes,
|
|
415
|
+
processOnly: appliedPersistenceModes.includes('process_only_ephemeral')
|
|
416
|
+
});
|
|
417
|
+
const warnings = [...insecureLocalWarning, ...persistence.warnings];
|
|
342
418
|
return {
|
|
343
|
-
ok,
|
|
344
|
-
status: ok ? 'configured' : (codexEnvironment.status || codexLogin.status),
|
|
419
|
+
ok: ok && drift.length === 0,
|
|
420
|
+
status: ok && drift.length === 0 ? 'configured' : drift.length ? 'setup_choice_drift' : (codexEnvironment.status || codexLogin.status),
|
|
421
|
+
plan: plan,
|
|
422
|
+
applied_actions: appliedActions,
|
|
423
|
+
drift,
|
|
424
|
+
persistence,
|
|
345
425
|
config_path: configPath,
|
|
346
426
|
env_path: envPath,
|
|
347
427
|
metadata_path: metadataPath,
|
|
348
428
|
base_url: baseUrl,
|
|
349
429
|
env_key: 'CODEX_LB_API_KEY',
|
|
350
430
|
keychain,
|
|
351
|
-
warnings
|
|
431
|
+
warnings,
|
|
352
432
|
auth_reconcile: authReconcile,
|
|
353
433
|
codex_lb: finalCodexLb,
|
|
354
434
|
codex_environment: codexEnvironment,
|
|
@@ -1188,10 +1268,10 @@ async function syncCodexLbProviderEnvironment(status = {}, opts = {}) {
|
|
|
1188
1268
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
1189
1269
|
const envPath = opts.envPath || status.env_path || codexLbEnvPath(home);
|
|
1190
1270
|
const envText = await readText(envPath, '');
|
|
1191
|
-
const apiKey = parseCodexLbEnvKey(envText);
|
|
1271
|
+
const apiKey = String(opts.apiKey || '').trim() || parseCodexLbEnvKey(envText);
|
|
1192
1272
|
if (!apiKey)
|
|
1193
1273
|
return { ok: false, status: 'missing_env_key' };
|
|
1194
|
-
const baseUrl = status.base_url || parseCodexLbEnvBaseUrl(envText);
|
|
1274
|
+
const baseUrl = status.base_url || opts.baseUrl || parseCodexLbEnvBaseUrl(envText);
|
|
1195
1275
|
process.env.CODEX_LB_API_KEY = apiKey;
|
|
1196
1276
|
if (baseUrl)
|
|
1197
1277
|
process.env.CODEX_LB_BASE_URL = baseUrl;
|
|
@@ -1258,8 +1338,10 @@ async function syncCodexApiKeyLogin(apiKey, opts = {}) {
|
|
|
1258
1338
|
return { ok: true, status: 'synced' };
|
|
1259
1339
|
return { ok: false, status: 'login_failed', error: redactSecretText(login.stderr || login.stdout || 'codex login failed', [apiKey]).trim() };
|
|
1260
1340
|
}
|
|
1261
|
-
function upsertCodexLbConfig(text = '', baseUrl) {
|
|
1262
|
-
let next =
|
|
1341
|
+
function upsertCodexLbConfig(text = '', baseUrl, selectDefault = true) {
|
|
1342
|
+
let next = selectDefault
|
|
1343
|
+
? upsertTopLevelTomlString(text, 'model_provider', 'codex-lb')
|
|
1344
|
+
: removeTopLevelTomlKeyIfValue(text, 'model_provider', 'codex-lb');
|
|
1263
1345
|
const block = [
|
|
1264
1346
|
'[model_providers.codex-lb]',
|
|
1265
1347
|
'name = "OpenAI"',
|
|
@@ -1272,6 +1354,72 @@ function upsertCodexLbConfig(text = '', baseUrl) {
|
|
|
1272
1354
|
next = upsertTomlTable(next, 'model_providers.codex-lb', block);
|
|
1273
1355
|
return `${next.trim()}\n`;
|
|
1274
1356
|
}
|
|
1357
|
+
function detectCodexLbSetupDrift(state = {}) {
|
|
1358
|
+
const drift = [];
|
|
1359
|
+
if (state.useDefaultProvider && state.selected !== true)
|
|
1360
|
+
drift.push('default_provider_not_selected');
|
|
1361
|
+
if (!state.useDefaultProvider && state.selected === true)
|
|
1362
|
+
drift.push('default_provider_selected_despite_no_default_provider');
|
|
1363
|
+
if (state.writeEnvFile && state.envFile !== true)
|
|
1364
|
+
drift.push('env_file_not_written');
|
|
1365
|
+
if (!state.writeEnvFile && state.beforeState && state.afterState && state.beforeState.envHash !== state.afterState.envHash)
|
|
1366
|
+
drift.push('env_file_changed_despite_no_env_file');
|
|
1367
|
+
if (!state.writeEnvFile && !state.beforeState && state.envFile === true)
|
|
1368
|
+
drift.push('env_file_written_despite_no_env_file');
|
|
1369
|
+
if (!state.storeKeychain && state.keychain?.status && state.keychain.status !== 'skipped')
|
|
1370
|
+
drift.push('keychain_touched_despite_no_keychain');
|
|
1371
|
+
if (!state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
1372
|
+
drift.push('launchctl_synced_despite_no_launchctl');
|
|
1373
|
+
if (state.shellProfile === 'skip' && state.shellProfileResult?.status === 'installed')
|
|
1374
|
+
drift.push('shell_profile_written_despite_skip');
|
|
1375
|
+
if (state.shellProfile === 'skip' && state.beforeState && state.afterState && state.beforeState.profileHash !== state.afterState.profileHash)
|
|
1376
|
+
drift.push('shell_profile_changed_despite_skip');
|
|
1377
|
+
return drift;
|
|
1378
|
+
}
|
|
1379
|
+
async function captureCodexLbSetupWriteState({ home, configPath, envPath, shellProfile } = {}) {
|
|
1380
|
+
const profileFiles = profileFilesForDrift(home, shellProfile);
|
|
1381
|
+
return {
|
|
1382
|
+
configHash: await fileHashOrMissing(configPath),
|
|
1383
|
+
envHash: await fileHashOrMissing(envPath),
|
|
1384
|
+
profileHash: (await Promise.all(profileFiles.map((file) => fileHashOrMissing(file)))).join('|')
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
async function fileHashOrMissing(file) {
|
|
1388
|
+
const text = await readText(file, null).catch(() => null);
|
|
1389
|
+
return text === null ? 'missing' : await sha256Text(String(text));
|
|
1390
|
+
}
|
|
1391
|
+
function profileFilesForDrift(home, shellProfile) {
|
|
1392
|
+
const targets = {
|
|
1393
|
+
zsh: path.join(home, '.zshrc'),
|
|
1394
|
+
bash: path.join(home, '.bashrc'),
|
|
1395
|
+
fish: path.join(home, '.config', 'fish', 'config.fish')
|
|
1396
|
+
};
|
|
1397
|
+
if (shellProfile === 'zsh')
|
|
1398
|
+
return [targets.zsh];
|
|
1399
|
+
if (shellProfile === 'bash')
|
|
1400
|
+
return [targets.bash];
|
|
1401
|
+
if (shellProfile === 'fish')
|
|
1402
|
+
return [targets.fish];
|
|
1403
|
+
if (shellProfile === 'all')
|
|
1404
|
+
return [targets.zsh, targets.bash, targets.fish];
|
|
1405
|
+
return [targets.zsh, targets.bash, targets.fish];
|
|
1406
|
+
}
|
|
1407
|
+
function appliedCodexLbPersistenceModes(state = {}) {
|
|
1408
|
+
const modes = [];
|
|
1409
|
+
if (state.writeEnvFile && state.envFile === true)
|
|
1410
|
+
modes.push('durable_env_file');
|
|
1411
|
+
if (state.storeKeychain && state.keychain?.ok === true)
|
|
1412
|
+
modes.push('durable_keychain');
|
|
1413
|
+
if (state.syncLaunchctl && state.codexEnvironment?.launch_environment?.status === 'synced')
|
|
1414
|
+
modes.push('durable_launchctl');
|
|
1415
|
+
if (state.shellProfile !== 'skip' && state.shellProfileResult?.status === 'installed')
|
|
1416
|
+
modes.push('shell_profile');
|
|
1417
|
+
if (!modes.length && state.apiKeySource === 'process.env')
|
|
1418
|
+
modes.push('process_only_ephemeral');
|
|
1419
|
+
if (!modes.length)
|
|
1420
|
+
modes.push('none');
|
|
1421
|
+
return modes;
|
|
1422
|
+
}
|
|
1275
1423
|
export async function ensureGlobalCodexFastModeDuringInstall(opts = {}) {
|
|
1276
1424
|
if (process.env.SKS_SKIP_CODEX_FAST_MODE_REPAIR === '1')
|
|
1277
1425
|
return { status: 'skipped', reason: 'SKS_SKIP_CODEX_FAST_MODE_REPAIR=1' };
|
|
@@ -6,6 +6,7 @@ import { flag, readOption } from '../cli/args.js';
|
|
|
6
6
|
import { printJson } from '../cli/output.js';
|
|
7
7
|
import { codexLbMetrics, readCodexLbCircuit, recordCodexLbHealthEvent, resetCodexLbCircuit, codexLbProofEvidence } from '../core/codex-lb-circuit.js';
|
|
8
8
|
import { checkCodexLbResponseChain, codexLbStatus, configureCodexLb, formatCodexLbStatusText, releaseCodexLbAuthHold, repairCodexLbAuth, unselectCodexLbProvider } from '../cli/install-helpers.js';
|
|
9
|
+
import { buildCodexLbSetupPlan, codexLbPersistenceSummary, renderCodexLbSetupPlan } from '../core/codex-lb/codex-lb-setup.js';
|
|
9
10
|
export async function run(command, args = []) {
|
|
10
11
|
const root = await projectRoot();
|
|
11
12
|
const action = args[0] || 'status';
|
|
@@ -77,6 +78,17 @@ export async function run(command, args = []) {
|
|
|
77
78
|
}
|
|
78
79
|
if (action === 'setup' || action === 'reconfigure') {
|
|
79
80
|
const options = await codexLbSetupOptions(args);
|
|
81
|
+
const plan = buildCodexLbSetupPlan({
|
|
82
|
+
host_or_base_url: options.host || '',
|
|
83
|
+
api_key_source: options.apiKeySource,
|
|
84
|
+
use_as_default_provider: options.useDefaultProvider,
|
|
85
|
+
write_env_file: options.writeEnvFile,
|
|
86
|
+
store_keychain: options.keychain,
|
|
87
|
+
sync_launchctl: options.syncLaunchctl,
|
|
88
|
+
install_shell_profile: options.shellProfile,
|
|
89
|
+
run_health_check: options.health,
|
|
90
|
+
allow_insecure_localhost: options.allowInsecureLocalhost
|
|
91
|
+
});
|
|
80
92
|
if (!options.host || !options.apiKey) {
|
|
81
93
|
const result = {
|
|
82
94
|
schema: 'sks.codex-lb-setup.v1',
|
|
@@ -98,13 +110,84 @@ export async function run(command, args = []) {
|
|
|
98
110
|
process.exitCode = 1;
|
|
99
111
|
return;
|
|
100
112
|
}
|
|
101
|
-
|
|
113
|
+
if (flag(args, '--plan')) {
|
|
114
|
+
const result = { schema: 'sks.codex-lb-setup-plan-result.v1', ok: plan.blockers.length === 0, plan, writes: false, expected_actions: plan.expected_actions, persistence: plan.persistence };
|
|
115
|
+
if (flag(args, '--json'))
|
|
116
|
+
return printJson(result);
|
|
117
|
+
process.stdout.write(renderCodexLbSetupPlan(plan));
|
|
118
|
+
if (!result.ok)
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const processOnly = plan.persistence.effective_mode === 'process_only_ephemeral';
|
|
123
|
+
if (options.interactive && !options.yes) {
|
|
124
|
+
process.stdout.write(renderCodexLbSetupPlan(plan));
|
|
125
|
+
const confirm = (await ask('Apply this codex-lb setup plan? [y/N] ')).trim();
|
|
126
|
+
if (!/^(y|yes|예|네|응)$/i.test(confirm)) {
|
|
127
|
+
const result = { schema: 'sks.codex-lb-setup.v1', ok: false, status: 'cancelled', plan, applied_actions: [] };
|
|
128
|
+
if (flag(args, '--json'))
|
|
129
|
+
return printJson(result);
|
|
130
|
+
console.log('codex-lb setup cancelled.');
|
|
131
|
+
process.exitCode = 1;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (processOnly) {
|
|
135
|
+
const confirmProcessOnly = (await ask('This setup keeps credentials only in the current process. Type process-only to continue: ')).trim();
|
|
136
|
+
if (confirmProcessOnly !== 'process-only') {
|
|
137
|
+
const result = { schema: 'sks.codex-lb-setup.v1', ok: false, status: 'process_only_cancelled', plan, applied_actions: [], persistence: plan.persistence };
|
|
138
|
+
if (flag(args, '--json')) {
|
|
139
|
+
printJson(result);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
console.log('codex-lb setup cancelled: process-only ephemeral setup was not confirmed.');
|
|
144
|
+
process.exitCode = 1;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (processOnly && !options.yes) {
|
|
150
|
+
const result = {
|
|
151
|
+
schema: 'sks.codex-lb-setup.v1',
|
|
152
|
+
ok: false,
|
|
153
|
+
status: 'process_only_requires_yes',
|
|
154
|
+
plan,
|
|
155
|
+
applied_actions: [],
|
|
156
|
+
persistence: plan.persistence,
|
|
157
|
+
guidance: ['Pass --yes to acknowledge process_only_ephemeral setup, or enable --write-env-file, --keychain, --launchctl, or --shell-profile.']
|
|
158
|
+
};
|
|
159
|
+
if (flag(args, '--json')) {
|
|
160
|
+
printJson(result);
|
|
161
|
+
process.exitCode = 1;
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
console.error('codex-lb setup would be process-only ephemeral. Pass --yes to acknowledge, or enable a durable persistence mode.');
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const result = await configureCodexLb({
|
|
169
|
+
host: options.host,
|
|
170
|
+
apiKey: options.apiKey,
|
|
171
|
+
keychain: options.keychain,
|
|
172
|
+
storeKeychain: options.keychain,
|
|
173
|
+
useDefaultProvider: options.useDefaultProvider,
|
|
174
|
+
writeEnvFile: options.writeEnvFile,
|
|
175
|
+
syncLaunchctl: options.syncLaunchctl,
|
|
176
|
+
shellProfile: options.shellProfile,
|
|
177
|
+
runHealth: options.health,
|
|
178
|
+
apiKeySource: options.apiKeySource,
|
|
179
|
+
allowInsecureHttp: options.allowInsecureLocalhost
|
|
180
|
+
});
|
|
102
181
|
const shaped = { schema: 'sks.codex-lb-setup.v1', ...result, api_key: { present: Boolean(options.apiKey), redacted: true }, env_file_chmod: '0600' };
|
|
182
|
+
if (options.health)
|
|
183
|
+
shaped.applied_actions = [...(shaped.applied_actions || []), { type: 'run_health_check', target: 'codex-lb response chain', ok: true }];
|
|
103
184
|
if (options.health)
|
|
104
185
|
shaped.chain_health = result.ok ? await checkCodexLbResponseChain(result, { force: true, root }) : null;
|
|
105
186
|
if (flag(args, '--json'))
|
|
106
187
|
return printJson(shaped);
|
|
107
188
|
console.log(`codex-lb configured: ${result.base_url || result.status}`);
|
|
189
|
+
if (shaped.persistence?.warning)
|
|
190
|
+
console.log(`warning: ${shaped.persistence.warning}`);
|
|
108
191
|
if (!result.ok)
|
|
109
192
|
process.exitCode = 1;
|
|
110
193
|
return;
|
|
@@ -143,6 +226,18 @@ export async function run(command, args = []) {
|
|
|
143
226
|
process.exitCode = 1;
|
|
144
227
|
}
|
|
145
228
|
function shapeCodexLbStatus(status = {}) {
|
|
229
|
+
const mode = status.env_loader?.api_key?.source === 'env-file'
|
|
230
|
+
? 'durable_env_file'
|
|
231
|
+
: status.env_loader?.api_key?.source === 'keychain'
|
|
232
|
+
? 'durable_keychain'
|
|
233
|
+
: status.env_loader?.api_key?.source === 'process.env'
|
|
234
|
+
? 'process_only_ephemeral'
|
|
235
|
+
: 'none';
|
|
236
|
+
const persistence = codexLbPersistenceSummary({
|
|
237
|
+
selectedModes: mode === 'none' ? [] : [mode],
|
|
238
|
+
appliedModes: mode === 'none' ? ['none'] : [mode],
|
|
239
|
+
processOnly: mode === 'process_only_ephemeral'
|
|
240
|
+
});
|
|
146
241
|
return {
|
|
147
242
|
schema: 'sks.codex-lb-status.v1',
|
|
148
243
|
...status,
|
|
@@ -154,6 +249,7 @@ function shapeCodexLbStatus(status = {}) {
|
|
|
154
249
|
source: status.env_loader?.api_key?.source || null,
|
|
155
250
|
redacted: true
|
|
156
251
|
},
|
|
252
|
+
persistence,
|
|
157
253
|
env_loader: status.env_loader || null,
|
|
158
254
|
env_auto_load: Boolean(status.env_file && status.env_key_configured),
|
|
159
255
|
guidance: status.ok ? [] : [
|
|
@@ -167,22 +263,58 @@ async function codexLbSetupOptions(args = []) {
|
|
|
167
263
|
const baseUrl = readOption(args, '--base-url', null);
|
|
168
264
|
let host = baseUrl || readOption(args, '--host', readOption(args, '--domain', null));
|
|
169
265
|
let apiKey = readOption(args, '--api-key', readOption(args, '--key', null));
|
|
266
|
+
let apiKeySource = apiKey ? 'cli_option' : 'hidden_prompt';
|
|
170
267
|
let keychain = flag(args, '--keychain');
|
|
171
268
|
if (flag(args, '--api-key-stdin'))
|
|
172
269
|
apiKey = (await readStdin()).trim();
|
|
173
|
-
|
|
270
|
+
if (flag(args, '--api-key-stdin'))
|
|
271
|
+
apiKeySource = 'stdin';
|
|
272
|
+
let health = (flag(args, '--health') || flag(args, '--check')) && !flag(args, '--no-health');
|
|
273
|
+
let useDefaultProvider = flag(args, '--no-default-provider') ? false : true;
|
|
274
|
+
if (flag(args, '--use-default-provider'))
|
|
275
|
+
useDefaultProvider = true;
|
|
276
|
+
let writeEnvFile = flag(args, '--no-env-file') ? false : true;
|
|
277
|
+
if (flag(args, '--write-env-file'))
|
|
278
|
+
writeEnvFile = true;
|
|
279
|
+
if (flag(args, '--no-keychain'))
|
|
280
|
+
keychain = false;
|
|
281
|
+
let syncLaunchctl = flag(args, '--no-launchctl') ? false : true;
|
|
282
|
+
if (flag(args, '--launchctl'))
|
|
283
|
+
syncLaunchctl = true;
|
|
284
|
+
const shellProfile = normalizeShellProfile(readOption(args, '--shell-profile', 'skip'));
|
|
285
|
+
const allowInsecureLocalhost = flag(args, '--allow-insecure-localhost') || flag(args, '--allow-insecure-http');
|
|
286
|
+
const interactive = (!host || !apiKey || canAskInteractive(args)) && canAskInteractive(args);
|
|
174
287
|
if ((!host || !apiKey) && canAskInteractive(args)) {
|
|
175
288
|
console.log('SKS codex-lb setup\n');
|
|
176
289
|
host ||= (await ask('1. codex-lb domain or base URL?\n Example: lb.example.com or https://lb.example.com/backend-api/codex\n> ')).trim();
|
|
177
290
|
apiKey ||= (await askHidden('2. API key?\n Input hidden. Value will be stored securely and never printed.\n> ')).trim();
|
|
178
|
-
|
|
179
|
-
await ask('
|
|
291
|
+
apiKeySource = 'hidden_prompt';
|
|
292
|
+
useDefaultProvider = parseYesNo(await ask('3. Use this codex-lb as default for Codex launches? [Y/n] '), true);
|
|
293
|
+
writeEnvFile = parseYesNo(await ask('4. Write shell env loader to ~/.codex/sks-codex-lb.env? [Y/n] '), true);
|
|
180
294
|
const storeKeychain = (await ask('5. Store the key in macOS Keychain when available? [Y/n] ')).trim();
|
|
181
295
|
keychain = !/^(n|no|아니|아니요|ㄴ)$/i.test(storeKeychain || 'y');
|
|
182
|
-
|
|
296
|
+
syncLaunchctl = parseYesNo(await ask('6. Sync macOS launchctl environment when available? [Y/n] '), true);
|
|
297
|
+
const profile = (await ask('7. Install shell profile snippet? [zsh/bash/fish/all/skip] ')).trim();
|
|
298
|
+
const interactiveShellProfile = normalizeShellProfile(profile || 'skip');
|
|
299
|
+
const runHealth = (await ask('8. Run health check now? [Y/n] ')).trim();
|
|
183
300
|
health = !/^(n|no|아니|아니요|ㄴ)$/i.test(runHealth || 'y');
|
|
301
|
+
return { host, apiKey, health, keychain, useDefaultProvider, writeEnvFile, syncLaunchctl, shellProfile: interactiveShellProfile, allowInsecureLocalhost, apiKeySource, interactive: true, yes: flag(args, '--yes') };
|
|
184
302
|
}
|
|
185
|
-
return { host, apiKey, health, keychain };
|
|
303
|
+
return { host, apiKey, health, keychain, useDefaultProvider, writeEnvFile, syncLaunchctl, shellProfile, allowInsecureLocalhost, apiKeySource, interactive, yes: flag(args, '--yes') };
|
|
304
|
+
}
|
|
305
|
+
function normalizeShellProfile(value) {
|
|
306
|
+
const raw = String(value || 'skip').toLowerCase();
|
|
307
|
+
return raw === 'zsh' || raw === 'bash' || raw === 'fish' || raw === 'all' ? raw : 'skip';
|
|
308
|
+
}
|
|
309
|
+
function parseYesNo(value, fallback) {
|
|
310
|
+
const raw = String(value || '').trim();
|
|
311
|
+
if (!raw)
|
|
312
|
+
return fallback;
|
|
313
|
+
if (/^(y|yes|예|네|응)$/i.test(raw))
|
|
314
|
+
return true;
|
|
315
|
+
if (/^(n|no|아니|아니요|ㄴ)$/i.test(raw))
|
|
316
|
+
return false;
|
|
317
|
+
return fallback;
|
|
186
318
|
}
|
|
187
319
|
function canAskInteractive(args = []) {
|
|
188
320
|
return !flag(args, '--json') && !flag(args, '--yes') && Boolean(input.isTTY && output.isTTY && process.env.CI !== 'true');
|
package/dist/commands/wiki.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare function run(_command: any, args?: any): Promise<void | {
|
|
|
15
15
|
};
|
|
16
16
|
active_records: {
|
|
17
17
|
id: string;
|
|
18
|
-
kind: "incorrect_claim" | "overconfident_claim" | "stale_evidence" | "missing_evidence" | "test_failure" | "route_misclassification" | "scout_error" | "visual_anchor_error" | "image_bbox_error" | "db_safety_false_positive" | "db_safety_false_negative" | "hook_policy_mismatch" | "hook_semantic_mismatch" | "codex_lb_health_misread" | "codex_lb_missing_env_raw_message" | "computer_use_policy_misclassification" | "mock_real_confusion" | "user_intent_misread" | "artifact_schema_error" | "trust_status_overclaim";
|
|
18
|
+
kind: "incorrect_claim" | "overconfident_claim" | "stale_evidence" | "missing_evidence" | "test_failure" | "route_misclassification" | "scout_error" | "visual_anchor_error" | "image_bbox_error" | "db_safety_false_positive" | "db_safety_false_negative" | "hook_policy_mismatch" | "hook_semantic_mismatch" | "hook_strict_subset_misclassified" | "codex_lb_health_misread" | "codex_lb_missing_env_raw_message" | "codex_lb_setup_choice_drift" | "codex_lb_env_persistence_failure" | "computer_use_policy_misclassification" | "computer_use_live_smoke_mismatch" | "computer_use_external_block_overclaimed" | "mock_real_confusion" | "user_intent_misread" | "artifact_schema_error" | "trust_status_overclaim";
|
|
19
19
|
severity: "high" | "low" | "medium" | "critical";
|
|
20
20
|
route: string | null;
|
|
21
21
|
claim: string;
|