drop2run 0.2.0 → 0.3.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 CHANGED
@@ -72,7 +72,9 @@ DROP2RUN_TOKEN=d2r_...
72
72
  ```
73
73
 
74
74
  The environment wins over `~/.config/drop2run/config.json`, and that is the same file and precedence
75
- `@drop2run/mcp` uses, so signing in once covers both.
75
+ `@drop2run/mcp` uses, so signing in once covers both — in either direction. That server runs the same
76
+ two flows from its own `login` and `login_code` tools, so a sign-in done in a chat leaves this command
77
+ line signed in too.
76
78
 
77
79
  `drop2run where` says which source is in force without printing the token, so "why is it using the wrong
78
80
  account" is answerable in an issue report or a CI log.
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { join, dirname, relative, sep, resolve } from "node:path";
2
2
  import { readFileSync, writeFileSync, chmodSync, renameSync, mkdirSync } from "node:fs";
3
3
  import { homedir, hostname } from "node:os";
4
- import { realpath, readdir, stat, readFile } from "node:fs/promises";
5
4
  import { spawn } from "node:child_process";
6
5
  import { randomBytes, createHash } from "node:crypto";
7
6
  import { createServer } from "node:http";
7
+ import { realpath, readdir, stat, readFile } from "node:fs/promises";
8
8
  async function call(credentials, path, init2 = {}) {
9
9
  const response = await fetch(`${credentials.apiBaseUrl}/${path}`, {
10
10
  ...init2,
@@ -125,20 +125,188 @@ function clearToken(path = configPath()) {
125
125
  renameSync(temporary, path);
126
126
  return true;
127
127
  }
128
- function missingCredentialsMessage() {
129
- return [
130
- "No Drop2Run access token.",
131
- "",
132
- "Run `drop2run login` to sign in through a browser, which stores one for you.",
128
+ function missingCredentialsMessage(surface = "cli") {
129
+ const signIn = surface === "mcp" ? [
130
+ "Call the `login` tool to sign in through a browser, which stores a token.",
133
131
  "",
132
+ "Where no browser can be opened on this machine — a container, a remote host — call",
133
+ "`login_code` instead and pass on the short code it gives you."
134
+ ] : ["Run `drop2run login` to sign in through a browser, which stores one for you."];
135
+ const byHand = surface === "mcp" ? [
136
+ "To set one without signing in here, create a token at",
137
+ "https://dropto.run/account/tokens and either:",
138
+ ` - put it in ${configPath()} as {"token": "d2r_..."}, which this`,
139
+ " server reads on every call and needs no restart, or",
140
+ ` - set ${TOKEN_VARIABLE} in the environment and restart this server, since it reads`,
141
+ " the environment it was started in."
142
+ ] : [
134
143
  "For CI, or where no browser can be opened, create a token at",
135
144
  "https://dropto.run/account/tokens and either:",
136
145
  ` - set ${TOKEN_VARIABLE} in the environment, or`,
137
- ` - put it in ${configPath()} as {"token": "d2r_..."}`,
146
+ ` - put it in ${configPath()} as {"token": "d2r_..."}`
147
+ ];
148
+ return [
149
+ "No Drop2Run access token.",
150
+ "",
151
+ ...signIn,
152
+ "",
153
+ ...byHand,
138
154
  "",
139
155
  "The token is shown once when it is created and cannot be recovered afterwards."
140
156
  ].join("\n");
141
157
  }
158
+ const TIMEOUT_MS = 5 * 60 * 1e3;
159
+ function newAttempt() {
160
+ const verifier = randomBytes(32).toString("base64url");
161
+ return {
162
+ verifier,
163
+ challenge: createHash("sha256").update(verifier).digest("base64url"),
164
+ state: randomBytes(16).toString("base64url")
165
+ };
166
+ }
167
+ function clientName(surface) {
168
+ const clean2 = (value) => value.trim().replace(/[^A-Za-z0-9._-]/g, "-");
169
+ const label = "";
170
+ const raw = clean2(hostname()).slice(0, 64 - label.length);
171
+ return `${raw === "" ? "drop2run" : raw}${label}`;
172
+ }
173
+ function openBrowser(url) {
174
+ const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
175
+ try {
176
+ const child = spawn(command, [...args], { stdio: "ignore", detached: true });
177
+ child.unref();
178
+ child.on("error", () => {
179
+ });
180
+ return true;
181
+ } catch {
182
+ return false;
183
+ }
184
+ }
185
+ function listen(state) {
186
+ return new Promise((resolveListener, rejectListener) => {
187
+ let settle = null;
188
+ let fail = null;
189
+ const received = new Promise((resolveCode, rejectCode) => {
190
+ settle = resolveCode;
191
+ fail = rejectCode;
192
+ });
193
+ const server = createServer((request2, response) => {
194
+ const url = new URL(request2.url ?? "/", "http://127.0.0.1");
195
+ if (url.pathname !== "/cb") {
196
+ response.writeHead(404, { "Content-Type": "text/plain" });
197
+ response.end("Not found.\n");
198
+ return;
199
+ }
200
+ const code = url.searchParams.get("code") ?? "";
201
+ const returned = url.searchParams.get("state") ?? "";
202
+ if (returned !== state || code === "") {
203
+ respond(
204
+ response,
205
+ 400,
206
+ "Sign-in could not be completed",
207
+ "This did not come from the sign-in that was started here. Nothing has been changed."
208
+ );
209
+ fail?.(new Error("The browser returned a response that did not match this sign-in."));
210
+ return;
211
+ }
212
+ respond(response, 200, "Signed in", "You can close this tab now.");
213
+ settle?.({ code });
214
+ });
215
+ server.on("error", (error) => {
216
+ rejectListener(error);
217
+ fail?.(error instanceof Error ? error : new Error(String(error)));
218
+ });
219
+ server.listen(0, "127.0.0.1", () => {
220
+ const address = server.address();
221
+ if (address === null) {
222
+ rejectListener(new Error("Could not open a local port to receive the sign-in."));
223
+ return;
224
+ }
225
+ resolveListener({
226
+ port: address.port,
227
+ received,
228
+ close: () => server.close()
229
+ });
230
+ });
231
+ });
232
+ }
233
+ function respond(response, status, title, detail) {
234
+ response.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
235
+ response.end(
236
+ `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title} — drop2run</title><style>body{font:16px/1.6 system-ui,sans-serif;margin:20vh auto;max-width:28rem;padding:0 1.5rem;color:#12211f}h1{font-size:1.4rem;margin:0 0 .5rem}p{margin:0;color:#64706e}@media(prefers-color-scheme:dark){body{background:#0d1413;color:#e8efee}p{color:#9aa8a6}}</style></head><body><h1>${title}</h1><p>${detail}</p></body></html>
237
+ `
238
+ );
239
+ }
240
+ async function waitForCallback(received, timeoutMs = TIMEOUT_MS) {
241
+ let timer;
242
+ try {
243
+ return await Promise.race([
244
+ received,
245
+ new Promise((_, reject) => {
246
+ timer = setTimeout(
247
+ () => reject(new Error("Timed out waiting for the browser. Nothing has been changed.")),
248
+ timeoutMs
249
+ );
250
+ })
251
+ ]);
252
+ } finally {
253
+ if (timer !== void 0) clearTimeout(timer);
254
+ }
255
+ }
256
+ function consentUrl(dashboardUrl, attempt, port, name) {
257
+ const url = new URL("/cli-auth", dashboardUrl);
258
+ url.searchParams.set("challenge", attempt.challenge);
259
+ url.searchParams.set("state", attempt.state);
260
+ url.searchParams.set("port", String(port));
261
+ url.searchParams.set("name", name);
262
+ return url.toString();
263
+ }
264
+ async function startDevice(apiBaseUrl, clientName2) {
265
+ const response = await fetch(`${apiBaseUrl}/auth/cli/device`, {
266
+ method: "POST",
267
+ headers: { "Content-Type": "application/json" },
268
+ body: JSON.stringify({ clientName: clientName2 })
269
+ });
270
+ if (!response.ok) throw new Error(await problemDetail(response));
271
+ return await response.json();
272
+ }
273
+ async function pollDevice(apiBaseUrl, deviceCode) {
274
+ let response;
275
+ try {
276
+ response = await fetch(`${apiBaseUrl}/auth/cli/device/token`, {
277
+ method: "POST",
278
+ headers: { "Content-Type": "application/json" },
279
+ body: JSON.stringify({ deviceCode })
280
+ });
281
+ } catch {
282
+ return { state: "pending" };
283
+ }
284
+ if (response.ok) {
285
+ const body = await response.json();
286
+ return { state: "granted", token: { token: body.token, name: body.name, email: body.email } };
287
+ }
288
+ const problem = await response.json().catch(() => null);
289
+ if (problem?.type === "authorization_pending") return { state: "pending" };
290
+ if (problem?.type === "slow_down") return { state: "slow_down" };
291
+ return {
292
+ state: "dead",
293
+ message: problem?.detail ?? `The API answered ${response.status}.`
294
+ };
295
+ }
296
+ async function problemDetail(response) {
297
+ const problem = await response.json().catch(() => null);
298
+ return problem?.detail ?? `The API answered ${response.status}.`;
299
+ }
300
+ async function exchange(apiBaseUrl, code, verifier) {
301
+ const response = await fetch(`${apiBaseUrl}/auth/cli/token`, {
302
+ method: "POST",
303
+ headers: { "Content-Type": "application/json" },
304
+ body: JSON.stringify({ code, verifier })
305
+ });
306
+ if (!response.ok) throw new Error(await problemDetail(response));
307
+ const body = await response.json();
308
+ return { token: body.token, name: body.name, email: body.email };
309
+ }
142
310
  const PROJECT_FILE = "drop2run.json";
143
311
  function projectPath(directory = process.cwd()) {
144
312
  return join(directory, PROJECT_FILE);
@@ -349,7 +517,7 @@ function renameOfLoneHtmlPage(paths) {
349
517
  const lower = only.toLowerCase();
350
518
  return lower.endsWith(".html") || lower.endsWith(".htm") ? only : null;
351
519
  }
352
- function formatBytes(bytes) {
520
+ function formatBytes$1(bytes) {
353
521
  if (bytes < 1024) return `${bytes} B`;
354
522
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
355
523
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
@@ -380,7 +548,7 @@ function checkLimits(files, limits) {
380
548
  if (oversized) {
381
549
  throw new DeployError(
382
550
  ClientErrorCode.FileTooLarge,
383
- `${oversized.path} is ${formatBytes(oversized.bytes.length)}, and this plan allows ${formatBytes(limits.maxFileBytes)} per file.`,
551
+ `${oversized.path} is ${formatBytes$1(oversized.bytes.length)}, and this plan allows ${formatBytes$1(limits.maxFileBytes)} per file.`,
384
552
  {
385
553
  path: oversized.path,
386
554
  limit: limits.maxFileBytes,
@@ -392,7 +560,7 @@ function checkLimits(files, limits) {
392
560
  if (limits.maxSiteBytes !== null && total > limits.maxSiteBytes) {
393
561
  throw new DeployError(
394
562
  ClientErrorCode.TooLarge,
395
- `That drop is ${formatBytes(total)}, and this plan allows ${formatBytes(limits.maxSiteBytes)}.`,
563
+ `That drop is ${formatBytes$1(total)}, and this plan allows ${formatBytes$1(limits.maxSiteBytes)}.`,
396
564
  { limit: limits.maxSiteBytes, actual: total }
397
565
  );
398
566
  }
@@ -530,7 +698,7 @@ async function uploadAll(targets, uploadUrl, files, onProgress, signal, deps = {
530
698
  });
531
699
  done += 1;
532
700
  uploadedBytes += file.bytes.length;
533
- onProgress?.(done, targets.length, uploadedBytes);
701
+ onProgress?.(done, targets.length, uploadedBytes, file.path);
534
702
  }
535
703
  };
536
704
  const workers = Array.from(
@@ -625,12 +793,13 @@ async function deploy(source, siteId, onProgress, signal, options = {}) {
625
793
  prepared.upload,
626
794
  prepared.uploadUrl,
627
795
  hashed,
628
- (done, _total, bytes) => onProgress({
796
+ (done, _total, bytes, path) => onProgress({
629
797
  type: "uploading",
630
798
  done: prepared.reused + done,
631
799
  total: prepared.total,
632
800
  bytes,
633
- reused: prepared.reused
801
+ reused: prepared.reused,
802
+ path
634
803
  }),
635
804
  signal,
636
805
  options.upload
@@ -704,12 +873,12 @@ async function resolveSite(credentials, site) {
704
873
  );
705
874
  if (found === void 0) {
706
875
  throw new Error(
707
- `No site of yours is called "${site}". Leave the site out to publish to a new one, or use list_sites to see what exists.`
876
+ `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.`
708
877
  );
709
878
  }
710
879
  return found;
711
880
  }
712
- async function publish(credentials, source, site) {
881
+ async function publish(credentials, source, site, onProgress) {
713
882
  const target = await resolveSite(credentials, site);
714
883
  let files = 0;
715
884
  let url = target.url;
@@ -727,6 +896,7 @@ async function publish(credentials, source, site) {
727
896
  failure2 = event.message;
728
897
  failureDetail = event.detail;
729
898
  }
899
+ onProgress?.(event);
730
900
  };
731
901
  await deploy(source, target.siteId, record, void 0, {
732
902
  baseUrl: credentials.apiBaseUrl,
@@ -740,158 +910,75 @@ async function publish(credentials, source, site) {
740
910
  });
741
911
  return { url, siteId: target.siteId, subdomain: target.subdomain, files, unchanged };
742
912
  }
743
- function publishDirectory(credentials, directory, site) {
744
- return publish(credentials, directorySource(directory), site);
745
- }
746
- const TIMEOUT_MS = 5 * 60 * 1e3;
747
- function newAttempt() {
748
- const verifier = randomBytes(32).toString("base64url");
749
- return {
750
- verifier,
751
- challenge: createHash("sha256").update(verifier).digest("base64url"),
752
- state: randomBytes(16).toString("base64url")
753
- };
754
- }
755
- function clientName() {
756
- const raw = hostname().trim().replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 64);
757
- return raw === "" ? "drop2run-cli" : raw;
913
+ function publishDirectory(credentials, directory, site, onProgress) {
914
+ return publish(credentials, directorySource(directory), site, onProgress);
758
915
  }
759
- function openBrowser(url) {
760
- const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
761
- try {
762
- const child = spawn(command, [...args], { stdio: "ignore", detached: true });
763
- child.unref();
764
- child.on("error", () => {
765
- });
766
- return true;
767
- } catch {
768
- return false;
916
+ const silentProgress = Object.assign(() => {
917
+ }, { done: () => {
918
+ } });
919
+ const STEP = 1024;
920
+ const UNITS = ["B", "KB", "MB", "GB"];
921
+ function formatBytes(bytes) {
922
+ let value = bytes;
923
+ let unit = 0;
924
+ while (value >= STEP && unit < UNITS.length - 1) {
925
+ value /= STEP;
926
+ unit += 1;
769
927
  }
770
- }
771
- function listen(state) {
772
- return new Promise((resolveListener, rejectListener) => {
773
- let settle = null;
774
- let fail = null;
775
- const received = new Promise((resolveCode, rejectCode) => {
776
- settle = resolveCode;
777
- fail = rejectCode;
778
- });
779
- const server = createServer((request2, response) => {
780
- const url = new URL(request2.url ?? "/", "http://127.0.0.1");
781
- if (url.pathname !== "/cb") {
782
- response.writeHead(404, { "Content-Type": "text/plain" });
783
- response.end("Not found.\n");
784
- return;
785
- }
786
- const code = url.searchParams.get("code") ?? "";
787
- const returned = url.searchParams.get("state") ?? "";
788
- if (returned !== state || code === "") {
789
- respond(
790
- response,
791
- 400,
792
- "Sign-in could not be completed",
793
- "This did not come from the sign-in that was started here. Nothing has been changed."
794
- );
795
- fail?.(new Error("The browser returned a response that did not match this sign-in."));
796
- return;
797
- }
798
- respond(response, 200, "Signed in", "You can close this tab and go back to the terminal.");
799
- settle?.({ code });
800
- });
801
- server.on("error", (error) => {
802
- rejectListener(error);
803
- fail?.(error instanceof Error ? error : new Error(String(error)));
804
- });
805
- server.listen(0, "127.0.0.1", () => {
806
- const address = server.address();
807
- if (address === null) {
808
- rejectListener(new Error("Could not open a local port to receive the sign-in."));
809
- return;
810
- }
811
- resolveListener({
812
- port: address.port,
813
- received,
814
- close: () => server.close()
815
- });
816
- });
817
- });
818
- }
819
- function respond(response, status, title, detail) {
820
- response.writeHead(status, { "Content-Type": "text/html; charset=utf-8" });
821
- response.end(
822
- `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${title} — drop2run</title><style>body{font:16px/1.6 system-ui,sans-serif;margin:20vh auto;max-width:28rem;padding:0 1.5rem;color:#12211f}h1{font-size:1.4rem;margin:0 0 .5rem}p{margin:0;color:#64706e}@media(prefers-color-scheme:dark){body{background:#0d1413;color:#e8efee}p{color:#9aa8a6}}</style></head><body><h1>${title}</h1><p>${detail}</p></body></html>
823
- `
824
- );
825
- }
826
- async function waitForCallback(received, timeoutMs = TIMEOUT_MS) {
827
- let timer;
828
- try {
829
- return await Promise.race([
830
- received,
831
- new Promise((_, reject) => {
832
- timer = setTimeout(
833
- () => reject(new Error("Timed out waiting for the browser. Nothing has been changed.")),
834
- timeoutMs
835
- );
836
- })
837
- ]);
838
- } finally {
839
- if (timer !== void 0) clearTimeout(timer);
928
+ return `${unit === 0 ? value : value.toFixed(1)} ${UNITS[unit]}`;
929
+ }
930
+ function shortenPath(path, width) {
931
+ if (path.length <= width) return path;
932
+ return `…${path.slice(path.length - width + 1)}`;
933
+ }
934
+ function describe(event, width = 80) {
935
+ switch (event.type) {
936
+ case "collecting":
937
+ return `Collected ${event.files.toLocaleString()} ${event.files === 1 ? "file" : "files"}`;
938
+ case "hashing":
939
+ return `Hashing ${event.done.toLocaleString()}/${event.total.toLocaleString()}`;
940
+ case "preparing":
941
+ return "Asking the server which files it already has";
942
+ case "uploading": {
943
+ const counted = `Uploading ${event.done.toLocaleString()}/${event.total.toLocaleString()}`;
944
+ const reused = event.reused === 0 ? "" : ` (${event.reused.toLocaleString()} already there)`;
945
+ const head = `${counted}${reused} · ${formatBytes(event.bytes)}`;
946
+ const room = width - head.length - 3;
947
+ return event.path === void 0 || room < 12 ? head : `${head} · ${shortenPath(event.path, room)}`;
948
+ }
949
+ case "completing":
950
+ return "Publishing";
951
+ // Both end the deploy, and the command prints the URL from the result it returns. Saying it here
952
+ // as well would print it twice, once on stderr and once on stdout.
953
+ case "done":
954
+ case "unchanged":
955
+ case "error":
956
+ return null;
840
957
  }
841
958
  }
842
- function consentUrl(dashboardUrl, attempt, port, name) {
843
- const url = new URL("/cli-auth", dashboardUrl);
844
- url.searchParams.set("challenge", attempt.challenge);
845
- url.searchParams.set("state", attempt.state);
846
- url.searchParams.set("port", String(port));
847
- url.searchParams.set("name", name);
848
- return url.toString();
849
- }
850
- async function startDevice(apiBaseUrl, clientName2) {
851
- const response = await fetch(`${apiBaseUrl}/auth/cli/device`, {
852
- method: "POST",
853
- headers: { "Content-Type": "application/json" },
854
- body: JSON.stringify({ clientName: clientName2 })
959
+ function progressWriter(write, isTerminal, width = 80) {
960
+ let painted = 0;
961
+ let lastStage = null;
962
+ const writer = ((event) => {
963
+ const line = describe(event, width - 1);
964
+ if (line === null) return;
965
+ if (!isTerminal) {
966
+ if (event.type === lastStage) return;
967
+ lastStage = event.type;
968
+ write(`${line}
969
+ `);
970
+ return;
971
+ }
972
+ lastStage = event.type;
973
+ write(`\r${line}${" ".repeat(Math.max(0, painted - line.length))}`);
974
+ painted = line.length;
855
975
  });
856
- if (!response.ok) throw new Error(await problemDetail(response));
857
- return await response.json();
858
- }
859
- async function pollDevice(apiBaseUrl, deviceCode) {
860
- let response;
861
- try {
862
- response = await fetch(`${apiBaseUrl}/auth/cli/device/token`, {
863
- method: "POST",
864
- headers: { "Content-Type": "application/json" },
865
- body: JSON.stringify({ deviceCode })
866
- });
867
- } catch {
868
- return { state: "pending" };
869
- }
870
- if (response.ok) {
871
- const body = await response.json();
872
- return { state: "granted", token: { token: body.token, name: body.name, email: body.email } };
873
- }
874
- const problem = await response.json().catch(() => null);
875
- if (problem?.type === "authorization_pending") return { state: "pending" };
876
- if (problem?.type === "slow_down") return { state: "slow_down" };
877
- return {
878
- state: "dead",
879
- message: problem?.detail ?? `The API answered ${response.status}.`
976
+ writer.done = () => {
977
+ if (!isTerminal || painted === 0) return;
978
+ write(`\r${" ".repeat(painted)}\r`);
979
+ painted = 0;
880
980
  };
881
- }
882
- async function problemDetail(response) {
883
- const problem = await response.json().catch(() => null);
884
- return problem?.detail ?? `The API answered ${response.status}.`;
885
- }
886
- async function exchange(apiBaseUrl, code, verifier) {
887
- const response = await fetch(`${apiBaseUrl}/auth/cli/token`, {
888
- method: "POST",
889
- headers: { "Content-Type": "application/json" },
890
- body: JSON.stringify({ code, verifier })
891
- });
892
- if (!response.ok) throw new Error(await problemDetail(response));
893
- const body = await response.json();
894
- return { token: body.token, name: body.name, email: body.email };
981
+ return writer;
895
982
  }
896
983
  function failure(text) {
897
984
  return { text, json: { error: text }, code: 1 };
@@ -1035,14 +1122,14 @@ async function list() {
1035
1122
  code: 0
1036
1123
  };
1037
1124
  }
1038
- async function deployCommand(directory, site) {
1125
+ async function deployCommand(directory, site, report = silentProgress) {
1039
1126
  const found = credentialsOr();
1040
1127
  if ("result" in found) return found.result;
1041
1128
  const project = readProject();
1042
1129
  const target = site ?? project?.siteId;
1043
1130
  const folder = directory ?? project?.dir ?? ".";
1044
1131
  try {
1045
- const result = await publishDirectory(found.credentials, resolve(folder), target);
1132
+ const result = await publishDirectory(found.credentials, resolve(folder), target, report);
1046
1133
  const what = result.unchanged ? "Already up to date — nothing needed publishing." : `Published ${result.files.toLocaleString()} ${result.files === 1 ? "file" : "files"}.`;
1047
1134
  return {
1048
1135
  text: `${what}
@@ -1052,6 +1139,8 @@ ${result.url}`,
1052
1139
  };
1053
1140
  } catch (error) {
1054
1141
  return failureFrom(error);
1142
+ } finally {
1143
+ report.done();
1055
1144
  }
1056
1145
  }
1057
1146
  async function init(directory, site) {
@@ -1240,7 +1329,7 @@ async function run(argv) {
1240
1329
  const positional = argv.filter((argument) => !argument.startsWith("-"));
1241
1330
  const [command, ...rest] = positional;
1242
1331
  if (argv.includes("--version")) {
1243
- const { version } = await import("./package-C6bEGZf_.js").then(
1332
+ const { version } = await import("./package-B5PgBI_i.js").then(
1244
1333
  (module) => module.default
1245
1334
  );
1246
1335
  return { text: version, json: { version }, code: 0 };
@@ -1270,8 +1359,14 @@ async function dispatch(command, rest, site, json, device, confirmed) {
1270
1359
  return logout();
1271
1360
  case "init":
1272
1361
  return await init(rest[0] ?? ".", site);
1273
- case "deploy":
1274
- return await deployCommand(rest[0], site);
1362
+ case "deploy": {
1363
+ const report = json ? silentProgress : progressWriter(
1364
+ (text) => process.stderr.write(text),
1365
+ process.stderr.isTTY === true,
1366
+ process.stderr.columns ?? 80
1367
+ );
1368
+ return await deployCommand(rest[0], site, report);
1369
+ }
1275
1370
  case "ls":
1276
1371
  case "list":
1277
1372
  return await list();
@@ -1,5 +1,5 @@
1
1
  const name = "drop2run";
2
- const version = "0.2.0";
2
+ const version = "0.3.0";
3
3
  const description = "Publish a static site to Drop2Run from the command line.";
4
4
  const license = "MIT";
5
5
  const type = "module";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drop2run",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Publish a static site to Drop2Run from the command line.",
5
5
  "license": "MIT",
6
6
  "type": "module",