open-managed-agents 0.1.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.
Files changed (197) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +248 -0
  3. package/bin/oma.mjs +499 -0
  4. package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
  5. package/dist/scripts/alpha-smoke-models.mjs +92 -0
  6. package/dist/scripts/alpha-smoke.mjs +581 -0
  7. package/dist/scripts/oma-doctor.js +230 -0
  8. package/dist/scripts/oma-models.js +299 -0
  9. package/dist/scripts/oma-workspaces.js +133 -0
  10. package/dist/src/control-plane/admin/auth.js +43 -0
  11. package/dist/src/control-plane/admin/routes.js +76 -0
  12. package/dist/src/control-plane/admin/service.js +102 -0
  13. package/dist/src/control-plane/admission.js +90 -0
  14. package/dist/src/control-plane/agents/routes.js +63 -0
  15. package/dist/src/control-plane/agents/service.js +687 -0
  16. package/dist/src/control-plane/agents/store.js +391 -0
  17. package/dist/src/control-plane/agents/types.js +1 -0
  18. package/dist/src/control-plane/api-constants.js +3 -0
  19. package/dist/src/control-plane/app.js +907 -0
  20. package/dist/src/control-plane/console/auth.js +68 -0
  21. package/dist/src/control-plane/console/static.js +112 -0
  22. package/dist/src/control-plane/deployment-runtime-config.js +328 -0
  23. package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
  24. package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
  25. package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
  26. package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
  27. package/dist/src/control-plane/deployment-storage.js +354 -0
  28. package/dist/src/control-plane/egress/image.js +48 -0
  29. package/dist/src/control-plane/egress/policy.js +571 -0
  30. package/dist/src/control-plane/egress/presets.js +77 -0
  31. package/dist/src/control-plane/egress/proxy.js +72 -0
  32. package/dist/src/control-plane/egress/ssrf.js +106 -0
  33. package/dist/src/control-plane/egress/utils/debug.js +14 -0
  34. package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
  35. package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
  36. package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
  37. package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
  38. package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
  39. package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
  40. package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
  41. package/dist/src/control-plane/environments/routes.js +54 -0
  42. package/dist/src/control-plane/environments/service.js +112 -0
  43. package/dist/src/control-plane/environments/store.js +132 -0
  44. package/dist/src/control-plane/environments/types.js +1 -0
  45. package/dist/src/control-plane/errors.js +81 -0
  46. package/dist/src/control-plane/events/broadcaster.js +217 -0
  47. package/dist/src/control-plane/events/constants.js +2 -0
  48. package/dist/src/control-plane/events/persist.js +39 -0
  49. package/dist/src/control-plane/events/request.js +190 -0
  50. package/dist/src/control-plane/events/routes.js +159 -0
  51. package/dist/src/control-plane/events/runtime-helpers.js +134 -0
  52. package/dist/src/control-plane/events/service.js +1985 -0
  53. package/dist/src/control-plane/events/session-guards.js +29 -0
  54. package/dist/src/control-plane/events/sse.js +6 -0
  55. package/dist/src/control-plane/events/store.js +772 -0
  56. package/dist/src/control-plane/events/tool-persistence.js +102 -0
  57. package/dist/src/control-plane/events/types.js +21 -0
  58. package/dist/src/control-plane/files/routes.js +121 -0
  59. package/dist/src/control-plane/files/service.js +65 -0
  60. package/dist/src/control-plane/files/store-common.js +87 -0
  61. package/dist/src/control-plane/files/store-local.js +488 -0
  62. package/dist/src/control-plane/files/store-memory.js +270 -0
  63. package/dist/src/control-plane/files/store.js +2 -0
  64. package/dist/src/control-plane/files/types.js +7 -0
  65. package/dist/src/control-plane/http.js +25 -0
  66. package/dist/src/control-plane/ids.js +43 -0
  67. package/dist/src/control-plane/logging.js +263 -0
  68. package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
  69. package/dist/src/control-plane/models/catalog.js +78 -0
  70. package/dist/src/control-plane/models/config-security.js +376 -0
  71. package/dist/src/control-plane/models/deployment-config.js +93 -0
  72. package/dist/src/control-plane/models/routes.js +32 -0
  73. package/dist/src/control-plane/models/service.js +138 -0
  74. package/dist/src/control-plane/observability/instruments.js +65 -0
  75. package/dist/src/control-plane/observability/metrics.js +125 -0
  76. package/dist/src/control-plane/observability/routes.js +95 -0
  77. package/dist/src/control-plane/openapi/document.js +282 -0
  78. package/dist/src/control-plane/openapi/routes.js +78 -0
  79. package/dist/src/control-plane/request-idempotency.js +85 -0
  80. package/dist/src/control-plane/secrets/envelope.js +132 -0
  81. package/dist/src/control-plane/secrets/master-key.js +44 -0
  82. package/dist/src/control-plane/secrets/routes.js +20 -0
  83. package/dist/src/control-plane/secrets/service.js +71 -0
  84. package/dist/src/control-plane/secrets/store.js +212 -0
  85. package/dist/src/control-plane/secrets/types.js +3 -0
  86. package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
  87. package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
  88. package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
  89. package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
  90. package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
  91. package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
  92. package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
  93. package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
  94. package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
  95. package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
  96. package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
  97. package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
  98. package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
  99. package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
  100. package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
  101. package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
  102. package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
  103. package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
  104. package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
  105. package/dist/src/control-plane/sessions/pi/translator.js +100 -0
  106. package/dist/src/control-plane/sessions/request.js +164 -0
  107. package/dist/src/control-plane/sessions/resources.js +63 -0
  108. package/dist/src/control-plane/sessions/routes.js +88 -0
  109. package/dist/src/control-plane/sessions/serialize.js +24 -0
  110. package/dist/src/control-plane/sessions/service.js +733 -0
  111. package/dist/src/control-plane/sessions/store.js +538 -0
  112. package/dist/src/control-plane/sessions/types.js +1 -0
  113. package/dist/src/control-plane/skills/archive.js +169 -0
  114. package/dist/src/control-plane/skills/routes.js +41 -0
  115. package/dist/src/control-plane/skills/service.js +29 -0
  116. package/dist/src/control-plane/skills/store.js +233 -0
  117. package/dist/src/control-plane/skills/types.js +5 -0
  118. package/dist/src/control-plane/sqlite-transaction.js +15 -0
  119. package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
  120. package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
  121. package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
  122. package/dist/src/control-plane/vaults/routes.js +66 -0
  123. package/dist/src/control-plane/vaults/service.js +553 -0
  124. package/dist/src/control-plane/vaults/store.js +691 -0
  125. package/dist/src/control-plane/vaults/types.js +1 -0
  126. package/dist/src/control-plane/wake-loop.js +65 -0
  127. package/dist/src/control-plane/wiring.js +92 -0
  128. package/dist/src/control-plane/workspace.js +4 -0
  129. package/dist/src/control-plane/workspaces/store.js +158 -0
  130. package/dist/src/egress-proxy-main.js +103 -0
  131. package/dist/src/main.js +124 -0
  132. package/dist/src/types/agents.js +1 -0
  133. package/dist/src/types/common.js +1 -0
  134. package/dist/src/types/environments.js +1 -0
  135. package/dist/src/types/events.js +121 -0
  136. package/dist/src/types/files.js +1 -0
  137. package/dist/src/types/json.js +19 -0
  138. package/dist/src/types/sessions.js +1 -0
  139. package/dist/ui/managed-agents-console/README.md +52 -0
  140. package/dist/ui/managed-agents-console/docs/agents.md +23 -0
  141. package/dist/ui/managed-agents-console/docs/console.md +23 -0
  142. package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
  143. package/dist/ui/managed-agents-console/docs/environments.md +27 -0
  144. package/dist/ui/managed-agents-console/docs/events.md +19 -0
  145. package/dist/ui/managed-agents-console/docs/files.md +17 -0
  146. package/dist/ui/managed-agents-console/docs/github.md +11 -0
  147. package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
  148. package/dist/ui/managed-agents-console/docs/memory.md +11 -0
  149. package/dist/ui/managed-agents-console/docs/migration.md +17 -0
  150. package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
  151. package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
  152. package/dist/ui/managed-agents-console/docs/overview.md +41 -0
  153. package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
  154. package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
  155. package/dist/ui/managed-agents-console/docs/reference.md +27 -0
  156. package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
  157. package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
  158. package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
  159. package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
  160. package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
  161. package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
  162. package/dist/ui/managed-agents-console/docs/skills.md +15 -0
  163. package/dist/ui/managed-agents-console/docs/tools.md +23 -0
  164. package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
  165. package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
  166. package/dist/ui/managed-agents-console/index.html +47 -0
  167. package/dist/ui/managed-agents-console/serve.mjs +116 -0
  168. package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
  169. package/dist/ui/managed-agents-console/src/api.js +1043 -0
  170. package/dist/ui/managed-agents-console/src/app.jsx +633 -0
  171. package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
  172. package/dist/ui/managed-agents-console/src/console.css +733 -0
  173. package/dist/ui/managed-agents-console/src/data.js +188 -0
  174. package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
  175. package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
  176. package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
  177. package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
  178. package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
  179. package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
  180. package/dist/ui/managed-agents-console/src/sse.js +185 -0
  181. package/dist/ui/managed-agents-console/src/states.jsx +90 -0
  182. package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
  183. package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
  184. package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
  185. package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
  186. package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
  187. package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
  188. package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
  189. package/dist/ui/openapi-docs/VENDOR.md +27 -0
  190. package/dist/ui/openapi-docs/index.html +16 -0
  191. package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
  192. package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
  193. package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
  194. package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
  195. package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
  196. package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
  197. package/package.json +53 -0
