akm-cli 0.9.0-beta.53 → 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/clack.js +56 -0
- package/dist/cli/confirm.js +1 -1
- 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/html-report.js +33 -10
- 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 +15 -1492
- 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/outcome-loop.js +18 -16
- package/dist/commands/improve/preparation.js +23 -5
- 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/curate.js +4 -4
- package/dist/commands/read/remember-cli.js +132 -137
- package/dist/commands/read/search-cli.js +7 -5
- package/dist/commands/read/search.js +7 -3
- package/dist/commands/read/show.js +3 -5
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +12 -0
- 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 +2 -2
- package/dist/commands/tasks/default-tasks.js +12 -0
- 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/config/config.js +12 -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/core/warn.js +21 -0
- package/dist/indexer/db/db.js +72 -709
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +3 -2
- 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 +18 -0
- package/dist/indexer/indexer.js +94 -27
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +1 -1
- package/dist/integrations/agent/spawn.js +15 -66
- package/dist/llm/client.js +12 -0
- package/dist/llm/embedder.js +26 -2
- package/dist/llm/embedders/local.js +7 -1
- package/dist/output/text/helpers.js +13 -0
- package/dist/scripts/migrate-storage.js +6903 -7424
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +49 -44
- package/dist/setup/detect.js +9 -0
- 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/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +25 -1608
- 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/backends/index.js +9 -0
- package/dist/tasks/runner.js +11 -1
- package/package.json +2 -2
- package/dist/commands/improve/homeostatic.js +0 -497
|
@@ -53,12 +53,16 @@ function appendToLogFile(level, args) {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
function warn(...args) {
|
|
56
|
+
if (sinkOverride) {
|
|
57
|
+
sinkOverride("warn", args);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
56
60
|
appendToLogFile("WARN", args);
|
|
57
61
|
if (!quiet) {
|
|
58
62
|
console.warn(...args);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
var quiet = false, logFilePath;
|
|
65
|
+
var quiet = false, logFilePath, sinkOverride;
|
|
62
66
|
var init_warn = () => {};
|
|
63
67
|
|
|
64
68
|
// node_modules/dotenv/lib/main.js
|
|
@@ -8608,8 +8612,8 @@ function resolveJournalMode(raw) {
|
|
|
8608
8612
|
warnInvalidJournalModeOnce(raw);
|
|
8609
8613
|
return "WAL";
|
|
8610
8614
|
}
|
|
8611
|
-
function resolveConfiguredJournalMode() {
|
|
8612
|
-
return resolveJournalMode(
|
|
8615
|
+
function resolveConfiguredJournalMode(env = process.env) {
|
|
8616
|
+
return resolveJournalMode(env.AKM_SQLITE_JOURNAL_MODE);
|
|
8613
8617
|
}
|
|
8614
8618
|
function warnInvalidJournalModeOnce(raw) {
|
|
8615
8619
|
if (warnedInvalid)
|
|
@@ -8635,7 +8639,7 @@ function isNetworkFilesystem(fsType) {
|
|
|
8635
8639
|
return NETWORK_FS_MAGICS.has(fsType);
|
|
8636
8640
|
}
|
|
8637
8641
|
function applyStandardPragmas(db, opts = {}) {
|
|
8638
|
-
let mode = resolveConfiguredJournalMode();
|
|
8642
|
+
let mode = resolveConfiguredJournalMode(opts.env);
|
|
8639
8643
|
if (mode === "WAL" && opts.dataDir) {
|
|
8640
8644
|
const probe = opts.fsTypeProbe ?? statfsType;
|
|
8641
8645
|
if (isNetworkFilesystem(probe(opts.dataDir))) {
|
|
@@ -8672,48 +8676,8 @@ function openManagedDatabase(spec) {
|
|
|
8672
8676
|
return db;
|
|
8673
8677
|
}
|
|
8674
8678
|
|
|
8675
|
-
// src/core/assert.ts
|
|
8676
|
-
function assertNever(x, context) {
|
|
8677
|
-
let serialized;
|
|
8678
|
-
try {
|
|
8679
|
-
serialized = JSON.stringify(x);
|
|
8680
|
-
} catch {
|
|
8681
|
-
serialized = String(x);
|
|
8682
|
-
}
|
|
8683
|
-
if (serialized === undefined) {
|
|
8684
|
-
serialized = String(x);
|
|
8685
|
-
}
|
|
8686
|
-
const where = context ? ` (${context})` : "";
|
|
8687
|
-
throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
|
|
8688
|
-
}
|
|
8689
|
-
|
|
8690
|
-
// src/core/improve-types.ts
|
|
8691
|
-
function classifyImproveAction(mode) {
|
|
8692
|
-
switch (mode) {
|
|
8693
|
-
case "reflect":
|
|
8694
|
-
case "distill":
|
|
8695
|
-
case "memory-inference":
|
|
8696
|
-
case "graph-extraction":
|
|
8697
|
-
return "accepted";
|
|
8698
|
-
case "reflect-cooldown":
|
|
8699
|
-
case "reflect-skipped":
|
|
8700
|
-
case "distill-skipped":
|
|
8701
|
-
return "skipped";
|
|
8702
|
-
case "reflect-guard-rejected":
|
|
8703
|
-
return "rejected";
|
|
8704
|
-
case "reflect-failed":
|
|
8705
|
-
case "error":
|
|
8706
|
-
return "error";
|
|
8707
|
-
case "memory-prune":
|
|
8708
|
-
return "noop";
|
|
8709
|
-
default:
|
|
8710
|
-
return assertNever(mode);
|
|
8711
|
-
}
|
|
8712
|
-
}
|
|
8713
|
-
|
|
8714
8679
|
// src/core/state-db.ts
|
|
8715
8680
|
init_paths();
|
|
8716
|
-
init_warn();
|
|
8717
8681
|
|
|
8718
8682
|
// src/storage/engines/sqlite-migrations.ts
|
|
8719
8683
|
function ensureMigrationsTable(db) {
|
|
@@ -9178,6 +9142,47 @@ function getStateDbPath() {
|
|
|
9178
9142
|
function openStateDatabase(dbPath) {
|
|
9179
9143
|
return openManagedDatabase({ path: dbPath ?? getStateDbPath(), init: runMigrations2 });
|
|
9180
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
|
|
9181
9186
|
function computeImproveRunMetrics(result) {
|
|
9182
9187
|
const plannedCount = Array.isArray(result.plannedRefs) ? result.plannedRefs.length : 0;
|
|
9183
9188
|
const actions = Array.isArray(result.actions) ? result.actions : [];
|
package/dist/setup/detect.js
CHANGED
|
@@ -14,6 +14,11 @@ import { defaultWhich } from "../integrations/agent/detect.js";
|
|
|
14
14
|
import { SESSION_LOG_HARNESSES } from "../integrations/harnesses/index.js";
|
|
15
15
|
import { spawn } from "../runtime.js";
|
|
16
16
|
import { detectHarnessConfigs } from "./harness-config-import.js";
|
|
17
|
+
let detectOverrides;
|
|
18
|
+
/** TEST-ONLY. Swap the network/host probes; pass undefined to restore. */
|
|
19
|
+
export function _setDetectForTests(fakes) {
|
|
20
|
+
detectOverrides = fakes;
|
|
21
|
+
}
|
|
17
22
|
// ── Ollama Detection ────────────────────────────────────────────────────────
|
|
18
23
|
const OLLAMA_BASE = "http://localhost:11434";
|
|
19
24
|
/**
|
|
@@ -23,6 +28,8 @@ const OLLAMA_BASE = "http://localhost:11434";
|
|
|
23
28
|
* via subprocess. Returns available models sorted alphabetically.
|
|
24
29
|
*/
|
|
25
30
|
export async function detectOllama() {
|
|
31
|
+
if (detectOverrides?.detectOllama)
|
|
32
|
+
return detectOverrides.detectOllama();
|
|
26
33
|
const result = { available: false, models: [], endpoint: OLLAMA_BASE };
|
|
27
34
|
// Try HTTP API first
|
|
28
35
|
try {
|
|
@@ -120,6 +127,8 @@ const AGENT_PLATFORMS = SESSION_LOG_HARNESSES.filter((h) => h.setupDetectionDir)
|
|
|
120
127
|
* Supports both HOME (Unix) and USERPROFILE (Windows).
|
|
121
128
|
*/
|
|
122
129
|
export function detectAgentPlatforms() {
|
|
130
|
+
if (detectOverrides?.detectAgentPlatforms)
|
|
131
|
+
return detectOverrides.detectAgentPlatforms();
|
|
123
132
|
const home = process.env.HOME?.trim() || process.env.USERPROFILE?.trim();
|
|
124
133
|
if (!home)
|
|
125
134
|
return [];
|
|
@@ -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
|
+
};
|
|
@@ -44,6 +44,13 @@ const FALLBACK_STASHES = [
|
|
|
44
44
|
defaultSelected: false,
|
|
45
45
|
},
|
|
46
46
|
];
|
|
47
|
+
// ── Test seam ────────────────────────────────────────────────────────────────
|
|
48
|
+
// Swap-and-restore override. Inert in production; only tests call the setter.
|
|
49
|
+
let loadSetupStashesOverride;
|
|
50
|
+
/** TEST-ONLY. Swap the implementation of `loadSetupStashes`; pass undefined to restore. */
|
|
51
|
+
export function _setLoadSetupStashesForTests(fake) {
|
|
52
|
+
loadSetupStashesOverride = fake;
|
|
53
|
+
}
|
|
47
54
|
// ── Loader ──────────────────────────────────────────────────────────────────
|
|
48
55
|
/**
|
|
49
56
|
* Fetch available stashes from the registry and map to SetupStashEntry[].
|
|
@@ -55,6 +62,11 @@ const FALLBACK_STASHES = [
|
|
|
55
62
|
* @param timeoutMs Fetch timeout in ms (default: 4000).
|
|
56
63
|
*/
|
|
57
64
|
export async function loadSetupStashes(registryUrl, timeoutMs = 4000) {
|
|
65
|
+
if (loadSetupStashesOverride)
|
|
66
|
+
return loadSetupStashesOverride(registryUrl, timeoutMs);
|
|
67
|
+
return loadSetupStashesReal(registryUrl, timeoutMs);
|
|
68
|
+
}
|
|
69
|
+
async function loadSetupStashesReal(registryUrl, timeoutMs = 4000) {
|
|
58
70
|
try {
|
|
59
71
|
const response = await fetch(registryUrl, {
|
|
60
72
|
signal: AbortSignal.timeout(timeoutMs),
|
|
@@ -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
|
+
}
|