drop2run 0.1.1 → 0.2.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 +29 -95
- package/dist/index.js +58 -9
- package/dist/{package-BGfKrpb3.js → package-C6bEGZf_.js} +8 -2
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ drop2run login
|
|
|
8
8
|
drop2run deploy dist
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Node 20 or newer.
|
|
12
|
-
|
|
11
|
+
Node 20 or newer. `login` opens a browser and stores a token, `deploy` prints the URL. Full
|
|
12
|
+
documentation at [dropto.run/docs/cli](https://dropto.run/docs/cli).
|
|
13
13
|
|
|
14
14
|
```
|
|
15
15
|
drop2run login [--device] Sign in and store a token
|
|
@@ -23,6 +23,7 @@ drop2run rm <site> --yes Delete a site and everything on it
|
|
|
23
23
|
drop2run token list List your access tokens
|
|
24
24
|
drop2run whoami Check the token and whose it is
|
|
25
25
|
drop2run where Show which token source is in use
|
|
26
|
+
drop2run --version Print the version
|
|
26
27
|
```
|
|
27
28
|
|
|
28
29
|
`--json` on any command prints machine-readable output instead of text.
|
|
@@ -37,117 +38,50 @@ drop2run init dist # creates a site, writes drop2run.json
|
|
|
37
38
|
drop2run deploy # publishes dist to it, no arguments
|
|
38
39
|
```
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
`deploy` never creates a second site while a `drop2run.json` sits next to it — that would leave the real
|
|
42
|
+
site untouched and you looking at a URL you did not expect.
|
|
42
43
|
|
|
43
|
-
`rm`
|
|
44
|
-
|
|
45
|
-
terminal has no confirmation dialog, so the flag is the dialog.
|
|
46
|
-
|
|
47
|
-
`token create` and `token revoke` do not exist; see below.
|
|
44
|
+
`rm` prints the site it would delete and stops; it only runs with `--yes`. There is no undo — the files
|
|
45
|
+
go and the subdomain is released.
|
|
48
46
|
|
|
49
47
|
## Signing in
|
|
50
48
|
|
|
51
49
|
`drop2run login` opens a browser, waits on `127.0.0.1`, and stores the token it is handed in
|
|
52
|
-
`~/.config/drop2run/config.json` with mode `0600`.
|
|
53
|
-
|
|
54
|
-
Two things about it are worth knowing, because they decide when it works:
|
|
50
|
+
`~/.config/drop2run/config.json` with mode `0600`. It uses PKCE, so there is no client secret to leak.
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
`--device` there.
|
|
60
|
-
- **It uses PKCE, so there is no client secret.** A verifier is generated per sign-in and never leaves the
|
|
61
|
-
process; only its SHA-256 travels through the browser. A code intercepted anywhere along the way cannot
|
|
62
|
-
be exchanged without the verifier.
|
|
52
|
+
It needs a browser and loopback **on the same machine**: the token arrives by redirect to a temporary
|
|
53
|
+
server on `127.0.0.1`, which is what keeps it out of clipboards and scrollback. On a remote shell, use
|
|
54
|
+
`--device` instead.
|
|
63
55
|
|
|
64
56
|
### `--device`, for a machine with no browser
|
|
65
57
|
|
|
66
58
|
`drop2run login --device` prints a short code and waits. Enter it at <https://dropto.run/device> from any
|
|
67
|
-
machine you are signed in on — a phone will do — and the terminal picks up its token.
|
|
68
|
-
|
|
69
|
-
The short code is not the credential: it names the pending request, and approving it releases a long code
|
|
70
|
-
that never left the waiting process. Somebody reading it over your shoulder learns which sign-in is
|
|
71
|
-
waiting, not how to collect its token. It lasts fifteen minutes and works once.
|
|
72
|
-
|
|
73
|
-
For CI, neither flow applies — nothing there can open a browser or approve anything. Create a token at
|
|
74
|
-
<https://dropto.run/account/tokens> and either set it in the environment:
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
DROP2RUN_TOKEN=d2r_...
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
or put it in `~/.config/drop2run/config.json`:
|
|
81
|
-
|
|
82
|
-
```json
|
|
83
|
-
{ "token": "d2r_..." }
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
The environment wins. That is the same file and the same precedence `@drop2run/mcp` uses, so signing in
|
|
87
|
-
once covers both.
|
|
88
|
-
|
|
89
|
-
`drop2run where` says which source is in force without ever printing the token. That is deliberate: the
|
|
90
|
-
output of a command line ends up in issue reports, terminal recordings and CI logs, and "why is it using
|
|
91
|
-
the wrong account" is answerable without showing the secret.
|
|
59
|
+
machine you are already signed in on — a phone will do — and the terminal picks up its token.
|
|
92
60
|
|
|
93
|
-
|
|
61
|
+
The short code is not the credential; it names the pending request, so somebody reading it over your
|
|
62
|
+
shoulder learns which sign-in is waiting, not how to collect its token. It lasts fifteen minutes and
|
|
63
|
+
works once.
|
|
94
64
|
|
|
95
|
-
|
|
96
|
-
decision rather than an omission: a token that can mint tokens is not a leaked credential but a permanent
|
|
97
|
-
one — whoever takes it makes a second, and revoking the first changes nothing because the replacement is
|
|
98
|
-
one the owner never made and will not recognise. Both endpoints require a browser session. `gh` and
|
|
99
|
-
`vercel` draw the line in the same place.
|
|
65
|
+
### CI
|
|
100
66
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
`token revoke` is refused with a message rather than treated as `list` — somebody will type it, and
|
|
104
|
-
listing instead would read as having worked.
|
|
67
|
+
Neither flow works in CI — nothing there can open a browser or approve anything. Create a token at
|
|
68
|
+
<https://dropto.run/account/tokens> and set it in the environment:
|
|
105
69
|
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
Everything below is for whoever maintains this package. It is here rather than in the monorepo's docs
|
|
109
|
-
because it is about this directory — but it is on the npm page too, so it says who it is for.
|
|
110
|
-
|
|
111
|
-
## Releasing
|
|
112
|
-
|
|
113
|
-
The current version is whatever `package.json` says; the first release with `login` in it was `0.1.0`,
|
|
114
|
-
published 02/09/2026. `0.0.0` is also on the registry and is not a release — it held the bare name
|
|
115
|
-
`drop2run` (brief §9.1) and predates `login` entirely, so nothing should ever be pinned to it.
|
|
116
|
-
|
|
117
|
-
Check a publish from outside rather than from its own output, because the failure worth ruling out is a
|
|
118
|
-
tarball with no `dist` — which the publish log looks perfectly happy about:
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
$ npx drop2run@<version> --version
|
|
122
70
|
```
|
|
123
|
-
|
|
124
|
-
Publishing is one command:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
cd packages/cli && npm publish
|
|
71
|
+
DROP2RUN_TOKEN=d2r_...
|
|
128
72
|
```
|
|
129
73
|
|
|
130
|
-
|
|
131
|
-
`
|
|
132
|
-
a checkout that had not been built would ship a package that throws on its first line — at a version
|
|
133
|
-
number that can never be reused, because npm burns one even after `unpublish`.
|
|
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.
|
|
134
76
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
fail in Actions too. Provenance arrives with the `drop2run-cli` split in brief §9.2 — which has its own
|
|
138
|
-
precondition, a `gitleaks` sweep of the whole history rather than of HEAD.
|
|
77
|
+
`drop2run where` says which source is in force without printing the token, so "why is it using the wrong
|
|
78
|
+
account" is answerable in an issue report or a CI log.
|
|
139
79
|
|
|
140
|
-
|
|
141
|
-
versions have to move in that order, and the fact that this package is the only one of the four with a
|
|
142
|
-
licence field.
|
|
80
|
+
## `token create` and `token revoke` do not exist
|
|
143
81
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
82
|
+
Both need a browser session, and that is deliberate rather than missing. A token that can mint tokens is
|
|
83
|
+
not a leaked credential but a permanent one: whoever takes it makes a second, and revoking the first
|
|
84
|
+
changes nothing. Make and revoke tokens at <https://dropto.run/account/tokens>.
|
|
147
85
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
cd packages/cli && npm run build
|
|
152
|
-
node bin/drop2run.mjs --help
|
|
153
|
-
```
|
|
86
|
+
`token list` does exist and answers what a terminal can answer — which machines hold a credential, and
|
|
87
|
+
which of them has not used it since it was made. It prints prefixes, never secrets.
|
package/dist/index.js
CHANGED
|
@@ -299,6 +299,56 @@ function isDocumentPath(path) {
|
|
|
299
299
|
const lower = path.toLowerCase();
|
|
300
300
|
return DOCUMENT_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
301
301
|
}
|
|
302
|
+
const VIEWABLE_EXTENSIONS = [
|
|
303
|
+
// Documents — these also count at any depth, via DOCUMENT_EXTENSIONS.
|
|
304
|
+
".md",
|
|
305
|
+
".markdown",
|
|
306
|
+
".pdf",
|
|
307
|
+
// Prose the reader shows preformatted.
|
|
308
|
+
".txt",
|
|
309
|
+
".log",
|
|
310
|
+
".csv",
|
|
311
|
+
// Source the reader shows with highlighting.
|
|
312
|
+
".json",
|
|
313
|
+
".yaml",
|
|
314
|
+
".yml",
|
|
315
|
+
".toml",
|
|
316
|
+
".css",
|
|
317
|
+
".js",
|
|
318
|
+
".ts",
|
|
319
|
+
".tsx",
|
|
320
|
+
".jsx",
|
|
321
|
+
".sh",
|
|
322
|
+
".xml",
|
|
323
|
+
// Pictures the reader shows with an img element.
|
|
324
|
+
".png",
|
|
325
|
+
".jpg",
|
|
326
|
+
".jpeg",
|
|
327
|
+
".gif",
|
|
328
|
+
".webp",
|
|
329
|
+
".avif",
|
|
330
|
+
".svg",
|
|
331
|
+
".ico",
|
|
332
|
+
// See the note above: a floor for a caller that did not rename it.
|
|
333
|
+
".html",
|
|
334
|
+
".htm"
|
|
335
|
+
];
|
|
336
|
+
function isViewablePath(path) {
|
|
337
|
+
const lower = path.toLowerCase();
|
|
338
|
+
return VIEWABLE_EXTENSIONS.some((extension) => lower.endsWith(extension));
|
|
339
|
+
}
|
|
340
|
+
function canPublish(paths) {
|
|
341
|
+
if (paths.some((path) => path === REQUIRED_INDEX)) return true;
|
|
342
|
+
if (paths.some(isDocumentPath)) return true;
|
|
343
|
+
return paths.length === 1 && paths[0] !== void 0 && isViewablePath(paths[0]);
|
|
344
|
+
}
|
|
345
|
+
function renameOfLoneHtmlPage(paths) {
|
|
346
|
+
if (paths.length !== 1) return null;
|
|
347
|
+
const [only] = paths;
|
|
348
|
+
if (only === void 0 || only === REQUIRED_INDEX) return null;
|
|
349
|
+
const lower = only.toLowerCase();
|
|
350
|
+
return lower.endsWith(".html") || lower.endsWith(".htm") ? only : null;
|
|
351
|
+
}
|
|
302
352
|
function formatBytes(bytes) {
|
|
303
353
|
if (bytes < 1024) return `${bytes} B`;
|
|
304
354
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -312,11 +362,10 @@ function checkLimits(files, limits) {
|
|
|
312
362
|
"That drop contained no files to deploy. Folders like .git and node_modules are skipped."
|
|
313
363
|
);
|
|
314
364
|
}
|
|
315
|
-
|
|
316
|
-
if (!hasIndex && !files.some((file) => isDocumentPath(file.path))) {
|
|
365
|
+
if (!canPublish(files.map((file) => file.path))) {
|
|
317
366
|
throw new DeployError(
|
|
318
367
|
ClientErrorCode.MissingIndex,
|
|
319
|
-
`A site needs an ${REQUIRED_INDEX} at its top level, or at least one .md or .pdf file to publish as a documents site. Drop the folder that contains it, not the folder above.`
|
|
368
|
+
`A site needs an ${REQUIRED_INDEX} at its top level, or at least one .md or .pdf file to publish as a documents site — or be a single file that can be displayed on its own, such as a note, a PDF, a page or an image. Drop the folder that contains it, not the folder above.`
|
|
320
369
|
);
|
|
321
370
|
}
|
|
322
371
|
if (!limits) return;
|
|
@@ -327,9 +376,7 @@ function checkLimits(files, limits) {
|
|
|
327
376
|
{ limit: limits.maxFiles, actual: files.length }
|
|
328
377
|
);
|
|
329
378
|
}
|
|
330
|
-
const oversized = files.find(
|
|
331
|
-
(file) => file.bytes.length > limits.maxFileBytes
|
|
332
|
-
);
|
|
379
|
+
const oversized = files.find((file) => file.bytes.length > limits.maxFileBytes);
|
|
333
380
|
if (oversized) {
|
|
334
381
|
throw new DeployError(
|
|
335
382
|
ClientErrorCode.FileTooLarge,
|
|
@@ -558,8 +605,10 @@ function asDeployError(error) {
|
|
|
558
605
|
}
|
|
559
606
|
async function deploy(source, siteId, onProgress, signal, options = {}) {
|
|
560
607
|
try {
|
|
561
|
-
const
|
|
562
|
-
onProgress({ type: "collecting", files:
|
|
608
|
+
const collected = await source(signal);
|
|
609
|
+
onProgress({ type: "collecting", files: collected.length });
|
|
610
|
+
const rename = renameOfLoneHtmlPage(collected.map((file) => file.path));
|
|
611
|
+
const files = rename === null ? collected : collected.map((file) => file.path === rename ? { ...file, path: "index.html" } : file);
|
|
563
612
|
const hashed = await hashAll(
|
|
564
613
|
files,
|
|
565
614
|
(done, total) => onProgress({ type: "hashing", done, total }),
|
|
@@ -1191,7 +1240,7 @@ async function run(argv) {
|
|
|
1191
1240
|
const positional = argv.filter((argument) => !argument.startsWith("-"));
|
|
1192
1241
|
const [command, ...rest] = positional;
|
|
1193
1242
|
if (argv.includes("--version")) {
|
|
1194
|
-
const { version } = await import("./package-
|
|
1243
|
+
const { version } = await import("./package-C6bEGZf_.js").then(
|
|
1195
1244
|
(module) => module.default
|
|
1196
1245
|
);
|
|
1197
1246
|
return { text: version, json: { version }, code: 0 };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const name = "drop2run";
|
|
2
|
-
const version = "0.
|
|
2
|
+
const version = "0.2.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";
|
|
@@ -8,7 +8,9 @@ const files = ["bin", "dist"];
|
|
|
8
8
|
const engines = { "node": ">=20" };
|
|
9
9
|
const publishConfig = { "access": "public" };
|
|
10
10
|
const homepage = "https://dropto.run";
|
|
11
|
-
const
|
|
11
|
+
const repository = { "type": "git", "url": "git+https://github.com/Drop-to-run/drop2run-cli.git" };
|
|
12
|
+
const bugs = { "url": "https://github.com/Drop-to-run/drop2run-cli/issues" };
|
|
13
|
+
const keywords = ["static-site", "deploy", "hosting", "cli", "ai", "claude", "agent", "llm"];
|
|
12
14
|
const scripts = { "prepublishOnly": "npm run build && npm test", "build": "vite build", "typecheck": "tsc --noEmit", "test": "vitest run", "lint": "biome check .", "format": "biome check --write ." };
|
|
13
15
|
const devDependencies = { "@biomejs/biome": "^2.5.11", "@types/node": "^26.4.0", "typescript": "^5.9.3", "vite": "^7.2.1", "vitest": "^4.1.11" };
|
|
14
16
|
const _package = {
|
|
@@ -22,12 +24,15 @@ const _package = {
|
|
|
22
24
|
engines,
|
|
23
25
|
publishConfig,
|
|
24
26
|
homepage,
|
|
27
|
+
repository,
|
|
28
|
+
bugs,
|
|
25
29
|
keywords,
|
|
26
30
|
scripts,
|
|
27
31
|
devDependencies
|
|
28
32
|
};
|
|
29
33
|
export {
|
|
30
34
|
bin,
|
|
35
|
+
bugs,
|
|
31
36
|
_package as default,
|
|
32
37
|
description,
|
|
33
38
|
devDependencies,
|
|
@@ -38,6 +43,7 @@ export {
|
|
|
38
43
|
license,
|
|
39
44
|
name,
|
|
40
45
|
publishConfig,
|
|
46
|
+
repository,
|
|
41
47
|
scripts,
|
|
42
48
|
type,
|
|
43
49
|
version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drop2run",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Publish a static site to Drop2Run from the command line.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,11 +18,22 @@
|
|
|
18
18
|
"access": "public"
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://dropto.run",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/Drop-to-run/drop2run-cli.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/Drop-to-run/drop2run-cli/issues"
|
|
27
|
+
},
|
|
21
28
|
"keywords": [
|
|
22
29
|
"static-site",
|
|
23
30
|
"deploy",
|
|
24
31
|
"hosting",
|
|
25
|
-
"cli"
|
|
32
|
+
"cli",
|
|
33
|
+
"ai",
|
|
34
|
+
"claude",
|
|
35
|
+
"agent",
|
|
36
|
+
"llm"
|
|
26
37
|
],
|
|
27
38
|
"scripts": {
|
|
28
39
|
"prepublishOnly": "npm run build && npm test",
|