@kontextmind/kxm 0.7.165 → 0.7.167

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 (41) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/docs/README.md +1 -0
  4. package/docs/architecture/hosted-direction.md +9 -4
  5. package/docs/architecture/inventory.md +1 -0
  6. package/docs/concepts/trust-model.md +1 -1
  7. package/docs/contributing/decisions.md +153 -0
  8. package/docs/contributing/learnings.md +54 -53
  9. package/docs/contributing/operating-rules.md +19 -6
  10. package/docs/glossary.md +2 -2
  11. package/docs/operations/troubleshooting.md +2 -2
  12. package/docs/operations.md +62 -8
  13. package/docs/reference/cli-reference.md +11 -11
  14. package/docs/reference/config-reference.md +36 -11
  15. package/docs/reference/configuration.md +2 -2
  16. package/docs/reference/tools.md +1 -1
  17. package/docs/start/quickstart-claude-code.md +2 -2
  18. package/package.json +1 -1
  19. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  20. package/plugins/kxm/README.md +3 -3
  21. package/plugins/kxm/dist/claude-hook.js +417 -66
  22. package/plugins/kxm/dist/cli.js +1399 -900
  23. package/plugins/kxm/dist/extension.js +399 -64
  24. package/plugins/kxm/dist/mcp-server.js +622 -135
  25. package/plugins/kxm/dist/runtime-supervisor.js +668 -166
  26. package/plugins/kxm/dist/runtime.js +713 -211
  27. package/plugins/kxm/dist/server.js +466 -58
  28. package/plugins/kxm/package.json +1 -1
  29. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +15 -7
  30. package/plugins/kxm/src/cli/hub.ts +80 -21
  31. package/plugins/kxm/src/cli/types.ts +8 -1
  32. package/plugins/kxm/src/cli.ts +41 -7
  33. package/plugins/kxm/src/config.ts +246 -3
  34. package/plugins/kxm/src/extension.ts +8 -7
  35. package/plugins/kxm/src/hub-autostart.ts +20 -0
  36. package/plugins/kxm/src/hub-env.ts +83 -31
  37. package/plugins/kxm/src/hub-identity.ts +337 -0
  38. package/plugins/kxm/src/mcp-server.ts +17 -15
  39. package/plugins/kxm/src/project-name.ts +6 -47
  40. package/plugins/kxm/src/server.ts +22 -4
  41. package/scripts/kxm-hub.mjs +5 -0
