@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.
@@ -1,5 +1,5 @@
1
1
  import type { UploadsClientConfig } from "./config.js";
2
- export declare const UPLOADS_CONFIG_KEYS: readonly ["UPLOADS_API_URL", "UPLOADS_WORKSPACE", "UPLOADS_TOKEN", "UPLOADS_DEFAULT_PREFIX", "UPLOADS_DEFAULT_REPO", "UPLOADS_DEFAULT_REF", "UPLOADS_DEFAULT_WIDTH", "UPLOADS_NO_GIT", "UPLOADS_NO_OPTIMIZE", "UPLOADS_KEEP_EXIF", "UPLOADS_NO_AUTO_META"];
2
+ export declare const UPLOADS_CONFIG_KEYS: readonly ["UPLOADS_API_URL", "UPLOADS_WORKSPACE", "UPLOADS_TOKEN", "UPLOADS_DEFAULT_PREFIX", "UPLOADS_DEFAULT_REPO", "UPLOADS_DEFAULT_REF", "UPLOADS_DEFAULT_WIDTH", "UPLOADS_NO_GIT", "UPLOADS_NO_OPTIMIZE", "UPLOADS_KEEP_EXIF", "UPLOADS_NO_AUTO_META", "UPLOADS_SCREENSHOT_VIA"];
3
3
  export type UploadsConfigKey = (typeof UPLOADS_CONFIG_KEYS)[number];
4
4
  export type UploadsConfigValues = Partial<Record<UploadsConfigKey, string>>;
