akm-cli 0.8.1 → 0.9.0-beta.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/CHANGELOG.md +258 -0
- package/dist/assets/help/help-proposals.md +1 -2
- package/dist/assets/hints/cli-hints-full.md +34 -19
- package/dist/assets/hints/cli-hints-short.md +1 -1
- package/dist/assets/profiles/catchup.json +13 -0
- package/dist/assets/profiles/consolidate.json +13 -0
- package/dist/assets/profiles/frequent.json +13 -0
- package/dist/assets/stash-skeleton/README.md +76 -0
- package/dist/assets/tasks/core/backup.yml +4 -0
- package/dist/assets/tasks/core/extract.yml +4 -0
- package/dist/assets/tasks/core/improve.yml +4 -0
- package/dist/assets/tasks/core/index-refresh.yml +4 -0
- package/dist/assets/tasks/core/sync.yml +4 -0
- package/dist/assets/tasks/core/update-stashes.yml +4 -0
- package/dist/assets/tasks/core/version-check.yml +4 -0
- package/dist/cli/config-migrate.js +6 -6
- package/dist/cli/config-validate.js +4 -4
- package/dist/cli/confirm.js +3 -3
- package/dist/cli/parse-args.js +1 -1
- package/dist/cli/shared.js +51 -14
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +171 -3857
- package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
- package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
- package/dist/commands/agent/contribute-cli.js +200 -0
- package/dist/commands/completions.js +1 -1
- package/dist/commands/config-cli.js +240 -3
- package/dist/commands/config-edit.js +344 -0
- package/dist/commands/db-cli.js +2 -2
- package/dist/commands/env/env-cli.js +529 -0
- package/dist/commands/env/env.js +410 -0
- package/dist/commands/env/secret-cli.js +259 -0
- package/dist/commands/{secret.js → env/secret.js} +6 -47
- package/dist/commands/events.js +4 -4
- package/dist/commands/feedback-cli.js +18 -34
- package/dist/commands/graph/graph-cli.js +132 -0
- package/dist/commands/{graph.js → graph/graph.js} +22 -16
- package/dist/commands/health/checks.js +279 -0
- package/dist/commands/health.js +101 -249
- package/dist/commands/{consolidate.js → improve/consolidate.js} +52 -40
- package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
- package/dist/commands/{distill.js → improve/distill.js} +39 -18
- package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
- package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
- package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
- package/dist/commands/{extract.js → improve/extract.js} +185 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +4 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +45 -23
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +10 -5
- package/dist/commands/{improve.js → improve/improve.js} +536 -248
- package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
- package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
- package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
- package/dist/commands/{reflect.js → improve/reflect.js} +33 -28
- package/dist/commands/improve/session-asset.js +248 -0
- package/dist/commands/lint/agent-linter.js +1 -1
- package/dist/commands/lint/base-linter.js +55 -37
- package/dist/commands/lint/command-linter.js +1 -1
- package/dist/commands/lint/default-linter.js +1 -1
- package/dist/commands/lint/env-key-rules.js +1 -1
- package/dist/commands/lint/index.js +19 -25
- package/dist/commands/lint/knowledge-linter.js +1 -1
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +8 -8
- package/dist/commands/lint/skill-linter.js +1 -1
- package/dist/commands/lint/task-linter.js +1 -1
- package/dist/commands/lint/workflow-linter.js +1 -1
- package/dist/commands/lint.js +1 -1
- package/dist/commands/observability-cli.js +244 -0
- package/dist/commands/{proposal-drain-policies.js → proposal/drain-policies.js} +3 -3
- package/dist/commands/{proposal-drain.js → proposal/drain.js} +15 -10
- package/dist/commands/proposal/proposal-cli.js +478 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +5 -5
- package/dist/commands/{propose.js → proposal/propose.js} +11 -11
- package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
- package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
- package/dist/{core → commands/proposal/validators}/proposals.js +13 -7
- package/dist/commands/{curate.js → read/curate.js} +7 -7
- package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
- package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
- package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
- package/dist/commands/read/search-cli.js +207 -0
- package/dist/commands/{search.js → read/search.js} +22 -27
- package/dist/commands/{show.js → read/show.js} +77 -44
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +8 -8
- package/dist/commands/sources/add-cli.js +293 -0
- package/dist/commands/{history.js → sources/history.js} +27 -25
- package/dist/commands/{info.js → sources/info.js} +6 -6
- package/dist/commands/{init.js → sources/init.js} +10 -5
- package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
- package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
- package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
- package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
- package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
- package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
- package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
- package/dist/commands/sources/sources-cli.js +305 -0
- package/dist/commands/sources/stash-cli.js +219 -0
- package/dist/commands/sources/stash-skeleton.js +79 -0
- package/dist/commands/tasks/default-tasks.js +173 -0
- package/dist/commands/tasks/tasks-cli.js +210 -0
- package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
- package/dist/commands/wiki-cli.js +307 -0
- package/dist/commands/workflow-cli.js +329 -0
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/assert.js +40 -0
- package/dist/core/asset/asset-create.js +54 -0
- package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
- package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
- package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
- package/dist/core/{markdown.js → asset/markdown.js} +1 -1
- package/dist/core/asset/stash-meta.js +110 -0
- package/dist/core/best-effort.js +64 -0
- package/dist/core/common.js +32 -18
- package/dist/core/{config-io.js → config/config-io.js} +29 -19
- package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
- package/dist/core/{config-schema.js → config/config-schema.js} +45 -1
- package/dist/core/config/config-types.js +16 -0
- package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
- package/dist/core/{config.js → config/config.js} +10 -8
- package/dist/core/env-secret-ref.js +90 -0
- package/dist/core/errors.js +13 -3
- package/dist/core/events.js +27 -4
- package/dist/core/file-lock.js +1 -1
- package/dist/core/improve-types.js +48 -0
- package/dist/core/lesson-lint.js +2 -2
- package/dist/core/paths.js +2 -2
- package/dist/{setup/ripgrep-install.js → core/ripgrep/install.js} +2 -2
- package/dist/{setup/ripgrep-resolve.js → core/ripgrep/resolve.js} +2 -2
- package/dist/core/state-db.js +88 -46
- package/dist/core/text-truncation.js +148 -0
- package/dist/core/time.js +1 -1
- package/dist/core/write-source.js +98 -85
- package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
- package/dist/indexer/{db.js → db/db.js} +126 -116
- package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
- package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
- package/dist/indexer/ensure-index.js +4 -4
- package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
- package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
- package/dist/indexer/indexer.js +37 -30
- package/dist/indexer/init.js +54 -0
- package/dist/indexer/manifest.js +10 -10
- package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +92 -23
- package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
- package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
- package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
- package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
- package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
- package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
- package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
- package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
- package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
- package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
- package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
- package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
- package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
- package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
- package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
- package/dist/indexer/{walker.js → walk/walker.js} +4 -3
- package/dist/integrations/agent/builder-shared.js +39 -0
- package/dist/integrations/agent/builders.js +14 -81
- package/dist/integrations/agent/config.js +6 -4
- package/dist/integrations/agent/detect.js +1 -1
- package/dist/integrations/agent/index.js +23 -8
- package/dist/integrations/agent/prompts.js +2 -3
- package/dist/integrations/agent/runner.js +22 -3
- package/dist/integrations/agent/spawn.js +9 -10
- package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
- package/dist/integrations/harnesses/claude/config-import.js +70 -0
- package/dist/integrations/harnesses/claude/index.js +64 -0
- package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +16 -1
- package/dist/integrations/harnesses/index.js +144 -0
- package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
- package/dist/integrations/harnesses/opencode/config-import.js +82 -0
- package/dist/integrations/harnesses/opencode/index.js +59 -0
- package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
- package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
- package/dist/integrations/harnesses/types.js +43 -0
- package/dist/integrations/lockfile.js +7 -16
- package/dist/integrations/session-logs/index.js +82 -9
- package/dist/llm/call-ai.js +4 -4
- package/dist/llm/client.js +131 -6
- package/dist/llm/embedder.js +6 -6
- package/dist/llm/embedders/local.js +9 -22
- package/dist/llm/embedders/remote.js +2 -2
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +31 -12
- package/dist/llm/index-passes.js +1 -1
- package/dist/llm/memory-infer.js +12 -5
- package/dist/llm/metadata-enhance.js +2 -2
- package/dist/output/context.js +6 -44
- package/dist/output/renderers.js +88 -58
- package/dist/output/shapes/curate.js +7 -3
- package/dist/output/shapes/distill.js +7 -3
- package/dist/output/shapes/env-list.js +18 -16
- package/dist/output/shapes/events.js +5 -4
- package/dist/output/shapes/helpers.js +2 -4
- package/dist/output/shapes/history.js +7 -3
- package/dist/output/shapes/passthrough.js +8 -11
- package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
- package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
- package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
- package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
- package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
- package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
- package/dist/output/shapes/registry-search.js +7 -3
- package/dist/output/shapes/registry.js +12 -0
- package/dist/output/shapes/search.js +7 -3
- package/dist/output/shapes/secret-list.js +18 -16
- package/dist/output/shapes/show.js +7 -3
- package/dist/output/shapes.js +55 -30
- package/dist/output/text/add.js +2 -3
- package/dist/output/text/clone.js +2 -3
- package/dist/output/text/config.js +2 -3
- package/dist/output/text/curate.js +4 -3
- package/dist/output/text/distill.js +2 -3
- package/dist/output/text/enable-disable.js +5 -4
- package/dist/output/text/env.js +13 -0
- package/dist/output/text/events.js +5 -4
- package/dist/output/text/feedback.js +4 -3
- package/dist/output/text/helpers.js +54 -39
- package/dist/output/text/history.js +2 -3
- package/dist/output/text/import.js +2 -3
- package/dist/output/text/index.js +2 -3
- package/dist/output/text/info.js +2 -3
- package/dist/output/text/init.js +2 -3
- package/dist/output/text/list.js +2 -3
- package/dist/output/text/proposal/producer.js +9 -0
- package/dist/output/text/proposal/proposal.js +13 -0
- package/dist/output/text/registry-commands.js +8 -7
- package/dist/output/text/registry.js +12 -0
- package/dist/output/text/remember.js +4 -3
- package/dist/output/text/remove.js +2 -3
- package/dist/output/text/save.js +2 -3
- package/dist/output/text/search.js +4 -3
- package/dist/output/text/show.js +4 -3
- package/dist/output/text/update.js +2 -3
- package/dist/output/text/upgrade.js +2 -3
- package/dist/output/text/wiki.js +12 -11
- package/dist/output/text/workflow.js +12 -10
- package/dist/output/text.js +66 -32
- package/dist/registry/build-index.js +11 -10
- package/dist/registry/factory.js +1 -1
- package/dist/registry/origin-resolve.js +1 -1
- package/dist/registry/providers/index.js +2 -2
- package/dist/registry/providers/skills-sh.js +91 -72
- package/dist/registry/providers/static-index.js +75 -52
- package/dist/registry/resolve.js +3 -3
- package/dist/runtime.js +242 -0
- package/dist/scripts/migrate-storage.js +1594 -673
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +240 -166
- package/dist/setup/detect.js +338 -9
- package/dist/setup/harness-config-import.js +56 -0
- package/dist/setup/registry-stash-loader.js +99 -0
- package/dist/setup/setup.js +664 -96
- package/dist/sources/include.js +1 -1
- package/dist/sources/provider-factory.js +2 -2
- package/dist/sources/providers/filesystem.js +3 -3
- package/dist/sources/providers/git.js +9 -9
- package/dist/sources/providers/index.js +4 -4
- package/dist/sources/providers/npm.js +6 -6
- package/dist/sources/providers/provider-utils.js +13 -20
- package/dist/sources/providers/sync-from-ref.js +5 -5
- package/dist/sources/providers/tar-utils.js +2 -2
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +5 -5
- package/dist/sources/website-ingest.js +5 -5
- package/dist/storage/database.js +102 -0
- package/dist/storage/engines/sqlite-migrations.js +42 -0
- package/dist/storage/locations.js +25 -0
- package/dist/storage/repositories/index-db.js +43 -0
- package/dist/storage/repositories/workflow-runs-repository.js +141 -0
- package/dist/tasks/backends/cron.js +4 -4
- package/dist/tasks/backends/exec-utils.js +32 -0
- package/dist/tasks/backends/index.js +3 -3
- package/dist/tasks/backends/launchd.js +7 -14
- package/dist/tasks/backends/schtasks.js +7 -16
- package/dist/tasks/embedded.js +71 -0
- package/dist/tasks/parser.js +2 -2
- package/dist/tasks/resolveAkmBin.js +1 -1
- package/dist/tasks/runner.js +28 -15
- package/dist/tasks/schedule.js +1 -1
- package/dist/tasks/validator.js +7 -7
- package/dist/text-import-hook.mjs +51 -0
- package/dist/version.js +2 -1
- package/dist/wiki/wiki.js +7 -7
- package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
- package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
- package/dist/workflows/cli.js +1 -1
- package/dist/workflows/db.js +50 -32
- package/dist/workflows/parser.js +4 -4
- package/dist/workflows/renderer.js +5 -5
- package/dist/workflows/runtime/agent-identity.js +56 -0
- package/dist/workflows/runtime/checkin.js +57 -0
- package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
- package/dist/workflows/validate-summary.js +82 -0
- package/docs/README.md +1 -1
- package/docs/data-and-telemetry.md +6 -6
- package/package.json +16 -8
- package/dist/commands/add-cli.js +0 -279
- package/dist/commands/env.js +0 -213
- package/dist/integrations/agent/sdk-runner.js +0 -126
- package/dist/output/shapes/vault-list.js +0 -19
- package/dist/output/text/proposal-producer.js +0 -8
- package/dist/output/text/proposal.js +0 -12
- package/dist/output/text/vault.js +0 -16
- /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
- /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
- /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
- /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
- /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
- /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
- /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
- /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
|
@@ -18,31 +18,33 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import fs from "node:fs";
|
|
20
20
|
import path from "node:path";
|
|
21
|
-
import { parseAssetRef } from "
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import { listRelatedPathsForFile } from "
|
|
31
|
-
import { lookup } from "
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
21
|
+
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
22
|
+
import { parseFrontmatter } from "../../core/asset/frontmatter.js";
|
|
23
|
+
import { META_DIR, parseMetaRef, resolveMetaFilePath } from "../../core/asset/stash-meta.js";
|
|
24
|
+
import { asNonEmptyString } from "../../core/common.js";
|
|
25
|
+
import { loadConfig } from "../../core/config/config.js";
|
|
26
|
+
import { NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
|
|
27
|
+
import { appendEvent, readEvents } from "../../core/events.js";
|
|
28
|
+
import { findEntryIdByRef } from "../../indexer/db/db.js";
|
|
29
|
+
import { ensureIndex } from "../../indexer/ensure-index.js";
|
|
30
|
+
import { listRelatedPathsForFile } from "../../indexer/graph/graph-boost.js";
|
|
31
|
+
import { lookup } from "../../indexer/indexer.js";
|
|
32
|
+
import { buildEditHint, findSourceForPath, isEditable, resolveSourceEntries } from "../../indexer/search/search-source.js";
|
|
33
|
+
import { insertUsageEvent } from "../../indexer/usage/usage-events.js";
|
|
34
|
+
import { buildFileContext, buildRenderContext, getRenderer, runMatchers } from "../../indexer/walk/file-context.js";
|
|
35
|
+
import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
|
|
36
|
+
import { resolveSourcesForOrigin } from "../../registry/origin-resolve.js";
|
|
37
|
+
import { withIndexDb } from "../../storage/repositories/index-db.js";
|
|
36
38
|
// Eagerly import source providers to trigger self-registration.
|
|
37
|
-
import "
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
39
|
+
import "../../sources/providers/index.js";
|
|
40
|
+
import { getCurrentWorkflowScopeKey } from "../../workflows/authoring/scope-key.js";
|
|
41
|
+
import { getActiveWorkflowRun } from "../../workflows/runtime/runs.js";
|
|
40
42
|
/**
|
|
41
43
|
* Show a wiki root (no page path) — returns the same payload as
|
|
42
44
|
* `akm wiki show <name>`.
|
|
43
45
|
*/
|
|
44
46
|
async function showWikiRoot(stashDir, wikiName) {
|
|
45
|
-
const { showWiki } = await import("
|
|
47
|
+
const { showWiki } = await import("../../wiki/wiki.js");
|
|
46
48
|
const result = showWiki(stashDir, wikiName);
|
|
47
49
|
return {
|
|
48
50
|
type: "wiki",
|
|
@@ -58,7 +60,7 @@ async function showWikiRoot(stashDir, wikiName) {
|
|
|
58
60
|
};
|
|
59
61
|
}
|
|
60
62
|
async function showWikiRootForSource(stashDir, source, wikiName) {
|
|
61
|
-
const { showWikiAtPath } = await import("
|
|
63
|
+
const { showWikiAtPath } = await import("../../wiki/wiki.js");
|
|
62
64
|
if (source.wikiName === wikiName) {
|
|
63
65
|
const result = showWikiAtPath(wikiName, source.path);
|
|
64
66
|
return {
|
|
@@ -105,6 +107,16 @@ function resolveRegisteredWikiAssetPath(wikiRoot, wikiName, assetName) {
|
|
|
105
107
|
*/
|
|
106
108
|
export async function akmShowUnified(input) {
|
|
107
109
|
const ref = input.ref.trim();
|
|
110
|
+
// 0a. Stash `.meta/` convention: `[origin//]meta[:name]` direct-reads a
|
|
111
|
+
// human-authored orientation doc from the stash's `.meta/` directory.
|
|
112
|
+
// These files are not indexed (the walker skips dot-dirs), so they are
|
|
113
|
+
// resolved here before the index lookup and the `type:name` parser,
|
|
114
|
+
// which would otherwise reject the non-asset-type `meta`.
|
|
115
|
+
{
|
|
116
|
+
const metaRef = parseMetaRef(ref);
|
|
117
|
+
if (metaRef)
|
|
118
|
+
return showStashMeta(metaRef);
|
|
119
|
+
}
|
|
108
120
|
// 0. Wiki-root shortcut: `wiki:<name>` with no page path routes to the
|
|
109
121
|
// wiki summary (same payload as `akm wiki show <name>`). Honour
|
|
110
122
|
// `parsed.origin` by resolving against the matching stash source(s),
|
|
@@ -145,13 +157,49 @@ export async function akmShowUnified(input) {
|
|
|
145
157
|
}
|
|
146
158
|
// Count prior shows of this ref before logging the current one.
|
|
147
159
|
const priorShowCount = recentShowCount(ref);
|
|
148
|
-
logShowEvent(ref,
|
|
160
|
+
logShowEvent(ref, input.eventSource);
|
|
149
161
|
if (priorShowCount >= 2) {
|
|
150
162
|
// Agent has shown this same asset 3+ times — inject a loop-break hint.
|
|
151
163
|
result.showLoopWarning = priorShowCount + 1;
|
|
152
164
|
}
|
|
153
165
|
return result;
|
|
154
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Resolve a stash `.meta/` doc and return it as a lightweight ShowResponse.
|
|
169
|
+
*
|
|
170
|
+
* With no origin the working stash (and other configured sources, in order)
|
|
171
|
+
* is searched and the first hit wins. With an origin the lookup is narrowed
|
|
172
|
+
* to that stash; an uninstalled origin yields an actionable "not installed"
|
|
173
|
+
* error. The file is read directly from disk — `.meta/` is never indexed.
|
|
174
|
+
*/
|
|
175
|
+
async function showStashMeta(metaRef) {
|
|
176
|
+
const allSources = resolveSourceEntries();
|
|
177
|
+
const sources = resolveSourcesForOrigin(metaRef.origin, allSources);
|
|
178
|
+
if (metaRef.origin && sources.length === 0) {
|
|
179
|
+
throw new NotFoundError(`Stash "${metaRef.origin}" is not installed, so its ${META_DIR}/ docs are unavailable. ` +
|
|
180
|
+
`Run: akm add ${metaRef.origin}`);
|
|
181
|
+
}
|
|
182
|
+
const config = loadConfig();
|
|
183
|
+
for (const source of sources) {
|
|
184
|
+
const filePath = resolveMetaFilePath(source.path, metaRef.name);
|
|
185
|
+
if (!filePath)
|
|
186
|
+
continue;
|
|
187
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
188
|
+
const editable = isEditable(filePath, config);
|
|
189
|
+
appendEvent({ eventType: "show", ref: `meta:${metaRef.name}`, metadata: { type: "meta", name: metaRef.name } });
|
|
190
|
+
return {
|
|
191
|
+
type: "meta",
|
|
192
|
+
name: metaRef.name,
|
|
193
|
+
path: filePath,
|
|
194
|
+
content,
|
|
195
|
+
origin: source.registryId ?? null,
|
|
196
|
+
editable,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
throw new NotFoundError(`No ${META_DIR}/${metaRef.name} doc found${metaRef.origin ? ` in "${metaRef.origin}"` : ""}. ` +
|
|
200
|
+
`Stash maintainers can create ${META_DIR}/${metaRef.name}.md to describe this stash ` +
|
|
201
|
+
`(purpose, key assets, conventions, maintainer).`);
|
|
202
|
+
}
|
|
155
203
|
function hasAnyScopeKey(scope) {
|
|
156
204
|
return Boolean(scope.user || scope.agent || scope.run || scope.channel);
|
|
157
205
|
}
|
|
@@ -206,7 +254,7 @@ function recentShowCount(ref) {
|
|
|
206
254
|
return 0;
|
|
207
255
|
}
|
|
208
256
|
}
|
|
209
|
-
function logShowEvent(ref,
|
|
257
|
+
function logShowEvent(ref, eventSource = "user") {
|
|
210
258
|
// Emit a structured event to events.jsonl so workflow-trace consumers
|
|
211
259
|
// detect akm show invocations without relying on stdout scraping.
|
|
212
260
|
const parsed = parseAssetRef(ref);
|
|
@@ -241,19 +289,14 @@ function logShowEvent(ref, existingDb, eventSource = "user") {
|
|
|
241
289
|
/* fire-and-forget — select is best-effort */
|
|
242
290
|
}
|
|
243
291
|
try {
|
|
244
|
-
|
|
245
|
-
try {
|
|
292
|
+
withIndexDb((db) => {
|
|
246
293
|
insertUsageEvent(db, {
|
|
247
294
|
event_type: "show",
|
|
248
295
|
entry_ref: ref,
|
|
249
296
|
entry_id: findEntryIdByRef(db, ref),
|
|
250
297
|
source: eventSource,
|
|
251
298
|
});
|
|
252
|
-
}
|
|
253
|
-
finally {
|
|
254
|
-
if (!existingDb)
|
|
255
|
-
closeDatabase(db);
|
|
256
|
-
}
|
|
299
|
+
});
|
|
257
300
|
}
|
|
258
301
|
catch (err) {
|
|
259
302
|
rethrowIfTestIsolationError(err);
|
|
@@ -324,20 +367,16 @@ export async function showLocal(input) {
|
|
|
324
367
|
editable,
|
|
325
368
|
...(!editable ? { editHint: buildEditHint(assetPath, parsed.type, parsed.name, source?.registryId) } : {}),
|
|
326
369
|
related: (() => {
|
|
327
|
-
let db;
|
|
328
370
|
try {
|
|
329
|
-
db
|
|
330
|
-
|
|
331
|
-
|
|
371
|
+
return withIndexDb((db) => {
|
|
372
|
+
const related = listRelatedPathsForFile(sourceStashDir, assetPath, 5, db);
|
|
373
|
+
return { total: related.length, hits: related };
|
|
374
|
+
});
|
|
332
375
|
}
|
|
333
376
|
catch (err) {
|
|
334
377
|
rethrowIfTestIsolationError(err);
|
|
335
378
|
return { total: 0, hits: [] };
|
|
336
379
|
}
|
|
337
|
-
finally {
|
|
338
|
-
if (db)
|
|
339
|
-
closeDatabase(db);
|
|
340
|
-
}
|
|
341
380
|
})(),
|
|
342
381
|
};
|
|
343
382
|
const activeRun = await getActiveWorkflowRun(getCurrentWorkflowScopeKey());
|
|
@@ -435,8 +474,6 @@ export function normalizeShowArgv(argv) {
|
|
|
435
474
|
// argv[0]=bun argv[1]=script argv[2]=subcommand argv[3]=ref argv[4..]=rest
|
|
436
475
|
if (argv[2] !== "show")
|
|
437
476
|
return argv;
|
|
438
|
-
if (argv[3] === "proposal")
|
|
439
|
-
return argv;
|
|
440
477
|
if (argv.includes("--view") || argv.includes("--heading") || argv.includes("--start") || argv.includes("--end")) {
|
|
441
478
|
throw new UsageError('Legacy show flags are no longer supported. Use positional syntax like `akm show knowledge:guide toc` or `akm show knowledge:guide section "Auth"`.');
|
|
442
479
|
}
|
|
@@ -447,11 +484,7 @@ export function normalizeShowArgv(argv) {
|
|
|
447
484
|
const showArgs = [];
|
|
448
485
|
for (let i = 0; i < rest.length; i++) {
|
|
449
486
|
const arg = rest[i];
|
|
450
|
-
if (arg === "--quiet" ||
|
|
451
|
-
arg === "-q" ||
|
|
452
|
-
arg === "--verbose" ||
|
|
453
|
-
arg === "--for-agent" ||
|
|
454
|
-
arg === "--for-agent=true") {
|
|
487
|
+
if (arg === "--quiet" || arg === "-q" || arg === "--verbose") {
|
|
455
488
|
globalFlags.push(arg);
|
|
456
489
|
continue;
|
|
457
490
|
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
|
-
import { parsePositiveIntFlag } from "../cli/parse-args";
|
|
6
|
-
import { output, runWithJsonErrors } from "../cli/shared";
|
|
7
|
-
import { DEFAULT_CONFIG, loadUserConfig, saveConfig } from "../core/config";
|
|
8
|
-
import { UsageError } from "../core/errors";
|
|
9
|
-
import { warn } from "../core/warn";
|
|
10
|
-
import { getHyphenatedArg, getHyphenatedBoolean } from "../output/context";
|
|
11
|
-
import { buildRegistryIndex, writeRegistryIndex } from "../registry/build-index";
|
|
12
|
-
import { searchRegistry } from "./registry-search";
|
|
5
|
+
import { parsePositiveIntFlag } from "../cli/parse-args.js";
|
|
6
|
+
import { output, runWithJsonErrors } from "../cli/shared.js";
|
|
7
|
+
import { DEFAULT_CONFIG, loadUserConfig, saveConfig } from "../core/config/config.js";
|
|
8
|
+
import { UsageError } from "../core/errors.js";
|
|
9
|
+
import { warn } from "../core/warn.js";
|
|
10
|
+
import { getHyphenatedArg, getHyphenatedBoolean } from "../output/context.js";
|
|
11
|
+
import { buildRegistryIndex, writeRegistryIndex } from "../registry/build-index.js";
|
|
12
|
+
import { searchRegistry } from "./read/registry-search.js";
|
|
13
13
|
export const registryCommand = defineCommand({
|
|
14
14
|
meta: { name: "registry", description: "Manage stash registries" },
|
|
15
15
|
subCommands: {
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
* heuristic derivation, LLM enrichment) is testable in isolation and the
|
|
9
9
|
* CLI entry point stays focused on argument parsing + output routing.
|
|
10
10
|
*/
|
|
11
|
-
import { serializeFrontmatter } from "../core/asset-serialize";
|
|
12
|
-
import { toErrorMessage, tryReadStdinText } from "../core/common";
|
|
13
|
-
import { getDefaultLlmConfig, loadConfig } from "../core/config";
|
|
14
|
-
import { UsageError } from "../core/errors";
|
|
15
|
-
import { warn } from "../core/warn";
|
|
16
|
-
import { SCOPE_KEYS } from "../indexer/metadata";
|
|
17
|
-
import { parseFlagValue } from "../output/context";
|
|
11
|
+
import { serializeFrontmatter } from "../core/asset/asset-serialize.js";
|
|
12
|
+
import { toErrorMessage, tryReadStdinText } from "../core/common.js";
|
|
13
|
+
import { getDefaultLlmConfig, loadConfig } from "../core/config/config.js";
|
|
14
|
+
import { UsageError } from "../core/errors.js";
|
|
15
|
+
import { warn } from "../core/warn.js";
|
|
16
|
+
import { SCOPE_KEYS } from "../indexer/passes/metadata.js";
|
|
17
|
+
import { parseFlagValue } from "../output/context.js";
|
|
18
18
|
/**
|
|
19
19
|
* Parse a shorthand duration string to a number of milliseconds.
|
|
20
20
|
* Supports: `30d` (days), `12h` (hours), `6m` (months, approximated as 30d).
|
|
@@ -148,7 +148,7 @@ export async function runLlmEnrich(body) {
|
|
|
148
148
|
warn("Warning: --enrich requires an LLM to be configured. Run `akm setup` to configure one.");
|
|
149
149
|
return { tags: [] };
|
|
150
150
|
}
|
|
151
|
-
const { chatCompletion, parseEmbeddedJsonResponse: parseJsonResponse } = await import("../llm/client");
|
|
151
|
+
const { chatCompletion, parseEmbeddedJsonResponse: parseJsonResponse } = await import("../llm/client.js");
|
|
152
152
|
const prompt = `You are a memory tagger for a developer knowledge base.
|
|
153
153
|
Given the memory text below, return ONLY a JSON object with these fields:
|
|
154
154
|
- "tags": array of 1-5 short lowercase keyword tags
|
|
@@ -0,0 +1,293 @@
|
|
|
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.js";
|
|
9
|
+
import { UsageError } from "../../core/errors.js";
|
|
10
|
+
import { appendEvent } from "../../core/events.js";
|
|
11
|
+
import { warn } from "../../core/warn.js";
|
|
12
|
+
import { getHyphenatedBoolean } from "../../output/context.js";
|
|
13
|
+
import { akmRemove } from "./installed-stashes.js";
|
|
14
|
+
import { akmAdd } from "./source-add.js";
|
|
15
|
+
import { addStash } from "./source-manage.js";
|
|
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
|
+
/** Scan every env file in the freshly-installed stash for dangerous env keys. */
|
|
43
|
+
function collectDangerousKeyFindings(installedStashRoot, checkEnvForDangerousKeys) {
|
|
44
|
+
const allFindings = [];
|
|
45
|
+
const subdir = "env";
|
|
46
|
+
const prefix = "env";
|
|
47
|
+
const dir = path.join(installedStashRoot, subdir);
|
|
48
|
+
const envFiles = fs.existsSync(dir) ? fs.readdirSync(dir).filter((f) => f.endsWith(".env")) : [];
|
|
49
|
+
for (const envFile of envFiles) {
|
|
50
|
+
const envPath = path.join(dir, envFile);
|
|
51
|
+
const baseName = path.basename(envFile, ".env");
|
|
52
|
+
const vaultRef = baseName === "" ? `${prefix}:default` : `${prefix}:${baseName}`;
|
|
53
|
+
const relPath = path.join(subdir, envFile);
|
|
54
|
+
const findings = checkEnvForDangerousKeys(envPath, relPath, vaultRef);
|
|
55
|
+
for (const finding of findings) {
|
|
56
|
+
// Extract the key name from the detail string for the summary line.
|
|
57
|
+
const keyMatch = finding.detail.match(/Env key `([^`]+)`/);
|
|
58
|
+
const keyName = keyMatch ? keyMatch[1] : finding.file;
|
|
59
|
+
allFindings.push({ vaultRef, keyName, relPath });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return allFindings;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Audit a freshly-installed stash for dangerous env keys and decide whether the
|
|
66
|
+
* install must be blocked. Returns a typed decision instead of calling
|
|
67
|
+
* `process.exit`, so the abort cannot be lost to a swallowed exception. See the
|
|
68
|
+
* block comment above for the security rationale.
|
|
69
|
+
*/
|
|
70
|
+
export async function auditInstalledStashForDangerousKeys(opts) {
|
|
71
|
+
const { installedStashRoot, ref, allowDangerousKeys, rollbackTarget, isTTY } = opts;
|
|
72
|
+
// Best-effort scan: if collecting findings itself throws (corrupt env file,
|
|
73
|
+
// fs error) there is nothing concrete to block on, so fail soft. Crucially,
|
|
74
|
+
// this soft path runs BEFORE any findings exist — it can never re-open an
|
|
75
|
+
// already-detected dangerous install.
|
|
76
|
+
let allFindings;
|
|
77
|
+
try {
|
|
78
|
+
const { checkEnvForDangerousKeys } = await import("../lint/env-key-rules.js");
|
|
79
|
+
allFindings = collectDangerousKeyFindings(installedStashRoot, checkEnvForDangerousKeys);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { blocked: false };
|
|
83
|
+
}
|
|
84
|
+
if (allFindings.length === 0)
|
|
85
|
+
return { blocked: false };
|
|
86
|
+
if (allowDangerousKeys) {
|
|
87
|
+
// Operator has explicitly accepted the risk — warn and continue.
|
|
88
|
+
for (const f of allFindings) {
|
|
89
|
+
warn(`[dangerous-vault-key] ${f.relPath}: key \`${f.keyName}\` in ${f.vaultRef} can hijack process execution via \`akm env run\`. Proceeding because --allow-insecure was set.`);
|
|
90
|
+
}
|
|
91
|
+
return { blocked: false };
|
|
92
|
+
}
|
|
93
|
+
// Helper: roll the install back before aborting. Rollback is best-effort; a
|
|
94
|
+
// failed rollback never UN-blocks the install — we still abort, just with a
|
|
95
|
+
// warning telling the operator to remove the stash manually.
|
|
96
|
+
async function rollback() {
|
|
97
|
+
try {
|
|
98
|
+
await akmRemove({ target: rollbackTarget });
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
catch (_rollbackErr) {
|
|
102
|
+
return (`Rollback failed — stash may still be installed at ${installedStashRoot}. ` +
|
|
103
|
+
`Remove it manually with: akm remove ${rollbackTarget}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (isTTY) {
|
|
107
|
+
// Interactive path: show findings and ask the user to confirm.
|
|
108
|
+
// Guard on stdin (not stdout) because p.confirm() reads from stdin;
|
|
109
|
+
// stdout may be a TTY while stdin is piped, which would cause a hang.
|
|
110
|
+
const stashLabel = ref;
|
|
111
|
+
const groupedByVault = new Map();
|
|
112
|
+
for (const f of allFindings) {
|
|
113
|
+
const existing = groupedByVault.get(f.vaultRef) ?? [];
|
|
114
|
+
existing.push(f.keyName);
|
|
115
|
+
groupedByVault.set(f.vaultRef, existing);
|
|
116
|
+
}
|
|
117
|
+
for (const [vaultRef, keys] of groupedByVault) {
|
|
118
|
+
warn(`[warn] Env "${vaultRef}" in stash "${stashLabel}" contains potentially dangerous keys:`);
|
|
119
|
+
for (const key of keys) {
|
|
120
|
+
warn(` - ${key}: can hijack process execution via \`akm env run\``);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const confirmed = await p.confirm({
|
|
124
|
+
message: "Install anyway?",
|
|
125
|
+
initialValue: false,
|
|
126
|
+
});
|
|
127
|
+
if (p.isCancel(confirmed) || confirmed !== true) {
|
|
128
|
+
const rollbackWarning = await rollback();
|
|
129
|
+
console.error(JSON.stringify({
|
|
130
|
+
ok: false,
|
|
131
|
+
error: "Install aborted: stash contains dangerous env keys. Remove the keys or re-run with --allow-insecure to bypass.",
|
|
132
|
+
code: "DANGEROUS_VAULT_KEY",
|
|
133
|
+
...(rollbackWarning ? { rollbackWarning } : {}),
|
|
134
|
+
}, null, 2));
|
|
135
|
+
return { blocked: true, exitCode: 1 };
|
|
136
|
+
}
|
|
137
|
+
// Operator confirmed at the prompt — allow the install to proceed.
|
|
138
|
+
return { blocked: false };
|
|
139
|
+
}
|
|
140
|
+
// Non-interactive path without bypass flag: fail hard.
|
|
141
|
+
const rollbackWarning = await rollback();
|
|
142
|
+
const keyList = allFindings.map((f) => ` - ${f.keyName} (${f.vaultRef})`).join("\n");
|
|
143
|
+
console.error(JSON.stringify({
|
|
144
|
+
ok: false,
|
|
145
|
+
error: `Install blocked: stash "${ref}" contains dangerous env keys that can hijack process execution via \`akm env run\`:\n${keyList}\nRe-run with --allow-insecure to bypass this check after reviewing the env file.`,
|
|
146
|
+
code: "DANGEROUS_VAULT_KEY",
|
|
147
|
+
...(rollbackWarning ? { rollbackWarning } : {}),
|
|
148
|
+
}, null, 2));
|
|
149
|
+
return { blocked: true, exitCode: 1 };
|
|
150
|
+
}
|
|
151
|
+
// ── Command definition ────────────────────────────────────────────────────────
|
|
152
|
+
export const addCommand = defineCommand({
|
|
153
|
+
meta: {
|
|
154
|
+
name: "add",
|
|
155
|
+
description: "Add a source (local directory, website, npm package, GitHub repo, git URL, or remote provider)",
|
|
156
|
+
},
|
|
157
|
+
args: {
|
|
158
|
+
ref: {
|
|
159
|
+
type: "positional",
|
|
160
|
+
description: "Path, URL, or registry ref (website URL, npm package, owner/repo, git URL, or local directory)",
|
|
161
|
+
required: true,
|
|
162
|
+
},
|
|
163
|
+
provider: { type: "string", description: "Provider type (e.g. website, npm). Required for URL sources." },
|
|
164
|
+
options: { type: "string", description: 'Provider options as JSON (e.g. \'{"apiKey":"key"}\').' },
|
|
165
|
+
name: { type: "string", description: "Human-friendly name for the source" },
|
|
166
|
+
writable: {
|
|
167
|
+
type: "boolean",
|
|
168
|
+
description: "Mark a git stash as writable so changes can be pushed back",
|
|
169
|
+
default: false,
|
|
170
|
+
},
|
|
171
|
+
type: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description: "Override asset type for all files in this stash (currently supports: wiki)",
|
|
174
|
+
},
|
|
175
|
+
"max-pages": { type: "string", description: "Maximum pages to crawl for website sources (default: 50)" },
|
|
176
|
+
"max-depth": { type: "string", description: "Maximum crawl depth for website sources (default: 3)" },
|
|
177
|
+
"allow-insecure": {
|
|
178
|
+
type: "boolean",
|
|
179
|
+
description: "Allow a plain HTTP source URL and skip confirmation for dangerous env keys (e.g. LD_PRELOAD, PATH). Use only after explicitly reviewing the stash.",
|
|
180
|
+
default: false,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
async run({ args }) {
|
|
184
|
+
await runWithJsonErrors(async () => {
|
|
185
|
+
const ref = args.ref.trim();
|
|
186
|
+
const allowInsecure = getHyphenatedBoolean(args, "allow-insecure");
|
|
187
|
+
const allowDangerousKeys = allowInsecure;
|
|
188
|
+
// URL with --provider → stash source (remote or git provider)
|
|
189
|
+
if (args.provider) {
|
|
190
|
+
if (shouldWarnOnPlainHttp(ref)) {
|
|
191
|
+
if (!allowInsecure) {
|
|
192
|
+
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
193
|
+
"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.");
|
|
194
|
+
}
|
|
195
|
+
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
196
|
+
}
|
|
197
|
+
let parsedOptions;
|
|
198
|
+
if (args.options) {
|
|
199
|
+
try {
|
|
200
|
+
const parsed = JSON.parse(args.options);
|
|
201
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
202
|
+
throw new UsageError("--options must be a JSON object");
|
|
203
|
+
}
|
|
204
|
+
parsedOptions = parsed;
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
if (err instanceof UsageError)
|
|
208
|
+
throw err;
|
|
209
|
+
throw new UsageError("--options must be valid JSON");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const result = addStash({
|
|
213
|
+
target: ref,
|
|
214
|
+
name: args.name,
|
|
215
|
+
providerType: args.provider,
|
|
216
|
+
options: parsedOptions,
|
|
217
|
+
writable: args.writable,
|
|
218
|
+
});
|
|
219
|
+
appendEvent({
|
|
220
|
+
eventType: "add",
|
|
221
|
+
metadata: { target: ref, provider: args.provider, name: args.name ?? null, writable: args.writable === true },
|
|
222
|
+
});
|
|
223
|
+
output("add", result);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (shouldWarnOnPlainHttp(ref)) {
|
|
227
|
+
if (!allowInsecure) {
|
|
228
|
+
throw new UsageError("Source URL uses plain HTTP (not HTTPS). An on-path attacker could substitute a malicious payload. " +
|
|
229
|
+
"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.");
|
|
230
|
+
}
|
|
231
|
+
warn("Warning: source URL uses plain HTTP (not HTTPS). --allow-insecure was set; an on-path attacker could substitute a malicious payload.");
|
|
232
|
+
}
|
|
233
|
+
const websiteOptions = buildWebsiteOptions(args);
|
|
234
|
+
if (args.type === "wiki") {
|
|
235
|
+
const { registerWikiSource } = await import("./source-add.js");
|
|
236
|
+
const result = await registerWikiSource({
|
|
237
|
+
ref,
|
|
238
|
+
name: args.name,
|
|
239
|
+
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
240
|
+
writable: args.writable,
|
|
241
|
+
});
|
|
242
|
+
appendEvent({
|
|
243
|
+
eventType: "add",
|
|
244
|
+
metadata: { target: ref, type: "wiki", name: args.name ?? null, writable: args.writable === true },
|
|
245
|
+
});
|
|
246
|
+
output("add", result);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const result = await akmAdd({
|
|
250
|
+
ref,
|
|
251
|
+
name: args.name,
|
|
252
|
+
overrideType: args.type,
|
|
253
|
+
options: Object.keys(websiteOptions).length > 0 ? websiteOptions : undefined,
|
|
254
|
+
writable: args.writable,
|
|
255
|
+
});
|
|
256
|
+
appendEvent({
|
|
257
|
+
eventType: "add",
|
|
258
|
+
metadata: {
|
|
259
|
+
target: ref,
|
|
260
|
+
name: args.name ?? null,
|
|
261
|
+
overrideType: args.type ?? null,
|
|
262
|
+
writable: args.writable === true,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
// ── Post-install env key audit ──────────────────────────────────────────
|
|
266
|
+
// Resolve the stash root from the install result and scan any env files
|
|
267
|
+
// for dangerous env var keys. When findings are present the install is
|
|
268
|
+
// gated: TTY → interactive confirmation prompt; non-TTY without
|
|
269
|
+
// --allow-insecure → hard failure (exit 1). Pass
|
|
270
|
+
// --allow-insecure to skip the prompt non-interactively.
|
|
271
|
+
const installedStashRoot = result.installed?.stashRoot ??
|
|
272
|
+
(result.sourceAdded && "stashRoot" in result.sourceAdded ? result.sourceAdded.stashRoot : undefined);
|
|
273
|
+
if (installedStashRoot) {
|
|
274
|
+
// Use the canonical installed id (most reliably resolved by akmRemove) rather
|
|
275
|
+
// than the raw user-supplied ref which may not match after URL normalisation.
|
|
276
|
+
const rollbackTarget = result.installed?.id ?? result.sourceAdded?.stashRoot ?? ref;
|
|
277
|
+
// The audit RETURNS its decision; we decide `process.exit` here, OUTSIDE
|
|
278
|
+
// any catch, so the abort cannot be lost to a swallowed exception (C3).
|
|
279
|
+
const decision = await auditInstalledStashForDangerousKeys({
|
|
280
|
+
installedStashRoot,
|
|
281
|
+
ref,
|
|
282
|
+
allowDangerousKeys,
|
|
283
|
+
rollbackTarget,
|
|
284
|
+
isTTY: process.stdin.isTTY === true,
|
|
285
|
+
});
|
|
286
|
+
if (decision.blocked) {
|
|
287
|
+
process.exit(decision.exitCode);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
output("add", result);
|
|
291
|
+
});
|
|
292
|
+
},
|
|
293
|
+
});
|
|
@@ -1,12 +1,28 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
/**
|
|
5
|
+
* `akm history` — surfaces internal mutation/usage events for a single asset
|
|
6
|
+
* (`--ref`) or stash-wide.
|
|
7
|
+
*
|
|
8
|
+
* Event sources:
|
|
9
|
+
* - `usage_events` SQLite table: search, show, and feedback events recorded
|
|
10
|
+
* by the local indexer during normal CLI use.
|
|
11
|
+
* - `events.jsonl` append-only stream (opt-in via `--include-proposals`):
|
|
12
|
+
* proposal lifecycle events (`promoted`, `rejected`) emitted by
|
|
13
|
+
* `akm proposal accept` / `akm proposal reject`. Use this flag to see
|
|
14
|
+
* the full proposal review trail alongside usage events.
|
|
15
|
+
*
|
|
16
|
+
* The two sources are merged and sorted chronologically (oldest first) so
|
|
17
|
+
* consumers see a coherent lifecycle trail in a single output.
|
|
18
|
+
*/
|
|
19
|
+
import { parseAssetRef } from "../../core/asset/asset-ref.js";
|
|
20
|
+
import { UsageError } from "../../core/errors.js";
|
|
21
|
+
import { readEvents } from "../../core/events.js";
|
|
22
|
+
import { isoToSqlite, parseSinceToIso } from "../../core/time.js";
|
|
23
|
+
import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
|
|
24
|
+
import { getUsageEvents } from "../../indexer/usage/usage-events.js";
|
|
25
|
+
import { listProposals } from "../proposal/validators/proposals.js";
|
|
10
26
|
// Proposal lifecycle event types emitted by the proposal substrate (#225).
|
|
11
27
|
const PROPOSAL_EVENT_TYPES = new Set(["promoted", "rejected"]);
|
|
12
28
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -71,25 +87,11 @@ export async function akmHistory(options = {}) {
|
|
|
71
87
|
const db = options.db ?? openExistingDatabase();
|
|
72
88
|
const ownsDb = options.db === undefined;
|
|
73
89
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
if (sinceNormalized !== undefined) {
|
|
81
|
-
conditions.push("created_at >= ?");
|
|
82
|
-
params.push(sinceNormalized);
|
|
83
|
-
}
|
|
84
|
-
if (options.source !== undefined) {
|
|
85
|
-
conditions.push("source = ?");
|
|
86
|
-
params.push(options.source);
|
|
87
|
-
}
|
|
88
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
89
|
-
const sql = `SELECT id, event_type, query, entry_id, entry_ref, signal, metadata, source, created_at
|
|
90
|
-
FROM usage_events ${where}
|
|
91
|
-
ORDER BY id ASC`;
|
|
92
|
-
const rows = db.prepare(sql).all(...params);
|
|
90
|
+
const rows = getUsageEvents(db, {
|
|
91
|
+
entry_ref: normalizedRef,
|
|
92
|
+
since: sinceNormalized,
|
|
93
|
+
source: options.source,
|
|
94
|
+
});
|
|
93
95
|
const usageEntries = rows.map(toEntry);
|
|
94
96
|
// ── Proposal lifecycle events (opt-in) ────────────────────────────────
|
|
95
97
|
const sources = ["usage_events"];
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import fs from "node:fs";
|
|
5
|
-
import { getAssetTypes } from "
|
|
6
|
-
import { getSources, loadConfig } from "
|
|
7
|
-
import { getDbPath } from "
|
|
8
|
-
import { closeDatabase, getEntryCount, getMeta, isVecAvailable, openExistingDatabase } from "
|
|
9
|
-
import { getEffectiveSemanticStatus, readSemanticStatus } from "
|
|
10
|
-
import { pkgVersion } from "
|
|
5
|
+
import { getAssetTypes } from "../../core/asset/asset-spec.js";
|
|
6
|
+
import { getSources, loadConfig } from "../../core/config/config.js";
|
|
7
|
+
import { getDbPath } from "../../core/paths.js";
|
|
8
|
+
import { closeDatabase, getEntryCount, getMeta, isVecAvailable, openExistingDatabase } from "../../indexer/db/db.js";
|
|
9
|
+
import { getEffectiveSemanticStatus, readSemanticStatus } from "../../indexer/search/semantic-status.js";
|
|
10
|
+
import { pkgVersion } from "../../version.js";
|
|
11
11
|
/**
|
|
12
12
|
* Assemble system info describing the current capabilities, configuration,
|
|
13
13
|
* and index state. Used by `akm info`.
|