mcpmake 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/README.md +0 -691
- 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,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durable per-user usage accounting tied to a billing period.
|
|
3
|
-
*
|
|
4
|
-
* Records tool calls (and total requests) for the served MCP traffic so that
|
|
5
|
-
* quota can be enforced and usage survives process restarts. Backed by the
|
|
6
|
-
* `usage_summaries` Postgres table when a database is configured; otherwise an
|
|
7
|
-
* in-memory fallback is used (dev / single-process).
|
|
8
|
-
*/
|
|
9
|
-
import { randomBytes } from 'node:crypto';
|
|
10
|
-
/**
|
|
11
|
-
* Compute the billing period containing `now`.
|
|
12
|
-
*
|
|
13
|
-
* When `anchorEndMs` (a Stripe subscription's current_period_end) is provided
|
|
14
|
-
* and in the future, the period is the month ending at that anchor; otherwise
|
|
15
|
-
* it falls back to the calendar month (UTC). Calendar-month alignment is a safe
|
|
16
|
-
* default for the free tier and approximates monthly subscriptions closely.
|
|
17
|
-
*/
|
|
18
|
-
export function getBillingPeriod(now = new Date(), anchorEndMs) {
|
|
19
|
-
if (anchorEndMs && anchorEndMs > now.getTime()) {
|
|
20
|
-
const end = new Date(anchorEndMs);
|
|
21
|
-
// Compute "one month before end" without JS month-overflow. Using
|
|
22
|
-
// setUTCMonth(-1) on e.g. Mar 31 yields Mar 3 (Feb has no 31st), which would
|
|
23
|
-
// leave a 2–3 day gap where usage is keyed to a different period and quota
|
|
24
|
-
// appears to reset. Clamp the day to the target month's length instead.
|
|
25
|
-
const targetMonthIndex = end.getUTCMonth() - 1;
|
|
26
|
-
const year = targetMonthIndex < 0 ? end.getUTCFullYear() - 1 : end.getUTCFullYear();
|
|
27
|
-
const month = ((targetMonthIndex % 12) + 12) % 12;
|
|
28
|
-
const daysInTarget = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
|
|
29
|
-
const day = Math.min(end.getUTCDate(), daysInTarget);
|
|
30
|
-
const start = new Date(Date.UTC(year, month, day, end.getUTCHours(), end.getUTCMinutes(), end.getUTCSeconds(), end.getUTCMilliseconds()));
|
|
31
|
-
if (start.getTime() <= now.getTime()) {
|
|
32
|
-
return { start: start.toISOString(), end: end.toISOString() };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
|
36
|
-
const end = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1));
|
|
37
|
-
return { start: start.toISOString(), end: end.toISOString() };
|
|
38
|
-
}
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// In-memory implementation (dev / no database)
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
const MAX_INMEMORY_KEYS = 50_000;
|
|
43
|
-
export class InMemoryUsageStore {
|
|
44
|
-
counts = new Map();
|
|
45
|
-
key(userId, period) {
|
|
46
|
-
return `${userId}::${period.start}`;
|
|
47
|
-
}
|
|
48
|
-
async record(userId, period, toolCalls, requests) {
|
|
49
|
-
if (this.counts.size > MAX_INMEMORY_KEYS) {
|
|
50
|
-
// Drop the oldest-inserted half to bound memory.
|
|
51
|
-
const keep = Math.floor(MAX_INMEMORY_KEYS / 2);
|
|
52
|
-
this.counts = new Map([...this.counts].slice(-keep));
|
|
53
|
-
}
|
|
54
|
-
const k = this.key(userId, period);
|
|
55
|
-
const entry = this.counts.get(k) ?? { toolCalls: 0, requests: 0 };
|
|
56
|
-
entry.toolCalls += toolCalls;
|
|
57
|
-
entry.requests += requests;
|
|
58
|
-
this.counts.set(k, entry);
|
|
59
|
-
}
|
|
60
|
-
async getToolCalls(userId, period) {
|
|
61
|
-
return this.counts.get(this.key(userId, period))?.toolCalls ?? 0;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Postgres implementation
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
export class PgUsageStore {
|
|
68
|
-
db;
|
|
69
|
-
constructor(db) {
|
|
70
|
-
this.db = db;
|
|
71
|
-
}
|
|
72
|
-
async record(userId, period, toolCalls, requests) {
|
|
73
|
-
await this.db.query(`INSERT INTO usage_summaries (id, user_id, period_start, period_end, request_count, tool_calls)
|
|
74
|
-
VALUES ($1, $2, $3, $4, $5, $6)
|
|
75
|
-
ON CONFLICT (user_id, period_start)
|
|
76
|
-
DO UPDATE SET request_count = usage_summaries.request_count + EXCLUDED.request_count,
|
|
77
|
-
tool_calls = usage_summaries.tool_calls + EXCLUDED.tool_calls`, [randomBytes(16).toString('hex'), userId, period.start, period.end, requests, toolCalls]);
|
|
78
|
-
}
|
|
79
|
-
async getToolCalls(userId, period) {
|
|
80
|
-
const { rows } = await this.db.query(`SELECT COALESCE(SUM(tool_calls), 0) AS calls
|
|
81
|
-
FROM usage_summaries WHERE user_id = $1 AND period_start = $2`, [userId, period.start]);
|
|
82
|
-
const value = rows[0]?.calls ?? 0;
|
|
83
|
-
return typeof value === 'number' ? value : parseInt(value, 10) || 0;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
export type UsageEventType = 'session-start' | 'session-end' | 'tool-call' | 'screenshot';
|
|
2
|
-
export interface UsageEvent {
|
|
3
|
-
userId: string;
|
|
4
|
-
slug: string;
|
|
5
|
-
eventType: UsageEventType;
|
|
6
|
-
timestamp: Date;
|
|
7
|
-
/** Duration in milliseconds (for session-end events) */
|
|
8
|
-
durationMs?: number;
|
|
9
|
-
/** Extra metadata */
|
|
10
|
-
metadata?: Record<string, unknown>;
|
|
11
|
-
}
|
|
12
|
-
export interface UsageSummary {
|
|
13
|
-
userId: string;
|
|
14
|
-
totalSessionsMs: number;
|
|
15
|
-
totalToolCalls: number;
|
|
16
|
-
totalScreenshots: number;
|
|
17
|
-
/** Usage broken down by site slug */
|
|
18
|
-
bySlug: Map<string, {
|
|
19
|
-
sessionsMs: number;
|
|
20
|
-
toolCalls: number;
|
|
21
|
-
screenshots: number;
|
|
22
|
-
}>;
|
|
23
|
-
}
|
|
24
|
-
export declare class UsageTracker {
|
|
25
|
-
private events;
|
|
26
|
-
private activeSessions;
|
|
27
|
-
private appendEvent;
|
|
28
|
-
/** Start a new session for a user + site slug. Returns a session key. */
|
|
29
|
-
startSession(userId: string, slug: string): string;
|
|
30
|
-
/** End an active session. Records the duration. */
|
|
31
|
-
endSession(sessionKey: string): void;
|
|
32
|
-
/** Record a tool call event. */
|
|
33
|
-
recordToolCall(userId: string, slug: string, metadata?: Record<string, unknown>): void;
|
|
34
|
-
/** Record a screenshot capture event. */
|
|
35
|
-
recordScreenshot(userId: string, slug: string, metadata?: Record<string, unknown>): void;
|
|
36
|
-
/** Get a usage summary for a specific user. */
|
|
37
|
-
getUsageSummary(userId: string): UsageSummary;
|
|
38
|
-
}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory usage tracker for billing purposes.
|
|
3
|
-
* Tracks sessions, tool calls, and screenshots per user/slug.
|
|
4
|
-
*/
|
|
5
|
-
const MAX_EVENTS = 50_000;
|
|
6
|
-
export class UsageTracker {
|
|
7
|
-
events = [];
|
|
8
|
-
activeSessions = new Map();
|
|
9
|
-
appendEvent(event) {
|
|
10
|
-
this.events.push(event);
|
|
11
|
-
if (this.events.length > MAX_EVENTS) {
|
|
12
|
-
// Keep the newest half to avoid unbounded memory growth
|
|
13
|
-
this.events = this.events.slice(-Math.floor(MAX_EVENTS / 2));
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
/** Start a new session for a user + site slug. Returns a session key. */
|
|
17
|
-
startSession(userId, slug) {
|
|
18
|
-
const key = `${userId}:${slug}:${Date.now()}`;
|
|
19
|
-
this.activeSessions.set(key, { userId, slug, startedAt: new Date() });
|
|
20
|
-
this.appendEvent({
|
|
21
|
-
userId,
|
|
22
|
-
slug,
|
|
23
|
-
eventType: 'session-start',
|
|
24
|
-
timestamp: new Date(),
|
|
25
|
-
});
|
|
26
|
-
return key;
|
|
27
|
-
}
|
|
28
|
-
/** End an active session. Records the duration. */
|
|
29
|
-
endSession(sessionKey) {
|
|
30
|
-
const session = this.activeSessions.get(sessionKey);
|
|
31
|
-
if (!session)
|
|
32
|
-
return;
|
|
33
|
-
const durationMs = Date.now() - session.startedAt.getTime();
|
|
34
|
-
this.appendEvent({
|
|
35
|
-
userId: session.userId,
|
|
36
|
-
slug: session.slug,
|
|
37
|
-
eventType: 'session-end',
|
|
38
|
-
timestamp: new Date(),
|
|
39
|
-
durationMs,
|
|
40
|
-
});
|
|
41
|
-
this.activeSessions.delete(sessionKey);
|
|
42
|
-
}
|
|
43
|
-
/** Record a tool call event. */
|
|
44
|
-
recordToolCall(userId, slug, metadata) {
|
|
45
|
-
this.appendEvent({
|
|
46
|
-
userId,
|
|
47
|
-
slug,
|
|
48
|
-
eventType: 'tool-call',
|
|
49
|
-
timestamp: new Date(),
|
|
50
|
-
metadata,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
/** Record a screenshot capture event. */
|
|
54
|
-
recordScreenshot(userId, slug, metadata) {
|
|
55
|
-
this.appendEvent({
|
|
56
|
-
userId,
|
|
57
|
-
slug,
|
|
58
|
-
eventType: 'screenshot',
|
|
59
|
-
timestamp: new Date(),
|
|
60
|
-
metadata,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
/** Get a usage summary for a specific user. */
|
|
64
|
-
getUsageSummary(userId) {
|
|
65
|
-
const userEvents = this.events.filter((e) => e.userId === userId);
|
|
66
|
-
const bySlug = new Map();
|
|
67
|
-
let totalSessionsMs = 0;
|
|
68
|
-
let totalToolCalls = 0;
|
|
69
|
-
let totalScreenshots = 0;
|
|
70
|
-
for (const event of userEvents) {
|
|
71
|
-
let slugEntry = bySlug.get(event.slug);
|
|
72
|
-
if (!slugEntry) {
|
|
73
|
-
slugEntry = { sessionsMs: 0, toolCalls: 0, screenshots: 0 };
|
|
74
|
-
bySlug.set(event.slug, slugEntry);
|
|
75
|
-
}
|
|
76
|
-
switch (event.eventType) {
|
|
77
|
-
case 'session-end':
|
|
78
|
-
if (event.durationMs != null) {
|
|
79
|
-
totalSessionsMs += event.durationMs;
|
|
80
|
-
slugEntry.sessionsMs += event.durationMs;
|
|
81
|
-
}
|
|
82
|
-
break;
|
|
83
|
-
case 'tool-call':
|
|
84
|
-
totalToolCalls++;
|
|
85
|
-
slugEntry.toolCalls++;
|
|
86
|
-
break;
|
|
87
|
-
case 'screenshot':
|
|
88
|
-
totalScreenshots++;
|
|
89
|
-
slugEntry.screenshots++;
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return { userId, totalSessionsMs, totalToolCalls, totalScreenshots, bySlug };
|
|
94
|
-
}
|
|
95
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spec -> Generated project pipeline.
|
|
3
|
-
*
|
|
4
|
-
* Takes an uploaded spec file (OpenAPI or HAR), runs it through the existing
|
|
5
|
-
* parser/transformer/emitter pipeline, audits the output, and returns a
|
|
6
|
-
* BuildResult with the slug and image metadata.
|
|
7
|
-
*/
|
|
8
|
-
export interface BuildResult {
|
|
9
|
-
slug: string;
|
|
10
|
-
imageName: string;
|
|
11
|
-
imageTag: string;
|
|
12
|
-
buildDir: string;
|
|
13
|
-
toolCount: number;
|
|
14
|
-
/** Detected server type based on project dependencies. */
|
|
15
|
-
serverType: 'http' | 'playwright';
|
|
16
|
-
}
|
|
17
|
-
export interface BuildOptions {
|
|
18
|
-
name?: string;
|
|
19
|
-
format: 'openapi' | 'har';
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Build a project from an uploaded spec file.
|
|
23
|
-
*
|
|
24
|
-
* Steps:
|
|
25
|
-
* 1. Generate slug and create temp build dir
|
|
26
|
-
* 2. Run the appropriate pipeline (OpenAPI or HAR) to generate the project
|
|
27
|
-
* 3. Audit the generated code for dangerous patterns
|
|
28
|
-
* 4. Return build result
|
|
29
|
-
*/
|
|
30
|
-
export declare function buildFromSpec(specPath: string, opts: BuildOptions): Promise<BuildResult>;
|
|
31
|
-
/**
|
|
32
|
-
* Detect the spec format from a file path.
|
|
33
|
-
*/
|
|
34
|
-
export declare function detectFormat(filePath: string, contentType?: string): 'openapi' | 'har';
|
|
35
|
-
/**
|
|
36
|
-
* Detect format by inspecting file content.
|
|
37
|
-
* Useful when the file extension is ambiguous (e.g., .json).
|
|
38
|
-
*/
|
|
39
|
-
export declare function detectFormatFromContent(filePath: string): Promise<'openapi' | 'har'>;
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spec -> Generated project pipeline.
|
|
3
|
-
*
|
|
4
|
-
* Takes an uploaded spec file (OpenAPI or HAR), runs it through the existing
|
|
5
|
-
* parser/transformer/emitter pipeline, audits the output, and returns a
|
|
6
|
-
* BuildResult with the slug and image metadata.
|
|
7
|
-
*/
|
|
8
|
-
import { randomBytes } from 'node:crypto';
|
|
9
|
-
import { mkdir, readFile, rm, access } from 'node:fs/promises';
|
|
10
|
-
import { join, extname, resolve } from 'node:path';
|
|
11
|
-
import { logger } from '../utils/logger.js';
|
|
12
|
-
import { auditGeneratedCode } from './security.js';
|
|
13
|
-
// OpenAPI pipeline imports
|
|
14
|
-
import { loadOpenApiSpec } from '../parser/openapi-loader.js';
|
|
15
|
-
import { extractOperations } from '../parser/operation-extractor.js';
|
|
16
|
-
import { detectAuthSchemes } from '../transformer/auth-detector.js';
|
|
17
|
-
import { buildAllTools } from '../transformer/tool-builder.js';
|
|
18
|
-
import { buildResources, buildPrompts } from '../transformer/resource-builder.js';
|
|
19
|
-
import { emitProject } from '../emitter/index.js';
|
|
20
|
-
// HAR pipeline imports
|
|
21
|
-
import { loadHarFile } from '../parser/har-loader.js';
|
|
22
|
-
import { filterHarEntries } from '../parser/har-filter.js';
|
|
23
|
-
import { normalizeEntry } from '../parser/har-normalizer.js';
|
|
24
|
-
import { clusterEntries } from '../transformer/har-clusterer.js';
|
|
25
|
-
import { clustersToOperations } from '../transformer/har-to-operations.js';
|
|
26
|
-
const BUILD_BASE_DIR = '/tmp/mcpmake-build';
|
|
27
|
-
/**
|
|
28
|
-
* Generate a slug from a name (or random if not provided).
|
|
29
|
-
* Format: kebab-case-name-xxxx where xxxx is 4 random hex chars.
|
|
30
|
-
*/
|
|
31
|
-
function generateSlug(name) {
|
|
32
|
-
const suffix = randomBytes(2).toString('hex');
|
|
33
|
-
if (!name) {
|
|
34
|
-
return `mcp-server-${suffix}`;
|
|
35
|
-
}
|
|
36
|
-
const base = name
|
|
37
|
-
.toLowerCase()
|
|
38
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
39
|
-
.replace(/^-|-$/g, '')
|
|
40
|
-
.slice(0, 40);
|
|
41
|
-
return base ? `${base}-${suffix}` : `mcp-server-${suffix}`;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Convert a title to a valid package name.
|
|
45
|
-
*/
|
|
46
|
-
function toPackageName(title) {
|
|
47
|
-
return title
|
|
48
|
-
.toLowerCase()
|
|
49
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
50
|
-
.replace(/^-|-$/g, '');
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Build a project from an uploaded spec file.
|
|
54
|
-
*
|
|
55
|
-
* Steps:
|
|
56
|
-
* 1. Generate slug and create temp build dir
|
|
57
|
-
* 2. Run the appropriate pipeline (OpenAPI or HAR) to generate the project
|
|
58
|
-
* 3. Audit the generated code for dangerous patterns
|
|
59
|
-
* 4. Return build result
|
|
60
|
-
*/
|
|
61
|
-
export async function buildFromSpec(specPath, opts) {
|
|
62
|
-
const slug = generateSlug(opts.name);
|
|
63
|
-
const buildDir = join(BUILD_BASE_DIR, slug);
|
|
64
|
-
// Path traversal guard: ensure buildDir stays within BUILD_BASE_DIR
|
|
65
|
-
const resolvedBuildDir = resolve(buildDir);
|
|
66
|
-
const resolvedBase = resolve(BUILD_BASE_DIR);
|
|
67
|
-
if (!resolvedBuildDir.startsWith(resolvedBase + '/')) {
|
|
68
|
-
throw new Error('Invalid build directory path');
|
|
69
|
-
}
|
|
70
|
-
await mkdir(buildDir, { recursive: true });
|
|
71
|
-
logger.info(`Build directory: ${buildDir}`);
|
|
72
|
-
let toolCount;
|
|
73
|
-
let buildSuccess = false;
|
|
74
|
-
try {
|
|
75
|
-
if (opts.format === 'openapi') {
|
|
76
|
-
toolCount = await runOpenApiPipeline(specPath, buildDir, opts.name);
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
toolCount = await runHarPipeline(specPath, buildDir, opts.name);
|
|
80
|
-
}
|
|
81
|
-
// Audit the entire generated project (not just src/)
|
|
82
|
-
const audit = await auditGeneratedCode(buildDir);
|
|
83
|
-
if (!audit.safe) {
|
|
84
|
-
const findingsList = audit.findings.join('\n - ');
|
|
85
|
-
throw new Error(`Code audit failed. Dangerous patterns found in generated code:\n - ${findingsList}`);
|
|
86
|
-
}
|
|
87
|
-
logger.info('Code audit passed');
|
|
88
|
-
buildSuccess = true;
|
|
89
|
-
}
|
|
90
|
-
finally {
|
|
91
|
-
// Clean up build directory on failure
|
|
92
|
-
if (!buildSuccess) {
|
|
93
|
-
try {
|
|
94
|
-
await rm(buildDir, { recursive: true, force: true });
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
// Ignore cleanup errors
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Detect server type from generated project dependencies
|
|
102
|
-
const serverType = await detectServerType(buildDir);
|
|
103
|
-
return {
|
|
104
|
-
slug,
|
|
105
|
-
imageName: `mcpmake/${slug}`,
|
|
106
|
-
imageTag: 'latest',
|
|
107
|
-
buildDir,
|
|
108
|
-
toolCount,
|
|
109
|
-
serverType,
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Run the OpenAPI pipeline: parse spec -> extract operations -> build tools -> emit project.
|
|
114
|
-
* Returns the number of tools generated.
|
|
115
|
-
*/
|
|
116
|
-
async function runOpenApiPipeline(specPath, outputDir, name) {
|
|
117
|
-
logger.info(`Running OpenAPI pipeline for: ${specPath}`);
|
|
118
|
-
const { api } = await loadOpenApiSpec(specPath);
|
|
119
|
-
const { operations, baseUrl, securitySchemes, info } = extractOperations(api);
|
|
120
|
-
if (operations.length === 0) {
|
|
121
|
-
throw new Error('No operations found in the OpenAPI spec');
|
|
122
|
-
}
|
|
123
|
-
logger.info(`Found ${operations.length} operations`);
|
|
124
|
-
const tools = buildAllTools(operations);
|
|
125
|
-
const { authSchemes, envVars } = detectAuthSchemes(securitySchemes);
|
|
126
|
-
const serverName = name ? toPackageName(name) : toPackageName(info.title);
|
|
127
|
-
const resolvedBaseUrl = baseUrl;
|
|
128
|
-
if (!resolvedBaseUrl) {
|
|
129
|
-
throw new Error('No base URL found in spec');
|
|
130
|
-
}
|
|
131
|
-
const resources = buildResources(operations);
|
|
132
|
-
const prompts = buildPrompts(operations);
|
|
133
|
-
const manifest = {
|
|
134
|
-
serverName,
|
|
135
|
-
serverVersion: info.version ?? '1.0.0',
|
|
136
|
-
baseUrl: resolvedBaseUrl,
|
|
137
|
-
transport: 'http', // Always HTTP for hosted servers
|
|
138
|
-
tools,
|
|
139
|
-
resources,
|
|
140
|
-
prompts,
|
|
141
|
-
authSchemes,
|
|
142
|
-
envVars: [
|
|
143
|
-
{
|
|
144
|
-
name: 'BASE_URL',
|
|
145
|
-
description: 'API base URL',
|
|
146
|
-
required: true,
|
|
147
|
-
example: resolvedBaseUrl,
|
|
148
|
-
},
|
|
149
|
-
...envVars,
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
-
await emitProject(manifest, {
|
|
153
|
-
outputDir,
|
|
154
|
-
force: true,
|
|
155
|
-
dryRun: false,
|
|
156
|
-
});
|
|
157
|
-
return tools.length;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Run the HAR pipeline: load HAR -> filter -> normalize -> cluster -> emit project.
|
|
161
|
-
* Returns the number of tools generated.
|
|
162
|
-
*/
|
|
163
|
-
async function runHarPipeline(specPath, outputDir, name) {
|
|
164
|
-
logger.info(`Running HAR pipeline for: ${specPath}`);
|
|
165
|
-
const har = await loadHarFile(specPath);
|
|
166
|
-
logger.info(`Found ${har.log.entries.length} total entries`);
|
|
167
|
-
// Filter noise (no domain restriction for hosted builds)
|
|
168
|
-
const filtered = filterHarEntries(har.log.entries, {
|
|
169
|
-
includeErrors: false,
|
|
170
|
-
});
|
|
171
|
-
if (filtered.length === 0) {
|
|
172
|
-
throw new Error('No API requests found in HAR file after filtering');
|
|
173
|
-
}
|
|
174
|
-
logger.info(`${filtered.length} API entries after filtering`);
|
|
175
|
-
// Normalize paths
|
|
176
|
-
const normalized = filtered.map(normalizeEntry);
|
|
177
|
-
// Cluster into operations
|
|
178
|
-
const clusters = clusterEntries(normalized);
|
|
179
|
-
logger.info(`Clustered into ${clusters.length} operations`);
|
|
180
|
-
// Convert to OperationDescriptors
|
|
181
|
-
const { operations, baseUrl, detectedAuth } = clustersToOperations(clusters);
|
|
182
|
-
if (operations.length === 0) {
|
|
183
|
-
throw new Error('No operations could be derived from HAR file');
|
|
184
|
-
}
|
|
185
|
-
// Build auth schemes and env vars
|
|
186
|
-
const authSchemes = [];
|
|
187
|
-
const envVars = [];
|
|
188
|
-
for (const auth of detectedAuth) {
|
|
189
|
-
if (auth.type === 'bearer') {
|
|
190
|
-
authSchemes.push({
|
|
191
|
-
type: 'http-bearer',
|
|
192
|
-
envVarName: 'BEARER_TOKEN',
|
|
193
|
-
description: 'Bearer token detected in HAR',
|
|
194
|
-
});
|
|
195
|
-
envVars.push({
|
|
196
|
-
name: 'BEARER_TOKEN',
|
|
197
|
-
description: 'Bearer authentication token',
|
|
198
|
-
required: true,
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
else if (auth.type === 'basic') {
|
|
202
|
-
authSchemes.push({
|
|
203
|
-
type: 'http-basic',
|
|
204
|
-
envVarName: 'BASIC_USERNAME',
|
|
205
|
-
description: 'Basic auth detected in HAR',
|
|
206
|
-
});
|
|
207
|
-
envVars.push({ name: 'BASIC_USERNAME', description: 'Basic auth username', required: true }, { name: 'BASIC_PASSWORD', description: 'Basic auth password', required: true });
|
|
208
|
-
}
|
|
209
|
-
else if (auth.type === 'apiKey') {
|
|
210
|
-
authSchemes.push({
|
|
211
|
-
type: 'apiKey',
|
|
212
|
-
envVarName: 'API_KEY',
|
|
213
|
-
headerName: auth.headerName,
|
|
214
|
-
in: 'header',
|
|
215
|
-
description: `API key header: ${auth.headerName}`,
|
|
216
|
-
});
|
|
217
|
-
envVars.push({
|
|
218
|
-
name: 'API_KEY',
|
|
219
|
-
description: `API key (sent as header "${auth.headerName}")`,
|
|
220
|
-
required: true,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
// Dedupe env vars
|
|
225
|
-
const seen = new Set();
|
|
226
|
-
const uniqueEnvVars = envVars.filter((v) => {
|
|
227
|
-
if (seen.has(v.name))
|
|
228
|
-
return false;
|
|
229
|
-
seen.add(v.name);
|
|
230
|
-
return true;
|
|
231
|
-
});
|
|
232
|
-
const tools = buildAllTools(operations);
|
|
233
|
-
const serverName = name ? toPackageName(name) : toPackageName(new URL(baseUrl).hostname);
|
|
234
|
-
const manifest = {
|
|
235
|
-
serverName,
|
|
236
|
-
serverVersion: '1.0.0',
|
|
237
|
-
baseUrl,
|
|
238
|
-
transport: 'http', // Always HTTP for hosted servers
|
|
239
|
-
tools,
|
|
240
|
-
authSchemes,
|
|
241
|
-
envVars: [
|
|
242
|
-
{ name: 'BASE_URL', description: 'API base URL', required: true, example: baseUrl },
|
|
243
|
-
...uniqueEnvVars,
|
|
244
|
-
],
|
|
245
|
-
};
|
|
246
|
-
await emitProject(manifest, {
|
|
247
|
-
outputDir,
|
|
248
|
-
force: true,
|
|
249
|
-
dryRun: false,
|
|
250
|
-
});
|
|
251
|
-
return tools.length;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Detect whether a generated project is a Playwright-based server
|
|
255
|
-
* by checking for a "playwright" dependency in its package.json.
|
|
256
|
-
*/
|
|
257
|
-
async function detectServerType(buildDir) {
|
|
258
|
-
const pkgPath = join(buildDir, 'package.json');
|
|
259
|
-
try {
|
|
260
|
-
await access(pkgPath);
|
|
261
|
-
const raw = await readFile(pkgPath, 'utf-8');
|
|
262
|
-
const pkg = JSON.parse(raw);
|
|
263
|
-
const allDeps = {
|
|
264
|
-
...pkg.dependencies,
|
|
265
|
-
...pkg.devDependencies,
|
|
266
|
-
};
|
|
267
|
-
if ('playwright' in allDeps || 'playwright-core' in allDeps) {
|
|
268
|
-
logger.info('Detected Playwright server from package.json dependencies');
|
|
269
|
-
return 'playwright';
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
catch {
|
|
273
|
-
// No package.json or unreadable — default to http
|
|
274
|
-
}
|
|
275
|
-
return 'http';
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Detect the spec format from a file path.
|
|
279
|
-
*/
|
|
280
|
-
export function detectFormat(filePath, contentType) {
|
|
281
|
-
const ext = extname(filePath).toLowerCase();
|
|
282
|
-
if (ext === '.har')
|
|
283
|
-
return 'har';
|
|
284
|
-
if (ext === '.yaml' || ext === '.yml' || ext === '.json') {
|
|
285
|
-
// Could be either OpenAPI JSON or HAR JSON; check content type hint
|
|
286
|
-
if (contentType?.includes('har'))
|
|
287
|
-
return 'har';
|
|
288
|
-
return 'openapi';
|
|
289
|
-
}
|
|
290
|
-
// Default to OpenAPI
|
|
291
|
-
return 'openapi';
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Detect format by inspecting file content.
|
|
295
|
-
* Useful when the file extension is ambiguous (e.g., .json).
|
|
296
|
-
*/
|
|
297
|
-
export async function detectFormatFromContent(filePath) {
|
|
298
|
-
const raw = await readFile(filePath, 'utf-8');
|
|
299
|
-
try {
|
|
300
|
-
const parsed = JSON.parse(raw);
|
|
301
|
-
// HAR files have a top-level "log" object with "entries"
|
|
302
|
-
if (parsed.log && Array.isArray(parsed.log.entries)) {
|
|
303
|
-
return 'har';
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
catch {
|
|
307
|
-
// Not valid JSON — probably YAML, assume OpenAPI
|
|
308
|
-
}
|
|
309
|
-
return 'openapi';
|
|
310
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bounded-concurrency queue for expensive build work.
|
|
3
|
-
*
|
|
4
|
-
* Each hosted-server build runs `npm install` + `docker build`, which are
|
|
5
|
-
* CPU/IO heavy. Without a limit, a burst of create requests can exhaust the
|
|
6
|
-
* host (a DoS / cryptomining vector). This serializes the heavy steps to a
|
|
7
|
-
* small number of concurrent slots; excess work waits in a FIFO queue rather
|
|
8
|
-
* than running all at once.
|
|
9
|
-
*
|
|
10
|
-
* Concurrency is `BUILD_CONCURRENCY` (default 2).
|
|
11
|
-
*/
|
|
12
|
-
export declare class BuildQueue {
|
|
13
|
-
private readonly concurrency;
|
|
14
|
-
private active;
|
|
15
|
-
private readonly waiters;
|
|
16
|
-
constructor(concurrency: number);
|
|
17
|
-
/** Number of tasks currently running. */
|
|
18
|
-
get activeCount(): number;
|
|
19
|
-
/** Number of tasks waiting for a free slot. */
|
|
20
|
-
get queuedCount(): number;
|
|
21
|
-
private acquire;
|
|
22
|
-
private release;
|
|
23
|
-
/**
|
|
24
|
-
* Run `fn` once a build slot is available, releasing the slot afterwards
|
|
25
|
-
* (even if `fn` throws).
|
|
26
|
-
*/
|
|
27
|
-
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
28
|
-
}
|
|
29
|
-
/** Shared build queue used by all create / re-deploy paths. */
|
|
30
|
-
export declare const buildQueue: BuildQueue;
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bounded-concurrency queue for expensive build work.
|
|
3
|
-
*
|
|
4
|
-
* Each hosted-server build runs `npm install` + `docker build`, which are
|
|
5
|
-
* CPU/IO heavy. Without a limit, a burst of create requests can exhaust the
|
|
6
|
-
* host (a DoS / cryptomining vector). This serializes the heavy steps to a
|
|
7
|
-
* small number of concurrent slots; excess work waits in a FIFO queue rather
|
|
8
|
-
* than running all at once.
|
|
9
|
-
*
|
|
10
|
-
* Concurrency is `BUILD_CONCURRENCY` (default 2).
|
|
11
|
-
*/
|
|
12
|
-
import { logger } from '../utils/logger.js';
|
|
13
|
-
export class BuildQueue {
|
|
14
|
-
concurrency;
|
|
15
|
-
active = 0;
|
|
16
|
-
waiters = [];
|
|
17
|
-
constructor(concurrency) {
|
|
18
|
-
this.concurrency = concurrency;
|
|
19
|
-
}
|
|
20
|
-
/** Number of tasks currently running. */
|
|
21
|
-
get activeCount() {
|
|
22
|
-
return this.active;
|
|
23
|
-
}
|
|
24
|
-
/** Number of tasks waiting for a free slot. */
|
|
25
|
-
get queuedCount() {
|
|
26
|
-
return this.waiters.length;
|
|
27
|
-
}
|
|
28
|
-
acquire() {
|
|
29
|
-
if (this.active < this.concurrency) {
|
|
30
|
-
this.active++;
|
|
31
|
-
return Promise.resolve();
|
|
32
|
-
}
|
|
33
|
-
return new Promise((resolve) => this.waiters.push(resolve));
|
|
34
|
-
}
|
|
35
|
-
release() {
|
|
36
|
-
const next = this.waiters.shift();
|
|
37
|
-
if (next) {
|
|
38
|
-
// Transfer the slot directly to the next waiter (active stays counted).
|
|
39
|
-
next();
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
this.active--;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Run `fn` once a build slot is available, releasing the slot afterwards
|
|
47
|
-
* (even if `fn` throws).
|
|
48
|
-
*/
|
|
49
|
-
async run(fn) {
|
|
50
|
-
const waitingBefore = this.queuedCount;
|
|
51
|
-
if (this.active >= this.concurrency) {
|
|
52
|
-
logger.info(`[build-queue] at capacity (${this.activeCount} active); queueing build (${waitingBefore + 1} waiting)`);
|
|
53
|
-
}
|
|
54
|
-
await this.acquire();
|
|
55
|
-
try {
|
|
56
|
-
return await fn();
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
this.release();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function parseConcurrency() {
|
|
64
|
-
const raw = parseInt(process.env.BUILD_CONCURRENCY ?? '2', 10);
|
|
65
|
-
if (!Number.isFinite(raw) || raw < 1)
|
|
66
|
-
return 2;
|
|
67
|
-
return Math.min(raw, 16);
|
|
68
|
-
}
|
|
69
|
-
/** Shared build queue used by all create / re-deploy paths. */
|
|
70
|
-
export const buildQueue = new BuildQueue(parseConcurrency());
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dynamic reverse proxy routing via Caddy Admin API.
|
|
3
|
-
*
|
|
4
|
-
* Manages routes that map {slug}.{domain} -> localhost:{port}.
|
|
5
|
-
* Caddy Admin API is assumed to run at http://localhost:2019.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Add a route: {slug}.{domain} -> localhost:{port}
|
|
9
|
-
*/
|
|
10
|
-
export declare function addRoute(slug: string, port: number, domain: string): Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Remove a route by slug.
|
|
13
|
-
*/
|
|
14
|
-
export declare function removeRoute(slug: string, _domain: string): Promise<void>;
|
|
15
|
-
/**
|
|
16
|
-
* Check if a route exists for a given slug.
|
|
17
|
-
*/
|
|
18
|
-
export declare function routeExists(slug: string, _domain: string): Promise<boolean>;
|