akm-cli 0.9.8 → 0.9.9-beta.1
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/CHANGELOG.md +57 -0
- package/dist/commands/health/checks.js +40 -0
- package/dist/commands/health.js +57 -31
- package/dist/commands/migrate-cli.js +29 -189
- package/dist/commands/sources/add-cli.js +7 -0
- package/dist/commands/sources/installed-stashes.js +36 -8
- package/dist/commands/sources/self-update.js +104 -62
- package/dist/commands/sources/source-add.js +6 -5
- package/dist/commands/sources/sources-cli.js +7 -18
- package/dist/commands/tasks/tasks-cli.js +4 -3
- package/dist/commands/tasks/tasks.js +13 -6
- package/dist/core/adapter/adapter-ids.js +35 -0
- package/dist/core/adapter/adapters/index.js +29 -0
- package/dist/core/adapter/detect-adapter.js +91 -3
- package/dist/core/config/config.js +1 -1
- package/dist/core/config/schema/sources-bundles.js +23 -0
- package/dist/core/extra-params.js +1 -1
- package/dist/core/state/migrations.js +2 -4
- package/dist/core/state-db.js +31 -8
- package/dist/indexer/indexer.js +64 -1
- package/dist/scripts/akm-migrate-node.js +86534 -20434
- package/dist/scripts/akm-migrate.js +86415 -20280
- package/dist/tasks/backends/cron.js +21 -6
- package/dist/tasks/resolve-akm-bin.js +1 -1
- package/docs/README.md +1 -0
- package/docs/integration/bundling-akm.md +276 -0
- package/docs/migration/v0.9.0-troubleshooting.md +10 -14
- package/docs/migration/v0.9.1-to-v0.9.2.md +12 -16
- package/docs/reference/cli.md +59 -31
- package/docs/reference/tasks.md +4 -10
- package/package.json +2 -1
- package/dist/commands/migrate/config-extra-params.js +0 -61
- package/dist/commands/migrate/dead-residue.js +0 -113
- package/dist/commands/migrate/stale-txn.js +0 -49
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
// `LOGNAME`/`USER` only). The cron line uses an absolute akm path
|
|
22
22
|
// resolved at install time so it doesn't rely on the inherited PATH.
|
|
23
23
|
// • BSD `crontab -l` returns exit 1 with "no crontab for <user>" on a
|
|
24
|
-
// fresh user;
|
|
24
|
+
// fresh user; a supercronic-managed PATH shim (#910, e.g. OpenPalm's
|
|
25
|
+
// `/tmp/openpalm-bin/crontab` before any spool exists) does the same
|
|
26
|
+
// with empty stdout instead. Both mean "empty crontab", not "broken
|
|
27
|
+
// install" — only a genuinely missing binary (ENOENT from the spawn
|
|
28
|
+
// itself) gets the "install crontab" remedy.
|
|
25
29
|
//
|
|
26
30
|
// Tests inject a fake exec so unit tests don't touch the real crontab.
|
|
27
31
|
import { spawnSync } from "node:child_process";
|
|
@@ -480,12 +484,21 @@ function readCrontab(exec) {
|
|
|
480
484
|
const result = exec.read();
|
|
481
485
|
if (result.status === 0)
|
|
482
486
|
return result.stdout ?? "";
|
|
483
|
-
//
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
+
// The spawn itself failed to find the binary (ENOENT) — this is the only
|
|
488
|
+
// case where "install/PATH the crontab binary" is the correct remedy.
|
|
489
|
+
if (result.enoent) {
|
|
490
|
+
throw new ConfigError("crontab -l failed: the `crontab` binary was not found on PATH.", "INVALID_CONFIG_FILE", "Install the `crontab` binary (e.g. cron/cronie/vixie-cron) or add one to PATH.");
|
|
491
|
+
}
|
|
492
|
+
// #910: a nonzero exit that says nothing at all, or whose stderr says
|
|
493
|
+
// "no crontab" (BSD's "no crontab for <user>"; a supercronic-managed PATH
|
|
494
|
+
// shim like OpenPalm's before any spool exists), is cron's own contract
|
|
495
|
+
// for "empty crontab" — not evidence the binary is missing or broken. A
|
|
496
|
+
// nonzero exit that DID say something else (a permission refusal) is
|
|
497
|
+
// reported as what it said, below.
|
|
498
|
+
const stderr = (result.stderr ?? "").trim();
|
|
499
|
+
if (((result.stdout ?? "").trim() === "" && stderr === "") || /no crontab/i.test(stderr))
|
|
487
500
|
return "";
|
|
488
|
-
throw new ConfigError(`crontab -l failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "
|
|
501
|
+
throw new ConfigError(`crontab -l failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "The `crontab` binary ran but did not report success; check its output above.");
|
|
489
502
|
}
|
|
490
503
|
function writeCrontab(exec, content) {
|
|
491
504
|
const normalised = content.endsWith("\n") || content.length === 0 ? content : `${content}\n`;
|
|
@@ -517,6 +530,7 @@ function defaultCronExec() {
|
|
|
517
530
|
status: r.status ?? 1,
|
|
518
531
|
stdout: r.stdout ?? "",
|
|
519
532
|
stderr: r.stderr ?? "",
|
|
533
|
+
enoent: r.error?.code === "ENOENT",
|
|
520
534
|
};
|
|
521
535
|
},
|
|
522
536
|
write(content) {
|
|
@@ -525,6 +539,7 @@ function defaultCronExec() {
|
|
|
525
539
|
status: r.status ?? 1,
|
|
526
540
|
stdout: r.stdout ?? "",
|
|
527
541
|
stderr: r.stderr ?? "",
|
|
542
|
+
enoent: r.error?.code === "ENOENT",
|
|
528
543
|
};
|
|
529
544
|
},
|
|
530
545
|
};
|
|
@@ -159,7 +159,7 @@ function samePath(left, right) {
|
|
|
159
159
|
// once-only flag) so a differently-invoked probe later in the same process
|
|
160
160
|
// still gets its own answer instead of a stale one.
|
|
161
161
|
let cachedNpmGlobalRoot;
|
|
162
|
-
function resolveNpmGlobalRoot(nodePath, env) {
|
|
162
|
+
export function resolveNpmGlobalRoot(nodePath, env) {
|
|
163
163
|
if (cachedNpmGlobalRoot && cachedNpmGlobalRoot.nodePath === nodePath) {
|
|
164
164
|
return cachedNpmGlobalRoot.value;
|
|
165
165
|
}
|
package/docs/README.md
CHANGED
|
@@ -41,6 +41,7 @@ Package complete capabilities and turn knowledge into repeatable work.
|
|
|
41
41
|
- [Bundle Author's Guide](https://github.com/itlackey/akm/blob/main/docs/guides/author-bundles.md) -- Build a bundle, make it discoverable, and share it so others can install it with `akm bundle add`
|
|
42
42
|
- [Author's Guide: Writing Workflows](https://github.com/itlackey/akm/blob/main/docs/guides/author-workflows.md) -- Write and test a workflow definition, from a minimal example to gates and outputs
|
|
43
43
|
- [Claude Code workflows vs. akm workflows](https://github.com/itlackey/akm/blob/main/docs/guides/claude-code-vs-akm-workflows.md) -- Short decision guide for choosing between a session-native workflow and an akm workflow ([full technical comparison](https://github.com/itlackey/akm/blob/main/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md))
|
|
44
|
+
- [Bundling akm](integration/bundling-akm.md) -- Ship akm inside your own product: the pin/migrate/health boot contract, plan JSON shapes, and exit codes for bundlers
|
|
44
45
|
|
|
45
46
|
### Maintainers
|
|
46
47
|
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Bundling akm
|
|
2
|
+
|
|
3
|
+
This is for anyone shipping `akm` inside their own product: a Docker image, a
|
|
4
|
+
plugin whose `node_modules` carries its own akm, or any other install a human
|
|
5
|
+
never runs `akm setup` inside. It says exactly what to call at boot and what
|
|
6
|
+
each call's JSON means — nothing about akm's internals you don't need.
|
|
7
|
+
|
|
8
|
+
## The contract
|
|
9
|
+
|
|
10
|
+
Three lines cover it:
|
|
11
|
+
|
|
12
|
+
1. **Pin** an exact `akm-cli` version and install it however you install
|
|
13
|
+
things (npm, the standalone binary, a `node_modules` dependency).
|
|
14
|
+
2. **At boot, run `akm migrate apply`** (or `akm upgrade`, which runs it).
|
|
15
|
+
It is offline, idempotent, needs no root, takes its own safety copies,
|
|
16
|
+
prints one JSON plan, and exits `0` unless a file is genuinely blocked.
|
|
17
|
+
3. **Read `akm health --format json`.** Never grep error text — akm's
|
|
18
|
+
wording is not a stable interface; the JSON fields are.
|
|
19
|
+
|
|
20
|
+
Everything below is detail in service of those three lines.
|
|
21
|
+
|
|
22
|
+
## `akm migrate apply`
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
akm migrate status # read-only: what would change
|
|
26
|
+
akm migrate apply --dry-run
|
|
27
|
+
akm migrate apply
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
In order, every run applies (or, under `status`/`--dry-run`, plans):
|
|
31
|
+
|
|
32
|
+
1. **Legacy config lift** — `extraParams` keys on an engine config moved onto
|
|
33
|
+
first-class fields.
|
|
34
|
+
2. **Pending `state.db` migrations**, historical-destructive ones included.
|
|
35
|
+
This is the only path (besides `akm upgrade`, which calls the same code)
|
|
36
|
+
that is allowed to apply a destructive migration to an existing,
|
|
37
|
+
unversioned or behind-generation `state.db` — see
|
|
38
|
+
[One-way state.db](#one-way-note-statedb-migrations-are-one-way) below.
|
|
39
|
+
3. **Task sources**: task-v2 files to task v3, then task-v3 files to task
|
|
40
|
+
source v4. Each generation keeps its own lock and backup, so a file
|
|
41
|
+
blocked in one generation does not stop the other from converting files
|
|
42
|
+
that are already current.
|
|
43
|
+
4. **Stash residue sweeps**: superseded pre-0.9.0 `.akm` files and stale
|
|
44
|
+
filesystem transactions, scoped to the configured bundle (skipped
|
|
45
|
+
entirely when no bundle is configured yet).
|
|
46
|
+
|
|
47
|
+
### The plan JSON
|
|
48
|
+
|
|
49
|
+
One JSON object on stdout, always. A current install, nothing to do:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"schemaVersion": 1,
|
|
54
|
+
"status": "current",
|
|
55
|
+
"blockers": [],
|
|
56
|
+
"configExtraParams": { "applied": false, "lifted": [], "conflicts": [] },
|
|
57
|
+
"stateMigrations": { "pending": [] },
|
|
58
|
+
"taskV3Migration": { "schemaVersion": 1, "generation": "task-v2-to-v3", "changed": 0, "skipped": 4, "blocked": 0, "files": [] },
|
|
59
|
+
"taskV4Migration": { "schemaVersion": 1, "generation": "task-v3-to-v4", "changed": 0, "skipped": 4, "blocked": 0, "files": [] }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`akm migrate status` against a tree with one blocked task file (`taskV3Migration`
|
|
64
|
+
omitted below for brevity — its shape is identical):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"schemaVersion": 1,
|
|
69
|
+
"status": "blocked",
|
|
70
|
+
"blockers": ["with-on-non-command-target"],
|
|
71
|
+
"stateMigrations": { "pending": [] },
|
|
72
|
+
"taskV4Migration": {
|
|
73
|
+
"schemaVersion": 1,
|
|
74
|
+
"generation": "task-v3-to-v4",
|
|
75
|
+
"changed": 3,
|
|
76
|
+
"skipped": 1,
|
|
77
|
+
"blocked": 1,
|
|
78
|
+
"files": [
|
|
79
|
+
{
|
|
80
|
+
"filePath": "tasks/nightly.yml",
|
|
81
|
+
"status": "blocked",
|
|
82
|
+
"reason": "with-on-non-command-target",
|
|
83
|
+
"beforeHash": "…"
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`status` is one of:
|
|
91
|
+
|
|
92
|
+
- **`current`** — no task-source file needs converting, and no config
|
|
93
|
+
extraParams lift is pending. A no-op boot.
|
|
94
|
+
- **`ready`** — task sources have eligible changes and no config-lift
|
|
95
|
+
conflict; a real `apply` will clear them.
|
|
96
|
+
- **`blocked`** — at least one item cannot be applied automatically (a
|
|
97
|
+
genuine authoring problem, e.g. a task file the migrator can't rewrite
|
|
98
|
+
unambiguously, or a config `extraParams` value that conflicts with its
|
|
99
|
+
first-class field). `blockers` names each one. `apply` still applies
|
|
100
|
+
everything it *can*, then reports `blocked` and exits non-zero.
|
|
101
|
+
|
|
102
|
+
A pending `state.db` migration needs no human decision, so it is never
|
|
103
|
+
`blocked`: under `akm migrate status` or `apply --dry-run` it reads as
|
|
104
|
+
`ready` (the run would change `state.db`) and is listed in
|
|
105
|
+
`stateMigrations.pending`; a real `apply` always applies it.
|
|
106
|
+
|
|
107
|
+
Key fields:
|
|
108
|
+
|
|
109
|
+
| Field | Meaning |
|
|
110
|
+
| --- | --- |
|
|
111
|
+
| `stateMigrations` | `{ pending: string[] }` under `status`/`--dry-run`; `{ applied: string[], safetyCopyPath?: string }` after a real `apply`. `safetyCopyPath` is present only when a historical-destructive migration ran — see below. |
|
|
112
|
+
| `taskV3Migration` / `taskV4Migration` | Per-generation summary: `changed`/`skipped`/`blocked` counts and a `files[]` array with each file's `status` and `reason`. |
|
|
113
|
+
| `backupPath` / `taskV4BackupPath` | Present after a real apply that changed at least one file in that generation — a timestamped snapshot directory. |
|
|
114
|
+
| `deadResidue` / `staleTxns` | Present only when a bundle is configured. `{ pending: [...] }` under a read-only run, `{ removed: [...] }` / `{ recovered: [...] }` after apply. |
|
|
115
|
+
|
|
116
|
+
**Backups and their retention:**
|
|
117
|
+
|
|
118
|
+
- Task-source backups (task-v2→v3 and v3→v4) go to
|
|
119
|
+
`<dataDir>/backups/task-v3/<timestamp>-<uuid>/` and
|
|
120
|
+
`<dataDir>/backups/task-v4/<timestamp>-<uuid>/` respectively — one
|
|
121
|
+
directory per apply run, pruned to the **5 most recent** automatically.
|
|
122
|
+
- Config backups go to `<cacheDir>/config-backups/config-<ISO-ts>.json`,
|
|
123
|
+
also capped at **5**.
|
|
124
|
+
- A `state.db` historical-destructive migration writes a verified sibling
|
|
125
|
+
snapshot next to `state.db` itself:
|
|
126
|
+
`state.db.pre-<migrationId>.<UTC-digits>.<UUID>.bak`. This one is **never
|
|
127
|
+
auto-pruned** — it is a one-time, rare event (a single destructive ledger
|
|
128
|
+
boundary), so clean it up yourself once you've confirmed the upgrade, if
|
|
129
|
+
disk space matters to your image.
|
|
130
|
+
|
|
131
|
+
**Exit codes:**
|
|
132
|
+
|
|
133
|
+
| Exit | Meaning |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `0` | `current` or `ready`-and-applied — nothing left to do. |
|
|
136
|
+
| `1` | `blocked` — at least one item needs a human. |
|
|
137
|
+
| anything else | A crash, not a migration outcome: `{"ok":false,"error":"...","code"?:"...","hint"?:"..."}` on stderr, with the exit code from akm's normal error-classification table (`2` usage, `70` internal, `78` config). |
|
|
138
|
+
|
|
139
|
+
**Idempotency:** re-running `apply` against a `current` database/tree is a
|
|
140
|
+
no-op — safe to put in every boot, every time, on every replica.
|
|
141
|
+
|
|
142
|
+
**Offline, no root:** every step reads and writes files/SQLite it already
|
|
143
|
+
owns under your configured `$DATA`/`$CACHE`/bundle directories. Nothing here
|
|
144
|
+
touches the network or needs elevated privileges.
|
|
145
|
+
|
|
146
|
+
## `akm upgrade` in a bundled install
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
akm upgrade # install a newer release if there is one, then migrate
|
|
150
|
+
akm upgrade --check # check for updates only — no install, no migration step
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`akm upgrade` always runs the migration step after its install step — on
|
|
154
|
+
install success, on install failure, and when there was nothing to install.
|
|
155
|
+
Its result carries the same plan under `migration`:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"currentVersion": "0.9.8",
|
|
160
|
+
"newVersion": "0.9.9",
|
|
161
|
+
"upgraded": false,
|
|
162
|
+
"installMethod": "package-local",
|
|
163
|
+
"message": "akm runs as a dependency of the package at /opt/myapp; upgrade that package to move akm.",
|
|
164
|
+
"migration": { "schemaVersion": 1, "status": "current", "blockers": [], "stateMigrations": { "pending": [] } }
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**`installMethod: "package-local"`** is the detection that matters for a
|
|
169
|
+
bundler: an `akm` living inside *another* package's `node_modules` (your
|
|
170
|
+
image's `tools/` dependency, a plugin's own `node_modules/akm-cli`) is never
|
|
171
|
+
`npm install -g`'d over — that would silently diverge from the copy the
|
|
172
|
+
parent package actually executes. akm reports `upgraded: false` and names the
|
|
173
|
+
parent package to upgrade instead; **its migrations still run**, exactly as
|
|
174
|
+
in every other install method. `installMethod` is one of `"binary"`,
|
|
175
|
+
`"bun"`, `"npm"`, `"pnpm"`, `"package-local"`, or `"unknown"`.
|
|
176
|
+
|
|
177
|
+
**Exit code:** `akm upgrade` exits `1` when `migration.status` is `"blocked"`
|
|
178
|
+
or `"failed"`, even if the install itself succeeded — an upgrade whose
|
|
179
|
+
migration didn't finish is not done. This makes plain `akm upgrade` (no
|
|
180
|
+
flags) a safe, idempotent container entrypoint step on every boot: on an
|
|
181
|
+
already-current install with nothing pending it is a fast no-op that exits 0.
|
|
182
|
+
|
|
183
|
+
**`--check`** skips the migration step entirely — it only compares versions
|
|
184
|
+
and reports `updateAvailable`. Use it for a version-drift alert, not as your
|
|
185
|
+
boot check.
|
|
186
|
+
|
|
187
|
+
## `akm health`
|
|
188
|
+
|
|
189
|
+
```sh
|
|
190
|
+
akm health --format json
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Read the JSON. Never parse the human-readable text or grep for a phrase —
|
|
194
|
+
that wording is not a stable interface.
|
|
195
|
+
|
|
196
|
+
**Exit codes:** `0` (`status: "pass"`), `4` (`status: "warn"`), `1`
|
|
197
|
+
(`status: "fail"`).
|
|
198
|
+
|
|
199
|
+
The check that replaces grepping akm's refusal text is a **hard check**
|
|
200
|
+
named `state-db-migrations`:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"name": "state-db-migrations",
|
|
205
|
+
"status": "fail",
|
|
206
|
+
"message": "1 pending state.db migration(s) (018-drop-dead-lane-schema); run `akm migrate apply`.",
|
|
207
|
+
"evidence": { "path": "/data/akm/state.db", "pending": ["018-drop-dead-lane-schema"] }
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
It reads `evidence.pending` — a read-only preflight, never the managed open
|
|
212
|
+
— so `akm health` can report this state even though **an ordinary command
|
|
213
|
+
that opens `state.db` refuses to touch a pending historical-destructive
|
|
214
|
+
migration by design**, naming `akm upgrade` and `akm migrate apply` as the
|
|
215
|
+
only two commands allowed to apply one. Before this check existed, that
|
|
216
|
+
refusal surfaced as a crash (config-error exit) instead of a normal `fail`
|
|
217
|
+
row — this is what a bundler should now watch for `state-db-migrations` to
|
|
218
|
+
report, instead of grepping error text for a fixed remedy string.
|
|
219
|
+
|
|
220
|
+
## The `akm-migrate` executable
|
|
221
|
+
|
|
222
|
+
`akm-migrate` is the second `bin` entry the `akm-cli` npm package installs
|
|
223
|
+
(`akm` and `akm-migrate` side by side), and it is also embedded directly in
|
|
224
|
+
the compiled standalone binary — a release binary re-execs itself internally
|
|
225
|
+
to reach it, so it needs no separate download. Same surface either way:
|
|
226
|
+
|
|
227
|
+
```sh
|
|
228
|
+
akm-migrate status
|
|
229
|
+
akm-migrate apply --dry-run
|
|
230
|
+
akm-migrate apply
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`akm migrate status`/`apply` (under the `akm` CLI) are a thin wrapper over
|
|
234
|
+
this same executable — same plan, same exit codes. Reach for `akm-migrate`
|
|
235
|
+
directly when you don't want `akm`'s `--format`/`--shape`/`--detail` output
|
|
236
|
+
handling in the way, e.g. a shell script that just wants the raw JSON line
|
|
237
|
+
on stdout and a plain exit code.
|
|
238
|
+
|
|
239
|
+
## Container entrypoint sketch
|
|
240
|
+
|
|
241
|
+
```sh
|
|
242
|
+
#!/bin/sh
|
|
243
|
+
set -e
|
|
244
|
+
akm --version # optional: confirm the pinned version actually landed
|
|
245
|
+
akm migrate apply # offline, idempotent — exits 1 only if genuinely blocked
|
|
246
|
+
akm task sync --rebind # only if you schedule akm tasks inside this image
|
|
247
|
+
akm health --format json # read this JSON; act on ok/status, never on message text
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Environment variables
|
|
251
|
+
|
|
252
|
+
A bundler that controls the filesystem layout should set these explicitly
|
|
253
|
+
rather than rely on `$HOME`-derived defaults (names verified against
|
|
254
|
+
`src/core/paths.ts` and `src/tasks/scheduler-invocation.ts`):
|
|
255
|
+
|
|
256
|
+
| Variable | What it points at |
|
|
257
|
+
| --- | --- |
|
|
258
|
+
| `AKM_BUNDLE_DIR` | The content bundle (`$STASH`) — assets akm indexes and serves. |
|
|
259
|
+
| `AKM_CONFIG_DIR` | `config.json`'s directory. |
|
|
260
|
+
| `AKM_DATA_DIR` | Durable, non-regenerable data: **`index.db` and `state.db` live here.** This is the directory a migration snapshot's safety copy sits beside. |
|
|
261
|
+
| `AKM_CACHE_DIR` | Regenerable cache: registry downloads, config backups, task logs. Safe to discard between image builds (not between boots of the same running install). |
|
|
262
|
+
| `AKM_STATE_DIR` | **Not** where `state.db` lives, despite the name — this is the XDG "state" directory used for scheduled-task invocation context and companion-plugin hook state (Claude Code / OpenCode hook logs). Set it anyway if you schedule akm tasks inside the image, so that context is captured consistently rather than falling back to `$HOME/.local/state/akm`. |
|
|
263
|
+
|
|
264
|
+
Set all five to paths that persist across container restarts (a mounted
|
|
265
|
+
volume), or `akm migrate apply` will see an empty `state.db` on every boot
|
|
266
|
+
and never actually converge.
|
|
267
|
+
|
|
268
|
+
## One-way note: `state.db` migrations are one-way
|
|
269
|
+
|
|
270
|
+
Once a migration has run against `state.db`, an **older** akm binary that
|
|
271
|
+
later opens the same file refuses it outright — the migration ledger moved
|
|
272
|
+
forward, and there is no downgrade path. If you need the ability to roll
|
|
273
|
+
back a bundled akm version, take your own backup of the whole data directory
|
|
274
|
+
(`$AKM_DATA_DIR`) before running `akm upgrade`/`akm migrate apply` — the
|
|
275
|
+
per-migration safety copy described above exists to protect that one
|
|
276
|
+
destructive step, not as a general rollback mechanism for your deployment.
|
|
@@ -18,23 +18,19 @@ Managed current databases apply exact-prefix additive schema migrations
|
|
|
18
18
|
automatically. Released migration 002's `task_history` table rebuild is also
|
|
19
19
|
automatic and preserves every existing row. Released migration 018 is the
|
|
20
20
|
exception: it removes retired dead-lane state, so an ordinary command stops
|
|
21
|
-
before that migration and tells you to run:
|
|
21
|
+
before that migration and tells you to run either of:
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
|
-
akm upgrade
|
|
24
|
+
akm upgrade # applies pending state migrations first, then installs if a release is newer
|
|
25
|
+
akm migrate apply # applies them alongside the task-source and config migrations
|
|
25
26
|
```
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
akm upgrade --state-only
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Both admit migration 018 and both take the same verified safety copy described
|
|
37
|
-
below; `--state-only` simply skips the executable replacement. Immediately before
|
|
28
|
+
Both run on an already-current install and never need an install to reach
|
|
29
|
+
the migration, so either is safe as a container entrypoint: on a current
|
|
30
|
+
database the step is a no-op. `akm upgrade` installs a newer release first
|
|
31
|
+
when there is one, then runs `akm-migrate apply` — the migrator that shipped
|
|
32
|
+
with whatever is now installed; `akm migrate apply` is that step without the
|
|
33
|
+
release check. Immediately before
|
|
38
34
|
the migration, AKM takes a SQLite writer-exclusion lock, rechecks the exact
|
|
39
35
|
ledger, and writes a consistent SQLite snapshot beside the database as
|
|
40
36
|
`state.db.pre-018-drop-dead-lane-schema.<UTC-digits>.<UUID>.bak`. The randomized
|
|
@@ -51,7 +47,7 @@ general storage migrator.
|
|
|
51
47
|
|
|
52
48
|
An existing database with no applied migration IDs is never treated as a fresh
|
|
53
49
|
install, whether its `schema_migrations` table is absent or empty. Ordinary
|
|
54
|
-
commands reject it without writing. The explicit `akm upgrade
|
|
50
|
+
commands reject it without writing. The explicit `akm upgrade` / `akm migrate apply` path
|
|
55
51
|
takes and verifies a descriptor-bound snapshot named
|
|
56
52
|
`state.db.pre-001-initial-schema.<UTC-digits>.<UUID>.bak` before it creates the
|
|
57
53
|
ledger or applies any migration from 001 through 022. A truly new database is
|
|
@@ -13,12 +13,14 @@ steps; no run data is lost.
|
|
|
13
13
|
The released 0.9.1 state ledger already includes historical migration 018, so
|
|
14
14
|
the normal 0.9.1→0.9.2 database step is additive and automatic. If a pre-release
|
|
15
15
|
database has an exact prefix before 018, ordinary commands stop before its
|
|
16
|
-
destructive dead-lane cleanup. Run `akm
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
destructive dead-lane cleanup. Run `akm migrate apply` (or `akm upgrade`, which
|
|
17
|
+
runs it after its install step regardless of whether an install happened): AKM
|
|
18
|
+
creates and verifies a sibling pre-018 SQLite safety copy before applying that
|
|
19
|
+
immutable released migration. Its locked ledger recheck, snapshot, and
|
|
20
|
+
migration share one writer-exclusion window, so another WAL writer cannot
|
|
21
|
+
commit between the copy and 018. Unknown or divergent ledgers remain
|
|
22
|
+
unsupported. See [Bundling akm](../integration/bundling-akm.md) for the full
|
|
23
|
+
migration contract.
|
|
22
24
|
|
|
23
25
|
## Before you upgrade
|
|
24
26
|
|
|
@@ -250,16 +252,10 @@ Common blocked reasons and what to do about each:
|
|
|
250
252
|
| `read-only-source` | The owning source or file is not writable. | Move or re-source the file somewhere writable, or edit it by hand. |
|
|
251
253
|
| `invalid-v3-task` | The v3 document itself is structurally invalid (unknown fields, missing selector, malformed trigger, etc). | Fix the underlying v3 document first — the migrator translates structure, it does not repair it. |
|
|
252
254
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
```sh
|
|
259
|
-
akm-migrate task-v4-status
|
|
260
|
-
akm-migrate task-v4-apply --dry-run
|
|
261
|
-
akm-migrate task-v4-apply
|
|
262
|
-
```
|
|
255
|
+
The migrator is also installed as its own executable, `akm-migrate`, with the
|
|
256
|
+
same `status` / `apply [--dry-run]` surface as `akm migrate`. A tree that is
|
|
257
|
+
already all `version: 3` simply reports the first generation as current and
|
|
258
|
+
runs this one.
|
|
263
259
|
|
|
264
260
|
## Task history result vocabulary (`target.kind`)
|
|
265
261
|
|
package/docs/reference/cli.md
CHANGED
|
@@ -282,7 +282,7 @@ Primary result fields:
|
|
|
282
282
|
| Field | Description |
|
|
283
283
|
| --- | --- |
|
|
284
284
|
| `status` | Overall health verdict: `pass`, `warn`, or `fail` |
|
|
285
|
-
| `hardChecks` | Deterministic checks such as `state-db-schema`, `state-db-round-trip`, `task-log-backing`, `active-runs`, `default-engine`, and `model-map-files` |
|
|
285
|
+
| `hardChecks` | Deterministic checks such as `state-db-schema`, `state-db-round-trip`, `state-db-migrations`, `task-log-backing`, `active-runs`, `default-engine`, and `model-map-files` |
|
|
286
286
|
| `advisories` | Non-fatal warnings including `semantic-search-runtime` and `session-extraction` (akmExtract pipeline health) |
|
|
287
287
|
| `metrics` | Aggregate task/runtime metrics: `taskFailRate`, `agentFailureRate`, `stuckActiveRuns`, `logBackingRate`, `probeRoundTripMs` |
|
|
288
288
|
| `improve` | Recent improve-loop counts derived from `improve_invoked`, `improve_skipped`, and `improve_completed` events |
|
|
@@ -292,6 +292,14 @@ memory-prune actions, memory-inference writes, graph-extraction refreshes,
|
|
|
292
292
|
session-extraction outcomes (`sessionsScanned`, `sessionsExtracted`, `proposalsCreated`),
|
|
293
293
|
dead-URL detections, and skip reasons observed in the selected time window.
|
|
294
294
|
|
|
295
|
+
`state-db-migrations` reports whether `state.db`'s migration ledger has any
|
|
296
|
+
pending entries (checked read-only, without applying anything). It `fail`s
|
|
297
|
+
when migrations are pending — naming them and pointing at `akm migrate apply`
|
|
298
|
+
— rather than the command crashing, which is what happens when `state.db`
|
|
299
|
+
holds a pending historical-destructive migration and something other than
|
|
300
|
+
`akm upgrade` / `akm migrate apply` opens it directly. Read this check's
|
|
301
|
+
`status` instead of grepping akm's error text for that case.
|
|
302
|
+
|
|
295
303
|
The `session-extraction` advisory reflects the health of the `akmExtract` pipeline
|
|
296
304
|
(Phase 0.4 of `akm improve`). It warns on harness errors or when no proposals are
|
|
297
305
|
generated across five or more scanned sessions.
|
|
@@ -1010,33 +1018,45 @@ Upgrade `akm` itself to the latest release. Standalone binaries are downloaded,
|
|
|
1010
1018
|
checksummed, and staged before replacement; npm, Bun, and pnpm global installs
|
|
1011
1019
|
use their package manager.
|
|
1012
1020
|
|
|
1013
|
-
Upgrade replaces the installed program
|
|
1014
|
-
|
|
1015
|
-
|
|
1021
|
+
Upgrade replaces the installed program when a newer release exists, then runs
|
|
1022
|
+
`akm-migrate apply` — the migrator that shipped with whatever is now installed
|
|
1023
|
+
— and rebuilds the derived index. The migration step runs on every
|
|
1024
|
+
`akm upgrade`, install or no install, and its plan is reported under
|
|
1025
|
+
`migration`; an upgrade whose migration is blocked or could not run exits 1.
|
|
1026
|
+
That makes `akm upgrade` safe as a container entrypoint: on a current
|
|
1027
|
+
installation it is a no-op. An akm installed as a dependency of another
|
|
1028
|
+
package (`installMethod: "package-local"`) is never reinstalled — the parent
|
|
1029
|
+
package owns that copy — but its migrations still run. Standalone downloads
|
|
1030
|
+
use a temporary rollback copy only during atomic executable replacement.
|
|
1016
1031
|
|
|
1017
1032
|
Standalone downloads are streamed directly to the staged file while SHA-256 is
|
|
1018
1033
|
computed, with a 256 MiB binary limit. Release/checksum metadata is capped at
|
|
1019
1034
|
1 MiB; an oversized response is cancelled and the staged file is removed.
|
|
1020
1035
|
|
|
1021
1036
|
```sh
|
|
1022
|
-
akm upgrade #
|
|
1023
|
-
akm upgrade --check # Check for updates without installing
|
|
1024
|
-
akm upgrade --force # Force
|
|
1025
|
-
akm upgrade --state-only # Apply pending state.db migrations; install nothing
|
|
1037
|
+
akm upgrade # Install a newer release if there is one, then run every pending migration
|
|
1038
|
+
akm upgrade --check # Check for updates without installing (no migration step)
|
|
1039
|
+
akm upgrade --force # Force the install even if already on latest
|
|
1026
1040
|
```
|
|
1027
1041
|
|
|
1028
1042
|
| Flag | Description |
|
|
1029
1043
|
| --- | --- |
|
|
1030
1044
|
| `--check` | Check for updates without installing |
|
|
1031
1045
|
| `--force` | Force upgrade even if on latest version |
|
|
1032
|
-
| `--state-only` | Apply pending `state.db` migrations without installing a new akm. For installs that cannot rewrite their own binary — a container shipping akm globally, an unprivileged runtime user — where the install step would fail `EACCES` before the migration could run. Takes the same verified safety copy as `--force`; it changes who may request the migration, not what it does. |
|
|
1033
1046
|
| `--skip-post-upgrade` | Skip the post-upgrade index rebuild |
|
|
1034
1047
|
|
|
1048
|
+
Offline, or to migrate without a release check, run `akm migrate apply`
|
|
1049
|
+
directly: it is the same step.
|
|
1050
|
+
|
|
1035
1051
|
Checksum verification is not optional and has no flag. If a release's
|
|
1036
1052
|
`checksums.txt` is genuinely unreachable, the recovery hatch is the
|
|
1037
1053
|
`AKM_UPGRADE_SKIP_CHECKSUM=1` environment variable (Internal — deliberately
|
|
1038
1054
|
not a discoverable, tab-completable flag). See STABILITY.md.
|
|
1039
1055
|
|
|
1056
|
+
Shipping akm inside your own product (a Docker image, a plugin's own
|
|
1057
|
+
`node_modules`)? See [Bundling akm](../integration/bundling-akm.md) for the
|
|
1058
|
+
full boot contract, JSON shapes, and exit codes.
|
|
1059
|
+
|
|
1040
1060
|
### clone
|
|
1041
1061
|
|
|
1042
1062
|
Copy an asset from any source into a managed writable bundle or an unmanaged
|
|
@@ -1440,33 +1460,41 @@ akm registry remove my-team --yes # Skip the confirmation prompt
|
|
|
1440
1460
|
|
|
1441
1461
|
### migrate
|
|
1442
1462
|
|
|
1443
|
-
Inspect or apply
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1463
|
+
Inspect or apply every pending migration in one plan. `akm migrate` is a thin
|
|
1464
|
+
wrapper over the standalone `akm-migrate` executable (installed alongside
|
|
1465
|
+
`akm`, and embedded in the release binary), which owns every historical shape
|
|
1466
|
+
akm has ever written so the CLI proper reads only current schemas. The steps,
|
|
1467
|
+
in order:
|
|
1468
|
+
|
|
1469
|
+
1. legacy config `extraParams` keys lifted onto first-class engine fields
|
|
1470
|
+
(`configExtraParams`);
|
|
1471
|
+
2. pending `state.db` migrations, historical-destructive ones included, with
|
|
1472
|
+
a verified sibling safety copy (`stateMigrations`) — the only path besides
|
|
1473
|
+
`akm upgrade` that admits released migration 018, which an ordinary
|
|
1474
|
+
command refuses;
|
|
1475
|
+
3. task-v2 files to task v3, then task-v3 files to task source v4
|
|
1476
|
+
(`taskV3Migration`, `taskV4Migration`), each keeping its own lock, backup,
|
|
1477
|
+
prevalidation, and rollback, so a file blocked in the first generation does
|
|
1478
|
+
not stop the second from converting files already at `version: 3`;
|
|
1479
|
+
4. superseded pre-0.9.0 `.akm` residue and stale filesystem transactions
|
|
1480
|
+
(`deadResidue`, `staleTxns`).
|
|
1452
1481
|
|
|
1453
1482
|
```sh
|
|
1454
1483
|
akm migrate status
|
|
1455
1484
|
akm migrate apply --dry-run
|
|
1456
1485
|
akm migrate apply
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
table and worked examples.
|
|
1486
|
+
akm-migrate apply # the same, without the akm wrapper
|
|
1487
|
+
```
|
|
1488
|
+
|
|
1489
|
+
`status` and `apply --dry-run` are read-only. Apply skips blocked task
|
|
1490
|
+
sources rather than failing (and exits 1 while any remain), backs up each
|
|
1491
|
+
changed file immediately before replacement, and atomically publishes strict
|
|
1492
|
+
task source v4 YAML; it is idempotent, so a current installation is a no-op.
|
|
1493
|
+
`akm upgrade` runs `apply` after its install step. See [Tasks: Migrating to
|
|
1494
|
+
task source v4](tasks.md#migrating-to-task-source-v4) for the full
|
|
1495
|
+
blocked-reason table and worked examples, and
|
|
1496
|
+
[Bundling akm](../integration/bundling-akm.md) for the plan JSON shape and
|
|
1497
|
+
how to drive this from a container/image boot step.
|
|
1470
1498
|
|
|
1471
1499
|
### config
|
|
1472
1500
|
|
package/docs/reference/tasks.md
CHANGED
|
@@ -371,16 +371,10 @@ Two cases produce one today:
|
|
|
371
371
|
v4 task is runnable manually with `akm task run` whether or not it has a
|
|
372
372
|
`schedule:`. The migrated document simply has no `schedule:` key.
|
|
373
373
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
```sh
|
|
380
|
-
akm-migrate task-v4-status
|
|
381
|
-
akm-migrate task-v4-apply --dry-run
|
|
382
|
-
akm-migrate task-v4-apply
|
|
383
|
-
```
|
|
374
|
+
The migrator is also installed as its own executable, `akm-migrate`, with the
|
|
375
|
+
same `status` / `apply [--dry-run]` surface; `akm migrate` wraps it, and
|
|
376
|
+
`akm upgrade` runs `apply` after installing a release. A tree that is already
|
|
377
|
+
all `version: 3` simply reports the first generation as current.
|
|
384
378
|
|
|
385
379
|
See the [0.9.1 to 0.9.2 migration guide](../migration/v0.9.1-to-v0.9.2.md#migrating-task-v3-to-task-source-v4)
|
|
386
380
|
for full before/after examples and recovery guidance.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
|
|
6
6
|
"keywords": [
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"docs/migration/v0.8-to-v0.9.md",
|
|
52
52
|
"docs/migration/v0.9.0-troubleshooting.md",
|
|
53
53
|
"docs/migration/v0.9.1-to-v0.9.2.md",
|
|
54
|
+
"docs/integration/bundling-akm.md",
|
|
54
55
|
"docs/reference/bundle-types.md",
|
|
55
56
|
"docs/reference/cli.md",
|
|
56
57
|
"docs/reference/configuration.md",
|