@@ -0,0 +1,388 @@
1
+ export const CMA_GLOB_READY_MARKER = "__OMA_GLOB_READY__";
2
+ const MAX_PATTERN_BYTES = 1_024;
3
+ const MAX_BRACE_DEPTH = 4;
4
+ const MAX_ALTERNATIVES = 64;
5
+ const MAX_CLASS_BYTES = 256;
6
+ const MAX_COMPILED_STATES = 4_096;
7
+ export class CmaGlobReadinessFilter {
8
+ expectedToken;
9
+ pending = Buffer.alloc(0);
10
+ ready = false;
11
+ constructor(expectedToken) {
12
+ this.expectedToken = expectedToken;
13
+ }
14
+ push(chunk) {
15
+ if (this.ready)
16
+ return chunk;
17
+ this.pending = Buffer.concat([this.pending, chunk]);
18
+ const delimiter = this.pending.indexOf(0);
19
+ if (delimiter < 0) {
20
+ if (this.pending.length > 256)
21
+ throw new Error("Glob readiness marker exceeds limit");
22
+ return undefined;
23
+ }
24
+ const marker = this.pending.subarray(0, delimiter).toString("utf8");
25
+ if (marker !== this.expectedToken)
26
+ throw new Error("Invalid glob readiness marker");
27
+ this.ready = true;
28
+ const remainder = this.pending.subarray(delimiter + 1);
29
+ this.pending = Buffer.alloc(0);
30
+ return remainder.length === 0 ? undefined : remainder;
31
+ }
32
+ assertReady() {
33
+ if (!this.ready)
34
+ throw new Error("Glob process did not publish readiness");
35
+ }
36
+ }
37
+ export class CmaGlobStreamCollector {
38
+ matcher;
39
+ opts;
40
+ matches = [];
41
+ pending = Buffer.alloc(0);
42
+ rawBytes = 0;
43
+ outputBytes = 0;
44
+ limitReached = false;
45
+ constructor(matcher, opts) {
46
+ this.matcher = matcher;
47
+ this.opts = opts;
48
+ }
49
+ push(chunk) {
50
+ if (this.limitReached)
51
+ return;
52
+ this.rawBytes += chunk.byteLength;
53
+ if (this.rawBytes > this.opts.maxRawBytes) {
54
+ throw new Error(`Glob enumeration exceeds ${this.opts.maxRawBytes} raw bytes`);
55
+ }
56
+ this.pending = Buffer.concat([this.pending, chunk]);
57
+ for (;;) {
58
+ const delimiter = this.pending.indexOf(0);
59
+ if (delimiter < 0)
60
+ return;
61
+ const record = this.pending.subarray(0, delimiter);
62
+ this.pending = this.pending.subarray(delimiter + 1);
63
+ this.accept(record);
64
+ if (this.limitReached)
65
+ return;
66
+ }
67
+ }
68
+ finish() {
69
+ if (!this.limitReached && this.pending.length !== 0) {
70
+ throw new Error("Glob enumeration returned an unterminated filename");
71
+ }
72
+ }
73
+ accept(record) {
74
+ if (record.includes(0))
75
+ throw new Error("Glob filename contains NUL");
76
+ let relativePath = record.toString("utf8");
77
+ if (relativePath.startsWith("./"))
78
+ relativePath = relativePath.slice(2);
79
+ if (relativePath.length === 0 || !this.matcher.matches(relativePath))
80
+ return;
81
+ const absolutePath = this.opts.join(this.opts.root, relativePath);
82
+ const outputPath = this.opts.formatForOutput?.(absolutePath, relativePath) ?? absolutePath;
83
+ const addedBytes = Buffer.byteLength(outputPath, "utf8") +
84
+ (this.matches.length === 0 ? 0 : 1);
85
+ if (this.outputBytes + addedBytes > this.opts.maxOutputBytes) {
86
+ throw new Error(`Glob output exceeds ${this.opts.maxOutputBytes} bytes`);
87
+ }
88
+ this.outputBytes += addedBytes;
89
+ this.matches.push(absolutePath);
90
+ if (this.matches.length >= this.opts.maxMatches) {
91
+ this.limitReached = true;
92
+ this.opts.onLimit();
93
+ }
94
+ }
95
+ }
96
+ export class CmaGlobPatternError extends Error {
97
+ constructor(message) {
98
+ super(message);
99
+ this.name = "CmaGlobPatternError";
100
+ }
101
+ }
102
+ export function compileCmaGlob(pattern) {
103
+ if (Buffer.byteLength(pattern, "utf8") > MAX_PATTERN_BYTES) {
104
+ throw patternError(`pattern exceeds ${MAX_PATTERN_BYTES} bytes`);
105
+ }
106
+ assertBraceDepth(pattern);
107
+ const alternatives = expandBraces(pattern);
108
+ // CMA plain patterns match at any descendant basename. Encode that as one
109
+ // NFA prefix instead of rescanning every slash-delimited suffix.
110
+ const tokenSets = alternatives.map((alternative) => [
111
+ { type: "globstarPrefix" },
112
+ ...tokenize(alternative),
113
+ ]);
114
+ const compiledStates = tokenSets.reduce((total, tokens) => total + tokens.length + 1, 0);
115
+ if (compiledStates > MAX_COMPILED_STATES) {
116
+ throw patternError(`compiled states exceed ${MAX_COMPILED_STATES}`);
117
+ }
118
+ return {
119
+ matches(value) {
120
+ // Provider boundaries normalize platform separators. Backslash remains a
121
+ // valid POSIX filename character and must not be rewritten here.
122
+ return tokenSets.some((tokens) => matchTokens(tokens, value));
123
+ },
124
+ };
125
+ }
126
+ /** Translate CMA's implicit descendant-suffix matching to ripgrep's
127
+ * root-relative glob syntax. Both grammars share the validated wildcard,
128
+ * class, brace, and escape forms used by CMA; the anchoring rule differs. */
129
+ export function cmaGlobToRipgrepGlob(pattern) {
130
+ compileCmaGlob(pattern);
131
+ return pattern.startsWith("**/") ? pattern : `**/${pattern}`;
132
+ }
133
+ function expandBraces(pattern) {
134
+ let escaped = false;
135
+ let open = -1;
136
+ for (let index = 0; index < pattern.length; index += 1) {
137
+ const char = pattern[index];
138
+ if (escaped) {
139
+ escaped = false;
140
+ continue;
141
+ }
142
+ if (char === "\\") {
143
+ escaped = true;
144
+ continue;
145
+ }
146
+ if (char === "}")
147
+ throw patternError("unmatched closing brace");
148
+ if (char === "{") {
149
+ open = index;
150
+ break;
151
+ }
152
+ }
153
+ if (escaped)
154
+ throw patternError("trailing escape");
155
+ if (open < 0)
156
+ return [pattern];
157
+ const parts = [];
158
+ let nested = 0;
159
+ let partStart = open + 1;
160
+ let close = -1;
161
+ escaped = false;
162
+ for (let index = open + 1; index < pattern.length; index += 1) {
163
+ const char = pattern[index];
164
+ if (escaped) {
165
+ escaped = false;
166
+ continue;
167
+ }
168
+ if (char === "\\") {
169
+ escaped = true;
170
+ }
171
+ else if (char === "{") {
172
+ nested += 1;
173
+ }
174
+ else if (char === "}" && nested > 0) {
175
+ nested -= 1;
176
+ }
177
+ else if (char === "}" && nested === 0) {
178
+ parts.push(pattern.slice(partStart, index));
179
+ close = index;
180
+ break;
181
+ }
182
+ else if (char === "," && nested === 0) {
183
+ parts.push(pattern.slice(partStart, index));
184
+ partStart = index + 1;
185
+ }
186
+ }
187
+ if (close < 0)
188
+ throw patternError("unclosed brace");
189
+ if (parts.length < 2 || parts.some((part) => part.length === 0)) {
190
+ throw patternError("brace expression requires non-empty alternatives");
191
+ }
192
+ const prefix = pattern.slice(0, open);
193
+ const suffix = pattern.slice(close + 1);
194
+ const expanded = [];
195
+ for (const part of parts) {
196
+ for (const value of expandBraces(`${prefix}${part}${suffix}`)) {
197
+ expanded.push(value);
198
+ if (expanded.length > MAX_ALTERNATIVES) {
199
+ throw patternError(`expanded alternatives exceed ${MAX_ALTERNATIVES}`);
200
+ }
201
+ }
202
+ }
203
+ return expanded;
204
+ }
205
+ function assertBraceDepth(pattern) {
206
+ let depth = 0;
207
+ let escaped = false;
208
+ for (const char of pattern) {
209
+ if (escaped) {
210
+ escaped = false;
211
+ }
212
+ else if (char === "\\") {
213
+ escaped = true;
214
+ }
215
+ else if (char === "{") {
216
+ depth += 1;
217
+ if (depth > MAX_BRACE_DEPTH) {
218
+ throw patternError(`brace nesting exceeds ${MAX_BRACE_DEPTH}`);
219
+ }
220
+ }
221
+ else if (char === "}") {
222
+ depth -= 1;
223
+ if (depth < 0)
224
+ throw patternError("unmatched closing brace");
225
+ }
226
+ }
227
+ if (depth !== 0)
228
+ throw patternError("unclosed brace");
229
+ }
230
+ function tokenize(pattern) {
231
+ const tokens = [];
232
+ for (let index = 0; index < pattern.length;) {
233
+ const char = pattern[index];
234
+ if (char === "\\") {
235
+ const escaped = codePointAt(pattern, index + 1);
236
+ if (escaped === undefined)
237
+ throw patternError("trailing escape");
238
+ tokens.push({ type: "literal", value: escaped.value });
239
+ index = escaped.next;
240
+ }
241
+ else if (char === "*") {
242
+ if (pattern[index + 1] === "*" && pattern[index + 2] === "/") {
243
+ tokens.push({ type: "globstarPrefix" });
244
+ index += 3;
245
+ }
246
+ else if (pattern[index + 1] === "*") {
247
+ tokens.push({ type: "globstar" });
248
+ index += 2;
249
+ }
250
+ else {
251
+ tokens.push({ type: "star" });
252
+ index += 1;
253
+ }
254
+ }
255
+ else if (char === "?") {
256
+ tokens.push({ type: "any" });
257
+ index += 1;
258
+ }
259
+ else if (char === "[") {
260
+ const parsed = parseClass(pattern, index);
261
+ tokens.push(parsed.token);
262
+ index = parsed.end + 1;
263
+ }
264
+ else if (char === "]") {
265
+ throw patternError("unmatched closing bracket");
266
+ }
267
+ else {
268
+ const literal = codePointAt(pattern, index);
269
+ tokens.push({ type: "literal", value: literal.value });
270
+ index = literal.next;
271
+ }
272
+ }
273
+ return tokens;
274
+ }
275
+ function parseClass(pattern, start) {
276
+ let end = start + 1;
277
+ let escaped = false;
278
+ for (; end < pattern.length; end += 1) {
279
+ const char = pattern[end];
280
+ if (escaped)
281
+ escaped = false;
282
+ else if (char === "\\")
283
+ escaped = true;
284
+ else if (char === "]")
285
+ break;
286
+ }
287
+ if (end >= pattern.length)
288
+ throw patternError("unclosed character class");
289
+ const source = pattern.slice(start + 1, end);
290
+ if (Buffer.byteLength(source, "utf8") > MAX_CLASS_BYTES) {
291
+ throw patternError(`character class exceeds ${MAX_CLASS_BYTES} bytes`);
292
+ }
293
+ let offset = 0;
294
+ const negated = source[0] === "!" || source[0] === "^";
295
+ if (negated)
296
+ offset = 1;
297
+ const chars = [];
298
+ while (offset < source.length) {
299
+ let escaped = false;
300
+ if (source[offset] === "\\") {
301
+ escaped = true;
302
+ offset += 1;
303
+ if (offset >= source.length)
304
+ throw patternError("trailing escape in character class");
305
+ }
306
+ const item = codePointAt(source, offset);
307
+ chars.push({ codePoint: item.value.codePointAt(0), escaped });
308
+ offset = item.next;
309
+ }
310
+ if (chars.length === 0)
311
+ throw patternError("empty character class");
312
+ const ranges = [];
313
+ for (let index = 0; index < chars.length; index += 1) {
314
+ const separator = chars[index + 1];
315
+ if (index + 2 < chars.length && separator.codePoint === 45 && !separator.escaped) {
316
+ if (chars[index].codePoint > chars[index + 2].codePoint) {
317
+ throw patternError("descending character range");
318
+ }
319
+ ranges.push([chars[index].codePoint, chars[index + 2].codePoint]);
320
+ index += 2;
321
+ }
322
+ else {
323
+ ranges.push([chars[index].codePoint, chars[index].codePoint]);
324
+ }
325
+ }
326
+ return { token: { type: "class", negated, ranges }, end };
327
+ }
328
+ function matchTokens(tokens, value) {
329
+ const chars = Array.from(value);
330
+ let states = new Set([0]);
331
+ states = epsilonClosure(tokens, states);
332
+ for (const char of chars) {
333
+ const next = new Set();
334
+ for (const state of states) {
335
+ const token = tokens[state];
336
+ if (token === undefined)
337
+ continue;
338
+ if (token.type === "globstar")
339
+ next.add(state);
340
+ else if (token.type === "globstarPrefix") {
341
+ next.add(state);
342
+ if (char === "/")
343
+ next.add(state + 1);
344
+ }
345
+ else if (token.type === "star" && char !== "/")
346
+ next.add(state);
347
+ else if (token.type === "any" && char !== "/")
348
+ next.add(state + 1);
349
+ else if (token.type === "literal" && char === token.value)
350
+ next.add(state + 1);
351
+ else if (token.type === "class" && char !== "/" && classMatches(token, char))
352
+ next.add(state + 1);
353
+ }
354
+ states = epsilonClosure(tokens, next);
355
+ if (states.size === 0)
356
+ return false;
357
+ }
358
+ return epsilonClosure(tokens, states).has(tokens.length);
359
+ }
360
+ function epsilonClosure(tokens, initial) {
361
+ const states = new Set(initial);
362
+ const pending = [...states];
363
+ while (pending.length > 0) {
364
+ const state = pending.pop();
365
+ const token = tokens[state];
366
+ if ((token?.type === "star" || token?.type === "globstar" || token?.type === "globstarPrefix") &&
367
+ !states.has(state + 1)) {
368
+ states.add(state + 1);
369
+ pending.push(state + 1);
370
+ }
371
+ }
372
+ return states;
373
+ }
374
+ function classMatches(token, char) {
375
+ const codePoint = char.codePointAt(0);
376
+ const included = token.ranges.some(([start, end]) => codePoint >= start && codePoint <= end);
377
+ return token.negated ? !included : included;
378
+ }
379
+ function codePointAt(value, index) {
380
+ const codePoint = value.codePointAt(index);
381
+ if (codePoint === undefined)
382
+ return undefined;
383
+ const character = String.fromCodePoint(codePoint);
384
+ return { value: character, next: index + character.length };
385
+ }
386
+ function patternError(message) {
387
+ return new CmaGlobPatternError(`Invalid glob pattern: ${message}`);
388
+ }
@@ -0,0 +1,124 @@
1
+ import { posix } from "node:path";
2
+ import { toPosix } from "./glob.js";
3
+ export const CMA_GREP_READY_MARKER = "__OMA_GREP_READY__";
4
+ export const CMA_GREP_MAX_PATTERN_BYTES = 4_096;
5
+ export const CMA_GREP_MAX_PATH_BYTES = 4_096;
6
+ export const CMA_GREP_MAX_MATCHES = 100;
7
+ export const CMA_GREP_MAX_RAW_BYTES = 1024 * 1024;
8
+ export const CMA_GREP_MAX_OUTPUT_BYTES = 64 * 1024;
9
+ export const CMA_GREP_TIMEOUT_MS = 10_000;
10
+ export class CmaGrepInputError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "CmaGrepInputError";
14
+ }
15
+ }
16
+ export class CmaGrepStreamCollector {
17
+ opts;
18
+ matches = [];
19
+ pending = Buffer.alloc(0);
20
+ rawBytes = 0;
21
+ outputBytes = 0;
22
+ limitReached = false;
23
+ constructor(opts) {
24
+ this.opts = opts;
25
+ }
26
+ push(chunk) {
27
+ if (this.limitReached)
28
+ return;
29
+ this.rawBytes += chunk.byteLength;
30
+ if (this.rawBytes > this.opts.maxRawBytes) {
31
+ throw new Error(`Grep output exceeds ${this.opts.maxRawBytes} raw bytes`);
32
+ }
33
+ this.pending = Buffer.concat([this.pending, chunk]);
34
+ for (;;) {
35
+ const delimiter = this.pending.indexOf(0);
36
+ if (delimiter < 0)
37
+ return;
38
+ const record = this.pending.subarray(0, delimiter);
39
+ this.pending = this.pending.subarray(delimiter + 1);
40
+ this.accept(record);
41
+ if (this.limitReached)
42
+ return;
43
+ }
44
+ }
45
+ finish() {
46
+ if (!this.limitReached && this.pending.length !== 0) {
47
+ throw new Error("Grep output returned an unterminated filename");
48
+ }
49
+ }
50
+ accept(record) {
51
+ if (record.includes(0))
52
+ throw new Error("Grep filename contains NUL");
53
+ let path = record.toString("utf8");
54
+ if (path.startsWith("./"))
55
+ path = path.slice(2);
56
+ if (path.length === 0)
57
+ return;
58
+ const absolutePath = posix.isAbsolute(path) ? path : this.opts.join(this.opts.root, path);
59
+ const relativePath = posix.isAbsolute(path)
60
+ ? relativeToSearchRoot(this.opts.root, absolutePath)
61
+ : path;
62
+ if (this.opts.matcher !== undefined && !this.opts.matcher.matches(relativePath))
63
+ return;
64
+ const outputPath = this.opts.formatForOutput?.(absolutePath, relativePath) ?? absolutePath;
65
+ const addedBytes = Buffer.byteLength(outputPath, "utf8") +
66
+ (this.matches.length === 0 ? 0 : 1);
67
+ if (this.outputBytes + addedBytes > this.opts.maxOutputBytes) {
68
+ throw new Error(`Grep output exceeds ${this.opts.maxOutputBytes} bytes`);
69
+ }
70
+ this.outputBytes += addedBytes;
71
+ this.matches.push(absolutePath);
72
+ if (this.matches.length >= this.opts.maxMatches) {
73
+ this.limitReached = true;
74
+ this.opts.onLimit();
75
+ }
76
+ }
77
+ }
78
+ function relativeToSearchRoot(root, absolutePath) {
79
+ const relativePath = posix.relative(root, absolutePath);
80
+ return relativePath === "" ? posix.basename(absolutePath) : relativePath;
81
+ }
82
+ export function assertCmaGrepPattern(pattern) {
83
+ if (typeof pattern !== "string" || pattern.length === 0) {
84
+ throw new CmaGrepInputError("grep pattern is required");
85
+ }
86
+ if (Buffer.byteLength(pattern, "utf8") > CMA_GREP_MAX_PATTERN_BYTES) {
87
+ throw new CmaGrepInputError(`grep pattern exceeds ${CMA_GREP_MAX_PATTERN_BYTES} bytes`);
88
+ }
89
+ return pattern;
90
+ }
91
+ export function assertCmaGrepPath(path) {
92
+ if (typeof path !== "string" || path.length === 0) {
93
+ throw new CmaGrepInputError("grep path is required");
94
+ }
95
+ if (!path.startsWith("/")) {
96
+ throw new CmaGrepInputError("grep path must be absolute");
97
+ }
98
+ if (Buffer.byteLength(path, "utf8") > CMA_GREP_MAX_PATH_BYTES) {
99
+ throw new CmaGrepInputError(`grep path exceeds ${CMA_GREP_MAX_PATH_BYTES} bytes`);
100
+ }
101
+ return path;
102
+ }
103
+ export function assertCmaGrepContext(context) {
104
+ if (context === undefined)
105
+ return 0;
106
+ if (typeof context !== "number" || !Number.isSafeInteger(context) || context < 0 || context > 100) {
107
+ throw new CmaGrepInputError("grep context must be an integer from 0 through 100");
108
+ }
109
+ return context;
110
+ }
111
+ export function assertCmaGrepHeadLimit(headLimit) {
112
+ if (headLimit === undefined)
113
+ return CMA_GREP_MAX_MATCHES;
114
+ if (typeof headLimit !== "number" || !Number.isSafeInteger(headLimit) || headLimit < 1 || headLimit > CMA_GREP_MAX_MATCHES) {
115
+ throw new CmaGrepInputError(`grep head_limit must be an integer from 1 through ${CMA_GREP_MAX_MATCHES}`);
116
+ }
117
+ return headLimit;
118
+ }
119
+ export function formatCmaGrepOutput(matches) {
120
+ return matches.length === 0 ? "No matches found" : matches.join("\n");
121
+ }
122
+ export function outputRelativeTo(base, absolutePath) {
123
+ return toPosix(posix.relative(base, absolutePath));
124
+ }