@kontextmind/kxm 0.7.103 → 0.7.105

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 (36) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +39 -8
  3. package/docs/concepts/data-and-storage.md +1 -1
  4. package/docs/contributing/test-matrix.md +11 -6
  5. package/docs/operations/backup-and-restore.md +59 -27
  6. package/docs/operations/deploy.md +1 -1
  7. package/docs/reference/cli-reference.md +73 -55
  8. package/docs/reference/config-reference.md +1 -1
  9. package/docs/reference/workflow-definitions.md +4 -4
  10. package/docs/start/first-workflow.md +6 -2
  11. package/docs/start/quickstart-claude-code.md +11 -1
  12. package/docs/templates/runbook.md +2 -1
  13. package/package.json +1 -1
  14. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  15. package/plugins/kxm/dist/cli.js +1802 -1011
  16. package/plugins/kxm/dist/mcp-server.js +1 -1
  17. package/plugins/kxm/dist/runtime-supervisor.js +322 -292
  18. package/plugins/kxm/dist/runtime.js +427 -351
  19. package/plugins/kxm/dist/server.js +78 -78
  20. package/plugins/kxm/package.json +1 -1
  21. package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +14 -7
  22. package/plugins/kxm/src/cli/project.ts +132 -30
  23. package/plugins/kxm/src/cli/system.ts +19 -1
  24. package/plugins/kxm/src/cli/tasks.ts +55 -31
  25. package/plugins/kxm/src/cli/workflows.ts +52 -54
  26. package/plugins/kxm/src/cli.ts +8 -6
  27. package/plugins/kxm/src/database.ts +193 -65
  28. package/plugins/kxm/src/engine.ts +79 -3
  29. package/plugins/kxm/src/harness.ts +6 -5
  30. package/plugins/kxm/src/mcp-server.ts +1 -1
  31. package/plugins/kxm/src/runtime-paths.ts +50 -0
  32. package/plugins/kxm/src/runtime-store.ts +22 -53
  33. package/plugins/kxm/src/runtime-supervisor.ts +38 -17
  34. package/plugins/kxm/src/suggest.ts +132 -79
  35. package/plugins/kxm/src/workflow-manager.ts +42 -19
  36. package/schemas/backup-manifest.schema.json +15 -0
