akm-cli 0.7.5 → 0.8.0-rc.10
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/{.github/CHANGELOG.md → CHANGELOG.md} +192 -2
- package/README.md +22 -6
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2569 -1449
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +2122 -0
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1075 -77
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +5 -23
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +86 -31
- package/dist/commands/reflect.js +1091 -73
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +69 -6
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +148 -25
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +364 -981
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +91 -138
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +163 -254
- package/dist/indexer/db.js +975 -103
- package/dist/indexer/ensure-index.js +64 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -124
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +523 -301
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +6 -17
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +214 -80
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +118 -23
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +77 -124
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -70
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +77 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -320
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +300 -257
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -516
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1092
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +138 -21
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +227 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +77 -92
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +30 -12
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -328
- package/dist/templates/wiki-templates.js +0 -100
|
@@ -1,559 +1,155 @@
|
|
|
1
|
-
|
|
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
|
+
* Config CLI commands — `akm config get/set/unset/list`.
|
|
6
|
+
*
|
|
7
|
+
* Thin wrappers around the schema walker in `core/config-walker.ts`. Adding a
|
|
8
|
+
* new config field is one line of Zod schema in `core/config-schema.ts` and
|
|
9
|
+
* zero lines here — the walker handles get/set/unset/coercion uniformly.
|
|
10
|
+
*
|
|
11
|
+
* Legacy behaviour preserved:
|
|
12
|
+
* - `akm config set llm.<x>` writes to `profiles.llm.<defaults.llm>` (or
|
|
13
|
+
* auto-creates a "default" profile), mirroring the pre-rewrite shim.
|
|
14
|
+
* - `akm config set embedding.ollamaOptions.numCtx` is sugar for
|
|
15
|
+
* `embedding.ollamaOptions.num_ctx` (camelCase ↔ snake_case bridge).
|
|
16
|
+
* - `parseConfigValue` returns a Partial<AkmConfig> so it can be merged with
|
|
17
|
+
* the runtime config object via `mergeConfigValue`.
|
|
18
|
+
*/
|
|
19
|
+
import { DEFAULT_CONFIG, getSources } from "../core/config";
|
|
20
|
+
import { configGet, configSet, configUnset, unknownKeyHint } from "../core/config-walker";
|
|
2
21
|
import { UsageError } from "../core/errors";
|
|
3
|
-
// ──
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
case "llm.endpoint":
|
|
43
|
-
return { llm: mergeLlmLike(undefined, { endpoint: requireNonEmptyString(value, key) }) };
|
|
44
|
-
case "llm.model":
|
|
45
|
-
return { llm: mergeLlmLike(undefined, { model: requireNonEmptyString(value, key) }) };
|
|
46
|
-
case "llm.apiKey":
|
|
47
|
-
return { llm: mergeLlmLike(undefined, { apiKey: requireNonEmptyString(value, key) }) };
|
|
48
|
-
case "registries":
|
|
49
|
-
return { registries: parseRegistriesValue(value) };
|
|
50
|
-
case "sources":
|
|
51
|
-
case "stashes":
|
|
52
|
-
// "stashes" is kept as an alias for backwards-compat; both write to `sources`.
|
|
53
|
-
return { sources: parseStashesValue(value) };
|
|
54
|
-
case "output.format":
|
|
55
|
-
return { output: { format: parseOutputFormat(value) } };
|
|
56
|
-
case "output.detail":
|
|
57
|
-
return { output: { detail: parseOutputDetail(value) } };
|
|
58
|
-
case "security.installAudit.enabled":
|
|
59
|
-
return { security: { installAudit: { enabled: parseBooleanValue(value, key) } } };
|
|
60
|
-
case "security.installAudit.blockOnCritical":
|
|
61
|
-
return { security: { installAudit: { blockOnCritical: parseBooleanValue(value, key) } } };
|
|
62
|
-
case "security.installAudit.blockUnlistedRegistries":
|
|
63
|
-
return { security: { installAudit: { blockUnlistedRegistries: parseBooleanValue(value, key) } } };
|
|
64
|
-
case "security.installAudit.registryAllowlist":
|
|
65
|
-
return { security: { installAudit: { registryAllowlist: parseStringArrayValue(value, key) } } };
|
|
66
|
-
case "security.installAudit.registryWhitelist":
|
|
67
|
-
return { security: { installAudit: { registryAllowlist: parseStringArrayValue(value, key) } } };
|
|
68
|
-
case "security.installAudit.allowedFindings":
|
|
69
|
-
return { security: { installAudit: { allowedFindings: parseAllowedFindingsValue(value, key) } } };
|
|
70
|
-
default:
|
|
71
|
-
throw new UsageError(`Unknown config key: ${key}`, "INVALID_FLAG_VALUE", UNKNOWN_CONFIG_KEY_HINT);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const UNKNOWN_CONFIG_KEY_HINT = "Valid top-level keys: stashDir, embedding, llm, registries, sources, agent, output, semanticSearchMode. Use dotted paths like `embedding.endpoint` or `output.format` for nested values.";
|
|
22
|
+
// ── Legacy `llm.*` → `profiles.llm.<default>.*` aliasing ────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* Map a legacy top-level `llm.<sub>` path onto the actual schema path. The
|
|
25
|
+
* default profile name is "default" when `defaults.llm` is unset.
|
|
26
|
+
*/
|
|
27
|
+
function rewriteLegacyLlmPath(config, key) {
|
|
28
|
+
if (key !== "llm" && !key.startsWith("llm."))
|
|
29
|
+
return key;
|
|
30
|
+
const sub = key === "llm" ? "" : key.slice("llm.".length);
|
|
31
|
+
const profileName = config.defaults?.llm ?? "default";
|
|
32
|
+
return sub ? `profiles.llm.${profileName}.${sub}` : `profiles.llm.${profileName}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Translate the legacy `embedding.ollamaOptions.numCtx` to the actual schema
|
|
36
|
+
* key `embedding.ollamaOptions.num_ctx`.
|
|
37
|
+
*/
|
|
38
|
+
function rewriteEmbeddingPath(key) {
|
|
39
|
+
if (key === "embedding.ollamaOptions.numCtx")
|
|
40
|
+
return "embedding.ollamaOptions.num_ctx";
|
|
41
|
+
return key;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Translate the deprecated `stashes` alias for `sources` (one-way: both read
|
|
45
|
+
* and write go through `sources`).
|
|
46
|
+
*/
|
|
47
|
+
function rewriteSourcesAlias(key) {
|
|
48
|
+
if (key === "stashes")
|
|
49
|
+
return "sources";
|
|
50
|
+
if (key.startsWith("stashes."))
|
|
51
|
+
return `sources.${key.slice("stashes.".length)}`;
|
|
52
|
+
return key;
|
|
53
|
+
}
|
|
54
|
+
function rewriteKey(config, key) {
|
|
55
|
+
let k = rewriteLegacyLlmPath(config, key);
|
|
56
|
+
k = rewriteEmbeddingPath(k);
|
|
57
|
+
k = rewriteSourcesAlias(k);
|
|
58
|
+
return k;
|
|
59
|
+
}
|
|
60
|
+
// ── Public API ──────────────────────────────────────────────────────────────
|
|
75
61
|
export function getConfigValue(config, key) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return config.stashDir ?? null;
|
|
79
|
-
case "defaultWriteTarget":
|
|
80
|
-
return config.defaultWriteTarget ?? null;
|
|
81
|
-
case "semanticSearchMode":
|
|
82
|
-
return config.semanticSearchMode;
|
|
83
|
-
case "embedding":
|
|
84
|
-
return config.embedding ?? null;
|
|
85
|
-
case "embedding.endpoint":
|
|
86
|
-
return config.embedding?.endpoint ?? null;
|
|
87
|
-
case "embedding.model":
|
|
88
|
-
return config.embedding?.model ?? null;
|
|
89
|
-
case "embedding.apiKey":
|
|
90
|
-
return config.embedding?.apiKey ?? null;
|
|
91
|
-
case "embedding.contextLength":
|
|
92
|
-
return config.embedding?.contextLength ?? null;
|
|
93
|
-
case "embedding.ollamaOptions.numCtx":
|
|
94
|
-
return config.embedding?.ollamaOptions?.num_ctx ?? null;
|
|
95
|
-
case "llm":
|
|
96
|
-
return config.llm ?? null;
|
|
97
|
-
case "llm.endpoint":
|
|
98
|
-
return config.llm?.endpoint ?? null;
|
|
99
|
-
case "llm.model":
|
|
100
|
-
return config.llm?.model ?? null;
|
|
101
|
-
case "llm.apiKey":
|
|
102
|
-
return config.llm?.apiKey ?? null;
|
|
103
|
-
case "registries":
|
|
104
|
-
return config.registries ?? DEFAULT_CONFIG.registries ?? [];
|
|
105
|
-
case "sources":
|
|
106
|
-
case "stashes":
|
|
107
|
-
// "stashes" is an alias for "sources" for backwards-compat.
|
|
108
|
-
return config.sources ?? config.stashes ?? [];
|
|
109
|
-
case "output.format":
|
|
110
|
-
return config.output?.format ?? null;
|
|
111
|
-
case "output.detail":
|
|
112
|
-
return config.output?.detail ?? null;
|
|
113
|
-
case "security":
|
|
114
|
-
return config.security ?? null;
|
|
115
|
-
case "security.installAudit.enabled":
|
|
116
|
-
return config.security?.installAudit?.enabled ?? null;
|
|
117
|
-
case "security.installAudit.blockOnCritical":
|
|
118
|
-
return config.security?.installAudit?.blockOnCritical ?? null;
|
|
119
|
-
case "security.installAudit.blockUnlistedRegistries":
|
|
120
|
-
return config.security?.installAudit?.blockUnlistedRegistries ?? null;
|
|
121
|
-
case "security.installAudit.registryAllowlist":
|
|
122
|
-
return getInstallAuditAllowlist(config);
|
|
123
|
-
case "security.installAudit.registryWhitelist":
|
|
124
|
-
return getInstallAuditAllowlist(config);
|
|
125
|
-
case "security.installAudit.allowedFindings":
|
|
126
|
-
return config.security?.installAudit?.allowedFindings ?? null;
|
|
127
|
-
default:
|
|
128
|
-
throw new UsageError(`Unknown config key: ${key}`, "INVALID_FLAG_VALUE", UNKNOWN_CONFIG_KEY_HINT);
|
|
129
|
-
}
|
|
62
|
+
const k = rewriteKey(config, key);
|
|
63
|
+
return configGet(config, k);
|
|
130
64
|
}
|
|
131
65
|
export function setConfigValue(config, key, rawValue) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
...config,
|
|
163
|
-
embedding: mergeLlmLikeEmbedding(config.embedding, { apiKey: requireNonEmptyString(rawValue, key) }),
|
|
164
|
-
};
|
|
165
|
-
case "embedding.contextLength":
|
|
166
|
-
return {
|
|
167
|
-
...config,
|
|
168
|
-
embedding: mergeLlmLikeEmbedding(config.embedding, { contextLength: parsePositiveInteger(rawValue, key) }),
|
|
66
|
+
// #454: reject the legacy aliases up front so the error message names the
|
|
67
|
+
// env var the user typed (AKM_LLM_API_KEY) rather than the rewritten profile
|
|
68
|
+
// env var (AKM_PROFILE_DEFAULT_API_KEY) — both work at runtime, but the
|
|
69
|
+
// shorter name matches the user's mental model.
|
|
70
|
+
if (key === "llm.apiKey") {
|
|
71
|
+
throw new UsageError("apiKey cannot be persisted in config; export AKM_LLM_API_KEY instead. (key: llm.apiKey)", "INVALID_FLAG_VALUE", "Storing API keys in config.json leaks them through backups, logs, and version control. " +
|
|
72
|
+
"Use the corresponding environment variable. AKM reads it at request time.");
|
|
73
|
+
}
|
|
74
|
+
if (key === "embedding.apiKey") {
|
|
75
|
+
throw new UsageError("apiKey cannot be persisted in config; export AKM_EMBED_API_KEY instead. (key: embedding.apiKey)", "INVALID_FLAG_VALUE", "Storing API keys in config.json leaks them through backups, logs, and version control. " +
|
|
76
|
+
"Use the corresponding environment variable. AKM reads it at request time.");
|
|
77
|
+
}
|
|
78
|
+
const k = rewriteKey(config, key);
|
|
79
|
+
// Legacy ergonomic: `akm config set semanticSearchMode true|false`
|
|
80
|
+
let coerced = rawValue;
|
|
81
|
+
if (k === "semanticSearchMode") {
|
|
82
|
+
if (rawValue === "true")
|
|
83
|
+
coerced = "auto";
|
|
84
|
+
else if (rawValue === "false")
|
|
85
|
+
coerced = "off";
|
|
86
|
+
}
|
|
87
|
+
let next = configSet(config, k, coerced);
|
|
88
|
+
// Legacy ergonomic shim: when the user sets `llm.<field>` and no
|
|
89
|
+
// `defaults.llm` is set, point it at the freshly-created profile so the
|
|
90
|
+
// value actually takes effect at runtime.
|
|
91
|
+
if (key === "llm" || key.startsWith("llm.")) {
|
|
92
|
+
if (!next.defaults?.llm) {
|
|
93
|
+
next = {
|
|
94
|
+
...next,
|
|
95
|
+
defaults: { ...(next.defaults ?? {}), llm: "default" },
|
|
169
96
|
};
|
|
170
|
-
case "embedding.ollamaOptions.numCtx":
|
|
171
|
-
return {
|
|
172
|
-
...config,
|
|
173
|
-
embedding: mergeLlmLikeEmbedding(config.embedding, {
|
|
174
|
-
ollamaOptions: { ...(config.embedding?.ollamaOptions ?? {}), num_ctx: parsePositiveInteger(rawValue, key) },
|
|
175
|
-
}),
|
|
176
|
-
};
|
|
177
|
-
case "llm.endpoint":
|
|
178
|
-
return { ...config, llm: mergeLlmLike(config.llm, { endpoint: requireNonEmptyString(rawValue, key) }) };
|
|
179
|
-
case "llm.model":
|
|
180
|
-
return { ...config, llm: mergeLlmLike(config.llm, { model: requireNonEmptyString(rawValue, key) }) };
|
|
181
|
-
case "llm.apiKey":
|
|
182
|
-
return { ...config, llm: mergeLlmLike(config.llm, { apiKey: requireNonEmptyString(rawValue, key) }) };
|
|
183
|
-
case "defaultWriteTarget": {
|
|
184
|
-
const name = requireNonEmptyString(rawValue, key);
|
|
185
|
-
const knownNames = (config.sources ?? config.stashes ?? [])
|
|
186
|
-
.map((s) => s.name)
|
|
187
|
-
.filter((n) => typeof n === "string");
|
|
188
|
-
if (knownNames.length > 0 && !knownNames.includes(name)) {
|
|
189
|
-
throw new UsageError(`Unknown source name "${name}" for defaultWriteTarget; configured source names: ${knownNames.map((n) => `"${n}"`).join(", ")}`);
|
|
190
|
-
}
|
|
191
|
-
return { ...config, defaultWriteTarget: name };
|
|
192
97
|
}
|
|
193
|
-
default:
|
|
194
|
-
throw new UsageError(`Unknown config key: ${key}`, "INVALID_FLAG_VALUE", UNKNOWN_CONFIG_KEY_HINT);
|
|
195
98
|
}
|
|
99
|
+
return next;
|
|
196
100
|
}
|
|
197
101
|
export function unsetConfigValue(config, key) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!config.embedding)
|
|
217
|
-
return config;
|
|
218
|
-
const { contextLength: _cl, ...rest } = config.embedding;
|
|
219
|
-
return { ...config, embedding: rest };
|
|
220
|
-
}
|
|
221
|
-
case "embedding.ollamaOptions.numCtx": {
|
|
222
|
-
if (!config.embedding?.ollamaOptions)
|
|
223
|
-
return config;
|
|
224
|
-
const { num_ctx: _nc, ...restOpts } = config.embedding.ollamaOptions;
|
|
225
|
-
const ollamaOptions = Object.keys(restOpts).length > 0 ? restOpts : undefined;
|
|
226
|
-
return { ...config, embedding: { ...config.embedding, ollamaOptions } };
|
|
227
|
-
}
|
|
228
|
-
case "llm":
|
|
229
|
-
return { ...config, llm: undefined };
|
|
230
|
-
case "llm.endpoint":
|
|
231
|
-
return { ...config, llm: mergeLlmLike(config.llm, { endpoint: "" }) };
|
|
232
|
-
case "llm.model":
|
|
233
|
-
return { ...config, llm: mergeLlmLike(config.llm, { model: "" }) };
|
|
234
|
-
case "llm.apiKey": {
|
|
235
|
-
if (!config.llm)
|
|
236
|
-
return config;
|
|
237
|
-
const { apiKey: _b, ...restLlm } = config.llm;
|
|
238
|
-
return { ...config, llm: restLlm };
|
|
102
|
+
const k = rewriteKey(config, key);
|
|
103
|
+
return configUnset(config, k);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Compatibility shim: returns a `Partial<AkmConfig>` containing just the
|
|
107
|
+
* change. Older code merged this onto the live config — new code should call
|
|
108
|
+
* `setConfigValue` directly (which returns the full merged config).
|
|
109
|
+
*/
|
|
110
|
+
export function parseConfigValue(key, value) {
|
|
111
|
+
// Use a "marker" base so we can detect which top-level fields actually got
|
|
112
|
+
// touched by the set call. Anything still equal to the marker is untouched.
|
|
113
|
+
const SENTINEL = Symbol("untouched");
|
|
114
|
+
const base = { semanticSearchMode: SENTINEL };
|
|
115
|
+
const next = setConfigValue(base, key, value);
|
|
116
|
+
const patch = {};
|
|
117
|
+
for (const k of Object.keys(next)) {
|
|
118
|
+
if (next[k] !== SENTINEL) {
|
|
119
|
+
patch[k] = next[k];
|
|
239
120
|
}
|
|
240
|
-
case "registries":
|
|
241
|
-
return { ...config, registries: undefined };
|
|
242
|
-
case "sources":
|
|
243
|
-
case "stashes":
|
|
244
|
-
// "stashes" is kept as an alias for backwards-compat; both clear `sources`.
|
|
245
|
-
return { ...config, sources: undefined, stashes: undefined };
|
|
246
|
-
case "output.format":
|
|
247
|
-
return { ...config, output: mergeOutputConfig(config.output, { format: undefined }) };
|
|
248
|
-
case "output.detail":
|
|
249
|
-
return { ...config, output: mergeOutputConfig(config.output, { detail: undefined }) };
|
|
250
|
-
case "security":
|
|
251
|
-
return { ...config, security: undefined };
|
|
252
|
-
case "security.installAudit.enabled":
|
|
253
|
-
return { ...config, security: mergeSecurityConfig(config.security, { installAudit: { enabled: undefined } }) };
|
|
254
|
-
case "security.installAudit.blockOnCritical":
|
|
255
|
-
return {
|
|
256
|
-
...config,
|
|
257
|
-
security: mergeSecurityConfig(config.security, { installAudit: { blockOnCritical: undefined } }),
|
|
258
|
-
};
|
|
259
|
-
case "security.installAudit.blockUnlistedRegistries":
|
|
260
|
-
return {
|
|
261
|
-
...config,
|
|
262
|
-
security: mergeSecurityConfig(config.security, { installAudit: { blockUnlistedRegistries: undefined } }),
|
|
263
|
-
};
|
|
264
|
-
case "security.installAudit.registryAllowlist":
|
|
265
|
-
case "security.installAudit.registryWhitelist":
|
|
266
|
-
return {
|
|
267
|
-
...config,
|
|
268
|
-
security: mergeSecurityConfig(config.security, {
|
|
269
|
-
installAudit: { registryAllowlist: undefined, registryWhitelist: undefined },
|
|
270
|
-
}),
|
|
271
|
-
};
|
|
272
|
-
case "security.installAudit.allowedFindings":
|
|
273
|
-
return {
|
|
274
|
-
...config,
|
|
275
|
-
security: mergeSecurityConfig(config.security, {
|
|
276
|
-
installAudit: { allowedFindings: undefined },
|
|
277
|
-
}),
|
|
278
|
-
};
|
|
279
|
-
default:
|
|
280
|
-
throw new UsageError(`Unknown or unsupported unset key: ${key}`, "INVALID_FLAG_VALUE", UNKNOWN_CONFIG_KEY_HINT);
|
|
281
121
|
}
|
|
122
|
+
return patch;
|
|
282
123
|
}
|
|
283
124
|
export function listConfig(config) {
|
|
284
125
|
const result = {
|
|
285
126
|
semanticSearchMode: config.semanticSearchMode,
|
|
286
127
|
registries: config.registries ?? DEFAULT_CONFIG.registries ?? [],
|
|
287
|
-
output:
|
|
128
|
+
output: { ...(DEFAULT_CONFIG.output ?? {}), ...(config.output ?? {}) },
|
|
288
129
|
stashDir: config.stashDir ?? null,
|
|
289
130
|
installed: config.installed ?? [],
|
|
290
|
-
sources: config
|
|
131
|
+
sources: getSources(config),
|
|
291
132
|
};
|
|
292
133
|
if (config.defaultWriteTarget)
|
|
293
134
|
result.defaultWriteTarget = config.defaultWriteTarget;
|
|
294
135
|
if (config.embedding)
|
|
295
136
|
result.embedding = config.embedding;
|
|
296
|
-
if (config.
|
|
297
|
-
result.
|
|
298
|
-
if (config.
|
|
299
|
-
result.
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
...(base ?? {}),
|
|
313
|
-
...(override ?? {}),
|
|
314
|
-
};
|
|
315
|
-
return merged.format || merged.detail ? merged : undefined;
|
|
316
|
-
}
|
|
317
|
-
function mergeSecurityConfig(base, override) {
|
|
318
|
-
const mergedInstallAudit = mergeInstallAuditConfig(base?.installAudit, override?.installAudit);
|
|
319
|
-
return mergedInstallAudit ? { installAudit: mergedInstallAudit } : undefined;
|
|
320
|
-
}
|
|
321
|
-
function mergeInstallAuditConfig(base, override) {
|
|
322
|
-
const merged = {
|
|
323
|
-
...(base ?? {}),
|
|
324
|
-
...(override ?? {}),
|
|
325
|
-
};
|
|
326
|
-
const hasValue = Object.values(merged).some((value) => value !== undefined);
|
|
327
|
-
return hasValue ? merged : undefined;
|
|
328
|
-
}
|
|
329
|
-
function parseOutputFormat(value) {
|
|
330
|
-
if (value === "json" || value === "yaml" || value === "text")
|
|
331
|
-
return value;
|
|
332
|
-
throw new UsageError(`Invalid value for output.format: expected one of json|yaml|text`);
|
|
333
|
-
}
|
|
334
|
-
function parseOutputDetail(value) {
|
|
335
|
-
if (value === "brief" || value === "normal" || value === "full")
|
|
336
|
-
return value;
|
|
337
|
-
throw new UsageError(`Invalid value for output.detail: expected one of brief|normal|full`);
|
|
338
|
-
}
|
|
339
|
-
function parseBooleanValue(value, key) {
|
|
340
|
-
if (value === "true")
|
|
341
|
-
return true;
|
|
342
|
-
if (value === "false")
|
|
343
|
-
return false;
|
|
344
|
-
throw new UsageError(`Invalid value for ${key}: expected true or false`);
|
|
345
|
-
}
|
|
346
|
-
function parseStringArrayValue(value, key) {
|
|
347
|
-
let parsed;
|
|
348
|
-
try {
|
|
349
|
-
parsed = JSON.parse(value);
|
|
350
|
-
}
|
|
351
|
-
catch {
|
|
352
|
-
throw new UsageError(`Invalid value for ${key}: expected a JSON array of strings`);
|
|
353
|
-
}
|
|
354
|
-
if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== "string")) {
|
|
355
|
-
throw new UsageError(`Invalid value for ${key}: expected a JSON array of strings`);
|
|
356
|
-
}
|
|
357
|
-
return parsed;
|
|
358
|
-
}
|
|
359
|
-
function getInstallAuditAllowlist(config) {
|
|
360
|
-
return config.security?.installAudit?.registryAllowlist ?? config.security?.installAudit?.registryWhitelist ?? null;
|
|
361
|
-
}
|
|
362
|
-
function parseAllowedFindingsValue(value, key) {
|
|
363
|
-
let parsed;
|
|
364
|
-
try {
|
|
365
|
-
parsed = JSON.parse(value);
|
|
366
|
-
}
|
|
367
|
-
catch {
|
|
368
|
-
throw new UsageError(`Invalid value for ${key}: expected a JSON array of {id, ref?, path?, reason?} objects`);
|
|
369
|
-
}
|
|
370
|
-
if (!Array.isArray(parsed)) {
|
|
371
|
-
throw new UsageError(`Invalid value for ${key}: expected a JSON array`);
|
|
372
|
-
}
|
|
373
|
-
return parsed.map((entry, i) => {
|
|
374
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
375
|
-
throw new UsageError(`Invalid value for ${key}[${i}]: expected an object with an "id" field`);
|
|
376
|
-
}
|
|
377
|
-
const obj = entry;
|
|
378
|
-
if (typeof obj.id !== "string" || !obj.id) {
|
|
379
|
-
throw new UsageError(`Invalid value for ${key}[${i}]: "id" is required`);
|
|
380
|
-
}
|
|
381
|
-
const result = { id: obj.id };
|
|
382
|
-
if (typeof obj.ref === "string" && obj.ref)
|
|
383
|
-
result.ref = obj.ref;
|
|
384
|
-
if (typeof obj.path === "string" && obj.path)
|
|
385
|
-
result.path = obj.path;
|
|
386
|
-
if (typeof obj.reason === "string" && obj.reason)
|
|
387
|
-
result.reason = obj.reason;
|
|
388
|
-
return result;
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
function parseRegistriesValue(value) {
|
|
392
|
-
if (value === "null" || value === "")
|
|
393
|
-
return undefined;
|
|
394
|
-
let parsed;
|
|
395
|
-
try {
|
|
396
|
-
parsed = JSON.parse(value);
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
throw new UsageError(`Invalid value for registries: expected JSON array of {url, name?, enabled?, provider?, options?} objects` +
|
|
400
|
-
` (e.g. '[{"url":"https://example.com/index.json","name":"my-registry"}]')`);
|
|
401
|
-
}
|
|
402
|
-
if (!Array.isArray(parsed)) {
|
|
403
|
-
throw new UsageError(`Invalid value for registries: expected a JSON array`);
|
|
404
|
-
}
|
|
405
|
-
return parsed.map((entry, i) => {
|
|
406
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
407
|
-
throw new UsageError(`Invalid value for registries[${i}]: expected an object with a "url" field`);
|
|
408
|
-
}
|
|
409
|
-
const obj = entry;
|
|
410
|
-
if (typeof obj.url !== "string" || !obj.url) {
|
|
411
|
-
throw new UsageError(`Invalid value for registries[${i}]: "url" is required`);
|
|
412
|
-
}
|
|
413
|
-
const result = { url: obj.url };
|
|
414
|
-
if (typeof obj.name === "string" && obj.name)
|
|
415
|
-
result.name = obj.name;
|
|
416
|
-
if (typeof obj.enabled === "boolean")
|
|
417
|
-
result.enabled = obj.enabled;
|
|
418
|
-
if (typeof obj.provider === "string" && obj.provider)
|
|
419
|
-
result.provider = obj.provider;
|
|
420
|
-
if (typeof obj.options === "object" && obj.options !== null && !Array.isArray(obj.options)) {
|
|
421
|
-
result.options = obj.options;
|
|
422
|
-
}
|
|
423
|
-
return result;
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
function parseEmbeddingConnectionValue(value) {
|
|
427
|
-
if (value === "null" || value === "")
|
|
428
|
-
return undefined;
|
|
429
|
-
const parsed = parseJsonObject(value, "embedding", {
|
|
430
|
-
endpoint: "http://localhost:11434/v1/embeddings",
|
|
431
|
-
model: "nomic-embed-text",
|
|
432
|
-
});
|
|
433
|
-
const localModel = typeof parsed.localModel === "string" && parsed.localModel ? parsed.localModel : undefined;
|
|
434
|
-
const endpoint = typeof parsed.endpoint === "string" ? parsed.endpoint : "";
|
|
435
|
-
if (!endpoint) {
|
|
436
|
-
if (!localModel) {
|
|
437
|
-
throw new UsageError(`Invalid value for embedding: endpoint/model are required for remote embeddings, or provide localModel`);
|
|
438
|
-
}
|
|
439
|
-
const localOnly = { endpoint: "", model: "", localModel };
|
|
440
|
-
if (typeof parsed.provider === "string" && parsed.provider)
|
|
441
|
-
localOnly.provider = parsed.provider;
|
|
442
|
-
return localOnly;
|
|
443
|
-
}
|
|
444
|
-
const result = {
|
|
445
|
-
endpoint: asRequiredString(parsed.endpoint, "embedding", "endpoint"),
|
|
446
|
-
model: asRequiredString(parsed.model, "embedding", "model"),
|
|
447
|
-
};
|
|
448
|
-
if (typeof parsed.provider === "string" && parsed.provider)
|
|
449
|
-
result.provider = parsed.provider;
|
|
450
|
-
if (parsed.dimension !== undefined)
|
|
451
|
-
result.dimension = parseUnknownPositiveInteger(parsed.dimension, "embedding.dimension");
|
|
452
|
-
if (typeof parsed.apiKey === "string" && parsed.apiKey)
|
|
453
|
-
result.apiKey = parsed.apiKey;
|
|
454
|
-
if (localModel)
|
|
455
|
-
result.localModel = localModel;
|
|
456
|
-
return result;
|
|
457
|
-
}
|
|
458
|
-
function parseLlmConnectionValue(value) {
|
|
459
|
-
if (value === "null" || value === "")
|
|
460
|
-
return undefined;
|
|
461
|
-
const parsed = parseJsonObject(value, "llm", {
|
|
462
|
-
endpoint: "http://localhost:11434/v1/chat/completions",
|
|
463
|
-
model: "llama3.2",
|
|
464
|
-
});
|
|
465
|
-
const result = {
|
|
466
|
-
endpoint: asRequiredString(parsed.endpoint, "llm", "endpoint"),
|
|
467
|
-
model: asRequiredString(parsed.model, "llm", "model"),
|
|
468
|
-
};
|
|
469
|
-
if (typeof parsed.provider === "string" && parsed.provider)
|
|
470
|
-
result.provider = parsed.provider;
|
|
471
|
-
if (parsed.temperature !== undefined)
|
|
472
|
-
result.temperature = parseUnknownNumber(parsed.temperature, "llm.temperature");
|
|
473
|
-
if (parsed.maxTokens !== undefined)
|
|
474
|
-
result.maxTokens = parseUnknownPositiveInteger(parsed.maxTokens, "llm.maxTokens");
|
|
475
|
-
if (typeof parsed.apiKey === "string" && parsed.apiKey)
|
|
476
|
-
result.apiKey = parsed.apiKey;
|
|
137
|
+
if (config.profiles)
|
|
138
|
+
result.profiles = config.profiles;
|
|
139
|
+
if (config.defaults)
|
|
140
|
+
result.defaults = config.defaults;
|
|
141
|
+
if (config.search)
|
|
142
|
+
result.search = config.search;
|
|
143
|
+
if (config.index)
|
|
144
|
+
result.index = config.index;
|
|
145
|
+
if (config.feedback)
|
|
146
|
+
result.feedback = config.feedback;
|
|
147
|
+
if (config.improve)
|
|
148
|
+
result.improve = config.improve;
|
|
149
|
+
if (config.archiveRetentionDays !== undefined)
|
|
150
|
+
result.archiveRetentionDays = config.archiveRetentionDays;
|
|
151
|
+
if (config.configVersion !== undefined)
|
|
152
|
+
result.configVersion = config.configVersion;
|
|
477
153
|
return result;
|
|
478
154
|
}
|
|
479
|
-
|
|
480
|
-
let parsed;
|
|
481
|
-
try {
|
|
482
|
-
parsed = JSON.parse(value);
|
|
483
|
-
}
|
|
484
|
-
catch {
|
|
485
|
-
throw new UsageError(`Invalid value for ${key}: expected JSON object with endpoint and model` +
|
|
486
|
-
` (e.g. '{"endpoint":"${example.endpoint}","model":"${example.model}"}')`, "INVALID_JSON_CONFIG_VALUE");
|
|
487
|
-
}
|
|
488
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
489
|
-
throw new UsageError(`Invalid value for ${key}: expected a JSON object`);
|
|
490
|
-
}
|
|
491
|
-
return parsed;
|
|
492
|
-
}
|
|
493
|
-
function asRequiredString(value, key, field) {
|
|
494
|
-
if (typeof value !== "string" || !value) {
|
|
495
|
-
throw new UsageError(`Invalid value for ${key}: "${field}" is a required string field`);
|
|
496
|
-
}
|
|
497
|
-
return value;
|
|
498
|
-
}
|
|
499
|
-
function requireNonEmptyString(value, key) {
|
|
500
|
-
if (!value) {
|
|
501
|
-
throw new UsageError(`Invalid value for ${key}: expected a non-empty string`);
|
|
502
|
-
}
|
|
503
|
-
return value;
|
|
504
|
-
}
|
|
505
|
-
function parseUnknownNumber(value, key) {
|
|
506
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
507
|
-
throw new UsageError(`Invalid value for ${key}: expected a number`);
|
|
508
|
-
}
|
|
509
|
-
return value;
|
|
510
|
-
}
|
|
511
|
-
function parseUnknownPositiveInteger(value, key) {
|
|
512
|
-
if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
|
|
513
|
-
throw new UsageError(`Invalid value for ${key}: expected a positive integer`);
|
|
514
|
-
}
|
|
515
|
-
return value;
|
|
516
|
-
}
|
|
517
|
-
function parsePositiveInteger(value, key) {
|
|
518
|
-
const n = Number(value);
|
|
519
|
-
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
|
|
520
|
-
throw new UsageError(`Invalid value for ${key}: expected a positive integer`);
|
|
521
|
-
}
|
|
522
|
-
return n;
|
|
523
|
-
}
|
|
524
|
-
function parseStashesValue(value) {
|
|
525
|
-
if (value === "null" || value === "")
|
|
526
|
-
return undefined;
|
|
527
|
-
let parsed;
|
|
528
|
-
try {
|
|
529
|
-
parsed = JSON.parse(value);
|
|
530
|
-
}
|
|
531
|
-
catch {
|
|
532
|
-
throw new UsageError(`Invalid value for sources: expected JSON array of {type, path?, url?, name?, enabled?, options?} objects`);
|
|
533
|
-
}
|
|
534
|
-
if (!Array.isArray(parsed)) {
|
|
535
|
-
throw new UsageError(`Invalid value for sources: expected a JSON array`);
|
|
536
|
-
}
|
|
537
|
-
return parsed.map((entry, i) => {
|
|
538
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
|
|
539
|
-
throw new UsageError(`Invalid value for sources[${i}]: expected an object with a "type" field`);
|
|
540
|
-
}
|
|
541
|
-
const obj = entry;
|
|
542
|
-
if (typeof obj.type !== "string" || !obj.type) {
|
|
543
|
-
throw new UsageError(`Invalid value for sources[${i}]: "type" is required`);
|
|
544
|
-
}
|
|
545
|
-
const result = { type: obj.type };
|
|
546
|
-
if (typeof obj.path === "string" && obj.path)
|
|
547
|
-
result.path = obj.path;
|
|
548
|
-
if (typeof obj.url === "string" && obj.url)
|
|
549
|
-
result.url = obj.url;
|
|
550
|
-
if (typeof obj.name === "string" && obj.name)
|
|
551
|
-
result.name = obj.name;
|
|
552
|
-
if (typeof obj.enabled === "boolean")
|
|
553
|
-
result.enabled = obj.enabled;
|
|
554
|
-
if (typeof obj.options === "object" && obj.options !== null && !Array.isArray(obj.options)) {
|
|
555
|
-
result.options = obj.options;
|
|
556
|
-
}
|
|
557
|
-
return result;
|
|
558
|
-
});
|
|
559
|
-
}
|
|
155
|
+
export { unknownKeyHint };
|