@amerged/ohmyhost-mcp 0.1.45 → 0.1.47

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-AJV4T4LM.js";
5
+ } from "./chunk-AH764FGJ.js";
6
6
  export {
7
7
  createOhmyhostMcpServer,
8
8
  ohmyhostMcpHandler
@@ -30,7 +30,7 @@ import {
30
30
  serverIdentityOf,
31
31
  setNegotiatedProtocolVersion,
32
32
  validateEnvelopeMeta
33
- } from "./chunk-AJV4T4LM.js";
33
+ } from "./chunk-AH764FGJ.js";
34
34
 
35
35
  // node_modules/.pnpm/ignore@7.0.12/node_modules/ignore/index.js
36
36
  var require_ignore = __commonJS({
@@ -24160,8 +24160,8 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
24160
24160
  from: "framework",
24161
24161
  text: {
24162
24162
  nextjs: "runtime.mode: edge",
24163
- "vite-static": "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, storage or functions.crons is set, and static otherwise",
24164
- "tanstack-start": "runtime.mode: edge, or static without functions.crons and storage",
24163
+ "vite-static": "runtime.mode: edge with src/ohmyhost/companion.ts when database, auth, mail, ai, storage or functions.crons is set, and static otherwise",
24164
+ "tanstack-start": "runtime.mode: edge, or static without functions.crons, storage and ai",
24165
24165
  astro: "runtime.mode: static for prerendered sites, or edge with the Cloudflare adapter"
24166
24166
  }
24167
24167
  }
@@ -24222,7 +24222,7 @@ var FRAMEWORK_CONVERSION_DETAILS = Object.freeze({
24222
24222
  },
24223
24223
  companion_entry_required: {
24224
24224
  paths: ["module"],
24225
- template: '"{{path}}" is missing. A Vite app with runtime.mode: edge or with database, auth, mail, storage or functions.crons needs this Worker module with export default { fetch }. Add it, or use runtime.mode: static without those settings; commit and push the change, then plan the new commit.'
24225
+ template: '"{{path}}" is missing. A Vite app with runtime.mode: edge or with database, auth, mail, ai, storage or functions.crons needs this Worker module with export default { fetch }. Add it, or use runtime.mode: static without those settings; commit and push the change, then plan the new commit.'
24226
24226
  },
24227
24227
  worker_module_required: {
24228
24228
  paths: ["module"],
@@ -24293,14 +24293,14 @@ function isFrameworkConversionDiagnosticCode(value) {
24293
24293
  function parseFrameworkConversionDiagnostic(value) {
24294
24294
  if (typeof value !== "object" || value === null || Array.isArray(value))
24295
24295
  return null;
24296
- const record8 = value;
24297
- const code = Object.hasOwn(record8, "code") ? record8["code"] : void 0;
24298
- const path2 = Object.hasOwn(record8, "path") ? record8["path"] : void 0;
24296
+ const record9 = value;
24297
+ const code = Object.hasOwn(record9, "code") ? record9["code"] : void 0;
24298
+ const path2 = Object.hasOwn(record9, "path") ? record9["path"] : void 0;
24299
24299
  if (!isFrameworkConversionDiagnosticCode(code) || typeof path2 !== "string")
24300
24300
  return null;
24301
24301
  const definition = DETAIL_DEFINITIONS[code];
24302
- const fields = Object.entries(definition.fields ?? {}).map(([name, grammar]) => [name, grammar, Object.hasOwn(record8, name) ? record8[name] : void 0]);
24303
- if (Object.keys(record8).length !== fields.length + 2 || pathClassOf(definition, path2) === null || fields.some(([, grammar, field]) => !fieldMatches(grammar, field)))
24302
+ const fields = Object.entries(definition.fields ?? {}).map(([name, grammar]) => [name, grammar, Object.hasOwn(record9, name) ? record9[name] : void 0]);
24303
+ if (Object.keys(record9).length !== fields.length + 2 || pathClassOf(definition, path2) === null || fields.some(([, grammar, field]) => !fieldMatches(grammar, field)))
24304
24304
  return null;
24305
24305
  return Object.freeze(Object.fromEntries([
24306
24306
  ["code", code],
@@ -25602,6 +25602,18 @@ var PROBLEM_POLICY = {
25602
25602
  action: "A migration already attaches a capture trigger to this table: keep it and skip the watch, or remove it from the migrations, deploy and create the watch again. A table carries one capture trigger.",
25603
25603
  title: "Table already has a capture trigger"
25604
25604
  },
25605
+ database_table_conflict: {
25606
+ status: 409,
25607
+ retryable: false,
25608
+ action: "A table with this name already exists with another definition. Choose another table name, or create the table again with the same columns, types, nullability, identity, defaults and primary key it has.",
25609
+ title: "Table already exists with another definition"
25610
+ },
25611
+ database_table_limit: {
25612
+ status: 409,
25613
+ retryable: false,
25614
+ action: "This database already holds the maximum number of tables in its public schema, so no table can be added. Use an existing table, or remove a table you no longer need through a deployed migration, then create the table again.",
25615
+ title: "Database table limit reached"
25616
+ },
25605
25617
  project_notes_conflict: {
25606
25618
  status: 409,
25607
25619
  retryable: false,
@@ -26285,8 +26297,8 @@ var isPaidValidationRecord = (value) => {
26285
26297
  if (typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).sort().join(",") !== "content,name,purpose,type") {
26286
26298
  return false;
26287
26299
  }
26288
- const record8 = value;
26289
- return (record8["type"] === "CNAME" || record8["type"] === "TXT") && boundedString(record8["name"], 1, 253) && boundedString(record8["content"], 1, 2048) && ["traffic", "ownership", "ssl", "dcv_delegation"].includes(String(record8["purpose"]));
26300
+ const record9 = value;
26301
+ return (record9["type"] === "CNAME" || record9["type"] === "TXT") && boundedString(record9["name"], 1, 253) && boundedString(record9["content"], 1, 2048) && ["traffic", "ownership", "ssl", "dcv_delegation"].includes(String(record9["purpose"]));
26290
26302
  };
26291
26303
 
26292
26304
  // apps/product-cli/dist/source-publisher.js
@@ -26556,7 +26568,7 @@ async function prepareLocalSourcePublication(input) {
26556
26568
  request = { ...request, expected_commit_sha: link2.source.last_confirmed_commit_sha };
26557
26569
  }
26558
26570
  parseManagedSourceRequest("upload", request);
26559
- const record8 = {
26571
+ const record9 = {
26560
26572
  version: 1,
26561
26573
  ...scope,
26562
26574
  operation_id: null,
@@ -26565,16 +26577,16 @@ async function prepareLocalSourcePublication(input) {
26565
26577
  request
26566
26578
  };
26567
26579
  if (original !== null) {
26568
- assertSameAttempt(original, record8);
26580
+ assertSameAttempt(original, record9);
26569
26581
  } else
26570
- await writePending(record8, true);
26582
+ await writePending(record9, true);
26571
26583
  return {
26572
26584
  request,
26573
26585
  accepted: async (operation) => {
26574
26586
  const current = await readPending(scope, replay);
26575
26587
  if (current === null)
26576
26588
  throw new LocalSourcePublicationError("source_publication_missing");
26577
- assertSameAttempt(current, record8);
26589
+ assertSameAttempt(current, record9);
26578
26590
  if (current.operation_id !== null && current.operation_id !== operation.id)
26579
26591
  throw new ResponseContractError();
26580
26592
  if (!replay)
@@ -26593,7 +26605,7 @@ async function prepareLocalSourcePublication(input) {
26593
26605
  const current = await readPending(scope);
26594
26606
  if (current === null || current.operation_id !== null)
26595
26607
  return;
26596
- assertSameAttempt(current, record8);
26608
+ assertSameAttempt(current, record9);
26597
26609
  } catch (error2) {
26598
26610
  if (error2 instanceof LocalSourcePublicationError && (error2.code === "source_publication_scope_mismatch" || error2.code === "source_publication_pending"))
26599
26611
  return;
@@ -26796,12 +26808,12 @@ async function readRecord(path2) {
26796
26808
  await handle?.close();
26797
26809
  }
26798
26810
  }
26799
- async function writePending(record8, create, completed = false) {
26800
- const parent = join3(record8.directory, ".ohmyhost");
26811
+ async function writePending(record9, create, completed = false) {
26812
+ const parent = join3(record9.directory, ".ohmyhost");
26801
26813
  for (const directory of [
26802
26814
  parent,
26803
- record8.metadata_directory,
26804
- join3(record8.metadata_directory, "publications")
26815
+ record9.metadata_directory,
26816
+ join3(record9.metadata_directory, "publications")
26805
26817
  ]) {
26806
26818
  try {
26807
26819
  await fs2.mkdir(directory, { mode: 448 });
@@ -26812,10 +26824,10 @@ async function writePending(record8, create, completed = false) {
26812
26824
  await checkedDirectory(directory);
26813
26825
  await fs2.chmod(directory, 448);
26814
26826
  }
26815
- const target = completed ? completedPath(record8) : pendingPath(record8);
26827
+ const target = completed ? completedPath(record9) : pendingPath(record9);
26816
26828
  const temporary = create ? target : `${target}.${randomUUID()}.tmp`;
26817
26829
  try {
26818
- await fs2.writeFile(temporary, `${JSON.stringify(record8)}
26830
+ await fs2.writeFile(temporary, `${JSON.stringify(record9)}
26819
26831
  `, { mode: 384, flag: "wx" });
26820
26832
  if (!create)
26821
26833
  await fs2.rename(temporary, target);
@@ -26945,7 +26957,7 @@ function exact2(value, keys) {
26945
26957
 
26946
26958
  // packages/contracts/dist/credit-pricing.js
26947
26959
  function assertOrganizationCreditUsage(value) {
26948
- const record8 = (input, keys) => {
26960
+ const record9 = (input, keys) => {
26949
26961
  if (!input || typeof input !== "object" || Array.isArray(input) || Object.keys(input).sort().join() !== keys.split(",").sort().join())
26950
26962
  throw new TypeError("Invalid usage fields");
26951
26963
  return input;
@@ -26953,30 +26965,30 @@ function assertOrganizationCreditUsage(value) {
26953
26965
  const identifier3 = (input) => typeof input === "string" && /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/u.test(input);
26954
26966
  const amount = (input) => typeof input === "string" && /^(0|[1-9][0-9]{0,37})$/u.test(input);
26955
26967
  const instant2 = (input) => typeof input === "string" && Number.isFinite(Date.parse(input)) && new Date(input).toISOString() === input;
26956
- const report = record8(value, "organization_id,as_of,unit,period,from,projects,days");
26968
+ const report = record9(value, "organization_id,as_of,unit,period,from,projects,days");
26957
26969
  if (!identifier3(report["organization_id"]) || !instant2(report["as_of"]) || !instant2(report["from"]) || report["unit"] !== "microcredits" || typeof report["period"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])$/u.test(report["period"]) || !Array.isArray(report["projects"]) || report["projects"].length > 1e3 || !Array.isArray(report["days"]) || report["days"].length > 15)
26958
26970
  throw new TypeError("Invalid usage report");
26959
26971
  let previous = "";
26960
26972
  for (const input of report["projects"]) {
26961
- const project = record8(input, "project_id,used_micros");
26973
+ const project = record9(input, "project_id,used_micros");
26962
26974
  if (!identifier3(project["project_id"]) || String(project["project_id"]) <= previous || !amount(project["used_micros"]))
26963
26975
  throw new TypeError("Invalid usage project");
26964
26976
  previous = String(project["project_id"]);
26965
26977
  }
26966
26978
  let previousDay = "";
26967
26979
  for (const input of report["days"]) {
26968
- const day = record8(input, "day,projects");
26980
+ const day = record9(input, "day,projects");
26969
26981
  if (typeof day["day"] !== "string" || !/^20[0-9]{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$/u.test(day["day"]) || day["day"] <= previousDay || !Array.isArray(day["projects"]) || day["projects"].length > 1e3)
26970
26982
  throw new TypeError("Invalid usage day");
26971
26983
  previousDay = day["day"];
26972
26984
  previous = "";
26973
26985
  for (const projectInput of day["projects"]) {
26974
- const project = record8(projectInput, "project_id,meters");
26986
+ const project = record9(projectInput, "project_id,meters");
26975
26987
  if (!identifier3(project["project_id"]) || String(project["project_id"]) <= previous || !Array.isArray(project["meters"]) || project["meters"].length > 1e3)
26976
26988
  throw new TypeError("Invalid usage project");
26977
26989
  previous = String(project["project_id"]);
26978
26990
  for (const meterInput of project["meters"]) {
26979
- const meter = record8(meterInput, "environment_id,meter,unit,rate_card_id,quantity,charged_micros");
26991
+ const meter = record9(meterInput, "environment_id,meter,unit,rate_card_id,quantity,charged_micros");
26980
26992
  if (meter["environment_id"] !== null && !identifier3(meter["environment_id"]))
26981
26993
  throw new TypeError("Invalid usage environment");
26982
26994
  for (const key of ["meter", "unit", "rate_card_id"])
@@ -27401,7 +27413,7 @@ function exact4(value, keys) {
27401
27413
  // apps/product-cli/package.json
27402
27414
  var package_default2 = {
27403
27415
  name: "@ohmyhost/product-cli",
27404
- version: "0.1.45",
27416
+ version: "0.1.47",
27405
27417
  private: true,
27406
27418
  ohmyhost: {
27407
27419
  deployment: "production",
@@ -27462,6 +27474,7 @@ var FIXED_PLATFORM_NAMES = /* @__PURE__ */ new Set([
27462
27474
  var INSTALLED_PLATFORM_KEYS = /* @__PURE__ */ new Set([
27463
27475
  "OHMYHOST_STORAGE_KEY",
27464
27476
  "OHMYHOST_MAIL_KEY",
27477
+ "OHMYHOST_AI_KEY",
27465
27478
  "BETTER_AUTH_SECRET"
27466
27479
  ]);
27467
27480
  function isUserSecretName(value, action = "set") {
@@ -27850,6 +27863,170 @@ function expectPositiveInteger(value, field, options = {}) {
27850
27863
  return value;
27851
27864
  }
27852
27865
 
27866
+ // packages/contracts/dist/database-tables.js
27867
+ var DATABASE_TABLE_COLUMN_TYPES = [
27868
+ "text",
27869
+ "integer",
27870
+ "bigint",
27871
+ "boolean",
27872
+ "numeric",
27873
+ "double precision",
27874
+ "timestamptz",
27875
+ "date",
27876
+ "uuid",
27877
+ "jsonb"
27878
+ ];
27879
+ var DATABASE_TABLE_DEFAULTS = ["now", "uuid"];
27880
+ var DATABASE_TABLE_MAX_COLUMNS = 64;
27881
+ var DATABASE_TABLE_MAX_PRIMARY_KEY_COLUMNS = 8;
27882
+ var DATABASE_TABLE_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/u;
27883
+ var RESERVED_WORDS = /* @__PURE__ */ new Set([
27884
+ "all",
27885
+ "analyse",
27886
+ "analyze",
27887
+ "and",
27888
+ "any",
27889
+ "array",
27890
+ "as",
27891
+ "asc",
27892
+ "asymmetric",
27893
+ "both",
27894
+ "case",
27895
+ "cast",
27896
+ "check",
27897
+ "collate",
27898
+ "column",
27899
+ "constraint",
27900
+ "create",
27901
+ "current_catalog",
27902
+ "current_date",
27903
+ "current_role",
27904
+ "current_time",
27905
+ "current_timestamp",
27906
+ "current_user",
27907
+ "default",
27908
+ "deferrable",
27909
+ "desc",
27910
+ "distinct",
27911
+ "do",
27912
+ "else",
27913
+ "end",
27914
+ "except",
27915
+ "false",
27916
+ "fetch",
27917
+ "for",
27918
+ "foreign",
27919
+ "from",
27920
+ "grant",
27921
+ "group",
27922
+ "having",
27923
+ "in",
27924
+ "initially",
27925
+ "intersect",
27926
+ "into",
27927
+ "lateral",
27928
+ "leading",
27929
+ "limit",
27930
+ "localtime",
27931
+ "localtimestamp",
27932
+ "not",
27933
+ "null",
27934
+ "offset",
27935
+ "on",
27936
+ "only",
27937
+ "or",
27938
+ "order",
27939
+ "placing",
27940
+ "primary",
27941
+ "references",
27942
+ "returning",
27943
+ "select",
27944
+ "session_user",
27945
+ "some",
27946
+ "symmetric",
27947
+ "table",
27948
+ "then",
27949
+ "to",
27950
+ "trailing",
27951
+ "true",
27952
+ "union",
27953
+ "unique",
27954
+ "user",
27955
+ "using",
27956
+ "variadic",
27957
+ "when",
27958
+ "where",
27959
+ "window",
27960
+ "with"
27961
+ ]);
27962
+ function isDatabaseTableIdentifier(value) {
27963
+ return typeof value === "string" && DATABASE_TABLE_IDENTIFIER_PATTERN.test(value) && !value.startsWith("pg_") && !RESERVED_WORDS.has(value);
27964
+ }
27965
+ function parseDatabaseTableCreateRequest(value) {
27966
+ if (!record7(value) || !onlyKeys(value, ["table", "columns", "primaryKey"]) || !isDatabaseTableIdentifier(value["table"]) || !Array.isArray(value["columns"]) || value["columns"].length < 1 || value["columns"].length > DATABASE_TABLE_MAX_COLUMNS)
27967
+ throw new TypeError("Invalid database table request");
27968
+ const inputs = value["columns"].map(parseColumn);
27969
+ const names = inputs.map((column) => column.name);
27970
+ if (new Set(names).size !== names.length || inputs.filter((column) => column.identity).length > 1)
27971
+ throw new TypeError("Invalid database table request");
27972
+ const primaryKey = parsePrimaryKey(value["primaryKey"], names);
27973
+ return {
27974
+ table: value["table"],
27975
+ columns: inputs.map((column) => completeColumn(column, primaryKey.includes(column.name))),
27976
+ primaryKey
27977
+ };
27978
+ }
27979
+ function parseColumn(value) {
27980
+ if (!record7(value) || !onlyKeys(value, ["name", "type", "nullable", "identity", "default"]) || !isDatabaseTableIdentifier(value["name"]) || !oneOf2(DATABASE_TABLE_COLUMN_TYPES, value["type"]) || value["nullable"] !== void 0 && typeof value["nullable"] !== "boolean" || value["identity"] !== void 0 && typeof value["identity"] !== "boolean" || value["default"] !== void 0 && !oneOf2(DATABASE_TABLE_DEFAULTS, value["default"]))
27981
+ throw new TypeError("Invalid database table request");
27982
+ const type = value["type"];
27983
+ const identity = value["identity"] === true;
27984
+ const defaultValue = value["default"] ?? null;
27985
+ if (identity && type !== "integer" && type !== "bigint")
27986
+ throw new TypeError("Invalid database table request");
27987
+ if (identity && defaultValue !== null)
27988
+ throw new TypeError("Invalid database table request");
27989
+ if (defaultValue === "now" && type !== "timestamptz")
27990
+ throw new TypeError("Invalid database table request");
27991
+ if (defaultValue === "uuid" && type !== "uuid")
27992
+ throw new TypeError("Invalid database table request");
27993
+ return {
27994
+ name: value["name"],
27995
+ type,
27996
+ nullable: value["nullable"],
27997
+ identity,
27998
+ default: defaultValue
27999
+ };
28000
+ }
28001
+ function parsePrimaryKey(value, names) {
28002
+ if (value === void 0)
28003
+ return [];
28004
+ if (!Array.isArray(value) || value.length < 1 || value.length > DATABASE_TABLE_MAX_PRIMARY_KEY_COLUMNS || new Set(value).size !== value.length || !value.every((name) => typeof name === "string" && names.includes(name)))
28005
+ throw new TypeError("Invalid database table request");
28006
+ return value;
28007
+ }
28008
+ function completeColumn(column, keyed) {
28009
+ const required = column.identity || keyed;
28010
+ if (required && column.nullable === true)
28011
+ throw new TypeError("Invalid database table request");
28012
+ return {
28013
+ name: column.name,
28014
+ type: column.type,
28015
+ nullable: required ? false : column.nullable ?? true,
28016
+ identity: column.identity,
28017
+ default: column.default
28018
+ };
28019
+ }
28020
+ function oneOf2(allowed, value) {
28021
+ return typeof value === "string" && allowed.includes(value);
28022
+ }
28023
+ function record7(value) {
28024
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28025
+ }
28026
+ function onlyKeys(value, allowed) {
28027
+ return Object.keys(value).every((key) => allowed.includes(key));
28028
+ }
28029
+
27853
28030
  // packages/workos-auth-contracts/dist/user-api-keys.js
27854
28031
  var WORKOS_USER_API_KEY_PATTERN = /^sk_[A-Za-z0-9_-]{20,128}$/u;
27855
28032
 
@@ -28518,6 +28695,7 @@ var HELP_USAGE = Object.freeze({
28518
28695
  "database watch create": "ohmyhost database watch create --project ULID --environment dev|prod --table NAME [--schema public] --columns COL[,COL] [--operations insert,update,delete] --idempotency-key KEY --yes --json (publishes only the listed columns, at most 32, of each row change as the event type data.<topic> printed in this answer; the table must exist as an ordinary table of the application; a sleeping database wakes; next, subscribe with 'ohmyhost database webhook create ... --event-types data.<topic>')",
28519
28696
  "database watch list": "ohmyhost database watch list --project ULID [--environment dev|prod] --json (dev when omitted; a sleeping database wakes)",
28520
28697
  "database watch delete": "ohmyhost database watch delete --project ULID --environment dev|prod --watch ID --idempotency-key KEY --yes --json",
28698
+ "database table create": "ohmyhost database table create --project ULID --environment dev|prod --table NAME --columns-file PATH [--primary-key COL[,COL]] --idempotency-key KEY --yes --json (the table is created in the public schema as the migration role, with the same access as migration tables, so a database watch can attach later; the columns file is a UTF-8 JSON array of 1 to 64 column objects, at most 64 KiB, each with name, type and optional nullable, identity and default; types: text, integer, bigint, boolean, numeric, double precision, timestamptz, date, uuid, jsonb; identity needs integer or bigint; default now needs timestamptz and default uuid needs uuid; identical repeats answer created false; at most 100 tables, 64 columns per table)",
28521
28699
  "database webhook create": "ohmyhost database webhook create --project ULID --environment dev|prod --url HTTPS_URL --event-types TYPE[,TYPE] [--headers-file PATH] --idempotency-key KEY --yes --json (the signing secret is shown once in this answer; types: database.created, database.ready, database.woke, database.slept, database.suspended, database.resized, database.deleted, data.<topic> for the rows of one watched table printed by database watch create, or data.* for every watched table; a bare * is not accepted and does not include data events; the headers file is a UTF-8 JSON object of at most 8 KiB)",
28522
28700
  "database webhook list": "ohmyhost database webhook list --project ULID [--environment dev|prod] --json (dev when omitted; the secret is never listed)",
28523
28701
  "database webhook get": "ohmyhost database webhook get --project ULID [--environment dev|prod] --webhook ID --json",
@@ -28660,12 +28838,12 @@ function buildClientParams(args, fields) {
28660
28838
  };
28661
28839
  const map = buildKeyMap(fields);
28662
28840
  function writeSlot(slot, key, value) {
28663
- let record8 = params[slot];
28664
- if (record8 === void 0) {
28665
- record8 = /* @__PURE__ */ Object.create(null);
28666
- params[slot] = record8;
28841
+ let record9 = params[slot];
28842
+ if (record9 === void 0) {
28843
+ record9 = /* @__PURE__ */ Object.create(null);
28844
+ params[slot] = record9;
28667
28845
  }
28668
- record8[key] = value;
28846
+ record9[key] = value;
28669
28847
  }
28670
28848
  let config;
28671
28849
  for (const [index, arg] of args.entries()) {
@@ -31312,6 +31490,30 @@ var revokeProjectDatabaseAccess = (parameters, options) => {
31312
31490
  ...params
31313
31491
  });
31314
31492
  };
31493
+ var createProjectDatabaseTable = (parameters, options) => {
31494
+ const params = buildClientParams([parameters], [
31495
+ {
31496
+ args: [
31497
+ { in: "headers", key: "X-Request-Id" },
31498
+ { in: "headers", key: "Idempotency-Key" },
31499
+ { in: "path", key: "project_id" },
31500
+ { key: "databaseTableCreateRequest", map: "body" }
31501
+ ]
31502
+ }
31503
+ ]);
31504
+ return (options?.client ?? client).post({
31505
+ responseStyle: "data",
31506
+ security: [{ scheme: "bearer", type: "http" }],
31507
+ url: "/v1/projects/{project_id}/database/tables",
31508
+ ...options,
31509
+ ...params,
31510
+ headers: {
31511
+ "Content-Type": "application/json",
31512
+ ...options?.headers,
31513
+ ...params.headers
31514
+ }
31515
+ });
31516
+ };
31315
31517
  var listProjectDatabaseWatches = (parameters, options) => {
31316
31518
  const params = buildClientParams([parameters], [
31317
31519
  {
@@ -32779,7 +32981,24 @@ function checkedResponse(kind, value) {
32779
32981
  // apps/product-cli/dist/product-api.js
32780
32982
  var WEBHOOK_SCOPE_MISMATCH = "Webhook response scope mismatch";
32781
32983
  var WATCH_SCOPE_MISMATCH = "Watch response scope mismatch";
32984
+ var TABLE_SCOPE_MISMATCH = "Table response scope mismatch";
32782
32985
  var SCHEDULE_SCOPE_MISMATCH = "Schedule response scope mismatch";
32986
+ function assertDatabaseTableAnswer(value) {
32987
+ const table = isRecordValue(value) ? value["table"] : void 0;
32988
+ const columns = isRecordValue(table) ? table["columns"] : void 0;
32989
+ const primaryKey = isRecordValue(table) ? table["primary_key"] : void 0;
32990
+ if (!isRecordValue(table) || !hasExactKeys(table, ["environment", "schema", "table", "columns", "primary_key", "created"]) || table["environment"] !== "dev" && table["environment"] !== "prod" || table["schema"] !== "public" || !isDatabaseTableIdentifier(table["table"]) || typeof table["created"] !== "boolean" || !Array.isArray(columns) || columns.length < 1 || !columns.every(isDatabaseTableAnswerColumn) || !Array.isArray(primaryKey) || !primaryKey.every((name) => columns.some((column) => column["name"] === name)))
32991
+ throw new TypeError("Invalid database table answer");
32992
+ }
32993
+ function isDatabaseTableAnswerColumn(value) {
32994
+ return isRecordValue(value) && hasExactKeys(value, ["name", "type", "nullable", "identity", "default"]) && isDatabaseTableIdentifier(value["name"]) && DATABASE_TABLE_COLUMN_TYPES.includes(value["type"]) && typeof value["nullable"] === "boolean" && typeof value["identity"] === "boolean" && (value["default"] === null || DATABASE_TABLE_DEFAULTS.includes(value["default"]));
32995
+ }
32996
+ function isRecordValue(value) {
32997
+ return typeof value === "object" && value !== null && !Array.isArray(value);
32998
+ }
32999
+ function hasExactKeys(value, keys) {
33000
+ return Object.keys(value).sort().join(",") === [...keys].sort().join(",");
33001
+ }
32783
33002
  var GeneratedSdkProductApi = class {
32784
33003
  #client;
32785
33004
  managedSource;
@@ -33012,6 +33231,22 @@ var GeneratedSdkProductApi = class {
33012
33231
  throw new TypeError(WATCH_SCOPE_MISMATCH);
33013
33232
  return value;
33014
33233
  }
33234
+ async createProjectDatabaseTable(input) {
33235
+ const value = await createProjectDatabaseTable({
33236
+ project_id: input.projectId,
33237
+ "Idempotency-Key": input.idempotencyKey,
33238
+ databaseTableCreateRequest: {
33239
+ environment: input.environment,
33240
+ table: input.request.table,
33241
+ columns: [...input.request.columns],
33242
+ ...input.request.primaryKey.length === 0 ? {} : { primary_key: [...input.request.primaryKey] }
33243
+ }
33244
+ }, { client: this.#client });
33245
+ assertDatabaseTableAnswer(value);
33246
+ if (value.table.environment !== input.environment || value.table.table !== input.request.table)
33247
+ throw new TypeError(TABLE_SCOPE_MISMATCH);
33248
+ return value;
33249
+ }
33015
33250
  /** The signing secret exists only in this answer; the CLI prints it once and stores nothing. */
33016
33251
  async createProjectDatabaseWebhook(input) {
33017
33252
  const value = await createProjectDatabaseWebhook({
@@ -33419,6 +33654,7 @@ var MAIL_RUNTIME_BINDINGS = Object.freeze([
33419
33654
  "OHMYHOST_MAIL_KEY",
33420
33655
  "OHMYHOST_PROJECT_ID"
33421
33656
  ]);
33657
+ var AI_RUNTIME_BINDINGS = Object.freeze(["OHMYHOST_AI_BASE_URL", "OHMYHOST_AI_KEY"]);
33422
33658
  var CUSTOMER_RUNTIME_SOURCE = `npm:@amerged/ohmyhost-runtime@${package_default2.version}`;
33423
33659
  var PENDING_CHECKS = Object.freeze({
33424
33660
  installed_version: "compatibility.frameworks lists a version range that no lockfile ohmyho.st reads (package-lock.json v2/v3 or pnpm-lock.yaml v6/v9) resolves. Planning admits it; after the frozen install, the build checks the installed version against the same security minimum.",
@@ -34312,6 +34548,21 @@ var GeneratedSdkLocalMcpClient = class {
34312
34548
  requireScope(value.environment === input.environment && value.table === input.table && value.schema === (input.schema ?? DATABASE_WATCH_DEFAULT_SCHEMA));
34313
34549
  return value;
34314
34550
  }
34551
+ async createDatabaseTable(input) {
34552
+ const value = await createProjectDatabaseTable({
34553
+ project_id: input.projectId,
34554
+ "Idempotency-Key": input.idempotencyKey,
34555
+ databaseTableCreateRequest: {
34556
+ environment: input.environment,
34557
+ table: input.table,
34558
+ columns: [...input.columns],
34559
+ ...input.primaryKey === void 0 || input.primaryKey.length === 0 ? {} : { primary_key: [...input.primaryKey] }
34560
+ }
34561
+ }, { client: this.#client });
34562
+ assertDatabaseTableAnswer(value);
34563
+ requireScope(value.table.environment === input.environment && value.table.table === input.table);
34564
+ return value;
34565
+ }
34315
34566
  async deleteDatabaseWatch(input) {
34316
34567
  const value = await deleteProjectDatabaseWatch({
34317
34568
  project_id: input.projectId,
@@ -34672,13 +34923,13 @@ function normalizeMcpSourceChanges(value) {
34672
34923
  }
34673
34924
  }
34674
34925
  function normalizeSourceChanges(value) {
34675
- const body = record7(value);
34926
+ const body = record8(value);
34676
34927
  const changes = body["changes"];
34677
34928
  if (!Array.isArray(changes) || changes.length < 1 || changes.length > MANAGED_SOURCE_LIMITS.files)
34678
34929
  invalid7();
34679
34930
  let textBytes = 0;
34680
34931
  const normalized = changes.map((change) => {
34681
- const entry = record7(change);
34932
+ const entry = record8(change);
34682
34933
  const hasText = Object.hasOwn(entry, "content_text");
34683
34934
  if (hasText === Object.hasOwn(entry, "content_base64"))
34684
34935
  invalid7();
@@ -34737,7 +34988,7 @@ function encodeBase64(bytes) {
34737
34988
  binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
34738
34989
  return btoa(binary);
34739
34990
  }
34740
- function record7(value) {
34991
+ function record8(value) {
34741
34992
  if (typeof value !== "object" || value === null || Array.isArray(value))
34742
34993
  invalid7();
34743
34994
  return value;
@@ -34753,6 +35004,7 @@ var databaseWebhookId = external_exports.string().regex(/^whk_[a-z0-9]{20}$/u);
34753
35004
  var databaseWatchId = external_exports.string().regex(/^pgcf_watch_[0-9a-f]{8}$/u);
34754
35005
  var databaseIdentifier = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/u);
34755
35006
  var databaseDeliveryId = external_exports.string().regex(/^dlv_[0-9a-f]{20}$/u);
35007
+ var databaseTableIdentifier = external_exports.string().refine(isDatabaseTableIdentifier);
34756
35008
  var databaseScheduleName = external_exports.string().regex(/^[a-z][a-z0-9_-]{0,62}$/u);
34757
35009
  var profileName = external_exports.string().regex(/^[a-z0-9][a-z0-9_-]{0,62}$/u).optional().describe("Saved login to act as (profile_list shows them). Needed only when several logins could run this call; OHMYHOST_PROFILE of this server must match it.");
34758
35010
  var idempotencyKey = external_exports.string().min(1).max(128);
@@ -35037,6 +35289,35 @@ function createLocalOhmyhostMcpServer(dependencies) {
35037
35289
  watchId: input.watch_id,
35038
35290
  idempotencyKey: input.idempotency_key
35039
35291
  }), destructiveAnnotations());
35292
+ registerProductTool(server, dependencies, "database_table_create", "Create one ordinary table in the public schema of a Dev or Prod database, as the migration role with the same access as migration tables, so a database_watch_create can attach later. Each column has a lower-case name, a type (text, integer, bigint, boolean, numeric, double precision, timestamptz, date, uuid or jsonb) and optional nullable, identity (integer or bigint only) and default (now for timestamptz, uuid for uuid); at most 64 columns, 8 primary-key columns and 100 tables per database. An identical repeat answers created: false; a table of the same name with another definition is refused with database_table_conflict. Tables created this way are not in the repository's migration files: record them in project notes and add a migration when the source should reproduce the database. Requires confirmed: true and an idempotency key. Refused with project_budget_exceeded while the Stop budget is reached.", external_exports.object({
35293
+ project_id: identifier2,
35294
+ environment: databaseEnvironment,
35295
+ table: databaseTableIdentifier,
35296
+ columns: external_exports.array(external_exports.object({
35297
+ name: databaseTableIdentifier,
35298
+ type: external_exports.enum(DATABASE_TABLE_COLUMN_TYPES),
35299
+ nullable: external_exports.boolean().optional(),
35300
+ identity: external_exports.boolean().optional(),
35301
+ default: external_exports.enum(DATABASE_TABLE_DEFAULTS).optional()
35302
+ })).min(1).max(DATABASE_TABLE_MAX_COLUMNS),
35303
+ primary_key: external_exports.array(databaseTableIdentifier).min(1).max(DATABASE_TABLE_MAX_PRIMARY_KEY_COLUMNS).optional(),
35304
+ idempotency_key: idempotencyKey,
35305
+ confirmed: external_exports.literal(true)
35306
+ }), (client2, input) => {
35307
+ const request = parseDatabaseTableCreateRequest({
35308
+ table: input.table,
35309
+ columns: input.columns,
35310
+ ...input.primary_key === void 0 ? {} : { primaryKey: input.primary_key }
35311
+ });
35312
+ return client2.createDatabaseTable({
35313
+ projectId: input.project_id,
35314
+ environment: input.environment,
35315
+ table: request.table,
35316
+ columns: input.columns,
35317
+ ...request.primaryKey.length === 0 ? {} : { primaryKey: request.primaryKey },
35318
+ idempotencyKey: input.idempotency_key
35319
+ });
35320
+ }, mutationAnnotations());
35040
35321
  registerProductTool(server, dependencies, "database_webhook_list", "List this project's database webhooks of one Dev or Prod environment (dev when omitted), without signing secrets. Each webhook lists its event types and header names only.", external_exports.object({ project_id: identifier2, environment: databaseEnvironment.optional() }), (client2, input) => client2.listDatabaseWebhooks({
35041
35322
  projectId: input.project_id,
35042
35323
  ...input.environment === void 0 ? {} : { environment: input.environment }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amerged/ohmyhost-mcp",
3
- "version": "0.1.45",
3
+ "version": "0.1.47",
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.45",
3
+ "version": "0.1.47",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -119,6 +119,7 @@ import {
119
119
  createProjectDatabaseWebhook,
120
120
  listProjectDatabaseWebhooks,
121
121
  deleteProjectDatabaseWebhook,
122
+ createProjectDatabaseTable,
122
123
  createProjectDatabaseWatch,
123
124
  listProjectDatabaseWatches,
124
125
  deleteProjectDatabaseWatch,
@@ -184,6 +185,8 @@ import {
184
185
  type LocalProfile,
185
186
  type ProfileRegistry,
186
187
  type WorkspaceSession,
188
+ type DatabaseTableAnswer,
189
+ assertDatabaseTableAnswer,
187
190
  } from "@ohmyhost/product-cli";
188
191
 
189
192
  export class OperationEventsUnavailableError extends Error {
@@ -893,6 +896,30 @@ export class GeneratedSdkLocalMcpClient implements LocalMcpProductClient {
893
896
  );
894
897
  return value;
895
898
  }
899
+ public async createDatabaseTable(
900
+ input: Parameters<LocalMcpProductClient["createDatabaseTable"]>[0],
901
+ ): Promise<DatabaseTableAnswer> {
902
+ const value = await createProjectDatabaseTable(
903
+ {
904
+ project_id: input.projectId,
905
+ "Idempotency-Key": input.idempotencyKey,
906
+ databaseTableCreateRequest: {
907
+ environment: input.environment,
908
+ table: input.table,
909
+ columns: [...input.columns],
910
+ ...(input.primaryKey === undefined || input.primaryKey.length === 0
911
+ ? {}
912
+ : { primary_key: [...input.primaryKey] }),
913
+ },
914
+ },
915
+ { client: this.#client },
916
+ );
917
+ assertDatabaseTableAnswer(value);
918
+ requireScope(
919
+ value.table.environment === input.environment && value.table.table === input.table,
920
+ );
921
+ return value;
922
+ }
896
923
  public async deleteDatabaseWatch(
897
924
  input: Parameters<LocalMcpProductClient["deleteDatabaseWatch"]>[0],
898
925
  ) {