mcpmake 0.1.0 → 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 +24 -42
- 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,336 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PostgreSQL-backed store implementations.
|
|
3
|
-
*
|
|
4
|
-
* Drop-in replacements for the in-memory ServerStore, UserStore, and
|
|
5
|
-
* SessionStore. Every query is parameterized.
|
|
6
|
-
*/
|
|
7
|
-
import { randomBytes } from 'node:crypto';
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
// Helpers
|
|
10
|
-
// ---------------------------------------------------------------------------
|
|
11
|
-
const SESSION_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
// PgServerStore
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
export class PgServerStore {
|
|
16
|
-
db;
|
|
17
|
-
constructor(db) {
|
|
18
|
-
this.db = db;
|
|
19
|
-
}
|
|
20
|
-
async create(record) {
|
|
21
|
-
const existing = await this.get(record.slug);
|
|
22
|
-
if (existing) {
|
|
23
|
-
throw new Error(`Server with slug "${record.slug}" already exists`);
|
|
24
|
-
}
|
|
25
|
-
await this.db.query(`INSERT INTO servers (slug, user_id, spec_hash, container_id, port, bearer_token, status, tool_count, created_at, last_active_at)
|
|
26
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
|
|
27
|
-
record.slug,
|
|
28
|
-
record.userId ?? null,
|
|
29
|
-
record.specHash,
|
|
30
|
-
record.containerId,
|
|
31
|
-
record.port,
|
|
32
|
-
record.bearerToken,
|
|
33
|
-
record.status,
|
|
34
|
-
record.toolCount,
|
|
35
|
-
record.createdAt,
|
|
36
|
-
record.lastActiveAt,
|
|
37
|
-
]);
|
|
38
|
-
}
|
|
39
|
-
async get(slug) {
|
|
40
|
-
const { rows } = await this.db.query('SELECT * FROM servers WHERE slug = $1', [slug]);
|
|
41
|
-
return rows.length > 0 ? rowToServerRecord(rows[0]) : undefined;
|
|
42
|
-
}
|
|
43
|
-
async list() {
|
|
44
|
-
const { rows } = await this.db.query('SELECT * FROM servers ORDER BY created_at DESC');
|
|
45
|
-
return rows.map(rowToServerRecord);
|
|
46
|
-
}
|
|
47
|
-
async update(slug, updates) {
|
|
48
|
-
const existing = await this.get(slug);
|
|
49
|
-
if (!existing) {
|
|
50
|
-
throw new Error(`Server with slug "${slug}" not found`);
|
|
51
|
-
}
|
|
52
|
-
// Build SET clause from non-slug fields
|
|
53
|
-
const { slug: _ignored, ...safeUpdates } = updates;
|
|
54
|
-
const fieldMap = {
|
|
55
|
-
userId: 'user_id',
|
|
56
|
-
specHash: 'spec_hash',
|
|
57
|
-
containerId: 'container_id',
|
|
58
|
-
port: 'port',
|
|
59
|
-
bearerToken: 'bearer_token',
|
|
60
|
-
status: 'status',
|
|
61
|
-
toolCount: 'tool_count',
|
|
62
|
-
createdAt: 'created_at',
|
|
63
|
-
lastActiveAt: 'last_active_at',
|
|
64
|
-
};
|
|
65
|
-
const setClauses = [];
|
|
66
|
-
const values = [];
|
|
67
|
-
let paramIndex = 1;
|
|
68
|
-
for (const [key, value] of Object.entries(safeUpdates)) {
|
|
69
|
-
const column = fieldMap[key];
|
|
70
|
-
if (column && value !== undefined) {
|
|
71
|
-
setClauses.push(`${column} = $${paramIndex}`);
|
|
72
|
-
values.push(value);
|
|
73
|
-
paramIndex++;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (setClauses.length === 0)
|
|
77
|
-
return;
|
|
78
|
-
values.push(slug);
|
|
79
|
-
await this.db.query(`UPDATE servers SET ${setClauses.join(', ')} WHERE slug = $${paramIndex}`, values);
|
|
80
|
-
}
|
|
81
|
-
async delete(slug) {
|
|
82
|
-
await this.db.query('DELETE FROM servers WHERE slug = $1', [slug]);
|
|
83
|
-
}
|
|
84
|
-
async findByPort(port) {
|
|
85
|
-
const { rows } = await this.db.query('SELECT * FROM servers WHERE port = $1 LIMIT 1', [
|
|
86
|
-
port,
|
|
87
|
-
]);
|
|
88
|
-
return rows.length > 0 ? rowToServerRecord(rows[0]) : undefined;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
function rowToServerRecord(row) {
|
|
92
|
-
return {
|
|
93
|
-
slug: row.slug,
|
|
94
|
-
userId: row.user_id ?? undefined,
|
|
95
|
-
specHash: row.spec_hash,
|
|
96
|
-
containerId: row.container_id,
|
|
97
|
-
port: row.port,
|
|
98
|
-
bearerToken: row.bearer_token,
|
|
99
|
-
status: row.status,
|
|
100
|
-
toolCount: row.tool_count,
|
|
101
|
-
createdAt: typeof row.created_at === 'string' ? row.created_at : new Date(row.created_at).toISOString(),
|
|
102
|
-
lastActiveAt: typeof row.last_active_at === 'string'
|
|
103
|
-
? row.last_active_at
|
|
104
|
-
: new Date(row.last_active_at).toISOString(),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
// PgUserStore
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
export class PgUserStore {
|
|
111
|
-
db;
|
|
112
|
-
constructor(db) {
|
|
113
|
-
this.db = db;
|
|
114
|
-
}
|
|
115
|
-
async create(record) {
|
|
116
|
-
const normalizedEmail = record.email.toLowerCase().trim();
|
|
117
|
-
const existing = await this.getByEmail(normalizedEmail);
|
|
118
|
-
if (existing) {
|
|
119
|
-
throw new Error(`User with email "${normalizedEmail}" already exists`);
|
|
120
|
-
}
|
|
121
|
-
await this.db.query(`INSERT INTO users (id, email, password_hash, plan, is_admin, created_at, last_login_at, stripe_customer_id)
|
|
122
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`, [
|
|
123
|
-
record.id,
|
|
124
|
-
normalizedEmail,
|
|
125
|
-
record.passwordHash,
|
|
126
|
-
record.plan,
|
|
127
|
-
record.isAdmin,
|
|
128
|
-
record.createdAt,
|
|
129
|
-
record.lastLoginAt,
|
|
130
|
-
record.stripeCustomerId ?? null,
|
|
131
|
-
]);
|
|
132
|
-
}
|
|
133
|
-
async getById(id) {
|
|
134
|
-
const { rows } = await this.db.query('SELECT * FROM users WHERE id = $1', [id]);
|
|
135
|
-
return rows.length > 0 ? rowToUserRecord(rows[0]) : undefined;
|
|
136
|
-
}
|
|
137
|
-
async getByEmail(email) {
|
|
138
|
-
const normalized = email.toLowerCase().trim();
|
|
139
|
-
const { rows } = await this.db.query('SELECT * FROM users WHERE email = $1', [normalized]);
|
|
140
|
-
return rows.length > 0 ? rowToUserRecord(rows[0]) : undefined;
|
|
141
|
-
}
|
|
142
|
-
async list() {
|
|
143
|
-
const { rows } = await this.db.query('SELECT * FROM users ORDER BY created_at DESC');
|
|
144
|
-
return rows.map(rowToUserRecord);
|
|
145
|
-
}
|
|
146
|
-
async update(id, updates) {
|
|
147
|
-
const existing = await this.getById(id);
|
|
148
|
-
if (!existing) {
|
|
149
|
-
throw new Error(`User with id "${id}" not found`);
|
|
150
|
-
}
|
|
151
|
-
const { id: _ignored, ...safeUpdates } = updates;
|
|
152
|
-
const fieldMap = {
|
|
153
|
-
email: 'email',
|
|
154
|
-
passwordHash: 'password_hash',
|
|
155
|
-
plan: 'plan',
|
|
156
|
-
isAdmin: 'is_admin',
|
|
157
|
-
createdAt: 'created_at',
|
|
158
|
-
lastLoginAt: 'last_login_at',
|
|
159
|
-
stripeCustomerId: 'stripe_customer_id',
|
|
160
|
-
passwordResetTokenHash: 'password_reset_token_hash',
|
|
161
|
-
passwordResetExpiresAt: 'password_reset_expires_at',
|
|
162
|
-
emailVerified: 'email_verified',
|
|
163
|
-
emailVerificationTokenHash: 'email_verification_token_hash',
|
|
164
|
-
subscriptionPeriodEnd: 'subscription_period_end',
|
|
165
|
-
creditsCalls: 'credits_calls',
|
|
166
|
-
};
|
|
167
|
-
const setClauses = [];
|
|
168
|
-
const values = [];
|
|
169
|
-
let paramIndex = 1;
|
|
170
|
-
for (const [key, value] of Object.entries(safeUpdates)) {
|
|
171
|
-
const column = fieldMap[key];
|
|
172
|
-
if (column && value !== undefined) {
|
|
173
|
-
setClauses.push(`${column} = $${paramIndex}`);
|
|
174
|
-
values.push(value);
|
|
175
|
-
paramIndex++;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (setClauses.length === 0)
|
|
179
|
-
return;
|
|
180
|
-
values.push(id);
|
|
181
|
-
await this.db.query(`UPDATE users SET ${setClauses.join(', ')} WHERE id = $${paramIndex}`, values);
|
|
182
|
-
}
|
|
183
|
-
async getByResetToken(tokenHash) {
|
|
184
|
-
const { rows } = await this.db.query('SELECT * FROM users WHERE password_reset_token_hash = $1 AND password_reset_expires_at > $2', [tokenHash, Date.now()]);
|
|
185
|
-
return rows.length > 0 ? rowToUserRecord(rows[0]) : undefined;
|
|
186
|
-
}
|
|
187
|
-
async getByVerificationToken(tokenHash) {
|
|
188
|
-
const { rows } = await this.db.query('SELECT * FROM users WHERE email_verification_token_hash = $1', [tokenHash]);
|
|
189
|
-
return rows.length > 0 ? rowToUserRecord(rows[0]) : undefined;
|
|
190
|
-
}
|
|
191
|
-
async getByStripeCustomerId(customerId) {
|
|
192
|
-
const { rows } = await this.db.query('SELECT * FROM users WHERE stripe_customer_id = $1 LIMIT 1', [customerId]);
|
|
193
|
-
return rows.length > 0 ? rowToUserRecord(rows[0]) : undefined;
|
|
194
|
-
}
|
|
195
|
-
async clearResetToken(id) {
|
|
196
|
-
await this.db.query('UPDATE users SET password_reset_token_hash = NULL, password_reset_expires_at = NULL WHERE id = $1', [id]);
|
|
197
|
-
}
|
|
198
|
-
async clearVerificationToken(id) {
|
|
199
|
-
await this.db.query('UPDATE users SET email_verification_token_hash = NULL WHERE id = $1', [
|
|
200
|
-
id,
|
|
201
|
-
]);
|
|
202
|
-
}
|
|
203
|
-
async delete(id) {
|
|
204
|
-
await this.db.query('DELETE FROM users WHERE id = $1', [id]);
|
|
205
|
-
}
|
|
206
|
-
async count() {
|
|
207
|
-
const { rows } = await this.db.query('SELECT count(*) AS count FROM users');
|
|
208
|
-
return parseInt(rows[0].count, 10);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
function rowToUserRecord(row) {
|
|
212
|
-
const record = {
|
|
213
|
-
id: row.id,
|
|
214
|
-
email: row.email,
|
|
215
|
-
passwordHash: row.password_hash,
|
|
216
|
-
plan: row.plan,
|
|
217
|
-
isAdmin: row.is_admin,
|
|
218
|
-
createdAt: typeof row.created_at === 'string' ? row.created_at : new Date(row.created_at).toISOString(),
|
|
219
|
-
lastLoginAt: typeof row.last_login_at === 'string'
|
|
220
|
-
? row.last_login_at
|
|
221
|
-
: new Date(row.last_login_at).toISOString(),
|
|
222
|
-
};
|
|
223
|
-
if (row.stripe_customer_id) {
|
|
224
|
-
record.stripeCustomerId = row.stripe_customer_id;
|
|
225
|
-
}
|
|
226
|
-
if (row.password_reset_token_hash) {
|
|
227
|
-
record.passwordResetTokenHash = row.password_reset_token_hash;
|
|
228
|
-
}
|
|
229
|
-
if (row.password_reset_expires_at != null) {
|
|
230
|
-
record.passwordResetExpiresAt =
|
|
231
|
-
typeof row.password_reset_expires_at === 'number'
|
|
232
|
-
? row.password_reset_expires_at
|
|
233
|
-
: Number(row.password_reset_expires_at);
|
|
234
|
-
}
|
|
235
|
-
if (row.email_verified != null) {
|
|
236
|
-
record.emailVerified = row.email_verified === true || row.email_verified === 't';
|
|
237
|
-
}
|
|
238
|
-
if (row.email_verification_token_hash) {
|
|
239
|
-
record.emailVerificationTokenHash = row.email_verification_token_hash;
|
|
240
|
-
}
|
|
241
|
-
if (row.subscription_period_end != null) {
|
|
242
|
-
record.subscriptionPeriodEnd =
|
|
243
|
-
typeof row.subscription_period_end === 'number'
|
|
244
|
-
? row.subscription_period_end
|
|
245
|
-
: Number(row.subscription_period_end);
|
|
246
|
-
}
|
|
247
|
-
if (row.credits_calls != null) {
|
|
248
|
-
record.creditsCalls =
|
|
249
|
-
typeof row.credits_calls === 'number' ? row.credits_calls : Number(row.credits_calls);
|
|
250
|
-
}
|
|
251
|
-
return record;
|
|
252
|
-
}
|
|
253
|
-
// ---------------------------------------------------------------------------
|
|
254
|
-
// PgSessionStore
|
|
255
|
-
// ---------------------------------------------------------------------------
|
|
256
|
-
export class PgSessionStore {
|
|
257
|
-
db;
|
|
258
|
-
constructor(db) {
|
|
259
|
-
this.db = db;
|
|
260
|
-
}
|
|
261
|
-
async create(userId) {
|
|
262
|
-
const token = randomBytes(32).toString('hex');
|
|
263
|
-
const csrfToken = randomBytes(16).toString('hex');
|
|
264
|
-
const now = Date.now();
|
|
265
|
-
const session = {
|
|
266
|
-
token,
|
|
267
|
-
userId,
|
|
268
|
-
csrfToken,
|
|
269
|
-
createdAt: now,
|
|
270
|
-
expiresAt: now + SESSION_MAX_AGE_MS,
|
|
271
|
-
};
|
|
272
|
-
await this.db.query(`INSERT INTO sessions (token, user_id, csrf_token, created_at, expires_at, pending_server_tokens)
|
|
273
|
-
VALUES ($1, $2, $3, $4, $5, $6)`, [token, userId, csrfToken, now, session.expiresAt, null]);
|
|
274
|
-
return session;
|
|
275
|
-
}
|
|
276
|
-
async get(token) {
|
|
277
|
-
const { rows } = await this.db.query('SELECT * FROM sessions WHERE token = $1', [token]);
|
|
278
|
-
if (rows.length === 0)
|
|
279
|
-
return undefined;
|
|
280
|
-
const session = rowToSessionRecord(rows[0]);
|
|
281
|
-
// Check expiry
|
|
282
|
-
if (Date.now() > session.expiresAt) {
|
|
283
|
-
await this.destroy(token);
|
|
284
|
-
return undefined;
|
|
285
|
-
}
|
|
286
|
-
return session;
|
|
287
|
-
}
|
|
288
|
-
async destroy(token) {
|
|
289
|
-
await this.db.query('DELETE FROM sessions WHERE token = $1', [token]);
|
|
290
|
-
}
|
|
291
|
-
async setPendingServerToken(sessionToken, slug, serverBearerToken) {
|
|
292
|
-
const session = await this.get(sessionToken);
|
|
293
|
-
if (!session)
|
|
294
|
-
return;
|
|
295
|
-
const pending = session.pendingServerTokens ?? {};
|
|
296
|
-
pending[slug] = serverBearerToken;
|
|
297
|
-
await this.db.query('UPDATE sessions SET pending_server_tokens = $1 WHERE token = $2', [
|
|
298
|
-
JSON.stringify(pending),
|
|
299
|
-
sessionToken,
|
|
300
|
-
]);
|
|
301
|
-
}
|
|
302
|
-
async consumePendingServerToken(sessionToken, slug) {
|
|
303
|
-
const session = await this.get(sessionToken);
|
|
304
|
-
if (!session?.pendingServerTokens)
|
|
305
|
-
return undefined;
|
|
306
|
-
const value = session.pendingServerTokens[slug];
|
|
307
|
-
if (value === undefined)
|
|
308
|
-
return undefined;
|
|
309
|
-
delete session.pendingServerTokens[slug];
|
|
310
|
-
const remaining = Object.keys(session.pendingServerTokens).length > 0
|
|
311
|
-
? JSON.stringify(session.pendingServerTokens)
|
|
312
|
-
: null;
|
|
313
|
-
await this.db.query('UPDATE sessions SET pending_server_tokens = $1 WHERE token = $2', [
|
|
314
|
-
remaining,
|
|
315
|
-
sessionToken,
|
|
316
|
-
]);
|
|
317
|
-
return value;
|
|
318
|
-
}
|
|
319
|
-
async cleanup() {
|
|
320
|
-
await this.db.query('DELETE FROM sessions WHERE expires_at < $1', [Date.now()]);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
function rowToSessionRecord(row) {
|
|
324
|
-
const pending = row.pending_server_tokens;
|
|
325
|
-
const record = {
|
|
326
|
-
token: row.token,
|
|
327
|
-
userId: row.user_id,
|
|
328
|
-
csrfToken: row.csrf_token,
|
|
329
|
-
createdAt: typeof row.created_at === 'number' ? row.created_at : Number(row.created_at),
|
|
330
|
-
expiresAt: typeof row.expires_at === 'number' ? row.expires_at : Number(row.expires_at),
|
|
331
|
-
};
|
|
332
|
-
if (pending && typeof pending === 'object' && Object.keys(pending).length > 0) {
|
|
333
|
-
record.pendingServerTokens = pending;
|
|
334
|
-
}
|
|
335
|
-
return record;
|
|
336
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory API failure-rate tracker.
|
|
3
|
-
*
|
|
4
|
-
* Counts completed HTTP responses by status class in a rolling ring of
|
|
5
|
-
* per-minute buckets. There is no single response choke point in the server
|
|
6
|
-
* (responses complete via sendJson, sendHtml, redirects, SSE, and the proxy
|
|
7
|
-
* stream), so this is fed once per response from the `res` `'finish'`/`'close'`
|
|
8
|
-
* events in the server callback — never from the individual writers.
|
|
9
|
-
*
|
|
10
|
-
* Purely in-memory and bounded (one bucket per minute, capped window). Longer
|
|
11
|
-
* history is derived by the periodic sampler into `metric_samples`.
|
|
12
|
-
*/
|
|
13
|
-
/** Aggregate counts over a window. */
|
|
14
|
-
export interface FailureWindow {
|
|
15
|
-
total: number;
|
|
16
|
-
c2xx: number;
|
|
17
|
-
c3xx: number;
|
|
18
|
-
c4xx: number;
|
|
19
|
-
c5xx: number;
|
|
20
|
-
/** Responses that never finished (client abort / destroyed socket). */
|
|
21
|
-
aborted: number;
|
|
22
|
-
/** (5xx + aborted) / total, as a percentage. The "is the API healthy" number. */
|
|
23
|
-
serverErrorRatePct: number;
|
|
24
|
-
/** 4xx / total, as a percentage. Client errors (often expected). */
|
|
25
|
-
clientErrorRatePct: number;
|
|
26
|
-
}
|
|
27
|
-
/** One per-minute point, for sparklines. */
|
|
28
|
-
export interface FailurePoint {
|
|
29
|
-
/** Minute index (epoch ms / 60000). */
|
|
30
|
-
minute: number;
|
|
31
|
-
total: number;
|
|
32
|
-
errors: number;
|
|
33
|
-
}
|
|
34
|
-
export declare class FailureTracker {
|
|
35
|
-
private buckets;
|
|
36
|
-
private static minute;
|
|
37
|
-
private bucket;
|
|
38
|
-
/** Record a completed response by its final status code. */
|
|
39
|
-
record(statusCode: number, now?: number): void;
|
|
40
|
-
/** Record a response that never finished (aborted / destroyed). */
|
|
41
|
-
recordAborted(now?: number): void;
|
|
42
|
-
/** Drop buckets older than the retained window. */
|
|
43
|
-
prune(now?: number): void;
|
|
44
|
-
/** Aggregate counts over the last `minutes` minutes (default 60). */
|
|
45
|
-
window(minutes?: number, now?: number): FailureWindow;
|
|
46
|
-
/**
|
|
47
|
-
* Per-minute series over the last `minutes` minutes (oldest first), with empty
|
|
48
|
-
* minutes filled as zero so a sparkline has a stable x-axis.
|
|
49
|
-
*/
|
|
50
|
-
series(minutes?: number, now?: number): FailurePoint[];
|
|
51
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-memory API failure-rate tracker.
|
|
3
|
-
*
|
|
4
|
-
* Counts completed HTTP responses by status class in a rolling ring of
|
|
5
|
-
* per-minute buckets. There is no single response choke point in the server
|
|
6
|
-
* (responses complete via sendJson, sendHtml, redirects, SSE, and the proxy
|
|
7
|
-
* stream), so this is fed once per response from the `res` `'finish'`/`'close'`
|
|
8
|
-
* events in the server callback — never from the individual writers.
|
|
9
|
-
*
|
|
10
|
-
* Purely in-memory and bounded (one bucket per minute, capped window). Longer
|
|
11
|
-
* history is derived by the periodic sampler into `metric_samples`.
|
|
12
|
-
*/
|
|
13
|
-
const WINDOW_MINUTES = 60;
|
|
14
|
-
/** Keep a little slack beyond the window so reads never race a prune. */
|
|
15
|
-
const MAX_BUCKETS = WINDOW_MINUTES + 5;
|
|
16
|
-
function emptyBucket() {
|
|
17
|
-
return { c2xx: 0, c3xx: 0, c4xx: 0, c5xx: 0, aborted: 0 };
|
|
18
|
-
}
|
|
19
|
-
export class FailureTracker {
|
|
20
|
-
buckets = new Map();
|
|
21
|
-
static minute(now) {
|
|
22
|
-
return Math.floor(now / 60_000);
|
|
23
|
-
}
|
|
24
|
-
bucket(now) {
|
|
25
|
-
const m = FailureTracker.minute(now);
|
|
26
|
-
let b = this.buckets.get(m);
|
|
27
|
-
if (!b) {
|
|
28
|
-
b = emptyBucket();
|
|
29
|
-
this.buckets.set(m, b);
|
|
30
|
-
// Bound memory: drop buckets older than the retained window.
|
|
31
|
-
if (this.buckets.size > MAX_BUCKETS)
|
|
32
|
-
this.prune(now);
|
|
33
|
-
}
|
|
34
|
-
return b;
|
|
35
|
-
}
|
|
36
|
-
/** Record a completed response by its final status code. */
|
|
37
|
-
record(statusCode, now = Date.now()) {
|
|
38
|
-
const b = this.bucket(now);
|
|
39
|
-
if (statusCode >= 500)
|
|
40
|
-
b.c5xx++;
|
|
41
|
-
else if (statusCode >= 400)
|
|
42
|
-
b.c4xx++;
|
|
43
|
-
else if (statusCode >= 300)
|
|
44
|
-
b.c3xx++;
|
|
45
|
-
else
|
|
46
|
-
b.c2xx++;
|
|
47
|
-
}
|
|
48
|
-
/** Record a response that never finished (aborted / destroyed). */
|
|
49
|
-
recordAborted(now = Date.now()) {
|
|
50
|
-
this.bucket(now).aborted++;
|
|
51
|
-
}
|
|
52
|
-
/** Drop buckets older than the retained window. */
|
|
53
|
-
prune(now = Date.now()) {
|
|
54
|
-
const cutoff = FailureTracker.minute(now) - MAX_BUCKETS;
|
|
55
|
-
for (const m of this.buckets.keys()) {
|
|
56
|
-
if (m < cutoff)
|
|
57
|
-
this.buckets.delete(m);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/** Aggregate counts over the last `minutes` minutes (default 60). */
|
|
61
|
-
window(minutes = WINDOW_MINUTES, now = Date.now()) {
|
|
62
|
-
const cutoff = FailureTracker.minute(now) - (minutes - 1);
|
|
63
|
-
const agg = emptyBucket();
|
|
64
|
-
for (const [m, b] of this.buckets) {
|
|
65
|
-
if (m < cutoff)
|
|
66
|
-
continue;
|
|
67
|
-
agg.c2xx += b.c2xx;
|
|
68
|
-
agg.c3xx += b.c3xx;
|
|
69
|
-
agg.c4xx += b.c4xx;
|
|
70
|
-
agg.c5xx += b.c5xx;
|
|
71
|
-
agg.aborted += b.aborted;
|
|
72
|
-
}
|
|
73
|
-
const total = agg.c2xx + agg.c3xx + agg.c4xx + agg.c5xx + agg.aborted;
|
|
74
|
-
const pct = (n) => (total > 0 ? (n / total) * 100 : 0);
|
|
75
|
-
return {
|
|
76
|
-
total,
|
|
77
|
-
c2xx: agg.c2xx,
|
|
78
|
-
c3xx: agg.c3xx,
|
|
79
|
-
c4xx: agg.c4xx,
|
|
80
|
-
c5xx: agg.c5xx,
|
|
81
|
-
aborted: agg.aborted,
|
|
82
|
-
serverErrorRatePct: pct(agg.c5xx + agg.aborted),
|
|
83
|
-
clientErrorRatePct: pct(agg.c4xx),
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Per-minute series over the last `minutes` minutes (oldest first), with empty
|
|
88
|
-
* minutes filled as zero so a sparkline has a stable x-axis.
|
|
89
|
-
*/
|
|
90
|
-
series(minutes = WINDOW_MINUTES, now = Date.now()) {
|
|
91
|
-
const end = FailureTracker.minute(now);
|
|
92
|
-
const start = end - (minutes - 1);
|
|
93
|
-
const out = [];
|
|
94
|
-
for (let m = start; m <= end; m++) {
|
|
95
|
-
const b = this.buckets.get(m);
|
|
96
|
-
const total = b ? b.c2xx + b.c3xx + b.c4xx + b.c5xx + b.aborted : 0;
|
|
97
|
-
const errors = b ? b.c5xx + b.aborted : 0;
|
|
98
|
-
out.push({ minute: m, total, errors });
|
|
99
|
-
}
|
|
100
|
-
return out;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Monitors container activity and stops idle containers.
|
|
3
|
-
*
|
|
4
|
-
* Runs a periodic check (default: every 60s) and stops containers
|
|
5
|
-
* that haven't had activity for longer than the configured threshold.
|
|
6
|
-
*/
|
|
7
|
-
import type { ServerStore } from './store.js';
|
|
8
|
-
export interface IdleMonitorOptions {
|
|
9
|
-
/** Check interval in ms (default: 60_000 = 1 minute) */
|
|
10
|
-
checkIntervalMs?: number;
|
|
11
|
-
/** Idle threshold in ms for http servers (default: 3_600_000 = 1 hour) */
|
|
12
|
-
idleThresholdMs?: number;
|
|
13
|
-
/** Idle threshold in ms for Playwright servers (default: 1_800_000 = 30 minutes) */
|
|
14
|
-
playwrightIdleThresholdMs?: number;
|
|
15
|
-
/** Domain for Caddy route removal */
|
|
16
|
-
domain?: string;
|
|
17
|
-
}
|
|
18
|
-
export declare class IdleMonitor {
|
|
19
|
-
private readonly store;
|
|
20
|
-
private readonly portRelease;
|
|
21
|
-
private timer;
|
|
22
|
-
private readonly checkIntervalMs;
|
|
23
|
-
private readonly idleThresholdMs;
|
|
24
|
-
private readonly playwrightIdleThresholdMs;
|
|
25
|
-
private readonly domain;
|
|
26
|
-
constructor(store: ServerStore, portRelease: (port: number) => void, options?: IdleMonitorOptions);
|
|
27
|
-
start(): void;
|
|
28
|
-
stop(): void;
|
|
29
|
-
check(): Promise<number>;
|
|
30
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Monitors container activity and stops idle containers.
|
|
3
|
-
*
|
|
4
|
-
* Runs a periodic check (default: every 60s) and stops containers
|
|
5
|
-
* that haven't had activity for longer than the configured threshold.
|
|
6
|
-
*/
|
|
7
|
-
import { logger } from '../utils/logger.js';
|
|
8
|
-
import { getContainerBackend } from './container-backend.js';
|
|
9
|
-
export class IdleMonitor {
|
|
10
|
-
store;
|
|
11
|
-
portRelease;
|
|
12
|
-
timer;
|
|
13
|
-
checkIntervalMs;
|
|
14
|
-
idleThresholdMs;
|
|
15
|
-
playwrightIdleThresholdMs;
|
|
16
|
-
domain;
|
|
17
|
-
constructor(store, portRelease, options = {}) {
|
|
18
|
-
this.store = store;
|
|
19
|
-
this.portRelease = portRelease;
|
|
20
|
-
this.checkIntervalMs = options.checkIntervalMs ?? 60_000;
|
|
21
|
-
this.idleThresholdMs = options.idleThresholdMs ?? 3_600_000;
|
|
22
|
-
// Playwright containers use more resources, so default to a shorter idle timeout
|
|
23
|
-
this.playwrightIdleThresholdMs = options.playwrightIdleThresholdMs ?? 1_800_000;
|
|
24
|
-
this.domain = options.domain ?? 'mcpmake.dev';
|
|
25
|
-
}
|
|
26
|
-
start() {
|
|
27
|
-
if (this.timer)
|
|
28
|
-
return;
|
|
29
|
-
logger.info(`Idle monitor started (check every ${this.checkIntervalMs / 1000}s, threshold ${this.idleThresholdMs / 1000}s)`);
|
|
30
|
-
this.timer = setInterval(() => this.check(), this.checkIntervalMs);
|
|
31
|
-
this.timer.unref(); // Don't prevent process exit
|
|
32
|
-
}
|
|
33
|
-
stop() {
|
|
34
|
-
if (this.timer) {
|
|
35
|
-
clearInterval(this.timer);
|
|
36
|
-
this.timer = undefined;
|
|
37
|
-
logger.info('Idle monitor stopped');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
async check() {
|
|
41
|
-
const now = Date.now();
|
|
42
|
-
const servers = this.store.list();
|
|
43
|
-
let stoppedCount = 0;
|
|
44
|
-
for (const server of servers) {
|
|
45
|
-
if (server.status !== 'running')
|
|
46
|
-
continue;
|
|
47
|
-
const lastActive = new Date(server.lastActiveAt).getTime();
|
|
48
|
-
const idleMs = now - lastActive;
|
|
49
|
-
const threshold = server.serverType === 'playwright' ? this.playwrightIdleThresholdMs : this.idleThresholdMs;
|
|
50
|
-
if (idleMs > threshold) {
|
|
51
|
-
logger.info(`Stopping idle server: ${server.slug} (idle for ${Math.round(idleMs / 60_000)}min)`);
|
|
52
|
-
try {
|
|
53
|
-
await getContainerBackend().stopContainer(server.containerId);
|
|
54
|
-
}
|
|
55
|
-
catch (err) {
|
|
56
|
-
logger.warn(`Failed to stop container for ${server.slug}: ${err}`);
|
|
57
|
-
}
|
|
58
|
-
// No Caddy route to remove — routing is backend-authoritative; the
|
|
59
|
-
// backend returns 503 for requests to a stopped server.
|
|
60
|
-
this.portRelease(server.port);
|
|
61
|
-
this.store.update(server.slug, { status: 'stopped' });
|
|
62
|
-
stoppedCount++;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (stoppedCount > 0) {
|
|
66
|
-
logger.info(`Idle monitor: stopped ${stoppedCount} idle server(s)`);
|
|
67
|
-
}
|
|
68
|
-
return stoppedCount;
|
|
69
|
-
}
|
|
70
|
-
}
|
package/dist/cloud/mailer.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal email sender for transactional emails (password reset, etc.).
|
|
3
|
-
*
|
|
4
|
-
* Supports two modes:
|
|
5
|
-
* 1. Console (default) — logs the email to stdout. Useful for development
|
|
6
|
-
* and self-hosted instances without email infrastructure.
|
|
7
|
-
* 2. SMTP — sends via an external SMTP relay. Requires env vars:
|
|
8
|
-
* SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM
|
|
9
|
-
*
|
|
10
|
-
* Uses Node.js built-in `net`/`tls` modules — zero external dependencies.
|
|
11
|
-
*/
|
|
12
|
-
export interface MailOptions {
|
|
13
|
-
to: string;
|
|
14
|
-
subject: string;
|
|
15
|
-
text: string;
|
|
16
|
-
html?: string;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Send an email. Falls back to console logging when SMTP is not configured.
|
|
20
|
-
*/
|
|
21
|
-
export declare function sendMail(options: MailOptions): Promise<void>;
|