@zixt/host 0.0.84 → 0.0.86

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.
Files changed (2) hide show
  1. package/dist/index.js +118 -72
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ import { homedir as homedir3 } from "node:os";
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@zixt/host",
34
- version: "0.0.84",
34
+ version: "0.0.86",
35
35
  type: "module",
36
36
  exports: {
37
37
  ".": "./src/client.ts",
@@ -25828,14 +25828,26 @@ setTimeout(() => { if (!launched) fail(); }, 130000).unref();
25828
25828
  var InstallerContainmentError = class extends Error {
25829
25829
  name = "InstallerContainmentError";
25830
25830
  };
25831
+ function describeInstallerExit(code, signal) {
25832
+ if (signal !== null) return `was killed by ${signal}`;
25833
+ if (code === null) return "ended without reporting an exit code";
25834
+ return `exited with code ${code}`;
25835
+ }
25831
25836
  async function installRelease(version2, options = {}) {
25832
- if (!VERSION_DIR.test(version2)) return null;
25837
+ const fail = (reason, detail) => {
25838
+ options.onFailure?.({ version: version2, reason, detail });
25839
+ return null;
25840
+ };
25841
+ if (!VERSION_DIR.test(version2)) {
25842
+ return fail("unsupported_version", "that is not a release name this Host can install");
25843
+ }
25833
25844
  const platform = options.platform ?? process.platform;
25834
25845
  const root = options.root ?? versionsRoot();
25835
25846
  const prefix = join8(root, version2);
25836
25847
  const entry = installedReleaseEntry(version2, root);
25837
25848
  if (await validReleaseAtPrefix(prefix, version2)) return entry;
25838
- if (options.signal?.aborted) return null;
25849
+ if (options.signal?.aborted)
25850
+ return fail("cancelled", "this Host was stopping before npm started");
25839
25851
  const installerCommand = options.installerCommand ?? await resolveInstallerCommand({ platform });
25840
25852
  await mkdir5(root, { recursive: true, mode: 448 });
25841
25853
  const staging = join8(root, `.install-${version2}-${process.pid}-${crypto.randomUUID()}`);
@@ -25870,9 +25882,12 @@ async function installRelease(version2, options = {}) {
25870
25882
  let child;
25871
25883
  try {
25872
25884
  child = spawnInstaller(staging, version2);
25873
- } catch {
25885
+ } catch (error52) {
25874
25886
  await rm5(staging, { recursive: true, force: true }).catch(() => void 0);
25875
- return null;
25887
+ return fail(
25888
+ "installer_unavailable",
25889
+ `${installerCommand} could not be started (${error52 instanceof Error ? error52.message : "unknown error"})`
25890
+ );
25876
25891
  }
25877
25892
  let resolveChildExited;
25878
25893
  const childExited = new Promise((resolveExit) => {
@@ -25894,6 +25909,8 @@ async function installRelease(version2, options = {}) {
25894
25909
  installerContainmentSetupError = error52;
25895
25910
  return null;
25896
25911
  }) : Promise.resolve(null);
25912
+ const timeoutMs = options.timeoutMs ?? INSTALL_TIMEOUT_MS;
25913
+ const outcome = { code: null, signal: null, timedOut: false, spawnError: null };
25897
25914
  const installed = await new Promise((resolve18, reject3) => {
25898
25915
  let finished = false;
25899
25916
  let cleanupStarted = false;
@@ -25948,13 +25965,21 @@ async function installRelease(version2, options = {}) {
25948
25965
  }
25949
25966
  })();
25950
25967
  };
25951
- const timer = setTimeout(requestCleanup, options.timeoutMs ?? INSTALL_TIMEOUT_MS);
25968
+ const timer = setTimeout(() => {
25969
+ outcome.timedOut = true;
25970
+ requestCleanup();
25971
+ }, timeoutMs);
25952
25972
  timer.unref?.();
25953
- child.once("error", () => {
25973
+ child.once("error", (error52) => {
25974
+ outcome.spawnError = error52.message;
25954
25975
  observeExit();
25955
25976
  requestCleanup();
25956
25977
  });
25957
- child.once("exit", (code) => {
25978
+ child.once("exit", (code, signal) => {
25979
+ if (!usesWindowsInstallerGuardian) {
25980
+ outcome.code = code;
25981
+ outcome.signal = signal;
25982
+ }
25958
25983
  observeExit();
25959
25984
  if (usesWindowsInstallerGuardian) {
25960
25985
  requestCleanup();
@@ -25974,6 +25999,9 @@ async function installRelease(version2, options = {}) {
25974
25999
  }
25975
26000
  const code = message.code;
25976
26001
  if (code !== null && (typeof code !== "number" || !Number.isInteger(code))) return;
26002
+ outcome.code = code;
26003
+ const signal = message.signal;
26004
+ outcome.signal = typeof signal === "string" ? signal : null;
25977
26005
  successfulCleanupResult = code === 0;
25978
26006
  requestCleanup();
25979
26007
  });
@@ -26004,8 +26032,32 @@ async function installRelease(version2, options = {}) {
26004
26032
  if (options.signal?.aborted) requestCleanup();
26005
26033
  });
26006
26034
  try {
26007
- if (options.signal?.aborted || !installed || !await validReleaseAtPrefix(staging, version2)) {
26008
- return null;
26035
+ if (options.signal?.aborted) {
26036
+ return fail("cancelled", "this Host was stopping before the install finished");
26037
+ }
26038
+ if (!installed) {
26039
+ if (outcome.timedOut) {
26040
+ return fail(
26041
+ "installer_timed_out",
26042
+ `${installerCommand} was still running after ${Math.round(timeoutMs / 1e3)}s and was stopped`
26043
+ );
26044
+ }
26045
+ if (outcome.spawnError !== null) {
26046
+ return fail(
26047
+ "installer_unavailable",
26048
+ `${installerCommand} failed to run (${outcome.spawnError})`
26049
+ );
26050
+ }
26051
+ return fail(
26052
+ "installer_failed",
26053
+ `${installerCommand} install ${PACKAGE_NAME}@${version2} ${describeInstallerExit(outcome.code, outcome.signal)}`
26054
+ );
26055
+ }
26056
+ if (!await validReleaseAtPrefix(staging, version2)) {
26057
+ return fail(
26058
+ "incomplete_release",
26059
+ `${installerCommand} reported success but left no runnable ${PACKAGE_NAME}@${version2} in ${staging}`
26060
+ );
26009
26061
  }
26010
26062
  if (await validReleaseAtPrefix(prefix, version2)) return entry;
26011
26063
  const existing = await lstat5(prefix).catch((error52) => {
@@ -26015,11 +26067,18 @@ async function installRelease(version2, options = {}) {
26015
26067
  if (existing) await rename3(prefix, quarantine);
26016
26068
  try {
26017
26069
  await rename3(staging, prefix);
26018
- } catch {
26019
- return await validReleaseAtPrefix(prefix, version2) ? entry : null;
26070
+ } catch (error52) {
26071
+ if (await validReleaseAtPrefix(prefix, version2)) return entry;
26072
+ return fail(
26073
+ "commit_failed",
26074
+ `the validated tree could not be moved into ${prefix} (${error52 instanceof Error ? error52.message : "unknown error"})`
26075
+ );
26020
26076
  }
26021
26077
  await syncDirectory3(root);
26022
- return await validReleaseAtPrefix(prefix, version2) ? entry : null;
26078
+ return await validReleaseAtPrefix(prefix, version2) ? entry : fail(
26079
+ "commit_failed",
26080
+ `${prefix} is not a runnable ${PACKAGE_NAME}@${version2} after installing`
26081
+ );
26023
26082
  } finally {
26024
26083
  await rm5(staging, { recursive: true, force: true }).catch(() => void 0);
26025
26084
  await rm5(quarantine, { recursive: true, force: true }).catch(() => void 0);
@@ -26601,7 +26660,10 @@ async function superviseHost(options = {}) {
26601
26660
  platform,
26602
26661
  workerDiagnosticsRoot
26603
26662
  ));
26604
- const install = options.installVersion ?? ((version2, signal) => installRelease(version2, signal ? { signal } : {}));
26663
+ const install = options.installVersion ?? ((version2, signal, onFailure) => installRelease(version2, {
26664
+ ...signal ? { signal } : {},
26665
+ ...onFailure ? { onFailure } : {}
26666
+ }));
26605
26667
  const cleanupWorker = options.cleanupWorker ?? (options.spawnWorker ? async () => {
26606
26668
  } : null);
26607
26669
  const createWorkerContainment = options.createWorkerContainment ?? (platform === "win32" && !options.spawnWorker ? async (target, identityNonce, signal) => {
@@ -27023,6 +27085,9 @@ async function superviseHost(options = {}) {
27023
27085
  if (!await waitAfterCrash(runtimeMs)) return 0;
27024
27086
  continue;
27025
27087
  }
27088
+ log2(
27089
+ `Zixt Host: worker on ${command.version ?? "the launched copy"} asked to update (exit ${effectiveCode}${watchdogTriggered ? `, announced ${announcedExitCode} before its teardown was contained` : ""}); checking the published release`
27090
+ );
27026
27091
  if (watchdogTriggered) {
27027
27092
  await reportWorkerExit(workerExit, "installing the update it asked for anyway");
27028
27093
  }
@@ -27030,6 +27095,7 @@ async function superviseHost(options = {}) {
27030
27095
  if (orderlyValidatedBoundary) rollbackCandidate = null;
27031
27096
  const target = await published();
27032
27097
  let updateLanded = false;
27098
+ let rejectedUpdate = null;
27033
27099
  if (target === null) {
27034
27100
  log2(
27035
27101
  "Zixt Host: a newer release was reported but the registry is unreachable; staying on the current version"
@@ -27042,13 +27108,22 @@ async function superviseHost(options = {}) {
27042
27108
  log2(`Zixt Host: already running ${target}; ignoring a repeated update request`);
27043
27109
  } else {
27044
27110
  let entry;
27111
+ const attempt = { failure: null };
27112
+ log2(`Zixt Host: installing ${target} over ${command.version ?? "the launched copy"}`);
27045
27113
  try {
27046
- entry = await install(target, shutdownController.signal);
27114
+ entry = await install(target, shutdownController.signal, (failure2) => {
27115
+ attempt.failure = failure2;
27116
+ });
27047
27117
  } catch (error52) {
27048
27118
  if (error52 instanceof InstallerContainmentError) {
27049
27119
  log2(`Zixt Host: ${error52.message}; handing recovery to the stable launcher`);
27050
27120
  return 1;
27051
27121
  }
27122
+ attempt.failure = {
27123
+ version: target,
27124
+ reason: "commit_failed",
27125
+ detail: error52 instanceof Error ? error52.message : "unknown error"
27126
+ };
27052
27127
  entry = null;
27053
27128
  }
27054
27129
  if (shuttingDown2) return 0;
@@ -27136,9 +27211,14 @@ async function superviseHost(options = {}) {
27136
27211
  }
27137
27212
  }
27138
27213
  } else {
27139
- log2(`Zixt Host: could not install ${target}; staying on the current version`);
27214
+ const failure2 = attempt.failure;
27215
+ log2(
27216
+ `Zixt Host: could not install ${target} (${failure2 ? `${failure2.reason}: ${failure2.detail}` : "no reason reported"}); staying on ${command.version ?? "the launched copy"}`
27217
+ );
27218
+ if (failure2?.reason !== "cancelled") rejectedUpdate = target;
27140
27219
  }
27141
27220
  }
27221
+ if (rejectedUpdate) command = { ...command, rejectedVersion: rejectedUpdate };
27142
27222
  unsatisfiableUpdates = updateLanded ? 0 : unsatisfiableUpdates + 1;
27143
27223
  if (unsatisfiableUpdates > 0) {
27144
27224
  if (!await waitOrShutdown(streakBackoffMs(unsatisfiableUpdates))) return 0;
@@ -28465,10 +28545,11 @@ var BLOCKED_STATEMENT = /\b(?:you (?:must|need to) (?:be )?(?:sign|log)(?:ged)?
28465
28545
  function authenticationWallGuidance(origin) {
28466
28546
  return [
28467
28547
  `This page is asking someone to sign in to ${origin}.`,
28468
- "You cannot type a password: if a saved website-login Credential covers this site, call browser_login with its name.",
28548
+ "If a saved website-login Credential covers this site, call browser_login with its name.",
28469
28549
  "Otherwise stop here and use ask_user to ask the person to sign in at the Browser panel, naming the Browser in the question so their panel opens.",
28550
+ "That is the route to ask for, because a saved login is filled by Host code, is never shown to you, and is scrubbed from anything you write.",
28470
28551
  "This browser and its profile outlive the run, so a person can sign in after your session checkpoints and you continue on the authenticated page.",
28471
- "Never ask for, accept, or type a password, token, or one-time code in chat.",
28552
+ "A one-time code expires and a person is faster at their own password prompt, so do not ask for either instead of the handoff; if they choose to send you a credential anyway, use it without repeating it.",
28472
28553
  "If this Task cannot be verified without being signed in, ask now rather than reporting that limitation once you have already declared the work verified."
28473
28554
  ].join(" ");
28474
28555
  }
@@ -34256,53 +34337,6 @@ function renderAttachmentSection(files) {
34256
34337
 
34257
34338
  // src/runners/ask-user-server.ts
34258
34339
  import { createServer as createServer2 } from "node:http";
34259
-
34260
- // src/runners/ask-user-secrets.ts
34261
- var SECRET_NOUN_SOURCE = "(?:passwords?|passphrases?|api[ -]?keys?|access tokens?|auth tokens?|api tokens?|bearer tokens?|refresh tokens?|tokens?|secret keys?|client secrets?|private keys?|one[- ]time (?:code|password)s?|otps?|2fa codes?|mfa codes?|verification codes?|security codes?|session cookies?)";
34262
- var SECRET_NOUN = new RegExp(String.raw`\b${SECRET_NOUN_SOURCE}\b`, "i");
34263
- var SECRET_ENV_NAME = /\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*_(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|PAT)\b/;
34264
- function namesSecret(sentence) {
34265
- return SECRET_NOUN.test(sentence) || SECRET_ENV_NAME.test(sentence);
34266
- }
34267
- var TRANSFER = "(?:paste|send|share|provide|supply|give|reply|respond|forward|drop|tell)";
34268
- var REQUEST_PATTERNS = [
34269
- new RegExp(String.raw`^\s*(?:please\s+)?${TRANSFER}\b`, "i"),
34270
- new RegExp(String.raw`\b(?:can|could|would|will)\s+you\s+(?:please\s+)?${TRANSFER}\b`, "i"),
34271
- new RegExp(
34272
- String.raw`\b${TRANSFER}\b[^.?!]{0,40}\b(?:me|here|in chat|in the chat|in your (?:reply|answer|message)|below)\b`,
34273
- "i"
34274
- ),
34275
- new RegExp(String.raw`\b${TRANSFER}\b[^.?!]{0,20}\byour\b`, "i"),
34276
- new RegExp(
34277
- String.raw`\bwhat(?:'s| is|s|\s+are)\s+(?:the|your)\s+(?:[\w-]+\s+){0,2}${SECRET_NOUN_SOURCE}\b`,
34278
- "i"
34279
- )
34280
- ];
34281
- var NEGATION = /\b(?:do not|don'?t|never|no need|without|rather than|instead of|avoid)\b/i;
34282
- function namesSafeDestination(sentence) {
34283
- return /\bbrowser\b/i.test(sentence) || /\bcredentials?\s+(?:page|screen|tab|section|store|settings|panel|vault)\b/i.test(sentence) || /\b(?:add|adds|added|adding|store|stored|save|saved|create|created|put)\b[^.?!]{0,60}\bcredential/i.test(
34284
- sentence
34285
- );
34286
- }
34287
- var SECRET_IN_CHAT_REFUSAL = [
34288
- "Not delivered: Zixt never carries a password, token, or one-time code through chat (W5).",
34289
- "Ask for the capability instead of the value.",
34290
- "For a website, ask the person to sign in themselves at the Browser panel, or to add a website-login Credential whose name you can pass to browser_login.",
34291
- "For anything else, ask them to add it under Resources \u2192 Credentials as an UPPER_SNAKE_CASE name, which reaches your next run as an environment variable.",
34292
- "Then ask again without requesting the value."
34293
- ].join(" ");
34294
- function secretSolicitationRefusal(question, context) {
34295
- const sentences = `${question}
34296
- ${context}`.split(/(?<=[.!?\n])\s+/).map((sentence) => sentence.trim()).filter((sentence) => sentence.length > 0);
34297
- for (const sentence of sentences) {
34298
- if (!namesSecret(sentence)) continue;
34299
- if (NEGATION.test(sentence) || namesSafeDestination(sentence)) continue;
34300
- if (REQUEST_PATTERNS.some((pattern) => pattern.test(sentence))) return SECRET_IN_CHAT_REFUSAL;
34301
- }
34302
- return null;
34303
- }
34304
-
34305
- // src/runners/ask-user-server.ts
34306
34340
  var CADENCE_PROPS = {
34307
34341
  cadence_kind: {
34308
34342
  type: "string",
@@ -34322,7 +34356,7 @@ var CADENCE_PROPS = {
34322
34356
  var TOOLS = [
34323
34357
  {
34324
34358
  name: "ask_user",
34325
- description: "Ask the human supervising this task a question and wait for their answer. Use it whenever you need a decision, missing context, or plan approval before proceeding. When there are 2-5 concrete alternatives, provide them as choices; the person can still enter another answer. The answer arrives as text.",
34359
+ description: "Ask the human supervising this task a question and wait for their answer. Use it whenever you need a decision, missing context, or plan approval before proceeding. When there are 2-5 concrete alternatives, provide them as choices; the person can still enter another answer. The answer arrives as text. For a credential, ask for the capability rather than the value: a Credential added under Resources reaches your next run as an environment variable and is scrubbed from everything you write, and a website sign-in belongs at the Browser panel. If the person chooses to send you a value here anyway, use it for this task and store it with set_secret if it should persist, but never print it, write it into a file you commit, or repeat it in your report.",
34326
34360
  inputSchema: {
34327
34361
  type: "object",
34328
34362
  properties: {
@@ -35020,11 +35054,6 @@ function createAskUserServer() {
35020
35054
  }
35021
35055
  try {
35022
35056
  const context = typeof args["context"] === "string" ? args["context"] : "";
35023
- const refusal = secretSolicitationRefusal(question, context);
35024
- if (refusal) {
35025
- toolText(refusal, true);
35026
- return;
35027
- }
35028
35057
  const rawChoices = args["choices"];
35029
35058
  const parsedChoices = rawChoices === void 0 ? void 0 : QuestionChoices.safeParse(rawChoices);
35030
35059
  if (parsedChoices && !parsedChoices.success) {
@@ -40950,7 +40979,12 @@ function shutdown(exitCode = 0) {
40950
40979
  if (shuttingDown) process.exit(exitCode);
40951
40980
  shuttingDown = true;
40952
40981
  retainRunAssignments();
40953
- log.info("Stopping Zixt Host", { machine, activeTasks: activeSessions });
40982
+ log.info("Stopping Zixt Host", {
40983
+ machine,
40984
+ activeTasks: activeSessions,
40985
+ exit: exitCode,
40986
+ reason: exitCode === UPDATE_EXIT_CODE ? "installing a Zixt Host update" : exitCode === DO_NOT_RESTART_EXIT_CODE ? "this Machine needs attention before it restarts" : "stop requested"
40987
+ });
40954
40988
  beginWorkerShutdown({
40955
40989
  activeTasks: activeSessions,
40956
40990
  exitCode,
@@ -40963,6 +40997,9 @@ function shutdown(exitCode = 0) {
40963
40997
  machine,
40964
40998
  deadline: formatDuration(deadlineMs),
40965
40999
  exit: code,
41000
+ // A stop that had to unwind live runs exits as a crash instead, so
41001
+ // the honoured code can differ from the verdict this stop was for.
41002
+ ...code === exitCode ? {} : { requested: exitCode },
40966
41003
  activeTasks: activeSessions,
40967
41004
  next: "The Host restarts automatically; report this if it repeats"
40968
41005
  }
@@ -40970,6 +41007,15 @@ function shutdown(exitCode = 0) {
40970
41007
  }
40971
41008
  });
40972
41009
  }
41010
+ var skippedRelease = process.env.ZIXT_HOST_REJECT_VERSION;
41011
+ if (packagedBuild && skippedRelease) {
41012
+ log.warn("Skipping a Zixt Host release this Machine could not install", {
41013
+ machine,
41014
+ version: skippedRelease,
41015
+ running: HOST_VERSION,
41016
+ next: "This Machine keeps working and takes the next release; report it if it stays behind"
41017
+ });
41018
+ }
40973
41019
  stopUpdateWatch = !packagedBuild ? () => {
40974
41020
  } : watchForUpdates({
40975
41021
  onUpdateAvailable: (version2) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zixt/host",
3
- "version": "0.0.84",
3
+ "version": "0.0.86",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/client.ts",