@riddledc/riddle-proof 0.5.54 → 0.5.56
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/basic-gameplay.cjs +306 -0
- package/dist/basic-gameplay.d.cts +169 -0
- package/dist/basic-gameplay.d.ts +169 -0
- package/dist/basic-gameplay.js +18 -0
- package/dist/{chunk-UR6ADV4Y.js → chunk-3CVGVQTQ.js} +84 -0
- package/dist/chunk-53DJMNQ6.js +276 -0
- package/dist/cli.cjs +110 -0
- package/dist/cli.js +34 -7
- package/dist/engine-harness.js +2 -2
- package/dist/index.cjs +366 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +30 -12
- package/dist/proof-run-engine.d.cts +3 -3
- package/dist/proof-run-engine.d.ts +3 -3
- package/dist/riddle-client.cjs +87 -2
- package/dist/riddle-client.d.cts +32 -1
- package/dist/riddle-client.d.ts +32 -1
- package/dist/riddle-client.js +5 -3
- package/package.json +7 -2
- package/dist/{chunk-A2AWRZ5B.js → chunk-2FBF2UDZ.js} +5 -5
|
@@ -87,12 +87,94 @@ async function deployRiddleStaticPreview(config, directory, label) {
|
|
|
87
87
|
raw: published
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
|
+
function createTarball(directory, label, exclude = []) {
|
|
91
|
+
const scratch = mkdtempSync(path.join(tmpdir(), "riddle-upload-"));
|
|
92
|
+
const tarball = path.join(scratch, `${label}.tar.gz`);
|
|
93
|
+
const excludeArgs = exclude.flatMap((item) => ["--exclude", item]);
|
|
94
|
+
try {
|
|
95
|
+
execFileSync("tar", ["czf", tarball, ...excludeArgs, "-C", directory, "."], { stdio: "pipe" });
|
|
96
|
+
return { scratch, tarball };
|
|
97
|
+
} catch (error) {
|
|
98
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
90
102
|
function parseRiddleViewport(value) {
|
|
91
103
|
if (!value) return void 0;
|
|
92
104
|
const match = /^(\d+)x(\d+)$/.exec(value.trim());
|
|
93
105
|
if (!match) throw new Error("viewport must look like 1280x720");
|
|
94
106
|
return { width: Number(match[1]), height: Number(match[2]) };
|
|
95
107
|
}
|
|
108
|
+
function scriptErrorFrom(job) {
|
|
109
|
+
const nested = job?.proof_of_execution && typeof job.proof_of_execution === "object" && !Array.isArray(job.proof_of_execution) ? job.proof_of_execution : null;
|
|
110
|
+
return typeof job?.script_error === "string" && job.script_error.trim() ? job.script_error : typeof nested?.script_error === "string" && nested.script_error.trim() ? nested.script_error : null;
|
|
111
|
+
}
|
|
112
|
+
async function runRiddleServerPreview(config, input) {
|
|
113
|
+
if (!input.directory?.trim()) throw new Error("directory is required");
|
|
114
|
+
if (!input.script?.trim()) throw new Error("script is required");
|
|
115
|
+
const port = input.port || 3e3;
|
|
116
|
+
const routePath = input.path || "/";
|
|
117
|
+
const timeoutSec = input.timeoutSec || 180;
|
|
118
|
+
const created = await riddleRequestJson(config, "/v1/server-preview", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: JSON.stringify({
|
|
121
|
+
image: input.image || "node:22-slim",
|
|
122
|
+
command: input.command || "node scripts/riddleSpaPreviewServer.mjs build 3000",
|
|
123
|
+
port,
|
|
124
|
+
path: routePath,
|
|
125
|
+
readiness_path: input.readinessPath || routePath,
|
|
126
|
+
readiness_timeout: input.readinessTimeoutSec || 90,
|
|
127
|
+
wait_until: input.waitUntil || "domcontentloaded",
|
|
128
|
+
wait_for_selector: input.waitForSelector || "body",
|
|
129
|
+
navigation_timeout: input.navigationTimeoutSec || 60,
|
|
130
|
+
viewport: input.viewport,
|
|
131
|
+
timeout: timeoutSec,
|
|
132
|
+
script: input.script
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
const jobId = String(created.job_id || "");
|
|
136
|
+
const uploadUrl = String(created.upload_url || "");
|
|
137
|
+
if (!jobId || !uploadUrl) {
|
|
138
|
+
throw new Error(`Riddle server preview create response was missing job_id or upload_url.`);
|
|
139
|
+
}
|
|
140
|
+
const { scratch, tarball } = createTarball(input.directory, jobId, [
|
|
141
|
+
".git",
|
|
142
|
+
"node_modules",
|
|
143
|
+
"test-results",
|
|
144
|
+
...input.exclude || []
|
|
145
|
+
]);
|
|
146
|
+
try {
|
|
147
|
+
const upload = await fetchFor(config)(uploadUrl, {
|
|
148
|
+
method: "PUT",
|
|
149
|
+
headers: { "Content-Type": "application/gzip" },
|
|
150
|
+
body: readFileSync(tarball)
|
|
151
|
+
});
|
|
152
|
+
if (!upload.ok) throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
await riddleRequestJson(config, `/v1/server-preview/${jobId}/start`, {
|
|
157
|
+
method: "POST"
|
|
158
|
+
});
|
|
159
|
+
let job = null;
|
|
160
|
+
const attempts = input.pollAttempts || Math.ceil((timeoutSec + 90) / 2);
|
|
161
|
+
const intervalMs = input.pollIntervalMs || 2e3;
|
|
162
|
+
for (let index = 0; index < attempts; index += 1) {
|
|
163
|
+
job = await riddleRequestJson(config, `/v1/server-preview/${jobId}`);
|
|
164
|
+
if (isTerminalRiddleJobStatus(job.status)) break;
|
|
165
|
+
if (index + 1 < attempts) await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
166
|
+
}
|
|
167
|
+
const status = job?.status ? String(job.status) : null;
|
|
168
|
+
const scriptError = scriptErrorFrom(job);
|
|
169
|
+
return {
|
|
170
|
+
ok: (status === "completed" || status === "complete") && !scriptError,
|
|
171
|
+
job_id: jobId,
|
|
172
|
+
status,
|
|
173
|
+
terminal: isTerminalRiddleJobStatus(status),
|
|
174
|
+
script_error: scriptError,
|
|
175
|
+
job
|
|
176
|
+
};
|
|
177
|
+
}
|
|
96
178
|
async function runRiddleScript(config, input) {
|
|
97
179
|
if (!input.url?.trim()) throw new Error("url is required");
|
|
98
180
|
if (!input.script?.trim()) throw new Error("script is required");
|
|
@@ -144,6 +226,7 @@ function createRiddleApiClient(config = {}) {
|
|
|
144
226
|
requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
|
|
145
227
|
deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
|
|
146
228
|
runScript: (input) => runRiddleScript(config, input),
|
|
229
|
+
runServerPreview: (input) => runRiddleServerPreview(config, input),
|
|
147
230
|
pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
|
|
148
231
|
};
|
|
149
232
|
}
|
|
@@ -156,6 +239,7 @@ export {
|
|
|
156
239
|
riddleRequestJson,
|
|
157
240
|
deployRiddleStaticPreview,
|
|
158
241
|
parseRiddleViewport,
|
|
242
|
+
runRiddleServerPreview,
|
|
159
243
|
runRiddleScript,
|
|
160
244
|
isTerminalRiddleJobStatus,
|
|
161
245
|
pollRiddleJob,
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// src/basic-gameplay.ts
|
|
2
|
+
var RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION = "riddle-proof.basic-gameplay.v1";
|
|
3
|
+
var RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION = "riddle-proof.basic-gameplay.assessment.v1";
|
|
4
|
+
var RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION = "riddle-proof.basic-gameplay.catch.v1";
|
|
5
|
+
var BASIC_GAMEPLAY_CONTAINER_KEYS = [
|
|
6
|
+
"basic_gameplay",
|
|
7
|
+
"basicGameplay",
|
|
8
|
+
"basic_gameplay_evidence",
|
|
9
|
+
"basicGameplayEvidence",
|
|
10
|
+
"gameplay_proof",
|
|
11
|
+
"gameplayProof"
|
|
12
|
+
];
|
|
13
|
+
function assessBasicGameplayEvidence(evidence, options = {}) {
|
|
14
|
+
const run = extractBasicGameplayEvidence(evidence);
|
|
15
|
+
if (!run) {
|
|
16
|
+
return {
|
|
17
|
+
version: RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
|
|
18
|
+
evidence_present: false,
|
|
19
|
+
passed: false,
|
|
20
|
+
checked_routes: 0,
|
|
21
|
+
passing_routes: 0,
|
|
22
|
+
failing_routes: [],
|
|
23
|
+
failure_counts: {},
|
|
24
|
+
warning_counts: {},
|
|
25
|
+
route_results: []
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const routeResults = (run.results || []).map((route) => assessBasicGameplayRoute(route, options));
|
|
29
|
+
const failingRoutes = routeResults.filter((result) => !result.ok).map((result) => ({
|
|
30
|
+
name: result.name,
|
|
31
|
+
path: result.path,
|
|
32
|
+
failures: result.failures,
|
|
33
|
+
warnings: result.warnings
|
|
34
|
+
}));
|
|
35
|
+
return {
|
|
36
|
+
version: RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
|
|
37
|
+
evidence_present: true,
|
|
38
|
+
passed: failingRoutes.length === 0,
|
|
39
|
+
checked_routes: routeResults.length,
|
|
40
|
+
passing_routes: routeResults.filter((result) => result.ok).length,
|
|
41
|
+
failing_routes: failingRoutes,
|
|
42
|
+
failure_counts: countCodes(routeResults.flatMap((result) => result.failures)),
|
|
43
|
+
warning_counts: countCodes(routeResults.flatMap((result) => result.warnings)),
|
|
44
|
+
route_results: routeResults
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function assessBasicGameplayRoute(route, options = {}) {
|
|
48
|
+
const maxMobileOverflowPx = options.maxMobileOverflowPx ?? 4;
|
|
49
|
+
const minBodyTextLength = options.minBodyTextLength ?? 20;
|
|
50
|
+
const minVisibleLargeNodes = options.minVisibleLargeNodes ?? 3;
|
|
51
|
+
const minSurfaceLargeNodes = options.minSurfaceLargeNodes ?? 8;
|
|
52
|
+
const warnOnMissingResetPath = options.warnOnMissingResetPath ?? true;
|
|
53
|
+
const warnOnConsoleError = options.warnOnConsoleError ?? true;
|
|
54
|
+
const failOnConsoleError = options.failOnConsoleError ?? false;
|
|
55
|
+
const failures = [];
|
|
56
|
+
const warnings = [];
|
|
57
|
+
const initial = route.initial || {};
|
|
58
|
+
const timed = route.timed || {};
|
|
59
|
+
const afterAction = route.after_action || route.afterAction || {};
|
|
60
|
+
const mobile = route.mobile || {};
|
|
61
|
+
const timedChange = changed(initial, timed);
|
|
62
|
+
const actionChange = changed(timed, afterAction);
|
|
63
|
+
const surfaceVisible = numberValue(initial.visible_canvas_count) > 0 || numberValue(initial.enabled_clickable_count) > 0 || numberValue(initial.visible_large_node_count) >= minSurfaceLargeNodes;
|
|
64
|
+
const actionResults = listValue(route.action_results || route.actionResults);
|
|
65
|
+
const actionAttempted = actionResults.some((result) => result.ok === true && result.action !== "wait");
|
|
66
|
+
const actionFailed = actionResults.some((result) => result.ok === false && result.action !== "wait");
|
|
67
|
+
const stateChangeObserved = actionChange.changed || timedChange.changed;
|
|
68
|
+
const resetPathPresent = numberValue(initial.reset_control_count) > 0 || numberValue(timed.reset_control_count) > 0 || numberValue(afterAction.reset_control_count) > 0;
|
|
69
|
+
const responseStatus = firstNumber(route.http_status, route.response_status, route.status);
|
|
70
|
+
const pageErrorCount = numberValue(route.page_error_count);
|
|
71
|
+
const consoleErrorCount = numberValue(route.console_error_count);
|
|
72
|
+
const mobileOverflowPx = numberValue(mobile.overflow_px);
|
|
73
|
+
if (responseStatus !== null && responseStatus >= 400) failures.push("route_http_error");
|
|
74
|
+
if (pageErrorCount > 0) failures.push("fatal_page_error");
|
|
75
|
+
if (numberValue(initial.body_text_length) < minBodyTextLength && numberValue(initial.visible_large_node_count) < minVisibleLargeNodes) {
|
|
76
|
+
failures.push("route_blank_or_thin");
|
|
77
|
+
}
|
|
78
|
+
if (!surfaceVisible) failures.push("no_game_surface");
|
|
79
|
+
if (mobileOverflowPx > maxMobileOverflowPx) failures.push("mobile_horizontal_overflow");
|
|
80
|
+
if (!actionAttempted && !timedChange.changed) failures.push("primary_control_missing");
|
|
81
|
+
if (actionAttempted && !stateChangeObserved) failures.push("primary_control_inert");
|
|
82
|
+
if (failOnConsoleError && consoleErrorCount > 0) failures.push("fatal_page_error");
|
|
83
|
+
if (numberValue(initial.visible_canvas_count) > 0 && actionAttempted && !timedChange.canvas_changed && !actionChange.canvas_changed && !actionChange.screenshot_changed) {
|
|
84
|
+
warnings.push("canvas_inert");
|
|
85
|
+
}
|
|
86
|
+
if (actionFailed) warnings.push("some_actions_failed");
|
|
87
|
+
if (warnOnMissingResetPath && !resetPathPresent && route.requires_reset !== false && actionAttempted && stateChangeObserved) {
|
|
88
|
+
warnings.push("missing_reset_path");
|
|
89
|
+
}
|
|
90
|
+
if (warnOnConsoleError && consoleErrorCount > 0) warnings.push("critical_console_error");
|
|
91
|
+
return {
|
|
92
|
+
name: route.name,
|
|
93
|
+
path: route.path,
|
|
94
|
+
ok: failures.length === 0,
|
|
95
|
+
failures,
|
|
96
|
+
warnings,
|
|
97
|
+
signals: {
|
|
98
|
+
route_loaded: !failures.includes("route_http_error") && !failures.includes("route_blank_or_thin"),
|
|
99
|
+
surface_visible: surfaceVisible,
|
|
100
|
+
action_attempted: actionAttempted,
|
|
101
|
+
timed_progression_observed: timedChange.changed,
|
|
102
|
+
first_interaction_observed: actionChange.changed,
|
|
103
|
+
state_change_observed: stateChangeObserved,
|
|
104
|
+
mobile_overflow_absent: mobileOverflowPx <= maxMobileOverflowPx,
|
|
105
|
+
reset_path_present: resetPathPresent,
|
|
106
|
+
fatal_errors_absent: pageErrorCount === 0
|
|
107
|
+
},
|
|
108
|
+
diffs: {
|
|
109
|
+
timed: timedChange,
|
|
110
|
+
after_action: actionChange
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function createBasicGameplayCatchSummary(input, options = {}) {
|
|
115
|
+
const before = summarizeAssessment(assessBasicGameplayEvidence(input.before, options));
|
|
116
|
+
const after = input.after === void 0 ? void 0 : summarizeAssessment(assessBasicGameplayEvidence(input.after, options));
|
|
117
|
+
const fixed = Boolean(after && before.notable_codes.length > 0 && after.passed && after.notable_codes.length === 0);
|
|
118
|
+
const title = input.title || [
|
|
119
|
+
input.site || "Basic gameplay",
|
|
120
|
+
input.route ? `${input.route} proof catch` : "proof catch"
|
|
121
|
+
].join(" ");
|
|
122
|
+
const beforeCodes = before.notable_codes.length ? before.notable_codes.join(", ") : "no failing or warning codes";
|
|
123
|
+
const afterCodes = after ? after.notable_codes.length ? after.notable_codes.join(", ") : "no failing or warning codes" : "not verified";
|
|
124
|
+
const summaryLines = [
|
|
125
|
+
`Before: ${before.checked_routes} checked, ${before.passing_routes} passing, codes: ${beforeCodes}.`,
|
|
126
|
+
after ? `After: ${after.checked_routes} checked, ${after.passing_routes} passing, codes: ${afterCodes}.` : "After: not provided."
|
|
127
|
+
];
|
|
128
|
+
if (input.fix?.summary) summaryLines.push(`Fix: ${input.fix.summary}`);
|
|
129
|
+
if (input.artifacts?.length) summaryLines.push(`Artifacts: ${input.artifacts.length} attached.`);
|
|
130
|
+
return {
|
|
131
|
+
version: RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
|
|
132
|
+
title,
|
|
133
|
+
site: input.site,
|
|
134
|
+
route: input.route,
|
|
135
|
+
detected_at: input.detected_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
136
|
+
before,
|
|
137
|
+
after,
|
|
138
|
+
fixed,
|
|
139
|
+
fix: input.fix,
|
|
140
|
+
artifacts: input.artifacts || [],
|
|
141
|
+
summary_lines: summaryLines,
|
|
142
|
+
marketing_summary: fixed ? `${title}: Riddle Proof caught ${beforeCodes}; after the fix, ${afterCodes}.` : `${title}: Riddle Proof caught ${beforeCodes}; after evidence is ${after ? "not yet clean" : "not yet attached"}.`
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function extractBasicGameplayEvidence(...sources) {
|
|
146
|
+
const seen = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const source of sources) {
|
|
148
|
+
const found = findBasicGameplayEvidence(source, seen);
|
|
149
|
+
if (found) return found;
|
|
150
|
+
}
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
function summarizeAssessment(assessment) {
|
|
154
|
+
return {
|
|
155
|
+
evidence_present: assessment.evidence_present,
|
|
156
|
+
passed: assessment.passed,
|
|
157
|
+
checked_routes: assessment.checked_routes,
|
|
158
|
+
passing_routes: assessment.passing_routes,
|
|
159
|
+
failing_routes: assessment.failing_routes,
|
|
160
|
+
failure_counts: assessment.failure_counts,
|
|
161
|
+
warning_counts: assessment.warning_counts,
|
|
162
|
+
notable_codes: [
|
|
163
|
+
...Object.keys(assessment.failure_counts),
|
|
164
|
+
...Object.keys(assessment.warning_counts)
|
|
165
|
+
].sort()
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function findBasicGameplayEvidence(value, seen, depth = 0) {
|
|
169
|
+
if (depth > 6 || value === null || value === void 0) return null;
|
|
170
|
+
if (typeof value === "string") {
|
|
171
|
+
const parsed = parseJson(value);
|
|
172
|
+
return parsed === null ? null : findBasicGameplayEvidence(parsed, seen, depth + 1);
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
if (seen.has(value)) return null;
|
|
176
|
+
seen.add(value);
|
|
177
|
+
if (value.some((item) => hasRouteShape(recordValue(item)))) {
|
|
178
|
+
return { results: value.filter((item) => hasRouteShape(recordValue(item))) };
|
|
179
|
+
}
|
|
180
|
+
for (const item of value) {
|
|
181
|
+
const found = findBasicGameplayEvidence(item, seen, depth + 1);
|
|
182
|
+
if (found) return found;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
const record = recordValue(value);
|
|
187
|
+
if (!record || seen.has(record)) return null;
|
|
188
|
+
seen.add(record);
|
|
189
|
+
if (record.version === RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION || Array.isArray(record.results)) {
|
|
190
|
+
return {
|
|
191
|
+
...record,
|
|
192
|
+
results: listValue(record.results).filter((item) => Boolean(recordValue(item)))
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
if (hasRouteShape(record)) {
|
|
196
|
+
return { results: [record] };
|
|
197
|
+
}
|
|
198
|
+
for (const key of BASIC_GAMEPLAY_CONTAINER_KEYS) {
|
|
199
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
200
|
+
const nested = findBasicGameplayEvidence(record[key], seen, depth + 1);
|
|
201
|
+
if (nested) return nested;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
for (const item of Object.values(record)) {
|
|
205
|
+
const found = findBasicGameplayEvidence(item, seen, depth + 1);
|
|
206
|
+
if (found) return found;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
function hasRouteShape(record) {
|
|
211
|
+
return Boolean(record && (record.initial || record.after_action || record.afterAction || record.action_results || record.actionResults) && (record.path || record.name));
|
|
212
|
+
}
|
|
213
|
+
function changed(before, after) {
|
|
214
|
+
const bodyTextChanged = Boolean(before.body_text_hash && after.body_text_hash && before.body_text_hash !== after.body_text_hash);
|
|
215
|
+
const screenshotChanged = Boolean(before.screenshot_hash && after.screenshot_hash && before.screenshot_hash !== after.screenshot_hash);
|
|
216
|
+
const beforeCanvasHashes = canvasHashes(before.canvases);
|
|
217
|
+
const afterCanvasHashes = canvasHashes(after.canvases);
|
|
218
|
+
const canvasChanged = Boolean(beforeCanvasHashes && afterCanvasHashes && beforeCanvasHashes !== afterCanvasHashes);
|
|
219
|
+
return {
|
|
220
|
+
body_text_changed: bodyTextChanged,
|
|
221
|
+
screenshot_changed: screenshotChanged,
|
|
222
|
+
canvas_changed: canvasChanged,
|
|
223
|
+
changed: bodyTextChanged || screenshotChanged || canvasChanged
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function canvasHashes(canvases) {
|
|
227
|
+
return (canvases || []).map((canvas) => canvas.hash).filter(Boolean).join("|");
|
|
228
|
+
}
|
|
229
|
+
function countCodes(codes) {
|
|
230
|
+
const counts = {};
|
|
231
|
+
for (const code of codes) counts[code] = (counts[code] || 0) + 1;
|
|
232
|
+
return counts;
|
|
233
|
+
}
|
|
234
|
+
function firstNumber(...values) {
|
|
235
|
+
for (const value of values) {
|
|
236
|
+
const number = numericValue(value);
|
|
237
|
+
if (number !== null) return number;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
function numberValue(value) {
|
|
242
|
+
return numericValue(value) ?? 0;
|
|
243
|
+
}
|
|
244
|
+
function numericValue(value) {
|
|
245
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
246
|
+
if (typeof value === "string" && value.trim()) {
|
|
247
|
+
const parsed = Number(value);
|
|
248
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
249
|
+
}
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
function recordValue(value) {
|
|
253
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
254
|
+
}
|
|
255
|
+
function listValue(value) {
|
|
256
|
+
return Array.isArray(value) ? value : [];
|
|
257
|
+
}
|
|
258
|
+
function parseJson(value) {
|
|
259
|
+
const trimmed = value.trim();
|
|
260
|
+
if (!trimmed || !/^[{[]/.test(trimmed)) return null;
|
|
261
|
+
try {
|
|
262
|
+
return JSON.parse(trimmed);
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export {
|
|
269
|
+
RIDDLE_PROOF_BASIC_GAMEPLAY_VERSION,
|
|
270
|
+
RIDDLE_PROOF_BASIC_GAMEPLAY_ASSESSMENT_VERSION,
|
|
271
|
+
RIDDLE_PROOF_BASIC_GAMEPLAY_CATCH_VERSION,
|
|
272
|
+
assessBasicGameplayEvidence,
|
|
273
|
+
assessBasicGameplayRoute,
|
|
274
|
+
createBasicGameplayCatchSummary,
|
|
275
|
+
extractBasicGameplayEvidence
|
|
276
|
+
};
|
package/dist/cli.cjs
CHANGED
|
@@ -6487,12 +6487,94 @@ async function deployRiddleStaticPreview(config, directory, label) {
|
|
|
6487
6487
|
raw: published
|
|
6488
6488
|
};
|
|
6489
6489
|
}
|
|
6490
|
+
function createTarball(directory, label, exclude = []) {
|
|
6491
|
+
const scratch = (0, import_node_fs5.mkdtempSync)(import_node_path5.default.join((0, import_node_os2.tmpdir)(), "riddle-upload-"));
|
|
6492
|
+
const tarball = import_node_path5.default.join(scratch, `${label}.tar.gz`);
|
|
6493
|
+
const excludeArgs = exclude.flatMap((item) => ["--exclude", item]);
|
|
6494
|
+
try {
|
|
6495
|
+
(0, import_node_child_process4.execFileSync)("tar", ["czf", tarball, ...excludeArgs, "-C", directory, "."], { stdio: "pipe" });
|
|
6496
|
+
return { scratch, tarball };
|
|
6497
|
+
} catch (error) {
|
|
6498
|
+
(0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
|
|
6499
|
+
throw error;
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6490
6502
|
function parseRiddleViewport(value) {
|
|
6491
6503
|
if (!value) return void 0;
|
|
6492
6504
|
const match = /^(\d+)x(\d+)$/.exec(value.trim());
|
|
6493
6505
|
if (!match) throw new Error("viewport must look like 1280x720");
|
|
6494
6506
|
return { width: Number(match[1]), height: Number(match[2]) };
|
|
6495
6507
|
}
|
|
6508
|
+
function scriptErrorFrom(job) {
|
|
6509
|
+
const nested = job?.proof_of_execution && typeof job.proof_of_execution === "object" && !Array.isArray(job.proof_of_execution) ? job.proof_of_execution : null;
|
|
6510
|
+
return typeof job?.script_error === "string" && job.script_error.trim() ? job.script_error : typeof nested?.script_error === "string" && nested.script_error.trim() ? nested.script_error : null;
|
|
6511
|
+
}
|
|
6512
|
+
async function runRiddleServerPreview(config, input) {
|
|
6513
|
+
if (!input.directory?.trim()) throw new Error("directory is required");
|
|
6514
|
+
if (!input.script?.trim()) throw new Error("script is required");
|
|
6515
|
+
const port = input.port || 3e3;
|
|
6516
|
+
const routePath = input.path || "/";
|
|
6517
|
+
const timeoutSec = input.timeoutSec || 180;
|
|
6518
|
+
const created = await riddleRequestJson(config, "/v1/server-preview", {
|
|
6519
|
+
method: "POST",
|
|
6520
|
+
body: JSON.stringify({
|
|
6521
|
+
image: input.image || "node:22-slim",
|
|
6522
|
+
command: input.command || "node scripts/riddleSpaPreviewServer.mjs build 3000",
|
|
6523
|
+
port,
|
|
6524
|
+
path: routePath,
|
|
6525
|
+
readiness_path: input.readinessPath || routePath,
|
|
6526
|
+
readiness_timeout: input.readinessTimeoutSec || 90,
|
|
6527
|
+
wait_until: input.waitUntil || "domcontentloaded",
|
|
6528
|
+
wait_for_selector: input.waitForSelector || "body",
|
|
6529
|
+
navigation_timeout: input.navigationTimeoutSec || 60,
|
|
6530
|
+
viewport: input.viewport,
|
|
6531
|
+
timeout: timeoutSec,
|
|
6532
|
+
script: input.script
|
|
6533
|
+
})
|
|
6534
|
+
});
|
|
6535
|
+
const jobId = String(created.job_id || "");
|
|
6536
|
+
const uploadUrl = String(created.upload_url || "");
|
|
6537
|
+
if (!jobId || !uploadUrl) {
|
|
6538
|
+
throw new Error(`Riddle server preview create response was missing job_id or upload_url.`);
|
|
6539
|
+
}
|
|
6540
|
+
const { scratch, tarball } = createTarball(input.directory, jobId, [
|
|
6541
|
+
".git",
|
|
6542
|
+
"node_modules",
|
|
6543
|
+
"test-results",
|
|
6544
|
+
...input.exclude || []
|
|
6545
|
+
]);
|
|
6546
|
+
try {
|
|
6547
|
+
const upload = await fetchFor(config)(uploadUrl, {
|
|
6548
|
+
method: "PUT",
|
|
6549
|
+
headers: { "Content-Type": "application/gzip" },
|
|
6550
|
+
body: (0, import_node_fs5.readFileSync)(tarball)
|
|
6551
|
+
});
|
|
6552
|
+
if (!upload.ok) throw new RiddleApiError(uploadUrl, upload.status, await upload.text());
|
|
6553
|
+
} finally {
|
|
6554
|
+
(0, import_node_fs5.rmSync)(scratch, { recursive: true, force: true });
|
|
6555
|
+
}
|
|
6556
|
+
await riddleRequestJson(config, `/v1/server-preview/${jobId}/start`, {
|
|
6557
|
+
method: "POST"
|
|
6558
|
+
});
|
|
6559
|
+
let job = null;
|
|
6560
|
+
const attempts = input.pollAttempts || Math.ceil((timeoutSec + 90) / 2);
|
|
6561
|
+
const intervalMs = input.pollIntervalMs || 2e3;
|
|
6562
|
+
for (let index = 0; index < attempts; index += 1) {
|
|
6563
|
+
job = await riddleRequestJson(config, `/v1/server-preview/${jobId}`);
|
|
6564
|
+
if (isTerminalRiddleJobStatus(job.status)) break;
|
|
6565
|
+
if (index + 1 < attempts) await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
6566
|
+
}
|
|
6567
|
+
const status = job?.status ? String(job.status) : null;
|
|
6568
|
+
const scriptError = scriptErrorFrom(job);
|
|
6569
|
+
return {
|
|
6570
|
+
ok: (status === "completed" || status === "complete") && !scriptError,
|
|
6571
|
+
job_id: jobId,
|
|
6572
|
+
status,
|
|
6573
|
+
terminal: isTerminalRiddleJobStatus(status),
|
|
6574
|
+
script_error: scriptError,
|
|
6575
|
+
job
|
|
6576
|
+
};
|
|
6577
|
+
}
|
|
6496
6578
|
async function runRiddleScript(config, input) {
|
|
6497
6579
|
if (!input.url?.trim()) throw new Error("url is required");
|
|
6498
6580
|
if (!input.script?.trim()) throw new Error("script is required");
|
|
@@ -6544,6 +6626,7 @@ function createRiddleApiClient(config = {}) {
|
|
|
6544
6626
|
requestJson: (pathname, init) => riddleRequestJson(config, pathname, init),
|
|
6545
6627
|
deployStaticPreview: (directory, label) => deployRiddleStaticPreview(config, directory, label),
|
|
6546
6628
|
runScript: (input) => runRiddleScript(config, input),
|
|
6629
|
+
runServerPreview: (input) => runRiddleServerPreview(config, input),
|
|
6547
6630
|
pollJob: (jobId, options) => pollRiddleJob(config, jobId, options)
|
|
6548
6631
|
};
|
|
6549
6632
|
}
|
|
@@ -6558,6 +6641,7 @@ function usage() {
|
|
|
6558
6641
|
" riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
|
|
6559
6642
|
" riddle-proof-loop status --state-path <path>",
|
|
6560
6643
|
" riddle-proof-loop riddle-preview-deploy <build-dir> <label>",
|
|
6644
|
+
" riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
|
|
6561
6645
|
" riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720]",
|
|
6562
6646
|
" riddle-proof-loop riddle-poll <job-id> [--wait]",
|
|
6563
6647
|
" riddle-proof-loop doctor local [--codex-command <path>]",
|
|
@@ -6801,6 +6885,32 @@ async function main() {
|
|
|
6801
6885
|
`);
|
|
6802
6886
|
return;
|
|
6803
6887
|
}
|
|
6888
|
+
if (command === "riddle-server-preview") {
|
|
6889
|
+
const directory = positional[1];
|
|
6890
|
+
const scriptFile = optionString(options, "scriptFile");
|
|
6891
|
+
if (!directory || !scriptFile) throw new Error("riddle-server-preview requires <directory> and --script-file.");
|
|
6892
|
+
const result = await createRiddleApiClient(riddleClientConfig(options)).runServerPreview({
|
|
6893
|
+
directory,
|
|
6894
|
+
script: (0, import_node_fs6.readFileSync)(scriptFile, "utf-8"),
|
|
6895
|
+
image: optionString(options, "image"),
|
|
6896
|
+
command: optionString(options, "command"),
|
|
6897
|
+
port: optionString(options, "port") ? Number(optionString(options, "port")) : void 0,
|
|
6898
|
+
path: optionString(options, "path"),
|
|
6899
|
+
readinessPath: optionString(options, "readinessPath"),
|
|
6900
|
+
readinessTimeoutSec: optionString(options, "readinessTimeout") ? Number(optionString(options, "readinessTimeout")) : void 0,
|
|
6901
|
+
waitForSelector: optionString(options, "waitForSelector"),
|
|
6902
|
+
navigationTimeoutSec: optionString(options, "navigationTimeout") ? Number(optionString(options, "navigationTimeout")) : void 0,
|
|
6903
|
+
viewport: parseRiddleViewport(optionString(options, "viewport")),
|
|
6904
|
+
timeoutSec: optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0,
|
|
6905
|
+
pollAttempts: optionString(options, "pollAttempts") ? Number(optionString(options, "pollAttempts")) : void 0,
|
|
6906
|
+
pollIntervalMs: optionString(options, "pollIntervalMs") ? Number(optionString(options, "pollIntervalMs")) : void 0,
|
|
6907
|
+
exclude: optionString(options, "exclude")?.split(",").map((item) => item.trim()).filter(Boolean)
|
|
6908
|
+
});
|
|
6909
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
6910
|
+
`);
|
|
6911
|
+
process.exitCode = result.ok ? 0 : 1;
|
|
6912
|
+
return;
|
|
6913
|
+
}
|
|
6804
6914
|
if (command === "riddle-run-script") {
|
|
6805
6915
|
const url = optionString(options, "url");
|
|
6806
6916
|
const scriptFile = optionString(options, "scriptFile");
|
package/dist/cli.js
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
import {
|
|
3
3
|
createRiddleApiClient,
|
|
4
4
|
parseRiddleViewport
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3CVGVQTQ.js";
|
|
6
6
|
import {
|
|
7
7
|
createDisabledRiddleProofAgentAdapter,
|
|
8
8
|
readRiddleProofRunStatus,
|
|
9
9
|
runRiddleProofEngineHarness
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-2FBF2UDZ.js";
|
|
11
|
+
import "./chunk-MO24D3PY.js";
|
|
11
12
|
import "./chunk-RFJ5BQF6.js";
|
|
13
|
+
import "./chunk-3UHWI3FO.js";
|
|
14
|
+
import {
|
|
15
|
+
createCheckpointResponseTemplate
|
|
16
|
+
} from "./chunk-33XO42CY.js";
|
|
12
17
|
import "./chunk-JFQXAJH2.js";
|
|
13
18
|
import {
|
|
14
19
|
createCodexExecAgentAdapter,
|
|
15
20
|
runCodexExecAgentDoctor
|
|
16
21
|
} from "./chunk-3266V3MO.js";
|
|
17
|
-
import "./chunk-MO24D3PY.js";
|
|
18
|
-
import "./chunk-3UHWI3FO.js";
|
|
19
|
-
import {
|
|
20
|
-
createCheckpointResponseTemplate
|
|
21
|
-
} from "./chunk-33XO42CY.js";
|
|
22
22
|
import "./chunk-DUFDZJOF.js";
|
|
23
23
|
|
|
24
24
|
// src/cli.ts
|
|
@@ -32,6 +32,7 @@ function usage() {
|
|
|
32
32
|
" riddle-proof-loop respond --state-path <path> --decision <decision> --summary <text> [--payload-json <file|json|->]",
|
|
33
33
|
" riddle-proof-loop status --state-path <path>",
|
|
34
34
|
" riddle-proof-loop riddle-preview-deploy <build-dir> <label>",
|
|
35
|
+
" riddle-proof-loop riddle-server-preview <directory> --script-file <file> [--path /route] [--wait-for-selector selector]",
|
|
35
36
|
" riddle-proof-loop riddle-run-script --url <url> --script-file <file> [--viewport 1280x720]",
|
|
36
37
|
" riddle-proof-loop riddle-poll <job-id> [--wait]",
|
|
37
38
|
" riddle-proof-loop doctor local [--codex-command <path>]",
|
|
@@ -275,6 +276,32 @@ async function main() {
|
|
|
275
276
|
`);
|
|
276
277
|
return;
|
|
277
278
|
}
|
|
279
|
+
if (command === "riddle-server-preview") {
|
|
280
|
+
const directory = positional[1];
|
|
281
|
+
const scriptFile = optionString(options, "scriptFile");
|
|
282
|
+
if (!directory || !scriptFile) throw new Error("riddle-server-preview requires <directory> and --script-file.");
|
|
283
|
+
const result = await createRiddleApiClient(riddleClientConfig(options)).runServerPreview({
|
|
284
|
+
directory,
|
|
285
|
+
script: readFileSync(scriptFile, "utf-8"),
|
|
286
|
+
image: optionString(options, "image"),
|
|
287
|
+
command: optionString(options, "command"),
|
|
288
|
+
port: optionString(options, "port") ? Number(optionString(options, "port")) : void 0,
|
|
289
|
+
path: optionString(options, "path"),
|
|
290
|
+
readinessPath: optionString(options, "readinessPath"),
|
|
291
|
+
readinessTimeoutSec: optionString(options, "readinessTimeout") ? Number(optionString(options, "readinessTimeout")) : void 0,
|
|
292
|
+
waitForSelector: optionString(options, "waitForSelector"),
|
|
293
|
+
navigationTimeoutSec: optionString(options, "navigationTimeout") ? Number(optionString(options, "navigationTimeout")) : void 0,
|
|
294
|
+
viewport: parseRiddleViewport(optionString(options, "viewport")),
|
|
295
|
+
timeoutSec: optionString(options, "timeout") ? Number(optionString(options, "timeout")) : void 0,
|
|
296
|
+
pollAttempts: optionString(options, "pollAttempts") ? Number(optionString(options, "pollAttempts")) : void 0,
|
|
297
|
+
pollIntervalMs: optionString(options, "pollIntervalMs") ? Number(optionString(options, "pollIntervalMs")) : void 0,
|
|
298
|
+
exclude: optionString(options, "exclude")?.split(",").map((item) => item.trim()).filter(Boolean)
|
|
299
|
+
});
|
|
300
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
301
|
+
`);
|
|
302
|
+
process.exitCode = result.ok ? 0 : 1;
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
278
305
|
if (command === "riddle-run-script") {
|
|
279
306
|
const url = optionString(options, "url");
|
|
280
307
|
const scriptFile = optionString(options, "scriptFile");
|
package/dist/engine-harness.js
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
createDisabledRiddleProofAgentAdapter,
|
|
3
3
|
readRiddleProofRunStatus,
|
|
4
4
|
runRiddleProofEngineHarness
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-RFJ5BQF6.js";
|
|
5
|
+
} from "./chunk-2FBF2UDZ.js";
|
|
7
6
|
import "./chunk-MO24D3PY.js";
|
|
7
|
+
import "./chunk-RFJ5BQF6.js";
|
|
8
8
|
import "./chunk-3UHWI3FO.js";
|
|
9
9
|
import "./chunk-33XO42CY.js";
|
|
10
10
|
import "./chunk-DUFDZJOF.js";
|