@drop2run/mcp 0.4.3 → 0.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/README.md +9 -1
- package/dist/index.js +49 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,12 @@ lose it.
|
|
|
71
71
|
Both publish tools take an optional `site` — a subdomain or site id to publish over. Leave it out and a
|
|
72
72
|
new site is created.
|
|
73
73
|
|
|
74
|
+
Both also take an optional `subdomain`, which names the new site instead of letting the server generate
|
|
75
|
+
one. It is for a person who asked for a particular address: the name becomes the URL, cannot be changed
|
|
76
|
+
afterwards, and a site made under a guessed one has to be deleted by hand. `site` and `subdomain` are
|
|
77
|
+
opposites — one publishes over a site that exists, the other creates one that does not — so passing both
|
|
78
|
+
is refused rather than resolved.
|
|
79
|
+
|
|
74
80
|
Both sign-in tools answer "not approved yet" rather than failing while they wait, and calling them
|
|
75
81
|
again keeps waiting on the same sign-in. Neither replaces a token that is already stored unless asked
|
|
76
82
|
to with `replace`.
|
|
@@ -78,7 +84,9 @@ to with `replace`.
|
|
|
78
84
|
## Behaviour worth knowing
|
|
79
85
|
|
|
80
86
|
**Publishing without a `site` creates a new one.** It never replaces your most recent site by default:
|
|
81
|
-
"put this online" is not "and overwrite what I published last time".
|
|
87
|
+
"put this online" is not "and overwrite what I published last time". Say what address you want and pass
|
|
88
|
+
it as `subdomain`; say nothing and the name is generated, which is the right default for something
|
|
89
|
+
nobody is going to link to.
|
|
82
90
|
|
|
83
91
|
**A site can be documents instead of a built site.** A publish needs an `index.html` at the top level,
|
|
84
92
|
or at least one `.md`, `.markdown` or `.pdf` file — those are served through the reader. So a single
|
package/dist/index.js
CHANGED
|
@@ -47,11 +47,17 @@ async function listSites(credentials) {
|
|
|
47
47
|
const body = await call(credentials, "sites");
|
|
48
48
|
return body.sites;
|
|
49
49
|
}
|
|
50
|
-
async function createSite(credentials) {
|
|
50
|
+
async function createSite(credentials, subdomain) {
|
|
51
51
|
const created = await call(
|
|
52
52
|
credentials,
|
|
53
53
|
"sites",
|
|
54
|
-
{
|
|
54
|
+
{
|
|
55
|
+
method: "POST",
|
|
56
|
+
// An absent field rather than a null one, because the API reads null, empty and missing the
|
|
57
|
+
// same way and a body carrying only what was asked for is the one that reads correctly in a
|
|
58
|
+
// log.
|
|
59
|
+
body: JSON.stringify(subdomain === void 0 ? {} : { subdomain })
|
|
60
|
+
}
|
|
55
61
|
);
|
|
56
62
|
return { ...created, name: null };
|
|
57
63
|
}
|
|
@@ -304,6 +310,7 @@ const ClientErrorCode = {
|
|
|
304
310
|
/** An upload kept failing after every retry. */
|
|
305
311
|
UploadFailed: "upload_failed"
|
|
306
312
|
};
|
|
313
|
+
const ACCOUNT_HEADER = "X-Drop2Run-Account";
|
|
307
314
|
const CLAIM_TOKEN_HEADER = "X-Claim-Token";
|
|
308
315
|
async function prepareDeploy(siteId, files, options, signal) {
|
|
309
316
|
const manifest = files.map((file) => ({
|
|
@@ -340,6 +347,7 @@ async function request(path, body, options, signal) {
|
|
|
340
347
|
if (body !== void 0) headers["Content-Type"] = "application/json";
|
|
341
348
|
if (options.claimToken) headers[CLAIM_TOKEN_HEADER] = options.claimToken;
|
|
342
349
|
if (options.token) headers.Authorization = `Bearer ${options.token}`;
|
|
350
|
+
if (options.accountId) headers[ACCOUNT_HEADER] = options.accountId;
|
|
343
351
|
const response = await doFetch(`${base}/${path}`, {
|
|
344
352
|
method: "POST",
|
|
345
353
|
headers,
|
|
@@ -633,7 +641,7 @@ async function uploadAll(targets, uploadUrl, files, onProgress, signal, deps = {
|
|
|
633
641
|
});
|
|
634
642
|
done += 1;
|
|
635
643
|
uploadedBytes += file.bytes.length;
|
|
636
|
-
onProgress?.(done, targets.length, uploadedBytes);
|
|
644
|
+
onProgress?.(done, targets.length, uploadedBytes, file.path);
|
|
637
645
|
}
|
|
638
646
|
};
|
|
639
647
|
const workers = Array.from(
|
|
@@ -728,12 +736,13 @@ async function deploy(source, siteId, onProgress, signal, options = {}) {
|
|
|
728
736
|
prepared.upload,
|
|
729
737
|
prepared.uploadUrl,
|
|
730
738
|
hashed,
|
|
731
|
-
(done, _total, bytes) => onProgress({
|
|
739
|
+
(done, _total, bytes, path) => onProgress({
|
|
732
740
|
type: "uploading",
|
|
733
741
|
done: prepared.reused + done,
|
|
734
742
|
total: prepared.total,
|
|
735
743
|
bytes,
|
|
736
|
-
reused: prepared.reused
|
|
744
|
+
reused: prepared.reused,
|
|
745
|
+
path
|
|
737
746
|
}),
|
|
738
747
|
signal,
|
|
739
748
|
options.upload
|
|
@@ -799,7 +808,15 @@ function escapesRoot(root, candidate) {
|
|
|
799
808
|
const inside = relative(root, candidate);
|
|
800
809
|
return inside === "" || inside.startsWith("..");
|
|
801
810
|
}
|
|
802
|
-
async function resolveSite(credentials, site) {
|
|
811
|
+
async function resolveSite(credentials, site, subdomain) {
|
|
812
|
+
if (subdomain !== void 0) {
|
|
813
|
+
if (site !== void 0) {
|
|
814
|
+
throw new Error(
|
|
815
|
+
"Give either an existing site or a subdomain for a new one, not both: publishing over a site and creating one are different requests."
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
return await createSite(credentials, subdomain);
|
|
819
|
+
}
|
|
803
820
|
if (site === void 0) return await createSite(credentials);
|
|
804
821
|
const wanted = site.trim().toLowerCase();
|
|
805
822
|
const found = (await listSites(credentials)).find(
|
|
@@ -807,13 +824,13 @@ async function resolveSite(credentials, site) {
|
|
|
807
824
|
);
|
|
808
825
|
if (found === void 0) {
|
|
809
826
|
throw new Error(
|
|
810
|
-
`No site of yours is called "${site}". Leave the site out to publish to a new one, or
|
|
827
|
+
`No site of yours is called "${site}". Leave the site out to publish to a new one, or list your sites to see what exists.`
|
|
811
828
|
);
|
|
812
829
|
}
|
|
813
830
|
return found;
|
|
814
831
|
}
|
|
815
|
-
async function publish(credentials, source, site) {
|
|
816
|
-
const target = await resolveSite(credentials, site);
|
|
832
|
+
async function publish(credentials, source, site, onProgress, subdomain) {
|
|
833
|
+
const target = await resolveSite(credentials, site, subdomain);
|
|
817
834
|
let files = 0;
|
|
818
835
|
let url = target.url;
|
|
819
836
|
let unchanged = false;
|
|
@@ -843,8 +860,8 @@ async function publish(credentials, source, site) {
|
|
|
843
860
|
});
|
|
844
861
|
return { url, siteId: target.siteId, subdomain: target.subdomain, files, unchanged };
|
|
845
862
|
}
|
|
846
|
-
function publishDirectory(credentials, directory, site) {
|
|
847
|
-
return publish(credentials, directorySource(directory), site);
|
|
863
|
+
function publishDirectory(credentials, directory, site, onProgress, subdomain) {
|
|
864
|
+
return publish(credentials, directorySource(directory), site, onProgress, subdomain);
|
|
848
865
|
}
|
|
849
866
|
function cleanPath(path) {
|
|
850
867
|
const cleaned = path.trim().replaceAll("\\", "/").replace(/^\.?\//, "");
|
|
@@ -858,7 +875,7 @@ function cleanPath(path) {
|
|
|
858
875
|
}
|
|
859
876
|
return cleaned;
|
|
860
877
|
}
|
|
861
|
-
async function publishFiles(credentials, files, site) {
|
|
878
|
+
async function publishFiles(credentials, files, site, subdomain) {
|
|
862
879
|
const encoder = new TextEncoder();
|
|
863
880
|
const collected = files.map((file) => ({
|
|
864
881
|
path: cleanPath(file.path),
|
|
@@ -869,7 +886,7 @@ async function publishFiles(credentials, files, site) {
|
|
|
869
886
|
if (seen.has(file.path)) throw new Error(`Two files were given the same path: ${file.path}`);
|
|
870
887
|
seen.add(file.path);
|
|
871
888
|
}
|
|
872
|
-
return publish(credentials, async () => collected, site);
|
|
889
|
+
return publish(credentials, async () => collected, site, void 0, subdomain);
|
|
873
890
|
}
|
|
874
891
|
const DEFAULT_WAIT_SECONDS = 120;
|
|
875
892
|
const MAX_WAIT_SECONDS = 240;
|
|
@@ -1047,7 +1064,7 @@ async function signInWithCode(waitSeconds, replace = false) {
|
|
|
1047
1064
|
"Call this tool again to keep waiting. Nothing has been changed yet."
|
|
1048
1065
|
].join("\n");
|
|
1049
1066
|
}
|
|
1050
|
-
const SERVER_VERSION = "0.
|
|
1067
|
+
const SERVER_VERSION = "0.5.0";
|
|
1051
1068
|
const CREDENTIAL_WRITE = {
|
|
1052
1069
|
readOnlyHint: false,
|
|
1053
1070
|
destructiveHint: false,
|
|
@@ -1126,6 +1143,12 @@ function createServer() {
|
|
|
1126
1143
|
"publish to a brand-new site; pass a subdomain to publish over an existing one. list_sites",
|
|
1127
1144
|
"shows what already exists.",
|
|
1128
1145
|
"",
|
|
1146
|
+
"A new site gets a generated name unless `subdomain` asks for one. Pass it when the person",
|
|
1147
|
+
"named the address they want, and otherwise leave it out — a name is what somebody will be",
|
|
1148
|
+
"given as a URL, it cannot be changed later, and a site created under a guessed one has to",
|
|
1149
|
+
"be deleted by hand. `site` and `subdomain` are opposites: one publishes over a site that",
|
|
1150
|
+
"exists, the other creates one that does not. Never pass both.",
|
|
1151
|
+
"",
|
|
1129
1152
|
"A site needs an index.html at its top level, or at least one .md, .markdown or .pdf file,",
|
|
1130
1153
|
"which publishes as a documents site and is read through a viewer. So one page goes at",
|
|
1131
1154
|
"index.html, and a single note is a whole publish that needs no wrapping in HTML.",
|
|
@@ -1183,13 +1206,16 @@ function createServer() {
|
|
|
1183
1206
|
content: z.string().describe("The file's contents.")
|
|
1184
1207
|
})
|
|
1185
1208
|
).min(1).describe("The files to publish."),
|
|
1186
|
-
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site.")
|
|
1209
|
+
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site."),
|
|
1210
|
+
subdomain: z.string().optional().describe(
|
|
1211
|
+
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, it cannot be changed afterwards, and an unused site made under a guessed name has to be deleted by hand."
|
|
1212
|
+
)
|
|
1187
1213
|
},
|
|
1188
1214
|
outputSchema: PUBLISH_OUTPUT,
|
|
1189
1215
|
annotations: REPLACES_A_SITE
|
|
1190
1216
|
},
|
|
1191
|
-
({ files, site }) => withCredentials(async (credentials) => {
|
|
1192
|
-
const result = await publishFiles(credentials, files, site);
|
|
1217
|
+
({ files, site, subdomain }) => withCredentials(async (credentials) => {
|
|
1218
|
+
const result = await publishFiles(credentials, files, site, subdomain);
|
|
1193
1219
|
return report(describe(result), { ...result });
|
|
1194
1220
|
})
|
|
1195
1221
|
);
|
|
@@ -1200,13 +1226,16 @@ function createServer() {
|
|
|
1200
1226
|
description: "Publishes a folder of static files and returns its URL. The folder needs an index.html at its root, or .md and .pdf files, which are served through the reader.",
|
|
1201
1227
|
inputSchema: {
|
|
1202
1228
|
path: z.string().min(1).describe("Absolute path of the folder to publish."),
|
|
1203
|
-
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site.")
|
|
1229
|
+
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site."),
|
|
1230
|
+
subdomain: z.string().optional().describe(
|
|
1231
|
+
"Subdomain to create the new site under, when the person asked for a particular address. Only for a new site: leave it out to publish over an existing one, and do not pass it together with `site`. Ask rather than inventing one — the name is the URL somebody will be given, it cannot be changed afterwards, and an unused site made under a guessed name has to be deleted by hand."
|
|
1232
|
+
)
|
|
1204
1233
|
},
|
|
1205
1234
|
outputSchema: PUBLISH_OUTPUT,
|
|
1206
1235
|
annotations: REPLACES_A_SITE
|
|
1207
1236
|
},
|
|
1208
|
-
({ path, site }) => withCredentials(async (credentials) => {
|
|
1209
|
-
const result = await publishDirectory(credentials, path, site);
|
|
1237
|
+
({ path, site, subdomain }) => withCredentials(async (credentials) => {
|
|
1238
|
+
const result = await publishDirectory(credentials, path, site, void 0, subdomain);
|
|
1210
1239
|
return report(describe(result), { ...result });
|
|
1211
1240
|
})
|
|
1212
1241
|
);
|