@guilz-dev/belay 0.2.0 → 0.4.0
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 +39 -12
- package/dist/adapters/cursor/runtime-entry.js +1 -0
- package/dist/adapters/shared/gate-runtime.d.ts +11 -14
- package/dist/adapters/shared/gate-runtime.js +289 -23
- package/dist/bundle/claude-runtime.mjs +4435 -708
- package/dist/bundle/codex-runtime.mjs +4448 -710
- package/dist/bundle/cursor-runtime.mjs +4437 -710
- package/dist/cli.js +333 -11
- package/dist/commands/approve.d.ts +1 -0
- package/dist/commands/approve.js +56 -26
- package/dist/commands/config.d.ts +70 -0
- package/dist/commands/config.js +577 -0
- package/dist/commands/doctor.js +13 -2
- package/dist/commands/explain.js +6 -1
- package/dist/commands/harvest.d.ts +29 -0
- package/dist/commands/harvest.js +72 -0
- package/dist/commands/health-snapshot.d.ts +6 -0
- package/dist/commands/health-snapshot.js +34 -0
- package/dist/commands/judge.d.ts +100 -0
- package/dist/commands/judge.js +304 -0
- package/dist/commands/metrics.js +34 -2
- package/dist/commands/quality.d.ts +38 -0
- package/dist/commands/quality.js +105 -0
- package/dist/commands/simulate.d.ts +1 -0
- package/dist/commands/simulate.js +7 -2
- package/dist/commands/standing-allow.d.ts +10 -0
- package/dist/commands/standing-allow.js +26 -0
- package/dist/commands/status.js +2 -2
- package/dist/commands/stdin-key.d.ts +1 -0
- package/dist/commands/stdin-key.js +8 -0
- package/dist/commands/tui.d.ts +48 -0
- package/dist/commands/tui.js +150 -0
- package/dist/config-io.js +10 -5
- package/dist/conformance/guarantee-table.js +12 -0
- package/dist/conformance/types.d.ts +2 -0
- package/dist/core/approval-replay-cli.d.ts +7 -0
- package/dist/core/approval-replay-cli.js +36 -0
- package/dist/core/approval-replay.d.ts +45 -0
- package/dist/core/approval-replay.js +141 -0
- package/dist/core/approval-service.d.ts +15 -0
- package/dist/core/approval-service.js +47 -2
- package/dist/core/approval.d.ts +24 -3
- package/dist/core/approval.js +47 -4
- package/dist/core/audit-analysis.d.ts +7 -1
- package/dist/core/audit-analysis.js +111 -0
- package/dist/core/audit-io.d.ts +2 -0
- package/dist/core/audit-io.js +14 -0
- package/dist/core/audit-metrics.d.ts +7 -0
- package/dist/core/audit-metrics.js +24 -4
- package/dist/core/audit-query.d.ts +1 -0
- package/dist/core/audit-query.js +3 -0
- package/dist/core/audit-replay-context.d.ts +35 -0
- package/dist/core/audit-replay-context.js +88 -0
- package/dist/core/audit-types.d.ts +24 -1
- package/dist/core/audit-types.js +1 -1
- package/dist/core/capability/index.d.ts +4 -3
- package/dist/core/capability/index.js +3 -2
- package/dist/core/capability/paths.d.ts +2 -1
- package/dist/core/capability/paths.js +76 -8
- package/dist/core/capability/reasons.d.ts +3 -0
- package/dist/core/capability/reasons.js +8 -0
- package/dist/core/capability/trusted-workspace-roots.d.ts +25 -0
- package/dist/core/capability/trusted-workspace-roots.js +149 -0
- package/dist/core/capability/types.d.ts +11 -1
- package/dist/core/capability-approval.d.ts +2 -1
- package/dist/core/capability-approval.js +100 -2
- package/dist/core/classify-subagent.js +2 -20
- package/dist/core/classify-tool.js +129 -30
- package/dist/core/config-layers.js +2 -1
- package/dist/core/config.d.ts +40 -2
- package/dist/core/config.js +159 -11
- package/dist/core/credential-store.d.ts +11 -0
- package/dist/core/credential-store.js +60 -0
- package/dist/core/gate-engine.js +104 -13
- package/dist/core/harvest.d.ts +53 -0
- package/dist/core/harvest.js +276 -0
- package/dist/core/index.d.ts +9 -3
- package/dist/core/index.js +6 -2
- package/dist/core/integrity.d.ts +2 -0
- package/dist/core/integrity.js +13 -0
- package/dist/core/judge-api-key.d.ts +19 -0
- package/dist/core/judge-api-key.js +74 -0
- package/dist/core/judge-cloud-consent.d.ts +13 -0
- package/dist/core/judge-cloud-consent.js +38 -0
- package/dist/core/judge-config.d.ts +41 -4
- package/dist/core/judge-config.js +263 -57
- package/dist/core/judge-doctor.d.ts +9 -1
- package/dist/core/judge-doctor.js +199 -94
- package/dist/core/judge-model-discovery.d.ts +24 -0
- package/dist/core/judge-model-discovery.js +168 -0
- package/dist/core/judge-model-policy.d.ts +5 -0
- package/dist/core/judge-model-policy.js +21 -0
- package/dist/core/judge-runtime-detection.d.ts +9 -0
- package/dist/core/judge-runtime-detection.js +68 -0
- package/dist/core/path-utils.d.ts +11 -0
- package/dist/core/path-utils.js +56 -6
- package/dist/core/reclassify.d.ts +3 -0
- package/dist/core/reclassify.js +44 -14
- package/dist/core/replay-scrub.d.ts +9 -0
- package/dist/core/replay-scrub.js +43 -0
- package/dist/core/shell-tokenizer.d.ts +2 -0
- package/dist/core/shell-tokenizer.js +82 -23
- package/dist/core/standing-allow.d.ts +50 -0
- package/dist/core/standing-allow.js +175 -0
- package/dist/core/transactional/diff-evaluator.js +1 -19
- package/dist/core/types.d.ts +17 -0
- package/dist/core/verdict/adapter.d.ts +1 -0
- package/dist/core/verdict/adapter.js +17 -2
- package/dist/core/verdict/containment.d.ts +8 -2
- package/dist/core/verdict/containment.js +45 -10
- package/dist/core/verdict/judge-audit.d.ts +1 -0
- package/dist/core/verdict/judge-audit.js +32 -1
- package/dist/core/verdict/judge-baseline.d.ts +20 -0
- package/dist/core/verdict/judge-baseline.js +48 -0
- package/dist/core/verdict/judge-broker-service.d.ts +42 -0
- package/dist/core/verdict/judge-broker-service.js +279 -0
- package/dist/core/verdict/judge-catalog.d.ts +40 -0
- package/dist/core/verdict/judge-catalog.js +148 -0
- package/dist/core/verdict/judge-cli-fingerprint.d.ts +5 -0
- package/dist/core/verdict/judge-cli-fingerprint.js +61 -0
- package/dist/core/verdict/judge-cli.d.ts +46 -0
- package/dist/core/verdict/judge-cli.js +401 -0
- package/dist/core/verdict/judge-factory.d.ts +15 -4
- package/dist/core/verdict/judge-factory.js +127 -15
- package/dist/core/verdict/judge-provider-matrix.d.ts +20 -0
- package/dist/core/verdict/judge-provider-matrix.js +66 -0
- package/dist/core/verdict/judge-runtime-config.d.ts +42 -0
- package/dist/core/verdict/judge-runtime-config.js +92 -0
- package/dist/core/verdict/judge-session-broker.d.ts +48 -0
- package/dist/core/verdict/judge-session-broker.js +195 -0
- package/dist/core/verdict/judge-session-guard.d.ts +27 -0
- package/dist/core/verdict/judge-session-guard.js +91 -0
- package/dist/core/verdict/judge-session-kill-switch.d.ts +11 -0
- package/dist/core/verdict/judge-session-kill-switch.js +43 -0
- package/dist/core/verdict/judge-session-mutex.d.ts +9 -0
- package/dist/core/verdict/judge-session-mutex.js +23 -0
- package/dist/core/verdict/judge-shadow.d.ts +24 -0
- package/dist/core/verdict/judge-shadow.js +116 -0
- package/dist/core/verdict/judge-transport.d.ts +40 -0
- package/dist/core/verdict/judge-transport.js +258 -0
- package/dist/core/verdict/judge.d.ts +33 -1
- package/dist/core/verdict/judge.js +84 -14
- package/dist/core/verdict/parser.js +6 -1
- package/dist/core/verdict/persistent-paths.d.ts +8 -0
- package/dist/core/verdict/persistent-paths.js +52 -0
- package/dist/core/verdict/types.d.ts +19 -2
- package/dist/core/verdict/verdict.js +166 -49
- package/dist/corpus/adversarial-probe.d.ts +86 -0
- package/dist/corpus/adversarial-probe.js +220 -0
- package/dist/corpus/evaluate.d.ts +45 -12
- package/dist/corpus/evaluate.js +62 -5
- package/dist/corpus/gates.d.ts +35 -0
- package/dist/corpus/gates.js +81 -0
- package/dist/corpus/judge-accuracy.d.ts +8 -0
- package/dist/corpus/judge-accuracy.js +39 -0
- package/dist/corpus/must-allow-commands.d.ts +5 -0
- package/dist/corpus/must-allow-commands.js +13 -0
- package/dist/corpus/mutators.d.ts +28 -0
- package/dist/corpus/mutators.js +151 -0
- package/dist/corpus/ratchet.d.ts +42 -0
- package/dist/corpus/ratchet.js +116 -0
- package/dist/corpus/runtime-match.d.ts +19 -0
- package/dist/corpus/runtime-match.js +45 -0
- package/dist/corpus/standing-allow-catalog.generated.d.ts +13 -0
- package/dist/corpus/standing-allow-catalog.generated.js +79 -0
- package/dist/corpus/types.d.ts +47 -0
- package/dist/corpus/types.js +157 -0
- package/dist/installer.js +69 -15
- package/dist/judge-broker-daemon.d.ts +1 -0
- package/dist/judge-broker-daemon.js +123 -0
- package/dist/services/sandbox-service.d.ts +1 -0
- package/dist/services/sandbox-service.js +2 -0
- package/dist/types.d.ts +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -3
- package/skills/belay/SKILL.md +15 -8
- package/skills/belay/belay-approve.md +17 -0
- package/dist/commands/init-wizard.d.ts +0 -21
- package/dist/commands/init-wizard.js +0 -63
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { canonicalStringify, toolFingerprint } from './fingerprint.js';
|
|
3
3
|
import { matchesSensitivePath } from './glob.js';
|
|
4
|
-
import { pathWithinRoot,
|
|
4
|
+
import { pathWithinRoot, resolveWorkspaceRootMatch } from './path-utils.js';
|
|
5
5
|
import { scrubValue } from './scrub.js';
|
|
6
|
-
import { classifyShell } from './verdict/adapter.js';
|
|
6
|
+
import { classifyShell, resolveClassifierTrustedCwd } from './verdict/adapter.js';
|
|
7
|
+
import { mutationPrescanRequiresAsk, tier1RequiresAsk } from './verdict/judge.js';
|
|
8
|
+
import { createJudgeFromConfig } from './verdict/judge-factory.js';
|
|
7
9
|
const DEFAULT_SENSITIVE_PATHS = ['.env', '.env.*', '**/credentials/**'];
|
|
8
10
|
const FILE_WRITE_TOOL_NAMES = new Set(['write']);
|
|
9
11
|
const FILE_EDIT_TOOL_NAMES = new Set([
|
|
@@ -74,6 +76,75 @@ function applyPatchTargets(patch) {
|
|
|
74
76
|
function normalizedToolName(toolName) {
|
|
75
77
|
return toolName.trim().toLowerCase();
|
|
76
78
|
}
|
|
79
|
+
function resolveFileTier1Judge(config, options, repoRoot) {
|
|
80
|
+
return options.tier1Judge ?? createJudgeFromConfig(config, { repoRoot });
|
|
81
|
+
}
|
|
82
|
+
async function classifyFileMutationWithTier1(params) {
|
|
83
|
+
const judge = resolveFileTier1Judge(params.config, params.options, params.repoRoot);
|
|
84
|
+
const trustedCwd = resolveClassifierTrustedCwd(params.cwd, params.options);
|
|
85
|
+
const prescan = mutationPrescanRequiresAsk({
|
|
86
|
+
targets: [params.filePath],
|
|
87
|
+
cwd: params.cwd,
|
|
88
|
+
repoRoot: params.repoRoot,
|
|
89
|
+
trustedCwd,
|
|
90
|
+
trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
|
|
91
|
+
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
92
|
+
});
|
|
93
|
+
if (prescan) {
|
|
94
|
+
return {
|
|
95
|
+
verdict: 'deny_pending_approval',
|
|
96
|
+
reason: 'tier1_catastrophic',
|
|
97
|
+
summary: params.filePath,
|
|
98
|
+
fingerprint: toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot),
|
|
99
|
+
assessment: {
|
|
100
|
+
reversibility: 'irreversible',
|
|
101
|
+
external: params.locationLabel === 'outside_repo',
|
|
102
|
+
blastRadius: params.locationLabel === 'outside_repo'
|
|
103
|
+
? 'outside the repository'
|
|
104
|
+
: 'sensitive repository file',
|
|
105
|
+
confidence: 0.95,
|
|
106
|
+
signals: [...params.signals, 'tier1_catastrophic', prescan.reason],
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const tier1Text = `file_mutation: ${params.toolKind} path=${params.resolvedPath} location=${params.locationLabel}`;
|
|
111
|
+
const tier1 = await judge.evaluate({
|
|
112
|
+
text: tier1Text,
|
|
113
|
+
context: { cwd: params.cwd, repoRoot: params.repoRoot },
|
|
114
|
+
});
|
|
115
|
+
const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
|
|
116
|
+
if (tier1RequiresAsk(tier1)) {
|
|
117
|
+
return {
|
|
118
|
+
verdict: 'deny_pending_approval',
|
|
119
|
+
reason: 'tier1_catastrophic',
|
|
120
|
+
summary: params.filePath,
|
|
121
|
+
fingerprint,
|
|
122
|
+
assessment: {
|
|
123
|
+
reversibility: 'irreversible',
|
|
124
|
+
external: params.locationLabel === 'outside_repo',
|
|
125
|
+
blastRadius: params.locationLabel === 'outside_repo'
|
|
126
|
+
? 'outside the repository'
|
|
127
|
+
: 'sensitive repository file',
|
|
128
|
+
confidence: 0.82,
|
|
129
|
+
signals: [...params.signals, 'tier1_catastrophic', tier1.reason],
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const reason = params.isDelete ? 'file_delete' : 'file_mutation';
|
|
134
|
+
return {
|
|
135
|
+
verdict: 'allow_flagged',
|
|
136
|
+
reason,
|
|
137
|
+
summary: params.filePath,
|
|
138
|
+
fingerprint,
|
|
139
|
+
assessment: {
|
|
140
|
+
reversibility: 'recoverable_with_cost',
|
|
141
|
+
external: params.locationLabel === 'outside_repo',
|
|
142
|
+
blastRadius: params.locationLabel === 'outside_repo' ? 'outside the repository' : 'this repository',
|
|
143
|
+
confidence: 0.72,
|
|
144
|
+
signals: [...params.signals, 'tier1_restorable', tier1.reason],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
77
148
|
export async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
78
149
|
const toolName = String(payload.tool_name ?? '');
|
|
79
150
|
const toolKind = normalizedToolName(toolName);
|
|
@@ -173,38 +244,66 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
173
244
|
},
|
|
174
245
|
};
|
|
175
246
|
}
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
247
|
+
const workspaceMatch = resolveWorkspaceRootMatch(repoRoot, options.trustedWorkspaceRoots, resolvedPath);
|
|
248
|
+
if (workspaceMatch === null) {
|
|
178
249
|
signals.push('outside_repo_path');
|
|
179
|
-
return {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
};
|
|
250
|
+
return classifyFileMutationWithTier1({
|
|
251
|
+
toolName,
|
|
252
|
+
toolKind,
|
|
253
|
+
filePath,
|
|
254
|
+
resolvedPath,
|
|
255
|
+
repoRoot,
|
|
256
|
+
cwd,
|
|
257
|
+
config,
|
|
258
|
+
options,
|
|
259
|
+
signals,
|
|
260
|
+
isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
|
|
261
|
+
locationLabel: 'outside_repo',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (workspaceMatch.kind === 'trusted') {
|
|
265
|
+
signals.push('trusted_workspace_root');
|
|
192
266
|
}
|
|
267
|
+
const trustedCwd = resolveClassifierTrustedCwd(cwd, options);
|
|
268
|
+
const workspacePrescan = mutationPrescanRequiresAsk({
|
|
269
|
+
targets: [filePath],
|
|
270
|
+
cwd,
|
|
271
|
+
repoRoot,
|
|
272
|
+
trustedCwd,
|
|
273
|
+
trustedWorkspaceRoots: options.trustedWorkspaceRoots,
|
|
274
|
+
sensitivePaths,
|
|
275
|
+
});
|
|
276
|
+
if (workspacePrescan) {
|
|
277
|
+
return classifyFileMutationWithTier1({
|
|
278
|
+
toolName,
|
|
279
|
+
toolKind,
|
|
280
|
+
filePath,
|
|
281
|
+
resolvedPath,
|
|
282
|
+
repoRoot,
|
|
283
|
+
cwd,
|
|
284
|
+
config,
|
|
285
|
+
options,
|
|
286
|
+
signals,
|
|
287
|
+
isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
|
|
288
|
+
locationLabel: 'sensitive_path',
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
const relativePath = workspaceMatch.relativePath;
|
|
193
292
|
if (matchesSensitivePath(relativePath, sensitivePaths)) {
|
|
194
293
|
signals.push('sensitive_path');
|
|
195
|
-
return {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
};
|
|
294
|
+
return classifyFileMutationWithTier1({
|
|
295
|
+
toolName,
|
|
296
|
+
toolKind,
|
|
297
|
+
filePath,
|
|
298
|
+
resolvedPath,
|
|
299
|
+
repoRoot,
|
|
300
|
+
cwd,
|
|
301
|
+
config,
|
|
302
|
+
options,
|
|
303
|
+
signals,
|
|
304
|
+
isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
|
|
305
|
+
locationLabel: 'sensitive_path',
|
|
306
|
+
});
|
|
208
307
|
}
|
|
209
308
|
if (FILE_DELETE_TOOL_NAMES.has(toolKind)) {
|
|
210
309
|
signals.push('file_delete');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { applyConfigPreset } from '../presets.js';
|
|
3
|
-
import { DEFAULT_CONFIG_V3, mergeConfig } from './config.js';
|
|
3
|
+
import { DEFAULT_CONFIG_V3, mergeConfig, rejectTeamLayerJudgeSecrets } from './config.js';
|
|
4
4
|
export function teamConfigPath(homedir = () => process.env.HOME ?? process.env.USERPROFILE ?? '') {
|
|
5
5
|
const xdg = process.env.XDG_CONFIG_HOME?.trim();
|
|
6
6
|
const base = xdg || path.join(homedir(), '.config');
|
|
@@ -40,6 +40,7 @@ export function resolveLayeredConfig(params) {
|
|
|
40
40
|
const teamRaw = teamFile.preset
|
|
41
41
|
? applyConfigPreset(teamFile.preset, teamFile.config ?? {})
|
|
42
42
|
: (teamFile.config ?? params.teamConfig);
|
|
43
|
+
rejectTeamLayerJudgeSecrets(teamRaw.judge, 'team');
|
|
43
44
|
config = mergeConfigLayer(config, asV3Layer(teamRaw));
|
|
44
45
|
provenance.push({
|
|
45
46
|
path: params.teamConfigPath ?? teamConfigPath(),
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ClassifierOptions, ControlPlaneIntegrity, ScrubOptions, UnknownLocalEffectPolicy, UnparseableShellPolicy } from './types.js';
|
|
2
|
+
import { type BelayJudgeRuntimeConfig } from './verdict/judge-runtime-config.js';
|
|
2
3
|
export type BelayMode = 'enforce' | 'audit';
|
|
3
4
|
export type { UnknownLocalEffectPolicy };
|
|
4
5
|
export interface BelayConfigV1 {
|
|
@@ -76,13 +77,33 @@ export interface BelayRedactionConfig {
|
|
|
76
77
|
maskKeyValueSecrets: boolean;
|
|
77
78
|
maskHighEntropyStrings: boolean;
|
|
78
79
|
}
|
|
79
|
-
export type JudgeProvider = 'ollama' | 'openai-compatible';
|
|
80
|
+
export type JudgeProvider = 'ollama' | 'openai-compatible' | 'anthropic';
|
|
81
|
+
export type JudgeProviderId = 'ollama' | 'codex' | 'claude' | 'cursor';
|
|
82
|
+
/** Read-only legacy ids; preserved on load with warning until `belay config` migrates. */
|
|
83
|
+
export type DeprecatedJudgeProviderId = 'openrouter' | 'custom';
|
|
84
|
+
export type JudgeCredentialMode = 'project' | 'apiKey';
|
|
85
|
+
export type JudgeCredentialRef = `store:judge` | `env:${string}`;
|
|
86
|
+
export interface JudgeCredentialConfig {
|
|
87
|
+
mode: JudgeCredentialMode;
|
|
88
|
+
ref?: JudgeCredentialRef;
|
|
89
|
+
}
|
|
90
|
+
export interface JudgeCloudConsent {
|
|
91
|
+
accepted: boolean;
|
|
92
|
+
at: string;
|
|
93
|
+
providerId: JudgeProviderId;
|
|
94
|
+
endpoint: string;
|
|
95
|
+
by: string;
|
|
96
|
+
}
|
|
80
97
|
export interface BelayJudgeConfig {
|
|
81
98
|
provider: JudgeProvider;
|
|
99
|
+
providerId?: JudgeProviderId | DeprecatedJudgeProviderId;
|
|
82
100
|
model: string;
|
|
83
101
|
timeoutMs: number;
|
|
84
102
|
endpoint: string | null;
|
|
85
103
|
keepAlive: string | null;
|
|
104
|
+
cloudConsent?: JudgeCloudConsent;
|
|
105
|
+
credential?: JudgeCredentialConfig;
|
|
106
|
+
runtime?: BelayJudgeRuntimeConfig;
|
|
86
107
|
}
|
|
87
108
|
export declare const DEFAULT_JUDGE_LOCAL_OLLAMA: BelayJudgeConfig;
|
|
88
109
|
export declare const DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE: BelayJudgeConfig;
|
|
@@ -118,6 +139,17 @@ export interface BelayApprovalSigningConfig {
|
|
|
118
139
|
/** When true, out-of-band approvals must present a signed token. */
|
|
119
140
|
required: boolean;
|
|
120
141
|
}
|
|
142
|
+
export type ApprovalFlow = 'one_step' | 'two_step';
|
|
143
|
+
export interface BelayApprovalAutoReplayScopes {
|
|
144
|
+
shell: boolean;
|
|
145
|
+
tool: boolean;
|
|
146
|
+
subagent: boolean;
|
|
147
|
+
}
|
|
148
|
+
export interface BelayApprovalConfig {
|
|
149
|
+
flow: ApprovalFlow;
|
|
150
|
+
autoReplayScopes: BelayApprovalAutoReplayScopes;
|
|
151
|
+
executionLeaseMs: number;
|
|
152
|
+
}
|
|
121
153
|
export interface BelayEgressConfig {
|
|
122
154
|
enabled: boolean;
|
|
123
155
|
listenHost: string;
|
|
@@ -141,6 +173,7 @@ export interface BelayConfigV4 {
|
|
|
141
173
|
controlPlane: BelayControlPlaneConfig;
|
|
142
174
|
notifications: BelayNotificationsConfig;
|
|
143
175
|
approvalSigning: BelayApprovalSigningConfig;
|
|
176
|
+
approval: BelayApprovalConfig;
|
|
144
177
|
egress: BelayEgressConfig;
|
|
145
178
|
sandbox: BelaySandboxConfig;
|
|
146
179
|
audit: BelayConfigV2['audit'];
|
|
@@ -165,6 +198,7 @@ export declare const DEFAULT_CONTROL_PLANE_V3: BelayControlPlaneConfig;
|
|
|
165
198
|
export declare const DEFAULT_SANDBOX_V3: BelaySandboxConfig;
|
|
166
199
|
export declare const DEFAULT_NOTIFICATIONS_V3: BelayNotificationsConfig;
|
|
167
200
|
export declare const DEFAULT_APPROVAL_SIGNING_V3: BelayApprovalSigningConfig;
|
|
201
|
+
export declare const DEFAULT_APPROVAL_CONFIG: BelayApprovalConfig;
|
|
168
202
|
export declare const DEFAULT_EGRESS_V3: BelayEgressConfig;
|
|
169
203
|
export declare function normalizeEgressListenHost(host: string): string;
|
|
170
204
|
export declare const DEFAULT_CONFIG_V2: BelayConfigV2;
|
|
@@ -180,8 +214,9 @@ export declare function isConfigV1(value: unknown): value is BelayConfigV1;
|
|
|
180
214
|
export declare function isConfigV2(value: unknown): value is BelayConfigV2;
|
|
181
215
|
export declare function isConfigV3(value: unknown): value is BelayConfigV4;
|
|
182
216
|
export declare function isConfigV4(value: unknown): value is BelayConfigV4;
|
|
183
|
-
export declare function normalizeJudgeProvider(provider: string | undefined): 'ollama' | 'openai-compatible';
|
|
217
|
+
export declare function normalizeJudgeProvider(provider: string | undefined): 'ollama' | 'openai-compatible' | 'anthropic';
|
|
184
218
|
export declare function normalizeJudgeConfig(judge: BelayJudgeConfig): BelayJudgeConfig;
|
|
219
|
+
export declare function rejectTeamLayerJudgeSecrets(judge: Partial<BelayJudgeConfig> | undefined, source: 'team' | 'repo'): void;
|
|
185
220
|
export declare function migrateV3ToV4(v3: BelayConfigV4, raw?: RawConfigInput): BelayConfigV4;
|
|
186
221
|
type RawConfigInput = Partial<{
|
|
187
222
|
version: number;
|
|
@@ -197,6 +232,9 @@ type RawConfigInput = Partial<{
|
|
|
197
232
|
controlPlane: Partial<BelayControlPlaneConfig>;
|
|
198
233
|
notifications: Partial<BelayNotificationsConfig>;
|
|
199
234
|
approvalSigning: Partial<BelayApprovalSigningConfig>;
|
|
235
|
+
approval: Partial<BelayApprovalConfig> & {
|
|
236
|
+
autoReplayScopes?: Partial<BelayApprovalAutoReplayScopes>;
|
|
237
|
+
};
|
|
200
238
|
egress: Partial<BelayEgressConfig>;
|
|
201
239
|
sandbox: Partial<BelaySandboxConfig>;
|
|
202
240
|
audit: Partial<BelayConfigV2['audit']>;
|
package/dist/core/config.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { warnDeprecatedJudgeModelAuto } from './judge-model-policy.js';
|
|
3
|
+
import { getJudgeProviderSpec, inferProviderIdFromConfig, isRemovedProviderId, normalizeLegacyProviderId, warnRemovedProviderId, } from './verdict/judge-catalog.js';
|
|
4
|
+
import { normalizeJudgeRuntimeConfig, } from './verdict/judge-runtime-config.js';
|
|
2
5
|
export const DEFAULT_JUDGE_LOCAL_OLLAMA = {
|
|
3
6
|
provider: 'ollama',
|
|
7
|
+
providerId: 'ollama',
|
|
4
8
|
model: 'gemma4:e2b',
|
|
5
9
|
endpoint: 'http://localhost:11434',
|
|
6
10
|
timeoutMs: 25000,
|
|
@@ -8,7 +12,8 @@ export const DEFAULT_JUDGE_LOCAL_OLLAMA = {
|
|
|
8
12
|
};
|
|
9
13
|
export const DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE = {
|
|
10
14
|
provider: 'openai-compatible',
|
|
11
|
-
|
|
15
|
+
providerId: 'codex',
|
|
16
|
+
model: 'gpt-5.3-codex-high',
|
|
12
17
|
timeoutMs: 8000,
|
|
13
18
|
endpoint: null,
|
|
14
19
|
keepAlive: null,
|
|
@@ -91,6 +96,15 @@ export const DEFAULT_NOTIFICATIONS_V3 = {};
|
|
|
91
96
|
export const DEFAULT_APPROVAL_SIGNING_V3 = {
|
|
92
97
|
required: false,
|
|
93
98
|
};
|
|
99
|
+
export const DEFAULT_APPROVAL_CONFIG = {
|
|
100
|
+
flow: 'one_step',
|
|
101
|
+
autoReplayScopes: {
|
|
102
|
+
shell: true,
|
|
103
|
+
tool: false,
|
|
104
|
+
subagent: false,
|
|
105
|
+
},
|
|
106
|
+
executionLeaseMs: 60_000,
|
|
107
|
+
};
|
|
94
108
|
export const DEFAULT_EGRESS_V3 = {
|
|
95
109
|
enabled: false,
|
|
96
110
|
listenHost: '127.0.0.1',
|
|
@@ -144,6 +158,7 @@ export const DEFAULT_CONFIG_V4 = {
|
|
|
144
158
|
controlPlane: { ...DEFAULT_CONTROL_PLANE_V3 },
|
|
145
159
|
notifications: { ...DEFAULT_NOTIFICATIONS_V3 },
|
|
146
160
|
approvalSigning: { ...DEFAULT_APPROVAL_SIGNING_V3 },
|
|
161
|
+
approval: { ...DEFAULT_APPROVAL_CONFIG },
|
|
147
162
|
egress: { ...DEFAULT_EGRESS_V3 },
|
|
148
163
|
sandbox: { ...DEFAULT_SANDBOX_V3 },
|
|
149
164
|
audit: { ...DEFAULT_CONFIG_V2.audit },
|
|
@@ -186,6 +201,7 @@ export function migrateV2ToV3(v2, rawOverrides) {
|
|
|
186
201
|
controlPlane: { ...LEGACY_CONTROL_PLANE_V3 },
|
|
187
202
|
notifications: { ...DEFAULT_NOTIFICATIONS_V3 },
|
|
188
203
|
approvalSigning: { ...DEFAULT_APPROVAL_SIGNING_V3 },
|
|
204
|
+
approval: { ...DEFAULT_APPROVAL_CONFIG },
|
|
189
205
|
egress: { ...DEFAULT_EGRESS_V3 },
|
|
190
206
|
sandbox: { ...DEFAULT_SANDBOX_V3 },
|
|
191
207
|
audit: v2.audit,
|
|
@@ -209,44 +225,141 @@ export function isConfigV4(value) {
|
|
|
209
225
|
return typeof value === 'object' && value !== null && value.version === 4;
|
|
210
226
|
}
|
|
211
227
|
export function normalizeJudgeProvider(provider) {
|
|
228
|
+
if (provider === 'anthropic') {
|
|
229
|
+
return 'anthropic';
|
|
230
|
+
}
|
|
212
231
|
if (provider === 'openai-compatible' || provider === 'cursor') {
|
|
213
232
|
return 'openai-compatible';
|
|
214
233
|
}
|
|
215
234
|
return 'ollama';
|
|
216
235
|
}
|
|
236
|
+
function defaultJudgeTemplateForProvider(provider) {
|
|
237
|
+
if (provider === 'ollama') {
|
|
238
|
+
return DEFAULT_JUDGE_LOCAL_OLLAMA;
|
|
239
|
+
}
|
|
240
|
+
if (provider === 'anthropic') {
|
|
241
|
+
return {
|
|
242
|
+
provider: 'anthropic',
|
|
243
|
+
providerId: 'claude',
|
|
244
|
+
model: 'claude-sonnet-4-6',
|
|
245
|
+
timeoutMs: 8000,
|
|
246
|
+
endpoint: null,
|
|
247
|
+
keepAlive: null,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return DEFAULT_JUDGE_OPENAI_COMPATIBLE_TEMPLATE;
|
|
251
|
+
}
|
|
217
252
|
function synthesizeJudgeFromRaw(raw) {
|
|
218
253
|
const judge = raw.judge;
|
|
219
254
|
if (judge?.provider) {
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
255
|
+
const rawProvider = String(judge.provider);
|
|
256
|
+
const provider = normalizeJudgeProvider(rawProvider);
|
|
257
|
+
const base = defaultJudgeTemplateForProvider(provider);
|
|
258
|
+
const providerId = judge.providerId && normalizeLegacyProviderId(judge.providerId)
|
|
259
|
+
? normalizeLegacyProviderId(judge.providerId)
|
|
260
|
+
: rawProvider === 'cursor'
|
|
261
|
+
? 'cursor'
|
|
262
|
+
: undefined;
|
|
224
263
|
return normalizeJudgeConfig({
|
|
225
264
|
...base,
|
|
226
265
|
...judge,
|
|
227
266
|
provider,
|
|
267
|
+
...(providerId ? { providerId } : {}),
|
|
228
268
|
});
|
|
229
269
|
}
|
|
230
270
|
return { ...DEFAULT_JUDGE_LOCAL_OLLAMA };
|
|
231
271
|
}
|
|
232
272
|
export function normalizeJudgeConfig(judge) {
|
|
233
273
|
const provider = normalizeJudgeProvider(judge.provider);
|
|
234
|
-
const base = provider
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
274
|
+
const base = defaultJudgeTemplateForProvider(provider);
|
|
275
|
+
const rawProviderId = judge.providerId ? String(judge.providerId) : undefined;
|
|
276
|
+
if (rawProviderId && isRemovedProviderId(rawProviderId)) {
|
|
277
|
+
warnRemovedProviderId(rawProviderId);
|
|
278
|
+
let model = typeof judge.model === 'string' && judge.model.trim() ? judge.model.trim() : base.model;
|
|
279
|
+
if (model === 'auto') {
|
|
280
|
+
warnDeprecatedJudgeModelAuto();
|
|
281
|
+
model = base.model;
|
|
282
|
+
}
|
|
283
|
+
const timeoutMs = typeof judge.timeoutMs === 'number' && judge.timeoutMs > 0 ? judge.timeoutMs : base.timeoutMs;
|
|
284
|
+
const endpoint = typeof judge.endpoint === 'string' && judge.endpoint.trim() ? judge.endpoint.trim() : null;
|
|
285
|
+
return {
|
|
286
|
+
provider,
|
|
287
|
+
providerId: rawProviderId,
|
|
288
|
+
model,
|
|
289
|
+
timeoutMs,
|
|
290
|
+
endpoint,
|
|
291
|
+
keepAlive: provider === 'ollama' && typeof judge.keepAlive === 'string' && judge.keepAlive.trim()
|
|
292
|
+
? judge.keepAlive.trim()
|
|
293
|
+
: provider === 'ollama'
|
|
294
|
+
? DEFAULT_JUDGE_LOCAL_OLLAMA.keepAlive
|
|
295
|
+
: null,
|
|
296
|
+
...(judge.cloudConsent ? { cloudConsent: judge.cloudConsent } : {}),
|
|
297
|
+
...(judge.credential ? { credential: judge.credential } : {}),
|
|
298
|
+
runtime: normalizeJudgeRuntimeConfig(judge.runtime),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
let providerId = rawProviderId && normalizeLegacyProviderId(rawProviderId)
|
|
302
|
+
? normalizeLegacyProviderId(rawProviderId)
|
|
303
|
+
: inferProviderIdFromConfig({ ...judge, provider });
|
|
304
|
+
const spec = getJudgeProviderSpec(providerId);
|
|
305
|
+
if (spec && spec.driver !== provider) {
|
|
306
|
+
providerId = inferProviderIdFromConfig({ ...judge, provider });
|
|
307
|
+
}
|
|
308
|
+
const catalogSpec = getJudgeProviderSpec(providerId);
|
|
309
|
+
let model = typeof judge.model === 'string' && judge.model.trim() ? judge.model.trim() : base.model;
|
|
310
|
+
if (model === 'auto' && catalogSpec?.defaultModel) {
|
|
311
|
+
warnDeprecatedJudgeModelAuto();
|
|
312
|
+
model = catalogSpec.defaultModel;
|
|
313
|
+
}
|
|
314
|
+
if (!model && catalogSpec?.defaultModel) {
|
|
315
|
+
model = catalogSpec.defaultModel;
|
|
316
|
+
}
|
|
238
317
|
const timeoutMs = typeof judge.timeoutMs === 'number' && judge.timeoutMs > 0 ? judge.timeoutMs : base.timeoutMs;
|
|
239
|
-
|
|
318
|
+
let endpoint = typeof judge.endpoint === 'string' && judge.endpoint.trim() ? judge.endpoint.trim() : null;
|
|
319
|
+
if (!endpoint && catalogSpec?.defaultEndpoint) {
|
|
320
|
+
endpoint = catalogSpec.defaultEndpoint;
|
|
321
|
+
}
|
|
322
|
+
const normalized = {
|
|
240
323
|
provider,
|
|
324
|
+
providerId,
|
|
241
325
|
model,
|
|
242
326
|
timeoutMs,
|
|
243
|
-
endpoint
|
|
327
|
+
endpoint,
|
|
244
328
|
keepAlive: provider === 'ollama' && typeof judge.keepAlive === 'string' && judge.keepAlive.trim()
|
|
245
329
|
? judge.keepAlive.trim()
|
|
246
330
|
: provider === 'ollama'
|
|
247
331
|
? DEFAULT_JUDGE_LOCAL_OLLAMA.keepAlive
|
|
248
332
|
: null,
|
|
249
333
|
};
|
|
334
|
+
if (judge.cloudConsent?.accepted) {
|
|
335
|
+
normalized.cloudConsent = {
|
|
336
|
+
accepted: true,
|
|
337
|
+
at: judge.cloudConsent.at,
|
|
338
|
+
providerId: judge.cloudConsent.providerId ?? providerId,
|
|
339
|
+
endpoint: judge.cloudConsent.endpoint,
|
|
340
|
+
by: judge.cloudConsent.by,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (judge.credential?.mode === 'project' || judge.credential?.mode === 'apiKey') {
|
|
344
|
+
normalized.credential = {
|
|
345
|
+
mode: judge.credential.mode,
|
|
346
|
+
...(judge.credential.ref ? { ref: judge.credential.ref } : {}),
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
normalized.runtime = normalizeJudgeRuntimeConfig(judge.runtime);
|
|
350
|
+
return normalized;
|
|
351
|
+
}
|
|
352
|
+
export function rejectTeamLayerJudgeSecrets(judge, source) {
|
|
353
|
+
if (source !== 'team' || !judge) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (judge.credential?.mode === 'apiKey') {
|
|
357
|
+
throw new Error('team config cannot set judge.credential.mode to apiKey.');
|
|
358
|
+
}
|
|
359
|
+
const raw = judge;
|
|
360
|
+
if (raw.credential && 'key' in raw.credential && raw.credential.key) {
|
|
361
|
+
throw new Error('team config cannot contain inline judge credential keys.');
|
|
362
|
+
}
|
|
250
363
|
}
|
|
251
364
|
export function migrateV3ToV4(v3, raw) {
|
|
252
365
|
return normalizeConfig({
|
|
@@ -296,6 +409,14 @@ function mergeV3FromRaw(base, raw) {
|
|
|
296
409
|
...base.approvalSigning,
|
|
297
410
|
...(raw.approvalSigning ?? {}),
|
|
298
411
|
},
|
|
412
|
+
approval: {
|
|
413
|
+
...base.approval,
|
|
414
|
+
...(raw.approval ?? {}),
|
|
415
|
+
autoReplayScopes: {
|
|
416
|
+
...base.approval.autoReplayScopes,
|
|
417
|
+
...(raw.approval?.autoReplayScopes ?? {}),
|
|
418
|
+
},
|
|
419
|
+
},
|
|
299
420
|
egress: {
|
|
300
421
|
...base.egress,
|
|
301
422
|
...(raw.egress ?? {}),
|
|
@@ -347,6 +468,14 @@ function normalizeV3Raw(raw) {
|
|
|
347
468
|
approvalSigning: {
|
|
348
469
|
required: raw.approvalSigning?.required === true,
|
|
349
470
|
},
|
|
471
|
+
approval: {
|
|
472
|
+
...DEFAULT_APPROVAL_CONFIG,
|
|
473
|
+
...(raw.approval ?? {}),
|
|
474
|
+
autoReplayScopes: {
|
|
475
|
+
...DEFAULT_APPROVAL_CONFIG.autoReplayScopes,
|
|
476
|
+
...(raw.approval?.autoReplayScopes ?? {}),
|
|
477
|
+
},
|
|
478
|
+
},
|
|
350
479
|
egress: {
|
|
351
480
|
...DEFAULT_EGRESS_V3,
|
|
352
481
|
...(raw.egress ?? {}),
|
|
@@ -577,6 +706,17 @@ export function normalizeConfig(config) {
|
|
|
577
706
|
approvalSigning: {
|
|
578
707
|
required: v4.approvalSigning?.required === true,
|
|
579
708
|
},
|
|
709
|
+
approval: {
|
|
710
|
+
flow: v4.approval?.flow === 'two_step' ? 'two_step' : DEFAULT_APPROVAL_CONFIG.flow,
|
|
711
|
+
autoReplayScopes: {
|
|
712
|
+
shell: v4.approval?.autoReplayScopes?.shell !== false,
|
|
713
|
+
tool: v4.approval?.autoReplayScopes?.tool === true,
|
|
714
|
+
subagent: v4.approval?.autoReplayScopes?.subagent === true,
|
|
715
|
+
},
|
|
716
|
+
executionLeaseMs: typeof v4.approval?.executionLeaseMs === 'number' && v4.approval.executionLeaseMs > 0
|
|
717
|
+
? v4.approval.executionLeaseMs
|
|
718
|
+
: DEFAULT_APPROVAL_CONFIG.executionLeaseMs,
|
|
719
|
+
},
|
|
580
720
|
egress: {
|
|
581
721
|
enabled: v4.egress?.enabled === true,
|
|
582
722
|
listenHost: normalizeEgressListenHost(typeof v4.egress?.listenHost === 'string' && v4.egress.listenHost.trim()
|
|
@@ -653,6 +793,14 @@ export function mergeConfig(existing, defaults = DEFAULT_CONFIG_V4) {
|
|
|
653
793
|
...defaults.approvalSigning,
|
|
654
794
|
...migrated.approvalSigning,
|
|
655
795
|
},
|
|
796
|
+
approval: {
|
|
797
|
+
...defaults.approval,
|
|
798
|
+
...migrated.approval,
|
|
799
|
+
autoReplayScopes: {
|
|
800
|
+
...defaults.approval.autoReplayScopes,
|
|
801
|
+
...migrated.approval.autoReplayScopes,
|
|
802
|
+
},
|
|
803
|
+
},
|
|
656
804
|
egress: {
|
|
657
805
|
...defaults.egress,
|
|
658
806
|
...migrated.egress,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface JudgeCredentialStoreFile {
|
|
2
|
+
version: 1;
|
|
3
|
+
judge?: string;
|
|
4
|
+
}
|
|
5
|
+
export declare function credentialStorePath(stateDir: string): string;
|
|
6
|
+
export declare function writeJudgeCredentialStore(stateDir: string, apiKey: string): Promise<string>;
|
|
7
|
+
export declare function clearJudgeCredentialStore(stateDir: string): Promise<void>;
|
|
8
|
+
export declare function readJudgeCredentialStore(stateDir: string): Promise<string | null>;
|
|
9
|
+
export declare function judgeCredentialStoreExists(stateDir: string): Promise<boolean>;
|
|
10
|
+
export type CredentialRef = `store:judge` | `env:${string}`;
|
|
11
|
+
export declare function parseCredentialRef(ref: string): CredentialRef | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const STORE_FILENAME = 'credentials.json';
|
|
4
|
+
export function credentialStorePath(stateDir) {
|
|
5
|
+
return path.join(stateDir, STORE_FILENAME);
|
|
6
|
+
}
|
|
7
|
+
export async function writeJudgeCredentialStore(stateDir, apiKey) {
|
|
8
|
+
await mkdir(stateDir, { recursive: true, mode: 0o700 });
|
|
9
|
+
const filePath = credentialStorePath(stateDir);
|
|
10
|
+
const payload = {
|
|
11
|
+
version: 1,
|
|
12
|
+
judge: apiKey.trim(),
|
|
13
|
+
};
|
|
14
|
+
await writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, {
|
|
15
|
+
encoding: 'utf8',
|
|
16
|
+
mode: 0o600,
|
|
17
|
+
});
|
|
18
|
+
await chmod(filePath, 0o600);
|
|
19
|
+
return filePath;
|
|
20
|
+
}
|
|
21
|
+
export async function clearJudgeCredentialStore(stateDir) {
|
|
22
|
+
try {
|
|
23
|
+
await unlink(credentialStorePath(stateDir));
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error.code !== 'ENOENT') {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function readJudgeCredentialStore(stateDir) {
|
|
32
|
+
const filePath = credentialStorePath(stateDir);
|
|
33
|
+
try {
|
|
34
|
+
const raw = await readFile(filePath, 'utf8');
|
|
35
|
+
const parsed = JSON.parse(raw);
|
|
36
|
+
const key = parsed.judge?.trim();
|
|
37
|
+
return key || null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export async function judgeCredentialStoreExists(stateDir) {
|
|
44
|
+
try {
|
|
45
|
+
await readFile(credentialStorePath(stateDir), 'utf8');
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function parseCredentialRef(ref) {
|
|
53
|
+
if (ref === 'store:judge') {
|
|
54
|
+
return ref;
|
|
55
|
+
}
|
|
56
|
+
if (ref.startsWith('env:') && ref.length > 4) {
|
|
57
|
+
return ref;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|