@cortad/connector 0.6.2 → 0.6.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.
package/lib/layer.mjs CHANGED
@@ -1,982 +1,4 @@
1
1
  // The parsers Cortad's read runs, bundled for the connector by the backend's build:layer. Not written here: change the backend.
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn, res) => function __init() {
4
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
- };
6
-
7
- var init_retired = __esm({
8
- "src/model/retired.mjs"() {
9
- "use strict";
10
- }
11
- });
12
-
13
- var init_models = __esm({
14
- "src/harness/models.mjs"() {
15
- "use strict";
16
- init_retired();
17
- }
18
- });
19
-
20
- var init_fault = __esm({
21
- "src/simulate/fault.ts"() {
22
- "use strict";
23
- init_models();
24
- }
25
- });
26
-
27
- var init_door_said = __esm({
28
- "src/customer/door-said.ts"() {
29
- "use strict";
30
- }
31
- });
32
-
33
- var init_body_wire = __esm({
34
- "src/customer/body-wire.mjs"() {
35
- "use strict";
36
- }
37
- });
38
-
39
- var init_retrieved = __esm({
40
- "src/simulate/retrieved.ts"() {
41
- "use strict";
42
- init_fault();
43
- }
44
- });
45
-
46
- var init_spec_doors = __esm({
47
- "src/harness/spec-doors.mjs"() {
48
- "use strict";
49
- }
50
- });
51
-
52
- var words, characters;
53
- var init_shorten = __esm({
54
- "src/text/shorten.ts"() {
55
- "use strict";
56
- words = new Intl.Segmenter(void 0, { granularity: "word" });
57
- characters = new Intl.Segmenter(void 0, { granularity: "grapheme" });
58
- }
59
- });
60
-
61
- var ENV_NAME, SECRET_GLOBS, SECRET_PATH, RULES;
62
- var init_redact = __esm({
63
- "src/jobs/redact.ts"() {
64
- "use strict";
65
- init_shorten();
66
- ENV_NAME = String.raw`[A-Za-z0-9_]*(?:(?:API|APP|ACCESS|AUTH|CLIENT|ENCRYPTION|MASTER|PRIVATE|SECRET|SIGNING)[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|CREDENTIALS?|DSN)`;
67
- SECRET_GLOBS = [
68
- ".env",
69
- ".env.*",
70
- ".envrc",
71
- ".netrc",
72
- ".git-credentials",
73
- ".npmrc",
74
- ".pgpass",
75
- "id_rsa*",
76
- "id_dsa*",
77
- "id_ecdsa*",
78
- "id_ed25519*",
79
- "*.pem",
80
- "*.key",
81
- "*.p12"
82
- ];
83
- SECRET_PATH = new RegExp(
84
- `(^|/)(${SECRET_GLOBS.map((g) => g.replace(/[.+^${}()|[\]\\]/g, String.raw`\$&`).replaceAll("*", ".*")).join("|")})$`,
85
- "i"
86
- );
87
- RULES = [
88
- { re: /(\b[a-z][a-z0-9+.-]*:\/\/)[^/?#\s]+@/gi, to: "$1<redacted>@" },
89
- { re: /\b(bearer|basic|token)\s+(?=[A-Za-z0-9._~+/=-]*[0-9])[A-Za-z0-9._~+/=-]{12,}/gi, to: "$1 <redacted>" },
90
- { re: /\b(authorization|proxy-authorization)\s*[:=]\s*(?![`']?(?:bearer|basic|token)\s+(?:<|\$\{|[^\s<"\\]{1,7}(?:[\s"\\`]|$)))\S+/gi, to: "$1: <redacted>" },
91
- { re: /\b(gh[pousr]|github_pat)_[A-Za-z0-9_]{16,}/g, to: "<redacted-github-token>" },
92
- { re: /\bsk-[A-Za-z0-9._-]{16,}/g, to: "<redacted-key>" },
93
- { re: /\bfw_[A-Za-z0-9]{16,}/g, to: "<redacted-key>" },
94
- { re: /\bAIza[A-Za-z0-9_-]{20,}/g, to: "<redacted-key>" },
95
- { re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, to: "<redacted-key>" },
96
- { re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, to: "<redacted-private-key>" },
97
- { re: new RegExp(String.raw`\b(${ENV_NAME})\s*=\s*\S{8,}`, "gi"), to: "$1=<redacted>" }
98
- ];
99
- }
100
- });
101
-
102
- var init_failure = __esm({
103
- "src/simulate/failure.ts"() {
104
- "use strict";
105
- init_redact();
106
- }
107
- });
108
-
109
- var init_two_step = __esm({
110
- "src/customer/two-step.ts"() {
111
- "use strict";
112
- init_endpoint();
113
- }
114
- });
115
-
116
- function turnSides(choices) {
117
- const words2 = choices.filter((c) => typeof c === "string");
118
- if (words2.length < 2) return null;
119
- const person = words2.find((w) => PERSON_SIDE.test(w)) ?? words2.find((w) => !NEITHER.test(w));
120
- const app = words2.find((w) => w !== person && APP_SIDE.test(w)) ?? words2.find((w) => w !== person && !NEITHER.test(w));
121
- return person && app ? [person, app] : null;
122
- }
123
- var ROLE_KEY, PERSON_SIDE, APP_SIDE, NEITHER;
124
- var init_turns = __esm({
125
- "src/customer/turns.ts"() {
126
- "use strict";
127
- ROLE_KEY = /^(?:role|sender|from|author|speaker|who|side)$/i;
128
- PERSON_SIDE = /^(?:user|human|learner|student|customer|client|person|visitor|guest|player|candidate|patient|member|caller|me|you|question)$/i;
129
- APP_SIDE = /^(?:assistant|ai|bot|model|agent|npc|coach|tutor|teacher|mentor|advisor|expert|character|answer|gpt|llm|robot)$/i;
130
- NEITHER = /^(?:system|developer|tool|function|event|log|meta|hint|error)$/i;
131
- }
132
- });
133
-
134
- var init_document = __esm({
135
- "src/customer/document.ts"() {
136
- "use strict";
137
- init_two_step();
138
- init_turns();
139
- }
140
- });
141
-
142
- function readsAsAsk(key) {
143
- const words2 = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").split(/[_\-\s]+/).filter(Boolean);
144
- return words2.length > 0 && ASK_WORD.test(words2.at(-1)) && !NOT_THE_PERSONS.test(words2[0]);
145
- }
146
- function numberFor(key) {
147
- if (/tokens?$/i.test(key)) return 1024;
148
- if (/(?:max|limit|count|turns|rounds|steps|size|length|total|top_?k|minutes|seconds|days|pages|attempts|questions)/i.test(key)) return 5;
149
- return 1;
150
- }
151
- function templateFromFields(fields) {
152
- const out = {};
153
- for (const field of fields) {
154
- if (MESSAGE_KEY.test(field)) out[field] = "";
155
- else if (CONVERSATION_KEY.test(field)) out[field] = [];
156
- }
157
- return Object.keys(out).length ? out : null;
158
- }
159
- function linkRefs(body) {
160
- if (!body || typeof body !== "object" || Array.isArray(body)) return body;
161
- const records = /* @__PURE__ */ new Map();
162
- const collect = (value) => {
163
- if (!value || typeof value !== "object") return;
164
- for (const [key, v] of Object.entries(value)) {
165
- if (Array.isArray(v)) {
166
- const rows = v.filter((r) => Boolean(r) && typeof r === "object" && !Array.isArray(r) && typeof r["id"] === "string");
167
- if (rows.length && rows.length === v.length && !CONVERSATION_KEY.test(key)) records.set(singular(key), rows);
168
- v.forEach(collect);
169
- } else collect(v);
170
- }
171
- };
172
- collect(body);
173
- if (!records.size) return body;
174
- const out = structuredClone(body);
175
- records.clear();
176
- collect(out);
177
- let changed = false;
178
- for (const rows of records.values()) {
179
- const seen = /* @__PURE__ */ new Set();
180
- rows.forEach((row, i) => {
181
- const id = row["id"];
182
- if (seen.has(id)) {
183
- row["id"] = /^[0-9a-f-]{36}$/i.test(id) ? `${id.slice(0, -4)}${String(i + 1).padStart(4, "0")}` : `${id}-${i + 1}`;
184
- changed = true;
185
- }
186
- seen.add(row["id"]);
187
- });
188
- }
189
- const point = (value) => {
190
- if (!value || typeof value !== "object") return;
191
- if (Array.isArray(value)) {
192
- value.forEach(point);
193
- return;
194
- }
195
- const row = value;
196
- for (const [key, v] of Object.entries(row)) {
197
- const m = /^(.*?)(?:Id|_id)$/.exec(key);
198
- const stem = m?.[1] ? m[1].replace(/([a-z0-9])([A-Z])/g, "$1_$2").split("_").filter(Boolean).at(-1).toLowerCase() : null;
199
- const rows = stem ? records.get(stem) : void 0;
200
- if (typeof v === "string" && rows && !rows.some((r) => r["id"] === v)) {
201
- row[key] = rows[0]["id"];
202
- changed = true;
203
- } else point(v);
204
- }
205
- };
206
- point(out);
207
- return changed ? out : body;
208
- }
209
- var CONVERSATION_KEY, MESSAGE_KEY, SESSION_KEY, ASK_WORD, NOT_THE_PERSONS, singular;
210
- var init_endpoint = __esm({
211
- "src/customer/endpoint.ts"() {
212
- "use strict";
213
- init_fault();
214
- init_door_said();
215
- init_body_wire();
216
- init_retrieved();
217
- init_spec_doors();
218
- init_failure();
219
- init_document();
220
- init_two_step();
221
- init_turns();
222
- CONVERSATION_KEY = /^(messages|history|chat_?history|message_?history|conversation(_?history)?|(?:recent|previous|prior)_?messages|turns)$/i;
223
- MESSAGE_KEY = /^(message|prompt|question|input|query|text|content|q|user_?message|msg|user_?input|task|topic|instructions?)$/i;
224
- SESSION_KEY = /^(?:(?:session|conversation|thread|chat|dialog(?:ue)?)(?:[_-]?(?:id|key|token|uuid))?|[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*[_-]?(?:conversation|thread|chat|dialog(?:ue)?)[_-]?(?:id|uuid))$/i;
225
- ASK_WORD = /^(?:message|prompt|question|query|input|content|text|instructions?|task|topic|ask|request|goal)$/i;
226
- NOT_THE_PERSONS = /^(?:system|assistant|developer|default|base|template|reply|response|answer|output|error|title|file|model)/i;
227
- singular = (key) => key.toLowerCase().replace(/ies$/, "y").replace(/(?<!s)s$/, "");
228
- }
229
- });
230
-
231
- var init_rank = __esm({
232
- "src/harness/rank.mjs"() {
233
- "use strict";
234
- }
235
- });
236
-
237
- var init_stacks = __esm({
238
- "src/harness/stacks.mjs"() {
239
- "use strict";
240
- init_rank();
241
- }
242
- });
243
-
244
- var init_devauth = __esm({
245
- "src/harness/devauth.mjs"() {
246
- "use strict";
247
- }
248
- });
249
-
250
- var PROGRAM;
251
- var init_identities = __esm({
252
- "src/harness/identities.mjs"() {
253
- "use strict";
254
- PROGRAM = String.raw`import base64, hashlib, hmac, http.cookiejar, json, os, re, secrets, time, urllib.error, urllib.request
255
-
256
- PLAN = json.loads(base64.b64decode("__PLAN__").decode("utf-8"))
257
- OUT = "__OUT__"
258
- FILE = "__FILE__"
259
- TTL = 6 * 3600
260
- # A dev server compiles its auth route on the first request: better-chatbot's sign-in timed out at 10s
261
- # and the caller was filed unreachable. The whole program stays inside the step's two minutes.
262
- DEADLINE = time.time() + 100
263
- SIGNUP = "/identitytoolkit.googleapis.com/v1/accounts:signUp?key=standin"
264
-
265
-
266
- def read_env(path):
267
- out = {}
268
- try:
269
- with open(path, encoding="utf-8", errors="replace") as f:
270
- for line in f:
271
- m = re.match(r"^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$", line)
272
- if not m:
273
- continue
274
- v = m.group(2).strip()
275
- if len(v) >= 2 and v[0] == v[-1] and v[0] in "\"'":
276
- v = v[1:-1]
277
- out[m.group(1)] = v
278
- except OSError:
279
- pass
280
- return out
281
-
282
-
283
- def slug(role):
284
- return re.sub(r"[^a-z0-9-]", "-", role)
285
-
286
-
287
- # One account per role, made once and signed into on every later run: a fresh account per run left
288
- # 1,600 active test logins in one app's own account store.
289
- def gen(role):
290
- if role in PLAN.get("accounts", {}):
291
- return PLAN["accounts"][role]
292
- tag = secrets.token_hex(4)
293
- return {
294
- "username": "bl-%s-%s" % (slug(role), tag),
295
- "email": "bl-%s-%s@example.invalid" % (slug(role), tag),
296
- "password": secrets.token_urlsafe(18) + "A1!",
297
- "name": "Brainsless %s" % role,
298
- }
299
-
300
-
301
- def source(spec, env, g, role):
302
- if spec.startswith("env:"):
303
- for name in spec[4:].split("|"):
304
- if env.get(name):
305
- return env[name]
306
- return None
307
- return {"gen:username": g["username"], "gen:email": g["email"], "gen:password": g["password"], "gen:name": g["name"], "role": role}.get(spec)
308
-
309
-
310
- def names(spec):
311
- return spec[4:].replace("|", " or ") if spec.startswith("env:") else spec
312
-
313
-
314
- def call(path, headers, body, jar, base=None):
315
- opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(jar))
316
- req = urllib.request.Request((base or PLAN["base"]) + path, data=json.dumps(body).encode("utf-8"), method="POST")
317
- for k, v in list(PLAN["headers"].items()) + list(headers.items()):
318
- req.add_header(k, v)
319
- req.add_header("content-type", "application/json")
320
- try:
321
- with opener.open(req, timeout=40) as r:
322
- return r.status, r.read(200000).decode("utf-8", "replace")
323
- except urllib.error.HTTPError as e:
324
- # The door's own sentence rides the refusal: a 500 with no body told nobody what the
325
- # sign-up died on.
326
- try:
327
- return e.code, e.read(4000).decode("utf-8", "replace")
328
- except Exception:
329
- return e.code, ""
330
- except Exception as e:
331
- return 0, type(e).__name__
332
-
333
-
334
- def token_in(value, depth=0):
335
- if isinstance(value, dict):
336
- for k, v in value.items():
337
- if isinstance(v, str) and v and re.fullmatch(r"(?i)(access_?token|token|jwt|session_?token|id_?token|api_?key)", k):
338
- return v
339
- if depth < 2:
340
- for v in value.values():
341
- found = token_in(v, depth + 1)
342
- if found:
343
- return found
344
- return None
345
-
346
-
347
- def bearer(header, value):
348
- if header == "authorization" and not value.lower().startswith("bearer "):
349
- return "Bearer " + value
350
- return value
351
-
352
-
353
- def minted(header, value, ttl=TTL):
354
- return {"status": "minted", "header": header, "line": "%s: %s" % (header, bearer(header, value)), "expires": int(time.time()) + ttl}
355
-
356
-
357
- # The account is named after the identity, not the role slot: staff:sales and sales are two callers
358
- # at two doors, and their door reads the name back on every login.
359
- def login(recipe, role, role_value, env):
360
- g = gen(role)
361
- ways = [recipe["steps"]] + ([recipe["alt"]] if recipe.get("alt") else [])
362
- for way in ways:
363
- last = ways[-1] is way
364
- got = walk(way, recipe, env, g, role_value)
365
- if got.get("status") != "refused" or last:
366
- return got
367
- return got
368
-
369
-
370
- def walk(steps, recipe, env, g, role_value):
371
- jar = http.cookiejar.CookieJar()
372
- status, token = 0, None
373
- for step in steps:
374
- headers = {}
375
- # A token the previous step minted signs this one: the key route wants the session that
376
- # just logged in, and a cookie jar alone does not carry a bearer.
377
- if token:
378
- headers[recipe["header"]] = bearer(recipe["header"], token)
379
- for h, spec in step.get("headers", {}).items():
380
- v = source(spec, env, g, role_value)
381
- if v is None:
382
- return {"status": "absent", "note": names(spec)}
383
- headers[h] = v
384
- body = {}
385
- unset = None
386
- for field, spec in step["body"].items():
387
- v = source(spec, env, g, role_value)
388
- if v is None:
389
- if not spec.startswith("env:"):
390
- return {"status": "absent", "note": names(spec)}
391
- # A switch in their code may make this one optional: ask without it, and a refusal
392
- # is then the variable they did not seal, not their door.
393
- unset = names(spec)
394
- continue
395
- body[field] = v
396
- status, text = call(step["route"], headers, body, jar)
397
- if status == 0:
398
- return {"status": "unreachable", "note": text}
399
- if status >= 400 and unset:
400
- return {"status": "absent", "note": unset}
401
- # The account this role was given on an earlier run is still there: go on and sign in.
402
- if status in (400, 409, 422) and step is not steps[-1] and re.search(r"exist|taken|already|duplicate|in use|registered", text or "", re.I):
403
- continue
404
- if status >= 400:
405
- said = re.sub(r"\s+", " ", re.sub(r"<[^>]+>", "", text or "")).strip()[:160]
406
- return {"status": "refused", "note": "HTTP %d at %s%s" % (status, step["route"], (": " + said) if said else "")}
407
- try:
408
- found = token_in(json.loads(text))
409
- except ValueError:
410
- found = None
411
- # A step that mints nothing keeps what the step before it minted.
412
- token = found or token
413
- cookies = "; ".join("%s=%s" % (c.name, c.value) for c in jar)
414
- # A cookie door reads its cookie by name. better-auth's sign-in answers a token AND sets
415
- # better-auth.session_token; sending the bare token as the cookie header named nothing, so its
416
- # proxy redirected every signed-in knock to the sign-in page.
417
- if recipe["header"] == "cookie" and cookies:
418
- return minted("cookie", cookies)
419
- if token:
420
- return minted(recipe["header"], token)
421
- if cookies:
422
- # ponytail: the cookie alone; a CSRF token their form also wants is the next thing to carry.
423
- return minted("cookie", cookies)
424
- return {"status": "refused", "note": "HTTP %d with no token in the reply" % status}
425
-
426
-
427
- def b64url(raw):
428
- return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii")
429
-
430
-
431
- def jwt(recipe, env):
432
- secret = source("env:" + "|".join(recipe["env"]), env, None, None)
433
- if not secret:
434
- return {"status": "absent", "note": " or ".join(recipe["env"])}
435
- now = int(time.time())
436
- # ponytail: subject 1, the first account of a restored copy; read a real id off the stand-in
437
- # database when a customer's users table does not start there.
438
- claims = {"sub": "1", "id": 1, "iat": now, "exp": now + TTL}
439
- claim = recipe.get("claim") or "sub"
440
- if claim not in claims:
441
- claims[claim] = 1
442
- head = b64url(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode("utf-8"))
443
- body = b64url(json.dumps(claims, separators=(",", ":")).encode("utf-8"))
444
- sig = b64url(hmac.new(secret.encode("utf-8"), ("%s.%s" % (head, body)).encode("ascii"), hashlib.sha256).digest())
445
- return minted("authorization", "%s.%s.%s" % (head, body, sig))
446
-
447
-
448
- # The Auth emulator the boot raised in place of their Firebase project: it signs any address up
449
- # under any key, and firebase-admin accepts its tokens while FIREBASE_AUTH_EMULATOR_HOST is set.
450
- def firebase(recipe, role, env):
451
- host = env.get("FIREBASE_AUTH_EMULATOR_HOST", "")
452
- if not re.fullmatch(r"[\w.-]+:\d{2,5}", host):
453
- return {"status": "upstream", "note": "Firebase"}
454
- g = gen(role)
455
- status, text = call(SIGNUP, {}, {"email": g["email"], "password": g["password"], "returnSecureToken": True}, http.cookiejar.CookieJar(), "http://" + host)
456
- if status == 0:
457
- return {"status": "unreachable", "note": text}
458
- try:
459
- reply = json.loads(text)
460
- except ValueError:
461
- reply = {}
462
- token = reply.get("idToken") if isinstance(reply, dict) else None
463
- if not isinstance(token, str) or not token:
464
- return {"status": "refused", "note": "HTTP %d at the Firebase emulator" % status}
465
- return minted(recipe["header"], token, int(reply.get("expiresIn") or 3600))
466
-
467
-
468
- def sealed(recipe, env):
469
- v = source("env:" + "|".join(recipe["env"]), env, None, None)
470
- if not v:
471
- return {"status": "absent", "note": " or ".join(recipe["env"])}
472
- return {"status": "sealed", "header": recipe["header"], "line": "%s: %s" % (recipe["header"], bearer(recipe["header"], v)), "note": " or ".join(recipe["env"])}
473
-
474
-
475
- def keep(path, line):
476
- fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
477
- with os.fdopen(fd, "w", encoding="utf-8") as f:
478
- f.write(line + "\n")
479
-
480
-
481
- # A token their door issued earlier and still honours is reused: sign-in doors are rate limited,
482
- # and a run re-minting on every boot would spend that budget on itself.
483
- def previous():
484
- try:
485
- with open(OUT, encoding="utf-8") as f:
486
- rows = json.load(f)["identities"]
487
- except Exception:
488
- return {}
489
- now = time.time()
490
- out = {}
491
- for r in rows:
492
- if r.get("status") not in ("minted", "sealed"):
493
- continue
494
- if not os.path.exists(FILE % (slug(r["role"]), "header" if r["status"] == "minted" else "sealed")):
495
- continue
496
- if r.get("expires") is not None and r["expires"] < now + 60:
497
- continue
498
- out[r["role"]] = r
499
- return out
500
-
501
-
502
- def main():
503
- env = read_env(PLAN["envPath"])
504
- kept = previous()
505
- rows = []
506
- for recipe in PLAN["recipes"]:
507
- for role_value in recipe.get("roles") or [None]:
508
- role = "%s:%s" % (recipe["role"], role_value) if role_value else recipe["role"]
509
- steps = recipe.get("steps") or []
510
- base = {
511
- "role": role, "kind": recipe["kind"], "header": recipe["header"], "where": recipe["where"],
512
- "opens": [o.replace("{role}", role_value or "") for o in recipe.get("opens", [])],
513
- "door": steps[-1]["route"] if steps else SIGNUP if recipe["kind"] == "firebase" else recipe["header"],
514
- }
515
- if role in kept:
516
- rows.append(dict(base, **{k: v for k, v in kept[role].items() if k in ("status", "header", "expires", "note")}))
517
- continue
518
- if time.time() > DEADLINE:
519
- rows.append(dict(base, status="unreachable", note="out of time"))
520
- continue
521
- try:
522
- if recipe["kind"] == "header":
523
- got = sealed(recipe, env)
524
- elif recipe["kind"] == "jwt":
525
- got = jwt(recipe, env)
526
- elif recipe["kind"] == "login":
527
- got = login(recipe, role, role_value, env)
528
- elif recipe["kind"] == "firebase":
529
- got = firebase(recipe, role, env)
530
- else:
531
- got = {"status": "upstream", "note": " or ".join(recipe["env"]) or recipe["role"]}
532
- except Exception as err:
533
- got = {"status": "unreachable", "note": type(err).__name__}
534
- line = got.pop("line", None)
535
- if line:
536
- keep(FILE % (slug(role), "header" if got["status"] == "minted" else "sealed"), line)
537
- rows.append(dict(base, **got))
538
- report = {"identities": rows}
539
- with open(OUT, "w", encoding="utf-8") as f:
540
- json.dump(report, f)
541
- print("BL_IDENTITIES " + json.dumps(report))
542
-
543
-
544
- main()
545
- `;
546
- }
547
- });
548
-
549
- var init_port = __esm({
550
- "src/harness/port.mjs"() {
551
- "use strict";
552
- }
553
- });
554
-
555
- var COMPILED, MANIFESTS;
556
- var init_library = __esm({
557
- "src/harness/library.mjs"() {
558
- "use strict";
559
- init_rank();
560
- COMPILED = ["go.mod", "Gemfile", "composer.json", "pom.xml", "build.gradle", "build.gradle.kts"];
561
- MANIFESTS = ["pyproject.toml", "setup.py", "package.json", ...COMPILED];
562
- }
563
- });
564
-
565
- var ENV_PREFIXES, WAITS, TRY_BOOT;
566
- var init_actions = __esm({
567
- "src/harness/repair/actions.mjs"() {
568
- "use strict";
569
- ENV_PREFIXES = /* @__PURE__ */ new Map([
570
- ["goproxy-direct", "GOPROXY=direct GOFLAGS=-mod=mod"],
571
- ["no-telemetry", "NEXT_TELEMETRY_DISABLED=1 DO_NOT_TRACK=1 SCARF_ANALYTICS=false"],
572
- ["bind-all", "HOST=0.0.0.0"],
573
- ["dev-mode", "NODE_ENV=development"],
574
- ["npm-retries", "npm_config_fetch_retries=5 npm_config_fetch_retry_maxtimeout=120000"]
575
- ]);
576
- WAITS = /* @__PURE__ */ new Set([120, 180]);
577
- TRY_BOOT = {
578
- type: "function",
579
- function: {
580
- name: "try_boot",
581
- description: "Run one command from the menu. Pick by id. You cannot write a command; you can only choose one.",
582
- parameters: {
583
- type: "object",
584
- properties: {
585
- target: { type: "string", description: "An id from the menu, such as c0 or s3." },
586
- env: { type: "string", enum: [...ENV_PREFIXES.keys()], description: "An optional environment preset to run it under." },
587
- waitSeconds: { type: "integer", enum: [...WAITS], description: "Only when the log showed the app was still starting." },
588
- why: { type: "string", description: "One sentence the customer will read, in their language, about their repository." }
589
- },
590
- required: ["target", "why"]
591
- }
592
- }
593
- };
594
- }
595
- });
596
-
597
- var init_evidence = __esm({
598
- "src/harness/repair/evidence.mjs"() {
599
- "use strict";
600
- }
601
- });
602
-
603
- var BUDGET, READ_TOOLS, FIX_TOOLS, TOOLS;
604
- var init_repair = __esm({
605
- "src/harness/repair.mjs"() {
606
- "use strict";
607
- init_actions();
608
- init_evidence();
609
- BUDGET = {
610
- attempts: 5,
611
- passes: 20,
612
- calls: 40,
613
- fixes: 8,
614
- wallMs: 12 * 6e4,
615
- microUsd: 9e5
616
- };
617
- READ_TOOLS = [
618
- {
619
- type: "function",
620
- function: {
621
- name: "list_files",
622
- description: "Names of files in the repository. The application may live in a half of the tree the ladder never looked at.",
623
- parameters: { type: "object", properties: { dir: { type: "string" }, glob: { type: "string" }, depth: { type: "integer" } } }
624
- }
625
- },
626
- {
627
- type: "function",
628
- function: {
629
- name: "read_file",
630
- description: "Read part of a file in the repository. Environment files are never readable.",
631
- parameters: { type: "object", properties: { path: { type: "string" }, from: { type: "integer" }, to: { type: "integer" } }, required: ["path"] }
632
- }
633
- },
634
- {
635
- type: "function",
636
- function: {
637
- name: "search_code",
638
- description: "Find where something is defined, such as uvicorn, FastAPI( or app.listen.",
639
- parameters: { type: "object", properties: { pattern: { type: "string" }, dir: { type: "string" } }, required: ["pattern"] }
640
- }
641
- }
642
- ];
643
- FIX_TOOLS = [
644
- {
645
- type: "function",
646
- function: {
647
- name: "write_env",
648
- description: "Append stand-in NAME=value lines to the app's .env. Append-if-absent: anything the customer set stays. Use for configuration the app refuses to start without. Never a real credential -- a stand-in value like standin-key is the point.",
649
- parameters: { type: "object", properties: { lines: { type: "string", description: "NAME=value lines, one per line, at most 8." } }, required: ["lines"] }
650
- }
651
- },
652
- {
653
- type: "function",
654
- function: {
655
- name: "write_config",
656
- description: "Create a NEW config file the app needs to start (.json .yaml .toml .ini .cfg .conf .txt .properties). Never code, never .env files, never overwrites an existing file.",
657
- parameters: { type: "object", properties: { path: { type: "string" }, content: { type: "string" } }, required: ["path", "content"] }
658
- }
659
- },
660
- {
661
- type: "function",
662
- function: {
663
- name: "install_packages",
664
- description: "Install named registry packages the boot showed missing or mismatched, e.g. a version pin the app's own dependency check demands. Plain package specs only.",
665
- parameters: {
666
- type: "object",
667
- properties: {
668
- manager: { type: "string", enum: ["pip", "npm"] },
669
- packages: { type: "array", items: { type: "string" }, description: 'e.g. ["transformers==4.44.0"] or ["sharp"]' }
670
- },
671
- required: ["manager", "packages"]
672
- }
673
- }
674
- },
675
- {
676
- type: "function",
677
- function: {
678
- name: "run_build",
679
- description: "Run a build or prep step the repository declares for itself, before the app can start -- `pnpm build` for a monorepo whose dev server needs its packages built first, `make protogen` for a Go app that generates code. Only a named package.json script or Makefile target the repo already ships. Reach for this when a boot fails because something was not built (a missing dist/, a missing generated module).",
680
- parameters: {
681
- type: "object",
682
- properties: {
683
- tool: { type: "string", enum: ["npm", "pnpm", "yarn", "make"] },
684
- name: { type: "string", description: "The script or target name, e.g. build or protogen." },
685
- dir: { type: "string", description: "Directory to run it in, relative to the repo root. Default the root." }
686
- },
687
- required: ["tool", "name"]
688
- }
689
- }
690
- },
691
- {
692
- type: "function",
693
- function: {
694
- name: "edit_source",
695
- description: `Fix a bug in the customer's own source that stops the app importing or listening. Three modes, in order of reliability: (1) ADD A MISSING IMPORT: atTop:true + insert:"from typing import Any" -- goes at the top, no anchor. (2) REPLACE ONE LINE BY NUMBER: line:32 + replace:" default=8000," -- read_file showed you the numbers; this cannot miss. (3) find/replace with a UNIQUE find string (whole line, never a bare word). The change lives only in the sandbox and returns as a patch. Reach for this when the traceback points at their code; prefer mode 2 whenever you know the line.`,
696
- parameters: {
697
- type: "object",
698
- properties: {
699
- path: { type: "string", description: "The repository source file, e.g. gpt_researcher/actions/query_processing.py" },
700
- atTop: { type: "boolean", description: "Insert `insert` at the very top of the file (missing imports)." },
701
- insert: { type: "string", description: "With atTop, the line(s) to add." },
702
- line: { type: "integer", description: "Replace exactly this 1-based line with `replace`. The most reliable way to change a line." },
703
- find: { type: "string", description: "Fallback: exact unique text to replace." },
704
- replace: { type: "string", description: "The corrected text, for line or find modes." }
705
- },
706
- required: ["path"]
707
- }
708
- }
709
- }
710
- ];
711
- TOOLS = [...READ_TOOLS, ...FIX_TOOLS, TRY_BOOT];
712
- }
713
- });
714
-
715
- function lineAt(content, index) {
716
- let line = 1;
717
- for (let i = 0; i < index; i += 1) if (content.charCodeAt(i) === 10) line += 1;
718
- return line;
719
- }
720
- function commentedAt(content, index) {
721
- const start = content.lastIndexOf("\n", index - 1) + 1;
722
- return /^\s*(?:\/\/|\*|#)/.test(content.slice(start, index));
723
- }
724
- function comparisons(content) {
725
- const found = [];
726
- MEMBER_COMPARE.lastIndex = 0;
727
- for (const match of content.matchAll(MEMBER_COMPARE)) {
728
- if (commentedAt(content, match.index)) continue;
729
- found.push({ request: match[1], member: match[2], value: match[4], index: match.index, line: lineAt(content, match.index) });
730
- }
731
- REVERSE_COMPARE.lastIndex = 0;
732
- for (const match of content.matchAll(REVERSE_COMPARE)) {
733
- if (commentedAt(content, match.index)) continue;
734
- found.push({ request: match[3], member: match[4], value: match[2], index: match.index, line: lineAt(content, match.index) });
735
- }
736
- return found.sort((a, b) => a.index - b.index);
737
- }
738
- function sameGuard(content, left, right) {
739
- const start = Math.min(left.index, right.index);
740
- const end = Math.max(left.index, right.index);
741
- return !/[{};]/.test(content.slice(start, end)) && !/\b(?:if|switch|case|return)\s*\b/.test(content.slice(start + 1, end));
742
- }
743
- function rawNodeHttpRoutes(content) {
744
- if (typeof content !== "string" || !content.includes(".url") || !content.includes(".method")) return [];
745
- const facts = comparisons(content);
746
- const methods = facts.filter((fact) => fact.member === "method" && HTTP_METHOD.test(fact.value));
747
- const routes = [];
748
- const seen = /* @__PURE__ */ new Set();
749
- for (const url of facts) {
750
- if (url.member !== "url" || !url.value.startsWith("/") || url.value.length > 120 || url.value.includes("${")) continue;
751
- if (!REQUEST_NAME.test(url.request)) continue;
752
- const method = methods.filter((candidate) => candidate.request === url.request && Math.abs(candidate.line - url.line) <= 8 && sameGuard(content, candidate, url)).sort((a, b) => Math.abs(a.index - url.index) - Math.abs(b.index - url.index))[0];
753
- if (!method) continue;
754
- const key = `${method.value.toUpperCase()} ${url.value}`;
755
- if (seen.has(key)) continue;
756
- seen.add(key);
757
- routes.push({ method: method.value.toUpperCase(), path: url.value, line: url.line });
758
- }
759
- return routes;
760
- }
761
- var MEMBER_COMPARE, REVERSE_COMPARE, HTTP_METHOD, REQUEST_NAME;
762
- var init_raw_node_routes = __esm({
763
- "src/discover/raw-node-routes.mjs"() {
764
- "use strict";
765
- MEMBER_COMPARE = /\b([A-Za-z_$][\w$]*)\s*\.\s*(method|url)\s*(?:===|==|!==|!=)\s*(["'`])([^"'`\n]+)\3/g;
766
- REVERSE_COMPARE = /(["'`])([^"'`\n]+)\1\s*(?:===|==|!==|!=)\s*\b([A-Za-z_$][\w$]*)\s*\.\s*(method|url)/g;
767
- HTTP_METHOD = /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i;
768
- REQUEST_NAME = /^(?:req(?:uest)?|httpReq(?:uest)?|incomingReq(?:uest)?)$/i;
769
- }
770
- });
771
-
772
- var init_seed = __esm({
773
- "src/harness/seed.mjs"() {
774
- "use strict";
775
- }
776
- });
777
-
778
- var init_dotenv = __esm({
779
- "src/harness/dotenv.mjs"() {
780
- "use strict";
781
- }
782
- });
783
-
784
- var init_socket_door = __esm({
785
- "src/harness/socket-door.mjs"() {
786
- "use strict";
787
- }
788
- });
789
-
790
- var script;
791
- var init_source_baseline = __esm({
792
- "src/harness/source-baseline.mjs"() {
793
- "use strict";
794
- script = String.raw`
795
- const fs = require('node:fs');
796
- const path = require('node:path');
797
- const { execFileSync } = require('node:child_process');
798
- const [suppliedRoot, mode, rulesText] = process.argv.slice(1);
799
- const initialize = mode === 'initialize';
800
- const fail = (message) => { console.error((initialize ? 'SOURCE_BASELINE_FAILED: ' : 'SOURCE_BASELINE_MISSING: ') + message + ' Recompile from a fresh source archive.'); process.exit(42); };
801
- const lstat = (p) => { try { return fs.lstatSync(p); } catch (e) { if (e.code === 'ENOENT') return null; throw e; } };
802
- let stage = 'checking the source directory';
803
- try {
804
- const root = fs.realpathSync(suppliedRoot);
805
- if (!fs.statSync(root).isDirectory()) fail('The source root is not a directory.');
806
- const gitDir = path.join(root, '.git');
807
- const prior = lstat(gitDir);
808
- if (initialize && prior) fail('Refusing to initialize a tree that already has Git metadata.');
809
- if (!initialize && (!prior || !prior.isDirectory() || prior.isSymbolicLink())) fail('The saved sandbox has no safe Git baseline directory.');
810
- const env = { PATH: process.env.PATH || '/usr/local/bin:/usr/bin:/bin', HOME: '/nonexistent', XDG_CONFIG_HOME: '/nonexistent',
811
- LC_ALL: 'C', GIT_CONFIG_NOSYSTEM: '1', GIT_CONFIG_SYSTEM: '/dev/null', GIT_CONFIG_GLOBAL: '/dev/null',
812
- GIT_NO_REPLACE_OBJECTS: '1', GIT_TERMINAL_PROMPT: '0', GIT_INDEX_FILE: path.join(gitDir, 'index') };
813
- const base = ['--git-dir=' + gitDir, '--work-tree=' + root, '-c', 'safe.directory=' + root,
814
- '-c', 'core.hooksPath=/dev/null', '-c', 'core.fsmonitor=false', '-c', 'core.untrackedCache=false',
815
- '-c', 'core.autocrlf=false', '-c', 'core.fileMode=true', '-c', 'commit.gpgSign=false'];
816
- const git = (...args) => execFileSync('git', [...base, ...args], { cwd: root, env, stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: 16 * 1024 * 1024 });
817
- const receiptPath = path.join(gitDir, 'brainsless-source-baseline.json');
818
- if (initialize) {
819
- stage = 'initializing Git';
820
- git('init', '-q', '-b', 'main', '--template=');
821
- const { secret, output, stores } = JSON.parse(rulesText);
822
- fs.mkdirSync(path.join(gitDir, 'info'), { recursive: true });
823
- fs.appendFileSync(path.join(gitDir, 'info', 'exclude'), '\n# Runtime capabilities and generated output never enter the source baseline\n' +
824
- [...secret, ...output, ...stores.map((name) => name + '/')].join('\n') + '\n');
825
- const excluded = [...secret, ...output].flatMap((glob) => [glob, '**/' + glob])
826
- .concat(stores.flatMap((name) => [name + '/**', '**/' + name + '/**']))
827
- .map((glob) => ':(exclude,icase,glob)' + glob);
828
- stage = 'indexing archived source';
829
- try {
830
- git('add', '-A', '--', '.', ...excluded);
831
- } catch (error) {
832
- const said = String(error.stderr ?? '');
833
- if (!/ignored by one of your \.gitignore files/i.test(said)) throw error;
834
- }
835
- if (!git('ls-files', '-z').length) fail('The fresh archive has no trackable source files.');
836
- if (git('ls-files', '--stage', '-z').toString('utf8').split('\0').some((entry) => entry.startsWith('160000 '))) {
837
- fail('The archive contains a nested Git checkout instead of ordinary source files.');
838
- }
839
- stage = 'committing the source baseline';
840
- git('-c', 'user.name=Cortad Sandbox', '-c', 'user.email=sandbox@cortad.com', 'commit', '--no-verify', '-q', '-m', 'Source baseline');
841
- const head = git('rev-parse', '--verify', 'HEAD^{commit}').toString('utf8').trim();
842
- fs.writeFileSync(receiptPath, JSON.stringify({ version: 1, head }), { flag: 'wx', mode: 0o600 });
843
- }
844
- stage = 'verifying the source baseline';
845
- const receiptStat = lstat(receiptPath);
846
- if (!receiptStat || !receiptStat.isFile() || receiptStat.isSymbolicLink()) fail('The saved sandbox has no verified source baseline receipt.');
847
- const receipt = JSON.parse(fs.readFileSync(receiptPath, 'utf8'));
848
- const head = git('rev-parse', '--verify', 'HEAD^{commit}').toString('utf8').trim();
849
- if (receipt.version !== 1 || receipt.head !== head || !/^[a-f0-9]{40,64}$/.test(head)) fail('The saved source baseline no longer matches its original receipt.');
850
- const tracked = git('ls-files', '-z').toString('utf8').split('\0').filter(Boolean).length;
851
- if (!tracked || !git('ls-tree', '-r', '-z', '--name-only', 'HEAD').length) fail('The source baseline has no tracked files.');
852
- console.log('__brainsless_source_baseline_ready__:' + head + ':' + tracked);
853
- } catch (error) {
854
- const reason = (() => {
855
- const raw = String(error.stderr ?? '').split('\n').map((l) => l.trim()).filter(Boolean)[0] ?? '';
856
- if (!raw) return '';
857
- const safe = raw
858
- .replace(/'[^']*'/g, "'…'")
859
- .replace(/"[^"]*"/g, '"…"')
860
- .replace(/(^|\s)[^\s]*[/\\][^\s]*/g, '$1…')
861
- .replace(/[^\s]{40,}/g, '…')
862
- .slice(0, 160);
863
- return safe.length >= 8 ? ' Git said: ' + safe : '';
864
- })();
865
- const code = typeof error.status === 'number' ? ' (exit ' + error.status + ')' : error.code === 'ENOENT' ? ' (required file or Git executable missing)' : '';
866
- fail('Failed while ' + stage + code + '.' + reason);
867
- }
868
- `;
869
- }
870
- });
871
-
872
- var init_node_install_plan = __esm({
873
- "src/harness/node-install-plan.mjs"() {
874
- "use strict";
875
- }
876
- });
877
-
878
- var init_lanes = __esm({
879
- "src/harness/lanes.mjs"() {
880
- "use strict";
881
- }
882
- });
883
-
884
- import { execFile, execFileSync, execSync } from "node:child_process";
885
- import { promisify } from "node:util";
886
- function swapInText(text, swaps) {
887
- const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
888
- let out = text;
889
- let hits = 0;
890
- for (const swap of swaps) {
891
- if (!out.includes(swap.from)) continue;
892
- const PREFIX2 = "((?:[a-z][\\w.-]*[:/])?)";
893
- out = out.replace(new RegExp("([\"'`])" + PREFIX2 + esc(swap.from) + "\\1", "g"), (_, quote, prefix) => {
894
- hits += 1;
895
- return quote + prefix + swap.to + quote;
896
- });
897
- out = out.replace(new RegExp("^([ \\t]*(?:export[ \\t]+)?[A-Za-z_][\\w.-]*[ \\t]*[=:][ \\t]*)" + PREFIX2 + esc(swap.from) + "[ \\t]*$", "gm"), (_, head, prefix) => {
898
- hits += 1;
899
- return head + prefix + swap.to;
900
- });
901
- }
902
- return { text: out, hits };
903
- }
904
- var execFileP, ALLOW_CANARY, FOLD_PORTS, MODEL_SWAP_SCRIPT, FB_CONFIG;
905
- var init_boot = __esm({
906
- "src/harness/boot.mjs"() {
907
- "use strict";
908
- init_body_wire();
909
- init_endpoint();
910
- init_retired();
911
- init_stacks();
912
- init_rank();
913
- init_models();
914
- init_devauth();
915
- init_identities();
916
- init_port();
917
- init_library();
918
- init_repair();
919
- init_raw_node_routes();
920
- init_seed();
921
- init_dotenv();
922
- init_spec_doors();
923
- init_socket_door();
924
- init_source_baseline();
925
- init_node_install_plan();
926
- init_lanes();
927
- execFileP = promisify(execFile);
928
- ALLOW_CANARY = "registry.npmjs.org/-/ping";
929
- FOLD_PORTS = [
930
- `H=${ALLOW_CANARY.split("/")[0]}`,
931
- `command -v ip6tables-legacy >/dev/null || { echo no-iptables; exit 0; }`,
932
- `A6=$(getent ahostsv6 "$H" | awk '{print $1; exit}'); A4=$(getent ahostsv4 "$H" | awk '{print $1; exit}')`,
933
- `[ -n "$A6" ] && ip6tables-legacy -t nat -A OUTPUT -p tcp -d "$A6" ! --dport 443 -j DNAT --to-destination :443`,
934
- `[ -n "$A4" ] && iptables-legacy -t nat -A OUTPUT -p tcp -d "$A4" ! --dport 443 -j DNAT --to-destination :443`,
935
- `echo folded`
936
- ].join("\n");
937
- MODEL_SWAP_SCRIPT = `const fs = require("fs"), path = require("path");
938
- ${swapInText.toString()}
939
- const swaps = JSON.parse(fs.readFileSync("/tmp/bl-swaps.json", "utf8"));
940
- const SKIP = new Set(["node_modules", ".git", ".venv", "venv", "__pycache__", ".cache", ".pnpm-store"]);
941
- let files = 0, hits = 0;
942
- const walk = (dir) => {
943
- let entries = [];
944
- try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
945
- for (const entry of entries) {
946
- if (SKIP.has(entry.name)) continue;
947
- const at = path.join(dir, entry.name);
948
- if (entry.isDirectory()) { walk(at); continue; }
949
- if (!entry.isFile()) continue;
950
- let buf;
951
- try {
952
- if (fs.statSync(at).size > 2000000) continue;
953
- buf = fs.readFileSync(at);
954
- } catch { continue; }
955
- if (buf.includes(0)) continue;
956
- const before = buf.toString("utf8");
957
- const swapped = swapInText(before, swaps);
958
- if (swapped.text === before) continue;
959
- try { fs.writeFileSync(at, swapped.text); files += 1; hits += swapped.hits; } catch {}
960
- }
961
- };
962
- walk("/workspace/repo");
963
- process.stdout.write(JSON.stringify({ files, hits }));
964
- `;
965
- FB_CONFIG = JSON.stringify({ emulators: { ui: { enabled: false }, auth: { host: "127.0.0.1", port: 9099 }, firestore: { host: "127.0.0.1", port: 8080 } }, firestore: { rules: "/workspace/repo/firestore.rules" } });
966
- }
967
- });
968
-
969
- var SLOT, SLOTS;
970
- var init_request_cell = __esm({
971
- "src/simulate/request-cell.ts"() {
972
- "use strict";
973
- init_endpoint();
974
- init_boot();
975
- SLOT = /\/(?::(\w+)(?:\([^)]*\))?[?*]?|\{(\w+)[^}]*\}|\[(?:\.\.\.)?(\w+)\]|<(?:\w+:)?(\w+)>)(?=\/|$)/;
976
- SLOTS = new RegExp(SLOT.source, "g");
977
- }
978
- });
979
-
980
2
  function dynamicToolDiscovery(files) {
981
3
  const sites = [];
982
4
  for (const file of files) {
@@ -1337,8 +359,141 @@ var NEGATIVE = new RegExp(`${bounded(NEGATIVE_BOUNDED)}|${bare(NEGATIVE_BARE)}`,
1337
359
  var NEGATIVE_START = new RegExp(`^(?:${bounded(NEGATIVE_BOUNDED)}|${bare(NEGATIVE_BARE)})`, "iu");
1338
360
  var SPEAKS_TO_A_MODEL = new RegExp(`${ADDRESS.source}|${OBLIGATION.source}`, "giu");
1339
361
 
1340
- init_endpoint();
1341
- init_turns();
362
+ var words = new Intl.Segmenter(void 0, { granularity: "word" });
363
+ var characters = new Intl.Segmenter(void 0, { granularity: "grapheme" });
364
+
365
+ var INGEST_PREFIX = "ct_ing_";
366
+ var PUBLIC_PREFIX = "ct_pub_";
367
+ var MACHINE_PREFIX = "ct_mcp_";
368
+ var LIVE_PREFIX = "bl_live_";
369
+
370
+ var OUR_PREFIXES = [MACHINE_PREFIX, INGEST_PREFIX, PUBLIC_PREFIX, LIVE_PREFIX].map((p) => p.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`));
371
+ var ENV_NAME = String.raw`[A-Za-z0-9_]*(?:(?:API|APP|ACCESS|AUTH|CLIENT|ENCRYPTION|MASTER|PRIVATE|SECRET|SIGNING)[_-]?KEY|TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|CREDENTIALS?|DSN)`;
372
+ var SECRET_GLOBS = [
373
+ ".env",
374
+ ".env.*",
375
+ ".envrc",
376
+ ".netrc",
377
+ ".git-credentials",
378
+ ".npmrc",
379
+ ".pgpass",
380
+ "id_rsa*",
381
+ "id_dsa*",
382
+ "id_ecdsa*",
383
+ "id_ed25519*",
384
+ "*.pem",
385
+ "*.key",
386
+ "*.p12"
387
+ ];
388
+ var SECRET_PATH = new RegExp(
389
+ `(^|/)(${SECRET_GLOBS.map((g) => g.replace(/[.+^${}()|[\]\\]/g, String.raw`\$&`).replaceAll("*", ".*")).join("|")})$`,
390
+ "i"
391
+ );
392
+ var RULES = [
393
+ { re: /(\b[a-z][a-z0-9+.-]*:\/\/)[^/?#\s]+@/gi, to: "$1<redacted>@" },
394
+ { re: /\b(bearer|basic|token)\s+(?=[A-Za-z0-9._~+/=-]*[0-9])[A-Za-z0-9._~+/=-]{12,}/gi, to: "$1 <redacted>" },
395
+ { re: /\b(authorization|proxy-authorization)\s*[:=]\s*(?![`']?(?:bearer|basic|token)\s+(?:<|\$\{|[^\s<"\\]{1,7}(?:[\s"\\`]|$)))\S+/gi, to: "$1: <redacted>" },
396
+ { re: /\b(gh[pousr]|github_pat)_[A-Za-z0-9_]{16,}/g, to: "<redacted-github-token>" },
397
+ { re: /\bsk-[A-Za-z0-9._-]{16,}/g, to: "<redacted-key>" },
398
+ { re: /\bfw_[A-Za-z0-9]{16,}/g, to: "<redacted-key>" },
399
+ { re: /\bAIza[A-Za-z0-9_-]{20,}/g, to: "<redacted-key>" },
400
+ { re: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, to: "<redacted-key>" },
401
+ { re: new RegExp(String.raw`\b(?:${OUR_PREFIXES.join("|")})[A-Za-z0-9._-]{8,}`, "g"), to: "<redacted-key>" },
402
+ { re: /\bpscale_(?:pw|tkn)_[A-Za-z0-9._-]{16,}/g, to: "<redacted-key>" },
403
+ { re: /\b(?:sk|rk|pk)_(?:live|test)_[A-Za-z0-9]{16,}/g, to: "<redacted-key>" },
404
+ { re: /\bwhsec_[A-Za-z0-9+/=]{16,}/g, to: "<redacted-key>" },
405
+ { re: /\bAKIA[0-9A-Z]{16}\b/g, to: "<redacted-key>" },
406
+ { re: /\bglpat-[A-Za-z0-9_-]{16,}/g, to: "<redacted-key>" },
407
+ { re: /\bhf_[A-Za-z0-9]{30,}/g, to: "<redacted-key>" },
408
+ { re: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, to: "<redacted-private-key>" },
409
+ { re: new RegExp(String.raw`\b(${ENV_NAME})\s*=\s*\S{8,}`, "gi"), to: "$1=<redacted>" }
410
+ ];
411
+
412
+ var ROLE_KEY = /^(?:role|sender|from|author|speaker|who|side)$/i;
413
+ var PERSON_SIDE = /^(?:user|human|learner|student|customer|client|person|visitor|guest|player|candidate|patient|member|caller|me|you|question)$/i;
414
+ var APP_SIDE = /^(?:assistant|ai|bot|model|agent|npc|coach|tutor|teacher|mentor|advisor|expert|character|answer|gpt|llm|robot)$/i;
415
+ var NEITHER = /^(?:system|developer|tool|function|event|log|meta|hint|error)$/i;
416
+ function turnSides(choices) {
417
+ const words2 = choices.filter((c) => typeof c === "string");
418
+ if (words2.length < 2) return null;
419
+ const person = words2.find((w) => PERSON_SIDE.test(w)) ?? words2.find((w) => !NEITHER.test(w));
420
+ const app = words2.find((w) => w !== person && APP_SIDE.test(w)) ?? words2.find((w) => w !== person && !NEITHER.test(w));
421
+ return person && app ? [person, app] : null;
422
+ }
423
+
424
+ var CONVERSATION_KEY = /^(messages|history|chat_?history|message_?history|conversation(_?history)?|(?:recent|previous|prior)_?messages|turns)$/i;
425
+ var MESSAGE_KEY = /^(message|prompt|question|input|query|text|content|q|user_?message|msg|user_?input|task|topic|instructions?)$/i;
426
+ var SESSION_KEY = /^(?:(?:session|conversation|thread|chat|dialog(?:ue)?)(?:[_-]?(?:id|key|token|uuid))?|[a-z][a-z0-9]*(?:[_-][a-z0-9]+)*[_-]?(?:conversation|thread|chat|dialog(?:ue)?)[_-]?(?:id|uuid))$/i;
427
+ var ASK_WORD = /^(?:message|prompt|question|query|input|content|text|instructions?|task|topic|ask|request|goal)$/i;
428
+ var NOT_THE_PERSONS = /^(?:system|assistant|developer|default|base|template|reply|response|answer|output|error|title|file|model)/i;
429
+ function readsAsAsk(key) {
430
+ const words2 = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").split(/[_\-\s]+/).filter(Boolean);
431
+ return words2.length > 0 && ASK_WORD.test(words2.at(-1)) && !NOT_THE_PERSONS.test(words2[0]);
432
+ }
433
+ function numberFor(key) {
434
+ if (/tokens?$/i.test(key)) return 1024;
435
+ if (/(?:max|limit|count|turns|rounds|steps|size|length|total|top_?k|minutes|seconds|days|pages|attempts|questions)/i.test(key)) return 5;
436
+ return 1;
437
+ }
438
+ function templateFromFields(fields) {
439
+ const out = {};
440
+ for (const field of fields) {
441
+ if (MESSAGE_KEY.test(field)) out[field] = "";
442
+ else if (CONVERSATION_KEY.test(field)) out[field] = [];
443
+ }
444
+ return Object.keys(out).length ? out : null;
445
+ }
446
+ function linkRefs(body) {
447
+ if (!body || typeof body !== "object" || Array.isArray(body)) return body;
448
+ const records = /* @__PURE__ */ new Map();
449
+ const collect = (value) => {
450
+ if (!value || typeof value !== "object") return;
451
+ for (const [key, v] of Object.entries(value)) {
452
+ if (Array.isArray(v)) {
453
+ const rows = v.filter((r) => Boolean(r) && typeof r === "object" && !Array.isArray(r) && typeof r["id"] === "string");
454
+ if (rows.length && rows.length === v.length && !CONVERSATION_KEY.test(key)) records.set(singular(key), rows);
455
+ v.forEach(collect);
456
+ } else collect(v);
457
+ }
458
+ };
459
+ collect(body);
460
+ if (!records.size) return body;
461
+ const out = structuredClone(body);
462
+ records.clear();
463
+ collect(out);
464
+ let changed = false;
465
+ for (const rows of records.values()) {
466
+ const seen = /* @__PURE__ */ new Set();
467
+ rows.forEach((row, i) => {
468
+ const id = row["id"];
469
+ if (seen.has(id)) {
470
+ row["id"] = /^[0-9a-f-]{36}$/i.test(id) ? `${id.slice(0, -4)}${String(i + 1).padStart(4, "0")}` : `${id}-${i + 1}`;
471
+ changed = true;
472
+ }
473
+ seen.add(row["id"]);
474
+ });
475
+ }
476
+ const point = (value) => {
477
+ if (!value || typeof value !== "object") return;
478
+ if (Array.isArray(value)) {
479
+ value.forEach(point);
480
+ return;
481
+ }
482
+ const row = value;
483
+ for (const [key, v] of Object.entries(row)) {
484
+ const m = /^(.*?)(?:Id|_id)$/.exec(key);
485
+ const stem = m?.[1] ? m[1].replace(/([a-z0-9])([A-Z])/g, "$1_$2").split("_").filter(Boolean).at(-1).toLowerCase() : null;
486
+ const rows = stem ? records.get(stem) : void 0;
487
+ if (typeof v === "string" && rows && !rows.some((r) => r["id"] === v)) {
488
+ row[key] = rows[0]["id"];
489
+ changed = true;
490
+ } else point(v);
491
+ }
492
+ };
493
+ point(out);
494
+ return changed ? out : body;
495
+ }
496
+ var singular = (key) => key.toLowerCase().replace(/ies$/, "y").replace(/(?<!s)s$/, "");
1342
497
 
1343
498
  var DECLS = [
1344
499
  /^(\s*)(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/,
@@ -1614,9 +769,6 @@ function moduleFile(module, from, paths) {
1614
769
  return hits.length === 1 ? hits[0] : null;
1615
770
  }
1616
771
 
1617
- init_endpoint();
1618
- init_turns();
1619
-
1620
772
  var PROVIDER = new RegExp(
1621
773
  [
1622
774
  String.raw`['"](openai|groq-sdk|cohere-ai|ollama|litellm|@anthropic-ai\/[\w-]+|@google\/generative-ai|@mistralai\/[\w-]+|@aws-sdk\/client-bedrock-runtime)['"]`,
@@ -1627,8 +779,6 @@ var PROVIDER = new RegExp(
1627
779
  var NON_PRODUCT = /(^|\/)(__tests__|tests?|bench|benchmark|sims?|eval|evals|mocks?|fixtures?|scripts|examples?)(\/|_|-)|[._-](test|spec|bench|benchmark|mock|fixture)\.[a-z]+$|(^|\/)bench_/i;
1628
780
  var isProductPath = (path) => !NON_PRODUCT.test(path);
1629
781
 
1630
- init_request_cell();
1631
-
1632
782
  var emptyOwn = () => ({ typed: /* @__PURE__ */ new Map(), rows: [], factories: /* @__PURE__ */ new Map(), memo: /* @__PURE__ */ new Map() });
1633
783
  var uncommented = (text) => text.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:\\])\/\/[^\n]*/g, "$1");
1634
784
  var DECL = /^(?:export\s+)?(?:const|let|var)\s+[\w$]+\s*(?::\s*([\w$]+)\s*(\[\])?\s*)?=\s*(?=[[{])|^export\s+default\s+(?=[[{])/gm;
@@ -2823,6 +1973,32 @@ function lineStart(text, line) {
2823
1973
  return at;
2824
1974
  }
2825
1975
 
1976
+ var VERB = "GET|POST|PUT|PATCH|DELETE|Get|Post|Put|Patch|Delete|get|post|put|patch|delete";
1977
+ var PATTERNS = [
1978
+ { file: /\.go$/, line: /\.Handle(?:Func)?\(\s*"(?:(GET|POST|PUT|PATCH|DELETE)\s+)?(\/[^"]*)"/, method: 1, path: 2 },
1979
+ { file: /\.go$/, line: new RegExp(`\\.(${VERB})\\(\\s*"(\\/[^"]*)"`), method: 1, path: 2 },
1980
+ { file: /\.(?:java|kt|scala)$/, line: /@(Get|Post|Put|Patch|Delete)(?:Mapping)?\(\s*(?:(?:value|path)\s*=\s*)?\{?\s*"([^"]*)"/, method: 1, path: 2 },
1981
+ { file: /\.cs$/, line: /\.Map(Get|Post|Put|Patch|Delete)\(\s*@?"([^"]*)"/, method: 1, path: 2 },
1982
+ { file: /\.cs$/, line: /\[Http(Get|Post|Put|Patch|Delete)\(\s*"([^"]*)"/, method: 1, path: 2 },
1983
+ { file: /\.(?:rb|ex|exs)$/, line: /^\s*(get|post|put|patch|delete)\s*\(?\s*['"]([^'"\s]+)['"]/, method: 1, path: 2 },
1984
+ { file: /\.php$/, line: /Route::(get|post|put|patch|delete)\(\s*['"]([^'"]+)['"]/, method: 1, path: 2 },
1985
+ { file: /\.php$/, line: /\$\w+->(get|post|put|patch|delete)\(\s*['"](\/[^'"]*)['"]/, method: 1, path: 2 },
1986
+ { file: /\.php$/, line: /REQUEST_METHOD['"]\]\s*[!=]==?\s*['"](GET|POST|PUT|PATCH|DELETE)['"].*?[!=]==?\s*['"](\/[\w/.-]*)['"]/, method: 1, path: 2 },
1987
+ { file: /\.rs$/, line: /\.route\(\s*"([^"]+)"\s*,\s*(?:web::)?(get|post|put|patch|delete)\b/, method: 2, path: 1 },
1988
+ { file: /\.rs$/, line: /#\[(get|post|put|patch|delete)\(\s*"([^"]+)"/, method: 1, path: 2 }
1989
+ ];
1990
+ function frameworkRoute(path, line) {
1991
+ for (const p of PATTERNS) {
1992
+ if (!p.file.test(path)) continue;
1993
+ const m = p.line.exec(line);
1994
+ if (!m) continue;
1995
+ const route = m[p.path];
1996
+ if (/^https?:/.test(route)) continue;
1997
+ return [m[0], (p.method && m[p.method] ? m[p.method] : "ALL").toUpperCase(), route.startsWith("/") ? route : `/${route}`];
1998
+ }
1999
+ return null;
2000
+ }
2001
+
2826
2002
  function fileRoutes(path, content) {
2827
2003
  const matched = /(?:^|\/)src\/routes\/(.*?)\+server\.[cm]?[jt]s$/.exec(path);
2828
2004
  if (!matched) return [];
@@ -2964,7 +2140,57 @@ function joinPath(importer, spec) {
2964
2140
  return parts.join("/");
2965
2141
  }
2966
2142
 
2967
- init_raw_node_routes();
2143
+ var MEMBER_COMPARE = /\b([A-Za-z_$][\w$]*)\s*\.\s*(method|url)\s*(?:===|==|!==|!=)\s*(["'`])([^"'`\n]+)\3/g;
2144
+ var REVERSE_COMPARE = /(["'`])([^"'`\n]+)\1\s*(?:===|==|!==|!=)\s*\b([A-Za-z_$][\w$]*)\s*\.\s*(method|url)/g;
2145
+ var HTTP_METHOD = /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/i;
2146
+ var REQUEST_NAME = /^(?:req(?:uest)?|httpReq(?:uest)?|incomingReq(?:uest)?)$/i;
2147
+ function lineAt(content, index) {
2148
+ let line = 1;
2149
+ for (let i = 0; i < index; i += 1) if (content.charCodeAt(i) === 10) line += 1;
2150
+ return line;
2151
+ }
2152
+ function commentedAt(content, index) {
2153
+ const start = content.lastIndexOf("\n", index - 1) + 1;
2154
+ return /^\s*(?:\/\/|\*|#)/.test(content.slice(start, index));
2155
+ }
2156
+ function comparisons(content) {
2157
+ const found = [];
2158
+ MEMBER_COMPARE.lastIndex = 0;
2159
+ for (const match of content.matchAll(MEMBER_COMPARE)) {
2160
+ if (commentedAt(content, match.index)) continue;
2161
+ found.push({ request: match[1], member: match[2], value: match[4], index: match.index, line: lineAt(content, match.index) });
2162
+ }
2163
+ REVERSE_COMPARE.lastIndex = 0;
2164
+ for (const match of content.matchAll(REVERSE_COMPARE)) {
2165
+ if (commentedAt(content, match.index)) continue;
2166
+ found.push({ request: match[3], member: match[4], value: match[2], index: match.index, line: lineAt(content, match.index) });
2167
+ }
2168
+ return found.sort((a, b) => a.index - b.index);
2169
+ }
2170
+ function sameGuard(content, left, right) {
2171
+ const start = Math.min(left.index, right.index);
2172
+ const end = Math.max(left.index, right.index);
2173
+ return !/[{};]/.test(content.slice(start, end)) && !/\b(?:if|switch|case|return)\s*\b/.test(content.slice(start + 1, end));
2174
+ }
2175
+ function rawNodeHttpRoutes(content) {
2176
+ if (typeof content !== "string" || !content.includes(".url") || !content.includes(".method")) return [];
2177
+ const facts = comparisons(content);
2178
+ const methods = facts.filter((fact) => fact.member === "method" && HTTP_METHOD.test(fact.value));
2179
+ const routes = [];
2180
+ const seen = /* @__PURE__ */ new Set();
2181
+ for (const url of facts) {
2182
+ if (url.member !== "url" || !url.value.startsWith("/") || url.value.length > 120 || url.value.includes("${")) continue;
2183
+ if (!REQUEST_NAME.test(url.request)) continue;
2184
+ const method = methods.filter((candidate) => candidate.request === url.request && Math.abs(candidate.line - url.line) <= 8 && sameGuard(content, candidate, url)).sort((a, b) => Math.abs(a.index - url.index) - Math.abs(b.index - url.index))[0];
2185
+ if (!method) continue;
2186
+ const key = `${method.value.toUpperCase()} ${url.value}`;
2187
+ if (seen.has(key)) continue;
2188
+ seen.add(key);
2189
+ routes.push({ method: method.value.toUpperCase(), path: url.value, line: url.line });
2190
+ }
2191
+ return routes;
2192
+ }
2193
+
2968
2194
  var HTTP = /\b(?:app|router|api|server|r)\s*\.\s*(get|post|put|patch|delete|all|ws)\s*\(\s*[`'"]([^`'"]+)/;
2969
2195
  var CALLBACK_HTTP = /^[ \t]*[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*\s*\.\s*(get|post|put|patch|delete|all|ws)\s*\(\s*(["'`])([^"'`\n]+)\2\s*,\s*(?:async\s+)?(?:function\s*\w*\s*\(|\([^)]*\)\s*=>|[A-Za-z_$][\w$]*\s*=>)/;
2970
2196
  function callbackRoute(lines, at) {
@@ -3058,8 +2284,9 @@ var FAMILIES = [
3058
2284
  { test: /llama[\w.-]*/g, provider: "Meta" }
3059
2285
  ];
3060
2286
  var PROSE_TOOL = /(?:\[?CMD:?\s*)?\{?\s*["']tool["']\s*:\s*["']([a-z_]{3,40})["']|\[TOOL_CALL:\s*([a-z_]{3,40})\]|^\s*[-*]?\s*`?([a-z_]{3,40})`?\s*[—:-]\s+(?:use|call|when|emits?|sends?|sets?|creates?)\b/i;
3061
- var JSON_TOOL = /["']?name["']?\s*:\s*["']([a-z][a-z0-9_]{2,40})["']/;
3062
- var FN_OPEN = /\bfunction["']?\s*:\s*\{/;
2287
+ var KEY_OF = (key) => String.raw`(?:["']?${key}["']?\s*(?::|=>)|["']${key}["']\s*(?:,|\bto\b))\s*`;
2288
+ var JSON_TOOL = new RegExp(`${KEY_OF("name")}["']([a-z][a-z0-9_]{2,40})["']`);
2289
+ var FN_OPEN = new RegExp(String.raw`(?<![\w$])${KEY_OF("function")}(?:\{|\[|[\w.]*(?:of|Of)\s*\()`);
3063
2290
  var SDK_TOOL = /(?:\b([a-zA-Z_$][\w$]*)\s*:|(?<!\b(?:module|exports|this))\.\s*([a-zA-Z_$][\w$]*)\s*=(?!=)|\[\s*["']([\w-]+)["']\s*\]\s*=(?!=))\s*tool\s*\(/g;
3064
2291
  var DECORATOR = /^\s*@(?:tool|function_tool|mcp\.tool|server\.tool|agent\.tool)\b\s*\(?\s*["\x27]?([a-z][\w]*)?["\x27]?/;
3065
2292
  var PY_DEF2 = /^\s*(?:async\s+)?def\s+([a-z][\w]*)\s*\(/;
@@ -3069,7 +2296,7 @@ var RETURNS_NAME = /^\s*return\s+["\x27]([a-z][\w]*)["\x27]\s*$/;
3069
2296
  var TOOL_FILE = /BaseTool|crewai[._]tools|langchain[._]tools|llama_index\.core\.tools|from mcp\b|@tool\b/;
3070
2297
  var MANIFEST_ID = /"(?:pluginKey|plugin_key|toolId|tool_id|tool_name|slug)"\s*:\s*"([a-z][a-z0-9_-]{2,40})"/g;
3071
2298
  var REGISTERED = /\b(?:server|mcp|agent|registry)\.tool\s*\(\s*["\x27]([a-z][\w]*)["\x27]/g;
3072
- var FN_NAME = /["']?name["']?\s*:\s*["']([a-z_][a-z0-9_]{1,40})["']/;
2299
+ var FN_NAME = new RegExp(`${KEY_OF("name")}["']([a-z_][a-z0-9_]{1,40})["']`);
3073
2300
  var PY_CALLABLE_DEF = /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/;
3074
2301
  var TS_ENUM = /^\s*(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_]\w*)\s*\{/;
3075
2302
  var TS_ENUM_MEMBER = /^\s*([A-Za-z_]\w*)\s*=\s*["'`]([\w.-]{2,60})["'`]/;
@@ -3087,7 +2314,7 @@ function boundNames(content) {
3087
2314
  return names;
3088
2315
  }
3089
2316
  var PY_REGISTRY = /\b(?:bind_tools|ToolNode|create_react_agent)\s*\([^\n]*?\[([^\]\n]*)\]|\b(?:TOOLS?|tools)\s*(?::[^=\n]+)?=\s*\[([^\]\n]*)\]/;
3090
- var TOOL_BLOCK = /\btools?\s*[:=]\s*\[|["']?type["']?\s*:\s*["']function["']|function_declarations|tool_definitions|ACTIONS\s*[:=]|_TOOLS?\s*=\s*[[{]|\b(?:TOOLS?|SKILLS?|FUNCTIONS?|AGENT_TOOLS|TOOL_REGISTRY|AVAILABLE_TOOLS)\s*=\s*[[{]/;
2317
+ var TOOL_BLOCK = new RegExp(String.raw`\btools?\s*[:=]\s*\[|${KEY_OF("type")}["']function["']|function_declarations|tool_definitions|ACTIONS\s*[:=]|_TOOLS?\s*=\s*[[{]|\b(?:TOOLS?|SKILLS?|FUNCTIONS?|AGENT_TOOLS|TOOL_REGISTRY|AVAILABLE_TOOLS)\s*=\s*[[{]`);
3091
2318
  var CLI_MODULE = /@click\.command|@app\.cli|@cli\.command|\.command\s*\(\s*\)|\badd_command\s*\(|\bimport click\b|\bfrom click\b|argparse\.ArgumentParser/;
3092
2319
  var PY_ACTION_SET = /^\s*["']([a-z_]{3,40})["']\s*,?\s*(?:#.*)?$/;
3093
2320
  var EMPHASIS = /(NON-?NEGOTIABLE|FORBIDDEN|NEVER|MUST NOT|DO NOT|ALWAYS|CRITICAL|🚨|⛔|MANDATORY|REQUIRED)/;
@@ -3201,11 +2428,11 @@ var submodule = (imp) => imp.name ? `${imp.module}${imp.module.endsWith(".") ? "
3201
2428
  function applyPythonMounts(out, paths) {
3202
2429
  const py = out.python;
3203
2430
  if (py.routeOf.size === 0) return;
3204
- const moduleFile3 = pyModules(paths);
2431
+ const moduleFile2 = pyModules(paths);
3205
2432
  const routerAt = (file, name, hops = 0) => {
3206
2433
  if (py.routers.get(file)?.has(name)) return [file, name];
3207
2434
  const imp = py.imports.get(file)?.get(name);
3208
- const from = imp && hops < 4 ? moduleFile3(imp.module, file) : void 0;
2435
+ const from = imp && hops < 4 ? moduleFile2(imp.module, file) : void 0;
3209
2436
  return from && imp.name ? routerAt(from, imp.name, hops + 1) : void 0;
3210
2437
  };
3211
2438
  const included = (file, target) => {
@@ -3213,7 +2440,7 @@ function applyPythonMounts(out, paths) {
3213
2440
  if (dot < 0) return routerAt(file, target);
3214
2441
  const head = target.slice(0, dot);
3215
2442
  const imp = head.includes(".") ? void 0 : py.imports.get(file)?.get(head);
3216
- const module = moduleFile3(imp ? submodule(imp) : head, file);
2443
+ const module = moduleFile2(imp ? submodule(imp) : head, file);
3217
2444
  return module ? routerAt(module, target.slice(dot + 1)) : void 0;
3218
2445
  };
3219
2446
  const includedAs = /* @__PURE__ */ new Map();
@@ -3399,7 +2626,7 @@ function scanFile(path, content, out) {
3399
2626
  openMethod = (WRAPPED_METHOD.exec(line) ?? (chained ? CHAIN_OPEN.exec(line) : null))?.[1]?.toUpperCase() ?? "";
3400
2627
  const flask = FLASK_ROUTE.exec(line);
3401
2628
  const js = HTTP.exec(line) ?? (chained ? CHAIN_ROUTE.exec(line) ?? callbackRoute(lines, i) : null);
3402
- const http = js ?? FASTAPI.exec(line) ?? (flask ? [flask[0], flaskMethod(lines, i, flask[3]), flask[2]] : null);
2629
+ const http = js ?? FASTAPI.exec(line) ?? (flask ? [flask[0], flaskMethod(lines, i, flask[3]), flask[2]] : null) ?? frameworkRoute(path, line);
3403
2630
  if (http) {
3404
2631
  const socket = /^(?:websocket|ws)$/i.test(http[1]);
3405
2632
  const at = js ? underOwn(ownPrefix, http[2]) : { route: http[2] };
@@ -3542,7 +2769,7 @@ var QUOTED = /["'`]([^"'`\n]{2,80})["'`]/g;
3542
2769
  var SCHEME = /^\w+:\/\//;
3543
2770
  var REF = /^([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)$/;
3544
2771
  var GENERIC = /* @__PURE__ */ new Set(["model", "engine", "deployment", "name", "id", "self", "config", "options"]);
3545
- var SLOT2 = /^(?:model|modelId|model_id|model_name|modelName|deployment|deployment_name|deploymentName|engine)$/;
2772
+ var SLOT = /^(?:model|modelId|model_id|model_name|modelName|deployment|deployment_name|deploymentName|engine)$/;
3546
2773
  var ID_FIELD = /model[_-]?id/i;
3547
2774
  var NOT_A_NAME = /* @__PURE__ */ new Set(["true", "false", "null", "undefined", "None", "True", "False", "this", "cls", "string", "str", "number", "any", "unknown"]);
3548
2775
  function wrappedRefs(span) {
@@ -3552,7 +2779,7 @@ function wrappedRefs(span) {
3552
2779
  for (let peeled = 0; value !== null && !REF.test(value) && peeled < 4; peeled++) value = calledWith(value);
3553
2780
  const path = value === null ? void 0 : REF.exec(value)?.[1];
3554
2781
  const name = path?.slice(path.lastIndexOf(".") + 1) ?? "";
3555
- if (path && !GENERIC.has(name) && !(path.includes(".") && SLOT2.test(name)) && !NOT_A_NAME.has(path)) out.push(path);
2782
+ if (path && !GENERIC.has(name) && !(path.includes(".") && SLOT.test(name)) && !NOT_A_NAME.has(path)) out.push(path);
3556
2783
  }
3557
2784
  return out;
3558
2785
  }
@@ -3705,7 +2932,7 @@ function readModels(line, idIsModel = false) {
3705
2932
  const path = REF.exec(span.trim())?.[1];
3706
2933
  if (path) {
3707
2934
  const name = path.slice(path.lastIndexOf(".") + 1);
3708
- const copy = path.includes(".") && SLOT2.test(name);
2935
+ const copy = path.includes(".") && SLOT.test(name);
3709
2936
  if (!GENERIC.has(name) && !NOT_A_NAME.has(path)) refs.push({ ref: path, env: ENV_HOLDER.test(path), at: match.index, ...copy ? { copy } : {} });
3710
2937
  continue;
3711
2938
  }
@@ -4329,8 +3556,6 @@ function routesFor(index, path, fn) {
4329
3556
  return { named, module: named.length ? [] : (index.byModule.get(dir) ?? []).slice(0, 8) };
4330
3557
  }
4331
3558
 
4332
- init_endpoint();
4333
-
4334
3559
  import { existsSync, readFileSync } from "node:fs";
4335
3560
  import { createRequire } from "node:module";
4336
3561
  import { join } from "node:path";
@@ -4356,7 +3581,7 @@ function loadCompiler() {
4356
3581
  var JS = /\.(?:[cm]?[jt]sx?)$/;
4357
3582
  var EXT = /\.([cm]?[jt]sx?)$/;
4358
3583
  var MOST_FILES = 6e3;
4359
- var keyOf2 = (node) => `${node.path}:${node.span.line}`;
3584
+ var keyOf = (node) => `${node.path}:${node.span.line}`;
4360
3585
  function tsGraph(read, paths) {
4361
3586
  const files = paths.filter((p) => JS.test(p) && !/\.d\.ts$/.test(p) && !/(^|\/)node_modules\//.test(p));
4362
3587
  if (!files.length || files.length > MOST_FILES) return null;
@@ -4444,13 +3669,13 @@ function tsGraph(read, paths) {
4444
3669
  const inner = enclosing(spansOf2(p), line).at(-1);
4445
3670
  if (!inner) return null;
4446
3671
  const node = { path: p, span: inner };
4447
- const k = keyOf2(node);
3672
+ const k = keyOf(node);
4448
3673
  if (!nodes.has(k)) nodes.set(k, node);
4449
3674
  return nodes.get(k);
4450
3675
  };
4451
3676
  const edges = /* @__PURE__ */ new Map();
4452
3677
  const link = (from, to) => {
4453
- const a = keyOf2(from), b = keyOf2(to);
3678
+ const a = keyOf(from), b = keyOf(to);
4454
3679
  if (a === b) return;
4455
3680
  if (!edges.has(a)) edges.set(a, /* @__PURE__ */ new Set());
4456
3681
  edges.get(a).add(b);
@@ -4483,7 +3708,7 @@ function tsGraph(read, paths) {
4483
3708
  visit(sf);
4484
3709
  }
4485
3710
  const within = (path, span) => [...nodes.values()].filter((n) => n.path === path && n.span.line >= span.line && n.span.end <= span.end);
4486
- return { edges, nodes, key: keyOf2, within };
3711
+ return { edges, nodes, key: keyOf, within };
4487
3712
  }
4488
3713
  function reachBy(graph, start, isEnd, hops = 8) {
4489
3714
  const first = graph.within(start.path, start.span);
@@ -4510,7 +3735,7 @@ import { fileURLToPath } from "node:url";
4510
3735
  var PY = /\.py$/;
4511
3736
  var MOST_FILES2 = 6e3;
4512
3737
  var SCRIPT = fileURLToPath(new URL("./py/pyreach.py", import.meta.url));
4513
- var keyOf3 = (node) => `${node.path}:${node.span.line}`;
3738
+ var keyOf2 = (node) => `${node.path}:${node.span.line}`;
4514
3739
  function pyGraph(read, paths) {
4515
3740
  const files = paths.filter((p) => PY.test(p) && !/(^|\/)(?:node_modules|\.venv|venv|site-packages)\//.test(p));
4516
3741
  if (!files.length || files.length > MOST_FILES2) return null;
@@ -4537,7 +3762,7 @@ function pyGraph(read, paths) {
4537
3762
  const inner = enclosing(spansOf2(p), line).at(-1);
4538
3763
  if (!inner) return null;
4539
3764
  const node = { path: p, span: inner };
4540
- const k = keyOf3(node);
3765
+ const k = keyOf2(node);
4541
3766
  if (!nodes.has(k)) nodes.set(k, node);
4542
3767
  return nodes.get(k);
4543
3768
  };
@@ -4545,13 +3770,13 @@ function pyGraph(read, paths) {
4545
3770
  for (const e of parsed.edges) {
4546
3771
  const from = nodeAt(e.from.path, e.from.line), to = nodeAt(e.to.path, e.to.line);
4547
3772
  if (!from || !to) continue;
4548
- const a = keyOf3(from), b = keyOf3(to);
3773
+ const a = keyOf2(from), b = keyOf2(to);
4549
3774
  if (a === b) continue;
4550
3775
  if (!edges.has(a)) edges.set(a, /* @__PURE__ */ new Set());
4551
3776
  edges.get(a).add(b);
4552
3777
  }
4553
3778
  const within = (path, span) => [...nodes.values()].filter((n) => n.path === path && n.span.line >= span.line && n.span.end <= span.end);
4554
- return { edges, nodes, key: keyOf3, within };
3779
+ return { edges, nodes, key: keyOf2, within };
4555
3780
  }
4556
3781
 
4557
3782
  var TOOLING2 = /(^|\/)(tests?|__tests__|bench(mark)?s?|evals?|examples?|scripts?|docs?|node_modules)(\/|$)/;
@@ -5026,7 +4251,7 @@ function clientCalls(files) {
5026
4251
  return [...byDoor.values()];
5027
4252
  }
5028
4253
 
5029
- var RULES3 = [
4254
+ var RULES2 = [
5030
4255
  { shape: "websocket", re: /@\w+\.websocket\(|\.websocket\(|websocket\.accept\(|new WebSocketServer\b|\bwss?\.on\(\s*["']connection|handleUpgrade\(|\bws\.send\(/ },
5031
4256
  { shape: "sse", re: /["']Content-Type["']\s*[:,]\s*["']text\/event-stream|media_type\s*=\s*["']text\/event-stream|EventSourceResponse\(|\.write\(\s*`(?:data|event):|yield\s+f?["'](?:data|event):|`data: \$\{JSON\.stringify/ },
5032
4257
  { shape: "openai", re: /to_openai_sse_stream|OpenAICompletion\b|chat\.completion(?:\.chunk)?["']|choices\s*[:=]\s*\[\s*\{|["']choices["']\s*:\s*\[/ },
@@ -5065,7 +4290,7 @@ function replyShapesOf(index, read) {
5065
4290
  function shapeOfChain(route, chain, routes, read) {
5066
4291
  const ordered = [...chain.filter((r) => r.start), ...chain.filter((r) => !r.start)];
5067
4292
  const texts = ordered.map((r) => ({ path: r.path, from: r.span.line, lines: read(r.path).slice(r.span.line - 1, r.span.end), start: Boolean(r.start) }));
5068
- const hits = texts.map(classify2).filter((h) => h !== null);
4293
+ const hits = texts.map(classify).filter((h) => h !== null);
5069
4294
  if (!hits.length) return null;
5070
4295
  const hit = hits.reduce((a, b) => b.rank < a.rank ? b : a);
5071
4296
  const joined = texts.map((t) => t.lines.join("\n")).join("\n");
@@ -5090,14 +4315,14 @@ function shapeOfChain(route, chain, routes, read) {
5090
4315
  }
5091
4316
  return found;
5092
4317
  }
5093
- function classify2(text) {
4318
+ function classify(text) {
5094
4319
  let best = null;
5095
4320
  text.lines.forEach((line, i) => {
5096
4321
  if (GUARD.test(line) || !text.start && RETURNS.test(line)) return;
5097
- const rank = RULES3.findIndex((r) => r.re.test(line));
4322
+ const rank = RULES2.findIndex((r) => r.re.test(line));
5098
4323
  if (rank < 0 || best && best.rank <= rank) return;
5099
4324
  const contentType = /["']((?:audio|image|video)\/[\w.+-]+|application\/(?:pdf|octet-stream))["']/.exec(line)?.[1];
5100
- best = { shape: RULES3[rank].shape, rank, evidence: `${text.path}:${text.from + i}: ${line.trim().slice(0, 140)}`, text, ...contentType ? { contentType } : {} };
4325
+ best = { shape: RULES2[rank].shape, rank, evidence: `${text.path}:${text.from + i}: ${line.trim().slice(0, 140)}`, text, ...contentType ? { contentType } : {} };
5101
4326
  });
5102
4327
  return best;
5103
4328
  }
@@ -5426,6 +4651,9 @@ function callName(source, scope) {
5426
4651
 
5427
4652
  var literalName = ({ name, owner }) => name ? owner ? `${owner}.${name}` : name : null;
5428
4653
  var MIN_TEXT = 60;
4654
+ var SPANNING_QUOTES = /\.(?:rs|php|rb|ex|exs|pl|sh)$/i;
4655
+ var HEREDOC_FILE = /\.(?:rb|php|pl|sh)$/i;
4656
+ var HEREDOC = /^<<(?:<|[~-])?(["']?)([A-Z_][A-Z0-9_]*)\1/;
5429
4657
  function findLiterals(source, path = "") {
5430
4658
  const out = [];
5431
4659
  const fields = [];
@@ -5435,6 +4663,9 @@ function findLiterals(source, path = "") {
5435
4663
  let line = 1;
5436
4664
  const inJsLike = /\.(ts|tsx|js|jsx|mjs|cjs|mts|cts)$/i.test(path);
5437
4665
  const python = !inJsLike;
4666
+ const spans = SPANNING_QUOTES.test(path);
4667
+ const rust = /\.rs$/i.test(path);
4668
+ const heredocs = HEREDOC_FILE.test(path);
5438
4669
  const at = (offset) => source.slice(i, i + offset);
5439
4670
  while (i < source.length) {
5440
4671
  const ch = source[i];
@@ -5443,6 +4674,39 @@ function findLiterals(source, path = "") {
5443
4674
  i++;
5444
4675
  continue;
5445
4676
  }
4677
+ if (rust && ch === "r" && !/[\w$]/.test(source[i - 1] ?? "")) {
4678
+ const raw = /^r(#*)"/.exec(at(12));
4679
+ if (raw) {
4680
+ const close = source.indexOf(`"${raw[1]}`, i + raw[0].length);
4681
+ if (close > 0) {
4682
+ const text = source.slice(i + raw[0].length, close);
4683
+ const assigned = assignedName(source, i, openers, python);
4684
+ out.push({ line, text, kind: "template", ...assigned.name ? { name: assigned.name } : {} });
4685
+ line += text.split("\n").length - 1;
4686
+ i = close + 1 + raw[1].length;
4687
+ continue;
4688
+ }
4689
+ }
4690
+ }
4691
+ if (rust && ch === "'") {
4692
+ i += /^'(?:\\.|[^\\'\n])'/.exec(at(12))?.[0].length ?? 1;
4693
+ continue;
4694
+ }
4695
+ if (heredocs && ch === "<") {
4696
+ const doc = HEREDOC.exec(at(80));
4697
+ if (doc) {
4698
+ const body = source.indexOf("\n", i) + 1;
4699
+ const end = body > 0 ? new RegExp(`^[ \\t]*${doc[2]}\\b`, "m").exec(source.slice(body)) : null;
4700
+ if (end) {
4701
+ const text = source.slice(body, body + end.index);
4702
+ const assigned = assignedName(source, i, openers, python);
4703
+ out.push({ line: line + 1, text, kind: "template", ...assigned.name ? { name: assigned.name } : {} });
4704
+ line += text.split("\n").length;
4705
+ i = body + end.index + end[0].length;
4706
+ continue;
4707
+ }
4708
+ }
4709
+ }
5446
4710
  if (at(2) === "//") {
5447
4711
  while (i < source.length && source[i] !== "\n") i++;
5448
4712
  continue;
@@ -5461,13 +4725,13 @@ function findLiterals(source, path = "") {
5461
4725
  continue;
5462
4726
  }
5463
4727
  if (at(3) === '"""' || at(3) === "'''") {
5464
- const fence2 = at(3);
4728
+ const fence = at(3);
5465
4729
  const startLine = line;
5466
4730
  const direct = fieldBefore(source.slice(Math.max(0, i - 120), i), python);
5467
4731
  const field = direct ? { name: direct.name, scope: stack } : wrapperField(source, stack, python);
5468
4732
  i += 3;
5469
4733
  const from = i;
5470
- while (i < source.length && at(3) !== fence2) {
4734
+ while (i < source.length && at(3) !== fence) {
5471
4735
  if (source[i] === "\n") line++;
5472
4736
  i++;
5473
4737
  }
@@ -5490,7 +4754,7 @@ function findLiterals(source, path = "") {
5490
4754
  const field = assigned.field ? { name: assigned.name, scope: stack } : assigned.name ? void 0 : wrapperField(source, stack, python);
5491
4755
  const name = assigned.name ?? field?.name;
5492
4756
  const owner = assigned.owner;
5493
- const first = readLiteral(source, i, ch);
4757
+ const first = readLiteral(source, i, ch, spans);
5494
4758
  if (first) {
5495
4759
  let { text, end } = first;
5496
4760
  line += first.newlines;
@@ -5589,7 +4853,7 @@ function skipRegex(source, start) {
5589
4853
  }
5590
4854
  return start;
5591
4855
  }
5592
- function readLiteral(source, start, quote) {
4856
+ function readLiteral(source, start, quote, spans = false) {
5593
4857
  let i = start + 1;
5594
4858
  let newlines = 0;
5595
4859
  const parts = [];
@@ -5602,7 +4866,7 @@ function readLiteral(source, start, quote) {
5602
4866
  continue;
5603
4867
  }
5604
4868
  if (ch === "\n") {
5605
- if (quote !== "`") return null;
4869
+ if (quote !== "`" && !spans) return null;
5606
4870
  newlines++;
5607
4871
  i++;
5608
4872
  continue;
@@ -5828,8 +5092,9 @@ var SCAFFOLDING = /(^|\/)(bench|benchmarks?|sims?|tests?|__tests__|spec|specs|fi
5828
5092
  var DATA_FILE = /\.(?:json|jsonl|ndjson|csv|tsv|xml|html?|parquet|arrow|ipynb)$/i;
5829
5093
  var VENDORED = /(^|\/)(vendor|vendored|third[_-]?party|public|static|assets|dist|build|out|\.next|\.nuxt|generated|node_modules|\.venv|venv|site-packages|__pycache__|swagger|openapi)(\/|$)/i;
5830
5094
  var MINIFIED = /\.min\.(?:js|css|mjs)$/i;
5831
- function isScaffolding2(path) {
5832
- return SCAFFOLDING.test(path) || VENDORED.test(path) || MINIFIED.test(path);
5095
+ var DOCUMENT_ROOT = /(^|\/)(public|static)\/(?:[^/]+\/)*[^/]+\.php$/i;
5096
+ function isScaffolding(path) {
5097
+ return SCAFFOLDING.test(path) || VENDORED.test(path) && !(DOCUMENT_ROOT.test(path) && !/(^|\/)vendor\//.test(path)) || MINIFIED.test(path);
5833
5098
  }
5834
5099
  function looksMinified(text) {
5835
5100
  const lines = text.split("\n");
@@ -5890,8 +5155,8 @@ function searchPrompts(candidates, limit = 64) {
5890
5155
  }
5891
5156
  const own = found.filter((p) => !oneLiners.has(p) || !found.some((q) => q !== p && q.path === p.path && q.text.includes(p.text)));
5892
5157
  const ranked = own.sort((a, b) => b.score - a.score);
5893
- const reachable = ranked.filter((p) => p.uses > 0 && !isScaffolding2(p.path));
5894
- const scaffolding = ranked.filter((p) => p.uses > 0 && isScaffolding2(p.path));
5158
+ const reachable = ranked.filter((p) => p.uses > 0 && !isScaffolding(p.path));
5159
+ const scaffolding = ranked.filter((p) => p.uses > 0 && isScaffolding(p.path));
5895
5160
  const running = reachable.slice(0, limit);
5896
5161
  for (const prompt of running) {
5897
5162
  prompt.appends = appendedTo(read, prompt);
@@ -6220,7 +5485,7 @@ ${p.text.slice(0, PROMPT_HEAD)}${p.text.length > PROMPT_HEAD ? `
6220
5485
  const procedures = new Map(served2.entries.flatMap((e) => e.procedure ? [[`${e.method.toUpperCase()} ${e.route}`, e.procedure]] : []));
6221
5486
  const routes = /* @__PURE__ */ new Set([...Object.keys(bodies), ...Object.keys(fields), ...validated, ...shapes.keys(), ...conventionOf.keys(), ...procedures.keys()]);
6222
5487
  const edge = new Set(served2.entries.filter((e) => e.runtime === "edge").map((e) => `${e.method.toUpperCase()} ${e.route}`));
6223
- const contract2 = [...routes].sort().map((route) => {
5488
+ const contract = [...routes].sort().map((route) => {
6224
5489
  const shape2 = shapes.get(route);
6225
5490
  const conv = conventionOf.get(route);
6226
5491
  return {
@@ -6238,13 +5503,13 @@ ${p.text.slice(0, PROMPT_HEAD)}${p.text.length > PROMPT_HEAD ? `
6238
5503
  };
6239
5504
  });
6240
5505
  const named = (row) => [...Object.keys(row.body ?? {}).filter((k) => typeof row.body[k] === "string"), ...row.fields ?? []];
6241
- const values = ownValues(candidates.map((c) => ({ path: c.path, content: c.full ?? c.head })), new Set(contract2.flatMap(named)));
6242
- for (const row of contract2) {
5506
+ const values = ownValues(candidates.map((c) => ({ path: c.path, content: c.full ?? c.head })), new Set(contract.flatMap(named)));
5507
+ for (const row of contract) {
6243
5508
  const own = Object.fromEntries(named(row).filter((k) => values[k]).map((k) => [k, values[k]]));
6244
5509
  if (Object.keys(own).length) row.values = own;
6245
5510
  }
6246
5511
  const toolServers = toolServersOf(reaching, read, calls).filter((route) => !reached.some((r) => r.route === route));
6247
- return { text, chars: text.length, sites: calls.length, prompts: prompts.length, calls: siteList, doors, aiRoutes, bodies, fields, validated, requests, contract: contract2, toolServers };
5512
+ return { text, chars: text.length, sites: calls.length, prompts: prompts.length, calls: siteList, doors, aiRoutes, bodies, fields, validated, requests, contract, toolServers };
6248
5513
  }
6249
5514
  var LISTS_TOOLS = /ListToolsRequestSchema|["'`]tools\/list["'`]|\blist_tools\b/;
6250
5515
  var CALLS_TOOLS = /CallToolRequestSchema|ToolCallRequestSchema|["'`]tools\/call["'`]|\bcall_tool\b/;
@@ -6352,11 +5617,13 @@ var WRITER = `You read one file of an AI product's own evaluation suite and list
6352
5617
  - "turns": the case's later user messages in order, VERBATIM; [] when there is one message.
6353
5618
  - "reference": the author's expected answer or rubric for the reply when the file gives one in words, VERBATIM; "" when none.
6354
5619
  - "must": phrases the file says the reply must contain, VERBATIM; [] when none. "mustAny": groups of phrases of which one must appear, each group a list; [] when none. "mustNot": phrases the file says must not appear; [] when none.
5620
+ - Phrases go in "must", "mustAny" or "mustNot" only when the file's own check passes or fails a case on them. A list the file only counts or prints for a person to judge (a coverage share, hits shown beside the answer) goes in none of them.
5621
+ - "least": how many of "must" the file's check needs in the reply to pass the case. The number of "must" when it needs every one; fewer when it passes on some of them (half of them rounded up, any one).
6355
5622
  - "refuse": true only when the file says the reply should refuse, decline or ask for clarification instead of answering.
6356
5623
  - "store": the collection, index, dataset, grade or namespace this case is sent under, when the file names one on the case (collectionId, gradePrefix, namespace), VERBATIM; "" when none.
6357
5624
  - "endpoint": the path the file posts its cases to ("/v1/chat") when the file says; "" when it does not.
6358
5625
  - At most ${CASES_PER_FILE} cases, in the file's order.
6359
- Return ONLY JSON: {"endpoint":"","cases":[{"id":"","input":"","turns":[],"reference":"","must":[],"mustAny":[],"mustNot":[],"refuse":false,"store":""}]}`;
5626
+ Return ONLY JSON: {"endpoint":"","cases":[{"id":"","input":"","turns":[],"reference":"","must":[],"least":0,"mustAny":[],"mustNot":[],"refuse":false,"store":""}]}`;
6360
5627
 
6361
5628
  var TEST = /(?:^|\/)(?:tests?|__tests__|spec|e2e)\/|[._-](?:test|spec)\.[^/]+$|(?:^|\/)(?:test_[^/]+|conftest)\.py$/;
6362
5629
  var README = /(?:^|\/)(?:readme|contributing|architecture|design)[^/]*\.(?:md|mdx|txt|rst)$|^docs\/[^/]+\.(?:md|mdx)$/i;