wildcrane-runner 0.6.6 → 0.6.9

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.
@@ -1 +1 @@
1
- {"name":"wildcrane","version":"0.11.5","akbContract":2,"type":"module","private":true}
1
+ {"name":"wildcrane","version":"0.11.8","akbContract":4,"type":"module","private":true}
package/dist/launcher.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // wildcrane-runner 0.6.6 — built by runner/scripts/build.mjs. Do not edit.
1
+ // wildcrane-runner 0.6.9 — built by runner/scripts/build.mjs. Do not edit.
2
2
  import { createRequire as __ai4kanbanRequire } from 'node:module'
3
3
  const require = __ai4kanbanRequire(import.meta.url)
4
4
 
@@ -107,9 +107,10 @@ function status(line) {
107
107
  }
108
108
 
109
109
  // src/protocol.ts
110
- var VERSION = true ? "0.6.6" : "0.0.0";
110
+ var VERSION = true ? "0.6.9" : "0.0.0";
111
111
  var EXIT_REFUSED = 10;
112
112
  var EXIT_OUTDATED = 11;
113
+ var EXIT_UPDATE = 12;
113
114
 
114
115
  // src/update.ts
115
116
  import crypto2 from "node:crypto";
@@ -273,8 +274,8 @@ async function askTarget(machine) {
273
274
  signal: AbortSignal.timeout(ASK_MS)
274
275
  });
275
276
  if (!answer.ok) return null;
276
- const { version } = await answer.json();
277
- return typeof version === "string" && version ? version : null;
277
+ const { version, restartable } = await answer.json();
278
+ return typeof version === "string" && /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(version) ? { version, restartable: restartable === true } : null;
278
279
  } catch {
279
280
  return null;
280
281
  }
