@amerged/ohmyhost-mcp 0.1.26 → 0.1.28

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/index.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire as __ohmyhostCreateRequire } from "node:module"; const re
2
2
  import {
3
3
  createOhmyhostMcpServer,
4
4
  ohmyhostMcpHandler
5
- } from "./chunk-K57THTQL.js";
5
+ } from "./chunk-GIP4M4CK.js";
6
6
  export {
7
7
  createOhmyhostMcpServer,
8
8
  ohmyhostMcpHandler
@@ -27,7 +27,7 @@ import {
27
27
  serverIdentityOf,
28
28
  setNegotiatedProtocolVersion,
29
29
  validateEnvelopeMeta
30
- } from "./chunk-K57THTQL.js";
30
+ } from "./chunk-GIP4M4CK.js";
31
31
 
32
32
  // node_modules/.pnpm/@modelcontextprotocol+server@2.0.0/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
33
33
  var StdioServerTransport = class {
@@ -812,7 +812,7 @@ function exact2(v, keys) {
812
812
  // apps/product-cli/package.json
813
813
  var package_default2 = {
814
814
  name: "@ohmyhost/product-cli",
815
- version: "0.1.26",
815
+ version: "0.1.28",
816
816
  private: true,
817
817
  ohmyhost: {
818
818
  deployment: "production",
@@ -1906,7 +1906,7 @@ var ALL_LOCKFILES = Object.freeze(Object.values(PACKAGE_MANAGER_CONTRACTS).flatM
1906
1906
  var VERIFIED_VERSIONS = Object.freeze({
1907
1907
  vite: /* @__PURE__ */ new Set(["5.4.21", "8.0.16", "8.2.2"]),
1908
1908
  "tanstack-start": /* @__PURE__ */ new Set(["1.168.26", "1.168.49"]),
1909
- nextjs: /* @__PURE__ */ new Set(["15.5.23", "16.3.2"])
1909
+ nextjs: /* @__PURE__ */ new Set(["15.5.26", "16.3.7"])
1910
1910
  });
1911
1911
  var PUSH_AND_PLAN = "Commit and push the change, then plan the new commit.";
1912
1912
  var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
@@ -2431,6 +2431,12 @@ var PROBLEM_POLICY = {
2431
2431
  restAction: "A live project connection already holds this hostname. Remove it from the project you manage with 'ohmyhost domain paid delete --project ULID --hostname HOST --yes --json', then apply it to the new project. Repeating while that connection remains cannot work; report the request_id if you cannot manage its owning project.",
2432
2432
  title: "Domain hostname is already connected"
2433
2433
  },
2434
+ domain_dns_conflict: {
2435
+ status: 409,
2436
+ retryable: false,
2437
+ action: "Review DNS for the requested hostname and resolve the conflicting delegation or another project's managed records. Preserve mailbox MX, TXT and CAA records. Then repeat the original domain paid apply or delete with the same hostname and idempotency key; do not create another deployment or repeatedly resubmit unchanged conflicts.",
2438
+ title: "Domain DNS records conflict"
2439
+ },
2434
2440
  data_change_blocked: {
2435
2441
  status: 409,
2436
2442
  retryable: true,
@@ -2512,6 +2518,7 @@ var parsePaidDomain = (value) => {
2512
2518
  const records2 = input["validation_records"];
2513
2519
  if (![
2514
2520
  "not_configured",
2521
+ "awaiting_deployment",
2515
2522
  "pending",
2516
2523
  "active",
2517
2524
  "reconciliation_required",
@@ -2522,7 +2529,7 @@ var parsePaidDomain = (value) => {
2522
2529
  "paid_plan_required",
2523
2530
  "insufficient_organization_credits",
2524
2531
  "project_budget_exceeded"
2525
- ].includes(input["suspension_reason"]) || status === "suspended" !== (input["suspension_reason"] !== null) || status === "suspended" && input["url"] !== null || input["environment"] !== "prod" || !isSaasCnameTarget(input["cname_target"]) || hostname !== null && !isPaidHostname(hostname) || input["custom_hostname_status"] !== null && typeof input["custom_hostname_status"] !== "string" || input["ssl_status"] !== null && typeof input["ssl_status"] !== "string" || !Array.isArray(records2) || records2.length > 16 || !records2.every(isPaidValidationRecord) || input["url"] !== null && input["url"] !== `https://${String(hostname)}`) {
2532
+ ].includes(input["suspension_reason"]) || status === "suspended" !== (input["suspension_reason"] !== null) || status === "suspended" && input["url"] !== null || input["environment"] !== "prod" || !isSaasCnameTarget(input["cname_target"]) || hostname !== null && !isPaidHostname(hostname) || input["custom_hostname_status"] !== null && typeof input["custom_hostname_status"] !== "string" || input["ssl_status"] !== null && typeof input["ssl_status"] !== "string" || !Array.isArray(records2) || records2.length > 16 || !records2.every(isPaidValidationRecord) || input["url"] !== null && input["url"] !== `https://${String(hostname)}` || status === "awaiting_deployment" && (hostname === null || input["custom_hostname_status"] !== null || input["ssl_status"] !== null || input["url"] !== null || records2.length !== 0)) {
2526
2533
  throw new ResponseContractError();
2527
2534
  }
2528
2535
  return input;
@@ -7282,8 +7289,8 @@ function createLocalOhmyhostMcpServer(dependencies) {
7282
7289
  idempotencyKey: input.idempotency_key
7283
7290
  }), mutationAnnotations());
7284
7291
  registerProductTool(server, dependencies, "domain_cloudflare_status", "Read the project's customer DNS authorization state, zone, scopes and expiry without credentials. authorized is not DNS/TLS readiness. For a Paid hostname, use domain_paid_apply with the original hostname/key after consent, then domain_paid_status. If DNS/TLS is pending, tell the customer to ask their agent again after 60 minutes; do not repeat correct records or create another deployment.", external_exports.object({ project_id: identifier2 }), (client2, input) => client2.getCloudflareDnsStatus(input.project_id));
7285
- registerProductTool(server, dependencies, "domain_paid_plan", "Plan a customer-owned production hostname and return the manual CNAME/validation instructions. This does not activate or change DNS. Without Paid, only a project that shows the powered-by flag (powered_by_flag_get) can activate its own hostname; for any other Free project domain_paid_apply answers paid_plan_required. production_deployment_required means Prod has no active deployment: deploy to Prod or promote the current Dev deployment, then plan again.", external_exports.object({ project_id: identifier2, hostname: domainName }), (client2, input) => client2.planPaidDomain({ projectId: input.project_id, hostname: input.hostname }));
7286
- registerProductTool(server, dependencies, "domain_paid_apply", "Activate the explicitly requested customer hostname. Requires Paid access, or a Free workspace whose project shows the powered-by flag (powered_by_flag_set, only with the owner's consent); otherwise it answers paid_plan_required. The backend may reuse only its scoped customer OAuth grant. After domain_cloudflare_status is authorized, replay the original hostname/key to reconcile its exact DNS records. Otherwise return the manual DNS records to the human. Preserve the original hostname/key after uncertainty; no arbitrary DNS or provider credentials are accepted. production_deployment_required changed nothing: deploy to Prod or promote Dev first, then repeat the same hostname/key.", external_exports.object({
7292
+ registerProductTool(server, dependencies, "domain_paid_plan", "Plan a customer-owned production hostname before or after the first Prod deployment. This reads the project and describes declaration and activation without changing DNS. domain_paid_apply can declare a hostname as awaiting_deployment for early Cloudflare consent. Without Paid, only a project that shows the powered-by flag (powered_by_flag_get) can connect its own hostname; for any other Free project domain_paid_apply answers paid_plan_required.", external_exports.object({ project_id: identifier2, hostname: domainName }), (client2, input) => client2.planPaidDomain({ projectId: input.project_id, hostname: input.hostname }));
7293
+ registerProductTool(server, dependencies, "domain_paid_apply", "Declare or activate the explicitly requested customer hostname. Requires Paid access, or a Free workspace whose project shows the powered-by flag (powered_by_flag_set, only with the owner's consent). Without an active Prod deployment, returns awaiting_deployment and reserves the hostname without provider resources, DNS changes or domain charges; request Cloudflare consent now, then deploy to Prod. After Prod succeeds and domain_cloudflare_status is authorized, replay the original hostname/key to activate and reconcile its exact DNS records. Otherwise return the manual records to the human. Preserve the original hostname/key after uncertainty; no arbitrary DNS or provider credentials are accepted.", external_exports.object({
7287
7294
  project_id: identifier2,
7288
7295
  hostname: domainName,
7289
7296
  idempotency_key: idempotencyKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amerged/ohmyhost-mcp",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/mcp",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -576,7 +576,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
576
576
  server,
577
577
  dependencies,
578
578
  "domain_paid_plan",
579
- "Plan a customer-owned production hostname and return the manual CNAME/validation instructions. This does not activate or change DNS. Without Paid, only a project that shows the powered-by flag (powered_by_flag_get) can activate its own hostname; for any other Free project domain_paid_apply answers paid_plan_required. production_deployment_required means Prod has no active deployment: deploy to Prod or promote the current Dev deployment, then plan again.",
579
+ "Plan a customer-owned production hostname before or after the first Prod deployment. This reads the project and describes declaration and activation without changing DNS. domain_paid_apply can declare a hostname as awaiting_deployment for early Cloudflare consent. Without Paid, only a project that shows the powered-by flag (powered_by_flag_get) can connect its own hostname; for any other Free project domain_paid_apply answers paid_plan_required.",
580
580
  z.object({ project_id: identifier, hostname: domainName }),
581
581
  (client, input) =>
582
582
  client.planPaidDomain({ projectId: input.project_id, hostname: input.hostname }),
@@ -585,7 +585,7 @@ export function createLocalOhmyhostMcpServer(dependencies: LocalOhmyhostMcpDepen
585
585
  server,
586
586
  dependencies,
587
587
  "domain_paid_apply",
588
- "Activate the explicitly requested customer hostname. Requires Paid access, or a Free workspace whose project shows the powered-by flag (powered_by_flag_set, only with the owner's consent); otherwise it answers paid_plan_required. The backend may reuse only its scoped customer OAuth grant. After domain_cloudflare_status is authorized, replay the original hostname/key to reconcile its exact DNS records. Otherwise return the manual DNS records to the human. Preserve the original hostname/key after uncertainty; no arbitrary DNS or provider credentials are accepted. production_deployment_required changed nothing: deploy to Prod or promote Dev first, then repeat the same hostname/key.",
588
+ "Declare or activate the explicitly requested customer hostname. Requires Paid access, or a Free workspace whose project shows the powered-by flag (powered_by_flag_set, only with the owner's consent). Without an active Prod deployment, returns awaiting_deployment and reserves the hostname without provider resources, DNS changes or domain charges; request Cloudflare consent now, then deploy to Prod. After Prod succeeds and domain_cloudflare_status is authorized, replay the original hostname/key to activate and reconcile its exact DNS records. Otherwise return the manual records to the human. Preserve the original hostname/key after uncertainty; no arbitrary DNS or provider credentials are accepted.",
589
589
  z.object({
590
590
  project_id: identifier,
591
591
  hostname: domainName,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohmyhost/product-cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -1163,6 +1163,18 @@ function repositoryBlockers(
1163
1163
  existingConfiguration: OhmyhostConfig | null,
1164
1164
  ): RepositoryInitBlocker[] {
1165
1165
  const blockers: RepositoryInitBlocker[] = [];
1166
+ if (
1167
+ framework !== "nextjs" &&
1168
+ existingConfiguration !== null &&
1169
+ "command" in existingConfiguration.build &&
1170
+ existingConfiguration.build.ssg_cache_max_mib !== undefined
1171
+ ) {
1172
+ blockers.push({
1173
+ code: "framework_unsupported",
1174
+ message:
1175
+ "build.ssg_cache_max_mib is supported only by Next.js; remove it from this application's configuration.",
1176
+ });
1177
+ }
1166
1178
  if (framework === "unknown") {
1167
1179
  blockers.push({
1168
1180
  code: "framework_unsupported",
@@ -1230,7 +1242,7 @@ function repositoryBlockers(
1230
1242
  .join(", ");
1231
1243
  blockers.push({
1232
1244
  code: "framework_version_unsupported",
1233
- message: `${unsupported} is outside the versions ohmyho.st builds: next 15.5.x or 16.0.0–16.3.4, vite 5.4.0–8.2.2, @tanstack/react-start 1.168.26–1.168.49, as an exact version or a ^ or ~ range whose base version is inside. Pin a version in that range in package.json, update the lockfile, commit and push.`,
1245
+ message: `${unsupported} is outside the versions ohmyho.st builds: next 15.5.26+ (15.5.x) or 16.3.6–16.3.7, vite 5.4.0–8.2.2, @tanstack/react-start 1.168.26–1.168.49, as an exact version or a ^ or ~ range whose base version is inside. Pin a version in that range in package.json, update the lockfile, commit and push.`,
1234
1246
  });
1235
1247
  }
1236
1248
  const scripts = packageJson["scripts"];
@@ -1285,7 +1297,7 @@ function repositoryBlockers(
1285
1297
  blockers.push({
1286
1298
  code: "next_adapter_unconfigured",
1287
1299
  message:
1288
- 'Next.js needs scripts.build exactly "next build" or "next build --webpack", react and react-dom at exact versions such as "19.1.0" (no ^ or ~), next within 15.5.x or 16.0.0–16.3.4, and exactly one next.config.js, .mjs or .ts, with at most one open-next.config file. ohmyho.st adds the OpenNext build itself; do not make it the build script.',
1300
+ 'Next.js needs scripts.build exactly "next build" or "next build --webpack", react and react-dom at exact versions such as "19.1.0" (no ^ or ~), next within 15.5.26+ (15.5.x) or 16.3.6–16.3.7, and exactly one next.config.js, .mjs or .ts, with at most one open-next.config file. ohmyho.st adds the OpenNext build itself; do not make it the build script.',
1289
1301
  });
1290
1302
  }
1291
1303
  }
@@ -94,6 +94,7 @@ export interface PublicPaidDomainPlan {
94
94
  export interface PublicPaidDomain {
95
95
  readonly status:
96
96
  | "not_configured"
97
+ | "awaiting_deployment"
97
98
  | "pending"
98
99
  | "active"
99
100
  | "reconciliation_required"
@@ -153,6 +154,7 @@ export const parsePaidDomain = (value: unknown): PublicPaidDomain => {
153
154
  if (
154
155
  ![
155
156
  "not_configured",
157
+ "awaiting_deployment",
156
158
  "pending",
157
159
  "active",
158
160
  "reconciliation_required",
@@ -176,7 +178,13 @@ export const parsePaidDomain = (value: unknown): PublicPaidDomain => {
176
178
  !Array.isArray(records) ||
177
179
  records.length > 16 ||
178
180
  !records.every(isPaidValidationRecord) ||
179
- (input["url"] !== null && input["url"] !== `https://${String(hostname)}`)
181
+ (input["url"] !== null && input["url"] !== `https://${String(hostname)}`) ||
182
+ (status === "awaiting_deployment" &&
183
+ (hostname === null ||
184
+ input["custom_hostname_status"] !== null ||
185
+ input["ssl_status"] !== null ||
186
+ input["url"] !== null ||
187
+ records.length !== 0))
180
188
  ) {
181
189
  throw new ResponseContractError();
182
190
  }