@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 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,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 res = await fetch(url, init);
125
- const json = await res.json().catch(() => ({}));
126
- return { ok: res.ok, status: res.status, json };
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, 1500);
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 status = await waitForHealth(endpoint, 1000);
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' : '/status';
640
- const ret = await requestJson(endpoint, pathName);
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;
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.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "webauto": "bin/webauto.mjs"