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.
- package/dist/main.mjs +95 -35
- 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,
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
|