@cortad/connector 0.6.2 → 0.6.3
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/behavior-text.mjs +239 -0
- package/lib/cli.mjs +22 -2
- package/lib/dissection-text.mjs +1 -1
- package/lib/doors.mjs +61 -38
- package/lib/history/contract.mjs +29 -13
- package/lib/history/sources.mjs +11 -0
- package/lib/intelligence-text.mjs +39 -16
- package/lib/intelligence.mjs +38 -14
- package/lib/layer.mjs +293 -1026
- package/lib/mcp.mjs +4 -4
- package/lib/message-text.cjs +48 -0
- package/lib/proxy.mjs +25 -0
- package/lib/replay.mjs +3 -1
- package/lib/setup-live-text.mjs +19 -0
- package/lib/setup-live.mjs +35 -0
- package/lib/start.mjs +22 -0
- package/lib/text.mjs +1 -1
- package/lib/trace.cjs +7 -1
- package/lib/verbs.mjs +38 -7
- package/lib/wire.cjs +10 -2
- package/local.mjs +25 -11
- package/package.json +6 -1
- package/skill/SKILL.md +46 -71
- package/skill/references/results.md +54 -8
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
|
-
|
|
1341
|
-
|
|
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
|
-
|
|
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
|
|
3062
|
-
var
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ?
|
|
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 =
|
|
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
|
|
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(".") &&
|
|
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(".") &&
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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) !==
|
|
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
|
-
|
|
5832
|
-
|
|
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 && !
|
|
5894
|
-
const scaffolding = ranked.filter((p) => p.uses > 0 &&
|
|
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
|
|
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(
|
|
6242
|
-
for (const row of
|
|
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
|
|
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;
|