dropley 0.1.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.
Potentially problematic release.
This version of dropley might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/api.d.ts +55 -0
- package/dist/api.js +126 -0
- package/dist/api.js.map +1 -0
- package/dist/args.d.ts +21 -0
- package/dist/args.js +83 -0
- package/dist/args.js.map +1 -0
- package/dist/bin/dropley.d.ts +2 -0
- package/dist/bin/dropley.js +4 -0
- package/dist/bin/dropley.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +112 -0
- package/dist/cli.js.map +1 -0
- package/dist/collect.d.ts +15 -0
- package/dist/collect.js +59 -0
- package/dist/collect.js.map +1 -0
- package/dist/commands/context.d.ts +14 -0
- package/dist/commands/context.js +28 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/delete.d.ts +3 -0
- package/dist/commands/delete.js +14 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/pack.d.ts +10 -0
- package/dist/commands/pack.js +45 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/publish.d.ts +4 -0
- package/dist/commands/publish.js +94 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +27 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/update.d.ts +3 -0
- package/dist/commands/update.js +23 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +68 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +12 -0
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -0
- package/dist/mime.d.ts +1 -0
- package/dist/mime.js +47 -0
- package/dist/mime.js.map +1 -0
- package/dist/output.d.ts +9 -0
- package/dist/output.js +24 -0
- package/dist/output.js.map +1 -0
- package/dist/target.d.ts +5 -0
- package/dist/target.js +29 -0
- package/dist/target.js.map +1 -0
- package/dist/zip.d.ts +14 -0
- package/dist/zip.js +136 -0
- package/dist/zip.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dropley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# dropley
|
|
2
|
+
|
|
3
|
+
Publish files and static sites to [Dropley](https://dropley.app) from your terminal — no browser, no account.
|
|
4
|
+
|
|
5
|
+
`dropley` wraps the [Dropley public API](https://dropley.app/api/v1/openapi.json) so agents and humans can publish artifacts and manage them with four commands. Every published artifact gets a shareable URL and a token for later update/delete.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g dropley
|
|
11
|
+
# or run without installing:
|
|
12
|
+
npx dropley publish ./dist
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Node.js ≥ 20. Zero runtime dependencies.
|
|
16
|
+
|
|
17
|
+
## Quickstart
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
# Publish a directory (static site) — uploads each file, skipping OS junk (.DS_Store, .git, …)
|
|
21
|
+
dropley publish ./dist --expiry 7d
|
|
22
|
+
# url: https://preview.dropley.app/p/aB3cD5eF
|
|
23
|
+
# token: tok_a1b2c3… ← keep this; needed to update/delete
|
|
24
|
+
|
|
25
|
+
# A single file is published as the site entry (index.html)
|
|
26
|
+
dropley publish report.html
|
|
27
|
+
|
|
28
|
+
# Show metadata (public without a token)
|
|
29
|
+
dropley status https://preview.dropley.app/p/aB3cD5eF
|
|
30
|
+
|
|
31
|
+
# Change expiry (needs the artifact token)
|
|
32
|
+
dropley update aB3cD5eF --expiry 30d
|
|
33
|
+
|
|
34
|
+
# Delete permanently
|
|
35
|
+
dropley delete aB3cD5eF
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Agent usage
|
|
39
|
+
|
|
40
|
+
`dropley` is designed for agents: quiet, machine-parsable output on stdout, progress on stderr.
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
# JSON mode — everything needed in one object
|
|
44
|
+
dropley publish ./out --json
|
|
45
|
+
# {
|
|
46
|
+
# "shortId": "aB3cD5eF",
|
|
47
|
+
# "url": "https://preview.dropley.app/p/aB3cD5eF",
|
|
48
|
+
# "expiresAt": "2026-08-30T12:00:00.000Z",
|
|
49
|
+
# "artifactToken": "tok_a1b2c3…"
|
|
50
|
+
# }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
- Exit codes: `0` success, `1` failure (API/network errors), `2` usage error.
|
|
54
|
+
- API errors surface the server's `error`/`code`/`hint` verbatim on stderr, including `RATE_LIMITED` 429 responses with their `Retry-After`.
|
|
55
|
+
- The token returned by `publish` is saved locally (per artifact ID, `0600` perms, `~/.config/dropley/tokens.json` or `$XDG_CONFIG_HOME`/`$DROPLEY_CONFIG_DIR`) and reused by `status`/`update`/`delete` automatically.
|
|
56
|
+
|
|
57
|
+
See also the Dropley [agent skills](https://github.com/dropley) collection — `npx skills@latest add dropley/docs` for docs on publishing via agents.
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | Description |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `dropley publish <path> [--expiry <t>]` | Publish a file or directory; prints URL + token |
|
|
64
|
+
| `dropley status <url-or-shortId>` | Show artifact metadata |
|
|
65
|
+
| `dropley update <url-or-shortId> --expiry <t>` | Change an artifact's expiry |
|
|
66
|
+
| `dropley delete <url-or-shortId>` | Permanently delete an artifact |
|
|
67
|
+
| `dropley pack <dir> [--out archive.zip]` | Create a byte-reproducible zip (auxiliary; see below) |
|
|
68
|
+
|
|
69
|
+
### Global options
|
|
70
|
+
|
|
71
|
+
| Option | Description |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `--json` | Machine-readable JSON output on stdout |
|
|
74
|
+
| `--api <url>` | API base URL (default `https://dropley.app`; env `DROPLEY_API` for local previews) |
|
|
75
|
+
| `--token <tok>` | Artifact token (env `DROPLEY_TOKEN`); precedence: `--token` > `DROPLEY_TOKEN` > saved token |
|
|
76
|
+
| `--expiry <t>` | `1d` `3d` `7d` `15d` `30d` `1h`… — server-validated |
|
|
77
|
+
|
|
78
|
+
### Notes
|
|
79
|
+
|
|
80
|
+
- **Directory publishes** upload every file as an individual part with its relative path (Dropley requires the site entry to be `index.html` at the root). OS metadata (`.DS_Store`, `Thumbs.db`, `desktop.ini`, `._*`), VCS internals (`.git`, `.hg`, `.svn`) and `node_modules` are skipped.
|
|
81
|
+
- **`dropley pack`** writes a byte-for-byte reproducible zip archive (sorted entries, fixed timestamps, no junk). Dropley serves uploads as-is and does not expand archives, so publish directories directly for hosting — `pack` is for archiving and reproducible builds.
|
|
82
|
+
- 50MB total / 1000 files per artifact (checked locally before upload).
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npm install
|
|
88
|
+
npm run lint # eslint
|
|
89
|
+
npm run typecheck # tsc --noEmit
|
|
90
|
+
npm test # vitest (mocked fetch, no network)
|
|
91
|
+
npm run build # tsc → dist/
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Release
|
|
95
|
+
|
|
96
|
+
Release: push a `v*` tag; the release workflow runs checks and publishes to npm with `--provenance` via **trusted publishing** (OIDC — no npm token stored in GitHub).
|
|
97
|
+
|
|
98
|
+
Setup (once, by a maintainer):
|
|
99
|
+
|
|
100
|
+
1. Make the repo public (trusted publishing is identity-based and works fine on public repos).
|
|
101
|
+
2. On npmjs.com → package settings → **Trusted publishing**: add GitHub owner `dropley`, repository `cli`, environment `npm`, workflow `release.yml`.
|
|
102
|
+
3. On GitHub → repo settings → **Environments** → `npm`: configure required reviewers (the approval gate the publish waits on).
|
|
103
|
+
|
|
104
|
+
The first publish is always a human step: tag, push, approve the environment.
|
|
105
|
+
|
|
106
|
+
> Note: if the `dropley` package name is not yet claimed on npm, the very first publish may need a one-time token (npm requires the package to exist before trusted publishing can be configured); trusted publishing takes over for all subsequent releases.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export declare const DEFAULT_BASE_URL = "https://dropley.app";
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
readonly code?: string;
|
|
5
|
+
readonly hint?: string;
|
|
6
|
+
readonly retryAfter?: number;
|
|
7
|
+
readonly requestId?: string;
|
|
8
|
+
constructor(message: string, opts: {
|
|
9
|
+
status: number;
|
|
10
|
+
code?: string;
|
|
11
|
+
hint?: string;
|
|
12
|
+
retryAfter?: number;
|
|
13
|
+
requestId?: string;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export interface CreateResult {
|
|
17
|
+
shortId: string;
|
|
18
|
+
url: string;
|
|
19
|
+
expiresAt: string | null;
|
|
20
|
+
artifactToken: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ArtifactInfo {
|
|
23
|
+
shortId: string;
|
|
24
|
+
status: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
expiresAt?: string | null;
|
|
27
|
+
source?: string;
|
|
28
|
+
tags?: string[];
|
|
29
|
+
createdAt?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
export interface UpdateResult {
|
|
34
|
+
success: boolean;
|
|
35
|
+
expiresAt?: string | null;
|
|
36
|
+
source?: string;
|
|
37
|
+
tags?: string[];
|
|
38
|
+
[key: string]: unknown;
|
|
39
|
+
}
|
|
40
|
+
export interface UploadPart {
|
|
41
|
+
/** Path used as the multipart filename and manifest entry (POSIX style). */
|
|
42
|
+
path: string;
|
|
43
|
+
contentType: string;
|
|
44
|
+
data: Uint8Array;
|
|
45
|
+
}
|
|
46
|
+
export declare function createArtifact(baseUrl: string, parts: readonly UploadPart[], opts: {
|
|
47
|
+
expiry?: string;
|
|
48
|
+
entry?: string;
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
}): Promise<CreateResult>;
|
|
51
|
+
export declare function getArtifact(baseUrl: string, shortId: string, token?: string): Promise<ArtifactInfo>;
|
|
52
|
+
export declare function updateArtifact(baseUrl: string, shortId: string, token: string, patch: Record<string, unknown>): Promise<UpdateResult>;
|
|
53
|
+
export declare function deleteArtifact(baseUrl: string, shortId: string, token: string): Promise<{
|
|
54
|
+
success: boolean;
|
|
55
|
+
}>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export const DEFAULT_BASE_URL = 'https://dropley.app';
|
|
2
|
+
export class ApiError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
hint;
|
|
6
|
+
retryAfter;
|
|
7
|
+
requestId;
|
|
8
|
+
constructor(message, opts) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'ApiError';
|
|
11
|
+
this.status = opts.status;
|
|
12
|
+
this.code = opts.code;
|
|
13
|
+
this.hint = opts.hint;
|
|
14
|
+
this.retryAfter = opts.retryAfter;
|
|
15
|
+
this.requestId = opts.requestId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function parseErrorBody(status, text, retryAfterHeader) {
|
|
19
|
+
let body;
|
|
20
|
+
try {
|
|
21
|
+
body = JSON.parse(text);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// non-JSON body (e.g. HTML error page)
|
|
25
|
+
}
|
|
26
|
+
if (body && typeof body === 'object') {
|
|
27
|
+
// Nested rate-limit shape: { error: { code, message } }
|
|
28
|
+
if (body.error && typeof body.error === 'object') {
|
|
29
|
+
const nested = body.error;
|
|
30
|
+
return new ApiError(String(nested.message ?? 'Request failed'), {
|
|
31
|
+
status,
|
|
32
|
+
code: nested.code !== undefined ? String(nested.code) : undefined,
|
|
33
|
+
hint: nested.hint !== undefined ? String(nested.hint) : undefined,
|
|
34
|
+
retryAfter: parseRetryAfter(retryAfterHeader),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
// Flat shape: { error?, code?, message?, hint? }
|
|
38
|
+
const flatMessage = firstString(body.message) ??
|
|
39
|
+
firstString(body.error) ??
|
|
40
|
+
(body.requestId !== undefined ? `Server error (${String(body.requestId)})` : undefined) ??
|
|
41
|
+
'Request failed';
|
|
42
|
+
return new ApiError(flatMessage, {
|
|
43
|
+
status,
|
|
44
|
+
code: firstString(body.code),
|
|
45
|
+
hint: firstString(body.hint),
|
|
46
|
+
retryAfter: parseRetryAfter(retryAfterHeader),
|
|
47
|
+
requestId: body.requestId !== undefined ? String(body.requestId) : undefined,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return new ApiError(text.slice(0, 200) || 'Request failed', {
|
|
51
|
+
status,
|
|
52
|
+
retryAfter: parseRetryAfter(retryAfterHeader),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function parseRetryAfter(header) {
|
|
56
|
+
if (!header)
|
|
57
|
+
return undefined;
|
|
58
|
+
const n = Number.parseInt(header, 10);
|
|
59
|
+
return Number.isNaN(n) ? undefined : n;
|
|
60
|
+
}
|
|
61
|
+
function firstString(v) {
|
|
62
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
63
|
+
}
|
|
64
|
+
async function request(baseUrl, path, init, timeoutMs) {
|
|
65
|
+
let res;
|
|
66
|
+
try {
|
|
67
|
+
res = await fetch(`${baseUrl}${path}`, { ...init, signal: AbortSignal.timeout(timeoutMs) });
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
|
|
71
|
+
throw new ApiError(`Request timed out after ${timeoutMs / 1000}s`, {
|
|
72
|
+
status: 0,
|
|
73
|
+
hint: 'Check your network connection, or point --api at a reachable server.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
throw new ApiError(`Network error: ${err.message}`, {
|
|
77
|
+
status: 0,
|
|
78
|
+
hint: 'Check your network connection, or point --api at a reachable server.',
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
const text = await res.text().catch(() => '');
|
|
83
|
+
throw parseErrorBody(res.status, text, res.headers.get('retry-after'));
|
|
84
|
+
}
|
|
85
|
+
return res;
|
|
86
|
+
}
|
|
87
|
+
async function requestJson(baseUrl, path, init, timeoutMs = 30_000) {
|
|
88
|
+
const res = await request(baseUrl, path, init, timeoutMs);
|
|
89
|
+
return (await res.json());
|
|
90
|
+
}
|
|
91
|
+
function buildManifest(parts, entry) {
|
|
92
|
+
return JSON.stringify({
|
|
93
|
+
manifestVersion: 1,
|
|
94
|
+
entry,
|
|
95
|
+
files: parts.map((p) => ({
|
|
96
|
+
path: p.path,
|
|
97
|
+
size: p.data.length,
|
|
98
|
+
contentType: p.contentType,
|
|
99
|
+
})),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
export function createArtifact(baseUrl, parts, opts) {
|
|
103
|
+
const form = new FormData();
|
|
104
|
+
form.set('manifest', buildManifest(parts, opts.entry ?? 'index.html'));
|
|
105
|
+
if (opts.expiry !== undefined)
|
|
106
|
+
form.set('expiry', opts.expiry);
|
|
107
|
+
for (const part of parts) {
|
|
108
|
+
form.append('file', new File([part.data], part.path, { type: part.contentType }));
|
|
109
|
+
}
|
|
110
|
+
return requestJson(baseUrl, '/api/artifacts', { method: 'POST', body: form }, opts.timeoutMs ?? 300_000);
|
|
111
|
+
}
|
|
112
|
+
export function getArtifact(baseUrl, shortId, token) {
|
|
113
|
+
const query = token ? `?token=${encodeURIComponent(token)}` : '';
|
|
114
|
+
return requestJson(baseUrl, `/api/artifacts/${encodeURIComponent(shortId)}${query}`, { method: 'GET' });
|
|
115
|
+
}
|
|
116
|
+
export function updateArtifact(baseUrl, shortId, token, patch) {
|
|
117
|
+
return requestJson(baseUrl, `/api/artifacts/${encodeURIComponent(shortId)}`, {
|
|
118
|
+
method: 'PATCH',
|
|
119
|
+
headers: { 'X-Artifact-Token': token, 'Content-Type': 'application/json' },
|
|
120
|
+
body: JSON.stringify(patch),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export function deleteArtifact(baseUrl, shortId, token) {
|
|
124
|
+
return requestJson(baseUrl, `/api/artifacts/${encodeURIComponent(shortId)}`, { method: 'DELETE', headers: { 'X-Artifact-Token': token } });
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type FlagKind = 'boolean' | 'value';
|
|
2
|
+
export interface PositionalSpec {
|
|
3
|
+
name: string;
|
|
4
|
+
required: boolean;
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CommandSpec {
|
|
8
|
+
summary: string;
|
|
9
|
+
flags: Record<string, FlagKind>;
|
|
10
|
+
positionals: PositionalSpec[];
|
|
11
|
+
}
|
|
12
|
+
export declare const GLOBAL_FLAGS: Record<string, FlagKind>;
|
|
13
|
+
export interface ParsedInvocation {
|
|
14
|
+
command: string;
|
|
15
|
+
positionals: string[];
|
|
16
|
+
values: Record<string, string>;
|
|
17
|
+
flags: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
/** Shape handed to command handlers (everything except the command name). */
|
|
20
|
+
export type ParsedArgs = Omit<ParsedInvocation, 'command'>;
|
|
21
|
+
export declare function parseArgv(argv: readonly string[], specs: Record<string, CommandSpec>): ParsedInvocation;
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { UsageError } from './errors.js';
|
|
2
|
+
export const GLOBAL_FLAGS = {
|
|
3
|
+
json: 'boolean',
|
|
4
|
+
api: 'value',
|
|
5
|
+
token: 'value',
|
|
6
|
+
help: 'boolean',
|
|
7
|
+
version: 'boolean',
|
|
8
|
+
};
|
|
9
|
+
const VALUE_RE = /^(--[A-Za-z0-9][A-Za-z0-9-]*)=(.*)$/;
|
|
10
|
+
export function parseArgv(argv, specs) {
|
|
11
|
+
const commandArg = argv[0];
|
|
12
|
+
if (commandArg === undefined || commandArg === '') {
|
|
13
|
+
throw new UsageError('Missing command. Run `dropley --help` for usage.');
|
|
14
|
+
}
|
|
15
|
+
const spec = specs[commandArg];
|
|
16
|
+
if (!spec) {
|
|
17
|
+
throw new UsageError(`Unknown command: ${commandArg}. Run \`dropley --help\` for usage.`);
|
|
18
|
+
}
|
|
19
|
+
const flagKinds = { ...GLOBAL_FLAGS, ...spec.flags };
|
|
20
|
+
const positionals = [];
|
|
21
|
+
const values = {};
|
|
22
|
+
const flags = new Set();
|
|
23
|
+
for (let i = 1; i < argv.length; i++) {
|
|
24
|
+
const arg = argv[i];
|
|
25
|
+
if (arg === undefined)
|
|
26
|
+
break;
|
|
27
|
+
if (!arg.startsWith('-')) {
|
|
28
|
+
positionals.push(arg);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (arg === '--') {
|
|
32
|
+
for (const rest of argv.slice(i + 1))
|
|
33
|
+
positionals.push(rest);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (arg === '-') {
|
|
37
|
+
throw new UsageError("Unexpected argument '-'.");
|
|
38
|
+
}
|
|
39
|
+
let name;
|
|
40
|
+
let inlineValue;
|
|
41
|
+
const eqMatch = VALUE_RE.exec(arg);
|
|
42
|
+
if (eqMatch && eqMatch[1] !== undefined) {
|
|
43
|
+
name = eqMatch[1].slice(2);
|
|
44
|
+
inlineValue = eqMatch[2] ?? '';
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
name = arg.slice(2);
|
|
48
|
+
}
|
|
49
|
+
const kind = flagKinds[name];
|
|
50
|
+
if (!kind) {
|
|
51
|
+
throw new UsageError(`Unknown option --${name} for '${commandArg}'.`);
|
|
52
|
+
}
|
|
53
|
+
flags.add(name);
|
|
54
|
+
if (kind === 'boolean') {
|
|
55
|
+
if (inlineValue !== undefined && inlineValue !== 'true' && inlineValue !== 'false') {
|
|
56
|
+
throw new UsageError(`Option --${name} does not take a value.`);
|
|
57
|
+
}
|
|
58
|
+
if (inlineValue === 'false')
|
|
59
|
+
flags.delete(name);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const next = argv[i + 1];
|
|
63
|
+
const value = inlineValue ??
|
|
64
|
+
(next !== undefined && !next.startsWith('--') ? ((i++, next)) : undefined);
|
|
65
|
+
if (value === undefined) {
|
|
66
|
+
throw new UsageError(`Option --${name} requires a value.`);
|
|
67
|
+
}
|
|
68
|
+
values[name] = value;
|
|
69
|
+
}
|
|
70
|
+
if (flags.has('help') || flags.has('version')) {
|
|
71
|
+
return { command: commandArg, positionals, values, flags };
|
|
72
|
+
}
|
|
73
|
+
const requiredCount = spec.positionals.filter((p) => p.required).length;
|
|
74
|
+
if (positionals.length < requiredCount) {
|
|
75
|
+
const missing = spec.positionals[positionals.length];
|
|
76
|
+
throw new UsageError(`Missing argument: ${missing?.name ?? '<arg>'}.`);
|
|
77
|
+
}
|
|
78
|
+
if (positionals.length > spec.positionals.length) {
|
|
79
|
+
throw new UsageError(`Too many arguments for '${commandArg}' (expected ${spec.positionals.length}).`);
|
|
80
|
+
}
|
|
81
|
+
return { command: commandArg, positionals, values, flags };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=args.js.map
|
package/dist/args.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropley.js","sourceRoot":"","sources":["../../src/bin/dropley.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { parseArgv } from './args.js';
|
|
4
|
+
import { ApiError } from './api.js';
|
|
5
|
+
import { EXIT, AuthError, UsageError } from './errors.js';
|
|
6
|
+
import { createIo, formatApiError } from './output.js';
|
|
7
|
+
import { publish } from './commands/publish.js';
|
|
8
|
+
import { status } from './commands/status.js';
|
|
9
|
+
import { update } from './commands/update.js';
|
|
10
|
+
import { del } from './commands/delete.js';
|
|
11
|
+
import { pack } from './commands/pack.js';
|
|
12
|
+
const COMMANDS = {
|
|
13
|
+
publish: {
|
|
14
|
+
summary: 'Publish a file or directory and print its public URL + token.',
|
|
15
|
+
flags: { expiry: 'value' },
|
|
16
|
+
positionals: [{ name: '<path>', required: true, description: 'File or directory to publish' }],
|
|
17
|
+
handler: publish,
|
|
18
|
+
},
|
|
19
|
+
status: {
|
|
20
|
+
summary: 'Show metadata for a published artifact.',
|
|
21
|
+
flags: {},
|
|
22
|
+
positionals: [{ name: '<url-or-shortId>', required: true }],
|
|
23
|
+
handler: status,
|
|
24
|
+
},
|
|
25
|
+
update: {
|
|
26
|
+
summary: 'Update an artifact (expiry).',
|
|
27
|
+
flags: { expiry: 'value' },
|
|
28
|
+
positionals: [{ name: '<url-or-shortId>', required: true }],
|
|
29
|
+
handler: update,
|
|
30
|
+
},
|
|
31
|
+
delete: {
|
|
32
|
+
summary: 'Permanently delete an artifact.',
|
|
33
|
+
flags: {},
|
|
34
|
+
positionals: [{ name: '<url-or-shortId>', required: true }],
|
|
35
|
+
handler: del,
|
|
36
|
+
},
|
|
37
|
+
pack: {
|
|
38
|
+
summary: 'Create a byte-reproducible zip archive of a directory.',
|
|
39
|
+
flags: { out: 'value' },
|
|
40
|
+
positionals: [{ name: '<dir>', required: true }],
|
|
41
|
+
handler: pack,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
export const VERSION = (() => {
|
|
45
|
+
try {
|
|
46
|
+
// cli.js sits at <pkgroot>/dist/cli.js in both the repo and installed layout.
|
|
47
|
+
const pkgUrl = new URL('../package.json', import.meta.url);
|
|
48
|
+
return (JSON.parse(readFileSync(fileURLToPath(pkgUrl), 'utf8')).version ??
|
|
49
|
+
'0.0.0-dev');
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return '0.0.0-dev';
|
|
53
|
+
}
|
|
54
|
+
})();
|
|
55
|
+
function usage() {
|
|
56
|
+
const lines = [
|
|
57
|
+
`dropley ${VERSION} — publish files and static sites from your terminal`,
|
|
58
|
+
'',
|
|
59
|
+
'Usage:',
|
|
60
|
+
...Object.entries(COMMANDS).map(([name, spec]) => ` dropley ${name} ${spec.positionals.map((p) => p.name).join(' ')}${Object.keys(spec.flags).length ? ' [options]' : ''}`),
|
|
61
|
+
'',
|
|
62
|
+
'Commands:',
|
|
63
|
+
];
|
|
64
|
+
for (const [name, spec] of Object.entries(COMMANDS)) {
|
|
65
|
+
lines.push(` ${name.padEnd(10)} ${spec.summary}`);
|
|
66
|
+
}
|
|
67
|
+
lines.push('', 'Options:', ' --json Machine-readable output', ' --api <url> API base URL (default https://dropley.app; env DROPLEY_API)', ' --token <tok> Artifact token (env DROPLEY_TOKEN)', ' --expiry <t> e.g. 1h, 1d, 3d, 7d, 15d, 30d (server-validated)', '', 'Exit codes: 0 success · 1 failure · 2 usage error', 'Docs: https://github.com/dropley/cli');
|
|
68
|
+
return lines.join('\n');
|
|
69
|
+
}
|
|
70
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
71
|
+
if (argv.length === 0 || argv[0] === '--help' || argv[0] === '-h' || argv[0] === 'help') {
|
|
72
|
+
process.stdout.write(`${usage()}\n`);
|
|
73
|
+
return argv.length === 0 ? EXIT.usage : EXIT.ok;
|
|
74
|
+
}
|
|
75
|
+
if (argv[0] === '--version' || argv[0] === '-v' || argv[0] === 'version') {
|
|
76
|
+
process.stdout.write(`dropley ${VERSION}\n`);
|
|
77
|
+
return EXIT.ok;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const parsed = parseArgv(argv, COMMANDS);
|
|
81
|
+
if (parsed.flags.has('help')) {
|
|
82
|
+
process.stdout.write(`${usage()}\n`);
|
|
83
|
+
return EXIT.ok;
|
|
84
|
+
}
|
|
85
|
+
if (parsed.flags.has('version')) {
|
|
86
|
+
process.stdout.write(`dropley ${VERSION}\n`);
|
|
87
|
+
return EXIT.ok;
|
|
88
|
+
}
|
|
89
|
+
const spec = COMMANDS[parsed.command];
|
|
90
|
+
if (!spec)
|
|
91
|
+
throw new UsageError(`Unknown command: ${parsed.command}`);
|
|
92
|
+
const io = createIo(parsed.flags.has('json'));
|
|
93
|
+
return await spec.handler(parsed, io);
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
if (err instanceof UsageError) {
|
|
97
|
+
process.stderr.write(`${usage()}\n\nerror: ${err.message}\n`);
|
|
98
|
+
return EXIT.usage;
|
|
99
|
+
}
|
|
100
|
+
if (err instanceof AuthError) {
|
|
101
|
+
process.stderr.write(`${err.message}\n`);
|
|
102
|
+
return EXIT.failure;
|
|
103
|
+
}
|
|
104
|
+
if (err instanceof ApiError) {
|
|
105
|
+
process.stderr.write(`${formatApiError(err)}\n`);
|
|
106
|
+
return EXIT.failure;
|
|
107
|
+
}
|
|
108
|
+
process.stderr.write(`error: ${err.message ?? String(err)}\nRun \`dropley --help\` for usage.\n`);
|
|
109
|
+
return EXIT.failure;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const MAX_TOTAL_BYTES: number;
|
|
2
|
+
export declare const MAX_FILE_COUNT = 1000;
|
|
3
|
+
export interface CollectedFile {
|
|
4
|
+
/** Absolute path on disk. */
|
|
5
|
+
absPath: string;
|
|
6
|
+
/** Relative POSIX path used as the manifest path and multipart filename. */
|
|
7
|
+
path: string;
|
|
8
|
+
size: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Walks a directory recursively and returns every non-junk file, sorted by
|
|
12
|
+
* relative POSIX path (byte-stable ordering). Junk = OS metadata (.DS_Store,
|
|
13
|
+
* Thumbs.db, desktop.ini, AppleDouble ._*), VCS internals, and node_modules.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectFiles(rootDir: string): Promise<CollectedFile[]>;
|
package/dist/collect.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { UsageError } from './errors.js';
|
|
4
|
+
export const MAX_TOTAL_BYTES = 50 * 1024 * 1024;
|
|
5
|
+
export const MAX_FILE_COUNT = 1000;
|
|
6
|
+
const JUNK_FILES = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
|
|
7
|
+
const JUNK_DIRS = new Set(['.git', '.hg', '.svn']);
|
|
8
|
+
function isJunkName(name) {
|
|
9
|
+
return JUNK_FILES.has(name) || name.startsWith('._');
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Walks a directory recursively and returns every non-junk file, sorted by
|
|
13
|
+
* relative POSIX path (byte-stable ordering). Junk = OS metadata (.DS_Store,
|
|
14
|
+
* Thumbs.db, desktop.ini, AppleDouble ._*), VCS internals, and node_modules.
|
|
15
|
+
*/
|
|
16
|
+
export async function collectFiles(rootDir) {
|
|
17
|
+
const out = [];
|
|
18
|
+
let totalBytes = 0;
|
|
19
|
+
const walk = async (dir) => {
|
|
20
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
if (entry.name === 'node_modules' && entry.isDirectory())
|
|
23
|
+
continue;
|
|
24
|
+
if (entry.isDirectory()) {
|
|
25
|
+
if (!JUNK_DIRS.has(entry.name))
|
|
26
|
+
await walk(join(dir, entry.name));
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!entry.isFile())
|
|
30
|
+
continue;
|
|
31
|
+
if (isJunkName(entry.name))
|
|
32
|
+
continue;
|
|
33
|
+
const absPath = join(dir, entry.name);
|
|
34
|
+
const relPath = absPath.slice(rootDir.length + 1).split('\\').join('/');
|
|
35
|
+
const size = (await stat(absPath)).size;
|
|
36
|
+
totalBytes += size;
|
|
37
|
+
out.push({ absPath, path: relPath, size });
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
await walk(rootDir);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof UsageError)
|
|
45
|
+
throw err;
|
|
46
|
+
throw new UsageError(`Cannot read directory ${rootDir}: ${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
out.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
49
|
+
if (out.length === 0)
|
|
50
|
+
throw new UsageError(`No files found in ${rootDir}.`);
|
|
51
|
+
if (out.length > MAX_FILE_COUNT) {
|
|
52
|
+
throw new UsageError(`Too many files: ${out.length} (max ${MAX_FILE_COUNT}). Publish a build output directory instead.`);
|
|
53
|
+
}
|
|
54
|
+
if (totalBytes > MAX_TOTAL_BYTES) {
|
|
55
|
+
throw new UsageError(`Total size ${(totalBytes / 1024 / 1024).toFixed(1)}MB exceeds the ${MAX_TOTAL_BYTES / 1024 / 1024}MB limit.`);
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=collect.js.map
|