akm-cli 0.7.5 → 0.8.0-rc.6
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} +113 -2
- package/README.md +20 -4
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli.js +1995 -551
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +1531 -0
- package/dist/commands/curate.js +44 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +990 -75
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +5 -23
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +400 -0
- package/dist/commands/help/help-accept.md +9 -0
- package/dist/commands/help/help-improve.md +77 -0
- package/dist/commands/help/help-proposals.md +15 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +8 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-profiles.js +146 -0
- package/dist/commands/improve-result-file.js +103 -0
- package/dist/commands/improve.js +2175 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/index.js +183 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/vault-key-rules.js +139 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +5 -2
- package/dist/commands/proposal.js +66 -12
- package/dist/commands/propose.js +86 -31
- package/dist/commands/reflect.js +1119 -73
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember.js +69 -6
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +144 -25
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +438 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/commands/vault.js +130 -77
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +7 -0
- package/dist/core/asset-registry.js +7 -16
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +22 -0
- package/dist/core/common.js +157 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +625 -0
- package/dist/core/config-schema.js +501 -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 +327 -987
- package/dist/core/errors.js +40 -19
- package/dist/core/events.js +91 -138
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +3 -6
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +326 -14
- package/dist/core/proposal-quality-validators.js +364 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +498 -42
- package/dist/core/state-db.js +927 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/warn.js +62 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +152 -253
- package/dist/indexer/db.js +933 -103
- package/dist/indexer/ensure-index.js +64 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -124
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +506 -291
- package/dist/indexer/llm-cache.js +47 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +148 -160
- package/dist/indexer/memory-inference.js +99 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +255 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +5 -16
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +150 -74
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +118 -23
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +68 -0
- package/dist/integrations/session-logs/providers/claude-code.js +59 -0
- package/dist/integrations/session-logs/providers/opencode.js +55 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +72 -124
- package/dist/llm/embedder.js +3 -19
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +3 -0
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +89 -48
- package/dist/llm/graph-extract.js +676 -70
- package/dist/llm/index-passes.js +9 -23
- package/dist/llm/memory-infer.js +52 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +281 -0
- package/dist/output/cli-hints-short.md +65 -0
- package/dist/output/cli-hints.js +5 -318
- package/dist/output/context.js +3 -0
- package/dist/output/renderers.js +223 -256
- package/dist/output/shapes.js +150 -105
- package/dist/output/text.js +318 -30
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +3 -0
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +70 -49
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17307 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8900 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +7 -5
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +211 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +140 -10
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +62 -91
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +3 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +9 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +2 -2
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +20 -8
- package/.github/LICENSE +0 -374
- package/dist/commands/install-audit.js +0 -381
- package/dist/templates/wiki-templates.js +0 -100
package/dist/setup/steps.js
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* The interactive wizard in `setup.ts` historically ran a fixed series of
|
|
5
|
-
* step functions (`stepStashDir`, `stepOllama`, `stepLlm`, ...) inline.
|
|
6
|
-
* This module formalizes that pattern so steps can be:
|
|
7
|
-
* - reused by `akm init` (non-interactive preset, see Finding 31),
|
|
8
|
-
* - tested in isolation by passing a stub `SetupContext`, and
|
|
9
|
-
* - extended by plugins without touching the wizard call site.
|
|
10
|
-
*
|
|
11
|
-
* Steps mutate state through `SetupContext.apply()`, which accumulates a
|
|
12
|
-
* delta on top of the original config. `stepLlm` reading the embedding
|
|
13
|
-
* endpoint that `stepSemanticSearch` produced is the canonical example of
|
|
14
|
-
* why mutable accumulation is preferred over immutable returns.
|
|
15
|
-
*/
|
|
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/.
|
|
16
4
|
/**
|
|
17
5
|
* Build a fresh `SetupContext` over a starting config. The returned context
|
|
18
6
|
* applies deltas in-place onto an internal accumulator and exposes the
|
package/dist/sources/include.js
CHANGED
|
@@ -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 path from "node:path";
|
|
3
6
|
import { isWithin } from "../core/common";
|
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* to factory functions that build {@link SourceProvider} instances from a
|
|
6
|
-
* {@link SourceConfigEntry}.
|
|
7
|
-
*
|
|
8
|
-
* Distinct from the registry-discovery factory (`registry/factory.ts`).
|
|
9
|
-
* Both share `create-provider-registry.ts` for the underlying string→factory
|
|
10
|
-
* map.
|
|
11
|
-
*/
|
|
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 { getSources } from "../core/config";
|
|
12
5
|
import { createProviderRegistry } from "../registry/create-provider-registry";
|
|
13
6
|
// ── Factory map ─────────────────────────────────────────────────────────────
|
|
14
7
|
const registry = createProviderRegistry();
|
|
@@ -24,7 +17,7 @@ export function resolveSourceProviderFactory(type) {
|
|
|
24
17
|
*/
|
|
25
18
|
export function resolveSourceProviders(config) {
|
|
26
19
|
const providers = [];
|
|
27
|
-
for (const entry of config
|
|
20
|
+
for (const entry of getSources(config)) {
|
|
28
21
|
if (entry.enabled === false)
|
|
29
22
|
continue;
|
|
30
23
|
const factory = registry.resolve(entry.type);
|
package/dist/sources/provider.js
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* A SourceProvider gets files into a directory. The indexer walks `path()`
|
|
5
|
-
* and reads files from disk. Search and show go through the indexer, not
|
|
6
|
-
* through provider methods.
|
|
7
|
-
*
|
|
8
|
-
* Three required members + one optional:
|
|
9
|
-
* - name configured source name
|
|
10
|
-
* - kind "filesystem" | "git" | "website" | "npm"
|
|
11
|
-
* - init(ctx) called once after construction
|
|
12
|
-
* - path() the directory the indexer walks (stable for instance lifetime)
|
|
13
|
-
* - sync?() refresh the directory from upstream (no-op for filesystem)
|
|
14
|
-
*
|
|
15
|
-
* All other writing/reading concerns live outside this interface:
|
|
16
|
-
* - Writes: src/core/write-source.ts (Phase 5)
|
|
17
|
-
* - Reads: src/indexer.ts (Phase 4)
|
|
18
|
-
* - Install: src/sources/providers/sync-from-ref.ts (install-time helpers,
|
|
19
|
-
* separate from configured-source plumbing)
|
|
20
|
-
*/
|
|
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/.
|
|
21
4
|
export {};
|
|
@@ -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 { resolveStashDir } from "../../core/common";
|
|
2
5
|
import { ConfigError } from "../../core/errors";
|
|
3
6
|
import { registerSourceProvider } from "../provider-factory";
|
|
@@ -8,28 +11,21 @@ import { registerSourceProvider } from "../provider-factory";
|
|
|
8
11
|
* just `{ name, kind, init, path }`. No `sync()` — content is the user's
|
|
9
12
|
* own directory, never refreshed by akm.
|
|
10
13
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#stashDir;
|
|
15
|
-
constructor(entry) {
|
|
16
|
-
if (entry.type !== "filesystem") {
|
|
17
|
-
throw new ConfigError(`FilesystemSourceProvider invoked with type="${entry.type}"`);
|
|
18
|
-
}
|
|
19
|
-
this.#stashDir = entry.path ?? resolveStashDir();
|
|
20
|
-
if (!this.#stashDir) {
|
|
21
|
-
throw new ConfigError("filesystem source requires a `path`");
|
|
22
|
-
}
|
|
23
|
-
this.name = entry.name ?? this.#stashDir;
|
|
14
|
+
registerSourceProvider("filesystem", (entry) => {
|
|
15
|
+
if (entry.type !== "filesystem") {
|
|
16
|
+
throw new ConfigError(`filesystem source invoked with type="${entry.type}"`);
|
|
24
17
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
const stashDir = entry.path ?? resolveStashDir();
|
|
19
|
+
if (!stashDir) {
|
|
20
|
+
throw new ConfigError("filesystem source requires a `path`");
|
|
28
21
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
const name = entry.name ?? stashDir;
|
|
23
|
+
return {
|
|
24
|
+
kind: "filesystem",
|
|
25
|
+
name,
|
|
26
|
+
async init(_ctx) { },
|
|
27
|
+
path() {
|
|
28
|
+
return stashDir;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
});
|
|
@@ -1,10 +1,13 @@
|
|
|
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 { spawnSync } from "node:child_process";
|
|
2
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
3
6
|
import fs from "node:fs";
|
|
4
7
|
import path from "node:path";
|
|
5
8
|
import { TYPE_DIRS } from "../../core/asset-spec";
|
|
6
9
|
import { resolveStashDir } from "../../core/common";
|
|
7
|
-
import { loadConfig } from "../../core/config";
|
|
10
|
+
import { getSources, loadConfig } from "../../core/config";
|
|
8
11
|
import { ConfigError, UsageError } from "../../core/errors";
|
|
9
12
|
import { getRegistryCacheDir, getRegistryIndexCacheDir } from "../../core/paths";
|
|
10
13
|
import { sanitizeCommitMessage } from "../../core/write-source";
|
|
@@ -15,7 +18,6 @@ import { applyAkmIncludeConfig, buildInstallCacheDir, copyDirectoryContents, det
|
|
|
15
18
|
const CACHE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
16
19
|
/** Maximum stale age allowed when refresh fails (7 days). */
|
|
17
20
|
const CACHE_STALE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
18
|
-
const GIT_STASH_TYPES = new Set(["git"]);
|
|
19
21
|
/**
|
|
20
22
|
* Git source provider — clones (and re-pulls) a remote repo into a local
|
|
21
23
|
* cache directory. Implements the v1 {@link SourceProvider} interface (spec
|
|
@@ -407,10 +409,10 @@ export function saveGitStash(name, message, writableOverride) {
|
|
|
407
409
|
let writable = false;
|
|
408
410
|
if (name) {
|
|
409
411
|
const config = loadConfig();
|
|
410
|
-
const stash = findGitStashByTarget(config
|
|
412
|
+
const stash = findGitStashByTarget(getSources(config), name);
|
|
411
413
|
if (!stash)
|
|
412
414
|
throw new UsageError(`No git stash found with name "${name}"`);
|
|
413
|
-
if (
|
|
415
|
+
if (stash.type !== "git") {
|
|
414
416
|
throw new UsageError(`Stash "${name}" is not a git stash (type: ${stash.type})`);
|
|
415
417
|
}
|
|
416
418
|
if (!stash.url)
|
|
@@ -472,7 +474,7 @@ function findGitStashByTarget(stashes, target) {
|
|
|
472
474
|
return stashes.find((stash) => matchesGitStashTarget(stash, target));
|
|
473
475
|
}
|
|
474
476
|
function matchesGitStashTarget(stash, target) {
|
|
475
|
-
if (
|
|
477
|
+
if (stash.type !== "git")
|
|
476
478
|
return false;
|
|
477
479
|
if (stash.name === target || stash.url === target)
|
|
478
480
|
return true;
|
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Distinct from the v1 {@link SourceProvider} interface (which only deals
|
|
5
|
-
* with "configured sources" — entries already resolved into a directory).
|
|
6
|
-
* These types describe the resolution+lockfile step that runs when
|
|
7
|
-
* `akm add <install-ref>` materialises an upstream artifact into a local
|
|
8
|
-
* cache directory.
|
|
9
|
-
*
|
|
10
|
-
* They live here, outside `provider.ts`, so the v1 SourceProvider
|
|
11
|
-
* interface stays minimal (`{ name, kind, init, path, sync? }`) per the
|
|
12
|
-
* architecture spec §2.1.
|
|
13
|
-
*/
|
|
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/.
|
|
14
4
|
export {};
|
|
@@ -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
|
* Npm-source stash provider.
|
|
3
6
|
*
|
|
@@ -5,10 +8,6 @@
|
|
|
5
8
|
* integrity, extracts it securely (via `extractTarGzSecure`), detects the
|
|
6
9
|
* stash root inside the package, and applies any nested `.akm-include`
|
|
7
10
|
* configuration. Cache hits short-circuit the fetch.
|
|
8
|
-
*
|
|
9
|
-
* Audit is intentionally NOT performed here — `akmAdd` calls
|
|
10
|
-
* `auditInstallCandidate` after `sync()` so the policy decision lives at
|
|
11
|
-
* the orchestrator layer where the `--trust` flag is known.
|
|
12
11
|
*/
|
|
13
12
|
import fs from "node:fs";
|
|
14
13
|
import path from "node:path";
|
|
@@ -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 { createHash } from "node:crypto";
|
|
2
5
|
import fs from "node:fs";
|
|
3
6
|
import path from "node:path";
|
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Replaces the historical `installRegistryRef()` entry point. Given an
|
|
5
|
-
* unparsed install ref, this resolves the right syncable provider and
|
|
6
|
-
* invokes its `sync()` method.
|
|
7
|
-
*
|
|
8
|
-
* Audit is intentionally NOT performed here; callers (`akmAdd`,
|
|
9
|
-
* `akmUpdate`) decide whether to run `auditInstallCandidate` on the
|
|
10
|
-
* synced `contentDir` because they own the `--trust` flag.
|
|
11
|
-
*/
|
|
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/.
|
|
12
4
|
import { UsageError } from "../../core/errors";
|
|
13
5
|
import { parseRegistryRef } from "../../registry/resolve";
|
|
14
6
|
import { detectStashRoot } from "./provider-utils";
|
|
@@ -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
|
* Tar archive extraction and integrity verification utilities.
|
|
3
6
|
*
|
|
@@ -1,27 +1,23 @@
|
|
|
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 { registerSourceProvider } from "../provider-factory";
|
|
2
5
|
import { ensureWebsiteMirror, getWebsiteCachePaths, validateWebsiteUrl } from "../website-ingest";
|
|
3
6
|
/**
|
|
4
7
|
* Website source provider — thin adapter over the shared website ingest module.
|
|
5
8
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
name;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
async sync() {
|
|
23
|
-
await ensureWebsiteMirror(this.#config, { requireStashDir: true });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
registerSourceProvider("website", (config) => new WebsiteSourceProvider(config));
|
|
27
|
-
export { WebsiteSourceProvider };
|
|
9
|
+
registerSourceProvider("website", (config) => {
|
|
10
|
+
const url = validateWebsiteUrl(config.url ?? "");
|
|
11
|
+
const name = config.name ?? "website";
|
|
12
|
+
return {
|
|
13
|
+
kind: "website",
|
|
14
|
+
name,
|
|
15
|
+
async init(_ctx) { },
|
|
16
|
+
path() {
|
|
17
|
+
return getWebsiteCachePaths(url).stashDir;
|
|
18
|
+
},
|
|
19
|
+
async sync() {
|
|
20
|
+
await ensureWebsiteMirror(config, { requireStashDir: true });
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
});
|
package/dist/sources/resolve.js
CHANGED
|
@@ -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 path from "node:path";
|
|
3
6
|
import { deriveCanonicalAssetNameFromStashRoot, isRelevantAssetFile, resolveAssetPathFromName, TYPE_DIRS, } from "../core/asset-spec";
|
package/dist/sources/types.js
CHANGED
|
@@ -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 { createHash } from "node:crypto";
|
|
2
5
|
import fs from "node:fs";
|
|
3
6
|
import path from "node:path";
|
|
@@ -167,6 +170,10 @@ async function crawlWebsite(startUrl, options) {
|
|
|
167
170
|
return pages;
|
|
168
171
|
}
|
|
169
172
|
async function fetchWebsitePage(pageUrl) {
|
|
173
|
+
const parsedUrl = new URL(pageUrl);
|
|
174
|
+
if (parsedUrl.hostname.endsWith(".invalid")) {
|
|
175
|
+
throw new Error(`Refusing to fetch reserved invalid hostname: ${parsedUrl.hostname}`);
|
|
176
|
+
}
|
|
170
177
|
const response = await fetchWithRetry(pageUrl, {
|
|
171
178
|
headers: {
|
|
172
179
|
Accept: "text/html, text/markdown, text/plain;q=0.9, application/xhtml+xml;q=0.8",
|
|
@@ -0,0 +1,203 @@
|
|
|
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
|
+
// crontab backend for `akm tasks` (Linux default).
|
|
5
|
+
//
|
|
6
|
+
// Each akm-owned entry is wrapped in markers so a hand-edited crontab keeps
|
|
7
|
+
// its other lines untouched:
|
|
8
|
+
//
|
|
9
|
+
// # akm:task <id> BEGIN
|
|
10
|
+
// [SCHED] /abs/akm tasks run <id> >> /home/.../tasks/logs/<id>.log 2>&1
|
|
11
|
+
// # akm:task <id> END
|
|
12
|
+
//
|
|
13
|
+
// The backend reads/writes the user's crontab via `crontab -l` and
|
|
14
|
+
// `crontab -`. Disabling a task comments the entry with `# akm:disabled `
|
|
15
|
+
// rather than removing it, so re-enabling preserves the original schedule.
|
|
16
|
+
//
|
|
17
|
+
// Platform notes:
|
|
18
|
+
// • Operates on the *per-user* crontab — system-wide /etc/cron.d entries
|
|
19
|
+
// are out of scope.
|
|
20
|
+
// • Cron runs jobs with a stripped environment (`SHELL`, `PATH`, `HOME`,
|
|
21
|
+
// `LOGNAME`/`USER` only). The cron line uses an absolute akm path
|
|
22
|
+
// resolved at install time so it doesn't rely on the inherited PATH.
|
|
23
|
+
// • BSD `crontab -l` returns exit 1 with "no crontab for <user>" on a
|
|
24
|
+
// fresh user; we treat that as an empty crontab rather than an error.
|
|
25
|
+
//
|
|
26
|
+
// Tests inject a fake exec so unit tests don't touch the real crontab.
|
|
27
|
+
import { spawnSync } from "node:child_process";
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
import path from "node:path";
|
|
30
|
+
import { ConfigError } from "../../core/errors";
|
|
31
|
+
import { getTaskLogDir } from "../../core/paths";
|
|
32
|
+
import { resolveAkmInvocation } from "../resolveAkmBin";
|
|
33
|
+
import { parseSchedule, translateToCron } from "../schedule";
|
|
34
|
+
const BEGIN = (id) => `# akm:task ${id} BEGIN`;
|
|
35
|
+
const END = (id) => `# akm:task ${id} END`;
|
|
36
|
+
const DISABLED_PREFIX = "# akm:disabled ";
|
|
37
|
+
const BLOCK_RE = /^# akm:task ([\w.@:_-]+) BEGIN$/;
|
|
38
|
+
export function CRON_BACKEND(options = {}) {
|
|
39
|
+
const exec = options.exec ?? defaultCronExec();
|
|
40
|
+
const logDir = options.logDir ?? getTaskLogDir();
|
|
41
|
+
const akmArgv = options.akmArgv ?? resolveAkmInvocation().argv;
|
|
42
|
+
return {
|
|
43
|
+
name: "cron",
|
|
44
|
+
install(task) {
|
|
45
|
+
// Create the log directory before writing the crontab line — cron
|
|
46
|
+
// appends with `>>` and the surrounding shell will fail the entire
|
|
47
|
+
// entry if the parent directory doesn't exist.
|
|
48
|
+
ensureDir(logDir);
|
|
49
|
+
const cronLine = buildCronLine(task, akmArgv, logDir);
|
|
50
|
+
const existing = readCrontab(exec);
|
|
51
|
+
const block = renderBlock(task.id, cronLine, task.enabled);
|
|
52
|
+
const next = upsertBlock(existing, task.id, block);
|
|
53
|
+
writeCrontab(exec, next);
|
|
54
|
+
},
|
|
55
|
+
uninstall(id) {
|
|
56
|
+
const existing = readCrontab(exec);
|
|
57
|
+
const next = removeBlock(existing, id);
|
|
58
|
+
writeCrontab(exec, next);
|
|
59
|
+
},
|
|
60
|
+
setEnabled(id, enabled) {
|
|
61
|
+
const existing = readCrontab(exec);
|
|
62
|
+
const next = toggleBlock(existing, id, enabled);
|
|
63
|
+
writeCrontab(exec, next);
|
|
64
|
+
},
|
|
65
|
+
list() {
|
|
66
|
+
const existing = readCrontab(exec);
|
|
67
|
+
const ids = [];
|
|
68
|
+
for (const line of existing.split(/\r?\n/)) {
|
|
69
|
+
const m = line.match(BLOCK_RE);
|
|
70
|
+
if (m)
|
|
71
|
+
ids.push(m[1]);
|
|
72
|
+
}
|
|
73
|
+
return ids.map((id) => ({ id }));
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ── helpers (exported for tests) ────────────────────────────────────────────
|
|
78
|
+
export function buildCronLine(task, akmArgv, logDir) {
|
|
79
|
+
const spec = parseSchedule(task.schedule, "cron");
|
|
80
|
+
const cronExpr = translateToCron(spec);
|
|
81
|
+
const logPath = path.join(logDir, `${task.id}.log`);
|
|
82
|
+
const cmd = [...akmArgv, "tasks", "run", task.id].map((part) => quoteForCron(part)).join(" ");
|
|
83
|
+
return `${cronExpr} ${cmd} >> ${quoteForCron(logPath)} 2>&1`;
|
|
84
|
+
}
|
|
85
|
+
export function renderBlock(id, cronLine, enabled) {
|
|
86
|
+
const body = enabled ? cronLine : `${DISABLED_PREFIX}${cronLine}`;
|
|
87
|
+
return [BEGIN(id), body, END(id)].join("\n");
|
|
88
|
+
}
|
|
89
|
+
export function upsertBlock(existing, id, block) {
|
|
90
|
+
const trimmed = existing.replace(/\s+$/g, "");
|
|
91
|
+
const removed = removeBlock(trimmed, id);
|
|
92
|
+
const sep = removed.length === 0 ? "" : "\n";
|
|
93
|
+
return `${removed}${sep}${block}\n`;
|
|
94
|
+
}
|
|
95
|
+
export function removeBlock(existing, id) {
|
|
96
|
+
const lines = existing.split(/\r?\n/);
|
|
97
|
+
const out = [];
|
|
98
|
+
let inBlock = false;
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
if (!inBlock && line === BEGIN(id)) {
|
|
101
|
+
inBlock = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (inBlock && line === END(id)) {
|
|
105
|
+
inBlock = false;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (inBlock)
|
|
109
|
+
continue;
|
|
110
|
+
out.push(line);
|
|
111
|
+
}
|
|
112
|
+
// Collapse trailing blank lines.
|
|
113
|
+
while (out.length > 0 && out[out.length - 1] === "")
|
|
114
|
+
out.pop();
|
|
115
|
+
return out.join("\n");
|
|
116
|
+
}
|
|
117
|
+
export function toggleBlock(existing, id, enabled) {
|
|
118
|
+
const lines = existing.split(/\r?\n/);
|
|
119
|
+
const out = [];
|
|
120
|
+
let inBlock = false;
|
|
121
|
+
for (const line of lines) {
|
|
122
|
+
if (!inBlock && line === BEGIN(id)) {
|
|
123
|
+
inBlock = true;
|
|
124
|
+
out.push(line);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (inBlock && line === END(id)) {
|
|
128
|
+
inBlock = false;
|
|
129
|
+
out.push(line);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (inBlock) {
|
|
133
|
+
const isComment = line.startsWith(DISABLED_PREFIX);
|
|
134
|
+
if (enabled && isComment) {
|
|
135
|
+
out.push(line.slice(DISABLED_PREFIX.length));
|
|
136
|
+
}
|
|
137
|
+
else if (!enabled && !isComment) {
|
|
138
|
+
out.push(`${DISABLED_PREFIX}${line}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
out.push(line);
|
|
142
|
+
}
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
out.push(line);
|
|
146
|
+
}
|
|
147
|
+
return out.join("\n");
|
|
148
|
+
}
|
|
149
|
+
function quoteForCron(part) {
|
|
150
|
+
// crontab passes the rest of the line to /bin/sh -c, so quote anything that
|
|
151
|
+
// isn't a plain shell-safe token. Single-quote and escape embedded single
|
|
152
|
+
// quotes via the standard shell idiom: `'foo'\''bar'`.
|
|
153
|
+
if (/^[A-Za-z0-9_\-./@:%=+,]+$/.test(part))
|
|
154
|
+
return part;
|
|
155
|
+
return `'${part.replace(/'/g, `'\\''`)}'`;
|
|
156
|
+
}
|
|
157
|
+
function readCrontab(exec) {
|
|
158
|
+
const result = exec.read();
|
|
159
|
+
if (result.status === 0)
|
|
160
|
+
return result.stdout ?? "";
|
|
161
|
+
// BSD crontab returns 1 with "no crontab for <user>" on stderr — treat as empty.
|
|
162
|
+
if (/no crontab for/i.test(result.stderr ?? ""))
|
|
163
|
+
return "";
|
|
164
|
+
if (/no crontab/i.test(result.stdout ?? ""))
|
|
165
|
+
return "";
|
|
166
|
+
throw new ConfigError(`crontab -l failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "Ensure the `crontab` binary is on PATH and your shell can read the user crontab.");
|
|
167
|
+
}
|
|
168
|
+
function writeCrontab(exec, content) {
|
|
169
|
+
const normalised = content.endsWith("\n") || content.length === 0 ? content : `${content}\n`;
|
|
170
|
+
const result = exec.write(normalised);
|
|
171
|
+
if (result.status !== 0) {
|
|
172
|
+
throw new ConfigError(`crontab - failed (exit ${result.status}): ${result.stderr || result.stdout || "no output"}.`, "INVALID_CONFIG_FILE", "Ensure the `crontab` binary is on PATH and your shell can write the user crontab.");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function ensureDir(dir) {
|
|
176
|
+
try {
|
|
177
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Best-effort: the install will surface a clearer error if the cron
|
|
181
|
+
// line later fails at runtime due to a missing redirection target.
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function defaultCronExec() {
|
|
185
|
+
return {
|
|
186
|
+
read() {
|
|
187
|
+
const r = spawnSync("crontab", ["-l"], { encoding: "utf8" });
|
|
188
|
+
return {
|
|
189
|
+
status: r.status ?? 1,
|
|
190
|
+
stdout: r.stdout ?? "",
|
|
191
|
+
stderr: r.stderr ?? "",
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
write(content) {
|
|
195
|
+
const r = spawnSync("crontab", ["-"], { encoding: "utf8", input: content });
|
|
196
|
+
return {
|
|
197
|
+
status: r.status ?? 1,
|
|
198
|
+
stdout: r.stdout ?? "",
|
|
199
|
+
stderr: r.stderr ?? "",
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { spawnSync } from "node:child_process";
|
|
5
|
+
/**
|
|
6
|
+
* Run a command synchronously, normalizing null results to safe defaults.
|
|
7
|
+
* args[0] is the binary; args[1..] are its arguments.
|
|
8
|
+
*/
|
|
9
|
+
export function spawnCommand(args) {
|
|
10
|
+
const [bin, ...rest] = args;
|
|
11
|
+
const r = spawnSync(bin, rest, { encoding: "utf8" });
|
|
12
|
+
return {
|
|
13
|
+
status: r.status ?? 1,
|
|
14
|
+
stdout: r.stdout ?? "",
|
|
15
|
+
stderr: r.stderr ?? "",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Escape a string for safe embedding in an XML attribute or text node.
|
|
20
|
+
*/
|
|
21
|
+
export function escapeXml(s) {
|
|
22
|
+
return s
|
|
23
|
+
.replace(/&/g, "&")
|
|
24
|
+
.replace(/</g, "<")
|
|
25
|
+
.replace(/>/g, ">")
|
|
26
|
+
.replace(/"/g, """)
|
|
27
|
+
.replace(/'/g, "'");
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { CRON_BACKEND } from "./cron";
|
|
5
|
+
import { LAUNCHD_BACKEND } from "./launchd";
|
|
6
|
+
import { SCHTASKS_BACKEND } from "./schtasks";
|
|
7
|
+
export function selectBackend(options = {}) {
|
|
8
|
+
const platform = options.platform ?? process.platform;
|
|
9
|
+
switch (platform) {
|
|
10
|
+
case "win32":
|
|
11
|
+
return SCHTASKS_BACKEND(options.schtasks);
|
|
12
|
+
case "darwin":
|
|
13
|
+
return LAUNCHD_BACKEND(options.launchd);
|
|
14
|
+
default:
|
|
15
|
+
return CRON_BACKEND(options.cron);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function backendNameForPlatform(platform = process.platform) {
|
|
19
|
+
if (platform === "win32")
|
|
20
|
+
return "schtasks";
|
|
21
|
+
if (platform === "darwin")
|
|
22
|
+
return "launchd";
|
|
23
|
+
return "cron";
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>Label</key>
|
|
6
|
+
<string>{{LABEL}}</string>
|
|
7
|
+
<key>ProgramArguments</key>
|
|
8
|
+
<array>
|
|
9
|
+
{{PROGRAM_ARGS}}
|
|
10
|
+
</array>
|
|
11
|
+
<key>StandardOutPath</key>
|
|
12
|
+
<string>{{LOG_PATH}}</string>
|
|
13
|
+
<key>StandardErrorPath</key>
|
|
14
|
+
<string>{{LOG_PATH}}</string>
|
|
15
|
+
<key>RunAtLoad</key>
|
|
16
|
+
<false/>
|
|
17
|
+
{{ENV_VARS}}{{TRIGGER_XML}}
|
|
18
|
+
</dict>
|
|
19
|
+
</plist>
|