akm-cli 0.9.8 → 0.9.9
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
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
-
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
-
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
/**
|
|
5
|
-
* Legacy `extraParams` -> first-class-field lift (#852, following #815), as
|
|
6
|
-
* an `akm migrate` concern.
|
|
7
|
-
*
|
|
8
|
-
* This used to run silently, in memory, on every config load
|
|
9
|
-
* (`liftLegacyEngineExtraParams` called from `parseAndValidateConfigText`)
|
|
10
|
-
* and never wrote the result back — so the lift, and its warning, recurred
|
|
11
|
-
* forever. config.json is akm-owned plain JSON; the lift is deterministic
|
|
12
|
-
* and total, so — like the dead-residue cleanup in `./dead-residue.ts` —
|
|
13
|
-
* it belongs in `akm migrate`, run once, persisted. `parseAndValidateConfigText`
|
|
14
|
-
* now fails closed on an unmigrated config instead of lifting it.
|
|
15
|
-
*/
|
|
16
|
-
import { acquireConfigLock, backupExistingConfig, parseConfigText, readConfigText, writeConfigAtomic, } from "../../core/config/config-io.js";
|
|
17
|
-
import { liftLegacyEngineExtraParams } from "../../core/extra-params.js";
|
|
18
|
-
function readRawConfig(configPath) {
|
|
19
|
-
const text = readConfigText(configPath);
|
|
20
|
-
if (text === undefined)
|
|
21
|
-
return undefined;
|
|
22
|
-
return parseConfigText(text, configPath);
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Read-only: what `akm migrate apply` would lift (or flag as conflicting) in
|
|
26
|
-
* `config.json`'s `extraParams`. Never touches disk. Returns an empty plan
|
|
27
|
-
* when the config file does not exist.
|
|
28
|
-
*/
|
|
29
|
-
export function findConfigExtraParamsLift(configPath) {
|
|
30
|
-
const raw = readRawConfig(configPath);
|
|
31
|
-
if (!raw)
|
|
32
|
-
return { lifted: [], conflicts: [] };
|
|
33
|
-
const { lifted, conflicts } = liftLegacyEngineExtraParams(raw);
|
|
34
|
-
return { lifted, conflicts };
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Persist the legacy extraParams -> first-class field lift to `config.json`,
|
|
38
|
-
* once. Best-effort like `removeDeadResidue`: a genuine conflict (extraParams
|
|
39
|
-
* and the first-class field set to different values) is left untouched here
|
|
40
|
-
* — `parseAndValidateConfigText` already hard-rejects that config at every
|
|
41
|
-
* load with the exact mismatch, so a second, weaker error here would only be
|
|
42
|
-
* noise — and is reported back via `conflicts` instead.
|
|
43
|
-
*/
|
|
44
|
-
export function applyConfigExtraParamsLift(configPath) {
|
|
45
|
-
const raw = readRawConfig(configPath);
|
|
46
|
-
if (!raw)
|
|
47
|
-
return { applied: false, lifted: [], conflicts: [] };
|
|
48
|
-
const { config, lifted, conflicts } = liftLegacyEngineExtraParams(raw);
|
|
49
|
-
if (conflicts.length > 0 || lifted.length === 0) {
|
|
50
|
-
return { applied: false, lifted, conflicts };
|
|
51
|
-
}
|
|
52
|
-
const release = acquireConfigLock();
|
|
53
|
-
try {
|
|
54
|
-
backupExistingConfig(configPath);
|
|
55
|
-
writeConfigAtomic(configPath, config);
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
release();
|
|
59
|
-
}
|
|
60
|
-
return { applied: true, lifted, conflicts: [] };
|
|
61
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dead pre-0.9.0 `$STASH/.akm` residue: superseded filesystem layouts with no
|
|
3
|
-
* live reader or writer anywhere in src/ (each verified by grep before being
|
|
4
|
-
* listed — see #889 for the audit). The largest, `.akm/proposals/`, was
|
|
5
|
-
* replaced by the `proposals` table in state.db in 0.9.0 and measured 135 MB
|
|
6
|
-
* on a real bundle.
|
|
7
|
-
*
|
|
8
|
-
* This lives under `migrate/` because removing a superseded layout IS
|
|
9
|
-
* migration — the tail end of the moves that created these paths' replacements.
|
|
10
|
-
* It was first shipped as a bolted-on `akm health --clean-dead-residue` flag
|
|
11
|
-
* plus a health advisory; that was the wrong shape (a special-purpose switch
|
|
12
|
-
* apologizing for migrations that did not finish their own job) and was
|
|
13
|
-
* removed. `akm migrate status` reports what is here; `akm migrate apply`
|
|
14
|
-
* removes it, exactly as it applies every other pending migration.
|
|
15
|
-
*/
|
|
16
|
-
import fs from "node:fs";
|
|
17
|
-
import path from "node:path";
|
|
18
|
-
const DEAD_RESIDUE_PATHS = [
|
|
19
|
-
{ name: "proposals", reason: "superseded by the `proposals` table in $DATA/state.db (0.9.0)" },
|
|
20
|
-
{ prefix: "runs.archived-", reason: "orphaned archive of a directory that no longer exists" },
|
|
21
|
-
{
|
|
22
|
-
name: "archive",
|
|
23
|
-
reason: "legacy consolidation archive; current prune writes .akm/memory-cleanup/archive/ instead",
|
|
24
|
-
},
|
|
25
|
-
{ name: "graph.json", reason: "superseded by the graph_* tables in $DATA/index.db" },
|
|
26
|
-
{ name: "consolidate-journal.json", reason: "legacy consolidation journal; unused" },
|
|
27
|
-
{ name: "proposals.db", reason: "empty legacy database file" },
|
|
28
|
-
{ name: "mv-transactions", reason: "legacy fs-txn journal location; unused" },
|
|
29
|
-
];
|
|
30
|
-
function dirSizeBytes(target) {
|
|
31
|
-
let total = 0;
|
|
32
|
-
let entries;
|
|
33
|
-
try {
|
|
34
|
-
entries = fs.readdirSync(target, { withFileTypes: true });
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return 0;
|
|
38
|
-
}
|
|
39
|
-
for (const entry of entries) {
|
|
40
|
-
const entryPath = path.join(target, entry.name);
|
|
41
|
-
if (entry.isDirectory()) {
|
|
42
|
-
total += dirSizeBytes(entryPath);
|
|
43
|
-
}
|
|
44
|
-
else if (entry.isFile()) {
|
|
45
|
-
try {
|
|
46
|
-
total += fs.statSync(entryPath).size;
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// Skip files that vanish or are inaccessible between readdir and stat.
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return total;
|
|
54
|
-
}
|
|
55
|
-
function sizeOf(target) {
|
|
56
|
-
const st = fs.statSync(target);
|
|
57
|
-
return st.isDirectory() ? dirSizeBytes(target) : st.size;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Find every Tier-1 dead-residue path that actually exists under
|
|
61
|
-
* `$STASH/.akm`, with its computed size. Read-only — never deletes.
|
|
62
|
-
*/
|
|
63
|
-
export function findDeadResidueEntries(stashDir) {
|
|
64
|
-
const akmDir = path.join(stashDir, ".akm");
|
|
65
|
-
let names;
|
|
66
|
-
try {
|
|
67
|
-
names = fs.readdirSync(akmDir);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
const found = [];
|
|
73
|
-
for (const spec of DEAD_RESIDUE_PATHS) {
|
|
74
|
-
const matches = spec.name !== undefined ? [spec.name] : names.filter((n) => n.startsWith(spec.prefix ?? "\0"));
|
|
75
|
-
for (const name of matches) {
|
|
76
|
-
if (!names.includes(name))
|
|
77
|
-
continue;
|
|
78
|
-
const absolutePath = path.join(akmDir, name);
|
|
79
|
-
let sizeBytes;
|
|
80
|
-
try {
|
|
81
|
-
sizeBytes = sizeOf(absolutePath);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
continue; // vanished between readdir and stat
|
|
85
|
-
}
|
|
86
|
-
found.push({ relativePath: path.join(".akm", name), absolutePath, sizeBytes, reason: spec.reason });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return found;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Delete every Tier-1 dead-residue path found under `$STASH/.akm`. Only
|
|
93
|
-
* invoked when the caller has explicitly opted in (`akm health
|
|
94
|
-
* --clean-dead-residue`) — never as a side effect of a plain `akm health`
|
|
95
|
-
* read. Best-effort per-path: one failure does not abort the rest.
|
|
96
|
-
*/
|
|
97
|
-
export function removeDeadResidue(stashDir) {
|
|
98
|
-
const entries = findDeadResidueEntries(stashDir);
|
|
99
|
-
return entries.map((entry) => {
|
|
100
|
-
try {
|
|
101
|
-
fs.rmSync(entry.absolutePath, { recursive: true, force: true });
|
|
102
|
-
return { relativePath: entry.relativePath, sizeBytes: entry.sizeBytes, removed: true };
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
return {
|
|
106
|
-
relativePath: entry.relativePath,
|
|
107
|
-
sizeBytes: entry.sizeBytes,
|
|
108
|
-
removed: false,
|
|
109
|
-
error: error instanceof Error ? error.message : String(error),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
-
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
-
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
/**
|
|
5
|
-
* Stale durable-transaction journals (`$DATA/txn/<rootNs24>/<id>/journal.json`,
|
|
6
|
-
* see `core/fs-txn.ts`) that never got recovered — a crash mid-transaction
|
|
7
|
-
* left a journal on disk with no process left to finish or roll it back.
|
|
8
|
-
* `akm health` used to only report these and point at a troubleshooting doc
|
|
9
|
-
* (issue: an advisory that tells the user to go read docs instead of the
|
|
10
|
-
* tool recovering its own interrupted state). Recovery IS a migration
|
|
11
|
-
* concern like dead `.akm/` residue: `akm migrate status` names what is
|
|
12
|
-
* here for the stash root, `akm migrate apply` recovers it, exactly like
|
|
13
|
-
* `dead-residue.ts`.
|
|
14
|
-
*
|
|
15
|
-
* Recovery goes through {@link recoverTxnsForRoot}, which requires every
|
|
16
|
-
* live kind's registrar to be IMPORTED first so its handler is registered
|
|
17
|
-
* (see fs-txn.ts's module docs). `proposal/repository.ts` registers the
|
|
18
|
-
* `proposal`/`proposal-reject` kinds specifically so "ANY recovery entry
|
|
19
|
-
* point ... can finish or roll back an interrupted proposal mutation for a
|
|
20
|
-
* root it touches" (its own comment on the registration) — this module is
|
|
21
|
-
* exactly that kind of entry point.
|
|
22
|
-
*/
|
|
23
|
-
import { canonicalTxnRoot, listTxnJournalsTolerant, recoverTxnsForRoot } from "../../core/fs-txn.js";
|
|
24
|
-
// Side-effect import: registers the `proposal`/`proposal-reject` txn kinds
|
|
25
|
-
// so recovery below can roll them forward/back for the stash root.
|
|
26
|
-
import "../proposal/repository.js";
|
|
27
|
-
/**
|
|
28
|
-
* Find every durable-transaction journal bound to `stashDir`'s namespace.
|
|
29
|
-
* Read-only, tolerant of a corrupt journal (counted, not thrown on) — mirrors
|
|
30
|
-
* `findDeadResidueEntries`'s read-only/never-mutates contract.
|
|
31
|
-
*/
|
|
32
|
-
export function findStaleTxnEntries(stashDir) {
|
|
33
|
-
const root = canonicalTxnRoot(stashDir);
|
|
34
|
-
const { matches } = listTxnJournalsTolerant((j) => canonicalTxnRoot(j.root) === root);
|
|
35
|
-
return matches.map(({ journal }) => journalToEntry(journal));
|
|
36
|
-
}
|
|
37
|
-
function journalToEntry(journal) {
|
|
38
|
-
return { transactionId: journal.transactionId, kind: journal.kind, phase: journal.phase, root: journal.root };
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Recover every durable transaction bound to `stashDir`'s namespace: roll
|
|
42
|
-
* back journals before their kind's commit point, roll forward the rest.
|
|
43
|
-
* The counterpart to {@link findStaleTxnEntries}, invoked only from `akm
|
|
44
|
-
* migrate apply`.
|
|
45
|
-
*/
|
|
46
|
-
export async function recoverStaleTxns(stashDir) {
|
|
47
|
-
const recovered = await recoverTxnsForRoot(stashDir);
|
|
48
|
-
return recovered.map(journalToEntry);
|
|
49
|
-
}
|