drivethrough-cli 0.1.31 → 0.1.33
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 +99 -36
- 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
|
}
|
|
@@ -41185,7 +41202,10 @@ function registerTools(server, ctx) {
|
|
|
41185
41202
|
outputSchema: OUTPUT_SCHEMAS.request_secrets,
|
|
41186
41203
|
title: "Request secrets",
|
|
41187
41204
|
description: `Get a one-time link where the user sets the API keys a site's integrations need (Pro plan) \u2014 two steps, like create_upload. Call it with the site: it returns a link (10 minutes, one use, no sign-in). Send it to the user as a clickable link and have them paste the keys there. NEVER ask for a key in the chat, and never put one in a file \u2014 this tool never receives or returns a key's value. When they say they're done, call again with the returned session: it reports which names are set. A deploy that needs an unset secret is refused with a fresh link already in the error \u2014 relay that one instead. Pass names to rotate a key that is already set. A webhook's secret comes FROM the other service: tell the user to add a webhook there pointing at its URL (get_integrations and the deploy error give it) and paste the signing secret it shows into the link page; a url_token secret is generated on the link page, which then shows the full URL to paste.`,
|
|
41188
|
-
|
|
41205
|
+
// Destructive: a link can replace a key that is already set, and the old
|
|
41206
|
+
// value is gone once the user saves (OpenAI's guidelines reserve false
|
|
41207
|
+
// for purely additive writes).
|
|
41208
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
41189
41209
|
inputSchema: {
|
|
41190
41210
|
site: external_exports.string().describe("Site name or id"),
|
|
41191
41211
|
names: external_exports.array(external_exports.string()).optional().describe("Which secret names the link asks for. Default: every declared secret that is still unset. Include a set one to replace (rotate) it."),
|
|
@@ -41216,7 +41236,7 @@ function registerTools(server, ctx) {
|
|
|
41216
41236
|
session,
|
|
41217
41237
|
set: Object.fromEntries(secrets2.map((s2) => [s2.name, s2.set])),
|
|
41218
41238
|
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
|
|
41239
|
+
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
41240
|
});
|
|
41221
41241
|
}
|
|
41222
41242
|
const link = await ctx.api.request("POST", `${sitePath(site2)}/secrets/link`, {
|
|
@@ -41231,6 +41251,69 @@ function registerTools(server, ctx) {
|
|
|
41231
41251
|
}
|
|
41232
41252
|
}
|
|
41233
41253
|
);
|
|
41254
|
+
server.registerTool(
|
|
41255
|
+
"preview_action",
|
|
41256
|
+
{
|
|
41257
|
+
outputSchema: OUTPUT_SCHEMAS.preview_action,
|
|
41258
|
+
title: "Preview action",
|
|
41259
|
+
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}`,
|
|
41260
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
41261
|
+
inputSchema: {
|
|
41262
|
+
site: external_exports.string().describe("Site name or id"),
|
|
41263
|
+
action: external_exports.string().describe(`The action's name under "actions" in drivethrough.json`),
|
|
41264
|
+
fields: fieldValues.describe("Sample row values for its {{field}} references; {} when it uses none")
|
|
41265
|
+
}
|
|
41266
|
+
},
|
|
41267
|
+
async ({ site: site2, action, fields }) => {
|
|
41268
|
+
try {
|
|
41269
|
+
return ok2(await testTarget(site2, "action", action, { fields, dry: true }));
|
|
41270
|
+
} catch (error62) {
|
|
41271
|
+
return toolError(error62);
|
|
41272
|
+
}
|
|
41273
|
+
}
|
|
41274
|
+
);
|
|
41275
|
+
server.registerTool(
|
|
41276
|
+
"preview_schedule",
|
|
41277
|
+
{
|
|
41278
|
+
outputSchema: OUTPUT_SCHEMAS.preview_schedule,
|
|
41279
|
+
title: "Preview schedule",
|
|
41280
|
+
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.`,
|
|
41281
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
41282
|
+
inputSchema: {
|
|
41283
|
+
site: external_exports.string().describe("Site name or id"),
|
|
41284
|
+
schedule: external_exports.string().describe(`The schedule's name under "schedules" in drivethrough.json`)
|
|
41285
|
+
}
|
|
41286
|
+
},
|
|
41287
|
+
async ({ site: site2, schedule }) => {
|
|
41288
|
+
try {
|
|
41289
|
+
return ok2(await testTarget(site2, "schedule", schedule, { dry: true }));
|
|
41290
|
+
} catch (error62) {
|
|
41291
|
+
return toolError(error62);
|
|
41292
|
+
}
|
|
41293
|
+
}
|
|
41294
|
+
);
|
|
41295
|
+
server.registerTool(
|
|
41296
|
+
"preview_webhook",
|
|
41297
|
+
{
|
|
41298
|
+
outputSchema: OUTPUT_SCHEMAS.preview_webhook,
|
|
41299
|
+
title: "Preview webhook",
|
|
41300
|
+
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).`,
|
|
41301
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
41302
|
+
inputSchema: {
|
|
41303
|
+
site: external_exports.string().describe("Site name or id"),
|
|
41304
|
+
webhook: external_exports.string().describe(`The webhook's name under "webhooks" in drivethrough.json (the <name> in /api/hook/<name>)`),
|
|
41305
|
+
fields: fieldValues.describe("Sample values for the fields its map fills")
|
|
41306
|
+
}
|
|
41307
|
+
},
|
|
41308
|
+
async ({ site: site2, webhook, fields }) => {
|
|
41309
|
+
try {
|
|
41310
|
+
return ok2(await testTarget(site2, "webhook", `/api/hook/${webhook.replace(/^\/?api\/hook\//, "")}`, { fields }));
|
|
41311
|
+
} catch (error62) {
|
|
41312
|
+
return toolError(error62);
|
|
41313
|
+
}
|
|
41314
|
+
}
|
|
41315
|
+
);
|
|
41316
|
+
if (!canDeployDirectory) return;
|
|
41234
41317
|
server.registerTool(
|
|
41235
41318
|
"test_route",
|
|
41236
41319
|
{
|
|
@@ -41258,7 +41341,7 @@ function registerTools(server, ctx) {
|
|
|
41258
41341
|
{
|
|
41259
41342
|
outputSchema: OUTPUT_SCHEMAS.run_action,
|
|
41260
41343
|
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.
|
|
41344
|
+
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
41345
|
// Destructive + open-world: the other service may act irreversibly on the request.
|
|
41263
41346
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
41264
41347
|
inputSchema: {
|
|
@@ -41269,7 +41352,7 @@ function registerTools(server, ctx) {
|
|
|
41269
41352
|
},
|
|
41270
41353
|
async ({ site: site2, action, fields }) => {
|
|
41271
41354
|
try {
|
|
41272
|
-
return ok2(await testTarget(site2, "action", action, fields));
|
|
41355
|
+
return ok2(await testTarget(site2, "action", action, { fields }));
|
|
41273
41356
|
} catch (error62) {
|
|
41274
41357
|
return toolError(error62);
|
|
41275
41358
|
}
|
|
@@ -41280,7 +41363,7 @@ function registerTools(server, ctx) {
|
|
|
41280
41363
|
{
|
|
41281
41364
|
outputSchema: OUTPUT_SCHEMAS.run_schedule,
|
|
41282
41365
|
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.`,
|
|
41366
|
+
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
41367
|
// Destructive + open-world: a rotate run fires an action the other service acts on.
|
|
41285
41368
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
41286
41369
|
inputSchema: {
|
|
@@ -41296,29 +41379,8 @@ function registerTools(server, ctx) {
|
|
|
41296
41379
|
}
|
|
41297
41380
|
}
|
|
41298
41381
|
);
|
|
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
41382
|
}
|
|
41321
|
-
var MAX_BUNDLE_MB, SERVER_INSTRUCTIONS, INTEGRATION_EXAMPLE_A, INTEGRATION_EXAMPLE_B;
|
|
41383
|
+
var MAX_BUNDLE_MB, SERVER_INSTRUCTIONS, INTEGRATION_EXAMPLE_A, INTEGRATION_EXAMPLE_B, INTEGRATION_REFERENCE;
|
|
41322
41384
|
var init_tools = __esm({
|
|
41323
41385
|
"../mcp-tools/src/tools.ts"() {
|
|
41324
41386
|
"use strict";
|
|
@@ -41399,7 +41461,7 @@ RECIPE \u2014 a searchable, filterable list (catalogue, directory, archive)
|
|
|
41399
41461
|
whose query the config doesn't declare (it will render empty for visitors),
|
|
41400
41462
|
and a declaration no page uses (pure cost on every insert).
|
|
41401
41463
|
|
|
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
|
|
41464
|
+
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
41465
|
|
|
41404
41466
|
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
41467
|
INTEGRATION_EXAMPLE_A = JSON.stringify({
|
|
@@ -41438,6 +41500,7 @@ Custom domains (paid plans): manage_domain attaches a domain the user already ow
|
|
|
41438
41500
|
},
|
|
41439
41501
|
schedules: { cycle: { every: "3h", rotate: "quote", action: "show" } }
|
|
41440
41502
|
});
|
|
41503
|
+
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
41504
|
}
|
|
41442
41505
|
});
|
|
41443
41506
|
|