@web-auto/webauto 0.1.16 → 0.1.18
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.
|
@@ -919,6 +919,9 @@ import path5 from "node:path";
|
|
|
919
919
|
import { promises as fs3 } from "node:fs";
|
|
920
920
|
var DEFAULT_HOST = "127.0.0.1";
|
|
921
921
|
var DEFAULT_PORT = 7716;
|
|
922
|
+
var DEFAULT_SNAPSHOT_TIMEOUT_MS = 35e3;
|
|
923
|
+
var DEFAULT_ACTION_TIMEOUT_MS = 3e4;
|
|
924
|
+
var DEFAULT_WAIT_PROBE_TIMEOUT_MS = 3e3;
|
|
922
925
|
function normalizePathForPlatform(raw, platform = process.platform) {
|
|
923
926
|
const input = String(raw || "").trim();
|
|
924
927
|
const isWinPath = platform === "win32" || /^[A-Za-z]:[\\/]/.test(input);
|
|
@@ -994,6 +997,21 @@ function toActionError(input, error, extra = {}) {
|
|
|
994
997
|
};
|
|
995
998
|
return payload;
|
|
996
999
|
}
|
|
1000
|
+
async function withTimeout(promise, timeoutMs, label) {
|
|
1001
|
+
const ms = readInt(timeoutMs, 0);
|
|
1002
|
+
if (ms <= 0) return promise;
|
|
1003
|
+
let timer = null;
|
|
1004
|
+
try {
|
|
1005
|
+
return await Promise.race([
|
|
1006
|
+
promise,
|
|
1007
|
+
new Promise((_resolve, reject) => {
|
|
1008
|
+
timer = setTimeout(() => reject(new Error(`${label}_timeout:${ms}`)), ms);
|
|
1009
|
+
})
|
|
1010
|
+
]);
|
|
1011
|
+
} finally {
|
|
1012
|
+
if (timer) clearTimeout(timer);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
997
1015
|
async function writeControlFile(host, port) {
|
|
998
1016
|
const payload = {
|
|
999
1017
|
pid: process.pid,
|
|
@@ -1386,7 +1404,11 @@ var UiCliBridge = class {
|
|
|
1386
1404
|
let snapshot;
|
|
1387
1405
|
if (includeSnapshot) {
|
|
1388
1406
|
try {
|
|
1389
|
-
snapshot = await
|
|
1407
|
+
snapshot = await withTimeout(
|
|
1408
|
+
win2.webContents.executeJavaScript(buildSnapshotScript(), true),
|
|
1409
|
+
readInt(process.env.WEBAUTO_UI_CLI_SNAPSHOT_TIMEOUT_MS, DEFAULT_SNAPSHOT_TIMEOUT_MS),
|
|
1410
|
+
"snapshot"
|
|
1411
|
+
);
|
|
1390
1412
|
} catch (err) {
|
|
1391
1413
|
return {
|
|
1392
1414
|
ok: false,
|
|
@@ -1418,7 +1440,12 @@ var UiCliBridge = class {
|
|
|
1418
1440
|
const win2 = this.options.getWindow();
|
|
1419
1441
|
if (!isUiReady(win2)) return toActionError(input, "window_not_ready");
|
|
1420
1442
|
try {
|
|
1421
|
-
const
|
|
1443
|
+
const timeoutMs = readInt(input?.timeoutMs, readInt(process.env.WEBAUTO_UI_CLI_ACTION_TIMEOUT_MS, DEFAULT_ACTION_TIMEOUT_MS));
|
|
1444
|
+
const out = await withTimeout(
|
|
1445
|
+
win2.webContents.executeJavaScript(buildActionScript(input), true),
|
|
1446
|
+
timeoutMs,
|
|
1447
|
+
"action"
|
|
1448
|
+
);
|
|
1422
1449
|
return out && typeof out === "object" ? out : toActionError(input, "empty_result");
|
|
1423
1450
|
} catch (err) {
|
|
1424
1451
|
return toActionError(input, err?.message || String(err), { details: err?.stack || null });
|
|
@@ -1449,7 +1476,11 @@ var UiCliBridge = class {
|
|
|
1449
1476
|
const disabled = Boolean(el && 'disabled' in el && el.disabled === true);
|
|
1450
1477
|
return { exists: Boolean(el), visible, text, value, disabled };
|
|
1451
1478
|
})()`;
|
|
1452
|
-
const state = await
|
|
1479
|
+
const state = await withTimeout(
|
|
1480
|
+
win2.webContents.executeJavaScript(checkScript, true),
|
|
1481
|
+
readInt(process.env.WEBAUTO_UI_CLI_WAIT_PROBE_TIMEOUT_MS, DEFAULT_WAIT_PROBE_TIMEOUT_MS),
|
|
1482
|
+
"wait_probe"
|
|
1483
|
+
);
|
|
1453
1484
|
const exists = Boolean(state?.exists);
|
|
1454
1485
|
const visible = Boolean(state?.visible);
|
|
1455
1486
|
const text = String(state?.text || "");
|
|
@@ -11,6 +11,15 @@ const APP_ROOT = path.resolve(__dirname, '..');
|
|
|
11
11
|
const ROOT = path.resolve(APP_ROOT, '..', '..');
|
|
12
12
|
const DEFAULT_HOST = process.env.WEBAUTO_UI_CLI_HOST || '127.0.0.1';
|
|
13
13
|
const DEFAULT_PORT = Number(process.env.WEBAUTO_UI_CLI_PORT || 7716);
|
|
14
|
+
const readEnvPositiveInt = (name, fallback) => {
|
|
15
|
+
const n = Number(process.env[name]);
|
|
16
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : fallback;
|
|
17
|
+
};
|
|
18
|
+
const DEFAULT_HTTP_TIMEOUT_MS = readEnvPositiveInt('WEBAUTO_UI_CLI_HTTP_TIMEOUT_MS', 25_000);
|
|
19
|
+
const DEFAULT_HTTP_RETRIES = readEnvPositiveInt('WEBAUTO_UI_CLI_HTTP_RETRIES', 1);
|
|
20
|
+
const DEFAULT_START_HEALTH_TIMEOUT_MS = readEnvPositiveInt('WEBAUTO_UI_CLI_START_HEALTH_TIMEOUT_MS', 8_000);
|
|
21
|
+
const DEFAULT_STATUS_TIMEOUT_MS = readEnvPositiveInt('WEBAUTO_UI_CLI_STATUS_TIMEOUT_MS', 45_000);
|
|
22
|
+
const DEFAULT_ACTION_HTTP_TIMEOUT_MS = readEnvPositiveInt('WEBAUTO_UI_CLI_ACTION_HTTP_TIMEOUT_MS', 40_000);
|
|
14
23
|
|
|
15
24
|
function normalizePathForPlatform(raw, platform = process.platform) {
|
|
16
25
|
const input = String(raw || '').trim();
|
|
@@ -121,9 +130,37 @@ function resolveEndpoint() {
|
|
|
121
130
|
|
|
122
131
|
async function requestJson(endpoint, pathname, init = {}) {
|
|
123
132
|
const url = `http://${endpoint.host}:${endpoint.port}${pathname}`;
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
133
|
+
const timeoutMs = parseIntSafe(init?.timeoutMs, DEFAULT_HTTP_TIMEOUT_MS);
|
|
134
|
+
const retries = Math.max(0, parseIntSafe(init?.retries, DEFAULT_HTTP_RETRIES));
|
|
135
|
+
const retryDelayMs = parseIntSafe(init?.retryDelayMs, 300);
|
|
136
|
+
const requestInit = { ...init };
|
|
137
|
+
delete requestInit.timeoutMs;
|
|
138
|
+
delete requestInit.retries;
|
|
139
|
+
delete requestInit.retryDelayMs;
|
|
140
|
+
|
|
141
|
+
let lastErr = null;
|
|
142
|
+
for (let attempt = 0; attempt <= retries; attempt += 1) {
|
|
143
|
+
const controller = new AbortController();
|
|
144
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
145
|
+
try {
|
|
146
|
+
const res = await fetch(url, { ...requestInit, signal: controller.signal });
|
|
147
|
+
clearTimeout(timeout);
|
|
148
|
+
const json = await res.json().catch(() => ({}));
|
|
149
|
+
return { ok: res.ok, status: res.status, json };
|
|
150
|
+
} catch (err) {
|
|
151
|
+
clearTimeout(timeout);
|
|
152
|
+
lastErr = err;
|
|
153
|
+
if (attempt < retries) {
|
|
154
|
+
await sleep(retryDelayMs * (attempt + 1));
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const msg = lastErr?.name === 'AbortError'
|
|
161
|
+
? `request_timeout:${pathname}:${timeoutMs}`
|
|
162
|
+
: (lastErr?.message || String(lastErr));
|
|
163
|
+
throw new Error(msg);
|
|
127
164
|
}
|
|
128
165
|
|
|
129
166
|
function sleep(ms) {
|
|
@@ -134,7 +171,7 @@ async function waitForHealth(endpoint, timeoutMs = 30_000) {
|
|
|
134
171
|
const started = Date.now();
|
|
135
172
|
while (Date.now() - started <= timeoutMs) {
|
|
136
173
|
try {
|
|
137
|
-
const ret = await requestJson(endpoint, '/health');
|
|
174
|
+
const ret = await requestJson(endpoint, '/health', { timeoutMs: 2500, retries: 0 });
|
|
138
175
|
if (ret.ok && ret.json?.ok) return ret.json;
|
|
139
176
|
} catch {
|
|
140
177
|
// keep polling
|
|
@@ -145,7 +182,7 @@ async function waitForHealth(endpoint, timeoutMs = 30_000) {
|
|
|
145
182
|
}
|
|
146
183
|
|
|
147
184
|
async function startConsoleIfNeeded(endpoint) {
|
|
148
|
-
const health = await waitForHealth(endpoint,
|
|
185
|
+
const health = await waitForHealth(endpoint, 3000);
|
|
149
186
|
if (health) return health;
|
|
150
187
|
|
|
151
188
|
const uiConsoleScript = path.join(APP_ROOT, 'entry', 'ui-console.mjs');
|
|
@@ -186,10 +223,17 @@ function printOutput(payload) {
|
|
|
186
223
|
}
|
|
187
224
|
|
|
188
225
|
async function sendAction(endpoint, payload) {
|
|
226
|
+
const actionBudgetMs = payload?.action === 'wait'
|
|
227
|
+
? parseIntSafe(payload?.timeoutMs, 15_000) + 5_000
|
|
228
|
+
: DEFAULT_ACTION_HTTP_TIMEOUT_MS;
|
|
229
|
+
const timeoutMs = Math.max(DEFAULT_HTTP_TIMEOUT_MS, actionBudgetMs);
|
|
230
|
+
const retries = payload?.action === 'wait' ? 0 : DEFAULT_HTTP_RETRIES;
|
|
189
231
|
return requestJson(endpoint, '/action', {
|
|
190
232
|
method: 'POST',
|
|
191
233
|
headers: { 'Content-Type': 'application/json' },
|
|
192
234
|
body: JSON.stringify(payload),
|
|
235
|
+
timeoutMs,
|
|
236
|
+
retries,
|
|
193
237
|
});
|
|
194
238
|
}
|
|
195
239
|
|
|
@@ -629,15 +673,20 @@ async function main() {
|
|
|
629
673
|
}
|
|
630
674
|
|
|
631
675
|
if (cmd === 'start') {
|
|
632
|
-
const
|
|
676
|
+
const startWaitMs = parseIntSafe(args.timeout, DEFAULT_START_HEALTH_TIMEOUT_MS);
|
|
677
|
+
const status = await waitForHealth(endpoint, startWaitMs);
|
|
633
678
|
if (!status) throw new Error('ui cli bridge not healthy');
|
|
634
679
|
printOutput({ ok: true, endpoint, status });
|
|
635
680
|
return;
|
|
636
681
|
}
|
|
637
682
|
|
|
638
683
|
if (cmd === 'status' || cmd === 'snapshot') {
|
|
639
|
-
const pathName = cmd === 'snapshot' ? '/snapshot' : '/
|
|
640
|
-
const
|
|
684
|
+
const pathName = cmd === 'snapshot' ? '/snapshot' : '/health';
|
|
685
|
+
const statusTimeoutMs = parseIntSafe(args.timeout, DEFAULT_STATUS_TIMEOUT_MS);
|
|
686
|
+
const ret = await requestJson(endpoint, pathName, {
|
|
687
|
+
timeoutMs: statusTimeoutMs,
|
|
688
|
+
retries: DEFAULT_HTTP_RETRIES,
|
|
689
|
+
});
|
|
641
690
|
if (!ret.ok) throw new Error(ret.json?.error || `http_${ret.status}`);
|
|
642
691
|
printOutput(ret.json);
|
|
643
692
|
return;
|