akm-cli 0.8.0-rc2 → 0.8.0
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} +191 -3
- 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 +93 -3
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2141 -1268
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +20 -12
- package/dist/commands/agent-support.js +11 -5
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +129 -517
- package/dist/commands/consolidate.js +1533 -144
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +5 -3
- package/dist/commands/distill.js +906 -100
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +3 -0
- package/dist/commands/events.js +3 -0
- 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 +260 -5
- package/dist/commands/health.js +977 -51
- package/dist/commands/help/help-accept.md +6 -3
- package/dist/commands/help/help-improve.md +36 -8
- package/dist/commands/help/help-proposals.md +7 -4
- package/dist/commands/help/help-reject.md +5 -2
- package/dist/commands/history.js +51 -16
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +236 -0
- package/dist/commands/improve-profiles.js +184 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +1725 -332
- package/dist/commands/info.js +3 -0
- package/dist/commands/init.js +49 -1
- package/dist/commands/installed-stashes.js +6 -23
- package/dist/commands/knowledge.js +3 -0
- package/dist/commands/lint/agent-linter.js +3 -0
- package/dist/commands/lint/base-linter.js +199 -5
- package/dist/commands/lint/command-linter.js +3 -0
- package/dist/commands/lint/default-linter.js +3 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +92 -3
- package/dist/commands/lint/knowledge-linter.js +3 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +3 -0
- package/dist/commands/lint/registry.js +3 -0
- package/dist/commands/lint/skill-linter.js +3 -0
- package/dist/commands/lint/task-linter.js +15 -12
- package/dist/commands/lint/types.js +3 -0
- package/dist/commands/lint/workflow-linter.js +3 -0
- package/dist/commands/lint.js +3 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal-drain-policies.js +128 -0
- package/dist/commands/proposal-drain.js +477 -0
- package/dist/commands/proposal.js +60 -6
- package/dist/commands/propose.js +24 -19
- package/dist/commands/reflect.js +1004 -94
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +3 -0
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +15 -6
- package/dist/commands/schema-repair.js +88 -15
- package/dist/commands/search.js +99 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +32 -13
- package/dist/commands/source-add.js +7 -35
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +3 -0
- package/dist/commands/tasks.js +161 -95
- package/dist/commands/url-checker.js +3 -0
- package/dist/core/action-contributors.js +3 -0
- package/dist/core/asset-ref.js +13 -2
- package/dist/core/asset-registry.js +9 -2
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +61 -5
- package/dist/core/common.js +93 -5
- package/dist/core/concurrent.js +3 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +558 -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 +366 -1077
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +31 -25
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -10
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +3 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +142 -14
- package/dist/core/parse.js +3 -0
- package/dist/core/paths.js +218 -50
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +11 -3
- package/dist/core/proposals.js +464 -5
- package/dist/core/state-db.js +349 -56
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +3 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +7 -2
- package/dist/core/write-source.js +12 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +136 -28
- package/dist/indexer/db.js +661 -166
- package/dist/indexer/ensure-index.js +3 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +162 -40
- package/dist/indexer/graph-db.js +241 -51
- package/dist/indexer/graph-dedup.js +3 -7
- package/dist/indexer/graph-extraction.js +242 -149
- package/dist/indexer/index-context.js +3 -9
- package/dist/indexer/indexer.js +84 -14
- package/dist/indexer/llm-cache.js +24 -19
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +184 -11
- package/dist/indexer/memory-inference.js +94 -50
- package/dist/indexer/metadata-contributors.js +3 -0
- package/dist/indexer/metadata.js +110 -50
- package/dist/indexer/path-resolver.js +3 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +134 -7
- package/dist/indexer/ranking.js +8 -1
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +91 -2
- package/dist/indexer/search-source.js +20 -1
- package/dist/indexer/semantic-status.js +4 -1
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +3 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +121 -401
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +6 -14
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +3 -0
- package/dist/integrations/agent/prompts.js +137 -8
- package/dist/integrations/agent/runner.js +208 -0
- package/dist/integrations/agent/sdk-runner.js +8 -2
- package/dist/integrations/agent/spawn.js +54 -14
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +22 -51
- package/dist/integrations/session-logs/index.js +4 -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 +226 -0
- package/dist/integrations/session-logs/providers/opencode.js +231 -25
- package/dist/integrations/session-logs/types.js +3 -0
- package/dist/llm/call-ai.js +14 -26
- package/dist/llm/client.js +16 -2
- 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 +92 -56
- package/dist/llm/graph-extract.js +401 -30
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +30 -2
- package/dist/llm/metadata-enhance.js +3 -7
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +24 -1
- package/dist/output/cli-hints-full.md +60 -32
- package/dist/output/cli-hints-short.md +10 -7
- package/dist/output/cli-hints.js +5 -2
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +170 -194
- 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 +105 -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 -549
- 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 +1059 -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 +12 -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 -1329
- 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 +11 -2
- package/dist/registry/providers/static-index.js +10 -1
- 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 +17767 -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 +306 -67
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +3 -11
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +171 -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 +3 -0
- package/dist/tasks/backends/cron.js +3 -0
- package/dist/tasks/backends/exec-utils.js +3 -0
- package/dist/tasks/backends/index.js +3 -11
- package/dist/tasks/backends/launchd.js +3 -0
- package/dist/tasks/backends/schtasks.js +3 -0
- package/dist/tasks/parser.js +51 -38
- package/dist/tasks/resolveAkmBin.js +3 -0
- package/dist/tasks/runner.js +35 -9
- package/dist/tasks/schedule.js +20 -1
- package/dist/tasks/schema.js +5 -3
- package/dist/tasks/validator.js +6 -3
- package/dist/version.js +3 -0
- package/dist/wiki/wiki-templates.js +3 -0
- package/dist/wiki/wiki.js +3 -0
- package/dist/workflows/authoring.js +3 -0
- 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 +3 -0
- package/dist/workflows/runs.js +18 -1
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +5 -9
- package/docs/README.md +7 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +57 -5
- package/docs/migration/v0.7-to-v0.8.md +1378 -0
- package/package.json +28 -11
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -385
- package/dist/commands/vault.js +0 -310
- package/dist/indexer/match-contributors.js +0 -141
- package/dist/integrations/agent/pipeline.js +0 -39
- package/dist/integrations/agent/runners.js +0 -31
|
@@ -0,0 +1,279 @@
|
|
|
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 fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import * as p from "@clack/prompts";
|
|
7
|
+
import { defineCommand } from "citty";
|
|
8
|
+
import { output, runWithJsonErrors } from "../cli/shared";
|
|
9
|
+
import { UsageError } from "../core/errors";
|
|
10
|
+
import { appendEvent } from "../core/events";
|
|
11
|
+
import { warn } from "../core/warn";
|
|
12
|
+
import { getHyphenatedBoolean } from "../output/context";
|
|
13
|
+
import { akmRemove } from "./installed-stashes";
|
|
14
|
+
import { akmAdd } from "./source-add";
|
|
15
|
+
import { addStash } from "./source-manage";
|
|
16
|
+
// ── Shared website-options helper (also used by wikiRegisterCommand) ──────────
|
|
17
|
+
export function buildWebsiteOptions(args) {
|
|
18
|
+
const websiteOptions = {};
|
|
19
|
+
if (typeof args["max-pages"] === "string" && args["max-pages"].length > 0)
|
|
20
|
+
websiteOptions.maxPages = args["max-pages"];
|
|
21
|
+
if (typeof args["max-depth"] === "string" && args["max-depth"].length > 0)
|
|
22
|
+
websiteOptions.maxDepth = args["max-depth"];
|
|
23
|
+
return websiteOptions;
|
|
24
|
+
}
|
|
25
|
+
// ── HTTP safety check ─────────────────────────────────────────────────────────
|
|
26
|
+
export function shouldWarnOnPlainHttp(ref) {
|
|
27
|
+
if (!ref.startsWith("http://"))
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
const hostname = new URL(ref).hostname.toLowerCase();
|
|
31
|
+
return (hostname !== "localhost" &&
|
|
32
|
+
hostname !== "127.0.0.1" &&
|
|
33
|
+
hostname !== "0.0.0.0" &&
|
|
34
|
+
hostname !== "::1" &&
|
|
35
|
+
hostname !== "[::1]" &&
|
|
36
|
+
!hostname.endsWith(".localhost"));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// ── Command definition ────────────────────────────────────────────────────────
|
|
43
|
+
export const addCommand = defineCommand({
|
|
44
|
+
meta: {
|
|
45
|
+
name: "add",
|
|
46
|
+
description: "Add a source (local directory, website, npm package, GitHub repo, git URL, or remote provider)",
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
ref: {
|
|
50
|
+
type: "positional",
|
|
51
|
+
description: "Path, URL, or registry ref (website URL, npm package, owner/repo, git URL, or local directory)",
|
|
52
|
+
required: true,
|
|
53
|
+
},
|
|
54
|
+
provider: { type: "string", description: "Provider type (e.g. website, npm). Required for URL sources." },
|
|
55
|
+
options: { type: "string", description: 'Provider options as JSON (e.g. \'{"apiKey":"key"}\').' },
|
|
56
|
+
name: { type: "string", description: "Human-friendly name for the source" },
|
|
57
|
+
writable: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
description: "Mark a git stash as writable so changes can be pushed back",
|
|
60
|
+
default: false,
|
|
61
|
+
},
|
|
62
|
+
type: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Override asset type for all files in this stash (currently supports: wiki)",
|
|
65
|
+
},
|
|
66
|
+
"max-pages": { type: "string", description: "Maximum pages to crawl for website sources (default: 50)" },
|
|
67
|
+
"max-depth": { type: "string", description: "Maximum crawl depth for website sources (default: 3)" },
|
|
68
|
+
"allow-insecure": {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
description: "Allow a plain HTTP source URL and skip confirmation for dangerous vault keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the stash.",
|
|
71
|
+
default: false,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
async run({ args }) {
|
|
75
|
+
await runWithJsonErrors(async () => {
|
|
76
|
+
const ref = args.ref.trim();
|
|
77
|
+
const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
|
|
78
|
+
const allowDangerousKeys = allowInsecure;
|
|
79
|
+
// URL with --provider → stash source (remote or git provider)
|
|
80
|
+
if (args.provider) {
|
|
81
|
+
if (shouldWarnOnPlainHttp(ref)) {
|
|
82
|
+
if (!allowInsecure) {
|
|
83
|
+
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
84
|
+
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
|
|
85
|
+
}
|
|
86
|
+
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
87
|
+
}
|
|
88
|
+
let parsedOptions;
|
|
89
|
+
if (args.options) {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(args.options);
|
|
92
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
93
|
+
throw new UsageError("--options must be a JSON object");
|
|
94
|
+
}
|
|
95
|
+
parsedOptions = parsed;
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
if (err instanceof UsageError)
|
|
99
|
+
throw err;
|
|
100
|
+
throw new UsageError("--options must be valid JSON");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const result = addStash({
|
|
104
|
+
target: ref,
|
|
105
|
+
name: args.name,
|
|
106
|
+
providerType: args.provider,
|
|
107
|
+
options: parsedOptions,
|
|
108
|
+
writable: args.writable,
|
|
109
|
+
});
|
|
110
|
+
appendEvent({
|
|
111
|
+
eventType: "add",
|
|
112
|
+
metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
|
|
113
|
+
});
|
|
114
|
+
output("add", result);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (shouldWarnOnPlainHttp(ref)) {
|
|
118
|
+
if (!allowInsecure) {
|
|
119
|
+
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
120
|
+
"Use https:// or pass --allow-insecure if you have explicitly accepted the risk.", "INVALID_FLAG_VALUE", "Re-run with `--allow-insecure` only after confirming the URL is trusted.");
|
|
121
|
+
}
|
|
122
|
+
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
123
|
+
}
|
|
124
|
+
const websiteOptions = buildWebsiteOptions(args);
|
|
125
|
+
if (args.type === "wiki") {
|
|
126
|
+
const { registerWikiSource } = await import("./source-add");
|
|
127
|
+
const result = await registerWikiSource({
|
|
128
|
+
ref,
|
|
129
|
+
name: args.name,
|
|
130
|
+
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
131
|
+
writable: args.writable,
|
|
132
|
+
});
|
|
133
|
+
appendEvent({
|
|
134
|
+
eventType: "add",
|
|
135
|
+
metadata: { target: ref, type: "wiki", name: args.name ?? null, writable: args.writable === true },
|
|
136
|
+
});
|
|
137
|
+
output("add", result);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const result = await akmAdd({
|
|
141
|
+
ref,
|
|
142
|
+
name: args.name,
|
|
143
|
+
overrideType: args.type,
|
|
144
|
+
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
145
|
+
writable: args.writable,
|
|
146
|
+
});
|
|
147
|
+
appendEvent({
|
|
148
|
+
eventType: "add",
|
|
149
|
+
metadata: {
|
|
150
|
+
target: ref,
|
|
151
|
+
name: args.name ?? null,
|
|
152
|
+
overrideType: args.type ?? null,
|
|
153
|
+
writable: args.writable === true,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
// ── Post-install vault key audit ────────────────────────────────────────
|
|
157
|
+
// Resolve the stash root from the install result and scan any vault files
|
|
158
|
+
// for dangerous env var keys. When findings are present the install is
|
|
159
|
+
// gated: TTY → interactive confirmation prompt; non-TTY without
|
|
160
|
+
// --allow-insecure → hard failure (exit 1). Pass
|
|
161
|
+
// --allow-insecure to skip the prompt non-interactively.
|
|
162
|
+
try {
|
|
163
|
+
const installedStashRoot = result.installed?.stashRoot ??
|
|
164
|
+
(result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
|
|
165
|
+
if (installedStashRoot) {
|
|
166
|
+
const { checkVaultForDangerousKeys } = await import("./lint/env-key-rules.js");
|
|
167
|
+
// Collect all dangerous-key findings across every env file (env/, and
|
|
168
|
+
// the deprecated vaults/) in the freshly-installed stash.
|
|
169
|
+
const allFindings = [];
|
|
170
|
+
for (const [subdir, prefix] of [
|
|
171
|
+
["env", "env"],
|
|
172
|
+
["vaults", "vault"],
|
|
173
|
+
]) {
|
|
174
|
+
const dir = path.join(installedStashRoot, subdir);
|
|
175
|
+
if (!fs.existsSync(dir))
|
|
176
|
+
continue;
|
|
177
|
+
const envFiles = fs.readdirSync(dir).filter((f) => f.endsWith(".env"));
|
|
178
|
+
for (const envFile of envFiles) {
|
|
179
|
+
const envPath = path.join(dir, envFile);
|
|
180
|
+
const baseName = path.basename(envFile, ".env");
|
|
181
|
+
const vaultRef = baseName === "" ? `${prefix}:default` : `${prefix}:${baseName}`;
|
|
182
|
+
const relPath = path.join(subdir, envFile);
|
|
183
|
+
const findings = checkVaultForDangerousKeys(envPath, relPath, vaultRef);
|
|
184
|
+
for (const finding of findings) {
|
|
185
|
+
// Extract the key name from the detail string for the summary line.
|
|
186
|
+
const keyMatch = finding.detail.match(/Env key `([^`]+)`/);
|
|
187
|
+
const keyName = keyMatch ? keyMatch[1] : finding.file;
|
|
188
|
+
allFindings.push({ vaultRef, keyName, relPath });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (allFindings.length > 0) {
|
|
193
|
+
if (allowDangerousKeys) {
|
|
194
|
+
// Operator has explicitly accepted the risk — warn and continue.
|
|
195
|
+
for (const f of allFindings) {
|
|
196
|
+
warn(`[dangerous-vault-key] ${f.relPath}: key \`${f.keyName}\` in ${f.vaultRef} can hijack process execution via \`akm vault run\`. Proceeding because --allow-insecure was set.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else if (process.stdin.isTTY) {
|
|
200
|
+
// Interactive path: show findings and ask the user to confirm.
|
|
201
|
+
// Guard on stdin (not stdout) because p.confirm() reads from stdin;
|
|
202
|
+
// stdout may be a TTY while stdin is piped, which would cause a hang.
|
|
203
|
+
const stashLabel = ref;
|
|
204
|
+
const groupedByVault = new Map();
|
|
205
|
+
for (const f of allFindings) {
|
|
206
|
+
const existing = groupedByVault.get(f.vaultRef) ?? [];
|
|
207
|
+
existing.push(f.keyName);
|
|
208
|
+
groupedByVault.set(f.vaultRef, existing);
|
|
209
|
+
}
|
|
210
|
+
for (const [vaultRef, keys] of groupedByVault) {
|
|
211
|
+
warn(`[warn] Vault "${vaultRef}" in stash "${stashLabel}" contains potentially dangerous keys:`);
|
|
212
|
+
for (const key of keys) {
|
|
213
|
+
warn(` - ${key}: can hijack process execution via \`akm vault run\``);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const confirmed = await p.confirm({
|
|
217
|
+
message: "Install anyway?",
|
|
218
|
+
initialValue: false,
|
|
219
|
+
});
|
|
220
|
+
if (p.isCancel(confirmed) || confirmed !== true) {
|
|
221
|
+
// Roll back the install before aborting.
|
|
222
|
+
// Use the canonical installed id (most reliably resolved by akmRemove) rather
|
|
223
|
+
// than the raw user-supplied ref which may not match after URL normalisation.
|
|
224
|
+
const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
|
|
225
|
+
let rollbackWarning;
|
|
226
|
+
try {
|
|
227
|
+
await akmRemove({ target: rollbackTarget });
|
|
228
|
+
}
|
|
229
|
+
catch (_rollbackErr) {
|
|
230
|
+
rollbackWarning =
|
|
231
|
+
`Rollback failed — stash may still be installed at ${installedStashRoot}. ` +
|
|
232
|
+
`Remove it manually with: akm remove ${rollbackTarget}`;
|
|
233
|
+
}
|
|
234
|
+
console.error(JSON.stringify({
|
|
235
|
+
ok: false,
|
|
236
|
+
error: "Install aborted: stash contains dangerous vault keys. Remove the keys or re-run with --allow-insecure to bypass.",
|
|
237
|
+
code: "DANGEROUS_VAULT_KEY",
|
|
238
|
+
...(rollbackWarning ? { rollbackWarning } : {}),
|
|
239
|
+
}, null, 2));
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// Non-interactive path without bypass flag: fail hard.
|
|
245
|
+
// Roll back the install before exiting.
|
|
246
|
+
// Use the canonical installed id (most reliably resolved by akmRemove) rather
|
|
247
|
+
// than the raw user-supplied ref which may not match after URL normalisation.
|
|
248
|
+
const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
|
|
249
|
+
let rollbackWarning;
|
|
250
|
+
try {
|
|
251
|
+
await akmRemove({ target: rollbackTarget });
|
|
252
|
+
}
|
|
253
|
+
catch (_rollbackErr) {
|
|
254
|
+
rollbackWarning =
|
|
255
|
+
`Rollback failed — stash may still be installed at ${installedStashRoot}. ` +
|
|
256
|
+
`Remove it manually with: akm remove ${rollbackTarget}`;
|
|
257
|
+
}
|
|
258
|
+
const keyList = allFindings.map((f) => ` - ${f.keyName} (${f.vaultRef})`).join("\n");
|
|
259
|
+
console.error(JSON.stringify({
|
|
260
|
+
ok: false,
|
|
261
|
+
error: `Install blocked: stash "${ref}" contains dangerous vault keys that can hijack process execution via \`akm vault run\`:\n${keyList}\nRe-run with --allow-insecure to bypass this check after reviewing the vault.`,
|
|
262
|
+
code: "DANGEROUS_VAULT_KEY",
|
|
263
|
+
...(rollbackWarning ? { rollbackWarning } : {}),
|
|
264
|
+
}, null, 2));
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (auditErr) {
|
|
271
|
+
// Only swallow errors that are NOT our intentional process.exit calls.
|
|
272
|
+
if (auditErr instanceof Error && auditErr.message === "process.exit called")
|
|
273
|
+
throw auditErr;
|
|
274
|
+
// Vault key audit is best-effort; never fail the install on unexpected audit errors.
|
|
275
|
+
}
|
|
276
|
+
output("add", result);
|
|
277
|
+
});
|
|
278
|
+
},
|
|
279
|
+
});
|
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* `akm agent <profile> [--prompt <text>] [--command <ref>] [--workflow <ref>] [args...]`
|
|
3
6
|
*
|
|
@@ -14,7 +17,8 @@ import fs from "node:fs";
|
|
|
14
17
|
import { parseAssetRef } from "../core/asset-ref";
|
|
15
18
|
import { NotFoundError, UsageError } from "../core/errors";
|
|
16
19
|
import { requireAgentProfile } from "../integrations/agent/config";
|
|
17
|
-
import {
|
|
20
|
+
import { runAgentSdk } from "../integrations/agent/sdk-runner";
|
|
21
|
+
import { runAgent } from "../integrations/agent/spawn";
|
|
18
22
|
/**
|
|
19
23
|
* Fill `{{0}}`, `{{1}}`, ... placeholders in `template` with the
|
|
20
24
|
* corresponding entries in `args`. Any placeholder index that exceeds the
|
|
@@ -76,17 +80,21 @@ export async function akmAgentDispatch(options) {
|
|
|
76
80
|
}
|
|
77
81
|
// When prompt is undefined, the agent is launched interactively.
|
|
78
82
|
const stdio = prompt === undefined && profile.sdkMode !== true ? "interactive" : profile.stdio;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
|
|
83
|
+
// Build the final dispatch request: merge the caller-supplied dispatch with
|
|
84
|
+
// the resolved prompt so the builder has all context in one place.
|
|
85
|
+
const dispatchRequest = options.dispatch
|
|
86
|
+
? { ...options.dispatch, prompt: prompt ?? options.dispatch.prompt }
|
|
87
|
+
: undefined;
|
|
88
|
+
const runOptions = {
|
|
89
|
+
stdio,
|
|
90
|
+
parseOutput: "text",
|
|
91
|
+
...(options.args?.length && !options.commandRef && !options.workflowRef ? { args: options.args } : {}),
|
|
92
|
+
...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
|
|
93
|
+
...(dispatchRequest !== undefined ? { dispatch: dispatchRequest } : {}),
|
|
94
|
+
};
|
|
95
|
+
const result = profile.sdkMode
|
|
96
|
+
? await runAgentSdk(profile, prompt ?? "", runOptions, options.llmConfig)
|
|
97
|
+
: await runAgent(profile, prompt, runOptions);
|
|
90
98
|
return {
|
|
91
99
|
schemaVersion: 1,
|
|
92
100
|
ok: result.ok,
|
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
/**
|
|
2
5
|
* Shared helpers for agent-based commands (reflect, propose, etc.).
|
|
3
6
|
*
|
|
@@ -6,15 +9,18 @@
|
|
|
6
9
|
* profile can import from here rather than copy-pasting.
|
|
7
10
|
*/
|
|
8
11
|
import { loadConfig } from "../core/config";
|
|
9
|
-
import {
|
|
12
|
+
import { requireAgentProfile, } from "../integrations/agent";
|
|
10
13
|
// ── Config helpers ───────────────────────────────────────────────────────────
|
|
11
14
|
/**
|
|
12
|
-
* Load the
|
|
13
|
-
*
|
|
15
|
+
* Load the loaded AkmConfig from disk.
|
|
16
|
+
*
|
|
17
|
+
* After 0.8.0, the legacy `agent` top-level block was removed — the agent
|
|
18
|
+
* profile data now lives on the unified `AkmConfig` (via `profiles.agent` and
|
|
19
|
+
* `defaults.agent`). This helper remains for source-compat with callers that
|
|
20
|
+
* still expect an "AgentConfig"; it now returns the loaded `AkmConfig`.
|
|
14
21
|
*/
|
|
15
22
|
export function loadAgentConfigFromDisk() {
|
|
16
|
-
|
|
17
|
-
return parseAgentConfig(config.agent);
|
|
23
|
+
return loadConfig();
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
26
|
* Resolve the agent profile for a command's options.
|
|
@@ -1,3 +1,6 @@
|
|
|
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/.
|
|
1
4
|
import fs from "node:fs";
|
|
2
5
|
import os from "node:os";
|
|
3
6
|
import path from "node:path";
|