sneakoscope 8.0.2 → 8.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 +47 -23
- package/config/skills-hash-ledger.v1.json +1 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +55 -13
- package/dist/bin/sks-dispatch.js +1 -1
- package/dist/cli/command-manifest-lite.js +4 -0
- package/dist/cli/command-registry.js +4 -0
- package/dist/cli/install-helpers-codex-lb-chain.js +187 -0
- package/dist/cli/install-helpers-codex-lb-config.js +74 -8
- package/dist/cli/install-helpers.js +89 -44
- package/dist/commands/codex-lb.js +117 -19
- package/dist/commands/doctor.js +697 -37
- package/dist/config/skills-manifest.json +3 -2
- package/dist/core/agents/agent-runner-codex-exec.js +5 -3
- package/dist/core/codex/codex-config-guard.js +238 -52
- package/dist/core/codex-adapter.js +5 -3
- package/dist/core/codex-app/menubar/constants.js +10 -1
- package/dist/core/codex-app/menubar/global-install.js +288 -0
- package/dist/core/codex-app/menubar/installer.js +79 -14
- package/dist/core/codex-app/menubar/launch-agent.js +267 -45
- package/dist/core/codex-app/menubar/paths.js +3 -0
- package/dist/core/codex-app/menubar/rollback.js +55 -7
- package/dist/core/codex-app/menubar/status.js +75 -4
- package/dist/core/codex-control/codex-0139-image-path-real-probe.js +3 -1
- package/dist/core/codex-control/codex-0139-web-search-probe.js +3 -1
- package/dist/core/codex-control/codex-app-server-runtime-env.js +13 -4
- package/dist/core/codex-control/codex-app-server-v2-client.js +3 -3
- package/dist/core/codex-exec-output-schema.js +4 -1
- package/dist/core/codex-lb/codex-lb-env.js +63 -72
- package/dist/core/codex-lb/codex-lb-setup.js +15 -2
- package/dist/core/codex-lb/desktop-center-credentials.js +517 -58
- package/dist/core/codex-lb/desktop-controller.js +153 -24
- package/dist/core/codex-lb/routing-truth.js +327 -0
- package/dist/core/codex-native/core-skill-manifest.js +1 -1
- package/dist/core/commands/basic-cli.js +7 -1
- package/dist/core/commands/qa-loop-command.js +3 -1
- package/dist/core/commands/telegram-command.js +241 -0
- package/dist/core/config-adopt/config-adopt-command.js +35 -0
- package/dist/core/config-adopt/config-adopt.js +346 -0
- package/dist/core/config-adopt/index.js +3 -0
- package/dist/core/doctor/command-alias-cleanup.js +4 -1
- package/dist/core/doctor/current-project-guidance-nested.js +37 -6
- package/dist/core/doctor/current-project-guidance.js +11 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +14 -0
- package/dist/core/doctor/retired-managed-residue-private.js +1 -1
- package/dist/core/doctor/sks-menubar-doctor.js +4 -0
- package/dist/core/feature-fixtures.js +8 -0
- package/dist/core/hooks-runtime/hook-context.js +9 -0
- package/dist/core/hooks-runtime/managed-skill-admission.js +54 -4
- package/dist/core/hooks-runtime/official-subagent-lifecycle.js +69 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +2 -2
- package/dist/core/hooks-runtime/subagent-skill-availability-evidence.js +3 -3
- package/dist/core/hooks-runtime/subagent-skill-availability-guards.js +43 -5
- package/dist/core/hooks-runtime/subagent-skill-availability.js +64 -30
- package/dist/core/hooks-runtime/subagent-stop-hook.js +15 -2
- package/dist/core/hooks-runtime.js +148 -4
- package/dist/core/imagegen/imagegen-capability.js +46 -0
- package/dist/core/imagegen/require-imagegen.js +2 -2
- package/dist/core/init/skills.js +17 -3
- package/dist/core/install/installed-package-smoke.js +8 -2
- package/dist/core/naruto/hardware-capacity-probe.js +9 -3
- package/dist/core/naruto/naruto-concurrency-governor.js +68 -24
- package/dist/core/perf/import-graph-budget.js +0 -1
- package/dist/core/product-design-app-server.js +13 -1
- package/dist/core/release/package-size-budget.js +2 -2
- package/dist/core/release/release-pack-receipt.js +1 -2
- package/dist/core/routes/dollar-manifest-lite.js +1 -1
- package/dist/core/routes.js +1 -1
- package/dist/core/security/private-credential-file.js +209 -0
- package/dist/core/subagents/agent-catalog.js +17 -32
- package/dist/core/subagents/naruto-help-contract.js +5 -5
- package/dist/core/subagents/official-subagent-config.js +16 -6
- package/dist/core/subagents/official-subagent-preparation.js +34 -15
- package/dist/core/subagents/official-subagent-prompt.js +4 -4
- package/dist/core/subagents/official-subagent-runner.js +27 -4
- package/dist/core/subagents/thread-budget.js +19 -12
- package/dist/core/subagents/wave-lifecycle.js +18 -6
- package/dist/core/telegram/access.js +54 -0
- package/dist/core/telegram/client.js +144 -0
- package/dist/core/telegram/confirmation.js +37 -0
- package/dist/core/telegram/doctor.js +151 -0
- package/dist/core/telegram/index.js +9 -0
- package/dist/core/telegram/keychain.js +265 -0
- package/dist/core/telegram/liveness.js +109 -0
- package/dist/core/telegram/poller.js +90 -0
- package/dist/core/telegram/redaction.js +15 -0
- package/dist/core/telegram/types.js +1 -0
- package/dist/core/update/update-diagnostics.js +52 -0
- package/dist/core/update/update-migration-state.js +5 -1
- package/dist/core/update/update-operation.js +2 -1
- package/dist/core/update/update-stage-diagnostics.js +38 -0
- package/dist/core/update/update-status.js +2 -9
- package/dist/core/update-check.js +147 -31
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-launcher.js +23 -4
- package/dist/native/sks-menubar/Sources/AppDelegate.swift +13 -0
- package/dist/native/sks-menubar/Sources/AppIdentity.swift +7 -0
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +3 -2
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +115 -0
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +17 -0
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +190 -0
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +100 -59
- package/dist/native/sks-menubar/Sources/SingletonInstanceGuard.swift +328 -0
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileStore.swift +320 -0
- package/dist/native/sks-menubar/Sources/TelegramPrivateFileSupport.swift +303 -0
- package/dist/native/sks-menubar/Sources/TelegramProcessGateway.swift +238 -0
- package/dist/native/sks-menubar/Sources/TelegramRuntimeSupport.swift +56 -0
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/native/sks-menubar/Sources/TelegramTransport.swift +474 -0
- package/dist/native/sks-menubar/Sources/main.swift +15 -1
- package/dist/native/sks-menubar/Tests/OperationCoordinatorTests.swift +4 -3
- package/dist/native/sks-menubar/Tests/ProvidersViewControllerTests.swift +129 -0
- package/dist/native/sks-menubar/Tests/SingletonInstanceGuardTests.swift +183 -0
- package/dist/native/sks-menubar/Tests/TelegramRuntimeSmokeTests.swift +267 -0
- package/dist/native/sks-menubar/Tests/TelegramRuntimeTests.swift +222 -0
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +1 -1
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +1 -1
- package/dist/scripts/current-command-surface-check.js +2 -4
- package/dist/scripts/official-subagent-workflow-check.js +1 -1
- package/dist/scripts/packlist-performance-check.js +1 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "sks.skills-manifest.v1",
|
|
3
|
-
"package_version": "8.0.
|
|
3
|
+
"package_version": "8.0.3",
|
|
4
4
|
"skills": [
|
|
5
5
|
{
|
|
6
6
|
"canonical_name": "sks",
|
|
@@ -326,8 +326,9 @@
|
|
|
326
326
|
{
|
|
327
327
|
"canonical_name": "sks-naruto",
|
|
328
328
|
"type": "core",
|
|
329
|
-
"content_sha256": "
|
|
329
|
+
"content_sha256": "34222c72e4e1ead3584907f4d905444b7de820283ee7348018f110d5e6b04461",
|
|
330
330
|
"hash_history": [
|
|
331
|
+
"f8e808622e222b4ad7dcb2907e5f06a890db83139ae443ec6c8c33781dfa3e37",
|
|
331
332
|
"49b5e3dcc1b537b49c2cf09e5b875cb16acb05c622b22798e265e64dacd8ac90"
|
|
332
333
|
],
|
|
333
334
|
"deprecated_aliases": []
|
|
@@ -5,6 +5,7 @@ import { agentWorkerEnv, validateAgentWorkerResult } from './agent-worker-pipeli
|
|
|
5
5
|
import { fastModeEnv, resolveFastModePolicy } from './fast-mode-policy.js';
|
|
6
6
|
import { buildCodexExecArgs } from '../codex/codex-cli-syntax-builder.js';
|
|
7
7
|
import { codexLbRecoveryBlockedProcessResult, withCodexLbCliLaunchRecovery } from '../codex-control/codex-lb-launch-recovery.js';
|
|
8
|
+
import { prepareCodexAppServerRuntimeEnv } from '../codex-control/codex-app-server-runtime-env.js';
|
|
8
9
|
export function buildCodexExecAgentArgs(agent, prompt, opts = {}) {
|
|
9
10
|
const resultFile = opts.resultFile || defaultCodexResultFile(agent, opts);
|
|
10
11
|
const sandbox = opts.workspaceWrite ? 'workspace-write' : 'read-only';
|
|
@@ -74,15 +75,16 @@ export async function runCodexExecAgent(agent, slice, opts = {}) {
|
|
|
74
75
|
const allowedCommandsFile = path.join(opts.agentRoot || opts.cwd || process.cwd(), 'agent-allowed-commands.json');
|
|
75
76
|
const workerEnv = agentWorkerEnv(agent, allowedCommandsFile);
|
|
76
77
|
const effectiveEnv = { ...process.env, ...(opts.env || {}) };
|
|
77
|
-
const
|
|
78
|
+
const runtimeEnv = await prepareCodexAppServerRuntimeEnv({ env: effectiveEnv });
|
|
79
|
+
const proxyEnv = managedProxyEnvForChild(runtimeEnv);
|
|
78
80
|
const execute = opts.runProcessImpl || runProcess;
|
|
79
81
|
const guarded = await withCodexLbCliLaunchRecovery({
|
|
80
82
|
root: opts.cwd || process.cwd(),
|
|
81
|
-
env:
|
|
83
|
+
env: runtimeEnv,
|
|
82
84
|
cliArgs: command.args.slice(0, -1),
|
|
83
85
|
...(typeof opts.recoveryFetch === 'function' ? { fetchImpl: opts.recoveryFetch } : {}),
|
|
84
86
|
...(opts.recoveryTimeoutMs === undefined ? {} : { timeoutMs: opts.recoveryTimeoutMs })
|
|
85
|
-
}, () => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...
|
|
87
|
+
}, () => execute(opts.codexBin || 'codex', command.args, { cwd: opts.cwd || process.cwd(), env: { ...runtimeEnv, ...proxyEnv, ...fastModeEnv(fastPolicy), ...workerEnv }, timeoutMs: opts.timeoutMs || 30 * 60 * 1000, maxOutputBytes: 256 * 1024, stdoutFile, stderrFile }));
|
|
86
88
|
const result = guarded.launched
|
|
87
89
|
? guarded.value
|
|
88
90
|
: codexLbRecoveryBlockedProcessResult(guarded.toolOutputRecovery);
|
|
@@ -5,6 +5,7 @@ import { appendJsonl, ensureDir, nowIso, readText, sha256, writeTextAtomic } fro
|
|
|
5
5
|
import { diffCodexAppUiSnapshots, snapshotCodexAppUiState } from '../codex-app/codex-app-ui-state-snapshot.js';
|
|
6
6
|
import { cleanupCodexConfigBackups, validateCodexConfigRoundTrip } from './codex-config-toml.js';
|
|
7
7
|
import { escapeRegExp } from '../text/regex.js';
|
|
8
|
+
export const SKS_MANAGED_CODEX_CONFIG_MARKER = '# SKS-MANAGED-CODEX-CONFIG';
|
|
8
9
|
const FAST_FEATURE_KEYS = [
|
|
9
10
|
'fast_mode',
|
|
10
11
|
'hooks',
|
|
@@ -23,18 +24,6 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
23
24
|
const root = path.resolve(input.root || process.cwd());
|
|
24
25
|
const cause = input.cause || 'codex-config';
|
|
25
26
|
const before = input.before === undefined ? String(await readText(configPath, '')) : String(input.before || '');
|
|
26
|
-
if (isUnmanagedProjectCodexConfig(root, configPath, before) && input.ownershipVerified !== true) {
|
|
27
|
-
const result = { ok: false, status: 'blocked_unmanaged_project_config', config_path: configPath, backup_path: null, changed: false };
|
|
28
|
-
await recordCodexConfigGuard(root, input.reportPath, {
|
|
29
|
-
cause,
|
|
30
|
-
config_path: configPath,
|
|
31
|
-
ok: false,
|
|
32
|
-
status: result.status,
|
|
33
|
-
blocker: 'user_owned_file_without_sks_marker',
|
|
34
|
-
changed: false
|
|
35
|
-
});
|
|
36
|
-
return result;
|
|
37
|
-
}
|
|
38
27
|
const beforeSmoke = codexConfigParseSmoke(before);
|
|
39
28
|
const beforeValidation = validateCodexConfigRoundTrip(before);
|
|
40
29
|
if (before.trim() && (!beforeSmoke.ok || beforeValidation.parse_error)) {
|
|
@@ -62,6 +51,21 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
62
51
|
next = preserved.text;
|
|
63
52
|
if (input.removeTopLevelModeLocks === true)
|
|
64
53
|
next = removeLegacyTopLevelCodexModeLocks(next);
|
|
54
|
+
const expectedBefore = normalizeText(before);
|
|
55
|
+
const unmanagedProjectConfig = isUnmanagedProjectCodexConfig(root, configPath, before)
|
|
56
|
+
&& input.ownershipVerified !== true;
|
|
57
|
+
if (unmanagedProjectConfig && next !== expectedBefore) {
|
|
58
|
+
const result = { ok: false, status: 'blocked_unmanaged_project_config', config_path: configPath, backup_path: null, changed: false };
|
|
59
|
+
await recordCodexConfigGuard(root, input.reportPath, {
|
|
60
|
+
cause,
|
|
61
|
+
config_path: configPath,
|
|
62
|
+
ok: false,
|
|
63
|
+
status: result.status,
|
|
64
|
+
blocker: 'user_owned_file_without_sks_marker',
|
|
65
|
+
changed: false
|
|
66
|
+
});
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
65
69
|
const forbiddenTopLevel = topLevelModeLocks(next);
|
|
66
70
|
const nextSmoke = codexConfigParseSmoke(next);
|
|
67
71
|
const nextValidation = validateCodexConfigRoundTrip(next);
|
|
@@ -80,17 +84,19 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
80
84
|
});
|
|
81
85
|
return result;
|
|
82
86
|
}
|
|
83
|
-
const expectedBefore = normalizeText(before);
|
|
84
87
|
if (next === expectedBefore) {
|
|
85
|
-
|
|
88
|
+
const verifyWithoutWrite = input.verifyUnchangedBeforeWrite === true || unmanagedProjectConfig;
|
|
89
|
+
if (verifyWithoutWrite) {
|
|
86
90
|
const observed = await readConfigCommitSnapshot(configPath);
|
|
87
|
-
const expectedExists = input.expectedBeforeExists ?? true;
|
|
91
|
+
const expectedExists = unmanagedProjectConfig ? true : (input.expectedBeforeExists ?? true);
|
|
88
92
|
if (!configCommitSnapshotMatches(observed, expectedExists, before, input.expectedBeforeMode)) {
|
|
89
|
-
return
|
|
93
|
+
return recordConfigCommitFailure({
|
|
90
94
|
root,
|
|
91
95
|
reportPath: input.reportPath,
|
|
92
96
|
cause,
|
|
93
97
|
configPath,
|
|
98
|
+
status: 'concurrent_change_detected',
|
|
99
|
+
changed: false,
|
|
94
100
|
before,
|
|
95
101
|
expectedExists,
|
|
96
102
|
...(input.expectedBeforeMode === undefined
|
|
@@ -106,11 +112,11 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
106
112
|
else {
|
|
107
113
|
await writeTextAtomic(configPath, next, { mode: 0o600 });
|
|
108
114
|
}
|
|
109
|
-
const expectedAfterExists =
|
|
110
|
-
? (input.expectedBeforeExists ?? true)
|
|
115
|
+
const expectedAfterExists = verifyWithoutWrite
|
|
116
|
+
? (unmanagedProjectConfig ? true : (input.expectedBeforeExists ?? true))
|
|
111
117
|
: true;
|
|
112
118
|
const expectedAfterMode = expectedAfterExists
|
|
113
|
-
?
|
|
119
|
+
? verifyWithoutWrite
|
|
114
120
|
? (input.expectedBeforeMode === undefined ? null : Number(input.expectedBeforeMode) & 0o777)
|
|
115
121
|
: 0o600
|
|
116
122
|
: null;
|
|
@@ -125,7 +131,7 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
125
131
|
expected_after: {
|
|
126
132
|
exists: expectedAfterExists,
|
|
127
133
|
text: expectedAfterExists
|
|
128
|
-
? (
|
|
134
|
+
? (verifyWithoutWrite ? before : next)
|
|
129
135
|
: '',
|
|
130
136
|
mode: expectedAfterMode
|
|
131
137
|
}
|
|
@@ -157,11 +163,13 @@ export async function writeCodexConfigGuarded(input) {
|
|
|
157
163
|
backupTag: input.backupTag || cause
|
|
158
164
|
});
|
|
159
165
|
if (!commit.ok) {
|
|
160
|
-
return
|
|
166
|
+
return recordConfigCommitFailure({
|
|
161
167
|
root,
|
|
162
168
|
reportPath: input.reportPath,
|
|
163
169
|
cause,
|
|
164
170
|
configPath,
|
|
171
|
+
status: commit.failure,
|
|
172
|
+
changed: commit.changed,
|
|
165
173
|
before,
|
|
166
174
|
expectedExists,
|
|
167
175
|
...(input.expectedBeforeMode === undefined
|
|
@@ -226,13 +234,13 @@ function configCommitSnapshotMatches(observed, expectedExists, expectedText, exp
|
|
|
226
234
|
|| expectedMode === undefined
|
|
227
235
|
|| observed.mode === (Number(expectedMode) & 0o777));
|
|
228
236
|
}
|
|
229
|
-
async function
|
|
237
|
+
async function recordConfigCommitFailure(input) {
|
|
230
238
|
const result = {
|
|
231
239
|
ok: false,
|
|
232
|
-
status:
|
|
240
|
+
status: input.status,
|
|
233
241
|
config_path: input.configPath,
|
|
234
242
|
backup_path: input.backupPath,
|
|
235
|
-
changed:
|
|
243
|
+
changed: input.changed,
|
|
236
244
|
repaired_keys: input.repairedKeys,
|
|
237
245
|
forbidden_top_level: input.forbiddenTopLevel
|
|
238
246
|
};
|
|
@@ -251,7 +259,7 @@ async function recordConcurrentConfigChange(input) {
|
|
|
251
259
|
observed_sha256: input.observed.ok ? sha256(input.observed.text) : null,
|
|
252
260
|
observed_status: input.observed.status,
|
|
253
261
|
backup_path: input.backupPath,
|
|
254
|
-
changed:
|
|
262
|
+
changed: input.changed
|
|
255
263
|
});
|
|
256
264
|
return result;
|
|
257
265
|
}
|
|
@@ -264,48 +272,168 @@ async function commitCodexConfigIfUnchanged(input) {
|
|
|
264
272
|
await writeTextAtomic(candidatePath, input.next, { mode: 0o600 });
|
|
265
273
|
const observed = await readConfigCommitSnapshot(input.configPath);
|
|
266
274
|
if (!configCommitSnapshotMatches(observed, input.expectedExists, input.before, input.expectedBeforeMode)) {
|
|
267
|
-
return {
|
|
275
|
+
return {
|
|
276
|
+
ok: false,
|
|
277
|
+
failure: 'concurrent_change_detected',
|
|
278
|
+
changed: false,
|
|
279
|
+
backupPath,
|
|
280
|
+
observed
|
|
281
|
+
};
|
|
268
282
|
}
|
|
283
|
+
const claimedBeforeMode = input.expectedBeforeMode === undefined
|
|
284
|
+
? (observed.ok && observed.exists ? observed.mode : undefined)
|
|
285
|
+
: Number(input.expectedBeforeMode) & 0o777;
|
|
269
286
|
if (input.expectedExists) {
|
|
270
287
|
backupPath = `${input.configPath}.sks-${safeBackupTag(input.backupTag)}-${token}.bak`;
|
|
271
288
|
try {
|
|
272
289
|
await fsp.rename(input.configPath, backupPath);
|
|
273
290
|
}
|
|
274
291
|
catch {
|
|
275
|
-
return {
|
|
292
|
+
return {
|
|
293
|
+
ok: false,
|
|
294
|
+
failure: 'concurrent_change_detected',
|
|
295
|
+
changed: false,
|
|
296
|
+
backupPath: null,
|
|
297
|
+
observed: await readConfigCommitSnapshot(input.configPath)
|
|
298
|
+
};
|
|
276
299
|
}
|
|
277
300
|
const claimed = await readConfigCommitSnapshot(backupPath);
|
|
278
|
-
if (!configCommitSnapshotMatches(claimed, true, input.before,
|
|
279
|
-
await
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
301
|
+
if (!configCommitSnapshotMatches(claimed, true, input.before, claimedBeforeMode)) {
|
|
302
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
303
|
+
candidatePath,
|
|
304
|
+
configPath: input.configPath,
|
|
305
|
+
backupPath,
|
|
306
|
+
before: input.before,
|
|
307
|
+
expectedExists: input.expectedExists,
|
|
308
|
+
expectedBeforeMode: claimedBeforeMode
|
|
309
|
+
});
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
failure: rollback.restored ? 'concurrent_change_detected' : 'postwrite_verification_failed',
|
|
313
|
+
changed: !rollback.restored,
|
|
314
|
+
backupPath: rollback.backupPath,
|
|
315
|
+
observed: rollback.observed
|
|
316
|
+
};
|
|
290
317
|
}
|
|
291
318
|
const afterClaim = await readConfigCommitSnapshot(input.configPath);
|
|
292
319
|
if (!afterClaim.ok || afterClaim.exists) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
320
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
321
|
+
candidatePath,
|
|
322
|
+
configPath: input.configPath,
|
|
323
|
+
backupPath,
|
|
324
|
+
before: input.before,
|
|
325
|
+
expectedExists: input.expectedExists,
|
|
326
|
+
expectedBeforeMode: claimedBeforeMode
|
|
327
|
+
});
|
|
328
|
+
return {
|
|
329
|
+
ok: false,
|
|
330
|
+
failure: 'concurrent_change_detected',
|
|
331
|
+
changed: !rollback.restored,
|
|
332
|
+
backupPath: rollback.backupPath,
|
|
333
|
+
observed: rollback.observed
|
|
334
|
+
};
|
|
296
335
|
}
|
|
297
336
|
}
|
|
298
337
|
try {
|
|
299
338
|
await fsp.link(candidatePath, input.configPath);
|
|
300
339
|
}
|
|
301
340
|
catch {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
341
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
342
|
+
candidatePath,
|
|
343
|
+
configPath: input.configPath,
|
|
344
|
+
backupPath,
|
|
345
|
+
before: input.before,
|
|
346
|
+
expectedExists: input.expectedExists,
|
|
347
|
+
expectedBeforeMode: claimedBeforeMode
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
ok: false,
|
|
351
|
+
failure: 'concurrent_change_detected',
|
|
352
|
+
changed: !rollback.restored,
|
|
353
|
+
backupPath: rollback.backupPath,
|
|
354
|
+
observed: rollback.observed
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
await fsp.chmod(input.configPath, 0o600);
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
362
|
+
candidatePath,
|
|
363
|
+
configPath: input.configPath,
|
|
364
|
+
backupPath,
|
|
365
|
+
before: input.before,
|
|
366
|
+
expectedExists: input.expectedExists,
|
|
367
|
+
expectedBeforeMode: claimedBeforeMode
|
|
368
|
+
});
|
|
369
|
+
return {
|
|
370
|
+
ok: false,
|
|
371
|
+
failure: 'postwrite_verification_failed',
|
|
372
|
+
changed: !rollback.restored,
|
|
373
|
+
backupPath: rollback.backupPath,
|
|
374
|
+
observed: rollback.observed
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
const committed = await readConfigCommitSnapshot(input.configPath);
|
|
378
|
+
if (!configCommitSnapshotMatches(committed, true, input.next, 0o600)) {
|
|
379
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
380
|
+
candidatePath,
|
|
381
|
+
configPath: input.configPath,
|
|
382
|
+
backupPath,
|
|
383
|
+
before: input.before,
|
|
384
|
+
expectedExists: input.expectedExists,
|
|
385
|
+
expectedBeforeMode: claimedBeforeMode
|
|
386
|
+
});
|
|
387
|
+
return {
|
|
388
|
+
ok: false,
|
|
389
|
+
failure: 'postwrite_verification_failed',
|
|
390
|
+
changed: !rollback.restored,
|
|
391
|
+
backupPath: rollback.backupPath,
|
|
392
|
+
observed: rollback.observed
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
if (backupPath) {
|
|
396
|
+
try {
|
|
397
|
+
await fsp.chmod(backupPath, 0o600);
|
|
398
|
+
const now = new Date();
|
|
399
|
+
await fsp.utimes(backupPath, now, now);
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
403
|
+
candidatePath,
|
|
404
|
+
configPath: input.configPath,
|
|
405
|
+
backupPath,
|
|
406
|
+
before: input.before,
|
|
407
|
+
expectedExists: input.expectedExists,
|
|
408
|
+
expectedBeforeMode: claimedBeforeMode
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
ok: false,
|
|
412
|
+
failure: 'postwrite_verification_failed',
|
|
413
|
+
changed: !rollback.restored,
|
|
414
|
+
backupPath: rollback.backupPath,
|
|
415
|
+
observed: rollback.observed
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
const hardenedBackup = await readConfigCommitSnapshot(backupPath);
|
|
419
|
+
if (!configCommitSnapshotMatches(hardenedBackup, true, input.before, 0o600)) {
|
|
420
|
+
const rollback = await rollbackCodexConfigCommit({
|
|
421
|
+
candidatePath,
|
|
422
|
+
configPath: input.configPath,
|
|
423
|
+
backupPath,
|
|
424
|
+
before: input.before,
|
|
425
|
+
expectedExists: input.expectedExists,
|
|
426
|
+
expectedBeforeMode: claimedBeforeMode
|
|
427
|
+
});
|
|
428
|
+
return {
|
|
429
|
+
ok: false,
|
|
430
|
+
failure: 'postwrite_verification_failed',
|
|
431
|
+
changed: !rollback.restored,
|
|
432
|
+
backupPath: rollback.backupPath,
|
|
433
|
+
observed: rollback.observed
|
|
434
|
+
};
|
|
305
435
|
}
|
|
306
|
-
return { ok: false, backupPath, observed: await readConfigCommitSnapshot(input.configPath) };
|
|
307
436
|
}
|
|
308
|
-
await fsp.chmod(input.configPath, 0o600).catch(() => undefined);
|
|
309
437
|
await cleanupCodexConfigBackups(input.configPath, { keepPerTag: 3, maxAgeMs: 30 * 24 * 60 * 60 * 1000 }).catch(() => undefined);
|
|
310
438
|
return { ok: true, backupPath };
|
|
311
439
|
}
|
|
@@ -313,10 +441,65 @@ async function commitCodexConfigIfUnchanged(input) {
|
|
|
313
441
|
await fsp.rm(candidatePath, { force: true }).catch(() => undefined);
|
|
314
442
|
}
|
|
315
443
|
}
|
|
316
|
-
async function
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
444
|
+
async function rollbackCodexConfigCommit(input) {
|
|
445
|
+
let config = await readConfigCommitSnapshot(input.configPath);
|
|
446
|
+
if (config.exists) {
|
|
447
|
+
try {
|
|
448
|
+
const [candidateStat, configStat] = await Promise.all([
|
|
449
|
+
fsp.lstat(input.candidatePath),
|
|
450
|
+
fsp.lstat(input.configPath)
|
|
451
|
+
]);
|
|
452
|
+
if (candidateStat.dev !== configStat.dev || candidateStat.ino !== configStat.ino) {
|
|
453
|
+
return { restored: false, backupPath: input.backupPath, observed: config };
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
return { restored: false, backupPath: input.backupPath, observed: config };
|
|
458
|
+
}
|
|
459
|
+
try {
|
|
460
|
+
await fsp.unlink(input.configPath);
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
return {
|
|
464
|
+
restored: false,
|
|
465
|
+
backupPath: input.backupPath,
|
|
466
|
+
observed: await readConfigCommitSnapshot(input.configPath)
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
config = await readConfigCommitSnapshot(input.configPath);
|
|
470
|
+
}
|
|
471
|
+
let remainingBackupPath = input.backupPath;
|
|
472
|
+
const restoreMode = input.expectedBeforeMode === undefined
|
|
473
|
+
? undefined
|
|
474
|
+
: Number(input.expectedBeforeMode) & 0o777;
|
|
475
|
+
if (input.expectedExists) {
|
|
476
|
+
if (!input.backupPath || !config.ok || config.exists) {
|
|
477
|
+
return { restored: false, backupPath: remainingBackupPath, observed: config };
|
|
478
|
+
}
|
|
479
|
+
const backup = await readConfigCommitSnapshot(input.backupPath);
|
|
480
|
+
if (!backup.ok || !backup.exists || sha256(backup.text) !== sha256(input.before)) {
|
|
481
|
+
return { restored: false, backupPath: remainingBackupPath, observed: backup };
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
await fsp.rename(input.backupPath, input.configPath);
|
|
485
|
+
remainingBackupPath = null;
|
|
486
|
+
if (restoreMode !== undefined)
|
|
487
|
+
await fsp.chmod(input.configPath, restoreMode);
|
|
488
|
+
}
|
|
489
|
+
catch {
|
|
490
|
+
return {
|
|
491
|
+
restored: false,
|
|
492
|
+
backupPath: remainingBackupPath,
|
|
493
|
+
observed: await readConfigCommitSnapshot(input.configPath)
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const observed = await readConfigCommitSnapshot(input.configPath);
|
|
498
|
+
return {
|
|
499
|
+
restored: configCommitSnapshotMatches(observed, input.expectedExists, input.before, restoreMode),
|
|
500
|
+
backupPath: remainingBackupPath,
|
|
501
|
+
observed
|
|
502
|
+
};
|
|
320
503
|
}
|
|
321
504
|
function safeBackupTag(value) {
|
|
322
505
|
return String(value || 'codex-config').replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 96) || 'codex-config';
|
|
@@ -384,12 +567,15 @@ export function isProjectCodexConfig(root, configPath) {
|
|
|
384
567
|
}
|
|
385
568
|
export function hasSksManagedCodexConfigMarker(text) {
|
|
386
569
|
const source = String(text || '');
|
|
387
|
-
return
|
|
570
|
+
return hasExplicitSksManagedCodexConfigMarker(source)
|
|
388
571
|
|| /(?:SKS managed|Sneakoscope|sneakoscope|sks_|agents\.native_agent|agents\.implementation_worker|multi_agent)/i.test(source)
|
|
389
572
|
|| /^\s*model_provider\s*=\s*["']codex-lb["']\s*(?:#.*)?$/mi.test(source)
|
|
390
573
|
|| /^\s*default_profile\s*=\s*["']sks-fast-high["']\s*(?:#.*)?$/mi.test(source)
|
|
391
574
|
|| /^\s*\[(?:user\.fast_mode|model_providers\.(?:"codex-lb"|codex-lb)|profiles\.(?:"sks-fast-high"|sks-fast-high))\]\s*(?:#.*)?$/mi.test(source);
|
|
392
575
|
}
|
|
576
|
+
export function hasExplicitSksManagedCodexConfigMarker(text) {
|
|
577
|
+
return /^\s*#\s*SKS-MANAGED-CODEX-CONFIG\b/im.test(String(text || ''));
|
|
578
|
+
}
|
|
393
579
|
export function isUnmanagedProjectCodexConfig(root, configPath, text) {
|
|
394
580
|
return isProjectCodexConfig(root, configPath)
|
|
395
581
|
&& String(text || '').trim().length > 0
|
|
@@ -4,6 +4,7 @@ import { preserveCodexModelArgs } from './codex-model-guard.js';
|
|
|
4
4
|
import { managedProxyEnvForChild } from './codex/managed-proxy-env.js';
|
|
5
5
|
import { resolveCodexRuntime } from './codex-runtime/resolve-codex-runtime.js';
|
|
6
6
|
import { codexLbRecoveryBlockedProcessResult, withCodexLbCliLaunchRecovery } from './codex-control/codex-lb-launch-recovery.js';
|
|
7
|
+
import { prepareCodexAppServerRuntimeEnv } from './codex-control/codex-app-server-runtime-env.js';
|
|
7
8
|
export async function findCodexBinary() {
|
|
8
9
|
const resolved = await resolveCodexRuntime({
|
|
9
10
|
explicitPath: process.env.DCODEX_CODEX_BIN || null,
|
|
@@ -40,13 +41,14 @@ export function buildCodexExecArgs({ root, prompt, outputFile, json = true, prof
|
|
|
40
41
|
args.push(prompt);
|
|
41
42
|
return args;
|
|
42
43
|
}
|
|
43
|
-
export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFile, json = true, profile = null, extraArgs = [], onStdout, onStderr, logDir = null, stdoutFile = null, stderrFile = null, maxBufferBytes = 256 * 1024, timeoutMs = null, env = process.env, codexBin = null, findCodexBinaryImpl = findCodexBinary, runProcessImpl = runProcess, recoveryFetch = undefined, recoveryTimeoutMs = undefined }) {
|
|
44
|
+
export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFile, json = true, profile = null, extraArgs = [], onStdout, onStderr, logDir = null, stdoutFile = null, stderrFile = null, maxBufferBytes = 256 * 1024, timeoutMs = null, env = process.env, codexBin = null, findCodexBinaryImpl = findCodexBinary, runProcessImpl = runProcess, prepareCodexRuntimeEnvImpl = prepareCodexAppServerRuntimeEnv, recoveryFetch = undefined, recoveryTimeoutMs = undefined }) {
|
|
44
45
|
const args = buildCodexExecArgs({ root, prompt, outputFile, json, profile, extraArgs });
|
|
45
46
|
const effectiveTimeoutMs = Number(timeoutMs || process.env.SKS_CODEX_TIMEOUT_MS || process.env.DCODEX_CODEX_TIMEOUT_MS || 30 * 60 * 1000);
|
|
46
47
|
const recoveryArgs = args.slice(1, -1);
|
|
48
|
+
const runtimeEnv = await prepareCodexRuntimeEnvImpl({ env });
|
|
47
49
|
const guarded = await withCodexLbCliLaunchRecovery({
|
|
48
50
|
root: recoveryRoot,
|
|
49
|
-
env,
|
|
51
|
+
env: runtimeEnv,
|
|
50
52
|
cliArgs: recoveryArgs,
|
|
51
53
|
...(typeof recoveryFetch === 'function' ? { fetchImpl: recoveryFetch } : {}),
|
|
52
54
|
...(recoveryTimeoutMs === undefined ? {} : { timeoutMs: recoveryTimeoutMs })
|
|
@@ -65,7 +67,7 @@ export async function runCodexExec({ root, recoveryRoot = root, prompt, outputFi
|
|
|
65
67
|
}
|
|
66
68
|
return runProcessImpl(bin, args, {
|
|
67
69
|
cwd: root,
|
|
68
|
-
env: { ...
|
|
70
|
+
env: { ...runtimeEnv, ...managedProxyEnvForChild(runtimeEnv) },
|
|
69
71
|
onStdout,
|
|
70
72
|
onStderr,
|
|
71
73
|
timeoutMs: effectiveTimeoutMs,
|
|
@@ -21,7 +21,9 @@ export const NATIVE_SOURCE_FILES = [
|
|
|
21
21
|
'UpdatesViewController.swift',
|
|
22
22
|
'MCPServersViewController.swift',
|
|
23
23
|
'ProvidersViewController.swift',
|
|
24
|
+
'ProvidersConnectTest.swift',
|
|
24
25
|
'ProvidersRoutingTruth.swift',
|
|
26
|
+
'ProvidersFastMode.swift',
|
|
25
27
|
'ProvidersOpenRouter.swift',
|
|
26
28
|
'ProvidersRoleModels.swift',
|
|
27
29
|
'ProvidersMultiProvider.swift',
|
|
@@ -33,9 +35,16 @@ export const NATIVE_SOURCE_FILES = [
|
|
|
33
35
|
'ProcessExecutionState.swift',
|
|
34
36
|
'ProcessIdentityGuard.swift',
|
|
35
37
|
'SecureProcessEnvelope.swift',
|
|
38
|
+
'TelegramPrivateFileSupport.swift',
|
|
39
|
+
'TelegramPrivateFileStore.swift',
|
|
40
|
+
'TelegramSupport.swift',
|
|
41
|
+
'TelegramRuntimeSupport.swift',
|
|
42
|
+
'TelegramTransport.swift',
|
|
43
|
+
'TelegramProcessGateway.swift',
|
|
36
44
|
'NotificationCoordinator.swift',
|
|
37
45
|
'AlertFactory.swift',
|
|
38
|
-
'AppIdentity.swift'
|
|
46
|
+
'AppIdentity.swift',
|
|
47
|
+
'SingletonInstanceGuard.swift'
|
|
39
48
|
];
|
|
40
49
|
export const NATIVE_RESOURCE_FILES = [
|
|
41
50
|
'AppIcon.icns',
|