@overcrow/mcp 0.0.0 → 0.1.1
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/LICENSE +21 -0
- package/README.md +91 -1
- package/dist/audit/index.js +103 -0
- package/dist/audit/lexer.js +417 -0
- package/dist/audit/project.js +200 -0
- package/dist/audit/rules.js +661 -0
- package/dist/bootstrap/download.js +229 -0
- package/dist/bootstrap/install.js +280 -0
- package/dist/bootstrap/pin.js +85 -0
- package/dist/bootstrap/pin.json +119 -0
- package/dist/bootstrap/zip.js +258 -0
- package/dist/cli.js +200 -0
- package/dist/confine.js +244 -0
- package/dist/content/docs/en/cli.md +768 -0
- package/dist/content/docs/en/creator-space.md +78 -0
- package/dist/content/docs/en/dev-channel.md +130 -0
- package/dist/content/docs/en/elements.md +469 -0
- package/dist/content/docs/en/forms.md +328 -0
- package/dist/content/docs/en/guide.md +252 -0
- package/dist/content/docs/en/index.md +85 -0
- package/dist/content/docs/en/limits.md +241 -0
- package/dist/content/docs/en/logic.md +446 -0
- package/dist/content/docs/en/manifest.md +388 -0
- package/dist/content/docs/en/mcp.md +165 -0
- package/dist/content/docs/en/package.md +131 -0
- package/dist/content/docs/en/publish-a-widget.md +94 -0
- package/dist/content/docs/en/publish-keys.md +95 -0
- package/dist/content/docs/en/publishing.md +167 -0
- package/dist/content/docs/en/reference.md +55 -0
- package/dist/content/docs/en/result-shapes.md +332 -0
- package/dist/content/docs/en/sdk.md +295 -0
- package/dist/content/docs/en/security.md +115 -0
- package/dist/content/docs/en/service-reference.md +344 -0
- package/dist/content/docs/en/services.md +517 -0
- package/dist/content/docs/en/style-properties.md +270 -0
- package/dist/content/docs/en/style.md +266 -0
- package/dist/content/docs/en/testing.md +400 -0
- package/dist/content/docs/en/verified-domain.md +46 -0
- package/dist/content/docs/en/view.md +338 -0
- package/dist/content/docs/en/widget-listing.md +65 -0
- package/dist/content/docs/en/widget-review.md +57 -0
- package/dist/content/docs/en/widget-statistics.md +134 -0
- package/dist/content/docs/en/widgets.md +76 -0
- package/dist/content/docs/fr/cli.md +802 -0
- package/dist/content/docs/fr/creator-space.md +83 -0
- package/dist/content/docs/fr/dev-channel.md +140 -0
- package/dist/content/docs/fr/elements.md +475 -0
- package/dist/content/docs/fr/forms.md +341 -0
- package/dist/content/docs/fr/guide.md +263 -0
- package/dist/content/docs/fr/index.md +90 -0
- package/dist/content/docs/fr/limits.md +244 -0
- package/dist/content/docs/fr/logic.md +461 -0
- package/dist/content/docs/fr/manifest.md +401 -0
- package/dist/content/docs/fr/mcp.md +173 -0
- package/dist/content/docs/fr/package.md +139 -0
- package/dist/content/docs/fr/publish-a-widget.md +101 -0
- package/dist/content/docs/fr/publish-keys.md +97 -0
- package/dist/content/docs/fr/publishing.md +177 -0
- package/dist/content/docs/fr/reference.md +59 -0
- package/dist/content/docs/fr/result-shapes.md +333 -0
- package/dist/content/docs/fr/sdk.md +301 -0
- package/dist/content/docs/fr/security.md +129 -0
- package/dist/content/docs/fr/service-reference.md +346 -0
- package/dist/content/docs/fr/services.md +540 -0
- package/dist/content/docs/fr/style-properties.md +272 -0
- package/dist/content/docs/fr/style.md +281 -0
- package/dist/content/docs/fr/testing.md +417 -0
- package/dist/content/docs/fr/verified-domain.md +49 -0
- package/dist/content/docs/fr/view.md +352 -0
- package/dist/content/docs/fr/widget-listing.md +69 -0
- package/dist/content/docs/fr/widget-review.md +59 -0
- package/dist/content/docs/fr/widget-statistics.md +141 -0
- package/dist/content/docs/fr/widgets.md +81 -0
- package/dist/content/examples/clock/LICENSE +21 -0
- package/dist/content/examples/clock/README.md +75 -0
- package/dist/content/examples/clock/listing.json +19 -0
- package/dist/content/examples/clock/locales/en.json +4 -0
- package/dist/content/examples/clock/locales/fr.json +4 -0
- package/dist/content/examples/clock/logic.ts +85 -0
- package/dist/content/examples/clock/manifest.json +44 -0
- package/dist/content/examples/clock/package.json +16 -0
- package/dist/content/examples/clock/style.ocss +26 -0
- package/dist/content/examples/clock/tests/hidden.scenario.json +68 -0
- package/dist/content/examples/clock/tests/load-logic.mjs +22 -0
- package/dist/content/examples/clock/tests/logic.test.mjs +89 -0
- package/dist/content/examples/clock/tests/options.scenario.json +108 -0
- package/dist/content/examples/clock/tests/preview.scenario.json +25 -0
- package/dist/content/examples/clock/tests/render.scenario.json +94 -0
- package/dist/content/examples/clock/tests/summer-time.scenario.json +74 -0
- package/dist/content/examples/clock/tests/ticks.scenario.json +120 -0
- package/dist/content/examples/clock/tsconfig.json +16 -0
- package/dist/content/examples/clock/view.ocml +8 -0
- package/dist/content/examples/countdown/LICENSE +20 -0
- package/dist/content/examples/countdown/listing.json +10 -0
- package/dist/content/examples/countdown/locales/en.json +15 -0
- package/dist/content/examples/countdown/locales/fr.json +15 -0
- package/dist/content/examples/countdown/logic.ts +179 -0
- package/dist/content/examples/countdown/manifest.json +34 -0
- package/dist/content/examples/countdown/package.json +15 -0
- package/dist/content/examples/countdown/style.ocss +94 -0
- package/dist/content/examples/countdown/tsconfig.json +16 -0
- package/dist/content/examples/countdown/view.ocml +42 -0
- package/dist/content/examples/fps/LICENSE +21 -0
- package/dist/content/examples/fps/README.md +64 -0
- package/dist/content/examples/fps/listing.json +19 -0
- package/dist/content/examples/fps/locales/en.json +11 -0
- package/dist/content/examples/fps/locales/fr.json +11 -0
- package/dist/content/examples/fps/logic.ts +64 -0
- package/dist/content/examples/fps/manifest.json +30 -0
- package/dist/content/examples/fps/package.json +16 -0
- package/dist/content/examples/fps/style.ocss +31 -0
- package/dist/content/examples/fps/submission.json +8 -0
- package/dist/content/examples/fps/tests/label.scenario.json +63 -0
- package/dist/content/examples/fps/tests/load-logic.mjs +22 -0
- package/dist/content/examples/fps/tests/logic.test.mjs +60 -0
- package/dist/content/examples/fps/tests/preview.scenario.json +29 -0
- package/dist/content/examples/fps/tests/refused.scenario.json +20 -0
- package/dist/content/examples/fps/tests/render.scenario.json +96 -0
- package/dist/content/examples/fps/tests/stale.scenario.json +91 -0
- package/dist/content/examples/fps/tests/unavailable.scenario.json +111 -0
- package/dist/content/examples/fps/tsconfig.json +16 -0
- package/dist/content/examples/fps/view.ocml +11 -0
- package/dist/content/examples/media/LICENSE +21 -0
- package/dist/content/examples/media/README.md +147 -0
- package/dist/content/examples/media/listing.json +19 -0
- package/dist/content/examples/media/locales/en.json +11 -0
- package/dist/content/examples/media/locales/fr.json +11 -0
- package/dist/content/examples/media/logic.ts +191 -0
- package/dist/content/examples/media/manifest.json +35 -0
- package/dist/content/examples/media/package.json +16 -0
- package/dist/content/examples/media/style.ocss +148 -0
- package/dist/content/examples/media/submission.json +12 -0
- package/dist/content/examples/media/tests/covers.scenario.json +144 -0
- package/dist/content/examples/media/tests/gesture.scenario.json +173 -0
- package/dist/content/examples/media/tests/load-logic.mjs +22 -0
- package/dist/content/examples/media/tests/logic.test.mjs +176 -0
- package/dist/content/examples/media/tests/marquee.scenario.json +127 -0
- package/dist/content/examples/media/tests/preview.scenario.json +52 -0
- package/dist/content/examples/media/tests/read-only.scenario.json +53 -0
- package/dist/content/examples/media/tests/refused.scenario.json +22 -0
- package/dist/content/examples/media/tests/refused.test.mjs +31 -0
- package/dist/content/examples/media/tests/render.scenario.json +136 -0
- package/dist/content/examples/media/tests/states.scenario.json +257 -0
- package/dist/content/examples/media/tests/unavailable.scenario.json +120 -0
- package/dist/content/examples/media/tsconfig.json +16 -0
- package/dist/content/examples/media/view.ocml +41 -0
- package/dist/content/examples/notes/LICENSE +21 -0
- package/dist/content/examples/notes/README.md +219 -0
- package/dist/content/examples/notes/listing.json +19 -0
- package/dist/content/examples/notes/locales/en.json +25 -0
- package/dist/content/examples/notes/locales/fr.json +25 -0
- package/dist/content/examples/notes/logic.ts +799 -0
- package/dist/content/examples/notes/manifest.json +40 -0
- package/dist/content/examples/notes/package.json +16 -0
- package/dist/content/examples/notes/style.ocss +469 -0
- package/dist/content/examples/notes/submission.json +12 -0
- package/dist/content/examples/notes/tests/checks.scenario.json +237 -0
- package/dist/content/examples/notes/tests/create.scenario.json +260 -0
- package/dist/content/examples/notes/tests/delete.scenario.json +342 -0
- package/dist/content/examples/notes/tests/drafts.scenario.json +714 -0
- package/dist/content/examples/notes/tests/editor.scenario.json +232 -0
- package/dist/content/examples/notes/tests/frame.scenario.json +93 -0
- package/dist/content/examples/notes/tests/interactive.scenario.json +344 -0
- package/dist/content/examples/notes/tests/limits.scenario.json +1351 -0
- package/dist/content/examples/notes/tests/load-logic.mjs +22 -0
- package/dist/content/examples/notes/tests/logic.test.mjs +514 -0
- package/dist/content/examples/notes/tests/preview.scenario.json +99 -0
- package/dist/content/examples/notes/tests/read-only.scenario.json +105 -0
- package/dist/content/examples/notes/tests/refused.scenario.json +33 -0
- package/dist/content/examples/notes/tests/save.scenario.json +378 -0
- package/dist/content/examples/notes/tests/states.scenario.json +640 -0
- package/dist/content/examples/notes/tests/typing.scenario.json +562 -0
- package/dist/content/examples/notes/tests/unavailable.scenario.json +246 -0
- package/dist/content/examples/notes/tsconfig.json +16 -0
- package/dist/content/examples/notes/view.ocml +154 -0
- package/dist/content/examples/performance/LICENSE +21 -0
- package/dist/content/examples/performance/README.md +134 -0
- package/dist/content/examples/performance/listing.json +19 -0
- package/dist/content/examples/performance/locales/en.json +20 -0
- package/dist/content/examples/performance/locales/fr.json +20 -0
- package/dist/content/examples/performance/logic.ts +210 -0
- package/dist/content/examples/performance/manifest.json +83 -0
- package/dist/content/examples/performance/package.json +16 -0
- package/dist/content/examples/performance/style.ocss +75 -0
- package/dist/content/examples/performance/submission.json +12 -0
- package/dist/content/examples/performance/tests/fahrenheit.scenario.json +110 -0
- package/dist/content/examples/performance/tests/horizontal.scenario.json +96 -0
- package/dist/content/examples/performance/tests/load-logic.mjs +22 -0
- package/dist/content/examples/performance/tests/logic.test.mjs +226 -0
- package/dist/content/examples/performance/tests/menu.scenario.json +170 -0
- package/dist/content/examples/performance/tests/no-fps-source.scenario.json +47 -0
- package/dist/content/examples/performance/tests/preview.scenario.json +45 -0
- package/dist/content/examples/performance/tests/refused.scenario.json +20 -0
- package/dist/content/examples/performance/tests/refused.test.mjs +19 -0
- package/dist/content/examples/performance/tests/region.scenario.json +89 -0
- package/dist/content/examples/performance/tests/render.scenario.json +112 -0
- package/dist/content/examples/performance/tests/sources.scenario.json +151 -0
- package/dist/content/examples/performance/tests/stale.scenario.json +111 -0
- package/dist/content/examples/performance/tests/telemetry-only.scenario.json +44 -0
- package/dist/content/examples/performance/tests/thresholds.scenario.json +147 -0
- package/dist/content/examples/performance/tests/waiting.scenario.json +105 -0
- package/dist/content/examples/performance/tsconfig.json +16 -0
- package/dist/content/examples/performance/view.ocml +18 -0
- package/dist/content/examples/playervox-journal/LICENSE +21 -0
- package/dist/content/examples/playervox-journal/README.md +150 -0
- package/dist/content/examples/playervox-journal/listing.json +19 -0
- package/dist/content/examples/playervox-journal/locales/en.json +25 -0
- package/dist/content/examples/playervox-journal/locales/fr.json +25 -0
- package/dist/content/examples/playervox-journal/logic.ts +556 -0
- package/dist/content/examples/playervox-journal/manifest.json +19 -0
- package/dist/content/examples/playervox-journal/package.json +16 -0
- package/dist/content/examples/playervox-journal/style.ocss +142 -0
- package/dist/content/examples/playervox-journal/submission.json +12 -0
- package/dist/content/examples/playervox-journal/tests/changes.scenario.json +482 -0
- package/dist/content/examples/playervox-journal/tests/delete.scenario.json +432 -0
- package/dist/content/examples/playervox-journal/tests/frame.scenario.json +169 -0
- package/dist/content/examples/playervox-journal/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-journal/tests/logic.test.mjs +349 -0
- package/dist/content/examples/playervox-journal/tests/paging.scenario.json +428 -0
- package/dist/content/examples/playervox-journal/tests/preview.scenario.json +184 -0
- package/dist/content/examples/playervox-journal/tests/read-only.scenario.json +157 -0
- package/dist/content/examples/playervox-journal/tests/refused.scenario.json +39 -0
- package/dist/content/examples/playervox-journal/tests/scale.scenario.json +286 -0
- package/dist/content/examples/playervox-journal/tests/signed-out.scenario.json +134 -0
- package/dist/content/examples/playervox-journal/tests/states.scenario.json +741 -0
- package/dist/content/examples/playervox-journal/tests/unavailable.scenario.json +183 -0
- package/dist/content/examples/playervox-journal/tsconfig.json +16 -0
- package/dist/content/examples/playervox-journal/view.ocml +52 -0
- package/dist/content/examples/playervox-rating/LICENSE +21 -0
- package/dist/content/examples/playervox-rating/README.md +166 -0
- package/dist/content/examples/playervox-rating/listing.json +19 -0
- package/dist/content/examples/playervox-rating/locales/en.json +35 -0
- package/dist/content/examples/playervox-rating/locales/fr.json +35 -0
- package/dist/content/examples/playervox-rating/logic.ts +581 -0
- package/dist/content/examples/playervox-rating/manifest.json +19 -0
- package/dist/content/examples/playervox-rating/package.json +16 -0
- package/dist/content/examples/playervox-rating/style.ocss +333 -0
- package/dist/content/examples/playervox-rating/submission.json +12 -0
- package/dist/content/examples/playervox-rating/tests/account.scenario.json +125 -0
- package/dist/content/examples/playervox-rating/tests/bounds.scenario.json +73 -0
- package/dist/content/examples/playervox-rating/tests/draft.scenario.json +199 -0
- package/dist/content/examples/playervox-rating/tests/form.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-rating/tests/logic.test.mjs +346 -0
- package/dist/content/examples/playervox-rating/tests/offline.scenario.json +142 -0
- package/dist/content/examples/playervox-rating/tests/preview.scenario.json +44 -0
- package/dist/content/examples/playervox-rating/tests/publish-keyboard.scenario.json +221 -0
- package/dist/content/examples/playervox-rating/tests/publish-pointer.scenario.json +314 -0
- package/dist/content/examples/playervox-rating/tests/read-only.scenario.json +70 -0
- package/dist/content/examples/playervox-rating/tests/refused.scenario.json +45 -0
- package/dist/content/examples/playervox-rating/tests/states.scenario.json +362 -0
- package/dist/content/examples/playervox-rating/tsconfig.json +16 -0
- package/dist/content/examples/playervox-rating/view.ocml +62 -0
- package/dist/content/examples/playervox-reviews/LICENSE +21 -0
- package/dist/content/examples/playervox-reviews/README.md +194 -0
- package/dist/content/examples/playervox-reviews/listing.json +19 -0
- package/dist/content/examples/playervox-reviews/locales/en.json +32 -0
- package/dist/content/examples/playervox-reviews/locales/fr.json +32 -0
- package/dist/content/examples/playervox-reviews/logic.ts +822 -0
- package/dist/content/examples/playervox-reviews/manifest.json +30 -0
- package/dist/content/examples/playervox-reviews/package.json +16 -0
- package/dist/content/examples/playervox-reviews/style.ocss +260 -0
- package/dist/content/examples/playervox-reviews/submission.json +8 -0
- package/dist/content/examples/playervox-reviews/tests/account.scenario.json +249 -0
- package/dist/content/examples/playervox-reviews/tests/bounds.scenario.json +336 -0
- package/dist/content/examples/playervox-reviews/tests/changes.scenario.json +515 -0
- package/dist/content/examples/playervox-reviews/tests/filter.scenario.json +400 -0
- package/dist/content/examples/playervox-reviews/tests/frame.scenario.json +185 -0
- package/dist/content/examples/playervox-reviews/tests/keyboard.scenario.json +188 -0
- package/dist/content/examples/playervox-reviews/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-reviews/tests/logic.test.mjs +559 -0
- package/dist/content/examples/playervox-reviews/tests/offline-start.scenario.json +152 -0
- package/dist/content/examples/playervox-reviews/tests/offline.scenario.json +311 -0
- package/dist/content/examples/playervox-reviews/tests/paging.scenario.json +497 -0
- package/dist/content/examples/playervox-reviews/tests/preview.scenario.json +286 -0
- package/dist/content/examples/playervox-reviews/tests/reading.scenario.json +591 -0
- package/dist/content/examples/playervox-reviews/tests/refused.scenario.json +54 -0
- package/dist/content/examples/playervox-reviews/tests/scale.scenario.json +303 -0
- package/dist/content/examples/playervox-reviews/tests/states.scenario.json +700 -0
- package/dist/content/examples/playervox-reviews/tests/unavailable.scenario.json +192 -0
- package/dist/content/examples/playervox-reviews/tsconfig.json +16 -0
- package/dist/content/examples/playervox-reviews/view.ocml +110 -0
- package/dist/content/examples/playervox-score/LICENSE +21 -0
- package/dist/content/examples/playervox-score/README.md +131 -0
- package/dist/content/examples/playervox-score/listing.json +19 -0
- package/dist/content/examples/playervox-score/locales/en.json +23 -0
- package/dist/content/examples/playervox-score/locales/fr.json +23 -0
- package/dist/content/examples/playervox-score/logic.ts +430 -0
- package/dist/content/examples/playervox-score/manifest.json +47 -0
- package/dist/content/examples/playervox-score/package.json +16 -0
- package/dist/content/examples/playervox-score/style.ocss +247 -0
- package/dist/content/examples/playervox-score/submission.json +8 -0
- package/dist/content/examples/playervox-score/tests/grades.scenario.json +279 -0
- package/dist/content/examples/playervox-score/tests/load-logic.mjs +22 -0
- package/dist/content/examples/playervox-score/tests/logic.test.mjs +229 -0
- package/dist/content/examples/playervox-score/tests/options.scenario.json +302 -0
- package/dist/content/examples/playervox-score/tests/preview.scenario.json +68 -0
- package/dist/content/examples/playervox-score/tests/pulse.scenario.json +157 -0
- package/dist/content/examples/playervox-score/tests/refused.scenario.json +21 -0
- package/dist/content/examples/playervox-score/tests/signed-out.scenario.json +46 -0
- package/dist/content/examples/playervox-score/tests/states.scenario.json +450 -0
- package/dist/content/examples/playervox-score/tests/unavailable.scenario.json +84 -0
- package/dist/content/examples/playervox-score/tsconfig.json +16 -0
- package/dist/content/examples/playervox-score/view.ocml +51 -0
- package/dist/content/examples/session/LICENSE +21 -0
- package/dist/content/examples/session/README.md +57 -0
- package/dist/content/examples/session/listing.json +19 -0
- package/dist/content/examples/session/locales/en.json +3 -0
- package/dist/content/examples/session/locales/fr.json +3 -0
- package/dist/content/examples/session/logic.ts +26 -0
- package/dist/content/examples/session/manifest.json +19 -0
- package/dist/content/examples/session/package.json +16 -0
- package/dist/content/examples/session/style.ocss +16 -0
- package/dist/content/examples/session/submission.json +8 -0
- package/dist/content/examples/session/tests/hidden.scenario.json +45 -0
- package/dist/content/examples/session/tests/load-logic.mjs +22 -0
- package/dist/content/examples/session/tests/logic.test.mjs +37 -0
- package/dist/content/examples/session/tests/preview.scenario.json +28 -0
- package/dist/content/examples/session/tests/refused.scenario.json +20 -0
- package/dist/content/examples/session/tests/refused.test.mjs +16 -0
- package/dist/content/examples/session/tests/render.scenario.json +95 -0
- package/dist/content/examples/session/tests/ticks.scenario.json +139 -0
- package/dist/content/examples/session/tests/unknown.scenario.json +37 -0
- package/dist/content/examples/session/tsconfig.json +16 -0
- package/dist/content/examples/session/view.ocml +11 -0
- package/dist/content/examples/stopwatch/LICENSE +21 -0
- package/dist/content/examples/stopwatch/README.md +99 -0
- package/dist/content/examples/stopwatch/listing.json +19 -0
- package/dist/content/examples/stopwatch/locales/en.json +8 -0
- package/dist/content/examples/stopwatch/locales/fr.json +8 -0
- package/dist/content/examples/stopwatch/logic.ts +93 -0
- package/dist/content/examples/stopwatch/manifest.json +19 -0
- package/dist/content/examples/stopwatch/package.json +16 -0
- package/dist/content/examples/stopwatch/style.ocss +54 -0
- package/dist/content/examples/stopwatch/submission.json +12 -0
- package/dist/content/examples/stopwatch/tests/errors.scenario.json +121 -0
- package/dist/content/examples/stopwatch/tests/gesture.scenario.json +154 -0
- package/dist/content/examples/stopwatch/tests/hidden.scenario.json +57 -0
- package/dist/content/examples/stopwatch/tests/hundredths.scenario.json +173 -0
- package/dist/content/examples/stopwatch/tests/load-logic.mjs +22 -0
- package/dist/content/examples/stopwatch/tests/logic.test.mjs +99 -0
- package/dist/content/examples/stopwatch/tests/preview.scenario.json +36 -0
- package/dist/content/examples/stopwatch/tests/read-only.scenario.json +42 -0
- package/dist/content/examples/stopwatch/tests/read-only.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/refused.scenario.json +22 -0
- package/dist/content/examples/stopwatch/tests/refused.test.mjs +18 -0
- package/dist/content/examples/stopwatch/tests/render.scenario.json +105 -0
- package/dist/content/examples/stopwatch/tests/states.scenario.json +246 -0
- package/dist/content/examples/stopwatch/tests/unknown.scenario.json +40 -0
- package/dist/content/examples/stopwatch/tsconfig.json +16 -0
- package/dist/content/examples/stopwatch/view.ocml +29 -0
- package/dist/content/examples/twitch-chat/LICENSE +21 -0
- package/dist/content/examples/twitch-chat/README.md +283 -0
- package/dist/content/examples/twitch-chat/listing.json +19 -0
- package/dist/content/examples/twitch-chat/locales/en.json +52 -0
- package/dist/content/examples/twitch-chat/locales/fr.json +52 -0
- package/dist/content/examples/twitch-chat/logic.ts +887 -0
- package/dist/content/examples/twitch-chat/manifest.json +33 -0
- package/dist/content/examples/twitch-chat/package.json +16 -0
- package/dist/content/examples/twitch-chat/style.ocss +544 -0
- package/dist/content/examples/twitch-chat/submission.json +12 -0
- package/dist/content/examples/twitch-chat/tests/account.scenario.json +325 -0
- package/dist/content/examples/twitch-chat/tests/burst.scenario.json +1866 -0
- package/dist/content/examples/twitch-chat/tests/channel-refused.scenario.json +372 -0
- package/dist/content/examples/twitch-chat/tests/channel.scenario.json +480 -0
- package/dist/content/examples/twitch-chat/tests/draft.scenario.json +279 -0
- package/dist/content/examples/twitch-chat/tests/fade.scenario.json +269 -0
- package/dist/content/examples/twitch-chat/tests/failures.scenario.json +420 -0
- package/dist/content/examples/twitch-chat/tests/favorites.scenario.json +376 -0
- package/dist/content/examples/twitch-chat/tests/frame.scenario.json +152 -0
- package/dist/content/examples/twitch-chat/tests/lifetime.scenario.json +186 -0
- package/dist/content/examples/twitch-chat/tests/load-logic.mjs +22 -0
- package/dist/content/examples/twitch-chat/tests/logic.test.mjs +1053 -0
- package/dist/content/examples/twitch-chat/tests/passive.scenario.json +536 -0
- package/dist/content/examples/twitch-chat/tests/preview.scenario.json +147 -0
- package/dist/content/examples/twitch-chat/tests/read-only.scenario.json +202 -0
- package/dist/content/examples/twitch-chat/tests/read-only.test.mjs +63 -0
- package/dist/content/examples/twitch-chat/tests/refused.scenario.json +65 -0
- package/dist/content/examples/twitch-chat/tests/refused.test.mjs +43 -0
- package/dist/content/examples/twitch-chat/tests/render.scenario.json +347 -0
- package/dist/content/examples/twitch-chat/tests/reply.scenario.json +384 -0
- package/dist/content/examples/twitch-chat/tests/send-refused.scenario.json +383 -0
- package/dist/content/examples/twitch-chat/tests/send.scenario.json +300 -0
- package/dist/content/examples/twitch-chat/tests/states.scenario.json +455 -0
- package/dist/content/examples/twitch-chat/tests/unavailable.scenario.json +256 -0
- package/dist/content/examples/twitch-chat/tests/unread.scenario.json +997 -0
- package/dist/content/examples/twitch-chat/tsconfig.json +16 -0
- package/dist/content/examples/twitch-chat/view.ocml +141 -0
- package/dist/content/examples/warframe-market/LICENSE +21 -0
- package/dist/content/examples/warframe-market/README.md +176 -0
- package/dist/content/examples/warframe-market/listing.json +19 -0
- package/dist/content/examples/warframe-market/locales/en.json +44 -0
- package/dist/content/examples/warframe-market/locales/fr.json +44 -0
- package/dist/content/examples/warframe-market/logic.ts +1265 -0
- package/dist/content/examples/warframe-market/manifest.json +92 -0
- package/dist/content/examples/warframe-market/package.json +16 -0
- package/dist/content/examples/warframe-market/style.ocss +422 -0
- package/dist/content/examples/warframe-market/submission.json +24 -0
- package/dist/content/examples/warframe-market/tests/bounds.scenario.json +60 -0
- package/dist/content/examples/warframe-market/tests/cached.scenario.json +70 -0
- package/dist/content/examples/warframe-market/tests/detail.scenario.json +251 -0
- package/dist/content/examples/warframe-market/tests/errors.scenario.json +105 -0
- package/dist/content/examples/warframe-market/tests/fixtures.json +295 -0
- package/dist/content/examples/warframe-market/tests/load-logic.mjs +22 -0
- package/dist/content/examples/warframe-market/tests/logic.test.mjs +505 -0
- package/dist/content/examples/warframe-market/tests/no-clipboard.scenario.json +104 -0
- package/dist/content/examples/warframe-market/tests/passive.scenario.json +87 -0
- package/dist/content/examples/warframe-market/tests/preview.scenario.json +146 -0
- package/dist/content/examples/warframe-market/tests/refresh.scenario.json +56 -0
- package/dist/content/examples/warframe-market/tests/refused.scenario.json +74 -0
- package/dist/content/examples/warframe-market/tests/render.scenario.json +210 -0
- package/dist/content/examples/warframe-market/tests/search.scenario.json +189 -0
- package/dist/content/examples/warframe-market/tests/unavailable.scenario.json +54 -0
- package/dist/content/examples/warframe-market/tsconfig.json +16 -0
- package/dist/content/examples/warframe-market/view.ocml +116 -0
- package/dist/content/examples/weather/LICENSE +20 -0
- package/dist/content/examples/weather/listing.json +10 -0
- package/dist/content/examples/weather/locales/en.json +9 -0
- package/dist/content/examples/weather/locales/fr.json +9 -0
- package/dist/content/examples/weather/logic.ts +62 -0
- package/dist/content/examples/weather/manifest.json +51 -0
- package/dist/content/examples/weather/package.json +15 -0
- package/dist/content/examples/weather/style.ocss +29 -0
- package/dist/content/examples/weather/submission.json +17 -0
- package/dist/content/examples/weather/tsconfig.json +16 -0
- package/dist/content/examples/weather/view.ocml +12 -0
- package/dist/content/index.json +3705 -0
- package/dist/content/reference/errors.json +1924 -0
- package/dist/content/reference/limits.json +1154 -0
- package/dist/content/reference/manifest.md +223 -0
- package/dist/content/reference/permissions.json +413 -0
- package/dist/content/templates/blank/locales/en.json +3 -0
- package/dist/content/templates/blank/locales/fr.json +3 -0
- package/dist/content/templates/blank/logic.ts +7 -0
- package/dist/content/templates/blank/manifest.json +16 -0
- package/dist/content/templates/blank/style.ocss +10 -0
- package/dist/content/templates/blank/tests/example.scenario.json +10 -0
- package/dist/content/templates/blank/view.ocml +6 -0
- package/dist/content/templates/chart/locales/en.json +3 -0
- package/dist/content/templates/chart/locales/fr.json +3 -0
- package/dist/content/templates/chart/logic.ts +27 -0
- package/dist/content/templates/chart/manifest.json +16 -0
- package/dist/content/templates/chart/style.ocss +27 -0
- package/dist/content/templates/chart/tests/example.scenario.json +10 -0
- package/dist/content/templates/chart/view.ocml +10 -0
- package/dist/content/templates/counter/locales/en.json +4 -0
- package/dist/content/templates/counter/locales/fr.json +4 -0
- package/dist/content/templates/counter/logic.ts +20 -0
- package/dist/content/templates/counter/manifest.json +16 -0
- package/dist/content/templates/counter/style.ocss +26 -0
- package/dist/content/templates/counter/tests/example.scenario.json +11 -0
- package/dist/content/templates/counter/view.ocml +13 -0
- package/dist/content/templates/list/locales/en.json +8 -0
- package/dist/content/templates/list/locales/fr.json +8 -0
- package/dist/content/templates/list/logic.ts +35 -0
- package/dist/content/templates/list/manifest.json +16 -0
- package/dist/content/templates/list/style.ocss +42 -0
- package/dist/content/templates/list/tests/example.scenario.json +10 -0
- package/dist/content/templates/list/view.ocml +17 -0
- package/dist/content/templates/shared/LICENSE +20 -0
- package/dist/content/templates/shared/package.json +14 -0
- package/dist/content/templates/shared/tsconfig.json +16 -0
- package/dist/content.js +68 -0
- package/dist/env.js +156 -0
- package/dist/fingerprint.js +32 -0
- package/dist/hidden.js +47 -0
- package/dist/ids.js +27 -0
- package/dist/index.js +82 -0
- package/dist/knowledge.js +222 -0
- package/dist/npm.js +149 -0
- package/dist/prompts.js +47 -0
- package/dist/resources.js +82 -0
- package/dist/run.js +120 -0
- package/dist/server.js +43 -0
- package/dist/session.js +205 -0
- package/dist/sources.js +218 -0
- package/dist/text.js +80 -0
- package/dist/texts.js +44 -0
- package/dist/tools/audit.js +105 -0
- package/dist/tools/build.js +453 -0
- package/dist/tools/common.js +122 -0
- package/dist/tools/docs.js +196 -0
- package/dist/tools/preview.js +87 -0
- package/dist/tools/project.js +183 -0
- package/dist/tools/setup.js +146 -0
- package/dist/tools/submission.js +369 -0
- package/dist/version.js +3 -0
- package/npm-shrinkwrap.json +402 -0
- package/package.json +50 -4
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Resources: the documentation pages (EN and FR), the manifest reference,
|
|
2
|
+
// the limits, the templates and the example widgets. All come from the
|
|
3
|
+
// package; none from the project or the network.
|
|
4
|
+
import { ResourceNotFoundError, ResourceTemplate, } from "@modelcontextprotocol/server";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { docMarkdown, docPages, limits, manifestReference, projectFile, projects, } from "./content.js";
|
|
7
|
+
function mimeOf(path) {
|
|
8
|
+
if (path.endsWith(".md"))
|
|
9
|
+
return "text/markdown";
|
|
10
|
+
if (path.endsWith(".json"))
|
|
11
|
+
return "application/json";
|
|
12
|
+
if (path.endsWith(".ts") || path.endsWith(".mjs"))
|
|
13
|
+
return "text/x-typescript";
|
|
14
|
+
return "text/plain";
|
|
15
|
+
}
|
|
16
|
+
const single = (value) => Array.isArray(value) ? (value[0] ?? "") : (value ?? "");
|
|
17
|
+
export function registerResources(server) {
|
|
18
|
+
server.registerResource("manifest-reference", "overcrow://schema/manifest", {
|
|
19
|
+
title: "Manifest reference",
|
|
20
|
+
description: "Every field of manifest.json, the permissions, the network rules and the capabilities.",
|
|
21
|
+
mimeType: "text/markdown",
|
|
22
|
+
}, async (uri) => ({
|
|
23
|
+
contents: [{ uri: uri.href, mimeType: "text/markdown", text: manifestReference() }],
|
|
24
|
+
}));
|
|
25
|
+
server.registerResource("limits", "overcrow://limits", {
|
|
26
|
+
title: "Limits",
|
|
27
|
+
description: "Every limit a widget meets: name, value and meaning (from the limits page).",
|
|
28
|
+
mimeType: "application/json",
|
|
29
|
+
}, async (uri) => ({
|
|
30
|
+
contents: [
|
|
31
|
+
{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(limits(), null, 1) },
|
|
32
|
+
],
|
|
33
|
+
}));
|
|
34
|
+
server.registerResource("docs", new ResourceTemplate("overcrow://docs/{locale}/{slug}", {
|
|
35
|
+
list: async () => ({
|
|
36
|
+
resources: docPages().flatMap((page) => ["en", "fr"].map((locale) => ({
|
|
37
|
+
uri: `overcrow://docs/${locale}/${page.slug || "index"}`,
|
|
38
|
+
name: `${locale}/${page.slug || "index"}`,
|
|
39
|
+
title: page.titles[locale] ?? page.slug,
|
|
40
|
+
mimeType: "text/markdown",
|
|
41
|
+
}))),
|
|
42
|
+
}),
|
|
43
|
+
}), {
|
|
44
|
+
title: "Creator documentation",
|
|
45
|
+
description: "A page of the OverCrow creator documentation.",
|
|
46
|
+
mimeType: "text/markdown",
|
|
47
|
+
}, async (uri, variables) => {
|
|
48
|
+
const locale = z.enum(["en", "fr"]).safeParse(single(variables.locale));
|
|
49
|
+
const slug = single(variables.slug);
|
|
50
|
+
const markdown = locale.success
|
|
51
|
+
? docMarkdown(slug === "index" ? "" : slug, locale.data)
|
|
52
|
+
: undefined;
|
|
53
|
+
if (markdown === undefined)
|
|
54
|
+
throw new ResourceNotFoundError(uri.href);
|
|
55
|
+
return { contents: [{ uri: uri.href, mimeType: "text/markdown", text: markdown }] };
|
|
56
|
+
});
|
|
57
|
+
for (const kind of ["examples", "templates"]) {
|
|
58
|
+
server.registerResource(kind, new ResourceTemplate(`overcrow://${kind}/{name}/{+path}`, {
|
|
59
|
+
list: async () => ({
|
|
60
|
+
resources: projects(kind)
|
|
61
|
+
.filter((project) => project.name !== "shared" || kind === "templates")
|
|
62
|
+
.flatMap((project) => project.files.map((file) => ({
|
|
63
|
+
uri: `overcrow://${kind}/${project.name}/${file}`,
|
|
64
|
+
name: `${project.name}/${file}`,
|
|
65
|
+
mimeType: mimeOf(file),
|
|
66
|
+
}))),
|
|
67
|
+
}),
|
|
68
|
+
}), {
|
|
69
|
+
title: kind === "examples" ? "Example widgets" : "Templates",
|
|
70
|
+
description: kind === "examples"
|
|
71
|
+
? "Source files of the reference widgets and of the documentation's examples."
|
|
72
|
+
: "Source files of the templates of a new widget.",
|
|
73
|
+
}, async (uri, variables) => {
|
|
74
|
+
const name = single(variables.name);
|
|
75
|
+
const path = single(variables.path);
|
|
76
|
+
const text = projectFile(kind, name, path);
|
|
77
|
+
if (text === undefined)
|
|
78
|
+
throw new ResourceNotFoundError(uri.href);
|
|
79
|
+
return { contents: [{ uri: uri.href, mimeType: mimeOf(path), text }] };
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Starts the programs the server relies on (the widget CLI, npm) and
|
|
2
|
+
// nothing else: no shell, arguments as an array, a minimal environment,
|
|
3
|
+
// stdin closed, a time limit, bounded output, and the whole process tree
|
|
4
|
+
// stopped on timeout or when the server exits.
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { windowsSystemRoot } from "./env.js";
|
|
8
|
+
export class RunError extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "RunError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const running = new Set();
|
|
15
|
+
/** Stops every process still running (the server is exiting). */
|
|
16
|
+
export function stopAll() {
|
|
17
|
+
for (const child of running)
|
|
18
|
+
stopTree(child);
|
|
19
|
+
}
|
|
20
|
+
function stopTree(child) {
|
|
21
|
+
if (child.pid === undefined || child.exitCode !== null || child.signalCode !== null)
|
|
22
|
+
return;
|
|
23
|
+
if (process.platform === "win32") {
|
|
24
|
+
// taskkill by its full path: the PATH is not trusted for this.
|
|
25
|
+
const killer = spawn(join(windowsSystemRoot(), "System32", "taskkill.exe"), ["/PID", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true, shell: false });
|
|
26
|
+
killer.on("error", () => child.kill("SIGKILL"));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
// The child leads its own process group (detached): stop all of it.
|
|
31
|
+
process.kill(-child.pid, "SIGTERM");
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
child.kill("SIGTERM");
|
|
35
|
+
}
|
|
36
|
+
const pid = child.pid;
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
try {
|
|
39
|
+
process.kill(-pid, "SIGKILL");
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Already gone.
|
|
43
|
+
}
|
|
44
|
+
}, 2000).unref();
|
|
45
|
+
}
|
|
46
|
+
/** Runs `file` with `args` and collects its output. */
|
|
47
|
+
export function runProcess(file, args, options) {
|
|
48
|
+
const maxStdout = options.maxStdoutBytes ?? 8 << 20;
|
|
49
|
+
const maxStderr = options.maxStderrBytes ?? 1 << 20;
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
let child;
|
|
52
|
+
try {
|
|
53
|
+
child = spawn(file, [...args], {
|
|
54
|
+
cwd: options.cwd,
|
|
55
|
+
env: options.env,
|
|
56
|
+
shell: false,
|
|
57
|
+
windowsHide: true,
|
|
58
|
+
detached: process.platform !== "win32",
|
|
59
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
reject(new RunError(`cannot start ${file}: ${error.message}`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
running.add(child);
|
|
67
|
+
const stdout = [];
|
|
68
|
+
const stderr = [];
|
|
69
|
+
let stdoutBytes = 0;
|
|
70
|
+
let stderrBytes = 0;
|
|
71
|
+
let truncated = false;
|
|
72
|
+
let timedOut = false;
|
|
73
|
+
let cancelled = false;
|
|
74
|
+
const collect = (chunks, max, size, chunk) => {
|
|
75
|
+
if (size >= max) {
|
|
76
|
+
truncated = true;
|
|
77
|
+
return size;
|
|
78
|
+
}
|
|
79
|
+
const room = max - size;
|
|
80
|
+
if (chunk.length > room)
|
|
81
|
+
truncated = true;
|
|
82
|
+
chunks.push(chunk.length > room ? chunk.subarray(0, room) : chunk);
|
|
83
|
+
return size + Math.min(chunk.length, room);
|
|
84
|
+
};
|
|
85
|
+
child.stdout?.on("data", (chunk) => {
|
|
86
|
+
stdoutBytes = collect(stdout, maxStdout, stdoutBytes, chunk);
|
|
87
|
+
});
|
|
88
|
+
child.stderr?.on("data", (chunk) => {
|
|
89
|
+
stderrBytes = collect(stderr, maxStderr, stderrBytes, chunk);
|
|
90
|
+
});
|
|
91
|
+
const timer = setTimeout(() => {
|
|
92
|
+
timedOut = true;
|
|
93
|
+
stopTree(child);
|
|
94
|
+
}, options.timeoutMs);
|
|
95
|
+
const onAbort = () => {
|
|
96
|
+
cancelled = true;
|
|
97
|
+
stopTree(child);
|
|
98
|
+
};
|
|
99
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
100
|
+
child.once("error", (error) => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
running.delete(child);
|
|
103
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
104
|
+
reject(new RunError(`cannot start ${file}: ${error.message}`));
|
|
105
|
+
});
|
|
106
|
+
child.once("close", (code) => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
running.delete(child);
|
|
109
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
110
|
+
resolve({
|
|
111
|
+
code,
|
|
112
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
113
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
114
|
+
timedOut,
|
|
115
|
+
cancelled,
|
|
116
|
+
truncated,
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// The MCP server: its identity, its instructions, its tools, resources and
|
|
2
|
+
// prompts. One instance per protocol era (see serveStdio); the session is
|
|
3
|
+
// shared.
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
5
|
+
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
6
|
+
import { registerPrompts } from "./prompts.js";
|
|
7
|
+
import { registerResources } from "./resources.js";
|
|
8
|
+
import { INSTRUCTIONS } from "./texts.js";
|
|
9
|
+
import { registerAuditTools } from "./tools/audit.js";
|
|
10
|
+
import { registerBuildTools } from "./tools/build.js";
|
|
11
|
+
import { registerDocTools } from "./tools/docs.js";
|
|
12
|
+
import { registerProjectTools } from "./tools/project.js";
|
|
13
|
+
import { registerSetupTools } from "./tools/setup.js";
|
|
14
|
+
import { registerSubmissionTool } from "./tools/submission.js";
|
|
15
|
+
import { VERSION } from "./version.js";
|
|
16
|
+
export function createServer(session) {
|
|
17
|
+
const server = new McpServer({ name: "overcrow", title: "PlayerVox OverCrow widgets", version: VERSION }, { instructions: INSTRUCTIONS, capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
18
|
+
const env = { session, server };
|
|
19
|
+
// Registration order is the order of tools/list: the order of the work.
|
|
20
|
+
registerSetupTools(env);
|
|
21
|
+
registerProjectTools(env);
|
|
22
|
+
registerBuildTools(env);
|
|
23
|
+
registerAuditTools(env);
|
|
24
|
+
registerSubmissionTool(env);
|
|
25
|
+
registerDocTools(env);
|
|
26
|
+
registerResources(server);
|
|
27
|
+
registerPrompts(server);
|
|
28
|
+
try {
|
|
29
|
+
// Clients of the 2025 protocol say when their roots change.
|
|
30
|
+
server.server.setNotificationHandler("notifications/roots/list_changed", () => session.rootsChanged());
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// The 2026-07-28 protocol has no such notification.
|
|
34
|
+
}
|
|
35
|
+
return server;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Serves `session` over this process's stdio, in the protocol era the
|
|
39
|
+
* client opens with (one instance per era, see serveStdio).
|
|
40
|
+
*/
|
|
41
|
+
export function serve(session, onerror) {
|
|
42
|
+
return serveStdio(() => createServer(session), { onerror });
|
|
43
|
+
}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// What one server process keeps between requests: the options it started
|
|
2
|
+
// with, the project folders it may use, and the creator tools once checked.
|
|
3
|
+
// `serveStdio` may build a server instance per protocol era; they all share
|
|
4
|
+
// this state.
|
|
5
|
+
import { lstat, readFile, rm } from "node:fs/promises";
|
|
6
|
+
import { CLIENT_CAPABILITIES_META_KEY, inputRequired, inputResponse, } from "@modelcontextprotocol/server";
|
|
7
|
+
import { DownloadError, downloadPinned } from "./bootstrap/download.js";
|
|
8
|
+
import { downloadPath, InstallError, install, loadInstalled, prepareCacheRoot, } from "./bootstrap/install.js";
|
|
9
|
+
import { DOWNLOAD_HOSTS, toolsUrl } from "./bootstrap/pin.js";
|
|
10
|
+
import { ZipError } from "./bootstrap/zip.js";
|
|
11
|
+
import { probeVersion } from "./cli.js";
|
|
12
|
+
import { checkRoot, Confinement, ConfinementError, labelRoots, rootUriToPath } from "./confine.js";
|
|
13
|
+
import { cacheRoot, homeDirectory, httpsProxy, namedLocations, toolsPlatform, } from "./env.js";
|
|
14
|
+
import { Redactor } from "./text.js";
|
|
15
|
+
export class Session {
|
|
16
|
+
options;
|
|
17
|
+
clientRoots;
|
|
18
|
+
launchRoots;
|
|
19
|
+
tools;
|
|
20
|
+
sdkVersion;
|
|
21
|
+
installing;
|
|
22
|
+
/** The last test run of each widget folder, with the fingerprint of its sources then. */
|
|
23
|
+
lastTests = new Map();
|
|
24
|
+
constructor(options) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
}
|
|
27
|
+
get platform() {
|
|
28
|
+
return toolsPlatform();
|
|
29
|
+
}
|
|
30
|
+
get cacheRoot() {
|
|
31
|
+
return this.options.cacheRootOverride ?? cacheRoot();
|
|
32
|
+
}
|
|
33
|
+
/** Forgets the client's roots (they changed). */
|
|
34
|
+
rootsChanged() {
|
|
35
|
+
this.clientRoots = undefined;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The folders the server may use for this request: the --root folders,
|
|
39
|
+
* else the client's roots (asked for once with an input request), else
|
|
40
|
+
* the working directory. Every folder is checked (no system folder, no
|
|
41
|
+
* disk root, not the whole home folder).
|
|
42
|
+
*/
|
|
43
|
+
async roots(ctx, server) {
|
|
44
|
+
try {
|
|
45
|
+
if (this.options.launchRoots.length > 0) {
|
|
46
|
+
this.launchRoots ??= await Promise.all(this.options.launchRoots.map(checkRoot));
|
|
47
|
+
return { confinement: new Confinement(labelRoots(this.launchRoots)) };
|
|
48
|
+
}
|
|
49
|
+
if (this.clientRoots === undefined) {
|
|
50
|
+
const answered = inputResponse(ctx.mcpReq.inputResponses, "roots");
|
|
51
|
+
if (answered.kind === "roots") {
|
|
52
|
+
this.clientRoots = answered.roots
|
|
53
|
+
.map((root) => rootUriToPath(root.uri))
|
|
54
|
+
.filter((path) => path !== undefined)
|
|
55
|
+
.slice(0, 16);
|
|
56
|
+
}
|
|
57
|
+
else if (clientSupportsRoots(ctx, server)) {
|
|
58
|
+
return { input: inputRequired({ inputRequests: { roots: inputRequired.listRoots() } }) };
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.clientRoots = [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const candidates = this.clientRoots.length > 0 ? this.clientRoots : [this.options.cwd];
|
|
65
|
+
const checked = [];
|
|
66
|
+
const refusals = [];
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
try {
|
|
69
|
+
checked.push(await checkRoot(candidate));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
refusals.push(error.message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (checked.length === 0)
|
|
76
|
+
return { error: refusals[0] ?? "No project folder is open." };
|
|
77
|
+
return { confinement: new Confinement(labelRoots([...new Set(checked)])) };
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (error instanceof ConfinementError)
|
|
81
|
+
return { error: error.message };
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Replaces the user's paths in text results. */
|
|
86
|
+
redactor(confinement) {
|
|
87
|
+
return new Redactor([
|
|
88
|
+
...(confinement?.roots ?? []).map((root) => ({
|
|
89
|
+
path: root.path,
|
|
90
|
+
label: root.label === "." ? "<project>" : `<${root.label}>`,
|
|
91
|
+
})),
|
|
92
|
+
...namedLocations(),
|
|
93
|
+
], homeDirectory());
|
|
94
|
+
}
|
|
95
|
+
/** The installed tools, checked against the pin (once per process). */
|
|
96
|
+
async installedTools() {
|
|
97
|
+
if (this.tools)
|
|
98
|
+
return this.tools;
|
|
99
|
+
const platform = this.platform;
|
|
100
|
+
const root = this.cacheRoot;
|
|
101
|
+
const release = this.options.pin.release;
|
|
102
|
+
if (!("platform" in platform) || !root || !release)
|
|
103
|
+
return undefined;
|
|
104
|
+
const pinned = this.options.pin.tools[platform.platform];
|
|
105
|
+
if (!pinned)
|
|
106
|
+
return undefined;
|
|
107
|
+
const found = await loadInstalled(pinned, release, platform.platform, root);
|
|
108
|
+
if (found)
|
|
109
|
+
await this.adopt(found);
|
|
110
|
+
return this.tools;
|
|
111
|
+
}
|
|
112
|
+
/** The SDK version the installed CLI embeds (from `--version --format json`). */
|
|
113
|
+
get cliSdkVersion() {
|
|
114
|
+
return this.sdkVersion;
|
|
115
|
+
}
|
|
116
|
+
async adopt(tools) {
|
|
117
|
+
const probe = this.options.probe ?? probeVersion;
|
|
118
|
+
const report = (await probe(tools.cli, tools.directory));
|
|
119
|
+
this.sdkVersion = typeof report?.sdk === "string" ? report.sdk : undefined;
|
|
120
|
+
this.tools = tools;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Installs the pinned creator tools: from `zipPath` (a local file the
|
|
124
|
+
* caller has confined) or --tools-zip, else by downloading the pinned ZIP.
|
|
125
|
+
*/
|
|
126
|
+
async setup(zipPath, onProgress, signal) {
|
|
127
|
+
const existing = await this.installedTools();
|
|
128
|
+
if (existing)
|
|
129
|
+
return { tools: existing, source: "cache" };
|
|
130
|
+
const platform = this.platform;
|
|
131
|
+
if (!("platform" in platform))
|
|
132
|
+
throw new SetupError(platform.unsupported);
|
|
133
|
+
const release = this.options.pin.release;
|
|
134
|
+
const pinned = this.options.pin.tools[platform.platform];
|
|
135
|
+
if (!release || !pinned) {
|
|
136
|
+
throw new SetupError("This version of @overcrow/mcp is not tied to a published OverCrow release yet: update it (npx -y @overcrow/mcp@latest).");
|
|
137
|
+
}
|
|
138
|
+
const root = this.cacheRoot;
|
|
139
|
+
if (!root)
|
|
140
|
+
throw new SetupError("No cache folder: set HOME (or LOCALAPPDATA on Windows).");
|
|
141
|
+
const local = zipPath ?? this.options.toolsZip;
|
|
142
|
+
this.installing ??= (async () => {
|
|
143
|
+
await prepareCacheRoot(root);
|
|
144
|
+
let archive;
|
|
145
|
+
if (local) {
|
|
146
|
+
const info = await lstat(local).catch(() => undefined);
|
|
147
|
+
if (!info?.isFile())
|
|
148
|
+
throw new SetupError("The creator tools ZIP was not found.");
|
|
149
|
+
if (info.size !== pinned.size)
|
|
150
|
+
throw new SetupError(`This ZIP is not ${pinned.name} of OverCrow ${release} (another size).`);
|
|
151
|
+
archive = await readFile(local);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
const temporary = downloadPath(root);
|
|
155
|
+
try {
|
|
156
|
+
const proxy = httpsProxy("github.com");
|
|
157
|
+
await downloadPinned(toolsUrl(release, pinned.name, this.options.releaseBase), temporary, {
|
|
158
|
+
allowedHosts: this.options.releaseBase
|
|
159
|
+
? [new URL(this.options.releaseBase).hostname, ...DOWNLOAD_HOSTS]
|
|
160
|
+
: DOWNLOAD_HOSTS,
|
|
161
|
+
expectedSize: pinned.size,
|
|
162
|
+
proxy,
|
|
163
|
+
ca: this.options.ca,
|
|
164
|
+
port: this.options.releasePort,
|
|
165
|
+
...(onProgress ? { onProgress } : {}),
|
|
166
|
+
signal,
|
|
167
|
+
});
|
|
168
|
+
archive = await readFile(temporary);
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
await rm(temporary, { force: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return install(archive, pinned, release, platform.platform, root, this.options.probe ?? probeVersion);
|
|
175
|
+
})();
|
|
176
|
+
try {
|
|
177
|
+
const tools = await this.installing;
|
|
178
|
+
await this.adopt(tools);
|
|
179
|
+
return { tools, source: local ? "local" : "download" };
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
if (error instanceof DownloadError)
|
|
183
|
+
throw new SetupError(`Cannot download the creator tools: ${error.reason}. Nothing was kept. Check the connection (or HTTPS_PROXY), or pass a local copy of ${pinned.name}.`);
|
|
184
|
+
if (error instanceof ZipError || error instanceof InstallError)
|
|
185
|
+
throw new SetupError(`The creator tools were refused: ${error.message}. Nothing was kept.`);
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
this.installing = undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
export class SetupError extends Error {
|
|
194
|
+
constructor(message) {
|
|
195
|
+
super(message);
|
|
196
|
+
this.name = "SetupError";
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function clientSupportsRoots(ctx, server) {
|
|
200
|
+
const envelope = ctx.mcpReq.envelope;
|
|
201
|
+
const modern = envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
202
|
+
if (modern)
|
|
203
|
+
return modern.roots !== undefined;
|
|
204
|
+
return server.server.getClientCapabilities()?.roots !== undefined;
|
|
205
|
+
}
|
package/dist/sources.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// The ZIP of a widget's sources, the file a creator sends to the OverCrow
|
|
2
|
+
// creator space (whose server rebuilds the package from it). It holds the
|
|
3
|
+
// widget folder minus what is never sent: hidden entries (.git, .env,
|
|
4
|
+
// .npmrc…), node_modules, dist, tests/output, links (never followed), built
|
|
5
|
+
// packages, key stores and system files. Each left-out entry is listed with
|
|
6
|
+
// its reason. The archive is deterministic (sorted names, fixed date,
|
|
7
|
+
// mode 644) and written atomically: a temporary file, then a rename.
|
|
8
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
9
|
+
import { constants } from "node:fs";
|
|
10
|
+
import { lstat, mkdir, open, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { crc32, deflateRawSync } from "node:zlib";
|
|
13
|
+
export const MAX_SOURCE_FILES = 2000;
|
|
14
|
+
export const MAX_SOURCE_DEPTH = 8;
|
|
15
|
+
/** Sum of the files read. */
|
|
16
|
+
export const MAX_SOURCE_BYTES = 64 * 1024 * 1024;
|
|
17
|
+
/** The creator space takes an archive of 32 MB at most. */
|
|
18
|
+
export const MAX_ZIP_BYTES = 32_000_000;
|
|
19
|
+
const SKIPPED_FOLDERS = {
|
|
20
|
+
node_modules: "installed packages: npm brings them back",
|
|
21
|
+
dist: "build output: the creator space builds its own package",
|
|
22
|
+
};
|
|
23
|
+
const KEY_FILE = /\.(pem|key|p12|pfx|pk8|jks|keystore|kdbx|gpg|asc)$|^id_(rsa|dsa|ecdsa|ed25519)(\.pub)?$/i;
|
|
24
|
+
const SYSTEM_FILE = /^(thumbs\.db|desktop\.ini|ehthumbs\.db)$/i;
|
|
25
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: the point is to refuse them
|
|
26
|
+
const NOT_PORTABLE = /[\u0000-\u001f\u007f<>:"\\|?*]|[ .]$/;
|
|
27
|
+
const WINDOWS_DEVICE = /^(con|prn|aux|nul|com[0-9¹²³]|lpt[0-9¹²³]|conin\$|conout\$)(\..*)?$/i;
|
|
28
|
+
/** Why a file of the widget folder is left out, or undefined to keep it. */
|
|
29
|
+
function fileReason(name) {
|
|
30
|
+
if (/\.ocpkg$/i.test(name))
|
|
31
|
+
return "a built package: the creator space builds its own";
|
|
32
|
+
if (KEY_FILE.test(name))
|
|
33
|
+
return "a key or a key store: never sent";
|
|
34
|
+
if (SYSTEM_FILE.test(name))
|
|
35
|
+
return "a system file";
|
|
36
|
+
if (NOT_PORTABLE.test(name) || WINDOWS_DEVICE.test(name))
|
|
37
|
+
return 'a name that Windows cannot use (control characters, < > : " \\ | ? *, or a final dot or space): rename it to send it';
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/** The files of the widget folder `directory` (already confined) that the ZIP holds. */
|
|
41
|
+
export async function selectSources(directory) {
|
|
42
|
+
const included = [];
|
|
43
|
+
const excluded = [];
|
|
44
|
+
let total = 0;
|
|
45
|
+
let problem = null;
|
|
46
|
+
const walk = async (relative, depth) => {
|
|
47
|
+
const entries = await readdir(relative ? join(directory, relative) : directory, {
|
|
48
|
+
withFileTypes: true,
|
|
49
|
+
});
|
|
50
|
+
entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
if (problem)
|
|
53
|
+
return;
|
|
54
|
+
const path = relative ? `${relative}/${entry.name}` : entry.name;
|
|
55
|
+
if (entry.isSymbolicLink()) {
|
|
56
|
+
excluded.push({ path, reason: "a link: links are never followed" });
|
|
57
|
+
}
|
|
58
|
+
else if (entry.isDirectory()) {
|
|
59
|
+
const skipped = SKIPPED_FOLDERS[entry.name];
|
|
60
|
+
if (entry.name.startsWith(".")) {
|
|
61
|
+
excluded.push({ path: `${path}/`, reason: "a hidden folder" });
|
|
62
|
+
}
|
|
63
|
+
else if (skipped) {
|
|
64
|
+
excluded.push({ path: `${path}/`, reason: skipped });
|
|
65
|
+
}
|
|
66
|
+
else if (path === "tests/output") {
|
|
67
|
+
excluded.push({ path: `${path}/`, reason: "test output: test writes it again" });
|
|
68
|
+
}
|
|
69
|
+
else if (NOT_PORTABLE.test(entry.name) || WINDOWS_DEVICE.test(entry.name)) {
|
|
70
|
+
excluded.push({ path: `${path}/`, reason: fileReason(entry.name) ?? "" });
|
|
71
|
+
}
|
|
72
|
+
else if (depth >= MAX_SOURCE_DEPTH) {
|
|
73
|
+
problem = `${path}/ is more than ${MAX_SOURCE_DEPTH} folders deep: flatten the widget folder.`;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await walk(path, depth + 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (!entry.isFile()) {
|
|
80
|
+
excluded.push({ path, reason: "not a regular file" });
|
|
81
|
+
}
|
|
82
|
+
else if (entry.name.startsWith(".")) {
|
|
83
|
+
excluded.push({
|
|
84
|
+
path,
|
|
85
|
+
reason: "a hidden file (such as .env or .npmrc, which often hold secrets)",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
const reason = fileReason(entry.name);
|
|
90
|
+
if (reason) {
|
|
91
|
+
excluded.push({ path, reason });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const info = await lstat(join(directory, path));
|
|
95
|
+
total += info.size;
|
|
96
|
+
included.push({ path, bytes: info.size });
|
|
97
|
+
if (included.length > MAX_SOURCE_FILES) {
|
|
98
|
+
problem = `The widget folder holds more than ${MAX_SOURCE_FILES} files to send: remove the ones the widget does not need.`;
|
|
99
|
+
}
|
|
100
|
+
else if (total > MAX_SOURCE_BYTES) {
|
|
101
|
+
problem = `The files to send weigh more than ${MAX_SOURCE_BYTES / 1048576} MiB: remove the large files the widget does not need.`;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
await walk("", 0);
|
|
107
|
+
return { included, excluded, problem };
|
|
108
|
+
}
|
|
109
|
+
const DOS_DATE_1980_01_01 = (1 << 5) | 1;
|
|
110
|
+
const UTF8_NAMES = 1 << 11;
|
|
111
|
+
const VERSION_NEEDED = 20;
|
|
112
|
+
const MADE_BY_UNIX = (3 << 8) | VERSION_NEEDED;
|
|
113
|
+
const REGULAR_FILE_644 = (0o100644 << 16) >>> 0;
|
|
114
|
+
/** A deterministic ZIP of `entries` (already in their final order). */
|
|
115
|
+
export function buildZip(entries) {
|
|
116
|
+
const locals = [];
|
|
117
|
+
const centrals = [];
|
|
118
|
+
let offset = 0;
|
|
119
|
+
for (const { name, data } of entries) {
|
|
120
|
+
const nameBytes = Buffer.from(name, "utf8");
|
|
121
|
+
const deflated = deflateRawSync(data, { level: 9 });
|
|
122
|
+
const stored = deflated.length >= data.length;
|
|
123
|
+
const body = stored ? data : deflated;
|
|
124
|
+
const crc = crc32(data);
|
|
125
|
+
const local = Buffer.alloc(30);
|
|
126
|
+
local.writeUInt32LE(0x04034b50, 0);
|
|
127
|
+
local.writeUInt16LE(VERSION_NEEDED, 4);
|
|
128
|
+
local.writeUInt16LE(UTF8_NAMES, 6);
|
|
129
|
+
local.writeUInt16LE(stored ? 0 : 8, 8);
|
|
130
|
+
local.writeUInt16LE(0, 10);
|
|
131
|
+
local.writeUInt16LE(DOS_DATE_1980_01_01, 12);
|
|
132
|
+
local.writeUInt32LE(crc, 14);
|
|
133
|
+
local.writeUInt32LE(body.length, 18);
|
|
134
|
+
local.writeUInt32LE(data.length, 22);
|
|
135
|
+
local.writeUInt16LE(nameBytes.length, 26);
|
|
136
|
+
local.writeUInt16LE(0, 28);
|
|
137
|
+
const central = Buffer.alloc(46);
|
|
138
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
139
|
+
central.writeUInt16LE(MADE_BY_UNIX, 4);
|
|
140
|
+
central.writeUInt16LE(VERSION_NEEDED, 6);
|
|
141
|
+
central.writeUInt16LE(UTF8_NAMES, 8);
|
|
142
|
+
central.writeUInt16LE(stored ? 0 : 8, 10);
|
|
143
|
+
central.writeUInt16LE(0, 12);
|
|
144
|
+
central.writeUInt16LE(DOS_DATE_1980_01_01, 14);
|
|
145
|
+
central.writeUInt32LE(crc, 16);
|
|
146
|
+
central.writeUInt32LE(body.length, 20);
|
|
147
|
+
central.writeUInt32LE(data.length, 24);
|
|
148
|
+
central.writeUInt16LE(nameBytes.length, 28);
|
|
149
|
+
central.writeUInt32LE(REGULAR_FILE_644, 38);
|
|
150
|
+
central.writeUInt32LE(offset, 42);
|
|
151
|
+
locals.push(local, nameBytes, body);
|
|
152
|
+
centrals.push(central, nameBytes);
|
|
153
|
+
offset += local.length + nameBytes.length + body.length;
|
|
154
|
+
}
|
|
155
|
+
const directory = Buffer.concat(centrals);
|
|
156
|
+
const end = Buffer.alloc(22);
|
|
157
|
+
end.writeUInt32LE(0x06054b50, 0);
|
|
158
|
+
end.writeUInt16LE(entries.length, 8);
|
|
159
|
+
end.writeUInt16LE(entries.length, 10);
|
|
160
|
+
end.writeUInt32LE(directory.length, 12);
|
|
161
|
+
end.writeUInt32LE(offset, 16);
|
|
162
|
+
return Buffer.concat([...locals, directory, end]);
|
|
163
|
+
}
|
|
164
|
+
/** Reads a selected file without following a link, if it is still as selected. */
|
|
165
|
+
async function readSelected(directory, file) {
|
|
166
|
+
// O_NOFOLLOW does not exist on Windows, where links were left out when selecting.
|
|
167
|
+
const flags = constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0);
|
|
168
|
+
const handle = await open(join(directory, file.path), flags).catch(() => undefined);
|
|
169
|
+
if (!handle)
|
|
170
|
+
return undefined;
|
|
171
|
+
try {
|
|
172
|
+
const info = await handle.stat();
|
|
173
|
+
if (!info.isFile() || info.size !== file.bytes)
|
|
174
|
+
return undefined;
|
|
175
|
+
const data = await handle.readFile();
|
|
176
|
+
return data.length === file.bytes ? data : undefined;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
await handle.close();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Writes the ZIP of `files` to `folder`/`name` (`folder` already confined),
|
|
184
|
+
* or returns why it is not written.
|
|
185
|
+
*/
|
|
186
|
+
export async function writeSourcesZip(directory, files, folder, name) {
|
|
187
|
+
const entries = [];
|
|
188
|
+
for (const file of files) {
|
|
189
|
+
const data = await readSelected(directory, file);
|
|
190
|
+
if (!data)
|
|
191
|
+
return {
|
|
192
|
+
problem: `${file.path} changed while the ZIP was made: run prepare_submission again.`,
|
|
193
|
+
};
|
|
194
|
+
entries.push({ name: file.path, data });
|
|
195
|
+
}
|
|
196
|
+
const zip = buildZip(entries);
|
|
197
|
+
if (zip.length > MAX_ZIP_BYTES) {
|
|
198
|
+
return {
|
|
199
|
+
problem: `The ZIP would weigh ${(zip.length / 1e6).toFixed(1)} MB, and the creator space takes 32 MB at most: shrink or remove large files.`,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
await mkdir(folder, { recursive: true });
|
|
203
|
+
const destination = join(folder, name);
|
|
204
|
+
const temporary = join(folder, `.${name}.${randomBytes(6).toString("hex")}.tmp`);
|
|
205
|
+
try {
|
|
206
|
+
await writeFile(temporary, zip, { flag: "wx", mode: 0o644 });
|
|
207
|
+
await rename(temporary, destination);
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
await rm(temporary, { force: true }).catch(() => undefined);
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
path: destination,
|
|
215
|
+
bytes: zip.length,
|
|
216
|
+
sha256: createHash("sha256").update(zip).digest("hex"),
|
|
217
|
+
};
|
|
218
|
+
}
|