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 +7 -7
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/cli/command-registry.js +1 -0
- package/dist/cli/global-mode-router.js +25 -0
- package/dist/cli/router.js +12 -0
- package/dist/commands/codex-app.js +10 -1
- package/dist/commands/codex.js +15 -1
- package/dist/core/codex-app/glm-model-profile.js +2 -0
- package/dist/core/codex-app/glm-profile-installer.js +61 -0
- package/dist/core/codex-app/glm-profile-schema.js +24 -0
- package/dist/core/codex-control/codex-0141-capability.js +95 -0
- package/dist/core/commands/glm-command.js +5 -0
- package/dist/core/fsx.js +1 -1
- package/dist/core/providers/glm/glm-52-profile.js +30 -0
- package/dist/core/providers/glm/glm-52-request.js +34 -0
- package/dist/core/providers/glm/glm-52-response-guard.js +34 -0
- package/dist/core/providers/glm/glm-52-settings.js +26 -0
- package/dist/core/providers/glm/glm-mad-mode.js +242 -0
- package/dist/core/providers/openrouter/openrouter-client.js +44 -0
- package/dist/core/providers/openrouter/openrouter-error.js +37 -0
- package/dist/core/providers/openrouter/openrouter-secret-store.js +113 -0
- package/dist/core/providers/openrouter/openrouter-types.js +2 -0
- package/dist/core/results.js +2 -0
- package/dist/core/secret-redaction.js +4 -0
- package/dist/core/security/redact-secrets.js +15 -0
- package/dist/core/version.js +1 -1
- package/package.json +4 -1
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.
|
|
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.
|
|
40
|
+
What changed in 4.0.3:
|
|
41
41
|
|
|
42
|
-
- **
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
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
|
|
|
@@ -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.
|
|
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
|
@@ -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
|
package/dist/cli/router.js
CHANGED
|
@@ -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) {
|
package/dist/commands/codex.js
CHANGED
|
@@ -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,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
|
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.
|
|
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
|
|
@@ -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
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '4.0.
|
|
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.
|
|
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",
|