sneakoscope 4.0.9 → 4.0.11
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 +9 -1
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/core/fsx.js +1 -1
- package/dist/core/pipeline-internals/runtime-gates.js +5 -2
- package/dist/core/providers/glm/glm-request-cache.js +9 -4
- package/dist/core/providers/glm/naruto/glm-naruto-apply-transaction.js +90 -0
- package/dist/core/providers/glm/naruto/glm-naruto-bench.js +65 -12
- package/dist/core/providers/glm/naruto/glm-naruto-combined-patch.js +49 -0
- package/dist/core/providers/glm/naruto/glm-naruto-command.js +9 -22
- package/dist/core/providers/glm/naruto/glm-naruto-concurrency-governor.js +10 -9
- package/dist/core/providers/glm/naruto/glm-naruto-conflict-graph.js +7 -1
- package/dist/core/providers/glm/naruto/glm-naruto-decomposer.js +54 -4
- package/dist/core/providers/glm/naruto/glm-naruto-finalizer.js +1 -0
- package/dist/core/providers/glm/naruto/glm-naruto-hunk-conflict.js +16 -0
- package/dist/core/providers/glm/naruto/glm-naruto-hunk-parser.js +36 -0
- package/dist/core/providers/glm/naruto/glm-naruto-isolation-policy.js +38 -0
- package/dist/core/providers/glm/naruto/glm-naruto-merge-planner.js +9 -4
- package/dist/core/providers/glm/naruto/glm-naruto-metrics.js +34 -0
- package/dist/core/providers/glm/naruto/glm-naruto-orchestrator.js +116 -28
- package/dist/core/providers/glm/naruto/glm-naruto-patch-candidate-gate.js +42 -0
- package/dist/core/providers/glm/naruto/glm-naruto-patch-candidate-parser.js +62 -0
- package/dist/core/providers/glm/naruto/glm-naruto-scoreboard.js +75 -0
- package/dist/core/providers/glm/naruto/glm-naruto-secret-audit.js +91 -0
- package/dist/core/providers/glm/naruto/glm-naruto-session-id.js +10 -0
- package/dist/core/providers/glm/naruto/glm-naruto-terminal.js +44 -0
- package/dist/core/providers/glm/naruto/glm-naruto-trace.js +47 -18
- package/dist/core/providers/glm/naruto/glm-naruto-usage-extractor.js +31 -0
- package/dist/core/providers/glm/naruto/glm-naruto-verifier-output.js +26 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-artifacts.js +56 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worker-pool.js +81 -12
- package/dist/core/providers/glm/naruto/glm-naruto-worker-runtime.js +217 -19
- package/dist/core/providers/glm/naruto/glm-naruto-worktree-cleanup.js +20 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worktree-manager.js +57 -0
- package/dist/core/providers/glm/naruto/glm-naruto-worktree-worker.js +44 -0
- package/dist/core/providers/openrouter/openrouter-client.js +1 -1
- package/dist/core/providers/openrouter/openrouter-provider-health.js +3 -2
- package/dist/core/providers/openrouter/openrouter-stream.js +31 -14
- package/dist/core/stop-gate/stop-gate-check.js +14 -2
- package/dist/core/stop-gate/stop-gate-writer.js +61 -11
- package/dist/core/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function resolveGlmNarutoIsolationPolicy(input) {
|
|
2
|
+
const requested = input.useWorktree ? 'git-worktree' : input.patchEnvelopeOnly ? 'patch-envelope-only' : 'auto';
|
|
3
|
+
if (input.patchEnvelopeOnly || !input.useWorktree) {
|
|
4
|
+
return {
|
|
5
|
+
schema: 'sks.glm-naruto-isolation-policy.v1',
|
|
6
|
+
requested,
|
|
7
|
+
selected: 'patch-envelope-only',
|
|
8
|
+
honest: true,
|
|
9
|
+
reason: input.patchEnvelopeOnly ? 'patch_envelope_only_requested' : 'worktree_not_requested',
|
|
10
|
+
blockers: [],
|
|
11
|
+
fallback_allowed: Boolean(input.fallbackAllowed),
|
|
12
|
+
workers_write_main_workspace: false
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
if (input.gitAvailable) {
|
|
16
|
+
return {
|
|
17
|
+
schema: 'sks.glm-naruto-isolation-policy.v1',
|
|
18
|
+
requested,
|
|
19
|
+
selected: 'git-worktree',
|
|
20
|
+
honest: true,
|
|
21
|
+
reason: 'git_worktree_available',
|
|
22
|
+
blockers: [],
|
|
23
|
+
fallback_allowed: Boolean(input.fallbackAllowed),
|
|
24
|
+
workers_write_main_workspace: false
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
schema: 'sks.glm-naruto-isolation-policy.v1',
|
|
29
|
+
requested,
|
|
30
|
+
selected: input.fallbackAllowed ? 'patch-envelope-only' : 'blocked',
|
|
31
|
+
honest: true,
|
|
32
|
+
reason: input.fallbackAllowed ? 'git_worktree_unavailable_fallback_allowed' : 'git_worktree_unavailable',
|
|
33
|
+
blockers: input.fallbackAllowed ? [] : ['glm_naruto_worktree_not_implemented_or_unavailable'],
|
|
34
|
+
fallback_allowed: Boolean(input.fallbackAllowed),
|
|
35
|
+
workers_write_main_workspace: false
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=glm-naruto-isolation-policy.js.map
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { getNonConflictingSets } from './glm-naruto-conflict-graph.js';
|
|
2
2
|
export function planMerge(input) {
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const scoreByPatch = new Map((input.scoreboard?.scores ?? []).map((score) => [score.patch_id, score]));
|
|
4
|
+
const passedNodes = input.graph.nodes.filter((n) => n.gate_passed && !scoreByPatch.get(n.patch_id)?.disqualified);
|
|
5
|
+
const filteredGraph = {
|
|
6
|
+
...input.graph,
|
|
7
|
+
nodes: passedNodes
|
|
8
|
+
};
|
|
9
|
+
const nonConflictingSets = getNonConflictingSets(filteredGraph);
|
|
5
10
|
const candidates = nonConflictingSets.map((patchIds) => {
|
|
6
11
|
const nodes = passedNodes.filter((n) => patchIds.includes(n.patch_id));
|
|
7
|
-
const totalScore = nodes.reduce((sum, n) => sum + n.score, 0);
|
|
12
|
+
const totalScore = nodes.reduce((sum, n) => sum + (scoreByPatch.get(n.patch_id)?.total_score ?? n.score), 0);
|
|
8
13
|
return { patch_ids: patchIds, total_score: totalScore, conflict_free: true };
|
|
9
|
-
});
|
|
14
|
+
}).filter((candidate) => candidate.patch_ids.every((id) => passedNodes.some((node) => node.patch_id === id)));
|
|
10
15
|
candidates.sort((a, b) => b.total_score - a.total_score);
|
|
11
16
|
let selected = [];
|
|
12
17
|
let rationale = '';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function summarizeGlmNarutoWorkerMetrics(traces) {
|
|
2
|
+
const ttft = traces.map((trace) => trace.ttft_ms).filter(isNumber).sort((a, b) => a - b);
|
|
3
|
+
const totals = traces.map((trace) => trace.total_ms).filter(isNumber).sort((a, b) => a - b);
|
|
4
|
+
const verifier = traces.filter((trace) => trace.status === 'verification_passed' || trace.status === 'verification_failed');
|
|
5
|
+
const verifierPassed = verifier.filter((trace) => trace.status === 'verification_passed').length;
|
|
6
|
+
return {
|
|
7
|
+
p50_ttft_ms: percentile(ttft, 0.5),
|
|
8
|
+
p90_ttft_ms: percentile(ttft, 0.9),
|
|
9
|
+
p50_total_ms: percentile(totals, 0.5),
|
|
10
|
+
p90_total_ms: percentile(totals, 0.9),
|
|
11
|
+
cached_tokens_sum: sumNullable(traces.map((trace) => trace.cached_tokens ?? null)),
|
|
12
|
+
cache_write_tokens_sum: sumNullable(traces.map((trace) => trace.cache_write_tokens ?? null)),
|
|
13
|
+
reasoning_tokens_sum: sumNullable(traces.map((trace) => trace.reasoning_tokens ?? null)),
|
|
14
|
+
workers_completed: traces.filter((trace) => trace.status === 'completed').length,
|
|
15
|
+
workers_failed: traces.filter((trace) => trace.status === 'failed').length,
|
|
16
|
+
verifier_pass_rate: verifier.length ? verifierPassed / verifier.length : 0
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function percentile(values, quantile) {
|
|
20
|
+
if (!values.length)
|
|
21
|
+
return null;
|
|
22
|
+
const index = Math.min(values.length - 1, Math.max(0, Math.ceil(values.length * quantile) - 1));
|
|
23
|
+
return values[index] ?? null;
|
|
24
|
+
}
|
|
25
|
+
function sumNullable(values) {
|
|
26
|
+
const present = values.filter(isNumber);
|
|
27
|
+
if (!present.length)
|
|
28
|
+
return null;
|
|
29
|
+
return present.reduce((sum, value) => sum + value, 0);
|
|
30
|
+
}
|
|
31
|
+
function isNumber(value) {
|
|
32
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=glm-naruto-metrics.js.map
|
|
@@ -2,20 +2,24 @@ import path from 'node:path';
|
|
|
2
2
|
import { nowIso, writeJsonAtomic } from '../../../fsx.js';
|
|
3
3
|
import { GLM_52_OPENROUTER_MODEL } from '../glm-52-settings.js';
|
|
4
4
|
import { resolveOpenRouterApiKey } from '../../openrouter/openrouter-secret-store.js';
|
|
5
|
-
import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
|
|
6
5
|
import { decomposeTask, validateWorkGraph } from './glm-naruto-decomposer.js';
|
|
7
6
|
import { planShardCandidates, computeInitialLaneMix } from './glm-naruto-shard-planner.js';
|
|
8
7
|
import { runPatchWorkerPool } from './glm-naruto-worker-pool.js';
|
|
9
8
|
import { runVerifierWorker } from './glm-naruto-worker-runtime.js';
|
|
10
9
|
import { buildConflictGraph } from './glm-naruto-conflict-graph.js';
|
|
11
|
-
import { planMerge } from './glm-naruto-merge-planner.js';
|
|
12
10
|
import { finalizeMergePlan } from './glm-naruto-finalizer.js';
|
|
13
11
|
import { planRepairWave } from './glm-naruto-repair-wave.js';
|
|
14
|
-
import { createBudget, checkBudget
|
|
12
|
+
import { createBudget, checkBudget } from './glm-naruto-budget.js';
|
|
15
13
|
import { createProviderHealthTracker } from '../../openrouter/openrouter-provider-health.js';
|
|
16
14
|
import { createMissionTrace, recordWorkerTrace, writeMissionArtifacts, buildMissionSummary } from './glm-naruto-trace.js';
|
|
17
15
|
import { runGlmJudge } from './glm-naruto-judge.js';
|
|
18
16
|
import { writeFinalStopGate } from '../../../stop-gate/stop-gate-writer.js';
|
|
17
|
+
import { auditGlmNarutoArtifactsForSecrets } from './glm-naruto-secret-audit.js';
|
|
18
|
+
import { resolveGlmNarutoIsolationPolicy } from './glm-naruto-isolation-policy.js';
|
|
19
|
+
import { getGitHead, getGitRoot } from './glm-naruto-worktree-manager.js';
|
|
20
|
+
import { buildGlmNarutoCandidateScoreboard } from './glm-naruto-scoreboard.js';
|
|
21
|
+
import { runGlmNarutoApplyTransaction } from './glm-naruto-apply-transaction.js';
|
|
22
|
+
import { finalizeGlmNarutoTerminal } from './glm-naruto-terminal.js';
|
|
19
23
|
import { GLM_NARUTO_LIMITS } from './glm-naruto-types.js';
|
|
20
24
|
export async function runGlmNarutoMission(input) {
|
|
21
25
|
const missionId = input.missionId || `glm-naruto-${nowIso().replace(/[:.]/g, '-')}`;
|
|
@@ -23,10 +27,29 @@ export async function runGlmNarutoMission(input) {
|
|
|
23
27
|
const startedMs = Date.now();
|
|
24
28
|
const key = await resolveOpenRouterApiKey({ env: process.env });
|
|
25
29
|
if (!key.key) {
|
|
26
|
-
return
|
|
30
|
+
return finalizeGlmNarutoTerminal({
|
|
31
|
+
root: cwd,
|
|
32
|
+
missionId,
|
|
33
|
+
result: missionResult(missionId, input.task, 'blocked', 'glm_missing_openrouter_key', 0, startedMs, [], [], ['glm_missing_openrouter_key'], [])
|
|
34
|
+
});
|
|
27
35
|
}
|
|
28
36
|
const mentionedPaths = extractMentionedPaths(input.task);
|
|
29
37
|
const gitStatus = await readGitStatus(cwd);
|
|
38
|
+
const gitRoot = await getGitRoot(cwd);
|
|
39
|
+
const baseCommit = gitRoot ? await getGitHead(cwd) : null;
|
|
40
|
+
const isolationPolicy = resolveGlmNarutoIsolationPolicy({
|
|
41
|
+
...(input.useWorktree !== undefined ? { useWorktree: input.useWorktree } : {}),
|
|
42
|
+
...(input.patchEnvelopeOnly !== undefined ? { patchEnvelopeOnly: input.patchEnvelopeOnly } : {}),
|
|
43
|
+
...(input.allowPatchEnvelopeFallback !== undefined ? { fallbackAllowed: input.allowPatchEnvelopeFallback } : {}),
|
|
44
|
+
gitAvailable: Boolean(gitRoot && baseCommit)
|
|
45
|
+
});
|
|
46
|
+
if (isolationPolicy.selected === 'blocked') {
|
|
47
|
+
return finalizeGlmNarutoTerminal({
|
|
48
|
+
root: cwd,
|
|
49
|
+
missionId,
|
|
50
|
+
result: missionResult(missionId, input.task, 'blocked', isolationPolicy.reason, 0, startedMs, [], [], isolationPolicy.blockers, [])
|
|
51
|
+
});
|
|
52
|
+
}
|
|
30
53
|
const graph = decomposeTask({
|
|
31
54
|
missionId,
|
|
32
55
|
task: input.task,
|
|
@@ -36,12 +59,20 @@ export async function runGlmNarutoMission(input) {
|
|
|
36
59
|
const isVerifyOnly = input.task.trim().toLowerCase().startsWith('verify');
|
|
37
60
|
const validation = validateWorkGraph(graph, isVerifyOnly);
|
|
38
61
|
if (!validation.ok) {
|
|
39
|
-
return
|
|
62
|
+
return finalizeGlmNarutoTerminal({
|
|
63
|
+
root: cwd,
|
|
64
|
+
missionId,
|
|
65
|
+
result: missionResult(missionId, input.task, 'blocked', validation.reason || 'invalid_work_graph', 0, startedMs, [], [], [validation.reason || 'invalid_work_graph'], [])
|
|
66
|
+
});
|
|
40
67
|
}
|
|
41
68
|
const budget = createBudget(missionId, input.deep || false);
|
|
42
69
|
const budgetCheck = checkBudget(budget);
|
|
43
70
|
if (!budgetCheck.ok) {
|
|
44
|
-
return
|
|
71
|
+
return finalizeGlmNarutoTerminal({
|
|
72
|
+
root: cwd,
|
|
73
|
+
missionId,
|
|
74
|
+
result: missionResult(missionId, input.task, 'budget_exhausted', budgetCheck.reason, 0, startedMs, [], [], [budgetCheck.reason], [])
|
|
75
|
+
});
|
|
45
76
|
}
|
|
46
77
|
const laneMix = computeInitialLaneMix(graph);
|
|
47
78
|
const strategies = planShardCandidates(graph);
|
|
@@ -60,10 +91,22 @@ export async function runGlmNarutoMission(input) {
|
|
|
60
91
|
contextSummary: JSON.stringify({ task: input.task, git_status: gitStatus || '' }),
|
|
61
92
|
maxWorkers: input.maxWorkers || laneMix.patch_workers,
|
|
62
93
|
workerTimeoutMs: GLM_NARUTO_LIMITS.max_worker_runtime_ms,
|
|
63
|
-
strategies: strategyMap
|
|
94
|
+
strategies: strategyMap,
|
|
95
|
+
isolationMode: isolationPolicy.selected,
|
|
96
|
+
cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
|
|
97
|
+
baseCommit
|
|
64
98
|
});
|
|
65
99
|
for (const trace of poolResult.traces) {
|
|
66
100
|
traceState = recordWorkerTrace(traceState, trace);
|
|
101
|
+
if (trace.ttft_ms !== null) {
|
|
102
|
+
healthTracker.record({
|
|
103
|
+
provider_slug: trace.provider_slug || 'openrouter',
|
|
104
|
+
model: trace.model,
|
|
105
|
+
p50_ttft_ms: trace.ttft_ms,
|
|
106
|
+
last_success: trace.status === 'completed' || trace.status === 'verification_passed' ? nowIso() : null,
|
|
107
|
+
last_failure: trace.status === 'failed' ? nowIso() : null
|
|
108
|
+
});
|
|
109
|
+
}
|
|
67
110
|
}
|
|
68
111
|
healthTracker.record({ provider_slug: 'openrouter', model: GLM_52_OPENROUTER_MODEL, count_429: 0, count_5xx: 0 });
|
|
69
112
|
let envelopes = poolResult.envelopes;
|
|
@@ -86,7 +129,10 @@ export async function runGlmNarutoMission(input) {
|
|
|
86
129
|
contextSummary: JSON.stringify({ task: input.task, repair: true }),
|
|
87
130
|
maxWorkers: input.maxWorkers || 3,
|
|
88
131
|
workerTimeoutMs: GLM_NARUTO_LIMITS.max_worker_runtime_ms,
|
|
89
|
-
strategies: new Map(repairPlan.shardsToRepair.map((s) => [s.id, [s.strategy]]))
|
|
132
|
+
strategies: new Map(repairPlan.shardsToRepair.map((s) => [s.id, [s.strategy]])),
|
|
133
|
+
isolationMode: isolationPolicy.selected,
|
|
134
|
+
cleanupWorktrees: input.cleanupWorktrees ?? !input.keepWorktrees,
|
|
135
|
+
baseCommit
|
|
90
136
|
});
|
|
91
137
|
envelopes = [...envelopes, ...repairPool.envelopes];
|
|
92
138
|
for (const trace of repairPool.traces) {
|
|
@@ -97,7 +143,9 @@ export async function runGlmNarutoMission(input) {
|
|
|
97
143
|
}
|
|
98
144
|
// 4.0.9: Verifier wave — run parallel verifier workers over gate-passed candidates.
|
|
99
145
|
let passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
|
|
100
|
-
|
|
146
|
+
let verifierWaveRun = false;
|
|
147
|
+
if (passedEnvelopes.length > 0 && !input.skipVerifier) {
|
|
148
|
+
verifierWaveRun = true;
|
|
101
149
|
const verifyApiKey = key.key;
|
|
102
150
|
const verifyResults = await Promise.allSettled(passedEnvelopes.map((env) => runVerifierWorker({
|
|
103
151
|
apiKey: verifyApiKey,
|
|
@@ -118,6 +166,15 @@ export async function runGlmNarutoMission(input) {
|
|
|
118
166
|
}
|
|
119
167
|
if (res.status === 'fulfilled') {
|
|
120
168
|
traceState = recordWorkerTrace(traceState, res.value.trace);
|
|
169
|
+
if (res.value.trace.ttft_ms !== null) {
|
|
170
|
+
healthTracker.record({
|
|
171
|
+
provider_slug: res.value.trace.provider_slug || 'openrouter',
|
|
172
|
+
model: res.value.trace.model,
|
|
173
|
+
p50_ttft_ms: res.value.trace.ttft_ms,
|
|
174
|
+
last_success: res.value.ok ? nowIso() : null,
|
|
175
|
+
last_failure: res.value.ok ? null : nowIso()
|
|
176
|
+
});
|
|
177
|
+
}
|
|
121
178
|
}
|
|
122
179
|
}
|
|
123
180
|
envelopes = envelopes.map((e) => {
|
|
@@ -126,6 +183,7 @@ export async function runGlmNarutoMission(input) {
|
|
|
126
183
|
});
|
|
127
184
|
passedEnvelopes = envelopes.filter((e) => e.status === 'gate_passed');
|
|
128
185
|
}
|
|
186
|
+
const warnings = input.skipVerifier && passedEnvelopes.length > 0 ? ['verifier_skipped_by_flag'] : [];
|
|
129
187
|
// Build conflict graph and merge plan
|
|
130
188
|
const nodes = passedEnvelopes.map((env) => ({
|
|
131
189
|
patch_id: env.worker_id,
|
|
@@ -136,6 +194,13 @@ export async function runGlmNarutoMission(input) {
|
|
|
136
194
|
patch_sha256: env.patch_sha256
|
|
137
195
|
}));
|
|
138
196
|
const conflictGraph = buildConflictGraph(passedEnvelopes, nodes);
|
|
197
|
+
const candidateScoreboard = buildGlmNarutoCandidateScoreboard({
|
|
198
|
+
missionId,
|
|
199
|
+
envelopes,
|
|
200
|
+
traces: traceState.workerTraces,
|
|
201
|
+
graph: conflictGraph,
|
|
202
|
+
requestedPaths: mentionedPaths
|
|
203
|
+
});
|
|
139
204
|
let judgeResult = null;
|
|
140
205
|
if (input.useJudge && passedEnvelopes.length > 1) {
|
|
141
206
|
judgeResult = await runGlmJudge({
|
|
@@ -149,26 +214,33 @@ export async function runGlmNarutoMission(input) {
|
|
|
149
214
|
missionId,
|
|
150
215
|
envelopes: passedEnvelopes,
|
|
151
216
|
...(judgeResult ? { judgeResult } : {}),
|
|
217
|
+
scoreboard: candidateScoreboard,
|
|
152
218
|
useJudge: input.useJudge || false,
|
|
153
219
|
xhighFinalizer: input.xhighFinalizer || false
|
|
154
220
|
});
|
|
155
221
|
// Apply winning merge plan
|
|
156
222
|
let appliedPatches = 0;
|
|
157
223
|
let applyResult = null;
|
|
224
|
+
let applyTransaction = null;
|
|
225
|
+
const artifactDir = path.join(cwd, '.sneakoscope', 'glm-naruto', missionId);
|
|
158
226
|
if (!input.noApply && mergePlan.selected_patches.length > 0) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
applyResult = { ok:
|
|
227
|
+
const transactionResult = await runGlmNarutoApplyTransaction({
|
|
228
|
+
cwd,
|
|
229
|
+
missionId,
|
|
230
|
+
envelopes,
|
|
231
|
+
selectedPatchIds: mergePlan.selected_patches,
|
|
232
|
+
artifactDir
|
|
233
|
+
});
|
|
234
|
+
applyTransaction = transactionResult.transaction;
|
|
235
|
+
appliedPatches = transactionResult.ok ? transactionResult.applied.length : 0;
|
|
236
|
+
applyResult = { ok: transactionResult.ok, applied: transactionResult.applied, ...(transactionResult.transaction.blockers[0] ? { blocker: transactionResult.transaction.blockers[0] } : {}) };
|
|
169
237
|
}
|
|
170
238
|
const terminalState = appliedPatches > 0 ? 'completed' : passedEnvelopes.length > 0 ? 'partial_candidates' : 'blocked';
|
|
171
|
-
const terminationReason = appliedPatches > 0
|
|
239
|
+
const terminationReason = appliedPatches > 0
|
|
240
|
+
? 'completed_merge_applied'
|
|
241
|
+
: applyResult && !applyResult.ok
|
|
242
|
+
? 'apply_transaction_failed'
|
|
243
|
+
: passedEnvelopes.length > 0 ? 'partial_no_apply' : 'no_gate_passed_candidates';
|
|
172
244
|
const summary = buildMissionSummary({
|
|
173
245
|
missionId,
|
|
174
246
|
startedMs,
|
|
@@ -198,10 +270,10 @@ export async function runGlmNarutoMission(input) {
|
|
|
198
270
|
failed_shards: summary.failed_shards,
|
|
199
271
|
repair_waves: summary.repair_waves,
|
|
200
272
|
budget_used_ms: summary.budget_used_ms,
|
|
201
|
-
blockers: terminalState === 'blocked' ? ['no_gate_passed_candidates'] : [],
|
|
202
|
-
warnings
|
|
273
|
+
blockers: terminalState === 'blocked' ? ['no_gate_passed_candidates'] : (applyResult && !applyResult.ok ? [applyResult.blocker || 'apply_transaction_failed'] : []),
|
|
274
|
+
warnings
|
|
203
275
|
};
|
|
204
|
-
const
|
|
276
|
+
const writtenArtifactDir = await writeMissionArtifacts({
|
|
205
277
|
root: cwd,
|
|
206
278
|
missionId,
|
|
207
279
|
workGraph: graph,
|
|
@@ -210,19 +282,31 @@ export async function runGlmNarutoMission(input) {
|
|
|
210
282
|
...(judgeResult ? { judgeResult } : {}),
|
|
211
283
|
workerTraces: traceState.workerTraces,
|
|
212
284
|
providerHealth: healthTracker.snapshot(),
|
|
285
|
+
concurrencyDecisions: poolResult.concurrencyDecisions,
|
|
286
|
+
isolationPolicy,
|
|
287
|
+
candidateScoreboard,
|
|
213
288
|
termination: { schema: 'sks.glm-naruto-termination.v1', mission_id: missionId, terminal_state: terminalState, reason: terminationReason, wall_clock_ms: summary.wall_clock_ms },
|
|
214
289
|
...(applyResult ? { applyResult: { ...applyResult, schema: 'sks.glm-naruto-apply-result.v1' } } : {}),
|
|
215
|
-
|
|
290
|
+
...(applyTransaction ? { applyTransaction } : {}),
|
|
291
|
+
verificationSummary: { schema: 'sks.glm-naruto-verification.v1', verified: passedEnvelopes.length, total: envelopes.length, verifier_wave_run: verifierWaveRun, skip_verifier: input.skipVerifier === true },
|
|
216
292
|
missionResult: result,
|
|
217
293
|
envelopes
|
|
218
294
|
});
|
|
295
|
+
const secretAudit = await auditGlmNarutoArtifactsForSecrets(path.join(cwd, '.sneakoscope', 'glm-naruto', missionId)).catch((err) => ({
|
|
296
|
+
schema: 'sks.glm-naruto-secret-audit.v1',
|
|
297
|
+
ok: false,
|
|
298
|
+
root: path.join(cwd, '.sneakoscope', 'glm-naruto', missionId),
|
|
299
|
+
scanned_files: 0,
|
|
300
|
+
findings: [`audit_failed:${err instanceof Error ? err.message : String(err)}`]
|
|
301
|
+
}));
|
|
302
|
+
await writeJsonAtomic(path.join(writtenArtifactDir, 'secret-audit.json'), secretAudit).catch(() => undefined);
|
|
219
303
|
// 4.0.9: Write canonical stop-gate artifacts for hook resolution.
|
|
220
304
|
await writeFinalStopGate({
|
|
221
305
|
root: cwd,
|
|
222
306
|
missionId,
|
|
223
307
|
route: 'GLM_NARUTO',
|
|
224
308
|
routeCommand: '$Naruto',
|
|
225
|
-
status: result.ok ? 'passed' : (terminalState === 'blocked' ? 'blocked' : 'failed'),
|
|
309
|
+
status: result.ok && secretAudit.ok ? 'passed' : (terminalState === 'blocked' || !secretAudit.ok ? 'blocked' : 'failed'),
|
|
226
310
|
terminal: terminalState === 'completed' || terminalState === 'blocked',
|
|
227
311
|
terminalState,
|
|
228
312
|
evidence: {
|
|
@@ -230,13 +314,17 @@ export async function runGlmNarutoMission(input) {
|
|
|
230
314
|
tests_passed: result.ok,
|
|
231
315
|
route_evidence_passed: result.ok,
|
|
232
316
|
per_worker_artifacts: true,
|
|
233
|
-
verifier_wave_run:
|
|
317
|
+
verifier_wave_run: verifierWaveRun,
|
|
234
318
|
model_guard_enforced: true,
|
|
319
|
+
proof_required: false,
|
|
320
|
+
proof_passed: true,
|
|
321
|
+
reflection_required: false,
|
|
322
|
+
reflection_passed: 'not_required',
|
|
235
323
|
},
|
|
236
|
-
blockers: result.blockers || [],
|
|
324
|
+
blockers: secretAudit.ok ? (result.blockers || []) : ['glm_naruto_secret_leak_detected'],
|
|
237
325
|
nativeGateFile: 'termination.json',
|
|
238
326
|
}).catch(() => null);
|
|
239
|
-
return { ...result, artifact_dir:
|
|
327
|
+
return { ...result, artifact_dir: writtenArtifactDir };
|
|
240
328
|
}
|
|
241
329
|
function missionResult(missionId, task, status, reason, patchCandidates, startedMs, envelopes, traces, blockers, warnings) {
|
|
242
330
|
return {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { checkAndApplyGlmPatch } from '../glm-patch-apply.js';
|
|
2
|
+
import { parseGlmNarutoPatchCandidate } from './glm-naruto-patch-candidate-parser.js';
|
|
3
|
+
const PROTECTED_PATH = /(^|\/)(\.github|dist|node_modules)(\/|$)/;
|
|
4
|
+
const SECRET_PATTERN = /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/;
|
|
5
|
+
export async function evaluateGlmNarutoPatchCandidateGate(input) {
|
|
6
|
+
const checks = [];
|
|
7
|
+
const parsed = parseGlmNarutoPatchCandidate(input.envelope.patch);
|
|
8
|
+
checks.push(check('candidate_parse', parsed.ok, parsed.blockers.join(',') || undefined));
|
|
9
|
+
checks.push(check('patch_section', Boolean(parsed.patch), parsed.patch ? undefined : 'missing_patch_section'));
|
|
10
|
+
checks.push(check('unified_diff', /^diff --git /m.test(parsed.patch), parsed.patch ? undefined : 'no_diff_git'));
|
|
11
|
+
const blockedPath = parsed.touched_paths.find((file) => PROTECTED_PATH.test(file));
|
|
12
|
+
checks.push(check('protected_path_guard', !blockedPath, blockedPath));
|
|
13
|
+
const secretLeak = SECRET_PATTERN.test(parsed.patch);
|
|
14
|
+
checks.push(check('secret_leakage_guard', !secretLeak, secretLeak ? 'secret_like_content' : undefined));
|
|
15
|
+
if (parsed.ok && !blockedPath && !secretLeak) {
|
|
16
|
+
const applyCheck = await checkAndApplyGlmPatch({
|
|
17
|
+
cwd: input.cwd,
|
|
18
|
+
patch: parsed.patch,
|
|
19
|
+
apply: input.apply === true
|
|
20
|
+
});
|
|
21
|
+
checks.push(check('git_apply_check', applyCheck.ok, applyCheck.ok ? undefined : applyCheck.error.code));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
checks.push({ id: 'git_apply_check', ok: false, reason: 'skipped_due_to_prior_blocker', ms: 0 });
|
|
25
|
+
}
|
|
26
|
+
const blockers = checks.filter((row) => !row.ok).map((row) => row.reason || row.id);
|
|
27
|
+
return {
|
|
28
|
+
schema: 'sks.glm-naruto-patch-candidate-gate.v1',
|
|
29
|
+
ok: blockers.length === 0,
|
|
30
|
+
worker_id: input.envelope.worker_id,
|
|
31
|
+
shard_id: input.envelope.shard_id,
|
|
32
|
+
patch_id: input.envelope.patch_sha256,
|
|
33
|
+
extracted_patch: parsed.patch,
|
|
34
|
+
touched_paths: parsed.touched_paths,
|
|
35
|
+
checks,
|
|
36
|
+
blockers
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function check(id, ok, reason) {
|
|
40
|
+
return { id, ok, ...(reason ? { reason } : {}), ms: 0 };
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=glm-naruto-patch-candidate-gate.js.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { parseUnifiedDiffPatch } from '../glm-patch-parser.js';
|
|
2
|
+
const CANDIDATE_OPEN = '<sks_patch_candidate>';
|
|
3
|
+
const CANDIDATE_CLOSE = '</sks_patch_candidate>';
|
|
4
|
+
const LEGACY_PATCH_OPEN = '<sks_patch>';
|
|
5
|
+
export function parseGlmNarutoPatchCandidate(text) {
|
|
6
|
+
const blockers = [];
|
|
7
|
+
if (text.includes(LEGACY_PATCH_OPEN) && !text.includes(CANDIDATE_OPEN)) {
|
|
8
|
+
return failed(text, ['legacy_sks_patch_envelope_rejected']);
|
|
9
|
+
}
|
|
10
|
+
const body = unwrapCandidateBody(text);
|
|
11
|
+
if (!body.ok)
|
|
12
|
+
return failed(text, body.blockers);
|
|
13
|
+
const extracted = extractPatchSection(body.body);
|
|
14
|
+
if (!extracted.ok)
|
|
15
|
+
return failed(body.body, extracted.blockers);
|
|
16
|
+
const parsed = parseUnifiedDiffPatch(extracted.patch);
|
|
17
|
+
if (parsed.empty)
|
|
18
|
+
blockers.push('patch_missing_unified_diff');
|
|
19
|
+
return {
|
|
20
|
+
ok: blockers.length === 0,
|
|
21
|
+
body: body.body,
|
|
22
|
+
patch: extracted.patch,
|
|
23
|
+
touched_paths: parsed.touchedPaths,
|
|
24
|
+
blockers
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function unwrapCandidateBody(text) {
|
|
28
|
+
const start = text.indexOf(CANDIDATE_OPEN);
|
|
29
|
+
const end = text.indexOf(CANDIDATE_CLOSE);
|
|
30
|
+
if (start >= 0 || end >= 0) {
|
|
31
|
+
if (start < 0 || end <= start)
|
|
32
|
+
return { ok: false, blockers: ['malformed_patch_candidate_envelope'] };
|
|
33
|
+
return { ok: true, body: text.slice(start + CANDIDATE_OPEN.length, end).trim() };
|
|
34
|
+
}
|
|
35
|
+
if (/^\s*patch\s*:/im.test(text))
|
|
36
|
+
return { ok: true, body: text.trim() };
|
|
37
|
+
return { ok: false, blockers: ['missing_patch_candidate_envelope'] };
|
|
38
|
+
}
|
|
39
|
+
function extractPatchSection(body) {
|
|
40
|
+
const lines = body.split(/\r?\n/);
|
|
41
|
+
const patchLine = lines.findIndex((line) => /^\s*patch\s*:/i.test(line));
|
|
42
|
+
if (patchLine < 0)
|
|
43
|
+
return { ok: false, blockers: ['missing_patch_section'] };
|
|
44
|
+
const firstLine = lines[patchLine] || '';
|
|
45
|
+
const inline = firstLine.replace(/^\s*patch\s*:\s*/i, '');
|
|
46
|
+
const tail = inline.trim() ? [inline, ...lines.slice(patchLine + 1)] : lines.slice(patchLine + 1);
|
|
47
|
+
const raw = tail.join('\n').replace(/^\s*```(?:diff|patch)?\s*/i, '').replace(/\s*```\s*$/i, '').trim();
|
|
48
|
+
const diffIndex = raw.search(/^diff --git /m);
|
|
49
|
+
if (diffIndex < 0)
|
|
50
|
+
return { ok: false, blockers: ['patch_missing_diff_git'] };
|
|
51
|
+
return { ok: true, patch: raw.slice(diffIndex).trimEnd() + '\n' };
|
|
52
|
+
}
|
|
53
|
+
function failed(body, blockers) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
body,
|
|
57
|
+
patch: '',
|
|
58
|
+
touched_paths: [],
|
|
59
|
+
blockers
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=glm-naruto-patch-candidate-parser.js.map
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const SECRET_PATTERN = /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/;
|
|
2
|
+
export function buildGlmNarutoCandidateScoreboard(input) {
|
|
3
|
+
const strategyCounts = new Map();
|
|
4
|
+
for (const envelope of input.envelopes) {
|
|
5
|
+
const set = strategyCounts.get(envelope.shard_id) ?? new Set();
|
|
6
|
+
set.add(envelope.strategy);
|
|
7
|
+
strategyCounts.set(envelope.shard_id, set);
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
schema: 'sks.glm-naruto-candidate-scoreboard.v1',
|
|
11
|
+
mission_id: input.missionId,
|
|
12
|
+
scores: input.envelopes.map((envelope) => {
|
|
13
|
+
const trace = input.traces.find((item) => item.worker_id === envelope.worker_id || item.patch_digest === envelope.patch_sha256);
|
|
14
|
+
return scoreEnvelope({
|
|
15
|
+
envelope,
|
|
16
|
+
...(trace ? { trace } : {}),
|
|
17
|
+
graph: input.graph,
|
|
18
|
+
requestedPaths: input.requestedPaths,
|
|
19
|
+
strategyDiversity: strategyCounts.get(envelope.shard_id)?.size ?? 1
|
|
20
|
+
});
|
|
21
|
+
})
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function scoreEnvelope(input) {
|
|
25
|
+
const disqualification_reasons = [];
|
|
26
|
+
const secretLeak = SECRET_PATTERN.test(input.envelope.patch) || input.envelope.blockers.includes('secret_like_content');
|
|
27
|
+
const verifierFailed = input.envelope.status === 'verification_failed' || input.envelope.verification_passed === false;
|
|
28
|
+
const gatePassed = input.envelope.status === 'gate_passed' || input.envelope.status === 'selected' || input.envelope.verification_passed === true;
|
|
29
|
+
if (!gatePassed)
|
|
30
|
+
disqualification_reasons.push('deterministic_gate_failed');
|
|
31
|
+
if (verifierFailed)
|
|
32
|
+
disqualification_reasons.push('verifier_failed');
|
|
33
|
+
if (secretLeak)
|
|
34
|
+
disqualification_reasons.push('secret_leak');
|
|
35
|
+
if (input.envelope.blockers.some((blocker) => blocker.includes('protected')))
|
|
36
|
+
disqualification_reasons.push('protected_path');
|
|
37
|
+
const conflictCount = input.graph.edges.filter((edge) => edge.left_patch_id === input.envelope.worker_id || edge.right_patch_id === input.envelope.worker_id).length;
|
|
38
|
+
const requested = new Set(input.requestedPaths);
|
|
39
|
+
const targetAligned = requested.size === 0 || input.envelope.target_paths.some((target) => requested.has(target));
|
|
40
|
+
const risk = clamp(input.trace?.verifier_risk_score ?? (verifierFailed ? 1 : 0));
|
|
41
|
+
const confidence = clamp(input.trace?.verifier_confidence ?? (input.envelope.verification_passed ? 1 : 0));
|
|
42
|
+
const latency = input.trace?.ttft_ms ?? input.trace?.total_ms ?? 0;
|
|
43
|
+
const cacheTokens = input.trace?.cached_tokens ?? 0;
|
|
44
|
+
const patchSizePenalty = Math.min(50, Math.ceil(input.envelope.patch.length / 400));
|
|
45
|
+
const components = {
|
|
46
|
+
deterministic_gate: gatePassed ? 100 : 0,
|
|
47
|
+
verifier: input.envelope.verification_passed === false ? -100 : input.envelope.verification_passed === true ? 50 : 0,
|
|
48
|
+
verifier_confidence: Math.round(confidence * 20),
|
|
49
|
+
verifier_risk_penalty: -Math.round(risk * 50),
|
|
50
|
+
patch_size_penalty: -patchSizePenalty,
|
|
51
|
+
touched_path_penalty: -Math.min(30, input.envelope.target_paths.length * 5),
|
|
52
|
+
target_alignment: targetAligned ? 20 : -30,
|
|
53
|
+
hunk_conflict_penalty: -Math.min(60, conflictCount * 20),
|
|
54
|
+
latency_penalty: -Math.min(25, Math.floor(latency / 2_000)),
|
|
55
|
+
cache_bonus: Math.min(20, Math.floor(cacheTokens / 1_000)),
|
|
56
|
+
strategy_diversity_bonus: input.strategyDiversity > 1 ? 10 : 0,
|
|
57
|
+
secret_safety: secretLeak ? -1_000 : 25
|
|
58
|
+
};
|
|
59
|
+
const total = Object.values(components).reduce((sum, value) => sum + value, 0);
|
|
60
|
+
return {
|
|
61
|
+
schema: 'sks.glm-naruto-candidate-score.v1',
|
|
62
|
+
patch_id: input.envelope.worker_id,
|
|
63
|
+
shard_id: input.envelope.shard_id,
|
|
64
|
+
total_score: disqualification_reasons.length ? -1_000 : total,
|
|
65
|
+
components,
|
|
66
|
+
disqualified: disqualification_reasons.length > 0,
|
|
67
|
+
disqualification_reasons
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function clamp(value) {
|
|
71
|
+
if (!Number.isFinite(value))
|
|
72
|
+
return 0;
|
|
73
|
+
return Math.max(0, Math.min(1, value));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=glm-naruto-scoreboard.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import fsp from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const SECRET_PATTERN = /\b(?:Bearer\s+[A-Za-z0-9._~+/-]+|sk-(?:or-)?[A-Za-z0-9_-]{12,}|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)\b/;
|
|
4
|
+
const SECRET_KEY_PATTERN = /^(authorization|api_key|apiKey|access_token|token|secret|password|OPENROUTER_API_KEY|SKS_OPENROUTER_API_KEY)$/i;
|
|
5
|
+
const REDACTED_MARKERS = new Set(['[REDACTED]', '<redacted>', 'sk-or-[REDACTED]', 'Bearer [REDACTED]']);
|
|
6
|
+
export async function auditGlmNarutoArtifactsForSecrets(root) {
|
|
7
|
+
const findings = [];
|
|
8
|
+
let scanned = 0;
|
|
9
|
+
await scan(root, async (file) => {
|
|
10
|
+
if (!/\.(json|jsonl|md|txt)$/i.test(file))
|
|
11
|
+
return;
|
|
12
|
+
scanned++;
|
|
13
|
+
const content = await fsp.readFile(file, 'utf8').catch(() => '');
|
|
14
|
+
const fileFindings = auditContentForSecrets(content);
|
|
15
|
+
if (fileFindings.length)
|
|
16
|
+
findings.push(`${path.relative(root, file)}:${fileFindings.join(',')}`);
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
schema: 'sks.glm-naruto-secret-audit.v1',
|
|
20
|
+
ok: findings.length === 0,
|
|
21
|
+
root,
|
|
22
|
+
scanned_files: scanned,
|
|
23
|
+
findings
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function auditContentForSecrets(content) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
if (SECRET_PATTERN.test(content))
|
|
29
|
+
findings.push('secret_like_content');
|
|
30
|
+
for (const parsed of parseJsonLike(content)) {
|
|
31
|
+
collectJsonSecretFindings(parsed, findings);
|
|
32
|
+
}
|
|
33
|
+
return [...new Set(findings)];
|
|
34
|
+
}
|
|
35
|
+
function parseJsonLike(content) {
|
|
36
|
+
const parsed = [];
|
|
37
|
+
try {
|
|
38
|
+
parsed.push(JSON.parse(content));
|
|
39
|
+
return parsed;
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
for (const line of content.split(/\r?\n/)) {
|
|
43
|
+
const trimmed = line.trim();
|
|
44
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('['))
|
|
45
|
+
continue;
|
|
46
|
+
try {
|
|
47
|
+
parsed.push(JSON.parse(trimmed));
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
50
|
+
}
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
function collectJsonSecretFindings(value, findings, key = '') {
|
|
54
|
+
if (!value || typeof value !== 'object') {
|
|
55
|
+
if (SECRET_KEY_PATTERN.test(key) && typeof value === 'string' && value.trim() && !REDACTED_MARKERS.has(value.trim())) {
|
|
56
|
+
findings.push(`secret_key:${key}`);
|
|
57
|
+
}
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (Array.isArray(value)) {
|
|
61
|
+
for (const item of value)
|
|
62
|
+
collectJsonSecretFindings(item, findings, key);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const [entryKey, entryValue] of Object.entries(value)) {
|
|
66
|
+
if (SECRET_KEY_PATTERN.test(entryKey)) {
|
|
67
|
+
if (typeof entryValue === 'string' && entryValue.trim() && !REDACTED_MARKERS.has(entryValue.trim()))
|
|
68
|
+
findings.push(`secret_key:${entryKey}`);
|
|
69
|
+
else if (entryValue && typeof entryValue === 'object')
|
|
70
|
+
findings.push(`secret_key:${entryKey}`);
|
|
71
|
+
}
|
|
72
|
+
collectJsonSecretFindings(entryValue, findings, entryKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function scan(dir, visit) {
|
|
76
|
+
let entries;
|
|
77
|
+
try {
|
|
78
|
+
entries = await fsp.readdir(dir, { withFileTypes: true });
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const p = path.join(dir, String(entry.name));
|
|
85
|
+
if (entry.isDirectory())
|
|
86
|
+
await scan(p, visit);
|
|
87
|
+
else if (entry.isFile())
|
|
88
|
+
await visit(p);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=glm-naruto-secret-audit.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
const SAFE_SESSION_ID = /^[A-Za-z0-9._:-]+$/;
|
|
3
|
+
export function normalizeGlmNarutoSessionId(raw) {
|
|
4
|
+
const sanitized = raw.replace(/[^A-Za-z0-9._:-]/g, '-');
|
|
5
|
+
if (sanitized.length <= 256 && SAFE_SESSION_ID.test(sanitized))
|
|
6
|
+
return sanitized;
|
|
7
|
+
const digest = crypto.createHash('sha256').update(sanitized).digest('hex').slice(0, 24);
|
|
8
|
+
return `${sanitized.slice(0, 231)}-${digest}`.slice(0, 256);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=glm-naruto-session-id.js.map
|