sneakoscope 1.0.4 → 1.0.5
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 +28 -1
- 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 +57 -0
- package/dist/cli/install-helpers.js +42 -5
- package/dist/commands/codex-lb.js +11 -5
- package/dist/commands/wiki.d.ts +1 -1
- package/dist/core/codex-compat/codex-compat-report.d.ts +32 -0
- package/dist/core/codex-compat/codex-compat-report.js +15 -3
- package/dist/core/codex-compat/codex-hook-output-builders.d.ts +45 -0
- package/dist/core/codex-compat/codex-hook-output-builders.js +122 -0
- package/dist/core/codex-compat/codex-hook-output-normalizer.d.ts +1 -1
- package/dist/core/codex-compat/codex-hook-output-normalizer.js +25 -48
- package/dist/core/codex-compat/codex-hook-schema.d.ts +6 -1
- package/dist/core/codex-compat/codex-hook-schema.js +14 -1
- package/dist/core/codex-compat/codex-hook-semantic-validator.d.ts +19 -0
- package/dist/core/codex-compat/codex-hook-semantic-validator.js +208 -0
- package/dist/core/codex-compat/codex-hook-warning-detector.d.ts +1 -0
- package/dist/core/codex-compat/codex-hook-warning-detector.js +9 -1
- package/dist/core/codex-compat/codex-schema-snapshot.d.ts +1 -0
- package/dist/core/codex-compat/codex-schema-snapshot.js +10 -2
- package/dist/core/codex-lb/codex-lb-env.d.ts +42 -0
- package/dist/core/codex-lb/codex-lb-env.js +150 -0
- package/dist/core/commands/wiki-command.d.ts +2 -2
- package/dist/core/fsx.d.ts +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/hooks-runtime.js +17 -48
- package/dist/core/proof/evidence-collector.d.ts +1 -1
- package/dist/core/proof/route-finalizer.js +15 -0
- package/dist/core/triwiki-wrongness/wrongness-cli.d.ts +2 -2
- package/dist/core/triwiki-wrongness/wrongness-ledger.js +6 -5
- 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 +12 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/vendor/openai-codex/rust-v0.131.0/hooks/snapshot-metadata.json +2 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@ 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.5** seals the Codex trust harness: hook outputs are validated against both vendored OpenAI Codex CLI `rust-v0.131.0` schemas and runtime semantic parser rules, codex-lb setup survives macOS user-session launches through env-file/Keychain/launchctl-aware repair surfaces, and Computer Use is the preferred macOS visual evidence capability when available.
|
|
8
|
+
|
|
9
|
+
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.
|
|
8
10
|
|
|
9
11
|
SKS **1.0.3** adds git-collaboration hygiene for shared TriWiki memory: `sks git ...`, tracked shared shards under `.sneakoscope/wiki/**`, runtime-only ignores, shared wrongness publish/sync, and Codex App hook trust-state generation for current hook trust syntax.
|
|
10
12
|
|
|
@@ -19,6 +21,31 @@ SKS does not try to clone every other harness. It focuses on one thing: making C
|
|
|
19
21
|

