@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
@@ -164,10 +164,10 @@ var require_code = __commonJS({
164
164
  function interpolate(x) {
165
165
  return typeof x == "number" || typeof x == "boolean" || x === null ? x : safeStringify(Array.isArray(x) ? x.join(",") : x);
166
166
  }
167
- function stringify3(x) {
167
+ function stringify4(x) {
168
168
  return new _Code(safeStringify(x));
169
169
  }
170
- exports.stringify = stringify3;
170
+ exports.stringify = stringify4;
171
171
  function safeStringify(x) {
172
172
  return JSON.stringify(x).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
173
173
  }
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve8.call(this, root, ref);
2990
+ let _sch = resolve9.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve8(root, ref) {
3017
+ function resolve9(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3835,11 +3835,11 @@ var require_fast_uri = __commonJS({
3835
3835
  normalizeString(uri, options);
3836
3836
  } else if (typeof uri === "object") {
3837
3837
  uri = /** @type {T} */
3838
- parse3(serialize(uri, options), options);
3838
+ parse4(serialize(uri, options), options);
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve8(baseURI, relativeURI, options) {
3842
+ function resolve9(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
3875
3875
  function resolveComponent(base, relative3, options, skipNormalization) {
3876
3876
  const target = {};
3877
3877
  if (!skipNormalization) {
3878
- base = parse3(serialize(base, options), options);
3879
- relative3 = parse3(serialize(relative3, options), options);
3878
+ base = parse4(serialize(base, options), options);
3879
+ relative3 = parse4(serialize(relative3, options), options);
3880
3880
  }
3881
3881
  options = options || {};
3882
3882
  if (!options.tolerant && relative3.scheme) {
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
4168
4168
  }
4169
4169
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
4170
4170
  }
4171
- function parse3(uri, opts) {
4171
+ function parse4(uri, opts) {
4172
4172
  return parseWithStatus(uri, opts).parsed;
4173
4173
  }
4174
4174
  function normalizeString(uri, opts) {
@@ -4201,11 +4201,11 @@ var require_fast_uri = __commonJS({
4201
4201
  var fastUri = {
4202
4202
  SCHEMES,
4203
4203
  normalize,
4204
- resolve: resolve8,
4204
+ resolve: resolve9,
4205
4205
  resolveComponent,
4206
4206
  equal,
4207
4207
  serialize,
4208
- parse: parse3
4208
+ parse: parse4
4209
4209
  };
4210
4210
  module.exports = fastUri;
4211
4211
  module.exports.default = fastUri;
@@ -8918,7 +8918,7 @@ var require_stringify = __commonJS({
8918
8918
  props.push(doc.directives.tagString(tag));
8919
8919
  return props.join(" ");
8920
8920
  }
8921
- function stringify3(item, ctx, onComment, onChompKeep) {
8921
+ function stringify4(item, ctx, onComment, onChompKeep) {
8922
8922
  if (identity.isPair(item))
8923
8923
  return item.toString(ctx, onComment, onChompKeep);
8924
8924
  if (identity.isAlias(item)) {
@@ -8947,7 +8947,7 @@ var require_stringify = __commonJS({
8947
8947
  ${ctx.indent}${str}`;
8948
8948
  }
8949
8949
  exports.createStringifyContext = createStringifyContext;
8950
- exports.stringify = stringify3;
8950
+ exports.stringify = stringify4;
8951
8951
  }
8952
8952
  });
8953
8953
 
@@ -8957,7 +8957,7 @@ var require_stringifyPair = __commonJS({
8957
8957
  "use strict";
8958
8958
  var identity = require_identity();
8959
8959
  var Scalar = require_Scalar();
8960
- var stringify3 = require_stringify();
8960
+ var stringify4 = require_stringify();
8961
8961
  var stringifyComment = require_stringifyComment();
8962
8962
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
8963
8963
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -8979,7 +8979,7 @@ var require_stringifyPair = __commonJS({
8979
8979
  });
8980
8980
  let keyCommentDone = false;
8981
8981
  let chompKeep = false;
8982
- let str = stringify3.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8982
+ let str = stringify4.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8983
8983
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
8984
8984
  if (simpleKeys)
8985
8985
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -9031,7 +9031,7 @@ ${indent}:`;
9031
9031
  ctx.indent = ctx.indent.substring(2);
9032
9032
  }
9033
9033
  let valueCommentDone = false;
9034
- const valueStr = stringify3.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9034
+ const valueStr = stringify4.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9035
9035
  let ws = " ";
9036
9036
  if (keyComment || vsb || vcb) {
9037
9037
  ws = vsb ? "\n" : "";
@@ -9172,7 +9172,7 @@ var require_addPairToJSMap = __commonJS({
9172
9172
  "use strict";
9173
9173
  var log = require_log();
9174
9174
  var merge = require_merge();
9175
- var stringify3 = require_stringify();
9175
+ var stringify4 = require_stringify();
9176
9176
  var identity = require_identity();
9177
9177
  var toJS = require_toJS();
9178
9178
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -9208,7 +9208,7 @@ var require_addPairToJSMap = __commonJS({
9208
9208
  if (typeof jsKey !== "object")
9209
9209
  return String(jsKey);
9210
9210
  if (identity.isNode(key) && ctx?.doc) {
9211
- const strCtx = stringify3.createStringifyContext(ctx.doc, {});
9211
+ const strCtx = stringify4.createStringifyContext(ctx.doc, {});
9212
9212
  strCtx.anchors = /* @__PURE__ */ new Set();
9213
9213
  for (const node of ctx.anchors.keys())
9214
9214
  strCtx.anchors.add(node.anchor);
@@ -9275,12 +9275,12 @@ var require_stringifyCollection = __commonJS({
9275
9275
  "node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
9276
9276
  "use strict";
9277
9277
  var identity = require_identity();
9278
- var stringify3 = require_stringify();
9278
+ var stringify4 = require_stringify();
9279
9279
  var stringifyComment = require_stringifyComment();
9280
9280
  function stringifyCollection(collection, ctx, options) {
9281
9281
  const flow = ctx.inFlow ?? collection.flow;
9282
- const stringify4 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
- return stringify4(collection, ctx, options);
9282
+ const stringify5 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
+ return stringify5(collection, ctx, options);
9284
9284
  }
9285
9285
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
9286
9286
  const { indent, options: { commentString } } = ctx;
@@ -9305,7 +9305,7 @@ var require_stringifyCollection = __commonJS({
9305
9305
  }
9306
9306
  }
9307
9307
  chompKeep = false;
9308
- let str2 = stringify3.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9308
+ let str2 = stringify4.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9309
9309
  if (comment2)
9310
9310
  str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
9311
9311
  if (chompKeep && comment2)
@@ -9372,7 +9372,7 @@ ${indent}${line}` : "\n";
9372
9372
  }
9373
9373
  if (comment)
9374
9374
  reqNewline = true;
9375
- let str = stringify3.stringify(item, itemCtx, () => comment = null);
9375
+ let str = stringify4.stringify(item, itemCtx, () => comment = null);
9376
9376
  reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
9377
9377
  if (i < items.length - 1) {
9378
9378
  str += ",";
@@ -10733,7 +10733,7 @@ var require_stringifyDocument = __commonJS({
10733
10733
  "node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
10734
10734
  "use strict";
10735
10735
  var identity = require_identity();
10736
- var stringify3 = require_stringify();
10736
+ var stringify4 = require_stringify();
10737
10737
  var stringifyComment = require_stringifyComment();
10738
10738
  function stringifyDocument(doc, options) {
10739
10739
  const lines = [];
@@ -10748,7 +10748,7 @@ var require_stringifyDocument = __commonJS({
10748
10748
  }
10749
10749
  if (hasDirectives)
10750
10750
  lines.push("---");
10751
- const ctx = stringify3.createStringifyContext(doc, options);
10751
+ const ctx = stringify4.createStringifyContext(doc, options);
10752
10752
  const { commentString } = ctx.options;
10753
10753
  if (doc.commentBefore) {
10754
10754
  if (lines.length !== 1)
@@ -10770,7 +10770,7 @@ var require_stringifyDocument = __commonJS({
10770
10770
  contentComment = doc.contents.comment;
10771
10771
  }
10772
10772
  const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
10773
- let body = stringify3.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10773
+ let body = stringify4.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10774
10774
  if (contentComment)
10775
10775
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
10776
10776
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -10778,7 +10778,7 @@ var require_stringifyDocument = __commonJS({
10778
10778
  } else
10779
10779
  lines.push(body);
10780
10780
  } else {
10781
- lines.push(stringify3.stringify(doc.contents, ctx));
10781
+ lines.push(stringify4.stringify(doc.contents, ctx));
10782
10782
  }
10783
10783
  if (doc.directives?.docEnd) {
10784
10784
  if (doc.comment) {
@@ -12914,7 +12914,7 @@ var require_cst_scalar = __commonJS({
12914
12914
  var require_cst_stringify = __commonJS({
12915
12915
  "node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
12916
12916
  "use strict";
12917
- var stringify3 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12917
+ var stringify4 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12918
12918
  function stringifyToken(token) {
12919
12919
  switch (token.type) {
12920
12920
  case "block-scalar": {
@@ -12967,7 +12967,7 @@ var require_cst_stringify = __commonJS({
12967
12967
  res += stringifyToken(value);
12968
12968
  return res;
12969
12969
  }
12970
- exports.stringify = stringify3;
12970
+ exports.stringify = stringify4;
12971
12971
  }
12972
12972
  });
12973
12973
 
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
14678
14678
  }
14679
14679
  return doc;
14680
14680
  }
14681
- function parse3(src, reviver, options) {
14681
+ function parse4(src, reviver, options) {
14682
14682
  let _reviver = void 0;
14683
14683
  if (typeof reviver === "function") {
14684
14684
  _reviver = reviver;
@@ -14697,7 +14697,7 @@ var require_public_api = __commonJS({
14697
14697
  }
14698
14698
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
14699
14699
  }
14700
- function stringify3(value, replacer, options) {
14700
+ function stringify4(value, replacer, options) {
14701
14701
  let _replacer = null;
14702
14702
  if (typeof replacer === "function" || Array.isArray(replacer)) {
14703
14703
  _replacer = replacer;
@@ -14719,10 +14719,10 @@ var require_public_api = __commonJS({
14719
14719
  return value.toString(options);
14720
14720
  return new Document.Document(value, _replacer, options).toString(options);
14721
14721
  }
14722
- exports.parse = parse3;
14722
+ exports.parse = parse4;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
- exports.stringify = stringify3;
14725
+ exports.stringify = stringify4;
14726
14726
  }
14727
14727
  });
14728
14728
 
@@ -23105,11 +23105,11 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
23105
23105
  state: { agents, messages, workflowRuns, journal, persistent: store.persistent },
23106
23106
  async start() {
23107
23107
  if (closed) throw new Error("hub is closed");
23108
- await new Promise((resolve8, reject) => {
23108
+ await new Promise((resolve9, reject) => {
23109
23109
  server.once("error", reject);
23110
23110
  server.listen(port2, host2, () => {
23111
23111
  server.off("error", reject);
23112
- resolve8();
23112
+ resolve9();
23113
23113
  });
23114
23114
  });
23115
23115
  cleanupTimer = setInterval(() => {
@@ -23139,7 +23139,7 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
23139
23139
  if (closed) return;
23140
23140
  closed = true;
23141
23141
  if (cleanupTimer) clearInterval(cleanupTimer);
23142
- const serverClosed = server.listening ? new Promise((resolve8, reject) => server.close((error) => error ? reject(error) : resolve8())) : Promise.resolve();
23142
+ const serverClosed = server.listening ? new Promise((resolve9, reject) => server.close((error) => error ? reject(error) : resolve9())) : Promise.resolve();
23143
23143
  const forceClose = setTimeout(() => server.closeAllConnections(), shutdownGraceMs);
23144
23144
  forceClose.unref();
23145
23145
  for (const clients of streams.values()) {
@@ -23166,12 +23166,403 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
23166
23166
  }
23167
23167
 
23168
23168
  // plugins/kxm/src/server.ts
23169
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync5 } from "node:fs";
23170
- import { dirname as dirname8, join as join7, resolve as resolve7 } from "node:path";
23169
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync6 } from "node:fs";
23170
+ import { dirname as dirname9, join as join8, resolve as resolve8 } from "node:path";
23171
+
23172
+ // plugins/kxm/src/hub-identity.ts
23173
+ import { spawnSync } from "node:child_process";
23174
+ var import_yaml7 = __toESM(require_dist(), 1);
23175
+
23176
+ // plugins/kxm/src/config.ts
23177
+ var import_yaml6 = __toESM(require_dist(), 1);
23178
+ import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
23179
+ import { tmpdir, homedir } from "node:os";
23180
+ import { dirname as dirname7, join as join7, resolve as resolve7 } from "node:path";
23181
+ var KXM_CONFIG_SCHEMA = "kxm.config.v1";
23182
+ var KxmHubConfigError = class extends Error {
23183
+ code = "hub_config_invalid";
23184
+ constructor(message) {
23185
+ super(message);
23186
+ this.name = "KxmHubConfigError";
23187
+ }
23188
+ };
23189
+ var HUB_ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/;
23190
+ var HUB_OP_REF = /^op:\/\/\S+\/\S+\/\S+$/;
23191
+ var LITERAL_SECRET_KEY = /^(token|secret|password|authToken|auth_token|apiKey|api_key|api_token|bearer|credential|access_token)$/i;
23192
+ function isHubKeyEnvName(value) {
23193
+ return HUB_ENV_NAME.test(value);
23194
+ }
23195
+ function isHubOpReference(value) {
23196
+ return HUB_OP_REF.test(value);
23197
+ }
23198
+ var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
23199
+ var DEFAULT_KXM_CONFIG = {
23200
+ schema: KXM_CONFIG_SCHEMA,
23201
+ user: {
23202
+ theme: "dark",
23203
+ preferredCritics: ["reviewer-arch", "reviewer-cli"],
23204
+ tokenBudget: 16e3
23205
+ },
23206
+ defaults: {
23207
+ workflow: "software-engineering/feature-implementation",
23208
+ harness: "pi"
23209
+ },
23210
+ dash: {
23211
+ defaultScreen: "agents",
23212
+ refreshIntervalMs: 1e3,
23213
+ autoOpen: false
23214
+ },
23215
+ sync: {
23216
+ defaultTracker: "none"
23217
+ },
23218
+ hub: {
23219
+ autoStart: "background"
23220
+ },
23221
+ improvement: {
23222
+ promotionPolicy: "manual_pr",
23223
+ telemetryHalfLifeDays: 14,
23224
+ autoThreshold: {
23225
+ minRuns: 10,
23226
+ minPassRate: 0.95,
23227
+ minCostSavings: 0.5
23228
+ }
23229
+ },
23230
+ routing: {
23231
+ shadowExecution: {
23232
+ enabled: false,
23233
+ sampleRate: 0.05,
23234
+ candidateModels: []
23235
+ },
23236
+ circuitBreaker: {
23237
+ mode: "soft_demotion",
23238
+ failureThreshold: 3,
23239
+ windowSeconds: 3600,
23240
+ cooldownSeconds: 1800,
23241
+ penaltyMultiplier: 5
23242
+ }
23243
+ },
23244
+ telemetry: {
23245
+ federated: true,
23246
+ anonymize: true
23247
+ }
23248
+ };
23249
+ function userConfigDirectory(overrideDir) {
23250
+ if (overrideDir) return resolve7(overrideDir);
23251
+ return resolve7(process.env.KXM_USER_CONFIG_DIR?.trim() || join7(homedir(), ".config", "kxm"));
23252
+ }
23253
+ function repoConfigDirectory(repoRoot) {
23254
+ return resolve7(repoRoot, ".kxm");
23255
+ }
23256
+ function deepMerge(target, source) {
23257
+ const result = { ...target };
23258
+ for (const [key, val] of Object.entries(source)) {
23259
+ if (val && typeof val === "object" && !Array.isArray(val)) {
23260
+ const existing = result[key] && typeof result[key] === "object" && !Array.isArray(result[key]) ? result[key] : {};
23261
+ result[key] = deepMerge(existing, val);
23262
+ } else if (val !== void 0) {
23263
+ result[key] = val;
23264
+ }
23265
+ }
23266
+ return result;
23267
+ }
23268
+ function literalSecretMessage(path) {
23269
+ return `refusing literal hub token at ${path}: store an op:// reference (key.op) or an environment variable name (key.env), never the token`;
23270
+ }
23271
+ function rejectLiteralHubSecrets(value, path) {
23272
+ if (Array.isArray(value)) {
23273
+ value.forEach((item, index) => rejectLiteralHubSecrets(item, `${path}[${index}]`));
23274
+ return;
23275
+ }
23276
+ if (!value || typeof value !== "object") return;
23277
+ for (const [key, child] of Object.entries(value)) {
23278
+ const childPath = `${path}.${key}`;
23279
+ if (typeof child === "string" && (key === "key" || LITERAL_SECRET_KEY.test(key))) {
23280
+ throw new KxmHubConfigError(literalSecretMessage(childPath));
23281
+ }
23282
+ if (child && typeof child === "object") rejectLiteralHubSecrets(child, childPath);
23283
+ }
23284
+ }
23285
+ function parseKeyRef(raw, path) {
23286
+ if (raw === void 0) return void 0;
23287
+ if (typeof raw === "string") throw new KxmHubConfigError(literalSecretMessage(path));
23288
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23289
+ throw new KxmHubConfigError(`${path} must be a key reference with op and/or env, never a token`);
23290
+ }
23291
+ const row = raw;
23292
+ for (const key of Object.keys(row)) {
23293
+ if (key !== "op" && key !== "env") {
23294
+ throw new KxmHubConfigError(literalSecretMessage(`${path}.${key}`));
23295
+ }
23296
+ }
23297
+ const ref = {};
23298
+ if (row.op !== void 0) {
23299
+ if (typeof row.op !== "string" || !isHubOpReference(row.op)) {
23300
+ throw new KxmHubConfigError(`${path}.op must be an op://vault/item/field reference, not a token`);
23301
+ }
23302
+ ref.op = row.op;
23303
+ }
23304
+ if (row.env !== void 0) {
23305
+ if (typeof row.env !== "string" || !isHubKeyEnvName(row.env)) {
23306
+ throw new KxmHubConfigError(`${path}.env must be an environment variable name, not a token`);
23307
+ }
23308
+ ref.env = row.env;
23309
+ }
23310
+ if (!ref.op && !ref.env) throw new KxmHubConfigError(`${path} needs key.op, key.env, or both`);
23311
+ return ref;
23312
+ }
23313
+ function parseEndpoint(raw, path) {
23314
+ if (raw === void 0) return void 0;
23315
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23316
+ throw new KxmHubConfigError(`${path} must be an object with url, project, and key`);
23317
+ }
23318
+ const row = raw;
23319
+ const endpoint = {};
23320
+ if (row.url !== void 0) {
23321
+ if (typeof row.url !== "string" || !isHubUrl(row.url)) {
23322
+ throw new KxmHubConfigError(`${path}.url must be an http or https URL without credentials, query, or fragment`);
23323
+ }
23324
+ endpoint.url = row.url.replace(/\/$/, "");
23325
+ }
23326
+ if (row.project !== void 0) {
23327
+ if (typeof row.project !== "string" || !isHubProjectId(row.project)) {
23328
+ throw new KxmHubConfigError(`${path}.project must be a single-line project id, not a token`);
23329
+ }
23330
+ endpoint.project = row.project.trim();
23331
+ }
23332
+ const key = parseKeyRef(row.key, `${path}.key`);
23333
+ if (key) endpoint.key = key;
23334
+ for (const name of Object.keys(row)) {
23335
+ if (name !== "url" && name !== "project" && name !== "key") {
23336
+ if (typeof row[name] === "string") throw new KxmHubConfigError(literalSecretMessage(`${path}.${name}`));
23337
+ throw new KxmHubConfigError(`${path}.${name} is not a hub endpoint field`);
23338
+ }
23339
+ }
23340
+ return endpoint;
23341
+ }
23342
+ function isHubUrl(raw) {
23343
+ try {
23344
+ const parsed = new URL(raw);
23345
+ return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && parsed.search === "" && parsed.hash === "" && !raw.includes("?") && !raw.includes("#");
23346
+ } catch {
23347
+ return false;
23348
+ }
23349
+ }
23350
+ function isHubProjectId(raw) {
23351
+ const value = raw.trim();
23352
+ return value.length > 0 && value.length <= 200 && !/[\s\r\n]/.test(value);
23353
+ }
23354
+ function parseProjectMap(raw) {
23355
+ if (raw === void 0) return void 0;
23356
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23357
+ throw new KxmHubConfigError("hub.projects must be a map of project id to key reference, never a token");
23358
+ }
23359
+ const projects = {};
23360
+ for (const [project, value] of Object.entries(raw)) {
23361
+ if (!isHubProjectId(project)) {
23362
+ throw new KxmHubConfigError(`hub.projects key ${JSON.stringify(project)} must be a project id, not a token`);
23363
+ }
23364
+ const ref = parseKeyRef(value, `hub.projects.${project}`);
23365
+ if (!ref) throw new KxmHubConfigError(`hub.projects.${project} needs an op:// reference or an environment variable name`);
23366
+ projects[project.trim()] = ref;
23367
+ }
23368
+ return projects;
23369
+ }
23370
+ function normalizeHubConfig(raw) {
23371
+ const autoStart = raw?.autoStart;
23372
+ const hub2 = {
23373
+ autoStart: autoStart === "off" || autoStart === "background" ? autoStart : DEFAULT_KXM_CONFIG.hub.autoStart
23374
+ };
23375
+ if (raw === void 0) return hub2;
23376
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
23377
+ throw new KxmHubConfigError("hub must be an object");
23378
+ }
23379
+ rejectLiteralHubSecrets(raw, "hub");
23380
+ const row = raw;
23381
+ if (row.mode !== void 0) {
23382
+ if (row.mode !== "local" && row.mode !== "cloud") {
23383
+ throw new KxmHubConfigError("hub.mode must be local or cloud");
23384
+ }
23385
+ hub2.mode = row.mode;
23386
+ }
23387
+ const local = parseEndpoint(row.local, "hub.local");
23388
+ const cloud = parseEndpoint(row.cloud, "hub.cloud");
23389
+ const projects = parseProjectMap(row.projects);
23390
+ if (local) hub2.local = local;
23391
+ if (cloud) hub2.cloud = cloud;
23392
+ if (projects && Object.keys(projects).length > 0) hub2.projects = projects;
23393
+ return hub2;
23394
+ }
23395
+ function findConfigRoot(start) {
23396
+ let dir = resolve7(start);
23397
+ for (; ; ) {
23398
+ if (existsSync7(join7(dir, ".kxm", "project.yaml")) || existsSync7(join7(dir, ".kxm", "config.yaml"))) return dir;
23399
+ const parent = dirname7(dir);
23400
+ if (parent === dir) return resolve7(start);
23401
+ dir = parent;
23402
+ }
23403
+ }
23404
+ function userConfigDirForEnv(env) {
23405
+ const explicit = env.KXM_USER_CONFIG_DIR?.trim();
23406
+ if (explicit) return explicit;
23407
+ if (env === process.env) return void 0;
23408
+ return join7(tmpdir(), "kxm-no-user-config");
23409
+ }
23410
+ function loadHubSettings(start, env = process.env) {
23411
+ const userConfigDir = userConfigDirForEnv(env);
23412
+ return loadKxmConfig(findConfigRoot(start), userConfigDir === void 0 ? {} : { userConfigDir }).hub;
23413
+ }
23414
+ function recordOf(raw) {
23415
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
23416
+ }
23417
+ function finiteNumber(value) {
23418
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
23419
+ }
23420
+ function normalizeImprovementConfig(raw) {
23421
+ const value = recordOf(raw);
23422
+ const threshold = recordOf(value.autoThreshold);
23423
+ const policy = value.promotionPolicy;
23424
+ const halfLife = finiteNumber(value.telemetryHalfLifeDays);
23425
+ const minRuns = finiteNumber(threshold.minRuns);
23426
+ const minPassRate = finiteNumber(threshold.minPassRate);
23427
+ const minCostSavings = finiteNumber(threshold.minCostSavings);
23428
+ return {
23429
+ promotionPolicy: IMPROVEMENT_PROMOTION_POLICIES.includes(policy) ? policy : "manual_pr",
23430
+ telemetryHalfLifeDays: halfLife !== void 0 && halfLife > 0 && halfLife <= 3650 ? halfLife : 14,
23431
+ autoThreshold: {
23432
+ minRuns: minRuns !== void 0 && Number.isInteger(minRuns) && minRuns >= 1 && minRuns <= 1e6 ? minRuns : 10,
23433
+ minPassRate: minPassRate !== void 0 && minPassRate >= 0 && minPassRate <= 1 ? minPassRate : 0.95,
23434
+ minCostSavings: minCostSavings !== void 0 && minCostSavings >= 0 ? minCostSavings : 0.5
23435
+ }
23436
+ };
23437
+ }
23438
+ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
23439
+ const userDir = userConfigDirectory(options.userConfigDir);
23440
+ const userConfigFile = join7(userDir, "config.yaml");
23441
+ const repoDir = repoConfigDirectory(repoRoot);
23442
+ const repoConfigFile = join7(repoDir, "config.yaml");
23443
+ let userRaw = {};
23444
+ let userLoadedPath;
23445
+ if (existsSync7(userConfigFile)) {
23446
+ try {
23447
+ const text2 = readFileSync5(userConfigFile, "utf8");
23448
+ userRaw = (0, import_yaml6.parse)(text2) ?? {};
23449
+ userLoadedPath = userConfigFile;
23450
+ } catch (error) {
23451
+ throw new Error(`invalid user config YAML at ${userConfigFile}`, { cause: error });
23452
+ }
23453
+ }
23454
+ let repoRaw = {};
23455
+ let repoLoadedPath;
23456
+ if (existsSync7(repoConfigFile)) {
23457
+ try {
23458
+ const text2 = readFileSync5(repoConfigFile, "utf8");
23459
+ repoRaw = (0, import_yaml6.parse)(text2) ?? {};
23460
+ repoLoadedPath = repoConfigFile;
23461
+ } catch (error) {
23462
+ throw new Error(`invalid project config YAML at ${repoConfigFile}`, { cause: error });
23463
+ }
23464
+ }
23465
+ const baseCopy = JSON.parse(JSON.stringify(DEFAULT_KXM_CONFIG));
23466
+ const mergedUser = deepMerge(baseCopy, userRaw);
23467
+ const mergedAll = deepMerge(mergedUser, repoRaw);
23468
+ return {
23469
+ schema: KXM_CONFIG_SCHEMA,
23470
+ user: mergedAll.user ?? {},
23471
+ defaults: mergedAll.defaults ?? {},
23472
+ dash: mergedAll.dash ?? {},
23473
+ sync: mergedAll.sync ?? {},
23474
+ hub: normalizeHubConfig(mergedAll.hub),
23475
+ improvement: normalizeImprovementConfig(mergedAll.improvement),
23476
+ routing: mergedAll.routing ?? DEFAULT_KXM_CONFIG.routing,
23477
+ telemetry: mergedAll.telemetry ?? DEFAULT_KXM_CONFIG.telemetry,
23478
+ loadedFrom: {
23479
+ userConfigPath: userLoadedPath,
23480
+ repoConfigPath: repoLoadedPath
23481
+ }
23482
+ };
23483
+ }
23484
+
23485
+ // plugins/kxm/src/hub-binding.ts
23486
+ var CloudTokenError = class extends Error {
23487
+ code;
23488
+ constructor(code, message) {
23489
+ super(message);
23490
+ this.name = "CloudTokenError";
23491
+ this.code = code;
23492
+ }
23493
+ };
23494
+
23495
+ // plugins/kxm/src/hub-identity.ts
23496
+ var OP_TIMEOUT_MS = 15e3;
23497
+ var OP_MAX_BUFFER = 4096;
23498
+ function defaultOpRead(reference, env) {
23499
+ if (!isHubOpReference(reference)) {
23500
+ throw new CloudTokenError("cloud_token_command_invalid", `refusing to resolve ${reference}; an op:// reference is required`);
23501
+ }
23502
+ const result = spawnSync("op", ["read", reference], {
23503
+ encoding: "utf8",
23504
+ shell: false,
23505
+ timeout: OP_TIMEOUT_MS,
23506
+ maxBuffer: OP_MAX_BUFFER,
23507
+ env,
23508
+ windowsHide: true
23509
+ });
23510
+ if (result.error || result.status !== 0) {
23511
+ throw new CloudTokenError(
23512
+ "cloud_token_command_failed",
23513
+ `op read failed for ${reference}; the token was not saved`
23514
+ );
23515
+ }
23516
+ const line = (result.stdout ?? "").trim();
23517
+ if (!line || line.split(/\r?\n/).length !== 1) {
23518
+ throw new CloudTokenError(
23519
+ "cloud_token_command_failed",
23520
+ `op read did not print a single token for ${reference}; the token was not saved`
23521
+ );
23522
+ }
23523
+ return line;
23524
+ }
23525
+ function resolveKeyReference(ref, env, opRead = defaultOpRead) {
23526
+ if (!ref) return void 0;
23527
+ if (ref.env) {
23528
+ const value = env[ref.env]?.trim();
23529
+ if (value) return { value, source: `env:${ref.env}` };
23530
+ }
23531
+ if (ref.op) {
23532
+ const value = opRead(ref.op, env).trim();
23533
+ if (!value || value.split(/\r?\n/).length !== 1) {
23534
+ throw new CloudTokenError("cloud_token_command_failed", `op read did not print a single token for ${ref.op}`);
23535
+ }
23536
+ return { value, source: `op:${ref.op}` };
23537
+ }
23538
+ return void 0;
23539
+ }
23540
+ function selectHubProjectTokenMap(input) {
23541
+ if (input.explicit) return { tokens: input.explicit, source: "env" };
23542
+ if (input.configured && Object.keys(input.configured).length > 0) return { tokens: input.configured, source: "config" };
23543
+ if (input.file && Object.keys(input.file).length > 0) return { tokens: input.file, source: "file" };
23544
+ return { tokens: void 0, source: "none" };
23545
+ }
23546
+ function resolveConfiguredHubProjectTokens(cwd, env = process.env, opRead = defaultOpRead) {
23547
+ const projects = loadHubSettings(cwd, env).projects;
23548
+ if (!projects || Object.keys(projects).length === 0) return void 0;
23549
+ const tokens = {};
23550
+ for (const [project, ref] of Object.entries(projects)) {
23551
+ const resolved = resolveKeyReference(ref, env, opRead);
23552
+ if (!resolved?.value) {
23553
+ throw new CloudTokenError(
23554
+ "cloud_token_missing",
23555
+ `hub.projects.${project} did not resolve; set ${ref.env ?? "the named variable"} or fix the op:// reference. The token was not saved`
23556
+ );
23557
+ }
23558
+ tokens[project] = resolved.value;
23559
+ }
23560
+ return tokens;
23561
+ }
23171
23562
 
23172
23563
  // plugins/kxm/src/logger.ts
23173
- import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync5, renameSync as renameSync3, statSync as statSync2, unlinkSync as unlinkSync2 } from "node:fs";
23174
- import { dirname as dirname7 } from "node:path";
23564
+ import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync6, renameSync as renameSync3, statSync as statSync2, unlinkSync as unlinkSync2 } from "node:fs";
23565
+ import { dirname as dirname8 } from "node:path";
23175
23566
  var LOG_LEVEL_PRIORITY = {
23176
23567
  debug: 10,
23177
23568
  info: 20,
@@ -23208,7 +23599,7 @@ function redactLogValue(val, key) {
23208
23599
  function rotateLogFiles(filePath, maxFiles) {
23209
23600
  for (let i = maxFiles; i >= 1; i--) {
23210
23601
  const current = `${filePath}.${i}`;
23211
- if (existsSync7(current)) {
23602
+ if (existsSync8(current)) {
23212
23603
  if (i >= maxFiles) {
23213
23604
  try {
23214
23605
  unlinkSync2(current);
@@ -23222,7 +23613,7 @@ function rotateLogFiles(filePath, maxFiles) {
23222
23613
  }
23223
23614
  }
23224
23615
  }
23225
- if (existsSync7(filePath)) {
23616
+ if (existsSync8(filePath)) {
23226
23617
  try {
23227
23618
  renameSync3(filePath, `${filePath}.1`);
23228
23619
  } catch {
@@ -23239,7 +23630,7 @@ function createLogger(options) {
23239
23630
  const shouldStdout = options.stdout ?? !isDaemon;
23240
23631
  const correlationDefaults = options.correlation ?? {};
23241
23632
  let currentSize = 0;
23242
- if (filePath && existsSync7(filePath)) {
23633
+ if (filePath && existsSync8(filePath)) {
23243
23634
  try {
23244
23635
  currentSize = statSync2(filePath).size;
23245
23636
  } catch {
@@ -23277,7 +23668,7 @@ function createLogger(options) {
23277
23668
  currentSize = 0;
23278
23669
  }
23279
23670
  try {
23280
- mkdirSync5(dirname7(filePath), { recursive: true });
23671
+ mkdirSync6(dirname8(filePath), { recursive: true });
23281
23672
  appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
23282
23673
  currentSize += lineBytes;
23283
23674
  } catch {
@@ -23322,14 +23713,14 @@ function createLogger(options) {
23322
23713
  var host = process.env.KXM_HOST ?? "127.0.0.1";
23323
23714
  var port = Number.parseInt(process.env.KXM_PORT ?? String(DEFAULT_PORT), 10);
23324
23715
  var authToken = process.env.KXM_AUTH_TOKEN;
23325
- var workspaceDir = resolve7(process.env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
23326
- var configDir = resolve7(process.env.KXM_CONFIG_DIR?.trim() || join7(workspaceDir, "config"));
23327
- var logsDir = resolve7(process.env.KXM_LOGS_DIR?.trim() || join7(workspaceDir, "logs"));
23328
- var assetsDir = resolve7(process.env.KXM_ASSETS_DIR?.trim() || join7(workspaceDir, "assets"));
23329
- var stateDir = resolve7(process.env.KXM_STATE_DIR?.trim() || join7(workspaceDir, "state"));
23716
+ var workspaceDir = resolve8(process.env.KXM_WORKSPACE_DIR?.trim() || ".kxm");
23717
+ var configDir = resolve8(process.env.KXM_CONFIG_DIR?.trim() || join8(workspaceDir, "config"));
23718
+ var logsDir = resolve8(process.env.KXM_LOGS_DIR?.trim() || join8(workspaceDir, "logs"));
23719
+ var assetsDir = resolve8(process.env.KXM_ASSETS_DIR?.trim() || join8(workspaceDir, "assets"));
23720
+ var stateDir = resolve8(process.env.KXM_STATE_DIR?.trim() || join8(workspaceDir, "state"));
23330
23721
  var dataPathValue = process.env.KXM_DATA_PATH?.trim();
23331
- var dataPath = dataPathValue === ":memory:" ? dataPathValue : resolve7(dataPathValue || join7(stateDir, "kxm.db"));
23332
- var logPath = resolve7(process.env.KXM_LOG_PATH?.trim() || join7(logsDir, "kxm-hub.jsonl"));
23722
+ var dataPath = dataPathValue === ":memory:" ? dataPathValue : resolve8(dataPathValue || join8(stateDir, "kxm.db"));
23723
+ var logPath = resolve8(process.env.KXM_LOG_PATH?.trim() || join8(logsDir, "kxm-hub.jsonl"));
23333
23724
  var messageTtlMs = Number.parseInt(process.env.KXM_MESSAGE_TTL_MS ?? String(DEFAULT_MESSAGE_TTL_MS), 10);
23334
23725
  var messageRetentionMs = Number.parseInt(
23335
23726
  process.env.KXM_MESSAGE_RETENTION_MS ?? String(DEFAULT_MESSAGE_RETENTION_MS),
@@ -23340,17 +23731,17 @@ var rateLimitWindowMs = Number.parseInt(
23340
23731
  process.env.KXM_RATE_LIMIT_WINDOW_MS ?? String(DEFAULT_RATE_LIMIT_WINDOW_MS),
23341
23732
  10
23342
23733
  );
23343
- for (const directory of [configDir, logsDir, assetsDir, stateDir, dirname8(logPath)]) {
23344
- mkdirSync6(directory, { recursive: true });
23734
+ for (const directory of [configDir, logsDir, assetsDir, stateDir, dirname9(logPath)]) {
23735
+ mkdirSync7(directory, { recursive: true });
23345
23736
  }
23346
23737
  var structuredLog = createLogger({
23347
23738
  component: "hub",
23348
23739
  path: logPath
23349
23740
  });
23350
- function projectTokens() {
23351
- const raw = process.env.KXM_PROJECT_TOKENS?.trim();
23352
- if (!raw) return void 0;
23353
- const value = JSON.parse(raw);
23741
+ function parseProjectTokenEnv(raw) {
23742
+ const trimmed = raw?.trim();
23743
+ if (!trimmed) return void 0;
23744
+ const value = JSON.parse(trimmed);
23354
23745
  if (!value || Array.isArray(value) || typeof value !== "object") {
23355
23746
  throw new Error("KXM_PROJECT_TOKENS must be a JSON object of project names to tokens");
23356
23747
  }
@@ -23360,6 +23751,23 @@ function projectTokens() {
23360
23751
  }
23361
23752
  return Object.fromEntries(entries);
23362
23753
  }
23754
+ function projectTokens() {
23755
+ const explicit = process.env.KXM_PROJECT_TOKENS_EXPLICIT === "1";
23756
+ const fromEnv = parseProjectTokenEnv(process.env.KXM_PROJECT_TOKENS);
23757
+ const workdir = resolve8(process.env.KXM_WORKDIR?.trim() || process.cwd());
23758
+ const configured = explicit ? void 0 : resolveConfiguredHubProjectTokens(workdir, process.env);
23759
+ const selected = selectHubProjectTokenMap({
23760
+ explicit: explicit ? fromEnv : void 0,
23761
+ configured,
23762
+ file: explicit ? void 0 : fromEnv
23763
+ });
23764
+ if (selected.source === "config") {
23765
+ const count = Object.keys(selected.tokens ?? {}).length;
23766
+ process.stderr.write(`kxm hub: project tokens from .kxm/config.yaml (${count} projects, key references resolved, values not saved)
23767
+ `);
23768
+ }
23769
+ return selected.tokens;
23770
+ }
23363
23771
  if (!Number.isInteger(port) || port < 0 || port > 65535) {
23364
23772
  throw new Error("KXM_PORT must be an integer between 0 and 65535");
23365
23773
  }
@@ -23373,7 +23781,7 @@ if (inlineWorkflows && workflowFile) {
23373
23781
  throw new Error("configure only one of KXM_WEBHOOK_WORKFLOWS or KXM_WEBHOOK_WORKFLOWS_FILE");
23374
23782
  }
23375
23783
  var webhookWorkflows = parseWorkflowDefinitions(
23376
- workflowFile ? readFileSync5(resolve7(workflowFile), "utf8") : inlineWorkflows
23784
+ workflowFile ? readFileSync6(resolve8(workflowFile), "utf8") : inlineWorkflows
23377
23785
  );
23378
23786
  var hub = createMeshHub({
23379
23787
  host,