@lesliechan721/aw 0.1.0-beta.10 → 0.1.0-beta.12

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.
Files changed (227) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +1 -50
  2. package/dist/authoring/assets/aw-config.mjs +58 -928
  3. package/dist/authoring/assets/aw-config.mjs.map +1 -1
  4. package/dist/authoring/scripts/init-plugin.mjs +28 -33
  5. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  6. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
  8. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  9. package/dist/authoring/scripts/validate-plugin.mjs +159 -48
  10. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  11. package/dist/scripts/bundle-plugins.js +18 -4
  12. package/dist/scripts/bundle-plugins.js.map +1 -1
  13. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  14. package/dist/scripts/test-plugin-scripts.js +21 -5
  15. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  16. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  17. package/dist/src/commands/aw-capabilities.js +1 -1
  18. package/dist/src/commands/aw-capabilities.js.map +1 -1
  19. package/dist/src/commands/catalog-dev-source.d.ts +6 -3
  20. package/dist/src/commands/catalog-dev-source.js +96 -35
  21. package/dist/src/commands/catalog-dev-source.js.map +1 -1
  22. package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
  23. package/dist/src/commands/catalog-publication-inspection.js +54 -8
  24. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  25. package/dist/src/commands/catalog-publication-journal.js +1 -1
  26. package/dist/src/commands/catalog-publication-journal.js.map +1 -1
  27. package/dist/src/commands/catalog-source.d.ts +19 -4
  28. package/dist/src/commands/catalog-source.js +121 -12
  29. package/dist/src/commands/catalog-source.js.map +1 -1
  30. package/dist/src/commands/catalog-store.d.ts +26 -7
  31. package/dist/src/commands/catalog-store.js +361 -42
  32. package/dist/src/commands/catalog-store.js.map +1 -1
  33. package/dist/src/commands/console.js +28 -17
  34. package/dist/src/commands/console.js.map +1 -1
  35. package/dist/src/commands/operation-plan.d.ts +2 -1
  36. package/dist/src/commands/operation-plan.js +24 -0
  37. package/dist/src/commands/operation-plan.js.map +1 -1
  38. package/dist/src/commands/operation-planner.d.ts +2 -2
  39. package/dist/src/commands/operation-planner.js +22 -20
  40. package/dist/src/commands/operation-planner.js.map +1 -1
  41. package/dist/src/commands/options.d.ts +2 -2
  42. package/dist/src/commands/options.js +18 -4
  43. package/dist/src/commands/options.js.map +1 -1
  44. package/dist/src/commands/plugin-capabilities.js +2 -2
  45. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  46. package/dist/src/commands/rebuild-command.d.ts +19 -0
  47. package/dist/src/commands/rebuild-command.js +828 -283
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +25 -47
  50. package/dist/src/commands/rebuild-integration.js +1176 -1016
  51. package/dist/src/commands/rebuild-integration.js.map +1 -1
  52. package/dist/src/commands/rebuild-operations.d.ts +50 -12
  53. package/dist/src/commands/rebuild-operations.js +207 -101
  54. package/dist/src/commands/rebuild-operations.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +21 -2
  56. package/dist/src/commands/status-v6.js +163 -31
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/sync-command.d.ts +17 -0
  59. package/dist/src/commands/sync-command.js +45 -37
  60. package/dist/src/commands/sync-command.js.map +1 -1
  61. package/dist/src/commands/write-commands.d.ts +5 -0
  62. package/dist/src/commands/write-commands.js +50 -3
  63. package/dist/src/commands/write-commands.js.map +1 -1
  64. package/dist/src/generators/local-plugin.js +14 -4
  65. package/dist/src/generators/local-plugin.js.map +1 -1
  66. package/dist/src/index.js +260 -91
  67. package/dist/src/index.js.map +1 -1
  68. package/dist/src/output/command-receipt.js +1 -3
  69. package/dist/src/output/command-receipt.js.map +1 -1
  70. package/dist/src/output/rebuild-progress.js +1 -3
  71. package/dist/src/output/rebuild-progress.js.map +1 -1
  72. package/dist/src/portable/materializer.js +10 -8
  73. package/dist/src/portable/materializer.js.map +1 -1
  74. package/dist/src/tui/catalog-generation.js +41 -1
  75. package/dist/src/tui/catalog-generation.js.map +1 -1
  76. package/dist/src/tui/catalog-sources/session.d.ts +1 -0
  77. package/dist/src/tui/catalog-sources/session.js +16 -3
  78. package/dist/src/tui/catalog-sources/session.js.map +1 -1
  79. package/dist/src/tui/catalog-sources/types.d.ts +3 -2
  80. package/dist/src/tui/components.d.ts +4 -0
  81. package/dist/src/tui/components.js +36 -24
  82. package/dist/src/tui/components.js.map +1 -1
  83. package/dist/src/tui/console-app-v4.d.ts +13 -4
  84. package/dist/src/tui/console-app-v4.js +2065 -857
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +731 -62
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +156 -10
  89. package/dist/src/tui/controller.js +1187 -163
  90. package/dist/src/tui/controller.js.map +1 -1
  91. package/dist/src/tui/diagnostic-projection.d.ts +22 -0
  92. package/dist/src/tui/diagnostic-projection.js +437 -0
  93. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  94. package/dist/src/tui/diagnostics.d.ts +1 -1
  95. package/dist/src/tui/diagnostics.js +7 -2
  96. package/dist/src/tui/diagnostics.js.map +1 -1
  97. package/dist/src/tui/glossary.d.ts +23 -0
  98. package/dist/src/tui/glossary.js +42 -0
  99. package/dist/src/tui/glossary.js.map +1 -0
  100. package/dist/src/tui/index.d.ts +4 -1
  101. package/dist/src/tui/index.js +4 -1
  102. package/dist/src/tui/index.js.map +1 -1
  103. package/dist/src/tui/keys.d.ts +44 -0
  104. package/dist/src/tui/keys.js +43 -15
  105. package/dist/src/tui/keys.js.map +1 -1
  106. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  107. package/dist/src/tui/lifecycle-action-view.js +34 -0
  108. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  109. package/dist/src/tui/model.d.ts +12 -2
  110. package/dist/src/tui/model.js +128 -52
  111. package/dist/src/tui/model.js.map +1 -1
  112. package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
  113. package/dist/src/tui/operation-diagnostic-screens.js +101 -0
  114. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  115. package/dist/src/tui/operation-history.d.ts +80 -0
  116. package/dist/src/tui/operation-history.js +130 -0
  117. package/dist/src/tui/operation-history.js.map +1 -0
  118. package/dist/src/tui/operation-review-planner.js +366 -99
  119. package/dist/src/tui/operation-review-planner.js.map +1 -1
  120. package/dist/src/tui/router.d.ts +82 -17
  121. package/dist/src/tui/router.js +186 -38
  122. package/dist/src/tui/router.js.map +1 -1
  123. package/dist/src/tui/screen-registry.d.ts +546 -0
  124. package/dist/src/tui/screen-registry.js +179 -0
  125. package/dist/src/tui/screen-registry.js.map +1 -0
  126. package/dist/src/tui/session-console-app.d.ts +6 -0
  127. package/dist/src/tui/session-console-app.js +13 -0
  128. package/dist/src/tui/session-console-app.js.map +1 -0
  129. package/dist/src/tui/session-host.d.ts +44 -0
  130. package/dist/src/tui/session-host.js +197 -0
  131. package/dist/src/tui/session-host.js.map +1 -0
  132. package/dist/src/types.d.ts +64 -3
  133. package/dist/src/types.js +1 -1
  134. package/dist/src/types.js.map +1 -1
  135. package/dist/src/utils/agent-version.js +1 -1
  136. package/dist/src/utils/agent-version.js.map +1 -1
  137. package/dist/src/utils/artifact-plan.d.ts +2 -2
  138. package/dist/src/utils/artifact-plan.js +8 -6
  139. package/dist/src/utils/artifact-plan.js.map +1 -1
  140. package/dist/src/utils/artifact-renderer.d.ts +2 -1
  141. package/dist/src/utils/artifact-renderer.js +139 -120
  142. package/dist/src/utils/artifact-renderer.js.map +1 -1
  143. package/dist/src/utils/artifact-request.d.ts +5 -0
  144. package/dist/src/utils/artifact-request.js +40 -0
  145. package/dist/src/utils/artifact-request.js.map +1 -1
  146. package/dist/src/utils/bundle-layout.js +4 -4
  147. package/dist/src/utils/bundle-layout.js.map +1 -1
  148. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  149. package/dist/src/utils/catalog-capability-projection.js +14 -0
  150. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  151. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  152. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  153. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  154. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  155. package/dist/src/utils/catalog-generation-service.js +81 -47
  156. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  157. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  158. package/dist/src/utils/catalog-generation-store.js +60 -5
  159. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  160. package/dist/src/utils/catalog-operation-context.js +1 -0
  161. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  162. package/dist/src/utils/catalog-public-error.js +4 -0
  163. package/dist/src/utils/catalog-public-error.js.map +1 -1
  164. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  165. package/dist/src/utils/catalog-source-impact.js +142 -20
  166. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  167. package/dist/src/utils/catalog-source-references.js +95 -9
  168. package/dist/src/utils/catalog-source-references.js.map +1 -1
  169. package/dist/src/utils/catalog-sources.d.ts +1 -1
  170. package/dist/src/utils/catalog-sources.js +19 -3
  171. package/dist/src/utils/catalog-sources.js.map +1 -1
  172. package/dist/src/utils/codex-manager.js +8 -0
  173. package/dist/src/utils/codex-manager.js.map +1 -1
  174. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  175. package/dist/src/utils/codex-native-observation.js +132 -0
  176. package/dist/src/utils/codex-native-observation.js.map +1 -1
  177. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  178. package/dist/src/utils/codex-reconciler.js.map +1 -1
  179. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  180. package/dist/src/utils/codex-shared-native.js +8 -82
  181. package/dist/src/utils/codex-shared-native.js.map +1 -1
  182. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  183. package/dist/src/utils/git-catalog-provider.js +40 -1
  184. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  185. package/dist/src/utils/local-marketplace.d.ts +1 -0
  186. package/dist/src/utils/local-marketplace.js +69 -73
  187. package/dist/src/utils/local-marketplace.js.map +1 -1
  188. package/dist/src/utils/multi-source-catalog.js +1 -0
  189. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  190. package/dist/src/utils/native-cli.d.ts +9 -4
  191. package/dist/src/utils/native-cli.js +280 -34
  192. package/dist/src/utils/native-cli.js.map +1 -1
  193. package/dist/src/utils/path-catalog-provider.js +2 -0
  194. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  195. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  196. package/dist/src/utils/plugin-config-impact.js +298 -0
  197. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  198. package/dist/src/utils/plugin-config-write.d.ts +36 -14
  199. package/dist/src/utils/plugin-config-write.js +709 -225
  200. package/dist/src/utils/plugin-config-write.js.map +1 -1
  201. package/dist/src/utils/plugin-config.d.ts +58 -4
  202. package/dist/src/utils/plugin-config.js +328 -60
  203. package/dist/src/utils/plugin-config.js.map +1 -1
  204. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  205. package/dist/src/utils/plugin-scripts.js +389 -114
  206. package/dist/src/utils/plugin-scripts.js.map +1 -1
  207. package/dist/src/utils/process-tree.d.ts +2 -2
  208. package/dist/src/utils/process-tree.js +1 -1
  209. package/dist/src/utils/process-tree.js.map +1 -1
  210. package/dist/src/utils/project-boundary.d.ts +11 -1
  211. package/dist/src/utils/project-boundary.js +33 -11
  212. package/dist/src/utils/project-boundary.js.map +1 -1
  213. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  214. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  215. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  216. package/dist/src/utils/sensitive-content.d.ts +4 -0
  217. package/dist/src/utils/sensitive-content.js +36 -0
  218. package/dist/src/utils/sensitive-content.js.map +1 -0
  219. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  220. package/dist/src/utils/windows-claude-shim.js +141 -0
  221. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  222. package/dist/vitest.config.js +91 -10
  223. package/dist/vitest.config.js.map +1 -1
  224. package/package.json +9 -9
  225. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  226. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  227. package/dist/authoring/templates/aw-config-runtime.mjs.map +0 -1
