ravensight-playtest 0.7.0 → 0.7.5
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/README.md +61 -8
- package/package.json +3 -3
- package/src/auth/scopes.js +36 -0
- package/src/cli.js +42 -9
- package/src/commands/brief.js +1 -1
- package/src/commands/build.js +23 -1
- package/src/commands/check.js +5 -1
- package/src/commands/fakeCore.js +5 -1
- package/src/commands/fix.js +2 -0
- package/src/commands/hosted.js +144 -2
- package/src/commands/profile.js +2 -1
- package/src/commands/run.js +86 -5
- package/src/hosted/buildStatus.js +35 -0
- package/src/hosted/buildUpload.js +24 -2
- package/src/hosted/recordedRun.js +135 -3
- package/src/hosted/status.js +108 -0
- package/src/run/aggregate.js +69 -10
- package/src/run/budgetReserve.js +102 -0
- package/src/run/context.js +122 -3
- package/src/run/drivers/web.js +342 -126
- package/src/run/failureReason.js +77 -0
- package/src/run/gameRefusal.js +57 -0
- package/src/run/model.js +18 -1
- package/src/run/openPr.js +8 -1
- package/src/run/personaLoop.js +333 -76
- package/src/run/regenerate.js +4 -1
- package/src/run/report.js +54 -0
- package/src/run/tools.js +31 -3
- package/src/run/usage.js +54 -0
- package/src/ui/index.js +21 -6
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which failed actions are the game's state and which are the runner's.
|
|
3
|
+
*
|
|
4
|
+
* A web page that shows a control as disabled, hidden or not editable has
|
|
5
|
+
* refused the action itself: the click failing is what a player would see.
|
|
6
|
+
* In a real Lab Ledger run the "Run repeat panel" button stayed disabled
|
|
7
|
+
* after the page said it was ready, and the blocker the persona filed was
|
|
8
|
+
* dropped as "driver errors only" because Playwright's click timed out on
|
|
9
|
+
* a button it had found and that said `disabled`. That is evidence.
|
|
10
|
+
*
|
|
11
|
+
* A failure where the runner itself broke stays a driver error: a target
|
|
12
|
+
* that never resolved (a selector or ref problem), a browser or page that
|
|
13
|
+
* closed or crashed under the runner.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** Playwright's actionability waits that name the game's state. */
|
|
17
|
+
const NOT_ACTIONABLE = /element is not (enabled|editable|visible)/i;
|
|
18
|
+
|
|
19
|
+
/** The element a locator resolved to, from Playwright's call log. */
|
|
20
|
+
const RESOLVED = /locator resolved to (<[^\n]*)/i;
|
|
21
|
+
|
|
22
|
+
/** A resolved element whose own markup says it is disabled. */
|
|
23
|
+
const RESOLVED_DISABLED = /locator resolved to <[^>]*\s(?:disabled(?:[\s=>/]|$)|aria-disabled="true")/i;
|
|
24
|
+
|
|
25
|
+
/** The runner's browser, context or page went away under it. */
|
|
26
|
+
const RUNNER_GONE = /Target (?:page, context or browser has been closed|crashed)|browser has (?:been )?disconnected|Browser closed|Page crashed/i;
|
|
27
|
+
|
|
28
|
+
const STATE_WORDS = { enabled: 'disabled', editable: 'not editable', visible: 'hidden' };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The game's refusal named in a failed action's error, or null when the
|
|
32
|
+
* failure is not one.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} text the driver's error text (a call log may be cut short)
|
|
35
|
+
* @returns {{state: string, element: string|null}|null}
|
|
36
|
+
*/
|
|
37
|
+
export function gameRefusal(text) {
|
|
38
|
+
const value = String(text || '');
|
|
39
|
+
if (!value || RUNNER_GONE.test(value)) return null;
|
|
40
|
+
const resolved = value.match(RESOLVED);
|
|
41
|
+
if (!resolved) return null;
|
|
42
|
+
const element = resolved[1].trim().slice(0, 160);
|
|
43
|
+
const waited = value.match(NOT_ACTIONABLE);
|
|
44
|
+
if (waited) return { state: STATE_WORDS[waited[1].toLowerCase()], element };
|
|
45
|
+
if (RESOLVED_DISABLED.test(value)) return { state: 'disabled', element };
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* True when the error says the runner's browser or page went away.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} text
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
export function runnerGone(text) {
|
|
56
|
+
return RUNNER_GONE.test(String(text || ''));
|
|
57
|
+
}
|
package/src/run/model.js
CHANGED
|
@@ -170,7 +170,24 @@ export function toProxyError(error) {
|
|
|
170
170
|
if (key !== 'type' && key !== 'message') figures[key] = value;
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
return new ProxyError({ status, code, message, retryAfter, figures });
|
|
173
|
+
return new ProxyError({ status, code, message: withProviderReason(message, figures), retryAfter, figures });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The provider's own reason, when the proxy passed one on (a 400
|
|
178
|
+
* `upstream_rejected` carries `provider_type` and a cleaned
|
|
179
|
+
* `provider_message`), added to the message the developer is shown. Without
|
|
180
|
+
* it a refused body reads only "The model provider rejected this request."
|
|
181
|
+
*
|
|
182
|
+
* @param {string} message
|
|
183
|
+
* @param {Object} figures
|
|
184
|
+
* @returns {string}
|
|
185
|
+
*/
|
|
186
|
+
export function withProviderReason(message, figures = {}) {
|
|
187
|
+
const type = typeof figures.provider_type === 'string' ? figures.provider_type : '';
|
|
188
|
+
const reason = typeof figures.provider_message === 'string' ? figures.provider_message : '';
|
|
189
|
+
if (!type && !reason) return message;
|
|
190
|
+
return `${message} Provider said: ${[type, reason].filter(Boolean).join(': ')}`;
|
|
174
191
|
}
|
|
175
192
|
|
|
176
193
|
/**
|
package/src/run/openPr.js
CHANGED
|
@@ -85,7 +85,14 @@ export async function openPullRequest({ repoRoot, branch, paths, title: rawTitle
|
|
|
85
85
|
...paths.map(file => ` ${plainTitle(file)}`)
|
|
86
86
|
].join('\n');
|
|
87
87
|
// `yes: false` on purpose: --yes agreed to the price, not to publishing.
|
|
88
|
-
|
|
88
|
+
let go;
|
|
89
|
+
try { go = await confirm(question, { yes: false }); }
|
|
90
|
+
catch (error) {
|
|
91
|
+
// --yes cannot agree to publishing, so with no terminal the answer is a
|
|
92
|
+
// plain refusal, never a piped "y".
|
|
93
|
+
if (error?.code === 'no_terminal') return refuse('--open-pr asks before it pushes, and there is no terminal to ask in.');
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
89
96
|
if (!go) return refuse('Not confirmed.');
|
|
90
97
|
|
|
91
98
|
const added = await call('git', ['add', '--', ...paths]);
|