@sakupa/mcp 1.4.1 → 1.5.0
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/bin.js +224 -12
- package/dist/index.js +224 -12
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -403,7 +403,7 @@ function isFreeSiteAllowanceNetworkReference(value) {
|
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
// ../core/dist/domain/version.js
|
|
406
|
-
var SAKUPA_MCP_VERSION = "1.
|
|
406
|
+
var SAKUPA_MCP_VERSION = "1.5.0";
|
|
407
407
|
|
|
408
408
|
// ../core/dist/domain/errors.js
|
|
409
409
|
var HTTP_STATUS = {
|
|
@@ -2208,15 +2208,15 @@ function strFromU8(dat, latin1) {
|
|
|
2208
2208
|
var slzh = function(d, b) {
|
|
2209
2209
|
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
2210
2210
|
};
|
|
2211
|
-
var zh = function(d, b,
|
|
2211
|
+
var zh = function(d, b, z8) {
|
|
2212
2212
|
var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
|
|
2213
|
-
var _a2 = z64hs(d, es, efl,
|
|
2213
|
+
var _a2 = z64hs(d, es, efl, z8, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a2[0], su = _a2[1], off = _a2[2];
|
|
2214
2214
|
return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
|
|
2215
2215
|
};
|
|
2216
|
-
var z64hs = function(d, b, l,
|
|
2216
|
+
var z64hs = function(d, b, l, z8, sc, su, off) {
|
|
2217
2217
|
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
2218
2218
|
var nf = nsc + nsu + noff;
|
|
2219
|
-
if (
|
|
2219
|
+
if (z8 && nf) {
|
|
2220
2220
|
for (; b + 4 < e; b += 4 + b2(d, b + 2)) {
|
|
2221
2221
|
if (b2(d, b) == 1) {
|
|
2222
2222
|
return [
|
|
@@ -2227,7 +2227,7 @@ var z64hs = function(d, b, l, z7, sc, su, off) {
|
|
|
2227
2227
|
];
|
|
2228
2228
|
}
|
|
2229
2229
|
}
|
|
2230
|
-
if (
|
|
2230
|
+
if (z8 < 2)
|
|
2231
2231
|
err(13);
|
|
2232
2232
|
}
|
|
2233
2233
|
return [sc, su, off, 0];
|
|
@@ -2244,18 +2244,18 @@ function unzipSync(data, opts) {
|
|
|
2244
2244
|
if (!c)
|
|
2245
2245
|
return {};
|
|
2246
2246
|
var o = b4(data, e + 16);
|
|
2247
|
-
var
|
|
2248
|
-
if (
|
|
2247
|
+
var z8 = b4(data, e - 20) == 117853008;
|
|
2248
|
+
if (z8) {
|
|
2249
2249
|
var ze = b4(data, e - 12);
|
|
2250
|
-
|
|
2251
|
-
if (
|
|
2250
|
+
z8 = b4(data, ze) == 101075792;
|
|
2251
|
+
if (z8) {
|
|
2252
2252
|
c = b4(data, ze + 32);
|
|
2253
2253
|
o = b4(data, ze + 48);
|
|
2254
2254
|
}
|
|
2255
2255
|
}
|
|
2256
2256
|
var fltr = opts && opts.filter;
|
|
2257
2257
|
for (var i = 0; i < c; ++i) {
|
|
2258
|
-
var _a2 = zh(data, o,
|
|
2258
|
+
var _a2 = zh(data, o, z8), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
|
|
2259
2259
|
o = no;
|
|
2260
2260
|
if (!fltr || fltr({
|
|
2261
2261
|
name: fn,
|
|
@@ -3436,7 +3436,8 @@ var TARGET_MCP_TOOL_NAMES = [
|
|
|
3436
3436
|
"change",
|
|
3437
3437
|
"support",
|
|
3438
3438
|
"report",
|
|
3439
|
-
"apps"
|
|
3439
|
+
"apps",
|
|
3440
|
+
"delete"
|
|
3440
3441
|
];
|
|
3441
3442
|
var STRUCTURED_TOOL_OUTPUT_SCHEMA = z.object({
|
|
3442
3443
|
schemaVersion: z.literal(1),
|
|
@@ -6170,6 +6171,7 @@ var TOOL_TOPICS = [
|
|
|
6170
6171
|
"support",
|
|
6171
6172
|
"report",
|
|
6172
6173
|
"apps",
|
|
6174
|
+
"delete",
|
|
6173
6175
|
"help"
|
|
6174
6176
|
];
|
|
6175
6177
|
var HELP_TOPICS = ["diagnose", "overview", "terminology", ...TOOL_TOPICS];
|
|
@@ -6280,6 +6282,18 @@ var TOOL_MANUALS = {
|
|
|
6280
6282
|
nextStep: "Use the preview resumeWith arguments only after the user confirms.",
|
|
6281
6283
|
terminology: ["credentialRotation"]
|
|
6282
6284
|
},
|
|
6285
|
+
delete: {
|
|
6286
|
+
purpose: "Delete the bound FREE site after an explicit preview and confirmation.",
|
|
6287
|
+
sideEffects: "Preview is read-only. Confirmed deletion removes content, the public URL, installed apps, stored submissions and the local .sakupa/site.json.",
|
|
6288
|
+
preconditions: "A valid local site credential and a free site. A subscribed site is refused until the subscription ends (change \u2192 period-end cancellation) and the site reverts to free.",
|
|
6289
|
+
parameterNames: ["confirmed", "operationId", "confirmation"],
|
|
6290
|
+
parameters: "No arguments for the preview; confirmed=true with the exact operationId and confirmation copied from the preview decision.",
|
|
6291
|
+
warnings: [
|
|
6292
|
+
"Irreversible; the next deploy creates a brand-new site with a new URL.",
|
|
6293
|
+
"Never propose delete to free up the free-site allowance; use the deploy handoff decision instead."
|
|
6294
|
+
],
|
|
6295
|
+
nextStep: "Present every decision option; after confirmation copy the option arguments verbatim."
|
|
6296
|
+
},
|
|
6283
6297
|
plans: {
|
|
6284
6298
|
purpose: "Read the authoritative hosting plan catalog and rules.",
|
|
6285
6299
|
sideEffects: "Read-only public API request.",
|
|
@@ -7258,6 +7272,196 @@ function registerAppsTools(server, baseCtx) {
|
|
|
7258
7272
|
);
|
|
7259
7273
|
}
|
|
7260
7274
|
|
|
7275
|
+
// src/tools/delete.ts
|
|
7276
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
7277
|
+
import { z as z7 } from "zod";
|
|
7278
|
+
var confirmationSchema = z7.object({
|
|
7279
|
+
siteId: z7.string(),
|
|
7280
|
+
expectedSiteUpdatedAt: z7.string(),
|
|
7281
|
+
expectedStatus: z7.string(),
|
|
7282
|
+
expectedMode: z7.enum(["free", "paid"]),
|
|
7283
|
+
expectedServingMode: z7.string(),
|
|
7284
|
+
expectedShortId: z7.string().optional(),
|
|
7285
|
+
expectedSubscriptionStatus: z7.string().optional(),
|
|
7286
|
+
expectedPlan: z7.string().optional(),
|
|
7287
|
+
expectedCancelAtPeriodEnd: z7.boolean().optional(),
|
|
7288
|
+
expectedCurrentPeriodEnd: z7.string().optional(),
|
|
7289
|
+
expectedLastDeploymentId: z7.string().optional(),
|
|
7290
|
+
expectedBoundHostnames: z7.array(z7.string()),
|
|
7291
|
+
acknowledge: z7.literal("delete_and_cancel_renewal")
|
|
7292
|
+
});
|
|
7293
|
+
function registerDeleteTools(server, baseCtx) {
|
|
7294
|
+
server.registerTool(
|
|
7295
|
+
"delete",
|
|
7296
|
+
{
|
|
7297
|
+
title: "Delete site",
|
|
7298
|
+
description: "Delete the site bound to this project. Without arguments it only previews (zero writes) and returns a decision; after the user confirms, call again with the exact preview arguments (confirmed: true, operationId, confirmation). A FREE site is deleted immediately: content, public URL, installed apps and stored submissions are gone and the local .sakupa/site.json is removed, so the next deploy creates a brand-new site. A subscribed site cannot be deleted: end the subscription first (change \u2192 period-end cancellation on Stripe); when it ends the site reverts to a free site and delete becomes available. Owner-only.",
|
|
7299
|
+
inputSchema: z7.object({
|
|
7300
|
+
confirmed: z7.boolean().optional().describe("true only from the exact decision arguments returned by the preview."),
|
|
7301
|
+
operationId: z7.string().optional().describe("Copied verbatim from the preview."),
|
|
7302
|
+
confirmation: confirmationSchema.optional().describe("Copied verbatim from the preview; the cloud rejects any drift.")
|
|
7303
|
+
}),
|
|
7304
|
+
outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
|
|
7305
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
|
|
7306
|
+
},
|
|
7307
|
+
withDecisionReentry("delete", async (args, call) => {
|
|
7308
|
+
try {
|
|
7309
|
+
const ctx = await withProjectDir(baseCtx, call);
|
|
7310
|
+
const site = requireSiteFile(ctx);
|
|
7311
|
+
if (args.confirmed === true) {
|
|
7312
|
+
if (!args.operationId || !args.confirmation) {
|
|
7313
|
+
return structuredToolResult({
|
|
7314
|
+
schemaVersion: 1,
|
|
7315
|
+
outcome: "blocked",
|
|
7316
|
+
resultCode: "delete_preview_required",
|
|
7317
|
+
summary: summaryMarkdown({
|
|
7318
|
+
title: "Nothing was deleted \u2014 run the preview first",
|
|
7319
|
+
lead: "A confirmed delete must carry the exact operationId and confirmation from the preview. Call delete with no arguments, show the preview to the user, then reuse its decision arguments.",
|
|
7320
|
+
next: ["`delete` with no arguments"]
|
|
7321
|
+
}),
|
|
7322
|
+
data: { siteId: site.siteId },
|
|
7323
|
+
nextActions: [{ tool: "delete", arguments: {}, allowed: true }]
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
7326
|
+
const result = await ctx.client.deleteSite(site.siteId, site.credential, {
|
|
7327
|
+
operationId: args.operationId,
|
|
7328
|
+
confirmation: args.confirmation
|
|
7329
|
+
});
|
|
7330
|
+
deleteSiteFile(ctx.projectDir);
|
|
7331
|
+
removeCreation(site.siteId);
|
|
7332
|
+
return structuredToolResult({
|
|
7333
|
+
schemaVersion: 1,
|
|
7334
|
+
outcome: "completed",
|
|
7335
|
+
resultCode: result.alreadyDeleted ? "site_already_deleted" : "site_deleted",
|
|
7336
|
+
summary: summaryMarkdown({
|
|
7337
|
+
title: result.alreadyDeleted ? "Site was already deleted" : "Site deleted",
|
|
7338
|
+
lead: `${site.url ?? site.siteId} no longer serves anything (visitors get HTTP 410). The local credential file .sakupa/site.json was removed; this project is no longer bound to any site.`,
|
|
7339
|
+
facts: [
|
|
7340
|
+
["Site ID", result.siteId],
|
|
7341
|
+
["Public URL released", site.url],
|
|
7342
|
+
[
|
|
7343
|
+
"Custom hostnames removed",
|
|
7344
|
+
result.removedHostnames.length ? result.removedHostnames.join(", ") : "none"
|
|
7345
|
+
],
|
|
7346
|
+
["Serving cleanup still pending", result.servingDeletionPending]
|
|
7347
|
+
],
|
|
7348
|
+
notes: [
|
|
7349
|
+
"Installed apps and stored form submissions were deleted with the site.",
|
|
7350
|
+
"The free-site allowance this site used on its deployment network is released."
|
|
7351
|
+
],
|
|
7352
|
+
next: ["`deploy` to publish a brand-new site (new URL, new credential)"]
|
|
7353
|
+
}),
|
|
7354
|
+
data: {
|
|
7355
|
+
...result,
|
|
7356
|
+
projectDir: ctx.projectDir,
|
|
7357
|
+
localSiteFileRemoved: true,
|
|
7358
|
+
releasedUrl: site.url
|
|
7359
|
+
},
|
|
7360
|
+
nextActions: []
|
|
7361
|
+
});
|
|
7362
|
+
}
|
|
7363
|
+
const operationId = randomUUID7();
|
|
7364
|
+
const preview = await ctx.client.previewDeleteSite(site.siteId, site.credential, {
|
|
7365
|
+
operationId
|
|
7366
|
+
});
|
|
7367
|
+
const c = preview.consequences;
|
|
7368
|
+
if (c.requiresFreeModeBeforeDelete) {
|
|
7369
|
+
const cancellationScheduled = preview.confirmation.expectedCancelAtPeriodEnd === true;
|
|
7370
|
+
return structuredToolResult({
|
|
7371
|
+
schemaVersion: 1,
|
|
7372
|
+
outcome: "blocked",
|
|
7373
|
+
resultCode: "delete_requires_free_mode",
|
|
7374
|
+
summary: summaryMarkdown({
|
|
7375
|
+
title: "This site is subscription-backed \u2014 it cannot be deleted yet",
|
|
7376
|
+
lead: "Nothing was changed. Sakupa deletes only FREE sites. End the subscription first; when the paid period ends the site automatically becomes a free 30-day site, and delete becomes available.",
|
|
7377
|
+
facts: [
|
|
7378
|
+
["Site", site.url ?? site.siteId],
|
|
7379
|
+
["Plan", preview.confirmation.expectedPlan],
|
|
7380
|
+
["Period-end cancellation already scheduled", cancellationScheduled ? "yes" : "no"],
|
|
7381
|
+
[
|
|
7382
|
+
"Paid access ends",
|
|
7383
|
+
c.currentPaidAccessEndsAt ? timestampForAgent(c.currentPaidAccessEndsAt) : void 0
|
|
7384
|
+
],
|
|
7385
|
+
[
|
|
7386
|
+
"Custom hostnames removed when it reverts",
|
|
7387
|
+
c.removesCustomHostnames.join(", ") || "none"
|
|
7388
|
+
]
|
|
7389
|
+
],
|
|
7390
|
+
steps: cancellationScheduled ? [
|
|
7391
|
+
"Wait for the paid period to end; Sakupa then reverts the site to a free 30-day site (content kept, custom domains removed).",
|
|
7392
|
+
"Call delete again at that point \u2014 or simply let the free site expire."
|
|
7393
|
+
] : [
|
|
7394
|
+
"Call change and open the Stripe-hosted link; the user chooses period-end cancellation there (Sakupa never cancels on its own).",
|
|
7395
|
+
"When the paid period ends, Sakupa reverts the site to a free 30-day site (content kept, custom domains removed).",
|
|
7396
|
+
"Call delete again at that point \u2014 or simply let the free site expire."
|
|
7397
|
+
],
|
|
7398
|
+
next: cancellationScheduled ? ["`billing`"] : ["`change`", "`billing`"]
|
|
7399
|
+
}),
|
|
7400
|
+
data: {
|
|
7401
|
+
siteId: site.siteId,
|
|
7402
|
+
operationId: preview.operationId,
|
|
7403
|
+
consequences: c,
|
|
7404
|
+
cancellationScheduled
|
|
7405
|
+
},
|
|
7406
|
+
nextActions: [
|
|
7407
|
+
{ tool: cancellationScheduled ? "billing" : "change", arguments: {}, allowed: true }
|
|
7408
|
+
]
|
|
7409
|
+
});
|
|
7410
|
+
}
|
|
7411
|
+
const confirmArguments = {
|
|
7412
|
+
confirmed: true,
|
|
7413
|
+
operationId: preview.operationId,
|
|
7414
|
+
confirmation: preview.confirmation
|
|
7415
|
+
};
|
|
7416
|
+
return presentDecision(baseCtx.decisions, call, "delete", {
|
|
7417
|
+
resultCode: "delete_confirmation_required",
|
|
7418
|
+
summary: summaryMarkdown({
|
|
7419
|
+
title: `Delete ${site.url ?? site.siteId}? Nothing was changed.`,
|
|
7420
|
+
lead: "This is a preview. Deleting cannot be undone: the content, the public URL, installed apps and stored form submissions disappear at once, and the local credential file is removed.",
|
|
7421
|
+
facts: [
|
|
7422
|
+
["Site ID", site.siteId],
|
|
7423
|
+
["Public URL", site.url],
|
|
7424
|
+
["Permanent URL released", c.releasesPermanentUrl],
|
|
7425
|
+
["Custom hostnames removed", c.removesCustomHostnames.join(", ") || "none"],
|
|
7426
|
+
["Exact confirm arguments", JSON.stringify(confirmArguments)]
|
|
7427
|
+
],
|
|
7428
|
+
notes: [
|
|
7429
|
+
"After deletion this project is unbound; the next deploy creates a brand-new site with a new URL and credential.",
|
|
7430
|
+
"To keep the URL but replace the content, deploy again instead of deleting."
|
|
7431
|
+
]
|
|
7432
|
+
}),
|
|
7433
|
+
data: {
|
|
7434
|
+
siteId: site.siteId,
|
|
7435
|
+
operationId: preview.operationId,
|
|
7436
|
+
consequences: c,
|
|
7437
|
+
confirmation: preview.confirmation,
|
|
7438
|
+
confirmArguments
|
|
7439
|
+
},
|
|
7440
|
+
prompt: `Delete the site ${site.url ?? site.siteId} now? This cannot be undone.`,
|
|
7441
|
+
options: [
|
|
7442
|
+
callToolDecisionOption({
|
|
7443
|
+
id: "delete_site",
|
|
7444
|
+
label: `Delete ${site.url ?? site.siteId}`,
|
|
7445
|
+
description: "Remove the site, its content, apps and submissions, and release the URL.",
|
|
7446
|
+
consequences: [
|
|
7447
|
+
"Visitors get HTTP 410 immediately.",
|
|
7448
|
+
"The local credential file is removed; this project is unbound."
|
|
7449
|
+
],
|
|
7450
|
+
tool: "delete",
|
|
7451
|
+
arguments: confirmArguments,
|
|
7452
|
+
reasonCode: "explicit_site_delete_confirmation"
|
|
7453
|
+
}),
|
|
7454
|
+
noActionDecisionOption({ description: "Keep the site exactly as it is." })
|
|
7455
|
+
],
|
|
7456
|
+
legacyUserAction: { type: "confirm_in_mcp", provider: "sakupa" }
|
|
7457
|
+
});
|
|
7458
|
+
} catch (error) {
|
|
7459
|
+
return toolError(error);
|
|
7460
|
+
}
|
|
7461
|
+
})
|
|
7462
|
+
);
|
|
7463
|
+
}
|
|
7464
|
+
|
|
7261
7465
|
// src/transport.ts
|
|
7262
7466
|
var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
7263
7467
|
var DEFAULT_UPLOAD_TIMEOUT_MS = 3e4;
|
|
@@ -7445,6 +7649,13 @@ Workflow:
|
|
|
7445
7649
|
payment, refund and other customer-service requests. report is the LAST resort only when
|
|
7446
7650
|
help explicitly recommends a product bug report, and submission still requires user review.
|
|
7447
7651
|
|
|
7652
|
+
Deleting a site: delete with no arguments only previews and returns a decision; after the user
|
|
7653
|
+
confirms, call delete again with the exact preview arguments. A FREE site is deleted immediately
|
|
7654
|
+
(content, URL, apps and submissions gone; .sakupa/site.json removed; the next deploy creates a new
|
|
7655
|
+
site). A subscribed site cannot be deleted: end the subscription first (change \u2192 period-end
|
|
7656
|
+
cancellation on Stripe); when it ends the site reverts to a free 30-day site and delete becomes
|
|
7657
|
+
available. Never suggest delete as a way to free up the free-site allowance during deploy.
|
|
7658
|
+
|
|
7448
7659
|
Decision-options contract: when a result contains decision, present EVERY numbered option from the
|
|
7449
7660
|
tool to the user and wait for their selection. No option is selected by default. Never choose from
|
|
7450
7661
|
context, paraphrase a selection into different arguments, or invent another option. After the user
|
|
@@ -7601,6 +7812,7 @@ function createSakupaMcpServer(opts) {
|
|
|
7601
7812
|
registerBillingTools(server, ctx);
|
|
7602
7813
|
registerCredentialTools(server, ctx);
|
|
7603
7814
|
registerAppsTools(server, ctx);
|
|
7815
|
+
registerDeleteTools(server, ctx);
|
|
7604
7816
|
registerHelpTools(server, ctx);
|
|
7605
7817
|
return server;
|
|
7606
7818
|
}
|
package/dist/index.js
CHANGED
|
@@ -148,7 +148,7 @@ function isFreeSiteAllowanceNetworkReference(value) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// ../core/dist/domain/version.js
|
|
151
|
-
var SAKUPA_MCP_VERSION = "1.
|
|
151
|
+
var SAKUPA_MCP_VERSION = "1.5.0";
|
|
152
152
|
|
|
153
153
|
// ../core/dist/domain/errors.js
|
|
154
154
|
var HTTP_STATUS = {
|
|
@@ -2243,7 +2243,8 @@ var TARGET_MCP_TOOL_NAMES = [
|
|
|
2243
2243
|
"change",
|
|
2244
2244
|
"support",
|
|
2245
2245
|
"report",
|
|
2246
|
-
"apps"
|
|
2246
|
+
"apps",
|
|
2247
|
+
"delete"
|
|
2247
2248
|
];
|
|
2248
2249
|
var STRUCTURED_TOOL_OUTPUT_SCHEMA = z.object({
|
|
2249
2250
|
schemaVersion: z.literal(1),
|
|
@@ -3084,15 +3085,15 @@ function strFromU8(dat, latin1) {
|
|
|
3084
3085
|
var slzh = function(d, b) {
|
|
3085
3086
|
return b + 30 + b2(d, b + 26) + b2(d, b + 28);
|
|
3086
3087
|
};
|
|
3087
|
-
var zh = function(d, b,
|
|
3088
|
+
var zh = function(d, b, z8) {
|
|
3088
3089
|
var fnl = b2(d, b + 28), efl = b2(d, b + 30), fn = strFromU8(d.subarray(b + 46, b + 46 + fnl), !(b2(d, b + 8) & 2048)), es = b + 46 + fnl;
|
|
3089
|
-
var _a2 = z64hs(d, es, efl,
|
|
3090
|
+
var _a2 = z64hs(d, es, efl, z8, b4(d, b + 20), b4(d, b + 24), b4(d, b + 42)), sc = _a2[0], su = _a2[1], off = _a2[2];
|
|
3090
3091
|
return [b2(d, b + 10), sc, su, fn, es + efl + b2(d, b + 32), off];
|
|
3091
3092
|
};
|
|
3092
|
-
var z64hs = function(d, b, l,
|
|
3093
|
+
var z64hs = function(d, b, l, z8, sc, su, off) {
|
|
3093
3094
|
var nsc = sc == 4294967295, nsu = su == 4294967295, noff = off == 4294967295, e = b + l;
|
|
3094
3095
|
var nf = nsc + nsu + noff;
|
|
3095
|
-
if (
|
|
3096
|
+
if (z8 && nf) {
|
|
3096
3097
|
for (; b + 4 < e; b += 4 + b2(d, b + 2)) {
|
|
3097
3098
|
if (b2(d, b) == 1) {
|
|
3098
3099
|
return [
|
|
@@ -3103,7 +3104,7 @@ var z64hs = function(d, b, l, z7, sc, su, off) {
|
|
|
3103
3104
|
];
|
|
3104
3105
|
}
|
|
3105
3106
|
}
|
|
3106
|
-
if (
|
|
3107
|
+
if (z8 < 2)
|
|
3107
3108
|
err(13);
|
|
3108
3109
|
}
|
|
3109
3110
|
return [sc, su, off, 0];
|
|
@@ -3120,18 +3121,18 @@ function unzipSync(data, opts) {
|
|
|
3120
3121
|
if (!c)
|
|
3121
3122
|
return {};
|
|
3122
3123
|
var o = b4(data, e + 16);
|
|
3123
|
-
var
|
|
3124
|
-
if (
|
|
3124
|
+
var z8 = b4(data, e - 20) == 117853008;
|
|
3125
|
+
if (z8) {
|
|
3125
3126
|
var ze = b4(data, e - 12);
|
|
3126
|
-
|
|
3127
|
-
if (
|
|
3127
|
+
z8 = b4(data, ze) == 101075792;
|
|
3128
|
+
if (z8) {
|
|
3128
3129
|
c = b4(data, ze + 32);
|
|
3129
3130
|
o = b4(data, ze + 48);
|
|
3130
3131
|
}
|
|
3131
3132
|
}
|
|
3132
3133
|
var fltr = opts && opts.filter;
|
|
3133
3134
|
for (var i = 0; i < c; ++i) {
|
|
3134
|
-
var _a2 = zh(data, o,
|
|
3135
|
+
var _a2 = zh(data, o, z8), c_2 = _a2[0], sc = _a2[1], su = _a2[2], fn = _a2[3], no = _a2[4], off = _a2[5], b = slzh(data, off);
|
|
3135
3136
|
o = no;
|
|
3136
3137
|
if (!fltr || fltr({
|
|
3137
3138
|
name: fn,
|
|
@@ -6289,6 +6290,7 @@ var TOOL_TOPICS = [
|
|
|
6289
6290
|
"support",
|
|
6290
6291
|
"report",
|
|
6291
6292
|
"apps",
|
|
6293
|
+
"delete",
|
|
6292
6294
|
"help"
|
|
6293
6295
|
];
|
|
6294
6296
|
var HELP_TOPICS = ["diagnose", "overview", "terminology", ...TOOL_TOPICS];
|
|
@@ -6399,6 +6401,18 @@ var TOOL_MANUALS = {
|
|
|
6399
6401
|
nextStep: "Use the preview resumeWith arguments only after the user confirms.",
|
|
6400
6402
|
terminology: ["credentialRotation"]
|
|
6401
6403
|
},
|
|
6404
|
+
delete: {
|
|
6405
|
+
purpose: "Delete the bound FREE site after an explicit preview and confirmation.",
|
|
6406
|
+
sideEffects: "Preview is read-only. Confirmed deletion removes content, the public URL, installed apps, stored submissions and the local .sakupa/site.json.",
|
|
6407
|
+
preconditions: "A valid local site credential and a free site. A subscribed site is refused until the subscription ends (change \u2192 period-end cancellation) and the site reverts to free.",
|
|
6408
|
+
parameterNames: ["confirmed", "operationId", "confirmation"],
|
|
6409
|
+
parameters: "No arguments for the preview; confirmed=true with the exact operationId and confirmation copied from the preview decision.",
|
|
6410
|
+
warnings: [
|
|
6411
|
+
"Irreversible; the next deploy creates a brand-new site with a new URL.",
|
|
6412
|
+
"Never propose delete to free up the free-site allowance; use the deploy handoff decision instead."
|
|
6413
|
+
],
|
|
6414
|
+
nextStep: "Present every decision option; after confirmation copy the option arguments verbatim."
|
|
6415
|
+
},
|
|
6402
6416
|
plans: {
|
|
6403
6417
|
purpose: "Read the authoritative hosting plan catalog and rules.",
|
|
6404
6418
|
sideEffects: "Read-only public API request.",
|
|
@@ -7377,6 +7391,196 @@ function registerAppsTools(server, baseCtx) {
|
|
|
7377
7391
|
);
|
|
7378
7392
|
}
|
|
7379
7393
|
|
|
7394
|
+
// src/tools/delete.ts
|
|
7395
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
7396
|
+
import { z as z7 } from "zod";
|
|
7397
|
+
var confirmationSchema = z7.object({
|
|
7398
|
+
siteId: z7.string(),
|
|
7399
|
+
expectedSiteUpdatedAt: z7.string(),
|
|
7400
|
+
expectedStatus: z7.string(),
|
|
7401
|
+
expectedMode: z7.enum(["free", "paid"]),
|
|
7402
|
+
expectedServingMode: z7.string(),
|
|
7403
|
+
expectedShortId: z7.string().optional(),
|
|
7404
|
+
expectedSubscriptionStatus: z7.string().optional(),
|
|
7405
|
+
expectedPlan: z7.string().optional(),
|
|
7406
|
+
expectedCancelAtPeriodEnd: z7.boolean().optional(),
|
|
7407
|
+
expectedCurrentPeriodEnd: z7.string().optional(),
|
|
7408
|
+
expectedLastDeploymentId: z7.string().optional(),
|
|
7409
|
+
expectedBoundHostnames: z7.array(z7.string()),
|
|
7410
|
+
acknowledge: z7.literal("delete_and_cancel_renewal")
|
|
7411
|
+
});
|
|
7412
|
+
function registerDeleteTools(server, baseCtx) {
|
|
7413
|
+
server.registerTool(
|
|
7414
|
+
"delete",
|
|
7415
|
+
{
|
|
7416
|
+
title: "Delete site",
|
|
7417
|
+
description: "Delete the site bound to this project. Without arguments it only previews (zero writes) and returns a decision; after the user confirms, call again with the exact preview arguments (confirmed: true, operationId, confirmation). A FREE site is deleted immediately: content, public URL, installed apps and stored submissions are gone and the local .sakupa/site.json is removed, so the next deploy creates a brand-new site. A subscribed site cannot be deleted: end the subscription first (change \u2192 period-end cancellation on Stripe); when it ends the site reverts to a free site and delete becomes available. Owner-only.",
|
|
7418
|
+
inputSchema: z7.object({
|
|
7419
|
+
confirmed: z7.boolean().optional().describe("true only from the exact decision arguments returned by the preview."),
|
|
7420
|
+
operationId: z7.string().optional().describe("Copied verbatim from the preview."),
|
|
7421
|
+
confirmation: confirmationSchema.optional().describe("Copied verbatim from the preview; the cloud rejects any drift.")
|
|
7422
|
+
}),
|
|
7423
|
+
outputSchema: STRUCTURED_TOOL_OUTPUT_SCHEMA,
|
|
7424
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true }
|
|
7425
|
+
},
|
|
7426
|
+
withDecisionReentry("delete", async (args, call) => {
|
|
7427
|
+
try {
|
|
7428
|
+
const ctx = await withProjectDir(baseCtx, call);
|
|
7429
|
+
const site = requireSiteFile(ctx);
|
|
7430
|
+
if (args.confirmed === true) {
|
|
7431
|
+
if (!args.operationId || !args.confirmation) {
|
|
7432
|
+
return structuredToolResult({
|
|
7433
|
+
schemaVersion: 1,
|
|
7434
|
+
outcome: "blocked",
|
|
7435
|
+
resultCode: "delete_preview_required",
|
|
7436
|
+
summary: summaryMarkdown({
|
|
7437
|
+
title: "Nothing was deleted \u2014 run the preview first",
|
|
7438
|
+
lead: "A confirmed delete must carry the exact operationId and confirmation from the preview. Call delete with no arguments, show the preview to the user, then reuse its decision arguments.",
|
|
7439
|
+
next: ["`delete` with no arguments"]
|
|
7440
|
+
}),
|
|
7441
|
+
data: { siteId: site.siteId },
|
|
7442
|
+
nextActions: [{ tool: "delete", arguments: {}, allowed: true }]
|
|
7443
|
+
});
|
|
7444
|
+
}
|
|
7445
|
+
const result = await ctx.client.deleteSite(site.siteId, site.credential, {
|
|
7446
|
+
operationId: args.operationId,
|
|
7447
|
+
confirmation: args.confirmation
|
|
7448
|
+
});
|
|
7449
|
+
deleteSiteFile(ctx.projectDir);
|
|
7450
|
+
removeCreation(site.siteId);
|
|
7451
|
+
return structuredToolResult({
|
|
7452
|
+
schemaVersion: 1,
|
|
7453
|
+
outcome: "completed",
|
|
7454
|
+
resultCode: result.alreadyDeleted ? "site_already_deleted" : "site_deleted",
|
|
7455
|
+
summary: summaryMarkdown({
|
|
7456
|
+
title: result.alreadyDeleted ? "Site was already deleted" : "Site deleted",
|
|
7457
|
+
lead: `${site.url ?? site.siteId} no longer serves anything (visitors get HTTP 410). The local credential file .sakupa/site.json was removed; this project is no longer bound to any site.`,
|
|
7458
|
+
facts: [
|
|
7459
|
+
["Site ID", result.siteId],
|
|
7460
|
+
["Public URL released", site.url],
|
|
7461
|
+
[
|
|
7462
|
+
"Custom hostnames removed",
|
|
7463
|
+
result.removedHostnames.length ? result.removedHostnames.join(", ") : "none"
|
|
7464
|
+
],
|
|
7465
|
+
["Serving cleanup still pending", result.servingDeletionPending]
|
|
7466
|
+
],
|
|
7467
|
+
notes: [
|
|
7468
|
+
"Installed apps and stored form submissions were deleted with the site.",
|
|
7469
|
+
"The free-site allowance this site used on its deployment network is released."
|
|
7470
|
+
],
|
|
7471
|
+
next: ["`deploy` to publish a brand-new site (new URL, new credential)"]
|
|
7472
|
+
}),
|
|
7473
|
+
data: {
|
|
7474
|
+
...result,
|
|
7475
|
+
projectDir: ctx.projectDir,
|
|
7476
|
+
localSiteFileRemoved: true,
|
|
7477
|
+
releasedUrl: site.url
|
|
7478
|
+
},
|
|
7479
|
+
nextActions: []
|
|
7480
|
+
});
|
|
7481
|
+
}
|
|
7482
|
+
const operationId = randomUUID7();
|
|
7483
|
+
const preview = await ctx.client.previewDeleteSite(site.siteId, site.credential, {
|
|
7484
|
+
operationId
|
|
7485
|
+
});
|
|
7486
|
+
const c = preview.consequences;
|
|
7487
|
+
if (c.requiresFreeModeBeforeDelete) {
|
|
7488
|
+
const cancellationScheduled = preview.confirmation.expectedCancelAtPeriodEnd === true;
|
|
7489
|
+
return structuredToolResult({
|
|
7490
|
+
schemaVersion: 1,
|
|
7491
|
+
outcome: "blocked",
|
|
7492
|
+
resultCode: "delete_requires_free_mode",
|
|
7493
|
+
summary: summaryMarkdown({
|
|
7494
|
+
title: "This site is subscription-backed \u2014 it cannot be deleted yet",
|
|
7495
|
+
lead: "Nothing was changed. Sakupa deletes only FREE sites. End the subscription first; when the paid period ends the site automatically becomes a free 30-day site, and delete becomes available.",
|
|
7496
|
+
facts: [
|
|
7497
|
+
["Site", site.url ?? site.siteId],
|
|
7498
|
+
["Plan", preview.confirmation.expectedPlan],
|
|
7499
|
+
["Period-end cancellation already scheduled", cancellationScheduled ? "yes" : "no"],
|
|
7500
|
+
[
|
|
7501
|
+
"Paid access ends",
|
|
7502
|
+
c.currentPaidAccessEndsAt ? timestampForAgent(c.currentPaidAccessEndsAt) : void 0
|
|
7503
|
+
],
|
|
7504
|
+
[
|
|
7505
|
+
"Custom hostnames removed when it reverts",
|
|
7506
|
+
c.removesCustomHostnames.join(", ") || "none"
|
|
7507
|
+
]
|
|
7508
|
+
],
|
|
7509
|
+
steps: cancellationScheduled ? [
|
|
7510
|
+
"Wait for the paid period to end; Sakupa then reverts the site to a free 30-day site (content kept, custom domains removed).",
|
|
7511
|
+
"Call delete again at that point \u2014 or simply let the free site expire."
|
|
7512
|
+
] : [
|
|
7513
|
+
"Call change and open the Stripe-hosted link; the user chooses period-end cancellation there (Sakupa never cancels on its own).",
|
|
7514
|
+
"When the paid period ends, Sakupa reverts the site to a free 30-day site (content kept, custom domains removed).",
|
|
7515
|
+
"Call delete again at that point \u2014 or simply let the free site expire."
|
|
7516
|
+
],
|
|
7517
|
+
next: cancellationScheduled ? ["`billing`"] : ["`change`", "`billing`"]
|
|
7518
|
+
}),
|
|
7519
|
+
data: {
|
|
7520
|
+
siteId: site.siteId,
|
|
7521
|
+
operationId: preview.operationId,
|
|
7522
|
+
consequences: c,
|
|
7523
|
+
cancellationScheduled
|
|
7524
|
+
},
|
|
7525
|
+
nextActions: [
|
|
7526
|
+
{ tool: cancellationScheduled ? "billing" : "change", arguments: {}, allowed: true }
|
|
7527
|
+
]
|
|
7528
|
+
});
|
|
7529
|
+
}
|
|
7530
|
+
const confirmArguments = {
|
|
7531
|
+
confirmed: true,
|
|
7532
|
+
operationId: preview.operationId,
|
|
7533
|
+
confirmation: preview.confirmation
|
|
7534
|
+
};
|
|
7535
|
+
return presentDecision(baseCtx.decisions, call, "delete", {
|
|
7536
|
+
resultCode: "delete_confirmation_required",
|
|
7537
|
+
summary: summaryMarkdown({
|
|
7538
|
+
title: `Delete ${site.url ?? site.siteId}? Nothing was changed.`,
|
|
7539
|
+
lead: "This is a preview. Deleting cannot be undone: the content, the public URL, installed apps and stored form submissions disappear at once, and the local credential file is removed.",
|
|
7540
|
+
facts: [
|
|
7541
|
+
["Site ID", site.siteId],
|
|
7542
|
+
["Public URL", site.url],
|
|
7543
|
+
["Permanent URL released", c.releasesPermanentUrl],
|
|
7544
|
+
["Custom hostnames removed", c.removesCustomHostnames.join(", ") || "none"],
|
|
7545
|
+
["Exact confirm arguments", JSON.stringify(confirmArguments)]
|
|
7546
|
+
],
|
|
7547
|
+
notes: [
|
|
7548
|
+
"After deletion this project is unbound; the next deploy creates a brand-new site with a new URL and credential.",
|
|
7549
|
+
"To keep the URL but replace the content, deploy again instead of deleting."
|
|
7550
|
+
]
|
|
7551
|
+
}),
|
|
7552
|
+
data: {
|
|
7553
|
+
siteId: site.siteId,
|
|
7554
|
+
operationId: preview.operationId,
|
|
7555
|
+
consequences: c,
|
|
7556
|
+
confirmation: preview.confirmation,
|
|
7557
|
+
confirmArguments
|
|
7558
|
+
},
|
|
7559
|
+
prompt: `Delete the site ${site.url ?? site.siteId} now? This cannot be undone.`,
|
|
7560
|
+
options: [
|
|
7561
|
+
callToolDecisionOption({
|
|
7562
|
+
id: "delete_site",
|
|
7563
|
+
label: `Delete ${site.url ?? site.siteId}`,
|
|
7564
|
+
description: "Remove the site, its content, apps and submissions, and release the URL.",
|
|
7565
|
+
consequences: [
|
|
7566
|
+
"Visitors get HTTP 410 immediately.",
|
|
7567
|
+
"The local credential file is removed; this project is unbound."
|
|
7568
|
+
],
|
|
7569
|
+
tool: "delete",
|
|
7570
|
+
arguments: confirmArguments,
|
|
7571
|
+
reasonCode: "explicit_site_delete_confirmation"
|
|
7572
|
+
}),
|
|
7573
|
+
noActionDecisionOption({ description: "Keep the site exactly as it is." })
|
|
7574
|
+
],
|
|
7575
|
+
legacyUserAction: { type: "confirm_in_mcp", provider: "sakupa" }
|
|
7576
|
+
});
|
|
7577
|
+
} catch (error) {
|
|
7578
|
+
return toolError(error);
|
|
7579
|
+
}
|
|
7580
|
+
})
|
|
7581
|
+
);
|
|
7582
|
+
}
|
|
7583
|
+
|
|
7380
7584
|
// src/server.ts
|
|
7381
7585
|
import { resolve as resolve6 } from "node:path";
|
|
7382
7586
|
var instructionsFor = (hostPattern) => `Sakupa publishes AI-made static websites. AI-made pages, live in seconds.
|
|
@@ -7413,6 +7617,13 @@ Workflow:
|
|
|
7413
7617
|
payment, refund and other customer-service requests. report is the LAST resort only when
|
|
7414
7618
|
help explicitly recommends a product bug report, and submission still requires user review.
|
|
7415
7619
|
|
|
7620
|
+
Deleting a site: delete with no arguments only previews and returns a decision; after the user
|
|
7621
|
+
confirms, call delete again with the exact preview arguments. A FREE site is deleted immediately
|
|
7622
|
+
(content, URL, apps and submissions gone; .sakupa/site.json removed; the next deploy creates a new
|
|
7623
|
+
site). A subscribed site cannot be deleted: end the subscription first (change \u2192 period-end
|
|
7624
|
+
cancellation on Stripe); when it ends the site reverts to a free 30-day site and delete becomes
|
|
7625
|
+
available. Never suggest delete as a way to free up the free-site allowance during deploy.
|
|
7626
|
+
|
|
7416
7627
|
Decision-options contract: when a result contains decision, present EVERY numbered option from the
|
|
7417
7628
|
tool to the user and wait for their selection. No option is selected by default. Never choose from
|
|
7418
7629
|
context, paraphrase a selection into different arguments, or invent another option. After the user
|
|
@@ -7569,6 +7780,7 @@ function createSakupaMcpServer(opts) {
|
|
|
7569
7780
|
registerBillingTools(server, ctx);
|
|
7570
7781
|
registerCredentialTools(server, ctx);
|
|
7571
7782
|
registerAppsTools(server, ctx);
|
|
7783
|
+
registerDeleteTools(server, ctx);
|
|
7572
7784
|
registerHelpTools(server, ctx);
|
|
7573
7785
|
return server;
|
|
7574
7786
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sakupa/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"mcpName": "io.github.myerwang/sakupa",
|
|
5
5
|
"description": "Sakupa MCP server: publish AI-made static sites from your AI tool. AI-made pages, live in seconds.",
|
|
6
6
|
"homepage": "https://sakupa.com/manual/",
|