@buildinternet/uploads 0.11.1 → 0.12.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.
@@ -6,6 +6,7 @@ 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
+ import type { DetectRoots } from "./screenshot-local.js";
9
10
  /** Parallel fan-out for multi-file put/attach (matches files-sdk bulk default). */
10
11
  export declare const UPLOAD_BATCH_CONCURRENCY = 8;
11
12
  /** @deprecated Use UPLOAD_BATCH_CONCURRENCY. */
@@ -25,6 +26,8 @@ export declare function readFileArg(fileArg: string): Uint8Array;
25
26
  * neither is present. Shared by the CLI flags and the MCP tool arguments.
26
27
  */
27
28
  export declare function makeGhTarget(pr: number | undefined, issue: number | undefined, repoArg: string | undefined, run: CommandRunner): GhTarget | undefined;
29
+ /** Reads --pr/--issue (+ --repo) into a GhTarget; undefined when neither flag is present. */
30
+ export declare function ghTargetFromFlags(flags: CommandFlags["flags"], run: CommandRunner): GhTarget | undefined;
28
31
  /** Shared put/attach optimize flags + UPLOADS_NO_OPTIMIZE default. */
29
32
  export declare function optimizeOptionsFromFlags(flags: CommandFlags["flags"], defaults: PutDefaults): OptimizeImageOptions;
30
33
  export type PreparedUpload = OptimizeImageResult & {
@@ -37,6 +40,53 @@ export declare function prepareImageForUpload(bytes: Uint8Array, filename: strin
37
40
  frameFit?: "cover" | "contain";
38
41
  optimize: OptimizeImageOptions;
39
42
  }): Promise<PreparedUpload>;
43
+ export interface UploadPreparedImageOptions {
44
+ frame: {
45
+ frameId?: string;
46
+ frameUrl?: string;
47
+ frameFit?: "cover" | "contain";
48
+ };
49
+ optimize: OptimizeImageOptions;
50
+ /** gh attachment key wins over `key` when both are set (matches every call site). */
51
+ ghTarget?: GhTarget;
52
+ key?: string;
53
+ prefix?: string;
54
+ repo?: string;
55
+ ref?: string;
56
+ deriveRepoFromGit?: boolean;
57
+ contentType?: string;
58
+ dryRun?: boolean;
59
+ metadata?: Record<string, string>;
60
+ provenanceClient?: string;
61
+ /**
62
+ * Alt text for the markdown. Takes the prepared result so callers whose
63
+ * default depends on the post-frame/optimize filename can use it — each
64
+ * call site's existing default is preserved verbatim (see
65
+ * .context/2026-07-16-screenshot-command-RESULT.md, "Simplify pass").
66
+ */
67
+ alt: (prepared: PreparedUpload) => string;
68
+ width?: number;
69
+ }
70
+ export interface UploadPreparedImageResult {
71
+ result: PutResult;
72
+ prepared: PreparedUpload;
73
+ markdown: string;
74
+ }
75
+ /**
76
+ * Shared bytes-oriented upload tail: frame + optimize the bytes, resolve the
77
+ * object key (gh attachment key wins over an explicit key; extension
78
+ * rewritten post-optimize), put, and build the GitHub embed markdown. Used by
79
+ * the screenshot CLI command, the MCP screenshot tool, and the MCP put
80
+ * tool's contentBase64 path — the three in-memory-bytes call sites.
81
+ * uploadPuts/uploadAttachments loop over file paths with their own bounded
82
+ * concurrency and delegate here per item.
83
+ */
84
+ export declare function uploadPreparedImage(client: UploadsClient, bytes: Uint8Array, sourceName: string, opts: UploadPreparedImageOptions): Promise<UploadPreparedImageResult>;
85
+ export declare function frameOptionsFromFlags(flags: CommandFlags["flags"]): {
86
+ frameId?: string;
87
+ frameUrl?: string;
88
+ frameFit?: "cover" | "contain";
89
+ };
40
90
  /**
41
91
  * List every attachment under the target's prefix and create/update the
42
92
  * managed comment. Throws on gh failure — callers decide whether that is
@@ -174,7 +224,27 @@ export interface DoctorReport {
174
224
  /** Workspace/token mismatch warning (also present in hints). */
