akm-cli 0.9.16-alpha.1 → 0.9.16
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 +56 -132
- package/dist/assets/hints/cli-hints-full.md +13 -6
- package/dist/assets/tasks/core/index-refresh.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +3 -6
- package/dist/cli/retired-commands.js +0 -4
- package/dist/cli/unknown-flags.js +3 -36
- package/dist/commands/env/env-binding.js +4 -4
- package/dist/commands/env/env-cli.js +3 -3
- package/dist/commands/improve/collapse-detector.js +2 -2
- package/dist/commands/improve/consolidate.js +4 -6
- package/dist/commands/improve/improve-cli.js +20 -15
- package/dist/commands/improve/reflect.js +23 -2
- package/dist/commands/lint/base-linter.js +9 -0
- package/dist/commands/lint/env-key-rules.js +2 -2
- package/dist/commands/proposal/propose.js +15 -1
- package/dist/commands/proposal/repository.js +3 -12
- package/dist/commands/proposal/validators/proposal-quality-validators.js +40 -3
- package/dist/commands/proposal/validators/proposal-validators.js +5 -4
- package/dist/commands/read/curate.js +44 -34
- package/dist/commands/read/search.js +35 -54
- package/dist/commands/read/show.js +21 -2
- package/dist/commands/registry-cli.js +5 -5
- package/dist/commands/sources/add-cli.js +59 -16
- package/dist/commands/sources/bundle-cli.js +35 -11
- package/dist/commands/sources/bundle-config-ops.js +30 -0
- package/dist/commands/sources/dangerous-env-audit.js +4 -4
- package/dist/commands/sources/info.js +8 -8
- package/dist/commands/sources/installed-stashes.js +55 -61
- package/dist/commands/sources/source-add.js +39 -38
- package/dist/commands/sources/source-manage.js +34 -12
- package/dist/commands/sources/stash-cli.js +111 -119
- package/dist/commands/sources/stash-skeleton.js +6 -3
- package/dist/commands/tasks/explain.js +4 -1
- package/dist/commands/tasks/tasks-cli.js +31 -9
- package/dist/commands/tasks/tasks.js +239 -194
- package/dist/commands/tasks/validate.js +20 -32
- package/dist/core/activation-policy.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +8 -35
- package/dist/core/adapter/adapters/akm-metadata.js +1 -11
- package/dist/core/adapter/execution-source.js +10 -29
- package/dist/core/asset/asset-placement.js +0 -35
- package/dist/core/config/config-schema.js +64 -8
- package/dist/core/config/config-sources.js +96 -2
- package/dist/core/config/config.js +190 -24
- package/dist/core/config/legacy-source-shape-shim.js +9 -0
- package/dist/core/config/schema/embedding.js +30 -7
- package/dist/core/config/schema/execution.js +23 -0
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/scheduler.js +20 -0
- package/dist/core/config/schema/search.js +10 -12
- package/dist/core/config/schema/sources-bundles.js +32 -1
- package/dist/core/content-safety.js +52 -0
- package/dist/core/errors.js +2 -5
- package/dist/core/maintenance-barrier.js +11 -13
- package/dist/core/paths.js +11 -0
- package/dist/core/run-lock.js +2 -5
- package/dist/core/state/migrations.js +1 -26
- package/dist/core/state-db.js +27 -63
- package/dist/core/type-presentation.js +1 -1
- package/dist/core/write-source.js +13 -8
- package/dist/indexer/bundle-identity-guard.js +45 -8
- package/dist/indexer/ensure-index.js +0 -5
- package/dist/indexer/index-db-contention.js +56 -0
- package/dist/indexer/index-rebuild-lock.js +73 -0
- package/dist/indexer/index-written-assets.js +171 -133
- package/dist/indexer/indexer.js +1621 -458
- package/dist/indexer/lookup/adapter-concept-owner.js +5 -19
- package/dist/indexer/materialize-embeddings.js +785 -0
- package/dist/indexer/passes/dir-staleness.js +161 -0
- package/dist/indexer/passes/metadata.js +1 -18
- package/dist/indexer/scan/drain-dir.js +70 -27
- package/dist/indexer/search/db-search.js +89 -373
- package/dist/indexer/search/ranking-contributors.js +16 -21
- package/dist/indexer/search/ranking.js +57 -135
- package/dist/indexer/search/search-source.js +29 -11
- package/dist/integrations/agent/execution-lowering.js +3 -2
- package/dist/integrations/agent/execution-preparation.js +32 -1
- package/dist/integrations/agent/prompts.js +1 -1
- package/dist/integrations/agent/request-lowering.js +3 -2
- package/dist/llm/client.js +3 -11
- package/dist/llm/embedder.js +3 -10
- package/dist/llm/embedders/remote.js +104 -133
- package/dist/llm/feature-gate.js +2 -4
- package/dist/llm/rerank-client.js +3 -3
- package/dist/output/html-render.js +2 -1
- package/dist/output/shapes/passthrough.js +2 -1
- package/dist/output/stdout.js +24 -0
- package/dist/output/text/command-format.js +13 -19
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/index.js +2 -5
- package/dist/output/text.js +4 -3
- package/dist/registry/resolve.js +37 -10
- package/dist/scripts/akm-migrate-node.js +15197 -11351
- package/dist/scripts/akm-migrate.js +15514 -11668
- package/dist/setup/semantic-assets.js +2 -2
- package/dist/setup/setup.js +3 -3
- package/dist/setup/steps/connection.js +2 -3
- package/dist/setup/steps/tasks.js +29 -36
- package/dist/sources/providers/git-install.js +17 -11
- package/dist/sources/providers/git-provider.js +12 -5
- package/dist/sources/providers/git-stash.js +38 -16
- package/dist/sources/snapshot-fetchers/website-ingest.js +3 -3
- package/dist/storage/repositories/embedding-salvage-repository.js +184 -0
- package/dist/storage/repositories/index-connection.js +3 -1
- package/dist/storage/repositories/index-entries-repository.js +68 -77
- package/dist/storage/repositories/index-entry-schema.js +25 -16
- package/dist/storage/repositories/index-fts-repository.js +263 -29
- package/dist/storage/repositories/index-meta-repository.js +29 -0
- package/dist/storage/repositories/index-schema.js +122 -115
- package/dist/storage/repositories/index-utility-repository.js +1 -1
- package/dist/storage/repositories/index-vec-repository.js +435 -22
- package/dist/tasks/activation-config.js +90 -0
- package/dist/tasks/backends/cron.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -0
- package/dist/tasks/backends/schtasks.js +2 -0
- package/dist/tasks/embedded.js +4 -5
- package/dist/tasks/scheduler-binding.js +2 -2
- package/dist/tasks/scheduler-sync-preview.js +8 -1
- package/dist/tasks/scheduler-sync.js +19 -10
- package/dist/tasks/source/parse-task-source.js +10 -113
- package/dist/tasks/source/project-v4.js +2 -2
- package/dist/tasks/source/task-source-v4.js +4 -12
- package/dist/tasks/source/task-to-v3.js +4 -12
- package/dist/tasks/source/task-to-v4.js +40 -7
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.15.md +36 -34
- package/docs/migration/release-notes/0.9.16.md +60 -98
- package/docs/migration/release-notes/README.md +0 -5
- package/docs/migration/v0.9.1-to-v0.9.2.md +6 -9
- package/docs/reference/cli.md +124 -122
- package/docs/reference/configuration.md +137 -133
- package/docs/reference/data-and-telemetry.md +1 -2
- package/docs/reference/tasks.md +34 -29
- package/package.json +1 -1
- package/schemas/akm-config.json +170 -6
- package/schemas/akm-task.json +1 -2
- package/dist/commands/sources/index-status.js +0 -99
- package/dist/core/hash.js +0 -18
- package/dist/indexer/drain.js +0 -306
- package/dist/indexer/embedding-identity.js +0 -20
- package/dist/indexer/enrich.js +0 -260
- package/dist/indexer/reconcile.js +0 -890
- package/dist/indexer/scan/parse-file.js +0 -66
- package/dist/indexer/units/unit.js +0 -159
- package/dist/llm/embedders/provider-limits.js +0 -288
- package/dist/storage/repositories/files-repository.js +0 -181
- package/dist/storage/repositories/units-repository.js +0 -510
|
@@ -18,7 +18,7 @@ import fs from "node:fs";
|
|
|
18
18
|
import path from "node:path";
|
|
19
19
|
import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
|
|
20
20
|
import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
21
|
-
import { acquireConfigReadFence, bundleComponentConfig, getSources, loadConfig } from "../../core/config/config.js";
|
|
21
|
+
import { acquireConfigReadFence, bundleComponentConfig, getSources, loadConfig, resolveSecret, } from "../../core/config/config.js";
|
|
22
22
|
import { AkmError, ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
|
|
23
23
|
import { isPathAbsent } from "../../core/path-access.js";
|
|
24
24
|
import { getDbPath, getRegistryCacheDir } from "../../core/paths.js";
|
|
@@ -26,7 +26,7 @@ import { beginImmediateTransaction, getStateDbPath, openStateDatabase } from "..
|
|
|
26
26
|
import { warn } from "../../core/warn.js";
|
|
27
27
|
import { resolveGitContentRoot } from "../../core/write-source.js";
|
|
28
28
|
import { withAssetMutationLease } from "../../indexer/index-writer-lock.js";
|
|
29
|
-
import { akmIndex,
|
|
29
|
+
import { akmIndex, runEmbeddingPass } from "../../indexer/indexer.js";
|
|
30
30
|
import { compareAndSwapLockfileSnapshot, publishLockfileUpdate, readLockfile, readLockfileForUpdate, } from "../../integrations/lockfile.js";
|
|
31
31
|
import { parseRegistryRef } from "../../registry/resolve.js";
|
|
32
32
|
import { sha256Hex } from "../../runtime.js";
|
|
@@ -106,6 +106,7 @@ function listManagedInstalls(config) {
|
|
|
106
106
|
componentRoot,
|
|
107
107
|
requiredRoots: lock.localRoot ? [path.resolve(lock.localRoot, componentRoot)] : [],
|
|
108
108
|
auditConfigGeneration: bundleAuditGeneration(bundle),
|
|
109
|
+
...(bundle.credential !== undefined ? { credential: bundle.credential } : {}),
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
return out;
|
|
@@ -312,15 +313,11 @@ export async function akmRemove(input) {
|
|
|
312
313
|
config: {
|
|
313
314
|
sourceCount: getSources(updatedConfig).length,
|
|
314
315
|
},
|
|
315
|
-
// `IndexResponse.directoriesScanned`/`directoriesSkipped` were renamed/
|
|
316
|
-
// removed (#index-redesign W6) — this response's own `index` shape is
|
|
317
|
-
// unchanged, so bridge from the renamed source field;
|
|
318
|
-
// `directoriesSkipped` was already always 0.
|
|
319
316
|
index: {
|
|
320
317
|
mode: index.mode,
|
|
321
318
|
totalEntries: index.totalEntries,
|
|
322
|
-
directoriesScanned: index.
|
|
323
|
-
directoriesSkipped:
|
|
319
|
+
directoriesScanned: index.directoriesScanned,
|
|
320
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
324
321
|
},
|
|
325
322
|
};
|
|
326
323
|
}
|
|
@@ -346,15 +343,11 @@ export async function akmRemove(input) {
|
|
|
346
343
|
config: {
|
|
347
344
|
sourceCount: getSources(updatedConfig).length,
|
|
348
345
|
},
|
|
349
|
-
// `IndexResponse.directoriesScanned`/`directoriesSkipped` were renamed/
|
|
350
|
-
// removed (#index-redesign W6) — this response's own `index` shape is
|
|
351
|
-
// unchanged, so bridge from the renamed source field;
|
|
352
|
-
// `directoriesSkipped` was already always 0.
|
|
353
346
|
index: {
|
|
354
347
|
mode: index.mode,
|
|
355
348
|
totalEntries: index.totalEntries,
|
|
356
|
-
directoriesScanned: index.
|
|
357
|
-
directoriesSkipped:
|
|
349
|
+
directoriesScanned: index.directoriesScanned,
|
|
350
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
358
351
|
},
|
|
359
352
|
};
|
|
360
353
|
}
|
|
@@ -369,13 +362,14 @@ export async function akmRemove(input) {
|
|
|
369
362
|
function readCurrentIndexSummary() {
|
|
370
363
|
const db = openReadonlyExistingDatabase(getDbPath());
|
|
371
364
|
if (!db) {
|
|
372
|
-
return { mode: "incremental", totalEntries: 0,
|
|
365
|
+
return { mode: "incremental", totalEntries: 0, directoriesScanned: 0, directoriesSkipped: 0 };
|
|
373
366
|
}
|
|
374
367
|
try {
|
|
375
368
|
return {
|
|
376
369
|
mode: "incremental",
|
|
377
370
|
totalEntries: getAllEntries(db).length,
|
|
378
|
-
|
|
371
|
+
directoriesScanned: 0,
|
|
372
|
+
directoriesSkipped: 0,
|
|
379
373
|
};
|
|
380
374
|
}
|
|
381
375
|
finally {
|
|
@@ -397,15 +391,11 @@ function buildUpdateResponse(stashDir, target, all, processed, opts) {
|
|
|
397
391
|
config: {
|
|
398
392
|
sourceCount: getSources(finalConfig).length,
|
|
399
393
|
},
|
|
400
|
-
// `IndexResponse.directoriesScanned`/`directoriesSkipped` were renamed/
|
|
401
|
-
// removed (#index-redesign W6) — `UpdateResponse["index"]`'s own shape is
|
|
402
|
-
// unchanged, so bridge from the renamed source field;
|
|
403
|
-
// `directoriesSkipped` was already always 0.
|
|
404
394
|
index: {
|
|
405
395
|
mode: index.mode,
|
|
406
396
|
totalEntries: index.totalEntries,
|
|
407
|
-
directoriesScanned: index.
|
|
408
|
-
directoriesSkipped:
|
|
397
|
+
directoriesScanned: index.directoriesScanned,
|
|
398
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
409
399
|
...(index.scanComplete !== undefined ? { scanComplete: index.scanComplete } : {}),
|
|
410
400
|
// A real embedding pass (`akmIndex`/`runEmbeddingPass`) reports its own
|
|
411
401
|
// verified `semanticStatus`. When no pass ran this update (the
|
|
@@ -473,9 +463,7 @@ function openUnifiedUpdateTransaction() {
|
|
|
473
463
|
candidate.exec(`ATTACH DATABASE ${sqliteStringLiteral(statePath)} AS "${UPDATE_STATE_SCHEMA}"`);
|
|
474
464
|
// openIndexDatabase invokes this before ensureSchema, so the outer
|
|
475
465
|
// transaction begins before the first update-owned index mutation.
|
|
476
|
-
|
|
477
|
-
// report as INDEX_DB_CONTENDED, not the state.db default.
|
|
478
|
-
beginImmediateTransaction(candidate, "index");
|
|
466
|
+
beginImmediateTransaction(candidate);
|
|
479
467
|
},
|
|
480
468
|
});
|
|
481
469
|
return {
|
|
@@ -553,15 +541,7 @@ async function runPostCommitEmbeddingPass(index) {
|
|
|
553
541
|
return { ...index, verification };
|
|
554
542
|
}
|
|
555
543
|
catch (error) {
|
|
556
|
-
|
|
557
|
-
// drain catch uses (indexer.ts's reclassifyIndexDbContention) before
|
|
558
|
-
// building the message: this catch's first statement, openIndexDatabase
|
|
559
|
-
// (init runs ensureSchema and writes), can throw the raw SQLite driver
|
|
560
|
-
// error under contention, and this was the last surviving non-fatal
|
|
561
|
-
// catch that printed that raw text verbatim to the operator instead of
|
|
562
|
-
// reporting it as index.db contention.
|
|
563
|
-
const reportedError = reclassifyIndexDbContention(error);
|
|
564
|
-
const message = reportedError instanceof Error ? reportedError.message : String(reportedError);
|
|
544
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
565
545
|
warn(`[akm bundle update] post-commit embedding pass failed: ${message}`);
|
|
566
546
|
return {
|
|
567
547
|
...index,
|
|
@@ -662,12 +642,12 @@ async function verifyPublishedAudit(prepared, id) {
|
|
|
662
642
|
throw new ConfigError(`Writable source "${id}" changed while its audited Git target was materialized; refusing to activate unaudited bytes.`, "DANGEROUS_ENV_AUDIT_FAILED");
|
|
663
643
|
}
|
|
664
644
|
}
|
|
665
|
-
async function auditPreparedUpdate(prepared, id, ref,
|
|
645
|
+
async function auditPreparedUpdate(prepared, id, ref, allowDangerousEnvKeys) {
|
|
666
646
|
updateTransactionHook("staged", id);
|
|
667
647
|
const decision = await auditStashForDangerousKeys({
|
|
668
648
|
stashRoot: prepared.auditRoot,
|
|
669
649
|
ref,
|
|
670
|
-
allowDangerousKeys:
|
|
650
|
+
allowDangerousKeys: allowDangerousEnvKeys,
|
|
671
651
|
isTTY: process.stdin.isTTY === true,
|
|
672
652
|
operation: "update",
|
|
673
653
|
renderBlockedError: false,
|
|
@@ -734,6 +714,7 @@ function prepareWritableManagedUpdate(managed, force) {
|
|
|
734
714
|
writable: true,
|
|
735
715
|
writableRoot: stagedContentRoot,
|
|
736
716
|
...(stagedPhysicalRequiredRoots.length > 0 ? { writableRequiredRoots: stagedPhysicalRequiredRoots } : {}),
|
|
717
|
+
...(managed.credential ? { credential: resolveSecret(managed.credential, storeSecretResolver.resolveSecret) } : {}),
|
|
737
718
|
})
|
|
738
719
|
.then((staged) => {
|
|
739
720
|
const auditedTargetHead = gitHead(stagedRepo);
|
|
@@ -780,6 +761,9 @@ async function prepareManagedUpdate(managed, force) {
|
|
|
780
761
|
force,
|
|
781
762
|
writable: false,
|
|
782
763
|
cacheRootDir: stagingParent,
|
|
764
|
+
...(managed.credential
|
|
765
|
+
? { credential: resolveSecret(managed.credential, storeSecretResolver.resolveSecret) }
|
|
766
|
+
: {}),
|
|
783
767
|
});
|
|
784
768
|
if (!pathAtOrBelow(staged.cacheDir, stagingParent)) {
|
|
785
769
|
// Test/provider seams may return an already-isolated candidate. It remains
|
|
@@ -816,9 +800,9 @@ function resolveComponentAuditRoot(contentRoot, componentRoot, id) {
|
|
|
816
800
|
}
|
|
817
801
|
return resolved;
|
|
818
802
|
}
|
|
819
|
-
async function publishPreparedPlainUpdate(id, ref, prepared, stashDir,
|
|
803
|
+
async function publishPreparedPlainUpdate(id, ref, prepared, stashDir, allowDangerousEnvKeys, full, expectedConfigGeneration) {
|
|
820
804
|
try {
|
|
821
|
-
await auditPreparedUpdate(prepared, id, ref,
|
|
805
|
+
await auditPreparedUpdate(prepared, id, ref, allowDangerousEnvKeys);
|
|
822
806
|
return await withAssetMutationLease("source-update", async () => {
|
|
823
807
|
let transaction;
|
|
824
808
|
let releaseConfigFence;
|
|
@@ -892,14 +876,18 @@ async function prepareGitPlainUpdate(gitSource, componentRoot, writable) {
|
|
|
892
876
|
const stagingParent = createStagingParent(path.dirname(livePaths.rootDir));
|
|
893
877
|
const stagedPaths = getCachePaths(repo.canonicalUrl, stagingParent);
|
|
894
878
|
try {
|
|
895
|
-
const
|
|
896
|
-
|
|
879
|
+
const hasLiveCheckout = writable && fs.existsSync(livePaths.repoDir);
|
|
880
|
+
const expectedOldHead = hasLiveCheckout ? gitHead(livePaths.repoDir) : undefined;
|
|
881
|
+
if (hasLiveCheckout) {
|
|
897
882
|
fs.cpSync(livePaths.rootDir, stagedPaths.rootDir, { recursive: true, preserveTimestamps: true });
|
|
898
883
|
}
|
|
899
884
|
const staged = await syncMirroredRepo(gitSource, {
|
|
900
885
|
force: true,
|
|
901
886
|
writable,
|
|
902
887
|
cacheRootDir: stagingParent,
|
|
888
|
+
...(gitSource.credential
|
|
889
|
+
? { credential: resolveSecret(gitSource.credential, storeSecretResolver.resolveSecret) }
|
|
890
|
+
: {}),
|
|
903
891
|
});
|
|
904
892
|
const configuredStagedAuditRoot = resolveComponentAuditRoot(resolveGitContentRoot(staged.contentDir), componentRoot, gitSource.name ?? "git");
|
|
905
893
|
if (!pathAtOrBelow(staged.cacheDir, stagingParent)) {
|
|
@@ -922,7 +910,13 @@ async function prepareGitPlainUpdate(gitSource, componentRoot, writable) {
|
|
|
922
910
|
const physicalStagedRepo = canonicalWritablePath(stagedPaths.repoDir, "staged plain Git checkout");
|
|
923
911
|
auditRoot = containedWritablePath(configuredStagedAuditRoot, physicalStagedRepo, `Post-sync component root for "${gitSource.name ?? "git"}"`);
|
|
924
912
|
publishedAuditRoot = remapStagedPath(configuredStagedAuditRoot, staged.cacheDir, livePaths.rootDir);
|
|
925
|
-
|
|
913
|
+
if (hasLiveCheckout) {
|
|
914
|
+
publishedAuditContainmentRoot = canonicalWritablePath(livePaths.repoDir, "plain Git checkout");
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
const physicalCacheParent = canonicalWritablePath(path.dirname(livePaths.rootDir), "plain Git cache parent");
|
|
918
|
+
publishedAuditContainmentRoot = path.join(physicalCacheParent, path.basename(livePaths.rootDir), path.relative(livePaths.rootDir, livePaths.repoDir));
|
|
919
|
+
}
|
|
926
920
|
publishedAuditExpectedPhysicalRoot = path.join(publishedAuditContainmentRoot, path.relative(physicalStagedRepo, auditRoot));
|
|
927
921
|
}
|
|
928
922
|
return {
|
|
@@ -930,7 +924,7 @@ async function prepareGitPlainUpdate(gitSource, componentRoot, writable) {
|
|
|
930
924
|
...(publishedAuditRoot ? { publishedAuditRoot } : {}),
|
|
931
925
|
...(publishedAuditContainmentRoot ? { publishedAuditContainmentRoot } : {}),
|
|
932
926
|
...(publishedAuditExpectedPhysicalRoot ? { publishedAuditExpectedPhysicalRoot } : {}),
|
|
933
|
-
publication: writable
|
|
927
|
+
publication: writable && hasLiveCheckout
|
|
934
928
|
? writableGitPublication({
|
|
935
929
|
stagedRepo: stagedPaths.repoDir,
|
|
936
930
|
liveRepo: livePaths.repoDir,
|
|
@@ -945,14 +939,14 @@ async function prepareGitPlainUpdate(gitSource, componentRoot, writable) {
|
|
|
945
939
|
throw error;
|
|
946
940
|
}
|
|
947
941
|
}
|
|
948
|
-
async function syncGitPlainSource(gitSource, stashDir,
|
|
942
|
+
async function syncGitPlainSource(gitSource, stashDir, allowDangerousEnvKeys) {
|
|
949
943
|
const id = gitSource.name ?? gitSource.url ?? "";
|
|
950
944
|
const currentConfig = loadConfig();
|
|
951
945
|
const currentSource = currentPlainSource(currentConfig, id, "git");
|
|
952
946
|
const ref = currentSource.url ?? "";
|
|
953
947
|
const fence = bundleAuditFence(currentConfig, id);
|
|
954
948
|
const prepared = await prepareGitPlainUpdate(currentSource, fence.componentRoot, fence.writable);
|
|
955
|
-
const index = await publishPreparedPlainUpdate(id, ref, prepared, stashDir,
|
|
949
|
+
const index = await publishPreparedPlainUpdate(id, ref, prepared, stashDir, allowDangerousEnvKeys, true, fence.generation);
|
|
956
950
|
return { item: { id, kind: "git", ref }, index };
|
|
957
951
|
}
|
|
958
952
|
async function prepareWebsitePlainUpdate(websiteSource, componentRoot) {
|
|
@@ -983,24 +977,24 @@ async function prepareWebsitePlainUpdate(websiteSource, componentRoot) {
|
|
|
983
977
|
throw error;
|
|
984
978
|
}
|
|
985
979
|
}
|
|
986
|
-
async function syncWebsitePlainSource(websiteSource, stashDir,
|
|
980
|
+
async function syncWebsitePlainSource(websiteSource, stashDir, allowDangerousEnvKeys) {
|
|
987
981
|
const id = websiteSource.name ?? websiteSource.url ?? "";
|
|
988
982
|
const currentConfig = loadConfig();
|
|
989
983
|
const currentSource = currentPlainSource(currentConfig, id, "website");
|
|
990
984
|
const ref = currentSource.url ?? "";
|
|
991
985
|
const fence = bundleAuditFence(currentConfig, id);
|
|
992
986
|
const prepared = await prepareWebsitePlainUpdate(currentSource, fence.componentRoot);
|
|
993
|
-
const index = await publishPreparedPlainUpdate(id, ref, prepared, stashDir,
|
|
987
|
+
const index = await publishPreparedPlainUpdate(id, ref, prepared, stashDir, allowDangerousEnvKeys, true, fence.generation);
|
|
994
988
|
return { item: { id, kind: "website", ref }, index };
|
|
995
989
|
}
|
|
996
990
|
/** Sync a git-mirrored (plain) source and return an UpdateResponse (single-target path). */
|
|
997
|
-
async function updateGitSource(stashDir, target, all, gitSource,
|
|
998
|
-
const synced = await syncGitPlainSource(gitSource, stashDir,
|
|
991
|
+
async function updateGitSource(stashDir, target, all, gitSource, allowDangerousEnvKeys) {
|
|
992
|
+
const synced = await syncGitPlainSource(gitSource, stashDir, allowDangerousEnvKeys);
|
|
999
993
|
return buildUpdateResponse(stashDir, target, all, [], { plainSynced: [synced.item], index: synced.index });
|
|
1000
994
|
}
|
|
1001
995
|
/** Re-crawl a website (plain) source and return an UpdateResponse (single-target path). */
|
|
1002
|
-
async function updateWebsiteSource(stashDir, target, all, websiteSource,
|
|
1003
|
-
const synced = await syncWebsitePlainSource(websiteSource, stashDir,
|
|
996
|
+
async function updateWebsiteSource(stashDir, target, all, websiteSource, allowDangerousEnvKeys) {
|
|
997
|
+
const synced = await syncWebsitePlainSource(websiteSource, stashDir, allowDangerousEnvKeys);
|
|
1004
998
|
return buildUpdateResponse(stashDir, target, all, [], { plainSynced: [synced.item], index: synced.index });
|
|
1005
999
|
}
|
|
1006
1000
|
/** Reconcile a filesystem source's current bytes without provider hydration. */
|
|
@@ -1063,7 +1057,7 @@ function managedInstallViewOfPlainNpm(npmSource) {
|
|
|
1063
1057
|
* scripts; only the branch that can `rm -rf` needs a gate, not the whole
|
|
1064
1058
|
* command).
|
|
1065
1059
|
*/
|
|
1066
|
-
async function updateManagedInstall(managed, force, yes, stashDir,
|
|
1060
|
+
async function updateManagedInstall(managed, force, yes, stashDir, allowDangerousEnvKeys) {
|
|
1067
1061
|
// No pre-cleanup of the old root, even under --force: the providers already
|
|
1068
1062
|
// re-materialize staging-first (git clones into a `.tmp-*` sibling and swaps
|
|
1069
1063
|
// only on success), so destroying `managed.localRoot` BEFORE `syncFromRef`
|
|
@@ -1072,7 +1066,7 @@ async function updateManagedInstall(managed, force, yes, stashDir, allowInsecure
|
|
|
1072
1066
|
// config/lock publication and reindexing both succeed.
|
|
1073
1067
|
const prepared = await prepareManagedUpdate(managed, force);
|
|
1074
1068
|
try {
|
|
1075
|
-
await auditPreparedUpdate(prepared, managed.installId, managed.ref,
|
|
1069
|
+
await auditPreparedUpdate(prepared, managed.installId, managed.ref, allowDangerousEnvKeys);
|
|
1076
1070
|
const synced = prepared.synced;
|
|
1077
1071
|
const installedEntry = {
|
|
1078
1072
|
id: managed.installId,
|
|
@@ -1280,14 +1274,14 @@ export async function akmUpdate(input) {
|
|
|
1280
1274
|
const all = input?.all === true;
|
|
1281
1275
|
const force = input?.force === true;
|
|
1282
1276
|
const yes = input?.yes === true;
|
|
1283
|
-
const
|
|
1277
|
+
const allowDangerousEnvKeys = input?.allowDangerousEnvKeys === true;
|
|
1284
1278
|
const config = loadConfig();
|
|
1285
1279
|
const managedInstalls = listManagedInstalls(config);
|
|
1286
1280
|
if (target && !all) {
|
|
1287
1281
|
// Registry-managed install (lock-backed) — re-download from its locator.
|
|
1288
1282
|
const managed = resolveManagedTarget(config, target);
|
|
1289
1283
|
if (managed) {
|
|
1290
|
-
const updated = await updateManagedInstall(managed, force, yes, stashDir,
|
|
1284
|
+
const updated = await updateManagedInstall(managed, force, yes, stashDir, allowDangerousEnvKeys);
|
|
1291
1285
|
return buildUpdateResponse(stashDir, target, all, [updated.item], { index: updated.index });
|
|
1292
1286
|
}
|
|
1293
1287
|
// Plain git / website source (bundles without a lock) — provider re-sync.
|
|
@@ -1316,7 +1310,7 @@ export async function akmUpdate(input) {
|
|
|
1316
1310
|
return false;
|
|
1317
1311
|
});
|
|
1318
1312
|
if (gitMatch)
|
|
1319
|
-
return updateGitSource(stashDir, target, all, gitMatch,
|
|
1313
|
+
return updateGitSource(stashDir, target, all, gitMatch, allowDangerousEnvKeys);
|
|
1320
1314
|
const websiteMatch = stashes.find((s) => {
|
|
1321
1315
|
if (s.type !== "website")
|
|
1322
1316
|
return false;
|
|
@@ -1329,7 +1323,7 @@ export async function akmUpdate(input) {
|
|
|
1329
1323
|
return false;
|
|
1330
1324
|
});
|
|
1331
1325
|
if (websiteMatch)
|
|
1332
|
-
return updateWebsiteSource(stashDir, target, all, websiteMatch,
|
|
1326
|
+
return updateWebsiteSource(stashDir, target, all, websiteMatch, allowDangerousEnvKeys);
|
|
1333
1327
|
// Plain npm source (bundle without a lock) — sync via the registry
|
|
1334
1328
|
// pipeline and promote to a managed install (see
|
|
1335
1329
|
// managedInstallViewOfPlainNpm's doc comment for why npm can't stay
|
|
@@ -1344,7 +1338,7 @@ export async function akmUpdate(input) {
|
|
|
1344
1338
|
return false;
|
|
1345
1339
|
});
|
|
1346
1340
|
if (npmMatch) {
|
|
1347
|
-
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(npmMatch), force, yes, stashDir,
|
|
1341
|
+
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(npmMatch), force, yes, stashDir, allowDangerousEnvKeys);
|
|
1348
1342
|
return buildUpdateResponse(stashDir, target, all, [updated.item], { index: updated.index });
|
|
1349
1343
|
}
|
|
1350
1344
|
const filesystemMatch = stashes.find((source) => {
|
|
@@ -1367,7 +1361,7 @@ export async function akmUpdate(input) {
|
|
|
1367
1361
|
let latestIndex;
|
|
1368
1362
|
for (const managed of selected) {
|
|
1369
1363
|
try {
|
|
1370
|
-
const updated = await updateManagedInstall(managed, force, yes, stashDir,
|
|
1364
|
+
const updated = await updateManagedInstall(managed, force, yes, stashDir, allowDangerousEnvKeys);
|
|
1371
1365
|
processed.push(updated.item);
|
|
1372
1366
|
latestIndex = updated.index;
|
|
1373
1367
|
}
|
|
@@ -1392,17 +1386,17 @@ export async function akmUpdate(input) {
|
|
|
1392
1386
|
const id = plain.name ?? plain.path ?? plain.url ?? "";
|
|
1393
1387
|
try {
|
|
1394
1388
|
if (plain.type === "git") {
|
|
1395
|
-
const updated = await syncGitPlainSource(plain, stashDir,
|
|
1389
|
+
const updated = await syncGitPlainSource(plain, stashDir, allowDangerousEnvKeys);
|
|
1396
1390
|
plainSynced.push(updated.item);
|
|
1397
1391
|
latestIndex = updated.index;
|
|
1398
1392
|
}
|
|
1399
1393
|
else if (plain.type === "npm") {
|
|
1400
|
-
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(plain), force, yes, stashDir,
|
|
1394
|
+
const updated = await updateManagedInstall(managedInstallViewOfPlainNpm(plain), force, yes, stashDir, allowDangerousEnvKeys);
|
|
1401
1395
|
processed.push(updated.item);
|
|
1402
1396
|
latestIndex = updated.index;
|
|
1403
1397
|
}
|
|
1404
1398
|
else if (plain.type === "website") {
|
|
1405
|
-
const updated = await syncWebsitePlainSource(plain, stashDir,
|
|
1399
|
+
const updated = await syncWebsitePlainSource(plain, stashDir, allowDangerousEnvKeys);
|
|
1406
1400
|
plainSynced.push(updated.item);
|
|
1407
1401
|
latestIndex = updated.index;
|
|
1408
1402
|
}
|
|
@@ -6,7 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
|
|
7
7
|
import { isBundleSlug } from "../../core/asset/asset-ref.js";
|
|
8
8
|
import { isHttpUrl, resolveStashDir } from "../../core/common.js";
|
|
9
|
-
import { bundleEntryToSourceEntry, getSources, installedSourceDescriptor, loadConfig, mutateConfig, } from "../../core/config/config.js";
|
|
9
|
+
import { bundleEntryToSourceEntry, getSources, installedSourceDescriptor, loadConfig, mutateConfig, resolveSecret, } from "../../core/config/config.js";
|
|
10
10
|
import { ConfigError, UsageError } from "../../core/errors.js";
|
|
11
11
|
import { akmIndex } from "../../indexer/indexer.js";
|
|
12
12
|
import { deriveBundleId } from "../../indexer/installations.js";
|
|
@@ -16,7 +16,8 @@ import { detectStashRoot } from "../../sources/providers/provider-utils.js";
|
|
|
16
16
|
import { syncFromRef } from "../../sources/providers/sync-from-ref.js";
|
|
17
17
|
import { storeSecretResolver } from "../../sources/snapshot-fetchers/secret-seam.js";
|
|
18
18
|
import { ensureWebsiteMirror, shouldAllowPrivateWebsiteUrlForTests, validateWebsiteInputUrl, } from "../../sources/snapshot-fetchers/website-ingest.js";
|
|
19
|
-
import {
|
|
19
|
+
import { revokeSchedulerActivationsForBundle } from "../../tasks/activation-config.js";
|
|
20
|
+
import { bundleKeyForPath, bundleKeyForUrl, nextBundleKey, placeBundle, } from "./bundle-config-ops.js";
|
|
20
21
|
export async function akmAdd(input) {
|
|
21
22
|
const ref = input.ref.trim();
|
|
22
23
|
if (!ref)
|
|
@@ -24,23 +25,23 @@ export async function akmAdd(input) {
|
|
|
24
25
|
"Examples: `akm bundle add @scope/stash`, `akm bundle add github:owner/repo`, `akm bundle add ./local/path`");
|
|
25
26
|
const stashDir = resolveStashDir();
|
|
26
27
|
if (shouldAddAsWebsiteUrl(ref)) {
|
|
27
|
-
return addWebsiteSource(ref, stashDir, input.name, input.options);
|
|
28
|
+
return addWebsiteSource(ref, stashDir, input.name, input.options, input);
|
|
28
29
|
}
|
|
29
30
|
// Local directories become filesystem bundles; registry refs use the
|
|
30
31
|
// registry-backed bundle installer below.
|
|
31
32
|
try {
|
|
32
33
|
const parsed = parseRegistryRef(ref);
|
|
33
34
|
if (parsed.source === "local") {
|
|
34
|
-
return addLocalSource(ref, parsed.sourcePath, stashDir, input.name, input.adapter);
|
|
35
|
+
return addLocalSource(ref, parsed.sourcePath, stashDir, input.name, input.adapter, input);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
catch {
|
|
38
39
|
// Not a local ref — fall through to registry install
|
|
39
40
|
}
|
|
40
|
-
return addRegistryStash(ref, stashDir, input.writable);
|
|
41
|
+
return addRegistryStash(ref, stashDir, input.writable, input, input.credential);
|
|
41
42
|
}
|
|
42
43
|
/** Add a local directory as a filesystem bundle. */
|
|
43
|
-
async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitAdapter) {
|
|
44
|
+
async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitAdapter, position = {}) {
|
|
44
45
|
const stashRoot = detectStashRoot(sourcePath);
|
|
45
46
|
const resolvedPath = path.resolve(stashRoot);
|
|
46
47
|
const adapter = explicitAdapter ?? detectAdapterId(resolvedPath);
|
|
@@ -62,11 +63,11 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitA
|
|
|
62
63
|
}
|
|
63
64
|
const bundles = { ...(config.bundles ?? {}) };
|
|
64
65
|
bundleKey = nextBundleKey(bundles, explicitName, resolvedPath);
|
|
65
|
-
|
|
66
|
+
const entry = {
|
|
66
67
|
path: resolvedPath,
|
|
67
68
|
components: { main: { root: ".", adapter } },
|
|
68
69
|
};
|
|
69
|
-
return { ...config, bundles };
|
|
70
|
+
return { ...config, bundles: placeBundle(bundles, bundleKey, entry, position) };
|
|
70
71
|
});
|
|
71
72
|
const index = await akmIndex({ stashDir });
|
|
72
73
|
const updatedConfig = loadConfig();
|
|
@@ -86,18 +87,13 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitA
|
|
|
86
87
|
index: {
|
|
87
88
|
mode: index.mode,
|
|
88
89
|
totalEntries: index.totalEntries,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
// never a directory walk) — this response's own `directoriesScanned`/
|
|
92
|
-
// `directoriesSkipped` shape is unchanged, so bridge from the renamed
|
|
93
|
-
// source field; `directoriesSkipped` was already always 0.
|
|
94
|
-
directoriesScanned: index.sourcesScanned,
|
|
95
|
-
directoriesSkipped: 0,
|
|
90
|
+
directoriesScanned: index.directoriesScanned,
|
|
91
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
96
92
|
...(index.warnings?.length ? { warnings: index.warnings } : {}),
|
|
97
93
|
},
|
|
98
94
|
};
|
|
99
95
|
}
|
|
100
|
-
async function addWebsiteSource(ref, stashDir, name, options) {
|
|
96
|
+
async function addWebsiteSource(ref, stashDir, name, options, position = {}) {
|
|
101
97
|
const allowPrivateHosts = shouldAllowPrivateWebsiteUrlForTests(ref);
|
|
102
98
|
const normalizedUrl = validateWebsiteInputUrl(ref, { allowPrivateHosts });
|
|
103
99
|
const numberOption = (value) => (typeof value === "number" ? value : undefined);
|
|
@@ -129,9 +125,9 @@ async function addWebsiteSource(ref, stashDir, name, options) {
|
|
|
129
125
|
entry = bundleEntryToSourceEntry(key, bundles[key]);
|
|
130
126
|
return config;
|
|
131
127
|
}
|
|
132
|
-
bundles
|
|
128
|
+
const nextBundles = placeBundle(bundles, key, nextBundle, position);
|
|
133
129
|
entry = bundleEntryToSourceEntry(key, nextBundle);
|
|
134
|
-
return { ...config, bundles };
|
|
130
|
+
return { ...config, bundles: nextBundles };
|
|
135
131
|
});
|
|
136
132
|
const cachePaths = await ensureWebsiteMirror(entry, {
|
|
137
133
|
requireStashDir: true,
|
|
@@ -156,13 +152,8 @@ async function addWebsiteSource(ref, stashDir, name, options) {
|
|
|
156
152
|
index: {
|
|
157
153
|
mode: index.mode,
|
|
158
154
|
totalEntries: index.totalEntries,
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// never a directory walk) — this response's own `directoriesScanned`/
|
|
162
|
-
// `directoriesSkipped` shape is unchanged, so bridge from the renamed
|
|
163
|
-
// source field; `directoriesSkipped` was already always 0.
|
|
164
|
-
directoriesScanned: index.sourcesScanned,
|
|
165
|
-
directoriesSkipped: 0,
|
|
155
|
+
directoriesScanned: index.directoriesScanned,
|
|
156
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
166
157
|
...(index.warnings?.length ? { warnings: index.warnings } : {}),
|
|
167
158
|
},
|
|
168
159
|
};
|
|
@@ -171,11 +162,14 @@ async function addWebsiteSource(ref, stashDir, name, options) {
|
|
|
171
162
|
* Install a stash from a registry (npm, github, git) by dispatching to the
|
|
172
163
|
* matching syncable provider and persisting the lock entry.
|
|
173
164
|
*/
|
|
174
|
-
async function addRegistryStash(ref, stashDir, writable) {
|
|
165
|
+
async function addRegistryStash(ref, stashDir, writable, position = {}, credentialRef) {
|
|
175
166
|
const parsedRef = parseRegistryRef(ref);
|
|
176
167
|
if (writable === true && parsedRef.source !== "git" && parsedRef.source !== "github") {
|
|
177
168
|
throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
|
|
178
169
|
}
|
|
170
|
+
if (credentialRef && parsedRef.source !== "git" && parsedRef.source !== "github") {
|
|
171
|
+
throw new ConfigError("credential is only supported on git sources", "INVALID_CONFIG_FILE");
|
|
172
|
+
}
|
|
179
173
|
const currentConfig = loadConfig();
|
|
180
174
|
const existingBundleKey = findInstalledBundleKey(currentConfig.bundles ?? {}, parsedRef.id);
|
|
181
175
|
const existingBundle = existingBundleKey ? currentConfig.bundles?.[existingBundleKey] : undefined;
|
|
@@ -189,6 +183,11 @@ async function addRegistryStash(ref, stashDir, writable) {
|
|
|
189
183
|
writable: effectiveWritable,
|
|
190
184
|
...(effectiveWritable && priorLock?.localRoot ? { writableRoot: priorLock.localRoot } : {}),
|
|
191
185
|
...(requiredRoots.length > 0 ? { writableRequiredRoots: requiredRoots } : {}),
|
|
186
|
+
...(credentialRef
|
|
187
|
+
? { credential: resolveSecret(credentialRef, storeSecretResolver.resolveSecret) }
|
|
188
|
+
: existingBundle?.credential
|
|
189
|
+
? { credential: resolveSecret(existingBundle.credential, storeSecretResolver.resolveSecret) }
|
|
190
|
+
: {}),
|
|
192
191
|
});
|
|
193
192
|
const { config: updatedConfig, bundleId } = upsertInstalledRegistryEntry({
|
|
194
193
|
id: synced.id,
|
|
@@ -201,7 +200,7 @@ async function addRegistryStash(ref, stashDir, writable) {
|
|
|
201
200
|
cacheDir: synced.cacheDir,
|
|
202
201
|
installedAt: synced.syncedAt,
|
|
203
202
|
writable: synced.writable,
|
|
204
|
-
});
|
|
203
|
+
}, position, credentialRef);
|
|
205
204
|
// The prior materialized root (if this is a re-install) — read BEFORE the lock
|
|
206
205
|
// upsert overwrites it, so a moved cache root can be cleaned afterwards.
|
|
207
206
|
const priorLocalRoot = priorLock?.localRoot;
|
|
@@ -248,13 +247,8 @@ async function addRegistryStash(ref, stashDir, writable) {
|
|
|
248
247
|
index: {
|
|
249
248
|
mode: index.mode,
|
|
250
249
|
totalEntries: index.totalEntries,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
// never a directory walk) — this response's own `directoriesScanned`/
|
|
254
|
-
// `directoriesSkipped` shape is unchanged, so bridge from the renamed
|
|
255
|
-
// source field; `directoriesSkipped` was already always 0.
|
|
256
|
-
directoriesScanned: index.sourcesScanned,
|
|
257
|
-
directoriesSkipped: 0,
|
|
250
|
+
directoriesScanned: index.directoriesScanned,
|
|
251
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
258
252
|
...(index.warnings?.length ? { warnings: index.warnings } : {}),
|
|
259
253
|
},
|
|
260
254
|
};
|
|
@@ -267,7 +261,7 @@ async function addRegistryStash(ref, stashDir, writable) {
|
|
|
267
261
|
* via {@link upsertLockEntry} with the returned `bundleId`). Returns the config
|
|
268
262
|
* plus the derived bundle id so the caller keys its lock entry identically.
|
|
269
263
|
*/
|
|
270
|
-
export function upsertInstalledRegistryEntry(entry) {
|
|
264
|
+
export function upsertInstalledRegistryEntry(entry, position = {}, credential) {
|
|
271
265
|
let bundleId = entry.id;
|
|
272
266
|
const config = mutateConfig((current) => {
|
|
273
267
|
const bundles = { ...(current.bundles ?? {}) };
|
|
@@ -286,13 +280,15 @@ export function upsertInstalledRegistryEntry(entry) {
|
|
|
286
280
|
},
|
|
287
281
|
};
|
|
288
282
|
const descriptor = installedSourceDescriptor(entry.source, entry.ref, path.resolve(entry.stashRoot));
|
|
289
|
-
bundles[bundleId]
|
|
283
|
+
const effectiveCredential = credential ?? bundles[bundleId]?.credential;
|
|
284
|
+
const nextEntry = {
|
|
290
285
|
...descriptor,
|
|
286
|
+
...(effectiveCredential ? { credential: effectiveCredential } : {}),
|
|
291
287
|
...(entry.writable === true ? { writable: true } : {}),
|
|
292
288
|
...(entry.id !== bundleId ? { registryId: entry.id } : {}),
|
|
293
289
|
components: components,
|
|
294
290
|
};
|
|
295
|
-
return { ...current, bundles };
|
|
291
|
+
return { ...current, bundles: placeBundle(bundles, bundleId, nextEntry, position) };
|
|
296
292
|
}).config;
|
|
297
293
|
return { config, bundleId };
|
|
298
294
|
}
|
|
@@ -310,7 +306,12 @@ export async function removeInstalledRegistryEntry(id) {
|
|
|
310
306
|
return current;
|
|
311
307
|
removedKey = key;
|
|
312
308
|
delete bundles[key];
|
|
313
|
-
return {
|
|
309
|
+
return revokeSchedulerActivationsForBundle({
|
|
310
|
+
...current,
|
|
311
|
+
bundles: Object.keys(bundles).length > 0 ? bundles : undefined,
|
|
312
|
+
...(current.defaultBundle === key ? { defaultBundle: undefined } : {}),
|
|
313
|
+
...(current.defaultWriteTarget === key ? { defaultWriteTarget: undefined } : {}),
|
|
314
|
+
}, key);
|
|
314
315
|
}).config;
|
|
315
316
|
if (removedKey)
|
|
316
317
|
await removeLockEntry(removedKey);
|