@@ -6,6 +6,7 @@ import { canonicalizePluginConfigValues, formatPluginConfigIssue, matchPluginCon
6
6
  import { assertTestReadablePath, assertTestWritablePath, } from './test-path-fence.js';
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';
9
10
  export function codexNativeRefreshKeys(manifest) {
10
11
  return new Set((manifest.artifacts?.targets ?? []).flatMap((target) => target.platform === 'codex'
11
12
  && target.target === 'codex.skill'
@@ -13,6 +14,51 @@ export function codexNativeRefreshKeys(manifest) {
13
14
  ? target.refreshKeys
14
15
  : []));
15
16
  }
17
+ function readPluginConfigBinding(value, scope) {
18
+ if (!isRecord(value))
19
+ return null;
20
+ if (scope === 'user') {
21
+ return value.kind === 'source'
22
+ && typeof value.sourceIdentity === 'string'
23
+ && /^sha256:[0-9a-f]{64}$/u.test(value.sourceIdentity)
24
+ && Object.keys(value).every((key) => ['kind', 'sourceIdentity'].includes(key))
25
+ ? { kind: 'source', sourceIdentity: value.sourceIdentity }
26
+ : null;
27
+ }
28
+ return value.kind === 'catalog'
29
+ && typeof value.logicalIdentity === 'string'
30
+ && value.logicalIdentity.trim().length > 0
31
+ && Object.keys(value).every((key) => ['kind', 'logicalIdentity'].includes(key))
32
+ ? { kind: 'catalog', logicalIdentity: value.logicalIdentity }
33
+ : null;
34
+ }
35
+ const PLUGIN_CONFIG_ENVELOPE_V4_KEYS = new Set([
36
+ 'schemaVersion',
37
+ 'sourceId',
38
+ 'pluginName',
39
+ 'binding',
40
+ 'values',
41
+ ]);
42
+ function readPluginConfigEnvelopeV4(value, scope) {
43
+ if (!isRecord(value)
44
+ || value.schemaVersion !== 4
45
+ || typeof value.sourceId !== 'string'
46
+ || typeof value.pluginName !== 'string'
47
+ || !isRecord(value.values)
48
+ || Object.keys(value).some((key) => !PLUGIN_CONFIG_ENVELOPE_V4_KEYS.has(key))) {
49
+ return null;
50
+ }
51
+ const binding = readPluginConfigBinding(value.binding, scope);
52
+ if (!binding)
53
+ return null;
54
+ return {
55
+ schemaVersion: 4,
56
+ sourceId: value.sourceId,
57
+ pluginName: value.pluginName,
58
+ binding,
59
+ values: value.values,
60
+ };
61
+ }
16
62
  export class PluginConfigResetError extends Error {
17
63
  code;
18
64
  osCode;
@@ -356,17 +402,33 @@ async function readNamespacedStoreFile(options) {
356
402
  if (parsed.state === 'invalidJson')
357
403
  return parsed.store;
358
404
  const raw = parsed.value;
359
- if (!isRecord(raw) || raw.schemaVersion !== 3) {
360
- return invalidStore(options.filePath, 'schemaVersionUnsupported', 'Only plugin config store schemaVersion 3 is supported.', parsed.rawDigest);
405
+ if (!isRecord(raw) || raw.schemaVersion !== 4) {
406
+ return invalidStore(options.filePath, 'schemaVersionUnsupported', 'Only plugin config store schemaVersion 4 is supported.', parsed.rawDigest);
361
407
  }
362
- if (raw.sourceId !== options.sourceId
363
- || raw.pluginName !== options.pluginName
364
- || raw.sourceIdentity !== options.sourceIdentity) {
408
+ const envelope = readPluginConfigEnvelopeV4(raw, options.scope);
409
+ if (!envelope) {
410
+ return invalidStore(options.filePath, 'schemaVersionUnsupported', 'The plugin config store schemaVersion 4 envelope is invalid.', parsed.rawDigest);
411
+ }
412
+ if (envelope.sourceId !== options.sourceId
413
+ || envelope.pluginName !== options.pluginName) {
365
414
  return invalidStore(options.filePath, 'identityMismatch', 'The store identity does not match its source and namespaced path.', parsed.rawDigest);
366
415
  }
416
+ const binding = envelope.binding;
417
+ const matchesBinding = options.scope === 'user'
418
+ ? binding?.kind === 'source'
419
+ && typeof options.sourceIdentity === 'string'
420
+ && binding.sourceIdentity === options.sourceIdentity
421
+ : binding?.kind === 'catalog'
422
+ && typeof options.logicalIdentity === 'string'
423
+ && binding.logicalIdentity === options.logicalIdentity;
424
+ if (!matchesBinding) {
425
+ return invalidStore(options.filePath, 'identityMismatch', options.scope === 'user'
426
+ ? 'The user store is not bound to the requested source identity.'
427
+ : 'The project store is not bound to the requested Catalog logical identity.', parsed.rawDigest);
428
+ }
367
429
  return validateStoreValues({
368
430
  filePath: options.filePath,
369
- values: raw.values,
431
+ values: envelope.values,
370
432
  rawDigest: parsed.rawDigest,
371
433
  });
372
434
  }
@@ -374,14 +436,18 @@ function namespacedConfigContent(options) {
374
436
  const values = canonicalizePluginConfigValues(options.values);
375
437
  if (!options.sourceId)
376
438
  return `${JSON.stringify(values, null, 2)}\n`;
377
- if (!options.sourceIdentity) {
378
- throw new Error('sourceChanged: namespaced plugin config requires a source identity.');
379
- }
439
+ const binding = options.scope === 'user'
440
+ ? options.sourceIdentity
441
+ ? { kind: 'source', sourceIdentity: options.sourceIdentity }
442
+ : (() => { throw new Error('sourceChanged: user plugin config requires a source identity.'); })()
443
+ : options.logicalIdentity
444
+ ? { kind: 'catalog', logicalIdentity: options.logicalIdentity }
445
+ : (() => { throw new Error('catalogUnavailable: project plugin config requires verified Catalog logical identity evidence.'); })();
380
446
  const envelope = {
381
- schemaVersion: 3,
447
+ schemaVersion: 4,
382
448
  sourceId: options.sourceId,
383
- sourceIdentity: options.sourceIdentity,
384
449
  pluginName: options.pluginName,
450
+ binding,
385
451
  values,
386
452
  };
387
453
  return `${JSON.stringify(envelope, null, 2)}\n`;
@@ -431,7 +497,9 @@ export async function readPluginConfigStore(options) {
431
497
  ? readNamespacedStoreFile({
432
498
  filePath,
433
499
  sourceId: options.sourceId,
500
+ scope: options.scope,
434
501
  sourceIdentity: options.sourceIdentity,
502
+ logicalIdentity: options.logicalIdentity,
435
503
  pluginName: options.pluginName,
436
504
  })
437
505
  : readStoreFile(filePath);
@@ -447,16 +515,16 @@ export async function readNamespacedPluginConfigStoreIdentity(options) {
447
515
  if (parsed.state === 'invalidJson') {
448
516
  throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store.`);
449
517
  }
450
- const raw = parsed.value;
451
- if (!isRecord(raw) || raw.schemaVersion !== 3 || !isRecord(raw.values)
452
- || raw.sourceId !== options.sourceId || raw.pluginName !== options.pluginName
453
- || typeof raw.sourceIdentity !== 'string') {
518
+ const envelope = readPluginConfigEnvelopeV4(parsed.value, options.scope);
519
+ if (!envelope
520
+ || envelope.sourceId !== options.sourceId
521
+ || envelope.pluginName !== options.pluginName) {
454
522
  throw new Error('schemaVersionUnsupported: invalid namespaced plugin config store.');
455
523
  }
456
- const store = validateStoreValues({ filePath, values: raw.values });
524
+ const store = validateStoreValues({ filePath, values: envelope.values });
457
525
  assertReadyPluginConfigStore(store);
458
526
  return {
459
- sourceIdentity: raw.sourceIdentity,
527
+ binding: envelope.binding,
460
528
  store,
461
529
  };
462
530
  }
@@ -496,6 +564,64 @@ export async function listPluginConfigStorePluginNames(options) {
496
564
  const pluginNames = (await Promise.all(storeRoots.map((storeRoot) => listConfigStoreDirectoryPluginNames(storeRoot)))).flat();
497
565
  return [...new Set(pluginNames)].sort((left, right) => left.localeCompare(right));
498
566
  }
567
+ export async function listNamespacedPluginConfigStoreIdentities(options) {
568
+ const boundaryRoot = options.scope === 'user'
569
+ ? resolveAwHome(options.env)
570
+ : resolveAwProjectRoot(options.cwd);
571
+ if (options.scope === 'user') {
572
+ await canonicalizePathThroughExistingAncestor(boundaryRoot);
573
+ }
574
+ else {
575
+ await fs.realpath(boundaryRoot);
576
+ }
577
+ const storeRoot = path.join(boundaryRoot, ...(options.scope === 'project' ? ['.agent-workflows'] : []), CONFIG_DIR);
578
+ if (!await inspectConfigInventoryStoreRoot(boundaryRoot, storeRoot))
579
+ return [];
580
+ const output = [];
581
+ for (const sourceEntry of await fs.readdir(storeRoot, { withFileTypes: true })) {
582
+ if (!sourceEntry.isDirectory() || sourceEntry.isSymbolicLink())
583
+ continue;
584
+ let sourceId;
585
+ try {
586
+ sourceId = assertSafePluginConfigSegment(sourceEntry.name);
587
+ }
588
+ catch {
589
+ continue;
590
+ }
591
+ const sourceRoot = path.join(storeRoot, sourceId);
592
+ let pluginEntries;
593
+ try {
594
+ await assertNoConfigSymlink(boundaryRoot, sourceRoot);
595
+ pluginEntries = await fs.readdir(sourceRoot, { withFileTypes: true });
596
+ }
597
+ catch {
598
+ continue;
599
+ }
600
+ for (const pluginEntry of pluginEntries) {
601
+ if (!pluginEntry.isDirectory() || pluginEntry.isSymbolicLink())
602
+ continue;
603
+ let pluginName;
604
+ try {
605
+ pluginName = assertSafePluginConfigSegment(pluginEntry.name);
606
+ await assertNoConfigSymlink(boundaryRoot, path.join(sourceRoot, pluginName));
607
+ }
608
+ catch {
609
+ continue;
610
+ }
611
+ let target;
612
+ try {
613
+ target = await lstatIfExists(path.join(sourceRoot, pluginName, CONFIG_FILE));
614
+ }
615
+ catch {
616
+ continue;
617
+ }
618
+ if (!target)
619
+ continue;
620
+ output.push({ sourceId, pluginName, pluginId: `${sourceId}/${pluginName}` });
621
+ }
622
+ }
623
+ return output.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
624
+ }
499
625
  export async function listNamespacedPluginConfigStores(options) {
500
626
  const boundaryRoot = options.scope === 'user'
501
627
  ? resolveAwHome(options.env)
@@ -532,17 +658,17 @@ export async function listNamespacedPluginConfigStores(options) {
532
658
  if (parsed.state === 'invalidJson') {
533
659
  throw new Error(`${parsed.store.invalidCode}: invalid namespaced plugin config store "${filePath}".`);
534
660
  }
535
- const raw = parsed.value;
536
- if (!isRecord(raw) || raw.schemaVersion !== 3 || !isRecord(raw.values)
537
- || raw.sourceId !== sourceId || raw.pluginName !== pluginName
538
- || typeof raw.sourceIdentity !== 'string') {
661
+ const envelope = readPluginConfigEnvelopeV4(parsed.value, options.scope);
662
+ if (!envelope
663
+ || envelope.sourceId !== sourceId
664
+ || envelope.pluginName !== pluginName) {
539
665
  throw new Error(`schemaVersionUnsupported: invalid namespaced plugin config store "${filePath}".`);
540
666
  }
541
- const store = validateStoreValues({ filePath, values: raw.values });
667
+ const store = validateStoreValues({ filePath, values: envelope.values });
542
668
  assertReadyPluginConfigStore(store);
543
669
  output.push({
544
670
  sourceId,
545
- sourceIdentity: raw.sourceIdentity,
671
+ binding: envelope.binding,
546
672
  pluginName,
547
673
  pluginId: `${sourceId}/${pluginName}`,
548
674
  values: store.values,
@@ -560,22 +686,37 @@ export async function writePluginConfigStore(options) {
560
686
  const allowedRoot = resolvePluginConfigAllowedRoot(options);
561
687
  const validated = validateStoreValues({ filePath, values: options.values });
562
688
  assertReadyPluginConfigStore(validated);
689
+ const warnings = [];
563
690
  await withConfigFileMutation({
564
691
  filePath,
565
692
  allowedRoot,
566
- action: async () => Object.keys(validated.values).length === 0
567
- ? atomicRemoveConfigFile({ filePath, allowedRoot })
568
- : atomicReplaceConfigFile({
693
+ action: async () => {
694
+ if (Object.keys(validated.values).length === 0) {
695
+ return atomicRemoveConfigFile({ filePath, allowedRoot });
696
+ }
697
+ if (options.scope === 'project') {
698
+ const visibility = await assertProjectSharedConfigPathVisible({
699
+ cwd: options.cwd,
700
+ targetPath: filePath,
701
+ env: options.env,
702
+ });
703
+ if (visibility?.state === 'unverified') {
704
+ warnings.push(PROJECT_CONFIG_SHAREABILITY_UNVERIFIED_WARNING);
705
+ }
706
+ }
707
+ return atomicReplaceConfigFile({
569
708
  filePath,
570
709
  allowedRoot,
571
710
  content: namespacedConfigContent({ ...options, values: validated.values }),
572
711
  mode: 0o600,
573
- }),
712
+ });
713
+ },
574
714
  });
575
715
  return {
576
716
  pluginName: options.pluginName,
577
717
  scope: options.scope,
578
718
  path: filePath,
719
+ warnings,
579
720
  };
580
721
  }
581
722
  export async function patchPluginConfigStore(options) {
@@ -586,6 +727,7 @@ export async function patchPluginConfigStore(options) {
586
727
  });
587
728
  const allowedRoot = resolvePluginConfigAllowedRoot(options);
588
729
  const removedKeys = options.removeKeys ?? [];
730
+ const warnings = [];
589
731
  await withConfigFileMutation({
590
732
  filePath,
591
733
  allowedRoot,
@@ -596,7 +738,9 @@ export async function patchPluginConfigStore(options) {
596
738
  ? await readNamespacedStoreFile({
597
739
  filePath,
598
740
  sourceId: options.sourceId,
741
+ scope: options.scope,
599
742
  sourceIdentity: options.sourceIdentity,
743
+ logicalIdentity: options.logicalIdentity,
600
744
  pluginName: options.pluginName,
601
745
  })
602
746
  : await readStoreFile(filePath)
@@ -618,6 +762,16 @@ export async function patchPluginConfigStore(options) {
618
762
  await atomicRemoveConfigFile({ filePath, allowedRoot });
619
763
  }
620
764
  else {
765
+ if (options.scope === 'project') {
766
+ const visibility = await assertProjectSharedConfigPathVisible({
767
+ cwd: options.cwd,
768
+ targetPath: filePath,
769
+ env: options.env,
770
+ });
771
+ if (visibility?.state === 'unverified') {
772
+ warnings.push(PROJECT_CONFIG_SHAREABILITY_UNVERIFIED_WARNING);
773
+ }
774
+ }
621
775
  await atomicReplaceConfigFile({
622
776
  filePath,
623
777
  allowedRoot,
@@ -631,6 +785,7 @@ export async function patchPluginConfigStore(options) {
631
785
  pluginName: options.pluginName,
632
786
  scope: options.scope,
633
787
  path: filePath,
788
+ warnings,
634
789
  updatedKeys: Object.keys(options.updates),
635
790
  removedKeys,
636
791
  };
@@ -847,6 +1002,13 @@ function coerceConfigValue(key, field, value) {
847
1002
  export async function coercePluginConfigValue(options) {
848
1003
  return coerceConfigValue(options.key, options.field, options.value);
849
1004
  }
1005
+ function validateConfigReadValue(key, field, value) {
1006
+ const issues = validateCorePluginConfigValue(field, value, key);
1007
+ if (issues.length > 0) {
1008
+ throw new Error(formatPluginConfigIssue(issues[0]));
1009
+ }
1010
+ return value;
1011
+ }
850
1012
  function redactValue(field, value) {
851
1013
  return redactPluginConfigValue(field, value);
852
1014
  }
@@ -897,6 +1059,7 @@ export async function resolveEffectivePluginConfig(options) {
897
1059
  const userStore = await readPluginConfigStore({
898
1060
  sourceId: options.sourceId,
899
1061
  sourceIdentity: options.sourceIdentity,
1062
+ logicalIdentity: options.logicalIdentity,
900
1063
  pluginName,
901
1064
  scope: 'user',
902
1065
  cwd: options.cwd,
@@ -907,12 +1070,38 @@ export async function resolveEffectivePluginConfig(options) {
907
1070
  : await readPluginConfigStore({
908
1071
  sourceId: options.sourceId,
909
1072
  sourceIdentity: options.sourceIdentity,
1073
+ logicalIdentity: options.logicalIdentity,
910
1074
  pluginName,
911
1075
  scope: 'project',
912
1076
  cwd: options.cwd,
913
1077
  env: options.env,
914
1078
  });
915
1079
  assertReadyPluginConfigStore(userStore);
1080
+ if (projectStore)
1081
+ assertReadyPluginConfigStore(projectStore);
1082
+ return resolveEffectivePluginConfigFromStores({
1083
+ pluginName,
1084
+ manifest: options.manifest,
1085
+ targetScope: options.targetScope,
1086
+ projectRoot,
1087
+ stores: {
1088
+ user: userStore,
1089
+ ...(projectStore ? { project: projectStore } : {}),
1090
+ },
1091
+ });
1092
+ }
1093
+ export async function resolveEffectivePluginConfigFromStores(options) {
1094
+ const pluginName = assertSafePluginConfigSegment(options.pluginName ?? options.manifest.name);
1095
+ const schema = options.manifest.config?.fields;
1096
+ if (!schema) {
1097
+ throw new Error(`Plugin "${pluginName}" does not declare an AW config schema.`);
1098
+ }
1099
+ const userStore = options.stores.user;
1100
+ const projectStore = options.targetScope === 'user' ? undefined : options.stores.project;
1101
+ assertReadyPluginConfigStore(userStore);
1102
+ if (!projectStore && options.targetScope !== 'user') {
1103
+ throw new Error(`Project plugin config store is required for ${pluginName}.`);
1104
+ }
916
1105
  if (projectStore)
917
1106
  assertReadyPluginConfigStore(projectStore);
918
1107
  const values = {};
@@ -920,8 +1109,10 @@ export async function resolveEffectivePluginConfig(options) {
920
1109
  const sources = {};
921
1110
  const fieldStates = {};
922
1111
  const ignoredUnknownKeys = [];
1112
+ const unsupportedLayerOverrides = [];
923
1113
  const dangerousKeyIssues = [];
924
1114
  const schemaKeys = new Set(Object.keys(schema));
1115
+ const userOnlyKeys = codexNativeRefreshKeys(options.manifest);
925
1116
  for (const key of schemaKeys) {
926
1117
  sources[key] = 'missing';
927
1118
  }
@@ -967,13 +1158,26 @@ export async function resolveEffectivePluginConfig(options) {
967
1158
  }
968
1159
  }
969
1160
  }
1161
+ const supportedEntries = scope === 'project'
1162
+ ? Object.fromEntries(Object.entries(plainEntries).filter(([key]) => {
1163
+ if (!schemaKeys.has(key) || !userOnlyKeys.has(key))
1164
+ return true;
1165
+ unsupportedLayerOverrides.push({
1166
+ type: 'configLayerUnsupported',
1167
+ key,
1168
+ scope: 'project',
1169
+ source: 'project',
1170
+ });
1171
+ return false;
1172
+ }))
1173
+ : plainEntries;
970
1174
  await applyConfigLayer({
971
1175
  values,
972
1176
  redactedValues,
973
1177
  sources,
974
1178
  fieldStates,
975
1179
  schema,
976
- entries: plainEntries,
1180
+ entries: supportedEntries,
977
1181
  source,
978
1182
  });
979
1183
  }
@@ -993,12 +1197,13 @@ export async function resolveEffectivePluginConfig(options) {
993
1197
  }
994
1198
  return {
995
1199
  pluginName,
996
- projectRoot,
1200
+ projectRoot: options.projectRoot,
997
1201
  values,
998
1202
  redactedValues,
999
1203
  sources,
1000
1204
  fieldStates,
1001
1205
  issues,
1206
+ unsupportedLayerOverrides,
1002
1207
  ignoredUnknownKeys,
1003
1208
  schema,
1004
1209
  stores: {
@@ -1030,6 +1235,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1030
1235
  readPluginConfigStore({
1031
1236
  sourceId: options.sourceId,
1032
1237
  sourceIdentity: options.sourceIdentity,
1238
+ logicalIdentity: options.logicalIdentity,
1033
1239
  pluginName,
1034
1240
  scope: 'user',
1035
1241
  cwd: options.cwd,
@@ -1038,6 +1244,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1038
1244
  readPluginConfigStore({
1039
1245
  sourceId: options.sourceId,
1040
1246
  sourceIdentity: options.sourceIdentity,
1247
+ logicalIdentity: options.logicalIdentity,
1041
1248
  pluginName,
1042
1249
  scope: 'project',
1043
1250
  cwd: options.cwd,
@@ -1052,6 +1259,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1052
1259
  values: {},
1053
1260
  sources: {},
1054
1261
  issues: [],
1262
+ unsupportedLayerOverrides: [],
1055
1263
  ignoredUnknownKeys: [],
1056
1264
  schema: {},
1057
1265
  stores: { user: userStore, project: projectStore },
@@ -1067,6 +1275,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1067
1275
  values: effective.values,
1068
1276
  sources: effective.sources,
1069
1277
  issues: effective.issues,
1278
+ unsupportedLayerOverrides: effective.unsupportedLayerOverrides,
1070
1279
  ignoredUnknownKeys: effective.ignoredUnknownKeys,
1071
1280
  schema: effective.schema,
1072
1281
  stores: {
@@ -1077,13 +1286,9 @@ export async function resolveProjectArtifactRenderConfig(options) {
1077
1286
  }
1078
1287
  async function resolveRedactedLayerValue(options) {
1079
1288
  try {
1080
- const coerced = await coercePluginConfigValue({
1081
- key: options.key,
1082
- field: options.field,
1083
- value: options.value,
1084
- });
1289
+ const value = validateConfigReadValue(options.key, options.field, options.value);
1085
1290
  return {
1086
- value: redactValue(options.field, coerced),
1291
+ value: redactValue(options.field, value),
1087
1292
  };
1088
1293
  }
1089
1294
  catch (error) {
@@ -1155,6 +1360,7 @@ export async function resolveProjectScopedPluginConfigView(options) {
1155
1360
  sources: effective.sources,
1156
1361
  entries,
1157
1362
  issues: effective.issues,
1363
+ unsupportedLayerOverrides: effective.unsupportedLayerOverrides,
1158
1364
  ignoredUnknownKeys: effective.ignoredUnknownKeys,
1159
1365
  };
1160
1366
  }
@@ -1196,6 +1402,7 @@ async function buildManifestFieldEntries(options) {
1196
1402
  const userStore = await readPluginConfigStore({
1197
1403
  sourceId: options.plugin.sourceId,
1198
1404
  sourceIdentity: options.plugin.sourceIdentity,
1405
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1199
1406
  pluginName: options.plugin.manifest.name,
1200
1407
  scope: 'user',
1201
1408
  cwd: options.cwd,
@@ -1207,6 +1414,7 @@ async function buildManifestFieldEntries(options) {
1207
1414
  : await readPluginConfigStore({
1208
1415
  sourceId: options.plugin.sourceId,
1209
1416
  sourceIdentity: options.plugin.sourceIdentity,
1417
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1210
1418
  pluginName: options.plugin.manifest.name,
1211
1419
  scope: 'project',
1212
1420
  cwd: options.cwd,
@@ -1221,6 +1429,7 @@ async function buildManifestFieldEntries(options) {
1221
1429
  const effective = await resolveEffectivePluginConfig({
1222
1430
  sourceId: options.plugin.sourceId,
1223
1431
  sourceIdentity: options.plugin.sourceIdentity,
1432
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1224
1433
  pluginName: options.plugin.manifest.name,
1225
1434
  manifest: options.plugin.manifest,
1226
1435
  targetScope: options.scope === 'user' ? 'user' : 'project',
@@ -1228,12 +1437,65 @@ async function buildManifestFieldEntries(options) {
1228
1437
  env: options.env,
1229
1438
  });
1230
1439
  const userOnlyKeys = codexNativeRefreshKeys(options.plugin.manifest);
1440
+ const projectRemovalFallback = (key) => {
1441
+ const field = schema[key];
1442
+ if (Object.hasOwn(userPlainEntries, key)) {
1443
+ const value = userPlainEntries[key];
1444
+ const validationIssues = validateCorePluginConfigValue(field, value, key);
1445
+ if (validationIssues.length > 0) {
1446
+ return {
1447
+ state: 'invalid',
1448
+ sourceLayer: 'user',
1449
+ issues: validationIssues.map((issue) => ({
1450
+ type: 'invalid-config',
1451
+ key,
1452
+ source: 'user',
1453
+ message: formatPluginConfigIssue(issue),
1454
+ })),
1455
+ };
1456
+ }
1457
+ return {
1458
+ state: 'valid',
1459
+ effectiveValue: redactValue(field, value),
1460
+ sourceLayer: 'user',
1461
+ };
1462
+ }
1463
+ if (field.default === undefined)
1464
+ return { state: 'missing' };
1465
+ const validationIssues = validateCorePluginConfigValue(field, field.default, key);
1466
+ if (validationIssues.length > 0) {
1467
+ return {
1468
+ state: 'invalid',
1469
+ sourceLayer: 'default',
1470
+ issues: validationIssues.map((issue) => ({
1471
+ type: 'invalid-config',
1472
+ key,
1473
+ source: 'default',
1474
+ message: formatPluginConfigIssue(issue),
1475
+ })),
1476
+ };
1477
+ }
1478
+ return {
1479
+ state: 'valid',
1480
+ effectiveValue: redactValue(field, field.default),
1481
+ sourceLayer: 'default',
1482
+ };
1483
+ };
1231
1484
  const effectiveFieldState = (key) => {
1232
1485
  const source = effective.sources[key];
1233
1486
  const sourceLayer = toConfigSourceLayer(source);
1234
1487
  const field = schema[key];
1235
1488
  const fieldIssues = effective.issues.filter((entry) => entry.key === key);
1236
1489
  const fieldState = effective.fieldStates[key];
1490
+ const fallback = options.scope === 'project'
1491
+ ? projectRemovalFallback(key)
1492
+ : fieldState?.fallback
1493
+ ? {
1494
+ state: 'valid',
1495
+ effectiveValue: fieldState.fallback.redactedValue,
1496
+ sourceLayer: fieldState.fallback.source,
1497
+ }
1498
+ : undefined;
1237
1499
  if (source === undefined || source === 'missing') {
1238
1500
  return {
1239
1501
  valueState: fieldIssues.length > 0 ? 'invalid' : 'default',
@@ -1253,12 +1515,7 @@ async function buildManifestFieldEntries(options) {
1253
1515
  issues: fieldState.invalidOverride.issues,
1254
1516
  },
1255
1517
  } : {}),
1256
- ...(fieldState?.fallback ? {
1257
- fallback: {
1258
- effectiveValue: fieldState.fallback.redactedValue,
1259
- sourceLayer: fieldState.fallback.source,
1260
- },
1261
- } : {}),
1518
+ ...(fallback ? { fallback } : {}),
1262
1519
  ...configFieldProjection({
1263
1520
  field,
1264
1521
  value: issue ? undefined : effective.values[key],
@@ -1269,12 +1526,18 @@ async function buildManifestFieldEntries(options) {
1269
1526
  };
1270
1527
  const fields = [];
1271
1528
  for (const [key, field] of Object.entries(schema)) {
1529
+ const canSet = options.scope !== 'project'
1530
+ || (!userOnlyKeys.has(key) && field.sensitive !== true);
1272
1531
  const base = {
1273
1532
  key,
1274
1533
  title: field.title ?? key,
1275
1534
  kind: 'manifest',
1276
1535
  reserved: false,
1277
- writable: options.scope !== 'project' || !userOnlyKeys.has(key),
1536
+ writable: canSet,
1537
+ canSet,
1538
+ canUnset: options.scope === 'user'
1539
+ ? Object.hasOwn(userPlainEntries, key)
1540
+ : options.scope === 'project' && Object.hasOwn(projectPlainEntries, key),
1278
1541
  valueState: 'default',
1279
1542
  effectiveValue: null,
1280
1543
  sourceLayer: null,
@@ -1283,11 +1546,7 @@ async function buildManifestFieldEntries(options) {
1283
1546
  if (options.scope === 'user') {
1284
1547
  if (Object.hasOwn(userPlainEntries, key)) {
1285
1548
  try {
1286
- const value = await coercePluginConfigValue({
1287
- key,
1288
- field,
1289
- value: userPlainEntries[key],
1290
- });
1549
+ const value = validateConfigReadValue(key, field, userPlainEntries[key]);
1291
1550
  fields.push({
1292
1551
  ...base,
1293
1552
  valueState: 'configured',
@@ -1303,6 +1562,7 @@ async function buildManifestFieldEntries(options) {
1303
1562
  ...base,
1304
1563
  valueState: 'invalid',
1305
1564
  sourceLayer: 'user',
1565
+ source: 'user',
1306
1566
  storedSourceLayer: 'user',
1307
1567
  valid: false,
1308
1568
  issues: [{ type: 'invalid-config', key, source: 'user' }],
@@ -1312,11 +1572,7 @@ async function buildManifestFieldEntries(options) {
1312
1572
  }
1313
1573
  if (field.default !== undefined) {
1314
1574
  try {
1315
- const value = await coercePluginConfigValue({
1316
- key,
1317
- field,
1318
- value: field.default,
1319
- });
1575
+ const value = validateConfigReadValue(key, field, field.default);
1320
1576
  fields.push({
1321
1577
  ...base,
1322
1578
  effectiveValue: redactValue(field, value),
@@ -1342,11 +1598,7 @@ async function buildManifestFieldEntries(options) {
1342
1598
  let storedProjectValue;
1343
1599
  if (options.scope === 'project' && Object.hasOwn(projectPlainEntries, key)) {
1344
1600
  try {
1345
- const value = await coercePluginConfigValue({
1346
- key,
1347
- field,
1348
- value: projectPlainEntries[key],
1349
- });
1601
+ const value = validateConfigReadValue(key, field, projectPlainEntries[key]);
1350
1602
  storedProjectValue = {
1351
1603
  storedValue: redactValue(field, value),
1352
1604
  storedSourceLayer: 'project',
@@ -1375,6 +1627,7 @@ async function buildLegacyStoreFieldEntries(options) {
1375
1627
  const store = await readPluginConfigStore({
1376
1628
  sourceId: options.sourceId,
1377
1629
  sourceIdentity: options.sourceIdentity,
1630
+ logicalIdentity: options.logicalIdentity,
1378
1631
  pluginName: options.pluginName,
1379
1632
  scope: location.scope,
1380
1633
  cwd: options.cwd,
@@ -1387,6 +1640,8 @@ async function buildLegacyStoreFieldEntries(options) {
1387
1640
  kind: 'manifest',
1388
1641
  reserved: false,
1389
1642
  writable: false,
1643
+ canSet: false,
1644
+ canUnset: false,
1390
1645
  valueState: 'configured',
1391
1646
  effectiveValue: '[redacted]',
1392
1647
  sourceLayer: location.sourceLayer,
@@ -1437,6 +1692,7 @@ export async function resolveConfigRead(options) {
1437
1692
  : await buildLegacyStoreFieldEntries({
1438
1693
  sourceId: options.sourceId,
1439
1694
  sourceIdentity: options.sourceIdentity,
1695
+ logicalIdentity: options.logicalIdentity,
1440
1696
  pluginName,
1441
1697
  scope: options.scope,
1442
1698
  cwd: options.cwd,
@@ -1446,6 +1702,9 @@ export async function resolveConfigRead(options) {
1446
1702
  pluginName,
1447
1703
  scope: options.scope,
1448
1704
  ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
1705
+ ...(schemaAccess.plugin?.manifest.config?.fields
1706
+ ? { fieldDefinitions: schemaAccess.plugin.manifest.config.fields }
1707
+ : {}),
1449
1708
  fields: manifestFields,
1450
1709
  };
1451
1710
  }
@@ -1588,13 +1847,21 @@ export function toCliConfigPromptOptions(model) {
1588
1847
  requiredKeys: [...model.requiredKeys],
1589
1848
  };
1590
1849
  }
1591
- function formatRequiredConfigSetCommands(pluginName, issues, defaultScope) {
1850
+ export function formatPluginConfigSetRecoveryCommand(pluginName, key, field, scope) {
1851
+ return field.sensitive === true
1852
+ ? `aw config set ${pluginName} ${key} --scope user --value-stdin`
1853
+ : `aw config set ${pluginName} ${key} --scope ${scope} --value-json '<json>'`;
1854
+ }
1855
+ function formatRequiredConfigSetCommands(pluginName, issues, defaultScope, schema) {
1592
1856
  const commands = new Set();
1593
1857
  for (const issue of issues) {
1594
1858
  const scope = issue.source === 'user' || issue.source === 'project'
1595
1859
  ? issue.source
1596
1860
  : defaultScope;
1597
- commands.add(`aw config set ${pluginName} ${issue.key} --scope ${scope} --value-json '<json>'`);
1861
+ const field = schema[issue.key];
1862
+ if (!field)
1863
+ continue;
1864
+ commands.add(formatPluginConfigSetRecoveryCommand(pluginName, issue.key, field, scope));
1598
1865
  }
1599
1866
  return [...commands].map((command) => `\`${command}\``).join(' 或 ');
1600
1867
  }
@@ -1620,6 +1887,7 @@ export async function preflightRequiredPluginConfigs(options) {
1620
1887
  let effective = await resolveEffectivePluginConfig({
1621
1888
  sourceId: target.sourceId,
1622
1889
  sourceIdentity: target.sourceIdentity,
1890
+ logicalIdentity: target.logicalIdentity,
1623
1891
  pluginName,
1624
1892
  manifest: target.manifest,
1625
1893
  cwd: options.cwd,
@@ -1630,7 +1898,7 @@ export async function preflightRequiredPluginConfigs(options) {
1630
1898
  continue;
1631
1899
  }
1632
1900
  const details = blockingIssues.map((issue) => issue.key).join(', ');
1633
- const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope);
1901
+ const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope, target.manifest.config.fields);
1634
1902
  throw new Error(`${pluginName} requires valid AW config before install/upgrade: ${details}. ` +
1635
1903
  `Run ${setCommands}, or open \`aw\` to complete setup.`);
1636
1904
  }