@guilz-dev/belay 0.9.2 → 0.9.4

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.
Files changed (118) hide show
  1. package/README.md +36 -2
  2. package/dist/adapters/codex/hooks.d.ts +1 -0
  3. package/dist/adapters/codex/hooks.js +3 -0
  4. package/dist/adapters/codex/runtime-entry.d.ts +3 -0
  5. package/dist/adapters/codex/runtime-entry.js +27 -4
  6. package/dist/adapters/cursor/cwd-resolution.d.ts +10 -0
  7. package/dist/adapters/cursor/cwd-resolution.js +58 -0
  8. package/dist/adapters/cursor/dispatcher-generation.d.ts +3 -0
  9. package/dist/adapters/cursor/dispatcher-generation.js +5 -0
  10. package/dist/adapters/cursor/hook-dispatch-entry.d.ts +7 -0
  11. package/dist/adapters/cursor/hook-dispatch-entry.js +126 -0
  12. package/dist/adapters/cursor/hook-router.d.ts +23 -0
  13. package/dist/adapters/cursor/hook-router.js +266 -0
  14. package/dist/adapters/cursor/hooks.d.ts +5 -0
  15. package/dist/adapters/cursor/hooks.js +115 -3
  16. package/dist/adapters/cursor/routing-layout.d.ts +5 -0
  17. package/dist/adapters/cursor/routing-layout.js +29 -0
  18. package/dist/adapters/cursor/runtime-entry.d.ts +18 -0
  19. package/dist/adapters/cursor/runtime-entry.js +135 -34
  20. package/dist/adapters/layouts/scope.d.ts +5 -0
  21. package/dist/adapters/layouts/scope.js +71 -0
  22. package/dist/adapters/shared/gate-runtime.js +27 -3
  23. package/dist/adapters/shared/repo-root.js +20 -1
  24. package/dist/bundle/claude-runtime.mjs +2660 -1951
  25. package/dist/bundle/codex-runtime.mjs +2680 -1953
  26. package/dist/bundle/cursor-dispatcher.mjs +443 -0
  27. package/dist/bundle/cursor-runtime.mjs +5638 -4697
  28. package/dist/cli.js +33 -3
  29. package/dist/commands/doctor.js +215 -16
  30. package/dist/commands/health-snapshot.d.ts +3 -0
  31. package/dist/commands/health-snapshot.js +61 -0
  32. package/dist/commands/recovery-checkpoints.d.ts +3 -3
  33. package/dist/commands/report.js +14 -0
  34. package/dist/commands/status.js +15 -0
  35. package/dist/commands/where.d.ts +4 -0
  36. package/dist/commands/where.js +52 -0
  37. package/dist/config-io.js +11 -2
  38. package/dist/core/approval-repo-lookup.d.ts +16 -0
  39. package/dist/core/approval-repo-lookup.js +48 -0
  40. package/dist/core/audit-io.d.ts +1 -1
  41. package/dist/core/audit-io.js +1 -1
  42. package/dist/core/audit-legacy-archive.js +43 -25
  43. package/dist/core/audit-query.d.ts +1 -0
  44. package/dist/core/audit-query.js +7 -0
  45. package/dist/core/audit-serialize.d.ts +3 -0
  46. package/dist/core/audit-serialize.js +39 -3
  47. package/dist/core/audit-summary.d.ts +9 -0
  48. package/dist/core/audit-summary.js +58 -1
  49. package/dist/core/audit-types.d.ts +4 -0
  50. package/dist/core/classify-tool.js +16 -8
  51. package/dist/core/effect-ir/argv-delegate.d.ts +9 -0
  52. package/dist/core/effect-ir/argv-delegate.js +42 -0
  53. package/dist/core/effect-ir/shell-lower/argv-delegate-gate.d.ts +3 -0
  54. package/dist/core/effect-ir/shell-lower/argv-delegate-gate.js +40 -0
  55. package/dist/core/effect-ir/shell-lower/augment.d.ts +4 -0
  56. package/dist/core/effect-ir/shell-lower/augment.js +97 -0
  57. package/dist/core/effect-ir/shell-lower/context.d.ts +10 -0
  58. package/dist/core/effect-ir/shell-lower/context.js +1 -0
  59. package/dist/core/effect-ir/shell-lower/decode-process.d.ts +9 -0
  60. package/dist/core/effect-ir/shell-lower/decode-process.js +201 -0
  61. package/dist/core/effect-ir/shell-lower/decoders/belay.d.ts +2 -0
  62. package/dist/core/effect-ir/shell-lower/decoders/belay.js +29 -0
  63. package/dist/core/effect-ir/shell-lower/decoders/builtins.d.ts +7 -0
  64. package/dist/core/effect-ir/shell-lower/decoders/builtins.js +194 -0
  65. package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.d.ts +4 -0
  66. package/dist/core/effect-ir/shell-lower/decoders/decode-package-exec.js +68 -0
  67. package/dist/core/effect-ir/shell-lower/decoders/docker.d.ts +3 -0
  68. package/dist/core/effect-ir/shell-lower/decoders/docker.js +43 -0
  69. package/dist/core/effect-ir/shell-lower/decoders/filesystem.d.ts +9 -0
  70. package/dist/core/effect-ir/shell-lower/decoders/filesystem.js +217 -0
  71. package/dist/core/effect-ir/shell-lower/decoders/prisma.d.ts +2 -0
  72. package/dist/core/effect-ir/shell-lower/decoders/prisma.js +54 -0
  73. package/dist/core/effect-ir/shell-lower/decoders/ruby.d.ts +11 -0
  74. package/dist/core/effect-ir/shell-lower/decoders/ruby.js +147 -0
  75. package/dist/core/effect-ir/shell-lower/decoders/toolchain.d.ts +7 -0
  76. package/dist/core/effect-ir/shell-lower/decoders/toolchain.js +155 -0
  77. package/dist/core/effect-ir/shell-lower/requirement.d.ts +12 -0
  78. package/dist/core/effect-ir/shell-lower/requirement.js +65 -0
  79. package/dist/core/effect-ir/shell-lower/segment.d.ts +11 -0
  80. package/dist/core/effect-ir/shell-lower/segment.js +82 -0
  81. package/dist/core/effect-ir/shell-lower/tokens.d.ts +26 -0
  82. package/dist/core/effect-ir/shell-lower/tokens.js +183 -0
  83. package/dist/core/effect-ir/shell-lower.d.ts +2 -7
  84. package/dist/core/effect-ir/shell-lower.js +122 -1511
  85. package/dist/core/glob.js +32 -16
  86. package/dist/core/integrity.d.ts +2 -2
  87. package/dist/core/integrity.js +52 -12
  88. package/dist/core/replay-scrub.d.ts +3 -0
  89. package/dist/core/replay-scrub.js +30 -3
  90. package/dist/core/shell-substitution.js +2 -2
  91. package/dist/core/shell-tokenizer.d.ts +28 -0
  92. package/dist/core/shell-tokenizer.js +111 -29
  93. package/dist/core/shell-unparseable.js +3 -1
  94. package/dist/core/verdict/docker-compose-run.d.ts +18 -0
  95. package/dist/core/verdict/docker-compose-run.js +136 -0
  96. package/dist/core/verdict/launcher-resolve.js +28 -28
  97. package/dist/core/verdict/parser.d.ts +3 -0
  98. package/dist/core/verdict/parser.js +23 -40
  99. package/dist/core/verdict/recursive-invocation.d.ts +20 -0
  100. package/dist/core/verdict/recursive-invocation.js +224 -0
  101. package/dist/defaults.d.ts +7 -3
  102. package/dist/defaults.js +35 -21
  103. package/dist/installer/bootstrap.d.ts +1 -0
  104. package/dist/installer/bootstrap.js +2 -2
  105. package/dist/installer/runtime-artifacts.js +43 -13
  106. package/dist/installer/scope-config.d.ts +2 -2
  107. package/dist/installer.d.ts +10 -1
  108. package/dist/installer.js +83 -7
  109. package/dist/node-resolution.d.ts +1 -0
  110. package/dist/node-resolution.js +61 -0
  111. package/dist/templates.d.ts +7 -4
  112. package/dist/templates.js +42 -4
  113. package/dist/types.d.ts +35 -1
  114. package/dist/version.d.ts +1 -1
  115. package/dist/version.js +1 -1
  116. package/package.json +5 -2
  117. package/skills/belay/SKILL.md +5 -0
  118. package/skills/belay/belay-report.md +4 -1
