@buildinternet/uploads 0.42.2 → 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/dist/cli-help.js +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/screenshot.js +7 -1
- package/dist/commands.js +14 -2
- package/dist/comment-config.generated.d.ts +3 -0
- package/dist/comment-config.generated.js +14 -1
- 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/dist/cli-help.js
CHANGED
|
@@ -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
|
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
|
|
@@ -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
|
|
@@ -12,6 +12,7 @@ export interface RepoCommentConfig {
|
|
|
12
12
|
linkToFilePage?: boolean;
|
|
13
13
|
note?: string;
|
|
14
14
|
ingestGithubAttachments?: boolean;
|
|
15
|
+
ingestBotAttachments?: boolean;
|
|
15
16
|
}
|
|
16
17
|
export interface WorkspaceCommentDefaults {
|
|
17
18
|
imageWidth?: "full" | number;
|
|
@@ -20,6 +21,7 @@ export interface WorkspaceCommentDefaults {
|
|
|
20
21
|
linkToFilePage?: boolean;
|
|
21
22
|
note?: string;
|
|
22
23
|
ingestGithubAttachments?: boolean;
|
|
24
|
+
ingestBotAttachments?: boolean;
|
|
23
25
|
}
|
|
24
26
|
export interface ResolvedCommentOptions {
|
|
25
27
|
imageWidth: "auto" | "full" | number;
|
|
@@ -29,6 +31,7 @@ export interface ResolvedCommentOptions {
|
|
|
29
31
|
linkToFilePage: boolean;
|
|
30
32
|
note: string | null;
|
|
31
33
|
ingestGithubAttachments: boolean;
|
|
34
|
+
ingestBotAttachments: boolean;
|
|
32
35
|
}
|
|
33
36
|
export type OptionSource = "repo" | "workspace" | "auto";
|
|
34
37
|
export declare const AUTO_COMMENT_OPTIONS: ResolvedCommentOptions;
|
|
@@ -12,7 +12,8 @@ export const AUTO_COMMENT_OPTIONS = {
|
|
|
12
12
|
metaState: true,
|
|
13
13
|
linkToFilePage: true,
|
|
14
14
|
note: null,
|
|
15
|
-
ingestGithubAttachments:
|
|
15
|
+
ingestGithubAttachments: true,
|
|
16
|
+
ingestBotAttachments: false,
|
|
16
17
|
};
|
|
17
18
|
export const NOTE_MAX_CHARS = 500;
|
|
18
19
|
const WIDTH_MIN = 160;
|
|
@@ -72,6 +73,14 @@ export function parseRepoCommentConfig(text, format) {
|
|
|
72
73
|
else
|
|
73
74
|
warnings.push(`ingestGithubAttachments: expected a boolean; dropped`);
|
|
74
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
|
+
}
|
|
75
84
|
// meta.path / meta.state: booleans nested under `meta`
|
|
76
85
|
if ("meta" in c) {
|
|
77
86
|
const v = c.meta;
|
|
@@ -127,6 +136,9 @@ export function resolveCommentOptions(repo, ws) {
|
|
|
127
136
|
...(ws?.ingestGithubAttachments !== undefined
|
|
128
137
|
? { ingestGithubAttachments: ws.ingestGithubAttachments }
|
|
129
138
|
: {}),
|
|
139
|
+
...(ws?.ingestBotAttachments !== undefined
|
|
140
|
+
? { ingestBotAttachments: ws.ingestBotAttachments }
|
|
141
|
+
: {}),
|
|
130
142
|
};
|
|
131
143
|
const options = { ...AUTO_COMMENT_OPTIONS };
|
|
132
144
|
const source = Object.fromEntries(Object.keys(AUTO_COMMENT_OPTIONS).map((k) => [k, "auto"]));
|
|
@@ -138,6 +150,7 @@ export function resolveCommentOptions(repo, ws) {
|
|
|
138
150
|
"metaState",
|
|
139
151
|
"linkToFilePage",
|
|
140
152
|
"ingestGithubAttachments",
|
|
153
|
+
"ingestBotAttachments",
|
|
141
154
|
]) {
|
|
142
155
|
if (cfg[key] !== undefined && source[key] === "auto") {
|
|
143
156
|
options[key] = cfg[key];
|
package/dist/mcp/tools.js
CHANGED
|
@@ -108,7 +108,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
108
108
|
const { globals } = opts;
|
|
109
109
|
const run = opts.runner ?? execRunner;
|
|
110
110
|
const clientFactory = opts.clientFactory ?? createUploadsClient;
|
|
111
|
-
function clientFor(args, requireToken = true) {
|
|
111
|
+
async function clientFor(args, requireToken = true) {
|
|
112
112
|
const config = resolveConfig({
|
|
113
113
|
apiUrl: globals.apiUrl,
|
|
114
114
|
token: globals.token,
|
|
@@ -151,7 +151,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
151
151
|
const title = optString(args, "title");
|
|
152
152
|
if (!title)
|
|
153
153
|
usage("title is required");
|
|
154
|
-
const { client } = clientFor(args);
|
|
154
|
+
const { client } = await clientFor(args);
|
|
155
155
|
return client.createGallery({ title, description: optString(args, "description") });
|
|
156
156
|
},
|
|
157
157
|
},
|
|
@@ -171,7 +171,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
171
171
|
additionalProperties: false,
|
|
172
172
|
},
|
|
173
173
|
async handler(args) {
|
|
174
|
-
const { client } = clientFor(args);
|
|
174
|
+
const { client } = await clientFor(args);
|
|
175
175
|
return client.getGallery(galleryId(args));
|
|
176
176
|
},
|
|
177
177
|
},
|
|
@@ -197,7 +197,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
197
197
|
const objectKey = optString(args, "objectKey");
|
|
198
198
|
if (!objectKey)
|
|
199
199
|
usage("objectKey is required");
|
|
200
|
-
const { client } = clientFor(args);
|
|
200
|
+
const { client } = await clientFor(args);
|
|
201
201
|
const id = galleryId(args);
|
|
202
202
|
const current = await client.getGallery(id);
|
|
203
203
|
return client.addGalleryItem(id, objectKey, {
|
|
@@ -228,7 +228,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
228
228
|
additionalProperties: false,
|
|
229
229
|
},
|
|
230
230
|
async handler(args) {
|
|
231
|
-
const { client } = clientFor(args);
|
|
231
|
+
const { client } = await clientFor(args);
|
|
232
232
|
const id = galleryId(args);
|
|
233
233
|
const current = await client.getGallery(id);
|
|
234
234
|
return client.linkGalleryExternalReference(id, {
|
|
@@ -259,7 +259,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
259
259
|
additionalProperties: false,
|
|
260
260
|
},
|
|
261
261
|
async handler(args) {
|
|
262
|
-
const { client } = clientFor(args);
|
|
262
|
+
const { client } = await clientFor(args);
|
|
263
263
|
return client.findGalleriesByReference({
|
|
264
264
|
...galleryReference(args),
|
|
265
265
|
limit: optPosInt(args, "limit"),
|
|
@@ -426,7 +426,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
426
426
|
catch (err) {
|
|
427
427
|
usage(err instanceof Error ? err.message : String(err));
|
|
428
428
|
}
|
|
429
|
-
const { config, client } = clientFor(args);
|
|
429
|
+
const { config, client } = await clientFor(args);
|
|
430
430
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
431
431
|
const frameOpts = mcpFrameOptions(args);
|
|
432
432
|
const optimizeOpts = mcpOptimizeOptions(args, defaults);
|
|
@@ -736,7 +736,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
736
736
|
const metadata = metadataArgWithCanonical(args);
|
|
737
737
|
if (metadata)
|
|
738
738
|
validateMetaMap(metadata);
|
|
739
|
-
const { config, client } = clientFor(args);
|
|
739
|
+
const { config, client } = await clientFor(args);
|
|
740
740
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
741
741
|
const frameOpts = mcpFrameOptions(args);
|
|
742
742
|
const optimizeOpts = mcpOptimizeOptions(args, defaults);
|
|
@@ -969,7 +969,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
969
969
|
const explicitTarget = ghTargetFromArgs(args, run);
|
|
970
970
|
const target = explicitTarget ??
|
|
971
971
|
resolveCurrentPullRequest(resolveRepo(optString(args, "repo"), run), run);
|
|
972
|
-
const { config, client } = clientFor(args);
|
|
972
|
+
const { config, client } = await clientFor(args);
|
|
973
973
|
const contentType = optString(args, "contentType");
|
|
974
974
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
975
975
|
const frameOpts = mcpFrameOptions(args);
|
|
@@ -1037,7 +1037,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1037
1037
|
const prefixArg = optString(args, "prefix");
|
|
1038
1038
|
let prefix = prefixArg ?? (defaults.prefix ? `${defaults.prefix}/` : undefined);
|
|
1039
1039
|
const target = ghTargetFromArgs(args, run);
|
|
1040
|
-
const { client } = clientFor(args);
|
|
1040
|
+
const { client } = await clientFor(args);
|
|
1041
1041
|
// Also list every active private prefix, if any (issue #631) —
|
|
1042
1042
|
// mirrors syncAttachmentsComment's gh-fallback gather: a repo's
|
|
1043
1043
|
// attachment history can be split across the plain shape and
|
|
@@ -1093,7 +1093,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1093
1093
|
additionalProperties: false,
|
|
1094
1094
|
},
|
|
1095
1095
|
async handler(args) {
|
|
1096
|
-
const { client } = clientFor(args);
|
|
1096
|
+
const { client } = await clientFor(args);
|
|
1097
1097
|
const repo = resolveRepo(optString(args, "repo"), run);
|
|
1098
1098
|
const branch = optString(args, "branch") ?? resolveCurrentBranch(run);
|
|
1099
1099
|
return resolveStaged({ client, repo, branch });
|
|
@@ -1124,7 +1124,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1124
1124
|
usage("key is required");
|
|
1125
1125
|
if (optBool(args, "dryRun"))
|
|
1126
1126
|
return { key, deleted: false, dryRun: true };
|
|
1127
|
-
const { client } = clientFor(args);
|
|
1127
|
+
const { client } = await clientFor(args);
|
|
1128
1128
|
return client.delete(key);
|
|
1129
1129
|
},
|
|
1130
1130
|
},
|
|
@@ -1147,7 +1147,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1147
1147
|
const key = optString(args, "key");
|
|
1148
1148
|
if (!key)
|
|
1149
1149
|
usage("key is required");
|
|
1150
|
-
return clientFor(args).client.getMetadata(key);
|
|
1150
|
+
return (await clientFor(args)).client.getMetadata(key);
|
|
1151
1151
|
},
|
|
1152
1152
|
},
|
|
1153
1153
|
{
|
|
@@ -1184,7 +1184,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1184
1184
|
}
|
|
1185
1185
|
if (set)
|
|
1186
1186
|
validateMetaMap(set);
|
|
1187
|
-
const { client } = clientFor(args);
|
|
1187
|
+
const { client } = await clientFor(args);
|
|
1188
1188
|
return client.patchMetadata(key, { set, delete: del });
|
|
1189
1189
|
},
|
|
1190
1190
|
},
|
|
@@ -1223,7 +1223,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1223
1223
|
}
|
|
1224
1224
|
if (hasMeta)
|
|
1225
1225
|
validateMetaMap(filters);
|
|
1226
|
-
const { client } = clientFor(args);
|
|
1226
|
+
const { client } = await clientFor(args);
|
|
1227
1227
|
return client.findFiles(filters, {
|
|
1228
1228
|
name,
|
|
1229
1229
|
prefix: optString(args, "prefix"),
|
|
@@ -1249,7 +1249,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1249
1249
|
additionalProperties: false,
|
|
1250
1250
|
},
|
|
1251
1251
|
async handler(args) {
|
|
1252
|
-
const { client } = clientFor(args);
|
|
1252
|
+
const { client } = await clientFor(args);
|
|
1253
1253
|
const key = optString(args, "key");
|
|
1254
1254
|
return key ? client.listMetadataValues(key) : client.listMetadataKeys();
|
|
1255
1255
|
},
|
|
@@ -1266,7 +1266,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1266
1266
|
additionalProperties: false,
|
|
1267
1267
|
},
|
|
1268
1268
|
async handler(args) {
|
|
1269
|
-
const { client } = clientFor(args);
|
|
1269
|
+
const { client } = await clientFor(args);
|
|
1270
1270
|
return client.usage();
|
|
1271
1271
|
},
|
|
1272
1272
|
},
|
|
@@ -1282,7 +1282,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1282
1282
|
additionalProperties: false,
|
|
1283
1283
|
},
|
|
1284
1284
|
async handler(args) {
|
|
1285
|
-
const { client } = clientFor(args);
|
|
1285
|
+
const { client } = await clientFor(args);
|
|
1286
1286
|
return client.reconcile();
|
|
1287
1287
|
},
|
|
1288
1288
|
},
|
|
@@ -1298,7 +1298,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1298
1298
|
additionalProperties: false,
|
|
1299
1299
|
},
|
|
1300
1300
|
async handler(args) {
|
|
1301
|
-
const { client } = clientFor(args);
|
|
1301
|
+
const { client } = await clientFor(args);
|
|
1302
1302
|
return client.purgeExpired();
|
|
1303
1303
|
},
|
|
1304
1304
|
},
|
|
@@ -1320,7 +1320,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1320
1320
|
const target = ghTargetFromArgs(args, run);
|
|
1321
1321
|
if (!target)
|
|
1322
1322
|
usage("comment requires pr or issue");
|
|
1323
|
-
const { config, client } = clientFor(args);
|
|
1323
|
+
const { config, client } = await clientFor(args);
|
|
1324
1324
|
// Explicit resync, same as `uploads comment` (issue #480).
|
|
1325
1325
|
const result = await syncAttachmentsComment(client, target, run, config.workspace, {
|
|
1326
1326
|
resync: true,
|
|
@@ -1336,7 +1336,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1336
1336
|
description: "Check uploads.sh API liveness. No auth or arguments required.",
|
|
1337
1337
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1338
1338
|
async handler(args) {
|
|
1339
|
-
const { config, client } = clientFor(args, false);
|
|
1339
|
+
const { config, client } = await clientFor(args, false);
|
|
1340
1340
|
const result = await client.health();
|
|
1341
1341
|
return { ...result, apiUrl: config.apiUrl };
|
|
1342
1342
|
},
|
|
@@ -1353,7 +1353,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1353
1353
|
additionalProperties: false,
|
|
1354
1354
|
},
|
|
1355
1355
|
async handler(args) {
|
|
1356
|
-
const { config, client } = clientFor(args);
|
|
1356
|
+
const { config, client } = await clientFor(args);
|
|
1357
1357
|
return buildDoctorReport(config, client);
|
|
1358
1358
|
},
|
|
1359
1359
|
},
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-project-context nudge (issue #692 follow-up): a path-tagged upload with
|
|
3
|
+
* no repo/gh.repo/app metadata and no real (non-local) origin lands in the
|
|
4
|
+
* screenshots page's "local dev" / "Other" fallback buckets. One advisory
|
|
5
|
+
* stderr line teaches the fix at the moment the context went missing. The
|
|
6
|
+
* predicate mirrors apps/api's projectLabelFromMeta fallback rules exactly —
|
|
7
|
+
* a real URL host is a meaningful group, so it never fires there.
|
|
8
|
+
*/
|
|
9
|
+
export declare function noProjectContextNudge(meta: Record<string, string> | undefined): string | undefined;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-project-context nudge (issue #692 follow-up): a path-tagged upload with
|
|
3
|
+
* no repo/gh.repo/app metadata and no real (non-local) origin lands in the
|
|
4
|
+
* screenshots page's "local dev" / "Other" fallback buckets. One advisory
|
|
5
|
+
* stderr line teaches the fix at the moment the context went missing. The
|
|
6
|
+
* predicate mirrors apps/api's projectLabelFromMeta fallback rules exactly —
|
|
7
|
+
* a real URL host is a meaningful group, so it never fires there.
|
|
8
|
+
*/
|
|
9
|
+
/** Hosts that identify a dev machine, not an app — same set as the API's
|
|
10
|
+
* isLocalHostname (apps/api/src/file-metadata.ts). */
|
|
11
|
+
function isLocalHostname(hostname) {
|
|
12
|
+
const bare = hostname.toLowerCase();
|
|
13
|
+
return (bare === "localhost" ||
|
|
14
|
+
bare.endsWith(".localhost") ||
|
|
15
|
+
bare === "127.0.0.1" ||
|
|
16
|
+
bare === "0.0.0.0" ||
|
|
17
|
+
bare === "[::1]");
|
|
18
|
+
}
|
|
19
|
+
export function noProjectContextNudge(meta) {
|
|
20
|
+
// Only `path`-tagged uploads appear on the screenshots page at all.
|
|
21
|
+
if (!meta?.path)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (meta.repo || meta["gh.repo"] || meta.app)
|
|
24
|
+
return undefined;
|
|
25
|
+
let bucket = "Other";
|
|
26
|
+
if (meta.url) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(meta.url);
|
|
29
|
+
if (!isLocalHostname(parsed.hostname))
|
|
30
|
+
return undefined; // real host = real group
|
|
31
|
+
bucket = "local dev";
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// unparseable url is just "no url"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return (`note: no repo detected — the screenshots page will group this under "${bucket}". ` +
|
|
38
|
+
`Run from inside your project repo, or pass --app <name>.`);
|
|
39
|
+
}
|