@kontextmind/kxm 0.7.174 → 0.7.176
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +13 -0
- package/docs/contributing/decisions.md +22 -1
- package/docs/contributing/test-matrix.md +1 -1
- package/docs/operations/upgrade.md +5 -5
- package/docs/reference/cli-reference.md +8 -7
- package/docs/reference/config-reference.md +2 -2
- package/docs/start/install.md +3 -3
- package/package.json +6 -6
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +128 -52
- package/plugins/kxm/dist/extension.js +74 -11
- package/plugins/kxm/dist/mcp-server.js +33 -10
- package/plugins/kxm/dist/runtime.js +44 -27
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +8 -3
- package/plugins/kxm/src/cli/system.ts +74 -20
- package/plugins/kxm/src/cli.ts +4 -2
- package/plugins/kxm/src/harness.ts +50 -29
- package/plugins/kxm/src/kxm-install-kind.ts +6 -0
- package/plugins/kxm/src/kxm-update-config.ts +2 -2
- package/plugins/kxm/src/kxm-update.ts +11 -4
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/scripts/docker-install-smoke.mjs +2 -1
- package/scripts/npm-pack-json.d.mts +4 -0
- package/scripts/npm-pack-json.mjs +15 -0
- package/scripts/pr-land.mjs +45 -3
package/CHANGELOG.md
CHANGED
|
@@ -181,6 +181,19 @@ All notable user-facing changes are documented here. The project follows [Semant
|
|
|
181
181
|
|
|
182
182
|
### Changed
|
|
183
183
|
|
|
184
|
+
- **`kxm update --kxm` defaults to npm.** Missing or source-less `update.yaml` now
|
|
185
|
+
uses the public `@kontextmind/kxm` package instead of the GitHub release tarball.
|
|
186
|
+
`source: github` still selects the digest-checked tarball. When the working
|
|
187
|
+
directory is this repository, `--kxm` installs the local files and does not fetch.
|
|
188
|
+
- **`kxm update` is a full update by default.** Bare `kxm update` and `--all` run
|
|
189
|
+
detected harness `--self` first (default harness leading), then the kxm package,
|
|
190
|
+
then extensions and models. `--kxm` remains kxm-only. Detected harness updater
|
|
191
|
+
failures on `--all` degrade the receipt and do not fail the command; an unknown
|
|
192
|
+
harness id still exits 2.
|
|
193
|
+
- **`kxm land` runs `npm` on Windows.** The verify stage launches `npm.cmd` through `cmd.exe` when `npm` is not an `.exe`.
|
|
194
|
+
- **npm 12 `pack --json`.** The install smoke and pack tests accept the package-keyed object as well as the older one-element array.
|
|
195
|
+
- **Roadmap links on Windows.** `kxm land` keeps the git symlink text when this process cannot create the `docs/roadmap` links.
|
|
196
|
+
|
|
184
197
|
- **Tool policy is enforced at load and before dispatch.**
|
|
185
198
|
`coordinator` and `read-only` select the read-only one-shot profile.
|
|
186
199
|
`workspace-writer` and `tests-writer` select the edit profile. Dispatch
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
title: "Decisions"
|
|
3
3
|
description: "Decisions of medium significance or higher made by automated KXM work in this repository. One entry per decision: the options evaluated with their weights, and why the selected option won. Recorded the turn the decision is taken."
|
|
4
4
|
audience: "agents and maintainers"
|
|
5
|
-
updated: "2026-
|
|
5
|
+
updated: "2026-10-02"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Decisions
|
|
@@ -151,3 +151,24 @@ Evidence: Evidence: test/core/oneshot-producer.test.ts (real reviewer shapes), r
|
|
|
151
151
|
- Selected: port through the engine lane `vision-assert-port`; the PR is
|
|
152
152
|
closed by its replacement landing, not by hand.
|
|
153
153
|
Evidence: Evidence: merge-base 77beda4 predates the CLI restructure; merge-tree showed cli/vnext.ts deleted on main; vision-gate.ts already on main.
|
|
154
|
+
|
|
155
|
+
## 2026-10-02 — Porting the stale update-default branch onto main (landing)
|
|
156
|
+
|
|
157
|
+
- Context: `feat/update-npm-default-and-all` was based on 2026-09-24 main
|
|
158
|
+
and still uncommitted. `origin/main` had moved about 66 commits and
|
|
159
|
+
already contained the claude-bridge `apiKey` registration fix and the
|
|
160
|
+
`gh`/`curl` release downloader. `kxm land`'s rebase stage only resolves
|
|
161
|
+
union conflicts, which cannot merge the `planKxmPackageUpdate` signature
|
|
162
|
+
clash (`downloader` vs `localRoot`).
|
|
163
|
+
- Options:
|
|
164
|
+
- Rebase the 2026-09-24 branch through `kxm land`'s rebase stage — weight
|
|
165
|
+
0.2. Its union-only resolver cannot handle the `planKxmPackageUpdate`
|
|
166
|
+
signature clash.
|
|
167
|
+
- Stash-apply onto a fresh lane, take upstream for claude-bridge, `dist`,
|
|
168
|
+
and `package.json`, and merge the rest — weight 0.9. Keeps the reviewed
|
|
169
|
+
update-default work and the upstream fixes that must not be duplicated.
|
|
170
|
+
- Discard and re-dispatch to the writer — weight 0.3. Re-authors reviewed
|
|
171
|
+
work that already had a specified merge.
|
|
172
|
+
- Selected: stash-apply onto a fresh lane off `origin/main`, take upstream
|
|
173
|
+
for claude-bridge, `dist`, and `package.json`, and merge the rest.
|
|
174
|
+
Evidence: Evidence: `git diff HEAD origin/main` on the stale branch showed upstream's claude-bridge fix and curl downloader already landed; the lane port keeps both those and the npm/`--all` behavior.
|
|
@@ -145,7 +145,7 @@ The context suites in detail:
|
|
|
145
145
|
| Permission-diff trust: authority lattice, prose neutrality, Git base shadowing, CLI diff and check | `permission.test.ts`, `cli.test.ts`, `contracts.test.ts`, `package-install.test.ts` |
|
|
146
146
|
| KXM schemas, restricted YAML fixtures, cross-resource semantics and sync-safe rejection | `contracts.test.ts`, `restricted-yaml.test.ts` |
|
|
147
147
|
| Harness detection, auth and dispatch for the built-in catalog, including Windows launch rules | `harness.test.ts` |
|
|
148
|
-
| `kxm update`: `update.yaml` validation, GitHub
|
|
148
|
+
| `kxm update`: `update.yaml` validation, npm default / GitHub override, local checkout install, install-kind detection, `kxm-<v>.tgz` asset selection | `kxm-update.test.ts`, `kxm-update-cli.test.ts`, `kxm-install-kind.test.ts` |
|
|
149
149
|
|
|
150
150
|
The round trip seeds its Runtime stores in a project-local `.kxm/runtime/`
|
|
151
151
|
layout that the Runtime never writes. Separate tests seed two projects' event
|
|
@@ -6,7 +6,7 @@ Move the `kxm` CLI, the hub, the Runtime, and the Claude Code or Pi integrations
|
|
|
6
6
|
|
|
7
7
|
- Protected storage for a full backup; see [Back up and restore KXM](backup-and-restore.md).
|
|
8
8
|
- A window in which the hub, the Runtime supervisor and long-lived workers may stop.
|
|
9
|
-
- The GitHub CLI (`gh`, signed in)
|
|
9
|
+
- The GitHub CLI (`gh`, signed in), only when `update.yaml` sets `source: github`. When `gh` is not installed, that command downloads the same `kxm-<version>.tgz` with curl and still checks the release sha256 before installing. Without a working `kxm` either, use the curl commands in [Update the CLI without gh](#update-the-cli-without-gh).
|
|
10
10
|
|
|
11
11
|
## Check for an update
|
|
12
12
|
|
|
@@ -17,7 +17,7 @@ kxm update --check
|
|
|
17
17
|
Expected output when a newer release exists:
|
|
18
18
|
|
|
19
19
|
```text
|
|
20
|
-
kxm 0.7.1 → 0.7.2 available · kxm update
|
|
20
|
+
kxm 0.7.1 → 0.7.2 available · kxm update
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
From a source checkout, the check makes no network call and reports the checkout instead:
|
|
@@ -26,7 +26,7 @@ From a source checkout, the check makes no network call and reports the checkout
|
|
|
26
26
|
kxm 0.7.1 (running from source at /work/kxm)
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
The check reads `update.yaml` under the user state root. Its `source` is `github` (release tarballs)
|
|
29
|
+
The check reads `update.yaml` under the user state root. Its `source` is `npm` by default; `github` (release tarballs) is also accepted. When the working directory is this repository, `kxm update --kxm` installs the local files instead of fetching. See [Updater settings](../reference/config-reference.md#updater-settings-kxmupdatev1). Add `--json` to see the install kind KXM detected.
|
|
30
30
|
|
|
31
31
|
## Stop the services and back up
|
|
32
32
|
|
|
@@ -48,7 +48,7 @@ Use the path that matches how KXM was installed. `kxm update --kxm` applies an u
|
|
|
48
48
|
|
|
49
49
|
| Install kind | Upgrade with |
|
|
50
50
|
|---|---|
|
|
51
|
-
| Global npm install | `kxm update --kxm` (
|
|
51
|
+
| Global npm install | `kxm update --kxm` (npm registry, or GitHub tarball with `source: github`), or `npm install --global --omit=peer @kontextmind/kxm@latest` |
|
|
52
52
|
| Pi package | `pi update` |
|
|
53
53
|
| Claude Code marketplace plugin | Reinstall the plugin; see the plugin note below |
|
|
54
54
|
| Project dependency | `npm install @kontextmind/kxm@latest` in that project |
|
|
@@ -77,7 +77,7 @@ Refuse a tarball whose sha256 does not match the release asset digest.
|
|
|
77
77
|
|
|
78
78
|
### Update harnesses and plugins
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Bare `kxm update` (same as `--all`) runs detected harness `--self` updaters first, then the kxm package, then extensions and models; `--kxm` alone updates only kxm. Narrow it with a harness id and one scope:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
83
|
kxm update --dry-run # plan every detected harness
|
|
@@ -971,7 +971,7 @@ Not run: stores a credential.
|
|
|
971
971
|
## `kxm update`
|
|
972
972
|
|
|
973
973
|
```text
|
|
974
|
-
kxm update [harness] [--check | --kxm] [--self | --extensions | --models]
|
|
974
|
+
kxm update [harness] [--check | --kxm] [--all | --self | --extensions | --models]
|
|
975
975
|
```
|
|
976
976
|
|
|
977
977
|
Checks for or applies a KXM operator package update, and runs the native updaters of detected harnesses.
|
|
@@ -979,14 +979,15 @@ Checks for or applies a KXM operator package update, and runs the native updater
|
|
|
979
979
|
| Option | Argument | Default | Description |
|
|
980
980
|
|---|---|---|---|
|
|
981
981
|
| `--check` | none | off | Check for a kxm package update without applying |
|
|
982
|
-
| `--
|
|
983
|
-
| `--
|
|
982
|
+
| `--all` | none | off | Update harness CLIs first (`--self`), then kxm, extensions, and models (default for a bare `kxm update`) |
|
|
983
|
+
| `--kxm` | none | off | Apply only the kxm operator package update (npm by default, GitHub tarball when configured, or local files in this repo) |
|
|
984
|
+
| `--self` | none | off | Update only harness CLIs |
|
|
984
985
|
| `--extensions` | none | off | Update only extensions/plugins (Pi packages, Claude kxm) |
|
|
985
986
|
| `--models` | none | off | Refresh model catalogs where the harness supports it |
|
|
986
987
|
|
|
987
988
|
- Arguments: `[harness]`, Harness id (default: every detected harness).
|
|
988
|
-
- `--check` cannot be combined with any other update flag or a harness (exit 2, `scope_conflict`), and at most one of `--self`, `--extensions`, `--models` is allowed (exit 2).
|
|
989
|
-
- From a source
|
|
989
|
+
- `--check` cannot be combined with any other update flag or a harness (exit 2, `scope_conflict`), and at most one of `--all`, `--self`, `--extensions`, `--models` is allowed (exit 2).
|
|
990
|
+
- Default `source` is npm. Set `update.yaml` `source: github` for the release tarball. When the working directory is this repository (`@kontextmind/kxm` plus `.git`), `--check` reports `source: local` without a network fetch and `--kxm` runs `npm install --global --omit=peer <cwd>`. From a source-built binary used outside this repo, `--check` reports the running version without network access, and `--kxm` is refused (exit 2, `install_kind_source`) with an instruction to `git pull`. Only npm-global installs can apply a registry or GitHub `--kxm`; other install kinds exit 2 with `install_kind_<kind>`. A GitHub release must publish a sha256 digest for `kxm-<version>.tgz` or the install fails closed (`release_digest_missing`, `release_digest_mismatch`). When `gh` is missing, the github source downloads that same asset with `curl --fail --silent --show-error --location` and still verifies the digest before `npm install -g`.
|
|
990
991
|
|
|
991
992
|
On a host with neither `gh` nor a working `kxm`, download and check it yourself:
|
|
992
993
|
|
|
@@ -1001,7 +1002,7 @@ npm install --global --omit=peer "./kxm-${version}.tgz"
|
|
|
1001
1002
|
```
|
|
1002
1003
|
|
|
1003
1004
|
- Settings come only from `update.yaml` under the user state root (`auto: true` enables auto-apply); a project `.kxm/update.yaml` is ignored with a warning.
|
|
1004
|
-
- Without `--check` or a lone `--kxm`, KXM probes harnesses (as `harness list` does) and runs each updater for the selected scope. An unknown harness id exits 2 (`unknown_harness` step); a failed step exits 1.
|
|
1005
|
+
- Without `--check` or a lone `--kxm`, KXM probes harnesses (as `harness list` does) and runs each updater for the selected scope. An unknown harness id exits 2 (`unknown_harness` step); a failed step exits 1, except under `--all` (the default), where a failed harness updater sets `degraded: true` and exits 0.
|
|
1005
1006
|
- Honors `--dry-run`: steps are planned, not run, and the cached update notice is not refreshed.
|
|
1006
1007
|
- JSON keys: `--check` gives `current`, `available`, `auto`, `source`, `latest`, `message`, `installKind`, `root`; otherwise `dryRun`, `scope`, `notice`, `kxm` (when applying), `steps` (`harness`, `scope`, `command`, `args`, `outcome`, `detail`), `installKind`, `root`.
|
|
1007
1008
|
|
|
@@ -1010,7 +1011,7 @@ kxm update --check --dry-run --json
|
|
|
1010
1011
|
```
|
|
1011
1012
|
|
|
1012
1013
|
```text
|
|
1013
|
-
{"schema":"kxm.cli-result.v1","ok":true,"command":"update check","current":"0.7.1","available":false,"auto":false,"source":"
|
|
1014
|
+
{"schema":"kxm.cli-result.v1","ok":true,"command":"update check","current":"0.7.1","available":false,"auto":false,"source":"npm","installKind":"source","root":"/work/kxm","message":"kxm 0.7.1 (running from source at /work/kxm)"}
|
|
1014
1015
|
```
|
|
1015
1016
|
|
|
1016
1017
|
```bash
|
|
@@ -1656,14 +1656,14 @@ never mints a token or writes a file, and always exits 0. See the
|
|
|
1656
1656
|
|---|---|---|
|
|
1657
1657
|
| `schema` | `kxm.update.v1` | Required |
|
|
1658
1658
|
| `auto` | Boolean | Required |
|
|
1659
|
-
| `source` | `github` or `npm` | Optional, `
|
|
1659
|
+
| `source` | `github` or `npm` | Optional, `npm` |
|
|
1660
1660
|
|
|
1661
1661
|
Unknown fields are refused (`update.yaml unknown field <name>`).
|
|
1662
1662
|
|
|
1663
1663
|
```yaml
|
|
1664
1664
|
schema: kxm.update.v1
|
|
1665
1665
|
auto: false # required boolean
|
|
1666
|
-
source:
|
|
1666
|
+
source: npm # npm (default) | github
|
|
1667
1667
|
```
|
|
1668
1668
|
|
|
1669
1669
|
## Workspace layout: tracked, ignored, and state
|
package/docs/start/install.md
CHANGED
|
@@ -8,7 +8,7 @@ KXM has three parts you can install: the `kxm` command-line tool, the Claude Cod
|
|
|
8
8
|
- Git.
|
|
9
9
|
- Claude Code, if you want the plugin.
|
|
10
10
|
- Pi, if you want Pi agents. Install it with `npm install --global @earendil-works/pi-coding-agent` and sign in to a model provider as the [Pi documentation](https://pi.dev/docs/latest) describes.
|
|
11
|
-
- The GitHub CLI (`gh`), only if
|
|
11
|
+
- The GitHub CLI (`gh`), only if `update.yaml` sets `source: github` so `kxm update --kxm` downloads the release tarball. When `gh` is missing, that command falls back to curl and still checks the release checksum. The manual curl commands are in [Upgrade KXM](../operations/upgrade.md#update-the-cli-without-gh).
|
|
12
12
|
|
|
13
13
|
## Choose what to install
|
|
14
14
|
|
|
@@ -107,13 +107,13 @@ npm ci
|
|
|
107
107
|
node scripts/kxm.mjs --version
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Use `node scripts/kxm.mjs` wherever the docs show `kxm`. It runs the CLI bundle committed in `plugins/kxm/dist/`. Update a checkout with `git pull` and `npm ci
|
|
110
|
+
Use `node scripts/kxm.mjs` wherever the docs show `kxm`. It runs the CLI bundle committed in `plugins/kxm/dist/`. Update a checkout with `git pull` and `npm ci`. From this repository, `kxm update --kxm` installs those local files globally. [Develop KXM](../contributing/development.md) covers building and testing.
|
|
111
111
|
|
|
112
112
|
## Update
|
|
113
113
|
|
|
114
114
|
| Part | Command |
|
|
115
115
|
|---|---|
|
|
116
|
-
| `kxm` CLI | `npm install --global --omit=peer @kontextmind/kxm@latest` (check first with `kxm update --check`) |
|
|
116
|
+
| `kxm` CLI | `kxm update --kxm` or `npm install --global --omit=peer @kontextmind/kxm@latest` (check first with `kxm update --check`) |
|
|
117
117
|
| Claude Code plugin | `claude plugin marketplace update kxm`, then uninstall and reinstall the plugin; `claude plugin update` never refreshes it |
|
|
118
118
|
| Pi package | `pi update --extensions` |
|
|
119
119
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kontextmind/kxm",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.176",
|
|
4
4
|
"description": "KXM local-first multi-agent orchestration and operator dashboard",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "KontextMind",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"test:core": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts",
|
|
38
38
|
"test:simulations": "npm run build && node scripts/run-bounded.mjs 1200000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/simulations/*.test.ts",
|
|
39
39
|
"test:complete": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts test/simulations/*.test.ts",
|
|
40
|
-
"test": "npm run build && node scripts/run-bounded.mjs
|
|
40
|
+
"test": "npm run build && node scripts/run-bounded.mjs 1800000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
|
|
41
41
|
"test:ci-shard": "node scripts/ci-unit-shard.mjs",
|
|
42
42
|
"e2e": "node scripts/obscura.mjs --ensure && playwright test",
|
|
43
43
|
"test:coverage:core": "npm run build && node scripts/run-bounded.mjs 2400000 node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit --test-concurrency=4 --experimental-test-coverage --test-coverage-lines=91 --test-coverage-branches=80 --test-coverage-functions=92 --test-coverage-include=plugins/kxm/src/**/*.ts --test-coverage-exclude=plugins/kxm/src/server.ts --test-coverage-exclude=plugins/kxm/src/mcp-server.ts --test-coverage-exclude=plugins/kxm/src/runtime-supervisor.ts --test-coverage-include=packages/core/*/src/**/*.ts test/core/*.test.ts packages/core/*/tests/unit/*.test.ts",
|
|
@@ -77,9 +77,9 @@
|
|
|
77
77
|
]
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
|
-
"@anthropic-ai/claude-agent-sdk": "0.3.
|
|
80
|
+
"@anthropic-ai/claude-agent-sdk": "0.3.289",
|
|
81
81
|
"@earendil-works/pi-tui": "0.87.1",
|
|
82
|
-
"@modelcontextprotocol/sdk": "1.
|
|
82
|
+
"@modelcontextprotocol/sdk": "1.32.0",
|
|
83
83
|
"yaml": "^2.9.0"
|
|
84
84
|
},
|
|
85
85
|
"peerDependencies": {
|
|
@@ -96,9 +96,9 @@
|
|
|
96
96
|
},
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@earendil-works/pi-coding-agent": "0.87.1",
|
|
99
|
-
"@modelcontextprotocol/sdk": "1.
|
|
99
|
+
"@modelcontextprotocol/sdk": "1.32.0",
|
|
100
100
|
"@playwright/test": "1.63.0",
|
|
101
|
-
"@types/node": "26.6.
|
|
101
|
+
"@types/node": "26.6.4",
|
|
102
102
|
"ajv": "8.20.0",
|
|
103
103
|
"commander": "15.0.0",
|
|
104
104
|
"esbuild": "0.28.2",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.176",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -19780,8 +19780,17 @@ function argsFor(entry, scope) {
|
|
|
19780
19780
|
if (scope === "extensions") return entry.update.extensions;
|
|
19781
19781
|
return entry.update.models;
|
|
19782
19782
|
}
|
|
19783
|
+
function detectedHarnesses(inventory, requestedHarness) {
|
|
19784
|
+
if (requestedHarness) return inventory.harnesses.filter((entry) => entry.id === requestedHarness);
|
|
19785
|
+
const detected = inventory.harnesses.filter((entry) => entry.detected);
|
|
19786
|
+
const preferred = inventory.defaultHarness;
|
|
19787
|
+
return [
|
|
19788
|
+
...detected.filter((entry) => entry.id === preferred),
|
|
19789
|
+
...detected.filter((entry) => entry.id !== preferred)
|
|
19790
|
+
];
|
|
19791
|
+
}
|
|
19783
19792
|
function planHarnessUpdate(inventory, requested) {
|
|
19784
|
-
const selected =
|
|
19793
|
+
const selected = detectedHarnesses(inventory, requested.harness);
|
|
19785
19794
|
if (requested.harness && selected.length === 0) {
|
|
19786
19795
|
return [{
|
|
19787
19796
|
harness: requested.harness,
|
|
@@ -19792,36 +19801,44 @@ function planHarnessUpdate(inventory, requested) {
|
|
|
19792
19801
|
detail: isKnownHarnessId(requested.harness) ? "not_detected" : "unknown_harness"
|
|
19793
19802
|
}];
|
|
19794
19803
|
}
|
|
19804
|
+
const waves = requested.scope === "all" ? ["self", "extensions", "models"] : [requested.scope];
|
|
19795
19805
|
const steps = [];
|
|
19796
|
-
for (const
|
|
19797
|
-
const
|
|
19798
|
-
|
|
19799
|
-
|
|
19800
|
-
|
|
19801
|
-
|
|
19802
|
-
|
|
19803
|
-
|
|
19804
|
-
|
|
19805
|
-
|
|
19806
|
-
|
|
19807
|
-
|
|
19808
|
-
|
|
19809
|
-
|
|
19810
|
-
|
|
19811
|
-
|
|
19806
|
+
for (const scope of waves) {
|
|
19807
|
+
for (const status of selected) {
|
|
19808
|
+
const entry = BUILTIN_HARNESSES.find((candidate) => candidate.id === status.id);
|
|
19809
|
+
if (!entry) continue;
|
|
19810
|
+
const supported = scopesFor(requested.scope === "all" ? "all" : scope, entry);
|
|
19811
|
+
if (!supported.includes(scope)) {
|
|
19812
|
+
if (requested.scope !== "all") {
|
|
19813
|
+
steps.push({
|
|
19814
|
+
harness: status.id,
|
|
19815
|
+
scope,
|
|
19816
|
+
command: status.command ?? entry.commands[0],
|
|
19817
|
+
args: [],
|
|
19818
|
+
outcome: "skipped",
|
|
19819
|
+
detail: "no_updater"
|
|
19820
|
+
});
|
|
19821
|
+
}
|
|
19822
|
+
continue;
|
|
19823
|
+
}
|
|
19824
|
+
if (!status.detected || !status.command) {
|
|
19825
|
+
steps.push({
|
|
19826
|
+
harness: status.id,
|
|
19827
|
+
scope,
|
|
19828
|
+
command: entry.commands[0],
|
|
19829
|
+
args: argsFor(entry, scope) ?? [],
|
|
19830
|
+
outcome: "skipped",
|
|
19831
|
+
detail: "not_detected"
|
|
19832
|
+
});
|
|
19833
|
+
continue;
|
|
19834
|
+
}
|
|
19812
19835
|
steps.push({
|
|
19813
19836
|
harness: status.id,
|
|
19814
|
-
scope
|
|
19815
|
-
command:
|
|
19816
|
-
args: argsFor(entry,
|
|
19817
|
-
outcome: "
|
|
19818
|
-
detail: "not_detected"
|
|
19837
|
+
scope,
|
|
19838
|
+
command: status.command,
|
|
19839
|
+
args: argsFor(entry, scope) ?? [],
|
|
19840
|
+
outcome: "would"
|
|
19819
19841
|
});
|
|
19820
|
-
continue;
|
|
19821
|
-
}
|
|
19822
|
-
for (const scope of scopes) {
|
|
19823
|
-
const args = argsFor(entry, scope) ?? [];
|
|
19824
|
-
steps.push({ harness: status.id, scope, command: status.command, args, outcome: "would" });
|
|
19825
19842
|
}
|
|
19826
19843
|
}
|
|
19827
19844
|
return steps;
|
|
@@ -35526,7 +35543,7 @@ function compareSemver(left, right) {
|
|
|
35526
35543
|
}
|
|
35527
35544
|
function formatKxmUpdateNotice(notice) {
|
|
35528
35545
|
if (!notice.available) return notice.message;
|
|
35529
|
-
const apply = notice.auto ? "kxm update
|
|
35546
|
+
const apply = notice.auto ? "kxm update (auto)" : "kxm update";
|
|
35530
35547
|
return `kxm ${notice.current} \u2192 ${notice.latest} available \xB7 ${apply}`;
|
|
35531
35548
|
}
|
|
35532
35549
|
function withAsset(notice, asset) {
|
|
@@ -35644,7 +35661,11 @@ function verifyReleaseAssetDigest(path4, sha256) {
|
|
|
35644
35661
|
return false;
|
|
35645
35662
|
}
|
|
35646
35663
|
}
|
|
35647
|
-
function planKxmPackageUpdate(source, latest, releaseDir, asset, downloader = "gh") {
|
|
35664
|
+
function planKxmPackageUpdate(source, latest, releaseDir, asset, downloader = "gh", localRoot) {
|
|
35665
|
+
if (source === "local") {
|
|
35666
|
+
if (!localRoot) return [];
|
|
35667
|
+
return [{ kind: "install", command: "npm", args: ["install", "--global", "--omit=peer", localRoot] }];
|
|
35668
|
+
}
|
|
35648
35669
|
if (source === "npm") {
|
|
35649
35670
|
return [{ kind: "install", command: "npm", args: ["install", "--global", "--omit=peer", `@kontextmind/kxm@${latest}`] }];
|
|
35650
35671
|
}
|
|
@@ -51787,6 +51808,11 @@ init_hub_binding();
|
|
|
51787
51808
|
init_bindings();
|
|
51788
51809
|
import { existsSync as existsSync38, readFileSync as readFileSync34 } from "node:fs";
|
|
51789
51810
|
import { dirname as dirname21, join as join48 } from "node:path";
|
|
51811
|
+
function kxmSourceCheckoutRoot(dir) {
|
|
51812
|
+
if (!existsSync38(join48(dir, ".git"))) return void 0;
|
|
51813
|
+
if (!packageIsKxm(dir)) return void 0;
|
|
51814
|
+
return dir;
|
|
51815
|
+
}
|
|
51790
51816
|
function packageIsKxm(root) {
|
|
51791
51817
|
try {
|
|
51792
51818
|
const pkg = JSON.parse(readFileSync34(join48(root, "package.json"), "utf8"));
|
|
@@ -51872,7 +51898,7 @@ import { join as join49 } from "node:path";
|
|
|
51872
51898
|
init_bindings();
|
|
51873
51899
|
function loadKxmUpdateConfig(env = process.env) {
|
|
51874
51900
|
const path4 = join49(kxmUserStateRoot({ env }), "update.yaml");
|
|
51875
|
-
if (!existsSync39(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "
|
|
51901
|
+
if (!existsSync39(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "npm" };
|
|
51876
51902
|
let parsed;
|
|
51877
51903
|
try {
|
|
51878
51904
|
parsed = (0, import_yaml19.parse)(readFileSync35(path4, "utf8"));
|
|
@@ -51893,7 +51919,7 @@ function loadKxmUpdateConfig(env = process.env) {
|
|
|
51893
51919
|
if (typeof row.auto !== "boolean") {
|
|
51894
51920
|
throw new KxmUpdateConfigError("update.yaml auto must be a boolean");
|
|
51895
51921
|
}
|
|
51896
|
-
const source = row.source === void 0 ? "
|
|
51922
|
+
const source = row.source === void 0 ? "npm" : row.source;
|
|
51897
51923
|
if (source !== "npm" && source !== "github") {
|
|
51898
51924
|
throw new KxmUpdateConfigError("update.yaml source must be npm or github");
|
|
51899
51925
|
}
|
|
@@ -55090,7 +55116,7 @@ function applyKxmPackageUpdate(runtime, notice) {
|
|
|
55090
55116
|
const releaseDir = mkdtempSync3(join57(tmpdir3(), "kxm-pkg-update-"));
|
|
55091
55117
|
try {
|
|
55092
55118
|
const downloader = notice.source === "github" && !ghReleaseDownloadAvailable(runtime.env) ? "curl" : "gh";
|
|
55093
|
-
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset, downloader);
|
|
55119
|
+
const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset, downloader, notice.localRoot);
|
|
55094
55120
|
if (runtime.dryRun) {
|
|
55095
55121
|
return { ok: true, detail: planned.map(formatPackageUpdateStep).join(" && ") };
|
|
55096
55122
|
}
|
|
@@ -55216,22 +55242,34 @@ async function cmdSshClose(runtime, host) {
|
|
|
55216
55242
|
return 0;
|
|
55217
55243
|
}
|
|
55218
55244
|
async function cmdUpdate(runtime, harness, options) {
|
|
55219
|
-
if (options.check && (options.kxm || options.self || options.extensions || options.models || harness)) {
|
|
55245
|
+
if (options.check && (options.kxm || options.self || options.extensions || options.models || options.all || harness)) {
|
|
55220
55246
|
print(runtime.io, runtime.json, { ok: false, command: "update", error: "scope_conflict" }, "--check cannot be combined with other update flags");
|
|
55221
55247
|
return 2;
|
|
55222
55248
|
}
|
|
55223
|
-
const selected = [options.self, options.extensions, options.models].filter(Boolean).length;
|
|
55249
|
+
const selected = [options.self, options.extensions, options.models, options.all].filter(Boolean).length;
|
|
55224
55250
|
if (selected > 1) {
|
|
55225
|
-
print(runtime.io, runtime.json, { ok: false, command: "update", error: "scope_conflict" }, "specify at most one of --self, --extensions, or --models");
|
|
55251
|
+
print(runtime.io, runtime.json, { ok: false, command: "update", error: "scope_conflict" }, "specify at most one of --all, --self, --extensions, or --models");
|
|
55226
55252
|
return 2;
|
|
55227
55253
|
}
|
|
55228
55254
|
warnIgnoredProjectUpdateYaml(runtime);
|
|
55229
55255
|
const probe = installProbeFrom(runtime);
|
|
55230
55256
|
const classified = classifyInstallRoot(probe);
|
|
55231
55257
|
const current = installedKxmVersion(classified.root) ?? installedKxmVersion(findKxmRepoRoot(import.meta.url));
|
|
55258
|
+
const localRoot = kxmSourceCheckoutRoot(runtime.dirs.workdir) ?? kxmSourceCheckoutRoot(runtime.cwd);
|
|
55232
55259
|
let notice;
|
|
55233
55260
|
let kindReport = classified;
|
|
55234
|
-
if (
|
|
55261
|
+
if (localRoot) {
|
|
55262
|
+
const localVersion = installedKxmVersion(localRoot) ?? current ?? "unknown";
|
|
55263
|
+
notice = {
|
|
55264
|
+
current: current ?? localVersion,
|
|
55265
|
+
latest: localVersion,
|
|
55266
|
+
available: true,
|
|
55267
|
+
auto: false,
|
|
55268
|
+
source: "local",
|
|
55269
|
+
message: `kxm ${current ?? localVersion} (local ${localRoot})`,
|
|
55270
|
+
localRoot
|
|
55271
|
+
};
|
|
55272
|
+
} else if (classified.kind === "source") {
|
|
55235
55273
|
if (options.check) {
|
|
55236
55274
|
const message = `kxm ${current ?? "unknown"} (running from source at ${classified.root})`;
|
|
55237
55275
|
print(runtime.io, runtime.json, {
|
|
@@ -55240,7 +55278,7 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55240
55278
|
current: current ?? "unknown",
|
|
55241
55279
|
available: false,
|
|
55242
55280
|
auto: false,
|
|
55243
|
-
source: "
|
|
55281
|
+
source: "npm",
|
|
55244
55282
|
installKind: "source",
|
|
55245
55283
|
root: classified.root,
|
|
55246
55284
|
message
|
|
@@ -55262,7 +55300,7 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55262
55300
|
current: current ?? "unknown",
|
|
55263
55301
|
available: false,
|
|
55264
55302
|
auto: false,
|
|
55265
|
-
source: "
|
|
55303
|
+
source: "npm",
|
|
55266
55304
|
message: `kxm ${current ?? "unknown"} (running from source)`
|
|
55267
55305
|
};
|
|
55268
55306
|
} else {
|
|
@@ -55285,13 +55323,30 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55285
55323
|
}, notice.message);
|
|
55286
55324
|
return 0;
|
|
55287
55325
|
}
|
|
55288
|
-
const
|
|
55326
|
+
const fullUpdate = Boolean(options.all) || selected === 0 && !options.kxm && !harness;
|
|
55327
|
+
const applyKxm = Boolean(options.kxm || fullUpdate || notice.auto);
|
|
55289
55328
|
let kxmApply;
|
|
55290
|
-
|
|
55329
|
+
const applyKxmPackage = (requireSuccess) => {
|
|
55330
|
+
if (!applyKxm) return void 0;
|
|
55331
|
+
if (notice.source === "local" && notice.localRoot) {
|
|
55332
|
+
kxmApply = applyKxmPackageUpdate(runtime, notice);
|
|
55333
|
+
if (!kxmApply.ok && requireSuccess) {
|
|
55334
|
+
print(runtime.io, runtime.json, {
|
|
55335
|
+
ok: false,
|
|
55336
|
+
command: "update",
|
|
55337
|
+
error: kxmApply.error,
|
|
55338
|
+
kxm: kxmApply,
|
|
55339
|
+
notice,
|
|
55340
|
+
...installKindPayload(classified)
|
|
55341
|
+
}, kxmApply.detail);
|
|
55342
|
+
return 1;
|
|
55343
|
+
}
|
|
55344
|
+
return void 0;
|
|
55345
|
+
}
|
|
55291
55346
|
const resolved = resolveInstallKind(probe, npmGlobalRootFn(runtime));
|
|
55292
55347
|
kindReport = resolved;
|
|
55293
55348
|
if (resolved.kind !== "npm-global") {
|
|
55294
|
-
if (
|
|
55349
|
+
if (requireSuccess) {
|
|
55295
55350
|
print(runtime.io, runtime.json, {
|
|
55296
55351
|
ok: false,
|
|
55297
55352
|
command: "update",
|
|
@@ -55305,9 +55360,11 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55305
55360
|
}
|
|
55306
55361
|
if (notice.available) runtime.io.stderr(`kxm: ${resolved.instruction}
|
|
55307
55362
|
`);
|
|
55308
|
-
|
|
55363
|
+
return void 0;
|
|
55364
|
+
}
|
|
55365
|
+
if (notice.available) {
|
|
55309
55366
|
kxmApply = applyKxmPackageUpdate(runtime, notice);
|
|
55310
|
-
if (!kxmApply.ok &&
|
|
55367
|
+
if (!kxmApply.ok && requireSuccess) {
|
|
55311
55368
|
print(runtime.io, runtime.json, {
|
|
55312
55369
|
ok: false,
|
|
55313
55370
|
command: "update",
|
|
@@ -55319,9 +55376,12 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55319
55376
|
return 1;
|
|
55320
55377
|
}
|
|
55321
55378
|
}
|
|
55322
|
-
|
|
55323
|
-
|
|
55379
|
+
return void 0;
|
|
55380
|
+
};
|
|
55381
|
+
const skipHarness = Boolean(options.kxm && !fullUpdate && selected === 0 && !harness && !notice.auto);
|
|
55324
55382
|
if (skipHarness) {
|
|
55383
|
+
const aborted = applyKxmPackage(true);
|
|
55384
|
+
if (aborted !== void 0) return aborted;
|
|
55325
55385
|
print(
|
|
55326
55386
|
runtime.io,
|
|
55327
55387
|
runtime.json,
|
|
@@ -55332,23 +55392,39 @@ async function cmdUpdate(runtime, harness, options) {
|
|
|
55332
55392
|
}
|
|
55333
55393
|
const scope = options.self ? "self" : options.extensions ? "extensions" : options.models ? "models" : "all";
|
|
55334
55394
|
const inventory = await probeHarnessesAsync({ env: runtime.env });
|
|
55335
|
-
const planned = planHarnessUpdate(inventory, { ...harness ? { harness } : {}, scope });
|
|
55336
|
-
const
|
|
55337
|
-
|
|
55395
|
+
const planned = planHarnessUpdate(inventory, { ...harness ? { harness } : {}, scope: fullUpdate ? "all" : scope });
|
|
55396
|
+
const runOpts = { env: runtime.env, dryRun: runtime.dryRun };
|
|
55397
|
+
let steps;
|
|
55398
|
+
if (fullUpdate) {
|
|
55399
|
+
const selfSteps = runHarnessUpdate(planned.filter((step) => step.scope === "self"), runOpts);
|
|
55400
|
+
applyKxmPackage(false);
|
|
55401
|
+
const restSteps = runHarnessUpdate(planned.filter((step) => step.scope !== "self"), runOpts);
|
|
55402
|
+
steps = [...selfSteps, ...restSteps];
|
|
55403
|
+
} else {
|
|
55404
|
+
const aborted = applyKxmPackage(Boolean(options.kxm));
|
|
55405
|
+
if (aborted !== void 0) return aborted;
|
|
55406
|
+
steps = runHarnessUpdate(planned, runOpts);
|
|
55407
|
+
}
|
|
55408
|
+
const kxmFailed = kxmApply?.ok === false;
|
|
55338
55409
|
const skippedUnknown = steps.some((step) => step.detail === "unknown_harness");
|
|
55410
|
+
const harnessFailed = steps.some((step) => step.outcome === "failed");
|
|
55339
55411
|
const text = [notice.available ? notice.message : void 0, kxmApply?.detail, formatHarnessUpdate(steps)].filter(Boolean).join("\n");
|
|
55340
55412
|
print(runtime.io, runtime.json, {
|
|
55341
|
-
ok: !
|
|
55413
|
+
ok: !kxmFailed && !skippedUnknown,
|
|
55342
55414
|
command: "update",
|
|
55343
55415
|
dryRun: runtime.dryRun,
|
|
55344
|
-
scope,
|
|
55416
|
+
scope: fullUpdate ? "all" : scope,
|
|
55417
|
+
defaultHarness: inventory.defaultHarness,
|
|
55345
55418
|
notice,
|
|
55346
55419
|
...kxmApply ? { kxm: kxmApply } : {},
|
|
55347
55420
|
steps,
|
|
55421
|
+
...harnessFailed ? { degraded: true } : {},
|
|
55348
55422
|
...installKindPayload(kindReport)
|
|
55349
55423
|
}, text);
|
|
55350
55424
|
if (skippedUnknown) return 2;
|
|
55351
|
-
|
|
55425
|
+
if (kxmFailed) return 1;
|
|
55426
|
+
if (harnessFailed && !fullUpdate) return 1;
|
|
55427
|
+
return 0;
|
|
55352
55428
|
}
|
|
55353
55429
|
async function cmdValidate(runtime, fileFlag) {
|
|
55354
55430
|
const worker = gateOf(runtime, "validate");
|
|
@@ -56420,7 +56496,7 @@ function createProgram(ctx, result, argv) {
|
|
|
56420
56496
|
addGlobalOptions(authCmd.command("token").description("Inspect, issue, or clear local disk session tokens")).option("--status", "Check status of the active session token").option("--clear", "Clear persisted disk session token").option("--issue", "Force issuing a fresh session token").action(async function authTokenAction(options) {
|
|
56421
56497
|
result.code = await cmdAuthToken(runtimeFrom(ctx, this), options);
|
|
56422
56498
|
});
|
|
56423
|
-
addGlobalOptions(program2.command("update").description("Update kxm, harness CLIs, extensions, plugins, and model catalogs").argument("[harness]", "Harness id (default: every detected harness)").option("--check", "Check for a kxm package update without applying").option("--kxm", "Apply the kxm operator package update
|
|
56499
|
+
addGlobalOptions(program2.command("update").description("Update kxm, harness CLIs, extensions, plugins, and model catalogs").argument("[harness]", "Harness id (default: every detected harness)").option("--check", "Check for a kxm package update without applying").option("--all", "Update harness CLIs first (--self), then kxm, extensions, and models (default)").option("--kxm", "Apply only the kxm operator package update").option("--self", "Update only harness CLIs").option("--extensions", "Update only extensions/plugins (Pi packages, Claude kxm)").option("--models", "Refresh model catalogs where the harness supports it")).action(async function updateAction(harness, options) {
|
|
56424
56500
|
result.code = await cmdUpdate(runtimeFrom(ctx, this), harness, options);
|
|
56425
56501
|
});
|
|
56426
56502
|
const runtimeCmd = addGlobalOptions(program2.command("runtime").description("Manage the KXM Runtime supervisor"));
|