@buildinternet/uploads 0.11.0 → 0.11.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 CHANGED
@@ -128,7 +128,7 @@ Config layers (first match wins): CLI flags → env vars → `--env-file` → `~
128
128
 
129
129
  ## MCP server
130
130
 
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` and `find_files` mirror `uploads meta set` and `uploads 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).
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`, `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
132
 
133
133
  ```json
134
134
  { "command": "uploads", "args": ["--env-file", "/path/to/.env", "mcp"] }
@@ -136,7 +136,7 @@ Config layers (first match wins): CLI flags → env vars → `--env-file` → `~
136
136
 
137
137
  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
138
 
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.
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 (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
140
 
141
141
  ## Programmatic use
142
142
 
@@ -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
+ }
@@ -59,7 +59,7 @@ export const ROOT_COMMANDS = [
59
59
  },
60
60
  {
61
61
  name: "put",
62
- usage: "put <file>",
62
+ usage: "put <file...>",
63
63
  summary: "Upload (+ URL + markdown for GitHub)",
64
64
  essential: true,
65
65
  },
@@ -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
  }
@@ -1,4 +1,4 @@
1
- import { type UploadsClient } from "./client.js";
1
+ import { type PutResult, type UploadsClient } from "./client.js";
2
2
  import { type CommandFlags } from "./cli-args.js";
3
3
  import { type ResolvedConfig } from "./config.js";
4
4
  import { type GhTarget } from "./github.js";
@@ -6,6 +6,10 @@ import { type CommandRunner } from "./github-gh.js";
6
6
  import { type OptimizeImageOptions, type OptimizeImageResult } from "./optimize.js";
7
7
  import { type FrameResult } from "./frame.js";
8
8
  import type { PutDefaults } from "./config-file.js";
9
+ /** Parallel fan-out for multi-file put/attach (matches files-sdk bulk default). */
10
+ export declare const UPLOAD_BATCH_CONCURRENCY = 8;
11
+ /** @deprecated Use UPLOAD_BATCH_CONCURRENCY. */
12
+ export declare const ATTACH_CONCURRENCY = 8;
9
13
  export { formatUsageHuman } from "./format-usage.js";
10
14
  export interface CliContext {
11
15
  config: ResolvedConfig;
@@ -42,6 +46,92 @@ export declare function syncAttachmentsComment(client: UploadsClient, target: Gh
42
46
  action: "created" | "updated" | "skipped";
43
47
  count: number;
44
48
  }>;
49
+ export type AttachUploadItem = PutResult & {
50
+ file: string;
51
+ markdown: string;
52
+ optimize: {
53
+ optimized: boolean;
54
+ skippedReason?: OptimizeImageResult["skippedReason"];
55
+ originalBytes: number;
56
+ outputBytes: number;
57
+ filename: string;
58
+ };
59
+ frame?: PreparedUpload["frame"];
60
+ };
61
+ export type AttachFailure = {
62
+ file: string;
63
+ error: {
64
+ message: string;
65
+ code?: string;
66
+ status?: number;
67
+ };
68
+ };
69
+ /**
70
+ * Prepare + put each path as a PR/issue attachment with bounded concurrency.
71
+ * Per-file errors collect in `failures` (does not throw). `firstError` is the
72
+ * original cause of the first failure — for rethrowing single-file CLI paths.
73
+ */
74
+ export declare function uploadAttachments(opts: {
75
+ client: UploadsClient;
76
+ target: GhTarget;
77
+ files: readonly string[];
78
+ contentType?: string;
79
+ optimize: OptimizeImageOptions;
80
+ frame: {
81
+ frameId?: string;
82
+ frameUrl?: string;
83
+ frameFit?: "cover" | "contain";
84
+ };
85
+ metadata?: Record<string, string>;
86
+ /** Provenance `client` field (default uploads-cli). */
87
+ provenanceClient?: string;
88
+ concurrency?: number;
89
+ }): Promise<{
90
+ uploads: AttachUploadItem[];
91
+ failures: AttachFailure[];
92
+ firstError?: unknown;
93
+ }>;
94
+ export type PutUploadItem = PutResult & {
95
+ file: string;
96
+ markdown: string;
97
+ optimize: AttachUploadItem["optimize"];
98
+ frame?: PreparedUpload["frame"];
99
+ };
100
+ /**
101
+ * Prepare + put each path with put-style key resolution and bounded concurrency.
102
+ * Same partial-failure shape as uploadAttachments.
103
+ */
104
+ export declare function uploadPuts(opts: {
105
+ client: UploadsClient;
106
+ files: readonly string[];
107
+ /** Single-file --name leaf override. */
108
+ nameOverride?: string;
109
+ /** Single-file --key. */
110
+ explicitKey?: string;
111
+ ghTarget?: GhTarget;
112
+ prefix?: string;
113
+ repo?: string;
114
+ ref?: string;
115
+ deriveRepoFromGit?: boolean;
116
+ contentType?: string;
117
+ dryRun?: boolean;
118
+ optimize: OptimizeImageOptions;
119
+ frame: {
120
+ frameId?: string;
121
+ frameUrl?: string;
122
+ frameFit?: "cover" | "contain";
123
+ };
124
+ metadata?: Record<string, string>;
125
+ provenanceClient?: string;
126
+ /** When set, used as alt for every file; else each file's basename. */
127
+ alt?: string;
128
+ width?: number;
129
+ concurrency?: number;
130
+ }): Promise<{
131
+ uploads: PutUploadItem[];
132
+ failures: AttachFailure[];
133
+ firstError?: unknown;
134
+ }>;
45
135
  export declare function runAttach(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
46
136
  export declare function runPut(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
47
137
  export declare function runGallery(ctx: CliContext, args: string[], help?: boolean): Promise<number>;