@pome-sh/cli 0.21.13 → 0.21.15
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/dist/build-info.json +3 -3
- package/dist/{checks-MM2OJJES.js → checks-OFV75GDL.js} +1 -1
- package/dist/chunk-4MQULI7E.js +427 -0
- package/dist/{chunk-LH4EGGQW.js → chunk-6OT4IW25.js} +18 -2
- package/dist/{chunk-BV35ZSB6.js → chunk-BA4N7AOZ.js} +12 -12
- package/dist/{chunk-M7HRXBVU.js → chunk-F4RV373I.js} +2 -2
- package/dist/{chunk-UCLRWEKB.js → chunk-GDZ6Z3B2.js} +1 -1
- package/dist/{chunk-CC27LWIA.js → chunk-IYDTDANH.js} +1 -1
- package/dist/{chunk-NY55QTVQ.js → chunk-JWJYNAWI.js} +7 -1
- package/dist/{chunk-2TSYKRER.js → chunk-OQWXMHRG.js} +1 -1
- package/dist/{chunk-EU6SGN7A.js → chunk-SE44FKWH.js} +7 -2
- package/dist/{chunk-3QVSXFJI.js → chunk-XBT6ZLBG.js} +1 -1
- package/dist/{runDemo-4ASIOIMV.js → runDemo-ELZN7OT2.js} +4 -4
- package/dist/{runTrialGroup-UQKE6XQF.js → runTrialGroup-WEBKLT7T.js} +3 -3
- package/dist/src/cli/main.js +16 -16
- package/dist/{src-YH33B2LP.js → src-566DJYOC.js} +441 -339
- package/dist/{src-JWB5RQSN.js → src-ECRGORXR.js} +139 -44
- package/dist/{src-5RPY72NF.js → src-GIKWIJM2.js} +719 -269
- package/dist/{src-B63VPRRZ.js → src-PUE6CMDF.js} +649 -433
- package/dist/{src-RUUCDQU4.js → src-V5JLDT6T.js} +371 -195
- package/dist/twinHarness-QGMYTU5N.js +5 -0
- package/dist/{twinStart-TTVUZ7PC.js → twinStart-36ZSLZE7.js} +2 -2
- package/package.json +1 -1
- package/dist/twinHarness-BHSSYAEG.js +0 -5
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "pome-sh",
|
|
3
|
-
"version": "0.21.
|
|
4
|
-
"git_sha": "
|
|
5
|
-
"build_time": "2026-08-
|
|
3
|
+
"version": "0.21.15",
|
|
4
|
+
"git_sha": "38acaf971b6be8d5cdffaa62a9e077dbc0e54ff7",
|
|
5
|
+
"build_time": "2026-08-08T01:16:03.754Z"
|
|
6
6
|
}
|
|
@@ -183,7 +183,7 @@ async function checkTwinReachable(_configDir) {
|
|
|
183
183
|
});
|
|
184
184
|
let harness;
|
|
185
185
|
try {
|
|
186
|
-
const { bootTwin } = await import('./twinHarness-
|
|
186
|
+
const { bootTwin } = await import('./twinHarness-QGMYTU5N.js');
|
|
187
187
|
harness = await bootTwin({
|
|
188
188
|
twin: "github",
|
|
189
189
|
seedState: void 0,
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ../packages/sdk/dist/route-inputs.js
|
|
4
|
+
var INPUT_LOCATIONS = [
|
|
5
|
+
"path",
|
|
6
|
+
"query",
|
|
7
|
+
"header",
|
|
8
|
+
"body",
|
|
9
|
+
"argument"
|
|
10
|
+
];
|
|
11
|
+
var ROUTE_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "ALL"];
|
|
12
|
+
var BRACKETED = /* @__PURE__ */ Symbol.for("pome.routeInputs.bracketed");
|
|
13
|
+
function bracketedQuery(schema) {
|
|
14
|
+
const wrapper = { [BRACKETED]: true, schema };
|
|
15
|
+
return wrapper;
|
|
16
|
+
}
|
|
17
|
+
function isBracketed(value) {
|
|
18
|
+
return typeof value === "object" && value !== null && BRACKETED in value;
|
|
19
|
+
}
|
|
20
|
+
function unwrapBracketed(value) {
|
|
21
|
+
return isBracketed(value) ? value.schema : value;
|
|
22
|
+
}
|
|
23
|
+
function mountDeclaredRoute(router, declaration, handler) {
|
|
24
|
+
const verb = declaration.method.toLowerCase();
|
|
25
|
+
router[verb](declaration.path, handler);
|
|
26
|
+
}
|
|
27
|
+
var UndeclaredInputError = class extends Error {
|
|
28
|
+
location;
|
|
29
|
+
names;
|
|
30
|
+
surface;
|
|
31
|
+
constructor(location, names, surface) {
|
|
32
|
+
super(`${surface} does not declare ${location} parameter${names.length > 1 ? "s" : ""}: ` + names.map((name) => `\`${name}\``).join(", "));
|
|
33
|
+
this.location = location;
|
|
34
|
+
this.names = names;
|
|
35
|
+
this.surface = surface;
|
|
36
|
+
this.name = "UndeclaredInputError";
|
|
37
|
+
}
|
|
38
|
+
/** The first offending name — what a vendor envelope usually reports. */
|
|
39
|
+
get first() {
|
|
40
|
+
return this.names[0] ?? "";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
var MalformedBodyError = class extends Error {
|
|
44
|
+
surface;
|
|
45
|
+
constructor(surface) {
|
|
46
|
+
super(`${surface} could not parse the request body as JSON`);
|
|
47
|
+
this.surface = surface;
|
|
48
|
+
this.name = "MalformedBodyError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
function declareRouteInputs(spec) {
|
|
52
|
+
if (!ROUTE_METHODS.includes(spec.method)) {
|
|
53
|
+
throw new Error(`route-inputs: unknown method '${spec.method}' for ${spec.path}`);
|
|
54
|
+
}
|
|
55
|
+
if (!spec.path.startsWith("/")) {
|
|
56
|
+
throw new Error(`route-inputs: path must start with '/' (got '${spec.path}')`);
|
|
57
|
+
}
|
|
58
|
+
const surface = `${spec.method} ${spec.path}`;
|
|
59
|
+
const pathShape = spec.pathParams ?? {};
|
|
60
|
+
const queryShape = spec.query ?? {};
|
|
61
|
+
const headerShape = spec.headers ?? {};
|
|
62
|
+
const bodyShape = spec.body ?? {};
|
|
63
|
+
const bodyEncoding = spec.bodyEncoding ?? (Object.keys(bodyShape).length > 0 ? "json" : "none");
|
|
64
|
+
if (bodyEncoding === "none" && Object.keys(bodyShape).length > 0) {
|
|
65
|
+
throw new Error(`route-inputs: ${surface} declares body inputs with bodyEncoding 'none'`);
|
|
66
|
+
}
|
|
67
|
+
if (bodyEncoding === "media" && !spec.mediaField) {
|
|
68
|
+
throw new Error(`route-inputs: ${surface} uses bodyEncoding 'media' without mediaField`);
|
|
69
|
+
}
|
|
70
|
+
const { named, wildcard } = splitPathParams(surface, spec.path, Object.keys(pathShape));
|
|
71
|
+
const inputs = [];
|
|
72
|
+
for (const [location, shape] of [
|
|
73
|
+
["path", pathShape],
|
|
74
|
+
["query", queryShape],
|
|
75
|
+
["header", headerShape],
|
|
76
|
+
["body", bodyShape]
|
|
77
|
+
]) {
|
|
78
|
+
for (const [name, rawSchema] of Object.entries(shape)) {
|
|
79
|
+
const schema = unwrapBracketed(rawSchema);
|
|
80
|
+
inputs.push({
|
|
81
|
+
name,
|
|
82
|
+
location,
|
|
83
|
+
// Requiredness is asked of the validator rather than read off the
|
|
84
|
+
// schema's internals: "would this parse with the input absent" is the
|
|
85
|
+
// question the declaration answers, so the answer comes from the same
|
|
86
|
+
// code path that answers it at request time.
|
|
87
|
+
required: !schema.safeParse(void 0).success,
|
|
88
|
+
type: jsonSchemaTypeOf(schema)
|
|
89
|
+
});
|
|
90
|
+
if (isBracketed(rawSchema) && location !== "query") {
|
|
91
|
+
throw new Error(`route-inputs: bracketedQuery() is only valid for query inputs (${surface} '${name}')`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
inputs.sort((a, b) => INPUT_LOCATIONS.indexOf(a.location) - INPUT_LOCATIONS.indexOf(b.location) || a.name.localeCompare(b.name));
|
|
96
|
+
const declaredQuery = new Set(Object.keys(queryShape));
|
|
97
|
+
const bracketedQueryNames = new Set(Object.entries(queryShape).filter(([, schema]) => isBracketed(schema)).map(([name]) => name));
|
|
98
|
+
const declaredBody = new Set(Object.keys(bodyShape));
|
|
99
|
+
const arrayQuery = new Set(Object.entries(queryShape).filter(([, schema]) => jsonSchemaTypeOf(unwrapBracketed(schema)) === "array").map(([name]) => name));
|
|
100
|
+
const pathSchema = z.object(mapShape(pathShape));
|
|
101
|
+
const querySchema = z.object(mapShape(queryShape));
|
|
102
|
+
const headerSchema = z.object(mapShape(headerShape));
|
|
103
|
+
const bodySchema = z.object(mapShape(bodyShape));
|
|
104
|
+
return {
|
|
105
|
+
method: spec.method,
|
|
106
|
+
path: spec.path,
|
|
107
|
+
surface,
|
|
108
|
+
bodyEncoding,
|
|
109
|
+
inputs,
|
|
110
|
+
// Deduplicated: pome-cloud's comparator diffs NAME sets, and a name that
|
|
111
|
+
// arrives in two locations is still one name to it.
|
|
112
|
+
names: [...new Set(inputs.map((input) => input.name))],
|
|
113
|
+
async parse(request) {
|
|
114
|
+
const path = pathSchema.parse(readPathParams(request, named, wildcard, spec.path));
|
|
115
|
+
const searchParams = new URL(request.url, "http://twin.invalid").searchParams;
|
|
116
|
+
refuseUndeclared("query", surface, [...searchParams.keys()], declaredQuery, bracketedQueryNames);
|
|
117
|
+
const query = querySchema.parse(readQuery(searchParams, declaredQuery, bracketedQueryNames, arrayQuery));
|
|
118
|
+
const header = headerSchema.parse(Object.fromEntries(Object.keys(headerShape).map((name) => [name, request.header(name)])));
|
|
119
|
+
const raw = await decodeBody(request, bodyEncoding, surface, spec.mediaField);
|
|
120
|
+
refuseUndeclared("body", surface, Object.keys(raw), declaredBody, /* @__PURE__ */ new Set());
|
|
121
|
+
const body = bodySchema.parse(raw);
|
|
122
|
+
return { path, query, header, body };
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function mapShape(shape) {
|
|
127
|
+
return Object.fromEntries(Object.entries(shape).map(([name, schema]) => [name, unwrapBracketed(schema)]));
|
|
128
|
+
}
|
|
129
|
+
function splitPathParams(surface, pattern, declared) {
|
|
130
|
+
const patternNames = [...pattern.matchAll(/:([A-Za-z0-9_]+)/g)].map((match) => match[1]);
|
|
131
|
+
const wildcards = pattern.split("/").filter((segment) => segment === "*" || segment.endsWith("*"));
|
|
132
|
+
if (wildcards.length > 1) {
|
|
133
|
+
throw new Error(`route-inputs: ${surface} has more than one wildcard segment`);
|
|
134
|
+
}
|
|
135
|
+
const missing = patternNames.filter((name) => !declared.includes(name));
|
|
136
|
+
if (missing.length > 0) {
|
|
137
|
+
throw new Error(`route-inputs: ${surface} does not declare path param${missing.length > 1 ? "s" : ""} ` + missing.map((name) => `'${name}'`).join(", "));
|
|
138
|
+
}
|
|
139
|
+
const extra = declared.filter((name) => !patternNames.includes(name));
|
|
140
|
+
if (wildcards.length === 0) {
|
|
141
|
+
if (extra.length > 0) {
|
|
142
|
+
throw new Error(`route-inputs: ${surface} declares path param${extra.length > 1 ? "s" : ""} ${extra.map((name) => `'${name}'`).join(", ")} its pattern does not contain`);
|
|
143
|
+
}
|
|
144
|
+
return { named: patternNames, wildcard: null };
|
|
145
|
+
}
|
|
146
|
+
if (extra.length !== 1) {
|
|
147
|
+
throw new Error(`route-inputs: ${surface} has a wildcard segment, so exactly one declared path param must name it (found ${extra.length}: ${extra.map((name) => `'${name}'`).join(", ") || "none"})`);
|
|
148
|
+
}
|
|
149
|
+
return { named: patternNames, wildcard: extra[0] };
|
|
150
|
+
}
|
|
151
|
+
function readPathParams(request, named, wildcard, pattern) {
|
|
152
|
+
const out = {};
|
|
153
|
+
for (const name of named)
|
|
154
|
+
out[name] = request.param(name);
|
|
155
|
+
if (wildcard)
|
|
156
|
+
out[wildcard] = readWildcard(request, pattern, named, out);
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
function readWildcard(request, pattern, named, resolved) {
|
|
160
|
+
const pathname = new URL(request.url, "http://twin.invalid").pathname;
|
|
161
|
+
const prefix = pattern.slice(0, pattern.lastIndexOf("*")).replace(/:([A-Za-z0-9_]+)(?:\{[^}]*\})?/g, (whole, name) => named.includes(name) ? encodeURIComponent(resolved[name] ?? "") : whole);
|
|
162
|
+
const at = pathname.indexOf(prefix);
|
|
163
|
+
if (at < 0)
|
|
164
|
+
return void 0;
|
|
165
|
+
const tail = pathname.slice(at + prefix.length);
|
|
166
|
+
return tail.length > 0 ? safeDecode(tail) : void 0;
|
|
167
|
+
}
|
|
168
|
+
function safeDecode(value) {
|
|
169
|
+
try {
|
|
170
|
+
return decodeURIComponent(value);
|
|
171
|
+
} catch {
|
|
172
|
+
return value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function refuseUndeclared(location, surface, present, declared, bracketed) {
|
|
176
|
+
const offenders = [];
|
|
177
|
+
for (const key of present) {
|
|
178
|
+
if (declared.has(key))
|
|
179
|
+
continue;
|
|
180
|
+
const base = key.slice(0, key.indexOf("["));
|
|
181
|
+
if (key.includes("[") && bracketed.has(base))
|
|
182
|
+
continue;
|
|
183
|
+
if (!offenders.includes(key))
|
|
184
|
+
offenders.push(key);
|
|
185
|
+
}
|
|
186
|
+
if (offenders.length > 0)
|
|
187
|
+
throw new UndeclaredInputError(location, offenders, surface);
|
|
188
|
+
}
|
|
189
|
+
function readQuery(searchParams, declared, bracketed, arrays) {
|
|
190
|
+
const out = {};
|
|
191
|
+
for (const name of declared) {
|
|
192
|
+
if (arrays.has(name)) {
|
|
193
|
+
out[name] = searchParams.getAll(name);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (bracketed.has(name)) {
|
|
197
|
+
const nested = readBracketed(searchParams, name);
|
|
198
|
+
if (nested !== void 0)
|
|
199
|
+
out[name] = nested;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (searchParams.has(name))
|
|
203
|
+
out[name] = searchParams.get(name);
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
function readBracketed(searchParams, name) {
|
|
208
|
+
const flat = searchParams.get(name);
|
|
209
|
+
const nested = {};
|
|
210
|
+
for (const [key, value] of searchParams.entries()) {
|
|
211
|
+
if (!key.startsWith(`${name}[`) || !key.endsWith("]"))
|
|
212
|
+
continue;
|
|
213
|
+
const inner = key.slice(name.length + 1, -1);
|
|
214
|
+
if (inner.length > 0)
|
|
215
|
+
nested[inner] = value;
|
|
216
|
+
}
|
|
217
|
+
if (Object.keys(nested).length > 0)
|
|
218
|
+
return flat === null ? nested : { ...nested, value: flat };
|
|
219
|
+
return flat === null ? void 0 : flat;
|
|
220
|
+
}
|
|
221
|
+
async function decodeBody(request, encoding, surface, mediaField) {
|
|
222
|
+
if (encoding === "none")
|
|
223
|
+
return {};
|
|
224
|
+
const contentType = request.header("content-type") ?? "";
|
|
225
|
+
if (encoding === "json" || encoding === "json-optional") {
|
|
226
|
+
const value = await request.json().catch(() => void 0);
|
|
227
|
+
if (isPlainObject(value))
|
|
228
|
+
return value;
|
|
229
|
+
if (encoding === "json-optional")
|
|
230
|
+
return {};
|
|
231
|
+
throw new MalformedBodyError(surface);
|
|
232
|
+
}
|
|
233
|
+
if (encoding === "form") {
|
|
234
|
+
if (contentType.includes("application/json") || contentType === "") {
|
|
235
|
+
const value = await request.json().catch(() => void 0);
|
|
236
|
+
if (isPlainObject(value))
|
|
237
|
+
return value;
|
|
238
|
+
if (contentType.includes("application/json"))
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
241
|
+
const form = await request.parseBody({ all: true }).catch(() => void 0);
|
|
242
|
+
return form ? expandBrackets(form) : {};
|
|
243
|
+
}
|
|
244
|
+
return decodeMedia(request, contentType, surface, mediaField);
|
|
245
|
+
}
|
|
246
|
+
async function decodeMedia(request, contentType, surface, mediaField) {
|
|
247
|
+
if (/^application\/json\b/i.test(contentType) || /^text\/json\b/i.test(contentType)) {
|
|
248
|
+
const value = await request.json().catch(() => void 0);
|
|
249
|
+
if (isPlainObject(value))
|
|
250
|
+
return value;
|
|
251
|
+
throw new MalformedBodyError(surface);
|
|
252
|
+
}
|
|
253
|
+
if (/^multipart\/related\b/i.test(contentType)) {
|
|
254
|
+
const boundary = contentType.match(/boundary=(?:"([^"]+)"|([^;\s]+))/i)?.slice(1).find(Boolean);
|
|
255
|
+
if (!boundary)
|
|
256
|
+
throw new MalformedBodyError(surface);
|
|
257
|
+
const parts = splitRelated(Buffer.from(await request.arrayBuffer()), boundary);
|
|
258
|
+
if (parts.length !== 2)
|
|
259
|
+
throw new MalformedBodyError(surface);
|
|
260
|
+
let metadata;
|
|
261
|
+
try {
|
|
262
|
+
metadata = JSON.parse(parts[0].toString("utf8"));
|
|
263
|
+
} catch {
|
|
264
|
+
throw new MalformedBodyError(surface);
|
|
265
|
+
}
|
|
266
|
+
if (!isPlainObject(metadata))
|
|
267
|
+
throw new MalformedBodyError(surface);
|
|
268
|
+
return setPath({ ...metadata }, mediaField, parts[1]);
|
|
269
|
+
}
|
|
270
|
+
return setPath({}, mediaField, Buffer.from(await request.arrayBuffer()));
|
|
271
|
+
}
|
|
272
|
+
function splitRelated(bytes, boundary) {
|
|
273
|
+
const marker = Buffer.from(`--${boundary}`);
|
|
274
|
+
const end = Buffer.from(`--${boundary}--`);
|
|
275
|
+
const parts = [];
|
|
276
|
+
let cursor = 0;
|
|
277
|
+
while (cursor < bytes.length) {
|
|
278
|
+
const start = bytes.indexOf(marker, cursor);
|
|
279
|
+
if (start < 0 || bytes.subarray(start, start + end.length).equals(end))
|
|
280
|
+
break;
|
|
281
|
+
const lineEnd = bytes.indexOf(10, start + marker.length);
|
|
282
|
+
if (lineEnd < 0)
|
|
283
|
+
break;
|
|
284
|
+
const next = bytes.indexOf(marker, lineEnd + 1);
|
|
285
|
+
if (next < 0)
|
|
286
|
+
break;
|
|
287
|
+
let chunk = bytes.subarray(lineEnd + 1, next);
|
|
288
|
+
while (chunk.length > 0 && (chunk.at(-1) === 10 || chunk.at(-1) === 13)) {
|
|
289
|
+
chunk = chunk.subarray(0, -1);
|
|
290
|
+
}
|
|
291
|
+
const crlf = chunk.indexOf(Buffer.from("\r\n\r\n"));
|
|
292
|
+
const lf = chunk.indexOf(Buffer.from("\n\n"));
|
|
293
|
+
const separator = crlf >= 0 ? { index: crlf, length: 4 } : { index: lf, length: 2 };
|
|
294
|
+
if (separator.index < 0)
|
|
295
|
+
break;
|
|
296
|
+
parts.push(Buffer.from(chunk.subarray(separator.index + separator.length)));
|
|
297
|
+
cursor = next;
|
|
298
|
+
}
|
|
299
|
+
return parts;
|
|
300
|
+
}
|
|
301
|
+
function setPath(target, dotted, value) {
|
|
302
|
+
const keys = dotted.split(".");
|
|
303
|
+
let cursor = target;
|
|
304
|
+
for (const key of keys.slice(0, -1)) {
|
|
305
|
+
const next = cursor[key];
|
|
306
|
+
cursor[key] = isPlainObject(next) ? { ...next } : {};
|
|
307
|
+
cursor = cursor[key];
|
|
308
|
+
}
|
|
309
|
+
cursor[keys.at(-1)] = value;
|
|
310
|
+
return target;
|
|
311
|
+
}
|
|
312
|
+
var POLLUTION_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
313
|
+
function expandBrackets(form) {
|
|
314
|
+
const out = {};
|
|
315
|
+
for (const [rawKey, value] of Object.entries(form)) {
|
|
316
|
+
const segments = [];
|
|
317
|
+
for (const match of rawKey.matchAll(/([^[\]]+)|\[([^[\]]*)\]/g)) {
|
|
318
|
+
const piece = match[1] ?? match[2] ?? "";
|
|
319
|
+
segments.push(/^\d+$/.test(piece) ? Number(piece) : piece);
|
|
320
|
+
}
|
|
321
|
+
if (segments.some((segment) => typeof segment === "string" && POLLUTION_KEYS.has(segment))) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
let cursor = out;
|
|
325
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
326
|
+
const key = segments[index];
|
|
327
|
+
if (index === segments.length - 1) {
|
|
328
|
+
cursor[key] = Array.isArray(value) && value.length === 1 ? value[0] : value;
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
const nextKey = segments[index + 1];
|
|
332
|
+
if (cursor[key] === void 0)
|
|
333
|
+
cursor[key] = typeof nextKey === "number" ? [] : {};
|
|
334
|
+
cursor = cursor[key];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return out;
|
|
338
|
+
}
|
|
339
|
+
function isPlainObject(value) {
|
|
340
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
341
|
+
}
|
|
342
|
+
function integerInput(options = {}) {
|
|
343
|
+
let schema = z.coerce.number().int();
|
|
344
|
+
if (options.min !== void 0)
|
|
345
|
+
schema = schema.min(options.min);
|
|
346
|
+
if (options.max !== void 0)
|
|
347
|
+
schema = schema.max(options.max);
|
|
348
|
+
return schema;
|
|
349
|
+
}
|
|
350
|
+
var booleanInput = z.union([
|
|
351
|
+
z.boolean(),
|
|
352
|
+
z.literal("true").transform(() => true),
|
|
353
|
+
z.literal("false").transform(() => false)
|
|
354
|
+
]);
|
|
355
|
+
function repeatedInput(options = {}) {
|
|
356
|
+
const items = options.max === void 0 ? z.array(z.string()) : z.array(z.string()).max(options.max);
|
|
357
|
+
return items.default([]);
|
|
358
|
+
}
|
|
359
|
+
function jsonSchemaTypeOf(schema) {
|
|
360
|
+
let json;
|
|
361
|
+
try {
|
|
362
|
+
json = z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
|
|
363
|
+
} catch {
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
return typeNameFromJsonSchema(json);
|
|
367
|
+
}
|
|
368
|
+
function typeNameFromJsonSchema(json) {
|
|
369
|
+
if (!isPlainObject(json))
|
|
370
|
+
return null;
|
|
371
|
+
if (typeof json.type === "string")
|
|
372
|
+
return json.type;
|
|
373
|
+
if (Array.isArray(json.type)) {
|
|
374
|
+
const names2 = json.type.filter((name) => typeof name === "string" && name !== "null");
|
|
375
|
+
return names2.length === 1 ? names2[0] : names2.length > 1 ? [...new Set(names2)].sort().join("|") : null;
|
|
376
|
+
}
|
|
377
|
+
const branches = Array.isArray(json.anyOf) ? json.anyOf : Array.isArray(json.oneOf) ? json.oneOf : null;
|
|
378
|
+
if (!branches)
|
|
379
|
+
return null;
|
|
380
|
+
const names = /* @__PURE__ */ new Set();
|
|
381
|
+
for (const branch of branches) {
|
|
382
|
+
const name = typeNameFromJsonSchema(branch);
|
|
383
|
+
if (name && name !== "null")
|
|
384
|
+
names.add(name);
|
|
385
|
+
}
|
|
386
|
+
if (names.size === 0)
|
|
387
|
+
return null;
|
|
388
|
+
return [...names].sort().join("|");
|
|
389
|
+
}
|
|
390
|
+
var declaredInputSchema = z.strictObject({
|
|
391
|
+
name: z.string().min(1),
|
|
392
|
+
location: z.enum(["path", "query", "header", "body", "argument"]),
|
|
393
|
+
required: z.boolean(),
|
|
394
|
+
type: z.string().min(1).nullable()
|
|
395
|
+
});
|
|
396
|
+
var routeInputSurfaceSchema = z.strictObject({
|
|
397
|
+
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "ALL"]),
|
|
398
|
+
path: z.string().min(1),
|
|
399
|
+
surface: z.string().min(1),
|
|
400
|
+
inputs: z.array(declaredInputSchema).min(1)
|
|
401
|
+
});
|
|
402
|
+
var graphqlArgumentSurfaceSchema = z.strictObject({
|
|
403
|
+
surface: z.string().min(1),
|
|
404
|
+
root: z.enum(["query", "mutation"]),
|
|
405
|
+
inputs: z.array(declaredInputSchema).min(1)
|
|
406
|
+
});
|
|
407
|
+
z.strictObject({
|
|
408
|
+
twin: z.string().min(1),
|
|
409
|
+
package: z.string().min(1),
|
|
410
|
+
artifact_version: z.literal(1),
|
|
411
|
+
generated_by: z.string().min(1),
|
|
412
|
+
source: z.array(z.string().min(1)).min(1),
|
|
413
|
+
/** Surfaces with at least one declared input; a zero-input surface is
|
|
414
|
+
* omitted, because an empty declaration compares empty against empty and
|
|
415
|
+
* reports a match nobody measured. */
|
|
416
|
+
surface_count: z.number().int().nonnegative(),
|
|
417
|
+
input_count: z.number().int().nonnegative(),
|
|
418
|
+
surfaces: z.array(routeInputSurfaceSchema),
|
|
419
|
+
/**
|
|
420
|
+
* Present only for a twin whose non-MCP API layer is GraphQL. Same
|
|
421
|
+
* omit-when-empty rule as `surfaces`: a root field with no arguments is left
|
|
422
|
+
* out rather than published with `[]`.
|
|
423
|
+
*/
|
|
424
|
+
graphql_surfaces: z.array(graphqlArgumentSurfaceSchema).optional()
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
export { MalformedBodyError, UndeclaredInputError, booleanInput, bracketedQuery, declareRouteInputs, integerInput, mountDeclaredRoute, repeatedInput };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { oneOf, statePath, defineCheck, VACUITY_SENTINEL, childStatePath } from './chunk-
|
|
1
|
+
import { oneOf, statePath, defineCheck, VACUITY_SENTINEL, childStatePath } from './chunk-JWJYNAWI.js';
|
|
2
2
|
|
|
3
3
|
// ../packages/twin-slack/dist/src/check-params.js
|
|
4
4
|
var channelName = {
|
|
@@ -177,7 +177,23 @@ var noReactionAdded = defineCheck({
|
|
|
177
177
|
params: { reaction: emojiName, channel: channelName },
|
|
178
178
|
substrate: "final",
|
|
179
179
|
polarity: () => "negative",
|
|
180
|
-
|
|
180
|
+
// The reaction name, because that is the literal this predicate compares
|
|
181
|
+
// against `reactions[].name` — the line below has said so since the check
|
|
182
|
+
// shipped, and this field said `null` anyway (F-1157).
|
|
183
|
+
//
|
|
184
|
+
// It is the sharpest case in the vocabulary for why that mattered. A NEGATIVE
|
|
185
|
+
// criterion asserting a scanned literal is absent reads a redactor's work as
|
|
186
|
+
// its own verdict: mask `reactions[].name` and the filter matches nothing, so
|
|
187
|
+
// `No "white_check_mark" reaction was added` passes — over an export where the
|
|
188
|
+
// agent added exactly that reaction. `probeRedactionSurvival` found it by
|
|
189
|
+
// destroying this literal in the FAILING world declared below and watching
|
|
190
|
+
// that world start to pass; it was the one `vacuous_pass` across all five
|
|
191
|
+
// twins' 45 declarations.
|
|
192
|
+
//
|
|
193
|
+
// Contrast `slack.no-message-posted` above, whose null is deliberate: its only
|
|
194
|
+
// literal RESOLVES a channel and the count it asserts on is derived. This one
|
|
195
|
+
// scans.
|
|
196
|
+
subject: ({ reaction }) => reaction,
|
|
181
197
|
// The channel RESOLVES (a miss already skips); the reaction name is SCANNED.
|
|
182
198
|
// D10: falsify the scanned one.
|
|
183
199
|
vacuityMutant: (args) => ({ ...args, reaction: VACUITY_SENTINEL }),
|
|
@@ -4,7 +4,7 @@ var package_default = {
|
|
|
4
4
|
|
|
5
5
|
// ../packages/twin-slack/package.json
|
|
6
6
|
var package_default2 = {
|
|
7
|
-
version: "0.3.
|
|
7
|
+
version: "0.3.6"};
|
|
8
8
|
|
|
9
9
|
// ../packages/twin-stripe/package.json
|
|
10
10
|
var package_default3 = {
|
|
@@ -12,7 +12,7 @@ var package_default3 = {
|
|
|
12
12
|
|
|
13
13
|
// ../packages/twin-gmail/package.json
|
|
14
14
|
var package_default4 = {
|
|
15
|
-
version: "0.3.
|
|
15
|
+
version: "0.3.6"};
|
|
16
16
|
|
|
17
17
|
// ../packages/twin-linear/package.json
|
|
18
18
|
var package_default5 = {
|
|
@@ -39,7 +39,7 @@ var TWIN_REGISTRY = {
|
|
|
39
39
|
defaultSeedState,
|
|
40
40
|
GitHubDomain,
|
|
41
41
|
openGitHubCloneDatabase
|
|
42
|
-
} = await import('./src-
|
|
42
|
+
} = await import('./src-GIKWIJM2.js');
|
|
43
43
|
const db = openGitHubCloneDatabase();
|
|
44
44
|
const domain = new GitHubDomain(db);
|
|
45
45
|
domain.seed(seedState === void 0 ? defaultSeedState() : seedState);
|
|
@@ -59,9 +59,9 @@ var TWIN_REGISTRY = {
|
|
|
59
59
|
envName: "SLACK",
|
|
60
60
|
defaultPort: 3333,
|
|
61
61
|
version: package_default2.version,
|
|
62
|
-
defaultSeed: async () => (await import('./src-
|
|
62
|
+
defaultSeed: async () => (await import('./src-V5JLDT6T.js')).defaultSeedState(),
|
|
63
63
|
async boot({ seedState, runId, recorder }) {
|
|
64
|
-
const { createSlackTwinApp, openSlackTwinDatabase, SlackDomain } = await import('./src-
|
|
64
|
+
const { createSlackTwinApp, openSlackTwinDatabase, SlackDomain } = await import('./src-V5JLDT6T.js');
|
|
65
65
|
const db = openSlackTwinDatabase(":memory:");
|
|
66
66
|
const domain = new SlackDomain(db);
|
|
67
67
|
domain.applySeed(seedState);
|
|
@@ -84,9 +84,9 @@ var TWIN_REGISTRY = {
|
|
|
84
84
|
envName: "STRIPE",
|
|
85
85
|
defaultPort: 3333,
|
|
86
86
|
version: package_default3.version,
|
|
87
|
-
defaultSeed: async () => (await import('./src-
|
|
87
|
+
defaultSeed: async () => (await import('./src-566DJYOC.js')).defaultSeed(),
|
|
88
88
|
async boot({ seedState, runId, recorder, twinBaseUrl }) {
|
|
89
|
-
const stripeTwin = await import('./src-
|
|
89
|
+
const stripeTwin = await import('./src-566DJYOC.js');
|
|
90
90
|
const { createApp } = await import('./server-DR56XIGP.js');
|
|
91
91
|
const {
|
|
92
92
|
applySeed: applyStripeSeed,
|
|
@@ -128,9 +128,9 @@ var TWIN_REGISTRY = {
|
|
|
128
128
|
portEnvName: "GMAIL_TWIN_PORT",
|
|
129
129
|
tokenEnvName: "POME_GMAIL_TOKEN",
|
|
130
130
|
version: package_default4.version,
|
|
131
|
-
defaultSeed: async () => (await import('./src-
|
|
131
|
+
defaultSeed: async () => (await import('./src-PUE6CMDF.js')).defaultSeedState(),
|
|
132
132
|
async boot({ seedState, runId, recorder }) {
|
|
133
|
-
const { createGmailTwinApp, GmailDomain, openGmailTwinDatabase, parseSeed } = await import('./src-
|
|
133
|
+
const { createGmailTwinApp, GmailDomain, openGmailTwinDatabase, parseSeed } = await import('./src-PUE6CMDF.js');
|
|
134
134
|
const db = openGmailTwinDatabase(":memory:");
|
|
135
135
|
const seed = parseSeed(seedState);
|
|
136
136
|
const domain = new GmailDomain(db);
|
|
@@ -149,7 +149,7 @@ var TWIN_REGISTRY = {
|
|
|
149
149
|
portEnvName: "LINEAR_TWIN_PORT",
|
|
150
150
|
tokenEnvName: "POME_LINEAR_TOKEN",
|
|
151
151
|
version: package_default5.version,
|
|
152
|
-
defaultSeed: async () => (await import('./src-
|
|
152
|
+
defaultSeed: async () => (await import('./src-ECRGORXR.js')).defaultSeedState(),
|
|
153
153
|
async boot({ seedState, runId, recorder }) {
|
|
154
154
|
const {
|
|
155
155
|
createLinearTwinApp,
|
|
@@ -157,7 +157,7 @@ var TWIN_REGISTRY = {
|
|
|
157
157
|
LinearDomain,
|
|
158
158
|
openLinearTwinDatabase,
|
|
159
159
|
parseSeed
|
|
160
|
-
} = await import('./src-
|
|
160
|
+
} = await import('./src-ECRGORXR.js');
|
|
161
161
|
const db = openLinearTwinDatabase(":memory:");
|
|
162
162
|
const seed = parseSeed(seedState);
|
|
163
163
|
const domain = new LinearDomain(db);
|
|
@@ -173,7 +173,7 @@ var TWIN_REGISTRY = {
|
|
|
173
173
|
}
|
|
174
174
|
};
|
|
175
175
|
async function createGitHubSmokeApp() {
|
|
176
|
-
const { createGitHubCloneApp } = await import('./src-
|
|
176
|
+
const { createGitHubCloneApp } = await import('./src-GIKWIJM2.js');
|
|
177
177
|
return createGitHubCloneApp();
|
|
178
178
|
}
|
|
179
179
|
function defaultPortFor(twin, env = process.env) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getAvailablePort } from './chunk-XDU6TD4O.js';
|
|
2
2
|
import { buildEgressAllowlist, readBlockedEgress } from './chunk-CBFKZZBR.js';
|
|
3
|
-
import { parseTaskFile, seedStateForTwin, runAgentCommand, writeRunArtifactsCore } from './chunk-
|
|
4
|
-
import { createRecorder, bootTwin } from './chunk-
|
|
3
|
+
import { parseTaskFile, seedStateForTwin, runAgentCommand, writeRunArtifactsCore } from './chunk-OQWXMHRG.js';
|
|
4
|
+
import { createRecorder, bootTwin } from './chunk-IYDTDANH.js';
|
|
5
5
|
import { eventSchema } from './chunk-VBATFCWR.js';
|
|
6
6
|
import { redactSecrets, redactEvent } from './chunk-SG6ZTIMT.js';
|
|
7
7
|
import { serve } from '@hono/node-server';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readManifest, normalizeManifestTwins } from './chunk-KUVTL4NZ.js';
|
|
2
|
-
import { createHostedClient, perTwinReturnedByCloud, parseTaskFile, runAgentCommand, writeRunArtifactsCore, toTwinHttpEvent, redactJsonl, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus } from './chunk-
|
|
2
|
+
import { createHostedClient, perTwinReturnedByCloud, parseTaskFile, runAgentCommand, writeRunArtifactsCore, toTwinHttpEvent, redactJsonl, uploadRunBlobs, scoreFromFinalizeResponse, scoreStatus } from './chunk-OQWXMHRG.js';
|
|
3
3
|
import { MOUNTED_TWINS, HostedAuthError, HostedDiscardRefusedError, HostedQuotaError, HostedOrchError, HostedTrialError, agentResponseSchema } from './chunk-PQYIAA6K.js';
|
|
4
4
|
import { redactSecrets, redactEvent } from './chunk-SG6ZTIMT.js';
|
|
5
5
|
import { existsSync } from 'node:fs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-
|
|
1
|
+
import { TWIN_NAMES, isTwinName, TWIN_REGISTRY } from './chunk-BA4N7AOZ.js';
|
|
2
2
|
import { createFileBackedRecorderStore, createRecorderStore } from './chunk-TV5S6WQV.js';
|
|
3
3
|
|
|
4
4
|
// src/recorder/recorder.ts
|
|
@@ -13,6 +13,12 @@ function childStatePath(base, ...segments) {
|
|
|
13
13
|
return `${base}${statePath(...segments)}`;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// ../packages/sdk/dist/check-redaction.js
|
|
17
|
+
var REDACTION_PLACEHOLDER = "[REDACTED]";
|
|
18
|
+
function isRedacted(value) {
|
|
19
|
+
return value === REDACTION_PLACEHOLDER;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
// ../packages/sdk/dist/checks.js
|
|
17
23
|
var VACUITY_SENTINEL = "pome-vacuity-never";
|
|
18
24
|
var VACUITY_SENTINEL_NUMBER = 987654321;
|
|
@@ -117,4 +123,4 @@ function checkNearMissPattern(def) {
|
|
|
117
123
|
return buildPattern(def.template, () => ".+?");
|
|
118
124
|
}
|
|
119
125
|
|
|
120
|
-
export { VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER, checkNearMissPattern, checkPattern, checksDigest, childStatePath, defineCheck, oneOf, renderCheck, repoRef, statePath, templateSlots };
|
|
126
|
+
export { VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER, checkNearMissPattern, checkPattern, checksDigest, childStatePath, defineCheck, isRedacted, oneOf, renderCheck, repoRef, statePath, templateSlots };
|
|
@@ -2,7 +2,7 @@ import { criterionSchema, finalizeResponseSchema, HostedDiscardRefusedError, Hos
|
|
|
2
2
|
import { seedSchema, parseSeed, defaultSeedState as defaultSeedState$2 } from './chunk-SGDUD7KK.js';
|
|
3
3
|
import { gmailSeedSchema, defaultSeedState } from './chunk-NJ246QPJ.js';
|
|
4
4
|
import { linearSeedSchema, defaultSeedState as defaultSeedState$1 } from './chunk-ZKID2HS3.js';
|
|
5
|
-
import { isTwinName, TWIN_REGISTRY } from './chunk-
|
|
5
|
+
import { isTwinName, TWIN_REGISTRY } from './chunk-BA4N7AOZ.js';
|
|
6
6
|
import { toTwinHttpEventRow } from './chunk-TV5S6WQV.js';
|
|
7
7
|
import { redactEvent, redactSecrets } from './chunk-SG6ZTIMT.js';
|
|
8
8
|
import { mkdir, appendFile, writeFile, readFile } from 'node:fs/promises';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { statePath, defineCheck, VACUITY_SENTINEL, childStatePath, VACUITY_SENTINEL_NUMBER } from './chunk-
|
|
1
|
+
import { statePath, defineCheck, VACUITY_SENTINEL, childStatePath, VACUITY_SENTINEL_NUMBER, isRedacted } from './chunk-JWJYNAWI.js';
|
|
2
2
|
|
|
3
3
|
// ../packages/twin-gmail/dist/src/check-params.js
|
|
4
4
|
var messageId = {
|
|
@@ -436,7 +436,12 @@ var mailboxLabelCount = defineCheck({
|
|
|
436
436
|
return { passed: false, status: "skipped", reason: "collection_truncated" };
|
|
437
437
|
}
|
|
438
438
|
if (final.mailboxes != null && !final.mailboxes.some((mb) => (mb.email ?? "").toLowerCase() === mailbox.toLowerCase())) {
|
|
439
|
-
|
|
439
|
+
const eaten = final.mailboxes.some((mb) => isRedacted(mb.email));
|
|
440
|
+
return {
|
|
441
|
+
passed: false,
|
|
442
|
+
status: "skipped",
|
|
443
|
+
reason: eaten ? `mailbox_redacted ("${mailbox}")` : `mailbox_not_found ("${mailbox}")`
|
|
444
|
+
};
|
|
440
445
|
}
|
|
441
446
|
const ids = labelIdsFor(final, label);
|
|
442
447
|
const labeled = new Set(final.messageLabels.filter((row) => ids.has((row.labelId ?? "").toLowerCase())).map((row) => (row.messageId ?? "").toLowerCase()));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { statePath, defineCheck, VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER, childStatePath } from './chunk-
|
|
1
|
+
import { statePath, defineCheck, VACUITY_SENTINEL, VACUITY_SENTINEL_NUMBER, childStatePath } from './chunk-JWJYNAWI.js';
|
|
2
2
|
|
|
3
3
|
// ../packages/twin-linear/dist/src/check-params.js
|
|
4
4
|
var issueTitle = {
|