@lesliechan721/aw 0.1.0-beta.63 → 0.1.0-beta.65
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/dist/authoring/assets/aw-runtime-config.mjs +3 -3
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
- package/dist/authoring/scripts/add-skill.mjs +3 -17
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +1 -0
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +95 -698
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
- package/dist/scripts/aw-skill-eval-materializer.js +74 -44
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
- package/dist/scripts/bundle-plugins.js +10 -2
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +1 -1
- package/dist/scripts/plugin-staging.js +1 -1
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +31 -31
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +1 -1
- package/dist/src/commands/aw-capabilities.js +2 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -1
- package/dist/src/commands/console.js +4 -3
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/intent-command.d.ts +30 -0
- package/dist/src/commands/intent-command.js +164 -0
- package/dist/src/commands/intent-command.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +2 -0
- package/dist/src/commands/operation-plan.js +2 -1
- package/dist/src/commands/operation-plan.js.map +1 -1
- package/dist/src/commands/options.d.ts +3 -11
- package/dist/src/commands/options.js +11 -17
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +27 -33
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +108 -6
- package/dist/src/commands/rebuild-command.js +617 -1549
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +19 -3
- package/dist/src/commands/rebuild-integration.js +500 -459
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.js +1 -2
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/review-inspection.d.ts +2 -2
- package/dist/src/commands/review-inspection.js +30 -86
- package/dist/src/commands/review-inspection.js.map +1 -1
- package/dist/src/commands/review-preparation.js +16 -7
- package/dist/src/commands/review-preparation.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +2 -0
- package/dist/src/commands/skill-eval.js +15 -9
- package/dist/src/commands/skill-eval.js.map +1 -1
- package/dist/src/commands/status-v6.d.ts +38 -5
- package/dist/src/commands/status-v6.js +269 -102
- package/dist/src/commands/status-v6.js.map +1 -1
- package/dist/src/commands/targets-command.d.ts +27 -0
- package/dist/src/commands/targets-command.js +77 -0
- package/dist/src/commands/targets-command.js.map +1 -0
- package/dist/src/commands/write-commands.js +5 -5
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +2 -0
- package/dist/src/eval/manifest-v1.js +7 -2
- package/dist/src/eval/manifest-v1.js.map +1 -1
- package/dist/src/generators/local-plugin.d.ts +2 -1
- package/dist/src/generators/local-plugin.js +111 -108
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +10 -17
- package/dist/src/index.js +585 -1349
- package/dist/src/index.js.map +1 -1
- package/dist/src/intent/deployment-preferences.d.ts +63 -0
- package/dist/src/intent/deployment-preferences.js +170 -0
- package/dist/src/intent/deployment-preferences.js.map +1 -0
- package/dist/src/intent/diagnostics.d.ts +11 -0
- package/dist/src/intent/diagnostics.js +14 -0
- package/dist/src/intent/diagnostics.js.map +1 -0
- package/dist/src/intent/intent-codec.d.ts +19 -0
- package/dist/src/intent/intent-codec.js +117 -0
- package/dist/src/intent/intent-codec.js.map +1 -0
- package/dist/src/intent/intent-store.d.ts +43 -0
- package/dist/src/intent/intent-store.js +118 -0
- package/dist/src/intent/intent-store.js.map +1 -0
- package/dist/src/intent/scope-context.d.ts +33 -0
- package/dist/src/intent/scope-context.js +145 -0
- package/dist/src/intent/scope-context.js.map +1 -0
- package/dist/src/intent/work-context.d.ts +13 -0
- package/dist/src/intent/work-context.js +22 -0
- package/dist/src/intent/work-context.js.map +1 -0
- package/dist/src/output/command-receipt.js +16 -9
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/portable/commands.d.ts +18 -8
- package/dist/src/portable/commands.js +46 -562
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/historical-materialization.d.ts +22 -0
- package/dist/src/portable/historical-materialization.js +124 -0
- package/dist/src/portable/historical-materialization.js.map +1 -0
- package/dist/src/portable/materializer.d.ts +8 -0
- package/dist/src/portable/materializer.js +75 -44
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/recovery-layout.d.ts +17 -0
- package/dist/src/portable/recovery-layout.js +55 -0
- package/dist/src/portable/recovery-layout.js.map +1 -0
- package/dist/src/portable/recovery.d.ts +143 -0
- package/dist/src/portable/recovery.js +339 -0
- package/dist/src/portable/recovery.js.map +1 -0
- package/dist/src/portable/writer.d.ts +64 -0
- package/dist/src/portable/writer.js +316 -0
- package/dist/src/portable/writer.js.map +1 -0
- package/dist/src/recovery/coordinator.d.ts +137 -0
- package/dist/src/recovery/coordinator.js +1198 -0
- package/dist/src/recovery/coordinator.js.map +1 -0
- package/dist/src/recovery/demand.d.ts +44 -0
- package/dist/src/recovery/demand.js +178 -0
- package/dist/src/recovery/demand.js.map +1 -0
- package/dist/src/recovery/joint-coordinator.d.ts +24 -0
- package/dist/src/recovery/joint-coordinator.js +770 -0
- package/dist/src/recovery/joint-coordinator.js.map +1 -0
- package/dist/src/recovery/joint-demand.d.ts +68 -0
- package/dist/src/recovery/joint-demand.js +166 -0
- package/dist/src/recovery/joint-demand.js.map +1 -0
- package/dist/src/recovery/receipt.d.ts +81 -0
- package/dist/src/recovery/receipt.js +2 -0
- package/dist/src/recovery/receipt.js.map +1 -0
- package/dist/src/recovery/retirement.d.ts +45 -0
- package/dist/src/recovery/retirement.js +366 -0
- package/dist/src/recovery/retirement.js.map +1 -0
- package/dist/src/recovery/status.d.ts +108 -0
- package/dist/src/recovery/status.js +417 -0
- package/dist/src/recovery/status.js.map +1 -0
- package/dist/src/recovery/types.d.ts +86 -0
- package/dist/src/recovery/types.js +2 -0
- package/dist/src/recovery/types.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +2 -1
- package/dist/src/review/review-profile.js +15 -15
- package/dist/src/review/review-profile.js.map +1 -1
- package/dist/src/review/review-selection.d.ts +1 -2
- package/dist/src/review/review-selection.js +40 -114
- package/dist/src/review/review-selection.js.map +1 -1
- package/dist/src/review/review-types.d.ts +4 -4
- package/dist/src/review/review-types.js.map +1 -1
- package/dist/src/review/skill-materialization.js +15 -6
- package/dist/src/review/skill-materialization.js.map +1 -1
- package/dist/src/review/source-binding.d.ts +14 -4
- package/dist/src/review/source-binding.js +62 -117
- package/dist/src/review/source-binding.js.map +1 -1
- package/dist/src/tui/agent-management.d.ts +23 -0
- package/dist/src/tui/agent-management.js +217 -0
- package/dist/src/tui/agent-management.js.map +1 -0
- package/dist/src/tui/agent-settings.d.ts +44 -0
- package/dist/src/tui/agent-settings.js +98 -0
- package/dist/src/tui/agent-settings.js.map +1 -0
- package/dist/src/tui/components.d.ts +3 -0
- package/dist/src/tui/components.js +10 -1
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-references.d.ts +6 -3
- package/dist/src/tui/config-references.js +17 -12
- package/dist/src/tui/config-references.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -2
- package/dist/src/tui/console-app-v4.js +1533 -1047
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +2 -1
- package/dist/src/tui/controller-runtime.js +457 -1634
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +131 -81
- package/dist/src/tui/controller.js +307 -227
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +2 -2
- package/dist/src/tui/entity-cards.js +98 -20
- package/dist/src/tui/entity-cards.js.map +1 -1
- package/dist/src/tui/glossary.d.ts +2 -0
- package/dist/src/tui/glossary.js +20 -0
- package/dist/src/tui/glossary.js.map +1 -1
- package/dist/src/tui/model.d.ts +28 -10
- package/dist/src/tui/model.js +41 -41
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-review-planner.d.ts +2 -0
- package/dist/src/tui/operation-review-planner.js +58 -122
- package/dist/src/tui/operation-review-planner.js.map +1 -1
- package/dist/src/tui/plugin-content.js +7 -4
- package/dist/src/tui/plugin-content.js.map +1 -1
- package/dist/src/tui/plugin-detail.d.ts +22 -3
- package/dist/src/tui/plugin-detail.js +113 -26
- package/dist/src/tui/plugin-detail.js.map +1 -1
- package/dist/src/tui/reader.d.ts +1 -0
- package/dist/src/tui/reader.js +3 -1
- package/dist/src/tui/reader.js.map +1 -1
- package/dist/src/tui/router.d.ts +23 -33
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/scoped-flow.d.ts +45 -0
- package/dist/src/tui/scoped-flow.js +96 -0
- package/dist/src/tui/scoped-flow.js.map +1 -0
- package/dist/src/tui/scoped-inventory.d.ts +122 -0
- package/dist/src/tui/scoped-inventory.js +225 -0
- package/dist/src/tui/scoped-inventory.js.map +1 -0
- package/dist/src/tui/screen-registry.d.ts +25 -25
- package/dist/src/tui/screen-registry.js +3 -7
- package/dist/src/tui/screen-registry.js.map +1 -1
- package/dist/src/tui/session-console-app.js +1 -1
- package/dist/src/tui/session-console-app.js.map +1 -1
- package/dist/src/tui/session-host.js +12 -2
- package/dist/src/tui/session-host.js.map +1 -1
- package/dist/src/types.d.ts +15 -12
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.js +2 -0
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.js +13 -6
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +6 -3
- package/dist/src/utils/artifact-renderer.js +27 -32
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +1 -0
- package/dist/src/utils/artifact-request.js +22 -12
- package/dist/src/utils/artifact-request.js.map +1 -1
- package/dist/src/utils/artifact-sources.d.ts +2 -2
- package/dist/src/utils/bundle-layout.js +18 -16
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.js +2 -0
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-source-references.d.ts +5 -0
- package/dist/src/utils/catalog-source-references.js +42 -57
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
- package/dist/src/utils/claude-native-render-receipt.js +26 -0
- package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
- package/dist/src/utils/claude-skills-root.js +14 -3
- package/dist/src/utils/claude-skills-root.js.map +1 -1
- package/dist/src/utils/codex-manager.js +6 -12
- package/dist/src/utils/codex-manager.js.map +1 -1
- package/dist/src/utils/codex-native-observation.js +29 -13
- package/dist/src/utils/codex-native-observation.js.map +1 -1
- package/dist/src/utils/codex-reconciler.d.ts +2 -5
- package/dist/src/utils/codex-reconciler.js +25 -1
- package/dist/src/utils/codex-reconciler.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.js +5 -3
- package/dist/src/utils/codex-surface-analysis.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +14 -2
- package/dist/src/utils/local-marketplace.js +209 -42
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/managed-command-evidence.js +1 -1
- package/dist/src/utils/managed-command-evidence.js.map +1 -1
- package/dist/src/utils/managed-command-materialization.d.ts +4 -0
- package/dist/src/utils/managed-command-materialization.js +15 -6
- package/dist/src/utils/managed-command-materialization.js.map +1 -1
- package/dist/src/utils/managed-command-store.d.ts +15 -0
- package/dist/src/utils/managed-command-store.js +73 -8
- package/dist/src/utils/managed-command-store.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +2 -0
- package/dist/src/utils/native-cli.js +22 -0
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/native-demand.d.ts +10 -0
- package/dist/src/utils/native-demand.js +4 -0
- package/dist/src/utils/native-demand.js.map +1 -0
- package/dist/src/utils/ownership.js +2 -0
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/paths.js +1 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/platform-support.d.ts +1 -1
- package/dist/src/utils/platform-support.js +1 -1
- package/dist/src/utils/platform-support.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +3 -0
- package/dist/src/utils/plugin-config-impact.js +115 -273
- package/dist/src/utils/plugin-config-impact.js.map +1 -1
- package/dist/src/utils/plugin-config-references.d.ts +11 -2
- package/dist/src/utils/plugin-config-references.js +85 -90
- package/dist/src/utils/plugin-config-references.js.map +1 -1
- package/dist/src/utils/plugin-config-write.d.ts +58 -1
- package/dist/src/utils/plugin-config-write.js +756 -809
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +47 -46
- package/dist/src/utils/plugin-config.js +292 -833
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-scripts.d.ts +11 -6
- package/dist/src/utils/plugin-scripts.js +52 -35
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/runtime-config.js +5 -4
- package/dist/src/utils/runtime-config.js.map +1 -1
- package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
- package/dist/src/utils/runtime-skill-closure.js.map +1 -1
- package/dist/src/utils/state-paths.d.ts +8 -3
- package/dist/src/utils/state-paths.js +53 -16
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/windows-npm-shim.d.ts +2 -0
- package/dist/src/utils/windows-npm-shim.js +5 -7
- package/dist/src/utils/windows-npm-shim.js.map +1 -1
- package/dist/vitest.config.js +1 -0
- package/dist/vitest.config.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/commands/sync-command.d.ts +0 -40
- package/dist/src/commands/sync-command.js +0 -254
- package/dist/src/commands/sync-command.js.map +0 -1
- package/dist/src/portable/skills-cli.d.ts +0 -51
- package/dist/src/portable/skills-cli.js +0 -202
- package/dist/src/portable/skills-cli.js.map +0 -1
- package/dist/src/tui/project-inventory.d.ts +0 -68
- package/dist/src/tui/project-inventory.js +0 -203
- package/dist/src/tui/project-inventory.js.map +0 -1
- package/dist/src/utils/plugin-intent-state.d.ts +0 -49
- package/dist/src/utils/plugin-intent-state.js +0 -320
- package/dist/src/utils/plugin-intent-state.js.map +0 -1
|
@@ -1,65 +1,37 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
|
+
import { resolveWorkContext } from '../intent/work-context.js';
|
|
2
3
|
import * as nodeFs from 'node:fs/promises';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import fs from 'fs-extra';
|
|
5
|
-
import { canonicalizePluginConfigValues, formatPluginConfigIssue, getPluginConfigDependencyKeys, getPluginConfigDependencyFields, matchPluginConfigPreset, redactPluginConfigValue, stablePluginConfigStringify, validatePluginConfigCandidate, validatePluginConfigDocument, validatePluginConfigValue as validateCorePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
6
|
-
import { assertTestReadablePath, assertTestWritablePath, } from './test-path-fence.js';
|
|
6
|
+
import { canonicalizePluginConfigValues, compilePluginConfigDefinition, PLATFORM_TARGETS, formatPluginConfigIssue, getPluginConfigDependencyKeys, getPluginConfigDependencyFields, matchPluginConfigPreset, redactPluginConfigValue, stablePluginConfigStringify, validatePluginConfigCandidate, validatePluginConfigDocument, validatePluginConfigValue as validateCorePluginConfigValue, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
7
7
|
import { canonicalizePathThroughExistingAncestor, resolveAwHome, resolveRepoRootSync, } from './paths.js';
|
|
8
8
|
import { assertRealFileParentWithinRoot, assertSafeFileMutationPath, atomicReplaceSafeFile, } from './safe-fs.js';
|
|
9
|
-
import { assertProjectSharedConfigPathVisible, PROJECT_CONFIG_SHAREABILITY_UNVERIFIED_WARNING, } from './project-boundary.js';
|
|
10
9
|
import { pluginConfigHasExternalReferences, resolvePluginConfigReferenceContext, } from './plugin-config-references.js';
|
|
11
10
|
export { resolvePluginConfigReferenceContext } from './plugin-config-references.js';
|
|
12
11
|
export function codexNativeRefreshKeys(manifest) {
|
|
13
12
|
return new Set((manifest.artifacts?.targets ?? []).flatMap((target) => target.platform === 'codex'
|
|
14
13
|
&& target.target === 'codex.skill'
|
|
15
|
-
&& target.delivery?.mode === 'codex-native'
|
|
16
14
|
? target.refreshKeys
|
|
17
15
|
: []));
|
|
18
16
|
}
|
|
19
|
-
function readPluginConfigBinding(value
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
&& value.logicalIdentity.trim().length > 0
|
|
33
|
-
&& Object.keys(value).every((key) => ['kind', 'logicalIdentity'].includes(key))
|
|
34
|
-
? { kind: 'catalog', logicalIdentity: value.logicalIdentity }
|
|
35
|
-
: null;
|
|
36
|
-
}
|
|
37
|
-
const PLUGIN_CONFIG_ENVELOPE_V5_KEYS = new Set([
|
|
38
|
-
'schemaVersion',
|
|
39
|
-
'sourceId',
|
|
40
|
-
'pluginName',
|
|
41
|
-
'binding',
|
|
42
|
-
'values',
|
|
43
|
-
]);
|
|
44
|
-
function readPluginConfigEnvelopeV5(value, scope) {
|
|
45
|
-
if (!isRecord(value)
|
|
46
|
-
|| value.schemaVersion !== 5
|
|
47
|
-
|| typeof value.sourceId !== 'string'
|
|
48
|
-
|| typeof value.pluginName !== 'string'
|
|
49
|
-
|| !isRecord(value.values)
|
|
50
|
-
|| Object.keys(value).some((key) => !PLUGIN_CONFIG_ENVELOPE_V5_KEYS.has(key))) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
const binding = readPluginConfigBinding(value.binding, scope);
|
|
54
|
-
if (!binding)
|
|
17
|
+
function readPluginConfigBinding(value) {
|
|
18
|
+
return isRecord(value) && value.kind === 'source'
|
|
19
|
+
&& typeof value.sourceIdentity === 'string'
|
|
20
|
+
&& /^sha256:[0-9a-f]{64}$/u.test(value.sourceIdentity)
|
|
21
|
+
&& Object.keys(value).every((key) => ['kind', 'sourceIdentity'].includes(key))
|
|
22
|
+
? { kind: 'source', sourceIdentity: value.sourceIdentity } : null;
|
|
23
|
+
}
|
|
24
|
+
function readPluginConfigEnvelopeV6(value, _scope) {
|
|
25
|
+
if (!isRecord(value) || value.schemaVersion !== 6
|
|
26
|
+
|| typeof value.sourceId !== 'string' || typeof value.pluginName !== 'string'
|
|
27
|
+
|| !isRecord(value.common) || !isRecord(value.platforms)
|
|
28
|
+
|| Object.keys(value).some((key) => !['schemaVersion', 'sourceId', 'pluginName', 'binding', 'common', 'platforms'].includes(key))
|
|
29
|
+
|| Object.keys(value.platforms).some((key) => !PLATFORM_TARGETS.includes(key)))
|
|
55
30
|
return null;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
binding,
|
|
61
|
-
values: value.values,
|
|
62
|
-
};
|
|
31
|
+
const binding = readPluginConfigBinding(value.binding);
|
|
32
|
+
return binding ? { schemaVersion: 6, sourceId: value.sourceId, pluginName: value.pluginName,
|
|
33
|
+
binding, common: value.common,
|
|
34
|
+
platforms: value.platforms } : null;
|
|
63
35
|
}
|
|
64
36
|
export class PluginConfigResetError extends Error {
|
|
65
37
|
code;
|
|
@@ -252,10 +224,11 @@ async function calculateConfigSchemaManifestDigestForPlugin(plugin) {
|
|
|
252
224
|
hash.update(JSON.stringify(plugin.manifest));
|
|
253
225
|
return `sha256:${hash.digest('hex')}`;
|
|
254
226
|
}
|
|
255
|
-
function createConfigSchemaEvidenceFromCurrentCatalog(plugin, manifestDigest) {
|
|
227
|
+
function createConfigSchemaEvidenceFromCurrentCatalog(plugin, manifestDigest, platform) {
|
|
256
228
|
return {
|
|
257
229
|
source: 'currentCatalog',
|
|
258
|
-
identity: createPluginConfigSchemaIdentity(plugin.manifest),
|
|
230
|
+
identity: createPluginConfigSchemaIdentity(plugin.manifest, platform),
|
|
231
|
+
platform,
|
|
259
232
|
pluginVersion: plugin.manifest.version,
|
|
260
233
|
manifestDigest,
|
|
261
234
|
};
|
|
@@ -283,41 +256,16 @@ export function resolveAwProjectRoot(cwd) {
|
|
|
283
256
|
return path.resolve(resolveRepoRootSync(cwd));
|
|
284
257
|
}
|
|
285
258
|
function resolvePluginConfigAllowedRoot(options) {
|
|
286
|
-
return options.
|
|
287
|
-
? resolveAwHome(options.env)
|
|
288
|
-
: path.join(resolveAwProjectRoot(options.cwd), '.agent-workflows');
|
|
259
|
+
return resolveAwHome(options.env);
|
|
289
260
|
}
|
|
261
|
+
/** Scope selects source context. All config values belong to the user document. */
|
|
290
262
|
export function resolvePluginConfigStorePath(options) {
|
|
291
263
|
const pluginName = assertSafePluginConfigSegment(options.pluginName);
|
|
292
|
-
const sourceId = options.sourceId === undefined
|
|
293
|
-
|
|
294
|
-
: assertSafePluginConfigSegment(options.sourceId);
|
|
295
|
-
const root = options.scope === 'user'
|
|
296
|
-
? resolveAwHome(options.env)
|
|
297
|
-
: path.join(resolveAwProjectRoot(options.cwd), '.agent-workflows');
|
|
298
|
-
const filePath = path.join(root, CONFIG_DIR, ...(sourceId ? [sourceId] : []), pluginName, CONFIG_FILE);
|
|
299
|
-
if (options.scope !== 'project') {
|
|
300
|
-
return filePath;
|
|
301
|
-
}
|
|
302
|
-
if (options.access === 'read') {
|
|
303
|
-
return assertTestReadablePath(filePath, 'project plugin config store (.agent-workflows/plugin-config)', '用 registerReadonlyFixtureRoot() 或 runWithRepositoryCwdAsReadOnly() 登记只读 project fixture 根;需要写入时改用 withSafeTestCwd() 或 registerWritableStateRoot()。');
|
|
304
|
-
}
|
|
305
|
-
if (options.access === 'write') {
|
|
306
|
-
return assertTestWritablePath(filePath, 'project plugin config store (.agent-workflows/plugin-config)', '用 withSafeTestCwd() 切到测试可写 cwd,或通过 registerWritableStateRoot() 登记 project fixture 根。');
|
|
307
|
-
}
|
|
308
|
-
return filePath;
|
|
264
|
+
const sourceId = options.sourceId === undefined ? undefined : assertSafePluginConfigSegment(options.sourceId);
|
|
265
|
+
return path.join(resolveAwHome(options.env), CONFIG_DIR, ...(sourceId ? [sourceId] : []), pluginName, CONFIG_FILE);
|
|
309
266
|
}
|
|
310
267
|
export function resolvePluginConfigStoreTargets(options) {
|
|
311
|
-
|
|
312
|
-
return [{
|
|
313
|
-
storeKind: 'user',
|
|
314
|
-
path: resolvePluginConfigStorePath(options),
|
|
315
|
-
}];
|
|
316
|
-
}
|
|
317
|
-
return [{
|
|
318
|
-
storeKind: 'project',
|
|
319
|
-
path: resolvePluginConfigStorePath(options),
|
|
320
|
-
}];
|
|
268
|
+
return [{ storeKind: 'user', path: resolvePluginConfigStorePath(options) }];
|
|
321
269
|
}
|
|
322
270
|
function isPluginConfigScalar(value) {
|
|
323
271
|
if (typeof value === 'string' || typeof value === 'boolean')
|
|
@@ -417,80 +365,75 @@ function validateStoreValues(options) {
|
|
|
417
365
|
? invalidStore(options.filePath, 'invalidValues', issues.join(' '), options.rawDigest)
|
|
418
366
|
: readyStore(options.filePath, values, options.rawDigest);
|
|
419
367
|
}
|
|
420
|
-
|
|
368
|
+
function documentStore(options) {
|
|
369
|
+
const common = validateStoreValues({ filePath: options.filePath, values: options.common, rawDigest: options.rawDigest });
|
|
370
|
+
if (common.state === 'invalid')
|
|
371
|
+
return common;
|
|
372
|
+
if (!isRecord(options.platforms) || Object.keys(options.platforms).some((key) => !PLATFORM_TARGETS.includes(key))) {
|
|
373
|
+
return invalidStore(options.filePath, 'invalidValues', 'The platforms field must contain known platform objects.', options.rawDigest);
|
|
374
|
+
}
|
|
375
|
+
const platforms = {};
|
|
376
|
+
for (const [platform, values] of Object.entries(options.platforms)) {
|
|
377
|
+
const store = validateStoreValues({ filePath: options.filePath, values, rawDigest: options.rawDigest });
|
|
378
|
+
if (store.state === 'invalid')
|
|
379
|
+
return store;
|
|
380
|
+
platforms[platform] = store.values;
|
|
381
|
+
}
|
|
382
|
+
return { ...common, common: common.values, platforms, ...(options.platform ? { platform: options.platform } : {}),
|
|
383
|
+
values: options.platform ? platforms[options.platform] ?? {} : common.values };
|
|
384
|
+
}
|
|
385
|
+
export function pluginConfigStoreForProfile(store, platform) {
|
|
386
|
+
return { ...store, platform, values: platform ? store.platforms?.[platform] ?? {} : store.common ?? store.values };
|
|
387
|
+
}
|
|
388
|
+
/** Replace one layer in memory without changing any other profile. */
|
|
389
|
+
export function replacePluginConfigStoreLayer(store, values, platform) {
|
|
390
|
+
const common = platform ? store.common ?? store.values : canonicalizePluginConfigValues(values);
|
|
391
|
+
const platforms = { ...store.platforms };
|
|
392
|
+
if (platform) {
|
|
393
|
+
if (Object.keys(values).length)
|
|
394
|
+
platforms[platform] = canonicalizePluginConfigValues(values);
|
|
395
|
+
else
|
|
396
|
+
delete platforms[platform];
|
|
397
|
+
}
|
|
398
|
+
const exists = Object.keys(common).length > 0 || Object.keys(platforms).length > 0;
|
|
399
|
+
return { ...store, common, platforms, platform, values: platform ? platforms[platform] ?? {} : common,
|
|
400
|
+
exists, state: exists ? 'ready' : 'missing', issues: [], rawDigest: undefined };
|
|
401
|
+
}
|
|
402
|
+
async function readStoreFile(filePath, platform) {
|
|
421
403
|
const parsed = await parseStoreJson(filePath);
|
|
422
404
|
if (parsed.state === 'invalidJson')
|
|
423
405
|
return parsed.store;
|
|
424
|
-
|
|
406
|
+
const raw = parsed.value;
|
|
407
|
+
if (!isRecord(raw) || raw.schemaVersion !== 6
|
|
408
|
+
|| Object.keys(raw).some((key) => !['schemaVersion', 'common', 'platforms'].includes(key))) {
|
|
409
|
+
return invalidStore(filePath, 'schemaVersionUnsupported', 'Only plugin config store schemaVersion 6 is supported. Reset the old document, then save the required values.', parsed.rawDigest);
|
|
410
|
+
}
|
|
411
|
+
return documentStore({ filePath, common: raw.common, platforms: raw.platforms, rawDigest: parsed.rawDigest, platform });
|
|
425
412
|
}
|
|
426
413
|
async function readNamespacedStoreFile(options) {
|
|
427
414
|
const parsed = await parseStoreJson(options.filePath);
|
|
428
415
|
if (parsed.state === 'invalidJson')
|
|
429
416
|
return parsed.store;
|
|
430
|
-
const
|
|
431
|
-
if (!
|
|
432
|
-
return invalidStore(options.filePath, 'schemaVersionUnsupported', 'Only plugin config store schemaVersion
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return invalidStore(options.filePath, 'schemaVersionUnsupported', 'The plugin config store schemaVersion 5 envelope is invalid.', parsed.rawDigest);
|
|
417
|
+
const envelope = readPluginConfigEnvelopeV6(parsed.value);
|
|
418
|
+
if (!envelope)
|
|
419
|
+
return invalidStore(options.filePath, 'schemaVersionUnsupported', 'Only a valid plugin config store schemaVersion 6 is supported. Reset the old document, then save the required values.', parsed.rawDigest);
|
|
420
|
+
if (envelope.sourceId !== options.sourceId || envelope.pluginName !== options.pluginName
|
|
421
|
+
|| !options.sourceIdentity || envelope.binding.sourceIdentity !== options.sourceIdentity) {
|
|
422
|
+
return invalidStore(options.filePath, 'identityMismatch', 'The user store is not bound to the requested source and plugin identity.', parsed.rawDigest);
|
|
437
423
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return invalidStore(options.filePath, 'identityMismatch', 'The store identity does not match its source and namespaced path.', parsed.rawDigest);
|
|
441
|
-
}
|
|
442
|
-
const binding = envelope.binding;
|
|
443
|
-
const matchesBinding = options.scope === 'user'
|
|
444
|
-
? binding?.kind === 'source'
|
|
445
|
-
&& typeof options.sourceIdentity === 'string'
|
|
446
|
-
&& binding.sourceIdentity === options.sourceIdentity
|
|
447
|
-
: binding?.kind === 'catalog'
|
|
448
|
-
&& typeof options.logicalIdentity === 'string'
|
|
449
|
-
&& binding.logicalIdentity === options.logicalIdentity;
|
|
450
|
-
if (!matchesBinding) {
|
|
451
|
-
return invalidStore(options.filePath, 'identityMismatch', options.scope === 'user'
|
|
452
|
-
? 'The user store is not bound to the requested source identity.'
|
|
453
|
-
: 'The project store is not bound to the requested Catalog logical identity.', parsed.rawDigest);
|
|
454
|
-
}
|
|
455
|
-
return validateStoreValues({
|
|
456
|
-
filePath: options.filePath,
|
|
457
|
-
values: envelope.values,
|
|
458
|
-
rawDigest: parsed.rawDigest,
|
|
459
|
-
});
|
|
424
|
+
return documentStore({ filePath: options.filePath, common: envelope.common, platforms: envelope.platforms,
|
|
425
|
+
rawDigest: parsed.rawDigest, platform: options.platform });
|
|
460
426
|
}
|
|
461
427
|
function namespacedConfigContent(options) {
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const binding = options.scope === 'user'
|
|
466
|
-
? options.sourceIdentity
|
|
467
|
-
? { kind: 'source', sourceIdentity: options.sourceIdentity }
|
|
468
|
-
: (() => { throw new Error('sourceChanged: user plugin config requires a source identity.'); })()
|
|
469
|
-
: options.logicalIdentity
|
|
470
|
-
? { kind: 'catalog', logicalIdentity: options.logicalIdentity }
|
|
471
|
-
: (() => { throw new Error('catalogUnavailable: project plugin config requires verified Catalog logical identity evidence.'); })();
|
|
472
|
-
const envelope = {
|
|
473
|
-
schemaVersion: 5,
|
|
474
|
-
sourceId: options.sourceId,
|
|
475
|
-
pluginName: options.pluginName,
|
|
476
|
-
binding,
|
|
477
|
-
values,
|
|
478
|
-
};
|
|
479
|
-
return `${JSON.stringify(envelope, null, 2)}\n`;
|
|
480
|
-
}
|
|
481
|
-
async function assertNoLegacyConfigCoexistence(options) {
|
|
428
|
+
const document = { schemaVersion: 6, common: canonicalizePluginConfigValues(options.common),
|
|
429
|
+
platforms: Object.fromEntries(Object.entries(options.platforms).sort(([a], [b]) => a.localeCompare(b, 'en'))
|
|
430
|
+
.map(([platform, values]) => [platform, canonicalizePluginConfigValues(values)])) };
|
|
482
431
|
if (!options.sourceId)
|
|
483
|
-
return
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
env: options.env,
|
|
489
|
-
...(options.scope === 'project' ? { access: 'read' } : {}),
|
|
490
|
-
});
|
|
491
|
-
if (await lstatIfExists(legacyPath)) {
|
|
492
|
-
throw new Error('legacyStateUnsupported: legacy and namespaced plugin config stores cannot coexist.');
|
|
493
|
-
}
|
|
432
|
+
return `${JSON.stringify(document, null, 2)}\n`;
|
|
433
|
+
if (!options.sourceIdentity)
|
|
434
|
+
throw new Error('sourceChanged: user plugin config requires a source identity.');
|
|
435
|
+
return `${JSON.stringify({ ...document, sourceId: options.sourceId, pluginName: options.pluginName,
|
|
436
|
+
binding: { kind: 'source', sourceIdentity: options.sourceIdentity } }, null, 2)}\n`;
|
|
494
437
|
}
|
|
495
438
|
export async function readPluginConfigStore(options) {
|
|
496
439
|
const filePath = resolvePluginConfigStorePath({
|
|
@@ -527,8 +470,9 @@ export async function readPluginConfigStore(options) {
|
|
|
527
470
|
sourceIdentity: options.sourceIdentity,
|
|
528
471
|
logicalIdentity: options.logicalIdentity,
|
|
529
472
|
pluginName: options.pluginName,
|
|
473
|
+
platform: options.platform,
|
|
530
474
|
})
|
|
531
|
-
: readStoreFile(filePath);
|
|
475
|
+
: readStoreFile(filePath, options.platform);
|
|
532
476
|
}
|
|
533
477
|
export async function readNamespacedPluginConfigStoreIdentity(options) {
|
|
534
478
|
const filePath = resolvePluginConfigStorePath({
|
|
@@ -541,13 +485,13 @@ export async function readNamespacedPluginConfigStoreIdentity(options) {
|
|
|
541
485
|
if (parsed.state === 'invalidJson') {
|
|
542
486
|
throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store.`);
|
|
543
487
|
}
|
|
544
|
-
const envelope =
|
|
488
|
+
const envelope = readPluginConfigEnvelopeV6(parsed.value, options.scope);
|
|
545
489
|
if (!envelope
|
|
546
490
|
|| envelope.sourceId !== options.sourceId
|
|
547
491
|
|| envelope.pluginName !== options.pluginName) {
|
|
548
492
|
throw new Error('schemaVersionUnsupported: invalid namespaced plugin config store.');
|
|
549
493
|
}
|
|
550
|
-
const store =
|
|
494
|
+
const store = documentStore({ filePath, common: envelope.common, platforms: envelope.platforms, platform: options.platform });
|
|
551
495
|
assertReadyPluginConfigStore(store);
|
|
552
496
|
return {
|
|
553
497
|
binding: envelope.binding,
|
|
@@ -578,29 +522,19 @@ async function listConfigStoreDirectoryPluginNames(storeRoot) {
|
|
|
578
522
|
return pluginNames;
|
|
579
523
|
}
|
|
580
524
|
export async function listPluginConfigStorePluginNames(options) {
|
|
581
|
-
const
|
|
582
|
-
const storeRoots = options.scope === 'user'
|
|
583
|
-
? [path.join(resolveAwHome(options.env), CONFIG_DIR)]
|
|
584
|
-
: options.scope === 'project'
|
|
585
|
-
? [path.join(projectRoot, '.agent-workflows', CONFIG_DIR)]
|
|
586
|
-
: [
|
|
587
|
-
path.join(resolveAwHome(options.env), CONFIG_DIR),
|
|
588
|
-
path.join(projectRoot, '.agent-workflows', CONFIG_DIR),
|
|
589
|
-
];
|
|
525
|
+
const storeRoots = [path.join(resolveAwHome(options.env), CONFIG_DIR)];
|
|
590
526
|
const pluginNames = (await Promise.all(storeRoots.map((storeRoot) => listConfigStoreDirectoryPluginNames(storeRoot)))).flat();
|
|
591
527
|
return [...new Set(pluginNames)].sort((left, right) => left.localeCompare(right));
|
|
592
528
|
}
|
|
593
529
|
export async function listNamespacedPluginConfigStoreIdentities(options) {
|
|
594
|
-
const boundaryRoot = options.
|
|
595
|
-
? resolveAwHome(options.env)
|
|
596
|
-
: resolveAwProjectRoot(options.cwd);
|
|
530
|
+
const boundaryRoot = resolveAwHome(options.env);
|
|
597
531
|
if (options.scope === 'user') {
|
|
598
532
|
await canonicalizePathThroughExistingAncestor(boundaryRoot);
|
|
599
533
|
}
|
|
600
534
|
else {
|
|
601
535
|
await fs.realpath(boundaryRoot);
|
|
602
536
|
}
|
|
603
|
-
const storeRoot = path.join(boundaryRoot,
|
|
537
|
+
const storeRoot = path.join(boundaryRoot, CONFIG_DIR);
|
|
604
538
|
if (!await inspectConfigInventoryStoreRoot(boundaryRoot, storeRoot))
|
|
605
539
|
return [];
|
|
606
540
|
const output = [];
|
|
@@ -649,16 +583,14 @@ export async function listNamespacedPluginConfigStoreIdentities(options) {
|
|
|
649
583
|
return output.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
|
|
650
584
|
}
|
|
651
585
|
export async function listNamespacedPluginConfigStores(options) {
|
|
652
|
-
const boundaryRoot = options.
|
|
653
|
-
? resolveAwHome(options.env)
|
|
654
|
-
: resolveAwProjectRoot(options.cwd);
|
|
586
|
+
const boundaryRoot = resolveAwHome(options.env);
|
|
655
587
|
if (options.scope === 'user') {
|
|
656
588
|
await canonicalizePathThroughExistingAncestor(boundaryRoot);
|
|
657
589
|
}
|
|
658
590
|
else {
|
|
659
591
|
await fs.realpath(boundaryRoot);
|
|
660
592
|
}
|
|
661
|
-
const storeRoot = path.join(boundaryRoot,
|
|
593
|
+
const storeRoot = path.join(boundaryRoot, CONFIG_DIR);
|
|
662
594
|
const output = [];
|
|
663
595
|
if (!await inspectConfigInventoryStoreRoot(boundaryRoot, storeRoot))
|
|
664
596
|
return output;
|
|
@@ -684,13 +616,13 @@ export async function listNamespacedPluginConfigStores(options) {
|
|
|
684
616
|
if (parsed.state === 'invalidJson') {
|
|
685
617
|
throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store "${filePath}".`);
|
|
686
618
|
}
|
|
687
|
-
const envelope =
|
|
619
|
+
const envelope = readPluginConfigEnvelopeV6(parsed.value, options.scope);
|
|
688
620
|
if (!envelope
|
|
689
621
|
|| envelope.sourceId !== sourceId
|
|
690
622
|
|| envelope.pluginName !== pluginName) {
|
|
691
623
|
throw new Error(`schemaVersionUnsupported: invalid namespaced plugin config store "${filePath}".`);
|
|
692
624
|
}
|
|
693
|
-
const store =
|
|
625
|
+
const store = documentStore({ filePath, common: envelope.common, platforms: envelope.platforms });
|
|
694
626
|
assertReadyPluginConfigStore(store);
|
|
695
627
|
output.push({
|
|
696
628
|
sourceId,
|
|
@@ -704,130 +636,51 @@ export async function listNamespacedPluginConfigStores(options) {
|
|
|
704
636
|
return output.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
|
|
705
637
|
}
|
|
706
638
|
export async function writePluginConfigStore(options) {
|
|
707
|
-
await
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
...(options.scope === 'project' ? { access: 'write' } : {}),
|
|
711
|
-
});
|
|
712
|
-
const allowedRoot = resolvePluginConfigAllowedRoot(options);
|
|
713
|
-
const validated = validateStoreValues({ filePath, values: options.values });
|
|
639
|
+
const current = await readPluginConfigStore(options);
|
|
640
|
+
assertReadyPluginConfigStore(current);
|
|
641
|
+
const validated = validateStoreValues({ filePath: current.path, values: options.values });
|
|
714
642
|
assertReadyPluginConfigStore(validated);
|
|
715
|
-
|
|
716
|
-
await withConfigFileMutation({
|
|
717
|
-
filePath,
|
|
718
|
-
allowedRoot,
|
|
719
|
-
action: async () => {
|
|
720
|
-
if (!options.sourceId && Object.keys(validated.values).length === 0) {
|
|
721
|
-
return atomicRemoveConfigFile({ filePath, allowedRoot });
|
|
722
|
-
}
|
|
723
|
-
if (options.sourceId && await lstatIfExists(filePath)) {
|
|
724
|
-
const current = await readNamespacedStoreFile({
|
|
725
|
-
filePath,
|
|
726
|
-
sourceId: options.sourceId,
|
|
727
|
-
scope: options.scope,
|
|
728
|
-
sourceIdentity: options.sourceIdentity,
|
|
729
|
-
logicalIdentity: options.logicalIdentity,
|
|
730
|
-
pluginName: options.pluginName,
|
|
731
|
-
});
|
|
732
|
-
assertReadyPluginConfigStore(current);
|
|
733
|
-
}
|
|
734
|
-
if (options.scope === 'project') {
|
|
735
|
-
const visibility = await assertProjectSharedConfigPathVisible({
|
|
736
|
-
cwd: options.cwd,
|
|
737
|
-
targetPath: filePath,
|
|
738
|
-
env: options.env,
|
|
739
|
-
});
|
|
740
|
-
if (visibility?.state === 'unverified') {
|
|
741
|
-
warnings.push(PROJECT_CONFIG_SHAREABILITY_UNVERIFIED_WARNING);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
return atomicReplaceConfigFile({
|
|
745
|
-
filePath,
|
|
746
|
-
allowedRoot,
|
|
747
|
-
content: namespacedConfigContent({ ...options, values: validated.values }),
|
|
748
|
-
mode: 0o600,
|
|
749
|
-
});
|
|
750
|
-
},
|
|
751
|
-
});
|
|
752
|
-
return {
|
|
753
|
-
pluginName: options.pluginName,
|
|
754
|
-
scope: options.scope,
|
|
755
|
-
path: filePath,
|
|
756
|
-
warnings,
|
|
757
|
-
};
|
|
643
|
+
return commitPluginConfigLayer(options, current, validated.values);
|
|
758
644
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const filePath = resolvePluginConfigStorePath({
|
|
762
|
-
...options,
|
|
763
|
-
...(options.scope === 'project' ? { access: 'write' } : {}),
|
|
764
|
-
});
|
|
645
|
+
async function commitPluginConfigLayer(options, current, values) {
|
|
646
|
+
const next = replacePluginConfigStoreLayer(current, values, options.platform);
|
|
765
647
|
const allowedRoot = resolvePluginConfigAllowedRoot(options);
|
|
648
|
+
await withConfigFileMutation({ filePath: current.path, allowedRoot, action: async () => {
|
|
649
|
+
if (!next.exists)
|
|
650
|
+
await atomicRemoveConfigFile({ filePath: current.path, allowedRoot });
|
|
651
|
+
else
|
|
652
|
+
await atomicReplaceConfigFile({ filePath: current.path, allowedRoot,
|
|
653
|
+
content: namespacedConfigContent({ ...options, common: next.common, platforms: next.platforms }), mode: 0o600 });
|
|
654
|
+
} });
|
|
655
|
+
return { pluginName: options.pluginName, scope: 'user', platform: options.platform, path: current.path, warnings: [] };
|
|
656
|
+
}
|
|
657
|
+
export async function patchPluginConfigStore(options) {
|
|
658
|
+
await options.reviewPreflight?.();
|
|
659
|
+
const current = await readPluginConfigStore(options);
|
|
660
|
+
assertReadyPluginConfigStore(current);
|
|
766
661
|
const removedKeys = options.removeKeys ?? [];
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
logicalIdentity: options.logicalIdentity,
|
|
781
|
-
pluginName: options.pluginName,
|
|
782
|
-
})
|
|
783
|
-
: await readStoreFile(filePath)
|
|
784
|
-
: readyStore(filePath, {});
|
|
785
|
-
assertReadyPluginConfigStore(current);
|
|
786
|
-
if (options.requireExistingRemoveKeys) {
|
|
787
|
-
const missingKey = removedKeys.find((key) => !Object.hasOwn(current.values, key));
|
|
788
|
-
if (missingKey)
|
|
789
|
-
throw new Error(`Unknown config key "${missingKey}".`);
|
|
790
|
-
}
|
|
791
|
-
const next = { ...current.values };
|
|
792
|
-
for (const key of removedKeys) {
|
|
793
|
-
delete next[key];
|
|
794
|
-
}
|
|
795
|
-
for (const [key, value] of Object.entries(options.updates)) {
|
|
796
|
-
next[key] = value;
|
|
797
|
-
}
|
|
798
|
-
if (Object.keys(next).length === 0) {
|
|
799
|
-
await atomicRemoveConfigFile({ filePath, allowedRoot });
|
|
800
|
-
}
|
|
801
|
-
else {
|
|
802
|
-
if (options.scope === 'project') {
|
|
803
|
-
const visibility = await assertProjectSharedConfigPathVisible({
|
|
804
|
-
cwd: options.cwd,
|
|
805
|
-
targetPath: filePath,
|
|
806
|
-
env: options.env,
|
|
807
|
-
});
|
|
808
|
-
if (visibility?.state === 'unverified') {
|
|
809
|
-
warnings.push(PROJECT_CONFIG_SHAREABILITY_UNVERIFIED_WARNING);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
await atomicReplaceConfigFile({
|
|
813
|
-
filePath,
|
|
814
|
-
allowedRoot,
|
|
815
|
-
content: namespacedConfigContent({ ...options, values: next }),
|
|
816
|
-
mode: 0o600,
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
},
|
|
820
|
-
});
|
|
821
|
-
return {
|
|
822
|
-
pluginName: options.pluginName,
|
|
823
|
-
scope: options.scope,
|
|
824
|
-
path: filePath,
|
|
825
|
-
warnings,
|
|
826
|
-
updatedKeys: Object.keys(options.updates),
|
|
827
|
-
removedKeys,
|
|
828
|
-
};
|
|
662
|
+
if (options.requireExistingRemoveKeys) {
|
|
663
|
+
const missing = removedKeys.find((key) => !Object.hasOwn(current.values, key));
|
|
664
|
+
if (missing)
|
|
665
|
+
throw new Error(`Unknown config key "${missing}".`);
|
|
666
|
+
}
|
|
667
|
+
const next = Object.assign(Object.create(null), current.values);
|
|
668
|
+
for (const key of removedKeys)
|
|
669
|
+
delete next[key];
|
|
670
|
+
for (const [key, value] of Object.entries(options.updates))
|
|
671
|
+
next[key] = value;
|
|
672
|
+
const validated = validateStoreValues({ filePath: current.path, values: next });
|
|
673
|
+
assertReadyPluginConfigStore(validated);
|
|
674
|
+
return { ...await commitPluginConfigLayer(options, current, validated.values), updatedKeys: Object.keys(options.updates), removedKeys };
|
|
829
675
|
}
|
|
830
676
|
export async function resetPluginConfigStore(options) {
|
|
677
|
+
return resetPluginConfigStoreTarget(options, false);
|
|
678
|
+
}
|
|
679
|
+
/** Delete the plugin's complete user document, including every platform profile. */
|
|
680
|
+
export async function deletePluginConfigStore(options) {
|
|
681
|
+
return resetPluginConfigStoreTarget(options, true);
|
|
682
|
+
}
|
|
683
|
+
async function resetPluginConfigStoreTarget(options, wholeDocument) {
|
|
831
684
|
const filePath = resolvePluginConfigStorePath({
|
|
832
685
|
...options,
|
|
833
686
|
...(options.scope === 'project' ? { access: 'write' } : {}),
|
|
@@ -846,8 +699,8 @@ export async function resetPluginConfigStore(options) {
|
|
|
846
699
|
if (!stats) {
|
|
847
700
|
return {
|
|
848
701
|
pluginName: options.pluginName,
|
|
849
|
-
scope:
|
|
850
|
-
storeKind:
|
|
702
|
+
scope: 'user',
|
|
703
|
+
storeKind: 'user',
|
|
851
704
|
path: filePath,
|
|
852
705
|
existed: false,
|
|
853
706
|
deleted: false,
|
|
@@ -863,8 +716,8 @@ export async function resetPluginConfigStore(options) {
|
|
|
863
716
|
if (!parentStats) {
|
|
864
717
|
return {
|
|
865
718
|
pluginName: options.pluginName,
|
|
866
|
-
scope:
|
|
867
|
-
storeKind:
|
|
719
|
+
scope: 'user',
|
|
720
|
+
storeKind: 'user',
|
|
868
721
|
path: filePath,
|
|
869
722
|
existed: false,
|
|
870
723
|
deleted: false,
|
|
@@ -879,21 +732,35 @@ export async function resetPluginConfigStore(options) {
|
|
|
879
732
|
if (!target) {
|
|
880
733
|
return {
|
|
881
734
|
pluginName: options.pluginName,
|
|
882
|
-
scope:
|
|
883
|
-
storeKind:
|
|
735
|
+
scope: 'user',
|
|
736
|
+
storeKind: 'user',
|
|
884
737
|
path: filePath,
|
|
885
738
|
existed: false,
|
|
886
739
|
deleted: false,
|
|
887
740
|
resetDisposition: 'absent',
|
|
888
741
|
};
|
|
889
742
|
}
|
|
743
|
+
let store;
|
|
744
|
+
if (target.isFile()) {
|
|
745
|
+
try {
|
|
746
|
+
store = await readPluginConfigStore(options);
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
// A broken document still has the schema-independent reset recovery below.
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
if (!wholeDocument && store && store.state !== 'invalid' && replacePluginConfigStoreLayer(store, {}, options.platform).exists) {
|
|
753
|
+
await commitPluginConfigLayer(options, store, {});
|
|
754
|
+
return { pluginName: options.pluginName, scope: 'user', platform: options.platform, storeKind: 'user',
|
|
755
|
+
path: filePath, existed: true, deleted: false, resetDisposition: 'clearedLayer' };
|
|
756
|
+
}
|
|
890
757
|
if (target.isFile() || target.isSymbolicLink()) {
|
|
891
758
|
await assertNoConfigSymlink(allowedRoot, path.dirname(filePath));
|
|
892
759
|
await nodeFs.unlink(filePath);
|
|
893
760
|
return {
|
|
894
761
|
pluginName: options.pluginName,
|
|
895
|
-
scope:
|
|
896
|
-
storeKind:
|
|
762
|
+
scope: 'user',
|
|
763
|
+
storeKind: 'user',
|
|
897
764
|
path: filePath,
|
|
898
765
|
existed: true,
|
|
899
766
|
deleted: true,
|
|
@@ -907,8 +774,8 @@ export async function resetPluginConfigStore(options) {
|
|
|
907
774
|
await nodeFs.rename(filePath, path.join(path.dirname(filePath), quarantineBasename));
|
|
908
775
|
return {
|
|
909
776
|
pluginName: options.pluginName,
|
|
910
|
-
scope:
|
|
911
|
-
storeKind:
|
|
777
|
+
scope: 'user',
|
|
778
|
+
storeKind: 'user',
|
|
912
779
|
path: filePath,
|
|
913
780
|
existed: true,
|
|
914
781
|
deleted: true,
|
|
@@ -982,41 +849,7 @@ function resetTargetInspection(kind, stats) {
|
|
|
982
849
|
};
|
|
983
850
|
}
|
|
984
851
|
export async function resetPluginConfigStores(options) {
|
|
985
|
-
await
|
|
986
|
-
const targets = resolvePluginConfigStoreTargets({
|
|
987
|
-
...options,
|
|
988
|
-
...(options.scope === 'project' ? { access: 'write' } : {}),
|
|
989
|
-
});
|
|
990
|
-
const allowedRoot = resolvePluginConfigAllowedRoot(options);
|
|
991
|
-
const receipts = [];
|
|
992
|
-
const existing = [];
|
|
993
|
-
for (const target of targets) {
|
|
994
|
-
await prepareConfigFileParent(target.path, allowedRoot);
|
|
995
|
-
await assertNoConfigSymlink(allowedRoot, target.path);
|
|
996
|
-
const stats = await lstatIfExists(target.path);
|
|
997
|
-
existing.push(stats !== null);
|
|
998
|
-
if (!stats)
|
|
999
|
-
continue;
|
|
1000
|
-
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
1001
|
-
throw new Error(`Config target must be a regular file: ${target.path}`);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
for (const [index, target] of targets.entries()) {
|
|
1005
|
-
const deleted = await atomicRemoveConfigFile({
|
|
1006
|
-
filePath: target.path,
|
|
1007
|
-
allowedRoot,
|
|
1008
|
-
});
|
|
1009
|
-
receipts.push({
|
|
1010
|
-
pluginName: options.pluginName,
|
|
1011
|
-
scope: options.scope,
|
|
1012
|
-
storeKind: target.storeKind,
|
|
1013
|
-
path: target.path,
|
|
1014
|
-
existed: existing[index],
|
|
1015
|
-
deleted,
|
|
1016
|
-
resetDisposition: deleted ? 'deletedFile' : 'absent',
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
return receipts;
|
|
852
|
+
return [await resetPluginConfigStore(options)];
|
|
1020
853
|
}
|
|
1021
854
|
function coerceConfigValue(key, field, value) {
|
|
1022
855
|
let coerced = value;
|
|
@@ -1039,19 +872,9 @@ function coerceConfigValue(key, field, value) {
|
|
|
1039
872
|
export async function coercePluginConfigValue(options) {
|
|
1040
873
|
return coerceConfigValue(options.key, options.field, options.value);
|
|
1041
874
|
}
|
|
1042
|
-
function validateConfigReadValue(key, field, value) {
|
|
1043
|
-
const issues = validateCorePluginConfigValue(field, value, key);
|
|
1044
|
-
if (issues.length > 0) {
|
|
1045
|
-
throw new Error(formatPluginConfigIssue(issues[0]));
|
|
1046
|
-
}
|
|
1047
|
-
return value;
|
|
1048
|
-
}
|
|
1049
875
|
function redactValue(field, value) {
|
|
1050
876
|
return redactPluginConfigValue(field, value);
|
|
1051
877
|
}
|
|
1052
|
-
function sourceForStoreLayer(scope) {
|
|
1053
|
-
return scope === 'user' ? 'user' : 'project';
|
|
1054
|
-
}
|
|
1055
878
|
async function applyConfigLayer(options) {
|
|
1056
879
|
for (const [key, field] of Object.entries(options.schema)) {
|
|
1057
880
|
if (!Object.hasOwn(options.entries, key)) {
|
|
@@ -1087,65 +910,25 @@ async function applyConfigLayer(options) {
|
|
|
1087
910
|
}
|
|
1088
911
|
}
|
|
1089
912
|
export async function resolveEffectivePluginConfig(options) {
|
|
913
|
+
const workContext = options.context ? await resolveWorkContext({ cwd: options.cwd, context: options.context }) : undefined;
|
|
1090
914
|
const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1093
|
-
throw new Error(`Plugin "${pluginName}" does not declare an AW config schema.`);
|
|
1094
|
-
}
|
|
1095
|
-
const projectRoot = resolveAwProjectRoot(options.cwd);
|
|
1096
|
-
const userStore = await readPluginConfigStore({
|
|
1097
|
-
sourceId: options.sourceId,
|
|
1098
|
-
sourceIdentity: options.sourceIdentity,
|
|
1099
|
-
logicalIdentity: options.logicalIdentity,
|
|
1100
|
-
pluginName,
|
|
1101
|
-
scope: 'user',
|
|
1102
|
-
cwd: options.cwd,
|
|
1103
|
-
env: options.env,
|
|
1104
|
-
});
|
|
1105
|
-
const projectStore = options.targetScope === 'user'
|
|
1106
|
-
? undefined
|
|
1107
|
-
: await readPluginConfigStore({
|
|
1108
|
-
sourceId: options.sourceId,
|
|
1109
|
-
sourceIdentity: options.sourceIdentity,
|
|
1110
|
-
logicalIdentity: options.logicalIdentity,
|
|
1111
|
-
pluginName,
|
|
1112
|
-
scope: 'project',
|
|
1113
|
-
cwd: options.cwd,
|
|
1114
|
-
env: options.env,
|
|
1115
|
-
});
|
|
1116
|
-
assertReadyPluginConfigStore(userStore);
|
|
1117
|
-
if (projectStore)
|
|
1118
|
-
assertReadyPluginConfigStore(projectStore);
|
|
915
|
+
const user = await readPluginConfigStore({ ...options, pluginName, scope: 'user' });
|
|
916
|
+
assertReadyPluginConfigStore(user);
|
|
1119
917
|
const references = !options.referenceContext && pluginConfigHasExternalReferences(options.manifest)
|
|
1120
|
-
? await resolvePluginConfigReferenceContext({ ...options, scope: options.targetScope ?? '
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
manifest: options.manifest,
|
|
1125
|
-
targetScope: options.targetScope,
|
|
1126
|
-
projectRoot,
|
|
1127
|
-
referenceContext: options.referenceContext ?? references?.context,
|
|
1128
|
-
referenceEvidence: references?.evidence,
|
|
1129
|
-
stores: {
|
|
1130
|
-
user: userStore,
|
|
1131
|
-
...(projectStore ? { project: projectStore } : {}),
|
|
1132
|
-
},
|
|
1133
|
-
});
|
|
918
|
+
? await resolvePluginConfigReferenceContext({ ...options, scope: options.context ?? options.targetScope ?? 'user', plugins: options.referencePlugins }) : undefined;
|
|
919
|
+
return resolveEffectivePluginConfigFromStores({ ...options, pluginName, projectRoot: workContext?.projectRoot ?? ((options.context ?? options.targetScope) === 'project' ? resolveAwProjectRoot(options.cwd) : ''),
|
|
920
|
+
referenceContext: options.referenceContext ?? references?.context, referenceEvidence: references?.evidence,
|
|
921
|
+
stores: { user } });
|
|
1134
922
|
}
|
|
1135
923
|
export async function resolveEffectivePluginConfigFromStores(options) {
|
|
1136
924
|
const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
|
|
1137
|
-
const
|
|
925
|
+
const definition = options.manifest.config ? compilePluginConfigDefinition(options.manifest.config, options.platform) : undefined;
|
|
926
|
+
const schema = definition?.fields;
|
|
1138
927
|
if (!schema) {
|
|
1139
|
-
throw new
|
|
928
|
+
throw new PluginConfigSchemaResolutionError('configSchemaUnavailable', `Plugin "${pluginName}" does not declare an AW config schema.`);
|
|
1140
929
|
}
|
|
1141
930
|
const userStore = options.stores.user;
|
|
1142
|
-
const projectStore = options.targetScope === 'user' ? undefined : options.stores.project;
|
|
1143
931
|
assertReadyPluginConfigStore(userStore);
|
|
1144
|
-
if (!projectStore && options.targetScope !== 'user') {
|
|
1145
|
-
throw new Error(`Project plugin config store is required for ${pluginName}.`);
|
|
1146
|
-
}
|
|
1147
|
-
if (projectStore)
|
|
1148
|
-
assertReadyPluginConfigStore(projectStore);
|
|
1149
932
|
const values = {};
|
|
1150
933
|
const redactedValues = {};
|
|
1151
934
|
const sources = {};
|
|
@@ -1154,7 +937,6 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1154
937
|
const unsupportedLayerOverrides = [];
|
|
1155
938
|
const dangerousKeyIssues = [];
|
|
1156
939
|
const schemaKeys = new Set(Object.keys(schema));
|
|
1157
|
-
const userOnlyKeys = codexNativeRefreshKeys(options.manifest);
|
|
1158
940
|
for (const key of schemaKeys) {
|
|
1159
941
|
sources[key] = 'missing';
|
|
1160
942
|
}
|
|
@@ -1172,54 +954,31 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1172
954
|
source: 'default',
|
|
1173
955
|
});
|
|
1174
956
|
}
|
|
1175
|
-
const layers =
|
|
1176
|
-
|
|
1177
|
-
: [
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
const source = sourceForStoreLayer(scope);
|
|
1184
|
-
const dangerousIssues = new Map(validatePluginConfigDocument(options.manifest.config, plainEntries).filter((issue) => issue.code === 'dangerousKey')
|
|
1185
|
-
.map((issue) => [issue.path.field, issue]));
|
|
957
|
+
const layers = [
|
|
958
|
+
{ entries: userStore.common ?? userStore.values, source: 'common' },
|
|
959
|
+
...(options.platform ? [{ entries: userStore.platforms?.[options.platform] ?? {}, source: 'platform' }] : []),
|
|
960
|
+
];
|
|
961
|
+
for (const { entries, source } of layers) {
|
|
962
|
+
const plainEntries = getStoreEntries(entries);
|
|
963
|
+
const dangerousIssues = new Map(validatePluginConfigDocument(definition, plainEntries)
|
|
964
|
+
.filter((issue) => issue.code === 'dangerousKey').map((issue) => [issue.path.field, issue]));
|
|
1186
965
|
for (const key of Object.keys(plainEntries)) {
|
|
1187
|
-
if (
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
message: formatPluginConfigIssue(dangerousIssue),
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1198
|
-
else {
|
|
1199
|
-
ignoredUnknownKeys.push({ scope, source, key });
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
966
|
+
if (schemaKeys.has(key))
|
|
967
|
+
continue;
|
|
968
|
+
const dangerousIssue = dangerousIssues.get(key);
|
|
969
|
+
if (dangerousIssue)
|
|
970
|
+
dangerousKeyIssues.push({ type: 'invalid-config', code: dangerousIssue.code,
|
|
971
|
+
key, source, message: formatPluginConfigIssue(dangerousIssue) });
|
|
972
|
+
else
|
|
973
|
+
ignoredUnknownKeys.push({ scope: 'user', source, key });
|
|
1202
974
|
}
|
|
1203
|
-
const supportedEntries = scope === 'project'
|
|
1204
|
-
? Object.fromEntries(Object.entries(plainEntries).filter(([key]) => {
|
|
1205
|
-
if (!schemaKeys.has(key) || !userOnlyKeys.has(key))
|
|
1206
|
-
return true;
|
|
1207
|
-
unsupportedLayerOverrides.push({
|
|
1208
|
-
type: 'configLayerUnsupported',
|
|
1209
|
-
key,
|
|
1210
|
-
scope: 'project',
|
|
1211
|
-
source: 'project',
|
|
1212
|
-
});
|
|
1213
|
-
return false;
|
|
1214
|
-
}))
|
|
1215
|
-
: plainEntries;
|
|
1216
975
|
await applyConfigLayer({
|
|
1217
976
|
values,
|
|
1218
977
|
redactedValues,
|
|
1219
978
|
sources,
|
|
1220
979
|
fieldStates,
|
|
1221
980
|
schema,
|
|
1222
|
-
entries:
|
|
981
|
+
entries: plainEntries,
|
|
1223
982
|
source,
|
|
1224
983
|
});
|
|
1225
984
|
}
|
|
@@ -1228,12 +987,12 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1228
987
|
const source = sources[key];
|
|
1229
988
|
if (source === 'default')
|
|
1230
989
|
candidateValues[key] = field.default;
|
|
1231
|
-
if (source === '
|
|
1232
|
-
candidateValues[key] = ownStoreValue(userStore.values, key);
|
|
1233
|
-
if (source === '
|
|
1234
|
-
candidateValues[key] = ownStoreValue(
|
|
990
|
+
if (source === 'common')
|
|
991
|
+
candidateValues[key] = ownStoreValue(userStore.common ?? userStore.values, key);
|
|
992
|
+
if (source === 'platform' && options.platform)
|
|
993
|
+
candidateValues[key] = ownStoreValue(userStore.platforms?.[options.platform] ?? {}, key);
|
|
1235
994
|
}
|
|
1236
|
-
const candidateIssues = validatePluginConfigCandidate(
|
|
995
|
+
const candidateIssues = validatePluginConfigCandidate(definition, candidateValues, undefined, options.referenceContext);
|
|
1237
996
|
const referenceIssues = candidateIssues.filter((issue) => issue.code.startsWith('valueRef')
|
|
1238
997
|
&& (!issue.path.plugin || issue.path.plugin === pluginName));
|
|
1239
998
|
const missingReferenceIssues = [];
|
|
@@ -1263,7 +1022,7 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1263
1022
|
for (const key of schemaKeys) {
|
|
1264
1023
|
let dependencies;
|
|
1265
1024
|
try {
|
|
1266
|
-
dependencies = getPluginConfigDependencyFields(
|
|
1025
|
+
dependencies = getPluginConfigDependencyFields(definition, [key], options.referenceContext);
|
|
1267
1026
|
}
|
|
1268
1027
|
catch {
|
|
1269
1028
|
continue; // The local association issue already reports the invalid schema.
|
|
@@ -1294,7 +1053,7 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1294
1053
|
for (const key of schemaKeys) {
|
|
1295
1054
|
if (invalidKeys.has(key))
|
|
1296
1055
|
continue;
|
|
1297
|
-
const dependency = getPluginConfigDependencyKeys(
|
|
1056
|
+
const dependency = getPluginConfigDependencyKeys(definition, [key])
|
|
1298
1057
|
.find((dependencyKey) => dependencyKey !== key && invalidKeys.has(dependencyKey));
|
|
1299
1058
|
if (dependency === undefined)
|
|
1300
1059
|
continue;
|
|
@@ -1333,6 +1092,9 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1333
1092
|
}
|
|
1334
1093
|
return {
|
|
1335
1094
|
pluginName,
|
|
1095
|
+
platform: options.platform,
|
|
1096
|
+
context: options.context ?? options.targetScope ?? 'user',
|
|
1097
|
+
definition: definition,
|
|
1336
1098
|
projectRoot: options.projectRoot,
|
|
1337
1099
|
values,
|
|
1338
1100
|
redactedValues,
|
|
@@ -1344,7 +1106,6 @@ export async function resolveEffectivePluginConfigFromStores(options) {
|
|
|
1344
1106
|
schema,
|
|
1345
1107
|
stores: {
|
|
1346
1108
|
user: userStore,
|
|
1347
|
-
...(projectStore ? { project: projectStore } : {}),
|
|
1348
1109
|
},
|
|
1349
1110
|
...(options.referenceContext ? { referenceContext: options.referenceContext } : {}),
|
|
1350
1111
|
...(options.referenceEvidence ? { referenceEvidence: options.referenceEvidence } : {}),
|
|
@@ -1356,43 +1117,26 @@ function ownStoreValue(values, key) {
|
|
|
1356
1117
|
/** Check cross-field relations before saving one complete layer candidate. */
|
|
1357
1118
|
export async function assertPluginConfigMutationCandidate(options) {
|
|
1358
1119
|
const user = await readPluginConfigStore({ ...options, scope: 'user' });
|
|
1359
|
-
const project = options.scope === 'project'
|
|
1360
|
-
? await readPluginConfigStore({ ...options, scope: 'project' })
|
|
1361
|
-
: undefined;
|
|
1362
1120
|
assertReadyPluginConfigStore(user);
|
|
1363
|
-
|
|
1364
|
-
assertReadyPluginConfigStore(project);
|
|
1365
|
-
const edited = options.scope === 'user' ? user : project;
|
|
1366
|
-
const values = { ...edited.values };
|
|
1121
|
+
const values = { ...user.values };
|
|
1367
1122
|
for (const key of options.removeKeys)
|
|
1368
1123
|
delete values[key];
|
|
1369
1124
|
for (const [key, value] of Object.entries(options.updates))
|
|
1370
1125
|
values[key] = value;
|
|
1371
|
-
const
|
|
1372
|
-
const
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
const before = await resolveEffectivePluginConfigFromStores({ manifest: options.manifest,
|
|
1386
|
-
targetScope: options.scope, projectRoot: resolveAwProjectRoot(options.cwd),
|
|
1387
|
-
stores: { user, ...(project ? { project } : {}) }, referenceContext: references?.context });
|
|
1388
|
-
const existingIssues = new Set(before.issues.map((issue) => `${issue.key}\0${issue.code}\0${issue.message}`));
|
|
1389
|
-
const editedKeys = new Set([...Object.keys(options.updates), ...options.removeKeys]);
|
|
1390
|
-
const issues = effective.issues.filter((issue) => issue.code?.startsWith('valueRef')
|
|
1391
|
-
&& (editedKeys.has(issue.key) || !existingIssues.has(`${issue.key}\0${issue.code}\0${issue.message}`)));
|
|
1392
|
-
if (issues.length > 0) {
|
|
1393
|
-
throw Object.assign(new Error(`configReferenceInvalid: ${issues.map((issue) => issue.message).join(' ')}`), {
|
|
1394
|
-
code: 'configReferenceInvalid',
|
|
1395
|
-
});
|
|
1126
|
+
const candidate = replacePluginConfigStoreLayer(user, values, options.platform);
|
|
1127
|
+
const profiles = options.platform ? [options.platform] : [undefined, ...PLATFORM_TARGETS];
|
|
1128
|
+
for (const platform of profiles) {
|
|
1129
|
+
const references = pluginConfigHasExternalReferences(options.manifest)
|
|
1130
|
+
? await resolvePluginConfigReferenceContext({ ...options, platform, plugins: options.referencePlugins }) : undefined;
|
|
1131
|
+
const resolve = (store) => resolveEffectivePluginConfigFromStores({ ...options, platform,
|
|
1132
|
+
targetScope: options.scope, projectRoot: options.scope === 'project' ? resolveAwProjectRoot(options.cwd) : '', stores: { user: store }, referenceContext: references?.context });
|
|
1133
|
+
const [before, after] = await Promise.all([resolve(user), resolve(candidate)]);
|
|
1134
|
+
const existing = new Set(before.issues.map((issue) => `${issue.key}\0${issue.code}\0${issue.message}`));
|
|
1135
|
+
const editedKeys = new Set([...Object.keys(options.updates), ...options.removeKeys]);
|
|
1136
|
+
const issues = after.issues.filter((issue) => (editedKeys.has(issue.key) && Object.hasOwn(options.updates, issue.key))
|
|
1137
|
+
|| !existing.has(`${issue.key}\0${issue.code}\0${issue.message}`));
|
|
1138
|
+
if (issues.length)
|
|
1139
|
+
throw Object.assign(new Error(`configReferenceInvalid: ${platform ?? 'common'}: ${issues.map((issue) => issue.message).join(' ')}`), { code: 'configReferenceInvalid' });
|
|
1396
1140
|
}
|
|
1397
1141
|
}
|
|
1398
1142
|
export async function coercePluginConfigUpdates(options) {
|
|
@@ -1414,7 +1158,7 @@ export async function coercePluginConfigUpdates(options) {
|
|
|
1414
1158
|
export async function resolveProjectArtifactRenderConfig(options) {
|
|
1415
1159
|
if (!options.manifest.config?.fields) {
|
|
1416
1160
|
const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
|
|
1417
|
-
const [userStore
|
|
1161
|
+
const [userStore] = await Promise.all([
|
|
1418
1162
|
readPluginConfigStore({
|
|
1419
1163
|
sourceId: options.sourceId,
|
|
1420
1164
|
sourceIdentity: options.sourceIdentity,
|
|
@@ -1424,18 +1168,8 @@ export async function resolveProjectArtifactRenderConfig(options) {
|
|
|
1424
1168
|
cwd: options.cwd,
|
|
1425
1169
|
env: options.env,
|
|
1426
1170
|
}),
|
|
1427
|
-
readPluginConfigStore({
|
|
1428
|
-
sourceId: options.sourceId,
|
|
1429
|
-
sourceIdentity: options.sourceIdentity,
|
|
1430
|
-
logicalIdentity: options.logicalIdentity,
|
|
1431
|
-
pluginName,
|
|
1432
|
-
scope: 'project',
|
|
1433
|
-
cwd: options.cwd,
|
|
1434
|
-
env: options.env,
|
|
1435
|
-
}),
|
|
1436
1171
|
]);
|
|
1437
1172
|
assertReadyPluginConfigStore(userStore);
|
|
1438
|
-
assertReadyPluginConfigStore(projectStore);
|
|
1439
1173
|
return {
|
|
1440
1174
|
pluginName,
|
|
1441
1175
|
projectRoot: resolveAwProjectRoot(options.cwd),
|
|
@@ -1445,7 +1179,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
|
|
|
1445
1179
|
unsupportedLayerOverrides: [],
|
|
1446
1180
|
ignoredUnknownKeys: [],
|
|
1447
1181
|
schema: {},
|
|
1448
|
-
stores: { user: userStore
|
|
1182
|
+
stores: { user: userStore },
|
|
1449
1183
|
};
|
|
1450
1184
|
}
|
|
1451
1185
|
const effective = await resolveEffectivePluginConfig({
|
|
@@ -1463,101 +1197,11 @@ export async function resolveProjectArtifactRenderConfig(options) {
|
|
|
1463
1197
|
schema: effective.schema,
|
|
1464
1198
|
stores: {
|
|
1465
1199
|
user: effective.stores.user,
|
|
1466
|
-
project: effective.stores.project,
|
|
1467
|
-
},
|
|
1468
|
-
};
|
|
1469
|
-
}
|
|
1470
|
-
async function resolveRedactedLayerValue(options) {
|
|
1471
|
-
try {
|
|
1472
|
-
const value = validateConfigReadValue(options.key, options.field, options.value);
|
|
1473
|
-
return {
|
|
1474
|
-
value: redactValue(options.field, value),
|
|
1475
|
-
};
|
|
1476
|
-
}
|
|
1477
|
-
catch (error) {
|
|
1478
|
-
return {
|
|
1479
|
-
issue: {
|
|
1480
|
-
type: 'invalid-config',
|
|
1481
|
-
key: options.key,
|
|
1482
|
-
source: options.source,
|
|
1483
|
-
message: error instanceof Error ? error.message : String(error),
|
|
1484
|
-
},
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
export async function resolveProjectScopedPluginConfigView(options) {
|
|
1489
|
-
const effective = await resolveEffectivePluginConfig(options);
|
|
1490
|
-
const entries = {};
|
|
1491
|
-
for (const [key, field] of Object.entries(effective.schema)) {
|
|
1492
|
-
const entry = {
|
|
1493
|
-
key,
|
|
1494
|
-
effectiveValue: effective.redactedValues[key],
|
|
1495
|
-
effectiveSource: effective.sources[key] ?? 'missing',
|
|
1496
|
-
};
|
|
1497
|
-
if (field.default !== undefined) {
|
|
1498
|
-
const resolvedDefault = await resolveRedactedLayerValue({
|
|
1499
|
-
key,
|
|
1500
|
-
field,
|
|
1501
|
-
source: 'default',
|
|
1502
|
-
value: field.default,
|
|
1503
|
-
});
|
|
1504
|
-
if (resolvedDefault.value !== undefined) {
|
|
1505
|
-
entry.defaultValue = resolvedDefault.value;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
const userPlainEntries = getStoreEntries(effective.stores.user.values);
|
|
1509
|
-
if (Object.hasOwn(userPlainEntries, key)) {
|
|
1510
|
-
const resolvedUser = await resolveRedactedLayerValue({
|
|
1511
|
-
key,
|
|
1512
|
-
field,
|
|
1513
|
-
source: 'user',
|
|
1514
|
-
value: userPlainEntries[key],
|
|
1515
|
-
});
|
|
1516
|
-
if (resolvedUser.value !== undefined) {
|
|
1517
|
-
entry.userValue = resolvedUser.value;
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
const projectPlainEntries = getStoreEntries(effective.stores.project.values);
|
|
1521
|
-
if (Object.hasOwn(projectPlainEntries, key)) {
|
|
1522
|
-
const resolvedProject = await resolveRedactedLayerValue({
|
|
1523
|
-
key,
|
|
1524
|
-
field,
|
|
1525
|
-
source: 'project',
|
|
1526
|
-
value: projectPlainEntries[key],
|
|
1527
|
-
});
|
|
1528
|
-
if (resolvedProject.value !== undefined) {
|
|
1529
|
-
entry.projectValue = resolvedProject.value;
|
|
1530
|
-
}
|
|
1531
|
-
}
|
|
1532
|
-
entries[key] = entry;
|
|
1533
|
-
}
|
|
1534
|
-
return {
|
|
1535
|
-
pluginName: effective.pluginName,
|
|
1536
|
-
projectRoot: effective.projectRoot,
|
|
1537
|
-
schema: effective.schema,
|
|
1538
|
-
stores: {
|
|
1539
|
-
user: effective.stores.user,
|
|
1540
|
-
project: effective.stores.project,
|
|
1541
1200
|
},
|
|
1542
|
-
redactedValues: effective.redactedValues,
|
|
1543
|
-
sources: effective.sources,
|
|
1544
|
-
entries,
|
|
1545
|
-
issues: effective.issues,
|
|
1546
|
-
unsupportedLayerOverrides: effective.unsupportedLayerOverrides,
|
|
1547
|
-
ignoredUnknownKeys: effective.ignoredUnknownKeys,
|
|
1548
1201
|
};
|
|
1549
1202
|
}
|
|
1550
1203
|
function toConfigSourceLayer(source) {
|
|
1551
|
-
|
|
1552
|
-
return 'default';
|
|
1553
|
-
}
|
|
1554
|
-
if (source === 'user') {
|
|
1555
|
-
return 'user';
|
|
1556
|
-
}
|
|
1557
|
-
if (source === 'project') {
|
|
1558
|
-
return 'project';
|
|
1559
|
-
}
|
|
1560
|
-
return null;
|
|
1204
|
+
return source === undefined || source === 'missing' ? null : source;
|
|
1561
1205
|
}
|
|
1562
1206
|
function configFieldProjection(options) {
|
|
1563
1207
|
const source = options.source ?? 'missing';
|
|
@@ -1578,265 +1222,74 @@ function configFieldProjection(options) {
|
|
|
1578
1222
|
};
|
|
1579
1223
|
}
|
|
1580
1224
|
async function buildManifestFieldEntries(options) {
|
|
1581
|
-
const schema = options.plugin.manifest.config?.fields;
|
|
1582
|
-
if (!schema) {
|
|
1583
|
-
return [];
|
|
1584
|
-
}
|
|
1585
|
-
const userStore = await readPluginConfigStore({
|
|
1586
|
-
sourceId: options.plugin.sourceId,
|
|
1587
|
-
sourceIdentity: options.plugin.sourceIdentity,
|
|
1588
|
-
logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
1589
|
-
pluginName: options.plugin.manifest.name,
|
|
1590
|
-
scope: 'user',
|
|
1591
|
-
cwd: options.cwd,
|
|
1592
|
-
env: options.env,
|
|
1593
|
-
schema,
|
|
1594
|
-
});
|
|
1595
|
-
const projectStore = options.scope === 'user'
|
|
1596
|
-
? undefined
|
|
1597
|
-
: await readPluginConfigStore({
|
|
1598
|
-
sourceId: options.plugin.sourceId,
|
|
1599
|
-
sourceIdentity: options.plugin.sourceIdentity,
|
|
1600
|
-
logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
1601
|
-
pluginName: options.plugin.manifest.name,
|
|
1602
|
-
scope: 'project',
|
|
1603
|
-
cwd: options.cwd,
|
|
1604
|
-
env: options.env,
|
|
1605
|
-
schema,
|
|
1606
|
-
});
|
|
1607
|
-
assertReadyPluginConfigStore(userStore);
|
|
1608
|
-
if (projectStore)
|
|
1609
|
-
assertReadyPluginConfigStore(projectStore);
|
|
1610
|
-
const userPlainEntries = getStoreEntries(userStore.values);
|
|
1611
|
-
const projectPlainEntries = projectStore ? getStoreEntries(projectStore.values) : {};
|
|
1612
1225
|
const effective = await resolveEffectivePluginConfig({
|
|
1613
1226
|
sourceId: options.plugin.sourceId,
|
|
1614
1227
|
sourceIdentity: options.plugin.sourceIdentity,
|
|
1615
1228
|
logicalIdentity: options.plugin.catalogLogicalIdentity,
|
|
1616
|
-
pluginName: options.plugin.manifest.name,
|
|
1617
1229
|
manifest: options.plugin.manifest,
|
|
1618
|
-
|
|
1230
|
+
platform: options.platform,
|
|
1231
|
+
targetScope: options.context ?? (options.scope === 'project' ? 'project' : 'user'),
|
|
1619
1232
|
cwd: options.cwd,
|
|
1620
1233
|
env: options.env,
|
|
1621
1234
|
});
|
|
1622
|
-
const
|
|
1623
|
-
const
|
|
1624
|
-
const
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
return { state: 'missing' };
|
|
1648
|
-
const validationIssues = validateCorePluginConfigValue(field, field.default, key);
|
|
1649
|
-
if (validationIssues.length > 0) {
|
|
1650
|
-
return {
|
|
1651
|
-
state: 'invalid',
|
|
1652
|
-
sourceLayer: 'default',
|
|
1653
|
-
issues: validationIssues.map((issue) => ({
|
|
1654
|
-
type: 'invalid-config',
|
|
1655
|
-
key,
|
|
1656
|
-
source: 'default',
|
|
1657
|
-
message: formatPluginConfigIssue(issue),
|
|
1658
|
-
})),
|
|
1659
|
-
};
|
|
1660
|
-
}
|
|
1661
|
-
return {
|
|
1662
|
-
state: 'valid',
|
|
1663
|
-
effectiveValue: redactValue(field, field.default),
|
|
1664
|
-
sourceLayer: 'default',
|
|
1665
|
-
};
|
|
1666
|
-
};
|
|
1667
|
-
const effectiveFieldState = (key) => {
|
|
1668
|
-
const source = effective.sources[key];
|
|
1669
|
-
const sourceLayer = toConfigSourceLayer(source);
|
|
1670
|
-
const field = schema[key];
|
|
1671
|
-
const fieldIssues = effective.issues.filter((entry) => entry.key === key);
|
|
1672
|
-
const fieldState = effective.fieldStates[key];
|
|
1673
|
-
const fallback = options.scope === 'project'
|
|
1674
|
-
? projectRemovalFallback(key)
|
|
1675
|
-
: fieldState?.fallback
|
|
1676
|
-
? {
|
|
1677
|
-
state: 'valid',
|
|
1678
|
-
effectiveValue: fieldState.fallback.redactedValue,
|
|
1679
|
-
sourceLayer: fieldState.fallback.source,
|
|
1680
|
-
}
|
|
1681
|
-
: undefined;
|
|
1682
|
-
if (source === undefined || source === 'missing') {
|
|
1683
|
-
return {
|
|
1684
|
-
valueState: fieldIssues.length > 0 ? 'invalid' : 'default',
|
|
1685
|
-
effectiveValue: null,
|
|
1686
|
-
sourceLayer: null,
|
|
1687
|
-
...configFieldProjection({ field, source: 'missing', issues: fieldIssues }),
|
|
1688
|
-
};
|
|
1235
|
+
const stored = pluginConfigStoreForProfile(effective.stores.user, options.platform).values;
|
|
1236
|
+
const fields = await Promise.all(Object.entries(effective.schema).map(async ([key, field]) => {
|
|
1237
|
+
const state = effective.fieldStates[key];
|
|
1238
|
+
const source = effective.sources[key] ?? 'missing';
|
|
1239
|
+
const issues = effective.issues.filter((issue) => issue.key === key);
|
|
1240
|
+
const hasStored = Object.hasOwn(stored, key);
|
|
1241
|
+
const storedSourceLayer = options.platform ? 'platform' : 'common';
|
|
1242
|
+
let fallback;
|
|
1243
|
+
if (hasStored) {
|
|
1244
|
+
const inherited = { ...stored };
|
|
1245
|
+
delete inherited[key];
|
|
1246
|
+
const withoutOverride = await resolveEffectivePluginConfigFromStores({
|
|
1247
|
+
manifest: options.plugin.manifest,
|
|
1248
|
+
platform: options.platform,
|
|
1249
|
+
projectRoot: effective.projectRoot,
|
|
1250
|
+
stores: { user: replacePluginConfigStoreLayer(effective.stores.user, inherited, options.platform) },
|
|
1251
|
+
referenceContext: effective.referenceContext,
|
|
1252
|
+
});
|
|
1253
|
+
const fallbackIssues = withoutOverride.issues.filter((issue) => issue.key === key);
|
|
1254
|
+
const fallbackSource = withoutOverride.sources[key];
|
|
1255
|
+
fallback = fallbackSource === 'missing' || fallbackSource === undefined
|
|
1256
|
+
? { state: 'missing' }
|
|
1257
|
+
: fallbackIssues.length
|
|
1258
|
+
? { state: 'invalid', sourceLayer: fallbackSource, issues: fallbackIssues }
|
|
1259
|
+
: { state: 'valid', sourceLayer: fallbackSource, effectiveValue: withoutOverride.redactedValues[key] };
|
|
1689
1260
|
}
|
|
1690
|
-
const issue = fieldIssues.find((entry) => entry.source === source);
|
|
1691
1261
|
return {
|
|
1692
|
-
valueState: issue ? 'invalid' : source === 'default' ? 'default' : 'configured',
|
|
1693
|
-
effectiveValue: issue ? null : effective.redactedValues[key] ?? null,
|
|
1694
|
-
sourceLayer,
|
|
1695
|
-
...(fieldState?.invalidOverride ? {
|
|
1696
|
-
invalidOverride: {
|
|
1697
|
-
sourceLayer: fieldState.invalidOverride.source,
|
|
1698
|
-
issues: fieldState.invalidOverride.issues,
|
|
1699
|
-
},
|
|
1700
|
-
} : {}),
|
|
1701
|
-
...(fallback ? { fallback } : {}),
|
|
1702
|
-
...configFieldProjection({
|
|
1703
|
-
field,
|
|
1704
|
-
value: issue ? undefined : effective.values[key],
|
|
1705
|
-
source,
|
|
1706
|
-
issues: fieldIssues,
|
|
1707
|
-
}),
|
|
1708
|
-
};
|
|
1709
|
-
};
|
|
1710
|
-
const fields = [];
|
|
1711
|
-
for (const [key, field] of Object.entries(schema)) {
|
|
1712
|
-
const canSet = options.scope !== 'project'
|
|
1713
|
-
|| (!userOnlyKeys.has(key) && field.sensitive !== true);
|
|
1714
|
-
const base = {
|
|
1715
1262
|
key,
|
|
1716
1263
|
title: field.title ?? key,
|
|
1717
1264
|
kind: 'manifest',
|
|
1718
1265
|
reserved: false,
|
|
1719
|
-
writable:
|
|
1720
|
-
canSet,
|
|
1721
|
-
canUnset:
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
...
|
|
1266
|
+
writable: true,
|
|
1267
|
+
canSet: true,
|
|
1268
|
+
canUnset: hasStored,
|
|
1269
|
+
valueState: issues.length ? 'invalid' : source === 'default' || source === 'missing' ? 'default' : 'configured',
|
|
1270
|
+
effectiveValue: effective.redactedValues[key] ?? null,
|
|
1271
|
+
sourceLayer: toConfigSourceLayer(source),
|
|
1272
|
+
...(hasStored ? { storedSourceLayer, ...(validateCorePluginConfigValue(field, stored[key], key).length
|
|
1273
|
+
? {} : { storedValue: redactValue(field, stored[key]) }) } : {}),
|
|
1274
|
+
...(state?.invalidOverride ? { invalidOverride: { sourceLayer: state.invalidOverride.source, issues: state.invalidOverride.issues } } : {}),
|
|
1275
|
+
...(fallback ? { fallback } : {}),
|
|
1276
|
+
...configFieldProjection({ field, value: effective.values[key], source, issues }),
|
|
1728
1277
|
};
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
valueState: 'configured',
|
|
1736
|
-
effectiveValue: redactValue(field, value),
|
|
1737
|
-
sourceLayer: 'user',
|
|
1738
|
-
storedValue: redactValue(field, value),
|
|
1739
|
-
storedSourceLayer: 'user',
|
|
1740
|
-
...configFieldProjection({ field, value, source: 'user' }),
|
|
1741
|
-
});
|
|
1742
|
-
}
|
|
1743
|
-
catch {
|
|
1744
|
-
fields.push({
|
|
1745
|
-
...base,
|
|
1746
|
-
valueState: 'invalid',
|
|
1747
|
-
sourceLayer: 'user',
|
|
1748
|
-
source: 'user',
|
|
1749
|
-
storedSourceLayer: 'user',
|
|
1750
|
-
valid: false,
|
|
1751
|
-
issues: [{ type: 'invalid-config', key, source: 'user' }],
|
|
1752
|
-
});
|
|
1753
|
-
}
|
|
1754
|
-
continue;
|
|
1755
|
-
}
|
|
1756
|
-
if (field.default !== undefined) {
|
|
1757
|
-
try {
|
|
1758
|
-
const value = validateConfigReadValue(key, field, field.default);
|
|
1759
|
-
fields.push({
|
|
1760
|
-
...base,
|
|
1761
|
-
effectiveValue: redactValue(field, value),
|
|
1762
|
-
sourceLayer: 'default',
|
|
1763
|
-
...configFieldProjection({ field, value, source: 'default' }),
|
|
1764
|
-
});
|
|
1765
|
-
}
|
|
1766
|
-
catch {
|
|
1767
|
-
fields.push({
|
|
1768
|
-
...base,
|
|
1769
|
-
valueState: 'invalid',
|
|
1770
|
-
sourceLayer: 'default',
|
|
1771
|
-
source: 'default',
|
|
1772
|
-
valid: false,
|
|
1773
|
-
issues: [{ type: 'invalid-config', key, source: 'default' }],
|
|
1774
|
-
});
|
|
1775
|
-
}
|
|
1776
|
-
continue;
|
|
1777
|
-
}
|
|
1778
|
-
fields.push(base);
|
|
1779
|
-
continue;
|
|
1780
|
-
}
|
|
1781
|
-
let storedProjectValue;
|
|
1782
|
-
if (options.scope === 'project' && Object.hasOwn(projectPlainEntries, key)) {
|
|
1783
|
-
try {
|
|
1784
|
-
const value = validateConfigReadValue(key, field, projectPlainEntries[key]);
|
|
1785
|
-
storedProjectValue = {
|
|
1786
|
-
storedValue: redactValue(field, value),
|
|
1787
|
-
storedSourceLayer: 'project',
|
|
1788
|
-
};
|
|
1789
|
-
}
|
|
1790
|
-
catch {
|
|
1791
|
-
storedProjectValue = { storedSourceLayer: 'project' };
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
fields.push({ ...base, ...effectiveFieldState(key), ...storedProjectValue });
|
|
1278
|
+
}));
|
|
1279
|
+
for (const key of Object.keys(stored).filter((key) => !Object.hasOwn(effective.schema, key)).sort()) {
|
|
1280
|
+
const source = options.platform ? 'platform' : 'common';
|
|
1281
|
+
fields.push({ key, title: key, kind: 'manifest', reserved: false, writable: false, canSet: false, canUnset: true,
|
|
1282
|
+
valueState: 'configured', effectiveValue: '[redacted]', sourceLayer: source, storedSourceLayer: source,
|
|
1283
|
+
value: '[redacted]', source, matchedPreset: null, valid: false, issues: [] });
|
|
1795
1284
|
}
|
|
1796
1285
|
return fields;
|
|
1797
1286
|
}
|
|
1798
1287
|
async function buildLegacyStoreFieldEntries(options) {
|
|
1799
|
-
const
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
]
|
|
1804
|
-
: [{
|
|
1805
|
-
scope: options.scope,
|
|
1806
|
-
sourceLayer: options.scope === 'user' ? 'user' : 'project',
|
|
1807
|
-
}];
|
|
1808
|
-
const fields = new Map();
|
|
1809
|
-
for (const location of locations) {
|
|
1810
|
-
const store = await readPluginConfigStore({
|
|
1811
|
-
sourceId: options.sourceId,
|
|
1812
|
-
sourceIdentity: options.sourceIdentity,
|
|
1813
|
-
logicalIdentity: options.logicalIdentity,
|
|
1814
|
-
pluginName: options.pluginName,
|
|
1815
|
-
scope: location.scope,
|
|
1816
|
-
cwd: options.cwd,
|
|
1817
|
-
env: options.env,
|
|
1818
|
-
});
|
|
1819
|
-
for (const key of Object.keys(store.values)) {
|
|
1820
|
-
fields.set(key, {
|
|
1821
|
-
key,
|
|
1822
|
-
title: key,
|
|
1823
|
-
kind: 'manifest',
|
|
1824
|
-
reserved: false,
|
|
1825
|
-
writable: false,
|
|
1826
|
-
canSet: false,
|
|
1827
|
-
canUnset: false,
|
|
1828
|
-
valueState: 'configured',
|
|
1829
|
-
effectiveValue: '[redacted]',
|
|
1830
|
-
sourceLayer: location.sourceLayer,
|
|
1831
|
-
value: '[redacted]',
|
|
1832
|
-
source: location.sourceLayer,
|
|
1833
|
-
matchedPreset: null,
|
|
1834
|
-
valid: true,
|
|
1835
|
-
issues: [],
|
|
1836
|
-
});
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
return [...fields.values()].sort((left, right) => left.key.localeCompare(right.key));
|
|
1288
|
+
const store = await readPluginConfigStore({ ...options, scope: 'user' });
|
|
1289
|
+
const source = options.platform ? 'platform' : 'common';
|
|
1290
|
+
return Object.keys(store.values).sort().map((key) => ({ key, title: key, kind: 'manifest', reserved: false,
|
|
1291
|
+
writable: false, canSet: false, canUnset: true, valueState: 'configured', effectiveValue: '[redacted]',
|
|
1292
|
+
sourceLayer: source, value: '[redacted]', source, matchedPreset: null, valid: true, issues: [] }));
|
|
1840
1293
|
}
|
|
1841
1294
|
export async function resolvePluginConfigSchemaAccess(options) {
|
|
1842
1295
|
const pluginName = assertSafePluginConfigSegment(options.pluginName);
|
|
@@ -1852,15 +1305,18 @@ export async function resolvePluginConfigSchemaAccess(options) {
|
|
|
1852
1305
|
return {
|
|
1853
1306
|
pluginName,
|
|
1854
1307
|
plugin: options.currentPlugin,
|
|
1855
|
-
schema: createConfigSchemaEvidenceFromCurrentCatalog(options.currentPlugin, await calculateConfigSchemaManifestDigestForPlugin(options.currentPlugin)),
|
|
1308
|
+
schema: createConfigSchemaEvidenceFromCurrentCatalog(options.currentPlugin, await calculateConfigSchemaManifestDigestForPlugin(options.currentPlugin), options.platform),
|
|
1856
1309
|
};
|
|
1857
1310
|
}
|
|
1858
1311
|
export async function resolveConfigRead(options) {
|
|
1312
|
+
if (options.context)
|
|
1313
|
+
await resolveWorkContext({ cwd: options.cwd, context: options.context });
|
|
1859
1314
|
const pluginName = assertSafePluginConfigSegment(options.pluginName);
|
|
1860
1315
|
const schemaAccess = await resolvePluginConfigSchemaAccess({
|
|
1861
1316
|
pluginName,
|
|
1862
1317
|
currentPlugin: options.plugin,
|
|
1863
1318
|
currentPluginError: options.pluginError,
|
|
1319
|
+
platform: options.platform,
|
|
1864
1320
|
scope: options.scope,
|
|
1865
1321
|
cwd: options.cwd,
|
|
1866
1322
|
env: options.env,
|
|
@@ -1868,6 +1324,8 @@ export async function resolveConfigRead(options) {
|
|
|
1868
1324
|
const manifestFields = schemaAccess.plugin
|
|
1869
1325
|
? await buildManifestFieldEntries({
|
|
1870
1326
|
plugin: schemaAccess.plugin,
|
|
1327
|
+
context: options.context,
|
|
1328
|
+
platform: options.platform,
|
|
1871
1329
|
scope: options.scope,
|
|
1872
1330
|
cwd: options.cwd,
|
|
1873
1331
|
env: options.env,
|
|
@@ -1878,15 +1336,18 @@ export async function resolveConfigRead(options) {
|
|
|
1878
1336
|
logicalIdentity: options.logicalIdentity,
|
|
1879
1337
|
pluginName,
|
|
1880
1338
|
scope: options.scope,
|
|
1339
|
+
platform: options.platform,
|
|
1881
1340
|
cwd: options.cwd,
|
|
1882
1341
|
env: options.env,
|
|
1883
1342
|
});
|
|
1884
1343
|
return {
|
|
1885
1344
|
pluginName,
|
|
1345
|
+
platform: options.platform,
|
|
1346
|
+
context: options.context ?? 'user',
|
|
1886
1347
|
scope: options.scope,
|
|
1887
1348
|
...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
|
|
1888
1349
|
...(schemaAccess.plugin?.manifest.config?.fields
|
|
1889
|
-
? { fieldDefinitions: schemaAccess.plugin.manifest.config.fields }
|
|
1350
|
+
? { fieldDefinitions: compilePluginConfigDefinition(schemaAccess.plugin.manifest.config, options.platform).fields }
|
|
1890
1351
|
: {}),
|
|
1891
1352
|
fields: manifestFields,
|
|
1892
1353
|
};
|
|
@@ -1914,13 +1375,14 @@ export function summarizeEffectivePluginConfig(config) {
|
|
|
1914
1375
|
ignoredUnknownKeys: config.ignoredUnknownKeys,
|
|
1915
1376
|
};
|
|
1916
1377
|
}
|
|
1917
|
-
export function createPluginConfigSchemaIdentity(manifest) {
|
|
1378
|
+
export function createPluginConfigSchemaIdentity(manifest, platform) {
|
|
1918
1379
|
const hash = crypto
|
|
1919
1380
|
.createHash('sha256')
|
|
1920
1381
|
.update(JSON.stringify({
|
|
1921
1382
|
name: manifest.name,
|
|
1922
1383
|
version: manifest.version,
|
|
1923
|
-
|
|
1384
|
+
platform: platform ?? null,
|
|
1385
|
+
config: manifest.config ? compilePluginConfigDefinition(manifest.config, platform) : null,
|
|
1924
1386
|
}))
|
|
1925
1387
|
.digest('hex');
|
|
1926
1388
|
return `${manifest.name}:${hash}`;
|
|
@@ -1961,9 +1423,9 @@ export function toCliConfigPromptSchema(schema) {
|
|
|
1961
1423
|
return promptSchema;
|
|
1962
1424
|
}
|
|
1963
1425
|
export function createPluginConfigFormModel(options) {
|
|
1964
|
-
const schema = options.
|
|
1426
|
+
const schema = options.effective.schema;
|
|
1965
1427
|
if (!schema) {
|
|
1966
|
-
throw new
|
|
1428
|
+
throw new PluginConfigSchemaResolutionError('configSchemaUnavailable', `Plugin "${options.pluginName ?? options.manifest.name}" does not declare an AW config schema.`);
|
|
1967
1429
|
}
|
|
1968
1430
|
const requiredKeys = [...new Set(options.effective.issues
|
|
1969
1431
|
.filter((issue) => issue.type === 'missing-required' || issue.type === 'invalid-config')
|
|
@@ -1990,8 +1452,8 @@ export function createPluginConfigFormModel(options) {
|
|
|
1990
1452
|
});
|
|
1991
1453
|
return {
|
|
1992
1454
|
pluginName: options.pluginName ?? options.manifest.name,
|
|
1993
|
-
allowedScopes:
|
|
1994
|
-
initialScope:
|
|
1455
|
+
allowedScopes: ['user'],
|
|
1456
|
+
initialScope: 'user',
|
|
1995
1457
|
requiredKeys,
|
|
1996
1458
|
fields,
|
|
1997
1459
|
};
|
|
@@ -2030,21 +1492,17 @@ export function toCliConfigPromptOptions(model) {
|
|
|
2030
1492
|
requiredKeys: [...model.requiredKeys],
|
|
2031
1493
|
};
|
|
2032
1494
|
}
|
|
2033
|
-
export function formatPluginConfigSetRecoveryCommand(pluginName, key, field,
|
|
2034
|
-
|
|
2035
|
-
? `
|
|
2036
|
-
|
|
1495
|
+
export function formatPluginConfigSetRecoveryCommand(pluginName, key, field, context, platform) {
|
|
1496
|
+
const command = `aw plugin config set ${pluginName} ${key} --context ${context}`
|
|
1497
|
+
+ (platform ? ` --platform ${platform}` : '');
|
|
1498
|
+
return field.sensitive === true ? `${command} --value-stdin` : `${command} --value-json '<json>'`;
|
|
2037
1499
|
}
|
|
2038
|
-
function formatRequiredConfigSetCommands(pluginName, issues,
|
|
1500
|
+
function formatRequiredConfigSetCommands(pluginName, issues, context, schema, platform) {
|
|
2039
1501
|
const commands = new Set();
|
|
2040
1502
|
for (const issue of issues) {
|
|
2041
|
-
const scope = issue.source === 'user' || issue.source === 'project'
|
|
2042
|
-
? issue.source
|
|
2043
|
-
: defaultScope;
|
|
2044
1503
|
const field = schema[issue.key];
|
|
2045
|
-
if (
|
|
2046
|
-
|
|
2047
|
-
commands.add(formatPluginConfigSetRecoveryCommand(pluginName, issue.key, field, scope));
|
|
1504
|
+
if (field)
|
|
1505
|
+
commands.add(formatPluginConfigSetRecoveryCommand(pluginName, issue.key, field, context, platform));
|
|
2048
1506
|
}
|
|
2049
1507
|
return [...commands].map((command) => `\`${command}\``).join(' 或 ');
|
|
2050
1508
|
}
|
|
@@ -2057,8 +1515,8 @@ export async function preflightRequiredPluginConfigs(options) {
|
|
|
2057
1515
|
};
|
|
2058
1516
|
for (const target of options.targets) {
|
|
2059
1517
|
const pluginName = target.pluginName ?? target.manifest.name;
|
|
2060
|
-
const identity = target.schemaSourceIdentity ?? createPluginConfigSchemaIdentity(target.manifest);
|
|
2061
|
-
const dedupeKey = `${pluginName}\0${identity}`;
|
|
1518
|
+
const identity = target.schemaSourceIdentity ?? createPluginConfigSchemaIdentity(target.manifest, target.platform);
|
|
1519
|
+
const dedupeKey = `${pluginName}\0${target.platform ?? 'common'}\0${identity}`;
|
|
2062
1520
|
if (visited.has(dedupeKey)) {
|
|
2063
1521
|
continue;
|
|
2064
1522
|
}
|
|
@@ -2067,12 +1525,13 @@ export async function preflightRequiredPluginConfigs(options) {
|
|
|
2067
1525
|
if (!target.manifest.config?.fields) {
|
|
2068
1526
|
continue;
|
|
2069
1527
|
}
|
|
2070
|
-
|
|
1528
|
+
const effective = await resolveEffectivePluginConfig({
|
|
2071
1529
|
sourceId: target.sourceId,
|
|
2072
1530
|
sourceIdentity: target.sourceIdentity,
|
|
2073
1531
|
logicalIdentity: target.logicalIdentity,
|
|
2074
1532
|
pluginName,
|
|
2075
1533
|
manifest: target.manifest,
|
|
1534
|
+
platform: target.platform,
|
|
2076
1535
|
cwd: options.cwd,
|
|
2077
1536
|
env: options.context.env,
|
|
2078
1537
|
});
|
|
@@ -2081,7 +1540,7 @@ export async function preflightRequiredPluginConfigs(options) {
|
|
|
2081
1540
|
continue;
|
|
2082
1541
|
}
|
|
2083
1542
|
const details = blockingIssues.map((issue) => issue.key).join(', ');
|
|
2084
|
-
const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope, target.
|
|
1543
|
+
const setCommands = formatRequiredConfigSetCommands(target.sourceId ? `${target.sourceId}/${pluginName}` : pluginName, blockingIssues, options.defaultScope, effective.schema, target.platform);
|
|
2085
1544
|
throw new Error(`${pluginName} requires valid AW config before install/upgrade: ${details}. ` +
|
|
2086
1545
|
`Run ${setCommands}, or open \`aw\` to complete setup.`);
|
|
2087
1546
|
}
|