@buildinternet/uploads 0.42.1 → 0.43.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 +3 -3
- package/dist/cli-help.js +3 -3
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/hook.js +1 -0
- package/dist/commands/install.js +2 -2
- package/dist/commands/screenshot.js +7 -1
- package/dist/commands.js +14 -2
- package/dist/comment-config.d.ts +7 -54
- package/dist/comment-config.generated.d.ts +48 -0
- package/dist/comment-config.generated.js +178 -0
- package/dist/comment-config.js +7 -174
- package/dist/comment-render.generated.d.ts +151 -0
- package/dist/comment-render.generated.js +534 -0
- package/dist/github.d.ts +2 -140
- package/dist/github.js +4 -492
- package/dist/hooks-install.d.ts +10 -2
- package/dist/hooks-install.js +31 -3
- package/dist/mcp/tools.js +22 -22
- package/dist/project-context-nudge.d.ts +9 -0
- package/dist/project-context-nudge.js +39 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npx @buildinternet/uploads --help
|
|
|
15
15
|
uploads setup
|
|
16
16
|
uploads --version
|
|
17
17
|
uploads attach ./before.png ./after.png
|
|
18
|
-
uploads screenshot https://app.example --pr 123
|
|
18
|
+
uploads screenshot https://app.example --pr 123 # capture + host in one step
|
|
19
19
|
uploads screenshot ./report.html --dark --selector "main"
|
|
20
20
|
uploads screenshot http://localhost:3000 --via local --annotate ./callouts.json
|
|
21
21
|
uploads annotate ./shot.png --spec ./callouts.json --out ./shot.marked.png
|
|
@@ -24,7 +24,7 @@ uploads put ./shot.png --destination screenshots
|
|
|
24
24
|
uploads put ./shot.png --no-optimize
|
|
25
25
|
uploads put ./mobile.png --frame phone
|
|
26
26
|
uploads put ./ui.png --frame browser --frame-url "https://app.example"
|
|
27
|
-
uploads put ./after.png --pr 123
|
|
27
|
+
uploads put ./after.png --pr 123
|
|
28
28
|
uploads put ./capture-2026-…Z.png --pr 123 --name hero.png # clean leaf, stable path
|
|
29
29
|
uploads put ./shot.png --pr 123 --name hero.png --dry-run --format url # preview URL, no upload
|
|
30
30
|
uploads gallery create --title "Release screenshots"
|
|
@@ -190,7 +190,7 @@ When adding several keys, `uploads gallery add` processes them sequentially and
|
|
|
190
190
|
individual failures in `--json` output. Gallery item updates use the API's current version to
|
|
191
191
|
avoid overwriting concurrent changes.
|
|
192
192
|
|
|
193
|
-
Link a gallery to a GitHub issue or pull request with `gallery link --github`. Run `uploads comment --pr <number>`
|
|
193
|
+
Link a gallery to a GitHub issue or pull request with `gallery link --github`. Run `uploads comment --pr <number>` to refresh that target’s one managed comment with every linked gallery and loose attachment. Coordinates and strict `https://github.com/<owner>/<repo>/issues|pull/<number>` URLs are accepted; `gallery list --github` performs the authenticated reverse lookup. Links never change gallery identity, and GitHub repository visibility does not make the public gallery private.
|
|
194
194
|
|
|
195
195
|
Config layers (first match wins): CLI flags → env vars → `--env-file` → `~/.config/buildinternet/config`. See `config.example` for keys.
|
|
196
196
|
|
package/dist/cli-help.js
CHANGED
|
@@ -105,7 +105,7 @@ ${section(style, "Examples:")}
|
|
|
105
105
|
${style.command("uploads login")}
|
|
106
106
|
${style.command("uploads whoami")}
|
|
107
107
|
${style.command("uploads put")} ./shot.png --pr 123 --name hero.png
|
|
108
|
-
${style.command("uploads put")} ./after.png --pr 123
|
|
108
|
+
${style.command("uploads put")} ./after.png --pr 123
|
|
109
109
|
${style.command("uploads put")} ./bug.png --issue 45
|
|
110
110
|
${style.command("uploads put")} ./shot.png --meta path=/settings --state after
|
|
111
111
|
${style.command("uploads attach")} ./before.png ./after.png
|
|
@@ -131,7 +131,7 @@ ${section(style, "Config")} ${style.muted("(first match wins, per key):")}
|
|
|
131
131
|
${section(style, "Workspace")} ${style.muted("(within config layers):")}
|
|
132
132
|
--workspace, -w override — global (before command) or per-command (after)
|
|
133
133
|
UPLOADS_WORKSPACE env / config file
|
|
134
|
-
(else inferred from token up_<name>_…,
|
|
134
|
+
(else inferred from token up_<name>_…, or the API key's only workspace)
|
|
135
135
|
|
|
136
136
|
${section(style, "Other globals")} ${style.muted("(before command):")}
|
|
137
137
|
--api-url <url> default: https://api.uploads.sh
|
|
@@ -154,7 +154,7 @@ ${section(style, "Examples:")}
|
|
|
154
154
|
${style.command("uploads login")}
|
|
155
155
|
${style.command("uploads whoami")}
|
|
156
156
|
${style.command("uploads put")} ./shot.png --pr 123 --name hero.png
|
|
157
|
-
${style.command("uploads put")} ./after.png --pr 123
|
|
157
|
+
${style.command("uploads put")} ./after.png --pr 123
|
|
158
158
|
${style.command("uploads put")} ./bug.png --issue 45 --repo myorg/myapp
|
|
159
159
|
${style.command("uploads put")} ./shot.png --dry-run --format url
|
|
160
160
|
${style.command("uploads put")} ./shot.png --meta path=/settings --state after
|
package/dist/client.d.ts
CHANGED
|
@@ -565,7 +565,7 @@ export declare function mintWorkspaceToken(apiUrl: string, accessToken: string,
|
|
|
565
565
|
workspace: string;
|
|
566
566
|
scopes?: Array<TokenScope>;
|
|
567
567
|
label?: string;
|
|
568
|
-
ttlSeconds?: number;
|
|
568
|
+
ttlSeconds?: number | null;
|
|
569
569
|
}): Promise<MintTokenResult>;
|
|
570
570
|
/**
|
|
571
571
|
* Parse API error bodies. Prefers the nested envelope
|
package/dist/client.js
CHANGED
|
@@ -179,7 +179,7 @@ export function mintWorkspaceToken(apiUrl, accessToken, input) {
|
|
|
179
179
|
body: JSON.stringify({
|
|
180
180
|
grants: [{ workspace: input.workspace, ...(input.scopes ? { scopes: input.scopes } : {}) }],
|
|
181
181
|
...(input.label ? { label: input.label } : {}),
|
|
182
|
-
...(input.ttlSeconds ? { ttlSeconds: input.ttlSeconds } : {}),
|
|
182
|
+
...(input.ttlSeconds !== undefined ? { ttlSeconds: input.ttlSeconds } : {}),
|
|
183
183
|
}),
|
|
184
184
|
});
|
|
185
185
|
}
|
package/dist/commands/config.js
CHANGED
|
@@ -18,7 +18,7 @@ Subcommands:
|
|
|
18
18
|
Keys:
|
|
19
19
|
UPLOADS_API_URL API base URL (default: ${DEFAULT_API_URL})
|
|
20
20
|
UPLOADS_WORKSPACE Workspace / bucket tenant (default: ${DEFAULT_WORKSPACE})
|
|
21
|
-
UPLOADS_TOKEN Bearer token
|
|
21
|
+
UPLOADS_TOKEN Bearer token (up_<workspace>_…)
|
|
22
22
|
UPLOADS_SESSION_TOKEN Device-flow session (CLI version on account sessions)
|
|
23
23
|
UPLOADS_DEFAULT_PREFIX Default key prefix for put/list
|
|
24
24
|
UPLOADS_DEFAULT_REPO Default repo segment for put
|
package/dist/commands/hook.js
CHANGED
|
@@ -19,6 +19,7 @@ Usage:
|
|
|
19
19
|
uploads hook pre-pr-screenshot
|
|
20
20
|
|
|
21
21
|
Invoked by Claude Code / Codex / Grok / Cursor hooks. Never blocks.
|
|
22
|
+
Harness manifests no-op (exit 0, no output) when this binary is not on PATH.
|
|
22
23
|
|
|
23
24
|
pre-pr-screenshot
|
|
24
25
|
If the shell command is \`gh pr create\`, the branch touches UI files, and
|
package/dist/commands/install.js
CHANGED
|
@@ -2,7 +2,7 @@ import { flagBool, flagString, parseCommandArgs, UsageError, } from "../cli-args
|
|
|
2
2
|
import { resolveConfig } from "../config.js";
|
|
3
3
|
import { execRunner } from "../github-gh.js";
|
|
4
4
|
import { writeCommandHelp } from "../cli-style.js";
|
|
5
|
-
import { HOOK_COMMAND, installHookManifests } from "../hooks-install.js";
|
|
5
|
+
import { HOOK_COMMAND, HOOK_INVOCATION, installHookManifests, } from "../hooks-install.js";
|
|
6
6
|
export const DEFAULT_MCP_URL = "https://agents.uploads.sh/mcp";
|
|
7
7
|
const SKILL_SOURCE = "buildinternet/uploads";
|
|
8
8
|
const SKILL_NAMES = ["uploads-cli", "github-screenshots", "annotate-screenshots"];
|
|
@@ -15,7 +15,7 @@ hook for Grok / Cursor when those tools are present. The remote MCP endpoint
|
|
|
15
15
|
infers your workspace from the bearer token, so only the token is needed.
|
|
16
16
|
|
|
17
17
|
Claude Code and Codex ship the same reminder via their plugins (same command:
|
|
18
|
-
\`${
|
|
18
|
+
\`${HOOK_INVOCATION}\`) — install those plugins instead of relying on this step.
|
|
19
19
|
|
|
20
20
|
Safe to re-run. An MCP server already registered under this name is reported
|
|
21
21
|
as \`already configured\` and left as-is — including the token it was created
|
|
@@ -8,6 +8,7 @@ import { loadDefaultsRaw, resolveScreenshotDefaults } from "../config-file.js";
|
|
|
8
8
|
import { resolvePutPrefix } from "../destinations.js";
|
|
9
9
|
import { execRunner, ghMetadataFromTargetWithTitle, resolveRepo, } from "../github-gh.js";
|
|
10
10
|
import { deriveRepoSlugFromGit } from "../keys.js";
|
|
11
|
+
import { noProjectContextNudge } from "../project-context-nudge.js";
|
|
11
12
|
import { safeCaptureFacts } from "../capture-facts.js";
|
|
12
13
|
import { parseMetaFlags, validateMetaMap } from "../metadata.js";
|
|
13
14
|
import { mergeDerivedMeta } from "../metadata-vocab.js";
|
|
@@ -388,6 +389,9 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
388
389
|
else if (Object.keys(withFacts).length > 0) {
|
|
389
390
|
validateMetaMap(withFacts);
|
|
390
391
|
}
|
|
392
|
+
// #692 follow-up: same advisory as put — a capture with no repo/app context
|
|
393
|
+
// and only a local origin lands in the screenshots page's fallback buckets.
|
|
394
|
+
const contextNudge = !ctx.quiet && !putDefaults.noNudge && !noGit ? noProjectContextNudge(metadata) : undefined;
|
|
391
395
|
const logHuman = !ctx.quiet && format === "human";
|
|
392
396
|
if (logHuman)
|
|
393
397
|
process.stderr.write(`>> capturing ${target}\n`);
|
|
@@ -562,6 +566,8 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
562
566
|
}
|
|
563
567
|
if (bindingWarning)
|
|
564
568
|
process.stderr.write(`${bindingWarning}\n`);
|
|
569
|
+
if (contextNudge)
|
|
570
|
+
process.stderr.write(`${contextNudge}\n`);
|
|
565
571
|
process.stderr.write("\n");
|
|
566
572
|
}
|
|
567
573
|
// One JSON `hint` slot (mirrors bare put): the clip note (issue #652) wins
|
|
@@ -576,7 +582,7 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
576
582
|
const replacedHint = result.replaced && explicitMeta.state
|
|
577
583
|
? `re-capture replaced the previous state=${explicitMeta.state} object at ${result.key} — expected for repeat captures of the same URL + state`
|
|
578
584
|
: undefined;
|
|
579
|
-
const jsonHint = clipHint ?? bindingWarning ?? stagingNote ?? replacedHint;
|
|
585
|
+
const jsonHint = clipHint ?? bindingWarning ?? stagingNote ?? replacedHint ?? contextNudge;
|
|
580
586
|
switch (format) {
|
|
581
587
|
case "json":
|
|
582
588
|
await writeJson({
|
package/dist/commands.js
CHANGED
|
@@ -16,6 +16,7 @@ import { mergeSidecarMeta } from "./sidecar.js";
|
|
|
16
16
|
import { ghAttachmentKeyForMode, ghBranchAttachmentKeyForMode, ghBranchKeyPrefix, ghKeyPrefix, ghPrivateKeyPrefix, ghPrivateBranchKeyPrefix, ghMetadataFromTarget, parseGhKey, parseGhPrivateKey, ghMetadataForBranch, attachmentsCommentBody, attachmentsMarker, AUTO_RENDER_OPTIONS, GH_FALLBACK_AUTHOR_NOTE, normalizeGithubCoordinate, } from "./github.js";
|
|
17
17
|
import { resolveRepo, resolveCurrentPullRequest, resolveCurrentBranch, resolveDefaultBranch, classifyGhNumber, execRunner, timedExecRunner, ghMetadataFromTargetWithTitle, upsertAttachmentsComment, } from "./github-gh.js";
|
|
18
18
|
import { deriveRepoFromGit, deriveRepoSlugFromGit } from "./keys.js";
|
|
19
|
+
import { noProjectContextNudge } from "./project-context-nudge.js";
|
|
19
20
|
import { resolvePutPrefix } from "./destinations.js";
|
|
20
21
|
import { optimizeImageForUpload, rewriteKeyExtension, } from "./optimize.js";
|
|
21
22
|
import { applyFrame, resolveFrameId } from "./frame.js";
|
|
@@ -1812,6 +1813,11 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
1812
1813
|
if (slug)
|
|
1813
1814
|
metadata = mergeDerivedMeta(metadata, { repo: slug });
|
|
1814
1815
|
}
|
|
1816
|
+
// #692 follow-up: a path-tagged upload with no repo/gh.repo/app context and
|
|
1817
|
+
// no real (non-local) origin lands in the screenshots page's fallback
|
|
1818
|
+
// buckets — one advisory line at the moment the context went missing.
|
|
1819
|
+
// --no-git is an explicit choice, so it suppresses the nudge too.
|
|
1820
|
+
const contextNudge = !ctx.quiet && !defaults.noNudge && !noGit ? noProjectContextNudge(metadata) : undefined;
|
|
1815
1821
|
// Bare-put nudge (issue #393): only relevant when staging didn't take over
|
|
1816
1822
|
// — once `stagingTarget` resolves, staging IS the upgrade the nudge used to
|
|
1817
1823
|
// point at, so this is skipped entirely rather than firing redundantly.
|
|
@@ -1894,7 +1900,7 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
1894
1900
|
// warning); stderr prints the nudge/staging-note and binding-warning lines
|
|
1895
1901
|
// independently, below. pathHint only ever fires on the ghTarget path, so
|
|
1896
1902
|
// it never competes with the other three.
|
|
1897
|
-
const jsonHint = nudge ?? bindingWarning ?? stagingNote ?? pathHint;
|
|
1903
|
+
const jsonHint = nudge ?? bindingWarning ?? stagingNote ?? pathHint ?? contextNudge;
|
|
1898
1904
|
const galleriesByKey = new Map();
|
|
1899
1905
|
let galleryHadError = false;
|
|
1900
1906
|
if (galleryId && uploads.length > 0) {
|
|
@@ -1979,6 +1985,8 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
1979
1985
|
process.stderr.write(`${bindingWarning}\n`);
|
|
1980
1986
|
if (pathHint)
|
|
1981
1987
|
process.stderr.write(`${pathHint}\n`);
|
|
1988
|
+
if (contextNudge)
|
|
1989
|
+
process.stderr.write(`${contextNudge}\n`);
|
|
1982
1990
|
}
|
|
1983
1991
|
return failures.length === 0 && !galleryHadError ? 0 : 1;
|
|
1984
1992
|
}
|
|
@@ -2040,6 +2048,8 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
2040
2048
|
process.stderr.write(`${bindingWarning}\n`);
|
|
2041
2049
|
if (pathHint && format !== "json")
|
|
2042
2050
|
process.stderr.write(`${pathHint}\n`);
|
|
2051
|
+
if (contextNudge && format !== "json")
|
|
2052
|
+
process.stderr.write(`${contextNudge}\n`);
|
|
2043
2053
|
return gallery?.error ? 1 : 0;
|
|
2044
2054
|
}
|
|
2045
2055
|
// --- galleries ---
|
|
@@ -2726,7 +2736,9 @@ Scans the PR/issue description and comments for github.com/user-attachments
|
|
|
2726
2736
|
media, mirrors new ones into the workspace (indexed, not added to the managed
|
|
2727
2737
|
comment), and detaches ones no longer referenced. Works on any repo linked to
|
|
2728
2738
|
the workspace; the .uploads.yml ingestGithubAttachments knob only gates the
|
|
2729
|
-
automatic webhook path.
|
|
2739
|
+
automatic webhook path. Bot-authored attachments and images under 200px on
|
|
2740
|
+
either side are always skipped (the .uploads.yml ingestBotAttachments knob
|
|
2741
|
+
re-admits bot media on the webhook path only).
|
|
2730
2742
|
|
|
2731
2743
|
Examples:
|
|
2732
2744
|
uploads ingest --pr 123
|
package/dist/comment-config.d.ts
CHANGED
|
@@ -1,59 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* package's comment-config.test.ts and the canonical package's
|
|
8
|
-
* index.test.ts) — change both copies together. `readLocalRepoCommentConfig`
|
|
9
|
-
* below is CLI-only: it reads the six candidate config paths off the working
|
|
10
|
-
* tree (the server instead fetches them from GitHub's contents API —
|
|
11
|
-
* apps/api/src/repo-comment-config.ts).
|
|
2
|
+
* CLI comment-config: shared parser from the generated copy of
|
|
3
|
+
* `@uploads/comment-config`, plus the local working-tree reader.
|
|
4
|
+
* `readLocalRepoCommentConfig` is CLI-only — it reads the six candidate
|
|
5
|
+
* config paths off the working tree (the server instead fetches them from
|
|
6
|
+
* GitHub's contents API — apps/api/src/repo-comment-config.ts).
|
|
12
7
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
maxInlineImages?: number;
|
|
16
|
-
metaPath?: boolean;
|
|
17
|
-
metaState?: boolean;
|
|
18
|
-
linkToFilePage?: boolean;
|
|
19
|
-
note?: string;
|
|
20
|
-
ingestGithubAttachments?: boolean;
|
|
21
|
-
}
|
|
22
|
-
export interface WorkspaceCommentDefaults {
|
|
23
|
-
imageWidth?: "full" | number;
|
|
24
|
-
maxInlineImages?: number;
|
|
25
|
-
showMetadata?: boolean;
|
|
26
|
-
linkToFilePage?: boolean;
|
|
27
|
-
note?: string;
|
|
28
|
-
ingestGithubAttachments?: boolean;
|
|
29
|
-
}
|
|
30
|
-
export interface ResolvedCommentOptions {
|
|
31
|
-
imageWidth: "auto" | "full" | number;
|
|
32
|
-
maxInlineImages: number;
|
|
33
|
-
metaPath: boolean;
|
|
34
|
-
metaState: boolean;
|
|
35
|
-
linkToFilePage: boolean;
|
|
36
|
-
note: string | null;
|
|
37
|
-
ingestGithubAttachments: boolean;
|
|
38
|
-
}
|
|
39
|
-
export type OptionSource = "repo" | "workspace" | "auto";
|
|
40
|
-
export declare const AUTO_COMMENT_OPTIONS: ResolvedCommentOptions;
|
|
41
|
-
export declare const NOTE_MAX_CHARS = 500;
|
|
42
|
-
export declare function parseRepoCommentConfig(text: string, format: "yaml" | "json"): {
|
|
43
|
-
config: RepoCommentConfig | null;
|
|
44
|
-
warnings: string[];
|
|
45
|
-
};
|
|
46
|
-
export declare function resolveCommentOptions(repo: RepoCommentConfig | null, ws: WorkspaceCommentDefaults | null): {
|
|
47
|
-
options: ResolvedCommentOptions;
|
|
48
|
-
source: Record<keyof ResolvedCommentOptions, OptionSource>;
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Candidate paths, checked in this order — the first hit wins. Must match
|
|
52
|
-
* the server's REPO_CONFIG_PATHS exactly (apps/api/src/repo-comment-config.ts)
|
|
53
|
-
* so a committed config resolves identically whether the bot or the local
|
|
54
|
-
* gh fallback renders the comment.
|
|
55
|
-
*/
|
|
56
|
-
export declare const REPO_CONFIG_PATHS: readonly [".uploads.yml", ".uploads.yaml", ".uploads.json", ".github/uploads.yml", ".github/uploads.yaml", ".github/uploads.json"];
|
|
8
|
+
import { type RepoCommentConfig } from "./comment-config.generated.js";
|
|
9
|
+
export { AUTO_COMMENT_OPTIONS, NOTE_MAX_CHARS, REPO_CONFIG_PATHS, parseRepoCommentConfig, resolveCommentOptions, type OptionSource, type RepoCommentConfig, type ResolvedCommentOptions, type WorkspaceCommentDefaults, } from "./comment-config.generated.js";
|
|
57
10
|
/**
|
|
58
11
|
* Read the repo's comment config off the local working tree — the CLI has no
|
|
59
12
|
* GitHub App installation token to fetch via the contents API, so this reads
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED by packages/uploads/scripts/inline-shared.mjs — do not edit.
|
|
3
|
+
* Canonical source: packages/comment-config/src/index.ts
|
|
4
|
+
* The published CLI cannot import private @uploads/* packages, so this file
|
|
5
|
+
* is inlined into the tarball. Re-run the script after changing the source.
|
|
6
|
+
*/
|
|
7
|
+
export interface RepoCommentConfig {
|
|
8
|
+
imageWidth?: "auto" | "full" | number;
|
|
9
|
+
maxInlineImages?: number;
|
|
10
|
+
metaPath?: boolean;
|
|
11
|
+
metaState?: boolean;
|
|
12
|
+
linkToFilePage?: boolean;
|
|
13
|
+
note?: string;
|
|
14
|
+
ingestGithubAttachments?: boolean;
|
|
15
|
+
ingestBotAttachments?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface WorkspaceCommentDefaults {
|
|
18
|
+
imageWidth?: "full" | number;
|
|
19
|
+
maxInlineImages?: number;
|
|
20
|
+
showMetadata?: boolean;
|
|
21
|
+
linkToFilePage?: boolean;
|
|
22
|
+
note?: string;
|
|
23
|
+
ingestGithubAttachments?: boolean;
|
|
24
|
+
ingestBotAttachments?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ResolvedCommentOptions {
|
|
27
|
+
imageWidth: "auto" | "full" | number;
|
|
28
|
+
maxInlineImages: number;
|
|
29
|
+
metaPath: boolean;
|
|
30
|
+
metaState: boolean;
|
|
31
|
+
linkToFilePage: boolean;
|
|
32
|
+
note: string | null;
|
|
33
|
+
ingestGithubAttachments: boolean;
|
|
34
|
+
ingestBotAttachments: boolean;
|
|
35
|
+
}
|
|
36
|
+
export type OptionSource = "repo" | "workspace" | "auto";
|
|
37
|
+
export declare const AUTO_COMMENT_OPTIONS: ResolvedCommentOptions;
|
|
38
|
+
export declare const NOTE_MAX_CHARS = 500;
|
|
39
|
+
export declare function parseRepoCommentConfig(text: string, format: "yaml" | "json"): {
|
|
40
|
+
config: RepoCommentConfig | null;
|
|
41
|
+
warnings: string[];
|
|
42
|
+
};
|
|
43
|
+
export declare function resolveCommentOptions(repo: RepoCommentConfig | null, ws: WorkspaceCommentDefaults | null): {
|
|
44
|
+
options: ResolvedCommentOptions;
|
|
45
|
+
source: Record<keyof ResolvedCommentOptions, OptionSource>;
|
|
46
|
+
};
|
|
47
|
+
/** Candidate paths, checked in this order — the first hit wins. */
|
|
48
|
+
export declare const REPO_CONFIG_PATHS: readonly [".uploads.yml", ".uploads.yaml", ".uploads.json", ".github/uploads.yml", ".github/uploads.yaml", ".github/uploads.json"];
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED by packages/uploads/scripts/inline-shared.mjs — do not edit.
|
|
3
|
+
* Canonical source: packages/comment-config/src/index.ts
|
|
4
|
+
* The published CLI cannot import private @uploads/* packages, so this file
|
|
5
|
+
* is inlined into the tarball. Re-run the script after changing the source.
|
|
6
|
+
*/
|
|
7
|
+
import { parse as parseYaml } from "yaml";
|
|
8
|
+
export const AUTO_COMMENT_OPTIONS = {
|
|
9
|
+
imageWidth: "auto",
|
|
10
|
+
maxInlineImages: 16, // MAX_INLINE_ATTACHMENT_IMAGES — renderer copies own the constant
|
|
11
|
+
metaPath: true,
|
|
12
|
+
metaState: true,
|
|
13
|
+
linkToFilePage: true,
|
|
14
|
+
note: null,
|
|
15
|
+
ingestGithubAttachments: true,
|
|
16
|
+
ingestBotAttachments: false,
|
|
17
|
+
};
|
|
18
|
+
export const NOTE_MAX_CHARS = 500;
|
|
19
|
+
const WIDTH_MIN = 160;
|
|
20
|
+
const WIDTH_MAX = 1000;
|
|
21
|
+
const MAX_INLINE_MIN = 1;
|
|
22
|
+
const MAX_INLINE_MAX = 48;
|
|
23
|
+
const clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, Math.round(n)));
|
|
24
|
+
export function parseRepoCommentConfig(text, format) {
|
|
25
|
+
const warnings = [];
|
|
26
|
+
let root;
|
|
27
|
+
try {
|
|
28
|
+
root = format === "json" ? JSON.parse(text) : parseYaml(text);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return { config: null, warnings: ["config file could not be parsed; ignoring it"] };
|
|
32
|
+
}
|
|
33
|
+
if (typeof root !== "object" || root === null || Array.isArray(root)) {
|
|
34
|
+
return { config: null, warnings };
|
|
35
|
+
}
|
|
36
|
+
const comment = root.comment;
|
|
37
|
+
if (typeof comment !== "object" || comment === null || Array.isArray(comment)) {
|
|
38
|
+
return { config: null, warnings };
|
|
39
|
+
}
|
|
40
|
+
const c = comment;
|
|
41
|
+
const config = {};
|
|
42
|
+
// imageWidth: "auto" | "full" | finite number (clamped)
|
|
43
|
+
if ("imageWidth" in c) {
|
|
44
|
+
const v = c.imageWidth;
|
|
45
|
+
if (v === "auto" || v === "full")
|
|
46
|
+
config.imageWidth = v;
|
|
47
|
+
else if (typeof v === "number" && Number.isFinite(v))
|
|
48
|
+
config.imageWidth = clamp(v, WIDTH_MIN, WIDTH_MAX);
|
|
49
|
+
else
|
|
50
|
+
warnings.push(`imageWidth: expected "auto", "full", or a number; dropped`);
|
|
51
|
+
}
|
|
52
|
+
// maxInlineImages: finite number (clamped)
|
|
53
|
+
if ("maxInlineImages" in c) {
|
|
54
|
+
const v = c.maxInlineImages;
|
|
55
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
56
|
+
config.maxInlineImages = clamp(v, MAX_INLINE_MIN, MAX_INLINE_MAX);
|
|
57
|
+
else
|
|
58
|
+
warnings.push(`maxInlineImages: expected a number; dropped`);
|
|
59
|
+
}
|
|
60
|
+
// linkToFilePage: boolean
|
|
61
|
+
if ("linkToFilePage" in c) {
|
|
62
|
+
const v = c.linkToFilePage;
|
|
63
|
+
if (typeof v === "boolean")
|
|
64
|
+
config.linkToFilePage = v;
|
|
65
|
+
else
|
|
66
|
+
warnings.push(`linkToFilePage: expected a boolean; dropped`);
|
|
67
|
+
}
|
|
68
|
+
// ingestGithubAttachments: boolean
|
|
69
|
+
if ("ingestGithubAttachments" in c) {
|
|
70
|
+
const v = c.ingestGithubAttachments;
|
|
71
|
+
if (typeof v === "boolean")
|
|
72
|
+
config.ingestGithubAttachments = v;
|
|
73
|
+
else
|
|
74
|
+
warnings.push(`ingestGithubAttachments: expected a boolean; dropped`);
|
|
75
|
+
}
|
|
76
|
+
// ingestBotAttachments: boolean
|
|
77
|
+
if ("ingestBotAttachments" in c) {
|
|
78
|
+
const v = c.ingestBotAttachments;
|
|
79
|
+
if (typeof v === "boolean")
|
|
80
|
+
config.ingestBotAttachments = v;
|
|
81
|
+
else
|
|
82
|
+
warnings.push(`ingestBotAttachments: expected a boolean; dropped`);
|
|
83
|
+
}
|
|
84
|
+
// meta.path / meta.state: booleans nested under `meta`
|
|
85
|
+
if ("meta" in c) {
|
|
86
|
+
const v = c.meta;
|
|
87
|
+
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
88
|
+
const meta = v;
|
|
89
|
+
if ("path" in meta) {
|
|
90
|
+
if (typeof meta.path === "boolean")
|
|
91
|
+
config.metaPath = meta.path;
|
|
92
|
+
else
|
|
93
|
+
warnings.push(`meta.path: expected a boolean; dropped`);
|
|
94
|
+
}
|
|
95
|
+
if ("state" in meta) {
|
|
96
|
+
if (typeof meta.state === "boolean")
|
|
97
|
+
config.metaState = meta.state;
|
|
98
|
+
else
|
|
99
|
+
warnings.push(`meta.state: expected a boolean; dropped`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
warnings.push(`meta: expected an object; dropped`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// note: non-empty trimmed string, max NOTE_MAX_CHARS (never truncated)
|
|
107
|
+
if ("note" in c) {
|
|
108
|
+
const v = c.note;
|
|
109
|
+
if (typeof v === "string") {
|
|
110
|
+
const trimmed = v.trim();
|
|
111
|
+
if (trimmed.length === 0) {
|
|
112
|
+
// empty/whitespace note is treated as absent
|
|
113
|
+
}
|
|
114
|
+
else if (trimmed.length > NOTE_MAX_CHARS) {
|
|
115
|
+
warnings.push(`note: longer than ${NOTE_MAX_CHARS} characters; dropped (not truncated)`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
config.note = trimmed;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
warnings.push(`note: expected a string; dropped`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { config, warnings };
|
|
126
|
+
}
|
|
127
|
+
export function resolveCommentOptions(repo, ws) {
|
|
128
|
+
const wsAsRepo = {
|
|
129
|
+
...(ws?.imageWidth !== undefined ? { imageWidth: ws.imageWidth } : {}),
|
|
130
|
+
...(ws?.maxInlineImages !== undefined ? { maxInlineImages: ws.maxInlineImages } : {}),
|
|
131
|
+
...(ws?.showMetadata !== undefined
|
|
132
|
+
? { metaPath: ws.showMetadata, metaState: ws.showMetadata }
|
|
133
|
+
: {}),
|
|
134
|
+
...(ws?.linkToFilePage !== undefined ? { linkToFilePage: ws.linkToFilePage } : {}),
|
|
135
|
+
...(ws?.note ? { note: ws.note } : {}),
|
|
136
|
+
...(ws?.ingestGithubAttachments !== undefined
|
|
137
|
+
? { ingestGithubAttachments: ws.ingestGithubAttachments }
|
|
138
|
+
: {}),
|
|
139
|
+
...(ws?.ingestBotAttachments !== undefined
|
|
140
|
+
? { ingestBotAttachments: ws.ingestBotAttachments }
|
|
141
|
+
: {}),
|
|
142
|
+
};
|
|
143
|
+
const options = { ...AUTO_COMMENT_OPTIONS };
|
|
144
|
+
const source = Object.fromEntries(Object.keys(AUTO_COMMENT_OPTIONS).map((k) => [k, "auto"]));
|
|
145
|
+
const apply = (cfg, from) => {
|
|
146
|
+
for (const key of [
|
|
147
|
+
"imageWidth",
|
|
148
|
+
"maxInlineImages",
|
|
149
|
+
"metaPath",
|
|
150
|
+
"metaState",
|
|
151
|
+
"linkToFilePage",
|
|
152
|
+
"ingestGithubAttachments",
|
|
153
|
+
"ingestBotAttachments",
|
|
154
|
+
]) {
|
|
155
|
+
if (cfg[key] !== undefined && source[key] === "auto") {
|
|
156
|
+
options[key] = cfg[key];
|
|
157
|
+
source[key] = from;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (cfg.note !== undefined && source.note === "auto") {
|
|
161
|
+
options.note = cfg.note;
|
|
162
|
+
source.note = from;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
if (repo)
|
|
166
|
+
apply(repo, "repo");
|
|
167
|
+
apply(wsAsRepo, "workspace");
|
|
168
|
+
return { options, source };
|
|
169
|
+
}
|
|
170
|
+
/** Candidate paths, checked in this order — the first hit wins. */
|
|
171
|
+
export const REPO_CONFIG_PATHS = [
|
|
172
|
+
".uploads.yml",
|
|
173
|
+
".uploads.yaml",
|
|
174
|
+
".uploads.json",
|
|
175
|
+
".github/uploads.yml",
|
|
176
|
+
".github/uploads.yaml",
|
|
177
|
+
".github/uploads.json",
|
|
178
|
+
];
|