@buildinternet/uploads 0.38.0 → 0.41.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/dist/cli-catalog.js +9 -0
- package/dist/cli.js +6 -2
- package/dist/client.d.ts +99 -9
- package/dist/client.js +112 -30
- package/dist/commands/screenshot.js +24 -7
- package/dist/commands.d.ts +46 -1
- package/dist/commands.js +298 -31
- package/dist/comment-config.d.ts +3 -0
- package/dist/comment-config.js +13 -0
- package/dist/github.d.ts +62 -0
- package/dist/github.js +84 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1
- package/dist/keys.d.ts +6 -0
- package/dist/keys.js +17 -0
- package/dist/mcp/tools.js +65 -11
- package/dist/metadata-vocab.d.ts +1 -1
- package/dist/metadata-vocab.js +1 -0
- package/package.json +3 -3
package/dist/github.js
CHANGED
|
@@ -57,14 +57,78 @@ export function normalizeGithubCoordinate(value) {
|
|
|
57
57
|
* Inverse of `ghKeyPrefix`: parse the PR/issue coordinate back out of a
|
|
58
58
|
* stable attachment key (`gh/<owner>/<name>/<kind>/<num>/<filename>`), or
|
|
59
59
|
* undefined for any other key shape.
|
|
60
|
+
*
|
|
61
|
+
* A real GitHub owner CAN be named `private`, so the strict private-repo
|
|
62
|
+
* shape (`gh/private/<32-hex-id>/...`, see `parseGhPrivateKey`) is checked
|
|
63
|
+
* first and rejected here — otherwise a private-prefixed key would
|
|
64
|
+
* misparse as an ordinary key with owner "private". The accepted ambiguity:
|
|
65
|
+
* a key whose second segment is NOT 32-lowercase-hex (e.g.
|
|
66
|
+
* `gh/private/realrepo/pull/5/x.png`) still parses here as owner "private",
|
|
67
|
+
* repo "private/realrepo" — that's an ordinary public-repo key for a repo
|
|
68
|
+
* actually named "private", not a private-prefix key.
|
|
60
69
|
*/
|
|
61
70
|
export function parseGhKey(key) {
|
|
71
|
+
if (parseGhPrivateKey(key))
|
|
72
|
+
return undefined;
|
|
62
73
|
const match = /^gh\/([^/]+)\/([^/]+)\/(pull|issues)\/([1-9][0-9]*)\/./.exec(key);
|
|
63
74
|
if (!match)
|
|
64
75
|
return undefined;
|
|
65
76
|
const [, owner, name, kind, num] = match;
|
|
66
77
|
return { repo: `${owner}/${name}`, kind: kind, num: Number(num) };
|
|
67
78
|
}
|
|
79
|
+
/** Literal root under which every private-repo attachment key lives. */
|
|
80
|
+
export const GH_PRIVATE_ROOT = "gh/private/";
|
|
81
|
+
/** Strict shape for a randomized private-repo prefix id: 32 lowercase hex chars. */
|
|
82
|
+
const PRIVATE_PREFIX_ID_RE = /^[0-9a-f]{32}$/;
|
|
83
|
+
/**
|
|
84
|
+
* Guard every private-key builder against a malformed `prefixId` — a
|
|
85
|
+
* caller bug here must never silently produce a guessable-ish key.
|
|
86
|
+
*/
|
|
87
|
+
function assertPrivatePrefixId(prefixId) {
|
|
88
|
+
if (!PRIVATE_PREFIX_ID_RE.test(prefixId)) {
|
|
89
|
+
throw new Error(`invalid private prefix id: "${prefixId}" must be 32 lowercase hex characters`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Private-repo key prefix: `gh/private/<32-hex-id>/<kind>/<num>/`.
|
|
94
|
+
* Deliberately omits the repo (unlike `ghKeyPrefix`) — the id is a random,
|
|
95
|
+
* unguessable per-repo prefix rather than an owner/name path, so callers
|
|
96
|
+
* that need the repo back must read `gh.repo` metadata (see
|
|
97
|
+
* `parseGhPrivateKey`, which cannot recover it from the key alone).
|
|
98
|
+
*/
|
|
99
|
+
export function ghPrivateKeyPrefix(prefixId, target) {
|
|
100
|
+
assertPrivatePrefixId(prefixId);
|
|
101
|
+
return `${GH_PRIVATE_ROOT}${prefixId}/${target.kind}/${target.num}/`;
|
|
102
|
+
}
|
|
103
|
+
/** Private-repo attachment key: `ghPrivateKeyPrefix` + the sanitized filename. */
|
|
104
|
+
export function ghPrivateAttachmentKey(prefixId, target, filename) {
|
|
105
|
+
return `${ghPrivateKeyPrefix(prefixId, target)}${sanitizeKeySegment(filename)}`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Private-repo branch-staged key prefix: `gh/private/<32-hex-id>/branch/`.
|
|
109
|
+
* Unlike `ghBranchKeyPrefix`, there is deliberately NO branch-name segment —
|
|
110
|
+
* the branch name itself is not embedded in a private-repo key.
|
|
111
|
+
*/
|
|
112
|
+
export function ghPrivateBranchKeyPrefix(prefixId) {
|
|
113
|
+
assertPrivatePrefixId(prefixId);
|
|
114
|
+
return `${GH_PRIVATE_ROOT}${prefixId}/branch/`;
|
|
115
|
+
}
|
|
116
|
+
/** Private-repo branch-staged attachment key: `ghPrivateBranchKeyPrefix` + the sanitized filename. */
|
|
117
|
+
export function ghPrivateBranchAttachmentKey(prefixId, filename) {
|
|
118
|
+
return `${ghPrivateBranchKeyPrefix(prefixId)}${sanitizeKeySegment(filename)}`;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Inverse of `ghPrivateKeyPrefix`: parse the prefix id/kind/number back out
|
|
122
|
+
* of a private-repo attachment key, or undefined for any other key shape.
|
|
123
|
+
* Cannot recover the repo — callers that need it read `gh.repo` metadata.
|
|
124
|
+
*/
|
|
125
|
+
export function parseGhPrivateKey(key) {
|
|
126
|
+
const match = /^gh\/private\/([0-9a-f]{32})\/(pull|issues)\/([1-9][0-9]*)\/./.exec(key);
|
|
127
|
+
if (!match)
|
|
128
|
+
return undefined;
|
|
129
|
+
const [, prefixId, kind, num] = match;
|
|
130
|
+
return { prefixId, kind: kind, num: Number(num) };
|
|
131
|
+
}
|
|
68
132
|
export function ghKeyPrefix(target) {
|
|
69
133
|
const [owner, name] = target.repo.split("/");
|
|
70
134
|
return `gh/${sanitizeKeySegment(owner)}/${sanitizeKeySegment(name)}/${target.kind}/${target.num}/`;
|
|
@@ -91,6 +155,26 @@ export function ghBranchKeyPrefix(repo, branch) {
|
|
|
91
155
|
export function ghBranchAttachmentKey(repo, branch, filename) {
|
|
92
156
|
return `${ghBranchKeyPrefix(repo, branch)}${sanitizeKeySegment(filename)}`;
|
|
93
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Mode-owning attachment key builder: collapses the
|
|
160
|
+
* `mode === "private" ? ghPrivateAttachmentKey(...) : ghAttachmentKey(...)`
|
|
161
|
+
* ternary repeated across call sites into one place.
|
|
162
|
+
*/
|
|
163
|
+
export function ghAttachmentKeyForMode(mode, target, filename) {
|
|
164
|
+
return mode.mode === "private"
|
|
165
|
+
? ghPrivateAttachmentKey(mode.prefixId, target, filename)
|
|
166
|
+
: ghAttachmentKey(target, filename);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Mode-owning branch-staged attachment key builder. The private form
|
|
170
|
+
* ignores `repo`/`branch` (a private-repo key has no branch-name segment,
|
|
171
|
+
* see `ghPrivateBranchKeyPrefix`) and uses the prefix id instead.
|
|
172
|
+
*/
|
|
173
|
+
export function ghBranchAttachmentKeyForMode(mode, repo, branch, filename) {
|
|
174
|
+
return mode.mode === "private"
|
|
175
|
+
? ghPrivateBranchAttachmentKey(mode.prefixId, filename)
|
|
176
|
+
: ghBranchAttachmentKey(repo, branch, filename);
|
|
177
|
+
}
|
|
94
178
|
/**
|
|
95
179
|
* `gh.*` metadata for a branch-staged attach: `gh.repo`, `gh.kind=branch`,
|
|
96
180
|
* `gh.branch` (lowercased), and `gh.staged-at` (ISO 8601 UTC, no fractional
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ export { sanitizeKeySegment, sha256Short, deriveRepoFromGit, buildScreenshotKey
|
|
|
4
4
|
export { BUILTIN_DESTINATIONS, isBuiltinDestination, keyMatchesDestination, resolveDestinationRoot, resolvePutPrefix, type BuiltinDestinationId, } from "./destinations.js";
|
|
5
5
|
export { DEFAULT_API_URL, DEFAULT_WORKSPACE, UPLOADS_CONFIG_KEYS, defaultConfigPath, resolveConfigPath, loadConfigFile, loadEnvFile, resolveApiUrl, resolveConfig, describeConfigSources, redactToken, writeConfigKeys, configValuesFromClient, putDefaultsToConfigValues, resolvePutDefaults, mergePutDefaults, workspaceFromToken, workspaceMismatch, type UploadsClientConfig, type ResolvedConfig, type WorkspaceSource, type ConfigValueSource, type ConfigSources, type UploadsConfigKey, type UploadsConfigValues, type PutDefaults, } from "./config.js";
|
|
6
6
|
export { UploadsError, type UploadsErrorCode } from "./errors.js";
|
|
7
|
-
export { createUploadsClient, type UploadsClient, type PutOptions, type ProvenanceInput, type ListOptions, type PutResult, type ListItem, type ListResult, type HeadResult, type DeleteResult, type GalleryItem, type Gallery, type GallerySummary, type GalleryListOptions, type GalleryListResult, type CreateGalleryOptions, type AddGalleryItemOptions, type DeleteGalleryOptions, type HealthResult, type UsageResult, type ReconcileResult, type PurgeExpiredResult, type PurgeExpiredResponse, type FindFilesOptions, type FindFilesItem, type FindFilesResult, type MetadataKeysResult, type MetadataValuesResult, type GetMetadataResult, type PatchMetadataOptions, } from "./client.js";
|
|
7
|
+
export { createUploadsClient, type UploadsClient, type PutOptions, type ProvenanceInput, type ListOptions, type PutResult, type ListItem, type ListResult, type HeadResult, type DeleteResult, type GalleryItem, type Gallery, type GallerySummary, type GalleryListOptions, type GalleryListResult, type CreateGalleryOptions, type AddGalleryItemOptions, type DeleteGalleryOptions, type HealthResult, type UsageResult, type ReconcileResult, type PurgeExpiredResult, type PurgeExpiredResponse, type FindFilesOptions, type FindFilesItem, type FindFilesResult, type MetadataKeysResult, type MetadataValuesResult, type GetMetadataResult, type PatchMetadataOptions, type ResolveGhPrefixOptions, type ResolveGhPrefixResult, } from "./client.js";
|
|
8
8
|
export { buildCliProvenance } from "./provenance.js";
|
|
9
9
|
export { META_KEY_RE, META_VALUE_MAX, META_MAX_KEYS, META_MAX_TOTAL_BYTES, validateMetaEntry, parseMetaPair, parseMetaFlags, } from "./metadata.js";
|
|
10
|
-
export { ATTACHMENTS_MARKER, attachmentsCommentBody, GH_FALLBACK_AUTHOR_NOTE, ghAttachmentKey, ghKeyPrefix, ghMetadataFromTarget, ghBranchAttachmentKey, ghBranchKeyPrefix, ghMetadataForBranch, isValidRepo, parseRepoFromRemoteUrl, type AttachmentItem, type GhTarget, type GhTargetKind, } from "./github.js";
|
|
10
|
+
export { ATTACHMENTS_MARKER, attachmentsCommentBody, GH_FALLBACK_AUTHOR_NOTE, ghAttachmentKey, ghKeyPrefix, ghMetadataFromTarget, ghBranchAttachmentKey, ghBranchKeyPrefix, ghMetadataForBranch, isValidRepo, parseRepoFromRemoteUrl, GH_PRIVATE_ROOT, ghPrivateKeyPrefix, ghPrivateAttachmentKey, ghPrivateBranchKeyPrefix, ghPrivateBranchAttachmentKey, ghAttachmentKeyForMode, ghBranchAttachmentKeyForMode, parseGhKey, parseGhPrivateKey, type AttachmentItem, type GhTarget, type GhTargetKind, type GhKeyMode, } from "./github.js";
|
|
11
11
|
export { DEFAULT_OPTIMIZE_MAX_EDGE, DEFAULT_OPTIMIZE_QUALITY, optimizeImageForUpload, rewriteKeyExtension, withImageExtension, type OptimizeImageOptions, type OptimizeImageResult, type OptimizeOutputFormat, } from "./optimize.js";
|
|
12
12
|
export { FRAME_PRESETS, applyFrame, listFramePresets, resolveFrameId, type FrameFit, type FrameOptions, type FrameResult, } from "./frame.js";
|
|
13
13
|
export { execRunner, resolveRepo, upsertAttachmentsComment, type CommandRunner, } from "./github-gh.js";
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,11 @@ export { UploadsError } from "./errors.js";
|
|
|
7
7
|
export { createUploadsClient, } from "./client.js";
|
|
8
8
|
export { buildCliProvenance } from "./provenance.js";
|
|
9
9
|
export { META_KEY_RE, META_VALUE_MAX, META_MAX_KEYS, META_MAX_TOTAL_BYTES, validateMetaEntry, parseMetaPair, parseMetaFlags, } from "./metadata.js";
|
|
10
|
-
export { ATTACHMENTS_MARKER, attachmentsCommentBody, GH_FALLBACK_AUTHOR_NOTE, ghAttachmentKey, ghKeyPrefix, ghMetadataFromTarget, ghBranchAttachmentKey, ghBranchKeyPrefix, ghMetadataForBranch, isValidRepo, parseRepoFromRemoteUrl,
|
|
10
|
+
export { ATTACHMENTS_MARKER, attachmentsCommentBody, GH_FALLBACK_AUTHOR_NOTE, ghAttachmentKey, ghKeyPrefix, ghMetadataFromTarget, ghBranchAttachmentKey, ghBranchKeyPrefix, ghMetadataForBranch, isValidRepo, parseRepoFromRemoteUrl,
|
|
11
|
+
// Private-repo randomized-prefix builders (issue #631) — needed by the
|
|
12
|
+
// hosted MCP (apps/mcp), which builds keys in-process rather than via
|
|
13
|
+
// the CLI's own commands.ts.
|
|
14
|
+
GH_PRIVATE_ROOT, ghPrivateKeyPrefix, ghPrivateAttachmentKey, ghPrivateBranchKeyPrefix, ghPrivateBranchAttachmentKey, ghAttachmentKeyForMode, ghBranchAttachmentKeyForMode, parseGhKey, parseGhPrivateKey, } from "./github.js";
|
|
11
15
|
export { DEFAULT_OPTIMIZE_MAX_EDGE, DEFAULT_OPTIMIZE_QUALITY, optimizeImageForUpload, rewriteKeyExtension, withImageExtension, } from "./optimize.js";
|
|
12
16
|
export { FRAME_PRESETS, applyFrame, listFramePresets, resolveFrameId, } from "./frame.js";
|
|
13
17
|
export { execRunner, resolveRepo, upsertAttachmentsComment, } from "./github-gh.js";
|
package/dist/keys.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ export declare function sha256Short(bytes: Uint8Array): Promise<string>;
|
|
|
8
8
|
* every existing caller.
|
|
9
9
|
*/
|
|
10
10
|
export declare function deriveRepoFromGit(run?: (cmd: string, args: string[], input?: string) => string): string | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Full `owner/name` slug (lowercase, matching the `gh.repo` convention) from
|
|
13
|
+
* the cwd's git remote — the derived `repo` metadata value. Unlike
|
|
14
|
+
* `deriveRepoFromGit` above (key-segment name only), this keeps the owner.
|
|
15
|
+
*/
|
|
16
|
+
export declare function deriveRepoSlugFromGit(run?: (cmd: string, args: string[], input?: string) => string): string | undefined;
|
|
11
17
|
export declare function buildScreenshotKey(opts: {
|
|
12
18
|
filename: string;
|
|
13
19
|
fileBytes: Uint8Array;
|
package/dist/keys.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
+
import { parseRepoFromRemoteUrl } from "./github.js";
|
|
2
3
|
export function sanitizeKeySegment(s) {
|
|
3
4
|
return s.replace(/[^A-Za-z0-9._-]/g, "-");
|
|
4
5
|
}
|
|
@@ -28,6 +29,22 @@ export function deriveRepoFromGit(run) {
|
|
|
28
29
|
return undefined;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Full `owner/name` slug (lowercase, matching the `gh.repo` convention) from
|
|
34
|
+
* the cwd's git remote — the derived `repo` metadata value. Unlike
|
|
35
|
+
* `deriveRepoFromGit` above (key-segment name only), this keeps the owner.
|
|
36
|
+
*/
|
|
37
|
+
export function deriveRepoSlugFromGit(run) {
|
|
38
|
+
try {
|
|
39
|
+
const url = run
|
|
40
|
+
? run("git", ["config", "--get", "remote.origin.url"])
|
|
41
|
+
: execSync("git config --get remote.origin.url", { encoding: "utf8" });
|
|
42
|
+
return parseRepoFromRemoteUrl(url)?.toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
31
48
|
export async function buildScreenshotKey(opts) {
|
|
32
49
|
const dot = opts.filename.lastIndexOf(".");
|
|
33
50
|
const ext = dot >= 0 ? opts.filename.slice(dot + 1) : "";
|
package/dist/mcp/tools.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createUploadsClient } from "../client.js";
|
|
2
|
-
import { buildDoctorReport, makeGhTarget, mergeStagingMeta, resolvePutStagingTarget, resolveStaged, syncAttachmentsComment, uploadAttachments, uploadPreparedImage, uploadPuts, } from "../commands.js";
|
|
2
|
+
import { buildDoctorReport, ghListPrefixes, ghMergedList, makeGhTarget, mergeStagingMeta, resolveGhPrefixSafe, resolvePutStagingTarget, resolveStaged, syncAttachmentsComment, uploadAttachments, uploadPreparedImage, uploadPuts, } from "../commands.js";
|
|
3
3
|
import { resolveFrameId } from "../frame.js";
|
|
4
4
|
import { resolveConfig, resolvePutDefaults, } from "../config.js";
|
|
5
5
|
import { resolvePutPrefix } from "../destinations.js";
|
|
6
|
-
import {
|
|
6
|
+
import { ghKeyPrefix, ghPrivateKeyPrefix } from "../github.js";
|
|
7
7
|
import { safeCaptureFacts } from "../capture-facts.js";
|
|
8
|
+
import { deriveRepoSlugFromGit } from "../keys.js";
|
|
8
9
|
import { validateMetaMap } from "../metadata.js";
|
|
9
10
|
import { mergeDerivedMeta } from "../metadata-vocab.js";
|
|
10
11
|
import { execRunner, ghMetadataFromTargetWithTitle, resolveCurrentBranch, resolveCurrentPullRequest, resolveRepo, } from "../github-gh.js";
|
|
@@ -430,7 +431,23 @@ export function createUploadsMcpTools(opts) {
|
|
|
430
431
|
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
431
432
|
run,
|
|
432
433
|
});
|
|
433
|
-
|
|
434
|
+
// Derived `repo` metadata (spec: 2026-08-11-screenshots-project-grouping-design.md).
|
|
435
|
+
// Same derivation the CLI does; MCP always derives (no --no-auto), so
|
|
436
|
+
// this is only suppressed by noGit. metadataProp's contract: omitting
|
|
437
|
+
// `metadata` means "leave what's already stored for this key
|
|
438
|
+
// untouched" — an object (even {}) triggers a full replace. So only
|
|
439
|
+
// fold the derived repo into a defined object when the caller already
|
|
440
|
+
// supplied metadata, or branch staging is building one below anyway
|
|
441
|
+
// (mergeStagingMeta always returns a defined object); never
|
|
442
|
+
// synthesize metadata on a bare re-upload just to add repo, or a
|
|
443
|
+
// no-metadata put would silently wipe everything already stored.
|
|
444
|
+
const repoSlug = !noGit ? deriveRepoSlugFromGit(run) : undefined;
|
|
445
|
+
const metadataWithRepo = repoSlug !== undefined && (metadata !== undefined || stagingTarget !== undefined)
|
|
446
|
+
? mergeDerivedMeta(metadata ?? {}, { repo: repoSlug })
|
|
447
|
+
: metadata;
|
|
448
|
+
const putMetadata = stagingTarget
|
|
449
|
+
? mergeStagingMeta(metadataWithRepo, stagingTarget)
|
|
450
|
+
: metadataWithRepo;
|
|
434
451
|
const putShared = {
|
|
435
452
|
client,
|
|
436
453
|
ghTarget: target,
|
|
@@ -737,9 +754,15 @@ export function createUploadsMcpTools(opts) {
|
|
|
737
754
|
// Keep undefined when nothing at all was supplied or derived, so the
|
|
738
755
|
// "omit to leave stored metadata untouched" contract still holds.
|
|
739
756
|
const captureDerived = safeCaptureFacts(targetArg, viewport, colorSchemeArg);
|
|
740
|
-
|
|
757
|
+
// Derived `repo` metadata (spec: 2026-08-11-screenshots-project-grouping-design.md).
|
|
758
|
+
// Same derivation the CLI does, suppressed by noGit.
|
|
759
|
+
const repoSlug = !noGit ? deriveRepoSlugFromGit(run) : undefined;
|
|
760
|
+
const captureDerivedWithRepo = repoSlug
|
|
761
|
+
? { ...captureDerived, repo: repoSlug }
|
|
762
|
+
: captureDerived;
|
|
763
|
+
const metadataBase = metadata === undefined && Object.keys(captureDerivedWithRepo).length === 0
|
|
741
764
|
? undefined
|
|
742
|
-
: mergeDerivedMeta(metadata ?? {},
|
|
765
|
+
: mergeDerivedMeta(metadata ?? {}, captureDerivedWithRepo);
|
|
743
766
|
// gh.* metadata: explicit pr/issue target wins; staging wins the same
|
|
744
767
|
// way (matches attach --branch/bare put); otherwise capture-derived +
|
|
745
768
|
// explicit only.
|
|
@@ -778,14 +801,26 @@ export function createUploadsMcpTools(opts) {
|
|
|
778
801
|
}
|
|
779
802
|
throw err;
|
|
780
803
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
804
|
+
// Resolved once (issue #631), only now that upload is actually
|
|
805
|
+
// about to happen — never per file (screenshot uploads exactly one).
|
|
806
|
+
const ghPrefix = target
|
|
807
|
+
? await resolveGhPrefixSafe(client, {
|
|
808
|
+
repo: target.repo,
|
|
809
|
+
target: { kind: target.kind, num: target.num },
|
|
810
|
+
})
|
|
811
|
+
: stagingTarget
|
|
812
|
+
? await resolveGhPrefixSafe(client, {
|
|
813
|
+
repo: stagingTarget.repo,
|
|
814
|
+
branch: stagingTarget.branch,
|
|
815
|
+
})
|
|
816
|
+
: undefined;
|
|
784
817
|
const { result, prepared, markdown } = await uploadPreparedImage(client, captured.png, captured.filename, {
|
|
785
818
|
frame: frameOpts,
|
|
786
819
|
optimize: optimizeOpts,
|
|
787
820
|
ghTarget: target,
|
|
788
|
-
|
|
821
|
+
ghBranchTarget: stagingTarget,
|
|
822
|
+
ghPrefix,
|
|
823
|
+
key: keyArg,
|
|
789
824
|
prefix: resolvedPrefix ?? defaults.prefix,
|
|
790
825
|
repo: optString(args, "repo") ?? defaults.repo,
|
|
791
826
|
ref: refArg ?? defaults.ref,
|
|
@@ -957,16 +992,35 @@ export function createUploadsMcpTools(opts) {
|
|
|
957
992
|
const prefixArg = optString(args, "prefix");
|
|
958
993
|
let prefix = prefixArg ?? (defaults.prefix ? `${defaults.prefix}/` : undefined);
|
|
959
994
|
const target = ghTargetFromArgs(args, run);
|
|
995
|
+
const { client } = clientFor(args);
|
|
996
|
+
// Also list every active private prefix, if any (issue #631) —
|
|
997
|
+
// mirrors syncAttachmentsComment's gh-fallback gather: a repo's
|
|
998
|
+
// attachment history can be split across the plain shape and
|
|
999
|
+
// MULTIPLE private prefixes, not just the currently-resolved one.
|
|
1000
|
+
// `prefixes` stays undefined outside pr/issue (unchanged behavior);
|
|
1001
|
+
// collapses to `[prefix]` in plain mode, so the single-request path
|
|
1002
|
+
// below is byte-identical to pre-#631.
|
|
1003
|
+
let prefixes;
|
|
960
1004
|
if (target) {
|
|
961
1005
|
if (prefixArg)
|
|
962
1006
|
usage("prefix cannot be combined with pr/issue");
|
|
963
1007
|
prefix = ghKeyPrefix(target);
|
|
1008
|
+
const ghPrefix = await resolveGhPrefixSafe(client, {
|
|
1009
|
+
repo: target.repo,
|
|
1010
|
+
target: { kind: target.kind, num: target.num },
|
|
1011
|
+
});
|
|
1012
|
+
prefixes = ghListPrefixes(prefix, ghPrefix, (id) => ghPrivateKeyPrefix(id, target));
|
|
964
1013
|
}
|
|
965
1014
|
const limit = optPosInt(args, "limit");
|
|
966
1015
|
const cursor = optString(args, "cursor");
|
|
967
|
-
const { client } = clientFor(args);
|
|
968
1016
|
if (optBool(args, "all")) {
|
|
969
|
-
const items =
|
|
1017
|
+
const items = prefixes && prefixes.length > 1
|
|
1018
|
+
? await ghMergedList(prefixes, cursor, (p, c) => client.listAll({ prefix: p, limit, cursor: c }))
|
|
1019
|
+
: await client.listAll({ prefix, limit, cursor });
|
|
1020
|
+
return { items, cursor: null };
|
|
1021
|
+
}
|
|
1022
|
+
if (prefixes && prefixes.length > 1) {
|
|
1023
|
+
const items = await ghMergedList(prefixes, cursor, async (p, c) => (await client.list({ prefix: p, limit, cursor: c })).items);
|
|
970
1024
|
return { items, cursor: null };
|
|
971
1025
|
}
|
|
972
1026
|
return client.list({ prefix, limit, cursor });
|
package/dist/metadata-vocab.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const CANONICAL_META_KEYS: readonly ["url", "path", "env", "theme", "viewport", "device", "software", "captured", "state", "app"];
|
|
1
|
+
export declare const CANONICAL_META_KEYS: readonly ["url", "path", "env", "theme", "viewport", "device", "software", "captured", "state", "app", "repo"];
|
|
2
2
|
export type CanonicalMetaKey = (typeof CANONICAL_META_KEYS)[number];
|
|
3
3
|
/** Closed enum for `state` — the highest-value hand-supplied search facet. */
|
|
4
4
|
export declare const META_STATE_VALUES: readonly ["before", "after", "empty", "error", "loading"];
|
package/dist/metadata-vocab.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buildinternet/uploads",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "CLI and client for uploads.sh — workspace-scoped image hosting for GitHub embeds",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"node": ">=22"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"files-sdk": "^2.
|
|
41
|
+
"files-sdk": "^2.2.3"
|
|
42
42
|
},
|
|
43
43
|
"peerDependenciesMeta": {
|
|
44
44
|
"files-sdk": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/node": "^26.1.0",
|
|
50
50
|
"ai": "^6.0.0",
|
|
51
|
-
"files-sdk": "^2.
|
|
51
|
+
"files-sdk": "^2.2.3",
|
|
52
52
|
"typescript": "^7.0.2",
|
|
53
53
|
"vitest": "^4.1.10"
|
|
54
54
|
},
|