@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.
- package/README.md +12 -1
- package/dist/cli-catalog.d.ts +7 -0
- package/dist/cli-catalog.js +51 -0
- 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/screenshot.d.ts +6 -0
- package/dist/commands/screenshot.js +317 -0
- package/dist/commands.d.ts +71 -1
- package/dist/commands.js +114 -28
- 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/github-gh.js +6 -0
- package/dist/mcp/tools.d.ts +7 -0
- package/dist/mcp/tools.js +250 -27
- 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/dist/commands.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
419
|
-
|
|
461
|
+
const { result, prepared, markdown } = await uploadPreparedImage(opts.client, readFileArg(file), sourceName, {
|
|
462
|
+
frame: opts.frame,
|
|
420
463
|
optimize: opts.optimize,
|
|
421
|
-
|
|
422
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
package/dist/config-file.d.ts
CHANGED
|
@@ -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?: {
|
package/dist/config-file.js
CHANGED
|
@@ -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 =
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return
|
|
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",
|
package/dist/mcp/tools.d.ts
CHANGED
|
@@ -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";
|