@dropthis/cli 0.3.5 → 0.4.1
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 +39 -21
- package/dist/cli.cjs +270 -20
- package/dist/cli.cjs.map +1 -1
- package/node_modules/@dropthis/node/README.md +181 -53
- package/node_modules/@dropthis/node/dist/index.cjs +178 -157
- package/node_modules/@dropthis/node/dist/index.cjs.map +1 -1
- package/node_modules/@dropthis/node/dist/index.d.cts +87 -26
- package/node_modules/@dropthis/node/dist/index.d.ts +87 -26
- package/node_modules/@dropthis/node/dist/index.mjs +177 -157
- package/node_modules/@dropthis/node/dist/index.mjs.map +1 -1
- package/node_modules/@dropthis/node/package.json +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Requires Node.js >= 20.
|
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
dropthis login
|
|
17
|
-
dropthis
|
|
17
|
+
dropthis ./index.html # publish is the default command
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Authentication
|
|
@@ -47,6 +47,8 @@ Credentials resolve in this order:
|
|
|
47
47
|
2. `DROPTHIS_API_KEY` environment variable
|
|
48
48
|
3. Stored credential from `dropthis login`
|
|
49
49
|
|
|
50
|
+
If you run `dropthis publish` without credentials in an interactive terminal, the CLI prompts you to log in inline — no separate `dropthis login` step needed. Disable with `--no-interactive`.
|
|
51
|
+
|
|
50
52
|
```bash
|
|
51
53
|
dropthis whoami # Check current auth status
|
|
52
54
|
dropthis logout # Remove stored credentials
|
|
@@ -55,21 +57,29 @@ dropthis logout --revoke # Remove and revoke the key on the server
|
|
|
55
57
|
|
|
56
58
|
## Publish
|
|
57
59
|
|
|
60
|
+
`publish` is the default command — you can omit it and pass files directly:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# These are equivalent:
|
|
64
|
+
dropthis ./report.html
|
|
65
|
+
dropthis publish ./report.html
|
|
66
|
+
```
|
|
67
|
+
|
|
58
68
|
```bash
|
|
59
69
|
# HTML file
|
|
60
|
-
dropthis
|
|
70
|
+
dropthis ./report.html
|
|
61
71
|
|
|
62
72
|
# Directory (static site)
|
|
63
|
-
dropthis
|
|
73
|
+
dropthis ./dist
|
|
64
74
|
|
|
65
75
|
# Stdin
|
|
66
76
|
echo "<h1>Hello</h1>" | dropthis publish - --content-type text/html --path index.html
|
|
67
77
|
|
|
68
78
|
# Multiple files bundled into one drop
|
|
69
|
-
dropthis
|
|
79
|
+
dropthis index.html styles.css app.js
|
|
70
80
|
|
|
71
81
|
# Print only the URL (recommended for agents)
|
|
72
|
-
dropthis
|
|
82
|
+
dropthis ./dist --url
|
|
73
83
|
```
|
|
74
84
|
|
|
75
85
|
### Publish options
|
|
@@ -90,9 +100,7 @@ All publish flags:
|
|
|
90
100
|
| `--title <title>` | Drop title |
|
|
91
101
|
| `--visibility <public\|unlisted>` | Drop visibility |
|
|
92
102
|
| `--password <password>` | Set password protection |
|
|
93
|
-
| `--no-password` | Clear password |
|
|
94
103
|
| `--noindex` | Prevent search indexing |
|
|
95
|
-
| `--index` | Allow indexing |
|
|
96
104
|
| `--entry <path>` | Entry file for directories |
|
|
97
105
|
| `--content-type <mime>` | MIME type (required for stdin) |
|
|
98
106
|
| `--path <path>` | Filename (required for stdin) |
|
|
@@ -113,35 +121,39 @@ These flags are inherited by all commands:
|
|
|
113
121
|
|------|-------------|
|
|
114
122
|
| `--api-key <key>` | Override API key for this invocation |
|
|
115
123
|
| `--api-url <url>` | Override API base URL |
|
|
124
|
+
| `--json` | Force JSON output |
|
|
116
125
|
| `-q, --quiet` | Suppress status output and imply JSON |
|
|
126
|
+
| `--no-interactive` | Disable interactive prompts (inline auth, confirmations) |
|
|
117
127
|
|
|
118
128
|
## Update
|
|
119
129
|
|
|
130
|
+
Update an existing drop's content, metadata, or both via `drops update`:
|
|
131
|
+
|
|
120
132
|
```bash
|
|
121
133
|
# Replace content
|
|
122
|
-
dropthis update drop_abc123 ./dist-v2
|
|
134
|
+
dropthis drops update drop_abc123 ./dist-v2
|
|
123
135
|
|
|
124
136
|
# Update metadata only
|
|
125
|
-
dropthis update drop_abc123 --title "New title"
|
|
137
|
+
dropthis drops update drop_abc123 --title "New title"
|
|
126
138
|
|
|
127
139
|
# Optimistic concurrency
|
|
128
|
-
dropthis update drop_abc123 ./dist-v2 --if-revision 1
|
|
140
|
+
dropthis drops update drop_abc123 ./dist-v2 --if-revision 1
|
|
129
141
|
|
|
130
142
|
# Change vanity slug
|
|
131
|
-
dropthis update drop_abc123 --slug new-slug
|
|
143
|
+
dropthis drops update drop_abc123 --slug new-slug
|
|
132
144
|
```
|
|
133
145
|
|
|
134
|
-
|
|
146
|
+
`drops update` supports all publish flags plus `--slug` and `--if-revision`.
|
|
135
147
|
|
|
136
148
|
## Commands
|
|
137
149
|
|
|
138
150
|
```bash
|
|
139
|
-
dropthis
|
|
140
|
-
dropthis
|
|
151
|
+
dropthis [input...] # Publish content (default command)
|
|
152
|
+
dropthis publish [input...] # Same as above, explicit form
|
|
141
153
|
|
|
142
154
|
dropthis drops list # List your drops
|
|
143
155
|
dropthis drops get <drop-id> # Get drop details
|
|
144
|
-
dropthis drops update <drop-id>
|
|
156
|
+
dropthis drops update <drop-id> [input] # Update content or metadata
|
|
145
157
|
dropthis drops delete <drop-id> # Delete a drop
|
|
146
158
|
|
|
147
159
|
dropthis login # Authenticate with email OTP
|
|
@@ -166,10 +178,15 @@ dropthis commands # Machine-readable command metadata
|
|
|
166
178
|
|
|
167
179
|
The CLI is designed for non-interactive use. In non-TTY environments (pipes, CI, agents), output defaults to JSON automatically.
|
|
168
180
|
|
|
169
|
-
**Environment
|
|
181
|
+
**Environment variables:**
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
export DROPTHIS_API_KEY=sk_live_... # API key (same as --api-key)
|
|
185
|
+
export DROPTHIS_API_URL=https://... # Override API base URL (same as --api-url)
|
|
186
|
+
export DROPTHIS_NON_INTERACTIVE=1 # Disable interactive prompts (same as --no-interactive)
|
|
187
|
+
```
|
|
170
188
|
|
|
171
189
|
```bash
|
|
172
|
-
export DROPTHIS_API_KEY=sk_live_...
|
|
173
190
|
dropthis publish ./dist --url
|
|
174
191
|
```
|
|
175
192
|
|
|
@@ -178,6 +195,7 @@ dropthis publish ./dist --url
|
|
|
178
195
|
- Use `--url` to get only the published URL (cleanest for agents)
|
|
179
196
|
- Use `--json` for the full structured response
|
|
180
197
|
- Use `--yes` for destructive commands (`drops delete`, `api-keys delete`)
|
|
198
|
+
- Use `--no-interactive` to disable inline auth prompts
|
|
181
199
|
- All errors write to stderr as JSON with a `next_action` field
|
|
182
200
|
|
|
183
201
|
**Exit codes:**
|
|
@@ -211,18 +229,18 @@ Run diagnostics to verify CLI health:
|
|
|
211
229
|
dropthis doctor
|
|
212
230
|
```
|
|
213
231
|
|
|
214
|
-
Reports CLI version, auth source (`env`, `flag`, `
|
|
232
|
+
Reports CLI version, auth source (`env`, `flag`, `storage`, or `missing`), and credential storage backend (`secure`, `insecure`, or `none`).
|
|
215
233
|
|
|
216
234
|
```json
|
|
217
|
-
{"ok":true,"version":"0.
|
|
235
|
+
{"ok":true,"version":"0.4.1","auth":{"source":"env"},"storage":{"backend":"secure"}}
|
|
218
236
|
```
|
|
219
237
|
|
|
220
238
|
## Agent skills
|
|
221
239
|
|
|
222
|
-
|
|
240
|
+
For AI coding agents (Cursor, Claude Code, Windsurf, etc.), install the [dropthis-skills](https://github.com/dropthis-dev/dropthis-skills) package:
|
|
223
241
|
|
|
224
242
|
```bash
|
|
225
|
-
npx skills add dropthis-dev/dropthis-
|
|
243
|
+
npx skills add dropthis-dev/dropthis-skills
|
|
226
244
|
```
|
|
227
245
|
|
|
228
246
|
## Links
|
package/dist/cli.cjs
CHANGED
|
@@ -6,6 +6,13 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
9
16
|
var __copyProps = (to, from, except, desc) => {
|
|
10
17
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
18
|
for (let key of __getOwnPropNames(from))
|
|
@@ -23,6 +30,126 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
30
|
mod
|
|
24
31
|
));
|
|
25
32
|
|
|
33
|
+
// src/inline-auth.ts
|
|
34
|
+
var inline_auth_exports = {};
|
|
35
|
+
__export(inline_auth_exports, {
|
|
36
|
+
runInlineAuth: () => runInlineAuth
|
|
37
|
+
});
|
|
38
|
+
async function runInlineAuth(deps) {
|
|
39
|
+
try {
|
|
40
|
+
const prompt = deps.prompt ?? defaultPrompts();
|
|
41
|
+
deps.stderr("\nNot logged in. Let's fix that:\n");
|
|
42
|
+
const emailResult = await prompt.email();
|
|
43
|
+
if (typeof emailResult === "symbol") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const email = emailResult;
|
|
47
|
+
const otpRequest = await deps.client.auth.requestEmailOtp({ email });
|
|
48
|
+
if (otpRequest.error) {
|
|
49
|
+
deps.stderr(`${otpRequest.error.message}
|
|
50
|
+
`);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
deps.stderr("Code sent! Check your inbox.\n");
|
|
54
|
+
const maxAttempts = 3;
|
|
55
|
+
let session = null;
|
|
56
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
57
|
+
const codeResult = await prompt.code(attempt, maxAttempts);
|
|
58
|
+
if (typeof codeResult === "symbol") {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const code = codeResult;
|
|
62
|
+
const verifyResult = await deps.client.auth.verifyEmailOtp({
|
|
63
|
+
email,
|
|
64
|
+
code
|
|
65
|
+
});
|
|
66
|
+
if (!verifyResult.error) {
|
|
67
|
+
session = verifyResult.data;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (attempt < maxAttempts) {
|
|
71
|
+
deps.stderr(`${verifyResult.error.message}
|
|
72
|
+
`);
|
|
73
|
+
} else {
|
|
74
|
+
deps.stderr(
|
|
75
|
+
"Too many attempts. Run `dropthis login` to request a new code.\n"
|
|
76
|
+
);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (!session) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const authedClient = deps.createClient(session.token);
|
|
84
|
+
const apiKeyResult = await authedClient.apiKeys.create({
|
|
85
|
+
label: `CLI on ${deps.hostname ?? (0, import_node_os2.hostname)()}`
|
|
86
|
+
});
|
|
87
|
+
if (apiKeyResult.error) {
|
|
88
|
+
deps.stderr(`${apiKeyResult.error.message}
|
|
89
|
+
`);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const { id: keyId, key: apiKey, keyLast4, accountId } = apiKeyResult.data;
|
|
93
|
+
try {
|
|
94
|
+
await deps.store.save({
|
|
95
|
+
apiKey,
|
|
96
|
+
keyId,
|
|
97
|
+
keyLast4,
|
|
98
|
+
accountId: accountId ?? session.accountId,
|
|
99
|
+
email,
|
|
100
|
+
storage: "secure"
|
|
101
|
+
});
|
|
102
|
+
} catch (err) {
|
|
103
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
104
|
+
deps.stderr(`Failed to save credentials: ${message}
|
|
105
|
+
`);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
deps.stderr("\u2713 Logged in\n\n");
|
|
109
|
+
return {
|
|
110
|
+
apiKey,
|
|
111
|
+
source: "storage",
|
|
112
|
+
keyId,
|
|
113
|
+
keyLast4,
|
|
114
|
+
accountId: accountId ?? session.accountId,
|
|
115
|
+
email
|
|
116
|
+
};
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
deps.stderr(`Login failed: ${message}
|
|
120
|
+
`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function defaultPrompts() {
|
|
125
|
+
return {
|
|
126
|
+
email: () => readlineQuestion("Email: "),
|
|
127
|
+
code: (attempt) => readlineQuestion(
|
|
128
|
+
attempt === 1 ? "Code: " : "Try again \u2014 enter the code: "
|
|
129
|
+
)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function readlineQuestion(question) {
|
|
133
|
+
const rl = (0, import_node_readline.createInterface)({
|
|
134
|
+
input: process.stdin,
|
|
135
|
+
output: process.stderr
|
|
136
|
+
});
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
rl.question(question, (answer) => {
|
|
139
|
+
rl.close();
|
|
140
|
+
resolve(answer);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
var import_node_os2, import_node_readline;
|
|
145
|
+
var init_inline_auth = __esm({
|
|
146
|
+
"src/inline-auth.ts"() {
|
|
147
|
+
"use strict";
|
|
148
|
+
import_node_os2 = require("os");
|
|
149
|
+
import_node_readline = require("readline");
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
26
153
|
// src/program.ts
|
|
27
154
|
var import_commander = require("commander");
|
|
28
155
|
var import_picocolors4 = __toESM(require("picocolors"), 1);
|
|
@@ -93,11 +220,14 @@ function apiErrorEnvelope(error2) {
|
|
|
93
220
|
...error2.param ? { param: error2.param } : {},
|
|
94
221
|
...error2.currentRevision !== void 0 ? { current_revision: error2.currentRevision } : {},
|
|
95
222
|
...error2.requestId ? { request_id: error2.requestId } : {},
|
|
223
|
+
...error2.suggestion ? { suggestion: error2.suggestion } : {},
|
|
224
|
+
...error2.retryable !== void 0 && error2.retryable !== null ? { retryable: error2.retryable } : {},
|
|
96
225
|
next_action: nextActionForApiError(error2)
|
|
97
226
|
}
|
|
98
227
|
};
|
|
99
228
|
}
|
|
100
229
|
function nextActionForApiError(error2) {
|
|
230
|
+
if (error2.suggestion) return error2.suggestion;
|
|
101
231
|
const uploadNextAction = error2.code ? UPLOAD_NEXT_ACTIONS[error2.code] : void 0;
|
|
102
232
|
if (uploadNextAction) return uploadNextAction;
|
|
103
233
|
if (error2.code === "revision_conflict") {
|
|
@@ -221,8 +351,11 @@ function writeApiError(deps, details) {
|
|
|
221
351
|
if (deps.outputMode === "human") {
|
|
222
352
|
deps.stderr(`${error(details.message)}
|
|
223
353
|
`);
|
|
224
|
-
const nextAction = details
|
|
354
|
+
const nextAction = nextActionForApiError(details);
|
|
225
355
|
if (nextAction) deps.stderr(`${hint(nextAction)}
|
|
356
|
+
`);
|
|
357
|
+
if (details.requestId)
|
|
358
|
+
deps.stderr(`${import_picocolors2.default.dim(`Request ID: ${details.requestId}`)}
|
|
226
359
|
`);
|
|
227
360
|
} else {
|
|
228
361
|
deps.stderr(`${JSON.stringify(apiErrorEnvelope(details))}
|
|
@@ -394,6 +527,7 @@ var COMMAND_CATALOG = [
|
|
|
394
527
|
arguments: ["input"],
|
|
395
528
|
options: ["--json", "--url", "--dry-run", "--title", "--metadata"],
|
|
396
529
|
examples: [
|
|
530
|
+
"dropthis ./file.html",
|
|
397
531
|
"dropthis publish ./site --json",
|
|
398
532
|
"dropthis publish ./site --url",
|
|
399
533
|
"dropthis publish ./dist --dry-run"
|
|
@@ -555,13 +689,13 @@ async function runDoctor(_input, deps) {
|
|
|
555
689
|
const authSource = credential?.source ?? "missing";
|
|
556
690
|
const storageBackend = stored?.storage ?? "none";
|
|
557
691
|
const pairs = [
|
|
558
|
-
["Version", "0.
|
|
692
|
+
["Version", "0.4.1"],
|
|
559
693
|
["Auth", authSource],
|
|
560
694
|
["Storage", storageBackend]
|
|
561
695
|
];
|
|
562
696
|
writeKv(deps, pairs, {
|
|
563
697
|
ok: true,
|
|
564
|
-
version: "0.
|
|
698
|
+
version: "0.4.1",
|
|
565
699
|
auth: { source: authSource },
|
|
566
700
|
storage: { backend: storageBackend }
|
|
567
701
|
});
|
|
@@ -991,8 +1125,9 @@ async function readJsonObjectFile(path) {
|
|
|
991
1125
|
|
|
992
1126
|
// src/commands/publish.ts
|
|
993
1127
|
async function runPublish(input, raw, deps) {
|
|
1128
|
+
let credential;
|
|
994
1129
|
try {
|
|
995
|
-
await
|
|
1130
|
+
credential = await resolveCredential({
|
|
996
1131
|
...raw.apiKey ?? deps.apiKey ? { apiKey: raw.apiKey ?? deps.apiKey } : {},
|
|
997
1132
|
env: deps.env,
|
|
998
1133
|
store: deps.store
|
|
@@ -1000,8 +1135,26 @@ async function runPublish(input, raw, deps) {
|
|
|
1000
1135
|
} catch {
|
|
1001
1136
|
return writeAuthError(deps);
|
|
1002
1137
|
}
|
|
1138
|
+
let didInlineAuth = false;
|
|
1139
|
+
if (!credential) {
|
|
1140
|
+
if (deps.interactive && deps.inlineAuth) {
|
|
1141
|
+
try {
|
|
1142
|
+
credential = await deps.inlineAuth();
|
|
1143
|
+
} catch {
|
|
1144
|
+
return writeAuthError(deps);
|
|
1145
|
+
}
|
|
1146
|
+
if (!credential) return writeAuthError(deps);
|
|
1147
|
+
didInlineAuth = true;
|
|
1148
|
+
} else {
|
|
1149
|
+
return writeAuthError(deps);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
let client = deps.client;
|
|
1153
|
+
if (didInlineAuth && deps.createClient) {
|
|
1154
|
+
client = deps.createClient(credential.apiKey);
|
|
1155
|
+
}
|
|
1003
1156
|
if (raw.dryRun) {
|
|
1004
|
-
return handlePublishDryRun(input, raw, deps);
|
|
1157
|
+
return handlePublishDryRun(input, raw, deps, client);
|
|
1005
1158
|
}
|
|
1006
1159
|
const singleInput = Array.isArray(input) ? input[0] : input;
|
|
1007
1160
|
const spin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
|
|
@@ -1010,13 +1163,44 @@ async function runPublish(input, raw, deps) {
|
|
|
1010
1163
|
throw new Error("Use either <input> or --from-json, not both.");
|
|
1011
1164
|
}
|
|
1012
1165
|
const options = withIdempotencyKey(await parseDropOptions(raw), "pub");
|
|
1013
|
-
const
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
) : singleInput ? await deps.client.publish(singleInput, options) : (() => {
|
|
1166
|
+
const jsonBody = raw.fromJson ? await (deps.readJsonObject ?? readJsonObjectFile)(raw.fromJson) : void 0;
|
|
1167
|
+
const idempotencyOpt = options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {};
|
|
1168
|
+
const doPublish = (c) => jsonBody ? c.drops.createRaw(jsonBody, idempotencyOpt) : singleInput ? c.publish(singleInput, options) : (() => {
|
|
1017
1169
|
throw new Error("Publish requires <input> or --from-json.");
|
|
1018
1170
|
})();
|
|
1171
|
+
const result = await doPublish(client);
|
|
1019
1172
|
if (result.error) {
|
|
1173
|
+
if (result.error.statusCode === 401 && deps.interactive && deps.inlineAuth && !didInlineAuth) {
|
|
1174
|
+
spin?.stop();
|
|
1175
|
+
const newCredential = await deps.inlineAuth();
|
|
1176
|
+
if (newCredential && deps.createClient) {
|
|
1177
|
+
client = deps.createClient(newCredential.apiKey);
|
|
1178
|
+
const retrySpin = shouldSpin(deps.outputMode) ? createSpinner("Publishing\u2026", deps.stderr) : void 0;
|
|
1179
|
+
try {
|
|
1180
|
+
const retryResult = await doPublish(client);
|
|
1181
|
+
if (retryResult.error) {
|
|
1182
|
+
retrySpin?.fail();
|
|
1183
|
+
return writeApiError(deps, retryResult.error);
|
|
1184
|
+
}
|
|
1185
|
+
retrySpin?.stop();
|
|
1186
|
+
if (raw.url) deps.stdout(`${retryResult.data.url}
|
|
1187
|
+
`);
|
|
1188
|
+
else
|
|
1189
|
+
writeResult(
|
|
1190
|
+
deps,
|
|
1191
|
+
retryResult.data.url,
|
|
1192
|
+
"Published",
|
|
1193
|
+
retryResult.data
|
|
1194
|
+
);
|
|
1195
|
+
return { exitCode: 0 };
|
|
1196
|
+
} catch (retryError) {
|
|
1197
|
+
retrySpin?.fail();
|
|
1198
|
+
const message = retryError instanceof Error ? retryError.message : "Publish failed.";
|
|
1199
|
+
const code = isFileSystemError(retryError) ? "local_input_error" : "invalid_usage";
|
|
1200
|
+
return writeError(deps, code, message);
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1020
1204
|
spin?.fail();
|
|
1021
1205
|
return writeApiError(deps, result.error);
|
|
1022
1206
|
}
|
|
@@ -1032,7 +1216,7 @@ async function runPublish(input, raw, deps) {
|
|
|
1032
1216
|
return writeError(deps, code, message);
|
|
1033
1217
|
}
|
|
1034
1218
|
}
|
|
1035
|
-
async function handlePublishDryRun(input, raw, deps) {
|
|
1219
|
+
async function handlePublishDryRun(input, raw, deps, client) {
|
|
1036
1220
|
if (raw.url) {
|
|
1037
1221
|
return writeError(
|
|
1038
1222
|
deps,
|
|
@@ -1061,10 +1245,10 @@ async function handlePublishDryRun(input, raw, deps) {
|
|
|
1061
1245
|
if (Array.isArray(input)) {
|
|
1062
1246
|
return dryRunMultiFile(input, options, deps);
|
|
1063
1247
|
}
|
|
1064
|
-
if (!
|
|
1248
|
+
if (!client.prepare) {
|
|
1065
1249
|
throw new Error("Client does not support dry-run.");
|
|
1066
1250
|
}
|
|
1067
|
-
const prepared = await
|
|
1251
|
+
const prepared = await client.prepare(input, options);
|
|
1068
1252
|
const output = formatDryRunManifest(prepared);
|
|
1069
1253
|
validateManifestFileCount(
|
|
1070
1254
|
output.manifest.files.length
|
|
@@ -1396,11 +1580,13 @@ async function readJsonFile(path) {
|
|
|
1396
1580
|
}
|
|
1397
1581
|
async function writeCredentialFile(path, credential) {
|
|
1398
1582
|
await (0, import_promises4.mkdir)((0, import_node_path3.dirname)(path), { recursive: true });
|
|
1399
|
-
|
|
1583
|
+
const tmpPath = `${path}.${process.pid}.tmp`;
|
|
1584
|
+
await (0, import_promises4.writeFile)(tmpPath, `${JSON.stringify(credential, null, 2)}
|
|
1400
1585
|
`, {
|
|
1401
1586
|
mode: 384
|
|
1402
1587
|
});
|
|
1403
|
-
await (0, import_promises4.chmod)(
|
|
1588
|
+
await (0, import_promises4.chmod)(tmpPath, 384);
|
|
1589
|
+
await (0, import_promises4.rename)(tmpPath, path);
|
|
1404
1590
|
}
|
|
1405
1591
|
|
|
1406
1592
|
// src/program.ts
|
|
@@ -1415,7 +1601,7 @@ function buildProgram(options = {}) {
|
|
|
1415
1601
|
` ${import_picocolors4.default.cyan("dropthis login")}`,
|
|
1416
1602
|
` ${import_picocolors4.default.cyan("dropthis publish ./dist")}`
|
|
1417
1603
|
].join("\n")
|
|
1418
|
-
).version("0.
|
|
1604
|
+
).version("0.4.1").configureHelp({
|
|
1419
1605
|
subcommandTerm(cmd) {
|
|
1420
1606
|
const args = cmd.registeredArguments.map((a) => {
|
|
1421
1607
|
const name = a.name();
|
|
@@ -1433,7 +1619,8 @@ function buildProgram(options = {}) {
|
|
|
1433
1619
|
program.option("--api-url <url>", "Override API base URL");
|
|
1434
1620
|
program.option("--json", "Force JSON output");
|
|
1435
1621
|
program.option("-q, --quiet", "Suppress status output and imply JSON");
|
|
1436
|
-
program.
|
|
1622
|
+
program.option("--no-interactive", "Disable interactive prompts");
|
|
1623
|
+
program.command("publish [input...]", { isDefault: true }).description(
|
|
1437
1624
|
"Publish files, folders, URLs, strings, or stdin.\nMultiple files are bundled into one drop.\nUse - to read stdin explicitly, or pipe without args."
|
|
1438
1625
|
).option("--title <title>", "Drop title").option("--visibility <v>", "public or unlisted (default: public)").option("--password <password>", "Require password to view").option("--noindex", "Prevent search-engine indexing").option("--expires-at <datetime>", "Auto-delete after this ISO 8601 date").option(
|
|
1439
1626
|
"--content-type <mime>",
|
|
@@ -1448,8 +1635,18 @@ function buildProgram(options = {}) {
|
|
|
1448
1635
|
"--idempotency-key <key>",
|
|
1449
1636
|
"Prevent duplicate publishes on retry (auto-generated)"
|
|
1450
1637
|
).action(async (inputs, commandOptions) => {
|
|
1451
|
-
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1452
1638
|
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1639
|
+
if (inputs.length === 0 && stdinIsTTY && !commandOptions.fromJson) {
|
|
1640
|
+
program.outputHelp();
|
|
1641
|
+
return;
|
|
1642
|
+
}
|
|
1643
|
+
const { deps } = await buildPublishDeps(
|
|
1644
|
+
program,
|
|
1645
|
+
options,
|
|
1646
|
+
store,
|
|
1647
|
+
commandOptions,
|
|
1648
|
+
stdinIsTTY
|
|
1649
|
+
);
|
|
1453
1650
|
let publishInput;
|
|
1454
1651
|
try {
|
|
1455
1652
|
if (commandOptions.dryRun && inputs.length > 1) {
|
|
@@ -1538,10 +1735,15 @@ function buildProgram(options = {}) {
|
|
|
1538
1735
|
);
|
|
1539
1736
|
drops.command("delete <dropId>").description("Delete a drop").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
|
|
1540
1737
|
async (dropId, commandOptions) => {
|
|
1738
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1739
|
+
const global = globalOptions(program, commandOptions);
|
|
1541
1740
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1542
1741
|
const result = await runDropsDelete(
|
|
1543
1742
|
dropId,
|
|
1544
|
-
{
|
|
1743
|
+
{
|
|
1744
|
+
...commandOptions,
|
|
1745
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
1746
|
+
},
|
|
1545
1747
|
deps
|
|
1546
1748
|
);
|
|
1547
1749
|
process.exitCode = result.exitCode;
|
|
@@ -1560,10 +1762,15 @@ function buildProgram(options = {}) {
|
|
|
1560
1762
|
});
|
|
1561
1763
|
apiKeys.command("delete <keyId>").description("Delete an API key").option("--yes", "Confirm deletion").option("--json", "Force JSON output").action(
|
|
1562
1764
|
async (keyId, commandOptions) => {
|
|
1765
|
+
const stdinIsTTY = options.stdinIsTTY ?? process.stdin.isTTY ?? false;
|
|
1766
|
+
const global = globalOptions(program, commandOptions);
|
|
1563
1767
|
const deps = await commandDeps(program, options, store, commandOptions);
|
|
1564
1768
|
const result = await runApiKeysDelete(
|
|
1565
1769
|
keyId,
|
|
1566
|
-
{
|
|
1770
|
+
{
|
|
1771
|
+
...commandOptions,
|
|
1772
|
+
interactive: isInteractive(stdinIsTTY, deps.env, global)
|
|
1773
|
+
},
|
|
1567
1774
|
deps
|
|
1568
1775
|
);
|
|
1569
1776
|
process.exitCode = result.exitCode;
|
|
@@ -1708,13 +1915,56 @@ async function commandDeps(program, options, store, commandOptions) {
|
|
|
1708
1915
|
outputMode: context.output.mode
|
|
1709
1916
|
};
|
|
1710
1917
|
}
|
|
1918
|
+
async function buildPublishDeps(program, options, store, commandOptions, stdinIsTTY) {
|
|
1919
|
+
const global = globalOptions(program, commandOptions);
|
|
1920
|
+
const context = createContext({
|
|
1921
|
+
global,
|
|
1922
|
+
...options.env !== void 0 ? { env: options.env } : {},
|
|
1923
|
+
...options.stdout !== void 0 ? { stdout: options.stdout } : {},
|
|
1924
|
+
...options.stderr !== void 0 ? { stderr: options.stderr } : {},
|
|
1925
|
+
...options.stdoutIsTTY !== void 0 ? { stdoutIsTTY: options.stdoutIsTTY } : {}
|
|
1926
|
+
});
|
|
1927
|
+
const credential = await resolveCredential({
|
|
1928
|
+
...global.apiKey ? { apiKey: global.apiKey } : {},
|
|
1929
|
+
env: context.env,
|
|
1930
|
+
store
|
|
1931
|
+
});
|
|
1932
|
+
const interactive = isInteractive(stdinIsTTY, context.env, global);
|
|
1933
|
+
const inlineAuth = async () => {
|
|
1934
|
+
const { runInlineAuth: runInlineAuth2 } = await Promise.resolve().then(() => (init_inline_auth(), inline_auth_exports));
|
|
1935
|
+
return runInlineAuth2({
|
|
1936
|
+
client: context.createClient(),
|
|
1937
|
+
createClient: (apiKey) => context.createClient(apiKey),
|
|
1938
|
+
store,
|
|
1939
|
+
stderr: context.stderr
|
|
1940
|
+
});
|
|
1941
|
+
};
|
|
1942
|
+
return {
|
|
1943
|
+
deps: {
|
|
1944
|
+
store,
|
|
1945
|
+
client: options.client ?? context.createClient(credential?.apiKey),
|
|
1946
|
+
...global.apiKey ? { apiKey: global.apiKey } : {},
|
|
1947
|
+
env: context.env,
|
|
1948
|
+
stdout: context.stdout,
|
|
1949
|
+
stderr: context.stderr,
|
|
1950
|
+
outputMode: context.output.mode,
|
|
1951
|
+
interactive,
|
|
1952
|
+
inlineAuth,
|
|
1953
|
+
createClient: (apiKey) => context.createClient(apiKey)
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
function isInteractive(stdinIsTTY, env, global) {
|
|
1958
|
+
return stdinIsTTY && !env.CI && !env.DROPTHIS_NON_INTERACTIVE && global.interactive !== false;
|
|
1959
|
+
}
|
|
1711
1960
|
function globalOptions(program, commandOptions) {
|
|
1712
1961
|
const opts = program.opts();
|
|
1713
1962
|
return {
|
|
1714
1963
|
...opts.apiKey ? { apiKey: opts.apiKey } : {},
|
|
1715
1964
|
...opts.apiUrl ? { apiUrl: opts.apiUrl } : {},
|
|
1716
1965
|
...commandOptions.json !== void 0 ? { json: commandOptions.json } : opts.json !== void 0 ? { json: opts.json } : {},
|
|
1717
|
-
...commandOptions.quiet !== void 0 ? { quiet: commandOptions.quiet } : opts.quiet !== void 0 ? { quiet: opts.quiet } : {}
|
|
1966
|
+
...commandOptions.quiet !== void 0 ? { quiet: commandOptions.quiet } : opts.quiet !== void 0 ? { quiet: opts.quiet } : {},
|
|
1967
|
+
...opts.interactive !== void 0 ? { interactive: opts.interactive } : {}
|
|
1718
1968
|
};
|
|
1719
1969
|
}
|
|
1720
1970
|
function toDropOptions(options) {
|