@@ -3872,49 +3872,49 @@ var require_fast_uri = __commonJS({
3872
3872
  schemelessOptions.skipEscape = true;
3873
3873
  return serialize(resolved, schemelessOptions);
3874
3874
  }
3875
- function resolveComponent(base, relative2, options, skipNormalization) {
3875
+ function resolveComponent(base, relative3, options, skipNormalization) {
3876
3876
  const target = {};
3877
3877
  if (!skipNormalization) {
3878
3878
  base = parse3(serialize(base, options), options);
3879
- relative2 = parse3(serialize(relative2, options), options);
3879
+ relative3 = parse3(serialize(relative3, options), options);
3880
3880
  }
3881
3881
  options = options || {};
3882
- if (!options.tolerant && relative2.scheme) {
3883
- target.scheme = relative2.scheme;
3884
- target.userinfo = relative2.userinfo;
3885
- target.host = relative2.host;
3886
- target.port = relative2.port;
3887
- target.path = removeDotSegments(relative2.path || "");
3888
- target.query = relative2.query;
3882
+ if (!options.tolerant && relative3.scheme) {
3883
+ target.scheme = relative3.scheme;
3884
+ target.userinfo = relative3.userinfo;
3885
+ target.host = relative3.host;
3886
+ target.port = relative3.port;
3887
+ target.path = removeDotSegments(relative3.path || "");
3888
+ target.query = relative3.query;
3889
3889
  } else {
3890
- if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
3891
- target.userinfo = relative2.userinfo;
3892
- target.host = relative2.host;
3893
- target.port = relative2.port;
3894
- target.path = removeDotSegments(relative2.path || "");
3895
- target.query = relative2.query;
3890
+ if (relative3.userinfo !== void 0 || relative3.host !== void 0 || relative3.port !== void 0) {
3891
+ target.userinfo = relative3.userinfo;
3892
+ target.host = relative3.host;
3893
+ target.port = relative3.port;
3894
+ target.path = removeDotSegments(relative3.path || "");
3895
+ target.query = relative3.query;
3896
3896
  } else {
3897
- if (!relative2.path) {
3897
+ if (!relative3.path) {
3898
3898
  target.path = base.path;
3899
- if (relative2.query !== void 0) {
3900
- target.query = relative2.query;
3899
+ if (relative3.query !== void 0) {
3900
+ target.query = relative3.query;
3901
3901
  } else {
3902
3902
  target.query = base.query;
3903
3903
  }
3904
3904
  } else {
3905
- if (relative2.path[0] === "/") {
3906
- target.path = removeDotSegments(relative2.path);
3905
+ if (relative3.path[0] === "/") {
3906
+ target.path = removeDotSegments(relative3.path);
3907
3907
  } else {
3908
3908
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3909
- target.path = "/" + relative2.path;
3909
+ target.path = "/" + relative3.path;
3910
3910
  } else if (!base.path) {
3911
- target.path = relative2.path;
3911
+ target.path = relative3.path;
3912
3912
  } else {
3913
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
3913
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative3.path;
3914
3914
  }
3915
3915
  target.path = removeDotSegments(target.path);
3916
3916
  }
3917
- target.query = relative2.query;
3917
+ target.query = relative3.query;
3918
3918
  }
3919
3919
  target.userinfo = base.userinfo;
3920
3920
  target.host = base.host;
@@ -3922,7 +3922,7 @@ var require_fast_uri = __commonJS({
3922
3922
  }
3923
3923
  target.scheme = base.scheme;
3924
3924
  }
3925
- target.fragment = relative2.fragment;
3925
+ target.fragment = relative3.fragment;
3926
3926
  return target;
3927
3927
  }
3928
3928
  function equal(uriA, uriB, options) {
@@ -11411,10 +11411,10 @@ var require_resolve_block_map = __commonJS({
11411
11411
  let offset = bm.offset;
11412
11412
  let commentEnd = null;
11413
11413
  for (const collItem of bm.items) {
11414
- const { start, key, sep: sep2, value } = collItem;
11414
+ const { start, key, sep: sep3, value } = collItem;
11415
11415
  const keyProps = resolveProps.resolveProps(start, {
11416
11416
  indicator: "explicit-key-ind",
11417
- next: key ?? sep2?.[0],
11417
+ next: key ?? sep3?.[0],
11418
11418
  offset,
11419
11419
  onError,
11420
11420
  parentIndent: bm.indent,
@@ -11428,7 +11428,7 @@ var require_resolve_block_map = __commonJS({
11428
11428
  else if ("indent" in key && key.indent !== bm.indent)
11429
11429
  onError(offset, "BAD_INDENT", startColMsg);
11430
11430
  }
11431
- if (!keyProps.anchor && !keyProps.tag && !sep2) {
11431
+ if (!keyProps.anchor && !keyProps.tag && !sep3) {
11432
11432
  commentEnd = keyProps.end;
11433
11433
  if (keyProps.comment) {
11434
11434
  if (map.comment)
@@ -11452,7 +11452,7 @@ var require_resolve_block_map = __commonJS({
11452
11452
  ctx.atKey = false;
11453
11453
  if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
11454
11454
  onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
11455
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
11455
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
11456
11456
  indicator: "map-value-ind",
11457
11457
  next: value,
11458
11458
  offset: keyNode.range[2],
@@ -11468,7 +11468,7 @@ var require_resolve_block_map = __commonJS({
11468
11468
  if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
11469
11469
  onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
11470
11470
  }
11471
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
11471
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep3, null, valueProps, onError);
11472
11472
  if (ctx.schema.compat)
11473
11473
  utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
11474
11474
  offset = valueNode.range[2];
@@ -11559,7 +11559,7 @@ var require_resolve_end = __commonJS({
11559
11559
  let comment = "";
11560
11560
  if (end) {
11561
11561
  let hasSpace = false;
11562
- let sep2 = "";
11562
+ let sep3 = "";
11563
11563
  for (const token of end) {
11564
11564
  const { source, type } = token;
11565
11565
  switch (type) {
@@ -11573,13 +11573,13 @@ var require_resolve_end = __commonJS({
11573
11573
  if (!comment)
11574
11574
  comment = cb;
11575
11575
  else
11576
- comment += sep2 + cb;
11577
- sep2 = "";
11576
+ comment += sep3 + cb;
11577
+ sep3 = "";
11578
11578
  break;
11579
11579
  }
11580
11580
  case "newline":
11581
11581
  if (comment)
11582
- sep2 += source;
11582
+ sep3 += source;
11583
11583
  hasSpace = true;
11584
11584
  break;
11585
11585
  default:
@@ -11622,18 +11622,18 @@ var require_resolve_flow_collection = __commonJS({
11622
11622
  let offset = fc.offset + fc.start.source.length;
11623
11623
  for (let i = 0; i < fc.items.length; ++i) {
11624
11624
  const collItem = fc.items[i];
11625
- const { start, key, sep: sep2, value } = collItem;
11625
+ const { start, key, sep: sep3, value } = collItem;
11626
11626
  const props = resolveProps.resolveProps(start, {
11627
11627
  flow: fcName,
11628
11628
  indicator: "explicit-key-ind",
11629
- next: key ?? sep2?.[0],
11629
+ next: key ?? sep3?.[0],
11630
11630
  offset,
11631
11631
  onError,
11632
11632
  parentIndent: fc.indent,
11633
11633
  startOnNewline: false
11634
11634
  });
11635
11635
  if (!props.found) {
11636
- if (!props.anchor && !props.tag && !sep2 && !value) {
11636
+ if (!props.anchor && !props.tag && !sep3 && !value) {
11637
11637
  if (i === 0 && props.comma)
11638
11638
  onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
11639
11639
  else if (i < fc.items.length - 1)
@@ -11687,8 +11687,8 @@ var require_resolve_flow_collection = __commonJS({
11687
11687
  }
11688
11688
  }
11689
11689
  }
11690
- if (!isMap2 && !sep2 && !props.found) {
11691
- const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
11690
+ if (!isMap2 && !sep3 && !props.found) {
11691
+ const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep3, null, props, onError);
11692
11692
  coll.items.push(valueNode);
11693
11693
  offset = valueNode.range[2];
11694
11694
  if (isBlock(value))
@@ -11700,7 +11700,7 @@ var require_resolve_flow_collection = __commonJS({
11700
11700
  if (isBlock(key))
11701
11701
  onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
11702
11702
  ctx.atKey = false;
11703
- const valueProps = resolveProps.resolveProps(sep2 ?? [], {
11703
+ const valueProps = resolveProps.resolveProps(sep3 ?? [], {
11704
11704
  flow: fcName,
11705
11705
  indicator: "map-value-ind",
11706
11706
  next: value,
@@ -11711,8 +11711,8 @@ var require_resolve_flow_collection = __commonJS({
11711
11711
  });
11712
11712
  if (valueProps.found) {
11713
11713
  if (!isMap2 && !props.found && ctx.options.strict) {
11714
- if (sep2)
11715
- for (const st of sep2) {
11714
+ if (sep3)
11715
+ for (const st of sep3) {
11716
11716
  if (st === valueProps.found)
11717
11717
  break;
11718
11718
  if (st.type === "newline") {
@@ -11729,7 +11729,7 @@ var require_resolve_flow_collection = __commonJS({
11729
11729
  else
11730
11730
  onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
11731
11731
  }
11732
- const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
11732
+ const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep3, null, valueProps, onError) : null;
11733
11733
  if (valueNode) {
11734
11734
  if (isBlock(value))
11735
11735
  onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
@@ -11909,7 +11909,7 @@ var require_resolve_block_scalar = __commonJS({
11909
11909
  chompStart = i + 1;
11910
11910
  }
11911
11911
  let value = "";
11912
- let sep2 = "";
11912
+ let sep3 = "";
11913
11913
  let prevMoreIndented = false;
11914
11914
  for (let i = 0; i < contentStart; ++i)
11915
11915
  value += lines[i][0].slice(trimIndent) + "\n";
@@ -11926,24 +11926,24 @@ var require_resolve_block_scalar = __commonJS({
11926
11926
  indent = "";
11927
11927
  }
11928
11928
  if (type === Scalar.Scalar.BLOCK_LITERAL) {
11929
- value += sep2 + indent.slice(trimIndent) + content;
11930
- sep2 = "\n";
11929
+ value += sep3 + indent.slice(trimIndent) + content;
11930
+ sep3 = "\n";
11931
11931
  } else if (indent.length > trimIndent || content[0] === " ") {
11932
- if (sep2 === " ")
11933
- sep2 = "\n";
11934
- else if (!prevMoreIndented && sep2 === "\n")
11935
- sep2 = "\n\n";
11936
- value += sep2 + indent.slice(trimIndent) + content;
11937
- sep2 = "\n";
11932
+ if (sep3 === " ")
11933
+ sep3 = "\n";
11934
+ else if (!prevMoreIndented && sep3 === "\n")
11935
+ sep3 = "\n\n";
11936
+ value += sep3 + indent.slice(trimIndent) + content;
11937
+ sep3 = "\n";
11938
11938
  prevMoreIndented = true;
11939
11939
  } else if (content === "") {
11940
- if (sep2 === "\n")
11940
+ if (sep3 === "\n")
11941
11941
  value += "\n";
11942
11942
  else
11943
- sep2 = "\n";
11943
+ sep3 = "\n";
11944
11944
  } else {
11945
- value += sep2 + content;
11946
- sep2 = " ";
11945
+ value += sep3 + content;
11946
+ sep3 = " ";
11947
11947
  prevMoreIndented = false;
11948
11948
  }
11949
11949
  }
@@ -12126,25 +12126,25 @@ var require_resolve_flow_scalar = __commonJS({
12126
12126
  trimBoth = /^[ \t]+|[ \t]+$/g;
12127
12127
  }
12128
12128
  let res = match[1].replace(trimEnd, "");
12129
- let sep2 = " ";
12129
+ let sep3 = " ";
12130
12130
  let pos = line.lastIndex;
12131
12131
  while (match = line.exec(source)) {
12132
12132
  const lm = match[1].replace(trimBoth, "");
12133
12133
  if (lm === "") {
12134
- if (sep2 === "\n")
12135
- res += sep2;
12134
+ if (sep3 === "\n")
12135
+ res += sep3;
12136
12136
  else
12137
- sep2 = "\n";
12137
+ sep3 = "\n";
12138
12138
  } else {
12139
- res += sep2 + lm;
12140
- sep2 = " ";
12139
+ res += sep3 + lm;
12140
+ sep3 = " ";
12141
12141
  }
12142
12142
  pos = line.lastIndex;
12143
12143
  }
12144
12144
  const last = /[ \t]*(.*)/sy;
12145
12145
  last.lastIndex = pos;
12146
12146
  match = last.exec(source);
12147
- return res + sep2 + (match?.[1] ?? "");
12147
+ return res + sep3 + (match?.[1] ?? "");
12148
12148
  }
12149
12149
  function doubleQuotedValue(source, onError) {
12150
12150
  let res = "";
@@ -12954,14 +12954,14 @@ var require_cst_stringify = __commonJS({
12954
12954
  }
12955
12955
  }
12956
12956
  }
12957
- function stringifyItem({ start, key, sep: sep2, value }) {
12957
+ function stringifyItem({ start, key, sep: sep3, value }) {
12958
12958
  let res = "";
12959
12959
  for (const st of start)
12960
12960
  res += st.source;
12961
12961
  if (key)
12962
12962
  res += stringifyToken(key);
12963
- if (sep2)
12964
- for (const st of sep2)
12963
+ if (sep3)
12964
+ for (const st of sep3)
12965
12965
  res += st.source;
12966
12966
  if (value)
12967
12967
  res += stringifyToken(value);
@@ -14128,18 +14128,18 @@ var require_parser = __commonJS({
14128
14128
  if (this.type === "map-value-ind") {
14129
14129
  const prev = getPrevProps(this.peek(2));
14130
14130
  const start = getFirstKeyStartProps(prev);
14131
- let sep2;
14131
+ let sep3;
14132
14132
  if (scalar.end) {
14133
- sep2 = scalar.end;
14134
- sep2.push(this.sourceToken);
14133
+ sep3 = scalar.end;
14134
+ sep3.push(this.sourceToken);
14135
14135
  delete scalar.end;
14136
14136
  } else
14137
- sep2 = [this.sourceToken];
14137
+ sep3 = [this.sourceToken];
14138
14138
  const map = {
14139
14139
  type: "block-map",
14140
14140
  offset: scalar.offset,
14141
14141
  indent: scalar.indent,
14142
- items: [{ start, key: scalar, sep: sep2 }]
14142
+ items: [{ start, key: scalar, sep: sep3 }]
14143
14143
  };
14144
14144
  this.onKeyLine = true;
14145
14145
  this.stack[this.stack.length - 1] = map;
@@ -14292,15 +14292,15 @@ var require_parser = __commonJS({
14292
14292
  } else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
14293
14293
  const start2 = getFirstKeyStartProps(it.start);
14294
14294
  const key = it.key;
14295
- const sep2 = it.sep;
14296
- sep2.push(this.sourceToken);
14295
+ const sep3 = it.sep;
14296
+ sep3.push(this.sourceToken);
14297
14297
  delete it.key;
14298
14298
  delete it.sep;
14299
14299
  this.stack.push({
14300
14300
  type: "block-map",
14301
14301
  offset: this.offset,
14302
14302
  indent: this.indent,
14303
- items: [{ start: start2, key, sep: sep2 }]
14303
+ items: [{ start: start2, key, sep: sep3 }]
14304
14304
  });
14305
14305
  } else if (start.length > 0) {
14306
14306
  it.sep = it.sep.concat(start, this.sourceToken);
@@ -14494,13 +14494,13 @@ var require_parser = __commonJS({
14494
14494
  const prev = getPrevProps(parent);
14495
14495
  const start = getFirstKeyStartProps(prev);
14496
14496
  fixFlowSeqItems(fc);
14497
- const sep2 = fc.end.splice(1, fc.end.length);
14498
- sep2.push(this.sourceToken);
14497
+ const sep3 = fc.end.splice(1, fc.end.length);
14498
+ sep3.push(this.sourceToken);
14499
14499
  const map = {
14500
14500
  type: "block-map",
14501
14501
  offset: fc.offset,
14502
14502
  indent: fc.indent,
14503
- items: [{ start, key: fc, sep: sep2 }]
14503
+ items: [{ start, key: fc, sep: sep3 }]
14504
14504
  };
14505
14505
  this.onKeyLine = true;
14506
14506
  this.stack[this.stack.length - 1] = map;
@@ -19384,7 +19384,7 @@ import {
19384
19384
  unlinkSync,
19385
19385
  writeFileSync as writeFileSync4
19386
19386
  } from "node:fs";
19387
- import { basename as basename3, dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
19387
+ import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve4, sep as sep2 } from "node:path";
19388
19388
 
19389
19389
  // plugins/kxm/src/sqlite.ts
19390
19390
  import { createRequire } from "node:module";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.103",
3
+ "version": "0.7.105",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -15,7 +15,7 @@ invent restart or status subcommands.
15
15
  |---|---|---|
16
16
  | `kxm hub view` | Check `/health` and `/ready`; exits 1 when the hub is down | `--json` |
17
17
  | `kxm tenant status` | Hub metadata and Runtime run state as one labeled view; reads only and never starts the supervisor | `--json` |
18
- | `kxm backup` | Verified SQLite backup of the hub store and the Runtime stores with a hashed manifest; exits 1 when the backup is incomplete | `--out <dir>`, `--json` |
18
+ | `kxm backup` | Verified SQLite backup of this project's hub store and Runtime event store with a hashed manifest; exits 1 when the backup is incomplete | `--out <dir>`, `--all-projects`, `--json` |
19
19
 
20
20
  ```bash
21
21
  kxm hub view --json
@@ -30,9 +30,12 @@ runs have separate ID spaces, so a comparison with no shared ID is
30
30
  `unverified`, never agreement.
31
31
 
32
32
  The durable hub store defaults to `.kxm/state/kxm.db` (`KXM_DATA_PATH`). Do not
33
- hand-edit it. `kxm backup` also copies the Runtime registry and every
34
- project's run event store and prompt sidecar under the user state root, so a
35
- `kxm restore` rolls back every project on the machine, not only this one.
33
+ hand-edit it. `kxm backup` also copies this project's Runtime event store and
34
+ prompt sidecar from the user state root. The shared Runtime registry and other
35
+ projects' event stores are copied only with `--all-projects`, and only
36
+ `kxm restore --all-projects` restores them, rolling back every project on the
37
+ machine. Do not pass `--all-projects` unless the user asked for a whole-machine
38
+ backup or restore.
36
39
 
37
40
  ## Operator steps
38
41
 
@@ -45,7 +48,7 @@ the hub admin token or a project token.
45
48
  | `kxm hub stop` | Request managed hub and worker shutdown | `--wait-ms <ms>` |
46
49
  | `kxm hub bind <url>` | Bind this machine to a running hub | http or https URL |
47
50
  | `kxm hub unbind` | Remove this machine's hub binding | `--json` |
48
- | `kxm restore <manifest>` | Replace the live stores from a verified backup manifest | `--json` |
51
+ | `kxm restore <manifest>` | Replace this project's live stores from a verified backup manifest | `--all-projects`, `--json` |
49
52
 
50
53
  ```bash
51
54
  kxm hub start
@@ -70,5 +73,9 @@ kxm restore .kxm/backups/pre-upgrade/manifest.json
70
73
  claim is reclaimed automatically, an orphaned server is terminated first,
71
74
  and `kxm hub stop` recovers such orphans directly.
72
75
  - `kxm restore` replaces the live stores. Preview it with
73
- `kxm restore <manifest> --dry-run`, stop the hub first, and never skip
74
- restore verification.
76
+ `kxm restore <manifest> --dry-run`, and never skip restore verification. It
77
+ refuses before writing while the Runtime supervisor runs
78
+ (`restore_runtime_running`) or a hub holds the hub store
79
+ (`restore_hub_running`), so stop both first (`kxm runtime stop`,
80
+ `kxm hub stop`). A backup that holds the registry or another project's
81
+ event store is refused with `restore_requires_all_projects`.