@amerged/ohmyhost-mcp 0.1.44 → 0.1.45

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-NUWAJ4TH.js";
5
+ } from "./chunk-AJV4T4LM.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-NUWAJ4TH.js";
33
+ } from "./chunk-AJV4T4LM.js";
34
34
 
35
35
  // node_modules/.pnpm/ignore@7.0.12/node_modules/ignore/index.js
36
36
  var require_ignore = __commonJS({
@@ -25572,6 +25572,36 @@ var PROBLEM_POLICY = {
25572
25572
  action: "The platform is still retrying this webhook delivery. Read the delivery in the delivery list; replay it only if it ends dead_lettered or the receiver needs the event again.",
25573
25573
  title: "Webhook delivery still pending"
25574
25574
  },
25575
+ database_watch_limit: {
25576
+ status: 409,
25577
+ retryable: false,
25578
+ action: "List the project's database watches for this environment and delete one you no longer need, then create the watch again.",
25579
+ title: "Database watch limit reached"
25580
+ },
25581
+ database_watch_table_unavailable: {
25582
+ status: 409,
25583
+ retryable: false,
25584
+ action: "The table must exist as an ordinary table owned by the application, created by a deployed migration; views, partitions, temporary tables and the schemas pgcf, ohmyhost, information_schema and pg_* cannot be watched. Fix the name or deploy the migration, then create the watch again.",
25585
+ title: "Table cannot be watched"
25586
+ },
25587
+ database_watch_column_unavailable: {
25588
+ status: 409,
25589
+ retryable: false,
25590
+ action: "List only existing column names exactly as written, names are case-sensitive. Delete the watch before a migration drops a published column, because the write would fail.",
25591
+ title: "Watched column does not exist"
25592
+ },
25593
+ database_watch_events_unavailable: {
25594
+ status: 409,
25595
+ retryable: false,
25596
+ action: "The platform event schema is installed when the database wakes: run any query against that environment, wait for the answer, then create the watch again. If it still fails, report the request_id through feedback.",
25597
+ title: "Database events not installed yet"
25598
+ },
25599
+ database_watch_conflict: {
25600
+ status: 409,
25601
+ retryable: false,
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
+ title: "Table already has a capture trigger"
25604
+ },
25575
25605
  project_notes_conflict: {
25576
25606
  status: 409,
25577
25607
  retryable: false,
@@ -25836,7 +25866,7 @@ var PROBLEM_POLICY = {
25836
25866
  api_key_permissions_unavailable: {
25837
25867
  status: 503,
25838
25868
  retryable: false,
25839
- action: "Report the platform user-key permission configuration through feedback. Repeated login or creation will not fix it.",
25869
+ action: "Report the platform user-key permission configuration through feedback with this request_id; it affects every token the same way, whatever the integration. Repeated login does not fix it. After the platform confirms the fix, repeat the same create with the same name and Idempotency-Key.",
25840
25870
  title: "Token permissions unavailable"
25841
25871
  },
25842
25872
  reconciliation_exhausted: {
@@ -27093,6 +27123,9 @@ var DATABASE_WEBHOOK_EVENT_TYPES = [
27093
27123
  "database.resized",
27094
27124
  "database.deleted"
27095
27125
  ];
27126
+ var DATABASE_WATCH_OPERATIONS = ["insert", "update", "delete"];
27127
+ var DATABASE_WATCH_MAX_COLUMNS = 32;
27128
+ var DATABASE_WATCH_DEFAULT_SCHEMA = "public";
27096
27129
  var DATABASE_WEBHOOK_MAX_EVENT_TYPES = 16;
27097
27130
  var DATABASE_WEBHOOK_MAX_HEADERS = 8;
27098
27131
  var DATABASE_WEBHOOK_URL_MAX_LENGTH = 2048;
@@ -27135,15 +27168,61 @@ var USAGE_MAX_RANGE_MS = 31 * 24 * USAGE_HOUR_MS;
27135
27168
  var WEBHOOK_KEYS = ["id", "environment", "url", "event_types", "header_names", "created_at"];
27136
27169
  var WEBHOOK_ID_PATTERN = /^whk_[a-z0-9]{20}$/u;
27137
27170
  var DELIVERY_ID_PATTERN = /^dlv_[0-9a-f]{20}$/u;
27138
- var EVENT_ID_PATTERN = /^evt_[0-9a-f]{20}$/u;
27171
+ var EVENT_ID_PATTERN = /^(?:evt_[0-9a-f]{20}|[a-z][a-z0-9]{19}:[1-9][0-9]{0,15}:[1-9][0-9]{0,15})$/u;
27172
+ var DATA_SUBSCRIPTION_PATTERN = /^data\.(?:\*|[a-z][a-z0-9_]{0,47})$/u;
27173
+ var DATA_EVENT_TYPE_PATTERN = /^data\.[a-z][a-z0-9_]{0,47}$/u;
27174
+ var DATABASE_WATCH_ID_PATTERN = /^pgcf_watch_[0-9a-f]{8}$/u;
27175
+ var DATABASE_WATCH_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]{0,62}$/u;
27176
+ var DATABASE_WATCH_FORBIDDEN_SCHEMAS = ["information_schema", "pgcf", "ohmyhost"];
27177
+ var DATABASE_WATCH_KEYS = [
27178
+ "id",
27179
+ "environment",
27180
+ "schema",
27181
+ "table",
27182
+ "columns",
27183
+ "operations",
27184
+ "topic",
27185
+ "event_type"
27186
+ ];
27139
27187
  var SCHEDULE_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,62}$/u;
27140
27188
  var CURSOR_PATTERN = /^[A-Za-z0-9._|:=+/-]{1,200}$/u;
27141
27189
  var HEADER_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9-]{0,63}$/u;
27190
+ function isDatabaseWatchIdentifier(value) {
27191
+ return matches(value, DATABASE_WATCH_IDENTIFIER_PATTERN);
27192
+ }
27193
+ function isDatabaseWatchSchema(value) {
27194
+ if (!isDatabaseWatchIdentifier(value))
27195
+ return false;
27196
+ const lower = value.toLowerCase();
27197
+ return !lower.startsWith("pg_") && !DATABASE_WATCH_FORBIDDEN_SCHEMAS.includes(lower);
27198
+ }
27199
+ function dataEventTopic(tableName) {
27200
+ const mapped = Array.from(tableName, (character) => {
27201
+ const lower = Array.from(character.toLowerCase())[0] ?? "_";
27202
+ return /^[a-z0-9_]$/u.test(lower) ? lower : "_";
27203
+ }).join("");
27204
+ const topic = /^[a-z]/u.test(mapped) ? mapped : `t_${mapped}`;
27205
+ return topic.slice(0, 48);
27206
+ }
27142
27207
  function assertDatabaseWebhook(value) {
27143
27208
  if (!record4(value) || !exact4(value, WEBHOOK_KEYS))
27144
27209
  throw new TypeError("Invalid database webhook");
27145
27210
  assertWebhookFields(value);
27146
27211
  }
27212
+ function assertDatabaseWatch(value) {
27213
+ if (!record4(value) || !exact4(value, DATABASE_WATCH_KEYS) || !matches(value["id"], DATABASE_WATCH_ID_PATTERN) || !oneOf(["dev", "prod"], value["environment"]) || !isDatabaseWatchSchema(value["schema"]) || !isDatabaseWatchIdentifier(value["table"]) || !Array.isArray(value["columns"]) || value["columns"].length < 1 || value["columns"].length > DATABASE_WATCH_MAX_COLUMNS || !value["columns"].every(isDatabaseWatchIdentifier) || !isCanonicalOperations(value["operations"]) || value["topic"] !== dataEventTopic(value["table"]) || value["event_type"] !== `data.${value["topic"]}`)
27214
+ throw new TypeError("Invalid database watch");
27215
+ }
27216
+ function assertDatabaseWatchPage(value) {
27217
+ if (!record4(value) || !exact4(value, ["items"]) || !pageItems(value["items"]))
27218
+ throw new TypeError("Invalid database watch page");
27219
+ for (const item of value["items"])
27220
+ assertDatabaseWatch(item);
27221
+ }
27222
+ function assertDatabaseWatchDeleted(value) {
27223
+ if (!record4(value) || !exact4(value, ["id", "deleted"]) || !matches(value["id"], DATABASE_WATCH_ID_PATTERN) || value["deleted"] !== true)
27224
+ throw new TypeError("Invalid database watch deletion");
27225
+ }
27147
27226
  function assertDatabaseWebhookCreated(value) {
27148
27227
  if (!record4(value) || !exact4(value, [...WEBHOOK_KEYS, "secret"]) || typeof value["secret"] !== "string" || value["secret"].length === 0)
27149
27228
  throw new TypeError("Invalid database webhook");
@@ -27172,7 +27251,7 @@ function assertDatabaseWebhookDelivery(value) {
27172
27251
  "delivered_at",
27173
27252
  "last_status",
27174
27253
  "last_error"
27175
- ]) || !matches(value["id"], DELIVERY_ID_PATTERN) || !matches(value["event_id"], EVENT_ID_PATTERN) || !oneOf(DATABASE_WEBHOOK_EVENT_TYPES, value["event_type"]) || !matches(value["webhook_id"], WEBHOOK_ID_PATTERN) || !oneOf(["pending", "delivered", "dead_lettered"], value["state"]) || !nonNegativeInteger(value["attempts"]) || !timestamp3(value["created_at"]) || !nullable(value["next_attempt_at"], timestamp3) || !nullable(value["delivered_at"], timestamp3) || !nullable(value["last_status"], Number.isSafeInteger) || !nullable(value["last_error"], (error) => oneOf(["timeout", "network", "http", "expired", "permanent"], error)))
27254
+ ]) || !matches(value["id"], DELIVERY_ID_PATTERN) || !matches(value["event_id"], EVENT_ID_PATTERN) || !isDeliveredEventType(value["event_type"]) || !matches(value["webhook_id"], WEBHOOK_ID_PATTERN) || !oneOf(["pending", "delivered", "dead_lettered"], value["state"]) || !nonNegativeInteger(value["attempts"]) || !timestamp3(value["created_at"]) || !nullable(value["next_attempt_at"], timestamp3) || !nullable(value["delivered_at"], timestamp3) || !nullable(value["last_status"], Number.isSafeInteger) || !nullable(value["last_error"], (error) => oneOf(["timeout", "network", "http", "expired", "permanent"], error)))
27176
27255
  throw new TypeError("Invalid database webhook delivery");