@@ -0,0 +1,97 @@
1
+ import { isFdDuplication, isRedirectOperator } from '../../shell-tokenizer.js';
2
+ import { addSecretRead, addWriteEffects, requirement } from './requirement.js';
3
+ import { expandKnownVariables, resolvePathOperand } from './tokens.js';
4
+ export function addRedirectEffects(requirements, tokens, env, context, segment) {
5
+ for (let index = 0; index < tokens.length; index += 1) {
6
+ const operator = tokens[index] ?? '';
7
+ if (!isRedirectOperator(operator) || isFdDuplication(operator)) {
8
+ continue;
9
+ }
10
+ const rawTarget = tokens[index + 1];
11
+ if (!rawTarget) {
12
+ requirements.push(requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
13
+ 'shell.redirect_target_missing',
14
+ ]));
15
+ continue;
16
+ }
17
+ if (operator.includes('<<')) {
18
+ requirements.push(requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
19
+ 'shell.heredoc_opaque',
20
+ ]));
21
+ index += 1;
22
+ continue;
23
+ }
24
+ const target = expandKnownVariables(rawTarget, env);
25
+ const resolved = resolvePathOperand(target, context.cwd);
26
+ if (resolved === '/dev/null') {
27
+ index += 1;
28
+ continue;
29
+ }
30
+ if (operator.includes('<')) {
31
+ requirements.push(requirement('fs.read', 'fs.read', { kind: 'path', path: resolved }, segment, [
32
+ 'shell.input_redirect',
33
+ ]));
34
+ addSecretRead(requirements, resolved, segment);
35
+ }
36
+ if (operator.includes('>')) {
37
+ addWriteEffects(requirements, resolved, segment, ['shell.output_redirect']);
38
+ }
39
+ index += 1;
40
+ }
41
+ }
42
+ export function effectChangingEnvironmentSignals(head, env, changedNames) {
43
+ if (head === 'curl' &&
44
+ (env.CURL_HOME ||
45
+ changedNames.has('CURL_HOME') ||
46
+ changedNames.has('HOME') ||
47
+ changedNames.has('XDG_CONFIG_HOME'))) {
48
+ return ['egress.curl.environment_config_override'];
49
+ }
50
+ if (head === 'wget' && (env.WGETRC || changedNames.has('WGETRC') || changedNames.has('HOME'))) {
51
+ return ['egress.wget.environment_config_override'];
52
+ }
53
+ if (head !== 'git') {
54
+ return [];
55
+ }
56
+ const hazardousNames = new Set([
57
+ 'GIT_ASKPASS',
58
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
59
+ 'GIT_CEILING_DIRECTORIES',
60
+ 'GIT_COMMON_DIR',
61
+ 'GIT_CONFIG',
62
+ 'GIT_CONFIG_GLOBAL',
63
+ 'GIT_CONFIG_SYSTEM',
64
+ 'GIT_DIR',
65
+ 'GIT_DISCOVERY_ACROSS_FILESYSTEM',
66
+ 'GIT_EDITOR',
67
+ 'GIT_EXEC_PATH',
68
+ 'GIT_EXTERNAL_DIFF',
69
+ 'GIT_INDEX_FILE',
70
+ 'GIT_NAMESPACE',
71
+ 'GIT_OBJECT_DIRECTORY',
72
+ 'GIT_PAGER',
73
+ 'GIT_PROXY_COMMAND',
74
+ 'GIT_QUARANTINE_PATH',
75
+ 'GIT_SEQUENCE_EDITOR',
76
+ 'GIT_SHALLOW_FILE',
77
+ 'GIT_SSH',
78
+ 'GIT_SSH_COMMAND',
79
+ 'GIT_TEMPLATE_DIR',
80
+ 'GIT_WORK_TREE',
81
+ 'SSH_ASKPASS',
82
+ ]);
83
+ const overridden = changedNames.has('HOME') ||
84
+ changedNames.has('XDG_CONFIG_HOME') ||
85
+ Object.entries(env).some(([name, value]) => {
86
+ if (!value) {
87
+ return false;
88
+ }
89
+ if (hazardousNames.has(name) ||
90
+ /^GIT_CONFIG_(?:KEY|VALUE)_\d+$/.test(name) ||
91
+ /^GIT_TRACE/.test(name)) {
92
+ return true;
93
+ }
94
+ return name === 'GIT_CONFIG_COUNT' && value !== '0';
95
+ });
96
+ return overridden ? ['git.environment_execution_or_config_override'] : [];
97
+ }
@@ -0,0 +1,10 @@
1
+ export interface LowerShellEffectPlanParams {
2
+ command: string;
3
+ cwd: string;
4
+ repoRoot: string;
5
+ inputFingerprint: string;
6
+ env?: Readonly<Record<string, string | undefined>>;
7
+ }
8
+ export interface LowerContext extends LowerShellEffectPlanParams {
9
+ depth: number;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { ShellEffectRequirement } from '../shell-build.js';
2
+ export declare function decodeProcessOrFilesystem(params: {
3
+ tokens: string[];
4
+ head: string;
5
+ env: Readonly<Record<string, string | undefined>>;
6
+ cwd: string;
7
+ repoRoot: string;
8
+ segment: string;
9
+ }): ShellEffectRequirement[];
@@ -0,0 +1,201 @@
1
+ import path from 'node:path';
2
+ import { isCommandInspection } from '../../verdict/parser.js';
3
+ import { decodeBelay } from './decoders/belay.js';
4
+ import { bashSyntaxTarget, decodeShellControlBuiltin, validFilesystemTest, validLsof, validPs, } from './decoders/builtins.js';
5
+ import { decodeDockerComposeRun, validDockerInfo } from './decoders/docker.js';
6
+ import { decodeCopyMove, decodeRm, filesystemReadOperands, filesystemWriteOperands, } from './decoders/filesystem.js';
7
+ import { decodePrisma } from './decoders/prisma.js';
8
+ import { decodeBundleExecInner, decodeRuby, decodeRuntimeMetadataProcess } from './decoders/ruby.js';
9
+ import { decodeGo, decodeNode, decodeRsync, decodeSed, decodeTsc } from './decoders/toolchain.js';
10
+ import { addSecretRead, addWriteEffects, processRequirement, requirement, unsupportedProcess, } from './requirement.js';
11
+ import { ENV_PREFIX_PATTERN, isMetadataOnlyArgv, isShellHead, resolvePathOperand, } from './tokens.js';
12
+ export function decodeProcessOrFilesystem(params) {
13
+ const { tokens, head, env, cwd, repoRoot, segment } = params;
14
+ const args = tokens.slice(1);
15
+ if (tokens.length > 0 && tokens.every((token) => ENV_PREFIX_PATTERN.test(token))) {
16
+ return [];
17
+ }
18
+ const shellControl = decodeShellControlBuiltin(head, args);
19
+ if (shellControl) {
20
+ return shellControl;
21
+ }
22
+ if (isCommandInspection(tokens)) {
23
+ return [processRequirement(head, 'inspect', segment, ['process.inspect.command_lookup'])];
24
+ }
25
+ if (head === 'lsof') {
26
+ return validLsof(args)
27
+ ? [processRequirement(head, 'inspect', segment, ['process.inspect.lsof'])]
28
+ : unsupportedProcess(head, segment, 'process.lsof_grammar_incomplete');
29
+ }
30
+ if (head === 'ps') {
31
+ return validPs(args)
32
+ ? [processRequirement(head, 'inspect', segment, ['process.inspect.ps'])]
33
+ : unsupportedProcess(head, segment, 'process.ps_grammar_incomplete');
34
+ }
35
+ if (head === 'test' || head === '[') {
36
+ return validFilesystemTest(head, args)
37
+ ? [processRequirement(head, 'inspect', segment, ['process.inspect.filesystem_test'])]
38
+ : unsupportedProcess(head, segment, 'process.filesystem_test_grammar_incomplete');
39
+ }
40
+ if (head === 'docker') {
41
+ if (args[0] === 'info') {
42
+ return validDockerInfo(args.slice(1))
43
+ ? [processRequirement(head, 'inspect', segment, ['process.inspect.docker_info'])]
44
+ : unsupportedProcess(head, segment, 'process.docker_info_grammar_incomplete');
45
+ }
46
+ if (args[0] === 'compose' &&
47
+ ['up', 'down', 'start', 'stop', 'restart', 'create', 'rm'].includes(args[1] ?? '')) {
48
+ return [processRequirement(head, 'spawn', segment, ['service.local_mutation'])];
49
+ }
50
+ if (args[0] === 'push' ||
51
+ (args[0] === 'build' && args.includes('--push')) ||
52
+ (args[0] === 'buildx' &&
53
+ args[1] === 'build' &&
54
+ (args.includes('--push') ||
55
+ args.some((arg) => arg.startsWith('--output=type=registry') || arg.startsWith('-o=type=registry'))))) {
56
+ return [
57
+ processRequirement(head, 'spawn', segment, ['tier0_external', 'docker.remote_mutation']),
58
+ requirement('network.connect', 'network.connect', {
59
+ kind: 'network',
60
+ host: 'registry',
61
+ protocol: 'container-registry',
62
+ mode: 'mutate',
63
+ payload: 'present',
64
+ }, segment, ['tier0_external', 'docker.remote_mutation']),
65
+ ];
66
+ }
67
+ if (args[0] === 'run' ||
68
+ args[0] === 'create' ||
69
+ args[0] === 'pull' ||
70
+ args[0] === 'build' ||
71
+ (args[0] === 'buildx' && args[1] === 'build')) {
72
+ return [
73
+ processRequirement(head, 'spawn', segment, ['process.docker_spawn']),
74
+ requirement('network.acquire', 'network.connect', {
75
+ kind: 'network',
76
+ host: 'unknown',
77
+ protocol: 'container-registry',
78
+ mode: 'ambiguous',
79
+ payload: 'none',
80
+ }, segment, ['docker.image_acquisition_possible']),
81
+ ];
82
+ }
83
+ return [processRequirement(head, 'spawn', segment, ['process.docker_spawn'])];
84
+ }
85
+ if (isShellHead(head)) {
86
+ const syntax = bashSyntaxTarget(args);
87
+ if (syntax) {
88
+ return [
89
+ processRequirement(head, 'inspect', segment, ['process.inspect.shell_syntax']),
90
+ requirement('fs.read', 'fs.read', { kind: 'path', path: path.resolve(cwd, syntax) }, segment, ['shell.syntax_source_read']),
91
+ ];
92
+ }
93
+ return unsupportedProcess(head, segment, 'process.shell_grammar_incomplete');
94
+ }
95
+ if (head === 'prisma') {
96
+ return decodePrisma(args, env, repoRoot, segment);
97
+ }
98
+ const runtimeMetadata = decodeRuntimeMetadataProcess(head, args, segment);
99
+ if (runtimeMetadata) {
100
+ return runtimeMetadata;
101
+ }
102
+ if (head === 'ruby') {
103
+ return decodeRuby(args, cwd, repoRoot, segment);
104
+ }
105
+ if (head === 'rubocop' || head === 'rspec') {
106
+ const decoded = decodeBundleExecInner(head, args, segment);
107
+ if (decoded) {
108
+ return decoded;
109
+ }
110
+ }
111
+ const dockerCompose = decodeDockerComposeRun(head, args, segment);
112
+ if (dockerCompose) {
113
+ return dockerCompose;
114
+ }
115
+ if ((head === 'npm' || head === 'pnpm') && args.length === 1 && isMetadataOnlyArgv(args)) {
116
+ return [processRequirement(head, 'inspect', segment, ['process.inspect.package_manager'])];
117
+ }
118
+ if (head === 'belay') {
119
+ return decodeBelay(args, repoRoot, segment);
120
+ }
121
+ if (head === 'tsc') {
122
+ return decodeTsc(args, cwd, segment);
123
+ }
124
+ if (head === 'go') {
125
+ return decodeGo(args, segment);
126
+ }
127
+ if (head === 'rsync') {
128
+ return decodeRsync(args, cwd, segment);
129
+ }
130
+ if (head === 'sed') {
131
+ return decodeSed(args, cwd, segment);
132
+ }
133
+ if (head === 'base64' && args.length === 1 && ['-d', '-D', '--decode'].includes(args[0] ?? '')) {
134
+ return [processRequirement(head, 'inspect', segment, ['process.inspect.base64_stdin'])];
135
+ }
136
+ if (head === 'node') {
137
+ return decodeNode(args, cwd, segment);
138
+ }
139
+ if (head === 'vite' || head === 'vite-node') {
140
+ return [processRequirement(head, 'spawn', segment, ['process.local_dev_spawn'])];
141
+ }
142
+ if (((head === 'npm' || head === 'pnpm') && args[0] === 'publish') ||
143
+ (head === 'terraform' && args[0] === 'apply')) {
144
+ return [
145
+ processRequirement(head, 'spawn', segment, ['tier0_external']),
146
+ requirement('network.connect', 'network.connect', {
147
+ kind: 'network',
148
+ host: 'control-plane',
149
+ protocol: head,
150
+ mode: 'mutate',
151
+ payload: 'present',
152
+ }, segment, ['tier0_external']),
153
+ ];
154
+ }
155
+ if (head === 'cd') {
156
+ return [];
157
+ }
158
+ if (head === 'cp' || head === 'mv') {
159
+ return decodeCopyMove(head, args, cwd, segment);
160
+ }
161
+ if (head === 'rm') {
162
+ return decodeRm(args, cwd, repoRoot, segment);
163
+ }
164
+ if (head === 'vitest' || head === 'eslint' || head === 'biome' || head === 'belay') {
165
+ return [processRequirement(head, 'spawn', segment, ['process.known_local_spawn'])];
166
+ }
167
+ if (head === 'pwd' || head === 'which' || head === 'whoami') {
168
+ return [processRequirement(head, 'inspect', segment, ['process.pure_inspection'])];
169
+ }
170
+ const readOperands = filesystemReadOperands(head, args);
171
+ if (readOperands !== null) {
172
+ const lowered = [processRequirement(head, 'inspect', segment, ['process.filesystem_inspect'])];
173
+ for (const operand of readOperands) {
174
+ const resolved = resolvePathOperand(operand, cwd);
175
+ lowered.push(requirement('fs.read', 'fs.read', { kind: 'path', path: resolved }, segment, [
176
+ 'filesystem.read',
177
+ ]));
178
+ addSecretRead(lowered, resolved, segment);
179
+ }
180
+ return lowered;
181
+ }
182
+ const writeOperands = filesystemWriteOperands(head, args);
183
+ if (writeOperands !== null) {
184
+ const lowered = [processRequirement(head, 'spawn', segment, ['process.filesystem_mutation'])];
185
+ for (const operand of writeOperands) {
186
+ addWriteEffects(lowered, resolvePathOperand(operand, cwd), segment, ['filesystem.write']);
187
+ }
188
+ return lowered;
189
+ }
190
+ if (head === 'printf' || head === 'echo' || head === 'true' || head === 'false' || head === ':') {
191
+ return [];
192
+ }
193
+ if (!head) {
194
+ return [
195
+ requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
196
+ 'shell.segment_empty',
197
+ ]),
198
+ ];
199
+ }
200
+ return unsupportedProcess(head, segment, 'process.grammar_unknown');
201
+ }
@@ -0,0 +1,2 @@
1
+ import type { ShellEffectRequirement } from '../../shell-build.js';
2
+ export declare function decodeBelay(args: string[], repoRoot: string, segment: string): ShellEffectRequirement[];
@@ -0,0 +1,29 @@
1
+ import path from 'node:path';
2
+ import { processRequirement, requirement } from '../requirement.js';
3
+ export function decodeBelay(args, repoRoot, segment) {
4
+ const [section, operation, key] = args;
5
+ const judgeCommand = section === 'judge' &&
6
+ operation !== undefined &&
7
+ ['consent', 'list', 'status', 'test', 'use'].includes(operation);
8
+ const configRead = section === 'config' &&
9
+ (operation === undefined ||
10
+ operation === 'list' ||
11
+ (operation === 'get' && key?.startsWith('judge.')));
12
+ const configJudgeMutation = section === 'config' &&
13
+ ((['set', 'unset'].includes(operation ?? '') && key?.startsWith('judge.')) ||
14
+ (operation === 'credential' && key === 'mode'));
15
+ if (judgeCommand || configRead || configJudgeMutation) {
16
+ return [
17
+ processRequirement('belay', 'inspect', segment, [
18
+ 'belay_control_plane_command',
19
+ configJudgeMutation ? 'belay.config_judge_mutation' : 'belay.config_read',
20
+ ]),
21
+ ];
22
+ }
23
+ if (section === 'config' && ['set', 'unset', 'credential'].includes(operation ?? '')) {
24
+ return [
25
+ requirement('control_plane.write', 'control_plane.write', { kind: 'path', path: path.join(repoRoot, '.belay-control-plane') }, segment, ['belay.config_non_judge_mutation']),
26
+ ];
27
+ }
28
+ return [processRequirement('belay', 'spawn', segment, ['process.known_local_spawn'])];
29
+ }
@@ -0,0 +1,7 @@
1
+ import type { ShellEffectRequirement } from '../../shell-build.js';
2
+ export declare function decodeSetBuiltin(args: string[]): boolean;
3
+ export declare function decodeShellControlBuiltin(head: string, args: string[]): ShellEffectRequirement[] | null;
4
+ export declare function validLsof(args: string[]): boolean;
5
+ export declare function validPs(args: string[]): boolean;
6
+ export declare function validFilesystemTest(head: string, args: string[]): boolean;
7
+ export declare function bashSyntaxTarget(args: string[]): string | null;
@@ -0,0 +1,194 @@
1
+ import { isOptionToken } from '../tokens.js';
2
+ export function decodeSetBuiltin(args) {
3
+ let index = 0;
4
+ while (index < args.length) {
5
+ const arg = args[index] ?? '';
6
+ if (arg === '--') {
7
+ index += 1;
8
+ continue;
9
+ }
10
+ if (arg === '-o' || arg === '+o') {
11
+ if (!args[index + 1]) {
12
+ return false;
13
+ }
14
+ index += 2;
15
+ continue;
16
+ }
17
+ if (/^[-+][A-Za-z0-9]+$/.test(arg)) {
18
+ index += 1;
19
+ continue;
20
+ }
21
+ return false;
22
+ }
23
+ return true;
24
+ }
25
+ export function decodeShellControlBuiltin(head, args) {
26
+ if (head === 'set') {
27
+ return decodeSetBuiltin(args) ? [] : null;
28
+ }
29
+ if (head === 'wait') {
30
+ if (args.length === 0 || args.every((arg) => /^\d+$/.test(arg))) {
31
+ return [];
32
+ }
33
+ return null;
34
+ }
35
+ if (head === 'exit') {
36
+ if (args.length === 0 || (args.length === 1 && /^-?\d+$/.test(args[0] ?? ''))) {
37
+ return [];
38
+ }
39
+ return null;
40
+ }
41
+ return null;
42
+ }
43
+ export function validLsof(args) {
44
+ const flagsWithoutValues = new Set(['-a', '-b', '-l', '-n', '-P', '-R', '-t', '-V']);
45
+ const flagsWithValues = new Set(['-c', '-d', '-F', '-g', '-p', '-T', '-u', '+d', '+D']);
46
+ for (let index = 0; index < args.length; index += 1) {
47
+ const arg = args[index] ?? '';
48
+ if (flagsWithoutValues.has(arg) || arg === '-i') {
49
+ continue;
50
+ }
51
+ if (flagsWithValues.has(arg)) {
52
+ if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
53
+ return false;
54
+ }
55
+ index += 1;
56
+ continue;
57
+ }
58
+ if ((/^-[ablnPRtV]+$/.test(arg) &&
59
+ [...arg.slice(1)].every((flag) => 'ablnPRtV'.includes(flag))) ||
60
+ /^-i(?:TCP|UDP)?(?::\d+(?:-\d+)?)?$/.test(arg) ||
61
+ /^-s(?:TCP|UDP):[A-Za-z]+$/.test(arg) ||
62
+ /^-(?:c|d|F|g|p|T|u).+$/.test(arg) ||
63
+ /^\+(?:d|D).+$/.test(arg)) {
64
+ continue;
65
+ }
66
+ return false;
67
+ }
68
+ return true;
69
+ }
70
+ export function validPs(args) {
71
+ const bareForms = new Set(['aux', 'ax', 'x', 'u']);
72
+ const flagsWithoutValues = new Set(['-A', '-a', '-d', '-e', '-f', '-j', '-l', '-T', '-x']);
73
+ const flagsWithValues = new Set([
74
+ '--format',
75
+ '--group',
76
+ '--pid',
77
+ '--ppid',
78
+ '--sort',
79
+ '--tty',
80
+ '--user',
81
+ ]);
82
+ for (let index = 0; index < args.length; index += 1) {
83
+ const arg = args[index] ?? '';
84
+ if (bareForms.has(arg) || flagsWithoutValues.has(arg)) {
85
+ continue;
86
+ }
87
+ if (flagsWithValues.has(arg)) {
88
+ if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
89
+ return false;
90
+ }
91
+ index += 1;
92
+ continue;
93
+ }
94
+ if (/^(?:--format|--group|--pid|--ppid|--sort|--tty|--user)=.+$/.test(arg)) {
95
+ continue;
96
+ }
97
+ if (/^-[^-]/.test(arg)) {
98
+ const noValue = new Set(['A', 'a', 'd', 'e', 'f', 'j', 'l', 'T', 'x']);
99
+ const withValue = new Set(['G', 'g', 'N', 'o', 'p', 't', 'U', 'u']);
100
+ const characters = arg.slice(1);
101
+ let valid = true;
102
+ for (let optionIndex = 0; optionIndex < characters.length; optionIndex += 1) {
103
+ const option = characters[optionIndex] ?? '';
104
+ if (noValue.has(option)) {
105
+ continue;
106
+ }
107
+ if (withValue.has(option)) {
108
+ if (optionIndex === characters.length - 1) {
109
+ if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
110
+ return false;
111
+ }
112
+ index += 1;
113
+ }
114
+ break;
115
+ }
116
+ valid = false;
117
+ break;
118
+ }
119
+ if (valid) {
120
+ continue;
121
+ }
122
+ }
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ export function validFilesystemTest(head, args) {
128
+ const operands = head === '[' && args.at(-1) === ']' ? args.slice(0, -1) : args;
129
+ if (head === '[' && args.at(-1) !== ']') {
130
+ return false;
131
+ }
132
+ return (operands.length === 2 &&
133
+ new Set(['-b', '-c', '-d', '-e', '-f', '-h', '-L', '-r', '-s', '-w', '-x']).has(operands[0] ?? '') &&
134
+ Boolean(operands[1]));
135
+ }
136
+ export function bashSyntaxTarget(args) {
137
+ let syntaxOnly = false;
138
+ let script = null;
139
+ const shortFlags = new Set('abefhkmnptuvxBCEHPT'.split(''));
140
+ const longFlags = new Set([
141
+ '--debugger',
142
+ '--dump-po-strings',
143
+ '--dump-strings',
144
+ '--help',
145
+ '--login',
146
+ '--noediting',
147
+ '--noprofile',
148
+ '--norc',
149
+ '--posix',
150
+ '--restricted',
151
+ '--verbose',
152
+ '--version',
153
+ ]);
154
+ for (let index = 0; index < args.length; index += 1) {
155
+ const arg = args[index] ?? '';
156
+ if (arg === '-c' || arg === '-lc') {
157
+ return null;
158
+ }
159
+ if (arg === '-O' || arg === '+O' || arg === '--rcfile' || arg === '--init-file') {
160
+ if (!args[index + 1] || isOptionToken(args[index + 1] ?? '')) {
161
+ return null;
162
+ }
163
+ index += 1;
164
+ continue;
165
+ }
166
+ if ((arg.startsWith('-O') && arg.length > 2) ||
167
+ (arg.startsWith('+O') && arg.length > 2) ||
168
+ (arg.startsWith('--rcfile=') && arg.length > '--rcfile='.length) ||
169
+ (arg.startsWith('--init-file=') && arg.length > '--init-file='.length)) {
170
+ continue;
171
+ }
172
+ if (longFlags.has(arg)) {
173
+ continue;
174
+ }
175
+ if (/^[+-][A-Za-z]+$/.test(arg)) {
176
+ const options = arg.slice(1);
177
+ if ([...options].some((option) => !shortFlags.has(option))) {
178
+ return null;
179
+ }
180
+ syntaxOnly ||= options.includes('n');
181
+ continue;
182
+ }
183
+ if (arg === '--') {
184
+ script = args[index + 1] ?? null;
185
+ break;
186
+ }
187
+ if (arg.startsWith('-') || arg.startsWith('+')) {
188
+ return null;
189
+ }
190
+ script = arg;
191
+ break;
192
+ }
193
+ return syntaxOnly ? script : null;
194
+ }
@@ -0,0 +1,4 @@
1
+ import { type peelPackageExecArgv } from '../../package-exec.js';
2
+ import type { ShellEffectRequirement } from '../../shell-build.js';
3
+ export declare function decodePackageExec(peel: NonNullable<ReturnType<typeof peelPackageExecArgv>>, cwd: string, repoRoot: string, segment: string): ShellEffectRequirement[];
4
+ export declare function packageExecInnerIsMetadata(peel: NonNullable<ReturnType<typeof peelPackageExecArgv>>): boolean;
@@ -0,0 +1,68 @@
1
+ import { classifyPackageAcquisitionSpec, resolveLocalBin, } from '../../package-exec.js';
2
+ import { requirement } from '../requirement.js';
3
+ export function decodePackageExec(peel, cwd, repoRoot, segment) {
4
+ if (peel.opaque) {
5
+ return [
6
+ requirement('indeterminate', 'indeterminate', { kind: 'unknown' }, segment, [
7
+ peel.reason,
8
+ ...peel.signals,
9
+ ]),
10
+ ];
11
+ }
12
+ if (peel.reason === 'npx_wrapper_readonly') {
13
+ return [];
14
+ }
15
+ const binHead = peel.innerTokens[0] ?? '';
16
+ const metadataOnly = packageExecInnerIsMetadata(peel);
17
+ const local = !peel.forceAcquire ? resolveLocalBin(binHead, cwd, repoRoot) : null;
18
+ if (local) {
19
+ return metadataOnly
20
+ ? [
21
+ requirement('process.exec', 'process.exec', { kind: 'executable', command: local.path, operation: 'inspect' }, segment, ['package_exec.delegate', 'package_exec.local_bin_resolved']),
22
+ ]
23
+ : [];
24
+ }
25
+ const networkResources = peel.acquisitionSpecs
26
+ .map((spec) => classifyPackageAcquisitionSpec(spec))
27
+ .filter((source) => source.kind !== 'local')
28
+ .map((source) => source.kind === 'registry'
29
+ ? {
30
+ kind: 'network',
31
+ host: 'registry.npmjs.org',
32
+ protocol: 'registry',
33
+ mode: 'read',
34
+ payload: 'none',
35
+ }
36
+ : {
37
+ kind: 'network',
38
+ host: source.host,
39
+ ...(source.port ? { port: source.port } : {}),
40
+ protocol: source.protocol,
41
+ mode: 'read',
42
+ payload: 'none',
43
+ });
44
+ if (networkResources.length === 0) {
45
+ networkResources.push({
46
+ kind: 'network',
47
+ host: 'registry.npmjs.org',
48
+ protocol: 'registry',
49
+ mode: 'read',
50
+ payload: 'none',
51
+ });
52
+ }
53
+ return [
54
+ ...networkResources.map((resource) => requirement('network.acquire', 'network.connect', resource, segment, [
55
+ 'package_acquire_possible',
56
+ ])),
57
+ requirement('fs.write', 'fs.write', { kind: 'package-cache', manager: peel.launcher === 'pnpm' ? 'pnpm' : 'npm' }, segment, ['package_cache_write']),
58
+ ...(metadataOnly
59
+ ? [
60
+ requirement('process.exec', 'process.exec', { kind: 'executable', command: binHead || peel.launcher, operation: 'inspect' }, segment, ['package_exec.delegate']),
61
+ ]
62
+ : []),
63
+ ];
64
+ }
65
+ export function packageExecInnerIsMetadata(peel) {
66
+ const args = peel.innerTokens.slice(1);
67
+ return (args.length > 0 && args.every((arg) => ['--help', '--version', '-V', '-h', '-v'].includes(arg)));
68
+ }
@@ -0,0 +1,3 @@
1
+ import type { ShellEffectRequirement } from '../../shell-build.js';
2
+ export declare function decodeDockerComposeRun(head: string, args: string[], segment: string): ShellEffectRequirement[] | null;
3
+ export declare function validDockerInfo(args: string[]): boolean;