@web-auto/webauto 0.1.16 → 0.1.17

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 = 12e3;
923
+ var DEFAULT_ACTION_TIMEOUT_MS = 12e3;
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 win2.webContents.executeJavaScript(buildSnapshotScript(), true);
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 out = await win2.webContents.executeJavaScript(buildActionScript(input), true);
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 win2.webContents.executeJavaScript(checkScript, true);
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,13 @@ 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);
14
21
 
15
22
  function normalizePathForPlatform(raw, platform = process.platform) {
16
23
  const input = String(raw || '').trim();
@@ -121,9 +128,37 @@ function resolveEndpoint() {
121
128
 
122
129
  async function requestJson(endpoint, pathname, init = {}) {
123
130
  const url = `http://${endpoint.host}:${endpoint.port}${pathname}`;
124
- const res = await fetch(url, init);
125
- const json = await res.json().catch(() => ({}));
126
- return { ok: res.ok, status: res.status, json };
131
+ const timeoutMs = parseIntSafe(init?.timeoutMs, DEFAULT_HTTP_TIMEOUT_MS);
132
+ const retries = Math.max(0, parseIntSafe(init?.retries, DEFAULT_HTTP_RETRIES));
133
+ const retryDelayMs = parseIntSafe(init?.retryDelayMs, 300);
134
+ const requestInit = { ...init };
135
+ delete requestInit.timeoutMs;
136
+ delete requestInit.retries;
137
+ delete requestInit.retryDelayMs;
138
+
139
+ let lastErr = null;
140
+ for (let attempt = 0; attempt <= retries; attempt += 1) {
141
+ const controller = new AbortController();
142
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
143
+ try {
144
+ const res = await fetch(url, { ...requestInit, signal: controller.signal });
145
+ clearTimeout(timeout);
146
+ const json = await res.json().catch(() => ({}));
147
+ return { ok: res.ok, status: res.status, json };
148
+ } catch (err) {
149
+ clearTimeout(timeout);
150
+ lastErr = err;
151
+ if (attempt < retries) {
152
+ await sleep(retryDelayMs * (attempt + 1));
153
+ continue;
154
+ }
155
+ }
156
+ }
157
+
158
+ const msg = lastErr?.name === 'AbortError'
159
+ ? `request_timeout:${pathname}:${timeoutMs}`
160
+ : (lastErr?.message || String(lastErr));
161
+ throw new Error(msg);
127
162
  }
128
163
 
129
164
  function sleep(ms) {
@@ -134,7 +169,7 @@ async function waitForHealth(endpoint, timeoutMs = 30_000) {
134
169
  const started = Date.now();
135
170
  while (Date.now() - started <= timeoutMs) {
136
171
  try {
137
- const ret = await requestJson(endpoint, '/health');
172
+ const ret = await requestJson(endpoint, '/health', { timeoutMs: 2500, retries: 0 });
138
173
  if (ret.ok && ret.json?.ok) return ret.json;
139
174
  } catch {
140
175
  // keep polling
@@ -145,7 +180,7 @@ async function waitForHealth(endpoint, timeoutMs = 30_000) {
145
180
  }
146
181
 
147
182
  async function startConsoleIfNeeded(endpoint) {
148
- const health = await waitForHealth(endpoint, 1500);
183
+ const health = await waitForHealth(endpoint, 3000);
149
184
  if (health) return health;
150
185
 
151
186
  const uiConsoleScript = path.join(APP_ROOT, 'entry', 'ui-console.mjs');
@@ -186,10 +221,17 @@ function printOutput(payload) {
186
221
  }
187
222
 
188
223
  async function sendAction(endpoint, payload) {
224
+ const waitBudgetMs = payload?.action === 'wait'
225
+ ? parseIntSafe(payload?.timeoutMs, 15_000) + 5_000
226
+ : 0;
227
+ const timeoutMs = Math.max(DEFAULT_HTTP_TIMEOUT_MS, waitBudgetMs);
228
+ const retries = payload?.action === 'wait' ? 0 : DEFAULT_HTTP_RETRIES;
189
229
  return requestJson(endpoint, '/action', {
190
230
  method: 'POST',
191
231
  headers: { 'Content-Type': 'application/json' },
192
232
  body: JSON.stringify(payload),
233
+ timeoutMs,
234
+ retries,
193
235
  });
194
236
  }
195
237
 
@@ -629,7 +671,8 @@ async function main() {
629
671
  }
630
672
 
631
673
  if (cmd === 'start') {
632
- const status = await waitForHealth(endpoint, 1000);
674
+ const startWaitMs = parseIntSafe(args.timeout, DEFAULT_START_HEALTH_TIMEOUT_MS);
675
+ const status = await waitForHealth(endpoint, startWaitMs);
633
676
  if (!status) throw new Error('ui cli bridge not healthy');
634
677
  printOutput({ ok: true, endpoint, status });
635
678
  return;
@@ -637,7 +680,10 @@ async function main() {
637
680
 
638
681
  if (cmd === 'status' || cmd === 'snapshot') {
639
682
  const pathName = cmd === 'snapshot' ? '/snapshot' : '/status';
640
- const ret = await requestJson(endpoint, pathName);
683
+ const ret = await requestJson(endpoint, pathName, {
684
+ timeoutMs: parseIntSafe(args.timeout, DEFAULT_HTTP_TIMEOUT_MS),
685
+ retries: DEFAULT_HTTP_RETRIES,
686
+ });
641
687
  if (!ret.ok) throw new Error(ret.json?.error || `http_${ret.status}`);
642
688
  printOutput(ret.json);
643
689
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-auto/webauto",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "webauto": "bin/webauto.mjs"