@kontextmind/kxm 0.7.167 → 0.7.168

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.
@@ -9409,7 +9409,7 @@ var init_config = __esm({
9409
9409
  });
9410
9410
 
9411
9411
  // plugins/kxm/src/hub-binding.ts
9412
- import { spawnSync } from "node:child_process";
9412
+ import { spawnSync as spawnSync2 } from "node:child_process";
9413
9413
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
9414
9414
  import { homedir as homedir2 } from "node:os";
9415
9415
  import { dirname as dirname3, isAbsolute, join as join4, resolve as resolve2 } from "node:path";
@@ -9432,6 +9432,9 @@ function resolveUserStateRoot(env) {
9432
9432
  function hubBindingFile(env = process.env) {
9433
9433
  return join4(resolveUserStateRoot(env), "hub-binding.json");
9434
9434
  }
9435
+ function hubBindingCloudFile(env = process.env) {
9436
+ return join4(resolveUserStateRoot(env), "hub-binding.cloud.json");
9437
+ }
9435
9438
  function validateHubUrl(raw) {
9436
9439
  let parsed;
9437
9440
  try {
@@ -9517,7 +9520,7 @@ function resolveCloudHubToken(binding, env = process.env) {
9517
9520
  function runTokenCommand(command, env) {
9518
9521
  const argv = splitTokenCommand(command);
9519
9522
  const [program2, ...args] = argv;
9520
- const result = spawnSync(program2, args, {
9523
+ const result = spawnSync2(program2, args, {
9521
9524
  encoding: "utf8",
9522
9525
  shell: false,
9523
9526
  timeout: TOKEN_COMMAND_TIMEOUT_MS,
@@ -9560,33 +9563,35 @@ function isAbortError(error) {
9560
9563
  error && typeof error === "object" && ("name" in error && error.name === "AbortError" || "code" in error && error.code === "ABORT_ERR")
9561
9564
  );
9562
9565
  }
9563
- function readHubBinding(env = process.env) {
9564
- const file = hubBindingFile(env);
9565
- if (!existsSync4(file)) return void 0;
9566
+ function bindingFallbackWarning(file, reason) {
9567
+ return `hub binding at ${file} ${reason}; falling back to the local hub. Restart the Runtime supervisor, the hub, and workers after upgrading and before a cloud bind.`;
9568
+ }
9569
+ function writeBindingJson(file, value) {
9570
+ mkdirSync3(dirname3(file), { recursive: true, mode: 448 });
9571
+ const temporary = join4(dirname3(file), `.${file.endsWith("cloud.json") ? "hub-binding-cloud" : "hub-binding"}-${process.pid}.tmp`);
9572
+ try {
9573
+ writeFileSync3(temporary, `${JSON.stringify(value, null, 2)}
9574
+ `, { encoding: "utf8", mode: 384 });
9575
+ renameSync(temporary, file);
9576
+ } finally {
9577
+ rmSync(temporary, { force: true });
9578
+ }
9579
+ }
9580
+ function parseBindingObject(file) {
9581
+ if (!existsSync4(file)) return new HubBindingError(`missing hub binding at ${file}`);
9566
9582
  let parsed;
9567
9583
  try {
9568
9584
  parsed = JSON.parse(readFileSync3(file, "utf8"));
9569
9585
  } catch {
9570
- throw new HubBindingError(`malformed hub binding at ${file}`);
9586
+ return new HubBindingError(`malformed hub binding at ${file}`);
9571
9587
  }
9572
9588
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
9573
- throw new HubBindingError(`malformed hub binding at ${file}`);
9589
+ return new HubBindingError(`malformed hub binding at ${file}`);
9574
9590
  }
9575
- const row = parsed;
9576
- const keys = Object.keys(row);
9577
- const allowed = /* @__PURE__ */ new Set(["schema", "url", "boundAt", "cloud", "tokenEnv", "tokenCommand"]);
9578
- const cloudish = keys.some((key) => key === "cloud" || key === "tokenEnv" || key === "tokenCommand");
9579
- if (keys.some((key) => !allowed.has(key)) || !cloudish && keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
9580
- throw new HubBindingError(`malformed hub binding at ${file}`);
9581
- }
9582
- let url;
9583
- try {
9584
- url = validateHubUrl(row.url);
9585
- } catch {
9586
- throw new HubBindingError(`malformed hub binding at ${file}`);
9587
- }
9588
- if (!cloudish) return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
9589
- if (row.cloud !== true) throw new HubBindingError(`malformed hub binding at ${file}`);
9591
+ return parsed;
9592
+ }
9593
+ function cloudRecordFromRow(file, row, url, boundAt) {
9594
+ if (row.cloud !== true) return void 0;
9590
9595
  let tokenEnv;
9591
9596
  let tokenCommand;
9592
9597
  if (row.tokenEnv !== void 0) {
@@ -9608,33 +9613,203 @@ function readHubBinding(env = process.env) {
9608
9613
  return {
9609
9614
  schema: HUB_BINDING_SCHEMA,
9610
9615
  url,
9611
- boundAt: row.boundAt,
9616
+ boundAt,
9612
9617
  cloud: true,
9613
9618
  ...tokenEnv ? { tokenEnv } : {},
9614
9619
  ...tokenCommand ? { tokenCommand } : {}
9615
9620
  };
9616
9621
  }
9617
- function writeHubBinding(record, env = process.env) {
9622
+ function readLegacyV1HubBinding(env = process.env) {
9618
9623
  const file = hubBindingFile(env);
9619
- mkdirSync3(dirname3(file), { recursive: true, mode: 448 });
9620
- const temporary = join4(dirname3(file), `.hub-binding-${process.pid}.tmp`);
9624
+ if (!existsSync4(file)) return void 0;
9625
+ const parsed = parseBindingObject(file);
9626
+ if (parsed instanceof HubBindingError) throw parsed;
9627
+ const keys = Object.keys(parsed);
9628
+ if (keys.length !== 3 || keys.some((key) => !LEGACY_BINDING_KEYS.includes(key)) || parsed.schema !== HUB_BINDING_SCHEMA || typeof parsed.url !== "string" || typeof parsed.boundAt !== "string" || !isIsoTimestamp(parsed.boundAt)) {
9629
+ throw new HubBindingError(`malformed hub binding at ${file}`);
9630
+ }
9631
+ let url;
9621
9632
  try {
9622
- writeFileSync3(temporary, `${JSON.stringify(record, null, 2)}
9623
- `, { encoding: "utf8", mode: 384 });
9624
- renameSync(temporary, file);
9625
- } finally {
9626
- rmSync(temporary, { force: true });
9633
+ url = validateHubUrl(parsed.url);
9634
+ } catch {
9635
+ throw new HubBindingError(`malformed hub binding at ${file}`);
9627
9636
  }
9637
+ return { schema: HUB_BINDING_SCHEMA, url, boundAt: parsed.boundAt };
9638
+ }
9639
+ function interpretBindingFile(file) {
9640
+ if (!existsSync4(file)) return {};
9641
+ const parsed = parseBindingObject(file);
9642
+ if (parsed instanceof HubBindingError) throw parsed;
9643
+ const keys = Object.keys(parsed);
9644
+ const unknown = keys.filter((key) => !BINDING_KEYS.has(key));
9645
+ if (parsed.schema !== HUB_BINDING_SCHEMA || unknown.length > 0) {
9646
+ return { warning: bindingFallbackWarning(file, `is newer than this build understands (${String(parsed.schema)})`) };
9647
+ }
9648
+ if (typeof parsed.url !== "string" || typeof parsed.boundAt !== "string" || !isIsoTimestamp(parsed.boundAt)) {
9649
+ throw new HubBindingError(`malformed hub binding at ${file}`);
9650
+ }
9651
+ let url;
9652
+ try {
9653
+ url = validateHubUrl(parsed.url);
9654
+ } catch {
9655
+ throw new HubBindingError(`malformed hub binding at ${file}`);
9656
+ }
9657
+ const cloudish = keys.some((key) => key === "cloud" || key === "tokenEnv" || key === "tokenCommand");
9658
+ if (!cloudish) {
9659
+ if (keys.length !== 3) throw new HubBindingError(`malformed hub binding at ${file}`);
9660
+ return { record: { schema: HUB_BINDING_SCHEMA, url, boundAt: parsed.boundAt } };
9661
+ }
9662
+ const record = cloudRecordFromRow(file, parsed, url, parsed.boundAt);
9663
+ if (!record) throw new HubBindingError(`malformed hub binding at ${file}`);
9664
+ return { record, legacyCloud: true };
9665
+ }
9666
+ function loadHubBinding(env = process.env) {
9667
+ const cloudFile = hubBindingCloudFile(env);
9668
+ const cloud = interpretBindingFile(cloudFile);
9669
+ if (cloud.warning && !cloud.record) {
9670
+ const local = existsSync4(hubBindingFile(env)) ? interpretBindingFile(hubBindingFile(env)) : {};
9671
+ if (local.record && !local.record.cloud) return { record: local.record, warning: cloud.warning };
9672
+ return { warning: cloud.warning };
9673
+ }
9674
+ if (cloud.record?.cloud) return { record: cloud.record, ...cloud.warning ? { warning: cloud.warning } : {} };
9675
+ const main = interpretBindingFile(hubBindingFile(env));
9676
+ if (main.legacyCloud && main.record) {
9677
+ writeBindingJson(cloudFile, main.record);
9678
+ rmSync(hubBindingFile(env), { force: true });
9679
+ return {
9680
+ record: main.record,
9681
+ warning: bindingFallbackWarning(
9682
+ hubBindingFile(env),
9683
+ "stored a cloud bind in the pre-0.7.160 file"
9684
+ )
9685
+ };
9686
+ }
9687
+ return { ...main.record ? { record: main.record } : {}, ...main.warning ? { warning: main.warning } : {} };
9688
+ }
9689
+ function readHubBinding(env = process.env) {
9690
+ return loadHubBinding(env).record;
9691
+ }
9692
+ function writeHubBinding(record, env = process.env) {
9693
+ if (record.cloud) {
9694
+ const file2 = hubBindingCloudFile(env);
9695
+ writeBindingJson(file2, record);
9696
+ const main = hubBindingFile(env);
9697
+ if (existsSync4(main)) {
9698
+ try {
9699
+ const legacy = readLegacyV1HubBinding(env);
9700
+ if (!legacy || legacy.url === record.url) rmSync(main, { force: true });
9701
+ } catch (error) {
9702
+ if (!(error instanceof HubBindingError)) throw error;
9703
+ rmSync(main, { force: true });
9704
+ }
9705
+ }
9706
+ return file2;
9707
+ }
9708
+ rmSync(hubBindingCloudFile(env), { force: true });
9709
+ const file = hubBindingFile(env);
9710
+ writeBindingJson(file, {
9711
+ schema: HUB_BINDING_SCHEMA,
9712
+ url: record.url,
9713
+ boundAt: record.boundAt
9714
+ });
9628
9715
  return file;
9629
9716
  }
9630
9717
  function removeHubBinding(env = process.env) {
9631
- const file = hubBindingFile(env);
9718
+ const files = [hubBindingFile(env), hubBindingCloudFile(env)];
9719
+ let removed = false;
9720
+ for (const file of files) {
9721
+ try {
9722
+ rmSync(file);
9723
+ removed = true;
9724
+ } catch (error) {
9725
+ if (error.code !== "ENOENT") throw error;
9726
+ }
9727
+ }
9728
+ return removed;
9729
+ }
9730
+ function contentTypeBase(header) {
9731
+ return (header ?? "").split(";")[0].trim().toLowerCase();
9732
+ }
9733
+ function looksLikeHtml(contentType, body) {
9734
+ if (contentType === "text/html" || contentType === "application/xhtml+xml") return true;
9735
+ const sample = body.slice(0, 800).trim().toLowerCase();
9736
+ return sample.startsWith("<!doctype html") || sample.startsWith("<html") || sample.includes("<form") && (sample.includes("login") || sample.includes("authentik") || sample.includes("password"));
9737
+ }
9738
+ function jsonObject(text) {
9632
9739
  try {
9633
- rmSync(file);
9634
- return true;
9740
+ const parsed = JSON.parse(text);
9741
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return void 0;
9742
+ return parsed;
9743
+ } catch {
9744
+ return void 0;
9745
+ }
9746
+ }
9747
+ function hubEndpointShape(role, body) {
9748
+ if (role === "health") return body.ok === true && typeof body.agents === "number";
9749
+ return body.ok === true && (body.storage === "sqlite" || body.storage === "memory");
9750
+ }
9751
+ function classifyHubResponse(input) {
9752
+ const contentType = contentTypeBase(input.contentType);
9753
+ if (input.status >= 300 && input.status < 400) {
9754
+ return {
9755
+ ok: false,
9756
+ status: input.status,
9757
+ kind: "redirect",
9758
+ detail: `HTTP ${input.status} redirect; this looks like an auth proxy or a redirect, not a kxm hub`,
9759
+ body: { error: "auth_proxy", detail: `HTTP ${input.status} redirect` }
9760
+ };
9761
+ }
9762
+ if (looksLikeHtml(contentType, input.bodyText)) {
9763
+ return {
9764
+ ok: false,
9765
+ status: input.status,
9766
+ kind: "auth_proxy",
9767
+ detail: `HTTP ${input.status} returned an HTML login page${contentType ? ` (${contentType})` : ""}; this looks like an auth proxy, not a kxm hub`,
9768
+ body: { error: "auth_proxy", detail: "HTML login page" }
9769
+ };
9770
+ }
9771
+ const jsonType = contentType === "application/json" || contentType === "text/plain" || contentType === "";
9772
+ const body = jsonType ? jsonObject(input.bodyText) : void 0;
9773
+ if (body && hubEndpointShape(input.role, body) && input.status >= 200 && input.status < 300) {
9774
+ if (contentType !== "" && contentType !== "application/json" && contentType !== "text/plain") {
9775
+ return {
9776
+ ok: false,
9777
+ status: input.status,
9778
+ kind: "not_hub",
9779
+ detail: `HTTP ${input.status} content-type ${contentType} is not application/json`,
9780
+ body: { error: "not_hub", detail: contentType }
9781
+ };
9782
+ }
9783
+ return { ok: true, status: input.status, kind: "hub", detail: "kxm hub", body };
9784
+ }
9785
+ const typeNote = contentType && contentType !== "application/json" ? ` content-type ${contentType}` : "";
9786
+ return {
9787
+ ok: false,
9788
+ status: input.status,
9789
+ kind: "not_hub",
9790
+ detail: `HTTP ${input.status}${typeNote} is not a kxm hub ${input.role} response`,
9791
+ body: { error: "not_hub", detail: `HTTP ${input.status}${typeNote}` }
9792
+ };
9793
+ }
9794
+ async function readHubEndpoint(url, role, fetchImpl, init = {}) {
9795
+ try {
9796
+ const response = await fetchImpl(`${url}/${role}`, {
9797
+ ...init.headers ? { headers: init.headers } : {},
9798
+ ...init.signal ? { signal: init.signal } : {},
9799
+ redirect: "manual"
9800
+ });
9801
+ const bodyText = await response.text();
9802
+ return classifyHubResponse({
9803
+ role,
9804
+ status: response.status,
9805
+ contentType: response.headers.get("content-type"),
9806
+ bodyText
9807
+ });
9635
9808
  } catch (error) {
9636
- if (error.code === "ENOENT") return false;
9637
- throw error;
9809
+ if (isAbortError(error)) {
9810
+ return { ok: false, status: 0, kind: "timeout", detail: "no reply within the probe budget", body: { error: "hub_unreachable" } };
9811
+ }
9812
+ return { ok: false, status: 0, kind: "unreachable", detail: "nothing answered", body: { error: "hub_unreachable" } };
9638
9813
  }
9639
9814
  }
9640
9815
  async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
@@ -9642,25 +9817,14 @@ async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
9642
9817
  const controller = new AbortController();
9643
9818
  const timer = setTimeout(() => controller.abort(), timeoutMs);
9644
9819
  try {
9645
- const response = await fetchImpl(`${url}/health`, { signal: controller.signal });
9646
- if (!response.ok) return { health: "unknown", probeMs: Date.now() - started };
9647
- let body;
9648
- try {
9649
- body = await response.json();
9650
- } catch {
9651
- return { health: "unknown", probeMs: Date.now() - started };
9652
- }
9653
- if (body && typeof body === "object" && body.ok === true) {
9654
- return { health: "on", probeMs: Date.now() - started };
9655
- }
9656
- return { health: "unknown", probeMs: Date.now() - started };
9657
- } catch (error) {
9658
- return { health: isAbortError(error) ? "unknown" : "off", probeMs: Date.now() - started };
9820
+ const verdict = await readHubEndpoint(url, "health", fetchImpl, { signal: controller.signal });
9821
+ const health = verdict.kind === "hub" ? "on" : verdict.kind === "unreachable" ? "off" : "unknown";
9822
+ return { health, probeMs: Date.now() - started, kind: verdict.kind, detail: verdict.detail, status: verdict.status };
9659
9823
  } finally {
9660
9824
  clearTimeout(timer);
9661
9825
  }
9662
9826
  }
9663
- var HUB_BINDING_SCHEMA, HUB_HEALTH_PROBE_MS, CloudTokenError, TOKEN_ENV_NAME, TOKEN_COMMAND_TIMEOUT_MS, TOKEN_COMMAND_MAX_BUFFER, HubBindingError;
9827
+ var HUB_BINDING_SCHEMA, HUB_HEALTH_PROBE_MS, CloudTokenError, TOKEN_ENV_NAME, TOKEN_COMMAND_TIMEOUT_MS, TOKEN_COMMAND_MAX_BUFFER, HubBindingError, LEGACY_BINDING_KEYS, BINDING_KEYS;
9664
9828
  var init_hub_binding = __esm({
9665
9829
  "plugins/kxm/src/hub-binding.ts"() {
9666
9830
  "use strict";
@@ -9683,11 +9847,13 @@ var init_hub_binding = __esm({
9683
9847
  this.name = "HubBindingError";
9684
9848
  }
9685
9849
  };
9850
+ LEGACY_BINDING_KEYS = ["schema", "url", "boundAt"];
9851
+ BINDING_KEYS = /* @__PURE__ */ new Set([...LEGACY_BINDING_KEYS, "cloud", "tokenEnv", "tokenCommand"]);
9686
9852
  }
9687
9853
  });
9688
9854
 
9689
9855
  // plugins/kxm/src/hub-identity.ts
9690
- import { spawnSync as spawnSync2 } from "node:child_process";
9856
+ import { spawnSync as spawnSync3 } from "node:child_process";
9691
9857
  import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
9692
9858
  import { basename, dirname as dirname4, join as join5, resolve as resolve3 } from "node:path";
9693
9859
  function normalizeUrl(raw) {
@@ -9768,7 +9934,7 @@ function defaultOpRead(reference, env) {
9768
9934
  if (!isHubOpReference(reference)) {
9769
9935
  throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
9770
9936
  }
9771
- const result = spawnSync2("op", ["read", reference], {
9937
+ const result = spawnSync3("op", ["read", reference], {
9772
9938
  encoding: "utf8",
9773
9939
  shell: false,
9774
9940
  timeout: OP_TIMEOUT_MS,
@@ -9808,18 +9974,24 @@ function resolveKeyReference(ref, env, opRead = defaultOpRead) {
9808
9974
  }
9809
9975
  function keySourceFor(input) {
9810
9976
  const ref = input.endpoint?.key;
9811
- if (input.mode === "local" && input.env.KXM_AUTH_TOKEN?.trim()) return "env:KXM_AUTH_TOKEN";
9812
- if (ref?.env && input.env[ref.env]?.trim()) return `env:${ref.env}`;
9813
- if (ref?.op) return `op:${ref.op}`;
9814
- if (ref?.env) return `env:${ref.env}`;
9977
+ const envName = (name) => {
9978
+ if (!name) return void 0;
9979
+ return input.env[name]?.trim() ? `env:${name}` : void 0;
9980
+ };
9815
9981
  if (input.mode === "cloud") {
9816
- if (input.binding?.tokenEnv) return `env:${input.binding.tokenEnv}`;
9817
- if (input.binding?.tokenCommand) {
9818
- const op = opReferenceFromTokenCommand(input.binding.tokenCommand);
9819
- return op ? `op:${op}` : "command";
9820
- }
9982
+ const fromEnv2 = envName(ref?.env);
9983
+ if (fromEnv2) return fromEnv2;
9984
+ if (ref?.op) return `op:${ref.op}`;
9985
+ const fromBindingEnv = envName(input.binding?.tokenEnv);
9986
+ if (fromBindingEnv) return fromBindingEnv;
9987
+ if (input.binding?.tokenCommand) return "token-command";
9821
9988
  return "missing";
9822
9989
  }
9990
+ const fromAuth = envName("KXM_AUTH_TOKEN");
9991
+ if (fromAuth) return fromAuth;
9992
+ const fromEnv = envName(ref?.env);
9993
+ if (fromEnv) return fromEnv;
9994
+ if (ref?.op) return `op:${ref.op}`;
9823
9995
  if (input.hasSavedProjectToken?.(input.project)) return "hub-env";
9824
9996
  return "missing";
9825
9997
  }
@@ -18919,7 +19091,7 @@ var init_oneshot_process = __esm({
18919
19091
  });
18920
19092
 
18921
19093
  // plugins/kxm/src/harness.ts
18922
- import { spawnSync as spawnSync3 } from "node:child_process";
19094
+ import { spawnSync as spawnSync4 } from "node:child_process";
18923
19095
  import { existsSync as existsSync9 } from "node:fs";
18924
19096
  import { win32 as win32Path } from "node:path";
18925
19097
  function reportedModelId(value) {
@@ -19208,7 +19380,7 @@ function findWinNpmInnerExe(cliId, options = {}) {
19208
19380
  function defaultRunner(env) {
19209
19381
  return (command, args, timeoutMs) => {
19210
19382
  try {
19211
- const result = spawnSync3(command, [...args], {
19383
+ const result = spawnSync4(command, [...args], {
19212
19384
  encoding: "utf8",
19213
19385
  timeout: timeoutMs,
19214
19386
  windowsHide: true,
@@ -19942,7 +20114,7 @@ var init_harness = __esm({
19942
20114
  });
19943
20115
 
19944
20116
  // plugins/kxm/src/project-config.ts
19945
- import { spawnSync as spawnSync4 } from "node:child_process";
20117
+ import { spawnSync as spawnSync5 } from "node:child_process";
19946
20118
  import { createHash as createHash7 } from "node:crypto";
19947
20119
  import { existsSync as existsSync10, lstatSync, readFileSync as readFileSync8, readdirSync as readdirSync2, realpathSync } from "node:fs";
19948
20120
  import { basename as basename2, dirname as dirname7, extname, isAbsolute as isAbsolute3, join as join9, relative, resolve as resolve6, sep } from "node:path";
@@ -20821,7 +20993,7 @@ function developerCeilings() {
20821
20993
  const script = join9(findKxmRepoRoot(import.meta.url), "scripts", "harness-run.mjs");
20822
20994
  let loaded;
20823
20995
  try {
20824
- loaded = spawnSync4(process.execPath, ["--input-type=module", "-e", `const m = await import(${JSON.stringify(pathToFileURL(script).href)}); process.stdout.write(JSON.stringify({ROUTES:m.ROUTES,NATIVE_PI_BRAKE_PROVIDERS:m.NATIVE_PI_BRAKE_PROVIDERS,PI_ALLOWED_PROVIDERS:m.PI_ALLOWED_PROVIDERS,PI_NATIVE_VENDOR_PROVIDERS:m.PI_NATIVE_VENDOR_PROVIDERS}))`], {
20996
+ loaded = spawnSync5(process.execPath, ["--input-type=module", "-e", `const m = await import(${JSON.stringify(pathToFileURL(script).href)}); process.stdout.write(JSON.stringify({ROUTES:m.ROUTES,NATIVE_PI_BRAKE_PROVIDERS:m.NATIVE_PI_BRAKE_PROVIDERS,PI_ALLOWED_PROVIDERS:m.PI_ALLOWED_PROVIDERS,PI_NATIVE_VENDOR_PROVIDERS:m.PI_NATIVE_VENDOR_PROVIDERS}))`], {
20825
20997
  encoding: "utf8",
20826
20998
  timeout: 15e3
20827
20999
  });
@@ -20917,7 +21089,7 @@ function gitEnvironment() {
20917
21089
  function discoverGitRoot(start = process.cwd()) {
20918
21090
  let current = resolve6(start);
20919
21091
  if (existsSync10(current) && !lstatSync(current).isDirectory()) current = dirname7(current);
20920
- const result = spawnSync4("git", ["-C", current, "rev-parse", "--show-toplevel"], {
21092
+ const result = spawnSync5("git", ["-C", current, "rev-parse", "--show-toplevel"], {
20921
21093
  encoding: "utf8",
20922
21094
  env: gitEnvironment(),
20923
21095
  timeout: 5e3,
@@ -21437,7 +21609,7 @@ var init_sqlite = __esm({
21437
21609
  });
21438
21610
 
21439
21611
  // plugins/kxm/src/bindings.ts
21440
- import { spawnSync as spawnSync5 } from "node:child_process";
21612
+ import { spawnSync as spawnSync6 } from "node:child_process";
21441
21613
  import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
21442
21614
  import {
21443
21615
  chmodSync as chmodSync2,
@@ -21639,7 +21811,7 @@ function projectOperationLockFile(projectRoot) {
21639
21811
  for (const key of Object.keys(env)) {
21640
21812
  if (key.toLocaleUpperCase("en-US").startsWith("GIT_")) delete env[key];
21641
21813
  }
21642
- const result = spawnSync5("git", ["-C", projectRoot, "rev-parse", "--absolute-git-dir"], {
21814
+ const result = spawnSync6("git", ["-C", projectRoot, "rev-parse", "--absolute-git-dir"], {
21643
21815
  encoding: "utf8",
21644
21816
  env,
21645
21817
  timeout: 5e3,
@@ -22816,7 +22988,7 @@ var init_database = __esm({
22816
22988
  });
22817
22989
 
22818
22990
  // plugins/kxm/src/runtime-store.ts
22819
- import { spawnSync as spawnSync6 } from "node:child_process";
22991
+ import { spawnSync as spawnSync7 } from "node:child_process";
22820
22992
  import { createHash as createHash12, randomUUID as randomUUID4 } from "node:crypto";
22821
22993
  import { existsSync as existsSync14, lstatSync as lstatSync4, mkdirSync as mkdirSync8, readFileSync as readFileSync11, realpathSync as realpathSync4, statSync, writeFileSync as writeFileSync8 } from "node:fs";
22822
22994
  import { dirname as dirname10, isAbsolute as isAbsolute6, resolve as resolve11 } from "node:path";
@@ -22856,7 +23028,7 @@ function gitEnv() {
22856
23028
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
22857
23029
  }
22858
23030
  function gitCommonDirectory(projectRoot) {
22859
- const result = spawnSync6("git", ["-C", projectRoot, "rev-parse", "--git-common-dir"], {
23031
+ const result = spawnSync7("git", ["-C", projectRoot, "rev-parse", "--git-common-dir"], {
22860
23032
  encoding: "utf8",
22861
23033
  env: gitEnv(),
22862
23034
  timeout: 3e4,
@@ -28369,10 +28541,13 @@ function runtimeFrom(ctx, command) {
28369
28541
  const envServerUrl = ctx.env.KXM_SERVER_URL?.trim();
28370
28542
  let boundHubUrl;
28371
28543
  try {
28372
- boundHubUrl = readHubBinding(ctx.env)?.url;
28544
+ const loaded = loadHubBinding(ctx.env);
28545
+ if (loaded.warning) ctx.io.stderr(`kxm: ${loaded.warning}
28546
+ `);
28547
+ boundHubUrl = loaded.record?.url;
28373
28548
  } catch (error) {
28374
28549
  if (error instanceof HubBindingError) {
28375
- ctx.io.stderr(`kxm: ignoring malformed hub binding at ${hubBindingFile(ctx.env)}; run kxm hub bind <url> again
28550
+ ctx.io.stderr(`kxm: ignoring malformed hub binding at ${hubBindingFile(ctx.env)}; falling back to the local hub. Run kxm hub bind <url> again
28376
28551
  `);
28377
28552
  } else {
28378
28553
  throw error;
@@ -28621,7 +28796,7 @@ var init_vision_gate = __esm({
28621
28796
  });
28622
28797
 
28623
28798
  // plugins/kxm/src/permission.ts
28624
- import { spawnSync as spawnSync7 } from "node:child_process";
28799
+ import { spawnSync as spawnSync8 } from "node:child_process";
28625
28800
  import { createHash as createHash17 } from "node:crypto";
28626
28801
  import { existsSync as existsSync27, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync25, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
28627
28802
  import { tmpdir as tmpdir2 } from "node:os";
@@ -29095,7 +29270,7 @@ function gitEnvironment2() {
29095
29270
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
29096
29271
  }
29097
29272
  function git(root, args) {
29098
- const result = spawnSync7("git", ["-C", root, ...args], {
29273
+ const result = spawnSync8("git", ["-C", root, ...args], {
29099
29274
  encoding: "utf8",
29100
29275
  env: gitEnvironment2(),
29101
29276
  timeout: 15e3,
@@ -29113,7 +29288,7 @@ function git(root, args) {
29113
29288
  return result.stdout;
29114
29289
  }
29115
29290
  function gitBuffer(root, args) {
29116
- const result = spawnSync7("git", ["-C", root, ...args], {
29291
+ const result = spawnSync8("git", ["-C", root, ...args], {
29117
29292
  env: gitEnvironment2(),
29118
29293
  timeout: 15e3,
29119
29294
  windowsHide: true,
@@ -29311,7 +29486,7 @@ function loadBaseProjectDeclarations(projectFile) {
29311
29486
  return members;
29312
29487
  }
29313
29488
  function initShadowGitRoot(directory) {
29314
- const result = spawnSync7("git", ["-c", "init.defaultBranch=main", "init", "--quiet", directory], {
29489
+ const result = spawnSync8("git", ["-c", "init.defaultBranch=main", "init", "--quiet", directory], {
29315
29490
  encoding: "utf8",
29316
29491
  env: gitEnvironment2(),
29317
29492
  timeout: 1e4,
@@ -35174,6 +35349,7 @@ function useColor() {
35174
35349
  var program = new Command();
35175
35350
 
35176
35351
  // plugins/kxm/src/kxm-update.ts
35352
+ import { spawnSync } from "node:child_process";
35177
35353
  import { createHash } from "node:crypto";
35178
35354
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
35179
35355
  import { join } from "node:path";
@@ -35192,6 +35368,18 @@ var KxmUpdateConfigError = class extends Error {
35192
35368
  function kxmReleaseAssetName(version) {
35193
35369
  return `kxm-${version}.tgz`;
35194
35370
  }
35371
+ function kxmReleaseDownloadUrl(version, name = kxmReleaseAssetName(version)) {
35372
+ return `https://github.com/kontextmind/kxm/releases/download/v${version}/${name}`;
35373
+ }
35374
+ function ghReleaseDownloadAvailable(env = process.env) {
35375
+ const result = spawnSync("gh", ["--version"], {
35376
+ encoding: "utf8",
35377
+ env,
35378
+ windowsHide: true,
35379
+ timeout: 15e3
35380
+ });
35381
+ return !result.error && result.status === 0;
35382
+ }
35195
35383
  function readInstalledKxmVersion(root) {
35196
35384
  const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
35197
35385
  if (typeof pkg.version !== "string" || !parseSemver(pkg.version)) {
@@ -35340,13 +35528,17 @@ function verifyReleaseAssetDigest(path4, sha256) {
35340
35528
  return false;
35341
35529
  }
35342
35530
  }
35343
- function planKxmPackageUpdate(source, latest, releaseDir, asset) {
35531
+ function planKxmPackageUpdate(source, latest, releaseDir, asset, downloader = "gh") {
35344
35532
  if (source === "npm") {
35345
35533
  return [{ kind: "install", command: "npm", args: ["install", "--global", "--omit=peer", `@kontextmind/kxm@${latest}`] }];
35346
35534
  }
35347
35535
  const name = asset?.name ?? kxmReleaseAssetName(latest);
35348
35536
  const steps = [
35349
- {
35537
+ downloader === "curl" ? {
35538
+ kind: "download",
35539
+ command: "curl",
35540
+ args: ["--fail", "--silent", "--show-error", "--location", "--output", join(releaseDir, name), kxmReleaseDownloadUrl(latest, name)]
35541
+ } : {
35350
35542
  kind: "download",
35351
35543
  command: "gh",
35352
35544
  args: ["release", "download", `v${latest}`, "--repo", "kontextmind/kxm", "--pattern", name, "--dir", releaseDir, "--clobber"]
@@ -40076,7 +40268,7 @@ init_project();
40076
40268
  init_repo_root();
40077
40269
  init_harness();
40078
40270
  init_types();
40079
- import { spawnSync as spawnSync8 } from "node:child_process";
40271
+ import { spawnSync as spawnSync9 } from "node:child_process";
40080
40272
  var PLUGIN_SUPPORTED_HARNESSES = Object.freeze(["pi", "omp", "claude"]);
40081
40273
  async function cmdPluginInstall(runtime, options) {
40082
40274
  const repoRoot = findKxmRepoRoot(import.meta.url);
@@ -40115,7 +40307,7 @@ async function cmdPluginInstall(runtime, options) {
40115
40307
  detail: `would install kxm plugin into pi: ${command} ${args.join(" ")}`
40116
40308
  });
40117
40309
  } else {
40118
- const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, args) : spawnSync8(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
40310
+ const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, args) : spawnSync9(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
40119
40311
  if (run.error || run.status !== 0 && run.status !== null) {
40120
40312
  results.push({
40121
40313
  harness: "pi",
@@ -40146,7 +40338,7 @@ async function cmdPluginInstall(runtime, options) {
40146
40338
  detail: `would install kxm plugin into omp: ${command} ${args.join(" ")}`
40147
40339
  });
40148
40340
  } else {
40149
- const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, args) : spawnSync8(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
40341
+ const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, args) : spawnSync9(command, args, { encoding: "utf8", windowsHide: true, env: runtime.env });
40150
40342
  if (run.error || run.status !== 0 && run.status !== null) {
40151
40343
  results.push({
40152
40344
  harness: "omp",
@@ -40181,9 +40373,9 @@ async function cmdPluginInstall(runtime, options) {
40181
40373
  if (runtime.io.spawnSync) {
40182
40374
  runtime.io.spawnSync(command, marketplaceArgs);
40183
40375
  } else {
40184
- spawnSync8(command, marketplaceArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
40376
+ spawnSync9(command, marketplaceArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
40185
40377
  }
40186
- const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, installArgs) : spawnSync8(command, installArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
40378
+ const run = runtime.io.spawnSync ? runtime.io.spawnSync(command, installArgs) : spawnSync9(command, installArgs, { encoding: "utf8", windowsHide: true, env: runtime.env });
40187
40379
  if (run.error || run.status !== 0 && run.status !== null) {
40188
40380
  results.push({
40189
40381
  harness: "claude",
@@ -40307,7 +40499,7 @@ init_project_config();
40307
40499
  init_runtime_store();
40308
40500
  init_runtime_supervisor();
40309
40501
  init_types();
40310
- import { spawnSync as spawnSync9 } from "node:child_process";
40502
+ import { spawnSync as spawnSync10 } from "node:child_process";
40311
40503
  import { chmodSync as chmodSync6, existsSync as existsSync33, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync22 } from "node:fs";
40312
40504
  import { basename as basename8, dirname as dirname18, join as join37, resolve as resolve25 } from "node:path";
40313
40505
  var LANE_SCHEMA = "kxm.lanes.v1";
@@ -40322,7 +40514,7 @@ function gitEnv2() {
40322
40514
  return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.toUpperCase().startsWith("GIT_")));
40323
40515
  }
40324
40516
  function git2(cwd, args) {
40325
- const result = spawnSync9("git", ["-C", cwd, ...args], {
40517
+ const result = spawnSync10("git", ["-C", cwd, ...args], {
40326
40518
  encoding: "utf8",
40327
40519
  env: gitEnv2(),
40328
40520
  timeout: 3e4,
@@ -40897,7 +41089,7 @@ function cmdLand(runtime, options = {}) {
40897
41089
  // plugins/kxm/src/cli/assign.ts
40898
41090
  init_project_config();
40899
41091
  init_types();
40900
- import { spawnSync as spawnSync10 } from "node:child_process";
41092
+ import { spawnSync as spawnSync11 } from "node:child_process";
40901
41093
  import { existsSync as existsSync34 } from "node:fs";
40902
41094
  import { join as join39 } from "node:path";
40903
41095
  var kxmAssignCliSeams = {};
@@ -40983,7 +41175,7 @@ async function cmdAssign(runtime, subcommand, args) {
40983
41175
  return 0;
40984
41176
  }
40985
41177
  const spawn6 = kxmAssignCliSeams.spawn ?? ((commandName, commandArgs, options) => {
40986
- const result2 = spawnSync10(commandName, commandArgs, {
41178
+ const result2 = spawnSync11(commandName, commandArgs, {
40987
41179
  cwd: options.cwd,
40988
41180
  stdio: options.stdio,
40989
41181
  env: options.env,
@@ -49448,7 +49640,7 @@ var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
49448
49640
  // plugins/kxm/src/tui.ts
49449
49641
  import { mkdirSync as mkdirSync25 } from "node:fs";
49450
49642
  import { join as join44, resolve as resolve27, dirname as dirname20 } from "node:path";
49451
- import { spawnSync as spawnSync11 } from "node:child_process";
49643
+ import { spawnSync as spawnSync12 } from "node:child_process";
49452
49644
 
49453
49645
  // packages/core/tui/src/types/surface.ts
49454
49646
  var KXM_TUI_LIMITS = Object.freeze({
@@ -50005,16 +50197,16 @@ function applyMeshTuiKey(view, key, itemCount = 0) {
50005
50197
  function copyToClipboard(text) {
50006
50198
  try {
50007
50199
  if (process.platform === "darwin") {
50008
- const proc = spawnSync11("pbcopy", { input: text, encoding: "utf8", windowsHide: true });
50200
+ const proc = spawnSync12("pbcopy", { input: text, encoding: "utf8", windowsHide: true });
50009
50201
  return proc.status === 0;
50010
50202
  }
50011
50203
  if (process.platform === "win32") {
50012
- const proc = spawnSync11("clip", { input: text, encoding: "utf8", windowsHide: true });
50204
+ const proc = spawnSync12("clip", { input: text, encoding: "utf8", windowsHide: true });
50013
50205
  return proc.status === 0;
50014
50206
  }
50015
- const wl = spawnSync11("wl-copy", [text], { encoding: "utf8", windowsHide: true });
50207
+ const wl = spawnSync12("wl-copy", [text], { encoding: "utf8", windowsHide: true });
50016
50208
  if (wl.status === 0) return true;
50017
- const xclip = spawnSync11("xclip", ["-selection", "clipboard"], { input: text, encoding: "utf8", windowsHide: true });
50209
+ const xclip = spawnSync12("xclip", ["-selection", "clipboard"], { input: text, encoding: "utf8", windowsHide: true });
50018
50210
  return xclip.status === 0;
50019
50211
  } catch {
50020
50212
  return false;
@@ -50022,7 +50214,7 @@ function copyToClipboard(text) {
50022
50214
  }
50023
50215
  function spawnDegradeWorktree(repoRoot, runId, options) {
50024
50216
  const runner = options?.execFn ?? ((cmd, args) => {
50025
- const res = spawnSync11(cmd, args, {
50217
+ const res = spawnSync12(cmd, args, {
50026
50218
  cwd: repoRoot,
50027
50219
  encoding: "utf8",
50028
50220
  windowsHide: true,
@@ -50806,7 +50998,7 @@ async function runMeshTui(input) {
50806
50998
  }
50807
50999
 
50808
51000
  // plugins/kxm/src/session-work.ts
50809
- import { spawnSync as spawnSync12 } from "node:child_process";
51001
+ import { spawnSync as spawnSync13 } from "node:child_process";
50810
51002
  import { randomUUID as randomUUID13 } from "node:crypto";
50811
51003
  import { existsSync as existsSync36, mkdirSync as mkdirSync26, readFileSync as readFileSync32, renameSync as renameSync8, writeFileSync as writeFileSync23 } from "node:fs";
50812
51004
  import { join as join45 } from "node:path";
@@ -50867,10 +51059,10 @@ function formatShipLine(ship) {
50867
51059
  }
50868
51060
  function readGitShip(cwd) {
50869
51061
  try {
50870
- const dirty = spawnSync12("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
51062
+ const dirty = spawnSync13("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
50871
51063
  if (dirty.status !== 0) return void 0;
50872
51064
  const isDirty = dirty.stdout.trim().length > 0;
50873
- const upstream = spawnSync12("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
51065
+ const upstream = spawnSync13("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
50874
51066
  if (upstream.status === 0) {
50875
51067
  return {
50876
51068
  dirty: isDirty,
@@ -50878,9 +51070,9 @@ function readGitShip(cwd) {
50878
51070
  };
50879
51071
  }
50880
51072
  for (const baseRef of ["origin/HEAD", "main", "origin/main", "master", "origin/master"]) {
50881
- const mb = spawnSync12("git", ["-C", cwd, "merge-base", baseRef, "HEAD"], { encoding: "utf8", windowsHide: true });
51073
+ const mb = spawnSync13("git", ["-C", cwd, "merge-base", baseRef, "HEAD"], { encoding: "utf8", windowsHide: true });
50882
51074
  if (mb.status === 0 && mb.stdout.trim()) {
50883
- const count = spawnSync12("git", ["-C", cwd, "rev-list", "--count", `${mb.stdout.trim()}..HEAD`], { encoding: "utf8", windowsHide: true });
51075
+ const count = spawnSync13("git", ["-C", cwd, "rev-list", "--count", `${mb.stdout.trim()}..HEAD`], { encoding: "utf8", windowsHide: true });
50884
51076
  if (count.status === 0) {
50885
51077
  return {
50886
51078
  dirty: isDirty,
@@ -51424,20 +51616,6 @@ init_repo_root();
51424
51616
  import { fileURLToPath as fileURLToPath3 } from "node:url";
51425
51617
  import { dirname as dirname22 } from "node:path";
51426
51618
  import { homedir as homedir7 } from "node:os";
51427
- async function hubGet(url, fetchImpl, headers) {
51428
- try {
51429
- const response = await fetchImpl(url, headers ? { headers } : void 0);
51430
- const text = redactSecrets((await response.text()).slice(0, 8e3));
51431
- let body = text;
51432
- try {
51433
- body = JSON.parse(text);
51434
- } catch {
51435
- }
51436
- return { ok: response.ok, status: response.status, body };
51437
- } catch {
51438
- return { ok: false, status: 0, body: { error: "hub_unreachable" } };
51439
- }
51440
- }
51441
51619
  function installProbeFrom(runtime) {
51442
51620
  const partial = runtime.io.installProbe ?? {};
51443
51621
  const moduleDir = partial.moduleDir ?? dirname22(fileURLToPath3(import.meta.url));
@@ -51512,8 +51690,11 @@ async function cmdStatus(runtime) {
51512
51690
  }
51513
51691
  if (!(error instanceof HubBindingError)) throw error;
51514
51692
  }
51515
- const health = await hubGet(`${runtime.serverUrl}/health`, runtime.fetchImpl, headers);
51516
- const ready = await hubGet(`${runtime.serverUrl}/ready`, runtime.fetchImpl, headers);
51693
+ const health = await readHubEndpoint(runtime.serverUrl, "health", runtime.fetchImpl, headers ? { headers } : {});
51694
+ const ready = await readHubEndpoint(runtime.serverUrl, "ready", runtime.fetchImpl, headers ? { headers } : {});
51695
+ const proxyKind = [health.kind, ready.kind].find((kind) => kind === "redirect" || kind === "auth_proxy");
51696
+ const notHub = !proxyKind && (health.kind === "not_hub" || ready.kind === "not_hub");
51697
+ const probeNote = proxyKind === "redirect" ? " \xB7 auth proxy or redirect" : proxyKind === "auth_proxy" ? " \xB7 auth proxy (HTML login page)" : notHub ? " \xB7 not a kxm hub" : "";
51517
51698
  const effectiveScope = connection.scope;
51518
51699
  const overridden = connection.urlSource === "env";
51519
51700
  const identity = {
@@ -51537,7 +51718,7 @@ async function cmdStatus(runtime) {
51537
51718
  runtime.io,
51538
51719
  runtime.json,
51539
51720
  payload,
51540
- `hub health=${health.ok} ready=${ready.ok} \xB7 ${effectiveScope} hub${overridden ? " (KXM_SERVER_URL)" : ""} \xB7 mode=${identity.mode} project=${identity.project} (${connection.sourceLabel}) key=${identity.keySource}`
51721
+ `hub health=${health.ok} ready=${ready.ok} \xB7 ${effectiveScope} hub${overridden ? " (KXM_SERVER_URL)" : ""} \xB7 mode=${identity.mode} project=${identity.project} (${connection.sourceLabel}) key=${identity.keySource}${probeNote}`
51541
51722
  );
51542
51723
  return payload.ok ? 0 : 1;
51543
51724
  }
@@ -51748,7 +51929,7 @@ async function cmdHubBind(runtime, rawUrl, options = {}) {
51748
51929
  return 2;
51749
51930
  }
51750
51931
  }
51751
- const file = hubBindingFile(runtime.env);
51932
+ const file = cloud ? hubBindingCloudFile(runtime.env) : hubBindingFile(runtime.env);
51752
51933
  const bindingCommand = tokenCommand ?? (cloud && keyOp ? `op read ${keyOp}` : void 0);
51753
51934
  const cloudFields = cloud ? { cloud: true, ...tokenEnv ? { tokenEnv } : {}, ...bindingCommand ? { tokenCommand: bindingCommand } : {} } : {};
51754
51935
  const record = {
@@ -51773,6 +51954,27 @@ async function cmdHubBind(runtime, rawUrl, options = {}) {
51773
51954
  );
51774
51955
  return 0;
51775
51956
  }
51957
+ const probe = await probeHubHealth(url, runtime.fetchImpl);
51958
+ const acceptable = probe.kind === "hub" || probe.kind === "timeout" || probe.kind === "unreachable";
51959
+ if (!acceptable && options.force !== true) {
51960
+ print(
51961
+ runtime.io,
51962
+ runtime.json,
51963
+ {
51964
+ ok: false,
51965
+ command: "hub bind",
51966
+ error: "hub_not_kxm",
51967
+ url,
51968
+ scope,
51969
+ kind: probe.kind,
51970
+ status: probe.status,
51971
+ detail: probe.detail,
51972
+ nextAction: "bind_a_kxm_hub_or_pass_force"
51973
+ },
51974
+ `refusing to bind ${url}: ${probe.detail}; no binding was written. Pass --force to bind anyway`
51975
+ );
51976
+ return 2;
51977
+ }
51776
51978
  try {
51777
51979
  writeHubEndpoint(runtime.dirs.workdir, {
51778
51980
  mode: cloud ? "cloud" : "local",
@@ -51793,12 +51995,12 @@ async function cmdHubBind(runtime, rawUrl, options = {}) {
51793
51995
  throw error;
51794
51996
  }
51795
51997
  writeHubBinding({ ...record, boundAt: (/* @__PURE__ */ new Date()).toISOString() }, runtime.env);
51796
- const { health, probeMs } = await probeHubHealth(url, runtime.fetchImpl);
51998
+ const healthText = acceptable ? formatHubBindHealth(probe.health) : `${probe.detail} (--force)`;
51797
51999
  print(
51798
52000
  runtime.io,
51799
52001
  runtime.json,
51800
- { ok: true, command: "hub bind", url, scope, file, configFile, project: identity.project, health, probeMs, ...cloudFields },
51801
- `bound hub ${url} \xB7 ${scope} \xB7 ${formatHubBindHealth(health)}${cloud ? cloudNote : scope === "remote" ? " \xB7 token leaves this machine" : ""}`
52002
+ { ok: true, command: "hub bind", url, scope, file, configFile, project: identity.project, health: probe.health, probeMs: probe.probeMs, kind: probe.kind, ...cloudFields },
52003
+ `bound hub ${url} \xB7 ${scope} \xB7 ${healthText}${cloud ? cloudNote : scope === "remote" ? " \xB7 token leaves this machine" : ""}`
51802
52004
  );
51803
52005
  return 0;
51804
52006
  }
@@ -52231,7 +52433,7 @@ init_redact();
52231
52433
  init_telemetry();
52232
52434
  init_routing();
52233
52435
  init_prices();
52234
- import { spawnSync as spawnSync14 } from "node:child_process";
52436
+ import { spawnSync as spawnSync15 } from "node:child_process";
52235
52437
  import { createHash as createHash19 } from "node:crypto";
52236
52438
  import { existsSync as existsSync47, mkdtempSync as mkdtempSync3, readFileSync as readFileSync41, rmSync as rmSync13 } from "node:fs";
52237
52439
  import { tmpdir as tmpdir3 } from "node:os";
@@ -53110,7 +53312,7 @@ ${divider}
53110
53312
 
53111
53313
  // plugins/kxm/src/ssh-remote.ts
53112
53314
  init_safety_integrity();
53113
- import { spawnSync as spawnSync13 } from "node:child_process";
53315
+ import { spawnSync as spawnSync14 } from "node:child_process";
53114
53316
  import { existsSync as existsSync44, mkdirSync as mkdirSync30, readFileSync as readFileSync39, readdirSync as readdirSync14, rmSync as rmSync12, statSync as statSync7 } from "node:fs";
53115
53317
  import { homedir as homedir8 } from "node:os";
53116
53318
  import { join as join52, resolve as resolve31 } from "node:path";
@@ -53186,7 +53388,7 @@ function parseSshConfig(configPath) {
53186
53388
  return [];
53187
53389
  }
53188
53390
  }
53189
- function resolveSshHostG(host, execFn = spawnSync13) {
53391
+ function resolveSshHostG(host, execFn = spawnSync14) {
53190
53392
  try {
53191
53393
  const result = execFn("ssh", ["-G", host], { encoding: "utf-8" });
53192
53394
  if (result.status !== 0 || !result.stdout) {
@@ -53252,7 +53454,7 @@ function buildSshArgs(options) {
53252
53454
  args.push(options.host);
53253
53455
  return args;
53254
53456
  }
53255
- function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync13) {
53457
+ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
53256
53458
  const resolvedDir = ensureSocketDir(socketDir);
53257
53459
  const controlPath = join52(resolvedDir, "%C");
53258
53460
  try {
@@ -53264,7 +53466,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
53264
53466
  return false;
53265
53467
  }
53266
53468
  }
53267
- function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync13) {
53469
+ function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync14) {
53268
53470
  const resolvedDir = ensureSocketDir(socketDir);
53269
53471
  const controlPath = join52(resolvedDir, "%C");
53270
53472
  try {
@@ -53278,7 +53480,7 @@ function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
53278
53480
  }
53279
53481
  function executeSshRun(params) {
53280
53482
  const startTime = Date.now();
53281
- const execSyncFn = params.execFn ?? spawnSync13;
53483
+ const execSyncFn = params.execFn ?? spawnSync14;
53282
53484
  if (params.action === "info") {
53283
53485
  if (params.host) {
53284
53486
  const hostInfo = resolveSshHostG(params.host, execSyncFn);
@@ -54423,7 +54625,7 @@ init_types();
54423
54625
  init_repo_root();
54424
54626
  function cliSpawn(runtime, command, args, extra) {
54425
54627
  if (runtime.io.spawnSync) return runtime.io.spawnSync(command, args);
54426
- const result = spawnSync14(command, [...args], {
54628
+ const result = spawnSync15(command, [...args], {
54427
54629
  encoding: "utf8",
54428
54630
  windowsHide: true,
54429
54631
  shell: process.platform === "win32",
@@ -54463,7 +54665,8 @@ function applyKxmPackageUpdate(runtime, notice) {
54463
54665
  }
54464
54666
  const releaseDir = mkdtempSync3(join55(tmpdir3(), "kxm-pkg-update-"));
54465
54667
  try {
54466
- const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
54668
+ const downloader = notice.source === "github" && !ghReleaseDownloadAvailable(runtime.env) ? "curl" : "gh";
54669
+ const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset, downloader);
54467
54670
  if (runtime.dryRun) {
54468
54671
  return { ok: true, detail: planned.map(formatPackageUpdateStep).join(" && ") };
54469
54672
  }
@@ -56042,7 +56245,7 @@ function createProgram(ctx, result, argv) {
56042
56245
  addGlobalOptions(hub.command("stop").description("Request managed hub and worker shutdown")).option("--wait-ms <ms>", "How long to wait for PID files to clear").action(async function hubStopAction(options) {
56043
56246
  result.code = await cmdStop(runtimeFrom(ctx, this), options.waitMs);
56044
56247
  });
56045
- addGlobalOptions(hub.command("bind").description("Bind this machine to a running hub").argument("<url>", "Hub base URL (http or https)")).option("--cloud", "Mark the hub remote even when the URL is a loopback forward").option("--token-env <name>", "Environment variable that holds the hub token; the token is not stored").option("--token-command <command>", "Program that prints the hub token; the token is not stored").option("--key-op <ref>", "op:// reference for the hub token; resolved with op read, never stored").action(async function hubBindAction(url, options) {
56248
+ addGlobalOptions(hub.command("bind").description("Bind this machine to a running hub").argument("<url>", "Hub base URL (http or https)")).option("--cloud", "Mark the hub remote even when the URL is a loopback forward").option("--token-env <name>", "Environment variable that holds the hub token; the token is not stored").option("--token-command <command>", "Program that prints the hub token; the token is not stored").option("--key-op <ref>", "op:// reference for the hub token; resolved with op read, never stored").option("--force", "Bind even when the URL is not a kxm hub").action(async function hubBindAction(url, options) {
56046
56249
  result.code = await cmdHubBind(runtimeFrom(ctx, this), url, options);
56047
56250
  });
56048
56251
  addGlobalOptions(hub.command("unbind").description("Remove this machine's hub binding")).action(bind(cmdHubUnbind));