5
5
  export interface PutDefaults {
@@ -26,10 +26,36 @@ export declare function resolveConfigPath(flags?: {
26
26
  /** Parse UPLOADS_* keys from a dotenv-style file. Missing file → empty object. */
27
27
  export declare function loadConfigFile(path: string): UploadsConfigValues;
28
28
  export declare function mergePutDefaults(...layers: PutDefaults[]): PutDefaults;
29
+ /** Raw `--env-file` / user-config-file contents, keyed the way both `resolve*Defaults` need. */
30
+ export interface RawDefaultsConfig {
31
+ fromEnvFile: UploadsConfigValues;
32
+ fromUser: UploadsConfigValues;
33
+ }
34
+ /**
35
+ * Read the on-disk config once (`--env-file` when given, else the user
36
+ * config file) so callers that need both put-style and screenshot-style
37
+ * defaults from the same invocation (e.g. `runScreenshot`) don't each read
38
+ * the file separately. Pass the result to `resolvePutDefaults` /
39
+ * `resolveScreenshotDefaults` as their second argument.
40
+ */
41
+ export declare function loadDefaultsRaw(flags?: {
42
+ envFile?: string;
43
+ }): RawDefaultsConfig;
29
44
  /** Put defaults from env, optional env-file, and user config (same precedence as client config). */
30
45
  export declare function resolvePutDefaults(flags?: {
31
46
  envFile?: string;
32
- }): PutDefaults;
47
+ }, preloaded?: RawDefaultsConfig): PutDefaults;
48
+ export type ScreenshotBackendPref = "auto" | "local" | "remote";
49
+ export interface ScreenshotDefaults {
50
+ via?: ScreenshotBackendPref;
51
+ }
52
+ /**
53
+ * `screenshot --via` default: flag (applied by the caller) > env >
54
+ * --env-file > user config file > "auto" (the caller's own fallback).
55
+ */
56
+ export declare function resolveScreenshotDefaults(flags?: {
57
+ envFile?: string;
58
+ }, preloaded?: RawDefaultsConfig): ScreenshotDefaults;
33
59
  export declare function redactToken(token: string | undefined): string;
34
60
  /** Create or update UPLOADS_* keys in the shared config file. Preserves other keys. */
35
61
  export declare function writeConfigKeys(path: string, keys: UploadsConfigValues, opts?: {
@@ -13,6 +13,7 @@ export const UPLOADS_CONFIG_KEYS = [
13
13
  "UPLOADS_NO_OPTIMIZE",
14
14
  "UPLOADS_KEEP_EXIF",
15
15
  "UPLOADS_NO_AUTO_META",
16
+ "UPLOADS_SCREENSHOT_VIA",
16
17
  ];
17
18
  const PUT_DEFAULT_KEY_MAP = {
18
19
  prefix: "UPLOADS_DEFAULT_PREFIX",
@@ -165,14 +166,51 @@ export function mergePutDefaults(...layers) {
165
166
  }
166
167
  return out;
167
168
  }
169
+ /**
170
+ * Read the on-disk config once (`--env-file` when given, else the user
171
+ * config file) so callers that need both put-style and screenshot-style
172
+ * defaults from the same invocation (e.g. `runScreenshot`) don't each read
173
+ * the file separately. Pass the result to `resolvePutDefaults` /
174
+ * `resolveScreenshotDefaults` as their second argument.
175
+ */
176
+ export function loadDefaultsRaw(flags) {
177
+ const fromEnvFile = flags?.envFile ? loadConfigFile(flags.envFile) : {};
178
+ const fromUser = flags?.envFile ? {} : loadConfigFile(resolveConfigPath(flags));
179
+ return { fromEnvFile, fromUser };
180
+ }
168
181
  /** Put defaults from env, optional env-file, and user config (same precedence as client config). */
169
- export function resolvePutDefaults(flags) {
182
+ export function resolvePutDefaults(flags, preloaded) {
170
183
  const fromEnv = parsePutDefaultsFromEnv();
171
- const fromEnvFile = flags?.envFile ? parsePutDefaultsFromRaw(loadConfigFile(flags.envFile)) : {};
172
- const fromUser = flags?.envFile
173
- ? {}
174
- : parsePutDefaultsFromRaw(loadConfigFile(resolveConfigPath(flags)));
175
- return mergePutDefaults(fromUser, fromEnvFile, fromEnv);
184
+ const { fromEnvFile, fromUser } = preloaded ?? loadDefaultsRaw(flags);
185
+ return mergePutDefaults(parsePutDefaultsFromRaw(fromUser), parsePutDefaultsFromRaw(fromEnvFile), fromEnv);
186
+ }
187
+ function isScreenshotBackendPref(value) {
188
+ return value === "auto" || value === "local" || value === "remote";
189
+ }
190
+ function parseScreenshotDefaultsFromRaw(raw) {
191
+ const out = {};
192
+ if (isScreenshotBackendPref(raw.UPLOADS_SCREENSHOT_VIA))
193
+ out.via = raw.UPLOADS_SCREENSHOT_VIA;
194
+ return out;
195
+ }
196
+ function parseScreenshotDefaultsFromEnv() {
197
+ const raw = {};
198
+ if (process.env.UPLOADS_SCREENSHOT_VIA)
199
+ raw.UPLOADS_SCREENSHOT_VIA = process.env.UPLOADS_SCREENSHOT_VIA;
200
+ return parseScreenshotDefaultsFromRaw(raw);
201
+ }
202
+ /**
203
+ * `screenshot --via` default: flag (applied by the caller) > env >
204
+ * --env-file > user config file > "auto" (the caller's own fallback).
205
+ */
206
+ export function resolveScreenshotDefaults(flags, preloaded) {
207
+ const fromEnv = parseScreenshotDefaultsFromEnv();
208
+ const { fromEnvFile, fromUser } = preloaded ?? loadDefaultsRaw(flags);
209
+ return {
210
+ ...parseScreenshotDefaultsFromRaw(fromUser),
211
+ ...parseScreenshotDefaultsFromRaw(fromEnvFile),
212
+ ...fromEnv,
213
+ };
176
214
  }
177
215
  export function redactToken(token) {
178
216
  if (!token)
package/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { defaultConfigPath, resolveConfigPath, loadConfigFile, redactToken, writeConfigKeys, removeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, UPLOADS_CONFIG_KEYS, type UploadsConfigKey, type UploadsConfigValues, type PutDefaults, } from "./config-file.js";
1
+ export { defaultConfigPath, resolveConfigPath, loadConfigFile, redactToken, writeConfigKeys, removeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, resolveScreenshotDefaults, UPLOADS_CONFIG_KEYS, type UploadsConfigKey, type UploadsConfigValues, type PutDefaults, type ScreenshotDefaults, type ScreenshotBackendPref, } from "./config-file.js";
2
2
  export interface UploadsClientConfig {
3
3
  apiUrl: string;
4
4
  workspace: string;
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { loadConfigFile, resolveConfigPath } from "./config-file.js";
3
3
  import { UploadsError } from "./errors.js";
4
- export { defaultConfigPath, resolveConfigPath, loadConfigFile, redactToken, writeConfigKeys, removeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, UPLOADS_CONFIG_KEYS, } from "./config-file.js";
4
+ export { defaultConfigPath, resolveConfigPath, loadConfigFile, redactToken, writeConfigKeys, removeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, resolveScreenshotDefaults, UPLOADS_CONFIG_KEYS, } from "./config-file.js";
5
5
  export const DEFAULT_API_URL = "https://api.uploads.sh";
6
6
  export const DEFAULT_WORKSPACE = "default";
7
7
  const TOKEN_WORKSPACE_RE = /^up_([a-z0-9][a-z0-9-]{1,62})_/;
package/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type UploadsErrorCode = "MISSING_TOKEN" | "NO_PUBLIC_URL" | "FILE_NOT_FOUND" | "NOT_FOUND" | "UNAUTHORIZED" | "INVALID_KEY" | "KEY_POLICY" | "STORAGE_QUOTA" | "UPLOAD_BUDGET" | "GITHUB_REQUIRED" | "API_ERROR" | "NETWORK" | "USAGE";
1
+ export type UploadsErrorCode = "MISSING_TOKEN" | "NO_PUBLIC_URL" | "FILE_NOT_FOUND" | "NOT_FOUND" | "UNAUTHORIZED" | "INVALID_KEY" | "KEY_POLICY" | "STORAGE_QUOTA" | "UPLOAD_BUDGET" | "GITHUB_REQUIRED" | "API_ERROR" | "NETWORK" | "USAGE" | "BROWSER_NOT_FOUND" | "RENDER_FAILED" | "RATE_LIMITED";
2
2
  export declare class UploadsError extends Error {
3
3
  readonly code: UploadsErrorCode;
4
4
  readonly status?: number;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Tool handler failure that still carries structuredContent (e.g. multi-file
3
+ * total failure with a `failures` array). The MCP server maps this to
4
+ * isError: true while preserving structuredContent for agents.
5
+ */
6
+ export declare class ToolBatchError extends Error {
7
+ readonly structuredContent: unknown;
8
+ constructor(message: string, structuredContent: unknown);
9
+ }
10
+ /** One-line summary of a multi-file failure list. */
11
+ export declare function batchFailureMessage(failures: readonly {
12
+ file: string;
13
+ error: {
14
+ message: string;
15
+ };
16
+ }[]): string;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Tool handler failure that still carries structuredContent (e.g. multi-file
3
+ * total failure with a `failures` array). The MCP server maps this to
4
+ * isError: true while preserving structuredContent for agents.
5
+ */
6
+ export class ToolBatchError extends Error {
7
+ structuredContent;
8
+ constructor(message, structuredContent) {
9
+ super(message);
10
+ this.name = "ToolBatchError";
11
+ this.structuredContent = structuredContent;
12
+ }
13
+ }
14
+ /** One-line summary of a multi-file failure list. */
15
+ export function batchFailureMessage(failures) {
16
+ if (failures.length === 0)
17
+ return "upload failed";
18
+ if (failures.length === 1) {
19
+ const f = failures[0];
20
+ return `${f.file}: ${f.error.message}`;
21
+ }
22
+ const lines = failures.map((f) => ` ${f.file}: ${f.error.message}`);
23
+ return `${failures.length} uploads failed:\n${lines.join("\n")}`;
24
+ }
@@ -1,4 +1,5 @@
1
1
  export { METADATA_DESCRIPTION, metadataProp, optPosInt, optString, optStringArray, optStringRecord, usage, type ToolArgs, } from "./args.js";
2
+ export { ToolBatchError, batchFailureMessage } from "./batch-error.js";
2
3
  export interface McpTool {
3
4
  name: string;
4
5
  description: string;
@@ -9,7 +9,9 @@
9
9
  */
10
10
  import { UploadsError } from "../errors.js";
11
11
  import { errorCodeFromUnknown, recordEvent } from "../telemetry.js";
12
+ import { ToolBatchError } from "./batch-error.js";
12
13
  export { METADATA_DESCRIPTION, metadataProp, optPosInt, optString, optStringArray, optStringRecord, usage, } from "./args.js";
14
+ export { ToolBatchError, batchFailureMessage } from "./batch-error.js";
13
15
  const SUPPORTED_PROTOCOL_VERSIONS = new Set(["2025-06-18", "2025-03-26", "2024-11-05"]);
14
16
  const LATEST_PROTOCOL_VERSION = "2025-06-18";
15
17
  function response(id, result) {
@@ -59,6 +61,20 @@ export function createMcpServer(opts) {
59
61
  durationMs: Date.now() - start,
60
62
  errorCode: errorCodeFromUnknown(err),
61
63
  }, { apiUrl });
64
+ // Multi-file total failure: keep structuredContent so agents see every
65
+ // per-file error, not only the first message string.
66
+ if (err instanceof ToolBatchError) {
67
+ return response(id, {
68
+ content: [
69
+ {
70
+ type: "text",
71
+ text: JSON.stringify(err.structuredContent, null, 2),
72
+ },
73
+ ],
74
+ structuredContent: err.structuredContent,
75
+ isError: true,
76
+ });
77
+ }
62
78
  return response(id, {
63
79
  content: [{ type: "text", text: toolErrorText(err) }],
64
80
  isError: true,
@@ -1,8 +1,15 @@
1
+ /**
2
+ * MCP tool set mirroring the CLI commands (put, attach, list, delete,
3
+ * usage, reconcile, purge_expired, comment, health, doctor). Config is
4
+ * resolved fresh per tool call so a
5
+ * per-call `workspace` argument behaves like the CLI's --workspace flag, and
6
+ * a missing token surfaces as a tool error rather than a startup failure.
7
+ */
1
8
  import type { GlobalFlags } from "../cli-args.js";
2
9
  import { type UploadsClient } from "../client.js";
3
10
  import { type UploadsClientConfig } from "../config.js";
4
11
  import { type CommandRunner } from "../github-gh.js";
5
- import type { McpTool } from "./server.js";
12
+ import { type McpTool } from "./server.js";
6
13
  export declare function createUploadsMcpTools(opts: {
7
14
  globals: GlobalFlags;
8
15
  runner?: CommandRunner;