@buildinternet/uploads 0.42.2 → 0.44.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.
@@ -12,6 +12,8 @@ export interface RepoCommentConfig {
12
12
  linkToFilePage?: boolean;
13
13
  note?: string;
14
14
  ingestGithubAttachments?: boolean;
15
+ ingestBotAttachments?: boolean;
16
+ adoptLinkedFiles?: boolean;
15
17
  }
16
18
  export interface WorkspaceCommentDefaults {
17
19
  imageWidth?: "full" | number;
@@ -20,6 +22,8 @@ export interface WorkspaceCommentDefaults {
20
22
  linkToFilePage?: boolean;
21
23
  note?: string;
22
24
  ingestGithubAttachments?: boolean;
25
+ ingestBotAttachments?: boolean;
26
+ adoptLinkedFiles?: boolean;
23
27
  }
24
28
  export interface ResolvedCommentOptions {
25
29
  imageWidth: "auto" | "full" | number;
@@ -29,6 +33,8 @@ export interface ResolvedCommentOptions {
29
33
  linkToFilePage: boolean;
30
34
  note: string | null;
31
35
  ingestGithubAttachments: boolean;
36
+ ingestBotAttachments: boolean;
37
+ adoptLinkedFiles: boolean;
32
38
  }
33
39
  export type OptionSource = "repo" | "workspace" | "auto";
34
40
  export declare const AUTO_COMMENT_OPTIONS: ResolvedCommentOptions;
@@ -12,7 +12,11 @@ export const AUTO_COMMENT_OPTIONS = {
12
12
  metaState: true,
13
13
  linkToFilePage: true,
14
14
  note: null,
15
- ingestGithubAttachments: false,
15
+ ingestGithubAttachments: true,
16
+ ingestBotAttachments: false,
17
+ // Default ON for bound repos (issue #701) — binding is already an
18
+ // explicit opt-in relationship, same posture as ingestGithubAttachments.
19
+ adoptLinkedFiles: true,
16
20
  };
17
21
  export const NOTE_MAX_CHARS = 500;
18
22
  const WIDTH_MIN = 160;
@@ -72,6 +76,22 @@ export function parseRepoCommentConfig(text, format) {
72
76
  else
73
77
  warnings.push(`ingestGithubAttachments: expected a boolean; dropped`);
74
78
  }
79
+ // ingestBotAttachments: boolean
80
+ if ("ingestBotAttachments" in c) {
81
+ const v = c.ingestBotAttachments;
82
+ if (typeof v === "boolean")
83
+ config.ingestBotAttachments = v;
84
+ else
85
+ warnings.push(`ingestBotAttachments: expected a boolean; dropped`);
86
+ }
87
+ // adoptLinkedFiles: boolean
88
+ if ("adoptLinkedFiles" in c) {
89
+ const v = c.adoptLinkedFiles;
90
+ if (typeof v === "boolean")
91
+ config.adoptLinkedFiles = v;
92
+ else
93
+ warnings.push(`adoptLinkedFiles: expected a boolean; dropped`);
94
+ }
75
95
  // meta.path / meta.state: booleans nested under `meta`
76
96
  if ("meta" in c) {
77
97
  const v = c.meta;
@@ -127,6 +147,10 @@ export function resolveCommentOptions(repo, ws) {
127
147
  ...(ws?.ingestGithubAttachments !== undefined
128
148
  ? { ingestGithubAttachments: ws.ingestGithubAttachments }
129
149
  : {}),
150
+ ...(ws?.ingestBotAttachments !== undefined
151
+ ? { ingestBotAttachments: ws.ingestBotAttachments }
152
+ : {}),
153
+ ...(ws?.adoptLinkedFiles !== undefined ? { adoptLinkedFiles: ws.adoptLinkedFiles } : {}),
130
154
  };
131
155
  const options = { ...AUTO_COMMENT_OPTIONS };
132
156
  const source = Object.fromEntries(Object.keys(AUTO_COMMENT_OPTIONS).map((k) => [k, "auto"]));
@@ -138,6 +162,8 @@ export function resolveCommentOptions(repo, ws) {
138
162
  "metaState",
139
163
  "linkToFilePage",
140
164
  "ingestGithubAttachments",
165
+ "ingestBotAttachments",
166
+ "adoptLinkedFiles",
141
167
  ]) {
142
168
  if (cfg[key] !== undefined && source[key] === "auto") {
143
169
  options[key] = cfg[key];
@@ -147,5 +147,8 @@ export declare function attachmentImageWidth(filename: string, density?: Attachm
147
147
  /**
148
148
  * Render the one marker-owned GitHub comment. When there are no galleries this
149
149
  * intentionally preserves the legacy attachment-only body byte-for-byte.
150
+ *
151
+ * `target` is optional so goldens, the settings preview, and other callers
152
+ * without a PR/issue stay on the generic `--pr <N>` hint.
150
153
  */
151
- export declare function attachmentsCommentBody(items: AttachmentItem[], galleries?: GalleryCommentItem[], marker?: string, options?: CommentRenderOptions): string;
154
+ export declare function attachmentsCommentBody(items: AttachmentItem[], galleries?: GalleryCommentItem[], marker?: string, options?: CommentRenderOptions, target?: Pick<GhTarget, "kind" | "num">): string;
@@ -390,11 +390,24 @@ function countInlinableMedia(sorted, maxInlineImages) {
390
390
  }
391
391
  return count;
392
392
  }
393
+ /**
394
+ * Quiet add-media footer. When `target` is a real PR/issue, the command
395
+ * uses that number (`--pr 12` / `--issue 45`) so anyone reading the comment
396
+ * can copy it. Without a target the generic `--pr <N>` placeholder stays.
397
+ */
398
+ function addMediaFooter(target) {
399
+ const flag = target?.kind === "issues" ? "--issue" : "--pr";
400
+ const n = target && Number.isInteger(target.num) && target.num > 0 ? String(target.num) : "&lt;N&gt;";
401
+ return `<sub>Maintained by <a href="https://uploads.sh">uploads.sh</a> · add media: <code>uploads put &lt;file&gt; ${flag} ${n}</code> · <a href="https://uploads.sh/docs/github-app">docs</a></sub>`;
402
+ }
393
403
  /**
394
404
  * Render the one marker-owned GitHub comment. When there are no galleries this
395
405
  * intentionally preserves the legacy attachment-only body byte-for-byte.
406
+ *
407
+ * `target` is optional so goldens, the settings preview, and other callers
408
+ * without a PR/issue stay on the generic `--pr <N>` hint.
396
409
  */
397
- export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMENTS_MARKER, options = AUTO_RENDER_OPTIONS) {
410
+ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMENTS_MARKER, options = AUTO_RENDER_OPTIONS, target) {
398
411
  // Non-mutating sort (equivalent to Array#toSorted) — the api worker's
399
412
  // tsconfig targets lib ES2022, which predates Array#toSorted (ES2023);
400
413
  // packages/uploads/src/github.ts uses toSorted directly.
@@ -529,6 +542,6 @@ export function attachmentsCommentBody(items, galleries = [], marker = ATTACHMEN
529
542
  // Footer condensed to a single quiet line — the old two-line explainer
530
543
  // ("re-uploading updates everywhere", full add-media flags) repeats on
531
544
  // every PR and lost value with each appearance; details live in the docs.
532
- lines.push('<sub>Maintained by <a href="https://uploads.sh">uploads.sh</a> · add media: <code>uploads put &lt;file&gt; --pr &lt;N&gt;</code> · <a href="https://uploads.sh/docs/github-app">docs</a></sub>');
545
+ lines.push(addMediaFooter(target));
533
546
  return lines.join("\n");
534
547
  }
@@ -1,7 +1,7 @@
1
1
  import type { UploadsClientConfig } from "./config.js";
2
2
  export declare const UPLOADS_CONFIG_KEYS: readonly ["UPLOADS_API_URL", "UPLOADS_WORKSPACE", "UPLOADS_TOKEN",
3
3
  /** Better Auth device-flow session bearer — keeps session.cliVersion fresh. */
4
- "UPLOADS_SESSION_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", "UPLOADS_NO_NUDGE"];
4
+ "UPLOADS_SESSION_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", "UPLOADS_NO_NUDGE", "UPLOADS_NO_AUTO_PR"];
5
5
  export type UploadsConfigKey = (typeof UPLOADS_CONFIG_KEYS)[number];
6
6
  export type UploadsConfigValues = Partial<Record<UploadsConfigKey, string>>;
7
7
  export interface PutDefaults {
@@ -18,6 +18,9 @@ export interface PutDefaults {
18
18
  noAutoMeta?: boolean;
19
19
  /** When true, `put` never prints the bare-put --pr/attach nudge (issue #393). */
20
20
  noNudge?: boolean;
21
+ /** When true, a bare put/screenshot never auto-assumes an unambiguous open
22
+ * PR's context (issue #700) — falls back to the #403/#469 staging default. */
23
+ noAutoPr?: boolean;
21
24
  }
22
25
  declare const PUT_DEFAULT_KEY_MAP: Record<keyof PutDefaults, UploadsConfigKey>;
23
26
  export declare function putDefaultsToConfigValues(defaults: PutDefaults): UploadsConfigValues;
@@ -17,6 +17,7 @@ export const UPLOADS_CONFIG_KEYS = [
17
17
  "UPLOADS_NO_AUTO_META",
18
18
  "UPLOADS_SCREENSHOT_VIA",
19
19
  "UPLOADS_NO_NUDGE",
20
+ "UPLOADS_NO_AUTO_PR",
20
21
  ];
21
22
  const PUT_DEFAULT_KEY_MAP = {
22
23
  prefix: "UPLOADS_DEFAULT_PREFIX",
@@ -28,6 +29,7 @@ const PUT_DEFAULT_KEY_MAP = {
28
29
  keepExif: "UPLOADS_KEEP_EXIF",
29
30
  noAutoMeta: "UPLOADS_NO_AUTO_META",
30
31
  noNudge: "UPLOADS_NO_NUDGE",
32
+ noAutoPr: "UPLOADS_NO_AUTO_PR",
31
33
  };
32
34
  function isTruthyConfigFlag(value) {
33
35
  if (!value)
@@ -55,6 +57,8 @@ export function putDefaultsToConfigValues(defaults) {
55
57
  out.UPLOADS_NO_AUTO_META = "1";
56
58
  if (defaults.noNudge)
57
59
  out.UPLOADS_NO_NUDGE = "1";
60
+ if (defaults.noAutoPr)
61
+ out.UPLOADS_NO_AUTO_PR = "1";
58
62
  return out;
59
63
  }
60
64
  function parsePutDefaultsFromRaw(raw) {
@@ -80,6 +84,8 @@ function parsePutDefaultsFromRaw(raw) {
80
84
  out.noAutoMeta = true;
81
85
  if (isTruthyConfigFlag(raw.UPLOADS_NO_NUDGE))
82
86
  out.noNudge = true;
87
+ if (isTruthyConfigFlag(raw.UPLOADS_NO_AUTO_PR))
88
+ out.noAutoPr = true;
83
89
  return out;
84
90
  }
85
91
  function parsePutDefaultsFromEnv() {
@@ -102,6 +108,8 @@ function parsePutDefaultsFromEnv() {
102
108
  raw.UPLOADS_NO_AUTO_META = process.env.UPLOADS_NO_AUTO_META;
103
109
  if (process.env.UPLOADS_NO_NUDGE)
104
110
  raw.UPLOADS_NO_NUDGE = process.env.UPLOADS_NO_NUDGE;
111
+ if (process.env.UPLOADS_NO_AUTO_PR)
112
+ raw.UPLOADS_NO_AUTO_PR = process.env.UPLOADS_NO_AUTO_PR;
105
113
  return parsePutDefaultsFromRaw(raw);
106
114
  }
107
115
  /** XDG default shared across buildinternet skills (github-screenshots, uploads, …). */
@@ -175,6 +183,8 @@ export function mergePutDefaults(...layers) {
175
183
  out.noAutoMeta = layer.noAutoMeta;
176
184
  if (layer.noNudge != null)
177
185
  out.noNudge = layer.noNudge;
186
+ if (layer.noAutoPr != null)
187
+ out.noAutoPr = layer.noAutoPr;
178
188
  }
179
189
  return out;
180
190
  }
@@ -43,6 +43,25 @@ const promoteResultSchema = objectSchema({
43
43
  ]),
44
44
  },
45
45
  }, ["promoted", "skipped"]);
46
+ /** Hosted `promote` tool's `attached[]` entry (issue #702, `AttachExistingResponse`). */
47
+ const attachExistingResultSchema = objectSchema({
48
+ key: { type: "string" },
49
+ url: nullableString,
50
+ embedUrl: nullableString,
51
+ pageUrl: { type: "string" },
52
+ moved: { type: "boolean" },
53
+ source: objectSchema({ key: { type: "string" } }, ["key"]),
54
+ comment: commentResultSchema,
55
+ }, ["key", "url", "embedUrl", "moved", "source", "comment"]);
56
+ /** Hosted `promote` tool's `attachFailures[]` entry — one per bad `keys` source. */
57
+ const attachFailureSchema = objectSchema({
58
+ key: { type: "string" },
59
+ error: objectSchema({
60
+ message: { type: "string" },
61
+ code: { type: "string" },
62
+ status: { type: "number" },
63
+ }, ["message"]),
64
+ }, ["key", "error"]);
46
65
  const putObjectFields = {
47
66
  key: { type: "string" },
48
67
  url: nullableString,
@@ -209,10 +228,14 @@ export const healthResultSchema = objectSchema({
209
228
  apiUrl: { type: "string" },
210
229
  });
211
230
  export const promoteToolResultSchema = objectSchema({
231
+ // `promotion` is optional (issue #702): a `keys`-only call (no `branch`)
232
+ // never runs the branch sweep, so there's nothing to report under it.
212
233
  promotion: promoteResultSchema,
234
+ attached: { type: "array", items: attachExistingResultSchema },
235
+ attachFailures: { type: "array", items: attachFailureSchema },
213
236
  comment: commentResultSchema,
214
237
  commentError: { type: "string" },
215
- }, ["promotion"]);
238
+ });
216
239
  const galleryItemSchema = objectSchema({
217
240
  id: { type: "string" },
218
241
  objectKey: { type: "string" },