drivethrough-cli 0.1.31 → 0.1.32

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.
Files changed (2) hide show
  1. package/dist/main.mjs +95 -35
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -40013,7 +40013,7 @@ var init_remote_assets = __esm({
40013
40013
  });
40014
40014
 
40015
40015
  // ../mcp-tools/src/output-schemas.ts
40016
- var ok, site, deployResult, dataDoc, dnsRecord, fileEntry, testResult, OUTPUT_SCHEMAS;
40016
+ var ok, site, deployResult, dataDoc, dnsRecord, fileEntry, testResult, renderedRequest, OUTPUT_SCHEMAS;
40017
40017
  var init_output_schemas = __esm({
40018
40018
  "../mcp-tools/src/output-schemas.ts"() {
40019
40019
  "use strict";
@@ -40088,6 +40088,12 @@ var init_output_schemas = __esm({
40088
40088
  error: external_exports.string().nullish(),
40089
40089
  next: external_exports.string().optional()
40090
40090
  });
40091
+ renderedRequest = external_exports.looseObject({
40092
+ method: external_exports.string(),
40093
+ url: external_exports.string(),
40094
+ headers: external_exports.record(external_exports.string(), external_exports.string()),
40095
+ body: external_exports.string().nullable()
40096
+ }).describe("The request exactly as it would be sent, secret values replaced by [secret]");
40091
40097
  OUTPUT_SCHEMAS = {
40092
40098
  deploy_site: external_exports.looseObject({
40093
40099
  ok,
@@ -40255,7 +40261,8 @@ var init_output_schemas = __esm({
40255
40261
  actions: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("Declared actions, their fields and triggering types"),
40256
40262
  schedules: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("Each schedule's enabled, next_at, last_run_at, last_status, failures, disabled_reason"),
40257
40263
  webhooks: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe("Each webhook's url (what the other service POSTs to), verify, secret_set, last_event_at, last_status and setup steps"),
40258
- action_log: external_exports.array(external_exports.unknown()).optional().describe("The latest 20 outcomes, newest first"),
40264
+ action_log: external_exports.array(external_exports.unknown()).optional().describe("The latest 20 outcomes, newest first \u2014 actions, schedules, webhooks and sampled page use of routes"),
40265
+ reference: external_exports.string().optional().describe("With reference: true \u2014 the drivethrough.json integrations format and two complete examples"),
40259
40266
  next: external_exports.string().optional()
40260
40267
  }).describe("Everything declared plus recent outcomes"),
40261
40268
  request_secrets: external_exports.looseObject({
@@ -40271,6 +40278,13 @@ var init_output_schemas = __esm({
40271
40278
  test_route: testResult.describe("One GET through the route"),
40272
40279
  run_action: testResult.describe("One real run of the action"),
40273
40280
  run_schedule: testResult.describe("One real run of the schedule"),
40281
+ preview_action: testResult.extend({ request: renderedRequest.nullish() }).describe("The request the action would send, secrets redacted; nothing was sent"),
40282
+ preview_schedule: testResult.extend({
40283
+ request: renderedRequest.nullish(),
40284
+ next_row_seq: external_exports.number().nullish().describe("rotate: the row the next run would send"),
40285
+ into: external_exports.string().optional().describe("pull: the type a response would be saved as"),
40286
+ map: external_exports.record(external_exports.string(), external_exports.string()).optional().describe("pull: field \u2192 $.path in the response")
40287
+ }).describe("What the next run would send, secrets redacted; nothing was sent"),
40274
40288
  preview_webhook: testResult.extend({
40275
40289
  url: external_exports.string().optional().describe("The URL the other service POSTs to"),
40276
40290
  row: external_exports.record(external_exports.string(), external_exports.unknown()).nullish().describe("The row an event like the sample would store (nothing is written)")
@@ -41156,24 +41170,27 @@ function registerTools(server, ctx) {
41156
41170
  }
41157
41171
  }
41158
41172
  );
41159
- const testTarget = (site2, kind, target, fields) => ctx.api.request("POST", `${sitePath(site2)}/integrations/test`, {
41160
- body: { target, kind, ...fields ? { fields } : {} }
41173
+ const testTarget = (site2, kind, target, opts = {}) => ctx.api.request("POST", `${sitePath(site2)}/integrations/test`, {
41174
+ body: { target, kind, ...opts.fields ? { fields: opts.fields } : {}, ...opts.dry ? { dry: true } : {} }
41161
41175
  });
41162
41176
  const fieldValues = external_exports.record(external_exports.string(), external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]));
41177
+ const liveCheck = "To see a real run, ask the user to use the page once (submit the form, press the button, load the page), then read get_integrations' action_log.";
41163
41178
  server.registerTool(
41164
41179
  "get_integrations",
41165
41180
  {
41166
41181
  outputSchema: OUTPUT_SCHEMAS.get_integrations,
41167
41182
  title: "Get integrations",
41168
- description: `Show a site's connections to other services' APIs (Pro plan) and how they ran: which secrets are set (never their values), the declared routes and actions, each schedule's next run, last result and failures, each webhook's URL and last event, and the latest 20 outcomes. Read-only. THE CONFIGURATION FORMAT \u2014 integrations are declared in the site's drivethrough.json and deployed with deploy_site; once deployed, the platform runs them for the site, and the only operations you can perform on them are this tool, request_secrets, test_route, run_action, run_schedule and preview_webhook. A site can declare: "secrets" names the keys; "routes" proxies same-origin paths under /api/ext/<name> to an upstream, allowing only the listed methods and path prefixes and adding secret headers server-side (\${NAME}); "actions" POST a JSON body ({{field}} reads a row's field) when a type with "on_write" gets a row; "schedules" run every 1h/3h/6h/12h/1d/1w/1mo \u2014 "rotate" sends the next row of a type through an action, "pull" saves a route's JSON response as a new row ("map" uses $.dotted.paths; numeric strings become numbers); "webhooks" receive events another service POSTs to https://<site>/api/hook/<name> \u2014 "verify" is "stripe", "github", "standard" (Standard Webhooks: Resend, Clerk, Svix\u2026), "hmac_sha256" (with "header": the signature header's name) or, only for services that can't sign, "url_token"; "secret" names the declared secret; each verified event is mapped ("map", same $.paths) into a row of "into", which fires that type's on_write. Unverified, duplicate and replayed deliveries are never stored. No cron strings, no times of day, no timezones. Two complete examples \u2014 a read-only brokerage dashboard that also snapshots equity hourly for a chart: \`${INTEGRATION_EXAMPLE_A}\` \u2014 and a display that shows a quote when the owner clicks, and cycles through saved quotes every 3 hours: \`${INTEGRATION_EXAMPLE_B}\`. Every mistake is refused at deploy time with the corrected snippet \u2014 follow it. Keys are set with request_secrets; each piece is checked with test_route, run_action, run_schedule or preview_webhook. WHEN A DEPLOYED ACTION RUNS: when a visitor's form (or the owner's own button) on the page writes a row of a type with "on_write" \u2014 never from write_db, imports or schedule pulls. The only way for you to run one is run_action. Route access defaults to the owner only; set "access": "viewers" or "public" for pages others use. Schedules pause after 10 failures in a row (the owner gets one email); a deploy or a successful run_schedule re-enables them.`,
41183
+ description: `Show a site's connections to other services' APIs (Pro plan) and how they last went: which secrets are set (never their values), the declared routes, actions, schedules (next run, last result, failures) and webhooks (URL, last event), and the latest 20 outcomes, including real page use. Read-only. Pass reference: true for the drivethrough.json format these are declared in, with two complete examples \u2014 read it before adding one to a site.`,
41169
41184
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41170
41185
  inputSchema: {
41171
- site: external_exports.string().describe("Site name or id")
41186
+ site: external_exports.string().describe("Site name or id"),
41187
+ reference: external_exports.boolean().optional().describe("true: also return the drivethrough.json integrations format and two complete examples")
41172
41188
  }
41173
41189
  },
41174
- async ({ site: site2 }) => {
41190
+ async ({ site: site2, reference }) => {
41175
41191
  try {
41176
- return ok2(await ctx.api.request("GET", `${sitePath(site2)}/integrations`));
41192
+ const status = await ctx.api.request("GET", `${sitePath(site2)}/integrations`);
41193
+ return ok2(reference ? { ...status, reference: INTEGRATION_REFERENCE } : status);
41177
41194
  } catch (error62) {
41178
41195
  return toolError(error62);
41179
41196
  }
@@ -41216,7 +41233,7 @@ function registerTools(server, ctx) {
41216
41233
  session,
41217
41234
  set: Object.fromEntries(secrets2.map((s2) => [s2.name, s2.set])),
41218
41235
  all_set: unset.length === 0,
41219
- next: unset.length ? `Still unset: ${unset.join(", ")}. Ask the user to finish on the page \u2014 or, if the link was used or expired, call request_secrets again without a session for a fresh one.` : "Every declared secret is set. Deploy (or redeploy) the site, then check each piece with test_route, run_action, run_schedule or preview_webhook."
41236
+ next: unset.length ? `Still unset: ${unset.join(", ")}. Ask the user to finish on the page \u2014 or, if the link was used or expired, call request_secrets again without a session for a fresh one.` : "Every declared secret is set. Deploy (or redeploy) the site, then check each piece with preview_action, preview_schedule or preview_webhook, and have the user use the page once."
41220
41237
  });
41221
41238
  }
41222
41239
  const link = await ctx.api.request("POST", `${sitePath(site2)}/secrets/link`, {
@@ -41231,6 +41248,69 @@ function registerTools(server, ctx) {
41231
41248
  }
41232
41249
  }
41233
41250
  );
41251
+ server.registerTool(
41252
+ "preview_action",
41253
+ {
41254
+ outputSchema: OUTPUT_SCHEMAS.preview_action,
41255
+ title: "Preview action",
41256
+ description: `Show the exact request one of a site's declared actions would send for a sample row (Pro plan) \u2014 method, URL, headers and JSON body, with every secret value redacted \u2014 or why it can't be built (a secret not set, a bad URL). Nothing is sent, stored or charged. Check that the {{field}} values landed where they should. ${liveCheck}`,
41257
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41258
+ inputSchema: {
41259
+ site: external_exports.string().describe("Site name or id"),
41260
+ action: external_exports.string().describe(`The action's name under "actions" in drivethrough.json`),
41261
+ fields: fieldValues.describe("Sample row values for its {{field}} references; {} when it uses none")
41262
+ }
41263
+ },
41264
+ async ({ site: site2, action, fields }) => {
41265
+ try {
41266
+ return ok2(await testTarget(site2, "action", action, { fields, dry: true }));
41267
+ } catch (error62) {
41268
+ return toolError(error62);
41269
+ }
41270
+ }
41271
+ );
41272
+ server.registerTool(
41273
+ "preview_schedule",
41274
+ {
41275
+ outputSchema: OUTPUT_SCHEMAS.preview_schedule,
41276
+ title: "Preview schedule",
41277
+ description: `Show what a site's declared schedule would send on its next run (Pro plan), with every secret value redacted: for "rotate", the row that comes next and the request its action would send; for "pull", the GET it would make and the type and map its response would fill. Nothing is sent, stored or charged, and the rotation doesn't move. get_integrations shows when it next runs and how its last run went.`,
41278
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41279
+ inputSchema: {
41280
+ site: external_exports.string().describe("Site name or id"),
41281
+ schedule: external_exports.string().describe(`The schedule's name under "schedules" in drivethrough.json`)
41282
+ }
41283
+ },
41284
+ async ({ site: site2, schedule }) => {
41285
+ try {
41286
+ return ok2(await testTarget(site2, "schedule", schedule, { dry: true }));
41287
+ } catch (error62) {
41288
+ return toolError(error62);
41289
+ }
41290
+ }
41291
+ );
41292
+ server.registerTool(
41293
+ "preview_webhook",
41294
+ {
41295
+ outputSchema: OUTPUT_SCHEMAS.preview_webhook,
41296
+ title: "Preview webhook",
41297
+ description: `Dry-run one of a site's declared webhooks (Pro plan): builds an event from sample field values, takes it through the same mapping and validation a real delivery gets, and returns the row it would store and the URL the other service must POST to. Nothing is stored, sent or charged. Real delivery is proven by the other service's own "send test event", then get_integrations (last event).`,
41298
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41299
+ inputSchema: {
41300
+ site: external_exports.string().describe("Site name or id"),
41301
+ webhook: external_exports.string().describe(`The webhook's name under "webhooks" in drivethrough.json (the <name> in /api/hook/<name>)`),
41302
+ fields: fieldValues.describe("Sample values for the fields its map fills")
41303
+ }
41304
+ },
41305
+ async ({ site: site2, webhook, fields }) => {
41306
+ try {
41307
+ return ok2(await testTarget(site2, "webhook", `/api/hook/${webhook.replace(/^\/?api\/hook\//, "")}`, { fields }));
41308
+ } catch (error62) {
41309
+ return toolError(error62);
41310
+ }
41311
+ }
41312
+ );
41313
+ if (!canDeployDirectory) return;
41234
41314
  server.registerTool(
41235
41315
  "test_route",
41236
41316
  {
@@ -41258,7 +41338,7 @@ function registerTools(server, ctx) {
41258
41338
  {
41259
41339
  outputSchema: OUTPUT_SCHEMAS.run_action,
41260
41340
  title: "Run action",
41261
- description: `Run one of a site's declared actions now, for real (Pro plan): it sends the action's POST to the other service with fields standing in for the row that would normally trigger it, and returns that service's HTTP status and a redacted preview of its response. The other service acts on it as it would on any request \u2014 a display shows the text, a message is sent \u2014 so confirm with the user before running anything that can't be taken back. This is the way to trigger an action by hand: write_db, imports and schedule pulls never fire actions.`,
41341
+ description: `Run one of a site's declared actions now, for real (Pro plan): it sends the action's POST to the other service with fields standing in for the row that would normally trigger it, and returns that service's HTTP status and a redacted preview of its response. The other service acts on it as it would on any request \u2014 a display shows the text, a message is sent \u2014 so confirm with the user before running anything that can't be taken back. preview_action shows the same request without sending it.`,
41262
41342
  // Destructive + open-world: the other service may act irreversibly on the request.
41263
41343
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
41264
41344
  inputSchema: {
@@ -41269,7 +41349,7 @@ function registerTools(server, ctx) {
41269
41349
  },
41270
41350
  async ({ site: site2, action, fields }) => {
41271
41351
  try {
41272
- return ok2(await testTarget(site2, "action", action, fields));
41352
+ return ok2(await testTarget(site2, "action", action, { fields }));
41273
41353
  } catch (error62) {
41274
41354
  return toolError(error62);
41275
41355
  }
@@ -41280,7 +41360,7 @@ function registerTools(server, ctx) {
41280
41360
  {
41281
41361
  outputSchema: OUTPUT_SCHEMAS.run_schedule,
41282
41362
  title: "Run schedule",
41283
- description: `Run one of a site's declared schedules once, now, for real (Pro plan), and return the outcome. A "pull" schedule calls its route and saves the response as a new row; a "rotate" schedule sends the next row through its action (the other service acts on it) and advances the rotation. A successful run also re-enables a schedule that was paused after repeated failures. The regular timetable is unchanged.`,
41363
+ description: `Run one of a site's declared schedules once, now, for real (Pro plan), and return the outcome. A "pull" schedule calls its route and saves the response as a new row; a "rotate" schedule sends the next row through its action (the other service acts on it) and advances the rotation. A successful run also re-enables a schedule that was paused after repeated failures. The regular timetable is unchanged. preview_schedule shows the same run without sending anything.`,
41284
41364
  // Destructive + open-world: a rotate run fires an action the other service acts on.
41285
41365
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
41286
41366
  inputSchema: {
@@ -41296,29 +41376,8 @@ function registerTools(server, ctx) {
41296
41376
  }
41297
41377
  }
41298
41378
  );
41299
- server.registerTool(
41300
- "preview_webhook",
41301
- {
41302
- outputSchema: OUTPUT_SCHEMAS.preview_webhook,
41303
- title: "Preview webhook",
41304
- description: `Dry-run one of a site's declared webhooks (Pro plan): builds an event from sample field values, takes it through the same mapping and validation a real delivery gets, and returns the row it would store and the URL the other service must POST to. Nothing is stored, sent or charged. Real delivery is proven by the other service's own "send test event", then get_integrations (last event).`,
41305
- annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
41306
- inputSchema: {
41307
- site: external_exports.string().describe("Site name or id"),
41308
- webhook: external_exports.string().describe(`The webhook's name under "webhooks" in drivethrough.json (the <name> in /api/hook/<name>)`),
41309
- fields: fieldValues.describe("Sample values for the fields its map fills")
41310
- }
41311
- },
41312
- async ({ site: site2, webhook, fields }) => {
41313
- try {
41314
- return ok2(await testTarget(site2, "webhook", `/api/hook/${webhook.replace(/^\/?api\/hook\//, "")}`, fields));
41315
- } catch (error62) {
41316
- return toolError(error62);
41317
- }
41318
- }
41319
- );
41320
41379
  }
41321
- var MAX_BUNDLE_MB, SERVER_INSTRUCTIONS, INTEGRATION_EXAMPLE_A, INTEGRATION_EXAMPLE_B;
41380
+ var MAX_BUNDLE_MB, SERVER_INSTRUCTIONS, INTEGRATION_EXAMPLE_A, INTEGRATION_EXAMPLE_B, INTEGRATION_REFERENCE;
41322
41381
  var init_tools = __esm({
41323
41382
  "../mcp-tools/src/tools.ts"() {
41324
41383
  "use strict";
@@ -41399,7 +41458,7 @@ RECIPE \u2014 a searchable, filterable list (catalogue, directory, archive)
41399
41458
  whose query the config doesn't declare (it will render empty for visitors),
41400
41459
  and a declaration no page uses (pure cost on every insert).
41401
41460
 
41402
- Connecting a site to another service's API (Pro plan): this is site configuration, not something you run. The site's drivethrough.json declares it and deploy_site deploys it, like any other file; the format and two complete examples are in get_integrations' description. Every integration operation you can perform is its own tool: get_integrations (what is declared and how it ran), request_secrets (a one-time link where the user sets API keys \u2014 never ask for a key in the chat), test_route, run_action, run_schedule (one real run each) and preview_webhook (a dry run). Check each piece with them before telling the user it works.
41461
+ Connecting a site to another service's API (Pro plan): this is site configuration, not something you run. The site's drivethrough.json declares it and deploy_site deploys it, like any other file; get_integrations {reference: true} returns the format with two complete examples. The integration tools: get_integrations (what is declared and how it went), request_secrets (a one-time link where the user sets API keys \u2014 never ask for a key in the chat), and preview_action, preview_schedule, preview_webhook (show what would be sent or stored; nothing is sent). Preview each piece, then have the user use the page once and confirm the outcome in get_integrations before telling them it works.
41403
41462
 
41404
41463
  Custom domains (paid plans): manage_domain attaches a domain the user already owns. After attaching, check_domain {action: "connect"} looks up one-click setup at their registrar (relay the URL when offered, else the exact DNS records from attach), and check_domain {action: "status"} polls until "active" \u2014 DNS seen, then TLS issued.`;
41405
41464
  INTEGRATION_EXAMPLE_A = JSON.stringify({
@@ -41438,6 +41497,7 @@ Custom domains (paid plans): manage_domain attaches a domain the user already ow
41438
41497
  },
41439
41498
  schedules: { cycle: { every: "3h", rotate: "quote", action: "show" } }
41440
41499
  });
41500
+ INTEGRATION_REFERENCE = `Integrations are declared in the site's drivethrough.json and deployed with deploy_site, like any other file. Keys: "secrets" lists the key names (values are set by the user via request_secrets, never in chat or a file). "routes" maps a same-origin path under /api/ext/<name> to an upstream, allowing only the listed "methods" and "paths" prefixes, adding "headers" server-side (\${NAME} inserts a secret); "access" is "owner" (default), "viewers" or "public"; "cache" and "query" are optional. "actions" declare a POST ("post" URL, "headers", JSON "body" where {{field}} inserts a row's field); a type with "on_write": "<action>" sends it when a visitor's form or the owner's button on the page saves a row \u2014 never for write_db, imports or schedule pulls. "schedules" repeat every 1h/3h/6h/12h/1d/1w/1mo (no cron strings, times of day or timezones): "rotate": "<type>" with "action" sends the next row of the type through the action; "pull": {route, path, into, map} saves a route's JSON response as a new row ("map" uses $.dotted.paths; numeric strings become numbers). They pause after 10 failures in a row (the owner gets one email) until the next deploy. "webhooks" receive events another service POSTs to https://<site>/api/hook/<name>: "verify" is "stripe", "github", "standard" (Standard Webhooks: Resend, Clerk, Svix\u2026), "hmac_sha256" (with "header": the signature header's name) or, only for services that can't sign, "url_token"; "secret" names the declared secret; "map" (same $.paths) fills a row of "into". Unverified, duplicate and replayed deliveries are never stored. Every mistake is refused at deploy time with the corrected snippet \u2014 follow it. Two complete examples \u2014 a read-only brokerage dashboard that also snapshots equity hourly for a chart: ${INTEGRATION_EXAMPLE_A} \u2014 and a display that shows a quote when the owner clicks, and cycles through saved quotes every 3 hours: ${INTEGRATION_EXAMPLE_B}.`;
41441
41501
  }
41442
41502
  });
41443
41503
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drivethrough-cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.32",
4
4
  "description": "Agent-first hosting CLI. Say what you want. Stay in the car.",
5
5
  "type": "module",
6
6
  "license": "MIT",