@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.
- package/dist/cli-help.js +1 -1
- package/dist/client.d.ts +36 -1
- package/dist/client.js +15 -1
- package/dist/commands/config.js +1 -1
- package/dist/commands/hook.d.ts +6 -2
- package/dist/commands/hook.js +31 -9
- package/dist/commands/screenshot.js +96 -20
- package/dist/commands.d.ts +76 -0
- package/dist/commands.js +326 -78
- package/dist/comment-config.generated.d.ts +6 -0
- package/dist/comment-config.generated.js +27 -1
- package/dist/comment-render.generated.d.ts +4 -1
- package/dist/comment-render.generated.js +15 -2
- package/dist/config-file.d.ts +4 -1
- package/dist/config-file.js +10 -0
- package/dist/mcp/output-schemas.js +24 -1
- package/dist/mcp/tools.js +154 -49
- package/dist/project-context-nudge.d.ts +9 -0
- package/dist/project-context-nudge.js +39 -0
- package/package.json +1 -1
package/dist/mcp/tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createUploadsClient } from "../client.js";
|
|
2
|
-
import { buildDoctorReport, ghListPrefixes, ghMergedList, makeGhTarget, mergeStagingMeta, resolveGhPrefixSafe, resolvePutStagingTarget, resolveStaged, syncAttachmentsComment, uploadAttachments, uploadPreparedImage, uploadPuts, } from "../commands.js";
|
|
2
|
+
import { buildDoctorReport, ghListPrefixes, ghMergedList, makeGhTarget, mergeStagingMeta, resolveAutoPrTarget, resolveGhPrefixSafe, resolvePutNudgeContext, resolvePutStagingTarget, resolveStaged, syncAttachmentsComment, autoPrNoteText, putNudgeText, 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";
|
|
@@ -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"),
|
|
@@ -345,9 +345,13 @@ export function createUploadsMcpTools(opts) {
|
|
|
345
345
|
},
|
|
346
346
|
...frameProps,
|
|
347
347
|
noGit: { type: "boolean", description: "Don't derive the repo segment from git." },
|
|
348
|
+
noPr: {
|
|
349
|
+
type: "boolean",
|
|
350
|
+
description: "Skip auto-PR context (issue #700): without pr/issue/key/ref/prefix/destination, a call on a branch mapping to exactly one open PR otherwise behaves as if pr had been passed (stable key + managed comment sync when comment is set). Also opts out via UPLOADS_NO_AUTO_PR=1.",
|
|
351
|
+
},
|
|
348
352
|
comment: {
|
|
349
353
|
type: "boolean",
|
|
350
|
-
description: "With pr/issue: create or update the managed attachments comment. Posts as uploads-sh[bot] when the GitHub App is installed on the repo; otherwise via local gh auth (best-effort).",
|
|
354
|
+
description: "With pr/issue (or auto-detected PR context): create or update the managed attachments comment. Posts as uploads-sh[bot] when the GitHub App is installed on the repo; otherwise via local gh auth (best-effort).",
|
|
351
355
|
},
|
|
352
356
|
dryRun: {
|
|
353
357
|
type: "boolean",
|
|
@@ -426,21 +430,44 @@ export function createUploadsMcpTools(opts) {
|
|
|
426
430
|
catch (err) {
|
|
427
431
|
usage(err instanceof Error ? err.message : String(err));
|
|
428
432
|
}
|
|
429
|
-
const { config, client } = clientFor(args);
|
|
433
|
+
const { config, client } = await clientFor(args);
|
|
430
434
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
431
435
|
const frameOpts = mcpFrameOptions(args);
|
|
432
436
|
const optimizeOpts = mcpOptimizeOptions(args, defaults);
|
|
433
437
|
const noGit = optBool(args, "noGit") || defaults.noGit === true;
|
|
438
|
+
const noAutoPr = optBool(args, "noPr") || defaults.noAutoPr === true;
|
|
434
439
|
const alt = optString(args, "alt");
|
|
435
440
|
const width = optPosInt(args, "width") ?? defaults.width;
|
|
436
441
|
const contentType = optString(args, "contentType");
|
|
442
|
+
// Auto-PR context (issue #700): local stdio MCP put mirrors the CLI
|
|
443
|
+
// default — no pr/issue/key/ref/prefix/destination, not noGit/noPr,
|
|
444
|
+
// on a branch that maps to exactly one open PR behaves as if `pr`
|
|
445
|
+
// had been passed (stable key + managed comment sync) instead of
|
|
446
|
+
// the #403 staging default below. Never throws — see
|
|
447
|
+
// resolveAutoPrTarget.
|
|
448
|
+
const autoPrTarget = target
|
|
449
|
+
? undefined
|
|
450
|
+
: resolveAutoPrTarget({
|
|
451
|
+
ghTarget: target,
|
|
452
|
+
keyHint: keyArg,
|
|
453
|
+
refArg,
|
|
454
|
+
prefixArg,
|
|
455
|
+
destinationArg: destArg,
|
|
456
|
+
noGit,
|
|
457
|
+
noAutoPr,
|
|
458
|
+
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
459
|
+
run,
|
|
460
|
+
});
|
|
461
|
+
const effectiveTarget = target ?? autoPrTarget;
|
|
437
462
|
// Bare-put branch staging (issue #403): local stdio MCP put mirrors
|
|
438
463
|
// the CLI default — no pr/issue/key/ref/prefix/destination, not
|
|
439
464
|
// noGit, on a non-default git branch stages to the branch prefix
|
|
440
465
|
// (identical key/metadata to `attach --branch`) instead of the
|
|
441
466
|
// dated layout. Never throws — see resolvePutStagingTarget.
|
|
467
|
+
// effectiveTarget (explicit pr/issue OR the #700 auto-PR match)
|
|
468
|
+
// wins over staging, same as it wins over the dated layout.
|
|
442
469
|
const stagingTarget = resolvePutStagingTarget({
|
|
443
|
-
ghTarget:
|
|
470
|
+
ghTarget: effectiveTarget,
|
|
444
471
|
keyHint: keyArg,
|
|
445
472
|
refArg,
|
|
446
473
|
prefixArg,
|
|
@@ -449,6 +476,21 @@ export function createUploadsMcpTools(opts) {
|
|
|
449
476
|
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
450
477
|
run,
|
|
451
478
|
});
|
|
479
|
+
// Bare-put nudge context (issue #393/#700): only relevant when
|
|
480
|
+
// neither auto-PR nor staging took over. Finished into a hint once
|
|
481
|
+
// upload keys exist, below.
|
|
482
|
+
const nudgeCtx = effectiveTarget || stagingTarget
|
|
483
|
+
? undefined
|
|
484
|
+
: resolvePutNudgeContext({
|
|
485
|
+
quiet: false,
|
|
486
|
+
noNudge: defaults.noNudge === true,
|
|
487
|
+
ghTarget: target,
|
|
488
|
+
keyHint: keyArg,
|
|
489
|
+
noGit,
|
|
490
|
+
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
491
|
+
run,
|
|
492
|
+
});
|
|
493
|
+
const autoPrHint = autoPrTarget ? autoPrNoteText(autoPrTarget.num) : undefined;
|
|
452
494
|
// Derived `repo` metadata (spec: 2026-08-11-screenshots-project-grouping-design.md).
|
|
453
495
|
// Same derivation the CLI does; MCP always derives (no --no-auto), so
|
|
454
496
|
// this is only suppressed by noGit. metadataProp's contract: omitting
|
|
@@ -468,7 +510,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
468
510
|
: metadataWithRepo;
|
|
469
511
|
const putShared = {
|
|
470
512
|
client,
|
|
471
|
-
ghTarget:
|
|
513
|
+
ghTarget: effectiveTarget,
|
|
472
514
|
ghBranchTarget: stagingTarget,
|
|
473
515
|
prefix: resolvedPrefix ?? defaults.prefix,
|
|
474
516
|
repo: optString(args, "repo") ?? defaults.repo,
|
|
@@ -496,11 +538,15 @@ export function createUploadsMcpTools(opts) {
|
|
|
496
538
|
if (uploads.length === 0 && failures.length > 0) {
|
|
497
539
|
throw new ToolBatchError(batchFailureMessage(failures), { uploads, failures });
|
|
498
540
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
541
|
+
const hint = autoPrHint ??
|
|
542
|
+
(nudgeCtx && uploads.length > 0
|
|
543
|
+
? putNudgeText(nudgeCtx.branch, nudgeCtx.pr, uploads.map((u) => u.key))
|
|
544
|
+
: undefined);
|
|
545
|
+
if (wantComment && effectiveTarget && uploads.length > 0) {
|
|
546
|
+
const { comment, commentError } = await syncComment(client, effectiveTarget, config.workspace);
|
|
547
|
+
return { uploads, failures, comment, commentError, ...(hint ? { hint } : {}) };
|
|
502
548
|
}
|
|
503
|
-
return { uploads, failures };
|
|
549
|
+
return { uploads, failures, ...(hint ? { hint } : {}) };
|
|
504
550
|
}
|
|
505
551
|
// Single-file: contentBase64 still supported; paths go through uploadPuts.
|
|
506
552
|
if (contentBase64 !== undefined) {
|
|
@@ -509,7 +555,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
509
555
|
const { result, prepared, markdown } = await uploadPreparedImage(client, bytes, sourceName, {
|
|
510
556
|
frame: frameOpts,
|
|
511
557
|
optimize: optimizeOpts,
|
|
512
|
-
ghTarget:
|
|
558
|
+
ghTarget: effectiveTarget,
|
|
513
559
|
ghBranchTarget: stagingTarget,
|
|
514
560
|
key: keyArg,
|
|
515
561
|
prefix: resolvedPrefix ?? defaults.prefix,
|
|
@@ -531,9 +577,19 @@ export function createUploadsMcpTools(opts) {
|
|
|
531
577
|
outputBytes: prepared.outputBytes,
|
|
532
578
|
filename: prepared.filename,
|
|
533
579
|
};
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
580
|
+
const hint = autoPrHint ??
|
|
581
|
+
(nudgeCtx ? putNudgeText(nudgeCtx.branch, nudgeCtx.pr, [result.key]) : undefined);
|
|
582
|
+
if (wantComment && effectiveTarget) {
|
|
583
|
+
const { comment, commentError } = await syncComment(client, effectiveTarget, config.workspace);
|
|
584
|
+
return {
|
|
585
|
+
...result,
|
|
586
|
+
markdown,
|
|
587
|
+
optimize,
|
|
588
|
+
frame: prepared.frame,
|
|
589
|
+
comment,
|
|
590
|
+
commentError,
|
|
591
|
+
...(hint ? { hint } : {}),
|
|
592
|
+
};
|
|
537
593
|
}
|
|
538
594
|
return {
|
|
539
595
|
...result,
|
|
@@ -541,6 +597,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
541
597
|
optimize,
|
|
542
598
|
frame: prepared.frame,
|
|
543
599
|
...(dryRun ? { dryRun: true } : {}),
|
|
600
|
+
...(hint ? { hint } : {}),
|
|
544
601
|
};
|
|
545
602
|
}
|
|
546
603
|
const { uploads, failures, firstError } = await uploadPuts({
|
|
@@ -553,6 +610,8 @@ export function createUploadsMcpTools(opts) {
|
|
|
553
610
|
throw firstError instanceof Error ? firstError : new Error(String(firstError));
|
|
554
611
|
}
|
|
555
612
|
const u = uploads[0];
|
|
613
|
+
const hint = autoPrHint ??
|
|
614
|
+
(nudgeCtx ? putNudgeText(nudgeCtx.branch, nudgeCtx.pr, [u.key]) : undefined);
|
|
556
615
|
const flat = {
|
|
557
616
|
workspace: u.workspace,
|
|
558
617
|
key: u.key,
|
|
@@ -566,9 +625,10 @@ export function createUploadsMcpTools(opts) {
|
|
|
566
625
|
optimize: u.optimize,
|
|
567
626
|
frame: u.frame,
|
|
568
627
|
...(dryRun ? { dryRun: true } : {}),
|
|
628
|
+
...(hint ? { hint } : {}),
|
|
569
629
|
};
|
|
570
|
-
if (wantComment &&
|
|
571
|
-
const { comment, commentError } = await syncComment(client,
|
|
630
|
+
if (wantComment && effectiveTarget) {
|
|
631
|
+
const { comment, commentError } = await syncComment(client, effectiveTarget, config.workspace);
|
|
572
632
|
return { ...flat, comment, commentError };
|
|
573
633
|
}
|
|
574
634
|
return flat;
|
|
@@ -673,9 +733,13 @@ export function createUploadsMcpTools(opts) {
|
|
|
673
733
|
keepExif: { type: "boolean", description: "Keep EXIF/XMP/ICC when optimizing." },
|
|
674
734
|
...frameProps,
|
|
675
735
|
noGit: { type: "boolean", description: "Don't derive the repo segment from git." },
|
|
736
|
+
noPr: {
|
|
737
|
+
type: "boolean",
|
|
738
|
+
description: "Skip auto-PR context (issue #700): without pr/issue/key/ref/prefix/destination, a call on a branch mapping to exactly one open PR otherwise behaves as if pr had been passed. Also opts out via UPLOADS_NO_AUTO_PR=1.",
|
|
739
|
+
},
|
|
676
740
|
comment: {
|
|
677
741
|
type: "boolean",
|
|
678
|
-
description: "With pr/issue: create/update the managed attachments comment (best-effort).",
|
|
742
|
+
description: "With pr/issue (or auto-detected PR context): create/update the managed attachments comment (best-effort).",
|
|
679
743
|
},
|
|
680
744
|
galleryId: {
|
|
681
745
|
type: "string",
|
|
@@ -736,21 +800,43 @@ export function createUploadsMcpTools(opts) {
|
|
|
736
800
|
const metadata = metadataArgWithCanonical(args);
|
|
737
801
|
if (metadata)
|
|
738
802
|
validateMetaMap(metadata);
|
|
739
|
-
const { config, client } = clientFor(args);
|
|
803
|
+
const { config, client } = await clientFor(args);
|
|
740
804
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
741
805
|
const frameOpts = mcpFrameOptions(args);
|
|
742
806
|
const optimizeOpts = mcpOptimizeOptions(args, defaults);
|
|
743
807
|
const noGit = optBool(args, "noGit") || defaults.noGit === true;
|
|
808
|
+
const noAutoPr = optBool(args, "noPr") || defaults.noAutoPr === true;
|
|
744
809
|
const alt = optString(args, "alt");
|
|
745
810
|
const width = optPosInt(args, "width") ?? defaults.width;
|
|
811
|
+
// Auto-PR context (issue #700): mirrors the CLI screenshot command
|
|
812
|
+
// and the put tool above — no pr/issue/key/ref/prefix/destination,
|
|
813
|
+
// not noGit/noPr, on a branch that maps to exactly one open PR
|
|
814
|
+
// behaves as if `pr` had been passed (stable key + managed comment
|
|
815
|
+
// sync) instead of the #469 auto-staging default below. Never
|
|
816
|
+
// throws — see resolveAutoPrTarget.
|
|
817
|
+
const autoPrTarget = target
|
|
818
|
+
? undefined
|
|
819
|
+
: resolveAutoPrTarget({
|
|
820
|
+
ghTarget: target,
|
|
821
|
+
keyHint: keyArg,
|
|
822
|
+
refArg,
|
|
823
|
+
prefixArg,
|
|
824
|
+
destinationArg: destArg,
|
|
825
|
+
noGit,
|
|
826
|
+
noAutoPr,
|
|
827
|
+
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
828
|
+
run,
|
|
829
|
+
});
|
|
830
|
+
const effectiveTarget = target ?? autoPrTarget;
|
|
746
831
|
// Auto branch staging (issue #469 lever 1): mirrors the CLI screenshot
|
|
747
832
|
// command and the put tool above (issue #403) — no pr/issue/key/ref/
|
|
748
833
|
// prefix/destination, not noGit, on a non-default git branch stages
|
|
749
834
|
// to the branch prefix (identical key/metadata to `attach --branch`)
|
|
750
835
|
// instead of the dated `screenshots/<repo>/<date>/...` layout. Never
|
|
751
|
-
// throws — see resolvePutStagingTarget.
|
|
836
|
+
// throws — see resolvePutStagingTarget. effectiveTarget (explicit
|
|
837
|
+
// pr/issue OR the #700 auto-PR match) wins over staging.
|
|
752
838
|
const stagingTarget = resolvePutStagingTarget({
|
|
753
|
-
ghTarget:
|
|
839
|
+
ghTarget: effectiveTarget,
|
|
754
840
|
keyHint: keyArg,
|
|
755
841
|
refArg,
|
|
756
842
|
prefixArg,
|
|
@@ -759,13 +845,27 @@ export function createUploadsMcpTools(opts) {
|
|
|
759
845
|
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
760
846
|
run,
|
|
761
847
|
});
|
|
848
|
+
// Bare-screenshot nudge context (issue #393/#700): only relevant
|
|
849
|
+
// when neither auto-PR nor staging took over.
|
|
850
|
+
const nudgeCtx = effectiveTarget || stagingTarget
|
|
851
|
+
? undefined
|
|
852
|
+
: resolvePutNudgeContext({
|
|
853
|
+
quiet: false,
|
|
854
|
+
noNudge: defaults.noNudge === true,
|
|
855
|
+
ghTarget: target,
|
|
856
|
+
keyHint: keyArg,
|
|
857
|
+
noGit,
|
|
858
|
+
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
859
|
+
run,
|
|
860
|
+
});
|
|
861
|
+
const autoPrHint = autoPrTarget ? autoPrNoteText(autoPrTarget.num) : undefined;
|
|
762
862
|
let resolvedPrefix;
|
|
763
863
|
try {
|
|
764
864
|
resolvedPrefix = resolvePutPrefix({
|
|
765
865
|
destination: destArg,
|
|
766
866
|
prefix: prefixArg,
|
|
767
867
|
key: keyArg,
|
|
768
|
-
ghAttachment: Boolean(
|
|
868
|
+
ghAttachment: Boolean(effectiveTarget) || stagingTarget !== undefined,
|
|
769
869
|
});
|
|
770
870
|
}
|
|
771
871
|
catch (err) {
|
|
@@ -837,10 +937,10 @@ export function createUploadsMcpTools(opts) {
|
|
|
837
937
|
}
|
|
838
938
|
// Resolved once (issue #631), only now that upload is actually
|
|
839
939
|
// about to happen — never per file (screenshot uploads exactly one).
|
|
840
|
-
const ghPrefix =
|
|
940
|
+
const ghPrefix = effectiveTarget
|
|
841
941
|
? await resolveGhPrefixSafe(client, {
|
|
842
|
-
repo:
|
|
843
|
-
target: { kind:
|
|
942
|
+
repo: effectiveTarget.repo,
|
|
943
|
+
target: { kind: effectiveTarget.kind, num: effectiveTarget.num },
|
|
844
944
|
})
|
|
845
945
|
: stagingTarget
|
|
846
946
|
? await resolveGhPrefixSafe(client, {
|
|
@@ -851,7 +951,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
851
951
|
const { result, prepared, markdown } = await uploadPreparedImage(client, captured.png, captured.filename, {
|
|
852
952
|
frame: frameOpts,
|
|
853
953
|
optimize: optimizeOpts,
|
|
854
|
-
ghTarget:
|
|
954
|
+
ghTarget: effectiveTarget,
|
|
855
955
|
ghBranchTarget: stagingTarget,
|
|
856
956
|
ghPrefix,
|
|
857
957
|
key: keyArg,
|
|
@@ -897,14 +997,19 @@ export function createUploadsMcpTools(opts) {
|
|
|
897
997
|
frame: prepared.frame,
|
|
898
998
|
gallery,
|
|
899
999
|
...(dryRun ? { dryRun: true } : {}),
|
|
900
|
-
//
|
|
901
|
-
//
|
|
1000
|
+
// Hint precedence (mirrors the CLI): the full-page height cap note
|
|
1001
|
+
// (issue #652) is about the just-captured image itself, more
|
|
1002
|
+
// immediately actionable than the #700 auto-PR/nudge notes below.
|
|
902
1003
|
...(captured.capped?.clipped
|
|
903
1004
|
? { hint: screenshotModule.clipHintText(captured.capped.maxHeightPx, "maxHeight") }
|
|
904
|
-
:
|
|
1005
|
+
: autoPrHint
|
|
1006
|
+
? { hint: autoPrHint }
|
|
1007
|
+
: nudgeCtx
|
|
1008
|
+
? { hint: putNudgeText(nudgeCtx.branch, nudgeCtx.pr, [result.key]) }
|
|
1009
|
+
: {}),
|
|
905
1010
|
};
|
|
906
|
-
if (wantComment &&
|
|
907
|
-
const { comment, commentError } = await syncComment(client,
|
|
1011
|
+
if (wantComment && effectiveTarget) {
|
|
1012
|
+
const { comment, commentError } = await syncComment(client, effectiveTarget, config.workspace);
|
|
908
1013
|
return { ...flat, comment, commentError };
|
|
909
1014
|
}
|
|
910
1015
|
return flat;
|
|
@@ -969,7 +1074,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
969
1074
|
const explicitTarget = ghTargetFromArgs(args, run);
|
|
970
1075
|
const target = explicitTarget ??
|
|
971
1076
|
resolveCurrentPullRequest(resolveRepo(optString(args, "repo"), run), run);
|
|
972
|
-
const { config, client } = clientFor(args);
|
|
1077
|
+
const { config, client } = await clientFor(args);
|
|
973
1078
|
const contentType = optString(args, "contentType");
|
|
974
1079
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
975
1080
|
const frameOpts = mcpFrameOptions(args);
|
|
@@ -1037,7 +1142,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1037
1142
|
const prefixArg = optString(args, "prefix");
|
|
1038
1143
|
let prefix = prefixArg ?? (defaults.prefix ? `${defaults.prefix}/` : undefined);
|
|
1039
1144
|
const target = ghTargetFromArgs(args, run);
|
|
1040
|
-
const { client } = clientFor(args);
|
|
1145
|
+
const { client } = await clientFor(args);
|
|
1041
1146
|
// Also list every active private prefix, if any (issue #631) —
|
|
1042
1147
|
// mirrors syncAttachmentsComment's gh-fallback gather: a repo's
|
|
1043
1148
|
// attachment history can be split across the plain shape and
|
|
@@ -1093,7 +1198,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1093
1198
|
additionalProperties: false,
|
|
1094
1199
|
},
|
|
1095
1200
|
async handler(args) {
|
|
1096
|
-
const { client } = clientFor(args);
|
|
1201
|
+
const { client } = await clientFor(args);
|
|
1097
1202
|
const repo = resolveRepo(optString(args, "repo"), run);
|
|
1098
1203
|
const branch = optString(args, "branch") ?? resolveCurrentBranch(run);
|
|
1099
1204
|
return resolveStaged({ client, repo, branch });
|
|
@@ -1124,7 +1229,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1124
1229
|
usage("key is required");
|
|
1125
1230
|
if (optBool(args, "dryRun"))
|
|
1126
1231
|
return { key, deleted: false, dryRun: true };
|
|
1127
|
-
const { client } = clientFor(args);
|
|
1232
|
+
const { client } = await clientFor(args);
|
|
1128
1233
|
return client.delete(key);
|
|
1129
1234
|
},
|
|
1130
1235
|
},
|
|
@@ -1147,7 +1252,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1147
1252
|
const key = optString(args, "key");
|
|
1148
1253
|
if (!key)
|
|
1149
1254
|
usage("key is required");
|
|
1150
|
-
return clientFor(args).client.getMetadata(key);
|
|
1255
|
+
return (await clientFor(args)).client.getMetadata(key);
|
|
1151
1256
|
},
|
|
1152
1257
|
},
|
|
1153
1258
|
{
|
|
@@ -1184,7 +1289,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1184
1289
|
}
|
|
1185
1290
|
if (set)
|
|
1186
1291
|
validateMetaMap(set);
|
|
1187
|
-
const { client } = clientFor(args);
|
|
1292
|
+
const { client } = await clientFor(args);
|
|
1188
1293
|
return client.patchMetadata(key, { set, delete: del });
|
|
1189
1294
|
},
|
|
1190
1295
|
},
|
|
@@ -1223,7 +1328,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1223
1328
|
}
|
|
1224
1329
|
if (hasMeta)
|
|
1225
1330
|
validateMetaMap(filters);
|
|
1226
|
-
const { client } = clientFor(args);
|
|
1331
|
+
const { client } = await clientFor(args);
|
|
1227
1332
|
return client.findFiles(filters, {
|
|
1228
1333
|
name,
|
|
1229
1334
|
prefix: optString(args, "prefix"),
|
|
@@ -1249,7 +1354,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1249
1354
|
additionalProperties: false,
|
|
1250
1355
|
},
|
|
1251
1356
|
async handler(args) {
|
|
1252
|
-
const { client } = clientFor(args);
|
|
1357
|
+
const { client } = await clientFor(args);
|
|
1253
1358
|
const key = optString(args, "key");
|
|
1254
1359
|
return key ? client.listMetadataValues(key) : client.listMetadataKeys();
|
|
1255
1360
|
},
|
|
@@ -1266,7 +1371,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1266
1371
|
additionalProperties: false,
|
|
1267
1372
|
},
|
|
1268
1373
|
async handler(args) {
|
|
1269
|
-
const { client } = clientFor(args);
|
|
1374
|
+
const { client } = await clientFor(args);
|
|
1270
1375
|
return client.usage();
|
|
1271
1376
|
},
|
|
1272
1377
|
},
|
|
@@ -1282,7 +1387,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1282
1387
|
additionalProperties: false,
|
|
1283
1388
|
},
|
|
1284
1389
|
async handler(args) {
|
|
1285
|
-
const { client } = clientFor(args);
|
|
1390
|
+
const { client } = await clientFor(args);
|
|
1286
1391
|
return client.reconcile();
|
|
1287
1392
|
},
|
|
1288
1393
|
},
|
|
@@ -1298,7 +1403,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1298
1403
|
additionalProperties: false,
|
|
1299
1404
|
},
|
|
1300
1405
|
async handler(args) {
|
|
1301
|
-
const { client } = clientFor(args);
|
|
1406
|
+
const { client } = await clientFor(args);
|
|
1302
1407
|
return client.purgeExpired();
|
|
1303
1408
|
},
|
|
1304
1409
|
},
|
|
@@ -1320,7 +1425,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1320
1425
|
const target = ghTargetFromArgs(args, run);
|
|
1321
1426
|
if (!target)
|
|
1322
1427
|
usage("comment requires pr or issue");
|
|
1323
|
-
const { config, client } = clientFor(args);
|
|
1428
|
+
const { config, client } = await clientFor(args);
|
|
1324
1429
|
// Explicit resync, same as `uploads comment` (issue #480).
|
|
1325
1430
|
const result = await syncAttachmentsComment(client, target, run, config.workspace, {
|
|
1326
1431
|
resync: true,
|
|
@@ -1336,7 +1441,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1336
1441
|
description: "Check uploads.sh API liveness. No auth or arguments required.",
|
|
1337
1442
|
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1338
1443
|
async handler(args) {
|
|
1339
|
-
const { config, client } = clientFor(args, false);
|
|
1444
|
+
const { config, client } = await clientFor(args, false);
|
|
1340
1445
|
const result = await client.health();
|
|
1341
1446
|
return { ...result, apiUrl: config.apiUrl };
|
|
1342
1447
|
},
|
|
@@ -1353,7 +1458,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1353
1458
|
additionalProperties: false,
|
|
1354
1459
|
},
|
|
1355
1460
|
async handler(args) {
|
|
1356
|
-
const { config, client } = clientFor(args);
|
|
1461
|
+
const { config, client } = await clientFor(args);
|
|
1357
1462
|
return buildDoctorReport(config, client);
|
|
1358
1463
|
},
|
|
1359
1464
|
},
|
|
@@ -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
|
+
}
|