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

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 (218) 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 +165 -45
  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 +11 -19
  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 +7 -0
  47. package/dist/src/commands/rebuild-command.js +769 -279
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +24 -47
  50. package/dist/src/commands/rebuild-integration.js +1074 -1011
  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 +216 -85
  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 +6 -6
  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 +2067 -839
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +585 -33
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +152 -6
  89. package/dist/src/tui/controller.js +1168 -156
  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 +6 -1
  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 +306 -86
  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-renderer.d.ts +2 -1
  138. package/dist/src/utils/artifact-renderer.js +52 -52
  139. package/dist/src/utils/artifact-renderer.js.map +1 -1
  140. package/dist/src/utils/bundle-layout.js +4 -4
  141. package/dist/src/utils/bundle-layout.js.map +1 -1
  142. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  143. package/dist/src/utils/catalog-capability-projection.js +14 -0
  144. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  145. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  146. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  147. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  148. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  149. package/dist/src/utils/catalog-generation-service.js +81 -47
  150. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  151. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  152. package/dist/src/utils/catalog-generation-store.js +60 -5
  153. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  154. package/dist/src/utils/catalog-operation-context.js +1 -0
  155. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  156. package/dist/src/utils/catalog-public-error.js +4 -0
  157. package/dist/src/utils/catalog-public-error.js.map +1 -1
  158. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  159. package/dist/src/utils/catalog-source-impact.js +142 -20
  160. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  161. package/dist/src/utils/catalog-source-references.js +95 -9
  162. package/dist/src/utils/catalog-source-references.js.map +1 -1
  163. package/dist/src/utils/catalog-sources.d.ts +1 -1
  164. package/dist/src/utils/catalog-sources.js +19 -3
  165. package/dist/src/utils/catalog-sources.js.map +1 -1
  166. package/dist/src/utils/codex-manager.js +8 -0
  167. package/dist/src/utils/codex-manager.js.map +1 -1
  168. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  169. package/dist/src/utils/codex-native-observation.js +132 -0
  170. package/dist/src/utils/codex-native-observation.js.map +1 -1
  171. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  172. package/dist/src/utils/codex-reconciler.js.map +1 -1
  173. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  174. package/dist/src/utils/codex-shared-native.js +8 -82
  175. package/dist/src/utils/codex-shared-native.js.map +1 -1
  176. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  177. package/dist/src/utils/git-catalog-provider.js +40 -1
  178. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  179. package/dist/src/utils/local-marketplace.d.ts +1 -0
  180. package/dist/src/utils/local-marketplace.js +69 -73
  181. package/dist/src/utils/local-marketplace.js.map +1 -1
  182. package/dist/src/utils/multi-source-catalog.js +1 -0
  183. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  184. package/dist/src/utils/native-cli.d.ts +9 -4
  185. package/dist/src/utils/native-cli.js +280 -34
  186. package/dist/src/utils/native-cli.js.map +1 -1
  187. package/dist/src/utils/path-catalog-provider.js +2 -0
  188. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  189. package/dist/src/utils/plugin-config-write.d.ts +15 -13
  190. package/dist/src/utils/plugin-config-write.js +418 -198
  191. package/dist/src/utils/plugin-config-write.js.map +1 -1
  192. package/dist/src/utils/plugin-config.d.ts +48 -4
  193. package/dist/src/utils/plugin-config.js +289 -38
  194. package/dist/src/utils/plugin-config.js.map +1 -1
  195. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  196. package/dist/src/utils/plugin-scripts.js +376 -109
  197. package/dist/src/utils/plugin-scripts.js.map +1 -1
  198. package/dist/src/utils/process-tree.d.ts +2 -2
  199. package/dist/src/utils/process-tree.js +1 -1
  200. package/dist/src/utils/process-tree.js.map +1 -1
  201. package/dist/src/utils/project-boundary.d.ts +11 -1
  202. package/dist/src/utils/project-boundary.js +17 -8
  203. package/dist/src/utils/project-boundary.js.map +1 -1
  204. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  205. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  206. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  207. package/dist/src/utils/sensitive-content.d.ts +1 -0
  208. package/dist/src/utils/sensitive-content.js +15 -0
  209. package/dist/src/utils/sensitive-content.js.map +1 -0
  210. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  211. package/dist/src/utils/windows-claude-shim.js +141 -0
  212. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  213. package/dist/vitest.config.js +91 -10
  214. package/dist/vitest.config.js.map +1 -1
  215. package/package.json +9 -9
  216. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  217. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  218. 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);
