akm-cli 0.9.0-beta.54 → 0.9.0-beta.55
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/dist/cli.js +5 -3
- package/dist/commands/agent/contribute-cli.js +2 -3
- package/dist/commands/env/env-cli.js +187 -202
- package/dist/commands/env/secret-cli.js +109 -121
- package/dist/commands/feedback-cli.js +152 -155
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/improve-metrics.js +754 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +18 -0
- package/dist/commands/health/windows.js +196 -0
- package/dist/commands/health.js +14 -1624
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/collapse-detector.js +3 -2
- package/dist/commands/improve/consolidate.js +636 -633
- package/dist/commands/improve/dedup.js +1 -1
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +228 -0
- package/dist/commands/improve/distill/quality-gate.js +233 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill.js +49 -575
- package/dist/commands/improve/extract-cli.js +74 -76
- package/dist/commands/improve/extract.js +6 -4
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +3 -2
- package/dist/commands/improve/improve-cli.js +14 -13
- package/dist/commands/improve/improve-result-file.js +2 -1
- package/dist/commands/improve/improve.js +6 -5
- package/dist/commands/improve/loop-stages.js +19 -21
- package/dist/commands/improve/preparation.js +4 -2
- package/dist/commands/improve/procedural.js +10 -31
- package/dist/commands/improve/recombine.js +19 -43
- package/dist/commands/improve/reflect.js +1 -1
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +48 -0
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +2 -2
- package/dist/commands/proposal/drain.js +1 -1
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +3 -3
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +1 -1
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposals.js +5 -920
- package/dist/commands/read/remember-cli.js +132 -137
- package/dist/commands/read/search-cli.js +1 -1
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/sources/add-cli.js +90 -94
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/schema-repair.js +1 -1
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +1 -2
- package/dist/commands/wiki-cli.js +2 -3
- package/dist/core/common.js +3 -3
- package/dist/core/config/config-schema.js +6 -0
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/events.js +2 -1
- package/dist/core/logs-db.js +8 -13
- package/dist/core/paths.js +14 -14
- package/dist/core/state-db.js +13 -1140
- package/dist/indexer/db/db.js +66 -709
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/feedback/utility-policy.js +85 -0
- package/dist/indexer/graph/graph-extraction.js +2 -1
- package/dist/indexer/index-writer-lock.js +9 -0
- package/dist/indexer/indexer.js +78 -23
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/integrations/agent/spawn.js +15 -66
- package/dist/output/text/helpers.js +13 -0
- package/dist/scripts/migrate-storage.js +6891 -7436
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +44 -43
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +24 -1607
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +130 -0
- package/dist/storage/repositories/index-db.js +4 -7
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +3 -3
- package/dist/tasks/runner.js +2 -1
- package/package.json +1 -1
- package/dist/commands/improve/homeostatic.js +0 -497
|
@@ -8612,8 +8612,8 @@ function resolveJournalMode(raw) {
|
|
|
8612
8612
|
warnInvalidJournalModeOnce(raw);
|
|
8613
8613
|
return "WAL";
|
|
8614
8614
|
}
|
|
8615
|
-
function resolveConfiguredJournalMode() {
|
|
8616
|
-
return resolveJournalMode(
|
|
8615
|
+
function resolveConfiguredJournalMode(env = process.env) {
|
|
8616
|
+
return resolveJournalMode(env.AKM_SQLITE_JOURNAL_MODE);
|
|
8617
8617
|
}
|
|
8618
8618
|
function warnInvalidJournalModeOnce(raw) {
|
|
8619
8619
|
if (warnedInvalid)
|
|
@@ -8639,7 +8639,7 @@ function isNetworkFilesystem(fsType) {
|
|
|
8639
8639
|
return NETWORK_FS_MAGICS.has(fsType);
|
|
8640
8640
|
}
|
|
8641
8641
|
function applyStandardPragmas(db, opts = {}) {
|
|
8642
|
-
let mode = resolveConfiguredJournalMode();
|
|
8642
|
+
let mode = resolveConfiguredJournalMode(opts.env);
|
|
8643
8643
|
if (mode === "WAL" && opts.dataDir) {
|
|
8644
8644
|
const probe = opts.fsTypeProbe ?? statfsType;
|
|
8645
8645
|
if (isNetworkFilesystem(probe(opts.dataDir))) {
|
|
@@ -8676,48 +8676,8 @@ function openManagedDatabase(spec) {
|
|
|
8676
8676
|
return db;
|
|
8677
8677
|
}
|
|
8678
8678
|
|
|
8679
|
-
// src/core/assert.ts
|
|
8680
|
-
function assertNever(x, context) {
|
|
8681
|
-
let serialized;
|
|
8682
|
-
try {
|
|
8683
|
-
serialized = JSON.stringify(x);
|
|
8684
|
-
} catch {
|
|
8685
|
-
serialized = String(x);
|
|
8686
|
-
}
|
|
8687
|
-
if (serialized === undefined) {
|
|
8688
|
-
serialized = String(x);
|
|
8689
|
-
}
|
|
8690
|
-
const where = context ? ` (${context})` : "";
|
|
8691
|
-
throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
|
|
8692
|
-
}
|
|
8693
|
-
|
|
8694
|
-
// src/core/improve-types.ts
|
|
8695
|
-
function classifyImproveAction(mode) {
|
|
8696
|
-
switch (mode) {
|
|
8697
|
-
case "reflect":
|
|
8698
|
-
case "distill":
|
|
8699
|
-
case "memory-inference":
|
|
8700
|
-
case "graph-extraction":
|
|
8701
|
-
return "accepted";
|
|
8702
|
-
case "reflect-cooldown":
|
|
8703
|
-
case "reflect-skipped":
|
|
8704
|
-
case "distill-skipped":
|
|
8705
|
-
return "skipped";
|
|
8706
|
-
case "reflect-guard-rejected":
|
|
8707
|
-
return "rejected";
|
|
8708
|
-
case "reflect-failed":
|
|
8709
|
-
case "error":
|
|
8710
|
-
return "error";
|
|
8711
|
-
case "memory-prune":
|
|
8712
|
-
return "noop";
|
|
8713
|
-
default:
|
|
8714
|
-
return assertNever(mode);
|
|
8715
|
-
}
|
|
8716
|
-
}
|
|
8717
|
-
|
|
8718
8679
|
// src/core/state-db.ts
|
|
8719
8680
|
init_paths();
|
|
8720
|
-
init_warn();
|
|
8721
8681
|
|
|
8722
8682
|
// src/storage/engines/sqlite-migrations.ts
|
|
8723
8683
|
function ensureMigrationsTable(db) {
|
|
@@ -9182,6 +9142,47 @@ function getStateDbPath() {
|
|
|
9182
9142
|
function openStateDatabase(dbPath) {
|
|
9183
9143
|
return openManagedDatabase({ path: dbPath ?? getStateDbPath(), init: runMigrations2 });
|
|
9184
9144
|
}
|
|
9145
|
+
|
|
9146
|
+
// src/core/assert.ts
|
|
9147
|
+
function assertNever(x, context) {
|
|
9148
|
+
let serialized;
|
|
9149
|
+
try {
|
|
9150
|
+
serialized = JSON.stringify(x);
|
|
9151
|
+
} catch {
|
|
9152
|
+
serialized = String(x);
|
|
9153
|
+
}
|
|
9154
|
+
if (serialized === undefined) {
|
|
9155
|
+
serialized = String(x);
|
|
9156
|
+
}
|
|
9157
|
+
const where = context ? ` (${context})` : "";
|
|
9158
|
+
throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
|
|
9159
|
+
}
|
|
9160
|
+
|
|
9161
|
+
// src/core/improve-types.ts
|
|
9162
|
+
function classifyImproveAction(mode) {
|
|
9163
|
+
switch (mode) {
|
|
9164
|
+
case "reflect":
|
|
9165
|
+
case "distill":
|
|
9166
|
+
case "memory-inference":
|
|
9167
|
+
case "graph-extraction":
|
|
9168
|
+
return "accepted";
|
|
9169
|
+
case "reflect-cooldown":
|
|
9170
|
+
case "reflect-skipped":
|
|
9171
|
+
case "distill-skipped":
|
|
9172
|
+
return "skipped";
|
|
9173
|
+
case "reflect-guard-rejected":
|
|
9174
|
+
return "rejected";
|
|
9175
|
+
case "reflect-failed":
|
|
9176
|
+
case "error":
|
|
9177
|
+
return "error";
|
|
9178
|
+
case "memory-prune":
|
|
9179
|
+
return "noop";
|
|
9180
|
+
default:
|
|
9181
|
+
return assertNever(mode);
|
|
9182
|
+
}
|
|
9183
|
+
}
|
|
9184
|
+
|
|
9185
|
+
// src/storage/repositories/improve-runs-repository.ts
|
|
9185
9186
|
function computeImproveRunMetrics(result) {
|
|
9186
9187
|
const plannedCount = Array.isArray(result.plannedRefs) ? result.plannedRefs.length : 0;
|
|
9187
9188
|
const actions = Array.isArray(result.actions) ? result.actions : [];
|
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
import { getDefaultLlmConfig } from "../core/config/config.js";
|
|
5
|
+
import { warn } from "../core/warn.js";
|
|
6
|
+
import { v1ProfilePlatform } from "../integrations/harnesses/index.js";
|
|
7
|
+
/** Read the currently-configured LLM connection from a loaded config. */
|
|
8
|
+
export function getCurrentLlm(config) {
|
|
9
|
+
return getDefaultLlmConfig(config);
|
|
10
|
+
}
|
|
11
|
+
/** Read a synthesised legacy-shape agent block from the new-shape AkmConfig. */
|
|
12
|
+
export function getCurrentAgentBlock(config) {
|
|
13
|
+
if (!config.profiles?.agent && !config.defaults?.agent)
|
|
14
|
+
return undefined;
|
|
15
|
+
const block = {};
|
|
16
|
+
if (config.defaults?.agent)
|
|
17
|
+
block.default = config.defaults.agent;
|
|
18
|
+
if (config.profiles?.agent) {
|
|
19
|
+
const profiles = {};
|
|
20
|
+
for (const [name, raw] of Object.entries(config.profiles.agent)) {
|
|
21
|
+
profiles[name] = {
|
|
22
|
+
...(raw.platform === "opencode-sdk" ? { sdkMode: true } : {}),
|
|
23
|
+
...(raw.model ? { model: raw.model } : {}),
|
|
24
|
+
...(raw.bin ? { bin: raw.bin } : {}),
|
|
25
|
+
...(raw.args ? { args: raw.args } : {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
block.profiles = profiles;
|
|
29
|
+
}
|
|
30
|
+
return block;
|
|
31
|
+
}
|
|
32
|
+
/** Apply an LLM connection patch onto the new-shape config. */
|
|
33
|
+
export function applyLegacyLlm(config, llm) {
|
|
34
|
+
if (!llm) {
|
|
35
|
+
// Clear the default LLM profile.
|
|
36
|
+
const name = config.defaults?.llm ?? "default";
|
|
37
|
+
const remaining = { ...(config.profiles?.llm ?? {}) };
|
|
38
|
+
delete remaining[name];
|
|
39
|
+
return {
|
|
40
|
+
profiles: { ...(config.profiles ?? {}), llm: remaining },
|
|
41
|
+
defaults: { ...(config.defaults ?? {}), llm: undefined },
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const name = config.defaults?.llm ?? "default";
|
|
45
|
+
return {
|
|
46
|
+
profiles: {
|
|
47
|
+
...(config.profiles ?? {}),
|
|
48
|
+
llm: { ...(config.profiles?.llm ?? {}), [name]: llm },
|
|
49
|
+
},
|
|
50
|
+
defaults: { ...(config.defaults ?? {}), llm: name },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Apply a legacy-shape agent block onto the new-shape config. */
|
|
54
|
+
export function applyLegacyAgent(config, agent) {
|
|
55
|
+
if (!agent) {
|
|
56
|
+
return {
|
|
57
|
+
profiles: { ...(config.profiles ?? {}), agent: undefined },
|
|
58
|
+
defaults: { ...(config.defaults ?? {}), agent: undefined },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const v2Profiles = { ...(config.profiles?.agent ?? {}) };
|
|
62
|
+
for (const [name, profile] of Object.entries(agent.profiles ?? {})) {
|
|
63
|
+
// #566: resolve the platform via the harness registry instead of the old
|
|
64
|
+
// `name.includes("claude") ? "claude" : "opencode"` heuristic, which
|
|
65
|
+
// silently mapped Cursor/Copilot/any new harness to "opencode". An explicit
|
|
66
|
+
// sdkMode flag still wins; otherwise we ask the registry. A name the
|
|
67
|
+
// registry does not recognize is surfaced (warn) rather than silently
|
|
68
|
+
// misclassified, then kept as a best-effort "opencode" profile so the user
|
|
69
|
+
// does not lose a profile they explicitly configured.
|
|
70
|
+
let platform;
|
|
71
|
+
if (profile.sdkMode) {
|
|
72
|
+
platform = "opencode-sdk";
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const resolved = v1ProfilePlatform(name);
|
|
76
|
+
if (resolved) {
|
|
77
|
+
platform = resolved;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
warn(`[akm setup] Agent profile "${name}" did not match any known harness; ` +
|
|
81
|
+
`defaulting its platform to "opencode". Set its platform explicitly in config if this is wrong.`);
|
|
82
|
+
platform = "opencode";
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
v2Profiles[name] = {
|
|
86
|
+
platform,
|
|
87
|
+
...(profile.bin ? { bin: profile.bin } : {}),
|
|
88
|
+
...(profile.args ? { args: profile.args } : {}),
|
|
89
|
+
...(profile.model ? { model: profile.model } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
profiles: { ...(config.profiles ?? {}), agent: v2Profiles },
|
|
94
|
+
defaults: { ...(config.defaults ?? {}), agent: agent.default },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/** Deep-ish clone of an LLM connection config (capabilities + extraParams). */
|
|
98
|
+
export function cloneLlmConfig(llm) {
|
|
99
|
+
if (!llm)
|
|
100
|
+
return undefined;
|
|
101
|
+
return {
|
|
102
|
+
...llm,
|
|
103
|
+
...(llm.capabilities ? { capabilities: { ...llm.capabilities } } : {}),
|
|
104
|
+
...(llm.extraParams ? { extraParams: { ...llm.extraParams } } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
* Clack prompt shims for the setup wizard: cancel-aware prompting so pressing
|
|
6
|
+
* Escape offers a confirm-to-quit rather than crashing the wizard.
|
|
7
|
+
*/
|
|
8
|
+
import * as p from "../cli/clack.js";
|
|
9
|
+
export function bail() {
|
|
10
|
+
p.cancel("Setup cancelled. No changes were saved.");
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check if a prompt result was cancelled (Escape). If so, ask the user
|
|
15
|
+
* whether they really want to quit. Returns true if the user chose to
|
|
16
|
+
* stay (i.e. the caller should re-prompt), or calls bail() to exit.
|
|
17
|
+
*
|
|
18
|
+
* @internal Exported for testing only.
|
|
19
|
+
*/
|
|
20
|
+
export async function onCancel(value) {
|
|
21
|
+
if (!p.isCancel(value))
|
|
22
|
+
return false;
|
|
23
|
+
const confirmExit = await p.confirm({
|
|
24
|
+
message: "Exit the wizard? No changes will be saved.",
|
|
25
|
+
initialValue: false,
|
|
26
|
+
});
|
|
27
|
+
// Only exit when the user explicitly confirms "Yes".
|
|
28
|
+
// Pressing Escape on the confirmation (isCancel) or choosing "No"
|
|
29
|
+
// both mean "stay in the wizard".
|
|
30
|
+
if (confirmExit === true) {
|
|
31
|
+
bail();
|
|
32
|
+
}
|
|
33
|
+
// User chose to stay
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run a prompt function in a loop, retrying if the user presses Escape
|
|
38
|
+
* but decides to stay. Returns the non-cancelled result.
|
|
39
|
+
*/
|
|
40
|
+
export async function prompt(fn) {
|
|
41
|
+
for (;;) {
|
|
42
|
+
const result = await fn();
|
|
43
|
+
if (await onCancel(result))
|
|
44
|
+
continue;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Like `prompt`, but pressing Escape returns `null` instead of re-prompting.
|
|
50
|
+
* Use inside sub-actions so the user can back out to the parent menu.
|
|
51
|
+
*/
|
|
52
|
+
export async function promptOrBack(fn) {
|
|
53
|
+
const result = await fn();
|
|
54
|
+
if (p.isCancel(result))
|
|
55
|
+
return null;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
* Endpoint + default model for each cloud provider akm can recommend from a
|
|
6
|
+
* detected API key. A provider absent from this table yields no recommendation
|
|
7
|
+
* (the previous paired `switch`es both returned `undefined`).
|
|
8
|
+
*/
|
|
9
|
+
export const PROVIDER_DEFAULTS = {
|
|
10
|
+
anthropic: { endpoint: "https://api.anthropic.com/v1", model: "claude-sonnet-4-5" },
|
|
11
|
+
openai: { endpoint: "https://api.openai.com/v1", model: "gpt-4o-mini" },
|
|
12
|
+
gemini: { endpoint: "https://generativelanguage.googleapis.com/v1beta/openai", model: "gemini-1.5-flash" },
|
|
13
|
+
groq: { endpoint: "https://api.groq.com/openai/v1", model: "llama-3.3-70b-versatile" },
|
|
14
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
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
|
+
* Semantic-search asset preparation for the setup wizard. Isolates the one
|
|
6
|
+
* `bun add @huggingface/transformers` subprocess and the sqlite-vec probe so
|
|
7
|
+
* the rest of setup stays free of subprocess/DB I/O.
|
|
8
|
+
*/
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import os from "node:os";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import * as p from "../cli/clack.js";
|
|
13
|
+
import { isHttpUrl } from "../core/common.js";
|
|
14
|
+
import { closeDatabase, isVecAvailable, openIndexDatabase } from "../indexer/db/db.js";
|
|
15
|
+
import { checkEmbeddingAvailability, DEFAULT_LOCAL_MODEL, isTransformersAvailable } from "../llm/embedder.js";
|
|
16
|
+
import { getDirname, spawn } from "../runtime.js";
|
|
17
|
+
// Approximate first-download sizes used in the setup note.
|
|
18
|
+
// LOCAL_MODEL_APPROX_SIZE_MB tracks the default local model (DEFAULT_LOCAL_MODEL).
|
|
19
|
+
const LOCAL_MODEL_APPROX_SIZE_MB = 130;
|
|
20
|
+
// SQLITE_VEC_APPROX_SIZE_MB reflects the optional sqlite-vec install footprint.
|
|
21
|
+
const SQLITE_VEC_APPROX_SIZE_MB = 5;
|
|
22
|
+
export function isRemoteEmbeddingConfig(embedding) {
|
|
23
|
+
return isHttpUrl(embedding?.endpoint);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* @internal Exported for testing only.
|
|
27
|
+
*/
|
|
28
|
+
export function describeSemanticSearchAssets(embedding) {
|
|
29
|
+
if (isRemoteEmbeddingConfig(embedding)) {
|
|
30
|
+
return [
|
|
31
|
+
`• Embedding endpoint: ${embedding?.provider ?? "custom"} / ${embedding?.model} (no local model download)`,
|
|
32
|
+
`• sqlite-vec acceleration: optional native extension (~${SQLITE_VEC_APPROX_SIZE_MB} MB when installed separately)`,
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
return [
|
|
36
|
+
`• Local embedding model: ${embedding?.localModel ?? DEFAULT_LOCAL_MODEL} (~${LOCAL_MODEL_APPROX_SIZE_MB} MB download on first use)`,
|
|
37
|
+
`• sqlite-vec acceleration: optional native extension (~${SQLITE_VEC_APPROX_SIZE_MB} MB when installed separately)`,
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
export async function prepareSemanticSearchAssets(config) {
|
|
41
|
+
const remote = isRemoteEmbeddingConfig(config.embedding);
|
|
42
|
+
// For local embeddings, ensure the required package is installed first.
|
|
43
|
+
if (!remote) {
|
|
44
|
+
if (!isTransformersAvailable()) {
|
|
45
|
+
const spin = p.spinner();
|
|
46
|
+
spin.start("Installing @huggingface/transformers...");
|
|
47
|
+
try {
|
|
48
|
+
const pkgRoot = path.resolve(getDirname(import.meta.url), "../..");
|
|
49
|
+
const proc = spawn(["bun", "add", "@huggingface/transformers"], {
|
|
50
|
+
cwd: pkgRoot,
|
|
51
|
+
stdout: "pipe",
|
|
52
|
+
stderr: "pipe",
|
|
53
|
+
});
|
|
54
|
+
await proc.exited;
|
|
55
|
+
if (proc.exitCode !== 0) {
|
|
56
|
+
const stderr = await new Response(proc.stderr).text();
|
|
57
|
+
throw new Error(stderr || `exit code ${proc.exitCode}`);
|
|
58
|
+
}
|
|
59
|
+
spin.stop("@huggingface/transformers installed.");
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
+
spin.stop("Could not install @huggingface/transformers.");
|
|
64
|
+
p.log.warn(`Automatic install failed: ${msg}\n` +
|
|
65
|
+
"Install it manually with: bun add @huggingface/transformers\n" +
|
|
66
|
+
"Then re-run `akm setup` or `akm index --full --verbose`.");
|
|
67
|
+
return { ok: false, reason: "missing-package", message: `Automatic install failed: ${msg}` };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const spin = p.spinner();
|
|
72
|
+
spin.start(remote
|
|
73
|
+
? "Checking remote embedding endpoint..."
|
|
74
|
+
: `Downloading local embedding model (${config.embedding?.localModel ?? DEFAULT_LOCAL_MODEL})...`);
|
|
75
|
+
const result = await checkEmbeddingAvailability(config.embedding);
|
|
76
|
+
if (!result.available) {
|
|
77
|
+
spin.stop("Semantic-search assets could not be prepared.");
|
|
78
|
+
if (result.reason === "remote-unreachable") {
|
|
79
|
+
p.log.warn("The remote embedding endpoint is not reachable. Check your endpoint and credentials, then retry `akm index --full --verbose`.");
|
|
80
|
+
return { ok: false, reason: "remote-network", message: "The remote embedding endpoint is not reachable." };
|
|
81
|
+
}
|
|
82
|
+
else if (result.reason === "missing-package") {
|
|
83
|
+
p.log.warn("@huggingface/transformers is not installed. Install it with: bun add @huggingface/transformers\n" +
|
|
84
|
+
"Then re-run `akm setup` or `akm index --full --verbose`.");
|
|
85
|
+
return { ok: false, reason: "missing-package", message: "@huggingface/transformers is not installed." };
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
p.log.warn(`The local embedding model could not be downloaded: ${result.message}\n` +
|
|
89
|
+
"Retry `akm index --full --verbose` after confirming local model downloads are permitted.");
|
|
90
|
+
return { ok: false, reason: "local-model-download", message: result.message };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
spin.stop(remote ? "Remote embedding endpoint is ready." : "Local embedding model downloaded and ready.");
|
|
94
|
+
let db;
|
|
95
|
+
let probeDir;
|
|
96
|
+
try {
|
|
97
|
+
probeDir = fs.mkdtempSync(path.join(os.tmpdir(), "akm-setup-vec-probe-"));
|
|
98
|
+
db = openIndexDatabase(path.join(probeDir, "probe.db"), config.embedding?.dimension ? { embeddingDim: config.embedding.dimension } : undefined);
|
|
99
|
+
if (isVecAvailable(db)) {
|
|
100
|
+
p.log.info("sqlite-vec is available for fast vector search.");
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
p.log.info("sqlite-vec is not available. Semantic search will use the JS fallback until the optional extension is installed.");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
p.log.warn(`Could not open the local database or check for sqlite-vec. Semantic search will use the JS fallback. (${message})\n` +
|
|
109
|
+
"Check file permissions and available disk space in the cache directory, or run `akm index --full --verbose` to diagnose.");
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (db)
|
|
113
|
+
closeDatabase(db);
|
|
114
|
+
if (probeDir) {
|
|
115
|
+
try {
|
|
116
|
+
fs.rmSync(probeDir, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
/* ignore cleanup failure */
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { ok: true };
|
|
124
|
+
}
|