akm-cli 0.8.7 → 0.8.14
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 +428 -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/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/assets/templates/html/default.html +78 -0
- package/dist/assets/templates/html/health.html +560 -0
- package/dist/assets/templates/html/vendor/echarts.min.js +45 -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 +72 -19
- package/dist/cli-node.mjs +26 -0
- package/dist/cli.js +206 -3866
- 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 +230 -3
- 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/html-report.js +448 -0
- package/dist/commands/health.js +189 -266
- package/dist/commands/{consolidate.js → improve/consolidate.js} +48 -36
- 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} +221 -26
- package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
- package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
- package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
- package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
- package/dist/commands/{improve.js → improve/improve.js} +672 -292
- 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/improve/reflect-noise.js +0 -0
- package/dist/commands/{reflect.js → improve/reflect.js} +58 -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 +3 -3
- package/dist/commands/proposal/drain.js +87 -15
- package/dist/commands/proposal/proposal-cli.js +490 -0
- package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
- 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 +374 -345
- 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} +31 -45
- package/dist/commands/registry-cli.js +8 -8
- package/dist/commands/remember.js +14 -10
- 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} +6 -6
- 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/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
- 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/{stash-meta.js → asset/stash-meta.js} +1 -1
- 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} +50 -7
- 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/logs-db.js +304 -0
- package/dist/core/paths.js +2 -2
- package/dist/core/ripgrep/install.js +2 -2
- package/dist/core/ripgrep/resolve.js +2 -2
- package/dist/core/state-db.js +195 -60
- 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} +128 -118
- 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} +141 -33
- 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} +32 -5
- 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 +146 -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/llm/usage-persist.js +77 -0
- package/dist/llm/usage-telemetry.js +103 -0
- package/dist/output/context.js +9 -46
- package/dist/output/html-render.js +73 -0
- 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 +19 -5
- 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 +123 -40
- 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 +1654 -683
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
- package/dist/setup/detect.js +311 -9
- package/dist/setup/harness-config-import.js +6 -120
- package/dist/setup/setup.js +454 -43
- 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 +127 -31
- 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 +54 -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 +17 -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
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import fs from "node:fs";
|
|
14
14
|
import path from "node:path";
|
|
15
|
-
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
16
|
-
import { getCacheDir } from "../core/paths";
|
|
17
|
-
import { generateMetadataFlat, loadStashFile } from "../indexer/metadata";
|
|
18
|
-
import { walkStashFlat } from "../indexer/walker";
|
|
19
|
-
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
15
|
+
import { fetchWithRetry, jsonWithByteCap } from "../core/common.js";
|
|
16
|
+
import { getCacheDir } from "../core/paths.js";
|
|
17
|
+
import { generateMetadataFlat, loadStashFile } from "../indexer/passes/metadata.js";
|
|
18
|
+
import { walkStashFlat } from "../indexer/walk/walker.js";
|
|
19
|
+
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github.js";
|
|
20
|
+
import { writeResponseToFile } from "../runtime.js";
|
|
21
|
+
import { copyIncludedPaths, findNearestIncludeConfig } from "../sources/include.js";
|
|
22
|
+
import { detectStashRoot } from "../sources/providers/provider-utils.js";
|
|
23
|
+
import { extractTarGzSecure } from "../sources/providers/tar-utils.js";
|
|
24
|
+
import { parseRegistryIndex } from "./providers/static-index.js";
|
|
24
25
|
const DEFAULT_NPM_REGISTRY_BASE = "https://registry.npmjs.org";
|
|
25
26
|
const REQUIRED_KEYWORDS = ["akm-stash"];
|
|
26
27
|
const GITHUB_TOPICS = ["akm-stash"];
|
|
@@ -194,7 +195,7 @@ async function inspectArchive(url, headers) {
|
|
|
194
195
|
if (!response.ok) {
|
|
195
196
|
throw new Error(`Failed to fetch archive (${response.status}) from ${url}`);
|
|
196
197
|
}
|
|
197
|
-
await
|
|
198
|
+
await writeResponseToFile(archivePath, response);
|
|
198
199
|
// Reuse the secure extraction from registry-install which validates entries,
|
|
199
200
|
// uses --no-same-owner, strips components, and runs a post-extraction scan.
|
|
200
201
|
extractTarGzSecure(archivePath, extractDir);
|
package/dist/registry/factory.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* The legacy alias in `src/registry-provider.ts` is kept as a thin re-export
|
|
18
18
|
* for transitional callers and will be removed after the dust settles.
|
|
19
19
|
*/
|
|
20
|
-
import { createProviderRegistry } from "./create-provider-registry";
|
|
20
|
+
import { createProviderRegistry } from "./create-provider-registry.js";
|
|
21
21
|
// ── Factory map ─────────────────────────────────────────────────────────────
|
|
22
22
|
const registry = createProviderRegistry();
|
|
23
23
|
export function registerProvider(type, factory) {
|
|
@@ -2,7 +2,7 @@
|
|
|
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 path from "node:path";
|
|
5
|
-
import { parseRegistryRef } from "./resolve";
|
|
5
|
+
import { parseRegistryRef } from "./resolve.js";
|
|
6
6
|
/**
|
|
7
7
|
* Given an origin string (from an AssetRef) and the full list of stash
|
|
8
8
|
* sources, return the subset of sources to search.
|
|
@@ -1,13 +1,47 @@
|
|
|
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
|
-
import { fetchWithRetry } from "../../core/common";
|
|
5
|
-
import { rethrowIfTestIsolationError } from "../../core/errors";
|
|
6
|
-
import { closeDatabase, getRegistryIndexCache, openDatabase, upsertRegistryIndexCache } from "../../indexer/db";
|
|
7
|
-
import {
|
|
4
|
+
import { fetchWithRetry } from "../../core/common.js";
|
|
5
|
+
import { rethrowIfTestIsolationError } from "../../core/errors.js";
|
|
6
|
+
import { closeDatabase, getRegistryIndexCache, openDatabase, upsertRegistryIndexCache } from "../../indexer/db/db.js";
|
|
7
|
+
import { md5Hex } from "../../runtime.js";
|
|
8
|
+
import { registerProvider } from "../factory.js";
|
|
8
9
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
9
10
|
/** Per-query cache TTL in milliseconds (15 minutes). */
|
|
10
11
|
const QUERY_CACHE_TTL_MS = 15 * 60 * 1000;
|
|
12
|
+
// ── Cache DB lifecycle ────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* RAII-style lifecycle helper for the registry cache DB. Opens the DB (treating
|
|
15
|
+
* a failed open exactly like the legacy fall-through: the bun-test isolation
|
|
16
|
+
* guard is re-thrown, any other failure yields `db = undefined`), runs `fn`,
|
|
17
|
+
* and guarantees the DB is closed in a `finally` after `fn` has fully settled
|
|
18
|
+
* (the await is required: the callbacks are async, and closing before they
|
|
19
|
+
* settle would tear the DB down mid-write).
|
|
20
|
+
*/
|
|
21
|
+
async function withRegistryCacheDb(fn) {
|
|
22
|
+
let db;
|
|
23
|
+
try {
|
|
24
|
+
db = openDatabase();
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
// Never mask the bun-test isolation guard as "DB unavailable".
|
|
28
|
+
rethrowIfTestIsolationError(err);
|
|
29
|
+
db = undefined;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return await fn(db);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
if (db) {
|
|
36
|
+
try {
|
|
37
|
+
closeDatabase(db);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* ignore */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
11
45
|
// ── Provider class ──────────────────────────────────────────────────────────
|
|
12
46
|
class SkillsShProvider {
|
|
13
47
|
type = "skills-sh";
|
|
@@ -91,82 +125,73 @@ class SkillsShProvider {
|
|
|
91
125
|
async fetchSkills(query, limit) {
|
|
92
126
|
// Build a stable DB cache key for this query
|
|
93
127
|
const dbCacheKey = this.queryDbCacheKey(query, limit);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
try {
|
|
98
|
-
db = openDatabase();
|
|
99
|
-
dbCacheResult = getRegistryIndexCache(db, dbCacheKey, QUERY_CACHE_TTL_MS);
|
|
100
|
-
}
|
|
101
|
-
catch (err) {
|
|
102
|
-
// Never mask the bun-test isolation guard as "DB unavailable" — see
|
|
103
|
-
// rethrowIfTestIsolationError in src/core/errors.ts. Without this,
|
|
104
|
-
// a leaky test silently gets a cold cache + fresh fetch instead of
|
|
105
|
-
// the loud TEST_ISOLATION_MISSING failure the guard intends.
|
|
106
|
-
rethrowIfTestIsolationError(err);
|
|
107
|
-
// index.db not available yet (pre-migration install or test env) — fall through
|
|
108
|
-
}
|
|
109
|
-
if (dbCacheResult) {
|
|
128
|
+
return withRegistryCacheDb(async (db) => {
|
|
129
|
+
// ── Step 1: Try DB cache (index.db) ───────────────────────────────────
|
|
130
|
+
let dbCacheResult;
|
|
110
131
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const entries = parsed.filter(isValidSkillsEntry);
|
|
114
|
-
if (db)
|
|
115
|
-
closeDatabase(db);
|
|
116
|
-
return entries;
|
|
132
|
+
if (db) {
|
|
133
|
+
dbCacheResult = getRegistryIndexCache(db, dbCacheKey, QUERY_CACHE_TTL_MS);
|
|
117
134
|
}
|
|
118
135
|
}
|
|
119
|
-
catch {
|
|
120
|
-
|
|
136
|
+
catch (err) {
|
|
137
|
+
// Never mask the bun-test isolation guard as "DB unavailable" — see
|
|
138
|
+
// rethrowIfTestIsolationError in src/core/errors.ts. Without this,
|
|
139
|
+
// a leaky test silently gets a cold cache + fresh fetch instead of
|
|
140
|
+
// the loud TEST_ISOLATION_MISSING failure the guard intends.
|
|
141
|
+
rethrowIfTestIsolationError(err);
|
|
142
|
+
// index.db not available yet (pre-migration install or test env) — fall through
|
|
121
143
|
}
|
|
122
|
-
|
|
123
|
-
// ── Step 2: Fetch from API ─────────────────────────────────────────────
|
|
124
|
-
const baseUrl = this.config.url.replace(/\/+$/, "");
|
|
125
|
-
const url = `${baseUrl}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
126
|
-
try {
|
|
127
|
-
const response = await fetchWithRetry(url, undefined, { timeout: 10_000, retries: 1 });
|
|
128
|
-
if (!response.ok) {
|
|
129
|
-
throw new Error(`HTTP ${response.status}`);
|
|
130
|
-
}
|
|
131
|
-
const data = (await response.json());
|
|
132
|
-
const entries = parseSkillsResponse(data);
|
|
133
|
-
// Write to DB cache (primary)
|
|
134
|
-
if (db) {
|
|
144
|
+
if (dbCacheResult) {
|
|
135
145
|
try {
|
|
136
|
-
|
|
146
|
+
const parsed = JSON.parse(dbCacheResult.indexJson);
|
|
147
|
+
if (Array.isArray(parsed)) {
|
|
148
|
+
const entries = parsed.filter(isValidSkillsEntry);
|
|
149
|
+
return entries;
|
|
150
|
+
}
|
|
137
151
|
}
|
|
138
152
|
catch {
|
|
139
|
-
/*
|
|
153
|
+
/* corrupt DB entry — fall through */
|
|
140
154
|
}
|
|
141
|
-
closeDatabase(db);
|
|
142
155
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
// ── Step 2: Fetch from API ─────────────────────────────────────────────
|
|
157
|
+
const baseUrl = this.config.url.replace(/\/+$/, "");
|
|
158
|
+
const url = `${baseUrl}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
159
|
+
try {
|
|
160
|
+
const response = await fetchWithRetry(url, undefined, { timeout: 10_000, retries: 1 });
|
|
161
|
+
if (!response.ok) {
|
|
162
|
+
throw new Error(`HTTP ${response.status}`);
|
|
149
163
|
}
|
|
150
|
-
|
|
151
|
-
|
|
164
|
+
const data = (await response.json());
|
|
165
|
+
const entries = parseSkillsResponse(data);
|
|
166
|
+
// Write to DB cache (primary)
|
|
167
|
+
if (db) {
|
|
168
|
+
try {
|
|
169
|
+
upsertRegistryIndexCache(db, dbCacheKey, JSON.stringify(entries));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
/* best-effort */
|
|
173
|
+
}
|
|
152
174
|
}
|
|
175
|
+
return entries;
|
|
153
176
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
177
|
+
catch (err) {
|
|
178
|
+
// Fetch failed — use stale DB cache if available
|
|
179
|
+
if (dbCacheResult) {
|
|
180
|
+
try {
|
|
181
|
+
const parsed = JSON.parse(dbCacheResult.indexJson);
|
|
182
|
+
if (Array.isArray(parsed)) {
|
|
183
|
+
const entries = parsed.filter(isValidSkillsEntry);
|
|
184
|
+
if (entries.length > 0)
|
|
185
|
+
return entries;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
/* ignore */
|
|
162
190
|
}
|
|
163
191
|
}
|
|
164
|
-
|
|
165
|
-
/* ignore */
|
|
166
|
-
}
|
|
192
|
+
throw err;
|
|
167
193
|
}
|
|
168
|
-
|
|
169
|
-
}
|
|
194
|
+
});
|
|
170
195
|
}
|
|
171
196
|
mapToHits(entries) {
|
|
172
197
|
if (entries.length === 0)
|
|
@@ -221,13 +246,7 @@ class SkillsShProvider {
|
|
|
221
246
|
}
|
|
222
247
|
// ── DB cache key ────────────────────────────────────────────────────────
|
|
223
248
|
queryDbCacheKey(query, limit) {
|
|
224
|
-
const
|
|
225
|
-
hasher.update(this.config.url);
|
|
226
|
-
hasher.update("\0");
|
|
227
|
-
hasher.update(query.trim().toLowerCase());
|
|
228
|
-
hasher.update("\0");
|
|
229
|
-
hasher.update(String(limit));
|
|
230
|
-
const hash = hasher.digest("hex");
|
|
249
|
+
const hash = md5Hex(`${this.config.url}\0${query.trim().toLowerCase()}\0${String(limit)}`);
|
|
231
250
|
return `skills-sh:${hash}`;
|
|
232
251
|
}
|
|
233
252
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
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
|
-
import { fetchWithRetry, jsonWithByteCap, toErrorMessage } from "../../core/common";
|
|
5
|
-
import { rethrowIfTestIsolationError } from "../../core/errors";
|
|
6
|
-
import { closeDatabase, getRegistryIndexCache, openDatabase, upsertRegistryIndexCache } from "../../indexer/db";
|
|
7
|
-
import { asString } from "../../integrations/github";
|
|
8
|
-
import { registerProvider } from "../factory";
|
|
4
|
+
import { fetchWithRetry, jsonWithByteCap, toErrorMessage } from "../../core/common.js";
|
|
5
|
+
import { rethrowIfTestIsolationError } from "../../core/errors.js";
|
|
6
|
+
import { closeDatabase, getRegistryIndexCache, openDatabase, upsertRegistryIndexCache } from "../../indexer/db/db.js";
|
|
7
|
+
import { asString } from "../../integrations/github.js";
|
|
8
|
+
import { registerProvider } from "../factory.js";
|
|
9
9
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
10
10
|
/** Cache TTL in milliseconds (1 hour). */
|
|
11
11
|
const CACHE_TTL_MS = 60 * 60 * 1000;
|
|
@@ -115,58 +115,28 @@ function assetHitToPreview(hit) {
|
|
|
115
115
|
// ── Self-register ───────────────────────────────────────────────────────────
|
|
116
116
|
registerProvider("static-index", (config) => new StaticIndexProvider(config));
|
|
117
117
|
// ── Index loading with cache ────────────────────────────────────────────────
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
/**
|
|
119
|
+
* RAII-style lifecycle helper for the registry cache DB. Opens the DB (treating
|
|
120
|
+
* a failed open exactly like the legacy fall-through: the bun-test isolation
|
|
121
|
+
* guard is re-thrown, any other failure yields `db = undefined`), runs `fn`,
|
|
122
|
+
* and guarantees the DB is closed in a `finally` after `fn` has fully settled
|
|
123
|
+
* (the await is required: the callbacks are async, and closing before they
|
|
124
|
+
* settle would tear the DB down mid-write).
|
|
125
|
+
*/
|
|
126
|
+
async function withRegistryCacheDb(fn) {
|
|
120
127
|
let db;
|
|
121
|
-
let dbCacheResult;
|
|
122
128
|
try {
|
|
123
129
|
db = openDatabase();
|
|
124
|
-
dbCacheResult = getRegistryIndexCache(db, entry.url, CACHE_TTL_MS);
|
|
125
130
|
}
|
|
126
131
|
catch (err) {
|
|
127
|
-
// Never mask the bun-test isolation guard as "DB unavailable"
|
|
128
|
-
// rethrowIfTestIsolationError in src/core/errors.ts. Without this, a
|
|
129
|
-
// leaky test silently gets a cold cache instead of the loud
|
|
130
|
-
// TEST_ISOLATION_MISSING failure the guard intends.
|
|
132
|
+
// Never mask the bun-test isolation guard as "DB unavailable".
|
|
131
133
|
rethrowIfTestIsolationError(err);
|
|
132
|
-
|
|
134
|
+
db = undefined;
|
|
133
135
|
}
|
|
134
|
-
if (dbCacheResult) {
|
|
135
|
-
const index = parseRegistryIndex(JSON.parse(dbCacheResult.indexJson));
|
|
136
|
-
if (index) {
|
|
137
|
-
if (db)
|
|
138
|
-
closeDatabase(db);
|
|
139
|
-
return index;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// ── Step 2: Fetch fresh index from remote ────────────────────────────────
|
|
143
136
|
try {
|
|
144
|
-
|
|
145
|
-
if (!response.ok) {
|
|
146
|
-
throw new Error(`HTTP ${response.status}`);
|
|
147
|
-
}
|
|
148
|
-
// Cap at 50 MB — registry indexes can grow large but unbounded
|
|
149
|
-
// responses from a compromised server would OOM us.
|
|
150
|
-
const data = await jsonWithByteCap(response, 50 * 1024 * 1024);
|
|
151
|
-
const index = parseRegistryIndex(data);
|
|
152
|
-
if (index) {
|
|
153
|
-
// Write to DB cache (primary)
|
|
154
|
-
if (db) {
|
|
155
|
-
try {
|
|
156
|
-
const etag = response.headers.get("etag") ?? undefined;
|
|
157
|
-
const lastModified = response.headers.get("last-modified") ?? undefined;
|
|
158
|
-
upsertRegistryIndexCache(db, entry.url, JSON.stringify(index), { etag, lastModified });
|
|
159
|
-
}
|
|
160
|
-
catch {
|
|
161
|
-
/* best-effort */
|
|
162
|
-
}
|
|
163
|
-
closeDatabase(db);
|
|
164
|
-
}
|
|
165
|
-
return index;
|
|
166
|
-
}
|
|
167
|
-
throw new Error("Invalid registry index format");
|
|
137
|
+
return await fn(db);
|
|
168
138
|
}
|
|
169
|
-
|
|
139
|
+
finally {
|
|
170
140
|
if (db) {
|
|
171
141
|
try {
|
|
172
142
|
closeDatabase(db);
|
|
@@ -175,14 +145,67 @@ async function loadIndex(entry) {
|
|
|
175
145
|
/* ignore */
|
|
176
146
|
}
|
|
177
147
|
}
|
|
178
|
-
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function loadIndex(entry) {
|
|
151
|
+
return withRegistryCacheDb(async (db) => {
|
|
152
|
+
// ── Step 1: Try DB cache (index.db) ─────────────────────────────────────
|
|
153
|
+
let dbCacheResult;
|
|
154
|
+
try {
|
|
155
|
+
if (db) {
|
|
156
|
+
dbCacheResult = getRegistryIndexCache(db, entry.url, CACHE_TTL_MS);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
// Never mask the bun-test isolation guard as "DB unavailable" — see
|
|
161
|
+
// rethrowIfTestIsolationError in src/core/errors.ts. Without this, a
|
|
162
|
+
// leaky test silently gets a cold cache instead of the loud
|
|
163
|
+
// TEST_ISOLATION_MISSING failure the guard intends.
|
|
164
|
+
rethrowIfTestIsolationError(err);
|
|
165
|
+
// index.db read failed (pre-migration install or test env) — fall through
|
|
166
|
+
}
|
|
179
167
|
if (dbCacheResult) {
|
|
180
168
|
const index = parseRegistryIndex(JSON.parse(dbCacheResult.indexJson));
|
|
181
|
-
if (index)
|
|
169
|
+
if (index) {
|
|
182
170
|
return index;
|
|
171
|
+
}
|
|
183
172
|
}
|
|
184
|
-
|
|
185
|
-
|
|
173
|
+
// ── Step 2: Fetch fresh index from remote ────────────────────────────────
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetchWithRetry(entry.url, undefined, { timeout: 10_000 });
|
|
176
|
+
if (!response.ok) {
|
|
177
|
+
throw new Error(`HTTP ${response.status}`);
|
|
178
|
+
}
|
|
179
|
+
// Cap at 50 MB — registry indexes can grow large but unbounded
|
|
180
|
+
// responses from a compromised server would OOM us.
|
|
181
|
+
const data = await jsonWithByteCap(response, 50 * 1024 * 1024);
|
|
182
|
+
const index = parseRegistryIndex(data);
|
|
183
|
+
if (index) {
|
|
184
|
+
// Write to DB cache (primary)
|
|
185
|
+
if (db) {
|
|
186
|
+
try {
|
|
187
|
+
const etag = response.headers.get("etag") ?? undefined;
|
|
188
|
+
const lastModified = response.headers.get("last-modified") ?? undefined;
|
|
189
|
+
upsertRegistryIndexCache(db, entry.url, JSON.stringify(index), { etag, lastModified });
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
/* best-effort */
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return index;
|
|
196
|
+
}
|
|
197
|
+
throw new Error("Invalid registry index format");
|
|
198
|
+
}
|
|
199
|
+
catch (err) {
|
|
200
|
+
// Fetch failed — use stale DB cache if available
|
|
201
|
+
if (dbCacheResult) {
|
|
202
|
+
const index = parseRegistryIndex(JSON.parse(dbCacheResult.indexJson));
|
|
203
|
+
if (index)
|
|
204
|
+
return index;
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
186
209
|
}
|
|
187
210
|
export function isCacheExpired(mtimeMs) {
|
|
188
211
|
return Date.now() - mtimeMs > CACHE_TTL_MS;
|
package/dist/registry/resolve.js
CHANGED
|
@@ -6,9 +6,9 @@ import fs from "node:fs";
|
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
import { fetchWithRetry, jsonWithByteCap } from "../core/common";
|
|
10
|
-
import { NotFoundError, UsageError } from "../core/errors";
|
|
11
|
-
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github";
|
|
9
|
+
import { fetchWithRetry, jsonWithByteCap } from "../core/common.js";
|
|
10
|
+
import { NotFoundError, UsageError } from "../core/errors.js";
|
|
11
|
+
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "../integrations/github.js";
|
|
12
12
|
/**
|
|
13
13
|
* Validate that a URL is safe to pass to git.
|
|
14
14
|
* Allowlists https:, http:, ssh:, git: schemes and git@ SSH shorthand.
|