@@ -283,7 +284,7 @@ async function candidates(own) {
283
284
  const state = readState();
284
285
  const proven = state.good.find((v) => fs3.existsSync(entryOf(versionDir(v))));
285
286
  const current = proven && compare(proven, own.version) > 0 ? { version: proven, entry: entryOf(versionDir(proven)) } : own;
286
- const target = await askTarget(machineId());
287
+ const target = (await askTarget(machineId()))?.version;
287
288
  if (!target || target === current.version || state.bad.includes(target)) return [current];
288
289
  if (target === own.version) return [own, current];
289
290
  const got = await install(target);
@@ -294,16 +295,56 @@ async function candidates(own) {
294
295
  if (got.why === "node") status(t.nodeTooOld(got.detail));
295
296
  return [current];
296
297
  }
297
- function runOnce(candidate) {
298
+ function runOnce(candidate, options) {
298
299
  return new Promise((resolve) => {
299
300
  const child = spawn(process.execPath, [candidate.entry], {
300
- stdio: ["inherit", "inherit", "inherit", "ipc"],
301
- env: { ...process.env, WILDCRANE_RUNNER_VERSION: candidate.version }
301
+ stdio: ["ignore", options.quiet ? 2 : "inherit", "inherit", "ipc"],
302
+ env: { ...process.env, WILDCRANE_RUNNER_VERSION: candidate.version, WILDCRANE_RUNNER_KIND: options.kind ?? "terminal" }
302
303
  });
304
+ options.onSpawn?.((type) => {
305
+ if (child.connected) child.send({ type }, () => {
306
+ });
307
+ });
308
+ let update = null;
309
+ let checking = false;
310
+ const check = async () => {
311
+ if (checking || stopping || update) return;
312
+ checking = true;
313
+ try {
314
+ const desired = await askTarget(machineId());
315
+ if (!desired?.restartable) return;
316
+ const target = desired.version;
317
+ if (!target || target === candidate.version || readState().bad.includes(target)) return;
318
+ const got = await install(target);
319
+ if (!got.ok) {
320
+ status("Runner \u66F4\u65B0\u4E0B\u8F7D\u5931\u8D25\uFF0C\u7EE7\u7EED\u8FD0\u884C\u5F53\u524D\u7248\u672C\uFF0C\u7A0D\u540E\u91CD\u8BD5\u3002");
321
+ return;
322
+ }
323
+ if (stopping || child.exitCode !== null || child.signalCode !== null) return;
324
+ if ((await askTarget(machineId()))?.version !== target) return;
325
+ update = { version: target, entry: entryOf(got.dir), fresh: !readState().good.includes(target) };
326
+ child.send({ type: "update" }, () => {
327
+ });
328
+ } finally {
329
+ checking = false;
330
+ }
331
+ };
332
+ const poll = setInterval(() => {
333
+ if (connected) void check().catch(() => {
334
+ });
335
+ }, Number(process.env.WILDCRANE_UPDATE_MS) || 6e4);
303
336
  let connected = false;
304
337
  let timer = null;
305
338
  let timedOut = false;
306
339
  let stopping = false;
340
+ let forceKill;
341
+ if (candidate.fresh) {
342
+ timer = setTimeout(() => {
343
+ timedOut = true;
344
+ child.kill("SIGTERM");
345
+ forceKill = setTimeout(() => child.kill("SIGKILL"), 5e3);
346
+ }, Number(process.env.WILDCRANE_CONNECT_MS) || CONNECT_MS);
347
+ }
307
348
  const forward = (signal) => () => {
308
349
  stopping = true;
309
350
  child.kill(signal);
@@ -312,13 +353,11 @@ function runOnce(candidate) {
312
353
  const onTerm = forward("SIGTERM");
313
354
  process.on("SIGINT", onInt);
314
355
  process.on("SIGTERM", onTerm);
356
+ options.signal?.addEventListener("abort", onTerm, { once: true });
357
+ if (options.signal?.aborted) onTerm();
315
358
  child.on("message", (message) => {
316
- if (message.type === "started" && candidate.fresh && !connected) {
317
- timer = setTimeout(() => {
318
- timedOut = true;
319
- child.kill("SIGTERM");
320
- }, Number(process.env.WILDCRANE_CONNECT_MS) || CONNECT_MS);
321
- }
359
+ options.onMessage?.(message);
360
+ if (message.type === "update-deferred") update = null;
322
361
  if (message.type === "waiting" && timer) {
323
362
  clearTimeout(timer);
324
363
  timer = null;
@@ -334,9 +373,13 @@ function runOnce(candidate) {
334
373
  }
335
374
  });
336
375
  child.on("exit", (code, signal) => {
376
+ clearInterval(poll);
377
+ clearTimeout(forceKill);
337
378
  if (timer) clearTimeout(timer);
338
379
  process.off("SIGINT", onInt);
339
380
  process.off("SIGTERM", onTerm);
381
+ options.signal?.removeEventListener("abort", onTerm);
382
+ if (code === EXIT_UPDATE && update && !stopping) return resolve({ update });
340
383
  if (connected || !candidate.fresh || stopping) return resolve({ exit: code ?? (signal ? 1 : 0) });
341
384
  if (timedOut) return resolve({ failed: "timeout" });
342
385
  if (code === 0 || code === EXIT_REFUSED || code === EXIT_OUTDATED) return resolve({ exit: code });
@@ -344,13 +387,18 @@ function runOnce(candidate) {
344
387
  });
345
388
  });
346
389
  }
347
- async function launch(own) {
390
+ async function launch(own, options = {}) {
348
391
  const list = await candidates(own);
349
392
  for (let i = 0; i < list.length; i++) {
393
+ if (options.signal?.aborted) return 0;
350
394
  const candidate = list[i];
351
- const outcome = await runOnce(candidate);
395
+ const outcome = await runOnce(candidate, options);
352
396
  if ("exit" in outcome) return outcome.exit;
353
- if (outcome.failed === "crashed") markBad(candidate.version);
397
+ if ("update" in outcome) {
398
+ list.splice(i + 1, list.length, outcome.update, candidate);
399
+ continue;
400
+ }
401
+ markBad(candidate.version);
354
402
  const next = list[i + 1];
355
403
  if (next) status(t.rolledBack(next.version));
356
404
  }