@lesliechan721/aw 0.1.0-beta.3 → 0.1.0-beta.30
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-config.mjs +100 -501
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/scripts/add-skill.mjs +4 -4
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +67 -97
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +15 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +239 -232
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/plugin-status.mjs +8 -11
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
- package/dist/authoring/scripts/remove-plugin.mjs +367 -0
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -0
- package/dist/authoring/scripts/validate-plugin.mjs +307 -1173
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
- package/dist/scripts/aw-skill-eval-materializer.js +306 -0
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
- package/dist/scripts/bundle-plugins.d.ts +1 -2
- package/dist/scripts/bundle-plugins.js +24 -7
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +7 -24
- package/dist/scripts/plugin-staging.js +22 -142
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.d.ts +2 -0
- package/dist/scripts/test-plugin-scripts.js +27 -75
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/adapters/helpers.js +2 -2
- package/dist/src/adapters/helpers.js.map +1 -1
- package/dist/src/authoring/workspace-commands.d.ts +26 -2
- package/dist/src/authoring/workspace-commands.js +44 -19
- package/dist/src/authoring/workspace-commands.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +78 -0
- package/dist/src/commands/aw-capabilities.js +256 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +154 -0
- package/dist/src/commands/catalog-dev-source.js +655 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +21 -0
- package/dist/src/commands/catalog-generation.js +9 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +138 -0
- package/dist/src/commands/catalog-publication-inspection.js +757 -0
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -0
- package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
- package/dist/src/commands/catalog-publication-journal.js +292 -0
- package/dist/src/commands/catalog-publication-journal.js.map +1 -0
- package/dist/src/commands/catalog-publication-target.d.ts +18 -0
- package/dist/src/commands/catalog-publication-target.js +95 -0
- package/dist/src/commands/catalog-publication-target.js.map +1 -0
- package/dist/src/commands/catalog-publish.d.ts +77 -7
- package/dist/src/commands/catalog-publish.js +746 -316
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +60 -22
- package/dist/src/commands/catalog-source.js +311 -91
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-status.d.ts +56 -0
- package/dist/src/commands/catalog-status.js +217 -0
- package/dist/src/commands/catalog-status.js.map +1 -0
- package/dist/src/commands/catalog-store.d.ts +104 -0
- package/dist/src/commands/catalog-store.js +685 -0
- package/dist/src/commands/catalog-store.js.map +1 -0
- package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
- package/dist/src/commands/catalog-workspace-resolution.js +42 -0
- package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
- package/dist/src/commands/console.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
- package/dist/src/commands/dependency-operation-plan.js +289 -7
- package/dist/src/commands/dependency-operation-plan.js.map +1 -1
- package/dist/src/commands/init.d.ts +5 -11
- package/dist/src/commands/init.js +30 -51
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
- package/dist/src/commands/offline-cleanup-evidence.js +181 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +127 -0
- package/dist/src/commands/operation-plan.js +71 -0
- package/dist/src/commands/operation-plan.js.map +1 -0
- package/dist/src/commands/operation-planner.d.ts +46 -0
- package/dist/src/commands/operation-planner.js +141 -0
- package/dist/src/commands/operation-planner.js.map +1 -0
- package/dist/src/commands/options.d.ts +9 -7
- package/dist/src/commands/options.js +83 -21
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.d.ts +22 -0
- package/dist/src/commands/plugin-capabilities.js +159 -0
- package/dist/src/commands/plugin-capabilities.js.map +1 -0
- package/dist/src/commands/rebuild-command.d.ts +77 -30
- package/dist/src/commands/rebuild-command.js +3993 -711
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +120 -21
- package/dist/src/commands/rebuild-integration.js +2765 -1326
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +58 -30
- package/dist/src/commands/rebuild-operations.js +259 -319
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
- package/dist/src/commands/reconcile-operation-plan.js +47 -0
- package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
- package/dist/src/commands/review-inspection.d.ts +228 -0
- package/dist/src/commands/review-inspection.js +1341 -0
- package/dist/src/commands/review-inspection.js.map +1 -0
- package/dist/src/commands/review-preparation.d.ts +49 -0
- package/dist/src/commands/review-preparation.js +427 -0
- package/dist/src/commands/review-preparation.js.map +1 -0
- package/dist/src/commands/runtime.d.ts +1 -5
- package/dist/src/commands/runtime.js +24 -50
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +38 -0
- package/dist/src/commands/skill-eval.js +388 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +279 -0
- package/dist/src/commands/status-v6.js +2697 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +40 -0
- package/dist/src/commands/sync-command.js +250 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +3 -4
- package/dist/src/commands/wizard.js +2 -2
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +9 -3
- package/dist/src/commands/write-commands.js +124 -31
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +71 -0
- package/dist/src/eval/manifest-v1.js +370 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +5 -1
- package/dist/src/generators/local-plugin.js +225 -104
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.js +1787 -476
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +43 -46
- package/dist/src/output/command-receipt.js +256 -80
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/model.d.ts +3 -44
- package/dist/src/output/model.js +1 -144
- package/dist/src/output/model.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +1 -13
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +46 -7
- package/dist/src/portable/commands.js +349 -37
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +17 -4
- package/dist/src/portable/materializer.js +160 -38
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/skills-cli.d.ts +15 -7
- package/dist/src/portable/skills-cli.js +66 -19
- package/dist/src/portable/skills-cli.js.map +1 -1
- package/dist/src/review/eval-source-input.d.ts +43 -0
- package/dist/src/review/eval-source-input.js +207 -0
- package/dist/src/review/eval-source-input.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +17 -0
- package/dist/src/review/review-profile.js +177 -0
- package/dist/src/review/review-profile.js.map +1 -0
- package/dist/src/review/review-selection.d.ts +30 -0
- package/dist/src/review/review-selection.js +527 -0
- package/dist/src/review/review-selection.js.map +1 -0
- package/dist/src/review/review-types.d.ts +68 -0
- package/dist/src/review/review-types.js +16 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/review/skill-materialization.d.ts +13 -0
- package/dist/src/review/skill-materialization.js +75 -0
- package/dist/src/review/skill-materialization.js.map +1 -0
- package/dist/src/review/source-binding.d.ts +182 -0
- package/dist/src/review/source-binding.js +682 -0
- package/dist/src/review/source-binding.js.map +1 -0
- package/dist/src/tui/catalog-freshness.d.ts +1 -0
- package/dist/src/tui/catalog-freshness.js +2 -0
- package/dist/src/tui/catalog-freshness.js.map +1 -0
- package/dist/src/tui/catalog-generation.d.ts +21 -0
- package/dist/src/tui/catalog-generation.js +156 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
- package/dist/src/tui/catalog-sources/projection.js +17 -0
- package/dist/src/tui/catalog-sources/projection.js.map +1 -0
- package/dist/src/tui/catalog-sources/review.d.ts +3 -0
- package/dist/src/tui/catalog-sources/review.js +13 -0
- package/dist/src/tui/catalog-sources/review.js.map +1 -0
- package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
- package/dist/src/tui/catalog-sources/screens.js +40 -0
- package/dist/src/tui/catalog-sources/screens.js.map +1 -0
- package/dist/src/tui/catalog-sources/session.d.ts +28 -0
- package/dist/src/tui/catalog-sources/session.js +295 -0
- package/dist/src/tui/catalog-sources/session.js.map +1 -0
- package/dist/src/tui/catalog-sources/types.d.ts +53 -0
- package/dist/src/tui/catalog-sources/types.js +2 -0
- package/dist/src/tui/catalog-sources/types.js.map +1 -0
- package/dist/src/tui/catalog-store-task.d.ts +16 -0
- package/dist/src/tui/catalog-store-task.js +19 -0
- package/dist/src/tui/catalog-store-task.js.map +1 -0
- package/dist/src/tui/components.d.ts +128 -0
- package/dist/src/tui/components.js +251 -7
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +14 -4
- package/dist/src/tui/console-app-v4.js +9248 -1461
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +13 -1
- package/dist/src/tui/controller-runtime.js +3338 -410
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +614 -40
- package/dist/src/tui/controller.js +3478 -708
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph.d.ts +81 -0
- package/dist/src/tui/dependency-graph.js +455 -0
- package/dist/src/tui/dependency-graph.js.map +1 -0
- package/dist/src/tui/diagnostic-projection.d.ts +23 -0
- package/dist/src/tui/diagnostic-projection.js +567 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +30 -0
- package/dist/src/tui/diagnostics.js +76 -0
- package/dist/src/tui/diagnostics.js.map +1 -0
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +11 -4
- package/dist/src/tui/index.js +8 -2
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +8 -0
- package/dist/src/tui/input-display.js +36 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +36 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +235 -27
- package/dist/src/tui/model.js +1652 -304
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
- package/dist/src/tui/operation-diagnostic-screens.js +102 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +81 -0
- package/dist/src/tui/operation-history.js +135 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.d.ts +21 -0
- package/dist/src/tui/operation-review-planner.js +1254 -0
- package/dist/src/tui/operation-review-planner.js.map +1 -0
- package/dist/src/tui/plugin-content.d.ts +23 -0
- package/dist/src/tui/plugin-content.js +336 -0
- package/dist/src/tui/plugin-content.js.map +1 -0
- package/dist/src/tui/plugin-write-task.d.ts +69 -0
- package/dist/src/tui/plugin-write-task.js +286 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -0
- package/dist/src/tui/prompt.js +4 -15
- package/dist/src/tui/prompt.js.map +1 -1
- package/dist/src/tui/router.d.ts +183 -11
- package/dist/src/tui/router.js +352 -29
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +702 -0
- package/dist/src/tui/screen-registry.js +231 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +45 -0
- package/dist/src/tui/session-host.js +209 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/tui/source-ref-presets.d.ts +3 -0
- package/dist/src/tui/source-ref-presets.js +11 -0
- package/dist/src/tui/source-ref-presets.js.map +1 -0
- package/dist/src/tui/theme.d.ts +18 -10
- package/dist/src/tui/theme.js +18 -10
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +20 -0
- package/dist/src/tui/viewport.js +29 -0
- package/dist/src/tui/viewport.js.map +1 -0
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/tui/wizard.js +11 -9
- package/dist/src/tui/wizard.js.map +1 -1
- package/dist/src/types.d.ts +242 -20
- package/dist/src/types.js +1 -0
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.d.ts +7 -1
- package/dist/src/utils/agent-version.js +41 -8
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.d.ts +5 -4
- package/dist/src/utils/artifact-plan.js +18 -7
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +13 -7
- package/dist/src/utils/artifact-renderer.js +196 -219
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +21 -0
- package/dist/src/utils/artifact-request.js +82 -0
- package/dist/src/utils/artifact-request.js.map +1 -0
- package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
- package/dist/src/utils/aw-home-mutation-lock.js +32 -4
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
- package/dist/src/utils/bundle-integrity.d.ts +0 -1
- package/dist/src/utils/bundle-integrity.js +0 -25
- package/dist/src/utils/bundle-integrity.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +14 -29
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-digests.d.ts +10 -0
- package/dist/src/utils/catalog-digests.js +50 -2
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
- package/dist/src/utils/catalog-generation-error.d.ts +16 -0
- package/dist/src/utils/catalog-generation-error.js +30 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
- package/dist/src/utils/catalog-generation-resolver.js +273 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +172 -0
- package/dist/src/utils/catalog-generation-service.js +1271 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +22 -0
- package/dist/src/utils/catalog-generation-state.js +192 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +58 -0
- package/dist/src/utils/catalog-generation-store.js +452 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
- package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
- package/dist/src/utils/catalog-operation-context.d.ts +20 -0
- package/dist/src/utils/catalog-operation-context.js +142 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +28 -8
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +4 -7
- package/dist/src/utils/catalog-runtime-projection.js +7 -102
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-snapshot.d.ts +5 -3
- package/dist/src/utils/catalog-snapshot.js +11 -3
- package/dist/src/utils/catalog-snapshot.js.map +1 -1
- package/dist/src/utils/catalog-source-identity.d.ts +1 -0
- package/dist/src/utils/catalog-source-identity.js +21 -2
- package/dist/src/utils/catalog-source-identity.js.map +1 -1
- package/dist/src/utils/catalog-source-impact.d.ts +21 -0
- package/dist/src/utils/catalog-source-impact.js +344 -0
- package/dist/src/utils/catalog-source-impact.js.map +1 -0
- package/dist/src/utils/catalog-source-references.d.ts +3 -2
- package/dist/src/utils/catalog-source-references.js +195 -347
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +15 -1
- package/dist/src/utils/catalog-sources.js +206 -27
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-config.d.ts +2 -0
- package/dist/src/utils/codex-config.js +26 -5
- package/dist/src/utils/codex-config.js.map +1 -1
- package/dist/src/utils/codex-manager.d.ts +79 -0
- package/dist/src/utils/codex-manager.js +745 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
- package/dist/src/utils/codex-marketplace-path.js +45 -0
- package/dist/src/utils/codex-marketplace-path.js.map +1 -0
- package/dist/src/utils/codex-native-observation.d.ts +49 -0
- package/dist/src/utils/codex-native-observation.js +154 -0
- package/dist/src/utils/codex-native-observation.js.map +1 -0
- package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
- package/dist/src/utils/codex-native-render-receipt.js +205 -0
- package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
- package/dist/src/utils/codex-reconciler.d.ts +126 -0
- package/dist/src/utils/codex-reconciler.js +298 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +43 -0
- package/dist/src/utils/codex-shared-native.js +81 -0
- package/dist/src/utils/codex-shared-native.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +2 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.d.ts +46 -0
- package/dist/src/utils/codex-surface-analysis.js +247 -0
- package/dist/src/utils/codex-surface-analysis.js.map +1 -0
- package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
- package/dist/src/utils/dynamic-skill-tree.js +36 -6
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +18 -0
- package/dist/src/utils/git-catalog-provider.js +170 -144
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +15 -1
- package/dist/src/utils/local-marketplace.js +307 -84
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/manifest.d.ts +1 -4
- package/dist/src/utils/manifest.js +1 -4
- package/dist/src/utils/manifest.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.d.ts +3 -9
- package/dist/src/utils/multi-source-catalog.js +54 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +55 -7
- package/dist/src/utils/native-cli.js +386 -57
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +84 -20
- package/dist/src/utils/ownership.js +179 -222
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.d.ts +1 -1
- package/dist/src/utils/path-catalog-provider.js +54 -65
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/paths.d.ts +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/plugin-config-impact.d.ts +33 -0
- package/dist/src/utils/plugin-config-impact.js +298 -0
- package/dist/src/utils/plugin-config-impact.js.map +1 -0
- package/dist/src/utils/plugin-config-write.d.ts +52 -27
- package/dist/src/utils/plugin-config-write.js +990 -298
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +140 -93
- package/dist/src/utils/plugin-config.js +992 -980
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +49 -0
- package/dist/src/utils/plugin-intent-state.js +320 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +62 -125
- package/dist/src/utils/plugin-scripts.js +468 -525
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -13
- package/dist/src/utils/project-boundary.js +28 -34
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/safe-fs.d.ts +21 -0
- package/dist/src/utils/safe-fs.js +111 -0
- package/dist/src/utils/safe-fs.js.map +1 -0
- package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +4 -0
- package/dist/src/utils/sensitive-content.js +36 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- package/dist/src/utils/skill-validation.d.ts +1 -1
- package/dist/src/utils/skill-validation.js +1 -1
- package/dist/src/utils/skill-validation.js.map +1 -1
- package/dist/src/utils/state-paths.d.ts +1 -0
- package/dist/src/utils/state-paths.js +12 -2
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +8 -2
- package/dist/src/utils/target-eligibility.js +17 -8
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/dist/src/utils/windows-claude-shim.d.ts +9 -0
- package/dist/src/utils/windows-claude-shim.js +141 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/vitest.config.js +91 -10
- package/dist/vitest.config.js.map +1 -1
- package/package.json +22 -19
- package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
- package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
- package/dist/scripts/aw-skill-eval-engine.js +0 -31
- package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
- package/dist/scripts/codex-eval-app-server.d.ts +0 -89
- package/dist/scripts/codex-eval-app-server.js +0 -1228
- package/dist/scripts/codex-eval-app-server.js.map +0 -1
- package/dist/scripts/codex-eval-isolation.d.ts +0 -61
- package/dist/scripts/codex-eval-isolation.js +0 -318
- package/dist/scripts/codex-eval-isolation.js.map +0 -1
- package/dist/scripts/codex-eval-process-management.d.ts +0 -8
- package/dist/scripts/codex-eval-process-management.js +0 -69
- package/dist/scripts/codex-eval-process-management.js.map +0 -1
- package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
- package/dist/scripts/codex-eval-process-tree.js +0 -803
- package/dist/scripts/codex-eval-process-tree.js.map +0 -1
- package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
- package/dist/scripts/codex-eval-runtime-config.js +0 -843
- package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
- package/dist/scripts/codex-trigger-eval.d.ts +0 -84
- package/dist/scripts/codex-trigger-eval.js +0 -968
- package/dist/scripts/codex-trigger-eval.js.map +0 -1
- package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
- package/dist/scripts/eval-plugin-skills-audited.js +0 -261
- package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
- package/dist/scripts/eval-plugin-skills.d.ts +0 -45
- package/dist/scripts/eval-plugin-skills.js +0 -1586
- package/dist/scripts/eval-plugin-skills.js.map +0 -1
- package/dist/scripts/eval-run-layout.d.ts +0 -17
- package/dist/scripts/eval-run-layout.js +0 -26
- package/dist/scripts/eval-run-layout.js.map +0 -1
- package/dist/scripts/plugin-eval-contract.d.ts +0 -185
- package/dist/scripts/plugin-eval-contract.js +0 -695
- package/dist/scripts/plugin-eval-contract.js.map +0 -1
- package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
- package/dist/scripts/plugin-eval-materializer.js +0 -421
- package/dist/scripts/plugin-eval-materializer.js.map +0 -1
- package/dist/scripts/skill-reporting.d.ts +0 -212
- package/dist/scripts/skill-reporting.js +0 -922
- package/dist/scripts/skill-reporting.js.map +0 -1
- package/dist/src/commands/status-v4.d.ts +0 -102
- package/dist/src/commands/status-v4.js +0 -844
- package/dist/src/commands/status-v4.js.map +0 -1
- package/dist/src/commands/status.d.ts +0 -93
- package/dist/src/commands/status.js +0 -442
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
- package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
- package/dist/src/utils/catalog-manifest-v3.js +0 -3
- package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
- package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
- package/dist/src/utils/catalog-source-cache.d.ts +0 -61
- package/dist/src/utils/catalog-source-cache.js +0 -276
- package/dist/src/utils/catalog-source-cache.js.map +0 -1
- package/dist/src/utils/codex-project-surface.d.ts +0 -3
- package/dist/src/utils/codex-project-surface.js +0 -49
- package/dist/src/utils/codex-project-surface.js.map +0 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -175
- package/dist/src/utils/lifecycle-active-store.js +0 -1276
- package/dist/src/utils/lifecycle-active-store.js.map +0 -1
- package/dist/src/utils/plugin-authoring-capabilities.d.ts +0 -23
- package/dist/src/utils/plugin-authoring-capabilities.js +0 -37
- package/dist/src/utils/plugin-authoring-capabilities.js.map +0 -1
- package/dist/src/utils/plugin-authoring-capabilities.json +0 -92
- package/dist/src/utils/plugin-release.d.ts +0 -2
- package/dist/src/utils/plugin-release.js +0 -2
- package/dist/src/utils/plugin-release.js.map +0 -1
- package/dist/src/utils/skill-frontmatter-capabilities.d.ts +0 -201
- package/dist/src/utils/skill-frontmatter-capabilities.js +0 -5
- package/dist/src/utils/skill-frontmatter-capabilities.js.map +0 -1
- package/dist/src/utils/skill-frontmatter-capabilities.json +0 -200
- /package/dist/authoring/scripts/{check-plugin-versions.d.mts → remove-plugin.d.mts} +0 -0
|
@@ -6,6 +6,7 @@ import { createRequire } from 'node:module';
|
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
import { getPluginAuthoringAcceptedValues as getCorePluginAuthoringAcceptedValues, getSkillFrontmatterCapabilityRegistry, normalizePluginConfigDefinition, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
9
10
|
import { checkPluginVersions } from './lib/plugin-version-check.mjs';
|
|
10
11
|
import { scanJavaScriptImportSpecifiers } from './lib/javascript-import-scanner.mjs';
|
|
11
12
|
import { assertAuthoringPluginPathFence, assertCanonicalAuthoringPluginRoot, } from './lib/plugin-root.mjs';
|
|
@@ -17,8 +18,8 @@ const CLI_PACKAGE_ROOT = path.basename(authoringRuntimeRoot) === 'dist'
|
|
|
17
18
|
: authoringRuntimeRoot;
|
|
18
19
|
const errors = [];
|
|
19
20
|
const warnings = [];
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION = 12;
|
|
22
|
+
let capabilityDiagnostic = null;
|
|
22
23
|
const MAX_SKILL_NAME_LEN = 64;
|
|
23
24
|
const MAX_SKILL_DESCRIPTION_LEN = 1024;
|
|
24
25
|
const MAX_SKILL_COMPATIBILITY_LEN = 500;
|
|
@@ -50,9 +51,9 @@ const CODEX_POLICY_PRODUCTS = new Set([
|
|
|
50
51
|
'codex',
|
|
51
52
|
'CODEX',
|
|
52
53
|
]);
|
|
53
|
-
const MANIFEST_POLICY_PRODUCTS = new Set(getPluginAuthoringAcceptedValues('
|
|
54
|
-
const MARKETPLACE_AUTHENTICATION_POLICIES = new Set(getPluginAuthoringAcceptedValues('
|
|
55
|
-
const MARKETPLACE_INSTALLATION_POLICIES = new Set(getPluginAuthoringAcceptedValues('
|
|
54
|
+
const MANIFEST_POLICY_PRODUCTS = new Set(getPluginAuthoringAcceptedValues('manifest', 'marketplace.policy.products'));
|
|
55
|
+
const MARKETPLACE_AUTHENTICATION_POLICIES = new Set(getPluginAuthoringAcceptedValues('manifest', 'marketplace.policy.authentication'));
|
|
56
|
+
const MARKETPLACE_INSTALLATION_POLICIES = new Set(getPluginAuthoringAcceptedValues('manifest', 'marketplace.policy.installation'));
|
|
56
57
|
const PUBLIC_HOOK_EVENTS = new Set(['before_tool', 'after_tool', 'session_start']);
|
|
57
58
|
const PUBLIC_SESSION_START_SOURCES = new Set(['startup', 'resume', 'clear']);
|
|
58
59
|
const PUBLIC_SESSION_START_DEFAULT_MATCHER = 'startup|resume|clear';
|
|
@@ -124,15 +125,10 @@ const TEMPLATE_TEXT_FILE_EXTENSIONS = /\.(md|markdown|txt|json|yaml|yml|toml|rul
|
|
|
124
125
|
const SLASH_COMMAND_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
125
126
|
const DEFAULT_SLASH_COMMANDS_DIR = 'slash-commands';
|
|
126
127
|
const SCRIPT_NODE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
|
|
127
|
-
const SCRIPT_AUTO_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.sh', '.ps1', '.cmd', '.bat']);
|
|
128
|
-
const SCRIPT_RUNNERS = new Set(['node', 'sh', 'bash', 'pwsh', 'powershell', 'cmd', 'auto']);
|
|
129
|
-
const SCRIPT_OS_SELECTORS = new Set(['default', 'unix', 'windows', 'linux', 'darwin', 'win32']);
|
|
130
|
-
const SCRIPT_LIFECYCLE_EVENTS = new Set(['install', 'upgrade', 'uninstall']);
|
|
131
128
|
const SCRIPT_TIMEOUT_MIN_MS = 1000;
|
|
132
|
-
const SCRIPT_TIMEOUT_MAX_MS =
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const ARTIFACT_STATES = new Set(getPluginAuthoringAcceptedValues('config-lifecycle-artifact', 'ArtifactPlan.artifacts[].state'));
|
|
129
|
+
const SCRIPT_TIMEOUT_MAX_MS = 1800000;
|
|
130
|
+
const ARTIFACT_TARGETS = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].target'));
|
|
131
|
+
const ARTIFACT_STATES = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].states'));
|
|
136
132
|
const ARTIFACT_SCOPES = new Set(['user', 'project']);
|
|
137
133
|
const ARTIFACT_KINDS = new Set(['instruction', 'codexRole', 'skill']);
|
|
138
134
|
const ARTIFACT_PROJECT_MANAGED_KINDS = new Set(['instruction', 'agent', 'skill']);
|
|
@@ -149,23 +145,9 @@ const ARTIFACT_PROJECT_MANAGED_KIND_BY_TARGET = {
|
|
|
149
145
|
'codex.skill': 'skill',
|
|
150
146
|
};
|
|
151
147
|
const PLUGIN_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
152
|
-
const PLUGIN_EVAL_SCHEMA_VERSION = 2;
|
|
153
|
-
const PLUGIN_EVAL_CASE_SCHEMA_VERSION = 1;
|
|
154
|
-
const PLUGIN_EVAL_SCOPES = new Set(['user', 'project']);
|
|
155
|
-
const PLUGIN_EVAL_SPLITS = new Set(['train', 'validation', 'sanity', 'all']);
|
|
156
|
-
const PLUGIN_EVAL_TRIGGER_CASE_SPLITS = new Set(['train', 'validation', 'sanity']);
|
|
157
|
-
const PLUGIN_EVAL_COMMAND_SUPPORTS = new Set([
|
|
158
|
-
'split',
|
|
159
|
-
'runs',
|
|
160
|
-
'case',
|
|
161
|
-
'model',
|
|
162
|
-
'codexBin',
|
|
163
|
-
'timeout',
|
|
164
|
-
'allowEnvironmentSkips',
|
|
165
|
-
]);
|
|
166
148
|
let yamlParser = null;
|
|
167
|
-
let skillFrontmatterCapabilityRegistry = null;
|
|
168
149
|
let pluginVersionCheckResult = null;
|
|
150
|
+
let skillEvalManifestTools = null;
|
|
169
151
|
function error(msg) {
|
|
170
152
|
errors.push(msg);
|
|
171
153
|
}
|
|
@@ -323,57 +305,25 @@ function parseFrontmatter(content, sourceName) {
|
|
|
323
305
|
return null;
|
|
324
306
|
return parseYaml(match[1], sourceName);
|
|
325
307
|
}
|
|
326
|
-
function loadPluginAuthoringCapabilityRegistry() {
|
|
327
|
-
if (pluginAuthoringCapabilityRegistry)
|
|
328
|
-
return pluginAuthoringCapabilityRegistry;
|
|
329
|
-
const registryPath = [
|
|
330
|
-
path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'plugin-authoring-capabilities.json'),
|
|
331
|
-
path.resolve(__dirname, '..', '..', 'dist', 'src', 'utils', 'plugin-authoring-capabilities.json'),
|
|
332
|
-
].find((candidate) => fs.existsSync(candidate));
|
|
333
|
-
if (!registryPath) {
|
|
334
|
-
throw new Error('Plugin authoring capability registry not found in the built CLI.');
|
|
335
|
-
}
|
|
336
|
-
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
337
|
-
if (!registry || registry.schemaVersion !== 1 || !registry.surfaces) {
|
|
338
|
-
throw new Error(`Invalid plugin authoring capability registry: ${registryPath}`);
|
|
339
|
-
}
|
|
340
|
-
pluginAuthoringCapabilityRegistry = registry;
|
|
341
|
-
return registry;
|
|
342
|
-
}
|
|
343
308
|
function getPluginAuthoringAcceptedValues(surface, field) {
|
|
344
|
-
|
|
345
|
-
?.find((candidate) => candidate.field === field);
|
|
346
|
-
if (!entry || !Array.isArray(entry.acceptedValues)) {
|
|
347
|
-
throw new Error(`Plugin authoring capability ${surface}.${field} has no accepted values.`);
|
|
348
|
-
}
|
|
349
|
-
return entry.acceptedValues;
|
|
309
|
+
return getCorePluginAuthoringAcceptedValues(surface, field);
|
|
350
310
|
}
|
|
351
311
|
function loadSkillFrontmatterCapabilityRegistry() {
|
|
352
|
-
|
|
353
|
-
return skillFrontmatterCapabilityRegistry;
|
|
354
|
-
const registryPath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'skill-frontmatter-capabilities.json');
|
|
355
|
-
if (!fs.existsSync(registryPath)) {
|
|
356
|
-
throw new Error(`Skill frontmatter capability registry not found: ${registryPath}`);
|
|
357
|
-
}
|
|
358
|
-
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
359
|
-
if (!registry ||
|
|
360
|
-
registry.schemaVersion !== 1 ||
|
|
361
|
-
!registry.profiles ||
|
|
362
|
-
!registry.fields) {
|
|
363
|
-
throw new Error(`Invalid Skill frontmatter capability registry: ${registryPath}`);
|
|
364
|
-
}
|
|
365
|
-
skillFrontmatterCapabilityRegistry = registry;
|
|
366
|
-
return registry;
|
|
312
|
+
return getSkillFrontmatterCapabilityRegistry();
|
|
367
313
|
}
|
|
368
314
|
async function tryLoadCliManifestTools() {
|
|
369
315
|
const manifestModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'manifest.js');
|
|
370
316
|
const runtimeProjectionModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'catalog-runtime-projection.js');
|
|
371
|
-
|
|
317
|
+
const capabilityModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'commands', 'aw-capabilities.js');
|
|
318
|
+
if (!fs.existsSync(manifestModulePath)
|
|
319
|
+
|| !fs.existsSync(runtimeProjectionModulePath)
|
|
320
|
+
|| !fs.existsSync(capabilityModulePath))
|
|
372
321
|
return null;
|
|
373
322
|
try {
|
|
374
|
-
const [manifestModule, runtimeProjectionModule] = await Promise.all([
|
|
323
|
+
const [manifestModule, runtimeProjectionModule, capabilityModule] = await Promise.all([
|
|
375
324
|
import(pathToFileURL(manifestModulePath).href),
|
|
376
325
|
import(pathToFileURL(runtimeProjectionModulePath).href),
|
|
326
|
+
import(pathToFileURL(capabilityModulePath).href),
|
|
377
327
|
]);
|
|
378
328
|
if (manifestModule.SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
379
329
|
!== SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION) {
|
|
@@ -384,6 +334,8 @@ async function tryLoadCliManifestTools() {
|
|
|
384
334
|
validateSourcePluginManifest: manifestModule.validateSourcePluginManifest ?? null,
|
|
385
335
|
readPluginManifest: manifestModule.readPluginManifest ?? null,
|
|
386
336
|
discoverPlugins: manifestModule.discoverPlugins ?? null,
|
|
337
|
+
resolvePluginRequirements: manifestModule.resolvePluginRequirements ?? null,
|
|
338
|
+
implementedCapabilities: capabilityModule.IMPLEMENTED_AW_CAPABILITIES ?? null,
|
|
387
339
|
createRuntimeResolver: runtimeProjectionModule.createCatalogRuntimeImportResolver ?? null,
|
|
388
340
|
};
|
|
389
341
|
}
|
|
@@ -535,11 +487,12 @@ function validateManifestLightweight(manifest) {
|
|
|
535
487
|
error('top-level "commands" has been removed; use "slashCommands" instead');
|
|
536
488
|
}
|
|
537
489
|
validateAllowedKeys(input, 'plugin manifest', [
|
|
490
|
+
'schemaVersion',
|
|
538
491
|
'name',
|
|
539
492
|
'version',
|
|
540
493
|
'description',
|
|
541
494
|
'dependencies',
|
|
542
|
-
'
|
|
495
|
+
'requires',
|
|
543
496
|
'skills',
|
|
544
497
|
'slashCommands',
|
|
545
498
|
'mcpServers',
|
|
@@ -650,7 +603,7 @@ function validatePortableNativeArtifactCapabilityLightweight(manifest) {
|
|
|
650
603
|
&& Array.isArray(target.states)
|
|
651
604
|
&& target.states.includes('present'));
|
|
652
605
|
if (!hasOtherNativeDeclaration && !hasPresentNativeArtifact) {
|
|
653
|
-
const message = 'Invalid plugin manifest: portable-only plugins are not supported
|
|
606
|
+
const message = 'Invalid plugin manifest: portable-only plugins are not supported. Declare at least one non-empty native capability.';
|
|
654
607
|
if (!errors.includes(message))
|
|
655
608
|
error(message);
|
|
656
609
|
}
|
|
@@ -987,703 +940,19 @@ function validateNonEmptyString(value, fieldName, maxLength) {
|
|
|
987
940
|
}
|
|
988
941
|
return trimmed;
|
|
989
942
|
}
|
|
990
|
-
function
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
!path.isAbsolute(relative));
|
|
995
|
-
}
|
|
996
|
-
function validateRealPathBoundary(boundary, candidate, fieldName, boundaryName) {
|
|
997
|
-
const canonicalBoundary = fs.realpathSync(boundary);
|
|
998
|
-
const canonicalCandidate = fs.realpathSync(candidate);
|
|
999
|
-
if (!isPathInsideOrEqual(canonicalBoundary, canonicalCandidate)) {
|
|
1000
|
-
error(`${fieldName} must stay inside the ${boundaryName}`);
|
|
1001
|
-
return false;
|
|
1002
|
-
}
|
|
1003
|
-
return true;
|
|
1004
|
-
}
|
|
1005
|
-
function validateEvalRelativeFile(evalDir, value, fieldName) {
|
|
1006
|
-
const relativePath = validateNonEmptyString(value, fieldName);
|
|
1007
|
-
if (!relativePath)
|
|
1008
|
-
return null;
|
|
1009
|
-
const normalized = stripDotSlash(relativePath).split('\\').join('/');
|
|
1010
|
-
if (path.isAbsolute(normalized) || normalized.split('/').includes('..')) {
|
|
1011
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
const absolutePath = path.resolve(evalDir, normalized);
|
|
1015
|
-
const relativeToRoot = path.relative(evalDir, absolutePath);
|
|
1016
|
-
if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
|
|
1017
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
1018
|
-
return null;
|
|
1019
|
-
}
|
|
1020
|
-
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
|
|
1021
|
-
error(`${fieldName} does not exist: ${normalized}`);
|
|
1022
|
-
return null;
|
|
1023
|
-
}
|
|
1024
|
-
if (!validateRealPathBoundary(evalDir, absolutePath, fieldName, 'Skill eval directory')) {
|
|
1025
|
-
return null;
|
|
1026
|
-
}
|
|
1027
|
-
return absolutePath;
|
|
1028
|
-
}
|
|
1029
|
-
function validateEvalWorkspacePath(value, fieldName, options = {}) {
|
|
1030
|
-
const raw = validateNonEmptyString(value, fieldName);
|
|
1031
|
-
if (!raw)
|
|
1032
|
-
return null;
|
|
1033
|
-
const treePattern = options.allowTreePattern === true && raw.endsWith('/**');
|
|
1034
|
-
const candidate = treePattern ? raw.slice(0, -3) : raw;
|
|
1035
|
-
if (candidate === '' ||
|
|
1036
|
-
candidate.includes('\0') ||
|
|
1037
|
-
candidate.includes('\\') ||
|
|
1038
|
-
candidate.includes('*') ||
|
|
1039
|
-
candidate.includes('?') ||
|
|
1040
|
-
candidate.includes('[') ||
|
|
1041
|
-
candidate.includes(']') ||
|
|
1042
|
-
path.isAbsolute(candidate) ||
|
|
1043
|
-
path.posix.normalize(candidate) !== candidate ||
|
|
1044
|
-
candidate.split('/').includes('..')) {
|
|
1045
|
-
error(`${fieldName} must be a normalized POSIX path inside the eval workspace`);
|
|
1046
|
-
return null;
|
|
1047
|
-
}
|
|
1048
|
-
const firstSegment = candidate.split('/')[0];
|
|
1049
|
-
if (firstSegment === '.git' || firstSegment === '.agents' || firstSegment === '.codex') {
|
|
1050
|
-
error(`${fieldName} must not target an eval control directory`);
|
|
1051
|
-
return null;
|
|
1052
|
-
}
|
|
1053
|
-
return raw;
|
|
1054
|
-
}
|
|
1055
|
-
function evalWorkspacePathMatchesPattern(relativePath, pattern) {
|
|
1056
|
-
if (pattern.endsWith('/**')) {
|
|
1057
|
-
const prefix = pattern.slice(0, -3);
|
|
1058
|
-
return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
|
|
1059
|
-
}
|
|
1060
|
-
return relativePath === pattern;
|
|
1061
|
-
}
|
|
1062
|
-
function validateConversationCaseCorpus(evalDir, suite, fieldName) {
|
|
1063
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1064
|
-
if (!casesPath)
|
|
1065
|
-
return;
|
|
1066
|
-
let document;
|
|
943
|
+
async function loadSkillEvalManifestTools() {
|
|
944
|
+
if (skillEvalManifestTools)
|
|
945
|
+
return skillEvalManifestTools;
|
|
946
|
+
const modulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'eval', 'manifest-v1.js');
|
|
1067
947
|
try {
|
|
1068
|
-
|
|
948
|
+
skillEvalManifestTools = await import(pathToFileURL(modulePath).href);
|
|
949
|
+
return skillEvalManifestTools;
|
|
1069
950
|
}
|
|
1070
|
-
catch
|
|
1071
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
const corpus = expectRecord(document, `${fieldName}.cases document`);
|
|
1075
|
-
if (!corpus)
|
|
1076
|
-
return;
|
|
1077
|
-
const skillName = validateNonEmptyString(corpus.skill_name, `${fieldName}.cases.skill_name`);
|
|
1078
|
-
if (skillName && skillName !== suite.skill) {
|
|
1079
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(suite.skill)}`);
|
|
1080
|
-
}
|
|
1081
|
-
if (!Array.isArray(corpus.evals) || corpus.evals.length === 0) {
|
|
1082
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1083
|
-
return;
|
|
1084
|
-
}
|
|
1085
|
-
const ids = new Set();
|
|
1086
|
-
corpus.evals.forEach((value, caseIndex) => {
|
|
1087
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1088
|
-
const entry = expectRecord(value, caseField);
|
|
1089
|
-
if (!entry)
|
|
1090
|
-
return;
|
|
1091
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1092
|
-
if (id && ids.has(id)) {
|
|
1093
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1094
|
-
}
|
|
1095
|
-
else if (id) {
|
|
1096
|
-
ids.add(id);
|
|
1097
|
-
}
|
|
1098
|
-
validateNonEmptyString(entry.prompt, `${caseField}.prompt`);
|
|
1099
|
-
const sandbox = entry.sandbox === undefined
|
|
1100
|
-
? 'read-only'
|
|
1101
|
-
: validateNonEmptyString(entry.sandbox, `${caseField}.sandbox`);
|
|
1102
|
-
if (sandbox && sandbox !== 'read-only' && sandbox !== 'workspace-write') {
|
|
1103
|
-
error(`${caseField}.sandbox must be "read-only" or "workspace-write"`);
|
|
1104
|
-
}
|
|
1105
|
-
if (!Array.isArray(entry.scripted_replies)) {
|
|
1106
|
-
error(`${caseField}.scripted_replies must be an array`);
|
|
1107
|
-
}
|
|
1108
|
-
else {
|
|
1109
|
-
entry.scripted_replies.forEach((reply, replyIndex) => {
|
|
1110
|
-
const replyField = `${caseField}.scripted_replies[${replyIndex}]`;
|
|
1111
|
-
if (typeof reply === 'string')
|
|
1112
|
-
return;
|
|
1113
|
-
const replyEntry = expectRecord(reply, replyField);
|
|
1114
|
-
if (!replyEntry)
|
|
1115
|
-
return;
|
|
1116
|
-
if (!Array.isArray(replyEntry.answers_by_index) || replyEntry.answers_by_index.some((answer) => typeof answer !== 'string')) {
|
|
1117
|
-
error(`${replyField}.answers_by_index must be an array of strings`);
|
|
1118
|
-
}
|
|
1119
|
-
if (replyEntry.expected_question_count !== undefined &&
|
|
1120
|
-
(!Number.isInteger(replyEntry.expected_question_count) ||
|
|
1121
|
-
replyEntry.expected_question_count < 0)) {
|
|
1122
|
-
error(`${replyField}.expected_question_count must be a non-negative integer`);
|
|
1123
|
-
}
|
|
1124
|
-
validateNonEmptyString(replyEntry.unexpected_question_answer, `${replyField}.unexpected_question_answer`);
|
|
1125
|
-
});
|
|
1126
|
-
}
|
|
1127
|
-
validateNonEmptyString(entry.expected_output, `${caseField}.expected_output`);
|
|
1128
|
-
if (entry.workspace_files !== undefined) {
|
|
1129
|
-
const workspaceFiles = expectRecord(entry.workspace_files, `${caseField}.workspace_files`);
|
|
1130
|
-
if (workspaceFiles) {
|
|
1131
|
-
const workspaceFileEntries = Object.entries(workspaceFiles);
|
|
1132
|
-
if (workspaceFileEntries.length === 0) {
|
|
1133
|
-
error(`${caseField}.workspace_files must contain at least one file`);
|
|
1134
|
-
}
|
|
1135
|
-
workspaceFileEntries.forEach(([relativePath, content]) => {
|
|
1136
|
-
validateEvalWorkspacePath(relativePath, `${caseField}.workspace_files.${relativePath}`, { allowTreePattern: false });
|
|
1137
|
-
validateNonEmptyString(content, `${caseField}.workspace_files.${relativePath}`);
|
|
1138
|
-
});
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
const review = expectRecord(entry.review, `${caseField}.review`);
|
|
1142
|
-
if (review) {
|
|
1143
|
-
if (!Array.isArray(review.checks) || review.checks.length === 0 || review.checks.some((check) => typeof check !== 'string' || check.trim() === '')) {
|
|
1144
|
-
error(`${caseField}.review.checks must be a non-empty array of strings`);
|
|
1145
|
-
}
|
|
1146
|
-
if (review.workspace_unchanged !== undefined &&
|
|
1147
|
-
typeof review.workspace_unchanged !== 'boolean') {
|
|
1148
|
-
error(`${caseField}.review.workspace_unchanged must be boolean`);
|
|
1149
|
-
}
|
|
1150
|
-
const allowedWorkspaceChanges = [];
|
|
1151
|
-
if (review.allowed_workspace_changes !== undefined) {
|
|
1152
|
-
if (!Array.isArray(review.allowed_workspace_changes) ||
|
|
1153
|
-
review.allowed_workspace_changes.length === 0) {
|
|
1154
|
-
error(`${caseField}.review.allowed_workspace_changes must be a non-empty array`);
|
|
1155
|
-
}
|
|
1156
|
-
else {
|
|
1157
|
-
review.allowed_workspace_changes.forEach((pathValue, pathIndex) => {
|
|
1158
|
-
const normalized = validateEvalWorkspacePath(pathValue, `${caseField}.review.allowed_workspace_changes[${pathIndex}]`, { allowTreePattern: true });
|
|
1159
|
-
if (normalized)
|
|
1160
|
-
allowedWorkspaceChanges.push(normalized);
|
|
1161
|
-
});
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
1164
|
-
const artifactPaths = [];
|
|
1165
|
-
if (review.artifacts !== undefined) {
|
|
1166
|
-
if (!Array.isArray(review.artifacts) || review.artifacts.length === 0) {
|
|
1167
|
-
error(`${caseField}.review.artifacts must be a non-empty array`);
|
|
1168
|
-
}
|
|
1169
|
-
else {
|
|
1170
|
-
review.artifacts.forEach((artifactValue, artifactIndex) => {
|
|
1171
|
-
const artifactField = `${caseField}.review.artifacts[${artifactIndex}]`;
|
|
1172
|
-
const artifact = expectRecord(artifactValue, artifactField);
|
|
1173
|
-
if (!artifact)
|
|
1174
|
-
return;
|
|
1175
|
-
validateAllowedKeys(artifact, artifactField, ['path', 'exists', 'contains', 'not_contains', 'json']);
|
|
1176
|
-
const artifactPath = validateEvalWorkspacePath(artifact.path, `${artifactField}.path`);
|
|
1177
|
-
if (artifactPath)
|
|
1178
|
-
artifactPaths.push(artifactPath);
|
|
1179
|
-
if (artifact.exists !== undefined && typeof artifact.exists !== 'boolean') {
|
|
1180
|
-
error(`${artifactField}.exists must be boolean`);
|
|
1181
|
-
}
|
|
1182
|
-
for (const key of ['contains', 'not_contains']) {
|
|
1183
|
-
if (artifact[key] !== undefined &&
|
|
1184
|
-
(!Array.isArray(artifact[key]) || artifact[key].length === 0 || artifact[key].some((fragment) => typeof fragment !== 'string' || fragment.length === 0))) {
|
|
1185
|
-
error(`${artifactField}.${key} must be a non-empty array of strings`);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
if (artifact.json !== undefined && !isRecord(artifact.json)) {
|
|
1189
|
-
error(`${artifactField}.json must be an object`);
|
|
1190
|
-
}
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
if (new Set(artifactPaths).size !== artifactPaths.length) {
|
|
1195
|
-
error(`${caseField}.review.artifacts contains duplicate paths`);
|
|
1196
|
-
}
|
|
1197
|
-
if (sandbox === 'workspace-write') {
|
|
1198
|
-
if (review.workspace_unchanged !== false) {
|
|
1199
|
-
error(`${caseField} workspace-write requires review.workspace_unchanged=false`);
|
|
1200
|
-
}
|
|
1201
|
-
if (allowedWorkspaceChanges.length === 0 || artifactPaths.length === 0) {
|
|
1202
|
-
error(`${caseField} workspace-write requires allowed changes and artifacts`);
|
|
1203
|
-
}
|
|
1204
|
-
const unmatchedArtifact = artifactPaths.find((artifactPath) => !allowedWorkspaceChanges.some((pattern) => evalWorkspacePathMatchesPattern(artifactPath, pattern)));
|
|
1205
|
-
if (unmatchedArtifact) {
|
|
1206
|
-
error(`${caseField} artifact ${JSON.stringify(unmatchedArtifact)} is outside allowed changes`);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
});
|
|
1211
|
-
}
|
|
1212
|
-
function validateTriggerCaseCorpus(evalDir, suite, fieldName) {
|
|
1213
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1214
|
-
if (!casesPath)
|
|
1215
|
-
return;
|
|
1216
|
-
let document;
|
|
1217
|
-
try {
|
|
1218
|
-
document = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1219
|
-
}
|
|
1220
|
-
catch (e) {
|
|
1221
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1222
|
-
return;
|
|
1223
|
-
}
|
|
1224
|
-
const corpus = expectRecord(document, `${fieldName}.cases document`);
|
|
1225
|
-
if (!corpus)
|
|
1226
|
-
return;
|
|
1227
|
-
if (corpus.schemaVersion !== PLUGIN_EVAL_CASE_SCHEMA_VERSION) {
|
|
1228
|
-
error(`${fieldName}.cases.schemaVersion must be ${PLUGIN_EVAL_CASE_SCHEMA_VERSION}`);
|
|
1229
|
-
}
|
|
1230
|
-
if (corpus.skill !== suite.skill) {
|
|
1231
|
-
error(`${fieldName}.cases.skill must match ${JSON.stringify(suite.skill)}`);
|
|
1232
|
-
}
|
|
1233
|
-
if (!Array.isArray(corpus.cases) || corpus.cases.length === 0) {
|
|
1234
|
-
error(`${fieldName}.cases.cases must be a non-empty array`);
|
|
1235
|
-
return;
|
|
1236
|
-
}
|
|
1237
|
-
const ids = new Set();
|
|
1238
|
-
const seenSplits = new Set();
|
|
1239
|
-
let hasWorkspaceFixture = false;
|
|
1240
|
-
corpus.cases.forEach((value, caseIndex) => {
|
|
1241
|
-
const caseField = `${fieldName}.cases.cases[${caseIndex}]`;
|
|
1242
|
-
const entry = expectRecord(value, caseField);
|
|
1243
|
-
if (!entry)
|
|
1244
|
-
return;
|
|
1245
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1246
|
-
if (id && !/^[a-z0-9][a-z0-9_]*$/.test(id)) {
|
|
1247
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1248
|
-
}
|
|
1249
|
-
else if (id && ids.has(id)) {
|
|
1250
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1251
|
-
}
|
|
1252
|
-
else if (id) {
|
|
1253
|
-
ids.add(id);
|
|
1254
|
-
}
|
|
1255
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(entry.split)) {
|
|
1256
|
-
error(`${caseField}.split must be train, validation, or sanity`);
|
|
1257
|
-
}
|
|
1258
|
-
else {
|
|
1259
|
-
seenSplits.add(entry.split);
|
|
1260
|
-
}
|
|
1261
|
-
validateNonEmptyString(entry.query, `${caseField}.query`);
|
|
1262
|
-
const usesLegacyExpectation = entry.should_load_skill !== undefined;
|
|
1263
|
-
const usesRouteExpectation = entry.expected_loaded_skills !== undefined;
|
|
1264
|
-
if (usesLegacyExpectation === usesRouteExpectation) {
|
|
1265
|
-
error(`${caseField} must declare exactly one of should_load_skill or expected_loaded_skills`);
|
|
1266
|
-
}
|
|
1267
|
-
else if (usesLegacyExpectation && typeof entry.should_load_skill !== 'boolean') {
|
|
1268
|
-
error(`${caseField}.should_load_skill must be boolean`);
|
|
1269
|
-
}
|
|
1270
|
-
else if (usesRouteExpectation) {
|
|
1271
|
-
if (!Array.isArray(entry.expected_loaded_skills)) {
|
|
1272
|
-
error(`${caseField}.expected_loaded_skills must be an array`);
|
|
1273
|
-
}
|
|
1274
|
-
else {
|
|
1275
|
-
const expectedSkills = entry.expected_loaded_skills;
|
|
1276
|
-
expectedSkills.forEach((skillName, skillIndex) => {
|
|
1277
|
-
if (typeof skillName !== 'string' || skillName.trim() === '') {
|
|
1278
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must be a non-empty string`);
|
|
1279
|
-
}
|
|
1280
|
-
else if (!suite.skills.includes(skillName)) {
|
|
1281
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must name a participant Skill`);
|
|
1282
|
-
}
|
|
1283
|
-
});
|
|
1284
|
-
if (new Set(expectedSkills).size !== expectedSkills.length) {
|
|
1285
|
-
error(`${caseField}.expected_loaded_skills must not contain duplicates`);
|
|
1286
|
-
}
|
|
1287
|
-
if (suite.forbidMultipleSkillLoads === true && expectedSkills.length > 1) {
|
|
1288
|
-
error(`${caseField} expects multiple Skills but the suite forbids multiple Skill loads`);
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
if (suite.observeSpawnAgent === true) {
|
|
1293
|
-
if (typeof entry.should_spawn_agent !== 'boolean') {
|
|
1294
|
-
error(`${caseField}.should_spawn_agent is required when observeSpawnAgent=true`);
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
else if (entry.should_spawn_agent !== undefined) {
|
|
1298
|
-
error(`${caseField}.should_spawn_agent requires observeSpawnAgent=true`);
|
|
1299
|
-
}
|
|
1300
|
-
if (entry.workspace_fixture !== undefined) {
|
|
1301
|
-
hasWorkspaceFixture = true;
|
|
1302
|
-
validateNonEmptyString(entry.workspace_fixture, `${caseField}.workspace_fixture`);
|
|
1303
|
-
}
|
|
1304
|
-
if (entry.use_project_rules !== undefined &&
|
|
1305
|
-
typeof entry.use_project_rules !== 'boolean') {
|
|
1306
|
-
error(`${caseField}.use_project_rules must be boolean`);
|
|
1307
|
-
}
|
|
1308
|
-
if (entry.tags !== undefined) {
|
|
1309
|
-
if (!Array.isArray(entry.tags) || entry.tags.length === 0) {
|
|
1310
|
-
error(`${caseField}.tags must be a non-empty array`);
|
|
1311
|
-
}
|
|
1312
|
-
else {
|
|
1313
|
-
entry.tags.forEach((tag, tagIndex) => {
|
|
1314
|
-
validateNonEmptyString(tag, `${caseField}.tags[${tagIndex}]`);
|
|
1315
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
});
|
|
1319
|
-
if (hasWorkspaceFixture && suite.fixtureModule === undefined) {
|
|
1320
|
-
error(`${fieldName}.fixtureModule is required when cases declare workspace_fixture`);
|
|
1321
|
-
}
|
|
1322
|
-
if (Array.isArray(suite.releaseSplits)) {
|
|
1323
|
-
suite.releaseSplits.forEach((split) => {
|
|
1324
|
-
if (split !== 'all' && !seenSplits.has(split)) {
|
|
1325
|
-
error(`${fieldName}.cases has no cases for release split ${JSON.stringify(split)}`);
|
|
1326
|
-
}
|
|
1327
|
-
});
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
function resolveEvalProfilePlugin(pluginDir, ownerManifest, pluginName, fieldName) {
|
|
1331
|
-
if (pluginName === ownerManifest.name) {
|
|
1332
|
-
return { pluginDir, manifest: ownerManifest };
|
|
1333
|
-
}
|
|
1334
|
-
const companionDir = path.join(path.dirname(pluginDir), pluginName);
|
|
1335
|
-
const manifestPath = path.join(companionDir, '.agents-plugin', 'plugin.json');
|
|
1336
|
-
if (!fs.existsSync(manifestPath)) {
|
|
1337
|
-
error(`${fieldName} must reference repo-local plugin "${pluginName}"`);
|
|
1338
|
-
return null;
|
|
1339
|
-
}
|
|
1340
|
-
let manifest;
|
|
1341
|
-
try {
|
|
1342
|
-
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
1343
|
-
}
|
|
1344
|
-
catch (e) {
|
|
1345
|
-
error(`${fieldName} companion manifest is not valid JSON: ${e.message}`);
|
|
1346
|
-
return null;
|
|
1347
|
-
}
|
|
1348
|
-
if (!isRecord(manifest) || manifest.name !== pluginName) {
|
|
1349
|
-
error(`${fieldName} companion source must declare plugin name "${pluginName}"`);
|
|
951
|
+
catch {
|
|
1350
952
|
return null;
|
|
1351
953
|
}
|
|
1352
|
-
return { pluginDir: companionDir, manifest };
|
|
1353
954
|
}
|
|
1354
|
-
function
|
|
1355
|
-
const evalManifestPath = path.join(evalDir, 'manifest.json');
|
|
1356
|
-
const manifestField = path.relative(pluginDir, evalManifestPath).split(path.sep).join('/');
|
|
1357
|
-
let document;
|
|
1358
|
-
try {
|
|
1359
|
-
document = JSON.parse(fs.readFileSync(evalManifestPath, 'utf8'));
|
|
1360
|
-
}
|
|
1361
|
-
catch (e) {
|
|
1362
|
-
error(`${manifestField} is not valid JSON: ${e.message}`);
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
|
-
const manifest = expectRecord(document, manifestField);
|
|
1366
|
-
if (!manifest)
|
|
1367
|
-
return;
|
|
1368
|
-
validateAllowedKeys(manifest, manifestField, ['schemaVersion', 'plugin', 'skill', 'profiles', 'suites']);
|
|
1369
|
-
if (manifest.schemaVersion !== PLUGIN_EVAL_SCHEMA_VERSION) {
|
|
1370
|
-
error(`${manifestField}.schemaVersion must be ${PLUGIN_EVAL_SCHEMA_VERSION}`);
|
|
1371
|
-
}
|
|
1372
|
-
if (manifest.plugin !== pluginManifest.name) {
|
|
1373
|
-
error(`${manifestField}.plugin must match plugin name ${JSON.stringify(pluginManifest.name)}`);
|
|
1374
|
-
}
|
|
1375
|
-
const skill = validateNonEmptyString(manifest.skill, `${manifestField}.skill`);
|
|
1376
|
-
if (skill && skill !== expectedSkill) {
|
|
1377
|
-
error(`${manifestField}.skill must match owning Skill ${JSON.stringify(expectedSkill)}`);
|
|
1378
|
-
}
|
|
1379
|
-
if (skill && !collectDeclaredSkillNames(pluginDir, pluginManifest).all.has(skill)) {
|
|
1380
|
-
error(`${manifestField}.skill must reference a declared static or dynamic skill`);
|
|
1381
|
-
}
|
|
1382
|
-
if (!Array.isArray(manifest.profiles) || manifest.profiles.length === 0) {
|
|
1383
|
-
error(`${manifestField}.profiles must be a non-empty array`);
|
|
1384
|
-
return;
|
|
1385
|
-
}
|
|
1386
|
-
const profilesById = new Map();
|
|
1387
|
-
manifest.profiles.forEach((value, index) => {
|
|
1388
|
-
const fieldName = `${manifestField}.profiles[${index}]`;
|
|
1389
|
-
const profile = expectRecord(value, fieldName);
|
|
1390
|
-
if (!profile)
|
|
1391
|
-
return;
|
|
1392
|
-
validateAllowedKeys(profile, fieldName, ['id', 'scope', 'plugins', 'runModel', 'requiredForRelease']);
|
|
1393
|
-
const id = validateNonEmptyString(profile.id, `${fieldName}.id`);
|
|
1394
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1395
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1396
|
-
}
|
|
1397
|
-
else if (id && profilesById.has(id)) {
|
|
1398
|
-
error(`${manifestField} has duplicate profile id ${JSON.stringify(id)}`);
|
|
1399
|
-
}
|
|
1400
|
-
if (!PLUGIN_EVAL_SCOPES.has(profile.scope)) {
|
|
1401
|
-
error(`${fieldName}.scope must be user or project`);
|
|
1402
|
-
}
|
|
1403
|
-
const skillOwners = new Map();
|
|
1404
|
-
const installedPlugins = new Set();
|
|
1405
|
-
if (!Array.isArray(profile.plugins) || profile.plugins.length === 0) {
|
|
1406
|
-
error(`${fieldName}.plugins must be a non-empty array`);
|
|
1407
|
-
}
|
|
1408
|
-
else {
|
|
1409
|
-
profile.plugins.forEach((value, pluginIndex) => {
|
|
1410
|
-
const pluginField = `${fieldName}.plugins[${pluginIndex}]`;
|
|
1411
|
-
const installation = expectRecord(value, pluginField);
|
|
1412
|
-
if (!installation)
|
|
1413
|
-
return;
|
|
1414
|
-
validateAllowedKeys(installation, pluginField, ['name', 'config']);
|
|
1415
|
-
const pluginName = validateNonEmptyString(installation.name, `${pluginField}.name`);
|
|
1416
|
-
if (!pluginName)
|
|
1417
|
-
return;
|
|
1418
|
-
if (!PLUGIN_NAME_PATTERN.test(pluginName)) {
|
|
1419
|
-
error(`${pluginField}.name must use lowercase kebab-case`);
|
|
1420
|
-
}
|
|
1421
|
-
if (installedPlugins.has(pluginName)) {
|
|
1422
|
-
error(`${fieldName}.plugins has duplicate plugin ${JSON.stringify(pluginName)}`);
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
installedPlugins.add(pluginName);
|
|
1426
|
-
if (installation.config !== undefined && !isRecord(installation.config)) {
|
|
1427
|
-
error(`${pluginField}.config must be an object`);
|
|
1428
|
-
}
|
|
1429
|
-
const source = resolveEvalProfilePlugin(pluginDir, pluginManifest, pluginName, `${pluginField}.name`);
|
|
1430
|
-
if (!source)
|
|
1431
|
-
return;
|
|
1432
|
-
for (const skillName of collectDeclaredSkillNames(source.pluginDir, source.manifest).all) {
|
|
1433
|
-
const existing = skillOwners.get(skillName);
|
|
1434
|
-
if (existing && existing !== pluginName) {
|
|
1435
|
-
error(`${fieldName} installs duplicate Skill key ${JSON.stringify(skillName)} ` +
|
|
1436
|
-
`from plugins ${JSON.stringify(existing)} and ${JSON.stringify(pluginName)}`);
|
|
1437
|
-
}
|
|
1438
|
-
else {
|
|
1439
|
-
skillOwners.set(skillName, pluginName);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1444
|
-
if (!installedPlugins.has(pluginManifest.name)) {
|
|
1445
|
-
error(`${fieldName}.plugins must include owner plugin ${JSON.stringify(pluginManifest.name)}`);
|
|
1446
|
-
}
|
|
1447
|
-
if (typeof profile.runModel !== 'boolean') {
|
|
1448
|
-
error(`${fieldName}.runModel must be boolean`);
|
|
1449
|
-
}
|
|
1450
|
-
if (typeof profile.requiredForRelease !== 'boolean') {
|
|
1451
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1452
|
-
}
|
|
1453
|
-
if (profile.scope === 'user' && profile.runModel === true) {
|
|
1454
|
-
error(`${fieldName}: eval schema v2 does not allow real model execution for user scope`);
|
|
1455
|
-
}
|
|
1456
|
-
if (id && PLUGIN_NAME_PATTERN.test(id) && !profilesById.has(id)) {
|
|
1457
|
-
profilesById.set(id, { ...profile, skillOwners });
|
|
1458
|
-
}
|
|
1459
|
-
});
|
|
1460
|
-
if (!manifest.profiles.some((profile) => isRecord(profile) && profile.scope === 'project' && profile.runModel === true)) {
|
|
1461
|
-
error(`${manifestField} must define at least one project profile with runModel=true`);
|
|
1462
|
-
}
|
|
1463
|
-
if (!Array.isArray(manifest.suites) || manifest.suites.length === 0) {
|
|
1464
|
-
error(`${manifestField}.suites must be a non-empty array`);
|
|
1465
|
-
return;
|
|
1466
|
-
}
|
|
1467
|
-
const suiteIds = new Set();
|
|
1468
|
-
manifest.suites.forEach((value, index) => {
|
|
1469
|
-
const fieldName = `${manifestField}.suites[${index}]`;
|
|
1470
|
-
const suite = expectRecord(value, fieldName);
|
|
1471
|
-
if (!suite)
|
|
1472
|
-
return;
|
|
1473
|
-
const id = validateNonEmptyString(suite.id, `${fieldName}.id`);
|
|
1474
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1475
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1476
|
-
}
|
|
1477
|
-
else if (id && suiteIds.has(id)) {
|
|
1478
|
-
error(`${manifestField} has duplicate suite id ${JSON.stringify(id)}`);
|
|
1479
|
-
}
|
|
1480
|
-
else if (id) {
|
|
1481
|
-
suiteIds.add(id);
|
|
1482
|
-
}
|
|
1483
|
-
if (suite.kind !== 'command' && suite.kind !== 'conversation' && suite.kind !== 'trigger') {
|
|
1484
|
-
error(`${fieldName}.kind must be command, conversation, or trigger`);
|
|
1485
|
-
}
|
|
1486
|
-
const suiteSkills = new Set();
|
|
1487
|
-
if (!Array.isArray(suite.skills) || suite.skills.length === 0) {
|
|
1488
|
-
error(`${fieldName}.skills must be a non-empty array`);
|
|
1489
|
-
}
|
|
1490
|
-
else {
|
|
1491
|
-
suite.skills.forEach((skillName, skillIndex) => {
|
|
1492
|
-
const skillField = `${fieldName}.skills[${skillIndex}]`;
|
|
1493
|
-
const normalized = validateNonEmptyString(skillName, skillField);
|
|
1494
|
-
if (!normalized)
|
|
1495
|
-
return;
|
|
1496
|
-
if (!PLUGIN_NAME_PATTERN.test(normalized)) {
|
|
1497
|
-
error(`${skillField} must use lowercase kebab-case`);
|
|
1498
|
-
}
|
|
1499
|
-
else if (suiteSkills.has(normalized)) {
|
|
1500
|
-
error(`${fieldName}.skills has duplicate Skill ${JSON.stringify(normalized)}`);
|
|
1501
|
-
}
|
|
1502
|
-
else {
|
|
1503
|
-
suiteSkills.add(normalized);
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
}
|
|
1507
|
-
if (skill && !suiteSkills.has(skill)) {
|
|
1508
|
-
error(`${fieldName}.skills must include owner Skill ${JSON.stringify(skill)}`);
|
|
1509
|
-
}
|
|
1510
|
-
if (!Array.isArray(suite.profiles) || suite.profiles.length === 0) {
|
|
1511
|
-
error(`${fieldName}.profiles must be a non-empty array`);
|
|
1512
|
-
}
|
|
1513
|
-
else {
|
|
1514
|
-
suite.profiles.forEach((profileId, profileIndex) => {
|
|
1515
|
-
const profileField = `${fieldName}.profiles[${profileIndex}]`;
|
|
1516
|
-
if (typeof profileId !== 'string' || profileId.trim() === '') {
|
|
1517
|
-
error(`${profileField} must be a non-empty string`);
|
|
1518
|
-
return;
|
|
1519
|
-
}
|
|
1520
|
-
const profile = profilesById.get(profileId);
|
|
1521
|
-
if (!profile) {
|
|
1522
|
-
error(`${fieldName} references unknown profile ${JSON.stringify(profileId)}`);
|
|
1523
|
-
}
|
|
1524
|
-
else if (profile.runModel !== true) {
|
|
1525
|
-
error(`${fieldName} cannot execute materialize-only profile ${JSON.stringify(profileId)}`);
|
|
1526
|
-
}
|
|
1527
|
-
if (profile) {
|
|
1528
|
-
for (const skillName of suiteSkills) {
|
|
1529
|
-
if (!profile.skillOwners.has(skillName)) {
|
|
1530
|
-
error(`${fieldName} requires Skill ${JSON.stringify(skillName)}, but profile ` +
|
|
1531
|
-
`${JSON.stringify(profileId)} does not install a plugin that declares it`);
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
});
|
|
1536
|
-
}
|
|
1537
|
-
if (!Number.isInteger(suite.defaultRuns) || suite.defaultRuns <= 0) {
|
|
1538
|
-
error(`${fieldName}.defaultRuns must be a positive integer`);
|
|
1539
|
-
}
|
|
1540
|
-
if (!Number.isInteger(suite.perRunTimeoutMs) || suite.perRunTimeoutMs <= 0) {
|
|
1541
|
-
error(`${fieldName}.perRunTimeoutMs must be a positive integer`);
|
|
1542
|
-
}
|
|
1543
|
-
if (typeof suite.requiredForRelease !== 'boolean') {
|
|
1544
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1545
|
-
}
|
|
1546
|
-
if (!Array.isArray(suite.releaseSplits)) {
|
|
1547
|
-
error(`${fieldName}.releaseSplits must be an array`);
|
|
1548
|
-
}
|
|
1549
|
-
else {
|
|
1550
|
-
suite.releaseSplits.forEach((split, splitIndex) => {
|
|
1551
|
-
if (!PLUGIN_EVAL_SPLITS.has(split)) {
|
|
1552
|
-
error(`${fieldName}.releaseSplits[${splitIndex}] is unsupported`);
|
|
1553
|
-
}
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
if (suite.kind === 'command') {
|
|
1557
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1558
|
-
'id', 'kind', 'skills', 'entry', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1559
|
-
'requiredForRelease', 'perRunTimeoutMs', 'suiteTimeoutMs', 'supports',
|
|
1560
|
-
]);
|
|
1561
|
-
validateEvalRelativeFile(evalDir, suite.entry, `${fieldName}.entry`);
|
|
1562
|
-
if (!Number.isInteger(suite.suiteTimeoutMs) || suite.suiteTimeoutMs <= 0) {
|
|
1563
|
-
error(`${fieldName}.suiteTimeoutMs must be a positive integer`);
|
|
1564
|
-
}
|
|
1565
|
-
else if (suite.suiteTimeoutMs < suite.perRunTimeoutMs) {
|
|
1566
|
-
error(`${fieldName}.suiteTimeoutMs must be greater than or equal to perRunTimeoutMs`);
|
|
1567
|
-
}
|
|
1568
|
-
if (!Array.isArray(suite.supports)) {
|
|
1569
|
-
error(`${fieldName}.supports must be an array`);
|
|
1570
|
-
}
|
|
1571
|
-
else {
|
|
1572
|
-
suite.supports.forEach((support, supportIndex) => {
|
|
1573
|
-
if (!PLUGIN_EVAL_COMMAND_SUPPORTS.has(support)) {
|
|
1574
|
-
error(`${fieldName}.supports[${supportIndex}] is unsupported`);
|
|
1575
|
-
}
|
|
1576
|
-
});
|
|
1577
|
-
if (Array.isArray(suite.releaseSplits) &&
|
|
1578
|
-
suite.releaseSplits.length > 0 &&
|
|
1579
|
-
!suite.supports.includes('split')) {
|
|
1580
|
-
error(`${fieldName} declares releaseSplits but does not support split`);
|
|
1581
|
-
}
|
|
1582
|
-
const supportsCase = suite.supports.includes('case');
|
|
1583
|
-
if (supportsCase && suite.cases === undefined) {
|
|
1584
|
-
error(`${fieldName}.cases is required when supports includes case`);
|
|
1585
|
-
}
|
|
1586
|
-
else if (!supportsCase && suite.cases !== undefined) {
|
|
1587
|
-
error(`${fieldName}.cases requires supports to include case`);
|
|
1588
|
-
}
|
|
1589
|
-
if (supportsCase && suite.cases !== undefined) {
|
|
1590
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1591
|
-
if (casesPath) {
|
|
1592
|
-
try {
|
|
1593
|
-
const casesDocument = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1594
|
-
if (!isRecord(casesDocument) || casesDocument.skill_name !== skill) {
|
|
1595
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(skill)}`);
|
|
1596
|
-
}
|
|
1597
|
-
else if (!Array.isArray(casesDocument.evals) || casesDocument.evals.length === 0) {
|
|
1598
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1599
|
-
}
|
|
1600
|
-
else {
|
|
1601
|
-
const caseIds = new Set();
|
|
1602
|
-
casesDocument.evals.forEach((entry, caseIndex) => {
|
|
1603
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1604
|
-
if (!isRecord(entry) || typeof entry.id !== 'string' || !/^[a-z0-9_]+$/.test(entry.id)) {
|
|
1605
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1606
|
-
}
|
|
1607
|
-
else if (caseIds.has(entry.id)) {
|
|
1608
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(entry.id)}`);
|
|
1609
|
-
}
|
|
1610
|
-
else {
|
|
1611
|
-
caseIds.add(entry.id);
|
|
1612
|
-
}
|
|
1613
|
-
});
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
catch (cause) {
|
|
1617
|
-
error(`${fieldName}.cases is not valid JSON: ${cause.message}`);
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
else if (suite.kind === 'conversation' || suite.kind === 'trigger') {
|
|
1624
|
-
const normalizedSuite = { ...suite, skill };
|
|
1625
|
-
const isolation = suite.isolation ?? 'standard';
|
|
1626
|
-
if (isolation !== 'standard' && isolation !== 'strict') {
|
|
1627
|
-
error(`${fieldName}.isolation must be standard or strict`);
|
|
1628
|
-
}
|
|
1629
|
-
if (suite.kind === 'conversation') {
|
|
1630
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1631
|
-
'id', 'kind', 'skills', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1632
|
-
'requiredForRelease', 'perRunTimeoutMs', 'isolation',
|
|
1633
|
-
]);
|
|
1634
|
-
if (Array.isArray(suite.releaseSplits) && suite.releaseSplits.length > 0) {
|
|
1635
|
-
error(`${fieldName} cannot declare releaseSplits because conversation cases do not support split filtering`);
|
|
1636
|
-
}
|
|
1637
|
-
validateConversationCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1638
|
-
}
|
|
1639
|
-
else {
|
|
1640
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1641
|
-
'id', 'kind', 'skills', 'cases', 'fixtureModule', 'observeSpawnAgent', 'isolation',
|
|
1642
|
-
'strictNegativeSplits', 'minimumPassRate', 'forbidMultipleSkillLoads',
|
|
1643
|
-
'profiles', 'defaultRuns', 'releaseSplits',
|
|
1644
|
-
'requiredForRelease', 'perRunTimeoutMs',
|
|
1645
|
-
]);
|
|
1646
|
-
if (typeof suite.observeSpawnAgent !== 'boolean') {
|
|
1647
|
-
error(`${fieldName}.observeSpawnAgent must be boolean`);
|
|
1648
|
-
}
|
|
1649
|
-
if (!Array.isArray(suite.strictNegativeSplits)) {
|
|
1650
|
-
error(`${fieldName}.strictNegativeSplits must be an array`);
|
|
1651
|
-
}
|
|
1652
|
-
else {
|
|
1653
|
-
suite.strictNegativeSplits.forEach((split, splitIndex) => {
|
|
1654
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(split)) {
|
|
1655
|
-
error(`${fieldName}.strictNegativeSplits[${splitIndex}] is unsupported`);
|
|
1656
|
-
}
|
|
1657
|
-
});
|
|
1658
|
-
}
|
|
1659
|
-
if (suite.minimumPassRate !== undefined &&
|
|
1660
|
-
(typeof suite.minimumPassRate !== 'number' ||
|
|
1661
|
-
!Number.isFinite(suite.minimumPassRate) ||
|
|
1662
|
-
suite.minimumPassRate <= 0 ||
|
|
1663
|
-
suite.minimumPassRate > 1)) {
|
|
1664
|
-
error(`${fieldName}.minimumPassRate must be greater than 0 and at most 1`);
|
|
1665
|
-
}
|
|
1666
|
-
if (suite.forbidMultipleSkillLoads !== undefined &&
|
|
1667
|
-
typeof suite.forbidMultipleSkillLoads !== 'boolean') {
|
|
1668
|
-
error(`${fieldName}.forbidMultipleSkillLoads must be boolean`);
|
|
1669
|
-
}
|
|
1670
|
-
if (suite.fixtureModule !== undefined) {
|
|
1671
|
-
const fixturePath = validateEvalRelativeFile(evalDir, suite.fixtureModule, `${fieldName}.fixtureModule`);
|
|
1672
|
-
if (fixturePath && path.extname(fixturePath) !== '.mjs') {
|
|
1673
|
-
error(`${fieldName}.fixtureModule must reference an .mjs file`);
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
validateTriggerCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
});
|
|
1680
|
-
if (skill
|
|
1681
|
-
&& collectDeclaredSkillNames(pluginDir, pluginManifest).dynamic.has(skill)
|
|
1682
|
-
&& !manifest.suites.some((suite) => isRecord(suite) && suite.requiredForRelease === true)) {
|
|
1683
|
-
error(`${manifestField}.suites must include at least one release-required suite for dynamic Skill ${JSON.stringify(skill)}`);
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
955
|
+
async function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
1687
956
|
const legacyRoot = path.join(pluginDir, 'evals');
|
|
1688
957
|
if (fs.existsSync(legacyRoot)) {
|
|
1689
958
|
error('Plugin-root evals/ is legacy; put evals/ inside its owning static or dynamic Skill directory.');
|
|
@@ -1692,17 +961,33 @@ function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
|
1692
961
|
.sort()
|
|
1693
962
|
.map((skill) => ({ skill, root: path.join(pluginDir, 'skills', skill) }));
|
|
1694
963
|
const seenSkills = new Set();
|
|
964
|
+
let manifestTools = null;
|
|
1695
965
|
for (const owner of skillRoots) {
|
|
1696
966
|
const evalDir = path.join(owner.root, 'evals');
|
|
1697
967
|
const manifestPath = path.join(evalDir, 'manifest.json');
|
|
1698
968
|
if (!fs.existsSync(manifestPath))
|
|
1699
969
|
continue;
|
|
970
|
+
const manifestField = path.relative(pluginDir, manifestPath).split(path.sep).join('/');
|
|
1700
971
|
if (seenSkills.has(owner.skill)) {
|
|
1701
972
|
error(`Skill ${JSON.stringify(owner.skill)} has more than one evals/manifest.json owner`);
|
|
1702
973
|
continue;
|
|
1703
974
|
}
|
|
1704
975
|
seenSkills.add(owner.skill);
|
|
1705
|
-
|
|
976
|
+
manifestTools ??= await loadSkillEvalManifestTools();
|
|
977
|
+
if (typeof manifestTools?.validateAwSkillEvalManifest !== 'function') {
|
|
978
|
+
error('Canonical AwSkillEvalManifest v1 validator is unavailable; run the current built aw CLI.');
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
const validation = await manifestTools.validateAwSkillEvalManifest({
|
|
982
|
+
plugin: {
|
|
983
|
+
rootDir: pluginDir,
|
|
984
|
+
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
985
|
+
manifest: pluginManifest,
|
|
986
|
+
},
|
|
987
|
+
skill: owner.skill,
|
|
988
|
+
});
|
|
989
|
+
for (const message of validation.errors)
|
|
990
|
+
error(`${manifestField}: ${message}`);
|
|
1706
991
|
}
|
|
1707
992
|
}
|
|
1708
993
|
function countCharacters(value) {
|
|
@@ -2095,33 +1380,6 @@ function validateDeclaredNativeAssets(pluginDir, manifest) {
|
|
|
2095
1380
|
}
|
|
2096
1381
|
}
|
|
2097
1382
|
}
|
|
2098
|
-
function validatePluginVersionContracts(manifest, options) {
|
|
2099
|
-
if (manifest.compatibility === undefined) {
|
|
2100
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2101
|
-
return;
|
|
2102
|
-
}
|
|
2103
|
-
const compatibility = expectRecord(manifest.compatibility, 'compatibility');
|
|
2104
|
-
if (!compatibility) {
|
|
2105
|
-
return;
|
|
2106
|
-
}
|
|
2107
|
-
validateAllowedKeys(compatibility, 'compatibility', ['aw']);
|
|
2108
|
-
if (compatibility.aw === undefined) {
|
|
2109
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2110
|
-
return;
|
|
2111
|
-
}
|
|
2112
|
-
const aw = expectRecord(compatibility.aw, 'compatibility.aw');
|
|
2113
|
-
if (!aw) {
|
|
2114
|
-
return;
|
|
2115
|
-
}
|
|
2116
|
-
validateAllowedKeys(aw, 'compatibility.aw', ['minVersion']);
|
|
2117
|
-
const pluginMinVersion = validateStrictSemVerString(aw.minVersion, 'compatibility.aw.minVersion');
|
|
2118
|
-
if (!pluginMinVersion || !options.catalogMinVersion) {
|
|
2119
|
-
return;
|
|
2120
|
-
}
|
|
2121
|
-
if (compareStrictSemVer(pluginMinVersion, options.catalogMinVersion) > 0) {
|
|
2122
|
-
error(`Invalid compatibility.aw.minVersion for plugin "${pluginNameForError(manifest)}": received ${JSON.stringify(pluginMinVersion)}, which is higher than target catalog minimum ${JSON.stringify(options.catalogMinVersion)}.`);
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
1383
|
function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
2126
1384
|
const skillsRoot = typeof manifest.skills === 'string'
|
|
2127
1385
|
? path.join(pluginDir, manifest.skills)
|
|
@@ -2166,9 +1424,6 @@ function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
|
2166
1424
|
function isNodeRunnablePath(relativePath) {
|
|
2167
1425
|
return SCRIPT_NODE_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)));
|
|
2168
1426
|
}
|
|
2169
|
-
function isAutoRunnablePath(relativePath) {
|
|
2170
|
-
return SCRIPT_AUTO_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)).toLowerCase());
|
|
2171
|
-
}
|
|
2172
1427
|
function normalizePluginScriptPath(pluginDir, value, fieldName) {
|
|
2173
1428
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
2174
1429
|
error(`${fieldName} must be a non-empty string`);
|
|
@@ -2217,17 +1472,11 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2217
1472
|
}
|
|
2218
1473
|
validateAllowedKeys(entry, fieldName, ['runner', 'path', 'args']);
|
|
2219
1474
|
const runner = validateManifestString(entry.runner, `${fieldName}.runner`);
|
|
2220
|
-
if (runner
|
|
2221
|
-
error(`${fieldName}.runner has unsupported runner "${runner}"`);
|
|
2222
|
-
}
|
|
2223
|
-
if (options.requireNodeRunner && runner !== 'node') {
|
|
1475
|
+
if (runner !== 'node') {
|
|
2224
1476
|
error(`${fieldName}.runner must be "node"`);
|
|
2225
1477
|
}
|
|
2226
1478
|
const relativePath = normalizePluginScriptPath(pluginDir, entry.path, `${fieldName}.path`);
|
|
2227
1479
|
validateDeclaredScriptFile(pluginDir, relativePath, `${fieldName}.path`);
|
|
2228
|
-
if (runner === 'auto' && relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2229
|
-
error(`${fieldName}.path uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2230
|
-
}
|
|
2231
1480
|
if (options.requireNodePath && relativePath && !isNodeRunnablePath(relativePath)) {
|
|
2232
1481
|
error(`${fieldName}.path must use a Node runnable extension (.js, .mjs, or .cjs)`);
|
|
2233
1482
|
}
|
|
@@ -2245,51 +1494,6 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2245
1494
|
}
|
|
2246
1495
|
return runner && relativePath ? { runner, path: relativePath } : null;
|
|
2247
1496
|
}
|
|
2248
|
-
function validateLifecycleDefinition(pluginDir, value, fieldName, runtimeEntries) {
|
|
2249
|
-
if (typeof value === 'string') {
|
|
2250
|
-
const relativePath = normalizePluginScriptPath(pluginDir, value, fieldName);
|
|
2251
|
-
validateDeclaredScriptFile(pluginDir, relativePath, fieldName);
|
|
2252
|
-
if (relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2253
|
-
error(`${fieldName} uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2254
|
-
}
|
|
2255
|
-
if (relativePath && isNodeRunnablePath(relativePath)) {
|
|
2256
|
-
runtimeEntries.push({ path: relativePath, fieldName });
|
|
2257
|
-
}
|
|
2258
|
-
return { default: relativePath ? { runner: 'auto', path: relativePath } : null };
|
|
2259
|
-
}
|
|
2260
|
-
const selector = expectRecord(value, fieldName);
|
|
2261
|
-
if (!selector)
|
|
2262
|
-
return {};
|
|
2263
|
-
if ('runner' in selector || 'path' in selector || 'command' in selector) {
|
|
2264
|
-
error(`${fieldName} must declare lifecycle scripts under OS selectors`);
|
|
2265
|
-
}
|
|
2266
|
-
for (const selectorName of Object.keys(selector)) {
|
|
2267
|
-
if (!SCRIPT_OS_SELECTORS.has(selectorName)) {
|
|
2268
|
-
error(`${fieldName} has unknown OS selector "${selectorName}"`);
|
|
2269
|
-
}
|
|
2270
|
-
}
|
|
2271
|
-
const normalized = {};
|
|
2272
|
-
for (const [selectorName, entryValue] of Object.entries(selector)) {
|
|
2273
|
-
const entry = validateScriptEntry(pluginDir, entryValue, `${fieldName}.${selectorName}`);
|
|
2274
|
-
if (entry) {
|
|
2275
|
-
normalized[selectorName] = entry;
|
|
2276
|
-
if (entry.runner === 'node' || (entry.runner === 'auto' && isNodeRunnablePath(entry.path))) {
|
|
2277
|
-
runtimeEntries.push({ path: entry.path, fieldName: `${fieldName}.${selectorName}.path` });
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
}
|
|
2281
|
-
const orders = {
|
|
2282
|
-
linux: ['linux', 'unix', 'default'],
|
|
2283
|
-
darwin: ['darwin', 'unix', 'default'],
|
|
2284
|
-
win32: ['win32', 'windows', 'default'],
|
|
2285
|
-
};
|
|
2286
|
-
for (const [osName, order] of Object.entries(orders)) {
|
|
2287
|
-
if (!order.some((selectorName) => normalized[selectorName])) {
|
|
2288
|
-
error(`${fieldName} must include an entry that resolves on ${osName}`);
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
return normalized;
|
|
2292
|
-
}
|
|
2293
1497
|
function isNonRegistryDependencySpecifier(specifier) {
|
|
2294
1498
|
const trimmed = String(specifier).trim();
|
|
2295
1499
|
return (/^(?:workspace|file|link|portal):/i.test(trimmed) ||
|
|
@@ -2476,261 +1680,22 @@ function listFilesRecursive(rootDir) {
|
|
|
2476
1680
|
walk(rootDir);
|
|
2477
1681
|
return output;
|
|
2478
1682
|
}
|
|
2479
|
-
function validateConfigValueLightweight(field, value, fieldName) {
|
|
2480
|
-
if (!isRecord(field) || !CONFIG_FIELD_TYPES.has(field.type))
|
|
2481
|
-
return;
|
|
2482
|
-
if (field.type === 'string' || field.type === 'file' || field.type === 'directory') {
|
|
2483
|
-
validateManifestString(value, fieldName);
|
|
2484
|
-
return;
|
|
2485
|
-
}
|
|
2486
|
-
if (field.type === 'boolean') {
|
|
2487
|
-
if (typeof value !== 'boolean') {
|
|
2488
|
-
error(`${fieldName} must be a boolean`);
|
|
2489
|
-
}
|
|
2490
|
-
return;
|
|
2491
|
-
}
|
|
2492
|
-
if (field.type === 'number') {
|
|
2493
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
2494
|
-
error(`${fieldName} must be a finite number`);
|
|
2495
|
-
return;
|
|
2496
|
-
}
|
|
2497
|
-
if (typeof field.min === 'number' && value < field.min) {
|
|
2498
|
-
error(`${fieldName} must be greater than or equal to ${field.min}`);
|
|
2499
|
-
}
|
|
2500
|
-
if (typeof field.max === 'number' && value > field.max) {
|
|
2501
|
-
error(`${fieldName} must be less than or equal to ${field.max}`);
|
|
2502
|
-
}
|
|
2503
|
-
return;
|
|
2504
|
-
}
|
|
2505
|
-
if (field.type === 'enum') {
|
|
2506
|
-
if (typeof value !== 'string' || !Array.isArray(field.values) || !field.values.includes(value)) {
|
|
2507
|
-
error(`${fieldName} must be one of config schema values`);
|
|
2508
|
-
}
|
|
2509
|
-
}
|
|
2510
|
-
}
|
|
2511
|
-
function validateConfigPresentationLightweight(presentation, schema) {
|
|
2512
|
-
if (presentation === undefined)
|
|
2513
|
-
return;
|
|
2514
|
-
const root = expectRecord(presentation, 'config.presentation');
|
|
2515
|
-
if (!root)
|
|
2516
|
-
return;
|
|
2517
|
-
validateAllowedKeys(root, 'config.presentation', ['groups', 'fields', 'presets']);
|
|
2518
|
-
const schemaKeys = new Set(Object.keys(schema));
|
|
2519
|
-
if (root.groups !== undefined) {
|
|
2520
|
-
if (!Array.isArray(root.groups)) {
|
|
2521
|
-
error('config.presentation.groups must be an array');
|
|
2522
|
-
}
|
|
2523
|
-
else {
|
|
2524
|
-
const groupIds = new Set();
|
|
2525
|
-
const groupedFields = new Set();
|
|
2526
|
-
root.groups.forEach((rawGroup, index) => {
|
|
2527
|
-
const fieldName = `config.presentation.groups[${index}]`;
|
|
2528
|
-
const group = expectRecord(rawGroup, fieldName);
|
|
2529
|
-
if (!group)
|
|
2530
|
-
return;
|
|
2531
|
-
validateAllowedKeys(group, fieldName, ['id', 'title', 'description', 'fields', 'advanced']);
|
|
2532
|
-
const validId = validateManifestString(group.id, `${fieldName}.id`);
|
|
2533
|
-
const id = validId ? group.id : null;
|
|
2534
|
-
if (id && groupIds.has(id)) {
|
|
2535
|
-
error(`${fieldName}.id has duplicate group id "${id}"`);
|
|
2536
|
-
}
|
|
2537
|
-
else if (id) {
|
|
2538
|
-
groupIds.add(id);
|
|
2539
|
-
}
|
|
2540
|
-
validateManifestString(group.title, `${fieldName}.title`);
|
|
2541
|
-
if (group.description !== undefined) {
|
|
2542
|
-
validateManifestString(group.description, `${fieldName}.description`);
|
|
2543
|
-
}
|
|
2544
|
-
if (group.advanced !== undefined && typeof group.advanced !== 'boolean') {
|
|
2545
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2546
|
-
}
|
|
2547
|
-
if (!Array.isArray(group.fields)) {
|
|
2548
|
-
error(`${fieldName}.fields must be an array`);
|
|
2549
|
-
return;
|
|
2550
|
-
}
|
|
2551
|
-
group.fields.forEach((key, fieldIndex) => {
|
|
2552
|
-
const keyFieldName = `${fieldName}.fields[${fieldIndex}]`;
|
|
2553
|
-
const validKey = validateManifestString(key, keyFieldName);
|
|
2554
|
-
if (!validKey)
|
|
2555
|
-
return;
|
|
2556
|
-
if (!schemaKeys.has(key)) {
|
|
2557
|
-
error(`${keyFieldName} references unknown config key "${key}"`);
|
|
2558
|
-
}
|
|
2559
|
-
if (groupedFields.has(key)) {
|
|
2560
|
-
error(`${keyFieldName} assigns config key "${key}" to more than one group`);
|
|
2561
|
-
}
|
|
2562
|
-
else {
|
|
2563
|
-
groupedFields.add(key);
|
|
2564
|
-
}
|
|
2565
|
-
});
|
|
2566
|
-
});
|
|
2567
|
-
}
|
|
2568
|
-
}
|
|
2569
|
-
if (root.fields !== undefined) {
|
|
2570
|
-
const fields = expectRecord(root.fields, 'config.presentation.fields');
|
|
2571
|
-
if (fields) {
|
|
2572
|
-
for (const [key, rawField] of Object.entries(fields)) {
|
|
2573
|
-
const fieldName = `config.presentation.fields.${key}`;
|
|
2574
|
-
const schemaField = schema[key];
|
|
2575
|
-
if (!schemaKeys.has(key)) {
|
|
2576
|
-
error(`config.presentation.fields references unknown config key "${key}"`);
|
|
2577
|
-
}
|
|
2578
|
-
const field = expectRecord(rawField, fieldName);
|
|
2579
|
-
if (!field)
|
|
2580
|
-
continue;
|
|
2581
|
-
validateAllowedKeys(field, fieldName, [
|
|
2582
|
-
'valueLabels',
|
|
2583
|
-
'recommendedValue',
|
|
2584
|
-
'placeholder',
|
|
2585
|
-
'unit',
|
|
2586
|
-
'advanced',
|
|
2587
|
-
]);
|
|
2588
|
-
if (field.placeholder !== undefined) {
|
|
2589
|
-
validateManifestString(field.placeholder, `${fieldName}.placeholder`);
|
|
2590
|
-
}
|
|
2591
|
-
if (field.unit !== undefined) {
|
|
2592
|
-
validateManifestString(field.unit, `${fieldName}.unit`);
|
|
2593
|
-
}
|
|
2594
|
-
if (field.advanced !== undefined && typeof field.advanced !== 'boolean') {
|
|
2595
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2596
|
-
}
|
|
2597
|
-
if (field.valueLabels !== undefined) {
|
|
2598
|
-
const labels = expectRecord(field.valueLabels, `${fieldName}.valueLabels`);
|
|
2599
|
-
if (isRecord(schemaField) && schemaField.type !== 'enum') {
|
|
2600
|
-
error(`${fieldName}.valueLabels is only supported for enum config`);
|
|
2601
|
-
}
|
|
2602
|
-
if (labels) {
|
|
2603
|
-
for (const [token, label] of Object.entries(labels)) {
|
|
2604
|
-
if (!isRecord(schemaField) || !Array.isArray(schemaField.values) || !schemaField.values.includes(token)) {
|
|
2605
|
-
error(`${fieldName}.valueLabels references unknown enum token "${token}"`);
|
|
2606
|
-
}
|
|
2607
|
-
validateManifestString(label, `${fieldName}.valueLabels.${token}`);
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
if (field.recommendedValue !== undefined) {
|
|
2612
|
-
validateConfigValueLightweight(schemaField, field.recommendedValue, `${fieldName}.recommendedValue`);
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
}
|
|
2616
|
-
}
|
|
2617
|
-
if (root.presets !== undefined) {
|
|
2618
|
-
if (!Array.isArray(root.presets)) {
|
|
2619
|
-
error('config.presentation.presets must be an array');
|
|
2620
|
-
}
|
|
2621
|
-
else {
|
|
2622
|
-
const presetIds = new Set();
|
|
2623
|
-
root.presets.forEach((rawPreset, index) => {
|
|
2624
|
-
const fieldName = `config.presentation.presets[${index}]`;
|
|
2625
|
-
const preset = expectRecord(rawPreset, fieldName);
|
|
2626
|
-
if (!preset)
|
|
2627
|
-
return;
|
|
2628
|
-
validateAllowedKeys(preset, fieldName, ['id', 'title', 'description', 'values']);
|
|
2629
|
-
const validId = validateManifestString(preset.id, `${fieldName}.id`);
|
|
2630
|
-
const id = validId ? preset.id : null;
|
|
2631
|
-
if (id && presetIds.has(id)) {
|
|
2632
|
-
error(`${fieldName}.id has duplicate preset id "${id}"`);
|
|
2633
|
-
}
|
|
2634
|
-
else if (id) {
|
|
2635
|
-
presetIds.add(id);
|
|
2636
|
-
}
|
|
2637
|
-
validateManifestString(preset.title, `${fieldName}.title`);
|
|
2638
|
-
if (preset.description !== undefined) {
|
|
2639
|
-
validateManifestString(preset.description, `${fieldName}.description`);
|
|
2640
|
-
}
|
|
2641
|
-
const values = expectRecord(preset.values, `${fieldName}.values`);
|
|
2642
|
-
if (!values)
|
|
2643
|
-
return;
|
|
2644
|
-
for (const [key, value] of Object.entries(values)) {
|
|
2645
|
-
if (!schemaKeys.has(key)) {
|
|
2646
|
-
error(`${fieldName}.values references unknown config key "${key}"`);
|
|
2647
|
-
continue;
|
|
2648
|
-
}
|
|
2649
|
-
const schemaField = schema[key];
|
|
2650
|
-
if (isRecord(schemaField)
|
|
2651
|
-
&& (schemaField.sensitive === true
|
|
2652
|
-
|| schemaField.type === 'file'
|
|
2653
|
-
|| schemaField.type === 'directory')) {
|
|
2654
|
-
error(`${fieldName}.values.${key} cannot contain sensitive, file, or directory config values`);
|
|
2655
|
-
continue;
|
|
2656
|
-
}
|
|
2657
|
-
validateConfigValueLightweight(schemaField, value, `${fieldName}.values.${key}`);
|
|
2658
|
-
}
|
|
2659
|
-
});
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
1683
|
function validateConfigSchemaLightweight(config) {
|
|
2664
1684
|
if (config === undefined)
|
|
2665
1685
|
return;
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
if (!schema)
|
|
2672
|
-
return;
|
|
2673
|
-
for (const [key, rawField] of Object.entries(schema)) {
|
|
2674
|
-
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
2675
|
-
error(`config.schema has invalid key "${key}"`);
|
|
2676
|
-
}
|
|
2677
|
-
const fieldName = `config.schema.${key}`;
|
|
2678
|
-
const field = expectRecord(rawField, fieldName);
|
|
2679
|
-
if (!field)
|
|
2680
|
-
continue;
|
|
2681
|
-
validateAllowedKeys(field, fieldName, [
|
|
2682
|
-
'type',
|
|
2683
|
-
'title',
|
|
2684
|
-
'description',
|
|
2685
|
-
'default',
|
|
2686
|
-
'required',
|
|
2687
|
-
'sensitive',
|
|
2688
|
-
'values',
|
|
2689
|
-
'min',
|
|
2690
|
-
'max',
|
|
2691
|
-
]);
|
|
2692
|
-
if (typeof field.type !== 'string' || !CONFIG_FIELD_TYPES.has(field.type)) {
|
|
2693
|
-
error(`${fieldName}.type must be string, number, boolean, enum, file, or directory`);
|
|
2694
|
-
}
|
|
2695
|
-
if (field.required !== undefined && typeof field.required !== 'boolean') {
|
|
2696
|
-
error(`${fieldName}.required must be a boolean`);
|
|
2697
|
-
}
|
|
2698
|
-
if (field.sensitive !== undefined && typeof field.sensitive !== 'boolean') {
|
|
2699
|
-
error(`${fieldName}.sensitive must be a boolean`);
|
|
2700
|
-
}
|
|
2701
|
-
if (field.values !== undefined) {
|
|
2702
|
-
if (field.type !== 'enum' || !Array.isArray(field.values) || field.values.length === 0) {
|
|
2703
|
-
error(`${fieldName}.values is only supported for enum config and must be non-empty`);
|
|
2704
|
-
}
|
|
2705
|
-
}
|
|
2706
|
-
else if (field.type === 'enum') {
|
|
2707
|
-
error(`${fieldName}.values is required for enum config`);
|
|
2708
|
-
}
|
|
2709
|
-
if (field.type !== 'number' && (field.min !== undefined || field.max !== undefined)) {
|
|
2710
|
-
error(`${fieldName}.min/max are only supported for number config`);
|
|
2711
|
-
}
|
|
2712
|
-
if (field.min !== undefined && typeof field.min !== 'number') {
|
|
2713
|
-
error(`${fieldName}.min must be a number`);
|
|
2714
|
-
}
|
|
2715
|
-
if (field.max !== undefined && typeof field.max !== 'number') {
|
|
2716
|
-
error(`${fieldName}.max must be a number`);
|
|
2717
|
-
}
|
|
2718
|
-
if (typeof field.min === 'number' && typeof field.max === 'number' && field.min > field.max) {
|
|
2719
|
-
error(`${fieldName}.min must be less than or equal to max`);
|
|
2720
|
-
}
|
|
1686
|
+
try {
|
|
1687
|
+
normalizePluginConfigDefinition(config);
|
|
1688
|
+
}
|
|
1689
|
+
catch (validationError) {
|
|
1690
|
+
error(validationError instanceof Error ? validationError.message : String(validationError));
|
|
2721
1691
|
}
|
|
2722
|
-
validateConfigPresentationLightweight(configRoot.presentation, schema);
|
|
2723
1692
|
}
|
|
2724
1693
|
function configSchemaKeys(manifest) {
|
|
2725
|
-
const schema = manifest.config?.
|
|
1694
|
+
const schema = manifest.config?.fields;
|
|
2726
1695
|
return isRecord(schema) ? new Set(Object.keys(schema)) : new Set();
|
|
2727
1696
|
}
|
|
2728
|
-
function configSchemaField(manifest, key) {
|
|
2729
|
-
const field = manifest.config?.schema?.[key];
|
|
2730
|
-
return isRecord(field) ? field : null;
|
|
2731
|
-
}
|
|
2732
1697
|
function defaultConfigValues(manifest) {
|
|
2733
|
-
const schema = manifest.config?.
|
|
1698
|
+
const schema = manifest.config?.fields;
|
|
2734
1699
|
const values = {};
|
|
2735
1700
|
if (!isRecord(schema))
|
|
2736
1701
|
return values;
|
|
@@ -2741,6 +1706,35 @@ function defaultConfigValues(manifest) {
|
|
|
2741
1706
|
}
|
|
2742
1707
|
return values;
|
|
2743
1708
|
}
|
|
1709
|
+
function configFieldContainsSensitiveValue(field) {
|
|
1710
|
+
if (!isRecord(field))
|
|
1711
|
+
return false;
|
|
1712
|
+
if (field.sensitive === true)
|
|
1713
|
+
return true;
|
|
1714
|
+
if (field.type !== 'object')
|
|
1715
|
+
return false;
|
|
1716
|
+
return ((isRecord(field.properties)
|
|
1717
|
+
&& Object.values(field.properties).some((child) => isRecord(child) && child.sensitive === true))
|
|
1718
|
+
|| (isRecord(field.additionalProperties)
|
|
1719
|
+
&& field.additionalProperties.sensitive === true));
|
|
1720
|
+
}
|
|
1721
|
+
function codexStaticNativeComponentKeys(manifest) {
|
|
1722
|
+
if (!isCodexTargeted(manifest))
|
|
1723
|
+
return [];
|
|
1724
|
+
const keys = [];
|
|
1725
|
+
if (manifest.skills)
|
|
1726
|
+
keys.push('skills');
|
|
1727
|
+
if (manifest.mcpServers)
|
|
1728
|
+
keys.push('mcpServers');
|
|
1729
|
+
if ((Array.isArray(manifest.extensions?.hooks)
|
|
1730
|
+
&& manifest.extensions.hooks.some((hook) => hook?.agentOverrides?.codex !== null))
|
|
1731
|
+
|| (Array.isArray(manifest.extensions?.nativeHooks?.codex)
|
|
1732
|
+
&& manifest.extensions.nativeHooks.codex.length > 0))
|
|
1733
|
+
keys.push('hooks');
|
|
1734
|
+
if (manifest.apps)
|
|
1735
|
+
keys.push('apps');
|
|
1736
|
+
return keys;
|
|
1737
|
+
}
|
|
2744
1738
|
function validateArtifactId(value, fieldName) {
|
|
2745
1739
|
const id = validateManifestString(value, fieldName);
|
|
2746
1740
|
if (!id)
|
|
@@ -2801,7 +1795,7 @@ function validateStringArray(value, fieldName, options = {}) {
|
|
|
2801
1795
|
}
|
|
2802
1796
|
return output;
|
|
2803
1797
|
}
|
|
2804
|
-
function validateArtifactTargetEntry(entry, index, manifest,
|
|
1798
|
+
function validateArtifactTargetEntry(entry, index, manifest, pluginDir) {
|
|
2805
1799
|
const fieldName = `artifacts.targets[${index}]`;
|
|
2806
1800
|
const target = expectRecord(entry, fieldName);
|
|
2807
1801
|
if (!target)
|
|
@@ -2816,6 +1810,7 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2816
1810
|
'projectManagedKind',
|
|
2817
1811
|
'states',
|
|
2818
1812
|
'refreshKeys',
|
|
1813
|
+
'delivery',
|
|
2819
1814
|
'requiresCodexProjectConfig',
|
|
2820
1815
|
'roleKey',
|
|
2821
1816
|
'skillKey',
|
|
@@ -2828,6 +1823,30 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2828
1823
|
error(`${fieldName}.target: unsupported artifact target "${artifactTarget}"`);
|
|
2829
1824
|
}
|
|
2830
1825
|
const portable = artifactTarget === 'agent-skills.skill';
|
|
1826
|
+
let delivery;
|
|
1827
|
+
if (artifactTarget === 'codex.skill') {
|
|
1828
|
+
delivery = expectRecord(target.delivery, `${fieldName}.delivery`);
|
|
1829
|
+
if (delivery) {
|
|
1830
|
+
if (delivery.mode === 'aw-extension') {
|
|
1831
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode']);
|
|
1832
|
+
}
|
|
1833
|
+
else if (delivery.mode === 'codex-native') {
|
|
1834
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode', 'identity', 'renderProfile']);
|
|
1835
|
+
if (delivery.identity !== 'shared') {
|
|
1836
|
+
error(`${fieldName}.delivery.identity: expected "shared"`);
|
|
1837
|
+
}
|
|
1838
|
+
if (delivery.renderProfile !== 'user') {
|
|
1839
|
+
error(`${fieldName}.delivery.renderProfile: expected "user"`);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
else {
|
|
1843
|
+
error(`${fieldName}.delivery.mode: expected "codex-native" or "aw-extension"`);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
else if (target.delivery !== undefined) {
|
|
1848
|
+
error(`${fieldName}.delivery: delivery is only supported for codex.skill targets`);
|
|
1849
|
+
}
|
|
2831
1850
|
const platform = portable || target.platform === undefined
|
|
2832
1851
|
? undefined
|
|
2833
1852
|
: validateManifestString(target.platform, `${fieldName}.platform`);
|
|
@@ -2851,13 +1870,6 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2851
1870
|
error(`${fieldName}.distribution: platform targets do not support distribution`);
|
|
2852
1871
|
}
|
|
2853
1872
|
}
|
|
2854
|
-
const key = id && artifactTarget ? `${id}:${artifactTarget}` : null;
|
|
2855
|
-
if (key) {
|
|
2856
|
-
if (seen.has(key)) {
|
|
2857
|
-
error(`${fieldName}: duplicate artifact id + target "${key}"`);
|
|
2858
|
-
}
|
|
2859
|
-
seen.add(key);
|
|
2860
|
-
}
|
|
2861
1873
|
const expectedKind = ARTIFACT_KIND_BY_TARGET[artifactTarget];
|
|
2862
1874
|
const kind = target.kind === undefined
|
|
2863
1875
|
? expectedKind
|
|
@@ -2900,10 +1912,15 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2900
1912
|
error(`${fieldName}.refreshKeys[${refreshIndex}]: unknown config key "${refreshKey}"`);
|
|
2901
1913
|
continue;
|
|
2902
1914
|
}
|
|
2903
|
-
const
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
1915
|
+
const sensitiveRefreshKey = configFieldContainsSensitiveValue(manifest.config?.fields?.[refreshKey]);
|
|
1916
|
+
const skillTarget = artifactTarget === 'codex.skill'
|
|
1917
|
+
|| artifactTarget === 'claude.skill'
|
|
1918
|
+
|| artifactTarget === 'agent-skills.skill';
|
|
1919
|
+
if (skillTarget && sensitiveRefreshKey) {
|
|
1920
|
+
const native = delivery?.mode === 'codex-native';
|
|
1921
|
+
error(`${native ? 'codexNativeRenderSensitiveRefreshKey' : 'skillRenderSensitiveRefreshKey'}: ` +
|
|
1922
|
+
`${fieldName}.refreshKeys[${refreshIndex}]: ` +
|
|
1923
|
+
`${native ? 'codex-native renderers' : 'Skill renderers'} cannot read sensitive config field "${refreshKey}"`);
|
|
2907
1924
|
}
|
|
2908
1925
|
}
|
|
2909
1926
|
if (target.requiresCodexProjectConfig !== undefined &&
|
|
@@ -2992,6 +2009,7 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2992
2009
|
projectManagedKind,
|
|
2993
2010
|
states,
|
|
2994
2011
|
refreshKeys,
|
|
2012
|
+
delivery,
|
|
2995
2013
|
roleKey,
|
|
2996
2014
|
skillKey,
|
|
2997
2015
|
sourceDirs,
|
|
@@ -3011,10 +2029,69 @@ function validateArtifactsConfigLightweight(pluginDir, manifest) {
|
|
|
3011
2029
|
error('artifacts.targets must be an array');
|
|
3012
2030
|
return [];
|
|
3013
2031
|
}
|
|
3014
|
-
const seen = new Set();
|
|
3015
2032
|
const declarations = artifacts.targets
|
|
3016
|
-
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest,
|
|
2033
|
+
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest, pluginDir))
|
|
3017
2034
|
.filter(Boolean);
|
|
2035
|
+
const idsBySurfaceScope = new Map();
|
|
2036
|
+
const nativeIds = new Map();
|
|
2037
|
+
const nativeSkillKeys = new Map();
|
|
2038
|
+
for (const [declarationIndex, declaration] of declarations.entries()) {
|
|
2039
|
+
for (const scope of declaration.scopes) {
|
|
2040
|
+
const surface = declaration.platform ?? declaration.distribution;
|
|
2041
|
+
const key = `${surface}:${scope}:${declaration.id}`;
|
|
2042
|
+
const existing = idsBySurfaceScope.get(key);
|
|
2043
|
+
if (existing !== undefined) {
|
|
2044
|
+
error(`artifacts.targets has duplicate artifact id "${declaration.id}" for surface/scope "${surface}:${scope}" in targets[${existing}] and targets[${declarationIndex}]`);
|
|
2045
|
+
}
|
|
2046
|
+
else {
|
|
2047
|
+
idsBySurfaceScope.set(key, declarationIndex);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2051
|
+
const existingId = nativeIds.get(declaration.id);
|
|
2052
|
+
if (existingId !== undefined) {
|
|
2053
|
+
error(`artifacts.targets has duplicate codex-native artifact id "${declaration.id}" in the shared projection at targets[${existingId}] and targets[${declarationIndex}]`);
|
|
2054
|
+
}
|
|
2055
|
+
else {
|
|
2056
|
+
nativeIds.set(declaration.id, declarationIndex);
|
|
2057
|
+
}
|
|
2058
|
+
const existingSkillKey = nativeSkillKeys.get(declaration.skillKey);
|
|
2059
|
+
if (existingSkillKey !== undefined) {
|
|
2060
|
+
error(`artifacts.targets has duplicate codex-native skillKey "${declaration.skillKey}" in the shared projection at targets[${existingSkillKey}] and targets[${declarationIndex}]`);
|
|
2061
|
+
}
|
|
2062
|
+
else {
|
|
2063
|
+
nativeSkillKeys.set(declaration.skillKey, declarationIndex);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
const nativeComponents = codexStaticNativeComponentKeys(manifest).map((key) => ({
|
|
2068
|
+
key,
|
|
2069
|
+
scopes: ['project', 'user'],
|
|
2070
|
+
}));
|
|
2071
|
+
declarations.forEach((declaration, index) => {
|
|
2072
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2073
|
+
nativeComponents.push({ key: `artifacts.targets[${index}]`, scopes: declaration.scopes });
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
const expectedNativeComponent = nativeComponents[0];
|
|
2077
|
+
if (expectedNativeComponent) {
|
|
2078
|
+
const canonicalScopes = (component) => [...component.scopes].sort().join(',');
|
|
2079
|
+
const expectedScopes = canonicalScopes(expectedNativeComponent);
|
|
2080
|
+
const mismatch = nativeComponents.find((component) => canonicalScopes(component) !== expectedScopes);
|
|
2081
|
+
if (mismatch) {
|
|
2082
|
+
error(`codexSharedNativeScopeMismatch: Codex native component scope sets must match exactly; ` +
|
|
2083
|
+
`${expectedNativeComponent.key} has {${expectedScopes}} but ${mismatch.key} has ` +
|
|
2084
|
+
`{${canonicalScopes(mismatch)}}`);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
const staticSkillNames = new Set(collectDeclaredSkillNames(pluginDir, manifest).static);
|
|
2088
|
+
for (const [index, declaration] of declarations.entries()) {
|
|
2089
|
+
if (declaration.target === 'codex.skill'
|
|
2090
|
+
&& declaration.delivery?.mode === 'codex-native'
|
|
2091
|
+
&& staticSkillNames.has(declaration.skillKey)) {
|
|
2092
|
+
error(`artifacts.targets[${index}].skillKey: native dynamic Skill "${declaration.skillKey}" conflicts with a static Skill of the same name`);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
3018
2095
|
const owners = new Map();
|
|
3019
2096
|
for (const declaration of declarations) {
|
|
3020
2097
|
if ((declaration.target !== 'codex.skill' && declaration.target !== 'claude.skill' && declaration.target !== 'agent-skills.skill') ||
|
|
@@ -3281,20 +2358,11 @@ function validateRenderedSkillTree(options) {
|
|
|
3281
2358
|
if (!readme || readme.toString('utf8').trim() === '') {
|
|
3282
2359
|
error(`${fieldName} final file tree must contain a non-empty README.md`);
|
|
3283
2360
|
}
|
|
3284
|
-
const sensitiveValues = Object.entries(manifest.config?.schema ?? {})
|
|
3285
|
-
.filter(([, field]) => isRecord(field) && field.sensitive === true)
|
|
3286
|
-
.map(([key, field]) => Object.prototype.hasOwnProperty.call(field, 'default')
|
|
3287
|
-
? String(field.default)
|
|
3288
|
-
: String(defaultConfigValues(manifest)[key] ?? ''))
|
|
3289
|
-
.filter(Boolean);
|
|
3290
2361
|
for (const [relativePath, content] of files) {
|
|
3291
2362
|
const text = content.toString('utf8');
|
|
3292
2363
|
if (/\{\{[^{}]+\}\}/u.test(text)) {
|
|
3293
2364
|
error(`${fieldName} final file tree contains an unresolved template in "${relativePath}"`);
|
|
3294
2365
|
}
|
|
3295
|
-
if (sensitiveValues.some((value) => text.includes(value))) {
|
|
3296
|
-
error(`${fieldName} final file tree exposes sensitive config in "${relativePath}"`);
|
|
3297
|
-
}
|
|
3298
2366
|
}
|
|
3299
2367
|
if (declaration.target === 'codex.skill') {
|
|
3300
2368
|
const openAiYaml = files.get('agents/openai.yaml');
|
|
@@ -3490,26 +2558,83 @@ function validateRenderedArtifactPlan(plan, declarations, scopeLabel, options) {
|
|
|
3490
2558
|
}
|
|
3491
2559
|
}
|
|
3492
2560
|
}
|
|
2561
|
+
function resolveArtifactRendererFixtureRequests(declarations, manifest) {
|
|
2562
|
+
const awOwned = new Map();
|
|
2563
|
+
const codexNative = new Map();
|
|
2564
|
+
for (const declaration of declarations) {
|
|
2565
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2566
|
+
const scope = declaration.scopes.includes('user') ? 'user' : 'project';
|
|
2567
|
+
const request = codexNative.get(scope) ?? {
|
|
2568
|
+
kind: 'codex-native',
|
|
2569
|
+
surface: 'codex',
|
|
2570
|
+
scope,
|
|
2571
|
+
configScope: 'user',
|
|
2572
|
+
declarations: [],
|
|
2573
|
+
};
|
|
2574
|
+
request.declarations.push(declaration);
|
|
2575
|
+
codexNative.set(scope, request);
|
|
2576
|
+
continue;
|
|
2577
|
+
}
|
|
2578
|
+
const surface = declaration.platform ?? declaration.distribution;
|
|
2579
|
+
if (!surface)
|
|
2580
|
+
continue;
|
|
2581
|
+
for (const scope of declaration.scopes) {
|
|
2582
|
+
const key = `${surface}:${scope}`;
|
|
2583
|
+
const request = awOwned.get(key) ?? {
|
|
2584
|
+
kind: 'aw-owned',
|
|
2585
|
+
surface,
|
|
2586
|
+
scope,
|
|
2587
|
+
configScope: scope,
|
|
2588
|
+
declarations: [],
|
|
2589
|
+
};
|
|
2590
|
+
request.declarations.push(declaration);
|
|
2591
|
+
awOwned.set(key, request);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
const partitionedAwOwned = [...awOwned.values()].flatMap((request) => {
|
|
2595
|
+
const hasSkill = request.declarations.some((declaration) => declaration.target === 'codex.skill'
|
|
2596
|
+
|| declaration.target === 'claude.skill'
|
|
2597
|
+
|| declaration.target === 'agent-skills.skill');
|
|
2598
|
+
const hasSensitiveNonSkill = request.declarations.some((declaration) => declaration.target !== 'codex.skill'
|
|
2599
|
+
&& declaration.target !== 'claude.skill'
|
|
2600
|
+
&& declaration.target !== 'agent-skills.skill'
|
|
2601
|
+
&& declaration.refreshKeys.some((key) => configFieldContainsSensitiveValue(manifest.config?.fields?.[key])));
|
|
2602
|
+
if (!hasSkill || !hasSensitiveNonSkill)
|
|
2603
|
+
return [request];
|
|
2604
|
+
const skillDeclarations = request.declarations.filter((declaration) => declaration.target === 'codex.skill'
|
|
2605
|
+
|| declaration.target === 'claude.skill'
|
|
2606
|
+
|| declaration.target === 'agent-skills.skill');
|
|
2607
|
+
const nonSkillDeclarations = request.declarations.filter((declaration) => !skillDeclarations.includes(declaration));
|
|
2608
|
+
return [
|
|
2609
|
+
{ ...request, declarations: skillDeclarations },
|
|
2610
|
+
{ ...request, declarations: nonSkillDeclarations },
|
|
2611
|
+
];
|
|
2612
|
+
});
|
|
2613
|
+
return [...partitionedAwOwned, ...codexNative.values()];
|
|
2614
|
+
}
|
|
3493
2615
|
function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declarations, resolvedRuntimeImports, skillValidationTools) {
|
|
3494
2616
|
if (!renderEntry || declarations.length === 0)
|
|
3495
2617
|
return;
|
|
3496
|
-
const
|
|
2618
|
+
const requests = resolveArtifactRendererFixtureRequests(declarations, manifest);
|
|
3497
2619
|
const values = defaultConfigValues(manifest);
|
|
3498
2620
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aw-artifact-render-'));
|
|
3499
2621
|
try {
|
|
3500
|
-
for (const
|
|
3501
|
-
const
|
|
2622
|
+
for (const [requestIndex, request] of requests.entries()) {
|
|
2623
|
+
const { kind, surface, scope, configScope, declarations: scopedDeclarations, } = request;
|
|
3502
2624
|
const distribution = surface === 'agent-skills' ? surface : undefined;
|
|
3503
2625
|
const platform = distribution ? undefined : surface;
|
|
3504
2626
|
const event = distribution ? 'materialize' : 'install';
|
|
3505
|
-
const
|
|
3506
|
-
|
|
3507
|
-
const
|
|
2627
|
+
const requestRoot = path.join(tempRoot, `${requestIndex}-${kind}-${surface}-${scope}`);
|
|
2628
|
+
const rendererAwHome = path.join(requestRoot, 'aw-home');
|
|
2629
|
+
const rendererCodexHome = path.join(requestRoot, 'codex-home');
|
|
2630
|
+
const rendererClaudeHome = path.join(requestRoot, 'claude-home');
|
|
2631
|
+
fs.mkdirSync(requestRoot, { recursive: true });
|
|
2632
|
+
const contextPath = path.join(requestRoot, 'context.json');
|
|
3508
2633
|
const runtimeImports = scopedDeclarations.flatMap((target) => target.runtimeImports.map((runtimeImport, importIndex) => {
|
|
3509
2634
|
const resolved = resolvedRuntimeImports?.artifactImports.get(authorRuntimeImportKey(target.id, importIndex));
|
|
3510
2635
|
if (!resolved)
|
|
3511
2636
|
return null;
|
|
3512
|
-
const importPath = path.join(
|
|
2637
|
+
const importPath = path.join(requestRoot, `runtime-${target.id}-${importIndex}`);
|
|
3513
2638
|
fs.writeFileSync(importPath, resolved.bytes);
|
|
3514
2639
|
return {
|
|
3515
2640
|
artifactId: target.id,
|
|
@@ -3520,6 +2645,11 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3520
2645
|
digest: resolved.digest,
|
|
3521
2646
|
};
|
|
3522
2647
|
}).filter(Boolean));
|
|
2648
|
+
const refreshKeys = [...new Set(scopedDeclarations.flatMap((target) => target.refreshKeys))];
|
|
2649
|
+
const scopedSchema = Object.fromEntries(refreshKeys.flatMap((key) => manifest.config?.fields?.[key] === undefined
|
|
2650
|
+
? []
|
|
2651
|
+
: [[key, manifest.config.fields[key]]]));
|
|
2652
|
+
const scopedValues = Object.fromEntries(refreshKeys.flatMap((key) => Object.prototype.hasOwnProperty.call(values, key) ? [[key, values[key]]] : []));
|
|
3523
2653
|
fs.writeFileSync(contextPath, JSON.stringify({
|
|
3524
2654
|
event,
|
|
3525
2655
|
surface: distribution
|
|
@@ -3535,13 +2665,14 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3535
2665
|
},
|
|
3536
2666
|
config: {
|
|
3537
2667
|
mode: 'artifact-render',
|
|
3538
|
-
targetScope:
|
|
3539
|
-
|
|
3540
|
-
values,
|
|
3541
|
-
sources: Object.fromEntries(Object.keys(
|
|
2668
|
+
targetScope: configScope,
|
|
2669
|
+
...(manifest.config?.fields === undefined ? {} : { schema: scopedSchema }),
|
|
2670
|
+
values: scopedValues,
|
|
2671
|
+
sources: Object.fromEntries(Object.keys(scopedValues).map((key) => [key, 'default'])),
|
|
3542
2672
|
issues: [],
|
|
3543
2673
|
},
|
|
3544
2674
|
changedConfigKeys: [],
|
|
2675
|
+
requestedArtifactIds: scopedDeclarations.map((target) => target.id),
|
|
3545
2676
|
previousArtifacts: [],
|
|
3546
2677
|
artifactSources: scopedDeclarations.map((target) => ({
|
|
3547
2678
|
id: target.id,
|
|
@@ -3550,14 +2681,10 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3550
2681
|
})),
|
|
3551
2682
|
runtimeImports,
|
|
3552
2683
|
paths: {
|
|
3553
|
-
awHome:
|
|
3554
|
-
codexHome:
|
|
3555
|
-
claudeHome:
|
|
2684
|
+
awHome: rendererAwHome,
|
|
2685
|
+
codexHome: rendererCodexHome,
|
|
2686
|
+
claudeHome: rendererClaudeHome,
|
|
3556
2687
|
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
3557
|
-
configStores: {
|
|
3558
|
-
user: path.join(tempRoot, 'config-user.json'),
|
|
3559
|
-
project: path.join(tempRoot, 'config-project.json'),
|
|
3560
|
-
},
|
|
3561
2688
|
},
|
|
3562
2689
|
renderer: {
|
|
3563
2690
|
runner: renderEntry.runner,
|
|
@@ -3567,14 +2694,17 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3567
2694
|
rendererDigest: 'author-validator-fixture',
|
|
3568
2695
|
},
|
|
3569
2696
|
}), 'utf8');
|
|
3570
|
-
const rendererHome = path.join(
|
|
3571
|
-
const rendererTemp = path.join(
|
|
2697
|
+
const rendererHome = path.join(requestRoot, 'home');
|
|
2698
|
+
const rendererTemp = path.join(requestRoot, 'tmp');
|
|
2699
|
+
for (const rendererStateHome of [rendererAwHome, rendererCodexHome, rendererClaudeHome]) {
|
|
2700
|
+
fs.mkdirSync(rendererStateHome, { recursive: true });
|
|
2701
|
+
}
|
|
3572
2702
|
fs.mkdirSync(rendererHome, { recursive: true });
|
|
3573
2703
|
fs.mkdirSync(rendererTemp, { recursive: true });
|
|
3574
2704
|
const rendererEnv = {
|
|
3575
|
-
AW_HOME:
|
|
3576
|
-
AW_CODEX_HOME:
|
|
3577
|
-
AW_CLAUDE_HOME:
|
|
2705
|
+
AW_HOME: rendererAwHome,
|
|
2706
|
+
AW_CODEX_HOME: rendererCodexHome,
|
|
2707
|
+
AW_CLAUDE_HOME: rendererClaudeHome,
|
|
3578
2708
|
PATH: process.env.PATH ?? process.env.Path ?? '',
|
|
3579
2709
|
AW_PLUGIN_CONTEXT: contextPath,
|
|
3580
2710
|
AW_PLUGIN_ROOT: pluginDir,
|
|
@@ -3655,13 +2785,7 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3655
2785
|
const scripts = expectRecord(manifest.scripts, 'scripts');
|
|
3656
2786
|
if (!scripts)
|
|
3657
2787
|
return null;
|
|
3658
|
-
validateAllowedKeys(scripts, 'scripts', ['
|
|
3659
|
-
if (scripts.timeoutMs !== undefined &&
|
|
3660
|
-
(!Number.isInteger(scripts.timeoutMs) ||
|
|
3661
|
-
scripts.timeoutMs < SCRIPT_TIMEOUT_MIN_MS ||
|
|
3662
|
-
scripts.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
3663
|
-
error(`scripts.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
3664
|
-
}
|
|
2788
|
+
validateAllowedKeys(scripts, 'scripts', ['preApply', 'helpers', 'node', 'artifacts', 'tests']);
|
|
3665
2789
|
const runtimeNodeEntries = [];
|
|
3666
2790
|
let artifactRenderEntry = null;
|
|
3667
2791
|
let packageMeta = null;
|
|
@@ -3678,17 +2802,21 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3678
2802
|
runtimeNodeEntries.push({ path: helperPath, fieldName: 'scripts.helpers.config' });
|
|
3679
2803
|
}
|
|
3680
2804
|
}
|
|
3681
|
-
if (scripts.
|
|
3682
|
-
const
|
|
3683
|
-
if (
|
|
3684
|
-
validateAllowedKeys(
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
}
|
|
3690
|
-
validateLifecycleDefinition(pluginDir, definition, `scripts.lifecycle.${eventName}`, runtimeNodeEntries);
|
|
2805
|
+
if (scripts.preApply !== undefined) {
|
|
2806
|
+
const preApply = expectRecord(scripts.preApply, 'scripts.preApply');
|
|
2807
|
+
if (preApply) {
|
|
2808
|
+
validateAllowedKeys(preApply, 'scripts.preApply', ['path', 'timeoutMs']);
|
|
2809
|
+
const preApplyPath = normalizePluginScriptPath(pluginDir, preApply.path, 'scripts.preApply.path');
|
|
2810
|
+
validateDeclaredScriptFile(pluginDir, preApplyPath, 'scripts.preApply.path');
|
|
2811
|
+
if (preApplyPath && !isNodeRunnablePath(preApplyPath)) {
|
|
2812
|
+
error('scripts.preApply.path must use a Node runnable extension (.js, .mjs, or .cjs)');
|
|
3691
2813
|
}
|
|
2814
|
+
if (preApply.timeoutMs !== undefined && (!Number.isInteger(preApply.timeoutMs)
|
|
2815
|
+
|| preApply.timeoutMs < SCRIPT_TIMEOUT_MIN_MS || preApply.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
2816
|
+
error(`scripts.preApply.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
2817
|
+
}
|
|
2818
|
+
if (preApplyPath)
|
|
2819
|
+
runtimeNodeEntries.push({ path: preApplyPath, fieldName: 'scripts.preApply.path' });
|
|
3692
2820
|
}
|
|
3693
2821
|
}
|
|
3694
2822
|
if (scripts.node !== undefined) {
|
|
@@ -4167,8 +3295,6 @@ function printUsage() {
|
|
|
4167
3295
|
aw plugin validate <plugin-directory> [options]
|
|
4168
3296
|
|
|
4169
3297
|
Options:
|
|
4170
|
-
--catalog-min-version <semver>
|
|
4171
|
-
Validate compatibility.aw.minVersion against a target catalog.
|
|
4172
3298
|
--strict-agent-skills Allow only Agent Skills portable core frontmatter.
|
|
4173
3299
|
--claude-skill-profile <minimum|current>
|
|
4174
3300
|
Validate Claude fields at AW minimum 2.1.88 (default) or current docs.
|
|
@@ -4238,7 +3364,27 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4238
3364
|
}
|
|
4239
3365
|
const skillValidationTools = await tryLoadCliSkillValidationTools();
|
|
4240
3366
|
try {
|
|
4241
|
-
await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3367
|
+
const normalizedManifest = await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3368
|
+
const requirements = await cliManifestTools.resolvePluginRequirements({
|
|
3369
|
+
pluginRoot: pluginDir,
|
|
3370
|
+
manifest: normalizedManifest,
|
|
3371
|
+
implementedCapabilities: cliManifestTools.implementedCapabilities,
|
|
3372
|
+
});
|
|
3373
|
+
if (requirements.unsupportedByCurrentAw.length > 0) {
|
|
3374
|
+
capabilityDiagnostic = {
|
|
3375
|
+
errorCode: 'awCapabilityUnsupported',
|
|
3376
|
+
missingCapabilities: requirements.unsupportedByCurrentAw,
|
|
3377
|
+
availableCapabilities: [...cliManifestTools.implementedCapabilities].sort(),
|
|
3378
|
+
};
|
|
3379
|
+
error(`[awCapabilityUnsupported] missingCapabilities=${JSON.stringify(requirements.unsupportedByCurrentAw)}`);
|
|
3380
|
+
}
|
|
3381
|
+
const satisfiedCapabilities = new Set([...requirements.declared, ...requirements.derived]);
|
|
3382
|
+
const advisoryObservations = requirements.observations.filter((observation) => (observation.code !== 'aw-command-detected'
|
|
3383
|
+
|| observation.capability === undefined
|
|
3384
|
+
|| !satisfiedCapabilities.has(observation.capability)));
|
|
3385
|
+
for (const observation of advisoryObservations) {
|
|
3386
|
+
warn(`[awCapabilityObservation] ${observation.path}: ${observation.message}`);
|
|
3387
|
+
}
|
|
4242
3388
|
}
|
|
4243
3389
|
catch (e) {
|
|
4244
3390
|
error(e.message);
|
|
@@ -4249,8 +3395,7 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4249
3395
|
validateNoBuildOutputDirs(pluginDir);
|
|
4250
3396
|
validateDeclaredNativeAssets(pluginDir, manifest);
|
|
4251
3397
|
validateCanonicalSkillLayout(pluginDir, manifest);
|
|
4252
|
-
|
|
4253
|
-
validatePluginEvalManifest(pluginDir, manifest);
|
|
3398
|
+
await validatePluginEvalManifest(pluginDir, manifest);
|
|
4254
3399
|
const artifactRenderEntry = validateScriptsLightweight(pluginDir, manifest);
|
|
4255
3400
|
const runtimeImports = await resolveAuthorRuntimeImports({
|
|
4256
3401
|
pluginDir,
|
|
@@ -4300,7 +3445,6 @@ function parseArgs(argv) {
|
|
|
4300
3445
|
const args = argv.slice(2);
|
|
4301
3446
|
const options = {
|
|
4302
3447
|
pluginDir: null,
|
|
4303
|
-
catalogMinVersion: null,
|
|
4304
3448
|
strictAgentSkills: false,
|
|
4305
3449
|
claudeSkillProfile: 'minimum',
|
|
4306
3450
|
versionBase: 'HEAD',
|
|
@@ -4357,18 +3501,6 @@ function parseArgs(argv) {
|
|
|
4357
3501
|
}
|
|
4358
3502
|
exitCliError(`Unknown argument: ${arg}`, options.json);
|
|
4359
3503
|
}
|
|
4360
|
-
if (arg === '--catalog-min-version') {
|
|
4361
|
-
const value = args[index + 1];
|
|
4362
|
-
if (!value || value.startsWith('--')) {
|
|
4363
|
-
exitCliError('--catalog-min-version requires a value.', options.json);
|
|
4364
|
-
}
|
|
4365
|
-
index += 1;
|
|
4366
|
-
if (!STRICT_SEMVER_PATTERN.test(value)) {
|
|
4367
|
-
exitCliError(`Invalid --catalog-min-version: expected a strict SemVer 2.0.0 string. Received ${JSON.stringify(value)}.`, options.json);
|
|
4368
|
-
}
|
|
4369
|
-
options.catalogMinVersion = value;
|
|
4370
|
-
continue;
|
|
4371
|
-
}
|
|
4372
3504
|
exitCliError(`Unknown option: ${arg}`, options.json);
|
|
4373
3505
|
}
|
|
4374
3506
|
return options;
|
|
@@ -4400,7 +3532,6 @@ async function main() {
|
|
|
4400
3532
|
const repoRoot = requestedWorkspaceRoot ?? discoverCanonicalRepoRoot(repoRootCandidate);
|
|
4401
3533
|
const gitRoot = discoverCanonicalRepoRoot(findRepoRootCandidate(resolved));
|
|
4402
3534
|
await validatePluginDir(resolved, {
|
|
4403
|
-
catalogMinVersion: args.catalogMinVersion,
|
|
4404
3535
|
strictAgentSkills: args.strictAgentSkills,
|
|
4405
3536
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4406
3537
|
versionBase: args.versionBase,
|
|
@@ -4414,9 +3545,12 @@ async function main() {
|
|
|
4414
3545
|
profile: args.strictAgentSkills ? 'agent-skills-portable-core' : 'aw-source',
|
|
4415
3546
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4416
3547
|
pluginDirectory: resolved,
|
|
3548
|
+
workspaceRoot: process.env.AW_AUTHORING_WORKSPACE_ROOT ?? repoRoot,
|
|
3549
|
+
workspaceRootResolvedFrom: process.env.AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM ?? 'explicit',
|
|
4417
3550
|
versionCheck: pluginVersionCheckResult,
|
|
4418
3551
|
warnings,
|
|
4419
3552
|
errors,
|
|
3553
|
+
...(capabilityDiagnostic ?? {}),
|
|
4420
3554
|
}, null, 2));
|
|
4421
3555
|
if (errors.length > 0) {
|
|
4422
3556
|
process.exit(1);
|