@buildinternet/uploads 0.14.0 → 0.16.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 +11 -0
- package/dist/cli-catalog.js +10 -0
- package/dist/cli.js +6 -2
- package/dist/client.d.ts +89 -2
- package/dist/client.js +49 -0
- package/dist/commands/screenshot.js +34 -5
- package/dist/commands.d.ts +55 -1
- package/dist/commands.js +507 -22
- package/dist/github-gh.d.ts +9 -1
- package/dist/github-gh.js +35 -3
- package/dist/github.d.ts +35 -1
- package/dist/github.js +88 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp/tools.js +11 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,17 @@ infers the pull request for the current branch via `gh`, uploads stable URLs, an
|
|
|
83
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
|
|
84
84
|
the target explicitly, or `--no-comment` to upload without changing GitHub comments.
|
|
85
85
|
|
|
86
|
+
**Branch staging (pre-PR):** `attach <files> --branch [name]` (also on
|
|
87
|
+
`screenshot`) stages files against a git branch before any PR exists — the
|
|
88
|
+
working mode for coding agents capturing as they go. Staged files live under
|
|
89
|
+
`gh/<owner>/<repo>/branch/<branch>/…` and are promoted into the PR's
|
|
90
|
+
attachments when one opens: automatically via the
|
|
91
|
+
[GitHub App](https://uploads.sh/docs/github-app) webhook, or on the first
|
|
92
|
+
`attach` after the PR exists (`--promote` forces it with no new files,
|
|
93
|
+
`--no-promote` opts out). `uploads github link` inspects or claims the
|
|
94
|
+
workspace↔repo binding the webhook path uses. Promoted staging is cleaned up
|
|
95
|
+
server-side after ~7 days (~30 for branches that never got a PR).
|
|
96
|
+
|
|
86
97
|
**Screenshot capture:** `uploads screenshot <url|file.html>` renders a page to a
|
|
87
98
|
hosted image in one step — no separate browser tooling needed. `--via auto`
|
|
88
99
|
(default) drives a Chrome/Chromium already on the machine (`playwright-core`
|
package/dist/cli-catalog.js
CHANGED
|
@@ -25,6 +25,7 @@ export const PUT_LIKE_FLAGS = [
|
|
|
25
25
|
"--ref",
|
|
26
26
|
"--pr",
|
|
27
27
|
"--issue",
|
|
28
|
+
"--branch",
|
|
28
29
|
"--comment",
|
|
29
30
|
"--no-comment",
|
|
30
31
|
"--format",
|
|
@@ -75,6 +76,7 @@ export const SCREENSHOT_FLAGS = [
|
|
|
75
76
|
"--no-git",
|
|
76
77
|
"--pr",
|
|
77
78
|
"--issue",
|
|
79
|
+
"--branch",
|
|
78
80
|
"--comment",
|
|
79
81
|
"--gallery",
|
|
80
82
|
"--meta",
|
|
@@ -131,6 +133,14 @@ export const ROOT_COMMANDS = [
|
|
|
131
133
|
name: "comment",
|
|
132
134
|
summary: "Create/update a PR/issue attachments comment (via gh)",
|
|
133
135
|
},
|
|
136
|
+
{
|
|
137
|
+
name: "github",
|
|
138
|
+
summary: "Claim/inspect this workspace's binding to a GitHub repo",
|
|
139
|
+
subcommands: [
|
|
140
|
+
{ name: "link", summary: "Claim or inspect the repo binding" },
|
|
141
|
+
{ name: "doctor", summary: "Check the GitHub App's webhook event subscriptions" },
|
|
142
|
+
],
|
|
143
|
+
},
|
|
134
144
|
{
|
|
135
145
|
name: "list",
|
|
136
146
|
summary: "List objects (--meta k=v filters by queryable metadata)",
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import { UploadsError } from "./errors.js";
|
|
|
4
4
|
import { commandWorkspace, flagString, isHelpFlag, parseArgv, parseCommandArgs, UsageError, } from "./cli-args.js";
|
|
5
5
|
import { formatRootHelp, wantsFullHelp } from "./cli-help.js";
|
|
6
6
|
import { colorEnabled, createStyle } from "./cli-style.js";
|
|
7
|
-
import { runPut, runAttach, runList, runFind, runMeta, runDelete, runHealth, runDoctor, runComment, runUsage, runReconcile, runPurgeExpired, runGallery, } from "./commands.js";
|
|
7
|
+
import { runPut, runAttach, runList, runFind, runMeta, runDelete, runHealth, runDoctor, runComment, runGithub, runUsage, runReconcile, runPurgeExpired, runGallery, } from "./commands.js";
|
|
8
8
|
import { runConfig } from "./commands/config.js";
|
|
9
9
|
import { runSetup } from "./commands/setup.js";
|
|
10
10
|
import { runLogin } from "./commands/login.js";
|
|
@@ -278,7 +278,8 @@ export async function runCli(argv) {
|
|
|
278
278
|
case "reconcile":
|
|
279
279
|
case "purge-expired":
|
|
280
280
|
case "doctor":
|
|
281
|
-
case "comment":
|
|
281
|
+
case "comment":
|
|
282
|
+
case "github": {
|
|
282
283
|
const ctx = createContext(parsed.globals, !showHelp, cmdArgs);
|
|
283
284
|
switch (parsed.command) {
|
|
284
285
|
case "attach":
|
|
@@ -296,6 +297,9 @@ export async function runCli(argv) {
|
|
|
296
297
|
case "comment":
|
|
297
298
|
code = await runComment(ctx, cmdArgs, showHelp);
|
|
298
299
|
break;
|
|
300
|
+
case "github":
|
|
301
|
+
code = await runGithub(ctx, cmdArgs, showHelp);
|
|
302
|
+
break;
|
|
299
303
|
case "list":
|
|
300
304
|
code = await runList(ctx, cmdArgs, showHelp);
|
|
301
305
|
break;
|
package/dist/client.d.ts
CHANGED
|
@@ -73,6 +73,8 @@ export interface ListItem {
|
|
|
73
73
|
key: string;
|
|
74
74
|
url: string | null;
|
|
75
75
|
embedUrl?: string | null;
|
|
76
|
+
/** Canonical `/f/` page URL when the API provides it. Absent on older API deployments. */
|
|
77
|
+
pageUrl?: string;
|
|
76
78
|
size?: number;
|
|
77
79
|
uploaded?: string;
|
|
78
80
|
}
|
|
@@ -170,8 +172,16 @@ export interface FindGalleriesByReferenceOptions {
|
|
|
170
172
|
limit?: number;
|
|
171
173
|
cursor?: string;
|
|
172
174
|
}
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Reasons the bot did not post. The CLI falls back to the local `gh` path
|
|
177
|
+
* for all of these except `not_authorized` (issue #297 baseline control):
|
|
178
|
+
* the target repo is bound to a different workspace (or is unbound and the
|
|
179
|
+
* caller is the communal `default` workspace, which can't claim new repos).
|
|
180
|
+
* Falling back to `gh` there would let the human's own credentials post
|
|
181
|
+
* anyway, defeating the point of the server-side gate, so the CLI surfaces
|
|
182
|
+
* the decline instead.
|
|
183
|
+
*/
|
|
184
|
+
export type GithubCommentDeclineReason = "app_unconfigured" | "not_installed" | "forbidden" | "not_authorized" | "unavailable";
|
|
175
185
|
export type GithubCommentResult = {
|
|
176
186
|
posted: true;
|
|
177
187
|
action: "created" | "updated" | "skipped";
|
|
@@ -180,10 +190,54 @@ export type GithubCommentResult = {
|
|
|
180
190
|
} | {
|
|
181
191
|
posted: false;
|
|
182
192
|
reason: GithubCommentDeclineReason;
|
|
193
|
+
message?: string;
|
|
194
|
+
fixUrl?: string;
|
|
195
|
+
required?: string[];
|
|
183
196
|
};
|
|
197
|
+
/** `POST /v1/:workspace/github/promote` request/response (server contract, PR #310). */
|
|
198
|
+
export interface PromoteBranchAttachmentsOptions {
|
|
199
|
+
repo: string;
|
|
200
|
+
num: number;
|
|
201
|
+
branch: string;
|
|
202
|
+
}
|
|
203
|
+
export interface PromoteSkip {
|
|
204
|
+
key: string;
|
|
205
|
+
reason: string;
|
|
206
|
+
}
|
|
207
|
+
export interface PromoteBranchAttachmentsResult {
|
|
208
|
+
promoted: string[];
|
|
209
|
+
skipped: PromoteSkip[];
|
|
210
|
+
}
|
|
211
|
+
/** `GET`/`POST /v1/:workspace/github/link` result (server contract, phase 4b). */
|
|
212
|
+
export interface GithubLinkResult {
|
|
213
|
+
repo: string;
|
|
214
|
+
linked: boolean;
|
|
215
|
+
workspace: string | null;
|
|
216
|
+
source: string | null;
|
|
217
|
+
createdAt: string | null;
|
|
218
|
+
}
|
|
219
|
+
/** POST-only: whether THIS call's workspace ended up owning the binding. */
|
|
220
|
+
export interface GithubLinkClaimResult extends GithubLinkResult {
|
|
221
|
+
claimed: boolean;
|
|
222
|
+
}
|
|
223
|
+
/** `DELETE /v1/:workspace/github/link` result (issue #318, self-serve unlink). */
|
|
224
|
+
export interface GithubLinkUnlinkResult {
|
|
225
|
+
repo: string;
|
|
226
|
+
unlinked: boolean;
|
|
227
|
+
reason?: "not_linked";
|
|
228
|
+
}
|
|
184
229
|
export interface HealthResult {
|
|
185
230
|
ok: boolean;
|
|
186
231
|
}
|
|
232
|
+
/** `GET /v1/:workspace/github/health` result (issue #293 follow-up). */
|
|
233
|
+
export interface GithubHealthResult {
|
|
234
|
+
configured: boolean;
|
|
235
|
+
ok: boolean;
|
|
236
|
+
events: string[] | null;
|
|
237
|
+
missingEvents: string[];
|
|
238
|
+
requiredEvents: string[];
|
|
239
|
+
hint?: string;
|
|
240
|
+
}
|
|
187
241
|
export interface UsageResult {
|
|
188
242
|
workspace: string;
|
|
189
243
|
bytes: number;
|
|
@@ -412,6 +466,39 @@ export declare function createUploadsClient(config: UploadsClientConfig): {
|
|
|
412
466
|
num: number;
|
|
413
467
|
kind: "pull" | "issues";
|
|
414
468
|
}): Promise<GithubCommentResult>;
|
|
469
|
+
/**
|
|
470
|
+
* Promote a workspace's branch-staged attachments into a PR's stable
|
|
471
|
+
* attachment prefix (server contract, PR #310 — degrade-safe callers
|
|
472
|
+
* treat any failure, including a 404 from an older/self-hosted worker
|
|
473
|
+
* that doesn't have this route yet, as "nothing promoted").
|
|
474
|
+
*/
|
|
475
|
+
promoteBranchAttachments(opts: PromoteBranchAttachmentsOptions): Promise<PromoteBranchAttachmentsResult>;
|
|
476
|
+
/** Current binding for `repo`, or `{ linked: false }` if unclaimed. Throws
|
|
477
|
+
* `UploadsError` (status 404) on an older/self-hosted server without this
|
|
478
|
+
* route — callers treat that as "bindings unsupported". */
|
|
479
|
+
githubLinkStatus(repo: string): Promise<GithubLinkResult>;
|
|
480
|
+
/**
|
|
481
|
+
* Explicitly claim `repo` for this workspace (first-claim-wins — see
|
|
482
|
+
* github-repo-links.ts server-side). `claimed: false` in the result means
|
|
483
|
+
* the repo is already bound to a DIFFERENT workspace; this call never
|
|
484
|
+
* steals it. Throws `UploadsError` (status 404) on an older/self-hosted
|
|
485
|
+
* server without this route.
|
|
486
|
+
*/
|
|
487
|
+
githubLinkClaim(repo: string): Promise<GithubLinkClaimResult>;
|
|
488
|
+
/**
|
|
489
|
+
* GitHub App configuration + webhook event subscription check. Throws
|
|
490
|
+
* `UploadsError` (status 404) on an older/self-hosted server without
|
|
491
|
+
* this route — callers treat that as "unknown", not "broken".
|
|
492
|
+
*/
|
|
493
|
+
githubHealth(): Promise<GithubHealthResult>;
|
|
494
|
+
/**
|
|
495
|
+
* Self-serve unlink (issue #318): removes `repo`'s binding, but only if
|
|
496
|
+
* this workspace currently owns it. Throws `UploadsError` (status 403)
|
|
497
|
+
* when a different workspace owns the binding — never steals or
|
|
498
|
+
* overwrites another workspace's claim. Throws (status 404) on an
|
|
499
|
+
* older/self-hosted server without this route.
|
|
500
|
+
*/
|
|
501
|
+
githubLinkUnlink(repo: string): Promise<GithubLinkUnlinkResult>;
|
|
415
502
|
health(): Promise<HealthResult>;
|
|
416
503
|
/** Workspace storage / upload counters (+ limits when configured). */
|
|
417
504
|
usage(): Promise<UsageResult>;
|
package/dist/client.js
CHANGED
|
@@ -458,6 +458,55 @@ export function createUploadsClient(config) {
|
|
|
458
458
|
headers: { "Content-Type": "application/json" },
|
|
459
459
|
});
|
|
460
460
|
},
|
|
461
|
+
/**
|
|
462
|
+
* Promote a workspace's branch-staged attachments into a PR's stable
|
|
463
|
+
* attachment prefix (server contract, PR #310 — degrade-safe callers
|
|
464
|
+
* treat any failure, including a 404 from an older/self-hosted worker
|
|
465
|
+
* that doesn't have this route yet, as "nothing promoted").
|
|
466
|
+
*/
|
|
467
|
+
async promoteBranchAttachments(opts) {
|
|
468
|
+
return request("POST", `${config.apiUrl}/v1/${encodeURIComponent(config.workspace)}/github/promote`, {
|
|
469
|
+
body: new TextEncoder().encode(JSON.stringify(opts)),
|
|
470
|
+
headers: { "Content-Type": "application/json" },
|
|
471
|
+
});
|
|
472
|
+
},
|
|
473
|
+
/** Current binding for `repo`, or `{ linked: false }` if unclaimed. Throws
|
|
474
|
+
* `UploadsError` (status 404) on an older/self-hosted server without this
|
|
475
|
+
* route — callers treat that as "bindings unsupported". */
|
|
476
|
+
async githubLinkStatus(repo) {
|
|
477
|
+
return request("GET", `${config.apiUrl}/v1/${encodeURIComponent(config.workspace)}/github/link?repo=${encodeURIComponent(repo)}`);
|
|
478
|
+
},
|
|
479
|
+
/**
|
|
480
|
+
* Explicitly claim `repo` for this workspace (first-claim-wins — see
|
|
481
|
+
* github-repo-links.ts server-side). `claimed: false` in the result means
|
|
482
|
+
* the repo is already bound to a DIFFERENT workspace; this call never
|
|
483
|
+
* steals it. Throws `UploadsError` (status 404) on an older/self-hosted
|
|
484
|
+
* server without this route.
|
|
485
|
+
*/
|
|
486
|
+
async githubLinkClaim(repo) {
|
|
487
|
+
return request("POST", `${config.apiUrl}/v1/${encodeURIComponent(config.workspace)}/github/link`, {
|
|
488
|
+
body: new TextEncoder().encode(JSON.stringify({ repo })),
|
|
489
|
+
headers: { "Content-Type": "application/json" },
|
|
490
|
+
});
|
|
491
|
+
},
|
|
492
|
+
/**
|
|
493
|
+
* GitHub App configuration + webhook event subscription check. Throws
|
|
494
|
+
* `UploadsError` (status 404) on an older/self-hosted server without
|
|
495
|
+
* this route — callers treat that as "unknown", not "broken".
|
|
496
|
+
*/
|
|
497
|
+
async githubHealth() {
|
|
498
|
+
return request("GET", `${config.apiUrl}/v1/${encodeURIComponent(config.workspace)}/github/health`);
|
|
499
|
+
},
|
|
500
|
+
/**
|
|
501
|
+
* Self-serve unlink (issue #318): removes `repo`'s binding, but only if
|
|
502
|
+
* this workspace currently owns it. Throws `UploadsError` (status 403)
|
|
503
|
+
* when a different workspace owns the binding — never steals or
|
|
504
|
+
* overwrites another workspace's claim. Throws (status 404) on an
|
|
505
|
+
* older/self-hosted server without this route.
|
|
506
|
+
*/
|
|
507
|
+
async githubLinkUnlink(repo) {
|
|
508
|
+
return request("DELETE", `${config.apiUrl}/v1/${encodeURIComponent(config.workspace)}/github/link?repo=${encodeURIComponent(repo)}`);
|
|
509
|
+
},
|
|
461
510
|
async health() {
|
|
462
511
|
return request("GET", `${config.apiUrl}/health`, { auth: false });
|
|
463
512
|
},
|
|
@@ -2,11 +2,12 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { flagBool, flagInt, flagString, flagValues, parseCommandArgs, UsageError, } from "../cli-args.js";
|
|
4
4
|
import { writeCommandHelp } from "../cli-style.js";
|
|
5
|
-
import { frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, } from "../commands.js";
|
|
5
|
+
import { branchFromFlags, frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, } from "../commands.js";
|
|
6
6
|
import { resolvePutDefaults } from "../config.js";
|
|
7
7
|
import { loadDefaultsRaw, resolveScreenshotDefaults } from "../config-file.js";
|
|
8
8
|
import { resolvePutPrefix } from "../destinations.js";
|
|
9
|
-
import { execRunner, ghMetadataFromTargetWithTitle } from "../github-gh.js";
|
|
9
|
+
import { execRunner, ghMetadataFromTargetWithTitle, resolveRepo, } from "../github-gh.js";
|
|
10
|
+
import { ghBranchAttachmentKey, ghMetadataForBranch } from "../github.js";
|
|
10
11
|
import { parseMetaFlags, validateMetaMap } from "../metadata.js";
|
|
11
12
|
import { writeJson, writeStdout } from "../io.js";
|
|
12
13
|
import { assertHideSelector, captureScreenshot, parseViewport, parseWaitUntil, } from "../screenshot.js";
|
|
@@ -66,6 +67,11 @@ Options:
|
|
|
66
67
|
--keep-exif Keep EXIF/XMP/ICC when optimizing
|
|
67
68
|
--pr <num> Attach to a pull request (stable URL, no hash)
|
|
68
69
|
--issue <num> Attach to an issue
|
|
70
|
+
--branch [name] Stage against a branch, pre-PR (default: current git branch):
|
|
71
|
+
key gh/<owner>/<repo>/branch/<branch>/<name>; not with
|
|
72
|
+
--pr/--issue/--comment/--key/--ref/--prefix. No managed
|
|
73
|
+
comment exists yet — promoting into the PR's comment once
|
|
74
|
+
one opens ships in a later phase.
|
|
69
75
|
--comment With --pr/--issue: update the managed attachments comment.
|
|
70
76
|
Posts as uploads-sh[bot] when the GitHub App is installed;
|
|
71
77
|
otherwise via local gh.
|
|
@@ -84,6 +90,7 @@ Examples:
|
|
|
84
90
|
uploads screenshot http://localhost:3000 --via local --full-page
|
|
85
91
|
uploads screenshot https://uploads.sh --pr 128 --comment
|
|
86
92
|
uploads screenshot ./card.html --no-upload --out ./card.png
|
|
93
|
+
uploads screenshot https://app.example/settings --branch
|
|
87
94
|
`;
|
|
88
95
|
function colorSchemeFromFlags(flags) {
|
|
89
96
|
const dark = flagBool(flags, "--dark");
|
|
@@ -164,9 +171,13 @@ captureImpl = captureScreenshot) {
|
|
|
164
171
|
const destFlag = flagString(parsed.flags, "--destination");
|
|
165
172
|
const prefixFlag = flagString(parsed.flags, "--prefix");
|
|
166
173
|
const ghTarget = ghTargetFromFlags(parsed.flags, run);
|
|
174
|
+
const branchArg = branchFromFlags(parsed.flags, run);
|
|
167
175
|
const wantComment = parsed.flags.has("--comment");
|
|
168
176
|
const galleryId = flagString(parsed.flags, "--gallery");
|
|
169
177
|
const dryRun = flagBool(parsed.flags, "--dry-run");
|
|
178
|
+
if (branchArg !== undefined && ghTarget) {
|
|
179
|
+
throw new UsageError("--branch cannot be combined with --pr/--issue");
|
|
180
|
+
}
|
|
170
181
|
if (wantComment && !ghTarget)
|
|
171
182
|
throw new UsageError("--comment requires --pr or --issue");
|
|
172
183
|
if (ghTarget) {
|
|
@@ -177,6 +188,16 @@ captureImpl = captureScreenshot) {
|
|
|
177
188
|
if (prefixFlag)
|
|
178
189
|
throw new UsageError("--prefix cannot be combined with --pr/--issue");
|
|
179
190
|
}
|
|
191
|
+
if (branchArg !== undefined) {
|
|
192
|
+
if (wantComment)
|
|
193
|
+
throw new UsageError("--branch cannot be combined with --comment");
|
|
194
|
+
if (keyHint)
|
|
195
|
+
throw new UsageError("--key cannot be combined with --branch");
|
|
196
|
+
if (flagString(parsed.flags, "--ref"))
|
|
197
|
+
throw new UsageError("--ref cannot be combined with --branch");
|
|
198
|
+
if (prefixFlag)
|
|
199
|
+
throw new UsageError("--prefix cannot be combined with --branch");
|
|
200
|
+
}
|
|
180
201
|
if (dryRun) {
|
|
181
202
|
if (wantComment)
|
|
182
203
|
throw new UsageError("--dry-run cannot be combined with --comment");
|
|
@@ -185,13 +206,14 @@ captureImpl = captureScreenshot) {
|
|
|
185
206
|
if (noUpload)
|
|
186
207
|
throw new UsageError("--dry-run cannot be combined with --no-upload");
|
|
187
208
|
}
|
|
209
|
+
const branchRepo = branchArg !== undefined ? resolveRepo(flagString(parsed.flags, "--repo"), run) : undefined;
|
|
188
210
|
let resolvedPrefix;
|
|
189
211
|
try {
|
|
190
212
|
resolvedPrefix = resolvePutPrefix({
|
|
191
213
|
destination: destFlag,
|
|
192
214
|
prefix: prefixFlag,
|
|
193
215
|
key: keyHint,
|
|
194
|
-
ghAttachment: Boolean(ghTarget),
|
|
216
|
+
ghAttachment: Boolean(ghTarget) || branchArg !== undefined,
|
|
195
217
|
});
|
|
196
218
|
}
|
|
197
219
|
catch (err) {
|
|
@@ -218,6 +240,10 @@ captureImpl = captureScreenshot) {
|
|
|
218
240
|
metadata = { ...metaExtras, ...ghMetadataFromTargetWithTitle(ghTarget, run) };
|
|
219
241
|
validateMetaMap(metadata);
|
|
220
242
|
}
|
|
243
|
+
else if (branchArg !== undefined) {
|
|
244
|
+
metadata = { ...metaExtras, ...ghMetadataForBranch(branchRepo, branchArg) };
|
|
245
|
+
validateMetaMap(metadata);
|
|
246
|
+
}
|
|
221
247
|
else if (Object.keys(metaExtras).length > 0) {
|
|
222
248
|
validateMetaMap(metaExtras);
|
|
223
249
|
}
|
|
@@ -260,12 +286,15 @@ captureImpl = captureScreenshot) {
|
|
|
260
286
|
}
|
|
261
287
|
const repo = flagString(parsed.flags, "--repo") ?? putDefaults.repo;
|
|
262
288
|
const ref = flagString(parsed.flags, "--ref") ?? putDefaults.ref;
|
|
289
|
+
const branchKey = branchArg !== undefined
|
|
290
|
+
? ghBranchAttachmentKey(branchRepo, branchArg, captured.filename)
|
|
291
|
+
: undefined;
|
|
263
292
|
const alt = altFlag ?? basename(captured.filename);
|
|
264
293
|
const { result, prepared, markdown } = await uploadPreparedImage(ctx.client, captured.png, captured.filename, {
|
|
265
294
|
frame: frameOpts,
|
|
266
295
|
optimize: optimizeOpts,
|
|
267
296
|
ghTarget,
|
|
268
|
-
key: keyHint,
|
|
297
|
+
key: keyHint ?? branchKey,
|
|
269
298
|
prefix: resolvedPrefix ?? putDefaults.prefix,
|
|
270
299
|
repo,
|
|
271
300
|
ref,
|
|
@@ -295,7 +324,7 @@ captureImpl = captureScreenshot) {
|
|
|
295
324
|
let commentError;
|
|
296
325
|
if (wantComment && ghTarget) {
|
|
297
326
|
try {
|
|
298
|
-
comment = await syncAttachmentsComment(ctx.client, ghTarget, run);
|
|
327
|
+
comment = await syncAttachmentsComment(ctx.client, ghTarget, run, ctx.config.workspace);
|
|
299
328
|
if (logHuman)
|
|
300
329
|
process.stderr.write(`>> attachments comment ${comment.action}${commentViaSuffix(comment.via)}\n`);
|
|
301
330
|
}
|
package/dist/commands.d.ts
CHANGED
|
@@ -28,6 +28,17 @@ export declare function readFileArg(fileArg: string): Uint8Array;
|
|
|
28
28
|
export declare function makeGhTarget(pr: number | undefined, issue: number | undefined, repoArg: string | undefined, run: CommandRunner): GhTarget | undefined;
|
|
29
29
|
/** Reads --pr/--issue (+ --repo) into a GhTarget; undefined when neither flag is present. */
|
|
30
30
|
export declare function ghTargetFromFlags(flags: CommandFlags["flags"], run: CommandRunner): GhTarget | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Reads `--branch [name]` — an optional-value flag: `--branch` alone resolves
|
|
33
|
+
* the current git branch (`resolveCurrentBranch`); `--branch feature/x` uses
|
|
34
|
+
* the given name verbatim. Returns undefined when the flag is absent at all
|
|
35
|
+
* (distinct from an empty/whitespace value, which is rejected). Throws
|
|
36
|
+
* UsageError if `--branch` is given more than once, or if the value looks
|
|
37
|
+
* like a filename accidentally swallowed by the optional-value lookahead
|
|
38
|
+
* (e.g. `uploads attach --branch shot.png` with no other file args) — see
|
|
39
|
+
* `looksLikeFileNotBranch`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function branchFromFlags(flags: CommandFlags["flags"], run: CommandRunner): string | undefined;
|
|
31
42
|
/** Shared put/attach optimize flags + UPLOADS_NO_OPTIMIZE default. */
|
|
32
43
|
export declare function optimizeOptionsFromFlags(flags: CommandFlags["flags"], defaults: PutDefaults): OptimizeImageOptions;
|
|
33
44
|
export type PreparedUpload = OptimizeImageResult & {
|
|
@@ -104,7 +115,18 @@ export interface AttachmentsCommentResult {
|
|
|
104
115
|
}
|
|
105
116
|
/** Human-mode suffix noting who posted the managed comment. */
|
|
106
117
|
export declare function commentViaSuffix(via: AttachmentsCommentResult["via"]): string;
|
|
107
|
-
|
|
118
|
+
/**
|
|
119
|
+
* Thrown by `syncAttachmentsComment` when the server declines with
|
|
120
|
+
* `not_authorized` (issue #297 baseline control) — this repo is bound to a
|
|
121
|
+
* different workspace, or unbound and unclaimable by the communal `default`
|
|
122
|
+
* workspace. Deliberately not caught by the generic "bot endpoint
|
|
123
|
+
* unreachable" fallback below: falling back to gh here would let the
|
|
124
|
+
* human's own credentials post anyway, defeating the point of the
|
|
125
|
+
* server-side gate.
|
|
126
|
+
*/
|
|
127
|
+
export declare class GithubCommentAuthorizationError extends Error {
|
|
128
|
+
}
|
|
129
|
+
export declare function syncAttachmentsComment(client: UploadsClient, target: GhTarget, run: CommandRunner, workspace?: string): Promise<AttachmentsCommentResult>;
|
|
108
130
|
export type AttachUploadItem = PutResult & {
|
|
109
131
|
file: string;
|
|
110
132
|
markdown: string;
|
|
@@ -150,6 +172,37 @@ export declare function uploadAttachments(opts: {
|
|
|
150
172
|
failures: AttachFailure[];
|
|
151
173
|
firstError?: unknown;
|
|
152
174
|
}>;
|
|
175
|
+
/** A branch to stage attachments against pre-PR (`uploads attach --branch`). */
|
|
176
|
+
export interface BranchTarget {
|
|
177
|
+
repo: string;
|
|
178
|
+
branch: string;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Prepare + put each path as a branch-staged attachment (pre-PR) with
|
|
182
|
+
* bounded concurrency. Same shape as `uploadAttachments`, keyed under
|
|
183
|
+
* `gh/<owner>/<repo>/branch/<branch>/<filename>` instead of a PR/issue
|
|
184
|
+
* number. Never syncs the managed comment — callers must not call
|
|
185
|
+
* `syncAttachmentsComment` for a branch target.
|
|
186
|
+
*/
|
|
187
|
+
export declare function uploadBranchAttachments(opts: {
|
|
188
|
+
client: UploadsClient;
|
|
189
|
+
target: BranchTarget;
|
|
190
|
+
files: readonly string[];
|
|
191
|
+
contentType?: string;
|
|
192
|
+
optimize: OptimizeImageOptions;
|
|
193
|
+
frame: {
|
|
194
|
+
frameId?: string;
|
|
195
|
+
frameUrl?: string;
|
|
196
|
+
frameFit?: "cover" | "contain";
|
|
197
|
+
};
|
|
198
|
+
metadata?: Record<string, string>;
|
|
199
|
+
provenanceClient?: string;
|
|
200
|
+
concurrency?: number;
|
|
201
|
+
}): Promise<{
|
|
202
|
+
uploads: AttachUploadItem[];
|
|
203
|
+
failures: AttachFailure[];
|
|
204
|
+
firstError?: unknown;
|
|
205
|
+
}>;
|
|
153
206
|
export type PutUploadItem = PutResult & {
|
|
154
207
|
file: string;
|
|
155
208
|
markdown: string;
|
|
@@ -199,6 +252,7 @@ export declare function runFind(ctx: CliContext, args: string[], help?: boolean)
|
|
|
199
252
|
export declare function runMeta(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|
|
200
253
|
export declare function runDelete(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|
|
201
254
|
export declare function runComment(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
255
|
+
export declare function runGithub(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
202
256
|
export declare function runUsage(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|
|
203
257
|
export declare function runReconcile(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|
|
204
258
|
export declare function runPurgeExpired(ctx: CliContext, args: string[], help?: boolean): Promise<number>;
|