@buildinternet/uploads 0.11.0 → 0.12.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 +14 -3
- package/dist/async.d.ts +5 -0
- package/dist/async.js +21 -0
- package/dist/cli-catalog.d.ts +7 -0
- package/dist/cli-catalog.js +52 -1
- package/dist/cli.js +12 -0
- package/dist/client.d.ts +16 -0
- package/dist/client.js +13 -7
- package/dist/commands/completion.js +15 -5
- package/dist/commands/install.js +5 -0
- package/dist/commands/screenshot.d.ts +6 -0
- package/dist/commands/screenshot.js +317 -0
- package/dist/commands.d.ts +162 -2
- package/dist/commands.js +440 -143
- package/dist/config-file.d.ts +28 -2
- package/dist/config-file.js +44 -6
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/mcp/batch-error.d.ts +16 -0
- package/dist/mcp/batch-error.js +24 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +16 -0
- package/dist/mcp/tools.d.ts +8 -1
- package/dist/mcp/tools.js +393 -94
- package/dist/screenshot-local.d.ts +64 -0
- package/dist/screenshot-local.js +310 -0
- package/dist/screenshot-remote.d.ts +23 -0
- package/dist/screenshot-remote.js +79 -0
- package/dist/screenshot.d.ts +74 -0
- package/dist/screenshot.js +231 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ npx @buildinternet/uploads --help
|
|
|
15
15
|
uploads setup
|
|
16
16
|
uploads --version
|
|
17
17
|
uploads attach ./before.png ./after.png
|
|
18
|
+
uploads screenshot https://app.example --pr 123 --comment # capture + host in one step
|
|
19
|
+
uploads screenshot ./report.html --dark --selector "main"
|
|
18
20
|
uploads put ./shot.png
|
|
19
21
|
uploads put ./shot.png --destination screenshots
|
|
20
22
|
uploads put ./shot.png --no-optimize
|
|
@@ -37,7 +39,7 @@ Inside this monorepo only, `pnpm uploads …` builds the package first so you pi
|
|
|
37
39
|
up local source; product docs and PR “how to try it” examples should use the
|
|
38
40
|
global `uploads` form above.
|
|
39
41
|
|
|
40
|
-
Commands: `attach`, `put`, `gallery`, `comment`, `list`, `find`, `meta`, `delete`, `usage`,
|
|
42
|
+
Commands: `attach`, `put`, `screenshot`, `gallery`, `comment`, `list`, `find`, `meta`, `delete`, `usage`,
|
|
41
43
|
`reconcile`, `purge-expired`, `setup`, `install`, `login`, `whoami` (alias `status`),
|
|
42
44
|
`logout`, `invite`, `admin`, `config`, `telemetry`, `report`, `doctor`, `health`, `mcp`,
|
|
43
45
|
`completion`.
|
|
@@ -81,6 +83,15 @@ infers the pull request for the current branch via `gh`, uploads stable URLs, an
|
|
|
81
83
|
or updates one marker-owned GitHub comment. It keeps loose `gh/...` attachments and linked public galleries in distinct sections, shows up to three available gallery images inline, and updates that same comment in place on every sync. Use `--pr`, `--issue`, and `--repo` to select
|
|
82
84
|
the target explicitly, or `--no-comment` to upload without changing GitHub comments.
|
|
83
85
|
|
|
86
|
+
**Screenshot capture:** `uploads screenshot <url|file.html>` renders a page to a
|
|
87
|
+
hosted image in one step — no separate browser tooling needed. `--via auto`
|
|
88
|
+
(default) drives a Chrome/Chromium already on the machine (`playwright-core`
|
|
89
|
+
ships no browser; `--browser <path>`, `--cdp <endpoint>`, or the Playwright/
|
|
90
|
+
Puppeteer caches all work), and falls back to server-side rendering when none
|
|
91
|
+
is found. localhost/private URLs are local-only; `.html` files work on both
|
|
92
|
+
backends. After capture it joins the same pipeline as `put` (optimize, frames,
|
|
93
|
+
`--pr`/`--issue` comments, galleries). See `uploads screenshot --help`.
|
|
94
|
+
|
|
84
95
|
**Keys / destinations:** default put uses the `screenshots` layout. Typed destinations
|
|
85
96
|
(`--destination screenshots|gh|f`, MCP `destination`) set the root; `--pr`/`--issue`
|
|
86
97
|
use `gh/…`. Workspaces may restrict put/sign to those roots via
|
|
@@ -128,7 +139,7 @@ Config layers (first match wins): CLI flags → env vars → `--env-file` → `~
|
|
|
128
139
|
|
|
129
140
|
## MCP server
|
|
130
141
|
|
|
131
|
-
`uploads mcp` serves the Model Context Protocol over stdio (newline-delimited JSON-RPC, no extra dependencies). Tools include file operations plus public gallery workflows: `gallery_create`, `gallery_get`, `gallery_add`, `gallery_link`, and `gallery_find_by_reference`. Gallery tools return API-provided canonical URLs and never need GitHub credentials. The remaining stdio tools are `put`, `attach`, `list`, `delete`, `set_metadata`, `find_files`, `usage`, `reconcile`, `purge_expired`, `comment`, `health`, and `doctor` — with the same config resolution and defaults, plus a per-call `workspace` argument. `put` and `attach` accept a `metadata` param (same `gh.*` auto-injection as the CLI's `attach`); `set_metadata
|
|
142
|
+
`uploads mcp` serves the Model Context Protocol over stdio (newline-delimited JSON-RPC, no extra dependencies). Tools include file operations plus public gallery workflows: `gallery_create`, `gallery_get`, `gallery_add`, `gallery_link`, and `gallery_find_by_reference`. Gallery tools return API-provided canonical URLs and never need GitHub credentials. The remaining stdio tools are `put`, `attach`, `list`, `delete`, `get_metadata`, `set_metadata`, `find_files`, `usage`, `reconcile`, `purge_expired`, `comment`, `health`, and `doctor` — with the same config resolution and defaults, plus a per-call `workspace` argument. `put` and `attach` accept a `metadata` param (same `gh.*` auto-injection as the CLI's `attach`); `get_metadata`, `set_metadata`, and `find_files` mirror `uploads meta get` / `meta set` / `find`. Interactive/credential commands (`setup`, `login`, `admin`, `config`) are not exposed. A token isn't required to start the server; auth errors surface per tool call (`health` needs no auth).
|
|
132
143
|
|
|
133
144
|
```json
|
|
134
145
|
{ "command": "uploads", "args": ["--env-file", "/path/to/.env", "mcp"] }
|
|
@@ -136,7 +147,7 @@ Config layers (first match wins): CLI flags → env vars → `--env-file` → `~
|
|
|
136
147
|
|
|
137
148
|
Or with `UPLOADS_TOKEN`/`UPLOADS_WORKSPACE` in the environment or user config. Claude Code: `claude mcp add uploads -- uploads --env-file /path/to/.env mcp`.
|
|
138
149
|
|
|
139
|
-
For HTTP clients there's also a hosted variant at `https://agents.uploads.sh/mcp` — the workspace is inferred from the bearer token, so only the URL and token are needed (`https://agents.uploads.sh/<workspace>/mcp` and the `mcp.uploads.sh` hostname also work). Tools: file operations plus `gallery_create`, `gallery_get`, `gallery_add`, `gallery_link`, and `gallery_find_by_reference`; all use the same bearer-token workspace scopes and gallery URLs come from the API — see `apps/mcp` in the repo. The hosted `put` also accepts a `metadata` param. `uploads install` registers the skills + hosted MCP (short progress; `--verbose` for underlying output). Its `put` takes no content type: the stored type is sniffed server-side from the bytes and checked against the workspace allowlist, and writes are rate limited per workspace.
|
|
150
|
+
For HTTP clients there's also a hosted variant at `https://agents.uploads.sh/mcp` — the workspace is inferred from the bearer token, so only the URL and token are needed (`https://agents.uploads.sh/<workspace>/mcp` and the `mcp.uploads.sh` hostname also work). Tools: file operations (including `get_metadata` / `set_metadata` / `find_files`) plus `gallery_create`, `gallery_get`, `gallery_add`, `gallery_link`, and `gallery_find_by_reference`; all use the same bearer-token workspace scopes and gallery URLs come from the API — see `apps/mcp` in the repo. The hosted `put` also accepts a `metadata` param. `uploads install` registers the skills + hosted MCP (short progress; `--verbose` for underlying output). Its `put` takes no content type: the stored type is sniffed server-side from the bytes and checked against the workspace allowlist, and writes are rate limited per workspace.
|
|
140
151
|
|
|
141
152
|
## Programmatic use
|
|
142
153
|
|
package/dist/async.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `fn` over `values` with bounded concurrency, preserving input order
|
|
3
|
+
* in the returned array. Used by multi-file attach (and similar fan-outs).
|
|
4
|
+
*/
|
|
5
|
+
export declare function mapBounded<T, R>(values: readonly T[], concurrency: number, fn: (value: T, index: number) => Promise<R>): Promise<R[]>;
|
package/dist/async.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `fn` over `values` with bounded concurrency, preserving input order
|
|
3
|
+
* in the returned array. Used by multi-file attach (and similar fan-outs).
|
|
4
|
+
*/
|
|
5
|
+
export async function mapBounded(values, concurrency, fn) {
|
|
6
|
+
if (values.length === 0)
|
|
7
|
+
return [];
|
|
8
|
+
const limit = Math.max(1, Math.min(concurrency, values.length));
|
|
9
|
+
const result = Array.from({ length: values.length });
|
|
10
|
+
let next = 0;
|
|
11
|
+
async function worker() {
|
|
12
|
+
for (;;) {
|
|
13
|
+
const index = next++;
|
|
14
|
+
if (index >= values.length)
|
|
15
|
+
return;
|
|
16
|
+
result[index] = await fn(values[index], index);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
await Promise.all(Array.from({ length: limit }, worker));
|
|
20
|
+
return result;
|
|
21
|
+
}
|
package/dist/cli-catalog.d.ts
CHANGED
|
@@ -22,6 +22,13 @@ export declare const GLOBAL_FLAGS: readonly {
|
|
|
22
22
|
}[];
|
|
23
23
|
/** Common flags for put/attach (file-oriented commands). */
|
|
24
24
|
export declare const PUT_LIKE_FLAGS: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* All flags `uploads screenshot` actually reads (verified against
|
|
27
|
+
* commands/screenshot.ts) — kept as its own explicit list rather than
|
|
28
|
+
* spreading PUT_LIKE_FLAGS, which includes `--name`/`--no-comment` that
|
|
29
|
+
* screenshot never parses.
|
|
30
|
+
*/
|
|
31
|
+
export declare const SCREENSHOT_FLAGS: readonly string[];
|
|
25
32
|
export declare const LIST_LIKE_FLAGS: readonly string[];
|
|
26
33
|
export declare const ROOT_COMMANDS: readonly CatalogCommand[];
|
|
27
34
|
export declare const COMPLETION_SHELLS: readonly ["bash", "zsh", "fish"];
|
package/dist/cli-catalog.js
CHANGED
|
@@ -40,6 +40,51 @@ export const PUT_LIKE_FLAGS = [
|
|
|
40
40
|
"--help",
|
|
41
41
|
"-h",
|
|
42
42
|
];
|
|
43
|
+
/**
|
|
44
|
+
* All flags `uploads screenshot` actually reads (verified against
|
|
45
|
+
* commands/screenshot.ts) — kept as its own explicit list rather than
|
|
46
|
+
* spreading PUT_LIKE_FLAGS, which includes `--name`/`--no-comment` that
|
|
47
|
+
* screenshot never parses.
|
|
48
|
+
*/
|
|
49
|
+
export const SCREENSHOT_FLAGS = [
|
|
50
|
+
"--via",
|
|
51
|
+
"--browser",
|
|
52
|
+
"--cdp",
|
|
53
|
+
"--viewport",
|
|
54
|
+
"--selector",
|
|
55
|
+
"--full-page",
|
|
56
|
+
"--dark",
|
|
57
|
+
"--light",
|
|
58
|
+
"--wait",
|
|
59
|
+
"--out",
|
|
60
|
+
"--no-upload",
|
|
61
|
+
"--destination",
|
|
62
|
+
"--prefix",
|
|
63
|
+
"--repo",
|
|
64
|
+
"--ref",
|
|
65
|
+
"--key",
|
|
66
|
+
"--alt",
|
|
67
|
+
"--width",
|
|
68
|
+
"--frame",
|
|
69
|
+
"--frame-url",
|
|
70
|
+
"--frame-fit",
|
|
71
|
+
"--no-optimize",
|
|
72
|
+
"--optimize-max-edge",
|
|
73
|
+
"--optimize-quality",
|
|
74
|
+
"--keep-exif",
|
|
75
|
+
"--no-git",
|
|
76
|
+
"--pr",
|
|
77
|
+
"--issue",
|
|
78
|
+
"--comment",
|
|
79
|
+
"--gallery",
|
|
80
|
+
"--meta",
|
|
81
|
+
"--dry-run",
|
|
82
|
+
"--format",
|
|
83
|
+
"--workspace",
|
|
84
|
+
"-w",
|
|
85
|
+
"--help",
|
|
86
|
+
"-h",
|
|
87
|
+
];
|
|
43
88
|
export const LIST_LIKE_FLAGS = [
|
|
44
89
|
"--prefix",
|
|
45
90
|
"--limit",
|
|
@@ -59,10 +104,16 @@ export const ROOT_COMMANDS = [
|
|
|
59
104
|
},
|
|
60
105
|
{
|
|
61
106
|
name: "put",
|
|
62
|
-
usage: "put <file
|
|
107
|
+
usage: "put <file...>",
|
|
63
108
|
summary: "Upload (+ URL + markdown for GitHub)",
|
|
64
109
|
essential: true,
|
|
65
110
|
},
|
|
111
|
+
{
|
|
112
|
+
name: "screenshot",
|
|
113
|
+
usage: "screenshot <target>",
|
|
114
|
+
summary: "Capture a URL or .html file and host it (local browser or remote render)",
|
|
115
|
+
essential: true,
|
|
116
|
+
},
|
|
66
117
|
{
|
|
67
118
|
name: "gallery",
|
|
68
119
|
summary: "Create and organize public media galleries",
|
package/dist/cli.js
CHANGED
|
@@ -16,6 +16,7 @@ import { runCompletion } from "./commands/completion.js";
|
|
|
16
16
|
import { runLogout, runWhoami } from "./commands/session.js";
|
|
17
17
|
import { runTelemetry } from "./commands/telemetry.js";
|
|
18
18
|
import { runReport } from "./commands/report.js";
|
|
19
|
+
import { runScreenshot } from "./commands/screenshot.js";
|
|
19
20
|
import { packageVersion } from "./package-version.js";
|
|
20
21
|
import { checkForUpdate, maybeHintUpdate } from "./update-check.js";
|
|
21
22
|
import { errorCodeFromUnknown, maybeShowFirstRunNotice, recordEvent, telemetryCommandName, } from "./telemetry.js";
|
|
@@ -75,7 +76,12 @@ function exitCode(err) {
|
|
|
75
76
|
case "STORAGE_QUOTA":
|
|
76
77
|
case "UPLOAD_BUDGET":
|
|
77
78
|
return 3;
|
|
79
|
+
case "BROWSER_NOT_FOUND":
|
|
80
|
+
return 2;
|
|
78
81
|
case "NETWORK":
|
|
82
|
+
// Transient — same "retry, don't reconfigure" family as a network
|
|
83
|
+
// hiccup, not an auth/policy/budget denial (those are exit 3).
|
|
84
|
+
case "RATE_LIMITED":
|
|
79
85
|
return 4;
|
|
80
86
|
default:
|
|
81
87
|
return 1;
|
|
@@ -99,6 +105,8 @@ const ERROR_HINTS = {
|
|
|
99
105
|
UPLOAD_BUDGET: QUOTA_HINT,
|
|
100
106
|
KEY_POLICY: "hint: use a typed destination (`--destination screenshots|gh`) or an allowed prefix; operators set allowlists with `pnpm workspace:limits --allowed-prefixes`\n",
|
|
101
107
|
UNAUTHORIZED: "hint: token rejected — run `uploads login` to sign in again, or check UPLOADS_TOKEN / --token\n",
|
|
108
|
+
BROWSER_NOT_FOUND: "hint: no local browser found; try --via remote, or install Chrome / npx playwright install chromium\n",
|
|
109
|
+
RATE_LIMITED: "hint: transient rate limit — wait ~60s and retry\n",
|
|
102
110
|
};
|
|
103
111
|
function errorOut(err, format) {
|
|
104
112
|
const payload = err instanceof UploadsError
|
|
@@ -260,6 +268,7 @@ export async function runCli(argv) {
|
|
|
260
268
|
break;
|
|
261
269
|
case "attach":
|
|
262
270
|
case "put":
|
|
271
|
+
case "screenshot":
|
|
263
272
|
case "gallery":
|
|
264
273
|
case "list":
|
|
265
274
|
case "find":
|
|
@@ -278,6 +287,9 @@ export async function runCli(argv) {
|
|
|
278
287
|
case "put":
|
|
279
288
|
code = await runPut(ctx, cmdArgs, showHelp);
|
|
280
289
|
break;
|
|
290
|
+
case "screenshot":
|
|
291
|
+
code = await runScreenshot(ctx, cmdArgs, showHelp);
|
|
292
|
+
break;
|
|
281
293
|
case "gallery":
|
|
282
294
|
code = await runGallery(ctx, cmdArgs, showHelp);
|
|
283
295
|
break;
|
package/dist/client.d.ts
CHANGED
|
@@ -336,6 +336,22 @@ export declare function mintWorkspaceToken(apiUrl: string, accessToken: string,
|
|
|
336
336
|
label?: string;
|
|
337
337
|
ttlSeconds?: number;
|
|
338
338
|
}): Promise<MintTokenResult>;
|
|
339
|
+
/**
|
|
340
|
+
* Parse API error bodies. Prefers the nested envelope
|
|
341
|
+
* `{ error: { code, type, message, details? } }`; still accepts the legacy
|
|
342
|
+
* flat `{ error: string, code?: string }` shape. Exported so other backends
|
|
343
|
+
* (e.g. the screenshot render endpoint) share this parsing instead of
|
|
344
|
+
* duplicating it — each caller supplies its own `fallback` message.
|
|
345
|
+
*/
|
|
346
|
+
export declare function extractErrorFields(body: unknown, fallback?: string): {
|
|
347
|
+
message: string;
|
|
348
|
+
code?: string;
|
|
349
|
+
};
|
|
350
|
+
/** Fetch + parse an error-response body via {@link extractErrorFields}. */
|
|
351
|
+
export declare function parseErrorEnvelope(res: Response, fallback?: string): Promise<{
|
|
352
|
+
message: string;
|
|
353
|
+
code?: string;
|
|
354
|
+
}>;
|
|
339
355
|
export declare function createUploadsClient(config: UploadsClientConfig): {
|
|
340
356
|
put(body: Uint8Array, opts: PutOptions & {
|
|
341
357
|
filename: string;
|
package/dist/client.js
CHANGED
|
@@ -210,15 +210,17 @@ function mapApiError(status, error, code) {
|
|
|
210
210
|
/**
|
|
211
211
|
* Parse API error bodies. Prefers the nested envelope
|
|
212
212
|
* `{ error: { code, type, message, details? } }`; still accepts the legacy
|
|
213
|
-
* flat `{ error: string, code?: string }` shape.
|
|
213
|
+
* flat `{ error: string, code?: string }` shape. Exported so other backends
|
|
214
|
+
* (e.g. the screenshot render endpoint) share this parsing instead of
|
|
215
|
+
* duplicating it — each caller supplies its own `fallback` message.
|
|
214
216
|
*/
|
|
215
|
-
function extractErrorFields(body) {
|
|
217
|
+
export function extractErrorFields(body, fallback = "request failed") {
|
|
216
218
|
if (typeof body === "object" && body && "error" in body) {
|
|
217
219
|
const err = body.error;
|
|
218
220
|
if (typeof err === "object" && err && "message" in err) {
|
|
219
221
|
const nested = err;
|
|
220
222
|
return {
|
|
221
|
-
message: typeof nested.message === "string" ? nested.message :
|
|
223
|
+
message: typeof nested.message === "string" ? nested.message : fallback,
|
|
222
224
|
code: typeof nested.code === "string" ? nested.code : undefined,
|
|
223
225
|
};
|
|
224
226
|
}
|
|
@@ -229,12 +231,16 @@ function extractErrorFields(body) {
|
|
|
229
231
|
return { message: err, code };
|
|
230
232
|
}
|
|
231
233
|
}
|
|
232
|
-
return { message:
|
|
234
|
+
return { message: fallback };
|
|
233
235
|
}
|
|
234
|
-
|
|
236
|
+
/** Fetch + parse an error-response body via {@link extractErrorFields}. */
|
|
237
|
+
export async function parseErrorEnvelope(res, fallback = "request failed") {
|
|
235
238
|
const body = await res.json().catch(() => ({}));
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
return extractErrorFields(body, fallback);
|
|
240
|
+
}
|
|
241
|
+
async function parseErrorResponse(res) {
|
|
242
|
+
const { message, code } = await parseErrorEnvelope(res, res.statusText || "request failed");
|
|
243
|
+
return mapApiError(res.status, message, code);
|
|
238
244
|
}
|
|
239
245
|
export function createUploadsClient(config) {
|
|
240
246
|
async function request(method, path, opts) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parseCommandArgs, UsageError } from "../cli-args.js";
|
|
2
|
-
import { COMPLETION_SHELLS, GLOBAL_FLAGS, LIST_LIKE_FLAGS, PUT_LIKE_FLAGS, ROOT_COMMANDS, isCompletionShell, } from "../cli-catalog.js";
|
|
2
|
+
import { COMPLETION_SHELLS, GLOBAL_FLAGS, LIST_LIKE_FLAGS, PUT_LIKE_FLAGS, SCREENSHOT_FLAGS, ROOT_COMMANDS, isCompletionShell, } from "../cli-catalog.js";
|
|
3
3
|
import { writeCommandHelp } from "../cli-style.js";
|
|
4
4
|
const HELP = `uploads completion <shell>
|
|
5
5
|
|
|
@@ -28,6 +28,7 @@ function bashScript() {
|
|
|
28
28
|
const globals = GLOBAL_FLAGS.map((g) => g.flag).join(" ");
|
|
29
29
|
const putFlags = PUT_LIKE_FLAGS.join(" ");
|
|
30
30
|
const listFlags = LIST_LIKE_FLAGS.join(" ");
|
|
31
|
+
const screenshotFlags = SCREENSHOT_FLAGS.join(" ");
|
|
31
32
|
const subMaps = ROOT_COMMANDS.filter((c) => c.subcommands?.length).map((c) => {
|
|
32
33
|
const names = c.subcommands.map((s) => s.name).join(" ");
|
|
33
34
|
return ` ${c.name}) subs="${names}" ;;`;
|
|
@@ -52,6 +53,7 @@ _uploads() {
|
|
|
52
53
|
local -a globals=(${globals})
|
|
53
54
|
local -a put_flags=(${putFlags})
|
|
54
55
|
local -a list_flags=(${listFlags})
|
|
56
|
+
local -a screenshot_flags=(${screenshotFlags})
|
|
55
57
|
|
|
56
58
|
# Find the first non-global positional (the subcommand).
|
|
57
59
|
local cmd="" i=1
|
|
@@ -102,6 +104,9 @@ ${subMaps.join("\n")}
|
|
|
102
104
|
put|attach)
|
|
103
105
|
COMPREPLY=( $(compgen -W "\${put_flags[*]}" -- "$cur") )
|
|
104
106
|
;;
|
|
107
|
+
screenshot)
|
|
108
|
+
COMPREPLY=( $(compgen -W "\${screenshot_flags[*]}" -- "$cur") )
|
|
109
|
+
;;
|
|
105
110
|
list|find)
|
|
106
111
|
COMPREPLY=( $(compgen -W "\${list_flags[*]}" -- "$cur") )
|
|
107
112
|
;;
|
|
@@ -114,7 +119,7 @@ ${subMaps.join("\n")}
|
|
|
114
119
|
|
|
115
120
|
# File paths for upload-style commands.
|
|
116
121
|
case "$cmd" in
|
|
117
|
-
put|attach)
|
|
122
|
+
put|attach|screenshot)
|
|
118
123
|
COMPREPLY=( $(compgen -f -- "$cur") )
|
|
119
124
|
;;
|
|
120
125
|
esac
|
|
@@ -194,7 +199,7 @@ ${globalArgs} \\
|
|
|
194
199
|
args)
|
|
195
200
|
case $line[1] in
|
|
196
201
|
${subCases}
|
|
197
|
-
put|attach)
|
|
202
|
+
put|attach|screenshot)
|
|
198
203
|
_files
|
|
199
204
|
;;
|
|
200
205
|
esac
|
|
@@ -250,13 +255,18 @@ function fishScript() {
|
|
|
250
255
|
continue;
|
|
251
256
|
lines.push(`complete -c uploads -n '__fish_seen_subcommand_from put attach' -l ${flag.slice(2)}`);
|
|
252
257
|
}
|
|
258
|
+
for (const flag of SCREENSHOT_FLAGS) {
|
|
259
|
+
if (!flag.startsWith("--"))
|
|
260
|
+
continue;
|
|
261
|
+
lines.push(`complete -c uploads -n '__fish_seen_subcommand_from screenshot' -l ${flag.slice(2)}`);
|
|
262
|
+
}
|
|
253
263
|
for (const flag of LIST_LIKE_FLAGS) {
|
|
254
264
|
if (!flag.startsWith("--"))
|
|
255
265
|
continue;
|
|
256
266
|
lines.push(`complete -c uploads -n '__fish_seen_subcommand_from list find' -l ${flag.slice(2)}`);
|
|
257
267
|
}
|
|
258
|
-
// File completion for put/attach
|
|
259
|
-
lines.push(`complete -c uploads -n '__fish_seen_subcommand_from put attach' -F`);
|
|
268
|
+
// File completion for put/attach/screenshot
|
|
269
|
+
lines.push(`complete -c uploads -n '__fish_seen_subcommand_from put attach screenshot' -F`);
|
|
260
270
|
return lines.join("\n") + "\n";
|
|
261
271
|
}
|
|
262
272
|
export function generateCompletionScript(shell) {
|
package/dist/commands/install.js
CHANGED
|
@@ -189,6 +189,7 @@ export async function runInstall(args, opts, help = false) {
|
|
|
189
189
|
.filter(([step]) => step.startsWith("skill:"))
|
|
190
190
|
.map(([, r]) => r);
|
|
191
191
|
const skillsOk = skillResults.length > 0 && skillResults.every((r) => r.ok);
|
|
192
|
+
const skillsFailed = skillResults.some((r) => !r.ok);
|
|
192
193
|
if (!failed && !dryRun) {
|
|
193
194
|
const stepLabels = [
|
|
194
195
|
...new Set(Object.keys(results).map((k) => (k.startsWith("skill:") ? "skills" : k))),
|
|
@@ -201,5 +202,9 @@ export async function runInstall(args, opts, help = false) {
|
|
|
201
202
|
: "Fix the MCP step above, then re-run `uploads install mcp`.";
|
|
202
203
|
process.stdout.write(`\nSkills are installed. ${next}\n`);
|
|
203
204
|
}
|
|
205
|
+
else if (failed && !dryRun && skillsFailed) {
|
|
206
|
+
// Mixed or total skill failure used to print only per-step lines (#191).
|
|
207
|
+
process.stdout.write("\nSkill install incomplete. Fix the errors above, then re-run `uploads install skill`.\n");
|
|
208
|
+
}
|
|
204
209
|
return failed ? 1 : 0;
|
|
205
210
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type CliContext } from "../commands.js";
|
|
2
|
+
import { type CommandRunner } from "../github-gh.js";
|
|
3
|
+
import { captureScreenshot } from "../screenshot.js";
|
|
4
|
+
export declare function runScreenshot(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner,
|
|
5
|
+
/** Injectable for tests — avoids launching a real browser or hitting the network. */
|
|
6
|
+
captureImpl?: typeof captureScreenshot): Promise<number>;
|