27177
27256
  }
27178
27257
  function assertDatabaseWebhookDeliveryPage(value) {
@@ -27255,7 +27334,7 @@ function assertUsageRow(value) {
27255
27334
  throw new TypeError("Invalid database execution usage row");
27256
27335
  }
27257
27336
  function assertWebhookFields(value) {
27258
- if (!matches(value["id"], WEBHOOK_ID_PATTERN) || !oneOf(["dev", "prod"], value["environment"]) || !isHttpsUrl2(value["url"]) || !Array.isArray(value["event_types"]) || value["event_types"].length < 1 || value["event_types"].length > DATABASE_WEBHOOK_MAX_EVENT_TYPES || !value["event_types"].every((type) => oneOf(DATABASE_WEBHOOK_EVENT_TYPES, type)) || !Array.isArray(value["header_names"]) || value["header_names"].length > DATABASE_WEBHOOK_MAX_HEADERS || !value["header_names"].every((name) => matches(name, HEADER_NAME_PATTERN)) || !timestamp3(value["created_at"]))
27337
+ if (!matches(value["id"], WEBHOOK_ID_PATTERN) || !oneOf(["dev", "prod"], value["environment"]) || !isHttpsUrl2(value["url"]) || !Array.isArray(value["event_types"]) || value["event_types"].length < 1 || value["event_types"].length > DATABASE_WEBHOOK_MAX_EVENT_TYPES || !value["event_types"].every(isDatabaseWebhookEventPattern) || !Array.isArray(value["header_names"]) || value["header_names"].length > DATABASE_WEBHOOK_MAX_HEADERS || !value["header_names"].every((name) => matches(name, HEADER_NAME_PATTERN)) || !timestamp3(value["created_at"]))
27259
27338
  throw new TypeError("Invalid database webhook");
27260
27339
  }
27261
27340
  function isHttpsUrl2(value) {
@@ -27267,6 +27346,18 @@ function isHttpsUrl2(value) {
27267
27346
  return false;
27268
27347
  }
27269
27348
  }
27349
+ function isDatabaseWebhookEventPattern(value) {
27350
+ return oneOf(DATABASE_WEBHOOK_EVENT_TYPES, value) || matches(value, DATA_SUBSCRIPTION_PATTERN);
27351
+ }
27352
+ function isDeliveredEventType(value) {
27353
+ return oneOf(DATABASE_WEBHOOK_EVENT_TYPES, value) || matches(value, DATA_EVENT_TYPE_PATTERN);
27354
+ }
27355
+ function isCanonicalOperations(value) {
27356
+ if (!Array.isArray(value) || value.length < 1)
27357
+ return false;
27358
+ const canonical = DATABASE_WATCH_OPERATIONS.filter((operation) => value.includes(operation));
27359
+ return canonical.length === value.length && canonical.every((op, index) => op === value[index]);
27360
+ }
27270
27361
  function isCronText(value) {
27271
27362
  return typeof value === "string" && value.length >= DATABASE_SCHEDULE_CRON_MIN_LENGTH && value.length <= DATABASE_SCHEDULE_CRON_MAX_LENGTH;
27272
27363
  }
@@ -27310,7 +27401,7 @@ function exact4(value, keys) {
27310
27401
  // apps/product-cli/package.json
27311
27402
  var package_default2 = {
27312
27403
  name: "@ohmyhost/product-cli",
27313
- version: "0.1.44",
27404
+ version: "0.1.45",
27314
27405
  private: true,
27315
27406
  ohmyhost: {
27316
27407
  deployment: "production",
@@ -28424,7 +28515,10 @@ var HELP_USAGE = Object.freeze({
28424
28515
  "database access create": "ohmyhost database access create --project ULID --environment dev|prod [--mode read|write] [--ttl 5m|1h|24h|SECONDS] [--label TEXT] --yes --json\n The connection URI is shown once. Use it immediately, never store it in files, notes or source, and revoke it when finished.",
28425
28516
  "database access list": "ohmyhost database access list --project ULID [--environment dev|prod] --json",
28426
28517
  "database access revoke": "ohmyhost database access revoke --project ULID --access ULID --yes --json",
28427
- "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; the headers file is a UTF-8 JSON object of at most 8 KiB)",
28518
+ "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
+ "database watch list": "ohmyhost database watch list --project ULID [--environment dev|prod] --json (dev when omitted; a sleeping database wakes)",
28520
+ "database watch delete": "ohmyhost database watch delete --project ULID --environment dev|prod --watch ID --idempotency-key KEY --yes --json",
28521
+ "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)",
28428
28522
  "database webhook list": "ohmyhost database webhook list --project ULID [--environment dev|prod] --json (dev when omitted; the secret is never listed)",
28429
28523
  "database webhook get": "ohmyhost database webhook get --project ULID [--environment dev|prod] --webhook ID --json",
28430
28524
  "database webhook delete": "ohmyhost database webhook delete --project ULID --environment dev|prod --webhook ID --yes --json",
@@ -31218,6 +31312,68 @@ var revokeProjectDatabaseAccess = (parameters, options) => {
31218
31312
  ...params
31219
31313
  });
31220
31314
  };
31315
+ var listProjectDatabaseWatches = (parameters, options) => {
31316
+ const params = buildClientParams([parameters], [
31317
+ {
31318
+ args: [
31319
+ { in: "headers", key: "X-Request-Id" },
31320
+ { in: "path", key: "project_id" },
31321
+ { in: "query", key: "environment" }
31322
+ ]
31323
+ }
31324
+ ]);
31325
+ return (options?.client ?? client).get({
31326
+ responseStyle: "data",
31327
+ security: [{ scheme: "bearer", type: "http" }],
31328
+ url: "/v1/projects/{project_id}/database/watches",
31329
+ ...options,
31330
+ ...params
31331
+ });
31332
+ };
31333
+ var createProjectDatabaseWatch = (parameters, options) => {
31334
+ const params = buildClientParams([parameters], [
31335
+ {
31336
+ args: [
31337
+ { in: "headers", key: "X-Request-Id" },
31338
+ { in: "headers", key: "Idempotency-Key" },
31339
+ { in: "path", key: "project_id" },
31340
+ { key: "databaseWatchCreateRequest", map: "body" }
31341
+ ]
31342
+ }
31343
+ ]);
31344
+ return (options?.client ?? client).post({
31345
+ responseStyle: "data",
31346
+ security: [{ scheme: "bearer", type: "http" }],
31347
+ url: "/v1/projects/{project_id}/database/watches",
31348
+ ...options,
31349
+ ...params,
31350
+ headers: {
31351
+ "Content-Type": "application/json",
31352
+ ...options?.headers,
31353
+ ...params.headers
31354
+ }
31355
+ });
31356
+ };
31357
+ var deleteProjectDatabaseWatch = (parameters, options) => {
31358
+ const params = buildClientParams([parameters], [
31359
+ {
31360
+ args: [
31361
+ { in: "headers", key: "X-Request-Id" },
31362
+ { in: "headers", key: "Idempotency-Key" },
31363
+ { in: "path", key: "project_id" },
31364
+ { in: "path", key: "watch_id" },
31365
+ { in: "query", key: "environment" }
31366
+ ]
31367
+ }
31368
+ ]);
31369
+ return (options?.client ?? client).delete({
31370
+ responseStyle: "data",
31371
+ security: [{ scheme: "bearer", type: "http" }],
31372
+ url: "/v1/projects/{project_id}/database/watches/{watch_id}",
31373
+ ...options,
31374
+ ...params
31375
+ });
31376
+ };
31221
31377
  var listProjectDatabaseWebhooks = (parameters, options) => {
31222
31378
  const params = buildClientParams([parameters], [
31223
31379
  {
@@ -32622,6 +32778,7 @@ function checkedResponse(kind, value) {
32622
32778
 
32623
32779
  // apps/product-cli/dist/product-api.js
32624
32780
  var WEBHOOK_SCOPE_MISMATCH = "Webhook response scope mismatch";
32781
+ var WATCH_SCOPE_MISMATCH = "Watch response scope mismatch";
32625
32782
  var SCHEDULE_SCOPE_MISMATCH = "Schedule response scope mismatch";
32626
32783
  var GeneratedSdkProductApi = class {
32627
32784
  #client;
@@ -32819,6 +32976,42 @@ var GeneratedSdkProductApi = class {
32819
32976
  assertProjectDatabaseAccess(value);
32820
32977
  return value;
32821
32978
  }
32979
+ async createProjectDatabaseWatch(input) {
32980
+ const value = await createProjectDatabaseWatch({
32981
+ project_id: input.projectId,
32982
+ "Idempotency-Key": input.idempotencyKey,
32983
+ databaseWatchCreateRequest: {
32984
+ environment: input.environment,
32985
+ schema: input.request.schema,
32986
+ table: input.request.table,
32987
+ columns: [...input.request.columns],
32988
+ operations: [...input.request.operations]
32989
+ }
32990
+ }, { client: this.#client });
32991
+ assertDatabaseWatch(value);
32992
+ if (value.environment !== input.environment || value.schema !== input.request.schema || value.table !== input.request.table)
32993
+ throw new TypeError(WATCH_SCOPE_MISMATCH);
32994
+ return value;
32995
+ }
32996
+ async listProjectDatabaseWatches(input) {
32997
+ const value = await listProjectDatabaseWatches({ project_id: input.projectId, environment: input.environment }, { client: this.#client });
32998
+ assertDatabaseWatchPage(value);
32999
+ if (value.items.some((item) => item.environment !== input.environment))
33000
+ throw new TypeError(WATCH_SCOPE_MISMATCH);
33001
+ return value;
33002
+ }
33003
+ async deleteProjectDatabaseWatch(input) {
33004
+ const value = await deleteProjectDatabaseWatch({
33005
+ project_id: input.projectId,
33006
+ environment: input.environment,
33007
+ "Idempotency-Key": input.idempotencyKey,
33008
+ watch_id: input.watchId
33009
+ }, { client: this.#client });
33010
+ assertDatabaseWatchDeleted(value);
33011
+ if (value.id !== input.watchId)
33012
+ throw new TypeError(WATCH_SCOPE_MISMATCH);
33013
+ return value;
33014
+ }
32822
33015
  /** The signing secret exists only in this answer; the CLI prints it once and stores nothing. */
32823
33016
  async createProjectDatabaseWebhook(input) {
32824
33017
  const value = await createProjectDatabaseWebhook({
@@ -34096,6 +34289,40 @@ var GeneratedSdkLocalMcpClient = class {
34096
34289
  requireScope(value.items.every((webhook) => webhook.environment === environment2));
34097
34290
  return value;
34098
34291
  }
34292
+ async listDatabaseWatches(input) {
34293
+ const environment2 = input.environment ?? "dev";
34294
+ const value = await listProjectDatabaseWatches({ project_id: input.projectId, environment: environment2 }, { client: this.#client });
34295
+ assertDatabaseWatchPage(value);
34296
+ requireScope(value.items.every((watch) => watch.environment === environment2));
34297
+ return value;
34298
+ }
34299
+ async createDatabaseWatch(input) {
34300
+ const value = await createProjectDatabaseWatch({
34301
+ project_id: input.projectId,
34302
+ "Idempotency-Key": input.idempotencyKey,
34303
+ databaseWatchCreateRequest: {
34304
+ environment: input.environment,
34305
+ ...input.schema === void 0 ? {} : { schema: input.schema },
34306
+ table: input.table,
34307
+ columns: [...input.columns],
34308
+ ...input.operations === void 0 ? {} : { operations: [...input.operations] }
34309
+ }
34310
+ }, { client: this.#client });
34311
+ assertDatabaseWatch(value);
34312
+ requireScope(value.environment === input.environment && value.table === input.table && value.schema === (input.schema ?? DATABASE_WATCH_DEFAULT_SCHEMA));
34313
+ return value;
34314
+ }
34315
+ async deleteDatabaseWatch(input) {
34316
+ const value = await deleteProjectDatabaseWatch({
34317
+ project_id: input.projectId,
34318
+ environment: input.environment,
34319
+ "Idempotency-Key": input.idempotencyKey,
34320
+ watch_id: input.watchId
34321
+ }, { client: this.#client });
34322
+ assertDatabaseWatchDeleted(value);
34323
+ requireScope(value.id === input.watchId);
34324
+ return value;
34325
+ }
34099
34326
  /** The signing secret exists only in this response and is never stored by the client. */
34100
34327
  async createDatabaseWebhook(input) {
34101
34328
  const value = await createProjectDatabaseWebhook({
@@ -34523,6 +34750,8 @@ function invalid7() {
34523
34750
  var identifier2 = external_exports.string().min(1).max(256);
34524
34751
  var databaseEnvironment = external_exports.enum(["dev", "prod"]);
34525
34752
  var databaseWebhookId = external_exports.string().regex(/^whk_[a-z0-9]{20}$/u);
34753
+ var databaseWatchId = external_exports.string().regex(/^pgcf_watch_[0-9a-f]{8}$/u);
34754
+ var databaseIdentifier = external_exports.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/u);
34526
34755
  var databaseDeliveryId = external_exports.string().regex(/^dlv_[0-9a-f]{20}$/u);
34527
34756
  var databaseScheduleName = external_exports.string().regex(/^[a-z][a-z0-9_-]{0,62}$/u);
34528
34757
  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.");
@@ -34774,15 +35003,52 @@ function createLocalOhmyhostMcpServer(dependencies) {
34774
35003
  ...input.environment === void 0 ? {} : { environment: input.environment }
34775
35004
  }));
34776
35005
  registerProductTool(server, dependencies, "database_access_revoke", "Revoke one issued database credential immediately: open sessions end and its PostgreSQL role is removed. Revocation is idempotent and never touches application roles or data.", external_exports.object({ project_id: identifier2, access_id: identifier2 }), (client2, input) => client2.revokeDatabaseAccess({ projectId: input.project_id, accessId: input.access_id }), destructiveAnnotations());
35006
+ registerProductTool(server, dependencies, "database_watch_list", "List this project's database watches of one Dev or Prod environment (dev when omitted). Each watch shows its table, columns, operations and the event type data.<topic> it publishes. Listing wakes a sleeping database.", external_exports.object({ project_id: identifier2, environment: databaseEnvironment.optional() }), (client2, input) => client2.listDatabaseWatches({
35007
+ projectId: input.project_id,
35008
+ ...input.environment === void 0 ? {} : { environment: input.environment }
35009
+ }));
35010
+ registerProductTool(server, dependencies, "database_watch_create", "Publish row changes of one table of a Dev or Prod database as data events: only the listed columns (at most 32) of each insert, update or delete are published, with the event type data.<topic> returned by this call. Pass that event type to database_webhook_create. The table must exist as an ordinary table of the application, created by a deployed migration; a sleeping database wakes. Rows written before the watch exists are not published, and a published column must not be dropped while the watch exists. Requires confirmed: true and an idempotency key. Refused with project_budget_exceeded while the Stop budget is reached.", external_exports.object({
35011
+ project_id: identifier2,
35012
+ environment: databaseEnvironment,
35013
+ schema: databaseIdentifier.refine(isDatabaseWatchSchema).optional(),
35014
+ table: databaseIdentifier,
35015
+ columns: external_exports.array(databaseIdentifier).min(1).max(32).refine((columns) => new Set(columns).size === columns.length),
35016
+ operations: external_exports.array(external_exports.enum(DATABASE_WATCH_OPERATIONS)).min(1).max(3).refine((operations) => new Set(operations).size === operations.length).optional(),
35017
+ idempotency_key: idempotencyKey,
35018
+ confirmed: external_exports.literal(true)
35019
+ }), (client2, input) => client2.createDatabaseWatch({
35020
+ projectId: input.project_id,
35021
+ environment: input.environment,
35022
+ ...input.schema === void 0 ? {} : { schema: input.schema },
35023
+ table: input.table,
35024
+ columns: input.columns,
35025
+ ...input.operations === void 0 ? {} : { operations: input.operations },
35026
+ idempotencyKey: input.idempotency_key
35027
+ }), mutationAnnotations());
35028
+ registerProductTool(server, dependencies, "database_watch_delete", "Remove the capture trigger of one database watch: its table publishes no more data events. Destructive: requires confirmed: true and an idempotency key. Deleting an already deleted watch answers not found.", external_exports.object({
35029
+ project_id: identifier2,
35030
+ environment: databaseEnvironment,
35031
+ watch_id: databaseWatchId,
35032
+ idempotency_key: idempotencyKey,
35033
+ confirmed: external_exports.literal(true)
35034
+ }), (client2, input) => client2.deleteDatabaseWatch({
35035
+ projectId: input.project_id,
35036
+ environment: input.environment,
35037
+ watchId: input.watch_id,
35038
+ idempotencyKey: input.idempotency_key
35039
+ }), destructiveAnnotations());
34777
35040
  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({
34778
35041
  projectId: input.project_id,
34779
35042
  ...input.environment === void 0 ? {} : { environment: input.environment }
34780
35043
  }));
34781
- registerProductTool(server, dependencies, "database_webhook_create", "Subscribe one HTTPS receiver to database lifecycle events (database.created, ready, woke, slept, suspended, resized, deleted) of one Dev or Prod database. The signing secret is RETURNED EXACTLY ONCE: store it in a secret manager and never write it into files, notes, source, commit messages or later messages. Deliveries are signed, retried and dead-lettered when retries stop. At most five webhooks exist per project environment. Requires confirmed: true and an idempotency key; the same key with the same request returns the stored answer.", external_exports.object({
35044
+ registerProductTool(server, dependencies, "database_webhook_create", "Subscribe one HTTPS receiver to database lifecycle events (database.created, ready, woke, slept, suspended, resized, deleted) of one Dev or Prod database, or to data events: data.<topic> for the rows of one watched table (the event type database_watch_create returns) or data.* for every watched table; a bare * never matches data events. The signing secret is RETURNED EXACTLY ONCE: store it in a secret manager and never write it into files, notes, source, commit messages or later messages. Deliveries are signed, retried and dead-lettered when retries stop. At most five webhooks exist per project environment. Requires confirmed: true and an idempotency key; the same key with the same request returns the stored answer.", external_exports.object({
34782
35045
  project_id: identifier2,
34783
35046
  environment: databaseEnvironment,
34784
35047
  url: external_exports.string().max(2048).regex(/^https:\/\//u),
34785
- event_types: external_exports.array(external_exports.enum(DATABASE_WEBHOOK_EVENT_TYPES)).min(1).max(16),
35048
+ event_types: external_exports.array(external_exports.union([
35049
+ external_exports.enum(DATABASE_WEBHOOK_EVENT_TYPES),
35050
+ external_exports.string().regex(/^data\.(?:\*|[a-z][a-z0-9_]{0,47})$/u).refine(isDatabaseWebhookEventPattern)
35051
+ ])).min(1).max(16),
34786
35052
  headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
34787
35053
  idempotency_key: idempotencyKey,
34788
35054
  confirmed: external_exports.literal(true)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amerged/ohmyhost-mcp",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
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.44",
3
+ "version": "0.1.45",
4
4
  "private": true,
5
5
  "ohmyhost": {
6
6
  "deployment": "production",
@@ -20,6 +20,10 @@ import {
20
20
  assertDatabaseScheduleRunAccepted,
21
21
  assertDatabaseScheduleRunPage,
22
22
  assertDatabaseWebhookCreated,
23
+ assertDatabaseWatch,
24
+ assertDatabaseWatchDeleted,
25
+ assertDatabaseWatchPage,
26
+ DATABASE_WATCH_DEFAULT_SCHEMA,
23
27
  assertDatabaseWebhookDeleted,
24
28
  assertDatabaseWebhookDeliveryPage,
25
29
  assertDatabaseWebhookPage,
@@ -115,6 +119,9 @@ import {
115
119
  createProjectDatabaseWebhook,
116
120
  listProjectDatabaseWebhooks,
117
121
  deleteProjectDatabaseWebhook,
122
+ createProjectDatabaseWatch,
123
+ listProjectDatabaseWatches,
124
+ deleteProjectDatabaseWatch,
118
125
  testProjectDatabaseWebhook,
119
126
  listProjectDatabaseWebhookDeliveries,
120
127
  replayProjectDatabaseWebhookDelivery,
@@ -849,6 +856,59 @@ export class GeneratedSdkLocalMcpClient implements LocalMcpProductClient {
849
856
  requireScope(value.items.every((webhook) => webhook.environment === environment));
850
857
  return value;
851
858
  }
859
+ public async listDatabaseWatches(
860
+ input: Parameters<LocalMcpProductClient["listDatabaseWatches"]>[0],
861
+ ) {
862
+ const environment = input.environment ?? "dev";
863
+ const value = await listProjectDatabaseWatches(
864
+ { project_id: input.projectId, environment },
865
+ { client: this.#client },
866
+ );
867
+ assertDatabaseWatchPage(value);
868
+ requireScope(value.items.every((watch) => watch.environment === environment));
869
+ return value;
870
+ }
871
+ public async createDatabaseWatch(
872
+ input: Parameters<LocalMcpProductClient["createDatabaseWatch"]>[0],
873
+ ) {
874
+ const value = await createProjectDatabaseWatch(
875
+ {
876
+ project_id: input.projectId,
877
+ "Idempotency-Key": input.idempotencyKey,
878
+ databaseWatchCreateRequest: {
879
+ environment: input.environment,
880
+ ...(input.schema === undefined ? {} : { schema: input.schema }),
881
+ table: input.table,
882
+ columns: [...input.columns],
883
+ ...(input.operations === undefined ? {} : { operations: [...input.operations] }),
884
+ },
885
+ },
886
+ { client: this.#client },
887
+ );
888
+ assertDatabaseWatch(value);
889
+ requireScope(
890
+ value.environment === input.environment &&
891
+ value.table === input.table &&
892
+ value.schema === (input.schema ?? DATABASE_WATCH_DEFAULT_SCHEMA),
893
+ );
894
+ return value;
895
+ }
896
+ public async deleteDatabaseWatch(
897
+ input: Parameters<LocalMcpProductClient["deleteDatabaseWatch"]>[0],
898
+ ) {
899
+ const value = await deleteProjectDatabaseWatch(
900
+ {
901
+ project_id: input.projectId,
902
+ environment: input.environment,
903
+ "Idempotency-Key": input.idempotencyKey,
904
+ watch_id: input.watchId,
905
+ },
906
+ { client: this.#client },
907
+ );
908
+ assertDatabaseWatchDeleted(value);
909
+ requireScope(value.id === input.watchId);
910
+ return value;
911
+ }
852
912
  /** The signing secret exists only in this response and is never stored by the client. */
853
913
  public async createDatabaseWebhook(
854
914
  input: Parameters<LocalMcpProductClient["createDatabaseWebhook"]>[0],