175
225
  warning?: string;
176
226
  hints: string[];
227
+ /** `screenshot`'s local-browser detection (fs scans only — never launches a browser). */
228
+ browser: {
229
+ /** false when this runtime has no Node fs/process (e.g. the apps/mcp Worker). */
230
+ supported: boolean;
231
+ found: boolean;
232
+ /** Which backend `uploads screenshot --via auto` would pick right now. */
233
+ autoBackend: "local" | "remote";
234
+ candidates: {
235
+ source: string;
236
+ kind: string;
237
+ executablePath: string;
238
+ }[];
239
+ /** The best candidate by rank (may differ from candidates[0], which is scan order). */
240
+ winner?: {
241
+ source: string;
242
+ kind: string;
243
+ executablePath: string;
244
+ };
245
+ note?: string;
246
+ };
177
247
  }
178
248
  /** Doctor's health + auth + workspace checks, shared by the CLI and the MCP tool. */
179
- export declare function buildDoctorReport(config: ResolvedConfig, client: UploadsClient): Promise<DoctorReport>;
249
+ export declare function buildDoctorReport(config: ResolvedConfig, client: UploadsClient, detectRoots?: DetectRoots): Promise<DoctorReport>;
180
250
  export declare function runDoctor(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
package/dist/commands.js CHANGED
@@ -125,7 +125,7 @@ export function makeGhTarget(pr, issue, repoArg, run) {
125
125
  return { repo, kind: pr !== undefined ? "pull" : "issues", num: (pr ?? issue) };
126
126
  }
127
127
  /** Reads --pr/--issue (+ --repo) into a GhTarget; undefined when neither flag is present. */
128
- function ghTargetFromFlags(flags, run) {
128
+ export function ghTargetFromFlags(flags, run) {
129
129
  return makeGhTarget(flagInt(flags, "--pr", "--pr"), flagInt(flags, "--issue", "--issue"), flagString(flags, "--repo"), run);
130
130
  }
131
131
  /**
@@ -205,7 +205,50 @@ export async function prepareImageForUpload(bytes, filename, opts) {
205
205
  const optimized = await optimizeImageForUpload(currentBytes, currentName, opts.optimize);
206
206
  return { ...optimized, frame: frameMeta };
207
207
  }
208
- function frameOptionsFromFlags(flags) {
208
+ /**
209
+ * Shared bytes-oriented upload tail: frame + optimize the bytes, resolve the
210
+ * object key (gh attachment key wins over an explicit key; extension
211
+ * rewritten post-optimize), put, and build the GitHub embed markdown. Used by
212
+ * the screenshot CLI command, the MCP screenshot tool, and the MCP put
213
+ * tool's contentBase64 path — the three in-memory-bytes call sites.
214
+ * uploadPuts/uploadAttachments loop over file paths with their own bounded
215
+ * concurrency and delegate here per item.
216
+ */
217
+ export async function uploadPreparedImage(client, bytes, sourceName, opts) {
218
+ const prepared = await prepareImageForUpload(bytes, sourceName, {
219
+ frameId: opts.frame.frameId,
220
+ frameUrl: opts.frame.frameUrl,
221
+ frameFit: opts.frame.frameFit,
222
+ optimize: opts.optimize,
223
+ });
224
+ let key = opts.ghTarget ? ghAttachmentKey(opts.ghTarget, prepared.filename) : opts.key;
225
+ if (key && prepared.optimized)
226
+ key = rewriteKeyExtension(key, prepared.filename);
227
+ const result = await client.put(prepared.bytes, {
228
+ filename: prepared.filename,
229
+ key,
230
+ prefix: opts.prefix,
231
+ repo: opts.repo,
232
+ ref: opts.ref,
233
+ contentType: prepared.optimized ? prepared.contentType : opts.contentType,
234
+ deriveRepoFromGit: opts.deriveRepoFromGit,
235
+ dryRun: opts.dryRun,
236
+ provenance: buildCliProvenance({
237
+ sourceName,
238
+ client: opts.provenanceClient,
239
+ optimized: prepared.optimized,
240
+ frameId: prepared.frame?.framed ? prepared.frame.frameId : undefined,
241
+ keepExif: opts.optimize.keepExif === true,
242
+ }),
243
+ metadata: opts.metadata,
244
+ });
245
+ const markdown = buildMarkdown(urlForGithubEmbed(result.url, result.embedUrl), {
246
+ alt: opts.alt(prepared),
247
+ width: opts.width,
248
+ });
249
+ return { result, prepared, markdown };
250
+ }
251
+ export function frameOptionsFromFlags(flags) {
209
252
  const raw = flagString(flags, "--frame");
210
253
  let frameId;
211
254
  try {
@@ -415,43 +458,28 @@ export async function uploadPuts(opts) {
415
458
  ? basename(opts.explicitKey)
416
459
  : "stdin.bin"
417
460
  : basename(file));
418
- const prepared = await prepareImageForUpload(readFileArg(file), sourceName, {
419
- ...opts.frame,
461
+ const { result, prepared, markdown } = await uploadPreparedImage(opts.client, readFileArg(file), sourceName, {
462
+ frame: opts.frame,
420
463
  optimize: opts.optimize,
421
- });
422
- let key = opts.ghTarget
423
- ? ghAttachmentKey(opts.ghTarget, prepared.filename)
424
- : opts.explicitKey;
425
- if (key && prepared.optimized)
426
- key = rewriteKeyExtension(key, prepared.filename);
427
- const result = await opts.client.put(prepared.bytes, {
428
- filename: prepared.filename,
429
- key,
464
+ ghTarget: opts.ghTarget,
465
+ key: opts.explicitKey,
430
466
  prefix: opts.prefix,
431
467
  repo: opts.repo,
432
468
  ref: opts.ref,
433
- contentType: prepared.optimized ? prepared.contentType : opts.contentType,
434
469
  deriveRepoFromGit: opts.deriveRepoFromGit,
470
+ contentType: opts.contentType,
435
471
  dryRun: opts.dryRun,
436
- provenance: buildCliProvenance({
437
- sourceName,
438
- client: opts.provenanceClient,
439
- optimized: prepared.optimized,
440
- frameId: prepared.frame?.framed ? prepared.frame.frameId : undefined,
441
- keepExif: opts.optimize.keepExif === true,
442
- }),
443
472
  metadata: opts.metadata,
473
+ provenanceClient: opts.provenanceClient,
474
+ alt: () => opts.alt ?? basename(sourceName),
475
+ width: opts.width,
444
476
  });
445
- const alt = opts.alt ?? basename(sourceName);
446
477
  return {
447
478
  ok: true,
448
479
  upload: {
449
480
  ...result,
450
481
  file,
451
- markdown: buildMarkdown(urlForGithubEmbed(result.url, result.embedUrl), {
452
- alt,
453
- width: opts.width,
454
- }),
482
+ markdown,
455
483
  optimize: {
456
484
  optimized: prepared.optimized,
457
485
  skippedReason: prepared.skippedReason,
@@ -1434,8 +1462,52 @@ Examples:
1434
1462
  uploads --workspace acme --env-file .env doctor
1435
1463
  uploads doctor --json
1436
1464
  `;
1465
+ /**
1466
+ * Best-effort local-browser detection for doctor. fs scans only, no browser
1467
+ * launch. Guarded for non-Node runtimes as a precaution for any future
1468
+ * non-Node consumer of this module — apps/mcp today only imports
1469
+ * `buildMarkdown`/`buildScreenshotKey` from the package root, not
1470
+ * `buildDoctorReport`, so this guard isn't exercised on that path currently.
1471
+ */
1472
+ async function detectBrowserForDoctor(detectRoots) {
1473
+ if (typeof process === "undefined" || !process.versions?.node) {
1474
+ return {
1475
+ supported: false,
1476
+ found: false,
1477
+ autoBackend: "remote",
1478
+ candidates: [],
1479
+ note: "browser detection is not supported in this runtime",
1480
+ };
1481
+ }
1482
+ try {
1483
+ const { detectLocalBrowser } = await import("./screenshot-local.js");
1484
+ const { candidates, winner } = detectLocalBrowser(detectRoots);
1485
+ return {
1486
+ supported: true,
1487
+ found: Boolean(winner),
1488
+ autoBackend: winner ? "local" : "remote",
1489
+ candidates: candidates.map((c) => ({
1490
+ source: c.source,
1491
+ kind: c.kind,
1492
+ executablePath: c.executablePath,
1493
+ })),
1494
+ winner: winner
1495
+ ? { source: winner.source, kind: winner.kind, executablePath: winner.executablePath }
1496
+ : undefined,
1497
+ };
1498
+ }
1499
+ catch (err) {
1500
+ return {
1501
+ supported: true,
1502
+ found: false,
1503
+ autoBackend: "remote",
1504
+ candidates: [],
1505
+ note: err instanceof Error ? err.message : String(err),
1506
+ };
1507
+ }
1508
+ }
1437
1509
  /** Doctor's health + auth + workspace checks, shared by the CLI and the MCP tool. */
1438
- export async function buildDoctorReport(config, client) {
1510
+ export async function buildDoctorReport(config, client, detectRoots) {
1439
1511
  const mismatch = workspaceMismatch(config);
1440
1512
  const hints = [];
1441
1513
  if (mismatch)
@@ -1443,7 +1515,12 @@ export async function buildDoctorReport(config, client) {
1443
1515
  if (config.apiUrl.includes("localhost") || config.apiUrl.includes("127.0.0.1")) {
1444
1516
  hints.push("local API uses dev KV — prod tokens won't work unless minted with --local");
1445
1517
  }
1446
- const health = await client.health();
1518
+ // Independent checks fs-based browser detection doesn't depend on the
1519
+ // network health probe (or vice versa).
1520
+ const [browser, health] = await Promise.all([
1521
+ detectBrowserForDoctor(detectRoots),
1522
+ client.health(),
1523
+ ]);
1447
1524
  let authOk = false;
1448
1525
  let authError;
1449
1526
  try {
@@ -1491,6 +1568,7 @@ export async function buildDoctorReport(config, client) {
1491
1568
  usage,
1492
1569
  warning: mismatch,
1493
1570
  hints,
1571
+ browser,
1494
1572
  };
1495
1573
  }
1496
1574
  export async function runDoctor(ctx, args, help = false) {
@@ -1515,6 +1593,14 @@ export async function runDoctor(ctx, args, help = false) {
1515
1593
  ? `usage: ${formatByteSize(report.usage.bytes ?? 0)}, ${formatCount(report.usage.objects ?? 0)} objects, ${formatCount(report.usage.uploadsInPeriod ?? 0)} uploads this period`
1516
1594
  : `usage: failed — ${report.usage.error ?? "unknown"}`);
1517
1595
  }
1596
+ if (report.browser.supported) {
1597
+ lines.push(report.browser.found
1598
+ ? `browser: found (${report.browser.winner?.source}/${report.browser.winner?.kind}) — screenshot --via auto uses local`
1599
+ : `browser: none found — screenshot --via auto uses remote`);
1600
+ }
1601
+ else {
1602
+ lines.push(`browser: ${report.browser.note ?? "not supported in this runtime"}`);
1603
+ }
1518
1604
  if (report.warning)
1519
1605
  lines.push(`warning: ${report.warning}`);
1520
1606
  for (const h of report.hints)
@@ -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;
package/dist/github-gh.js CHANGED
@@ -42,9 +42,15 @@ export function resolveRepo(explicit, run = execRunner) {
42
42
  /** Resolve the pull request associated with the current branch. */
43
43
  export function resolveCurrentPullRequest(repo, run = execRunner) {
44
44
  try {
45
+ // `gh pr view --repo` requires an explicit selector (it refuses to infer
46
+ // from the current branch), so pass the branch name as the selector.
47
+ const branch = run("git", ["rev-parse", "--abbrev-ref", "HEAD"]).trim();
48
+ if (branch === "" || branch === "HEAD")
49
+ throw new Error("detached HEAD");
45
50
  const out = run("gh", [
46
51
  "pr",
47
52
  "view",
53
+ branch,
48
54
  "--repo",
49
55
  repo,
50
56
  "--json",
@@ -1,3 +1,10 @@
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";