@kontextmind/kxm 0.7.164 → 0.7.166

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 (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +2 -0
  4. package/docs/README.md +1 -0
  5. package/docs/architecture/inventory.md +4 -3
  6. package/docs/concepts/trust-model.md +1 -1
  7. package/docs/glossary.md +4 -4
  8. package/docs/operations/troubleshooting.md +4 -1
  9. package/docs/operations.md +208 -0
  10. package/docs/reference/cli-reference.md +12 -11
  11. package/docs/reference/config-reference.md +36 -11
  12. package/docs/reference/configuration.md +11 -1
  13. package/docs/reference/tools.md +1 -1
  14. package/docs/start/quickstart-claude-code.md +2 -2
  15. package/package.json +1 -1
  16. package/plugins/kxm/.claude-plugin/plugin.json +2 -2
  17. package/plugins/kxm/README.md +4 -4
  18. package/plugins/kxm/dist/claude-hook.js +511 -56
  19. package/plugins/kxm/dist/cli.js +15661 -14775
  20. package/plugins/kxm/dist/extension.js +645 -133
  21. package/plugins/kxm/dist/mcp-server.js +8152 -81
  22. package/plugins/kxm/dist/runtime-supervisor.js +804 -155
  23. package/plugins/kxm/dist/runtime.js +851 -202
  24. package/plugins/kxm/dist/server.js +466 -58
  25. package/plugins/kxm/package.json +1 -1
  26. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +17 -5
  27. package/plugins/kxm/src/cli/context-skills.ts +64 -10
  28. package/plugins/kxm/src/cli/hub.ts +192 -22
  29. package/plugins/kxm/src/cli/project.ts +7 -3
  30. package/plugins/kxm/src/cli/types.ts +14 -1
  31. package/plugins/kxm/src/cli/workflows.ts +12 -1
  32. package/plugins/kxm/src/cli.ts +63 -11
  33. package/plugins/kxm/src/config.ts +246 -3
  34. package/plugins/kxm/src/extension.ts +12 -7
  35. package/plugins/kxm/src/hub-autostart.ts +23 -0
  36. package/plugins/kxm/src/hub-binding.ts +182 -2
  37. package/plugins/kxm/src/hub-env.ts +132 -9
  38. package/plugins/kxm/src/hub-identity.ts +337 -0
  39. package/plugins/kxm/src/mcp-server.ts +17 -14
  40. package/plugins/kxm/src/project-name.ts +6 -20
  41. package/plugins/kxm/src/runtime-supervisor.ts +9 -3
  42. package/plugins/kxm/src/server.ts +22 -4
  43. package/plugins/kxm/src/tenant-status.ts +9 -2
  44. package/scripts/build-runtime.mjs +5 -0
  45. package/scripts/kxm-hub.mjs +5 -0
@@ -10319,7 +10319,7 @@ var require_compile = __commonJS({
10319
10319
  const schOrFunc = root.refs[ref];
10320
10320
  if (schOrFunc)
10321
10321
  return schOrFunc;
10322
- let _sch = resolve8.call(this, root, ref);
10322
+ let _sch = resolve9.call(this, root, ref);
10323
10323
  if (_sch === void 0) {
10324
10324
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
10325
10325
  const { schemaId } = this.opts;
@@ -10346,7 +10346,7 @@ var require_compile = __commonJS({
10346
10346
  function sameSchemaEnv(s1, s2) {
10347
10347
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
10348
10348
  }
10349
- function resolve8(root, ref) {
10349
+ function resolve9(root, ref) {
10350
10350
  let sch;
10351
10351
  while (typeof (sch = this.refs[ref]) == "string")
10352
10352
  ref = sch;
@@ -11171,7 +11171,7 @@ var require_fast_uri = __commonJS({
11171
11171
  }
11172
11172
  return uri;
11173
11173
  }
11174
- function resolve8(baseURI, relativeURI, options) {
11174
+ function resolve9(baseURI, relativeURI, options) {
11175
11175
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
11176
11176
  const {
11177
11177
  parsed: baseParsed,
@@ -11533,7 +11533,7 @@ var require_fast_uri = __commonJS({
11533
11533
  var fastUri = {
11534
11534
  SCHEMES,
11535
11535
  normalize,
11536
- resolve: resolve8,
11536
+ resolve: resolve9,
11537
11537
  resolveComponent,
11538
11538
  equal,
11539
11539
  serialize,
@@ -14523,9 +14523,9 @@ var require_dist2 = __commonJS({
14523
14523
  });
14524
14524
 
14525
14525
  // plugins/kxm/src/extension.ts
14526
- import { spawnSync as spawnSync2 } from "node:child_process";
14527
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
14528
- import { dirname as dirname7, join as join15 } from "node:path";
14526
+ import { spawnSync as spawnSync4 } from "node:child_process";
14527
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
14528
+ import { dirname as dirname8, join as join15 } from "node:path";
14529
14529
 
14530
14530
  // plugins/kxm/src/commands.ts
14531
14531
  import { chmodSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
@@ -14967,7 +14967,7 @@ var HubClient = class {
14967
14967
  if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
14968
14968
  const message = await this.getMessage(messageId);
14969
14969
  if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
14970
- await new Promise((resolve8, reject) => {
14970
+ await new Promise((resolve9, reject) => {
14971
14971
  const onAbort = () => {
14972
14972
  signal?.removeEventListener("abort", onAbort);
14973
14973
  clearTimeout(timer);
@@ -14975,7 +14975,7 @@ var HubClient = class {
14975
14975
  };
14976
14976
  const timer = setTimeout(() => {
14977
14977
  signal?.removeEventListener("abort", onAbort);
14978
- resolve8();
14978
+ resolve9();
14979
14979
  }, Math.min(500, Math.max(1, deadline - Date.now())));
14980
14980
  signal?.addEventListener("abort", onAbort, { once: true });
14981
14981
  if (signal?.aborted) onAbort();
@@ -15030,7 +15030,7 @@ var HubClient = class {
15030
15030
  } catch (error2) {
15031
15031
  if (this.stopped || error2 instanceof Error && error2.name === "AbortError") return;
15032
15032
  }
15033
- if (!this.stopped) await new Promise((resolve8) => setTimeout(resolve8, this.options.reconnectMs));
15033
+ if (!this.stopped) await new Promise((resolve9) => setTimeout(resolve9, this.options.reconnectMs));
15034
15034
  }
15035
15035
  }
15036
15036
  headers(includeIdentity = true) {
@@ -15995,9 +15995,25 @@ function enforceToolPolicy(commandName, env = process.env, options) {
15995
15995
  // plugins/kxm/src/config.ts
15996
15996
  var import_yaml = __toESM(require_dist(), 1);
15997
15997
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
15998
- import { homedir as homedir2 } from "node:os";
15998
+ import { tmpdir, homedir as homedir2 } from "node:os";
15999
15999
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
16000
16000
  var KXM_CONFIG_SCHEMA = "kxm.config.v1";
16001
+ var KxmHubConfigError = class extends Error {
16002
+ code = "hub_config_invalid";
16003
+ constructor(message) {
16004
+ super(message);
16005
+ this.name = "KxmHubConfigError";
16006
+ }
16007
+ };
16008
+ var HUB_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
16009
+ var HUB_OP_REF = /^op:\/\/\S+\/\S+\/\S+$/;
16010
+ var LITERAL_SECRET_KEY = /^(token|secret|password|authToken|auth_token|apiKey|api_key|api_token|bearer|credential|access_token)$/i;
16011
+ function isHubKeyEnvName(value) {
16012
+ return HUB_ENV_NAME.test(value);
16013
+ }
16014
+ function isHubOpReference(value) {
16015
+ return HUB_OP_REF.test(value);
16016
+ }
16001
16017
  var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
16002
16018
  var DEFAULT_KXM_CONFIG = {
16003
16019
  schema: KXM_CONFIG_SCHEMA,
@@ -16068,9 +16084,151 @@ function deepMerge(target, source) {
16068
16084
  }
16069
16085
  return result2;
16070
16086
  }
16087
+ function literalSecretMessage(path) {
16088
+ return `refusing literal hub token at ${path}: store an op:// reference (key.op) or an environment variable name (key.env), never the token`;
16089
+ }
16090
+ function rejectLiteralHubSecrets(value, path) {
16091
+ if (Array.isArray(value)) {
16092
+ value.forEach((item, index) => rejectLiteralHubSecrets(item, `${path}[${index}]`));
16093
+ return;
16094
+ }
16095
+ if (!value || typeof value !== "object") return;
16096
+ for (const [key, child] of Object.entries(value)) {
16097
+ const childPath = `${path}.${key}`;
16098
+ if (typeof child === "string" && (key === "key" || LITERAL_SECRET_KEY.test(key))) {
16099
+ throw new KxmHubConfigError(literalSecretMessage(childPath));
16100
+ }
16101
+ if (child && typeof child === "object") rejectLiteralHubSecrets(child, childPath);
16102
+ }
16103
+ }
16104
+ function parseKeyRef(raw, path) {
16105
+ if (raw === void 0) return void 0;
16106
+ if (typeof raw === "string") throw new KxmHubConfigError(literalSecretMessage(path));
16107
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
16108
+ throw new KxmHubConfigError(`${path} must be a key reference with op and/or env, never a token`);
16109
+ }
16110
+ const row = raw;
16111
+ for (const key of Object.keys(row)) {
16112
+ if (key !== "op" && key !== "env") {
16113
+ throw new KxmHubConfigError(literalSecretMessage(`${path}.${key}`));
16114
+ }
16115
+ }
16116
+ const ref = {};
16117
+ if (row.op !== void 0) {
16118
+ if (typeof row.op !== "string" || !isHubOpReference(row.op)) {
16119
+ throw new KxmHubConfigError(`${path}.op must be an op://vault/item/field reference, not a token`);
16120
+ }
16121
+ ref.op = row.op;
16122
+ }
16123
+ if (row.env !== void 0) {
16124
+ if (typeof row.env !== "string" || !isHubKeyEnvName(row.env)) {
16125
+ throw new KxmHubConfigError(`${path}.env must be an environment variable name, not a token`);
16126
+ }
16127
+ ref.env = row.env;
16128
+ }
16129
+ if (!ref.op && !ref.env) throw new KxmHubConfigError(`${path} needs key.op, key.env, or both`);
16130
+ return ref;
16131
+ }
16132
+ function parseEndpoint(raw, path) {
16133
+ if (raw === void 0) return void 0;
16134
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
16135
+ throw new KxmHubConfigError(`${path} must be an object with url, project, and key`);
16136
+ }
16137
+ const row = raw;
16138
+ const endpoint = {};
16139
+ if (row.url !== void 0) {
16140
+ if (typeof row.url !== "string" || !isHubUrl(row.url)) {
16141
+ throw new KxmHubConfigError(`${path}.url must be an http or https URL without credentials, query, or fragment`);
16142
+ }
16143
+ endpoint.url = row.url.replace(/\/$/, "");
16144
+ }
16145
+ if (row.project !== void 0) {
16146
+ if (typeof row.project !== "string" || !isHubProjectId(row.project)) {
16147
+ throw new KxmHubConfigError(`${path}.project must be a single-line project id, not a token`);
16148
+ }
16149
+ endpoint.project = row.project.trim();
16150
+ }
16151
+ const key = parseKeyRef(row.key, `${path}.key`);
16152
+ if (key) endpoint.key = key;
16153
+ for (const name of Object.keys(row)) {
16154
+ if (name !== "url" && name !== "project" && name !== "key") {
16155
+ if (typeof row[name] === "string") throw new KxmHubConfigError(literalSecretMessage(`${path}.${name}`));
16156
+ throw new KxmHubConfigError(`${path}.${name} is not a hub endpoint field`);
16157
+ }
16158
+ }
16159
+ return endpoint;
16160
+ }
16161
+ function isHubUrl(raw) {
16162
+ try {
16163
+ const parsed = new URL(raw);
16164
+ return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && !raw.includes("?") && !raw.includes("#");
16165
+ } catch {
16166
+ return false;
16167
+ }
16168
+ }
16169
+ function isHubProjectId(raw) {
16170
+ const value = raw.trim();
16171
+ return value.length > 0 && value.length <= 200 && !/[\s\r\n]/.test(value);
16172
+ }
16173
+ function parseProjectMap(raw) {
16174
+ if (raw === void 0) return void 0;
16175
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
16176
+ throw new KxmHubConfigError("hub.projects must be a map of project id to key reference, never a token");
16177
+ }
16178
+ const projects = {};
16179
+ for (const [project, value] of Object.entries(raw)) {
16180
+ if (!isHubProjectId(project)) {
16181
+ throw new KxmHubConfigError(`hub.projects key ${JSON.stringify(project)} must be a project id, not a token`);
16182
+ }
16183
+ const ref = parseKeyRef(value, `hub.projects.${project}`);
16184
+ if (!ref) throw new KxmHubConfigError(`hub.projects.${project} needs an op:// reference or an environment variable name`);
16185
+ projects[project.trim()] = ref;
16186
+ }
16187
+ return projects;
16188
+ }
16071
16189
  function normalizeHubConfig(raw) {
16072
16190
  const autoStart = raw?.autoStart;
16073
- return { autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart };
16191
+ const hub = {
16192
+ autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart
16193
+ };
16194
+ if (raw === void 0) return hub;
16195
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
16196
+ throw new KxmHubConfigError("hub must be an object");
16197
+ }
16198
+ rejectLiteralHubSecrets(raw, "hub");
16199
+ const row = raw;
16200
+ if (row.mode !== void 0) {
16201
+ if (row.mode !== "local" && row.mode !== "cloud") {
16202
+ throw new KxmHubConfigError("hub.mode must be local or cloud");
16203
+ }
16204
+ hub.mode = row.mode;
16205
+ }
16206
+ const local = parseEndpoint(row.local, "hub.local");
16207
+ const cloud = parseEndpoint(row.cloud, "hub.cloud");
16208
+ const projects = parseProjectMap(row.projects);
16209
+ if (local) hub.local = local;
16210
+ if (cloud) hub.cloud = cloud;
16211
+ if (projects && Object.keys(projects).length > 0) hub.projects = projects;
16212
+ return hub;
16213
+ }
16214
+ function findConfigRoot(start) {
16215
+ let dir = resolve2(start);
16216
+ for (; ; ) {
16217
+ if (existsSync2(join2(dir, ".kxm", "project.yaml")) || existsSync2(join2(dir, ".kxm", "config.yaml"))) return dir;
16218
+ const parent = dirname2(dir);
16219
+ if (parent === dir) return resolve2(start);
16220
+ dir = parent;
16221
+ }
16222
+ }
16223
+ function userConfigDirForEnv(env) {
16224
+ const explicit = env.KXM_USER_CONFIG_DIR?.trim();
16225
+ if (explicit) return explicit;
16226
+ if (env === process.env) return void 0;
16227
+ return join2(tmpdir(), "kxm-no-user-config");
16228
+ }
16229
+ function loadHubSettings(start, env = process.env) {
16230
+ const userConfigDir = userConfigDirForEnv(env);
16231
+ return loadKxmConfig(findConfigRoot(start), userConfigDir === void 0 ? {} : { userConfigDir }).hub;
16074
16232
  }
16075
16233
  function recordOf(raw) {
16076
16234
  return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
@@ -16145,15 +16303,27 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
16145
16303
 
16146
16304
  // plugins/kxm/src/hub-autostart.ts
16147
16305
  import { spawn } from "node:child_process";
16148
- import { closeSync, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync5 } from "node:fs";
16149
- import { join as join6, resolve as resolve5 } from "node:path";
16306
+ import { closeSync, mkdirSync as mkdirSync5, openSync, readFileSync as readFileSync6 } from "node:fs";
16307
+ import { join as join7, resolve as resolve6 } from "node:path";
16150
16308
 
16151
16309
  // plugins/kxm/src/hub-binding.ts
16310
+ import { spawnSync } from "node:child_process";
16152
16311
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
16153
16312
  import { homedir as homedir3 } from "node:os";
16154
16313
  import { dirname as dirname3, isAbsolute, join as join3, resolve as resolve3 } from "node:path";
16155
16314
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
16156
16315
  var HUB_HEALTH_PROBE_MS = 300;
16316
+ var CloudTokenError = class extends Error {
16317
+ code;
16318
+ constructor(code, message) {
16319
+ super(message);
16320
+ this.name = "CloudTokenError";
16321
+ this.code = code;
16322
+ }
16323
+ };
16324
+ var TOKEN_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
16325
+ var TOKEN_COMMAND_TIMEOUT_MS = 15e3;
16326
+ var TOKEN_COMMAND_MAX_BUFFER = 4096;
16157
16327
  var HubBindingError = class extends Error {
16158
16328
  constructor(message) {
16159
16329
  super(message);
@@ -16191,6 +16361,99 @@ function validateHubUrl(raw) {
16191
16361
  }
16192
16362
  return parsed.href.replace(/\/$/, "");
16193
16363
  }
16364
+ function splitTokenCommand(raw) {
16365
+ if (/[|&;<>$`\n\r]/.test(raw)) {
16366
+ throw new CloudTokenError(
16367
+ "cloud_token_command_invalid",
16368
+ "token command must be a program and arguments, not a shell pipeline"
16369
+ );
16370
+ }
16371
+ const args = [];
16372
+ let current = "";
16373
+ let quote;
16374
+ for (const ch of raw) {
16375
+ if (quote) {
16376
+ if (ch === quote) quote = void 0;
16377
+ else current += ch;
16378
+ continue;
16379
+ }
16380
+ if (ch === "'" || ch === '"') {
16381
+ quote = ch;
16382
+ continue;
16383
+ }
16384
+ if (ch === " " || ch === " ") {
16385
+ if (current.length > 0) {
16386
+ args.push(current);
16387
+ current = "";
16388
+ }
16389
+ continue;
16390
+ }
16391
+ current += ch;
16392
+ }
16393
+ if (quote) {
16394
+ throw new CloudTokenError("cloud_token_command_invalid", "token command has an unclosed quote");
16395
+ }
16396
+ if (current.length > 0) args.push(current);
16397
+ if (args.length === 0) {
16398
+ throw new CloudTokenError("cloud_token_command_invalid", "token command is empty");
16399
+ }
16400
+ return args;
16401
+ }
16402
+ function isCloudTokenEnvName(value) {
16403
+ return TOKEN_ENV_NAME.test(value);
16404
+ }
16405
+ function resolveCloudHubToken(binding, env = process.env) {
16406
+ if (!binding.cloud) {
16407
+ throw new CloudTokenError("cloud_token_missing", "cloud hub token is missing; the local hub-env token was not used");
16408
+ }
16409
+ if (binding.tokenEnv) {
16410
+ const value = env[binding.tokenEnv]?.trim();
16411
+ if (value) return value;
16412
+ }
16413
+ if (binding.tokenCommand) return runTokenCommand(binding.tokenCommand, env);
16414
+ const source = binding.tokenEnv ? `${binding.tokenEnv} is unset or empty` : "no token source is configured";
16415
+ throw new CloudTokenError(
16416
+ "cloud_token_missing",
16417
+ `cloud hub token is missing: ${source}; the local hub-env token was not used`
16418
+ );
16419
+ }
16420
+ function runTokenCommand(command, env) {
16421
+ const argv = splitTokenCommand(command);
16422
+ const [program, ...args] = argv;
16423
+ const result2 = spawnSync(program, args, {
16424
+ encoding: "utf8",
16425
+ shell: false,
16426
+ timeout: TOKEN_COMMAND_TIMEOUT_MS,
16427
+ maxBuffer: TOKEN_COMMAND_MAX_BUFFER,
16428
+ env,
16429
+ windowsHide: true
16430
+ });
16431
+ if (result2.error || result2.status !== 0) {
16432
+ throw new CloudTokenError(
16433
+ "cloud_token_command_failed",
16434
+ "token command failed; the local hub-env token was not used"
16435
+ );
16436
+ }
16437
+ const stdout = result2.stdout ?? "";
16438
+ if (stdout.trim().length === 0 || stdout.trim() !== stdout.trim().split(/\r?\n/)[0]) {
16439
+ throw new CloudTokenError(
16440
+ "cloud_token_command_failed",
16441
+ "token command did not print a single token; the local hub-env token was not used"
16442
+ );
16443
+ }
16444
+ return stdout.trim();
16445
+ }
16446
+ function hubBindingScope(url) {
16447
+ let host;
16448
+ try {
16449
+ host = new URL(url).hostname.toLowerCase();
16450
+ } catch {
16451
+ return "remote";
16452
+ }
16453
+ if (host === "localhost" || host === "::1" || host === "[::1]" || host.endsWith(".localhost")) return "loopback";
16454
+ const v4 = /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(host);
16455
+ return v4 && [v4[1], v4[2], v4[3]].every((part) => Number(part) <= 255) ? "loopback" : "remote";
16456
+ }
16194
16457
  function isIsoTimestamp(value) {
16195
16458
  if (Number.isNaN(Date.parse(value))) return false;
16196
16459
  return value === new Date(value).toISOString();
@@ -16214,7 +16477,9 @@ function readHubBinding(env = process.env) {
16214
16477
  }
16215
16478
  const row = parsed;
16216
16479
  const keys = Object.keys(row);
16217
- if (keys.length !== 3 || row.schema !== HUB_BINDING_SCHEMA || typeof row.url !== "string" || typeof row.boundAt !== "string" || !isIsoTimestamp(row.boundAt)) {
16480
+ const allowed = /* @__PURE__ */ new Set(["schema", "url", "boundAt", "cloud", "tokenEnv", "tokenCommand"]);
16481
+ const cloudish = keys.some((key) => key === "cloud" || key === "tokenEnv" || key === "tokenCommand");
16482
+ 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)) {
16218
16483
  throw new HubBindingError(`malformed hub binding at ${file}`);
16219
16484
  }
16220
16485
  let url;
@@ -16223,7 +16488,34 @@ function readHubBinding(env = process.env) {
16223
16488
  } catch {
16224
16489
  throw new HubBindingError(`malformed hub binding at ${file}`);
16225
16490
  }
16226
- return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
16491
+ if (!cloudish) return { schema: HUB_BINDING_SCHEMA, url, boundAt: row.boundAt };
16492
+ if (row.cloud !== true) throw new HubBindingError(`malformed hub binding at ${file}`);
16493
+ let tokenEnv;
16494
+ let tokenCommand;
16495
+ if (row.tokenEnv !== void 0) {
16496
+ if (typeof row.tokenEnv !== "string" || !isCloudTokenEnvName(row.tokenEnv)) {
16497
+ throw new HubBindingError(`malformed hub binding at ${file}`);
16498
+ }
16499
+ tokenEnv = row.tokenEnv;
16500
+ }
16501
+ if (row.tokenCommand !== void 0) {
16502
+ if (typeof row.tokenCommand !== "string") throw new HubBindingError(`malformed hub binding at ${file}`);
16503
+ try {
16504
+ splitTokenCommand(row.tokenCommand);
16505
+ } catch {
16506
+ throw new HubBindingError(`malformed hub binding at ${file}`);
16507
+ }
16508
+ tokenCommand = row.tokenCommand;
16509
+ }
16510
+ if (!tokenEnv && !tokenCommand) throw new HubBindingError(`malformed hub binding at ${file}`);
16511
+ return {
16512
+ schema: HUB_BINDING_SCHEMA,
16513
+ url,
16514
+ boundAt: row.boundAt,
16515
+ cloud: true,
16516
+ ...tokenEnv ? { tokenEnv } : {},
16517
+ ...tokenCommand ? { tokenCommand } : {}
16518
+ };
16227
16519
  }
16228
16520
  async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
16229
16521
  const started = Date.now();
@@ -16249,11 +16541,207 @@ async function probeHubHealth(url, fetchImpl, timeoutMs = HUB_HEALTH_PROBE_MS) {
16249
16541
  }
16250
16542
  }
16251
16543
 
16544
+ // plugins/kxm/src/hub-identity.ts
16545
+ var import_yaml2 = __toESM(require_dist(), 1);
16546
+ import { spawnSync as spawnSync2 } from "node:child_process";
16547
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
16548
+ import { basename, dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
16549
+ var OP_TIMEOUT_MS = 15e3;
16550
+ var OP_MAX_BUFFER = 4096;
16551
+ function normalizeUrl(raw) {
16552
+ try {
16553
+ return validateHubUrl(raw);
16554
+ } catch {
16555
+ return void 0;
16556
+ }
16557
+ }
16558
+ function projectYamlId(start) {
16559
+ let dir = resolve4(start);
16560
+ for (; ; ) {
16561
+ const file = join4(dir, ".kxm", "project.yaml");
16562
+ if (existsSync4(file)) {
16563
+ try {
16564
+ const doc = (0, import_yaml2.parse)(readFileSync4(file, "utf8"));
16565
+ if (doc && typeof doc === "object" && !Array.isArray(doc) && typeof doc.id === "string" && doc.id.trim()) {
16566
+ return doc.id.trim();
16567
+ }
16568
+ } catch {
16569
+ }
16570
+ return void 0;
16571
+ }
16572
+ const parent = dirname4(dir);
16573
+ if (parent === dir) return void 0;
16574
+ dir = parent;
16575
+ }
16576
+ }
16577
+ function packageName(cwd) {
16578
+ try {
16579
+ const pkg = JSON.parse(readFileSync4(join4(cwd, "package.json"), "utf8"));
16580
+ if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
16581
+ } catch {
16582
+ }
16583
+ return void 0;
16584
+ }
16585
+ function activeBinding(env) {
16586
+ try {
16587
+ return readHubBinding(env);
16588
+ } catch (error2) {
16589
+ if (error2 instanceof HubBindingError) return void 0;
16590
+ throw error2;
16591
+ }
16592
+ }
16593
+ function endpointFor(mode, settings) {
16594
+ return mode === "cloud" ? settings.cloud : settings.local;
16595
+ }
16596
+ function resolveProjectIdentity(cwd, env = process.env, explicit) {
16597
+ const fromExplicit = explicit?.trim();
16598
+ if (fromExplicit) return { project: fromExplicit, source: "flag", sourceLabel: "--project" };
16599
+ const fromEnv = env.KXM_PROJECT?.trim();
16600
+ if (fromEnv) return { project: fromEnv, source: "env", sourceLabel: "KXM_PROJECT" };
16601
+ const settings = loadHubSettings(cwd, env);
16602
+ const mode = settings.mode ?? (activeBinding(env)?.cloud ? "cloud" : "local");
16603
+ const fromConfig = endpointFor(mode, settings)?.project?.trim();
16604
+ if (fromConfig) {
16605
+ return { project: fromConfig, source: "config", sourceLabel: `.kxm/config.yaml hub.${mode}.project` };
16606
+ }
16607
+ const fromProject = projectYamlId(cwd);
16608
+ if (fromProject) return { project: fromProject, source: "project.yaml", sourceLabel: ".kxm/project.yaml id" };
16609
+ const fromPackage = packageName(cwd);
16610
+ if (fromPackage) return { project: fromPackage, source: "package.json", sourceLabel: "package.json name" };
16611
+ return { project: basename(cwd), source: "directory", sourceLabel: "directory name" };
16612
+ }
16613
+ function opReferenceFromTokenCommand(command) {
16614
+ try {
16615
+ const args = splitTokenCommand(command);
16616
+ if (args[0] === "op" && args[1] === "read" && args[2] && isHubOpReference(args[2])) return args[2];
16617
+ } catch {
16618
+ return void 0;
16619
+ }
16620
+ return void 0;
16621
+ }
16622
+ function defaultOpRead(reference, env) {
16623
+ if (!isHubOpReference(reference)) {
16624
+ throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
16625
+ }
16626
+ const result2 = spawnSync2("op", ["read", reference], {
16627
+ encoding: "utf8",
16628
+ shell: false,
16629
+ timeout: OP_TIMEOUT_MS,
16630
+ maxBuffer: OP_MAX_BUFFER,
16631
+ env,
16632
+ windowsHide: true
16633
+ });
16634
+ if (result2.error || result2.status !== 0) {
16635
+ throw new CloudTokenError(
16636
+ "cloud_token_command_failed",
16637
+ `op read failed for ${reference}; the token was not saved`
16638
+ );
16639
+ }
16640
+ const line = (result2.stdout ?? "").trim();
16641
+ if (!line || line.split(/\r?\n/).length !== 1) {
16642
+ throw new CloudTokenError(
16643
+ "cloud_token_command_failed",
16644
+ `op read did not print a single token for ${reference}; the token was not saved`
16645
+ );
16646
+ }
16647
+ return line;
16648
+ }
16649
+ function resolveKeyReference(ref, env, opRead = defaultOpRead) {
16650
+ if (!ref) return void 0;
16651
+ if (ref.env) {
16652
+ const value = env[ref.env]?.trim();
16653
+ if (value) return { value, source: `env:${ref.env}` };
16654
+ }
16655
+ if (ref.op) {
16656
+ const value = opRead(ref.op, env).trim();
16657
+ if (!value || value.split(/\r?\n/).length !== 1) {
16658
+ throw new CloudTokenError("cloud_token_command_failed", `op read did not print a single token for ${ref.op}`);
16659
+ }
16660
+ return { value, source: `op:${ref.op}` };
16661
+ }
16662
+ return void 0;
16663
+ }
16664
+ function keySourceFor(input) {
16665
+ const ref = input.endpoint?.key;
16666
+ if (input.mode === "local" && input.env.KXM_AUTH_TOKEN?.trim()) return "env:KXM_AUTH_TOKEN";
16667
+ if (ref?.env && input.env[ref.env]?.trim()) return `env:${ref.env}`;
16668
+ if (ref?.op) return `op:${ref.op}`;
16669
+ if (ref?.env) return `env:${ref.env}`;
16670
+ if (input.mode === "cloud") {
16671
+ if (input.binding?.tokenEnv) return `env:${input.binding.tokenEnv}`;
16672
+ if (input.binding?.tokenCommand) {
16673
+ const op = opReferenceFromTokenCommand(input.binding.tokenCommand);
16674
+ return op ? `op:${op}` : "command";
16675
+ }
16676
+ return "missing";
16677
+ }
16678
+ if (input.hasSavedProjectToken?.(input.project)) return "hub-env";
16679
+ return "missing";
16680
+ }
16681
+ function describeHubConnection(cwd, env = process.env, hooks = {}) {
16682
+ const settings = loadHubSettings(cwd, env);
16683
+ const binding = activeBinding(env);
16684
+ const mode = settings.mode ?? (binding?.cloud ? "cloud" : "local");
16685
+ const modeSource = settings.mode ? "config" : binding?.cloud ? "binding" : "default";
16686
+ const endpoint = endpointFor(mode, settings);
16687
+ const fromEnv = env.KXM_SERVER_URL?.trim();
16688
+ const fromConfig = endpoint?.url?.trim();
16689
+ const fromBinding = binding?.url;
16690
+ let url = "http://127.0.0.1:7331";
16691
+ let urlSource = "default";
16692
+ if (fromEnv) {
16693
+ url = fromEnv.replace(/\/$/, "");
16694
+ urlSource = "env";
16695
+ } else if (fromConfig) {
16696
+ url = fromConfig.replace(/\/$/, "");
16697
+ urlSource = "config";
16698
+ } else if (fromBinding) {
16699
+ url = fromBinding;
16700
+ urlSource = "binding";
16701
+ }
16702
+ const identity = resolveProjectIdentity(cwd, env, hooks.explicitProject);
16703
+ const configuredCloudUrl = settings.cloud?.url ?? (binding?.cloud ? binding.url : void 0);
16704
+ let scope;
16705
+ if (mode === "cloud") {
16706
+ const override = fromEnv ? normalizeUrl(fromEnv) : void 0;
16707
+ const named = configuredCloudUrl ? normalizeUrl(configuredCloudUrl) : void 0;
16708
+ scope = override && named && override !== named ? hubBindingScope(url) : "remote";
16709
+ } else {
16710
+ scope = hubBindingScope(url);
16711
+ }
16712
+ return {
16713
+ ...identity,
16714
+ mode,
16715
+ modeSource,
16716
+ url,
16717
+ urlSource,
16718
+ scope,
16719
+ ...endpoint ? { endpoint } : {},
16720
+ ...binding ? { binding } : {},
16721
+ keySource: keySourceFor({
16722
+ mode,
16723
+ env,
16724
+ ...endpoint ? { endpoint } : {},
16725
+ ...binding ? { binding } : {},
16726
+ project: identity.project,
16727
+ ...hooks.hasSavedProjectToken ? { hasSavedProjectToken: hooks.hasSavedProjectToken } : {}
16728
+ })
16729
+ };
16730
+ }
16731
+ function projectTokenMissingMessage(identity) {
16732
+ return [
16733
+ `kxm has no project token for project ${identity.project} (from ${identity.sourceLabel}).`,
16734
+ `Ask the user to set this project's token. Set KXM_AUTH_TOKEN, or set hub.local.project or hub.cloud.project in .kxm/config.yaml to the project key the hub already knows, or add a key reference under hub.projects.${identity.project} (key.op is an op:// reference, key.env is an environment variable name, never the token) and restart the hub.`,
16735
+ "KXM_PROJECT or --project overrides the id for one command.",
16736
+ "An agent never uses the hub admin token."
16737
+ ].join(" ");
16738
+ }
16739
+
16252
16740
  // plugins/kxm/src/hub-env.ts
16253
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
16741
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
16254
16742
  import { homedir as homedir4 } from "node:os";
16255
16743
  import { randomBytes } from "node:crypto";
16256
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join4, resolve as resolve4 } from "node:path";
16744
+ import { dirname as dirname5, isAbsolute as isAbsolute2, join as join5, resolve as resolve5 } from "node:path";
16257
16745
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
16258
16746
  var HubEnvError = class extends Error {
16259
16747
  constructor(message) {
@@ -16265,30 +16753,30 @@ function resolveUserStateRoot2(env) {
16265
16753
  const explicit = env.KXM_STATE_HOME?.trim();
16266
16754
  if (explicit) {
16267
16755
  if (!isAbsolute2(explicit)) throw new HubEnvError("local_state_root_not_absolute");
16268
- return resolve4(explicit);
16756
+ return resolve5(explicit);
16269
16757
  }
16270
16758
  if (process.platform === "win32") {
16271
16759
  const localAppData = env.LOCALAPPDATA?.trim();
16272
- const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join4(homedir4(), "AppData", "Local");
16273
- return resolve4(base2, "KXM");
16760
+ const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join5(homedir4(), "AppData", "Local");
16761
+ return resolve5(base2, "KXM");
16274
16762
  }
16275
- if (process.platform === "darwin") return resolve4(homedir4(), "Library", "Application Support", "KXM");
16763
+ if (process.platform === "darwin") return resolve5(homedir4(), "Library", "Application Support", "KXM");
16276
16764
  const xdgState = env.XDG_STATE_HOME?.trim();
16277
- const base = xdgState && isAbsolute2(xdgState) ? xdgState : join4(homedir4(), ".local", "state");
16278
- return resolve4(base, "kxm");
16765
+ const base = xdgState && isAbsolute2(xdgState) ? xdgState : join5(homedir4(), ".local", "state");
16766
+ return resolve5(base, "kxm");
16279
16767
  }
16280
16768
  function hubEnvFile(env = process.env) {
16281
- return join4(resolveUserStateRoot2(env), "hub-env.json");
16769
+ return join5(resolveUserStateRoot2(env), "hub-env.json");
16282
16770
  }
16283
16771
  function generateHubAuthToken() {
16284
16772
  return `kxm_admin_${randomBytes(24).toString("base64url")}`;
16285
16773
  }
16286
16774
  function readHubEnvRecord(env = process.env) {
16287
16775
  const file = hubEnvFile(env);
16288
- if (!existsSync4(file)) return void 0;
16776
+ if (!existsSync5(file)) return void 0;
16289
16777
  let parsed;
16290
16778
  try {
16291
- parsed = JSON.parse(readFileSync4(file, "utf8"));
16779
+ parsed = JSON.parse(readFileSync5(file, "utf8"));
16292
16780
  } catch (error2) {
16293
16781
  throw new HubEnvError(`hub env file is malformed at ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`);
16294
16782
  }
@@ -16322,9 +16810,9 @@ function readHubEnvRecord(env = process.env) {
16322
16810
  }
16323
16811
  function writeHubEnvRecord(record2, env = process.env) {
16324
16812
  const file = hubEnvFile(env);
16325
- const directory = dirname4(file);
16813
+ const directory = dirname5(file);
16326
16814
  mkdirSync4(directory, { recursive: true });
16327
- const temporary = join4(directory, `.hub-env-${process.pid}.tmp`);
16815
+ const temporary = join5(directory, `.hub-env-${process.pid}.tmp`);
16328
16816
  writeFileSync4(temporary, `${JSON.stringify(record2, void 0, 2)}
16329
16817
  `, { encoding: "utf8", mode: 384 });
16330
16818
  try {
@@ -16393,9 +16881,24 @@ function resolveHubCredentials(options = {}) {
16393
16881
  written: needsPersist === true
16394
16882
  };
16395
16883
  }
16396
- function resolveAgentHubAuthToken(env, project) {
16884
+ function cloudModeToken(cwd, env, explicitProject, opRead) {
16885
+ const connection = describeHubConnection(cwd, env, explicitProject === void 0 ? {} : { explicitProject });
16886
+ if (connection.mode !== "cloud") return void 0;
16887
+ const fromConfig = resolveKeyReference(connection.endpoint?.key, env, opRead);
16888
+ if (fromConfig) return fromConfig.value;
16889
+ if (connection.binding?.cloud) return resolveCloudHubToken(connection.binding, env);
16890
+ throw new CloudTokenError(
16891
+ "cloud_token_missing",
16892
+ "cloud hub token is missing; the local hub-env token was not used"
16893
+ );
16894
+ }
16895
+ function resolveAgentHubAuthToken(env, project, cwd = process.cwd(), opRead) {
16896
+ const cloud = cloudModeToken(cwd, env, void 0, opRead);
16897
+ if (cloud) return cloud;
16397
16898
  const envToken = env.KXM_AUTH_TOKEN?.trim();
16398
16899
  if (envToken) return envToken;
16900
+ const fromConfig = resolveKeyReference(describeHubConnection(cwd, env).endpoint?.key, env, opRead);
16901
+ if (fromConfig) return fromConfig.value;
16399
16902
  const tokens = readHubEnvRecord(env)?.projectTokens;
16400
16903
  if (!tokens || !Object.hasOwn(tokens, project)) return void 0;
16401
16904
  return tokens[project]?.trim() || void 0;
@@ -16403,26 +16906,30 @@ function resolveAgentHubAuthToken(env, project) {
16403
16906
  var AgentProjectTokenMissingError = class extends Error {
16404
16907
  code = "project_token_missing";
16405
16908
  project;
16406
- constructor(project) {
16407
- super(
16408
- `kxm has no project token for project ${project} on this machine. Set KXM_AUTH_TOKEN to that project's token, or add ${project} to the hub KXM_PROJECT_TOKENS (list every existing project too, because that variable replaces the saved map). An agent never uses the hub admin token.`
16409
- );
16909
+ projectSource;
16910
+ hint;
16911
+ constructor(projectOrIdentity) {
16912
+ const identity = typeof projectOrIdentity === "string" ? { project: projectOrIdentity, source: "env", sourceLabel: "the project id sent to the hub" } : projectOrIdentity;
16913
+ const message = projectTokenMissingMessage(identity);
16914
+ super(message);
16410
16915
  this.name = "AgentProjectTokenMissingError";
16411
- this.project = project;
16916
+ this.project = identity.project;
16917
+ this.projectSource = typeof projectOrIdentity === "string" ? "unknown" : identity.source;
16918
+ this.hint = message;
16412
16919
  }
16413
16920
  };
16414
16921
 
16415
16922
  // plugins/kxm/src/repo-root.ts
16416
- import { existsSync as existsSync5 } from "node:fs";
16417
- import { dirname as dirname5, join as join5 } from "node:path";
16923
+ import { existsSync as existsSync6 } from "node:fs";
16924
+ import { dirname as dirname6, join as join6 } from "node:path";
16418
16925
  import { fileURLToPath } from "node:url";
16419
16926
  var ROOT_MARKERS = ["scripts/kxm-hub.mjs", "scripts/kxm.mjs"];
16420
16927
  var MAX_WALK_DEPTH = 10;
16421
16928
  function tryFindKxmRepoRoot(fromUrl = import.meta.url) {
16422
- let dir = dirname5(fileURLToPath(fromUrl));
16929
+ let dir = dirname6(fileURLToPath(fromUrl));
16423
16930
  for (let depth = 0; depth < MAX_WALK_DEPTH; depth += 1) {
16424
- if (ROOT_MARKERS.some((marker) => existsSync5(join5(dir, marker)))) return dir;
16425
- const parent = dirname5(dir);
16931
+ if (ROOT_MARKERS.some((marker) => existsSync6(join6(dir, marker)))) return dir;
16932
+ const parent = dirname6(dir);
16426
16933
  if (parent === dir) break;
16427
16934
  dir = parent;
16428
16935
  }
@@ -16455,7 +16962,7 @@ function defaultProcessExists(pid) {
16455
16962
  function readLiveHubClaim(stateDir, processExists2 = defaultProcessExists) {
16456
16963
  let parsed;
16457
16964
  try {
16458
- parsed = JSON.parse(readFileSync5(join6(stateDir, "hub.pid"), "utf8"));
16965
+ parsed = JSON.parse(readFileSync6(join7(stateDir, "hub.pid"), "utf8"));
16459
16966
  } catch {
16460
16967
  return void 0;
16461
16968
  }
@@ -16470,7 +16977,7 @@ function readLiveHubClaim(stateDir, processExists2 = defaultProcessExists) {
16470
16977
  };
16471
16978
  }
16472
16979
  function defaultHubWrapperScriptPath(moduleUrl = import.meta.url) {
16473
- return join6(findKxmRepoRoot(moduleUrl), "scripts", "kxm-hub.mjs");
16980
+ return join7(findKxmRepoRoot(moduleUrl), "scripts", "kxm-hub.mjs");
16474
16981
  }
16475
16982
  function defaultHubSpawner(command, args, options) {
16476
16983
  const child = spawn(command, [...args], options);
@@ -16479,7 +16986,7 @@ function defaultHubSpawner(command, args, options) {
16479
16986
  }
16480
16987
  function logTail(logPath, maxChars = 2e3) {
16481
16988
  try {
16482
- return redactSecrets(readFileSync5(logPath, "utf8").trim().slice(-maxChars));
16989
+ return redactSecrets(readFileSync6(logPath, "utf8").trim().slice(-maxChars));
16483
16990
  } catch {
16484
16991
  return "";
16485
16992
  }
@@ -16492,11 +16999,28 @@ async function ensureHubRunning(options) {
16492
16999
  if (binding) {
16493
17000
  const { health } = await probeHubHealth(binding.url, fetchImpl);
16494
17001
  if (health === "on") return { status: "bound-healthy", url: binding.url };
17002
+ if (binding.cloud) return { status: "failed", reason: "cloud_hub_unreachable", logPath: "" };
17003
+ }
17004
+ if (!binding?.cloud) {
17005
+ try {
17006
+ const connection = describeHubConnection(options.cwd, env);
17007
+ if (connection.mode === "cloud") {
17008
+ const { health } = await probeHubHealth(connection.url, fetchImpl);
17009
+ if (health === "on") return { status: "bound-healthy", url: connection.url };
17010
+ return { status: "failed", reason: "cloud_hub_unreachable", logPath: "" };
17011
+ }
17012
+ } catch (error2) {
17013
+ return {
17014
+ status: "failed",
17015
+ reason: error2 instanceof Error ? error2.message : "hub_config_invalid",
17016
+ logPath: ""
17017
+ };
17018
+ }
16495
17019
  }
16496
- const workdir = resolve5(env.KXM_WORKDIR?.trim() || options.cwd);
16497
- const workspaceDir = resolve5(workdir, env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
16498
- const stateDir = resolve5(workdir, env.KXM_STATE_DIR?.trim() || join6(workspaceDir, "state"));
16499
- const logsDir = resolve5(workdir, env.KXM_LOGS_DIR?.trim() || join6(workspaceDir, "logs"));
17020
+ const workdir = resolve6(env.KXM_WORKDIR?.trim() || options.cwd);
17021
+ const workspaceDir = resolve6(workdir, env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
17022
+ const stateDir = resolve6(workdir, env.KXM_STATE_DIR?.trim() || join7(workspaceDir, "state"));
17023
+ const logsDir = resolve6(workdir, env.KXM_LOGS_DIR?.trim() || join7(workspaceDir, "logs"));
16500
17024
  const processExists2 = options.processExists ?? defaultProcessExists;
16501
17025
  const live = readLiveHubClaim(stateDir, processExists2);
16502
17026
  if (live) return { status: "claim-alive", pid: live.pid };
@@ -16506,7 +17030,7 @@ async function ensureHubRunning(options) {
16506
17030
  if (health === "on") return { status: "url-healthy", url: serverUrl };
16507
17031
  }
16508
17032
  const credentials = resolveHubCredentials({ env });
16509
- const logPath = join6(logsDir, HUB_AUTOSTART_LOG_NAME);
17033
+ const logPath = join7(logsDir, HUB_AUTOSTART_LOG_NAME);
16510
17034
  mkdirSync5(logsDir, { recursive: true });
16511
17035
  const logFd = openSync(logPath, "a");
16512
17036
  let child;
@@ -16553,20 +17077,6 @@ async function ensureHubRunning(options) {
16553
17077
  }
16554
17078
  }
16555
17079
 
16556
- // plugins/kxm/src/project-name.ts
16557
- import { readFileSync as readFileSync6 } from "node:fs";
16558
- import { basename, join as join7 } from "node:path";
16559
- function defaultProjectName(cwd, env = process.env) {
16560
- const fromEnv = env.KXM_PROJECT?.trim();
16561
- if (fromEnv) return fromEnv;
16562
- try {
16563
- const pkg = JSON.parse(readFileSync6(join7(cwd, "package.json"), "utf8"));
16564
- if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
16565
- } catch {
16566
- }
16567
- return basename(cwd);
16568
- }
16569
-
16570
17080
  // plugins/kxm/src/nous-pi.ts
16571
17081
  import { readFile } from "node:fs/promises";
16572
17082
 
@@ -18437,7 +18947,7 @@ function closeServerGracefully(server) {
18437
18947
  server.close();
18438
18948
  }
18439
18949
  function startCallbackServer(expectedState) {
18440
- return new Promise((resolve8, reject) => {
18950
+ return new Promise((resolve9, reject) => {
18441
18951
  let settled = false;
18442
18952
  let timeout;
18443
18953
  let resolveCode;
@@ -18516,7 +19026,7 @@ function startCallbackServer(expectedState) {
18516
19026
  finish(() => rejectCode(new Error("OAuth callback timed out waiting for browser login")));
18517
19027
  close();
18518
19028
  }, OAUTH_CALLBACK_TIMEOUT_MS);
18519
- resolve8({ server, waitForCode: () => codePromise, cleanup });
19029
+ resolve9({ server, waitForCode: () => codePromise, cleanup });
18520
19030
  });
18521
19031
  });
18522
19032
  }
@@ -19135,8 +19645,8 @@ var AssistantMessageEventStream = class {
19135
19645
  finalResultPromise;
19136
19646
  resolveFinalResult;
19137
19647
  constructor() {
19138
- this.finalResultPromise = new Promise((resolve8) => {
19139
- this.resolveFinalResult = resolve8;
19648
+ this.finalResultPromise = new Promise((resolve9) => {
19649
+ this.resolveFinalResult = resolve9;
19140
19650
  });
19141
19651
  }
19142
19652
  push(event) {
@@ -19166,8 +19676,8 @@ var AssistantMessageEventStream = class {
19166
19676
  } else if (this.done) {
19167
19677
  return;
19168
19678
  } else {
19169
- const next = await new Promise((resolve8) => {
19170
- this.waiting.push(resolve8);
19679
+ const next = await new Promise((resolve9) => {
19680
+ this.waiting.push(resolve9);
19171
19681
  });
19172
19682
  if (next.done) return;
19173
19683
  if (next.value) yield next.value;
@@ -20486,8 +20996,8 @@ var AssistantMessageEventStream2 = class {
20486
20996
  finalResultPromise;
20487
20997
  resolveFinalResult;
20488
20998
  constructor() {
20489
- this.finalResultPromise = new Promise((resolve8) => {
20490
- this.resolveFinalResult = resolve8;
20999
+ this.finalResultPromise = new Promise((resolve9) => {
21000
+ this.resolveFinalResult = resolve9;
20491
21001
  });
20492
21002
  }
20493
21003
  push(event) {
@@ -20517,8 +21027,8 @@ var AssistantMessageEventStream2 = class {
20517
21027
  } else if (this.done) {
20518
21028
  return;
20519
21029
  } else {
20520
- const next = await new Promise((resolve8) => {
20521
- this.waiting.push(resolve8);
21030
+ const next = await new Promise((resolve9) => {
21031
+ this.waiting.push(resolve9);
20522
21032
  });
20523
21033
  if (next.done) return;
20524
21034
  if (next.value) yield next.value;
@@ -20544,11 +21054,11 @@ function emptyUsage() {
20544
21054
  }
20545
21055
 
20546
21056
  // plugins/kxm/src/providers/claude-bridge/config.ts
20547
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "node:fs";
21057
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
20548
21058
  import { homedir as homedir5 } from "node:os";
20549
21059
  import { join as join8 } from "node:path";
20550
21060
  function tryParseJson(path) {
20551
- if (!existsSync6(path)) return {};
21061
+ if (!existsSync7(path)) return {};
20552
21062
  try {
20553
21063
  return JSON.parse(readFileSync7(path, "utf-8"));
20554
21064
  } catch (error2) {
@@ -33576,7 +34086,7 @@ var Protocol = class {
33576
34086
  return;
33577
34087
  }
33578
34088
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
33579
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
34089
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
33580
34090
  options?.signal?.throwIfAborted();
33581
34091
  }
33582
34092
  } catch (error2) {
@@ -33593,7 +34103,7 @@ var Protocol = class {
33593
34103
  */
33594
34104
  request(request, resultSchema, options) {
33595
34105
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
33596
- return new Promise((resolve8, reject) => {
34106
+ return new Promise((resolve9, reject) => {
33597
34107
  const earlyReject = (error2) => {
33598
34108
  reject(error2);
33599
34109
  };
@@ -33671,7 +34181,7 @@ var Protocol = class {
33671
34181
  if (!parseResult.success) {
33672
34182
  reject(parseResult.error);
33673
34183
  } else {
33674
- resolve8(parseResult.data);
34184
+ resolve9(parseResult.data);
33675
34185
  }
33676
34186
  } catch (error2) {
33677
34187
  reject(error2);
@@ -33932,12 +34442,12 @@ var Protocol = class {
33932
34442
  }
33933
34443
  } catch {
33934
34444
  }
33935
- return new Promise((resolve8, reject) => {
34445
+ return new Promise((resolve9, reject) => {
33936
34446
  if (signal.aborted) {
33937
34447
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
33938
34448
  return;
33939
34449
  }
33940
- const timeoutId = setTimeout(resolve8, interval);
34450
+ const timeoutId = setTimeout(resolve9, interval);
33941
34451
  signal.addEventListener("abort", () => {
33942
34452
  clearTimeout(timeoutId);
33943
34453
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -35028,7 +35538,7 @@ var McpServer = class {
35028
35538
  let task = createTaskResult.task;
35029
35539
  const pollInterval = task.pollInterval ?? 5e3;
35030
35540
  while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
35031
- await new Promise((resolve8) => setTimeout(resolve8, pollInterval));
35541
+ await new Promise((resolve9) => setTimeout(resolve9, pollInterval));
35032
35542
  const updatedTask = await extra.taskStore.getTask(taskId);
35033
35543
  if (!updatedTask) {
35034
35544
  throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
@@ -35646,8 +36156,8 @@ function makePromptStream() {
35646
36156
  try {
35647
36157
  while (true) {
35648
36158
  while (queue.length === 0 && !done && !failure) {
35649
- await new Promise((resolve8) => {
35650
- wake = resolve8;
36159
+ await new Promise((resolve9) => {
36160
+ wake = resolve9;
35651
36161
  });
35652
36162
  }
35653
36163
  if (failure) throw failure;
@@ -35668,8 +36178,8 @@ function makePromptStream() {
35668
36178
  }
35669
36179
  return {
35670
36180
  stream: gen(),
35671
- push: (msg) => failure || done ? Promise.reject(failure ?? new Error("prompt stream closed")) : new Promise((resolve8, reject) => {
35672
- queue.push({ msg, resolve: resolve8, reject });
36181
+ push: (msg) => failure || done ? Promise.reject(failure ?? new Error("prompt stream closed")) : new Promise((resolve9, reject) => {
36182
+ queue.push({ msg, resolve: resolve9, reject });
35673
36183
  kick();
35674
36184
  }),
35675
36185
  end: () => {
@@ -36080,8 +36590,8 @@ function buildMcpServers(tools, queryCtx) {
36080
36590
  queryCtx.pendingResults.delete(toolCallId);
36081
36591
  return result2;
36082
36592
  }
36083
- return new Promise((resolve8) => {
36084
- queryCtx.pendingToolCalls.set(toolCallId, { toolName: tool.name, resolve: resolve8 });
36593
+ return new Promise((resolve9) => {
36594
+ queryCtx.pendingToolCalls.set(toolCallId, { toolName: tool.name, resolve: resolve9 });
36085
36595
  });
36086
36596
  }
36087
36597
  }));
@@ -36601,20 +37111,20 @@ async function consumeWorkerRecoveryEnvelope(client, stateDir, agentName, projec
36601
37111
  }
36602
37112
 
36603
37113
  // plugins/kxm/src/session-work.ts
36604
- import { spawnSync } from "node:child_process";
37114
+ import { spawnSync as spawnSync3 } from "node:child_process";
36605
37115
  import { randomUUID } from "node:crypto";
36606
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
37116
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync as renameSync4, writeFileSync as writeFileSync6 } from "node:fs";
36607
37117
  import { join as join13 } from "node:path";
36608
37118
 
36609
37119
  // plugins/kxm/src/local-snapshot.ts
36610
- import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync10 } from "node:fs";
37120
+ import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync10 } from "node:fs";
36611
37121
  import { homedir as homedir6 } from "node:os";
36612
- import { isAbsolute as isAbsolute3, join as join11, resolve as resolve7 } from "node:path";
37122
+ import { isAbsolute as isAbsolute3, join as join11, resolve as resolve8 } from "node:path";
36613
37123
 
36614
37124
  // plugins/kxm/src/sqlite.ts
36615
- import { existsSync as existsSync7 } from "node:fs";
37125
+ import { existsSync as existsSync8 } from "node:fs";
36616
37126
  import { createRequire as createRequire2 } from "node:module";
36617
- import { resolve as resolve6 } from "node:path";
37127
+ import { resolve as resolve7 } from "node:path";
36618
37128
  import { pathToFileURL } from "node:url";
36619
37129
  var requireFromHere = createRequire2(import.meta.url);
36620
37130
  function loadNative() {
@@ -36653,15 +37163,15 @@ var DatabaseSync = class {
36653
37163
  }
36654
37164
  };
36655
37165
  function openReadOnlyDatabase(path) {
36656
- if (existsSync7(`${path}-wal`)) return new DatabaseSync(path, { readOnly: true });
36657
- const uri = pathToFileURL(resolve6(path));
37166
+ if (existsSync8(`${path}-wal`)) return new DatabaseSync(path, { readOnly: true });
37167
+ const uri = pathToFileURL(resolve7(path));
36658
37168
  uri.searchParams.set("immutable", "1");
36659
37169
  return new DatabaseSync(uri.href, { readOnly: true });
36660
37170
  }
36661
37171
 
36662
37172
  // plugins/kxm/src/telemetry.ts
36663
37173
  import { appendFileSync, mkdirSync as mkdirSync6, readFileSync as readFileSync9 } from "node:fs";
36664
- import { dirname as dirname6, join as join10 } from "node:path";
37174
+ import { dirname as dirname7, join as join10 } from "node:path";
36665
37175
  function telemetryPath(logsDir) {
36666
37176
  return join10(logsDir, "telemetry.jsonl");
36667
37177
  }
@@ -36825,34 +37335,34 @@ function readOpenMessageMetadata(database, where = OPEN_MESSAGE_WHERE, params =
36825
37335
  function resolveKxmSnapshotPaths(cwd, env = process.env) {
36826
37336
  const stateDir = env.KXM_STATE_DIR?.trim() || join11(cwd, ".kxm", "state");
36827
37337
  const configured = env.KXM_DATA_PATH?.trim();
36828
- const dataPath = configured ? resolve7(cwd, configured) : join11(stateDir, "kxm.db");
37338
+ const dataPath = configured ? resolve8(cwd, configured) : join11(stateDir, "kxm.db");
36829
37339
  return { dataPath, stateDir };
36830
37340
  }
36831
37341
  function resolveKxmStateRoot(stateDir, options) {
36832
- if (options?.kxmStateRoot && existsSync8(options.kxmStateRoot)) {
36833
- return resolve7(options.kxmStateRoot);
37342
+ if (options?.kxmStateRoot && existsSync9(options.kxmStateRoot)) {
37343
+ return resolve8(options.kxmStateRoot);
36834
37344
  }
36835
- if (existsSync8(join11(stateDir, "runtime", "registry.db")) || existsSync8(join11(stateDir, "runtime", "projects"))) {
37345
+ if (existsSync9(join11(stateDir, "runtime", "registry.db")) || existsSync9(join11(stateDir, "runtime", "projects"))) {
36836
37346
  return stateDir;
36837
37347
  }
36838
37348
  const env = options?.env ?? process.env;
36839
37349
  const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
36840
- if (explicit && isAbsolute3(explicit) && existsSync8(resolve7(explicit))) {
36841
- return resolve7(explicit);
37350
+ if (explicit && isAbsolute3(explicit) && existsSync9(resolve8(explicit))) {
37351
+ return resolve8(explicit);
36842
37352
  }
36843
37353
  let base;
36844
37354
  if (process.platform === "win32") {
36845
37355
  const localAppData = env.LOCALAPPDATA?.trim();
36846
37356
  base = localAppData && isAbsolute3(localAppData) ? localAppData : join11(homedir6(), "AppData", "Local");
36847
- base = resolve7(base, "KXM");
37357
+ base = resolve8(base, "KXM");
36848
37358
  } else if (process.platform === "darwin") {
36849
- base = resolve7(homedir6(), "Library", "Application Support", "KXM");
37359
+ base = resolve8(homedir6(), "Library", "Application Support", "KXM");
36850
37360
  } else {
36851
37361
  const xdgState = env.XDG_STATE_HOME?.trim();
36852
37362
  base = xdgState && isAbsolute3(xdgState) ? xdgState : join11(homedir6(), ".local", "state");
36853
- base = resolve7(base, "kxm");
37363
+ base = resolve8(base, "kxm");
36854
37364
  }
36855
- if (existsSync8(base)) return base;
37365
+ if (existsSync9(base)) return base;
36856
37366
  return void 0;
36857
37367
  }
36858
37368
  function readRuntimeRuns(eventDb, projectId) {
@@ -36885,7 +37395,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
36885
37395
  let legacyRuns = [];
36886
37396
  let legacyRunTotal = 0;
36887
37397
  let plans = [];
36888
- if (existsSync8(dataPath)) {
37398
+ if (existsSync9(dataPath)) {
36889
37399
  hasLegacy = true;
36890
37400
  const database = openReadOnlyDatabase(dataPath);
36891
37401
  try {
@@ -36920,7 +37430,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
36920
37430
  const registryDbPath = join11(runtimeDir, "registry.db");
36921
37431
  const projectsDir = join11(runtimeDir, "projects");
36922
37432
  const projectKeys = /* @__PURE__ */ new Set();
36923
- if (existsSync8(registryDbPath)) {
37433
+ if (existsSync9(registryDbPath)) {
36924
37434
  hasKxm = true;
36925
37435
  try {
36926
37436
  const regDb = openReadOnlyDatabase(registryDbPath);
@@ -36936,7 +37446,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
36936
37446
  } catch {
36937
37447
  }
36938
37448
  }
36939
- if (!scope && existsSync8(projectsDir)) {
37449
+ if (!scope && existsSync9(projectsDir)) {
36940
37450
  try {
36941
37451
  for (const entry of readdirSync(projectsDir, { withFileTypes: true })) {
36942
37452
  if (entry.isDirectory()) {
@@ -36949,7 +37459,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
36949
37459
  for (const key of projectKeys) {
36950
37460
  if (scope && !RUNTIME_PROJECT_KEY.test(key)) continue;
36951
37461
  const eventDbPath = join11(projectsDir, key, "run-events.db");
36952
- if (existsSync8(eventDbPath)) {
37462
+ if (existsSync9(eventDbPath)) {
36953
37463
  hasKxm = true;
36954
37464
  try {
36955
37465
  const eventDb = openReadOnlyDatabase(eventDbPath);
@@ -36967,7 +37477,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
36967
37477
  }
36968
37478
  }
36969
37479
  const pids = [];
36970
- if (existsSync8(stateDir)) {
37480
+ if (existsSync9(stateDir)) {
36971
37481
  for (const file of readdirSync(stateDir).filter((name) => name.endsWith(".pid"))) {
36972
37482
  try {
36973
37483
  const record2 = JSON.parse(readFileSync10(join11(stateDir, file), "utf8"));
@@ -37010,7 +37520,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
37010
37520
  source = "legacy";
37011
37521
  }
37012
37522
  const telemetryFile = join11(stateDir, "telemetry.jsonl");
37013
- const spend = existsSync8(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
37523
+ const spend = existsSync9(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
37014
37524
  return {
37015
37525
  source,
37016
37526
  agents: agents.sort((a, b) => Number(b.online) - Number(a.online) || a.name.localeCompare(b.name)),
@@ -37025,13 +37535,13 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
37025
37535
  }
37026
37536
 
37027
37537
  // plugins/kxm/src/kxm-update.ts
37028
- import { existsSync as existsSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync7 } from "node:fs";
37538
+ import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync5, mkdirSync as mkdirSync7 } from "node:fs";
37029
37539
  import { join as join12 } from "node:path";
37030
37540
  var KXM_UPDATE_CACHE = "update-check.json";
37031
37541
  var CACHE_TTL_MS = 6 * 60 * 60 * 1e3;
37032
37542
  function readUpdateCache(stateDir, now = Date.now()) {
37033
37543
  const path = join12(stateDir, KXM_UPDATE_CACHE);
37034
- if (!existsSync9(path)) return void 0;
37544
+ if (!existsSync10(path)) return void 0;
37035
37545
  try {
37036
37546
  const row = JSON.parse(readFileSync11(path, "utf8"));
37037
37547
  if (typeof row.checkedAt !== "number" || !row.notice || now - row.checkedAt > CACHE_TTL_MS) return void 0;
@@ -37099,10 +37609,10 @@ function formatShipLine(ship) {
37099
37609
  }
37100
37610
  function readGitShip(cwd) {
37101
37611
  try {
37102
- const dirty = spawnSync("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
37612
+ const dirty = spawnSync3("git", ["--no-optional-locks", "-C", cwd, "status", "--porcelain"], { encoding: "utf8", windowsHide: true });
37103
37613
  if (dirty.status !== 0) return void 0;
37104
37614
  const isDirty2 = dirty.stdout.trim().length > 0;
37105
- const upstream = spawnSync("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
37615
+ const upstream = spawnSync3("git", ["-C", cwd, "rev-list", "--count", "@{u}..HEAD"], { encoding: "utf8", windowsHide: true });
37106
37616
  if (upstream.status === 0) {
37107
37617
  return {
37108
37618
  dirty: isDirty2,
@@ -37110,9 +37620,9 @@ function readGitShip(cwd) {
37110
37620
  };
37111
37621
  }
37112
37622
  for (const baseRef of ["origin/HEAD", "main", "origin/main", "master", "origin/master"]) {
37113
- const mb = spawnSync("git", ["-C", cwd, "merge-base", baseRef, "HEAD"], { encoding: "utf8", windowsHide: true });
37623
+ const mb = spawnSync3("git", ["-C", cwd, "merge-base", baseRef, "HEAD"], { encoding: "utf8", windowsHide: true });
37114
37624
  if (mb.status === 0 && mb.stdout.trim()) {
37115
- const count = spawnSync("git", ["-C", cwd, "rev-list", "--count", `${mb.stdout.trim()}..HEAD`], { encoding: "utf8", windowsHide: true });
37625
+ const count = spawnSync3("git", ["-C", cwd, "rev-list", "--count", `${mb.stdout.trim()}..HEAD`], { encoding: "utf8", windowsHide: true });
37116
37626
  if (count.status === 0) {
37117
37627
  return {
37118
37628
  dirty: isDirty2,
@@ -37202,7 +37712,7 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
37202
37712
  function readCachedSessionBrief(stateDir) {
37203
37713
  const file = join13(stateDir, "session-brief.json");
37204
37714
  try {
37205
- if (!existsSync10(file)) return void 0;
37715
+ if (!existsSync11(file)) return void 0;
37206
37716
  const raw = readFileSync12(file, "utf8");
37207
37717
  const parsed = JSON.parse(raw);
37208
37718
  if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
@@ -37229,7 +37739,7 @@ function writeCachedSessionBrief(stateDir, brief) {
37229
37739
  function estimateSessionCost(stateDir) {
37230
37740
  try {
37231
37741
  const telemetryFile = join13(stateDir, "telemetry.jsonl");
37232
- if (!existsSync10(telemetryFile)) return void 0;
37742
+ if (!existsSync11(telemetryFile)) return void 0;
37233
37743
  const records = readRoutingRecords(telemetryFile);
37234
37744
  if (records.length === 0) return void 0;
37235
37745
  let totalCost = 0;
@@ -37404,11 +37914,11 @@ function kxmSlashCompletions(prefix) {
37404
37914
  }
37405
37915
 
37406
37916
  // plugins/kxm/src/workflow-tui.ts
37407
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
37917
+ import { existsSync as existsSync12, readFileSync as readFileSync13 } from "node:fs";
37408
37918
  import { join as join14 } from "node:path";
37409
37919
  function loadActiveWorkflowProgress(repoRoot = process.cwd(), targetRunId) {
37410
37920
  const dbPath = join14(repoConfigDirectory(repoRoot), "state", "kxm.db");
37411
- if (!existsSync11(dbPath)) return void 0;
37921
+ if (!existsSync12(dbPath)) return void 0;
37412
37922
  let db;
37413
37923
  try {
37414
37924
  db = new DatabaseSync(dbPath, { readOnly: true });
@@ -37445,7 +37955,7 @@ function loadActiveWorkflowProgress(repoRoot = process.cwd(), targetRunId) {
37445
37955
  try {
37446
37956
  const logsDir = join14(repoConfigDirectory(repoRoot), "logs");
37447
37957
  const telemFile = telemetryPath(logsDir);
37448
- if (existsSync11(telemFile)) {
37958
+ if (existsSync12(telemFile)) {
37449
37959
  const records = readRoutingRecords(telemFile);
37450
37960
  const routingRunId = (routing) => "runId" in routing ? routing.runId : routing.workflowRunId;
37451
37961
  let matching = records.filter((r) => routingRunId(r.routing) === runId);
@@ -37737,7 +38247,7 @@ function piMeshExtension(pi) {
37737
38247
  messageId: message.id,
37738
38248
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
37739
38249
  };
37740
- mkdirSync9(dirname7(path), { recursive: true });
38250
+ mkdirSync9(dirname8(path), { recursive: true });
37741
38251
  const tmp = `${path}.${process.pid}.tmp`;
37742
38252
  writeFileSync7(tmp, `${JSON.stringify(request)}
37743
38253
  `, { encoding: "utf8", mode: 384 });
@@ -37768,7 +38278,7 @@ function piMeshExtension(pi) {
37768
38278
  removeMatchingLegacyRecoveryContext();
37769
38279
  return;
37770
38280
  }
37771
- mkdirSync9(dirname7(path), { recursive: true });
38281
+ mkdirSync9(dirname8(path), { recursive: true });
37772
38282
  const tmp = `${path}.${process.pid}.tmp`;
37773
38283
  writeFileSync7(tmp, `${JSON.stringify({ version: 1, agentName, project: projectName, runId: runId ?? null, stageId: recoveryStageId ?? null, activeMessageIds, pendingMessageIds, artifactPointers: recoveryArtifacts.slice(0, 16), updatedAt: (/* @__PURE__ */ new Date()).toISOString() })}
37774
38284
  `, { encoding: "utf8", mode: 384 });
@@ -37818,7 +38328,7 @@ function piMeshExtension(pi) {
37818
38328
  ctx2.ui.setEditorText?.(item.prompt);
37819
38329
  }
37820
38330
  async function showKxmHub(ctx2) {
37821
- const url = (process.env.KXM_SERVER_URL ?? "http://127.0.0.1:7331").replace(/\/$/, "");
38331
+ const url = describeHubConnection(process.cwd(), process.env).url;
37822
38332
  let health = "unreachable";
37823
38333
  try {
37824
38334
  const response = await fetch(`${url}/health`);
@@ -38108,8 +38618,9 @@ function piMeshExtension(pi) {
38108
38618
  return;
38109
38619
  }
38110
38620
  currentPiSessionId = ctx2.sessionManager?.getSessionId();
38111
- const serverUrl = process.env.KXM_SERVER_URL ?? "http://127.0.0.1:7331";
38112
- const project = defaultProjectName(ctx2.cwd, process.env);
38621
+ const identity = resolveProjectIdentity(ctx2.cwd, process.env);
38622
+ const serverUrl = describeHubConnection(ctx2.cwd, process.env).url;
38623
+ const project = identity.project;
38113
38624
  const name = process.env.KXM_AGENT_NAME ?? pi.getSessionName() ?? `pi-${process.pid}`;
38114
38625
  agentName = name;
38115
38626
  projectName = project;
@@ -38135,14 +38646,15 @@ function piMeshExtension(pi) {
38135
38646
  }
38136
38647
  let hubAuthToken;
38137
38648
  try {
38138
- hubAuthToken = resolveAgentHubAuthToken(process.env, project);
38649
+ hubAuthToken = resolveAgentHubAuthToken(process.env, project, ctx2.cwd);
38139
38650
  } catch (error2) {
38140
- ctx2.ui.notify(`kxm could not read persisted hub credentials: ${error2 instanceof Error ? error2.message : String(error2)}`, "error");
38651
+ const detail = error2 instanceof CloudTokenError ? error2.message : `kxm could not read persisted hub credentials: ${error2 instanceof Error ? error2.message : String(error2)}`;
38652
+ ctx2.ui.notify(detail, "error");
38141
38653
  await applySessionChrome(ctx2, event, true);
38142
38654
  return;
38143
38655
  }
38144
38656
  if (!hubAuthToken) {
38145
- ctx2.ui.notify(new AgentProjectTokenMissingError(project).message, "error");
38657
+ ctx2.ui.notify(new AgentProjectTokenMissingError(identity).message, "error");
38146
38658
  await applySessionChrome(ctx2, event, true);
38147
38659
  return;
38148
38660
  }
@@ -38356,12 +38868,12 @@ function piMeshExtension(pi) {
38356
38868
  if (command === "memory") {
38357
38869
  const cwd = typeof ctx2.cwd === "string" ? ctx2.cwd : process.cwd();
38358
38870
  const kxmBin = process.env.KXM_BIN || "kxm";
38359
- const res = spawnSync2(kxmBin, ["memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
38871
+ const res = spawnSync4(kxmBin, ["memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
38360
38872
  let memText = res.status === 0 && res.stdout ? res.stdout.trim() : "";
38361
38873
  if (!memText) {
38362
38874
  const script = join15(cwd, "scripts", "kxm.mjs");
38363
- if (existsSync12(script)) {
38364
- const scriptRes = spawnSync2(process.execPath, [script, "memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
38875
+ if (existsSync13(script)) {
38876
+ const scriptRes = spawnSync4(process.execPath, [script, "memory", "brief"], { cwd, encoding: "utf8", windowsHide: true });
38365
38877
  if (scriptRes.status === 0 && scriptRes.stdout) memText = scriptRes.stdout.trim();
38366
38878
  }
38367
38879
  }