407
+ }
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);
361
411
  }
362
- if (raw.sourceId !== options.sourceId
363
- || raw.pluginName !== options.pluginName
364
- || raw.sourceIdentity !== options.sourceIdentity) {
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
  };
@@ -897,6 +1052,7 @@ export async function resolveEffectivePluginConfig(options) {
897
1052
  const userStore = await readPluginConfigStore({
898
1053
  sourceId: options.sourceId,
899
1054
  sourceIdentity: options.sourceIdentity,
1055
+ logicalIdentity: options.logicalIdentity,
900
1056
  pluginName,
901
1057
  scope: 'user',
902
1058
  cwd: options.cwd,
@@ -907,6 +1063,7 @@ export async function resolveEffectivePluginConfig(options) {
907
1063
  : await readPluginConfigStore({
908
1064
  sourceId: options.sourceId,
909
1065
  sourceIdentity: options.sourceIdentity,
1066
+ logicalIdentity: options.logicalIdentity,
910
1067
  pluginName,
911
1068
  scope: 'project',
912
1069
  cwd: options.cwd,
@@ -920,8 +1077,10 @@ export async function resolveEffectivePluginConfig(options) {
920
1077
  const sources = {};
921
1078
  const fieldStates = {};
922
1079
  const ignoredUnknownKeys = [];
1080
+ const unsupportedLayerOverrides = [];
923
1081
  const dangerousKeyIssues = [];
924
1082
  const schemaKeys = new Set(Object.keys(schema));
1083
+ const userOnlyKeys = codexNativeRefreshKeys(options.manifest);
925
1084
  for (const key of schemaKeys) {
926
1085
  sources[key] = 'missing';
927
1086
  }
@@ -967,13 +1126,26 @@ export async function resolveEffectivePluginConfig(options) {
967
1126
  }
968
1127
  }
969
1128
  }
1129
+ const supportedEntries = scope === 'project'
1130
+ ? Object.fromEntries(Object.entries(plainEntries).filter(([key]) => {
1131
+ if (!schemaKeys.has(key) || !userOnlyKeys.has(key))
1132
+ return true;
1133
+ unsupportedLayerOverrides.push({
1134
+ type: 'configLayerUnsupported',
1135
+ key,
1136
+ scope: 'project',
1137
+ source: 'project',
1138
+ });
1139
+ return false;
1140
+ }))
1141
+ : plainEntries;
970
1142
  await applyConfigLayer({
971
1143
  values,
972
1144
  redactedValues,
973
1145
  sources,
974
1146
  fieldStates,
975
1147
  schema,
976
- entries: plainEntries,
1148
+ entries: supportedEntries,
977
1149
  source,
978
1150
  });
979
1151
  }
@@ -999,6 +1171,7 @@ export async function resolveEffectivePluginConfig(options) {
999
1171
  sources,
1000
1172
  fieldStates,
1001
1173
  issues,
1174
+ unsupportedLayerOverrides,
1002
1175
  ignoredUnknownKeys,
1003
1176
  schema,
1004
1177
  stores: {
@@ -1030,6 +1203,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1030
1203
  readPluginConfigStore({
1031
1204
  sourceId: options.sourceId,
1032
1205
  sourceIdentity: options.sourceIdentity,
1206
+ logicalIdentity: options.logicalIdentity,
1033
1207
  pluginName,
1034
1208
  scope: 'user',
1035
1209
  cwd: options.cwd,
@@ -1038,6 +1212,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1038
1212
  readPluginConfigStore({
1039
1213
  sourceId: options.sourceId,
1040
1214
  sourceIdentity: options.sourceIdentity,
1215
+ logicalIdentity: options.logicalIdentity,
1041
1216
  pluginName,
1042
1217
  scope: 'project',
1043
1218
  cwd: options.cwd,
@@ -1052,6 +1227,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1052
1227
  values: {},
1053
1228
  sources: {},
1054
1229
  issues: [],
1230
+ unsupportedLayerOverrides: [],
1055
1231
  ignoredUnknownKeys: [],
1056
1232
  schema: {},
1057
1233
  stores: { user: userStore, project: projectStore },
@@ -1067,6 +1243,7 @@ export async function resolveProjectArtifactRenderConfig(options) {
1067
1243
  values: effective.values,
1068
1244
  sources: effective.sources,
1069
1245
  issues: effective.issues,
1246
+ unsupportedLayerOverrides: effective.unsupportedLayerOverrides,
1070
1247
  ignoredUnknownKeys: effective.ignoredUnknownKeys,
1071
1248
  schema: effective.schema,
1072
1249
  stores: {
@@ -1155,6 +1332,7 @@ export async function resolveProjectScopedPluginConfigView(options) {
1155
1332
  sources: effective.sources,
1156
1333
  entries,
1157
1334
  issues: effective.issues,
1335
+ unsupportedLayerOverrides: effective.unsupportedLayerOverrides,
1158
1336
  ignoredUnknownKeys: effective.ignoredUnknownKeys,
1159
1337
  };
1160
1338
  }
@@ -1196,6 +1374,7 @@ async function buildManifestFieldEntries(options) {
1196
1374
  const userStore = await readPluginConfigStore({
1197
1375
  sourceId: options.plugin.sourceId,
1198
1376
  sourceIdentity: options.plugin.sourceIdentity,
1377
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1199
1378
  pluginName: options.plugin.manifest.name,
1200
1379
  scope: 'user',
1201
1380
  cwd: options.cwd,
@@ -1207,6 +1386,7 @@ async function buildManifestFieldEntries(options) {
1207
1386
  : await readPluginConfigStore({
1208
1387
  sourceId: options.plugin.sourceId,
1209
1388
  sourceIdentity: options.plugin.sourceIdentity,
1389
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1210
1390
  pluginName: options.plugin.manifest.name,
1211
1391
  scope: 'project',
1212
1392
  cwd: options.cwd,
@@ -1221,6 +1401,7 @@ async function buildManifestFieldEntries(options) {
1221
1401
  const effective = await resolveEffectivePluginConfig({
1222
1402
  sourceId: options.plugin.sourceId,
1223
1403
  sourceIdentity: options.plugin.sourceIdentity,
1404
+ logicalIdentity: options.plugin.catalogLogicalIdentity,
1224
1405
  pluginName: options.plugin.manifest.name,
1225
1406
  manifest: options.plugin.manifest,
1226
1407
  targetScope: options.scope === 'user' ? 'user' : 'project',
@@ -1228,12 +1409,65 @@ async function buildManifestFieldEntries(options) {
1228
1409
  env: options.env,
1229
1410
  });
1230
1411
  const userOnlyKeys = codexNativeRefreshKeys(options.plugin.manifest);
1412
+ const projectRemovalFallback = (key) => {
1413
+ const field = schema[key];
1414
+ if (Object.hasOwn(userPlainEntries, key)) {
1415
+ const value = userPlainEntries[key];
1416
+ const validationIssues = validateCorePluginConfigValue(field, value, key);
1417
+ if (validationIssues.length > 0) {
1418
+ return {
1419
+ state: 'invalid',
1420
+ sourceLayer: 'user',
1421
+ issues: validationIssues.map((issue) => ({
1422
+ type: 'invalid-config',
1423
+ key,
1424
+ source: 'user',
1425
+ message: formatPluginConfigIssue(issue),
1426
+ })),
1427
+ };
1428
+ }
1429
+ return {
1430
+ state: 'valid',
1431
+ effectiveValue: redactValue(field, value),
1432
+ sourceLayer: 'user',
1433
+ };
1434
+ }
1435
+ if (field.default === undefined)
1436
+ return { state: 'missing' };
1437
+ const validationIssues = validateCorePluginConfigValue(field, field.default, key);
1438
+ if (validationIssues.length > 0) {
1439
+ return {
1440
+ state: 'invalid',
1441
+ sourceLayer: 'default',
1442
+ issues: validationIssues.map((issue) => ({
1443
+ type: 'invalid-config',
1444
+ key,
1445
+ source: 'default',
1446
+ message: formatPluginConfigIssue(issue),
1447
+ })),
1448
+ };
1449
+ }
1450
+ return {
1451
+ state: 'valid',
1452
+ effectiveValue: redactValue(field, field.default),
1453
+ sourceLayer: 'default',
1454
+ };
1455
+ };
1231
1456
  const effectiveFieldState = (key) => {
1232
1457
  const source = effective.sources[key];
1233
1458
  const sourceLayer = toConfigSourceLayer(source);
1234
1459
  const field = schema[key];
1235
1460
  const fieldIssues = effective.issues.filter((entry) => entry.key === key);
1236
1461
  const fieldState = effective.fieldStates[key];
1462
+ const fallback = options.scope === 'project'
1463
+ ? projectRemovalFallback(key)
1464
+ : fieldState?.fallback
1465
+ ? {
1466
+ state: 'valid',
1467
+ effectiveValue: fieldState.fallback.redactedValue,
1468
+ sourceLayer: fieldState.fallback.source,
1469
+ }
1470
+ : undefined;
1237
1471
  if (source === undefined || source === 'missing') {
1238
1472
  return {
1239
1473
  valueState: fieldIssues.length > 0 ? 'invalid' : 'default',
@@ -1253,12 +1487,7 @@ async function buildManifestFieldEntries(options) {
1253
1487
  issues: fieldState.invalidOverride.issues,
1254
1488
  },
1255
1489
  } : {}),
1256
- ...(fieldState?.fallback ? {
1257
- fallback: {
1258
- effectiveValue: fieldState.fallback.redactedValue,
1259
- sourceLayer: fieldState.fallback.source,
1260
- },
1261
- } : {}),
1490
+ ...(fallback ? { fallback } : {}),
1262
1491
  ...configFieldProjection({
1263
1492
  field,
1264
1493
  value: issue ? undefined : effective.values[key],
@@ -1269,12 +1498,18 @@ async function buildManifestFieldEntries(options) {
1269
1498
  };
1270
1499
  const fields = [];
1271
1500
  for (const [key, field] of Object.entries(schema)) {
1501
+ const canSet = options.scope !== 'project'
1502
+ || (!userOnlyKeys.has(key) && field.sensitive !== true);
1272
1503
  const base = {
1273
1504
  key,
1274
1505
  title: field.title ?? key,
1275
1506
  kind: 'manifest',
1276
1507
  reserved: false,
1277
- writable: options.scope !== 'project' || !userOnlyKeys.has(key),
1508
+ writable: canSet,
1509
+ canSet,
1510
+ canUnset: options.scope === 'user'
1511
+ ? Object.hasOwn(userPlainEntries, key)
1512
+ : options.scope === 'project' && Object.hasOwn(projectPlainEntries, key),
1278
1513
  valueState: 'default',
1279
1514
  effectiveValue: null,
1280
1515
  sourceLayer: null,
@@ -1375,6 +1610,7 @@ async function buildLegacyStoreFieldEntries(options) {
1375
1610
  const store = await readPluginConfigStore({
1376
1611
  sourceId: options.sourceId,
1377
1612
  sourceIdentity: options.sourceIdentity,
1613
+ logicalIdentity: options.logicalIdentity,
1378
1614
  pluginName: options.pluginName,
1379
1615
  scope: location.scope,
1380
1616
  cwd: options.cwd,
@@ -1387,6 +1623,8 @@ async function buildLegacyStoreFieldEntries(options) {
1387
1623
  kind: 'manifest',
1388
1624
  reserved: false,
1389
1625
  writable: false,
1626
+ canSet: false,
1627
+ canUnset: false,
1390
1628
  valueState: 'configured',
1391
1629
  effectiveValue: '[redacted]',
1392
1630
  sourceLayer: location.sourceLayer,
@@ -1437,6 +1675,7 @@ export async function resolveConfigRead(options) {
1437
1675
  : await buildLegacyStoreFieldEntries({
1438
1676
  sourceId: options.sourceId,
1439
1677
  sourceIdentity: options.sourceIdentity,
1678
+ logicalIdentity: options.logicalIdentity,
1440
1679
  pluginName,
1441
1680
  scope: options.scope,
1442
1681
  cwd: options.cwd,
@@ -1446,6 +1685,9 @@ export async function resolveConfigRead(options) {
1446
1685
  pluginName,
1447
1686
  scope: options.scope,
1448
1687
  ...(schemaAccess.schema ? { schema: schemaAccess.schema } : {}),
1688
+ ...(schemaAccess.plugin?.manifest.config?.fields
1689
+ ? { fieldDefinitions: schemaAccess.plugin.manifest.config.fields }
1690
+ : {}),
1449
1691
  fields: manifestFields,
1450
1692
  };
1451
1693
  }
@@ -1588,13 +1830,21 @@ export function toCliConfigPromptOptions(model) {
1588
1830
  requiredKeys: [...model.requiredKeys],
1589
1831
  };
1590
1832
  }
1591
- function formatRequiredConfigSetCommands(pluginName, issues, defaultScope) {
1833
+ export function formatPluginConfigSetRecoveryCommand(pluginName, key, field, scope) {
1834
+ return field.sensitive === true
1835
+ ? `aw config set ${pluginName} ${key} --scope user --value-stdin`
1836
+ : `aw config set ${pluginName} ${key} --scope ${scope} --value-json '<json>'`;
1837
+ }
1838
+ function formatRequiredConfigSetCommands(pluginName, issues, defaultScope, schema) {
1592
1839
  const commands = new Set();
1593
1840
  for (const issue of issues) {
1594
1841
  const scope = issue.source === 'user' || issue.source === 'project'
1595
1842
  ? issue.source
1596
1843
  : defaultScope;
1597
- commands.add(`aw config set ${pluginName} ${issue.key} --scope ${scope} --value-json '<json>'`);
1844
+ const field = schema[issue.key];
1845
+ if (!field)
1846
+ continue;
1847
+ commands.add(formatPluginConfigSetRecoveryCommand(pluginName, issue.key, field, scope));
1598
1848
  }
1599
1849
  return [...commands].map((command) => `\`${command}\``).join(' 或 ');
1600
1850
  }
@@ -1620,6 +1870,7 @@ export async function preflightRequiredPluginConfigs(options) {
1620
1870
  let effective = await resolveEffectivePluginConfig({
1621
1871
  sourceId: target.sourceId,
1622
1872
  sourceIdentity: target.sourceIdentity,
1873
+ logicalIdentity: target.logicalIdentity,
1623
1874
  pluginName,
1624
1875
  manifest: target.manifest,
1625
1876
  cwd: options.cwd,
@@ -1630,7 +1881,7 @@ export async function preflightRequiredPluginConfigs(options) {
1630
1881
  continue;
1631
1882
  }
1632
1883
  const details = blockingIssues.map((issue) => issue.key).join(', ');
1633
- const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope);
1884
+ const setCommands = formatRequiredConfigSetCommands(pluginName, blockingIssues, options.defaultScope, target.manifest.config.fields);
1634
1885
  throw new Error(`${pluginName} requires valid AW config before install/upgrade: ${details}. ` +
1635
1886
  `Run ${setCommands}, or open \`aw\` to complete setup.`);
1636
1887
  }