sneakoscope 4.0.2 → 4.0.3

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 CHANGED
@@ -35,15 +35,15 @@ Set up this agent project with Sneakoscope Codex. Use [[mandarange/Sneakoscope-C
35
35
 
36
36
  ## 🚀 Current Release
37
37
 
38
- SKS **4.0.2** is the TriWiki Turbo production completion release. It makes affected/confidence release runs TriWiki-first, restores build-once proof, bridges reusable TriWiki proof cards into release cache v2, runs gate packs through a resource-aware scheduler, and tightens semantic dirty doctor, sksd warmup, legacy purge, orphan detection, and actual five-minute SLA regression evidence.
38
+ SKS **4.0.3** adds a GLM 5.2-only MAD mode through OpenRouter while preserving the proof-first SKS pipeline. `sks --mad --glm` resolves the GLM profile, keeps GPT/OpenAI fallback disabled, and records model-lock proof; `sks --mad --glm --repair` rotates the OpenRouter API key outside project files.
39
39
 
40
- What changed in 4.0.2:
40
+ What changed in 4.0.3:
41
41
 
42
- - **TriWiki proof bank.** Proof cards now bind reusable gate proof to input hash, implementation hash, package lock hash, environment allowlist, fixture version, and tool version.
43
- - **Affected release-equivalent verification.** Changed files map to module cards, gate impact maps, and gate packs so small tasks can run fast without giving up scoped release confidence.
44
- - **Parallel SLA planning.** Release gate packs, resource budgets, and SLA certificates expose the planned critical path for five-minute foreground verification.
45
- - **New control surface.** `sks check`, `sks task`, `sks release`, `sks triwiki`, `sks proof bank status`, and `sks daemon` expose the fast proof pipeline.
46
- - **No silent legacy fallback.** Compatibility aliases are removed; removed runtimes now use explicit migration notices.
42
+ - **GLM 5.2 MAD mode.** `sks --mad --glm` enters a `mad-glm` profile using OpenRouter model `z-ai/glm-5.2`.
43
+ - **No GPT fallback.** GLM requests use `provider.allow_fallbacks: false`, omit fallback `models`, and reject non-GLM response model ids before mutation.
44
+ - **OpenRouter key lifecycle.** Keys resolve from `OPENROUTER_API_KEY`, `SKS_OPENROUTER_API_KEY`, or the user SKS secret store; stored keys use private permissions and redacted metadata.
45
+ - **Codex App profile.** `sks codex-app glm-profile install` writes the `sks/glm-5.2-mad` profile metadata for Codex App selection.
46
+ - **Codex 0.141 alignment.** SKS delegates remote relay, cwd/shell/path preservation, selected plugin MCP activation, App/MCP dedupe, bounded prompt-image cache, bounded feedback upload, and terminal resize behavior to Codex-native semantics where available.
47
47
 
48
48
  SKS **3.1.16** was a launch-reliability patch on the 3.1.15 doctor-reliability release. It made `sks --mad` self-bootstrap a fresh project instead of dead-ending on a missing Codex config.
49
49
 
@@ -76,7 +76,7 @@ dependencies = [
76
76
 
77
77
  [[package]]
78
78
  name = "sks-core"
79
- version = "4.0.2"
79
+ version = "4.0.3"
80
80
  dependencies = [
81
81
  "serde_json",
82
82
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "4.0.2"
3
+ version = "4.0.3"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -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.0.2"),
7
+ Some("--version") => println!("sks-rs 4.0.3"),
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const FAST_PACKAGE_VERSION = '4.0.2';
2
+ const FAST_PACKAGE_VERSION = '4.0.3';
3
3
  const args = process.argv.slice(2);
4
4
  try {
5
5
  if (args[0] === '--agent' && args[1] === 'worker') {
@@ -111,6 +111,7 @@ export const COMMANDS = {
111
111
  'zellij-slot-column-anchor': entry('beta', 'Render the compact SLOTS anchor pane for first-slot-down Zellij stacks', 'dist/commands/zellij-slot-column-anchor.js', directCommand(() => import('../commands/zellij-slot-column-anchor.js'), 'dist/commands/zellij-slot-column-anchor.js')),
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
+ 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')),
114
115
  'mad-db': entry('beta', 'Create or inspect one-cycle Mad-DB break-glass capability tokens', 'dist/commands/mad-db.js', directCommand(() => import('../commands/mad-db.js'), 'dist/commands/mad-db.js')),
115
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')),
116
117
  'dollar-commands': entry('stable', 'List Codex App dollar commands', 'dist/core/commands/basic-cli.js', basicArgs('dollarCommandsCommand')),
@@ -0,0 +1,25 @@
1
+ const RESERVED_COMMANDS = new Set(['help', '--help', '-h', 'version', '--version', '-v']);
2
+ export function detectGlobalMode(args = []) {
3
+ if (!args.length || RESERVED_COMMANDS.has(String(args[0])))
4
+ return null;
5
+ const hasMad = args.includes('--mad');
6
+ const hasGlm = args.includes('--glm');
7
+ if (hasMad && hasGlm)
8
+ return { kind: 'mad-glm', args: stripGlobalModeFlags(args) };
9
+ if (hasGlm && !hasMad)
10
+ return { kind: 'glm-without-mad', args: stripGlobalModeFlags(args) };
11
+ return null;
12
+ }
13
+ export function stripGlobalModeFlags(args) {
14
+ return args.filter((arg) => arg !== '--mad' && arg !== '--glm');
15
+ }
16
+ export function glmWithoutMadResult() {
17
+ return {
18
+ ok: false,
19
+ status: 'blocked',
20
+ mode: 'glm',
21
+ reason: 'glm_requires_mad',
22
+ hint: 'use sks --mad --glm'
23
+ };
24
+ }
25
+ //# sourceMappingURL=global-mode-router.js.map
@@ -1,4 +1,5 @@
1
1
  import { COMMAND_ALIASES, COMMANDS, } from './command-registry.js';
2
+ import { detectGlobalMode, glmWithoutMadResult } from './global-mode-router.js';
2
3
  export function isCommandName(value) {
3
4
  return Object.prototype.hasOwnProperty.call(COMMANDS, value);
4
5
  }
@@ -18,6 +19,17 @@ export function normalizeCommand(args = []) {
18
19
  }
19
20
  export async function dispatch(args) {
20
21
  const argv = args ?? process.argv.slice(2);
22
+ const globalMode = detectGlobalMode(argv);
23
+ if (globalMode?.kind === 'mad-glm') {
24
+ const mod = await import('../core/commands/glm-command.js');
25
+ return mod.glmCommand(globalMode.args);
26
+ }
27
+ if (globalMode?.kind === 'glm-without-mad') {
28
+ const result = glmWithoutMadResult();
29
+ console.error(`GLM mode requires MAD: ${result.hint}`);
30
+ process.exitCode = 1;
31
+ return result;
32
+ }
21
33
  const { command, rawCommand, args: rest } = normalizeCommand(argv);
22
34
  if (!command) {
23
35
  if (!argv.length) {
@@ -10,6 +10,7 @@ import { runCodexInitDeep } from '../core/codex-app/codex-init-deep.js';
10
10
  import { buildCodexHookLifecycle } from '../core/codex-app/codex-hook-lifecycle.js';
11
11
  import { resolveCodexAppExecutionProfile } from '../core/codex-app/codex-app-execution-profile.js';
12
12
  import { repairCodexNativeManagedAssets } from '../core/codex-native/codex-native-repair-transaction.js';
13
+ import { doctorCodexAppGlmProfile, installCodexAppGlmProfile } from '../core/codex-app/glm-profile-installer.js';
13
14
  export async function run(_command, args = []) {
14
15
  const action = args[0] || 'check';
15
16
  if (action === 'remote-control' || action === 'remote')
@@ -28,6 +29,14 @@ export async function run(_command, args = []) {
28
29
  return printCodexAppResult(args, await buildCodexHookLifecycle({ root: await sksRoot(), apply: flag(args, '--apply') || flag(args, '--fix') }));
29
30
  if (action === 'execution-profile')
30
31
  return printCodexAppResult(args, await resolveCodexAppExecutionProfile({ root: await sksRoot() }));
32
+ if (action === 'glm-profile') {
33
+ const subcommand = args[1] || 'doctor';
34
+ const root = await sksRoot();
35
+ const result = subcommand === 'install' || subcommand === 'repair'
36
+ ? await installCodexAppGlmProfile({ root, apply: true })
37
+ : await doctorCodexAppGlmProfile({ root });
38
+ return printCodexAppResult(args, result);
39
+ }
31
40
  if (action === 'product-design' || action === 'design-product' || action === 'ensure-product-design') {
32
41
  const checkOnly = flag(args, '--check-only') || flag(args, '--no-install');
33
42
  const status = await codexProductDesignPluginStatus({
@@ -88,7 +97,7 @@ export async function run(_command, args = []) {
88
97
  process.exitCode = 1;
89
98
  return;
90
99
  }
91
- console.error('Usage: sks codex-app check|status|harness-matrix|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
100
+ console.error('Usage: sks codex-app check|status|harness-matrix|skill-sync|agent-role-sync|init-deep|hook-lifecycle|execution-profile|glm-profile [install|doctor]|product-design [--check-only]|ensure-product-design|chrome-extension|pat status|remote-control [--json]');
92
101
  process.exitCode = 1;
93
102
  }
94
103
  function printCodexAppResult(args = [], result) {
@@ -3,6 +3,7 @@ import { printJson } from '../cli/output.js';
3
3
  import { codexCompatibilityReport, codexDoctorReport } from '../core/codex-compat/codex-compat-report.js';
4
4
  import { codexVersionReport } from '../core/codex-compat/codex-version.js';
5
5
  import { codexSchemaSnapshotReport } from '../core/codex-compat/codex-schema-snapshot.js';
6
+ import { detectCodex0141Capability } from '../core/codex-control/codex-0141-capability.js';
6
7
  export async function run(_command, args = []) {
7
8
  const action = args[0] || 'compatibility';
8
9
  if (action === 'compatibility' || action === 'compat') {
@@ -24,6 +25,19 @@ export async function run(_command, args = []) {
24
25
  console.log(`Codex detected: ${result.detected.version || 'not installed'} (${result.policy.status})`);
25
26
  return;
26
27
  }
28
+ if (action === '0.141' || action === '0141' || action === 'rust-v0.141.0') {
29
+ const result = await detectCodex0141Capability();
30
+ if (flag(args, '--json'))
31
+ return printJson(result);
32
+ console.log(`Codex 0.141 compatibility: ${result.ok ? 'ok' : 'blocked'}`);
33
+ for (const blocker of result.blockers || [])
34
+ console.log(`- blocker: ${blocker}`);
35
+ for (const warning of result.warnings || [])
36
+ console.log(`- warning: ${warning}`);
37
+ if (!result.ok)
38
+ process.exitCode = 1;
39
+ return;
40
+ }
27
41
  if (action === 'doctor') {
28
42
  const result = await codexDoctorReport();
29
43
  if (flag(args, '--json'))
@@ -42,7 +56,7 @@ export async function run(_command, args = []) {
42
56
  process.exitCode = 1;
43
57
  return;
44
58
  }
45
- console.error('Usage: sks codex compatibility|version|doctor|schema [--json]');
59
+ console.error('Usage: sks codex compatibility|version|doctor|schema|0.141 [--json]');
46
60
  process.exitCode = 1;
47
61
  }
48
62
  //# sourceMappingURL=codex.js.map
@@ -0,0 +1,2 @@
1
+ export { GLM_CODEX_APP_PROFILE_ID, GLM_CODEX_APP_PROFILE_LABEL, buildGlmCodexAppModelProfile } from '../providers/glm/glm-52-profile.js';
2
+ //# sourceMappingURL=glm-model-profile.js.map
@@ -0,0 +1,61 @@
1
+ import path from 'node:path';
2
+ import { readJson, writeJsonAtomic, nowIso } from '../fsx.js';
3
+ import { buildGlmCodexAppModelProfile } from './glm-model-profile.js';
4
+ import { validateGlmCodexAppModelProfile } from './glm-profile-schema.js';
5
+ import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
6
+ export async function installCodexAppGlmProfile(input) {
7
+ const root = path.resolve(input.root);
8
+ const profile = buildGlmCodexAppModelProfile();
9
+ const profilePath = path.join(root, '.sneakoscope', 'codex-app', 'glm-model-profile.json');
10
+ const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-app-glm-profile.json');
11
+ const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
12
+ const warnings = [
13
+ ...key.warnings,
14
+ ...(key.key ? [] : ['openrouter_key_missing_until_sks_--mad_--glm_--repair'])
15
+ ];
16
+ if (input.apply !== false)
17
+ await writeJsonAtomic(profilePath, profile);
18
+ const result = {
19
+ schema: 'sks.codex-app-glm-profile-result.v1',
20
+ generated_at: nowIso(),
21
+ ok: true,
22
+ status: input.apply === false ? 'valid' : 'installed',
23
+ profile,
24
+ profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
25
+ report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
26
+ openrouter_key_source: key.source,
27
+ blockers: [],
28
+ warnings
29
+ };
30
+ await writeJsonAtomic(reportPath, result).catch(() => undefined);
31
+ return result;
32
+ }
33
+ export async function doctorCodexAppGlmProfile(input) {
34
+ const root = path.resolve(input.root);
35
+ const profilePath = path.join(root, '.sneakoscope', 'codex-app', 'glm-model-profile.json');
36
+ const reportPath = path.join(root, '.sneakoscope', 'reports', 'codex-app-glm-profile.json');
37
+ const existing = await readJson(profilePath, null);
38
+ const validation = validateGlmCodexAppModelProfile(existing);
39
+ const key = await resolveOpenRouterApiKey({ env: input.env || process.env });
40
+ const profile = validation.profile || buildGlmCodexAppModelProfile();
41
+ const blockers = [...validation.blockers];
42
+ const warnings = [
43
+ ...key.warnings,
44
+ ...(key.key ? [] : ['openrouter_key_missing_until_sks_--mad_--glm_--repair'])
45
+ ];
46
+ const result = {
47
+ schema: 'sks.codex-app-glm-profile-result.v1',
48
+ generated_at: nowIso(),
49
+ ok: blockers.length === 0,
50
+ status: blockers.length === 0 ? 'valid' : 'blocked',
51
+ profile,
52
+ profile_path: '.sneakoscope/codex-app/glm-model-profile.json',
53
+ report_path: '.sneakoscope/reports/codex-app-glm-profile.json',
54
+ openrouter_key_source: key.source,
55
+ blockers,
56
+ warnings
57
+ };
58
+ await writeJsonAtomic(reportPath, result).catch(() => undefined);
59
+ return result;
60
+ }
61
+ //# sourceMappingURL=glm-profile-installer.js.map
@@ -0,0 +1,24 @@
1
+ import { GLM_CODEX_APP_PROFILE_ID, buildGlmCodexAppModelProfile } from './glm-model-profile.js';
2
+ import { GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE } from '../providers/glm/glm-52-settings.js';
3
+ export function validateGlmCodexAppModelProfile(value) {
4
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
5
+ return { ok: false, blockers: ['glm_codex_app_profile_missing'], profile: null };
6
+ }
7
+ const profile = value;
8
+ const expected = buildGlmCodexAppModelProfile();
9
+ const blockers = [
10
+ profile.schema === expected.schema ? null : 'glm_codex_app_profile_invalid_schema',
11
+ profile.id === GLM_CODEX_APP_PROFILE_ID ? null : 'glm_codex_app_profile_invalid_id',
12
+ profile.provider === 'openrouter' ? null : 'glm_codex_app_profile_invalid_provider',
13
+ profile.model === GLM_52_OPENROUTER_MODEL ? null : 'glm_codex_app_profile_invalid_model',
14
+ profile.mode === GLM_MAD_MODE ? null : 'glm_codex_app_profile_invalid_mode',
15
+ profile.strictModelLock === true ? null : 'glm_codex_app_profile_not_strict',
16
+ profile.gptFallbackAllowed === false ? null : 'glm_codex_app_profile_allows_gpt_fallback'
17
+ ].filter((item) => Boolean(item));
18
+ return {
19
+ ok: blockers.length === 0,
20
+ blockers,
21
+ profile: blockers.length === 0 ? profile : null
22
+ };
23
+ }
24
+ //# sourceMappingURL=glm-profile-schema.js.map
@@ -0,0 +1,95 @@
1
+ import path from 'node:path';
2
+ import { findCodexBinary } from '../codex-adapter.js';
3
+ import { compareSemverLike, parseCodexVersionText } from '../codex-compat/codex-version-policy.js';
4
+ import { nowIso, runProcess, writeJsonAtomic } from '../fsx.js';
5
+ export const CODEX_0141_FEATURE_KEYS = [
6
+ 'noise_relay_delegated',
7
+ 'native_cwd_shell_path_preserved',
8
+ 'permission_paths_preserved',
9
+ 'selected_plugin_mcp_per_thread',
10
+ 'auth_curated_marketplaces',
11
+ 'child_threads_api',
12
+ 'external_agent_import_results',
13
+ 'rate_limit_reset_credits',
14
+ 'realtime_startup_context_control',
15
+ 'tui_prompt_auto_resolve',
16
+ 'hook_trust_resume_persistence',
17
+ 'post_tool_use_blocking_respected',
18
+ 'plugin_app_mcp_dedupe',
19
+ 'windows_sandbox_repairs_delegated',
20
+ 'idle_exec_relay_keepalive',
21
+ 'wait_agent_interrupt_native',
22
+ 'sqlite_wal_reset_fix_delegated',
23
+ 'tls_p521_native',
24
+ 'tool_heavy_copy_reduction',
25
+ 'prompt_image_cache_bound_64_mib',
26
+ 'feedback_upload_bound_8_threads',
27
+ 'terminal_resize_reflow_always'
28
+ ];
29
+ export async function detectCodex0141Capability(input = {}) {
30
+ const fake = process.env.SKS_CODEX_0141_FAKE === '1';
31
+ const codexBin = fake ? input.codexBin || process.env.CODEX_BIN || 'codex' : input.codexBin || process.env.CODEX_BIN || await findCodexBinary();
32
+ const versionText = fake ? String(process.env.SKS_CODEX_VERSION_FAKE || 'codex-cli 0.141.0') : await readCodexVersionText(codexBin);
33
+ const parsed = parseCodexVersionText(versionText);
34
+ const supports0141 = Boolean(parsed && compareSemverLike(parsed, '0.141.0') >= 0);
35
+ const featureStates = Object.fromEntries(CODEX_0141_FEATURE_KEYS.map((key) => [key, featureStateFor(key, supports0141)]));
36
+ const blockers = [
37
+ ...(!codexBin ? ['codex_cli_missing'] : []),
38
+ ...(supports0141 ? [] : ['codex_0_141_required_for_0141_features'])
39
+ ];
40
+ return {
41
+ schema: 'sks.codex-0141-capability.v1',
42
+ generated_at: nowIso(),
43
+ ok: blockers.length === 0,
44
+ codex_version: parsed,
45
+ supports_0141: supports0141,
46
+ feature_states: featureStates,
47
+ blockers,
48
+ warnings: supports0141
49
+ ? CODEX_0141_FEATURE_KEYS.map((key) => `codex_0141_${key}_assumed_by_version`)
50
+ : [],
51
+ codex_bin: codexBin || null,
52
+ release_source: 'https://github.com/openai/codex/releases/tag/rust-v0.141.0'
53
+ };
54
+ }
55
+ export async function writeCodex0141CapabilityArtifacts(root, input = {}) {
56
+ const report = await detectCodex0141Capability({ codexBin: input.codexBin || null });
57
+ const rootArtifact = path.join(root, '.sneakoscope', 'codex-0141-capability.json');
58
+ await writeJsonAtomic(rootArtifact, report);
59
+ let missionArtifact = null;
60
+ if (input.missionId) {
61
+ missionArtifact = path.join(root, '.sneakoscope', 'missions', input.missionId, 'codex-0141-capability.json');
62
+ await writeJsonAtomic(missionArtifact, report);
63
+ }
64
+ return { report, root_artifact: rootArtifact, mission_artifact: missionArtifact };
65
+ }
66
+ function featureStateFor(key, supports0141) {
67
+ return {
68
+ supported: supports0141,
69
+ certainty: supports0141 ? 'assumed_by_version' : 'failed',
70
+ evidence: supports0141 ? ['codex_version>=0.141.0', `release:rust-v0.141.0:${key}`] : [],
71
+ blockers: supports0141 ? [] : ['codex_0_141_required_for_0141_features'],
72
+ sks_policy: sksPolicyFor(key)
73
+ };
74
+ }
75
+ function sksPolicyFor(key) {
76
+ if (key.includes('dedupe'))
77
+ return 'dedupe';
78
+ if (key.includes('bound') || key.includes('cache') || key.includes('feedback'))
79
+ return 'bound';
80
+ if (key.includes('blocking') || key.includes('trust'))
81
+ return 'respect';
82
+ return 'delegate';
83
+ }
84
+ async function readCodexVersionText(codexBin) {
85
+ if (!codexBin)
86
+ return null;
87
+ const result = await runProcess(codexBin, ['--version'], { timeoutMs: 10_000, maxOutputBytes: 16 * 1024 }).catch((err) => ({
88
+ code: 1,
89
+ stdout: '',
90
+ stderr: err instanceof Error ? err.message : String(err)
91
+ }));
92
+ const text = `${result.stdout || ''}${result.stderr || ''}`.trim();
93
+ return result.code === 0 ? text : text || null;
94
+ }
95
+ //# sourceMappingURL=codex-0141-capability.js.map
@@ -0,0 +1,5 @@
1
+ import { runMadGlmMode } from '../providers/glm/glm-mad-mode.js';
2
+ export async function glmCommand(args = []) {
3
+ return runMadGlmMode(args);
4
+ }
5
+ //# sourceMappingURL=glm-command.js.map
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '4.0.2';
8
+ export const PACKAGE_VERSION = '4.0.3';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -0,0 +1,30 @@
1
+ import { GLM_52_DEFAULT_REQUEST_SETTINGS, GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE } from './glm-52-settings.js';
2
+ export const GLM_CODEX_APP_PROFILE_ID = 'sks/glm-5.2-mad';
3
+ export const GLM_CODEX_APP_PROFILE_LABEL = 'GLM 5.2 (MAD / OpenRouter)';
4
+ export function buildGlmCodexAppModelProfile() {
5
+ return {
6
+ schema: 'sks.codex-app-model-profile.v1',
7
+ id: GLM_CODEX_APP_PROFILE_ID,
8
+ label: GLM_CODEX_APP_PROFILE_LABEL,
9
+ provider: 'openrouter',
10
+ model: GLM_52_OPENROUTER_MODEL,
11
+ mode: GLM_MAD_MODE,
12
+ strictModelLock: true,
13
+ gptFallbackAllowed: false,
14
+ requiresSecret: 'openrouter-api-key',
15
+ defaultSettings: {
16
+ temperature: GLM_52_DEFAULT_REQUEST_SETTINGS.temperature,
17
+ top_p: GLM_52_DEFAULT_REQUEST_SETTINGS.top_p,
18
+ reasoning_effort: 'high',
19
+ tool_choice: 'auto',
20
+ parallel_tool_calls: false
21
+ },
22
+ codexCompatibility: {
23
+ target: 'rust-v0.141.0',
24
+ selectedExecutorPluginMcp: 'defer-to-codex-native',
25
+ duplicateAppMcpDeclarations: 'dedupe-by-codex',
26
+ cwdShellPathSemantics: 'preserve-codex-native'
27
+ }
28
+ };
29
+ }
30
+ //# sourceMappingURL=glm-52-profile.js.map
@@ -0,0 +1,34 @@
1
+ import { GLM_52_DEFAULT_REQUEST_SETTINGS, GLM_52_OPENROUTER_MODEL, clampGlm52MaxTokens } from './glm-52-settings.js';
2
+ export function buildGlm52Request(input) {
3
+ const request = {
4
+ model: GLM_52_OPENROUTER_MODEL,
5
+ messages: input.messages,
6
+ stream: input.stream ?? GLM_52_DEFAULT_REQUEST_SETTINGS.stream,
7
+ temperature: GLM_52_DEFAULT_REQUEST_SETTINGS.temperature,
8
+ top_p: GLM_52_DEFAULT_REQUEST_SETTINGS.top_p,
9
+ reasoning: { effort: input.reasoningEffort ?? 'high' },
10
+ max_tokens: clampGlm52MaxTokens(input.maxTokens),
11
+ tool_choice: input.toolChoice ?? 'auto',
12
+ parallel_tool_calls: input.parallelToolCalls ?? false,
13
+ provider: {
14
+ allow_fallbacks: false,
15
+ require_parameters: true,
16
+ sort: input.providerSort ?? 'throughput'
17
+ }
18
+ };
19
+ return {
20
+ ...request,
21
+ ...(input.tools ? { tools: input.tools } : {}),
22
+ ...(input.responseFormat ? { response_format: input.responseFormat } : {})
23
+ };
24
+ }
25
+ export function buildGlm52KeyValidationRequest() {
26
+ return buildGlm52Request({
27
+ messages: [{ role: 'user', content: 'Reply with OK.' }],
28
+ stream: false,
29
+ maxTokens: 1,
30
+ toolChoice: 'none',
31
+ parallelToolCalls: false
32
+ });
33
+ }
34
+ //# sourceMappingURL=glm-52-request.js.map
@@ -0,0 +1,34 @@
1
+ import { GLM_52_OPENROUTER_MODEL } from './glm-52-settings.js';
2
+ export function assertGlm52ActualModel(responseModel) {
3
+ if (!responseModel) {
4
+ return {
5
+ ok: false,
6
+ code: 'glm_model_missing',
7
+ requestedModel: GLM_52_OPENROUTER_MODEL,
8
+ strictModelLock: true,
9
+ gptFallbackAllowed: false
10
+ };
11
+ }
12
+ const normalized = responseModel.toLowerCase();
13
+ if (normalized === GLM_52_OPENROUTER_MODEL ||
14
+ normalized.startsWith(`${GLM_52_OPENROUTER_MODEL}-`) ||
15
+ normalized.includes('glm-5.2')) {
16
+ return {
17
+ ok: true,
18
+ code: 'ok',
19
+ actualModel: responseModel,
20
+ requestedModel: GLM_52_OPENROUTER_MODEL,
21
+ strictModelLock: true,
22
+ gptFallbackAllowed: false
23
+ };
24
+ }
25
+ return {
26
+ ok: false,
27
+ code: 'glm_model_mismatch',
28
+ actualModel: responseModel,
29
+ requestedModel: GLM_52_OPENROUTER_MODEL,
30
+ strictModelLock: true,
31
+ gptFallbackAllowed: false
32
+ };
33
+ }
34
+ //# sourceMappingURL=glm-52-response-guard.js.map
@@ -0,0 +1,26 @@
1
+ export { OPENROUTER_CHAT_COMPLETIONS_URL } from '../openrouter/openrouter-types.js';
2
+ export const GLM_52_OPENROUTER_MODEL = 'z-ai/glm-5.2';
3
+ export const GLM_MAD_MODE = 'mad-glm';
4
+ export const GLM_52_MAX_TOKENS_DEFAULT = 32768;
5
+ export const GLM_52_MAX_TOKENS_LONG = 65536;
6
+ export const GLM_52_MAX_TOKENS_XLONG = 131072;
7
+ export const GLM_52_TOP_PROVIDER_MAX_COMPLETION_TOKENS = 262144;
8
+ export const GLM_52_DEFAULT_REQUEST_SETTINGS = {
9
+ model: GLM_52_OPENROUTER_MODEL,
10
+ temperature: 1,
11
+ top_p: 0.95,
12
+ reasoning_effort: 'high',
13
+ stream: true,
14
+ provider: {
15
+ allow_fallbacks: false,
16
+ require_parameters: true
17
+ },
18
+ tool_choice: 'auto',
19
+ parallel_tool_calls: false,
20
+ max_tokens: GLM_52_MAX_TOKENS_DEFAULT
21
+ };
22
+ export function clampGlm52MaxTokens(value) {
23
+ const numeric = Number.isFinite(value) ? Math.floor(Number(value)) : GLM_52_MAX_TOKENS_DEFAULT;
24
+ return Math.max(1, Math.min(numeric, GLM_52_TOP_PROVIDER_MAX_COMPLETION_TOKENS));
25
+ }
26
+ //# sourceMappingURL=glm-52-settings.js.map
@@ -0,0 +1,242 @@
1
+ import readline from 'node:readline/promises';
2
+ import path from 'node:path';
3
+ import { stdin as input, stdout as output } from 'node:process';
4
+ import { printJson } from '../../../cli/output.js';
5
+ import { flag } from '../../../cli/args.js';
6
+ import { nowIso, writeJsonAtomic } from '../../fsx.js';
7
+ import {} from '../openrouter/openrouter-types.js';
8
+ import { sendOpenRouterChatCompletion } from '../openrouter/openrouter-client.js';
9
+ import { resolveOpenRouterApiKey, writeStoredOpenRouterKey } from '../openrouter/openrouter-secret-store.js';
10
+ import { redactOpenRouterKey } from '../../security/redact-secrets.js';
11
+ import { buildGlmCodexAppModelProfile } from './glm-52-profile.js';
12
+ import { buildGlm52KeyValidationRequest, buildGlm52Request } from './glm-52-request.js';
13
+ import { assertGlm52ActualModel } from './glm-52-response-guard.js';
14
+ import { GLM_52_OPENROUTER_MODEL, GLM_MAD_MODE, OPENROUTER_CHAT_COMPLETIONS_URL } from './glm-52-settings.js';
15
+ export async function runMadGlmMode(args = [], adapters = {}) {
16
+ const runtime = buildDefaultAdapters(adapters);
17
+ const repair = flag(args, '--repair');
18
+ const noSaveKey = flag(args, '--no-save-key');
19
+ const skipValidation = flag(args, '--skip-validation');
20
+ const json = flag(args, '--json');
21
+ const profile = buildGlmCodexAppModelProfile();
22
+ let result;
23
+ if (repair) {
24
+ const key = await runtime.promptSecret('OpenRouter API key is required for GLM 5.2 mode.\nEnter OpenRouter API key: ');
25
+ if (!key) {
26
+ result = baseResult({
27
+ status: 'blocked',
28
+ blockers: ['glm_key_prompt_cancelled'],
29
+ warnings: []
30
+ });
31
+ }
32
+ else {
33
+ if (!noSaveKey)
34
+ await runtime.writeSecret(key);
35
+ const validation = skipValidation
36
+ ? { ok: true, value: validationValue(null) }
37
+ : await runtime.validateOpenRouterKey(key);
38
+ result = validation.ok
39
+ ? baseResult({
40
+ status: 'ready',
41
+ ...(validation.value.actual_model ? { actual_model: validation.value.actual_model } : {}),
42
+ openrouter_key_source: noSaveKey ? 'prompt' : 'user-secret-store',
43
+ key_preview: redactOpenRouterKey(key),
44
+ blockers: [],
45
+ warnings: noSaveKey ? ['openrouter_key_not_saved'] : []
46
+ })
47
+ : baseResult({
48
+ status: 'blocked',
49
+ openrouter_key_source: noSaveKey ? 'prompt' : 'user-secret-store',
50
+ key_preview: redactOpenRouterKey(key),
51
+ blockers: [validation.error.code],
52
+ warnings: []
53
+ });
54
+ }
55
+ }
56
+ else {
57
+ const resolved = await resolveOpenRouterApiKey({ env: runtime.env });
58
+ if (!resolved.key && process.stdin.isTTY) {
59
+ const key = await runtime.promptSecret('OpenRouter API key is required for GLM 5.2 mode.\nEnter OpenRouter API key: ');
60
+ if (!key) {
61
+ result = baseResult({ status: 'blocked', blockers: ['glm_key_prompt_cancelled'], warnings: [] });
62
+ }
63
+ else {
64
+ const save = noSaveKey ? false : await runtime.promptConfirm('Save this key for future SKS GLM runs? [Y/n] ', true);
65
+ if (save)
66
+ await runtime.writeSecret(key);
67
+ result = baseResult({
68
+ status: 'ready',
69
+ openrouter_key_source: save ? 'user-secret-store' : 'prompt',
70
+ key_preview: redactOpenRouterKey(key),
71
+ blockers: [],
72
+ warnings: save ? [] : ['openrouter_key_not_saved']
73
+ });
74
+ }
75
+ }
76
+ else if (!resolved.key) {
77
+ result = baseResult({
78
+ status: 'blocked',
79
+ blockers: resolved.blockers,
80
+ warnings: ['set_OPENROUTER_API_KEY_or_run_sks_--mad_--glm_--repair']
81
+ });
82
+ }
83
+ else {
84
+ result = baseResult({
85
+ status: 'ready',
86
+ ...(resolved.source ? { openrouter_key_source: resolved.source } : {}),
87
+ key_preview: resolved.key_preview,
88
+ blockers: [],
89
+ warnings: resolved.warnings
90
+ });
91
+ }
92
+ }
93
+ await writeGlmModeArtifacts(runtime.cwd, result, profile, runtime.nowIso()).catch(() => undefined);
94
+ if (json)
95
+ printJson(result);
96
+ else
97
+ printHumanGlmResult(result, runtime.log);
98
+ if (!result.ok)
99
+ process.exitCode = 1;
100
+ return result;
101
+ }
102
+ function baseResult(input) {
103
+ const result = {
104
+ schema: 'sks.glm-mode-result.v1',
105
+ ok: input.blockers.length === 0 && input.status !== 'failed',
106
+ status: input.status,
107
+ mode: GLM_MAD_MODE,
108
+ provider: 'openrouter',
109
+ model: GLM_52_OPENROUTER_MODEL,
110
+ requested_model: GLM_52_OPENROUTER_MODEL,
111
+ strict_model_lock: true,
112
+ gpt_fallback_allowed: false,
113
+ codex_app_profile_id: 'sks/glm-5.2-mad',
114
+ blockers: input.blockers,
115
+ warnings: input.warnings
116
+ };
117
+ return {
118
+ ...result,
119
+ ...(input.actual_model ? { actual_model: input.actual_model } : {}),
120
+ ...(input.openrouter_key_source ? { openrouter_key_source: input.openrouter_key_source } : {}),
121
+ ...(input.key_preview !== undefined ? { key_preview: input.key_preview } : {})
122
+ };
123
+ }
124
+ function buildDefaultAdapters(overrides) {
125
+ return {
126
+ nowIso: overrides.nowIso || nowIso,
127
+ env: overrides.env || process.env,
128
+ cwd: overrides.cwd || process.cwd(),
129
+ promptSecret: overrides.promptSecret || promptLine,
130
+ promptConfirm: overrides.promptConfirm || promptConfirmLine,
131
+ writeSecret: overrides.writeSecret || (async (value) => {
132
+ await writeStoredOpenRouterKey(value);
133
+ }),
134
+ validateOpenRouterKey: overrides.validateOpenRouterKey || validateOpenRouterKey,
135
+ sendOpenRouterRequest: overrides.sendOpenRouterRequest || (async (request, key) => sendOpenRouterChatCompletion({ request, apiKey: key })),
136
+ log: overrides.log || ((message) => console.log(message))
137
+ };
138
+ }
139
+ async function validateOpenRouterKey(key) {
140
+ const response = await sendOpenRouterChatCompletion({
141
+ apiKey: key,
142
+ request: buildGlm52KeyValidationRequest()
143
+ });
144
+ if (!response.ok)
145
+ return response;
146
+ const guard = assertGlm52ActualModel(response.value.model);
147
+ if (!guard.ok) {
148
+ return {
149
+ ok: false,
150
+ error: {
151
+ code: guard.code,
152
+ message: 'GLM model lock violated.',
153
+ severity: 'blocked'
154
+ }
155
+ };
156
+ }
157
+ return { ok: true, value: validationValue(response.value.model || null) };
158
+ }
159
+ function validationValue(actualModel) {
160
+ return {
161
+ schema: 'sks.openrouter-key-validation.v1',
162
+ ok: true,
163
+ requested_model: GLM_52_OPENROUTER_MODEL,
164
+ actual_model: actualModel,
165
+ strict_model_lock: true,
166
+ gpt_fallback_allowed: false
167
+ };
168
+ }
169
+ async function writeGlmModeArtifacts(cwd, result, profile, generatedAt) {
170
+ const dir = path.join(cwd, '.sneakoscope', 'glm');
171
+ await writeJsonAtomic(path.join(dir, 'mad-glm-session.json'), {
172
+ schema: 'sks.glm-mad-session.v1',
173
+ generated_at: generatedAt,
174
+ result,
175
+ profile_id: profile.id
176
+ });
177
+ await writeJsonAtomic(path.join(dir, 'openrouter-request-summary.json'), {
178
+ schema: 'sks.openrouter-request-summary.v1',
179
+ generated_at: generatedAt,
180
+ endpoint: OPENROUTER_CHAT_COMPLETIONS_URL,
181
+ model: GLM_52_OPENROUTER_MODEL,
182
+ temperature: 1,
183
+ top_p: 0.95,
184
+ reasoning_effort: 'high',
185
+ stream: true,
186
+ provider_allow_fallbacks: false,
187
+ require_parameters: true,
188
+ key_source: result.openrouter_key_source || null,
189
+ key_preview: result.key_preview || null
190
+ });
191
+ await writeJsonAtomic(path.join(dir, 'model-guard.json'), {
192
+ schema: 'sks.glm-model-guard.v1',
193
+ generated_at: generatedAt,
194
+ requested_model: GLM_52_OPENROUTER_MODEL,
195
+ actual_model: result.actual_model || null,
196
+ accepted: result.actual_model ? assertGlm52ActualModel(result.actual_model).ok : result.ok,
197
+ strict_model_lock: true,
198
+ gpt_fallback_allowed: false,
199
+ blockers: result.blockers
200
+ });
201
+ }
202
+ function printHumanGlmResult(result, log) {
203
+ log(`GLM 5.2 MAD mode: ${result.ok ? result.status : 'blocked'}`);
204
+ log(`Model: ${result.model}`);
205
+ log(`GPT fallback: ${result.gpt_fallback_allowed ? 'allowed' : 'blocked'}`);
206
+ if (result.openrouter_key_source)
207
+ log(`OpenRouter key: ${result.openrouter_key_source} ${result.key_preview || ''}`.trim());
208
+ for (const blocker of result.blockers)
209
+ log(`- blocker: ${blocker}`);
210
+ for (const warning of result.warnings)
211
+ log(`- warning: ${warning}`);
212
+ }
213
+ async function promptLine(prompt) {
214
+ if (!process.stdin.isTTY)
215
+ return null;
216
+ const rl = readline.createInterface({ input, output });
217
+ try {
218
+ const answer = await rl.question(prompt);
219
+ return answer.trim() || null;
220
+ }
221
+ finally {
222
+ rl.close();
223
+ }
224
+ }
225
+ async function promptConfirmLine(prompt, defaultYes) {
226
+ if (!process.stdin.isTTY)
227
+ return defaultYes;
228
+ const answer = await promptLine(prompt);
229
+ if (!answer)
230
+ return defaultYes;
231
+ return !/^n(o)?$/i.test(answer);
232
+ }
233
+ export function buildGlmModeDryRunRequest() {
234
+ return buildGlm52Request({
235
+ messages: [{ role: 'user', content: 'SKS GLM dry run.' }],
236
+ stream: false,
237
+ maxTokens: 1,
238
+ toolChoice: 'none',
239
+ parallelToolCalls: false
240
+ });
241
+ }
242
+ //# sourceMappingURL=glm-mad-mode.js.map
@@ -0,0 +1,44 @@
1
+ import { OPENROUTER_CHAT_COMPLETIONS_URL } from './openrouter-types.js';
2
+ import { invalidOpenRouterResponseIssue, normalizeOpenRouterError } from './openrouter-error.js';
3
+ import { redactOpenRouterString } from '../../security/redact-secrets.js';
4
+ export async function sendOpenRouterChatCompletion(input) {
5
+ try {
6
+ const doFetch = input.fetchImpl || fetch;
7
+ const response = await doFetch(input.endpoint || OPENROUTER_CHAT_COMPLETIONS_URL, {
8
+ method: 'POST',
9
+ headers: {
10
+ Authorization: `Bearer ${input.apiKey}`,
11
+ 'Content-Type': 'application/json',
12
+ 'X-OpenRouter-Title': 'Sneakoscope-Codex'
13
+ },
14
+ body: JSON.stringify(input.request)
15
+ });
16
+ const text = await response.text();
17
+ if (!response.ok)
18
+ return { ok: false, error: normalizeOpenRouterError(response.status, text) };
19
+ return parseOpenRouterResponse(text);
20
+ }
21
+ catch (err) {
22
+ return {
23
+ ok: false,
24
+ error: {
25
+ code: 'glm_openrouter_request_failed',
26
+ message: redactOpenRouterString(err instanceof Error ? err.message : String(err)),
27
+ severity: 'failed'
28
+ }
29
+ };
30
+ }
31
+ }
32
+ export function parseOpenRouterResponse(text) {
33
+ try {
34
+ const parsed = JSON.parse(text);
35
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
36
+ return { ok: false, error: invalidOpenRouterResponseIssue('OpenRouter response was not an object.', text) };
37
+ }
38
+ return { ok: true, value: parsed };
39
+ }
40
+ catch {
41
+ return { ok: false, error: invalidOpenRouterResponseIssue('OpenRouter response was not valid JSON.', text) };
42
+ }
43
+ }
44
+ //# sourceMappingURL=openrouter-client.js.map
@@ -0,0 +1,37 @@
1
+ import { redactOpenRouterString } from '../../security/redact-secrets.js';
2
+ export function normalizeOpenRouterError(status, body) {
3
+ const code = status === 401 || status === 403
4
+ ? 'glm_openrouter_unauthorized'
5
+ : status === 429
6
+ ? 'glm_openrouter_rate_limited'
7
+ : status >= 500
8
+ ? 'glm_openrouter_provider_unavailable'
9
+ : 'glm_openrouter_request_failed';
10
+ return {
11
+ code,
12
+ message: statusMessage(code),
13
+ severity: status >= 500 ? 'failed' : 'blocked',
14
+ status,
15
+ redacted_body_tail: redactOpenRouterString(body).slice(-2000)
16
+ };
17
+ }
18
+ export function invalidOpenRouterResponseIssue(message, body) {
19
+ const issue = {
20
+ code: 'glm_openrouter_invalid_response',
21
+ message,
22
+ severity: 'failed'
23
+ };
24
+ return body ? { ...issue, redacted_body_tail: redactOpenRouterString(body).slice(-2000) } : issue;
25
+ }
26
+ function statusMessage(code) {
27
+ if (code === 'glm_openrouter_unauthorized')
28
+ return 'OpenRouter rejected the GLM API key.';
29
+ if (code === 'glm_openrouter_rate_limited')
30
+ return 'OpenRouter rate limited the GLM request.';
31
+ if (code === 'glm_openrouter_provider_unavailable')
32
+ return 'OpenRouter provider is unavailable for GLM 5.2.';
33
+ if (code === 'glm_openrouter_invalid_response')
34
+ return 'OpenRouter returned an invalid response.';
35
+ return 'OpenRouter request failed.';
36
+ }
37
+ //# sourceMappingURL=openrouter-error.js.map
@@ -0,0 +1,113 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { redactOpenRouterKey } from '../../security/redact-secrets.js';
6
+ export const OPENROUTER_KEY_ENV_NAMES = ['OPENROUTER_API_KEY', 'SKS_OPENROUTER_API_KEY'];
7
+ export function openRouterSecretPaths(env = process.env) {
8
+ const sksHome = path.resolve(env.SKS_HOME || path.join(env.HOME || os.homedir(), '.sneakoscope'));
9
+ const secretDir = path.join(sksHome, 'secrets');
10
+ return {
11
+ sksHome,
12
+ secretDir,
13
+ keyPath: path.join(secretDir, 'openrouter-api-key'),
14
+ metadataPath: path.join(secretDir, 'openrouter-api-key.json')
15
+ };
16
+ }
17
+ export async function resolveOpenRouterApiKey(input = {}) {
18
+ const env = input.env || process.env;
19
+ for (const name of OPENROUTER_KEY_ENV_NAMES) {
20
+ const value = String(env[name] || '').trim();
21
+ if (value) {
22
+ return {
23
+ key: value,
24
+ source: 'env',
25
+ env_var: name,
26
+ key_preview: redactOpenRouterKey(value),
27
+ blockers: [],
28
+ warnings: name === 'OPENROUTER_API_KEY' ? [] : ['using_sks_openrouter_api_key_env']
29
+ };
30
+ }
31
+ }
32
+ const stored = await readStoredOpenRouterKey(input.paths || openRouterSecretPaths(env));
33
+ if (stored) {
34
+ return {
35
+ key: stored,
36
+ source: 'user-secret-store',
37
+ key_preview: redactOpenRouterKey(stored),
38
+ blockers: [],
39
+ warnings: []
40
+ };
41
+ }
42
+ return {
43
+ key: null,
44
+ source: null,
45
+ key_preview: null,
46
+ blockers: ['glm_missing_openrouter_key'],
47
+ warnings: []
48
+ };
49
+ }
50
+ export async function readStoredOpenRouterKey(paths) {
51
+ try {
52
+ const text = await fs.readFile(paths.keyPath, 'utf8');
53
+ const key = text.trim();
54
+ return key || null;
55
+ }
56
+ catch {
57
+ return null;
58
+ }
59
+ }
60
+ export async function writeStoredOpenRouterKey(value, input = {}) {
61
+ const key = value.trim();
62
+ if (!key)
63
+ throw new Error('OpenRouter key is empty.');
64
+ const paths = input.paths || openRouterSecretPaths();
65
+ const nowIso = input.nowIso || (() => new Date().toISOString());
66
+ const timestamp = nowIso();
67
+ const previous = input.previousRecord ?? await readOpenRouterKeyRecord(paths);
68
+ await ensureSecretDir(paths.secretDir);
69
+ const tmp = `${paths.keyPath}.${process.pid}.${crypto.randomBytes(3).toString('hex')}.tmp`;
70
+ try {
71
+ const handle = await fs.open(tmp, 'w', 0o600);
72
+ try {
73
+ await handle.writeFile(`${key}\n`, 'utf8');
74
+ await handle.sync().catch(() => undefined);
75
+ }
76
+ finally {
77
+ await handle.close().catch(() => undefined);
78
+ }
79
+ await fs.chmod(tmp, 0o600).catch(() => undefined);
80
+ await fs.rename(tmp, paths.keyPath);
81
+ await fs.chmod(paths.keyPath, 0o600).catch(() => undefined);
82
+ }
83
+ catch (err) {
84
+ await fs.rm(tmp, { force: true }).catch(() => undefined);
85
+ throw err;
86
+ }
87
+ const record = {
88
+ schema: 'sks.openrouter-key.v1',
89
+ created_at: previous?.created_at || timestamp,
90
+ updated_at: timestamp,
91
+ key_hash: crypto.createHash('sha256').update(key).digest('hex'),
92
+ key_preview: redactOpenRouterKey(key)
93
+ };
94
+ await fs.writeFile(paths.metadataPath, `${JSON.stringify(record, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
95
+ await fs.chmod(paths.metadataPath, 0o600).catch(() => undefined);
96
+ return record;
97
+ }
98
+ export async function readOpenRouterKeyRecord(paths) {
99
+ try {
100
+ const parsed = JSON.parse(await fs.readFile(paths.metadataPath, 'utf8'));
101
+ if (parsed.schema !== 'sks.openrouter-key.v1' || !parsed.key_hash || !parsed.key_preview)
102
+ return null;
103
+ return parsed;
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ async function ensureSecretDir(secretDir) {
110
+ await fs.mkdir(secretDir, { recursive: true, mode: 0o700 });
111
+ await fs.chmod(secretDir, 0o700).catch(() => undefined);
112
+ }
113
+ //# sourceMappingURL=openrouter-secret-store.js.map
@@ -0,0 +1,2 @@
1
+ export const OPENROUTER_CHAT_COMPLETIONS_URL = 'https://openrouter.ai/api/v1/chat/completions';
2
+ //# sourceMappingURL=openrouter-types.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=results.js.map
@@ -1,6 +1,8 @@
1
1
  const SECRET_ENV_NAMES = [
2
2
  'CODEX_ACCESS_TOKEN',
3
3
  'OPENAI_API_KEY',
4
+ 'OPENROUTER_API_KEY',
5
+ 'SKS_OPENROUTER_API_KEY',
4
6
  'CODEX_LB_API_KEY',
5
7
  'ANTHROPIC_API_KEY',
6
8
  'GITHUB_TOKEN',
@@ -8,6 +10,8 @@ const SECRET_ENV_NAMES = [
8
10
  ];
9
11
  const SECRET_PATTERNS = [
10
12
  /\bsk-proj-[A-Za-z0-9_-]{12,}\b/g,
13
+ /\bsk-or-v1-[A-Za-z0-9_-]{12,}\b/g,
14
+ /\bsk-or-[A-Za-z0-9_-]{12,}\b/g,
11
15
  /\bsk-[A-Za-z0-9_-]{20,}\b/g,
12
16
  /\bsk-clb-[A-Za-z0-9_-]{8,}\b/g,
13
17
  /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
@@ -0,0 +1,15 @@
1
+ import { redactSecrets, redactString } from '../secret-redaction.js';
2
+ export function redactOpenRouterKey(value) {
3
+ if (!value)
4
+ return '';
5
+ if (value.length <= 10)
6
+ return '<redacted>';
7
+ return `${value.slice(0, 6)}...${value.slice(-4)}`;
8
+ }
9
+ export function redactOpenRouterSecrets(value, env = process.env) {
10
+ return redactSecrets(value, env);
11
+ }
12
+ export function redactOpenRouterString(value, env = process.env) {
13
+ return redactString(String(value ?? ''), env);
14
+ }
15
+ //# sourceMappingURL=redact-secrets.js.map
@@ -1,2 +1,2 @@
1
- export const PACKAGE_VERSION = '4.0.2';
1
+ export const PACKAGE_VERSION = '4.0.3';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "4.0.2",
4
+ "version": "4.0.3",
5
5
  "description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -30,6 +30,8 @@
30
30
  "!dist/**/*.d.ts.map",
31
31
  "!dist/**/*.js.map",
32
32
  "!dist/**/*.tsbuildinfo",
33
+ "!dist/**/__tests__",
34
+ "!dist/**/*.test.js",
33
35
  "!dist/vendor/openai-codex/rust-v0.131.0",
34
36
  "!dist/scripts/*-blackbox.js",
35
37
  "!dist/scripts/*-check.js",
@@ -122,6 +124,7 @@
122
124
  "prepublish:fast-check": "node ./dist/scripts/prepublish-fast-check.js",
123
125
  "prepublish:release-check-or-fast": "node ./dist/scripts/prepublish-release-check-or-fast.js",
124
126
  "mad:preflight-blocks-unreadable-config": "node ./dist/scripts/mad-preflight-blocks-unreadable-config-check.js",
127
+ "codex:0.141-compat": "node ./dist/scripts/codex-0141-capability-check.js",
125
128
  "codex:0.136-compat": "node ./dist/scripts/codex-0-136-compat-check.js",
126
129
  "codex:0.136-compat:require-real": "node ./dist/scripts/codex-0-136-compat-check.js --require-real",
127
130
  "codex:0.135-compat": "node ./dist/scripts/codex-0-135-compat-check.js",