@@ -168,10 +168,10 @@ var require_code = __commonJS({
168
168
  function interpolate(x) {
169
169
  return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
170
170
  }
171
- function stringify(x) {
171
+ function stringify2(x) {
172
172
  return new _Code(safeStringify(x));
173
173
  }
174
- exports.stringify = stringify;
174
+ exports.stringify = stringify2;
175
175
  function safeStringify(x) {
176
176
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
177
177
  }
@@ -2991,7 +2991,7 @@ var require_compile = __commonJS({
2991
2991
  const schOrFunc = root.refs[ref];
2992
2992
  if (schOrFunc)
2993
2993
  return schOrFunc;
2994
- let _sch = resolve5.call(this, root, ref);
2994
+ let _sch = resolve6.call(this, root, ref);
2995
2995
  if (_sch === void 0) {
2996
2996
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
2997
2997
  const { schemaId } = this.opts;
@@ -3018,7 +3018,7 @@ var require_compile = __commonJS({
3018
3018
  function sameSchemaEnv(s1, s2) {
3019
3019
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3020
3020
  }
3021
- function resolve5(root, ref) {
3021
+ function resolve6(root, ref) {
3022
3022
  let sch;
3023
3023
  while (typeof (sch = this.refs[ref]) == "string")
3024
3024
  ref = sch;
@@ -3839,11 +3839,11 @@ var require_fast_uri = __commonJS({
3839
3839
  normalizeString(uri, options);
3840
3840
  } else if (typeof uri === "object") {
3841
3841
  uri = /** @type {T} */
3842
- parse3(serialize(uri, options), options);
3842
+ parse4(serialize(uri, options), options);
3843
3843
  }
3844
3844
  return uri;
3845
3845
  }
3846
- function resolve5(baseURI, relativeURI, options) {
3846
+ function resolve6(baseURI, relativeURI, options) {
3847
3847
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3848
3848
  const {
3849
3849
  parsed: baseParsed,
@@ -3879,8 +3879,8 @@ var require_fast_uri = __commonJS({
3879
3879
  function resolveComponent(base, relative, options, skipNormalization) {
3880
3880
  const target = {};
3881
3881
  if (!skipNormalization) {
3882
- base = parse3(serialize(base, options), options);
3883
- relative = parse3(serialize(relative, options), options);
3882
+ base = parse4(serialize(base, options), options);
3883
+ relative = parse4(serialize(relative, options), options);
3884
3884
  }
3885
3885
  options = options || {};
3886
3886
  if (!options.tolerant && relative.scheme) {
@@ -4172,7 +4172,7 @@ var require_fast_uri = __commonJS({
4172
4172
  }
4173
4173
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
4174
4174
  }
4175
- function parse3(uri, opts) {
4175
+ function parse4(uri, opts) {
4176
4176
  return parseWithStatus(uri, opts).parsed;
4177
4177
  }
4178
4178
  function normalizeString(uri, opts) {
@@ -4205,11 +4205,11 @@ var require_fast_uri = __commonJS({
4205
4205
  var fastUri = {
4206
4206
  SCHEMES,
4207
4207
  normalize,
4208
- resolve: resolve5,
4208
+ resolve: resolve6,
4209
4209
  resolveComponent,
4210
4210
  equal,
4211
4211
  serialize,
4212
- parse: parse3
4212
+ parse: parse4
4213
4213
  };
4214
4214
  module.exports = fastUri;
4215
4215
  module.exports.default = fastUri;
@@ -8664,7 +8664,7 @@ var require_stringify = __commonJS({
8664
8664
  props.push(doc.directives.tagString(tag));
8665
8665
  return props.join(" ");
8666
8666
  }
8667
- function stringify(item, ctx, onComment, onChompKeep) {
8667
+ function stringify2(item, ctx, onComment, onChompKeep) {
8668
8668
  if (identity.isPair(item))
8669
8669
  return item.toString(ctx, onComment, onChompKeep);
8670
8670
  if (identity.isAlias(item)) {
@@ -8693,7 +8693,7 @@ var require_stringify = __commonJS({
8693
8693
  ${ctx.indent}${str}`;
8694
8694
  }
8695
8695
  exports.createStringifyContext = createStringifyContext;
8696
- exports.stringify = stringify;
8696
+ exports.stringify = stringify2;
8697
8697
  }
8698
8698
  });
8699
8699
 
@@ -8703,7 +8703,7 @@ var require_stringifyPair = __commonJS({
8703
8703
  "use strict";
8704
8704
  var identity = require_identity();
8705
8705
  var Scalar = require_Scalar();
8706
- var stringify = require_stringify();
8706
+ var stringify2 = require_stringify();
8707
8707
  var stringifyComment = require_stringifyComment();
8708
8708
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
8709
8709
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -8725,7 +8725,7 @@ var require_stringifyPair = __commonJS({
8725
8725
  });
8726
8726
  let keyCommentDone = false;
8727
8727
  let chompKeep = false;
8728
- let str = stringify.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8728
+ let str = stringify2.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8729
8729
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
8730
8730
  if (simpleKeys)
8731
8731
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -8777,7 +8777,7 @@ ${indent}:`;
8777
8777
  ctx.indent = ctx.indent.substring(2);
8778
8778
  }
8779
8779
  let valueCommentDone = false;
8780
- const valueStr = stringify.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
8780
+ const valueStr = stringify2.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
8781
8781
  let ws = " ";
8782
8782
  if (keyComment || vsb || vcb) {
8783
8783
  ws = vsb ? "\n" : "";
@@ -8918,7 +8918,7 @@ var require_addPairToJSMap = __commonJS({
8918
8918
  "use strict";
8919
8919
  var log = require_log();
8920
8920
  var merge2 = require_merge();
8921
- var stringify = require_stringify();
8921
+ var stringify2 = require_stringify();
8922
8922
  var identity = require_identity();
8923
8923
  var toJS = require_toJS();
8924
8924
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -8954,7 +8954,7 @@ var require_addPairToJSMap = __commonJS({
8954
8954
  if (typeof jsKey !== "object")
8955
8955
  return String(jsKey);
8956
8956
  if (identity.isNode(key) && ctx?.doc) {
8957
- const strCtx = stringify.createStringifyContext(ctx.doc, {});
8957
+ const strCtx = stringify2.createStringifyContext(ctx.doc, {});
8958
8958
  strCtx.anchors = /* @__PURE__ */ new Set();
8959
8959
  for (const node of ctx.anchors.keys())
8960
8960
  strCtx.anchors.add(node.anchor);
@@ -9021,12 +9021,12 @@ var require_stringifyCollection = __commonJS({
9021
9021
  "node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
9022
9022
  "use strict";
9023
9023
  var identity = require_identity();
9024
- var stringify = require_stringify();
9024
+ var stringify2 = require_stringify();
9025
9025
  var stringifyComment = require_stringifyComment();
9026
9026
  function stringifyCollection(collection, ctx, options) {
9027
9027
  const flow = ctx.inFlow ?? collection.flow;
9028
- const stringify2 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9029
- return stringify2(collection, ctx, options);
9028
+ const stringify3 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9029
+ return stringify3(collection, ctx, options);
9030
9030
  }
9031
9031
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
9032
9032
  const { indent, options: { commentString } } = ctx;
@@ -9051,7 +9051,7 @@ var require_stringifyCollection = __commonJS({
9051
9051
  }
9052
9052
  }
9053
9053
  chompKeep = false;
9054
- let str2 = stringify.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9054
+ let str2 = stringify2.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9055
9055
  if (comment2)
9056
9056
  str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
9057
9057
  if (chompKeep && comment2)
@@ -9118,7 +9118,7 @@ ${indent}${line}` : "\n";
9118
9118
  }
9119
9119
  if (comment)
9120
9120
  reqNewline = true;
9121
- let str = stringify.stringify(item, itemCtx, () => comment = null);
9121
+ let str = stringify2.stringify(item, itemCtx, () => comment = null);
9122
9122
  reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
9123
9123
  if (i < items.length - 1) {
9124
9124
  str += ",";
@@ -10479,7 +10479,7 @@ var require_stringifyDocument = __commonJS({
10479
10479
  "node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
10480
10480
  "use strict";
10481
10481
  var identity = require_identity();
10482
- var stringify = require_stringify();
10482
+ var stringify2 = require_stringify();
10483
10483
  var stringifyComment = require_stringifyComment();
10484
10484
  function stringifyDocument(doc, options) {
10485
10485
  const lines = [];
@@ -10494,7 +10494,7 @@ var require_stringifyDocument = __commonJS({
10494
10494
  }
10495
10495
  if (hasDirectives)
10496
10496
  lines.push("---");
10497
- const ctx = stringify.createStringifyContext(doc, options);
10497
+ const ctx = stringify2.createStringifyContext(doc, options);
10498
10498
  const { commentString } = ctx.options;
10499
10499
  if (doc.commentBefore) {
10500
10500
  if (lines.length !== 1)
@@ -10516,7 +10516,7 @@ var require_stringifyDocument = __commonJS({
10516
10516
  contentComment = doc.contents.comment;
10517
10517
  }
10518
10518
  const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
10519
- let body = stringify.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10519
+ let body = stringify2.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10520
10520
  if (contentComment)
10521
10521
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
10522
10522
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -10524,7 +10524,7 @@ var require_stringifyDocument = __commonJS({
10524
10524
  } else
10525
10525
  lines.push(body);
10526
10526
  } else {
10527
- lines.push(stringify.stringify(doc.contents, ctx));
10527
+ lines.push(stringify2.stringify(doc.contents, ctx));
10528
10528
  }
10529
10529
  if (doc.directives?.docEnd) {
10530
10530
  if (doc.comment) {
@@ -12660,7 +12660,7 @@ var require_cst_scalar = __commonJS({
12660
12660
  var require_cst_stringify = __commonJS({
12661
12661
  "node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
12662
12662
  "use strict";
12663
- var stringify = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12663
+ var stringify2 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12664
12664
  function stringifyToken(token) {
12665
12665
  switch (token.type) {
12666
12666
  case "block-scalar": {
@@ -12713,7 +12713,7 @@ var require_cst_stringify = __commonJS({
12713
12713
  res += stringifyToken(value);
12714
12714
  return res;
12715
12715
  }
12716
- exports.stringify = stringify;
12716
+ exports.stringify = stringify2;
12717
12717
  }
12718
12718
  });
12719
12719
 
@@ -14424,7 +14424,7 @@ var require_public_api = __commonJS({
14424
14424
  }
14425
14425
  return doc;
14426
14426
  }
14427
- function parse3(src, reviver, options) {
14427
+ function parse4(src, reviver, options) {
14428
14428
  let _reviver = void 0;
14429
14429
  if (typeof reviver === "function") {
14430
14430
  _reviver = reviver;
@@ -14443,7 +14443,7 @@ var require_public_api = __commonJS({
14443
14443
  }
14444
14444
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
14445
14445
  }
14446
- function stringify(value, replacer, options) {
14446
+ function stringify2(value, replacer, options) {
14447
14447
  let _replacer = null;
14448
14448
  if (typeof replacer === "function" || Array.isArray(replacer)) {
14449
14449
  _replacer = replacer;
@@ -14465,10 +14465,10 @@ var require_public_api = __commonJS({
14465
14465
  return value.toString(options);
14466
14466
  return new Document.Document(value, _replacer, options).toString(options);
14467
14467
  }
14468
- exports.parse = parse3;
14468
+ exports.parse = parse4;
14469
14469
  exports.parseAllDocuments = parseAllDocuments;
14470
14470
  exports.parseDocument = parseDocument;
14471
- exports.stringify = stringify;
14471
+ exports.stringify = stringify2;
14472
14472
  }
14473
14473
  });
14474
14474
 
@@ -14526,7 +14526,7 @@ var require_dist2 = __commonJS({
14526
14526
 
14527
14527
  // plugins/kxm/src/mcp-server.ts
14528
14528
  import { statSync } from "node:fs";
14529
- import { join as join5 } from "node:path";
14529
+ import { join as join6 } from "node:path";
14530
14530
 
14531
14531
  // node_modules/zod/v4/core/core.js
14532
14532
  var _a;
@@ -21846,7 +21846,7 @@ var Protocol = class {
21846
21846
  return;
21847
21847
  }
21848
21848
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
21849
- await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
21849
+ await new Promise((resolve6) => setTimeout(resolve6, pollInterval));
21850
21850
  options?.signal?.throwIfAborted();
21851
21851
  }
21852
21852
  } catch (error2) {
@@ -21863,7 +21863,7 @@ var Protocol = class {
21863
21863
  */
21864
21864
  request(request, resultSchema, options) {
21865
21865
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
21866
- return new Promise((resolve5, reject) => {
21866
+ return new Promise((resolve6, reject) => {
21867
21867
  const earlyReject = (error2) => {
21868
21868
  reject(error2);
21869
21869
  };
@@ -21941,7 +21941,7 @@ var Protocol = class {
21941
21941
  if (!parseResult.success) {
21942
21942
  reject(parseResult.error);
21943
21943
  } else {
21944
- resolve5(parseResult.data);
21944
+ resolve6(parseResult.data);
21945
21945
  }
21946
21946
  } catch (error2) {
21947
21947
  reject(error2);
@@ -22202,12 +22202,12 @@ var Protocol = class {
22202
22202
  }
22203
22203
  } catch {
22204
22204
  }
22205
- return new Promise((resolve5, reject) => {
22205
+ return new Promise((resolve6, reject) => {
22206
22206
  if (signal.aborted) {
22207
22207
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
22208
22208
  return;
22209
22209
  }
22210
- const timeoutId = setTimeout(resolve5, interval);
22210
+ const timeoutId = setTimeout(resolve6, interval);
22211
22211
  signal.addEventListener("abort", () => {
22212
22212
  clearTimeout(timeoutId);
22213
22213
  reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
@@ -23083,12 +23083,12 @@ var StdioServerTransport = class {
23083
23083
  this.onclose?.();
23084
23084
  }
23085
23085
  send(message) {
23086
- return new Promise((resolve5) => {
23086
+ return new Promise((resolve6) => {
23087
23087
  const json = serializeMessage(message);
23088
23088
  if (this._stdout.write(json)) {
23089
- resolve5();
23089
+ resolve6();
23090
23090
  } else {
23091
- this._stdout.once("drain", resolve5);
23091
+ this._stdout.once("drain", resolve6);
23092
23092
  }
23093
23093
  });
23094
23094
  }
@@ -23504,7 +23504,7 @@ var HubClient = class {
23504
23504
  if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
23505
23505
  const message = await this.getMessage(messageId);
23506
23506
  if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
23507
- await new Promise((resolve5, reject) => {
23507
+ await new Promise((resolve6, reject) => {
23508
23508
  const onAbort = () => {
23509
23509
  signal?.removeEventListener("abort", onAbort);
23510
23510
  clearTimeout(timer);
@@ -23512,7 +23512,7 @@ var HubClient = class {
23512
23512
  };
23513
23513
  const timer = setTimeout(() => {
23514
23514
  signal?.removeEventListener("abort", onAbort);
23515
- resolve5();
23515
+ resolve6();
23516
23516
  }, Math.min(500, Math.max(1, deadline - Date.now())));
23517
23517
  signal?.addEventListener("abort", onAbort, { once: true });
23518
23518
  if (signal?.aborted) onAbort();
@@ -23567,7 +23567,7 @@ var HubClient = class {
23567
23567
  } catch (error2) {
23568
23568
  if (this.stopped || error2 instanceof Error && error2.name === "AbortError") return;
23569
23569
  }
23570
- if (!this.stopped) await new Promise((resolve5) => setTimeout(resolve5, this.options.reconnectMs));
23570
+ if (!this.stopped) await new Promise((resolve6) => setTimeout(resolve6, this.options.reconnectMs));
23571
23571
  }
23572
23572
  }
23573
23573
  headers(includeIdentity = true) {
@@ -23654,15 +23654,330 @@ async function hubJsonRequest(options, path, init, headers) {
23654
23654
  }
23655
23655
 
23656
23656
  // plugins/kxm/src/hub-env.ts
23657
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
23658
- import { homedir as homedir2 } from "node:os";
23659
- import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, resolve as resolve3 } from "node:path";
23657
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
23658
+ import { homedir as homedir3 } from "node:os";
23659
+ import { dirname as dirname4, isAbsolute as isAbsolute2, join as join4, resolve as resolve4 } from "node:path";
23660
+
23661
+ // plugins/kxm/src/config.ts
23662
+ var import_yaml = __toESM(require_dist2(), 1);
23663
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
23664
+ import { tmpdir, homedir } from "node:os";
23665
+ import { dirname, join, resolve } from "node:path";
23666
+ var KXM_CONFIG_SCHEMA = "kxm.config.v1";
23667
+ var KxmHubConfigError = class extends Error {
23668
+ code = "hub_config_invalid";
23669
+ constructor(message) {
23670
+ super(message);
23671
+ this.name = "KxmHubConfigError";
23672
+ }
23673
+ };
23674
+ var HUB_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
23675
+ var HUB_OP_REF = /^op:\/\/\S+\/\S+\/\S+$/;
23676
+ var LITERAL_SECRET_KEY = /^(token|secret|password|authToken|auth_token|apiKey|api_key|api_token|bearer|credential|access_token)$/i;
23677
+ function isHubKeyEnvName(value) {
23678
+ return HUB_ENV_NAME.test(value);
23679
+ }
23680
+ function isHubOpReference(value) {
23681
+ return HUB_OP_REF.test(value);
23682
+ }
23683
+ var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
23684
+ var DEFAULT_KXM_CONFIG = {
23685
+ schema: KXM_CONFIG_SCHEMA,
23686
+ user: {
23687
+ theme: "dark",
23688
+ preferredCritics: ["reviewer-arch", "reviewer-cli"],
23689
+ tokenBudget: 16e3
23690
+ },
23691
+ defaults: {
23692
+ workflow: "software-engineering/feature-implementation",
23693
+ harness: "pi"
23694
+ },
23695
+ dash: {
23696
+ defaultScreen: "agents",
23697
+ refreshIntervalMs: 1e3,
23698
+ autoOpen: false
23699
+ },
23700
+ sync: {
23701
+ defaultTracker: "none"
23702
+ },
23703
+ hub: {
23704
+ autoStart: "background"
23705
+ },
23706
+ improvement: {
23707
+ promotionPolicy: "manual_pr",
23708
+ telemetryHalfLifeDays: 14,
23709
+ autoThreshold: {
23710
+ minRuns: 10,
23711
+ minPassRate: 0.95,
23712
+ minCostSavings: 0.5
23713
+ }
23714
+ },
23715
+ routing: {
23716
+ shadowExecution: {
23717
+ enabled: false,
23718
+ sampleRate: 0.05,
23719
+ candidateModels: []
23720
+ },
23721
+ circuitBreaker: {
23722
+ mode: "soft_demotion",
23723
+ failureThreshold: 3,
23724
+ windowSeconds: 3600,
23725
+ cooldownSeconds: 1800,
23726
+ penaltyMultiplier: 5
23727
+ }
23728
+ },
23729
+ telemetry: {
23730
+ federated: true,
23731
+ anonymize: true
23732
+ }
23733
+ };
23734
+ function userConfigDirectory(overrideDir) {
23735
+ if (overrideDir) return resolve(overrideDir);
23736
+ return resolve(process.env.KXM_USER_CONFIG_DIR?.trim() || join(homedir(), ".config", "kxm"));
23737
+ }
23738
+ function repoConfigDirectory(repoRoot) {
23739
+ return resolve(repoRoot, ".kxm");
23740
+ }
23741
+ function deepMerge(target, source) {
23742
+ const result = { ...target };
23743
+ for (const [key, val] of Object.entries(source)) {
23744
+ if (val && typeof val === "object" && !Array.isArray(val)) {
23745
+ const existing = result[key] && typeof result[key] === "object" && !Array.isArray(result[key]) ? result[key] : {};
23746
+ result[key] = deepMerge(existing, val);
23747
+ } else if (val !== void 0) {
23748
+ result[key] = val;
23749
+ }
23750
+ }
23751
+ return result;
23752
+ }
23753
+ function literalSecretMessage(path) {
23754
+ return `refusing literal hub token at ${path}: store an op:// reference (key.op) or an environment variable name (key.env), never the token`;
23755
+ }
23756
+ function rejectLiteralHubSecrets(value, path) {
23757
+ if (Array.isArray(value)) {
23758
+ value.forEach((item, index) => rejectLiteralHubSecrets(item, `${path}[${index}]`));
23759
+ return;
23760
+ }
23761
+ if (!value || typeof value !== "object") return;
23762
+ for (const [key, child] of Object.entries(value)) {
23763
+ const childPath = `${path}.${key}`;
23764
+ if (typeof child === "string" && (key === "key" || LITERAL_SECRET_KEY.test(key))) {
23765
+ throw new KxmHubConfigError(literalSecretMessage(childPath));
23766
+ }
23767
+ if (child && typeof child === "object") rejectLiteralHubSecrets(child, childPath);
23768
+ }
23769
+ }
23770
+ function parseKeyRef(raw, path) {
23771
+ if (raw === void 0) return void 0;
23772
+ if (typeof raw === "string") throw new KxmHubConfigError(literalSecretMessage(path));
23773
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23774
+ throw new KxmHubConfigError(`${path} must be a key reference with op and/or env, never a token`);
23775
+ }
23776
+ const row = raw;
23777
+ for (const key of Object.keys(row)) {
23778
+ if (key !== "op" && key !== "env") {
23779
+ throw new KxmHubConfigError(literalSecretMessage(`${path}.${key}`));
23780
+ }
23781
+ }
23782
+ const ref = {};
23783
+ if (row.op !== void 0) {
23784
+ if (typeof row.op !== "string" || !isHubOpReference(row.op)) {
23785
+ throw new KxmHubConfigError(`${path}.op must be an op://vault/item/field reference, not a token`);
23786
+ }
23787
+ ref.op = row.op;
23788
+ }
23789
+ if (row.env !== void 0) {
23790
+ if (typeof row.env !== "string" || !isHubKeyEnvName(row.env)) {
23791
+ throw new KxmHubConfigError(`${path}.env must be an environment variable name, not a token`);
23792
+ }
23793
+ ref.env = row.env;
23794
+ }
23795
+ if (!ref.op && !ref.env) throw new KxmHubConfigError(`${path} needs key.op, key.env, or both`);
23796
+ return ref;
23797
+ }
23798
+ function parseEndpoint(raw, path) {
23799
+ if (raw === void 0) return void 0;
23800
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23801
+ throw new KxmHubConfigError(`${path} must be an object with url, project, and key`);
23802
+ }
23803
+ const row = raw;
23804
+ const endpoint = {};
23805
+ if (row.url !== void 0) {
23806
+ if (typeof row.url !== "string" || !isHubUrl(row.url)) {
23807
+ throw new KxmHubConfigError(`${path}.url must be an http or https URL without credentials, query, or fragment`);
23808
+ }
23809
+ endpoint.url = row.url.replace(/\/$/, "");
23810
+ }
23811
+ if (row.project !== void 0) {
23812
+ if (typeof row.project !== "string" || !isHubProjectId(row.project)) {
23813
+ throw new KxmHubConfigError(`${path}.project must be a single-line project id, not a token`);
23814
+ }
23815
+ endpoint.project = row.project.trim();
23816
+ }
23817
+ const key = parseKeyRef(row.key, `${path}.key`);
23818
+ if (key) endpoint.key = key;
23819
+ for (const name of Object.keys(row)) {
23820
+ if (name !== "url" && name !== "project" && name !== "key") {
23821
+ if (typeof row[name] === "string") throw new KxmHubConfigError(literalSecretMessage(`${path}.${name}`));
23822
+ throw new KxmHubConfigError(`${path}.${name} is not a hub endpoint field`);
23823
+ }
23824
+ }
23825
+ return endpoint;
23826
+ }
23827
+ function isHubUrl(raw) {
23828
+ try {
23829
+ const parsed = new URL(raw);
23830
+ return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && !raw.includes("?") && !raw.includes("#");
23831
+ } catch {
23832
+ return false;
23833
+ }
23834
+ }
23835
+ function isHubProjectId(raw) {
23836
+ const value = raw.trim();
23837
+ return value.length > 0 && value.length <= 200 && !/[\s\r\n]/.test(value);
23838
+ }
23839
+ function parseProjectMap(raw) {
23840
+ if (raw === void 0) return void 0;
23841
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23842
+ throw new KxmHubConfigError("hub.projects must be a map of project id to key reference, never a token");
23843
+ }
23844
+ const projects = {};
23845
+ for (const [project, value] of Object.entries(raw)) {
23846
+ if (!isHubProjectId(project)) {
23847
+ throw new KxmHubConfigError(`hub.projects key ${JSON.stringify(project)} must be a project id, not a token`);
23848
+ }
23849
+ const ref = parseKeyRef(value, `hub.projects.${project}`);
23850
+ if (!ref) throw new KxmHubConfigError(`hub.projects.${project} needs an op:// reference or an environment variable name`);
23851
+ projects[project.trim()] = ref;
23852
+ }
23853
+ return projects;
23854
+ }
23855
+ function normalizeHubConfig(raw) {
23856
+ const autoStart = raw?.autoStart;
23857
+ const hub = {
23858
+ autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart
23859
+ };
23860
+ if (raw === void 0) return hub;
23861
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23862
+ throw new KxmHubConfigError("hub must be an object");
23863
+ }
23864
+ rejectLiteralHubSecrets(raw, "hub");
23865
+ const row = raw;
23866
+ if (row.mode !== void 0) {
23867
+ if (row.mode !== "local" && row.mode !== "cloud") {
23868
+ throw new KxmHubConfigError("hub.mode must be local or cloud");
23869
+ }
23870
+ hub.mode = row.mode;
23871
+ }
23872
+ const local = parseEndpoint(row.local, "hub.local");
23873
+ const cloud = parseEndpoint(row.cloud, "hub.cloud");
23874
+ const projects = parseProjectMap(row.projects);
23875
+ if (local) hub.local = local;
23876
+ if (cloud) hub.cloud = cloud;
23877
+ if (projects && Object.keys(projects).length > 0) hub.projects = projects;
23878
+ return hub;
23879
+ }
23880
+ function findConfigRoot(start) {
23881
+ let dir = resolve(start);
23882
+ for (; ; ) {
23883
+ if (existsSync(join(dir, ".kxm", "project.yaml")) || existsSync(join(dir, ".kxm", "config.yaml"))) return dir;
23884
+ const parent = dirname(dir);
23885
+ if (parent === dir) return resolve(start);
23886
+ dir = parent;
23887
+ }
23888
+ }
23889
+ function userConfigDirForEnv(env) {
23890
+ const explicit = env.KXM_USER_CONFIG_DIR?.trim();
23891
+ if (explicit) return explicit;
23892
+ if (env === process.env) return void 0;
23893
+ return join(tmpdir(), "kxm-no-user-config");
23894
+ }
23895
+ function loadHubSettings(start, env = process.env) {
23896
+ const userConfigDir = userConfigDirForEnv(env);
23897
+ return loadKxmConfig(findConfigRoot(start), userConfigDir === void 0 ? {} : { userConfigDir }).hub;
23898
+ }
23899
+ function recordOf(raw) {
23900
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
23901
+ }
23902
+ function finiteNumber(value) {
23903
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
23904
+ }
23905
+ function normalizeImprovementConfig(raw) {
23906
+ const value = recordOf(raw);
23907
+ const threshold = recordOf(value.autoThreshold);
23908
+ const policy = value.promotionPolicy;
23909
+ const halfLife = finiteNumber(value.telemetryHalfLifeDays);
23910
+ const minRuns = finiteNumber(threshold.minRuns);
23911
+ const minPassRate = finiteNumber(threshold.minPassRate);
23912
+ const minCostSavings = finiteNumber(threshold.minCostSavings);
23913
+ return {
23914
+ promotionPolicy: IMPROVEMENT_PROMOTION_POLICIES.includes(policy) ? policy : "manual_pr",
23915
+ telemetryHalfLifeDays: halfLife !== void 0 && halfLife > 0 && halfLife <= 3650 ? halfLife : 14,
23916
+ autoThreshold: {
23917
+ minRuns: minRuns !== void 0 && Number.isInteger(minRuns) && minRuns >= 1 && minRuns <= 1e6 ? minRuns : 10,
23918
+ minPassRate: minPassRate !== void 0 && minPassRate >= 0 && minPassRate <= 1 ? minPassRate : 0.95,
23919
+ minCostSavings: minCostSavings !== void 0 && minCostSavings >= 0 ? minCostSavings : 0.5
23920
+ }
23921
+ };
23922
+ }
23923
+ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23924
+ const userDir = userConfigDirectory(options.userConfigDir);
23925
+ const userConfigFile = join(userDir, "config.yaml");
23926
+ const repoDir = repoConfigDirectory(repoRoot);
23927
+ const repoConfigFile = join(repoDir, "config.yaml");
23928
+ let userRaw = {};
23929
+ let userLoadedPath;
23930
+ if (existsSync(userConfigFile)) {
23931
+ try {
23932
+ const text = readFileSync(userConfigFile, "utf8");
23933
+ userRaw = (0, import_yaml.parse)(text) ?? {};
23934
+ userLoadedPath = userConfigFile;
23935
+ } catch (error2) {
23936
+ throw new Error(`invalid user config YAML at ${userConfigFile}`, { cause: error2 });
23937
+ }
23938
+ }
23939
+ let repoRaw = {};
23940
+ let repoLoadedPath;
23941
+ if (existsSync(repoConfigFile)) {
23942
+ try {
23943
+ const text = readFileSync(repoConfigFile, "utf8");
23944
+ repoRaw = (0, import_yaml.parse)(text) ?? {};
23945
+ repoLoadedPath = repoConfigFile;
23946
+ } catch (error2) {
23947
+ throw new Error(`invalid project config YAML at ${repoConfigFile}`, { cause: error2 });
23948
+ }
23949
+ }
23950
+ const baseCopy = JSON.parse(JSON.stringify(DEFAULT_KXM_CONFIG));
23951
+ const mergedUser = deepMerge(baseCopy, userRaw);
23952
+ const mergedAll = deepMerge(mergedUser, repoRaw);
23953
+ return {
23954
+ schema: KXM_CONFIG_SCHEMA,
23955
+ user: mergedAll.user ?? {},
23956
+ defaults: mergedAll.defaults ?? {},
23957
+ dash: mergedAll.dash ?? {},
23958
+ sync: mergedAll.sync ?? {},
23959
+ hub: normalizeHubConfig(mergedAll.hub),
23960
+ improvement: normalizeImprovementConfig(mergedAll.improvement),
23961
+ routing: mergedAll.routing ?? DEFAULT_KXM_CONFIG.routing,
23962
+ telemetry: mergedAll.telemetry ?? DEFAULT_KXM_CONFIG.telemetry,
23963
+ loadedFrom: {
23964
+ userConfigPath: userLoadedPath,
23965
+ repoConfigPath: repoLoadedPath
23966
+ }
23967
+ };
23968
+ }
23969
+
23970
+ // plugins/kxm/src/hub-identity.ts
23971
+ var import_yaml2 = __toESM(require_dist2(), 1);
23972
+ import { spawnSync as spawnSync2 } from "node:child_process";
23973
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
23974
+ import { basename, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
23660
23975
 
23661
23976
  // plugins/kxm/src/hub-binding.ts
23662
23977
  import { spawnSync } from "node:child_process";
23663
- import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
23664
- import { homedir } from "node:os";
23665
- import { dirname, isAbsolute, join, resolve } from "node:path";
23978
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
23979
+ import { homedir as homedir2 } from "node:os";
23980
+ import { dirname as dirname2, isAbsolute, join as join2, resolve as resolve2 } from "node:path";
23666
23981
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
23667
23982
  var CloudTokenError = class extends Error {
23668
23983
  code;
@@ -23685,20 +24000,20 @@ function resolveUserStateRoot(env) {
23685
24000
  const explicit = env.KXM_STATE_HOME?.trim();
23686
24001
  if (explicit) {
23687
24002
  if (!isAbsolute(explicit)) throw new HubBindingError("local_state_root_not_absolute");
23688
- return resolve(explicit);
24003
+ return resolve2(explicit);
23689
24004
  }
23690
24005
  if (process.platform === "win32") {
23691
24006
  const localAppData = env.LOCALAPPDATA?.trim();
23692
- const base2 = localAppData && isAbsolute(localAppData) ? localAppData : join(homedir(), "AppData", "Local");
23693
- return resolve(base2, "KXM");
24007
+ const base2 = localAppData && isAbsolute(localAppData) ? localAppData : join2(homedir2(), "AppData", "Local");
24008
+ return resolve2(base2, "KXM");
23694
24009
  }
23695
- if (process.platform === "darwin") return resolve(homedir(), "Library", "Application Support", "KXM");
24010
+ if (process.platform === "darwin") return resolve2(homedir2(), "Library", "Application Support", "KXM");
23696
24011
  const xdgState = env.XDG_STATE_HOME?.trim();
23697
- const base = xdgState && isAbsolute(xdgState) ? xdgState : join(homedir(), ".local", "state");
23698
- return resolve(base, "kxm");
24012
+ const base = xdgState && isAbsolute(xdgState) ? xdgState : join2(homedir2(), ".local", "state");
24013
+ return resolve2(base, "kxm");
23699
24014
  }
23700
24015
  function hubBindingFile(env = process.env) {
23701
- return join(resolveUserStateRoot(env), "hub-binding.json");
24016
+ return join2(resolveUserStateRoot(env), "hub-binding.json");
23702
24017
  }
23703
24018
  function validateHubUrl(raw) {
23704
24019
  let parsed;
@@ -23753,17 +24068,6 @@ function splitTokenCommand(raw) {
23753
24068
  function isCloudTokenEnvName(value) {
23754
24069
  return TOKEN_ENV_NAME.test(value);
23755
24070
  }
23756
- function resolveHubServerUrl(env = process.env) {
23757
- const fromEnv = env.KXM_SERVER_URL?.trim();
23758
- if (fromEnv) return fromEnv.replace(/\/$/, "");
23759
- try {
23760
- const binding = readHubBinding(env);
23761
- if (binding?.url) return binding.url;
23762
- } catch (error2) {
23763
- if (!(error2 instanceof HubBindingError)) throw error2;
23764
- }
23765
- return "http://127.0.0.1:7331";
23766
- }
23767
24071
  function resolveCloudHubToken(binding, env = process.env) {
23768
24072
  if (!binding.cloud) {
23769
24073
  throw new CloudTokenError("cloud_token_missing", "cloud hub token is missing; the local hub-env token was not used");
@@ -23805,16 +24109,27 @@ function runTokenCommand(command, env) {
23805
24109
  }
23806
24110
  return stdout.trim();
23807
24111
  }
24112
+ function hubBindingScope(url) {
24113
+ let host;
24114
+ try {
24115
+ host = new URL(url).hostname.toLowerCase();
24116
+ } catch {
24117
+ return "remote";
24118
+ }
24119
+ if (host === "localhost" || host === "::1" || host === "[::1]" || host.endsWith(".localhost")) return "loopback";
24120
+ const v4 = /^127\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/.exec(host);
24121
+ return v4 && [v4[1], v4[2], v4[3]].every((part) => Number(part) <= 255) ? "loopback" : "remote";
24122
+ }
23808
24123
  function isIsoTimestamp(value) {
23809
24124
  if (Number.isNaN(Date.parse(value))) return false;
23810
24125
  return value === new Date(value).toISOString();
23811
24126
  }
23812
24127
  function readHubBinding(env = process.env) {
23813
24128
  const file = hubBindingFile(env);
23814
- if (!existsSync(file)) return void 0;
24129
+ if (!existsSync2(file)) return void 0;
23815
24130
  let parsed;
23816
24131
  try {
23817
- parsed = JSON.parse(readFileSync(file, "utf8"));
24132
+ parsed = JSON.parse(readFileSync2(file, "utf8"));
23818
24133
  } catch {
23819
24134
  throw new HubBindingError(`malformed hub binding at ${file}`);
23820
24135
  }
@@ -23864,31 +24179,23 @@ function readHubBinding(env = process.env) {
23864
24179
  };
23865
24180
  }
23866
24181
 
23867
- // plugins/kxm/src/project-name.ts
23868
- var import_yaml = __toESM(require_dist2(), 1);
23869
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
23870
- import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
23871
- function defaultProjectName(cwd, env = process.env, explicit) {
23872
- const fromExplicit = explicit?.trim();
23873
- if (fromExplicit) return fromExplicit;
23874
- const fromEnv = env.KXM_PROJECT?.trim();
23875
- if (fromEnv) return fromEnv;
23876
- const fromProject = readProjectYamlId(cwd);
23877
- if (fromProject) return fromProject;
24182
+ // plugins/kxm/src/hub-identity.ts
24183
+ var OP_TIMEOUT_MS = 15e3;
24184
+ var OP_MAX_BUFFER = 4096;
24185
+ function normalizeUrl(raw) {
23878
24186
  try {
23879
- const pkg = JSON.parse(readFileSync2(join2(cwd, "package.json"), "utf8"));
23880
- if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
24187
+ return validateHubUrl(raw);
23881
24188
  } catch {
24189
+ return void 0;
23882
24190
  }
23883
- return basename(cwd);
23884
24191
  }
23885
- function readProjectYamlId(start) {
23886
- let dir = resolve2(start);
24192
+ function projectYamlId(start) {
24193
+ let dir = resolve3(start);
23887
24194
  for (; ; ) {
23888
- const file = join2(dir, ".kxm", "project.yaml");
23889
- if (existsSync2(file)) {
24195
+ const file = join3(dir, ".kxm", "project.yaml");
24196
+ if (existsSync3(file)) {
23890
24197
  try {
23891
- const doc = (0, import_yaml.parse)(readFileSync2(file, "utf8"));
24198
+ const doc = (0, import_yaml2.parse)(readFileSync3(file, "utf8"));
23892
24199
  if (doc && typeof doc === "object" && !Array.isArray(doc) && typeof doc.id === "string" && doc.id.trim()) {
23893
24200
  return doc.id.trim();
23894
24201
  }
@@ -23896,11 +24203,173 @@ function readProjectYamlId(start) {
23896
24203
  }
23897
24204
  return void 0;
23898
24205
  }
23899
- const parent = dirname2(dir);
24206
+ const parent = dirname3(dir);
23900
24207
  if (parent === dir) return void 0;
23901
24208
  dir = parent;
23902
24209
  }
23903
24210
  }
24211
+ function packageName(cwd) {
24212
+ try {
24213
+ const pkg = JSON.parse(readFileSync3(join3(cwd, "package.json"), "utf8"));
24214
+ if (typeof pkg.name === "string" && pkg.name.trim().length > 0) return pkg.name.trim();
24215
+ } catch {
24216
+ }
24217
+ return void 0;
24218
+ }
24219
+ function activeBinding(env) {
24220
+ try {
24221
+ return readHubBinding(env);
24222
+ } catch (error2) {
24223
+ if (error2 instanceof HubBindingError) return void 0;
24224
+ throw error2;
24225
+ }
24226
+ }
24227
+ function endpointFor(mode, settings) {
24228
+ return mode === "cloud" ? settings.cloud : settings.local;
24229
+ }
24230
+ function resolveProjectIdentity(cwd, env = process.env, explicit) {
24231
+ const fromExplicit = explicit?.trim();
24232
+ if (fromExplicit) return { project: fromExplicit, source: "flag", sourceLabel: "--project" };
24233
+ const fromEnv = env.KXM_PROJECT?.trim();
24234
+ if (fromEnv) return { project: fromEnv, source: "env", sourceLabel: "KXM_PROJECT" };
24235
+ const settings = loadHubSettings(cwd, env);
24236
+ const mode = settings.mode ?? (activeBinding(env)?.cloud ? "cloud" : "local");
24237
+ const fromConfig = endpointFor(mode, settings)?.project?.trim();
24238
+ if (fromConfig) {
24239
+ return { project: fromConfig, source: "config", sourceLabel: `.kxm/config.yaml hub.${mode}.project` };
24240
+ }
24241
+ const fromProject = projectYamlId(cwd);
24242
+ if (fromProject) return { project: fromProject, source: "project.yaml", sourceLabel: ".kxm/project.yaml id" };
24243
+ const fromPackage = packageName(cwd);
24244
+ if (fromPackage) return { project: fromPackage, source: "package.json", sourceLabel: "package.json name" };
24245
+ return { project: basename(cwd), source: "directory", sourceLabel: "directory name" };
24246
+ }
24247
+ function opReferenceFromTokenCommand(command) {
24248
+ try {
24249
+ const args = splitTokenCommand(command);
24250
+ if (args[0] === "op" && args[1] === "read" && args[2] && isHubOpReference(args[2])) return args[2];
24251
+ } catch {
24252
+ return void 0;
24253
+ }
24254
+ return void 0;
24255
+ }
24256
+ function defaultOpRead(reference, env) {
24257
+ if (!isHubOpReference(reference)) {
24258
+ throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
24259
+ }
24260
+ const result = spawnSync2("op", ["read", reference], {
24261
+ encoding: "utf8",
24262
+ shell: false,
24263
+ timeout: OP_TIMEOUT_MS,
24264
+ maxBuffer: OP_MAX_BUFFER,
24265
+ env,
24266
+ windowsHide: true
24267
+ });
24268
+ if (result.error || result.status !== 0) {
24269
+ throw new CloudTokenError(
24270
+ "cloud_token_command_failed",
24271
+ `op read failed for ${reference}; the token was not saved`
24272
+ );
24273
+ }
24274
+ const line = (result.stdout ?? "").trim();
24275
+ if (!line || line.split(/\r?\n/).length !== 1) {
24276
+ throw new CloudTokenError(
24277
+ "cloud_token_command_failed",
24278
+ `op read did not print a single token for ${reference}; the token was not saved`
24279
+ );
24280
+ }
24281
+ return line;
24282
+ }
24283
+ function resolveKeyReference(ref, env, opRead = defaultOpRead) {
24284
+ if (!ref) return void 0;
24285
+ if (ref.env) {
24286
+ const value = env[ref.env]?.trim();
24287
+ if (value) return { value, source: `env:${ref.env}` };
24288
+ }
24289
+ if (ref.op) {
24290
+ const value = opRead(ref.op, env).trim();
24291
+ if (!value || value.split(/\r?\n/).length !== 1) {
24292
+ throw new CloudTokenError("cloud_token_command_failed", `op read did not print a single token for ${ref.op}`);
24293
+ }
24294
+ return { value, source: `op:${ref.op}` };
24295
+ }
24296
+ return void 0;
24297
+ }
24298
+ function keySourceFor(input) {
24299
+ const ref = input.endpoint?.key;
24300
+ if (input.mode === "local" && input.env.KXM_AUTH_TOKEN?.trim()) return "env:KXM_AUTH_TOKEN";
24301
+ if (ref?.env && input.env[ref.env]?.trim()) return `env:${ref.env}`;
24302
+ if (ref?.op) return `op:${ref.op}`;
24303
+ if (ref?.env) return `env:${ref.env}`;
24304
+ if (input.mode === "cloud") {
24305
+ if (input.binding?.tokenEnv) return `env:${input.binding.tokenEnv}`;
24306
+ if (input.binding?.tokenCommand) {
24307
+ const op = opReferenceFromTokenCommand(input.binding.tokenCommand);
24308
+ return op ? `op:${op}` : "command";
24309
+ }
24310
+ return "missing";
24311
+ }
24312
+ if (input.hasSavedProjectToken?.(input.project)) return "hub-env";
24313
+ return "missing";
24314
+ }
24315
+ function describeHubConnection(cwd, env = process.env, hooks = {}) {
24316
+ const settings = loadHubSettings(cwd, env);
24317
+ const binding = activeBinding(env);
24318
+ const mode = settings.mode ?? (binding?.cloud ? "cloud" : "local");
24319
+ const modeSource = settings.mode ? "config" : binding?.cloud ? "binding" : "default";
24320
+ const endpoint = endpointFor(mode, settings);
24321
+ const fromEnv = env.KXM_SERVER_URL?.trim();
24322
+ const fromConfig = endpoint?.url?.trim();
24323
+ const fromBinding = binding?.url;
24324
+ let url = "http://127.0.0.1:7331";
24325
+ let urlSource = "default";
24326
+ if (fromEnv) {
24327
+ url = fromEnv.replace(/\/$/, "");
24328
+ urlSource = "env";
24329
+ } else if (fromConfig) {
24330
+ url = fromConfig.replace(/\/$/, "");
24331
+ urlSource = "config";
24332
+ } else if (fromBinding) {
24333
+ url = fromBinding;
24334
+ urlSource = "binding";
24335
+ }
24336
+ const identity = resolveProjectIdentity(cwd, env, hooks.explicitProject);
24337
+ const configuredCloudUrl = settings.cloud?.url ?? (binding?.cloud ? binding.url : void 0);
24338
+ let scope;
24339
+ if (mode === "cloud") {
24340
+ const override = fromEnv ? normalizeUrl(fromEnv) : void 0;
24341
+ const named = configuredCloudUrl ? normalizeUrl(configuredCloudUrl) : void 0;
24342
+ scope = override && named && override !== named ? hubBindingScope(url) : "remote";
24343
+ } else {
24344
+ scope = hubBindingScope(url);
24345
+ }
24346
+ return {
24347
+ ...identity,
24348
+ mode,
24349
+ modeSource,
24350
+ url,
24351
+ urlSource,
24352
+ scope,
24353
+ ...endpoint ? { endpoint } : {},
24354
+ ...binding ? { binding } : {},
24355
+ keySource: keySourceFor({
24356
+ mode,
24357
+ env,
24358
+ ...endpoint ? { endpoint } : {},
24359
+ ...binding ? { binding } : {},
24360
+ project: identity.project,
24361
+ ...hooks.hasSavedProjectToken ? { hasSavedProjectToken: hooks.hasSavedProjectToken } : {}
24362
+ })
24363
+ };
24364
+ }
24365
+ function projectTokenMissingMessage(identity) {
24366
+ return [
24367
+ `kxm has no project token for project ${identity.project} (from ${identity.sourceLabel}).`,
24368
+ `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.`,
24369
+ "KXM_PROJECT or --project overrides the id for one command.",
24370
+ "An agent never uses the hub admin token."
24371
+ ].join(" ");
24372
+ }
23904
24373
 
23905
24374
  // plugins/kxm/src/hub-env.ts
23906
24375
  var HUB_ENV_SCHEMA = "kxm.hub-env.v1";
@@ -23914,27 +24383,27 @@ function resolveUserStateRoot2(env) {
23914
24383
  const explicit = env.KXM_STATE_HOME?.trim();
23915
24384
  if (explicit) {
23916
24385
  if (!isAbsolute2(explicit)) throw new HubEnvError("local_state_root_not_absolute");
23917
- return resolve3(explicit);
24386
+ return resolve4(explicit);
23918
24387
  }
23919
24388
  if (process.platform === "win32") {
23920
24389
  const localAppData = env.LOCALAPPDATA?.trim();
23921
- const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join3(homedir2(), "AppData", "Local");
23922
- return resolve3(base2, "KXM");
24390
+ const base2 = localAppData && isAbsolute2(localAppData) ? localAppData : join4(homedir3(), "AppData", "Local");
24391
+ return resolve4(base2, "KXM");
23923
24392
  }
23924
- if (process.platform === "darwin") return resolve3(homedir2(), "Library", "Application Support", "KXM");
24393
+ if (process.platform === "darwin") return resolve4(homedir3(), "Library", "Application Support", "KXM");
23925
24394
  const xdgState = env.XDG_STATE_HOME?.trim();
23926
- const base = xdgState && isAbsolute2(xdgState) ? xdgState : join3(homedir2(), ".local", "state");
23927
- return resolve3(base, "kxm");
24395
+ const base = xdgState && isAbsolute2(xdgState) ? xdgState : join4(homedir3(), ".local", "state");
24396
+ return resolve4(base, "kxm");
23928
24397
  }
23929
24398
  function hubEnvFile(env = process.env) {
23930
- return join3(resolveUserStateRoot2(env), "hub-env.json");
24399
+ return join4(resolveUserStateRoot2(env), "hub-env.json");
23931
24400
  }
23932
24401
  function readHubEnvRecord(env = process.env) {
23933
24402
  const file = hubEnvFile(env);
23934
- if (!existsSync3(file)) return void 0;
24403
+ if (!existsSync4(file)) return void 0;
23935
24404
  let parsed;
23936
24405
  try {
23937
- parsed = JSON.parse(readFileSync3(file, "utf8"));
24406
+ parsed = JSON.parse(readFileSync4(file, "utf8"));
23938
24407
  } catch (error2) {
23939
24408
  throw new HubEnvError(`hub env file is malformed at ${file}: ${error2 instanceof Error ? error2.message : String(error2)}`);
23940
24409
  }
@@ -23966,33 +24435,48 @@ function readHubEnvRecord(env = process.env) {
23966
24435
  ...record2.projectTokens !== void 0 ? { projectTokens: record2.projectTokens } : {}
23967
24436
  };
23968
24437
  }
23969
- function activeCloudBinding(env) {
23970
- try {
23971
- const binding = readHubBinding(env);
23972
- return binding?.cloud ? binding : void 0;
23973
- } catch (error2) {
23974
- if (error2 instanceof HubBindingError) {
23975
- throw new HubEnvError(
23976
- `${error2.message}; refusing to guess a credential \u2014 repair or remove ${hubBindingFile(env)}`
23977
- );
23978
- }
23979
- throw error2;
23980
- }
24438
+ function cloudModeToken(cwd, env, explicitProject, opRead) {
24439
+ const connection = describeHubConnection(cwd, env, explicitProject === void 0 ? {} : { explicitProject });
24440
+ if (connection.mode !== "cloud") return void 0;
24441
+ const fromConfig = resolveKeyReference(connection.endpoint?.key, env, opRead);
24442
+ if (fromConfig) return fromConfig.value;
24443
+ if (connection.binding?.cloud) return resolveCloudHubToken(connection.binding, env);
24444
+ throw new CloudTokenError(
24445
+ "cloud_token_missing",
24446
+ "cloud hub token is missing; the local hub-env token was not used"
24447
+ );
23981
24448
  }
23982
- function resolveAgentHubAuthToken(env, project) {
23983
- const cloud = activeCloudBinding(env);
23984
- if (cloud) return resolveCloudHubToken(cloud, env);
24449
+ function resolveAgentHubAuthToken(env, project, cwd = process.cwd(), opRead) {
24450
+ const cloud = cloudModeToken(cwd, env, void 0, opRead);
24451
+ if (cloud) return cloud;
23985
24452
  const envToken = env.KXM_AUTH_TOKEN?.trim();
23986
24453
  if (envToken) return envToken;
24454
+ const fromConfig = resolveKeyReference(describeHubConnection(cwd, env).endpoint?.key, env, opRead);
24455
+ if (fromConfig) return fromConfig.value;
23987
24456
  const tokens = readHubEnvRecord(env)?.projectTokens;
23988
24457
  if (!tokens || !Object.hasOwn(tokens, project)) return void 0;
23989
24458
  return tokens[project]?.trim() || void 0;
23990
24459
  }
24460
+ var AgentProjectTokenMissingError = class extends Error {
24461
+ code = "project_token_missing";
24462
+ project;
24463
+ projectSource;
24464
+ hint;
24465
+ constructor(projectOrIdentity) {
24466
+ const identity = typeof projectOrIdentity === "string" ? { project: projectOrIdentity, source: "env", sourceLabel: "the project id sent to the hub" } : projectOrIdentity;
24467
+ const message = projectTokenMissingMessage(identity);
24468
+ super(message);
24469
+ this.name = "AgentProjectTokenMissingError";
24470
+ this.project = identity.project;
24471
+ this.projectSource = typeof projectOrIdentity === "string" ? "unknown" : identity.source;
24472
+ this.hint = message;
24473
+ }
24474
+ };
23991
24475
 
23992
24476
  // plugins/kxm/src/commands.ts
23993
- import { chmodSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
23994
- import { homedir as homedir3 } from "node:os";
23995
- import { dirname as dirname4, join as join4, resolve as resolve4 } from "node:path";
24477
+ import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
24478
+ import { homedir as homedir4 } from "node:os";
24479
+ import { dirname as dirname5, join as join5, resolve as resolve5 } from "node:path";
23996
24480
  function forwardedHops(handling) {
23997
24481
  if (!handling?.length) return void 0;
23998
24482
  return {
@@ -24755,11 +25239,11 @@ function parseSessionToken(token) {
24755
25239
  return void 0;
24756
25240
  }
24757
25241
  function resolveUserConfigDirectory(overrideDir) {
24758
- if (overrideDir) return resolve4(overrideDir);
24759
- return resolve4(process.env.KXM_USER_CONFIG_DIR?.trim() || join4(homedir3(), ".config", "kxm"));
25242
+ if (overrideDir) return resolve5(overrideDir);
25243
+ return resolve5(process.env.KXM_USER_CONFIG_DIR?.trim() || join5(homedir4(), ".config", "kxm"));
24760
25244
  }
24761
25245
  function sessionTokenPath(userConfigDir) {
24762
- return join4(resolveUserConfigDirectory(userConfigDir), "session.token");
25246
+ return join5(resolveUserConfigDirectory(userConfigDir), "session.token");
24763
25247
  }
24764
25248
  function matchToolPattern(pattern, toolName) {
24765
25249
  if (pattern === "*" || pattern === toolName) return true;
@@ -24852,10 +25336,10 @@ function enforceToolPolicy(commandName, env = process.env, options) {
24852
25336
  return { allowed: true };
24853
25337
  }
24854
25338
  const tokenFile = sessionTokenPath(env.KXM_USER_CONFIG_DIR);
24855
- if (existsSync4(tokenFile)) {
25339
+ if (existsSync5(tokenFile)) {
24856
25340
  let tokenRaw;
24857
25341
  try {
24858
- tokenRaw = readFileSync4(tokenFile, "utf8").trim();
25342
+ tokenRaw = readFileSync5(tokenFile, "utf8").trim();
24859
25343
  } catch {
24860
25344
  return { allowed: false, error: "session_token_invalid", detail: "Session token file on disk could not be read" };
24861
25345
  }
@@ -24897,7 +25381,7 @@ function sessionTokenFixHint(policy) {
24897
25381
  }
24898
25382
 
24899
25383
  // plugins/kxm/src/mcp-server.ts
24900
- var VERSION = "0.7.165";
25384
+ var VERSION = "0.7.167";
24901
25385
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
24902
25386
  var inbox = /* @__PURE__ */ new Map();
24903
25387
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -24927,10 +25411,13 @@ function asRecord2(value) {
24927
25411
  }
24928
25412
  function sessionIdentity() {
24929
25413
  const projectDir = process.env.KXM_PROJECT_DIR || process.env.CLAUDE_PROJECT_DIR || process.cwd();
25414
+ const connection = describeHubConnection(projectDir, process.env);
24930
25415
  return {
24931
25416
  projectDir,
24932
- project: defaultProjectName(projectDir, process.env),
24933
- serverUrl: resolveHubServerUrl(process.env)
25417
+ project: connection.project,
25418
+ projectSource: connection.source,
25419
+ sourceLabel: connection.sourceLabel,
25420
+ serverUrl: connection.url
24934
25421
  };
24935
25422
  }
24936
25423
  async function announce(message) {
@@ -25000,12 +25487,10 @@ async function ensureClient() {
25000
25487
  if (meshClient?.agent) return meshClient;
25001
25488
  if (starting) return starting;
25002
25489
  starting = (async () => {
25003
- const { project, serverUrl } = sessionIdentity();
25004
- const authToken = resolveAgentHubAuthToken(process.env, project);
25490
+ const { project, projectDir, serverUrl } = sessionIdentity();
25491
+ const authToken = resolveAgentHubAuthToken(process.env, project, projectDir);
25005
25492
  if (!authToken) {
25006
- throw new Error(
25007
- `KXM has no project token for project ${project} on this machine. Ask the user to set the kxm plugin auth_token (${CONFIGURE_PLUGIN}) or to add ${project} to the hub KXM_PROJECT_TOKENS, listing every existing project too because that variable replaces the saved map.`
25008
- );
25493
+ throw new AgentProjectTokenMissingError(resolveProjectIdentity(projectDir, process.env));
25009
25494
  }
25010
25495
  const name = process.env.KXM_AGENT_NAME?.trim() || `claude-${process.pid}`;
25011
25496
  try {
@@ -25048,8 +25533,10 @@ async function connectedClient() {
25048
25533
  }
25049
25534
  }
25050
25535
  function toolErrorText(error2) {
25051
- if (error2 instanceof HubHttpError && error2.code === "invalid_auth") {
25052
- return `KXM hub rejected the project token for project ${sessionIdentity().project}. Ask the user to set the kxm plugin auth_token (${CONFIGURE_PLUGIN}) to that project's token from the hub KXM_PROJECT_TOKENS.`;
25536
+ if (error2 instanceof AgentProjectTokenMissingError) return error2.message;
25537
+ if (error2 instanceof HubHttpError && (error2.code === "invalid_auth" || error2.code === "project_token_missing")) {
25538
+ const identity = sessionIdentity();
25539
+ return `KXM hub rejected the project token for project ${identity.project} (from ${identity.sourceLabel}). Set hub.local.project or hub.cloud.project in .kxm/config.yaml to the key the hub already knows, and set hub.local.key or hub.cloud.key to an op:// reference or an environment variable name. Ask the user before changing the kxm plugin auth_token (${CONFIGURE_PLUGIN}). Never put the token in config.`;
25053
25540
  }
25054
25541
  return error2 instanceof Error ? error2.message : String(error2);
25055
25542
  }
@@ -25083,8 +25570,8 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
25083
25570
  function registersAtStartup() {
25084
25571
  const { projectDir, project } = sessionIdentity();
25085
25572
  try {
25086
- if (!statSync(join5(projectDir, ".kxm")).isDirectory()) return false;
25087
- if (!resolveAgentHubAuthToken(process.env, project)) return false;
25573
+ if (!statSync(join6(projectDir, ".kxm")).isDirectory()) return false;
25574
+ if (!resolveAgentHubAuthToken(process.env, project, projectDir)) return false;
25088
25575
  } catch {
25089
25576
  return false;
25090
25577
  }