akm-cli 0.9.0-rc.1 → 0.9.0-rc.2
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 +223 -9
- package/SECURITY.md +23 -24
- package/dist/assets/help/help-improve.md +10 -10
- package/dist/assets/hints/cli-hints-full.md +44 -10
- package/dist/assets/hints/cli-hints-short.md +6 -2
- package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
- package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
- package/dist/assets/stash-skeleton/README.md +28 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
- package/dist/assets/tasks/core/backup.yml +1 -0
- package/dist/assets/tasks/core/extract.yml +1 -0
- package/dist/assets/tasks/core/improve.yml +1 -0
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +1 -0
- package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
- package/dist/assets/templates/html/health.html +5 -1
- package/dist/cli/config-migrate.js +31 -138
- package/dist/cli/config-validate.js +10 -8
- package/dist/cli.js +48 -14
- package/dist/commands/agent/agent-dispatch.js +17 -16
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +5 -15
- package/dist/commands/backup-cli.js +54 -0
- package/dist/commands/config-cli.js +45 -159
- package/dist/commands/env/secret.js +8 -5
- package/dist/commands/health/checks.js +130 -83
- package/dist/commands/health/html-report.js +4 -0
- package/dist/commands/health/improve-metrics.js +30 -32
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +4 -0
- package/dist/commands/health/metrics.js +2 -1
- package/dist/commands/health/surfaces.js +5 -4
- package/dist/commands/health.js +1 -1
- package/dist/commands/improve/consolidate/chunking.js +2 -2
- package/dist/commands/improve/consolidate.js +28 -25
- package/dist/commands/improve/distill/promote-memory.js +5 -12
- package/dist/commands/improve/distill/quality-gate.js +5 -7
- package/dist/commands/improve/distill.js +16 -5
- package/dist/commands/improve/eligibility.js +22 -12
- package/dist/commands/improve/extract-cli.js +47 -19
- package/dist/commands/improve/extract.js +110 -62
- package/dist/commands/improve/improve-cli.js +38 -16
- package/dist/commands/improve/improve-result-file.js +30 -24
- package/dist/commands/improve/improve-strategies.js +137 -0
- package/dist/commands/improve/improve.js +60 -30
- package/dist/commands/improve/locks.js +66 -45
- package/dist/commands/improve/loop-stages.js +75 -33
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
- package/dist/commands/improve/preparation.js +71 -73
- package/dist/commands/improve/procedural.js +3 -2
- package/dist/commands/improve/recombine.js +2 -1
- package/dist/commands/improve/reflect.js +119 -214
- package/dist/commands/improve/shared.js +11 -5
- package/dist/commands/lint/base-linter.js +152 -42
- package/dist/commands/mv-cli.js +809 -0
- package/dist/commands/proposal/proposal-cli.js +18 -8
- package/dist/commands/proposal/propose.js +64 -69
- package/dist/commands/read/knowledge.js +436 -4
- package/dist/commands/read/remember-cli.js +39 -2
- package/dist/commands/read/search-cli.js +6 -1
- package/dist/commands/registry-cli.js +29 -14
- package/dist/commands/remember.js +2 -0
- package/dist/commands/sources/init.js +13 -14
- package/dist/commands/sources/schema-repair.js +2 -4
- package/dist/commands/sources/source-add.js +62 -73
- package/dist/commands/sources/source-manage.js +50 -46
- package/dist/commands/sources/stash-cli.js +41 -4
- package/dist/commands/tasks/default-tasks.js +12 -12
- package/dist/commands/tasks/tasks-cli.js +7 -3
- package/dist/commands/tasks/tasks.js +113 -18
- package/dist/commands/wiki-cli.js +9 -10
- package/dist/core/asset/frontmatter.js +12 -2
- package/dist/core/common.js +5 -3
- package/dist/core/config/config-io.js +28 -17
- package/dist/core/config/config-schema.js +358 -66
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +98 -27
- package/dist/core/config/config.js +132 -266
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +32 -0
- package/dist/core/errors.js +2 -2
- package/dist/core/extra-params.js +61 -0
- package/dist/core/file-lock.js +201 -56
- package/dist/core/improve-result.js +178 -0
- package/dist/core/maintenance-barrier.js +119 -0
- package/dist/core/migration-backup.js +416 -0
- package/dist/core/paths.js +3 -0
- package/dist/core/redaction.js +358 -0
- package/dist/core/state/migrations.js +17 -2
- package/dist/core/state-db.js +44 -1
- package/dist/indexer/db/db.js +116 -1
- package/dist/indexer/graph/graph-extraction.js +28 -16
- package/dist/indexer/index-writer-lock.js +31 -24
- package/dist/indexer/index-written-assets.js +15 -6
- package/dist/indexer/indexer.js +47 -2
- package/dist/indexer/passes/memory-inference.js +10 -6
- package/dist/indexer/passes/metadata.js +250 -0
- package/dist/indexer/search/db-search.js +111 -44
- package/dist/indexer/search/fts-query.js +41 -0
- package/dist/indexer/search/ranking-contributors.js +48 -0
- package/dist/indexer/search/ranking.js +36 -23
- package/dist/indexer/search/search-fields.js +11 -1
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -52
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +202 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +7 -2
- package/dist/integrations/agent/profiles.js +6 -99
- package/dist/integrations/agent/runner-dispatch.js +76 -13
- package/dist/integrations/agent/runner.js +76 -207
- package/dist/integrations/agent/spawn.js +4 -6
- package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
- package/dist/integrations/harnesses/claude/index.js +0 -2
- package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +1 -24
- package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
- package/dist/integrations/harnesses/opencode/index.js +0 -6
- package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
- package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -32
- package/dist/integrations/lockfile.js +32 -21
- package/dist/llm/client.js +48 -14
- package/dist/llm/feature-gate.js +15 -47
- package/dist/llm/graph-extract.js +1 -1
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer-impl.js +1 -1
- package/dist/llm/usage-persist.js +4 -0
- package/dist/llm/usage-telemetry.js +35 -5
- package/dist/output/shapes/helpers.js +2 -1
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/helpers.js +3 -1
- package/dist/schemas/akm-config.json +11013 -8600
- package/dist/schemas/akm-task.json +87 -0
- package/dist/schemas/akm-workflow.json +57 -13
- package/dist/scripts/migrate-storage.js +8591 -509
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8623 -484
- package/dist/setup/detected-engines.js +142 -0
- package/dist/setup/engine-config.js +89 -0
- package/dist/setup/setup.js +236 -132
- package/dist/setup/steps/connection.js +61 -32
- package/dist/setup/steps/platforms.js +4 -4
- package/dist/setup/steps.js +3 -2
- package/dist/storage/database.js +13 -1
- package/dist/storage/engines/sqlite-migrations.js +1 -0
- package/dist/storage/repositories/improve-runs-repository.js +5 -5
- package/dist/storage/repositories/task-history-repository.js +78 -0
- package/dist/storage/repositories/workflow-runs-repository.js +9 -8
- package/dist/tasks/parser.js +138 -52
- package/dist/tasks/runner.js +71 -75
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/validator.js +11 -6
- package/dist/wiki/wiki.js +9 -8
- package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/db.js +65 -13
- package/dist/workflows/exec/brief.js +18 -24
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +124 -65
- package/dist/workflows/exec/report.js +93 -33
- package/dist/workflows/exec/run-workflow.js +38 -25
- package/dist/workflows/exec/scheduler.js +12 -41
- package/dist/workflows/exec/step-work.js +91 -35
- package/dist/workflows/ir/compile.js +13 -26
- package/dist/workflows/ir/freeze.js +243 -0
- package/dist/workflows/ir/plan-hash.js +40 -5
- package/dist/workflows/ir/schema.js +542 -1
- package/dist/workflows/parser.js +7 -0
- package/dist/workflows/program/parser.js +132 -23
- package/dist/workflows/program/project.js +3 -4
- package/dist/workflows/program/schema.js +2 -2
- package/dist/workflows/resource-limits.js +20 -0
- package/dist/workflows/runtime/plan-classifier.js +187 -0
- package/dist/workflows/runtime/runs.js +54 -47
- package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
- package/dist/workflows/validator.js +25 -0
- package/docs/data-and-telemetry.md +2 -2
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +5 -4
- package/docs/migration/v0.8-to-v0.9.md +401 -0
- package/package.json +4 -2
- package/schemas/akm-config.json +16638 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +372 -0
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/llm/call-ai.js +0 -62
- package/dist/setup/legacy-config.js +0 -106
- package/docs/README.md +0 -104
- /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
- /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
/** Compare legacy numeric or semver-like config versions for diagnostics only. */
|
|
5
|
+
export function compareConfigVersion(a, b) {
|
|
6
|
+
const left = normalize(a);
|
|
7
|
+
const right = normalize(b);
|
|
8
|
+
if (!left || !right)
|
|
9
|
+
return undefined;
|
|
10
|
+
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
|
|
11
|
+
const l = left[index] ?? 0;
|
|
12
|
+
const r = right[index] ?? 0;
|
|
13
|
+
if (l < r)
|
|
14
|
+
return -1;
|
|
15
|
+
if (l > r)
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
function normalize(value) {
|
|
21
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
22
|
+
return [0, Math.trunc(value), 0];
|
|
23
|
+
if (typeof value !== "string" || !value.trim())
|
|
24
|
+
return undefined;
|
|
25
|
+
const parts = value.trim().replace(/^v/i, "").split(/[-+]/, 1)[0].split(".");
|
|
26
|
+
if (parts.some((part) => !/^\d+$/.test(part)))
|
|
27
|
+
return undefined;
|
|
28
|
+
return parts.map(Number);
|
|
29
|
+
}
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
import { UsageError } from "../errors.js";
|
|
25
|
-
import { AkmConfigBaseSchema, listTopLevelConfigKeys } from "./config-schema.js";
|
|
25
|
+
import { AkmConfigBaseSchema, EngineConfigSchema, listTopLevelConfigKeys } from "./config-schema.js";
|
|
26
|
+
import { deepMergeConfig } from "./deep-merge.js";
|
|
26
27
|
/**
|
|
27
28
|
* Parse a dotted path into segments. Empty segments are rejected. Bracket
|
|
28
29
|
* notation (e.g. `sources[0]`) is NOT supported — arrays are set as JSON.
|
|
@@ -73,9 +74,10 @@ function unwrap(schema) {
|
|
|
73
74
|
* AkmConfig schema. Returns `undefined` if any path segment doesn't match a
|
|
74
75
|
* known schema field.
|
|
75
76
|
*/
|
|
76
|
-
function resolveSchemaAt(path) {
|
|
77
|
+
function resolveSchemaAt(path, config, raw) {
|
|
77
78
|
let schema = AkmConfigBaseSchema;
|
|
78
|
-
|
|
79
|
+
let existing = config;
|
|
80
|
+
for (const [index, segment] of path.entries()) {
|
|
79
81
|
schema = unwrap(schema);
|
|
80
82
|
if (schema instanceof z.ZodObject) {
|
|
81
83
|
const next = schema.shape[segment];
|
|
@@ -84,23 +86,56 @@ function resolveSchemaAt(path) {
|
|
|
84
86
|
const catchall = schema._def.catchall;
|
|
85
87
|
if (catchall && !(catchall instanceof z.ZodNever)) {
|
|
86
88
|
schema = catchall;
|
|
87
|
-
continue;
|
|
88
89
|
}
|
|
89
|
-
|
|
90
|
+
else {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
schema = next;
|
|
90
96
|
}
|
|
91
|
-
schema = next;
|
|
92
97
|
}
|
|
93
98
|
else if (schema instanceof z.ZodRecord) {
|
|
94
|
-
//
|
|
99
|
+
// Named records (engines, improve.strategies, sources, etc.)
|
|
95
100
|
// accept any string key — descend into the value schema.
|
|
96
101
|
schema = schema._def.valueType;
|
|
97
102
|
}
|
|
103
|
+
else if (schema instanceof z.ZodUnion) {
|
|
104
|
+
const option = selectUnionObjectOption(schema._def.options, segment, existing, index === path.length - 1 ? raw : undefined);
|
|
105
|
+
if (!option)
|
|
106
|
+
return undefined;
|
|
107
|
+
schema = option.shape[segment];
|
|
108
|
+
}
|
|
98
109
|
else {
|
|
99
110
|
// Cannot descend into a non-object schema.
|
|
100
111
|
return undefined;
|
|
101
112
|
}
|
|
113
|
+
existing = isPlainObject(existing) ? existing[segment] : undefined;
|
|
102
114
|
}
|
|
103
|
-
|
|
115
|
+
// Preserve leaf refinements/transforms for validation. Traversal unwraps at
|
|
116
|
+
// the start of each loop iteration, while coercion unwraps independently.
|
|
117
|
+
return schema;
|
|
118
|
+
}
|
|
119
|
+
function selectUnionObjectOption(candidates, segment, existing, raw) {
|
|
120
|
+
const options = candidates
|
|
121
|
+
.map((candidate) => unwrap(candidate))
|
|
122
|
+
.filter((candidate) => candidate instanceof z.ZodObject);
|
|
123
|
+
if (raw !== undefined) {
|
|
124
|
+
const requested = options.find((option) => {
|
|
125
|
+
const leaf = option.shape[segment];
|
|
126
|
+
if (!leaf || !(unwrap(leaf) instanceof z.ZodLiteral))
|
|
127
|
+
return false;
|
|
128
|
+
return leaf.safeParse(coerceForSchema(leaf, raw, segment)).success;
|
|
129
|
+
});
|
|
130
|
+
if (requested)
|
|
131
|
+
return requested;
|
|
132
|
+
}
|
|
133
|
+
if (isPlainObject(existing)) {
|
|
134
|
+
const selected = options.find((option) => Object.entries(option.shape).some(([key, field]) => key in existing && unwrap(field) instanceof z.ZodLiteral && field.safeParse(existing[key]).success));
|
|
135
|
+
if (selected)
|
|
136
|
+
return segment in selected.shape ? selected : undefined;
|
|
137
|
+
}
|
|
138
|
+
return options.find((option) => segment in option.shape);
|
|
104
139
|
}
|
|
105
140
|
/**
|
|
106
141
|
* Get the value at the dotted path from a config-shaped object. Returns
|
|
@@ -109,7 +144,7 @@ function resolveSchemaAt(path) {
|
|
|
109
144
|
*/
|
|
110
145
|
export function configGet(config, dotted) {
|
|
111
146
|
const path = parsePath(dotted);
|
|
112
|
-
const schema = resolveSchemaAt(path);
|
|
147
|
+
const schema = resolveSchemaAt(path, config);
|
|
113
148
|
if (!schema) {
|
|
114
149
|
throw new UsageError(`Unknown config key: ${dotted}`, "INVALID_FLAG_VALUE", unknownKeyHint(dotted));
|
|
115
150
|
}
|
|
@@ -129,23 +164,38 @@ export function configGet(config, dotted) {
|
|
|
129
164
|
*
|
|
130
165
|
* Throws {@link UsageError} on:
|
|
131
166
|
* - unknown path
|
|
132
|
-
* - apiKey paths
|
|
167
|
+
* - invalid apiKey paths
|
|
133
168
|
* - coercion failure
|
|
134
169
|
*/
|
|
135
170
|
export function configSet(config, dotted, raw) {
|
|
136
171
|
const path = parsePath(dotted);
|
|
172
|
+
if (path.length === 1 && path[0] === "configVersion") {
|
|
173
|
+
throw new UsageError("configVersion cannot be changed through config set.", "INVALID_FLAG_VALUE");
|
|
174
|
+
}
|
|
137
175
|
// #454: apiKey paths are not persistable. Throw at set time.
|
|
138
176
|
rejectApiKeyPath(path, dotted);
|
|
139
|
-
const schema = resolveSchemaAt(path);
|
|
140
|
-
|
|
177
|
+
const schema = resolveSchemaAt(path, config, raw);
|
|
178
|
+
const symbolicApiKey = (path[0] === "engines" && path[2] === "apiKey") ||
|
|
179
|
+
(path[0] === "embedding" && path.length === 2 && path[1] === "apiKey");
|
|
180
|
+
if (!schema && !symbolicApiKey) {
|
|
141
181
|
throw new UsageError(`Unknown config key: ${dotted}`, "INVALID_FLAG_VALUE", unknownKeyHint(dotted));
|
|
142
182
|
}
|
|
143
|
-
const value =
|
|
183
|
+
const value = path[0] === "engines" && path.length === 2
|
|
184
|
+
? parseObjectPatch(raw, dotted)
|
|
185
|
+
: symbolicApiKey
|
|
186
|
+
? raw
|
|
187
|
+
: coerceForSchema(schema, raw, dotted);
|
|
144
188
|
// Validate the coerced value against the leaf schema. This catches enum
|
|
145
189
|
// mismatches, out-of-range numbers, schema-level shape errors (writable
|
|
146
190
|
// npm/website sources via .superRefine, strict-mode unknown keys in nested
|
|
147
191
|
// objects, etc.) BEFORE we apply the patch.
|
|
148
|
-
const parsed =
|
|
192
|
+
const parsed = path[0] === "engines" && path.length === 2
|
|
193
|
+
? EngineConfigSchema.safeParse(value)
|
|
194
|
+
: symbolicApiKey
|
|
195
|
+
? /^\$[A-Za-z_][A-Za-z0-9_]*$|^\$\{[A-Za-z_][A-Za-z0-9_]*\}$/.test(raw)
|
|
196
|
+
? { success: true, data: value }
|
|
197
|
+
: { success: false, error: { issues: [{ path: [], message: `apiKey must be $VAR or \${VAR}` }] } }
|
|
198
|
+
: schema.safeParse(value);
|
|
149
199
|
if (!parsed.success) {
|
|
150
200
|
const lines = parsed.error.issues
|
|
151
201
|
.map((i) => {
|
|
@@ -155,7 +205,12 @@ export function configSet(config, dotted, raw) {
|
|
|
155
205
|
.join("\n");
|
|
156
206
|
throw new UsageError(`Invalid value for ${dotted}:\n${lines}`, "INVALID_FLAG_VALUE");
|
|
157
207
|
}
|
|
158
|
-
const
|
|
208
|
+
const existing = path.reduce((value, key) => {
|
|
209
|
+
if (value && typeof value === "object")
|
|
210
|
+
return value[key];
|
|
211
|
+
return undefined;
|
|
212
|
+
}, config);
|
|
213
|
+
const next = setPath(config, path, isPlainObject(existing) && isPlainObject(parsed.data) ? deepMergeConfig(existing, parsed.data) : parsed.data);
|
|
159
214
|
// Targeted invariant: defaultWriteTarget must point at a configured source
|
|
160
215
|
// (#464.a). Whole-config validation happens at save time; this check fires
|
|
161
216
|
// at set time so the user sees the typo immediately. Empty-sources case is
|
|
@@ -175,8 +230,11 @@ export function configSet(config, dotted, raw) {
|
|
|
175
230
|
*/
|
|
176
231
|
export function configUnset(config, dotted) {
|
|
177
232
|
const path = parsePath(dotted);
|
|
233
|
+
if (path.length === 1 && path[0] === "configVersion") {
|
|
234
|
+
throw new UsageError("configVersion cannot be removed through config unset.", "INVALID_FLAG_VALUE");
|
|
235
|
+
}
|
|
178
236
|
// Validate the path resolves to a real schema field (so typos don't no-op).
|
|
179
|
-
const schema = resolveSchemaAt(path);
|
|
237
|
+
const schema = resolveSchemaAt(path, config);
|
|
180
238
|
if (!schema) {
|
|
181
239
|
throw new UsageError(`Unknown config key: ${dotted}`, "INVALID_FLAG_VALUE", unknownKeyHint(dotted));
|
|
182
240
|
}
|
|
@@ -184,12 +242,14 @@ export function configUnset(config, dotted) {
|
|
|
184
242
|
}
|
|
185
243
|
// ── apiKey rejection (#454) ─────────────────────────────────────────────────
|
|
186
244
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* - `embedding.apiKey`
|
|
190
|
-
* - `profiles.llm.<name>.apiKey`
|
|
245
|
+
* Embedding credentials are never persisted. Engine credentials are symbolic
|
|
246
|
+
* configuration and are validated by the engine schema below.
|
|
191
247
|
*/
|
|
192
248
|
function rejectApiKeyPath(path, dotted) {
|
|
249
|
+
if (path[0] === "engines" && path[2] === "apiKey")
|
|
250
|
+
return;
|
|
251
|
+
if (path[0] === "embedding" && path.length === 2 && path[1] === "apiKey")
|
|
252
|
+
return;
|
|
193
253
|
const last = path[path.length - 1];
|
|
194
254
|
if (last !== "apiKey")
|
|
195
255
|
return;
|
|
@@ -197,14 +257,25 @@ function rejectApiKeyPath(path, dotted) {
|
|
|
197
257
|
throw new UsageError(`apiKey cannot be persisted in config; export ${recipe} instead. (key: ${dotted})`, "INVALID_FLAG_VALUE", "Storing API keys in config.json leaks them through backups, logs, and version control. " +
|
|
198
258
|
"Use the corresponding environment variable. AKM reads it at request time.");
|
|
199
259
|
}
|
|
260
|
+
function parseObjectPatch(raw, key) {
|
|
261
|
+
try {
|
|
262
|
+
const value = JSON.parse(raw);
|
|
263
|
+
if (!isPlainObject(value))
|
|
264
|
+
throw new Error("expected an object");
|
|
265
|
+
return value;
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
throw new UsageError(`Invalid JSON object for ${key}: ${err instanceof Error ? err.message : String(err)}`, "INVALID_JSON_CONFIG_VALUE");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function isPlainObject(value) {
|
|
272
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
273
|
+
}
|
|
200
274
|
function recipeForApiKey(path, _dotted) {
|
|
201
275
|
if (path[0] === "embedding")
|
|
202
276
|
return "AKM_EMBED_API_KEY";
|
|
203
|
-
if (path[0] === "
|
|
204
|
-
return "
|
|
205
|
-
if (path[0] === "profiles" && path[1] === "llm" && typeof path[2] === "string") {
|
|
206
|
-
const upper = path[2].toUpperCase().replace(/-/g, "_");
|
|
207
|
-
return `AKM_PROFILE_${upper}_API_KEY (or AKM_LLM_API_KEY for the default profile)`;
|
|
277
|
+
if (path[0] === "engines" && typeof path[1] === "string") {
|
|
278
|
+
return `AKM_ENGINE_${path[1].toUpperCase().replaceAll("-", "_")}_API_KEY`;
|
|
208
279
|
}
|
|
209
280
|
return "AKM_LLM_API_KEY / AKM_EMBED_API_KEY";
|
|
210
281
|
}
|
|
@@ -237,7 +308,7 @@ function coerceForSchema(schema, raw, key) {
|
|
|
237
308
|
return raw;
|
|
238
309
|
}
|
|
239
310
|
if (target instanceof z.ZodLiteral) {
|
|
240
|
-
return target._def.value;
|
|
311
|
+
return typeof target._def.value === "string" ? raw : target._def.value;
|
|
241
312
|
}
|
|
242
313
|
if (target instanceof z.ZodArray || target instanceof z.ZodObject || target instanceof z.ZodRecord) {
|
|
243
314
|
if (raw === "" || raw === "null")
|
|
@@ -333,5 +404,5 @@ function unsetPath(config, path) {
|
|
|
333
404
|
// ── Hint generation (#460) ──────────────────────────────────────────────────
|
|
334
405
|
export function unknownKeyHint(_attempted) {
|
|
335
406
|
const keys = listTopLevelConfigKeys();
|
|
336
|
-
return `Valid top-level keys: ${keys.join(", ")}. Use dotted paths for nested values (e.g. embedding.endpoint,
|
|
407
|
+
return `Valid top-level keys: ${keys.join(", ")}. Use dotted paths for nested values (e.g. embedding.endpoint, engines.<name>.model).`;
|
|
337
408
|
}
|