mcpmake 0.1.1 → 0.2.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/README.md +88 -635
- package/dist/commands/bundle.d.ts +1 -0
- package/dist/commands/bundle.d.ts.map +1 -0
- package/dist/commands/bundle.js +5 -4
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/ci.d.ts +1 -0
- package/dist/commands/ci.d.ts.map +1 -0
- package/dist/commands/ci.js +3 -2
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +4 -3
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +5 -4
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/from/describe.d.ts +1 -0
- package/dist/commands/from/describe.d.ts.map +1 -0
- package/dist/commands/from/describe.js +11 -10
- package/dist/commands/from/describe.js.map +1 -0
- package/dist/commands/from/har.d.ts +1 -0
- package/dist/commands/from/har.d.ts.map +1 -0
- package/dist/commands/from/har.js +14 -13
- package/dist/commands/from/har.js.map +1 -0
- package/dist/commands/from/openapi.d.ts +1 -0
- package/dist/commands/from/openapi.d.ts.map +1 -0
- package/dist/commands/from/openapi.js +17 -16
- package/dist/commands/from/openapi.js.map +1 -0
- package/dist/commands/from/postman.d.ts +1 -0
- package/dist/commands/from/postman.d.ts.map +1 -0
- package/dist/commands/from/postman.js +13 -12
- package/dist/commands/from/postman.js.map +1 -0
- package/dist/commands/from/stainless.d.ts +110 -0
- package/dist/commands/from/stainless.d.ts.map +1 -0
- package/dist/commands/from/stainless.js +272 -0
- package/dist/commands/from/stainless.js.map +1 -0
- package/dist/commands/from/target-support.d.ts +1 -0
- package/dist/commands/from/target-support.d.ts.map +1 -0
- package/dist/commands/from/target-support.js +2 -1
- package/dist/commands/from/target-support.js.map +1 -0
- package/dist/commands/from/url.d.ts +1 -0
- package/dist/commands/from/url.d.ts.map +1 -0
- package/dist/commands/from/url.js +14 -13
- package/dist/commands/from/url.js.map +1 -0
- package/dist/commands/from/website.d.ts +1 -0
- package/dist/commands/from/website.d.ts.map +1 -0
- package/dist/commands/from/website.js +17 -16
- package/dist/commands/from/website.js.map +1 -0
- package/dist/commands/lint.d.ts +1 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +6 -5
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/merge.d.ts +1 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +3 -2
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/publish.d.ts +1 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +4 -3
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/rescan.d.ts +1 -0
- package/dist/commands/rescan.d.ts.map +1 -0
- package/dist/commands/rescan.js +12 -11
- package/dist/commands/rescan.js.map +1 -0
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +10 -9
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.d.ts +1 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +7 -6
- package/dist/commands/verify.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -2
- package/dist/index.js.map +1 -0
- package/dist/registry/official-registry.d.ts +1 -0
- package/dist/registry/official-registry.d.ts.map +1 -0
- package/dist/registry/official-registry.js +1 -0
- package/dist/registry/official-registry.js.map +1 -0
- package/package.json +20 -46
- package/dist/analyzer/auth-detector.d.ts +0 -12
- package/dist/analyzer/auth-detector.js +0 -142
- package/dist/analyzer/dom-parser.d.ts +0 -10
- package/dist/analyzer/dom-parser.js +0 -259
- package/dist/analyzer/goal-crawler.d.ts +0 -25
- package/dist/analyzer/goal-crawler.js +0 -177
- package/dist/analyzer/hybrid-detector.d.ts +0 -28
- package/dist/analyzer/hybrid-detector.js +0 -96
- package/dist/analyzer/index.d.ts +0 -12
- package/dist/analyzer/index.js +0 -8
- package/dist/analyzer/screenshot-capture.d.ts +0 -29
- package/dist/analyzer/screenshot-capture.js +0 -42
- package/dist/analyzer/selector-builder.d.ts +0 -19
- package/dist/analyzer/selector-builder.js +0 -199
- package/dist/analyzer/semantic-analyzer.d.ts +0 -13
- package/dist/analyzer/semantic-analyzer.js +0 -145
- package/dist/analyzer/site-crawler.d.ts +0 -38
- package/dist/analyzer/site-crawler.js +0 -235
- package/dist/cloud/billing/billing-engine.d.ts +0 -44
- package/dist/cloud/billing/billing-engine.js +0 -81
- package/dist/cloud/billing/credit-store.d.ts +0 -64
- package/dist/cloud/billing/credit-store.js +0 -168
- package/dist/cloud/billing/index.d.ts +0 -4
- package/dist/cloud/billing/index.js +0 -2
- package/dist/cloud/billing/usage-store.d.ts +0 -42
- package/dist/cloud/billing/usage-store.js +0 -85
- package/dist/cloud/billing/usage-tracker.d.ts +0 -38
- package/dist/cloud/billing/usage-tracker.js +0 -95
- package/dist/cloud/build-pipeline.d.ts +0 -39
- package/dist/cloud/build-pipeline.js +0 -310
- package/dist/cloud/build-queue.d.ts +0 -30
- package/dist/cloud/build-queue.js +0 -70
- package/dist/cloud/caddy-manager.d.ts +0 -18
- package/dist/cloud/caddy-manager.js +0 -97
- package/dist/cloud/container-backend.d.ts +0 -62
- package/dist/cloud/container-backend.js +0 -59
- package/dist/cloud/container-manager.d.ts +0 -64
- package/dist/cloud/container-manager.js +0 -301
- package/dist/cloud/crypto.d.ts +0 -27
- package/dist/cloud/crypto.js +0 -63
- package/dist/cloud/db/index.d.ts +0 -27
- package/dist/cloud/db/index.js +0 -53
- package/dist/cloud/db/migrations.d.ts +0 -12
- package/dist/cloud/db/migrations.js +0 -329
- package/dist/cloud/db/pg-store.d.ts +0 -45
- package/dist/cloud/db/pg-store.js +0 -336
- package/dist/cloud/failure-tracker.d.ts +0 -51
- package/dist/cloud/failure-tracker.js +0 -102
- package/dist/cloud/idle-monitor.d.ts +0 -30
- package/dist/cloud/idle-monitor.js +0 -70
- package/dist/cloud/mailer.d.ts +0 -21
- package/dist/cloud/mailer.js +0 -193
- package/dist/cloud/mcp-proxy.d.ts +0 -58
- package/dist/cloud/mcp-proxy.js +0 -203
- package/dist/cloud/metric-samples.d.ts +0 -43
- package/dist/cloud/metric-samples.js +0 -85
- package/dist/cloud/metrics.d.ts +0 -26
- package/dist/cloud/metrics.js +0 -59
- package/dist/cloud/multipart.d.ts +0 -26
- package/dist/cloud/multipart.js +0 -132
- package/dist/cloud/observability.d.ts +0 -27
- package/dist/cloud/observability.js +0 -98
- package/dist/cloud/rate-limiter.d.ts +0 -31
- package/dist/cloud/rate-limiter.js +0 -58
- package/dist/cloud/request-security.d.ts +0 -5
- package/dist/cloud/request-security.js +0 -74
- package/dist/cloud/resource-monitor.d.ts +0 -69
- package/dist/cloud/resource-monitor.js +0 -130
- package/dist/cloud/secret-store.d.ts +0 -38
- package/dist/cloud/secret-store.js +0 -103
- package/dist/cloud/security.d.ts +0 -26
- package/dist/cloud/security.js +0 -142
- package/dist/cloud/server.d.ts +0 -21
- package/dist/cloud/server.js +0 -1079
- package/dist/cloud/shared-state.d.ts +0 -72
- package/dist/cloud/shared-state.js +0 -159
- package/dist/cloud/ssrf.d.ts +0 -43
- package/dist/cloud/ssrf.js +0 -150
- package/dist/cloud/store.d.ts +0 -41
- package/dist/cloud/store.js +0 -75
- package/dist/cloud/stripe.d.ts +0 -78
- package/dist/cloud/stripe.js +0 -317
- package/dist/cloud/telemetry-store.d.ts +0 -53
- package/dist/cloud/telemetry-store.js +0 -108
- package/dist/cloud/web/auth.d.ts +0 -225
- package/dist/cloud/web/auth.js +0 -555
- package/dist/cloud/web/charts.d.ts +0 -70
- package/dist/cloud/web/charts.js +0 -178
- package/dist/cloud/web/csrf.d.ts +0 -14
- package/dist/cloud/web/csrf.js +0 -22
- package/dist/cloud/web/docs.d.ts +0 -40
- package/dist/cloud/web/docs.js +0 -174
- package/dist/cloud/web/router.d.ts +0 -25
- package/dist/cloud/web/router.js +0 -1921
- package/dist/cloud/web/static/alpine.min.js +0 -5
- package/dist/cloud/web/static/favicon.svg +0 -4
- package/dist/cloud/web/static/htmx-sse.js +0 -290
- package/dist/cloud/web/static/htmx.min.js +0 -1
- package/dist/cloud/web/static/style.css +0 -2683
- package/dist/cloud/web/static-server.d.ts +0 -13
- package/dist/cloud/web/static-server.js +0 -73
- package/dist/cloud/web/template-engine.d.ts +0 -27
- package/dist/cloud/web/template-engine.js +0 -146
- package/dist/cloud/web/templates/layouts/admin.hbs +0 -57
- package/dist/cloud/web/templates/layouts/auth.hbs +0 -138
- package/dist/cloud/web/templates/layouts/base.hbs +0 -16
- package/dist/cloud/web/templates/layouts/dashboard.hbs +0 -39
- package/dist/cloud/web/templates/layouts/landing.hbs +0 -82
- package/dist/cloud/web/templates/pages/admin/overview.hbs +0 -123
- package/dist/cloud/web/templates/pages/admin/servers.hbs +0 -129
- package/dist/cloud/web/templates/pages/admin/telemetry.hbs +0 -39
- package/dist/cloud/web/templates/pages/admin/user-edit.hbs +0 -91
- package/dist/cloud/web/templates/pages/admin/users.hbs +0 -179
- package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +0 -25
- package/dist/cloud/web/templates/pages/auth/login.hbs +0 -33
- package/dist/cloud/web/templates/pages/auth/register.hbs +0 -32
- package/dist/cloud/web/templates/pages/auth/reset-password.hbs +0 -34
- package/dist/cloud/web/templates/pages/dashboard/billing.hbs +0 -140
- package/dist/cloud/web/templates/pages/dashboard/create.hbs +0 -173
- package/dist/cloud/web/templates/pages/dashboard/index.hbs +0 -8
- package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +0 -280
- package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +0 -35
- package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +0 -63
- package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +0 -21
- package/dist/cloud/web/templates/pages/dashboard/servers.hbs +0 -44
- package/dist/cloud/web/templates/pages/docs/show.hbs +0 -16
- package/dist/cloud/web/templates/pages/errors/404.hbs +0 -9
- package/dist/cloud/web/templates/pages/errors/500.hbs +0 -8
- package/dist/cloud/web/templates/pages/landing/index.hbs +0 -223
- package/dist/cloud/web/templates/pages/legal/privacy.hbs +0 -71
- package/dist/cloud/web/templates/pages/legal/terms.hbs +0 -73
- package/dist/cloud/web/templates/partials/admin-stats.hbs +0 -52
- package/dist/cloud/web/templates/partials/flash-message.hbs +0 -6
- package/dist/cloud/web/templates/partials/pricing-table.hbs +0 -103
- package/dist/cloud/web/templates/partials/server-card.hbs +0 -19
- package/dist/cloud/web/templates/partials/status-badge.hbs +0 -1
- package/dist/config/configurable-command.d.ts +0 -13
- package/dist/config/configurable-command.js +0 -70
- package/dist/config/mcpmake-config.d.ts +0 -68
- package/dist/config/mcpmake-config.js +0 -207
- package/dist/docs/cli.md +0 -400
- package/dist/docs/mcp-2026-07-28-migration.md +0 -78
- package/dist/docs/migrate-from-stainless.md +0 -94
- package/dist/docs/quickstart.md +0 -166
- package/dist/docs/show-hn.md +0 -26
- package/dist/docs/website-servers.md +0 -169
- package/dist/emitter/code-writer.d.ts +0 -8
- package/dist/emitter/code-writer.js +0 -25
- package/dist/emitter/index.d.ts +0 -32
- package/dist/emitter/index.js +0 -280
- package/dist/emitter/mcpb-bundler.d.ts +0 -31
- package/dist/emitter/mcpb-bundler.js +0 -172
- package/dist/emitter/project-scaffolder.d.ts +0 -4
- package/dist/emitter/project-scaffolder.js +0 -89
- package/dist/emitter/python-template-loader.d.ts +0 -4
- package/dist/emitter/python-template-loader.js +0 -30
- package/dist/emitter/python-templates/dockerfile.hbs +0 -14
- package/dist/emitter/python-templates/env.example.hbs +0 -6
- package/dist/emitter/python-templates/requirements.txt.hbs +0 -4
- package/dist/emitter/python-templates/server.py.hbs +0 -77
- package/dist/emitter/site-scaffolder.d.ts +0 -13
- package/dist/emitter/site-scaffolder.js +0 -70
- package/dist/emitter/site-template-loader.d.ts +0 -5
- package/dist/emitter/site-template-loader.js +0 -47
- package/dist/emitter/site-templates/browser-manager.ts.hbs +0 -233
- package/dist/emitter/site-templates/config.ts.hbs +0 -28
- package/dist/emitter/site-templates/dockerfile.hbs +0 -31
- package/dist/emitter/site-templates/env.example.hbs +0 -19
- package/dist/emitter/site-templates/package.json.hbs +0 -26
- package/dist/emitter/site-templates/server-main-http.ts.hbs +0 -108
- package/dist/emitter/site-templates/server-main.ts.hbs +0 -23
- package/dist/emitter/site-templates/tool-handler-action.ts.hbs +0 -86
- package/dist/emitter/site-templates/tool-handler-form.ts.hbs +0 -116
- package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +0 -146
- package/dist/emitter/site-templates/tool-index.ts.hbs +0 -11
- package/dist/emitter/template-loader.d.ts +0 -1
- package/dist/emitter/template-loader.js +0 -27
- package/dist/emitter/templates/auth-provider.ts.hbs +0 -57
- package/dist/emitter/templates/config.ts.hbs +0 -63
- package/dist/emitter/templates/discovery.ts.hbs +0 -301
- package/dist/emitter/templates/dockerfile.hbs +0 -34
- package/dist/emitter/templates/env.example.hbs +0 -28
- package/dist/emitter/templates/gitignore.hbs +0 -5
- package/dist/emitter/templates/http-executor.ts.hbs +0 -117
- package/dist/emitter/templates/oauth.ts.hbs +0 -188
- package/dist/emitter/templates/package.json.hbs +0 -25
- package/dist/emitter/templates/prompts.ts.hbs +0 -22
- package/dist/emitter/templates/readme.md.hbs +0 -123
- package/dist/emitter/templates/resources.ts.hbs +0 -63
- package/dist/emitter/templates/server-main-http.ts.hbs +0 -407
- package/dist/emitter/templates/server-main.ts.hbs +0 -40
- package/dist/emitter/templates/task-handlers.ts.hbs +0 -189
- package/dist/emitter/templates/task-manager.ts.hbs +0 -139
- package/dist/emitter/templates/task-sse.ts.hbs +0 -105
- package/dist/emitter/templates/tool-handler.ts.hbs +0 -124
- package/dist/emitter/templates/tool-index.ts.hbs +0 -11
- package/dist/emitter/templates/tool-test.ts.hbs +0 -57
- package/dist/emitter/templates/trace.ts.hbs +0 -79
- package/dist/emitter/templates/tsconfig.json.hbs +0 -16
- package/dist/emitter/templates/types.ts.hbs +0 -5
- package/dist/emitter/worker-template-loader.d.ts +0 -5
- package/dist/emitter/worker-template-loader.js +0 -33
- package/dist/emitter/worker-templates/config.ts.hbs +0 -54
- package/dist/emitter/worker-templates/dev-vars.example.hbs +0 -10
- package/dist/emitter/worker-templates/gitignore.hbs +0 -6
- package/dist/emitter/worker-templates/package.json.hbs +0 -24
- package/dist/emitter/worker-templates/readme.md.hbs +0 -53
- package/dist/emitter/worker-templates/server.test.ts.hbs +0 -20
- package/dist/emitter/worker-templates/tool-handler.ts.hbs +0 -85
- package/dist/emitter/worker-templates/tool-index.ts.hbs +0 -28
- package/dist/emitter/worker-templates/tsconfig.json.hbs +0 -17
- package/dist/emitter/worker-templates/worker.ts.hbs +0 -242
- package/dist/emitter/worker-templates/wrangler.toml.hbs +0 -19
- package/dist/generator/spec-generator.d.ts +0 -6
- package/dist/generator/spec-generator.js +0 -50
- package/dist/parser/har-filter.d.ts +0 -8
- package/dist/parser/har-filter.js +0 -71
- package/dist/parser/har-loader.d.ts +0 -2
- package/dist/parser/har-loader.js +0 -14
- package/dist/parser/har-normalizer.d.ts +0 -20
- package/dist/parser/har-normalizer.js +0 -78
- package/dist/parser/index.d.ts +0 -10
- package/dist/parser/index.js +0 -6
- package/dist/parser/openapi-loader.d.ts +0 -6
- package/dist/parser/openapi-loader.js +0 -308
- package/dist/parser/operation-extractor.d.ts +0 -13
- package/dist/parser/operation-extractor.js +0 -155
- package/dist/parser/overlay-loader.d.ts +0 -10
- package/dist/parser/overlay-loader.js +0 -184
- package/dist/parser/postman-loader.d.ts +0 -9
- package/dist/parser/postman-loader.js +0 -106
- package/dist/parser/schema-converter.d.ts +0 -12
- package/dist/parser/schema-converter.js +0 -117
- package/dist/plugins/adapter.d.ts +0 -40
- package/dist/plugins/adapter.js +0 -15
- package/dist/plugins/loader.d.ts +0 -25
- package/dist/plugins/loader.js +0 -58
- package/dist/pricing.d.ts +0 -55
- package/dist/pricing.js +0 -133
- package/dist/providers/index.d.ts +0 -15
- package/dist/providers/index.js +0 -56
- package/dist/recorder/browser-recorder.d.ts +0 -22
- package/dist/recorder/browser-recorder.js +0 -205
- package/dist/rescan/diff-engine.d.ts +0 -5
- package/dist/rescan/diff-engine.js +0 -312
- package/dist/rescan/index.d.ts +0 -3
- package/dist/rescan/index.js +0 -2
- package/dist/rescan/rescan-runner.d.ts +0 -42
- package/dist/rescan/rescan-runner.js +0 -69
- package/dist/rescan/rescan-scheduler.d.ts +0 -41
- package/dist/rescan/rescan-scheduler.js +0 -179
- package/dist/site-transformer/browser-tools.d.ts +0 -10
- package/dist/site-transformer/browser-tools.js +0 -59
- package/dist/site-transformer/index.d.ts +0 -2
- package/dist/site-transformer/index.js +0 -2
- package/dist/site-transformer/selector-healer.d.ts +0 -8
- package/dist/site-transformer/selector-healer.js +0 -106
- package/dist/site-transformer/tool-generator.d.ts +0 -13
- package/dist/site-transformer/tool-generator.js +0 -245
- package/dist/transformer/auth-detector.d.ts +0 -13
- package/dist/transformer/auth-detector.js +0 -90
- package/dist/transformer/catalog-builder.d.ts +0 -18
- package/dist/transformer/catalog-builder.js +0 -56
- package/dist/transformer/client-compat.d.ts +0 -6
- package/dist/transformer/client-compat.js +0 -44
- package/dist/transformer/har-clusterer.d.ts +0 -9
- package/dist/transformer/har-clusterer.js +0 -27
- package/dist/transformer/har-dedup.d.ts +0 -10
- package/dist/transformer/har-dedup.js +0 -81
- package/dist/transformer/har-schema-inferrer.d.ts +0 -15
- package/dist/transformer/har-schema-inferrer.js +0 -90
- package/dist/transformer/har-to-operations.d.ts +0 -13
- package/dist/transformer/har-to-operations.js +0 -192
- package/dist/transformer/index.d.ts +0 -8
- package/dist/transformer/index.js +0 -6
- package/dist/transformer/llm-namer.d.ts +0 -6
- package/dist/transformer/llm-namer.js +0 -59
- package/dist/transformer/naming.d.ts +0 -4
- package/dist/transformer/naming.js +0 -30
- package/dist/transformer/operation-filter.d.ts +0 -13
- package/dist/transformer/operation-filter.js +0 -52
- package/dist/transformer/resource-builder.d.ts +0 -12
- package/dist/transformer/resource-builder.js +0 -80
- package/dist/transformer/schema-merger.d.ts +0 -14
- package/dist/transformer/schema-merger.js +0 -65
- package/dist/transformer/tool-builder.d.ts +0 -3
- package/dist/transformer/tool-builder.js +0 -114
- package/dist/types/index.d.ts +0 -131
- package/dist/types/index.js +0 -1
- package/dist/types/site.d.ts +0 -284
- package/dist/types/site.js +0 -8
- package/dist/utils/fail.d.ts +0 -48
- package/dist/utils/fail.js +0 -204
- package/dist/utils/fs.d.ts +0 -5
- package/dist/utils/fs.js +0 -28
- package/dist/utils/interactive.d.ts +0 -6
- package/dist/utils/interactive.js +0 -30
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -2
- package/dist/utils/sanitize.d.ts +0 -28
- package/dist/utils/sanitize.js +0 -44
- package/dist/utils/watcher.d.ts +0 -11
- package/dist/utils/watcher.js +0 -36
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Compare two SiteDescriptors and produce a structured diff of changes.
|
|
3
|
-
*/
|
|
4
|
-
export function diffSiteDescriptors(oldSite, newSite) {
|
|
5
|
-
const changes = [];
|
|
6
|
-
const brokenSelectors = [];
|
|
7
|
-
const now = new Date().toISOString();
|
|
8
|
-
const oldPagesByUrl = new Map(oldSite.pages.map((p) => [p.url, p]));
|
|
9
|
-
const newPagesByUrl = new Map(newSite.pages.map((p) => [p.url, p]));
|
|
10
|
-
// Detect added pages
|
|
11
|
-
for (const [url, page] of newPagesByUrl) {
|
|
12
|
-
if (!oldPagesByUrl.has(url)) {
|
|
13
|
-
changes.push({
|
|
14
|
-
changeType: 'added',
|
|
15
|
-
elementType: 'page',
|
|
16
|
-
elementId: page.pageId,
|
|
17
|
-
pageId: page.pageId,
|
|
18
|
-
description: `Page added: ${url}`,
|
|
19
|
-
timestamp: now,
|
|
20
|
-
newValue: url,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
// Detect removed pages
|
|
25
|
-
for (const [url, page] of oldPagesByUrl) {
|
|
26
|
-
if (!newPagesByUrl.has(url)) {
|
|
27
|
-
changes.push({
|
|
28
|
-
changeType: 'removed',
|
|
29
|
-
elementType: 'page',
|
|
30
|
-
elementId: page.pageId,
|
|
31
|
-
pageId: page.pageId,
|
|
32
|
-
description: `Page removed: ${url}`,
|
|
33
|
-
timestamp: now,
|
|
34
|
-
oldValue: url,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// Compare matching pages
|
|
39
|
-
for (const [url, oldPage] of oldPagesByUrl) {
|
|
40
|
-
const newPage = newPagesByUrl.get(url);
|
|
41
|
-
if (!newPage)
|
|
42
|
-
continue;
|
|
43
|
-
diffForms(oldPage, newPage, changes, brokenSelectors, now);
|
|
44
|
-
diffButtons(oldPage, newPage, changes, brokenSelectors, now);
|
|
45
|
-
diffLinks(oldPage, newPage, changes, brokenSelectors, now);
|
|
46
|
-
}
|
|
47
|
-
const newVersion = oldSite.version + 1;
|
|
48
|
-
const updatedSite = {
|
|
49
|
-
...newSite,
|
|
50
|
-
version: newVersion,
|
|
51
|
-
};
|
|
52
|
-
return {
|
|
53
|
-
previousVersion: oldSite.version,
|
|
54
|
-
newVersion,
|
|
55
|
-
changes,
|
|
56
|
-
brokenSelectors,
|
|
57
|
-
newSiteDescriptor: updatedSite,
|
|
58
|
-
timestamp: now,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function diffForms(oldPage, newPage, changes, brokenSelectors, timestamp) {
|
|
62
|
-
const oldFormsById = new Map(oldPage.forms.map((f) => [f.formId, f]));
|
|
63
|
-
const newFormsById = new Map(newPage.forms.map((f) => [f.formId, f]));
|
|
64
|
-
for (const [id, form] of newFormsById) {
|
|
65
|
-
if (!oldFormsById.has(id)) {
|
|
66
|
-
changes.push({
|
|
67
|
-
changeType: 'added',
|
|
68
|
-
elementType: 'form',
|
|
69
|
-
elementId: id,
|
|
70
|
-
pageId: newPage.pageId,
|
|
71
|
-
description: `Form added: ${form.semanticName ?? id}`,
|
|
72
|
-
timestamp,
|
|
73
|
-
newValue: form.semanticName,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
for (const [id, form] of oldFormsById) {
|
|
78
|
-
if (!newFormsById.has(id)) {
|
|
79
|
-
changes.push({
|
|
80
|
-
changeType: 'removed',
|
|
81
|
-
elementType: 'form',
|
|
82
|
-
elementId: id,
|
|
83
|
-
pageId: oldPage.pageId,
|
|
84
|
-
description: `Form removed: ${form.semanticName ?? id}`,
|
|
85
|
-
timestamp,
|
|
86
|
-
oldValue: form.semanticName,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
for (const [id, oldForm] of oldFormsById) {
|
|
91
|
-
const newForm = newFormsById.get(id);
|
|
92
|
-
if (!newForm)
|
|
93
|
-
continue;
|
|
94
|
-
// Check for field changes
|
|
95
|
-
diffFormFields(oldForm, newForm, oldPage.pageId, changes, timestamp);
|
|
96
|
-
// Check for broken selectors
|
|
97
|
-
if (oldForm.selector.primary !== newForm.selector.primary) {
|
|
98
|
-
if (newForm.selector.confidence < 0.5) {
|
|
99
|
-
brokenSelectors.push({
|
|
100
|
-
toolName: newForm.semanticName ?? id,
|
|
101
|
-
selector: oldForm.selector,
|
|
102
|
-
});
|
|
103
|
-
changes.push({
|
|
104
|
-
changeType: 'selector-broken',
|
|
105
|
-
elementType: 'form',
|
|
106
|
-
elementId: id,
|
|
107
|
-
pageId: oldPage.pageId,
|
|
108
|
-
description: `Form selector changed with low confidence: ${oldForm.selector.primary}`,
|
|
109
|
-
timestamp,
|
|
110
|
-
oldValue: oldForm.selector.primary,
|
|
111
|
-
newValue: newForm.selector.primary,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
changes.push({
|
|
116
|
-
changeType: 'modified',
|
|
117
|
-
elementType: 'form',
|
|
118
|
-
elementId: id,
|
|
119
|
-
pageId: oldPage.pageId,
|
|
120
|
-
description: `Form selector updated: ${oldForm.selector.primary} → ${newForm.selector.primary}`,
|
|
121
|
-
timestamp,
|
|
122
|
-
oldValue: oldForm.selector.primary,
|
|
123
|
-
newValue: newForm.selector.primary,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
function diffFormFields(oldForm, newForm, pageId, changes, timestamp) {
|
|
130
|
-
const oldFieldsByName = new Map(oldForm.fields.map((f) => [f.name, f]));
|
|
131
|
-
const newFieldsByName = new Map(newForm.fields.map((f) => [f.name, f]));
|
|
132
|
-
for (const [name] of newFieldsByName) {
|
|
133
|
-
if (!oldFieldsByName.has(name)) {
|
|
134
|
-
changes.push({
|
|
135
|
-
changeType: 'added',
|
|
136
|
-
elementType: 'field',
|
|
137
|
-
elementId: `${oldForm.formId}:${name}`,
|
|
138
|
-
pageId,
|
|
139
|
-
description: `Field added to form ${oldForm.semanticName ?? oldForm.formId}: ${name}`,
|
|
140
|
-
timestamp,
|
|
141
|
-
newValue: name,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
for (const [name] of oldFieldsByName) {
|
|
146
|
-
if (!newFieldsByName.has(name)) {
|
|
147
|
-
changes.push({
|
|
148
|
-
changeType: 'removed',
|
|
149
|
-
elementType: 'field',
|
|
150
|
-
elementId: `${oldForm.formId}:${name}`,
|
|
151
|
-
pageId,
|
|
152
|
-
description: `Field removed from form ${oldForm.semanticName ?? oldForm.formId}: ${name}`,
|
|
153
|
-
timestamp,
|
|
154
|
-
oldValue: name,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
for (const [name, oldField] of oldFieldsByName) {
|
|
159
|
-
const newField = newFieldsByName.get(name);
|
|
160
|
-
if (!newField)
|
|
161
|
-
continue;
|
|
162
|
-
if (oldField.fieldType !== newField.fieldType) {
|
|
163
|
-
changes.push({
|
|
164
|
-
changeType: 'modified',
|
|
165
|
-
elementType: 'field',
|
|
166
|
-
elementId: `${oldForm.formId}:${name}`,
|
|
167
|
-
pageId,
|
|
168
|
-
description: `Field type changed in ${oldForm.semanticName ?? oldForm.formId}: ${oldField.fieldType} → ${newField.fieldType}`,
|
|
169
|
-
timestamp,
|
|
170
|
-
oldValue: oldField.fieldType,
|
|
171
|
-
newValue: newField.fieldType,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
if (oldField.required !== newField.required) {
|
|
175
|
-
changes.push({
|
|
176
|
-
changeType: 'modified',
|
|
177
|
-
elementType: 'field',
|
|
178
|
-
elementId: `${oldForm.formId}:${name}`,
|
|
179
|
-
pageId,
|
|
180
|
-
description: `Field required changed in ${oldForm.semanticName ?? oldForm.formId}: ${oldField.required} → ${newField.required}`,
|
|
181
|
-
timestamp,
|
|
182
|
-
oldValue: oldField.required,
|
|
183
|
-
newValue: newField.required,
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
function diffButtons(oldPage, newPage, changes, brokenSelectors, timestamp) {
|
|
189
|
-
const oldById = new Map(oldPage.buttons.map((b) => [b.buttonId, b]));
|
|
190
|
-
const newById = new Map(newPage.buttons.map((b) => [b.buttonId, b]));
|
|
191
|
-
for (const [id, btn] of newById) {
|
|
192
|
-
if (!oldById.has(id)) {
|
|
193
|
-
changes.push({
|
|
194
|
-
changeType: 'added',
|
|
195
|
-
elementType: 'button',
|
|
196
|
-
elementId: id,
|
|
197
|
-
pageId: newPage.pageId,
|
|
198
|
-
description: `Button added: ${btn.text ?? btn.semanticAction ?? id}`,
|
|
199
|
-
timestamp,
|
|
200
|
-
newValue: btn.text,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const [id, btn] of oldById) {
|
|
205
|
-
if (!newById.has(id)) {
|
|
206
|
-
changes.push({
|
|
207
|
-
changeType: 'removed',
|
|
208
|
-
elementType: 'button',
|
|
209
|
-
elementId: id,
|
|
210
|
-
pageId: oldPage.pageId,
|
|
211
|
-
description: `Button removed: ${btn.text ?? btn.semanticAction ?? id}`,
|
|
212
|
-
timestamp,
|
|
213
|
-
oldValue: btn.text,
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
for (const [id, oldBtn] of oldById) {
|
|
218
|
-
const newBtn = newById.get(id);
|
|
219
|
-
if (!newBtn)
|
|
220
|
-
continue;
|
|
221
|
-
if (oldBtn.text !== newBtn.text) {
|
|
222
|
-
changes.push({
|
|
223
|
-
changeType: 'modified',
|
|
224
|
-
elementType: 'button',
|
|
225
|
-
elementId: id,
|
|
226
|
-
pageId: oldPage.pageId,
|
|
227
|
-
description: `Button text changed: "${oldBtn.text}" → "${newBtn.text}"`,
|
|
228
|
-
timestamp,
|
|
229
|
-
oldValue: oldBtn.text,
|
|
230
|
-
newValue: newBtn.text,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
checkBrokenSelector(oldBtn.selector, newBtn.selector, oldBtn.semanticAction ?? id, id, 'button', oldPage.pageId, changes, brokenSelectors, timestamp);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function diffLinks(oldPage, newPage, changes, brokenSelectors, timestamp) {
|
|
237
|
-
const oldById = new Map(oldPage.links.map((l) => [l.linkId, l]));
|
|
238
|
-
const newById = new Map(newPage.links.map((l) => [l.linkId, l]));
|
|
239
|
-
for (const [id, link] of newById) {
|
|
240
|
-
if (!oldById.has(id)) {
|
|
241
|
-
changes.push({
|
|
242
|
-
changeType: 'added',
|
|
243
|
-
elementType: 'link',
|
|
244
|
-
elementId: id,
|
|
245
|
-
pageId: newPage.pageId,
|
|
246
|
-
description: `Link added: ${link.text ?? link.href}`,
|
|
247
|
-
timestamp,
|
|
248
|
-
newValue: link.href,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
for (const [id, link] of oldById) {
|
|
253
|
-
if (!newById.has(id)) {
|
|
254
|
-
changes.push({
|
|
255
|
-
changeType: 'removed',
|
|
256
|
-
elementType: 'link',
|
|
257
|
-
elementId: id,
|
|
258
|
-
pageId: oldPage.pageId,
|
|
259
|
-
description: `Link removed: ${link.text ?? link.href}`,
|
|
260
|
-
timestamp,
|
|
261
|
-
oldValue: link.href,
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
for (const [id, oldLink] of oldById) {
|
|
266
|
-
const newLink = newById.get(id);
|
|
267
|
-
if (!newLink)
|
|
268
|
-
continue;
|
|
269
|
-
if (oldLink.href !== newLink.href) {
|
|
270
|
-
changes.push({
|
|
271
|
-
changeType: 'modified',
|
|
272
|
-
elementType: 'link',
|
|
273
|
-
elementId: id,
|
|
274
|
-
pageId: oldPage.pageId,
|
|
275
|
-
description: `Link href changed: "${oldLink.href}" → "${newLink.href}"`,
|
|
276
|
-
timestamp,
|
|
277
|
-
oldValue: oldLink.href,
|
|
278
|
-
newValue: newLink.href,
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
checkBrokenSelector(oldLink.selector, newLink.selector, oldLink.semanticAction ?? id, id, 'link', oldPage.pageId, changes, brokenSelectors, timestamp);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
function checkBrokenSelector(oldSelector, newSelector, toolName, elementId, elementType, pageId, changes, brokenSelectors, timestamp) {
|
|
285
|
-
if (oldSelector.primary !== newSelector.primary) {
|
|
286
|
-
if (newSelector.confidence < 0.5) {
|
|
287
|
-
brokenSelectors.push({ toolName, selector: oldSelector });
|
|
288
|
-
changes.push({
|
|
289
|
-
changeType: 'selector-broken',
|
|
290
|
-
elementType,
|
|
291
|
-
elementId,
|
|
292
|
-
pageId,
|
|
293
|
-
description: `${elementType} selector changed with low confidence: ${oldSelector.primary}`,
|
|
294
|
-
timestamp,
|
|
295
|
-
oldValue: oldSelector.primary,
|
|
296
|
-
newValue: newSelector.primary,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
changes.push({
|
|
301
|
-
changeType: 'modified',
|
|
302
|
-
elementType,
|
|
303
|
-
elementId,
|
|
304
|
-
pageId,
|
|
305
|
-
description: `${elementType} selector updated: ${oldSelector.primary} → ${newSelector.primary}`,
|
|
306
|
-
timestamp,
|
|
307
|
-
oldValue: oldSelector.primary,
|
|
308
|
-
newValue: newSelector.primary,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
package/dist/rescan/index.d.ts
DELETED
package/dist/rescan/index.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the `mcpmake rescan` command: collecting fragile selectors
|
|
3
|
-
* to heal and summarizing a diff. Kept free of Playwright/LLM/IO so they can be
|
|
4
|
-
* unit-tested directly; the browser + LLM orchestration lives in the command.
|
|
5
|
-
*/
|
|
6
|
-
import type { SiteDescriptor, SelectorSet, RescanResult } from '../types/site.js';
|
|
7
|
-
/** A low-confidence selector flagged for healing, with a live object reference. */
|
|
8
|
-
export interface LowConfidenceSelector {
|
|
9
|
-
/** URL of the page this selector lives on (for navigation during healing). */
|
|
10
|
-
pageUrl: string;
|
|
11
|
-
/** Human-readable description used in the heal prompt. */
|
|
12
|
-
description: string;
|
|
13
|
-
/**
|
|
14
|
-
* The actual SelectorSet object inside the descriptor. Mutating this (via
|
|
15
|
-
* Object.assign) updates the descriptor in place so regeneration picks up
|
|
16
|
-
* the healed selector.
|
|
17
|
-
*/
|
|
18
|
-
selector: SelectorSet;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Walk a SiteDescriptor and collect every selector whose confidence is below
|
|
22
|
-
* `threshold` (default 0.5 — i.e. the brittle css-path / xpath fallbacks).
|
|
23
|
-
*/
|
|
24
|
-
export declare function collectLowConfidenceSelectors(site: SiteDescriptor, threshold?: number): LowConfidenceSelector[];
|
|
25
|
-
export interface ChangeCounts {
|
|
26
|
-
page: number;
|
|
27
|
-
form: number;
|
|
28
|
-
field: number;
|
|
29
|
-
button: number;
|
|
30
|
-
link: number;
|
|
31
|
-
}
|
|
32
|
-
export interface RescanSummary {
|
|
33
|
-
previousVersion: number;
|
|
34
|
-
newVersion: number;
|
|
35
|
-
added: ChangeCounts;
|
|
36
|
-
removed: ChangeCounts;
|
|
37
|
-
modified: ChangeCounts;
|
|
38
|
-
brokenSelectors: number;
|
|
39
|
-
totalChanges: number;
|
|
40
|
-
}
|
|
41
|
-
/** Summarize a RescanResult into per-kind change counts for reporting. */
|
|
42
|
-
export declare function summarizeRescan(result: RescanResult): RescanSummary;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure helpers for the `mcpmake rescan` command: collecting fragile selectors
|
|
3
|
-
* to heal and summarizing a diff. Kept free of Playwright/LLM/IO so they can be
|
|
4
|
-
* unit-tested directly; the browser + LLM orchestration lives in the command.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Walk a SiteDescriptor and collect every selector whose confidence is below
|
|
8
|
-
* `threshold` (default 0.5 — i.e. the brittle css-path / xpath fallbacks).
|
|
9
|
-
*/
|
|
10
|
-
export function collectLowConfidenceSelectors(site, threshold = 0.5) {
|
|
11
|
-
const out = [];
|
|
12
|
-
const consider = (selector, pageUrl, description) => {
|
|
13
|
-
if (selector && selector.confidence < threshold) {
|
|
14
|
-
out.push({ pageUrl, description, selector });
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
for (const page of site.pages) {
|
|
18
|
-
for (const form of page.forms) {
|
|
19
|
-
const formName = form.semanticName ?? form.formId;
|
|
20
|
-
consider(form.selector, page.url, `form "${formName}"`);
|
|
21
|
-
consider(form.submitButton, page.url, `submit button of form "${formName}"`);
|
|
22
|
-
for (const field of form.fields) {
|
|
23
|
-
consider(field.selector, page.url, `field "${field.name}" (${field.label ?? field.fieldType}) in form "${formName}"`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
for (const button of page.buttons) {
|
|
27
|
-
consider(button.selector, page.url, `button "${button.text ?? button.semanticAction ?? button.buttonId}"`);
|
|
28
|
-
}
|
|
29
|
-
for (const link of page.links) {
|
|
30
|
-
consider(link.selector, page.url, `link "${link.text ?? link.href}"`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return out;
|
|
34
|
-
}
|
|
35
|
-
function emptyCounts() {
|
|
36
|
-
return { page: 0, form: 0, field: 0, button: 0, link: 0 };
|
|
37
|
-
}
|
|
38
|
-
/** Summarize a RescanResult into per-kind change counts for reporting. */
|
|
39
|
-
export function summarizeRescan(result) {
|
|
40
|
-
const added = emptyCounts();
|
|
41
|
-
const removed = emptyCounts();
|
|
42
|
-
const modified = emptyCounts();
|
|
43
|
-
const bump = (counts, elementType) => {
|
|
44
|
-
counts[elementType] += 1;
|
|
45
|
-
};
|
|
46
|
-
for (const change of result.changes) {
|
|
47
|
-
switch (change.changeType) {
|
|
48
|
-
case 'added':
|
|
49
|
-
bump(added, change.elementType);
|
|
50
|
-
break;
|
|
51
|
-
case 'removed':
|
|
52
|
-
bump(removed, change.elementType);
|
|
53
|
-
break;
|
|
54
|
-
case 'modified':
|
|
55
|
-
bump(modified, change.elementType);
|
|
56
|
-
break;
|
|
57
|
-
// 'selector-broken' is reported separately via brokenSelectors.
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return {
|
|
61
|
-
previousVersion: result.previousVersion,
|
|
62
|
-
newVersion: result.newVersion,
|
|
63
|
-
added,
|
|
64
|
-
removed,
|
|
65
|
-
modified,
|
|
66
|
-
brokenSelectors: result.brokenSelectors.length,
|
|
67
|
-
totalChanges: result.changes.length,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
export interface ScheduleEntry {
|
|
2
|
-
slug: string;
|
|
3
|
-
cronExpr: string;
|
|
4
|
-
partial: boolean;
|
|
5
|
-
nextRunAt: Date;
|
|
6
|
-
createdAt: Date;
|
|
7
|
-
}
|
|
8
|
-
export type RescanCallback = (slug: string, partial: boolean) => void | Promise<void>;
|
|
9
|
-
/**
|
|
10
|
-
* In-memory scheduler for periodic site rescans.
|
|
11
|
-
* Checks every 60 seconds for due rescans and invokes the callback.
|
|
12
|
-
*/
|
|
13
|
-
export declare class RescanScheduler {
|
|
14
|
-
private schedules;
|
|
15
|
-
private timer;
|
|
16
|
-
private callback;
|
|
17
|
-
private running;
|
|
18
|
-
constructor(callback: RescanCallback);
|
|
19
|
-
/** Register or update a rescan schedule for a site slug. */
|
|
20
|
-
scheduleRescan(slug: string, cronExpr: string, partial?: boolean): void;
|
|
21
|
-
/** Cancel a pending rescan schedule. */
|
|
22
|
-
cancelRescan(slug: string): boolean;
|
|
23
|
-
/** Start the periodic checker (every 60s). */
|
|
24
|
-
start(): void;
|
|
25
|
-
/** Stop the periodic checker. */
|
|
26
|
-
stop(): void;
|
|
27
|
-
/** Visible for testing: run a single check cycle. */
|
|
28
|
-
tick(): Promise<void>;
|
|
29
|
-
/** Get all registered schedules (for introspection / testing). */
|
|
30
|
-
getSchedules(): ReadonlyMap<string, ScheduleEntry>;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Parse a simplified cron expression and compute the next run time after `after`.
|
|
34
|
-
*
|
|
35
|
-
* Supported format: "minute hour dayOfMonth month dayOfWeek"
|
|
36
|
-
* - Each field can be a number or '*'
|
|
37
|
-
* - Ranges (1-5), lists (1,3,5), and step values (*\/10) are supported for minute/hour
|
|
38
|
-
*
|
|
39
|
-
* Returns null if the expression is invalid.
|
|
40
|
-
*/
|
|
41
|
-
export declare function computeNextRun(cronExpr: string, after: Date): Date | null;
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { logger } from '../utils/logger.js';
|
|
2
|
-
/**
|
|
3
|
-
* In-memory scheduler for periodic site rescans.
|
|
4
|
-
* Checks every 60 seconds for due rescans and invokes the callback.
|
|
5
|
-
*/
|
|
6
|
-
export class RescanScheduler {
|
|
7
|
-
schedules = new Map();
|
|
8
|
-
timer = null;
|
|
9
|
-
callback;
|
|
10
|
-
running = false;
|
|
11
|
-
constructor(callback) {
|
|
12
|
-
this.callback = callback;
|
|
13
|
-
}
|
|
14
|
-
/** Register or update a rescan schedule for a site slug. */
|
|
15
|
-
scheduleRescan(slug, cronExpr, partial = false) {
|
|
16
|
-
const nextRunAt = computeNextRun(cronExpr, new Date());
|
|
17
|
-
if (!nextRunAt) {
|
|
18
|
-
logger.warn(`Invalid cron expression for slug "${slug}": ${cronExpr}`);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
this.schedules.set(slug, {
|
|
22
|
-
slug,
|
|
23
|
-
cronExpr,
|
|
24
|
-
partial,
|
|
25
|
-
nextRunAt,
|
|
26
|
-
createdAt: new Date(),
|
|
27
|
-
});
|
|
28
|
-
logger.info(`Scheduled rescan for "${slug}" — next run at ${nextRunAt.toISOString()}`);
|
|
29
|
-
}
|
|
30
|
-
/** Cancel a pending rescan schedule. */
|
|
31
|
-
cancelRescan(slug) {
|
|
32
|
-
const deleted = this.schedules.delete(slug);
|
|
33
|
-
if (deleted) {
|
|
34
|
-
logger.info(`Cancelled rescan schedule for "${slug}"`);
|
|
35
|
-
}
|
|
36
|
-
return deleted;
|
|
37
|
-
}
|
|
38
|
-
/** Start the periodic checker (every 60s). */
|
|
39
|
-
start() {
|
|
40
|
-
if (this.running)
|
|
41
|
-
return;
|
|
42
|
-
this.running = true;
|
|
43
|
-
this.timer = setInterval(() => {
|
|
44
|
-
void this.tick();
|
|
45
|
-
}, 60_000);
|
|
46
|
-
logger.info('Rescan scheduler started');
|
|
47
|
-
}
|
|
48
|
-
/** Stop the periodic checker. */
|
|
49
|
-
stop() {
|
|
50
|
-
if (!this.running)
|
|
51
|
-
return;
|
|
52
|
-
this.running = false;
|
|
53
|
-
if (this.timer !== null) {
|
|
54
|
-
clearInterval(this.timer);
|
|
55
|
-
this.timer = null;
|
|
56
|
-
}
|
|
57
|
-
logger.info('Rescan scheduler stopped');
|
|
58
|
-
}
|
|
59
|
-
/** Visible for testing: run a single check cycle. */
|
|
60
|
-
async tick() {
|
|
61
|
-
const now = new Date();
|
|
62
|
-
for (const entry of this.schedules.values()) {
|
|
63
|
-
if (entry.nextRunAt <= now) {
|
|
64
|
-
try {
|
|
65
|
-
await this.callback(entry.slug, entry.partial);
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
logger.error(`Rescan callback failed for "${entry.slug}": ${err instanceof Error ? err.message : String(err)}`);
|
|
69
|
-
}
|
|
70
|
-
// Advance to the next scheduled run
|
|
71
|
-
const next = computeNextRun(entry.cronExpr, now);
|
|
72
|
-
if (next) {
|
|
73
|
-
entry.nextRunAt = next;
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
// Invalid cron — remove the schedule
|
|
77
|
-
this.schedules.delete(entry.slug);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/** Get all registered schedules (for introspection / testing). */
|
|
83
|
-
getSchedules() {
|
|
84
|
-
return this.schedules;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// ─── Simple Cron Parsing ──────────────────────────────────────────
|
|
88
|
-
/**
|
|
89
|
-
* Parse a simplified cron expression and compute the next run time after `after`.
|
|
90
|
-
*
|
|
91
|
-
* Supported format: "minute hour dayOfMonth month dayOfWeek"
|
|
92
|
-
* - Each field can be a number or '*'
|
|
93
|
-
* - Ranges (1-5), lists (1,3,5), and step values (*\/10) are supported for minute/hour
|
|
94
|
-
*
|
|
95
|
-
* Returns null if the expression is invalid.
|
|
96
|
-
*/
|
|
97
|
-
export function computeNextRun(cronExpr, after) {
|
|
98
|
-
const parts = cronExpr.trim().split(/\s+/);
|
|
99
|
-
if (parts.length !== 5)
|
|
100
|
-
return null;
|
|
101
|
-
const minuteSpec = parts[0];
|
|
102
|
-
const hourSpec = parts[1];
|
|
103
|
-
const domSpec = parts[2];
|
|
104
|
-
const monthSpec = parts[3];
|
|
105
|
-
const dowSpec = parts[4];
|
|
106
|
-
const minutes = expandField(minuteSpec, 0, 59);
|
|
107
|
-
const hours = expandField(hourSpec, 0, 23);
|
|
108
|
-
const doms = expandField(domSpec, 1, 31);
|
|
109
|
-
const months = expandField(monthSpec, 1, 12);
|
|
110
|
-
const dows = expandField(dowSpec, 0, 6);
|
|
111
|
-
if (!minutes || !hours || !doms || !months || !dows)
|
|
112
|
-
return null;
|
|
113
|
-
// Brute-force search over the next 400 days to find the first matching time
|
|
114
|
-
const candidate = new Date(after.getTime());
|
|
115
|
-
candidate.setSeconds(0, 0);
|
|
116
|
-
candidate.setMinutes(candidate.getMinutes() + 1); // Start from the next minute
|
|
117
|
-
const limit = new Date(after.getTime() + 7 * 24 * 60 * 60 * 1000);
|
|
118
|
-
while (candidate < limit) {
|
|
119
|
-
const m = candidate.getMinutes();
|
|
120
|
-
const h = candidate.getHours();
|
|
121
|
-
const dom = candidate.getDate();
|
|
122
|
-
const mon = candidate.getMonth() + 1; // JS months are 0-based
|
|
123
|
-
const dow = candidate.getDay();
|
|
124
|
-
if (minutes.includes(m) &&
|
|
125
|
-
hours.includes(h) &&
|
|
126
|
-
doms.includes(dom) &&
|
|
127
|
-
months.includes(mon) &&
|
|
128
|
-
dows.includes(dow)) {
|
|
129
|
-
return candidate;
|
|
130
|
-
}
|
|
131
|
-
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
function expandField(spec, min, max) {
|
|
136
|
-
if (spec === '*') {
|
|
137
|
-
return range(min, max);
|
|
138
|
-
}
|
|
139
|
-
// Step: */N
|
|
140
|
-
const stepMatch = spec.match(/^\*\/(\d+)$/);
|
|
141
|
-
if (stepMatch) {
|
|
142
|
-
const step = parseInt(stepMatch[1], 10);
|
|
143
|
-
if (step <= 0 || step > max)
|
|
144
|
-
return null;
|
|
145
|
-
const result = [];
|
|
146
|
-
for (let i = min; i <= max; i += step) {
|
|
147
|
-
result.push(i);
|
|
148
|
-
}
|
|
149
|
-
return result;
|
|
150
|
-
}
|
|
151
|
-
// List: 1,3,5
|
|
152
|
-
if (spec.includes(',')) {
|
|
153
|
-
const values = spec.split(',').map((s) => parseInt(s.trim(), 10));
|
|
154
|
-
if (values.some((v) => isNaN(v) || v < min || v > max))
|
|
155
|
-
return null;
|
|
156
|
-
return values;
|
|
157
|
-
}
|
|
158
|
-
// Range: 1-5
|
|
159
|
-
const rangeMatch = spec.match(/^(\d+)-(\d+)$/);
|
|
160
|
-
if (rangeMatch) {
|
|
161
|
-
const start = parseInt(rangeMatch[1], 10);
|
|
162
|
-
const end = parseInt(rangeMatch[2], 10);
|
|
163
|
-
if (start < min || end > max || start > end)
|
|
164
|
-
return null;
|
|
165
|
-
return range(start, end);
|
|
166
|
-
}
|
|
167
|
-
// Single number
|
|
168
|
-
const num = parseInt(spec, 10);
|
|
169
|
-
if (isNaN(num) || num < min || num > max)
|
|
170
|
-
return null;
|
|
171
|
-
return [num];
|
|
172
|
-
}
|
|
173
|
-
function range(start, end) {
|
|
174
|
-
const result = [];
|
|
175
|
-
for (let i = start; i <= end; i++) {
|
|
176
|
-
result.push(i);
|
|
177
|
-
}
|
|
178
|
-
return result;
|
|
179
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in browser lifecycle tools that every generated site MCP server includes.
|
|
3
|
-
* These are not derived from site analysis — they manage the Playwright browser.
|
|
4
|
-
*/
|
|
5
|
-
import type { SiteToolDefinition } from '../types/site.js';
|
|
6
|
-
/**
|
|
7
|
-
* Returns the three built-in browser lifecycle tools:
|
|
8
|
-
* start_browser, stop_browser, take_screenshot
|
|
9
|
-
*/
|
|
10
|
-
export declare function buildBrowserLifecycleTools(): SiteToolDefinition[];
|