|
|
20
22
|
|
|
21
23
|
|
|
24
|
+
## 1.0.5 Ultimate Harness Seal
|
|
25
|
+
|
|
26
|
+
SKS 1.0.5 treats Codex hook semantic compatibility as stricter than schema compatibility. `sks hooks warning-check --json` and `npm run hooks:semantic-check` fail if an output uses `permissionDecision:"ask"`, PreToolUse `allow` without `updatedInput`, Stop `continue:false`, `stopReason`, `suppressOutput`, snake_case keys, unknown fields, or legacy top-level hook decisions.
|
|
27
|
+
|
|
28
|
+
codex-lb setup now has a durable setup/repair path:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
sks codex-lb setup --host lb.example.com --api-key-stdin --yes --json
|
|
32
|
+
sks codex-lb status --json
|
|
33
|
+
sks codex-lb doctor --deep --json
|
|
34
|
+
npm run codex-lb:missing-env-regression
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The API key is written only to redacted status surfaces. The env file is `~/.codex/sks-codex-lb.env`, metadata is `~/.codex/sks-codex-lb.json`, and reports expose only a redacted presence state plus a fingerprint. Raw `CODEX_LB_API_KEY` missing-env errors are release failures.
|
|
38
|
+
|
|
39
|
+
Computer Use is a Codex App/macOS capability, not a MAD-SKS or DB permission. Visual routes use:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
sks computer-use status --json
|
|
43
|
+
sks computer-use require --route '$QA-LOOP' --json
|
|
44
|
+
npm run computer-use:visual-route-fixture
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If Codex App or macOS blocks the capability, SKS records `external_capability_blocked`, `codex_app_missing`, `macos_permission_missing`, or the closest structured status and does not fabricate UI evidence.
|
|
48
|
+
|
|
22
49
|
## 1.0.4 Codex CLI Compatibility
|
|
23
50
|
|
|
24
51
|
SKS 1.0.4 targets OpenAI Codex CLI `rust-v0.131.0`. Hook outputs are validated against vendored upstream schemas, so SKS fails release checks if it emits deprecated hook shapes or unknown fields. `sks codex-lb setup` now guides users through domain/base URL and API key setup, stores secrets securely, and prevents raw `CODEX_LB_API_KEY` missing messages. On macOS, Computer Use is treated as a first-class Codex App visual evidence capability and is never blocked by MAD-SKS or a generic SKS safety policy.
|
|
@@ -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.5"),
|
|
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.5",
|
|
4
4
|
"typescript": true,
|
|
5
5
|
"mjs_runtime_files": 0,
|
|
6
6
|
"files": [
|
|
@@ -188,10 +188,14 @@
|
|
|
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-output-builders.d.ts",
|
|
192
|
+
"core/codex-compat/codex-hook-output-builders.js",
|
|
191
193
|
"core/codex-compat/codex-hook-output-normalizer.d.ts",
|
|
192
194
|
"core/codex-compat/codex-hook-output-normalizer.js",
|
|
193
195
|
"core/codex-compat/codex-hook-schema.d.ts",
|
|
194
196
|
"core/codex-compat/codex-hook-schema.js",
|
|
197
|
+
"core/codex-compat/codex-hook-semantic-validator.d.ts",
|
|
198
|
+
"core/codex-compat/codex-hook-semantic-validator.js",
|
|
195
199
|
"core/codex-compat/codex-hook-warning-detector.d.ts",
|
|
196
200
|
"core/codex-compat/codex-hook-warning-detector.js",
|
|
197
201
|
"core/codex-compat/codex-schema-snapshot.d.ts",
|
|
@@ -202,6 +206,8 @@
|
|
|
202
206
|
"core/codex-compat/codex-version.js",
|
|
203
207
|
"core/codex-lb-circuit.d.ts",
|
|
204
208
|
"core/codex-lb-circuit.js",
|
|
209
|
+
"core/codex-lb/codex-lb-env.d.ts",
|
|
210
|
+
"core/codex-lb/codex-lb-env.js",
|
|
205
211
|
"core/codex-model-guard.d.ts",
|
|
206
212
|
"core/codex-model-guard.js",
|
|
207
213
|
"core/commands/autoresearch-command.d.ts",
|
|
@@ -58,8 +58,11 @@ export type ConfigureCodexLbResult = {
|
|
|
58
58
|
status: string;
|
|
59
59
|
config_path?: string;
|
|
60
60
|
env_path?: string;
|
|
61
|
+
metadata_path?: string;
|
|
61
62
|
base_url?: string;
|
|
62
63
|
env_key?: string;
|
|
64
|
+
keychain?: Record<string, unknown>;
|
|
65
|
+
warnings?: string[];
|
|
63
66
|
auth_reconcile?: CodexLbAuthReconcileResult;
|
|
64
67
|
codex_lb?: CodexLbStatusSnapshot;
|
|
65
68
|
codex_environment?: CodexLbEnvSyncResult;
|
|
@@ -97,6 +100,24 @@ export declare function codexLbStatus(opts?: any): Promise<{
|
|
|
97
100
|
env_file: boolean;
|
|
98
101
|
env_key_configured: boolean;
|
|
99
102
|
env_base_url_configured: boolean;
|
|
103
|
+
env_loader: {
|
|
104
|
+
configured: boolean;
|
|
105
|
+
missing: string[];
|
|
106
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
107
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
108
|
+
api_key: {
|
|
109
|
+
present: boolean;
|
|
110
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
111
|
+
redacted: true;
|
|
112
|
+
fingerprint: string | null;
|
|
113
|
+
};
|
|
114
|
+
keychain: {
|
|
115
|
+
checked: boolean;
|
|
116
|
+
available: boolean;
|
|
117
|
+
status: string;
|
|
118
|
+
};
|
|
119
|
+
env_paths: string[];
|
|
120
|
+
};
|
|
100
121
|
base_url: string | null;
|
|
101
122
|
auth_path: any;
|
|
102
123
|
auth_mode: string;
|
|
@@ -185,6 +206,24 @@ export declare function maybePromptCodexLbSetupForLaunch(args?: any, opts?: any)
|
|
|
185
206
|
env_file: boolean;
|
|
186
207
|
env_key_configured: boolean;
|
|
187
208
|
env_base_url_configured: boolean;
|
|
209
|
+
env_loader: {
|
|
210
|
+
configured: boolean;
|
|
211
|
+
missing: string[];
|
|
212
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
213
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
214
|
+
api_key: {
|
|
215
|
+
present: boolean;
|
|
216
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
217
|
+
redacted: true;
|
|
218
|
+
fingerprint: string | null;
|
|
219
|
+
};
|
|
220
|
+
keychain: {
|
|
221
|
+
checked: boolean;
|
|
222
|
+
available: boolean;
|
|
223
|
+
status: string;
|
|
224
|
+
};
|
|
225
|
+
env_paths: string[];
|
|
226
|
+
};
|
|
188
227
|
base_url: string | null;
|
|
189
228
|
auth_path: any;
|
|
190
229
|
auth_mode: string;
|
|
@@ -205,6 +244,24 @@ export declare function maybePromptCodexLbSetupForLaunch(args?: any, opts?: any)
|
|
|
205
244
|
env_file: boolean;
|
|
206
245
|
env_key_configured: boolean;
|
|
207
246
|
env_base_url_configured: boolean;
|
|
247
|
+
env_loader: {
|
|
248
|
+
configured: boolean;
|
|
249
|
+
missing: string[];
|
|
250
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource;
|
|
251
|
+
source_priority: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource[];
|
|
252
|
+
api_key: {
|
|
253
|
+
present: boolean;
|
|
254
|
+
source: import("../core/codex-lb/codex-lb-env.js").CodexLbEnvSource | null;
|
|
255
|
+
redacted: true;
|
|
256
|
+
fingerprint: string | null;
|
|
257
|
+
};
|
|
258
|
+
keychain: {
|
|
259
|
+
checked: boolean;
|
|
260
|
+
available: boolean;
|
|
261
|
+
status: string;
|
|
262
|
+
};
|
|
263
|
+
env_paths: string[];
|
|
264
|
+
};
|
|
208
265
|
base_url: string | null;
|
|
209
266
|
auth_path: any;
|
|
210
267
|
auth_mode: string;
|
|
@@ -11,6 +11,7 @@ import { context7ConfigToml, DOLLAR_SKILL_NAMES, GETDESIGN_REFERENCE, hasContext
|
|
|
11
11
|
import { codexLaunchCommand, platformTmuxInstallHint, tmuxReadiness, tmuxReadinessCatchFallback } from '../core/tmux-ui.js';
|
|
12
12
|
import { reconcileCodexAppUpgradeProcesses } from '../core/codex-app.js';
|
|
13
13
|
import { recordCodexLbHealthEvent } from '../core/codex-lb-circuit.js';
|
|
14
|
+
import { loadCodexLbEnv, writeCodexLbKeychain, codexLbMetadataPath } from '../core/codex-lb/codex-lb-env.js';
|
|
14
15
|
const DEFAULT_CODEX_APP_PLUGINS = [
|
|
15
16
|
['browser', 'openai-bundled'],
|
|
16
17
|
['chrome', 'openai-bundled'],
|
|
@@ -293,7 +294,7 @@ async function restorePostinstallCodexLbConfigSnapshot(snapshot) {
|
|
|
293
294
|
export function normalizeCodexLbBaseUrl(input = '') {
|
|
294
295
|
let host = String(input || '').trim();
|
|
295
296
|
if (!host)
|
|
296
|
-
|
|
297
|
+
return '';
|
|
297
298
|
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(host))
|
|
298
299
|
host = `https://${host}`;
|
|
299
300
|
host = host.replace(/\/+$/, '');
|
|
@@ -303,16 +304,35 @@ export async function configureCodexLb(opts = {}) {
|
|
|
303
304
|
const home = opts.home || process.env.HOME || os.homedir();
|
|
304
305
|
const configPath = opts.configPath || codexLbConfigPath(home);
|
|
305
306
|
const envPath = opts.envPath || codexLbEnvPath(home);
|
|
306
|
-
const
|
|
307
|
+
const rawHost = String(opts.host || opts.baseUrl || '');
|
|
308
|
+
const baseUrl = normalizeCodexLbBaseUrl(rawHost);
|
|
307
309
|
const apiKey = String(opts.apiKey || '').trim();
|
|
310
|
+
if (!baseUrl)
|
|
311
|
+
return { ok: false, status: 'missing_host_or_base_url', config_path: configPath, env_path: envPath };
|
|
312
|
+
if (/[\u0000-\u001f\u007f\s]/.test(rawHost.trim()))
|
|
313
|
+
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' };
|
|
308
314
|
if (!apiKey)
|
|
309
315
|
return { ok: false, status: 'missing_api_key', config_path: configPath, env_path: envPath };
|
|
316
|
+
const insecureLocalWarning = /^http:\/\//i.test(baseUrl) && !/^http:\/\/(?:localhost|127\.0\.0\.1|\[::1\])(?::|\/|$)/i.test(baseUrl) && !opts.allowInsecureHttp
|
|
317
|
+
? ['codex-lb base URL uses http outside localhost; prefer https or pass an explicit allow flag in the calling surface.']
|
|
318
|
+
: [];
|
|
310
319
|
await ensureDir(path.dirname(configPath));
|
|
311
320
|
const current = await readText(configPath, '');
|
|
312
321
|
const next = normalizeCodexFastModeUiConfig(upsertCodexLbConfig(current, baseUrl));
|
|
313
322
|
await writeTextAtomic(configPath, next);
|
|
314
323
|
await writeTextAtomic(envPath, `export CODEX_LB_BASE_URL=${shellSingleQuote(baseUrl)}\nexport CODEX_LB_API_KEY=${shellSingleQuote(apiKey)}\n`);
|
|
315
324
|
await fsp.chmod(envPath, 0o600).catch(() => { });
|
|
325
|
+
const keyFingerprint = await sha256Text(apiKey);
|
|
326
|
+
const metadataPath = opts.metadataPath || codexLbMetadataPath(home);
|
|
327
|
+
await writeTextAtomic(metadataPath, `${JSON.stringify({
|
|
328
|
+
schema: 'sks.codex-lb-metadata.v1',
|
|
329
|
+
base_url: baseUrl,
|
|
330
|
+
updated_at: new Date().toISOString(),
|
|
331
|
+
source: opts.source || 'setup',
|
|
332
|
+
api_key: { redacted: true, sha256: keyFingerprint }
|
|
333
|
+
}, null, 2)}\n`);
|
|
334
|
+
await fsp.chmod(metadataPath, 0o600).catch(() => { });
|
|
335
|
+
const keychain = opts.keychain ? await writeCodexLbKeychain(apiKey, opts).catch((err) => ({ ok: false, status: 'keychain_store_failed', error: err.message })) : { ok: false, status: 'skipped' };
|
|
316
336
|
const codexEnvironment = await syncCodexLbProviderEnvironment({ env_path: envPath, base_url: baseUrl }, { ...opts, home });
|
|
317
337
|
const codexLogin = await maybeSyncCodexLbSharedLogin(apiKey, { ...opts, home, force: true });
|
|
318
338
|
const codexLb = await codexLbStatus({ ...opts, home, configPath, envPath });
|
|
@@ -324,8 +344,11 @@ export async function configureCodexLb(opts = {}) {
|
|
|
324
344
|
status: ok ? 'configured' : (codexEnvironment.status || codexLogin.status),
|
|
325
345
|
config_path: configPath,
|
|
326
346
|
env_path: envPath,
|
|
347
|
+
metadata_path: metadataPath,
|
|
327
348
|
base_url: baseUrl,
|
|
328
349
|
env_key: 'CODEX_LB_API_KEY',
|
|
350
|
+
keychain,
|
|
351
|
+
warnings: insecureLocalWarning,
|
|
329
352
|
auth_reconcile: authReconcile,
|
|
330
353
|
codex_lb: finalCodexLb,
|
|
331
354
|
codex_environment: codexEnvironment,
|
|
@@ -340,13 +363,14 @@ export async function codexLbStatus(opts = {}) {
|
|
|
340
363
|
const config = await readText(configPath, '');
|
|
341
364
|
const envExists = await exists(envPath);
|
|
342
365
|
const envText = envExists ? await readText(envPath, '') : '';
|
|
366
|
+
const envLoad = await loadCodexLbEnv({ ...opts, home, envPath });
|
|
343
367
|
const authPath = opts.authPath || codexAuthPath(home);
|
|
344
368
|
const authText = await readText(authPath, '');
|
|
345
369
|
const authMode = codexAuthModeSummary(authText);
|
|
346
|
-
const envKeyConfigured = Boolean(
|
|
370
|
+
const envKeyConfigured = Boolean(envLoad.api_key.present);
|
|
347
371
|
const providerConfigured = /\[model_providers\.codex-lb\]/.test(config);
|
|
348
372
|
const selected = hasTopLevelCodexLbSelected(config);
|
|
349
|
-
const baseUrl = codexLbProviderBaseUrl(config) ||
|
|
373
|
+
const baseUrl = codexLbProviderBaseUrl(config) || envLoad.base_url || null;
|
|
350
374
|
const providerRequiresOpenAiAuth = codexLbProviderRequiresOpenAiAuth(config);
|
|
351
375
|
return {
|
|
352
376
|
ok: providerConfigured && envKeyConfigured && Boolean(baseUrl) && providerRequiresOpenAiAuth,
|
|
@@ -357,7 +381,16 @@ export async function codexLbStatus(opts = {}) {
|
|
|
357
381
|
selected,
|
|
358
382
|
env_file: envExists,
|
|
359
383
|
env_key_configured: envKeyConfigured,
|
|
360
|
-
env_base_url_configured: Boolean(
|
|
384
|
+
env_base_url_configured: Boolean(envLoad.base_url),
|
|
385
|
+
env_loader: {
|
|
386
|
+
configured: envLoad.configured,
|
|
387
|
+
missing: envLoad.missing,
|
|
388
|
+
source: envLoad.source,
|
|
389
|
+
source_priority: envLoad.source_priority,
|
|
390
|
+
api_key: envLoad.api_key,
|
|
391
|
+
keychain: envLoad.keychain,
|
|
392
|
+
env_paths: envLoad.env_paths
|
|
393
|
+
},
|
|
361
394
|
base_url: baseUrl,
|
|
362
395
|
auth_path: authPath,
|
|
363
396
|
auth_mode: authMode.mode,
|
|
@@ -1482,6 +1515,10 @@ function redactSecretText(text = '', secrets = []) {
|
|
|
1482
1515
|
}
|
|
1483
1516
|
return out;
|
|
1484
1517
|
}
|
|
1518
|
+
async function sha256Text(value = '') {
|
|
1519
|
+
const { createHash } = await import('node:crypto');
|
|
1520
|
+
return createHash('sha256').update(String(value || '')).digest('hex');
|
|
1521
|
+
}
|
|
1485
1522
|
function escapeRegExp(value) {
|
|
1486
1523
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1487
1524
|
}
|
|
@@ -39,7 +39,8 @@ export async function run(command, args = []) {
|
|
|
39
39
|
}
|
|
40
40
|
if (action === 'health' || action === 'verify-chain' || action === 'chain') {
|
|
41
41
|
const status = await codexLbStatus();
|
|
42
|
-
const
|
|
42
|
+
const blocker = !status.env_key_configured ? 'missing_env_key' : !status.base_url ? 'missing_base_url' : 'not_configured';
|
|
43
|
+
const result = status.ok ? await checkCodexLbResponseChain(status, { force: true, root }) : { ok: false, status: blocker, codex_lb: status };
|
|
43
44
|
if (flag(args, '--json'))
|
|
44
45
|
return printJson(result);
|
|
45
46
|
console.log(`codex-lb response chain: ${result.ok ? 'ok' : `failed (${result.status})`}`);
|
|
@@ -97,7 +98,7 @@ export async function run(command, args = []) {
|
|
|
97
98
|
process.exitCode = 1;
|
|
98
99
|
return;
|
|
99
100
|
}
|
|
100
|
-
const result = await configureCodexLb({ host: options.host, apiKey: options.apiKey });
|
|
101
|
+
const result = await configureCodexLb({ host: options.host, apiKey: options.apiKey, keychain: options.keychain });
|
|
101
102
|
const shaped = { schema: 'sks.codex-lb-setup.v1', ...result, api_key: { present: Boolean(options.apiKey), redacted: true }, env_file_chmod: '0600' };
|
|
102
103
|
if (options.health)
|
|
103
104
|
shaped.chain_health = result.ok ? await checkCodexLbResponseChain(result, { force: true, root }) : null;
|
|
@@ -147,11 +148,13 @@ function shapeCodexLbStatus(status = {}) {
|
|
|
147
148
|
...status,
|
|
148
149
|
configured: Boolean(status.ok),
|
|
149
150
|
setup_needed: !status.ok,
|
|
151
|
+
repair_available: !status.ok,
|
|
150
152
|
api_key: {
|
|
151
153
|
present: Boolean(status.env_key_configured),
|
|
152
|
-
source: status.
|
|
154
|
+
source: status.env_loader?.api_key?.source || null,
|
|
153
155
|
redacted: true
|
|
154
156
|
},
|
|
157
|
+
env_loader: status.env_loader || null,
|
|
155
158
|
env_auto_load: Boolean(status.env_file && status.env_key_configured),
|
|
156
159
|
guidance: status.ok ? [] : [
|
|
157
160
|
'codex-lb API key is not configured.',
|
|
@@ -164,6 +167,7 @@ async function codexLbSetupOptions(args = []) {
|
|
|
164
167
|
const baseUrl = readOption(args, '--base-url', null);
|
|
165
168
|
let host = baseUrl || readOption(args, '--host', readOption(args, '--domain', null));
|
|
166
169
|
let apiKey = readOption(args, '--api-key', readOption(args, '--key', null));
|
|
170
|
+
let keychain = flag(args, '--keychain');
|
|
167
171
|
if (flag(args, '--api-key-stdin'))
|
|
168
172
|
apiKey = (await readStdin()).trim();
|
|
169
173
|
let health = flag(args, '--health') || flag(args, '--check');
|
|
@@ -173,10 +177,12 @@ async function codexLbSetupOptions(args = []) {
|
|
|
173
177
|
apiKey ||= (await askHidden('2. API key?\n Input hidden. Value will be stored securely and never printed.\n> ')).trim();
|
|
174
178
|
await ask('3. Use this codex-lb as default for Codex launches? [Y/n] ');
|
|
175
179
|
await ask('4. Write shell env loader to ~/.codex/sks-codex-lb.env? [Y/n] ');
|
|
176
|
-
const
|
|
180
|
+
const storeKeychain = (await ask('5. Store the key in macOS Keychain when available? [Y/n] ')).trim();
|
|
181
|
+
keychain = !/^(n|no|아니|아니요|ㄴ)$/i.test(storeKeychain || 'y');
|
|
182
|
+
const runHealth = (await ask('6. Run health check now? [Y/n] ')).trim();
|
|
177
183
|
health = !/^(n|no|아니|아니요|ㄴ)$/i.test(runHealth || 'y');
|
|
178
184
|
}
|
|
179
|
-
return { host, apiKey, health };
|
|
185
|
+
return { host, apiKey, health, keychain };
|
|
180
186
|
}
|
|
181
187
|
function canAskInteractive(args = []) {
|
|
182
188
|
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" | "codex_lb_health_misread" | "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" | "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";
|
|
19
19
|
severity: "high" | "low" | "medium" | "critical";
|
|
20
20
|
route: string | null;
|
|
21
21
|
claim: string;
|
|
@@ -6,6 +6,22 @@ export declare function codexCompatibilityReport(opts?: any): Promise<{
|
|
|
6
6
|
snapshot: string;
|
|
7
7
|
ok: boolean;
|
|
8
8
|
files: number;
|
|
9
|
+
metadata: {
|
|
10
|
+
upstream: any;
|
|
11
|
+
tag: any;
|
|
12
|
+
commit: any;
|
|
13
|
+
captured_at: any;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
hooks_semantic: {
|
|
17
|
+
ok: boolean;
|
|
18
|
+
warnings_count: number;
|
|
19
|
+
events: {
|
|
20
|
+
event: "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PermissionRequest" | "Stop" | "PreCompact" | "PostCompact" | "SessionStart";
|
|
21
|
+
checked: number;
|
|
22
|
+
ok: boolean;
|
|
23
|
+
warnings: string[];
|
|
24
|
+
}[];
|
|
9
25
|
};
|
|
10
26
|
ok: boolean;
|
|
11
27
|
status: string;
|
|
@@ -23,6 +39,22 @@ export declare function codexDoctorReport(opts?: any): Promise<{
|
|
|
23
39
|
snapshot: string;
|
|
24
40
|
ok: boolean;
|
|
25
41
|
files: number;
|
|
42
|
+
metadata: {
|
|
43
|
+
upstream: any;
|
|
44
|
+
tag: any;
|
|
45
|
+
commit: any;
|
|
46
|
+
captured_at: any;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
hooks_semantic: {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
warnings_count: number;
|
|
52
|
+
events: {
|
|
53
|
+
event: "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PermissionRequest" | "Stop" | "PreCompact" | "PostCompact" | "SessionStart";
|
|
54
|
+
checked: number;
|
|
55
|
+
ok: boolean;
|
|
56
|
+
warnings: string[];
|
|
57
|
+
}[];
|
|
26
58
|
};
|
|
27
59
|
ok: boolean;
|
|
28
60
|
status: string;
|
|
@@ -7,7 +7,8 @@ export async function codexCompatibilityReport(opts = {}) {
|
|
|
7
7
|
const root = opts.root || await projectRoot();
|
|
8
8
|
const version = await codexVersionReport(opts);
|
|
9
9
|
const snapshot = await codexSchemaSnapshotReport();
|
|
10
|
-
const
|
|
10
|
+
const hooks = await codexHookWarningCheck(root, { recordWrongness: false });
|
|
11
|
+
const ok = Boolean(version.policy.ok && snapshot.ok && hooks.ok);
|
|
11
12
|
return {
|
|
12
13
|
schema: CODEX_COMPAT_SCHEMA,
|
|
13
14
|
required_baseline: CODEX_REQUIRED_BASELINE_TAG,
|
|
@@ -15,11 +16,22 @@ export async function codexCompatibilityReport(opts = {}) {
|
|
|
15
16
|
hooks_schema: {
|
|
16
17
|
snapshot: CODEX_REQUIRED_BASELINE_TAG,
|
|
17
18
|
ok: snapshot.ok,
|
|
18
|
-
files: snapshot.files.length
|
|
19
|
+
files: snapshot.files.length,
|
|
20
|
+
metadata: {
|
|
21
|
+
upstream: snapshot.metadata?.upstream || null,
|
|
22
|
+
tag: snapshot.metadata?.tag || null,
|
|
23
|
+
commit: snapshot.metadata?.commit || null,
|
|
24
|
+
captured_at: snapshot.metadata?.captured_at || null
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
hooks_semantic: {
|
|
28
|
+
ok: hooks.ok,
|
|
29
|
+
warnings_count: hooks.warnings_count,
|
|
30
|
+
events: hooks.events
|
|
19
31
|
},
|
|
20
32
|
ok,
|
|
21
33
|
status: ok ? version.policy.status : 'blocked',
|
|
22
|
-
warnings: version.policy.warnings,
|
|
34
|
+
warnings: [...version.policy.warnings, ...(hooks.ok ? [] : hooks.warnings)],
|
|
23
35
|
root
|
|
24
36
|
};
|
|
25
37
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type CodexHookEventName } from './codex-schema-snapshot.js';
|
|
2
|
+
export type CodexHookOutput = Record<string, unknown>;
|
|
3
|
+
export declare function buildPreToolUseContinue(options?: {
|
|
4
|
+
systemMessage?: string;
|
|
5
|
+
}): CodexHookOutput;
|
|
6
|
+
export declare function buildPreToolUseDeny(reason: unknown, options?: {
|
|
7
|
+
systemMessage?: string;
|
|
8
|
+
}): CodexHookOutput;
|
|
9
|
+
export declare function buildPreToolUseAllowRewrite(updatedInput: unknown, options?: {
|
|
10
|
+
systemMessage?: string;
|
|
11
|
+
}): CodexHookOutput;
|
|
12
|
+
export declare function buildPermissionRequestAllow(options?: {
|
|
13
|
+
systemMessage?: string;
|
|
14
|
+
}): CodexHookOutput;
|
|
15
|
+
export declare function buildPermissionRequestDeny(message: unknown, options?: {
|
|
16
|
+
systemMessage?: string;
|
|
17
|
+
}): CodexHookOutput;
|
|
18
|
+
export declare function buildPostToolUseContinue(options?: {
|
|
19
|
+
additionalContext?: string;
|
|
20
|
+
systemMessage?: string;
|
|
21
|
+
}): CodexHookOutput;
|
|
22
|
+
export declare function buildPostToolUseBlock(reason: unknown, options?: {
|
|
23
|
+
systemMessage?: string;
|
|
24
|
+
}): CodexHookOutput;
|
|
25
|
+
export declare function buildUserPromptSubmitContinue(options?: {
|
|
26
|
+
additionalContext?: string;
|
|
27
|
+
systemMessage?: string;
|
|
28
|
+
}): CodexHookOutput;
|
|
29
|
+
export declare function buildUserPromptSubmitBlock(reason: unknown, options?: {
|
|
30
|
+
systemMessage?: string;
|
|
31
|
+
}): CodexHookOutput;
|
|
32
|
+
export declare function buildStopContinue(options?: {
|
|
33
|
+
systemMessage?: string;
|
|
34
|
+
}): CodexHookOutput;
|
|
35
|
+
export declare function buildStopBlock(reason: unknown, options?: {
|
|
36
|
+
systemMessage?: string;
|
|
37
|
+
}): CodexHookOutput;
|
|
38
|
+
export declare function buildCompactContinue(event?: Extract<CodexHookEventName, 'PreCompact' | 'PostCompact'>, options?: {
|
|
39
|
+
systemMessage?: string;
|
|
40
|
+
}): CodexHookOutput;
|
|
41
|
+
export declare function buildSessionStartContinue(options?: {
|
|
42
|
+
additionalContext?: string;
|
|
43
|
+
systemMessage?: string;
|
|
44
|
+
}): CodexHookOutput;
|
|
45
|
+
//# sourceMappingURL=codex-hook-output-builders.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {} from './codex-schema-snapshot.js';
|
|
2
|
+
export function buildPreToolUseContinue(options = {}) {
|
|
3
|
+
return withOptionalSystemMessage({ continue: true }, options.systemMessage);
|
|
4
|
+
}
|
|
5
|
+
export function buildPreToolUseDeny(reason, options = {}) {
|
|
6
|
+
const trimmed = requiredReason(reason, 'PreToolUse deny requires a non-empty reason');
|
|
7
|
+
return withOptionalSystemMessage({
|
|
8
|
+
continue: true,
|
|
9
|
+
hookSpecificOutput: {
|
|
10
|
+
hookEventName: 'PreToolUse',
|
|
11
|
+
permissionDecision: 'deny',
|
|
12
|
+
permissionDecisionReason: trimmed
|
|
13
|
+
}
|
|
14
|
+
}, options.systemMessage);
|
|
15
|
+
}
|
|
16
|
+
export function buildPreToolUseAllowRewrite(updatedInput, options = {}) {
|
|
17
|
+
if (updatedInput === undefined || updatedInput === null) {
|
|
18
|
+
throw new Error('PreToolUse allow rewrite requires updatedInput');
|
|
19
|
+
}
|
|
20
|
+
return withOptionalSystemMessage({
|
|
21
|
+
continue: true,
|
|
22
|
+
hookSpecificOutput: {
|
|
23
|
+
hookEventName: 'PreToolUse',
|
|
24
|
+
permissionDecision: 'allow',
|
|
25
|
+
updatedInput
|
|
26
|
+
}
|
|
27
|
+
}, options.systemMessage);
|
|
28
|
+
}
|
|
29
|
+
export function buildPermissionRequestAllow(options = {}) {
|
|
30
|
+
return withOptionalSystemMessage({
|
|
31
|
+
continue: true,
|
|
32
|
+
hookSpecificOutput: {
|
|
33
|
+
hookEventName: 'PermissionRequest',
|
|
34
|
+
decision: { behavior: 'allow' }
|
|
35
|
+
}
|
|
36
|
+
}, options.systemMessage);
|
|
37
|
+
}
|
|
38
|
+
export function buildPermissionRequestDeny(message, options = {}) {
|
|
39
|
+
const trimmed = requiredReason(message, 'PermissionRequest deny requires a non-empty message');
|
|
40
|
+
return withOptionalSystemMessage({
|
|
41
|
+
continue: true,
|
|
42
|
+
hookSpecificOutput: {
|
|
43
|
+
hookEventName: 'PermissionRequest',
|
|
44
|
+
decision: { behavior: 'deny', message: trimmed }
|
|
45
|
+
}
|
|
46
|
+
}, options.systemMessage);
|
|
47
|
+
}
|
|
48
|
+
export function buildPostToolUseContinue(options = {}) {
|
|
49
|
+
const output = { continue: true };
|
|
50
|
+
const additionalContext = optionalText(options.additionalContext);
|
|
51
|
+
if (additionalContext) {
|
|
52
|
+
output.hookSpecificOutput = {
|
|
53
|
+
hookEventName: 'PostToolUse',
|
|
54
|
+
additionalContext
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return withOptionalSystemMessage(output, options.systemMessage);
|
|
58
|
+
}
|
|
59
|
+
export function buildPostToolUseBlock(reason, options = {}) {
|
|
60
|
+
return withOptionalSystemMessage({
|
|
61
|
+
continue: true,
|
|
62
|
+
decision: 'block',
|
|
63
|
+
reason: requiredReason(reason, 'PostToolUse block requires a non-empty reason')
|
|
64
|
+
}, options.systemMessage);
|
|
65
|
+
}
|
|
66
|
+
export function buildUserPromptSubmitContinue(options = {}) {
|
|
67
|
+
const output = { continue: true };
|
|
68
|
+
const additionalContext = optionalText(options.additionalContext);
|
|
69
|
+
if (additionalContext) {
|
|
70
|
+
output.hookSpecificOutput = {
|
|
71
|
+
hookEventName: 'UserPromptSubmit',
|
|
72
|
+
additionalContext
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return withOptionalSystemMessage(output, options.systemMessage);
|
|
76
|
+
}
|
|
77
|
+
export function buildUserPromptSubmitBlock(reason, options = {}) {
|
|
78
|
+
return withOptionalSystemMessage({
|
|
79
|
+
continue: true,
|
|
80
|
+
decision: 'block',
|
|
81
|
+
reason: requiredReason(reason, 'UserPromptSubmit block requires a non-empty reason')
|
|
82
|
+
}, options.systemMessage);
|
|
83
|
+
}
|
|
84
|
+
export function buildStopContinue(options = {}) {
|
|
85
|
+
return withOptionalSystemMessage({ continue: true }, options.systemMessage);
|
|
86
|
+
}
|
|
87
|
+
export function buildStopBlock(reason, options = {}) {
|
|
88
|
+
return withOptionalSystemMessage({
|
|
89
|
+
continue: true,
|
|
90
|
+
decision: 'block',
|
|
91
|
+
reason: requiredReason(reason, 'Stop block requires a non-empty reason')
|
|
92
|
+
}, options.systemMessage);
|
|
93
|
+
}
|
|
94
|
+
export function buildCompactContinue(event = 'PreCompact', options = {}) {
|
|
95
|
+
void event;
|
|
96
|
+
return withOptionalSystemMessage({ continue: true }, options.systemMessage);
|
|
97
|
+
}
|
|
98
|
+
export function buildSessionStartContinue(options = {}) {
|
|
99
|
+
const output = { continue: true };
|
|
100
|
+
const additionalContext = optionalText(options.additionalContext);
|
|
101
|
+
if (additionalContext) {
|
|
102
|
+
output.hookSpecificOutput = {
|
|
103
|
+
hookEventName: 'SessionStart',
|
|
104
|
+
additionalContext
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return withOptionalSystemMessage(output, options.systemMessage);
|
|
108
|
+
}
|
|
109
|
+
function withOptionalSystemMessage(output, systemMessage) {
|
|
110
|
+
const message = optionalText(systemMessage);
|
|
111
|
+
return message ? { ...output, systemMessage: message } : output;
|
|
112
|
+
}
|
|
113
|
+
function requiredReason(value, fallback) {
|
|
114
|
+
const trimmed = optionalText(value);
|
|
115
|
+
if (!trimmed)
|
|
116
|
+
throw new Error(fallback);
|
|
117
|
+
return trimmed;
|
|
118
|
+
}
|
|
119
|
+
function optionalText(value) {
|
|
120
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=codex-hook-output-builders.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare function normalizeCodexHookOutput(name: unknown, result?: any):
|
|
1
|
+
export declare function normalizeCodexHookOutput(name: unknown, result?: any): import("./codex-hook-output-builders.js").CodexHookOutput;
|
|
2
2
|
//# sourceMappingURL=codex-hook-output-normalizer.d.ts.map
|