@buildinternet/uploads 0.50.1 → 0.51.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/client.d.ts +25 -0
- package/dist/client.js +21 -0
- package/dist/commands/screenshot.js +17 -2
- package/dist/commands.d.ts +27 -1
- package/dist/commands.js +97 -38
- package/dist/github-gh.d.ts +25 -0
- package/dist/github-gh.js +49 -0
- package/dist/mcp/tools.js +65 -10
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -326,6 +326,22 @@ export interface PromoteSkip {
|
|
|
326
326
|
export interface PromoteBranchAttachmentsResult {
|
|
327
327
|
promoted: string[];
|
|
328
328
|
skipped: PromoteSkip[];
|
|
329
|
+
/** Branch-name lineage the sweep covered, current name first (issue #920).
|
|
330
|
+
* Present only when the branch was renamed at least once. */
|
|
331
|
+
lineage?: string[];
|
|
332
|
+
}
|
|
333
|
+
/** `POST /v1/workspaces/:workspace/github/branch-rename` request (server contract, issue #920). */
|
|
334
|
+
export interface RegisterBranchRenameOptions {
|
|
335
|
+
repo: string;
|
|
336
|
+
/** Previous branch name (`git branch -m <from> <to>`). */
|
|
337
|
+
from: string;
|
|
338
|
+
/** Name the branch was renamed to. */
|
|
339
|
+
to: string;
|
|
340
|
+
}
|
|
341
|
+
/** `POST /v1/workspaces/:workspace/github/branch-rename` response. `recorded:
|
|
342
|
+
* false` means the pair was already known (or the server has no such route). */
|
|
343
|
+
export interface RegisterBranchRenameResult {
|
|
344
|
+
recorded: boolean;
|
|
329
345
|
}
|
|
330
346
|
/**
|
|
331
347
|
* `POST /v1/workspaces/:workspace/github/attach` request/response (server
|
|
@@ -758,6 +774,15 @@ export declare function createUploadsClient(config: UploadsClientConfig): {
|
|
|
758
774
|
* that doesn't have this route yet, as "nothing promoted").
|
|
759
775
|
*/
|
|
760
776
|
promoteBranchAttachments(opts: PromoteBranchAttachmentsOptions): Promise<PromoteBranchAttachmentsResult>;
|
|
777
|
+
/**
|
|
778
|
+
* Record one `git branch -m` step so promote sweeps the branch's whole
|
|
779
|
+
* name lineage (issue #920). Degrade-safe on the one failure that is
|
|
780
|
+
* expected in the wild: a 404 from an older/self-hosted worker without
|
|
781
|
+
* this route collapses to `{ recorded: false }` — nothing recorded, same
|
|
782
|
+
* as `promoteBranchAttachments`' callers treat a missing route. Other
|
|
783
|
+
* failures throw; the CLI's `registerRenamesBestEffort` swallows those.
|
|
784
|
+
*/
|
|
785
|
+
registerBranchRename(opts: RegisterBranchRenameOptions): Promise<RegisterBranchRenameResult>;
|
|
761
786
|
/**
|
|
762
787
|
* Attach an already-uploaded object to a PR/issue via a server-side copy
|
|
763
788
|
* (issue #702) — see `AttachExistingOptions`. Throws `UploadsError` on
|
package/dist/client.js
CHANGED
|
@@ -724,6 +724,27 @@ export function createUploadsClient(config) {
|
|
|
724
724
|
headers: { "Content-Type": "application/json" },
|
|
725
725
|
});
|
|
726
726
|
},
|
|
727
|
+
/**
|
|
728
|
+
* Record one `git branch -m` step so promote sweeps the branch's whole
|
|
729
|
+
* name lineage (issue #920). Degrade-safe on the one failure that is
|
|
730
|
+
* expected in the wild: a 404 from an older/self-hosted worker without
|
|
731
|
+
* this route collapses to `{ recorded: false }` — nothing recorded, same
|
|
732
|
+
* as `promoteBranchAttachments`' callers treat a missing route. Other
|
|
733
|
+
* failures throw; the CLI's `registerRenamesBestEffort` swallows those.
|
|
734
|
+
*/
|
|
735
|
+
async registerBranchRename(opts) {
|
|
736
|
+
try {
|
|
737
|
+
return await request("POST", `${config.apiUrl}/v1/workspaces/${encodeURIComponent(config.workspace)}/github/branch-rename`, {
|
|
738
|
+
body: new TextEncoder().encode(JSON.stringify(opts)),
|
|
739
|
+
headers: { "Content-Type": "application/json" },
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
if (err instanceof UploadsError && err.status === 404)
|
|
744
|
+
return { recorded: false };
|
|
745
|
+
throw err;
|
|
746
|
+
}
|
|
747
|
+
},
|
|
727
748
|
/**
|
|
728
749
|
* Attach an already-uploaded object to a PR/issue via a server-side copy
|
|
729
750
|
* (issue #702) — see `AttachExistingOptions`. Throws `UploadsError` on
|
|
@@ -2,7 +2,7 @@ import { readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { extractDashValue, flagBool, flagInt, flagString, flagValues, parseCommandArgs, UsageError, } from "../cli-args.js";
|
|
4
4
|
import { writeCommandHelp } from "../cli-style.js";
|
|
5
|
-
import { branchFromFlags, derivedMetaEnabled, frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, stateAppMetaFromFlags, warnNearMissMeta, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, resolvePutStagingTarget, putStagingNoteText, resolveStageBindingWarning, mergeStagingMeta, writeReplacedNote, resolveGhPrefixSafe, resolveAutoPrTarget, resolvePutNudgeContext, putNudgeText, autoPrNoteText, } from "../commands.js";
|
|
5
|
+
import { branchFromFlags, derivedMetaEnabled, frameOptionsFromFlags, ghTargetFromFlags, optimizeOptionsFromFlags, stateAppMetaFromFlags, warnNearMissMeta, syncAttachmentsComment, commentViaSuffix, uploadPreparedImage, resolvePutStagingTarget, putStagingNoteText, resolveStageBindingWarning, mergeStagingMeta, registerRenamesBestEffort, writeReplacedNote, resolveGhPrefixSafe, resolveAutoPrTarget, resolvePutNudgeContext, putNudgeText, autoPrNoteText, } from "../commands.js";
|
|
6
6
|
import { resolvePutDefaults } from "../config.js";
|
|
7
7
|
import { loadDefaultsRaw, resolveScreenshotDefaults } from "../config-file.js";
|
|
8
8
|
import { resolvePutPrefix } from "../destinations.js";
|
|
@@ -351,7 +351,7 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
351
351
|
// maps to exactly one open PR behaves as if --pr <n> had been passed —
|
|
352
352
|
// stable key + managed comment sync — instead of the #469 auto-staging
|
|
353
353
|
// default below. Mirrors put's #700 handling exactly (resolveAutoPrTarget).
|
|
354
|
-
const
|
|
354
|
+
const autoPrMatch = ghTarget || branchArg !== undefined
|
|
355
355
|
? undefined
|
|
356
356
|
: resolveAutoPrTarget({
|
|
357
357
|
ghTarget,
|
|
@@ -365,6 +365,7 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
365
365
|
repoArg: flagString(parsed.flags, "--repo") ?? putDefaults.repo,
|
|
366
366
|
run,
|
|
367
367
|
});
|
|
368
|
+
const autoPrTarget = autoPrMatch?.target;
|
|
368
369
|
const effectiveGhTarget = ghTarget ?? autoPrTarget;
|
|
369
370
|
// Auto branch staging (issue #469 lever 1): mirrors bare `put`'s auto-staging
|
|
370
371
|
// (issue #403). When no --branch/--pr/--issue/--key/--ref/--prefix/--destination
|
|
@@ -428,9 +429,23 @@ loadAnnotateModule = () => import("../annotate/index.js")) {
|
|
|
428
429
|
if (effectiveGhTarget) {
|
|
429
430
|
metadata = { ...withFacts, ...ghMetadataFromTargetWithTitle(effectiveGhTarget, run) };
|
|
430
431
|
validateMetaMap(metadata);
|
|
432
|
+
// The #700 auto-PR match suppresses staging, so the staging branch below
|
|
433
|
+
// never runs — but this capture still comes from a branch that may have
|
|
434
|
+
// been renamed while files were staged under the old name. Register the
|
|
435
|
+
// lineage here too (issue #920), reusing the branch the match already
|
|
436
|
+
// resolved. Only for the auto-PR match: an explicit --pr/--issue names
|
|
437
|
+
// no branch of its own.
|
|
438
|
+
if (autoPrMatch && !dryRun && !noUpload) {
|
|
439
|
+
await registerRenamesBestEffort(ctx.client, run, autoPrMatch.target.repo, autoPrMatch.branch);
|
|
440
|
+
}
|
|
431
441
|
}
|
|
432
442
|
else if (stagingTarget !== undefined) {
|
|
433
443
|
metadata = mergeStagingMeta(withFacts, stagingTarget);
|
|
444
|
+
// Same rename registration as `put`/`attach --branch` staging (issue
|
|
445
|
+
// #920): best-effort, never fails the capture.
|
|
446
|
+
if (!dryRun && !noUpload) {
|
|
447
|
+
await registerRenamesBestEffort(ctx.client, run, stagingTarget.repo, stagingTarget.branch);
|
|
448
|
+
}
|
|
434
449
|
}
|
|
435
450
|
else if (Object.keys(withFacts).length > 0) {
|
|
436
451
|
validateMetaMap(withFacts);
|
package/dist/commands.d.ts
CHANGED
|
@@ -394,6 +394,23 @@ export declare function uploadPuts(opts: {
|
|
|
394
394
|
width?: number;
|
|
395
395
|
concurrency?: number;
|
|
396
396
|
}): Promise<UploadBatchResult<PutUploadItem>>;
|
|
397
|
+
/**
|
|
398
|
+
* Best-effort branch-rename registration (issue #920): reads this branch's
|
|
399
|
+
* reflog for `git branch -m` steps and tells the server about each one, so a
|
|
400
|
+
* later promote sweeps the branch's whole name lineage instead of only its
|
|
401
|
+
* current name. No-op when the reflog has no rename (the common case) and
|
|
402
|
+
* when the client predates the route. Every failure is swallowed — this runs
|
|
403
|
+
* alongside staging and promote, and must never fail either. Set
|
|
404
|
+
* `UPLOADS_DEBUG=1` to see what was skipped.
|
|
405
|
+
*
|
|
406
|
+
* `opts.explicit` marks a branch the user named themselves (`--from-branch`
|
|
407
|
+
* / the MCP `fromBranch` argument): that is the manual escape hatch (PR
|
|
408
|
+
* #919), and its lineage belongs to a different name than the one we are
|
|
409
|
+
* standing on, so nothing is registered for it.
|
|
410
|
+
*/
|
|
411
|
+
export declare function registerRenamesBestEffort(client: UploadsClient, run: CommandRunner, repo: string, branch: string, opts?: {
|
|
412
|
+
explicit?: boolean;
|
|
413
|
+
}): Promise<void>;
|
|
397
414
|
export declare function runAttach(ctx: CliContext, args: string[], help?: boolean, run?: CommandRunner): Promise<number>;
|
|
398
415
|
/**
|
|
399
416
|
* One source of truth for the "staged, but not going to auto-attach" advisory
|
|
@@ -465,6 +482,11 @@ export declare function resolvePutNudgeContext(opts: {
|
|
|
465
482
|
branch: string;
|
|
466
483
|
pr: number | undefined;
|
|
467
484
|
} | undefined;
|
|
485
|
+
/** A #700 auto-PR match: the PR the current branch maps to, plus that branch. */
|
|
486
|
+
export interface AutoPrMatch {
|
|
487
|
+
target: GhTarget;
|
|
488
|
+
branch: string;
|
|
489
|
+
}
|
|
468
490
|
/**
|
|
469
491
|
* Auto-PR context (issue #700): when a bare put/screenshot has no explicit
|
|
470
492
|
* destination flag at all (`--pr`/`--issue`/`--key`/`--ref`/`--prefix`/
|
|
@@ -481,6 +503,10 @@ export declare function resolvePutNudgeContext(opts: {
|
|
|
481
503
|
* default branch, or with `--no-git`; any failure (not a repo, detached
|
|
482
504
|
* HEAD, gh missing/unauthenticated/timed out, no open PR) degrades to
|
|
483
505
|
* undefined so the caller falls back to its normal staging/dated behavior.
|
|
506
|
+
*
|
|
507
|
+
* The match carries the `branch` it was resolved from, so callers that need
|
|
508
|
+
* the current branch (the #920 rename registration) reuse it instead of
|
|
509
|
+
* spawning `git rev-parse` a second time.
|
|
484
510
|
*/
|
|
485
511
|
export declare function resolveAutoPrTarget(opts: {
|
|
486
512
|
ghTarget: GhTarget | undefined;
|
|
@@ -495,7 +521,7 @@ export declare function resolveAutoPrTarget(opts: {
|
|
|
495
521
|
noAutoPr: boolean;
|
|
496
522
|
repoArg: string | undefined;
|
|
497
523
|
run: CommandRunner;
|
|
498
|
-
}):
|
|
524
|
+
}): AutoPrMatch | undefined;
|
|
499
525
|
/**
|
|
500
526
|
* Bare-put branch-staging trigger (issue #403): put on a non-default git
|
|
501
527
|
* branch stages to the branch prefix by default — the branch becomes the
|
package/dist/commands.js
CHANGED
|
@@ -15,7 +15,7 @@ import { parseMetaFlags, validateMetaMap } from "./metadata.js";
|
|
|
15
15
|
import { mergeDerivedMeta, nearMissMetaWarnings, validateStateValue } from "./metadata-vocab.js";
|
|
16
16
|
import { mergeSidecarMeta } from "./sidecar.js";
|
|
17
17
|
import { ghAttachmentKeyForMode, ghBranchAttachmentKeyForMode, ghBranchKeyPrefix, ghKeyPrefix, ghPrivateKeyPrefix, ghPrivateBranchKeyPrefix, ghMetadataFromTarget, parseGhKey, parseGhPrivateKey, ghMetadataForBranch, attachmentsCommentBody, attachmentsMarker, AUTO_RENDER_OPTIONS, GH_FALLBACK_AUTHOR_NOTE, normalizeGithubCoordinate, } from "./github.js";
|
|
18
|
-
import { resolveRepo, resolveCurrentPullRequest, resolveCurrentBranch, resolveDefaultBranch, classifyGhNumber, execRunner, timedExecRunner, ghMetadataFromTargetWithTitle, upsertAttachmentsComment, hasLinkCandidate, extractCandidateUrls, fetchAdoptionCandidateText, } from "./github-gh.js";
|
|
18
|
+
import { resolveRepo, resolveCurrentPullRequest, resolveCurrentBranch, resolveCurrentBranchSafe, resolveDefaultBranch, classifyGhNumber, execRunner, timedExecRunner, ghMetadataFromTargetWithTitle, upsertAttachmentsComment, hasLinkCandidate, extractCandidateUrls, fetchAdoptionCandidateText, renameLineageFromReflog, } from "./github-gh.js";
|
|
19
19
|
import { deriveRepoFromGit, deriveRepoSlugFromGit } from "./keys.js";
|
|
20
20
|
import { noProjectContextNudge } from "./project-context-nudge.js";
|
|
21
21
|
import { resolvePutPrefix } from "./destinations.js";
|
|
@@ -809,11 +809,13 @@ takes effect with zero files and cannot combine with --branch/--issue/
|
|
|
809
809
|
--no-promote. Promotion never applies to issues. Staged files stay findable
|
|
810
810
|
with "uploads find gh.branch=<branch>" either way.
|
|
811
811
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
812
|
+
A branch renamed with "git branch -m" is followed automatically: the rename
|
|
813
|
+
is read from the branch reflog and registered, so promotion sweeps the older
|
|
814
|
+
names too. That needs one uploads run after the rename. If the branch was
|
|
815
|
+
renamed without one, or was deleted, pass "--from-branch <old-name>" with
|
|
816
|
+
"--pr <num>". With no file arguments, this promotes the stale branch prefix
|
|
817
|
+
and refreshes the managed comment. With file or existing-key arguments, it
|
|
818
|
+
promotes the stale prefix before the normal attach flow.
|
|
817
819
|
|
|
818
820
|
Options:
|
|
819
821
|
--pr <num> Attach to this pull request
|
|
@@ -1174,6 +1176,45 @@ export async function uploadPuts(opts) {
|
|
|
1174
1176
|
}
|
|
1175
1177
|
return { uploads, failures, firstError, sentMetadata };
|
|
1176
1178
|
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Best-effort branch-rename registration (issue #920): reads this branch's
|
|
1181
|
+
* reflog for `git branch -m` steps and tells the server about each one, so a
|
|
1182
|
+
* later promote sweeps the branch's whole name lineage instead of only its
|
|
1183
|
+
* current name. No-op when the reflog has no rename (the common case) and
|
|
1184
|
+
* when the client predates the route. Every failure is swallowed — this runs
|
|
1185
|
+
* alongside staging and promote, and must never fail either. Set
|
|
1186
|
+
* `UPLOADS_DEBUG=1` to see what was skipped.
|
|
1187
|
+
*
|
|
1188
|
+
* `opts.explicit` marks a branch the user named themselves (`--from-branch`
|
|
1189
|
+
* / the MCP `fromBranch` argument): that is the manual escape hatch (PR
|
|
1190
|
+
* #919), and its lineage belongs to a different name than the one we are
|
|
1191
|
+
* standing on, so nothing is registered for it.
|
|
1192
|
+
*/
|
|
1193
|
+
export async function registerRenamesBestEffort(client, run, repo, branch, opts = {}) {
|
|
1194
|
+
if (opts.explicit)
|
|
1195
|
+
return;
|
|
1196
|
+
let lineage;
|
|
1197
|
+
try {
|
|
1198
|
+
lineage = renameLineageFromReflog(run, branch);
|
|
1199
|
+
}
|
|
1200
|
+
catch {
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
if (lineage.length === 0)
|
|
1204
|
+
return;
|
|
1205
|
+
for (const step of lineage) {
|
|
1206
|
+
try {
|
|
1207
|
+
await client.registerBranchRename({ repo, from: step.from, to: step.to });
|
|
1208
|
+
}
|
|
1209
|
+
catch (err) {
|
|
1210
|
+
if (process.env.UPLOADS_DEBUG === "1") {
|
|
1211
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
1212
|
+
process.stderr.write(`debug: could not register branch rename ${step.from} -> ${step.to}: ${detail}\n`);
|
|
1213
|
+
}
|
|
1214
|
+
return; // one failure means the route is unavailable; don't retry the rest
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1177
1218
|
/**
|
|
1178
1219
|
* Best-effort call to `POST /v1/workspaces/:workspace/github/promote` (server contract,
|
|
1179
1220
|
* PR #310). Degrade-safe like `syncAttachmentsComment`'s bot path: an older
|
|
@@ -1189,11 +1230,20 @@ async function attemptPromoteBranch(client, target, branch) {
|
|
|
1189
1230
|
return undefined;
|
|
1190
1231
|
}
|
|
1191
1232
|
}
|
|
1192
|
-
/**
|
|
1233
|
+
/**
|
|
1234
|
+
* Human-mode note(s) for a promotion that actually promoted something. When
|
|
1235
|
+
* the server-side sweep followed a rename it adds a second line naming the
|
|
1236
|
+
* older branch names (issue #920): `lineage` is current-name-first, so the
|
|
1237
|
+
* older names are its tail.
|
|
1238
|
+
*/
|
|
1193
1239
|
function promotionNote(promotion, branch) {
|
|
1194
1240
|
const n = promotion.promoted.length;
|
|
1195
1241
|
const branchSuffix = branch ? ` from branch ${branch}` : "";
|
|
1196
|
-
|
|
1242
|
+
const promoted = `>> promoted ${n} staged attachment${n === 1 ? "" : "s"}${branchSuffix}\n`;
|
|
1243
|
+
const older = (promotion.lineage ?? []).slice(1);
|
|
1244
|
+
if (older.length === 0)
|
|
1245
|
+
return promoted;
|
|
1246
|
+
return `${promoted}>> followed rename from ${older.join(", ")}\n`;
|
|
1197
1247
|
}
|
|
1198
1248
|
export async function runAttach(ctx, args, help = false, run = execRunner) {
|
|
1199
1249
|
const parsed = parseCommandArgs(args);
|
|
@@ -1344,16 +1394,11 @@ export async function runAttach(ctx, args, help = false, run = execRunner) {
|
|
|
1344
1394
|
let promotion;
|
|
1345
1395
|
let promotedBranch;
|
|
1346
1396
|
if (target.kind === "pull" && !parsed.flags.has("--no-promote")) {
|
|
1347
|
-
promotedBranch = fromBranch;
|
|
1348
|
-
if (promotedBranch === undefined) {
|
|
1349
|
-
try {
|
|
1350
|
-
promotedBranch = resolveCurrentBranch(run);
|
|
1351
|
-
}
|
|
1352
|
-
catch {
|
|
1353
|
-
promotedBranch = undefined;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1397
|
+
promotedBranch = fromBranch ?? resolveCurrentBranchSafe(run);
|
|
1356
1398
|
if (promotedBranch !== undefined) {
|
|
1399
|
+
await registerRenamesBestEffort(ctx.client, run, target.repo, promotedBranch, {
|
|
1400
|
+
explicit: fromBranch !== undefined,
|
|
1401
|
+
});
|
|
1357
1402
|
promotion = await attemptPromoteBranch(ctx.client, target, promotedBranch);
|
|
1358
1403
|
}
|
|
1359
1404
|
}
|
|
@@ -1449,6 +1494,9 @@ async function runAttachBranch(ctx, parsed, branch, run) {
|
|
|
1449
1494
|
process.stderr.write(`>> uploading ${n} file${n === 1 ? "" : "s"} (staged for branch ${branch})\n`);
|
|
1450
1495
|
}
|
|
1451
1496
|
const target = { repo, branch };
|
|
1497
|
+
// Register any `git branch -m` steps behind this name (issue #920) so the
|
|
1498
|
+
// promote that runs when the PR opens sweeps the older names too.
|
|
1499
|
+
await registerRenamesBestEffort(ctx.client, run, repo, branch);
|
|
1452
1500
|
const { uploads, failures, firstError } = await uploadBranchAttachments({
|
|
1453
1501
|
client: ctx.client,
|
|
1454
1502
|
target,
|
|
@@ -1567,7 +1615,11 @@ async function runAttachPromoteOnly(ctx, parsed, run) {
|
|
|
1567
1615
|
if (target.kind !== "pull") {
|
|
1568
1616
|
throw new UsageError("--from-branch only promotes into a pull request");
|
|
1569
1617
|
}
|
|
1570
|
-
const
|
|
1618
|
+
const explicitFromBranch = flagString(parsed.flags, "--from-branch");
|
|
1619
|
+
const branch = explicitFromBranch ?? resolveCurrentBranch(run);
|
|
1620
|
+
await registerRenamesBestEffort(ctx.client, run, target.repo, branch, {
|
|
1621
|
+
explicit: explicitFromBranch !== undefined,
|
|
1622
|
+
});
|
|
1571
1623
|
const promotion = await attemptPromoteBranch(ctx.client, target, branch);
|
|
1572
1624
|
let comment;
|
|
1573
1625
|
let commentError;
|
|
@@ -1656,13 +1708,9 @@ export function resolvePutNudgeContext(opts) {
|
|
|
1656
1708
|
try {
|
|
1657
1709
|
if (deriveRepoFromGit(run) === undefined)
|
|
1658
1710
|
return undefined; // not a (usable) git repo
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
branch = resolveCurrentBranch(run);
|
|
1662
|
-
}
|
|
1663
|
-
catch {
|
|
1711
|
+
const branch = resolveCurrentBranchSafe(run);
|
|
1712
|
+
if (branch === undefined)
|
|
1664
1713
|
return undefined; // detached HEAD, or git unavailable
|
|
1665
|
-
}
|
|
1666
1714
|
const defaultBranch = resolveDefaultBranch(run);
|
|
1667
1715
|
const onDefaultBranch = defaultBranch
|
|
1668
1716
|
? branch === defaultBranch
|
|
@@ -1704,6 +1752,10 @@ export function resolvePutNudgeContext(opts) {
|
|
|
1704
1752
|
* default branch, or with `--no-git`; any failure (not a repo, detached
|
|
1705
1753
|
* HEAD, gh missing/unauthenticated/timed out, no open PR) degrades to
|
|
1706
1754
|
* undefined so the caller falls back to its normal staging/dated behavior.
|
|
1755
|
+
*
|
|
1756
|
+
* The match carries the `branch` it was resolved from, so callers that need
|
|
1757
|
+
* the current branch (the #920 rename registration) reuse it instead of
|
|
1758
|
+
* spawning `git rev-parse` a second time.
|
|
1707
1759
|
*/
|
|
1708
1760
|
export function resolveAutoPrTarget(opts) {
|
|
1709
1761
|
const { ghTarget, keyHint, refArg, prefixArg, destinationArg, branchArg, noGit, noAutoPr, repoArg, run, } = opts;
|
|
@@ -1716,13 +1768,9 @@ export function resolveAutoPrTarget(opts) {
|
|
|
1716
1768
|
try {
|
|
1717
1769
|
if (deriveRepoFromGit(run) === undefined)
|
|
1718
1770
|
return undefined; // not a (usable) git repo
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
branch = resolveCurrentBranch(run);
|
|
1722
|
-
}
|
|
1723
|
-
catch {
|
|
1771
|
+
const branch = resolveCurrentBranchSafe(run);
|
|
1772
|
+
if (branch === undefined)
|
|
1724
1773
|
return undefined; // detached HEAD, or git unavailable
|
|
1725
|
-
}
|
|
1726
1774
|
const defaultBranch = resolveDefaultBranch(run);
|
|
1727
1775
|
const onDefaultBranch = defaultBranch
|
|
1728
1776
|
? branch === defaultBranch
|
|
@@ -1733,7 +1781,7 @@ export function resolveAutoPrTarget(opts) {
|
|
|
1733
1781
|
// this must never be felt as a hang.
|
|
1734
1782
|
const timed = run === execRunner ? timedExecRunner(PUT_NUDGE_GH_TIMEOUT_MS) : run;
|
|
1735
1783
|
const repo = resolveRepo(repoArg, timed);
|
|
1736
|
-
return resolveCurrentPullRequest(repo, timed);
|
|
1784
|
+
return { target: resolveCurrentPullRequest(repo, timed), branch };
|
|
1737
1785
|
}
|
|
1738
1786
|
catch {
|
|
1739
1787
|
return undefined; // gh/git unavailable, no open PR, or repo unresolvable
|
|
@@ -1766,13 +1814,9 @@ export function resolvePutStagingTarget(opts) {
|
|
|
1766
1814
|
try {
|
|
1767
1815
|
if (deriveRepoFromGit(run) === undefined)
|
|
1768
1816
|
return undefined; // not a (usable) git repo
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
branch = resolveCurrentBranch(run);
|
|
1772
|
-
}
|
|
1773
|
-
catch {
|
|
1817
|
+
const branch = resolveCurrentBranchSafe(run);
|
|
1818
|
+
if (branch === undefined)
|
|
1774
1819
|
return undefined; // detached HEAD, or git unavailable
|
|
1775
|
-
}
|
|
1776
1820
|
const defaultBranch = resolveDefaultBranch(run);
|
|
1777
1821
|
const onDefaultBranch = defaultBranch
|
|
1778
1822
|
? branch === defaultBranch
|
|
@@ -1993,7 +2037,7 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
1993
2037
|
// resolveAutoPrTarget. Supersedes both the #403 staging default and the
|
|
1994
2038
|
// #393 nudge for this case; computed before the gh.* metadata resolution
|
|
1995
2039
|
// below since it takes over that resolution entirely.
|
|
1996
|
-
const
|
|
2040
|
+
const autoPrMatch = ghTarget
|
|
1997
2041
|
? undefined
|
|
1998
2042
|
: resolveAutoPrTarget({
|
|
1999
2043
|
ghTarget,
|
|
@@ -2006,6 +2050,7 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
2006
2050
|
repoArg: flagString(parsed.flags, "--repo") ?? defaults.repo,
|
|
2007
2051
|
run,
|
|
2008
2052
|
});
|
|
2053
|
+
const autoPrTarget = autoPrMatch?.target;
|
|
2009
2054
|
const effectiveGhTarget = ghTarget ?? autoPrTarget;
|
|
2010
2055
|
// Comment sync runs by default with --pr/--issue (matches `attach`); opt
|
|
2011
2056
|
// out with --no-comment. --comment is accepted as a redundant no-op for
|
|
@@ -2137,9 +2182,23 @@ export async function runPut(ctx, args, help = false, run = execRunner) {
|
|
|
2137
2182
|
validateMetaMap(merged); // enforce 24-key/8KB caps on the merged map (matches attach)
|
|
2138
2183
|
metadata = merged;
|
|
2139
2184
|
attachedRef = merged["gh.ref"];
|
|
2185
|
+
// Auto-PR (#700) suppresses staging, so the staging branch below never
|
|
2186
|
+
// runs — but this put still comes from a branch that may have been
|
|
2187
|
+
// renamed while files were staged under the old name. Register the
|
|
2188
|
+
// lineage here too (issue #920), reusing the branch the match already
|
|
2189
|
+
// resolved. Only for the auto-PR match: an explicit --pr/--issue names
|
|
2190
|
+
// no branch of its own.
|
|
2191
|
+
if (autoPrMatch && !dryRun) {
|
|
2192
|
+
await registerRenamesBestEffort(ctx.client, run, autoPrMatch.target.repo, autoPrMatch.branch);
|
|
2193
|
+
}
|
|
2140
2194
|
}
|
|
2141
2195
|
else if (stagingTarget) {
|
|
2142
2196
|
metadata = mergeStagingMeta(userMeta, stagingTarget);
|
|
2197
|
+
// Branch staging: register any rename behind this name (issue #920) so
|
|
2198
|
+
// the PR-time promote finds files staged under the older names too.
|
|
2199
|
+
if (!dryRun) {
|
|
2200
|
+
await registerRenamesBestEffort(ctx.client, run, stagingTarget.repo, stagingTarget.branch);
|
|
2201
|
+
}
|
|
2143
2202
|
}
|
|
2144
2203
|
else {
|
|
2145
2204
|
// gh.* additionally needs git, which the shared derived gate ignores.
|
package/dist/github-gh.d.ts
CHANGED
|
@@ -19,6 +19,31 @@ export declare function resolveRepo(explicit: string | undefined, run?: CommandR
|
|
|
19
19
|
export declare function resolveCurrentPullRequest(repo: string, run?: CommandRunner): GhTarget;
|
|
20
20
|
/** Resolve the current git branch (`--branch` with no value). Throws UsageError on detached HEAD or outside a git repo. */
|
|
21
21
|
export declare function resolveCurrentBranch(run?: CommandRunner): string;
|
|
22
|
+
/**
|
|
23
|
+
* `resolveCurrentBranch` for best-effort callers: undefined instead of a
|
|
24
|
+
* throw when the branch can't be determined (detached HEAD, not a git repo,
|
|
25
|
+
* git unavailable). Use it wherever a missing branch means "skip this step",
|
|
26
|
+
* not "fail the command".
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCurrentBranchSafe(run?: CommandRunner): string | undefined;
|
|
29
|
+
/** One `git branch -m` step read out of a branch's reflog: `from` was renamed to `to`. */
|
|
30
|
+
export interface BranchRenameStep {
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Rename lineage for `branch`, read from its own reflog (issue #920). `git
|
|
36
|
+
* branch -m old new` writes `Branch: renamed refs/heads/old to
|
|
37
|
+
* refs/heads/new` into the NEW branch's reflog, and chained renames
|
|
38
|
+
* accumulate there, newest entry first — so the parsed pairs are reversed
|
|
39
|
+
* into oldest-first order, which is the order the server wants them
|
|
40
|
+
* registered.
|
|
41
|
+
*
|
|
42
|
+
* Best-effort by construction: any git failure (not a repo, no reflog for
|
|
43
|
+
* the branch, git missing) and any non-rename reflog line yield an empty
|
|
44
|
+
* array. Callers must treat "no lineage" as the normal case.
|
|
45
|
+
*/
|
|
46
|
+
export declare function renameLineageFromReflog(run?: CommandRunner, branch?: string): BranchRenameStep[];
|
|
22
47
|
/**
|
|
23
48
|
* Best-effort default-branch name via the local `origin/HEAD` ref (no
|
|
24
49
|
* network call — just reads the ref git already cached from the last
|
package/dist/github-gh.js
CHANGED
|
@@ -114,6 +114,55 @@ export function resolveCurrentBranch(run = execRunner) {
|
|
|
114
114
|
}
|
|
115
115
|
return branch;
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* `resolveCurrentBranch` for best-effort callers: undefined instead of a
|
|
119
|
+
* throw when the branch can't be determined (detached HEAD, not a git repo,
|
|
120
|
+
* git unavailable). Use it wherever a missing branch means "skip this step",
|
|
121
|
+
* not "fail the command".
|
|
122
|
+
*/
|
|
123
|
+
export function resolveCurrentBranchSafe(run = execRunner) {
|
|
124
|
+
try {
|
|
125
|
+
return resolveCurrentBranch(run);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Rename lineage for `branch`, read from its own reflog (issue #920). `git
|
|
133
|
+
* branch -m old new` writes `Branch: renamed refs/heads/old to
|
|
134
|
+
* refs/heads/new` into the NEW branch's reflog, and chained renames
|
|
135
|
+
* accumulate there, newest entry first — so the parsed pairs are reversed
|
|
136
|
+
* into oldest-first order, which is the order the server wants them
|
|
137
|
+
* registered.
|
|
138
|
+
*
|
|
139
|
+
* Best-effort by construction: any git failure (not a repo, no reflog for
|
|
140
|
+
* the branch, git missing) and any non-rename reflog line yield an empty
|
|
141
|
+
* array. Callers must treat "no lineage" as the normal case.
|
|
142
|
+
*/
|
|
143
|
+
export function renameLineageFromReflog(run = execRunner, branch) {
|
|
144
|
+
if (!branch)
|
|
145
|
+
return [];
|
|
146
|
+
let out;
|
|
147
|
+
try {
|
|
148
|
+
out = run("git", ["reflog", "show", "--format=%gs", `refs/heads/${branch}`]);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return []; // no reflog, not a repo, git unavailable — nothing to follow
|
|
152
|
+
}
|
|
153
|
+
const steps = [];
|
|
154
|
+
for (const line of out.split("\n")) {
|
|
155
|
+
const match = /^Branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/.exec(line.trim());
|
|
156
|
+
if (!match)
|
|
157
|
+
continue;
|
|
158
|
+
const from = match[1].trim();
|
|
159
|
+
const to = match[2].trim();
|
|
160
|
+
if (!from || !to || from === to)
|
|
161
|
+
continue;
|
|
162
|
+
steps.push({ from, to });
|
|
163
|
+
}
|
|
164
|
+
return steps.reverse(); // reflog is newest-first; register oldest-first
|
|
165
|
+
}
|
|
117
166
|
/**
|
|
118
167
|
* Best-effort default-branch name via the local `origin/HEAD` ref (no
|
|
119
168
|
* network call — just reads the ref git already cached from the last
|
package/dist/mcp/tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createUploadsClient } from "../client.js";
|
|
2
|
-
import { buildDoctorReport, ghListPrefixes, ghMergedList, makeGhTarget, mergeStagingMeta, resolveAutoPrTarget, resolveGhPrefixSafe, resolvePutNudgeContext, resolvePutStagingTarget, resolveStaged, syncAttachmentsComment, autoPrNoteText, putNudgeText, uploadAttachments, uploadPreparedImage, uploadPuts, } from "../commands.js";
|
|
1
|
+
import { createUploadsClient, } from "../client.js";
|
|
2
|
+
import { buildDoctorReport, ghListPrefixes, ghMergedList, makeGhTarget, mergeStagingMeta, resolveAutoPrTarget, resolveGhPrefixSafe, resolvePutNudgeContext, registerRenamesBestEffort, 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";
|
|
@@ -9,7 +9,7 @@ import { safeCaptureFacts } from "../capture-facts.js";
|
|
|
9
9
|
import { deriveRepoSlugFromGit } from "../keys.js";
|
|
10
10
|
import { validateMetaMap } from "../metadata.js";
|
|
11
11
|
import { mergeDerivedMeta } from "../metadata-vocab.js";
|
|
12
|
-
import { execRunner, ghMetadataFromTargetWithTitle, resolveCurrentBranch, resolveCurrentPullRequest, resolveRepo, } from "../github-gh.js";
|
|
12
|
+
import { execRunner, ghMetadataFromTargetWithTitle, resolveCurrentBranch, resolveCurrentBranchSafe, resolveCurrentPullRequest, resolveRepo, } from "../github-gh.js";
|
|
13
13
|
import { appProp, canonicalMetaFromArgs, METADATA_PATH_CUE, metadataArgWithCanonical, metadataProp, optBool, optPosInt, optString, optStringArray, optStringRecord, stateProp, usage, } from "./args.js";
|
|
14
14
|
import { batchFailureMessage, mcpDestroyPublic, mcpNoAuth, mcpOAuthAny, mcpOAuthDelete, mcpOAuthRead, mcpOAuthWrite, mcpRead, mcpWriteInternal, mcpWritePublic, stdioOutputSchemas, withOutputSchemas, ToolBatchError, } from "./server.js";
|
|
15
15
|
import { attachmentFromText, buildReportPayload, parseReportType, REPORT_TYPES, submitReport, validateReportMessage, } from "../report.js";
|
|
@@ -131,6 +131,25 @@ export function createUploadsMcpTools(opts) {
|
|
|
131
131
|
}
|
|
132
132
|
return { comment, commentError };
|
|
133
133
|
};
|
|
134
|
+
/**
|
|
135
|
+
* Best-effort branch promote for the stdio `attach` tool (CLI `attach --pr`
|
|
136
|
+
* parity, issue #920). Registers any `git branch -m` steps first so the
|
|
137
|
+
* sweep follows renames, then promotes. Never throws: a failure surfaces as
|
|
138
|
+
* `promoteError` so the uploads themselves still return.
|
|
139
|
+
*/
|
|
140
|
+
const attemptPromote = async (client, target, branch) => {
|
|
141
|
+
try {
|
|
142
|
+
const promotion = await client.promoteBranchAttachments({
|
|
143
|
+
repo: target.repo,
|
|
144
|
+
num: target.num,
|
|
145
|
+
branch,
|
|
146
|
+
});
|
|
147
|
+
return { promotion };
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
return { promoteError: err instanceof Error ? err.message : String(err) };
|
|
151
|
+
}
|
|
152
|
+
};
|
|
134
153
|
const tools = [
|
|
135
154
|
{
|
|
136
155
|
name: "gallery_create",
|
|
@@ -466,7 +485,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
466
485
|
// had been passed (stable key + managed comment sync) instead of
|
|
467
486
|
// the #403 staging default below. Never throws — see
|
|
468
487
|
// resolveAutoPrTarget.
|
|
469
|
-
const autoPrTarget = target
|
|
488
|
+
const autoPrTarget = (target
|
|
470
489
|
? undefined
|
|
471
490
|
: resolveAutoPrTarget({
|
|
472
491
|
ghTarget: target,
|
|
@@ -478,7 +497,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
478
497
|
noAutoPr,
|
|
479
498
|
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
480
499
|
run,
|
|
481
|
-
});
|
|
500
|
+
}))?.target;
|
|
482
501
|
const effectiveTarget = target ?? autoPrTarget;
|
|
483
502
|
// Bare-put branch staging (issue #403): local stdio MCP put mirrors
|
|
484
503
|
// the CLI default — no pr/issue/key/ref/prefix/destination, not
|
|
@@ -858,7 +877,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
858
877
|
// behaves as if `pr` had been passed (stable key + managed comment
|
|
859
878
|
// sync) instead of the #469 auto-staging default below. Never
|
|
860
879
|
// throws — see resolveAutoPrTarget.
|
|
861
|
-
const autoPrTarget = target
|
|
880
|
+
const autoPrTarget = (target
|
|
862
881
|
? undefined
|
|
863
882
|
: resolveAutoPrTarget({
|
|
864
883
|
ghTarget: target,
|
|
@@ -870,7 +889,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
870
889
|
noAutoPr,
|
|
871
890
|
repoArg: optString(args, "repo") ?? defaults.repo,
|
|
872
891
|
run,
|
|
873
|
-
});
|
|
892
|
+
}))?.target;
|
|
874
893
|
const effectiveTarget = target ?? autoPrTarget;
|
|
875
894
|
// Auto branch staging (issue #469 lever 1): mirrors the CLI screenshot
|
|
876
895
|
// command and the put tool above (issue #403) — no pr/issue/key/ref/
|
|
@@ -1064,7 +1083,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1064
1083
|
title: "Attach to GitHub",
|
|
1065
1084
|
annotations: mcpDestroyPublic,
|
|
1066
1085
|
securitySchemes: mcpOAuthWrite,
|
|
1067
|
-
description: "Upload one or more files as stable PR/issue attachments (in parallel) and maintain a managed GitHub comment. Returns `uploads` and `failures` (one bad file does not abort the batch). Each success has `url`, `embedUrl`, and `markdown` (prefer embedUrl for GitHub). With no pr/issue, targets the current branch PR. Attachments are public and keys are predictable; upload only non-sensitive media.",
|
|
1086
|
+
description: "Upload one or more files as stable PR/issue attachments (in parallel) and maintain a managed GitHub comment. Returns `uploads` and `failures` (one bad file does not abort the batch). Each success has `url`, `embedUrl`, and `markdown` (prefer embedUrl for GitHub). With no pr/issue, targets the current branch PR, and files already staged for that branch are promoted into it (`promotion`/`promoteError`; renamed branches are followed automatically, or name the old branch with fromBranch). Attachments are public and keys are predictable; upload only non-sensitive media.",
|
|
1068
1087
|
inputSchema: {
|
|
1069
1088
|
type: "object",
|
|
1070
1089
|
properties: {
|
|
@@ -1078,6 +1097,14 @@ export function createUploadsMcpTools(opts) {
|
|
|
1078
1097
|
type: "boolean",
|
|
1079
1098
|
description: "Upload only; don't create/update the managed comment.",
|
|
1080
1099
|
},
|
|
1100
|
+
noPromote: {
|
|
1101
|
+
type: "boolean",
|
|
1102
|
+
description: "Skip promoting files staged for the current branch into the PR.",
|
|
1103
|
+
},
|
|
1104
|
+
fromBranch: {
|
|
1105
|
+
type: "string",
|
|
1106
|
+
description: "Promote files staged under this branch name instead of the current branch (for a branch renamed before the PR opened).",
|
|
1107
|
+
},
|
|
1081
1108
|
contentType: {
|
|
1082
1109
|
type: "string",
|
|
1083
1110
|
description: "Override the Content-Type (applied to every file; ignored when optimize rewrites).",
|
|
@@ -1109,6 +1136,7 @@ export function createUploadsMcpTools(opts) {
|
|
|
1109
1136
|
examples: [
|
|
1110
1137
|
{ files: ["./after.png"], pr: 12, state: "after" },
|
|
1111
1138
|
{ files: ["./before.png", "./after.png"], pr: 12 },
|
|
1139
|
+
{ files: ["./after.png"], pr: 12, fromBranch: "old-branch-name" },
|
|
1112
1140
|
],
|
|
1113
1141
|
},
|
|
1114
1142
|
async handler(args) {
|
|
@@ -1118,6 +1146,13 @@ export function createUploadsMcpTools(opts) {
|
|
|
1118
1146
|
const explicitTarget = ghTargetFromArgs(args, run);
|
|
1119
1147
|
const target = explicitTarget ??
|
|
1120
1148
|
resolveCurrentPullRequest(resolveRepo(optString(args, "repo"), run), run);
|
|
1149
|
+
const fromBranch = optString(args, "fromBranch");
|
|
1150
|
+
if (fromBranch !== undefined && optBool(args, "noPromote")) {
|
|
1151
|
+
usage("fromBranch cannot be combined with noPromote");
|
|
1152
|
+
}
|
|
1153
|
+
if (fromBranch !== undefined && target.kind !== "pull") {
|
|
1154
|
+
usage("fromBranch only promotes into a pull request");
|
|
1155
|
+
}
|
|
1121
1156
|
const { config, client } = await clientFor(args);
|
|
1122
1157
|
const contentType = optString(args, "contentType");
|
|
1123
1158
|
const defaults = resolvePutDefaults({ envFile: globals.envFile });
|
|
@@ -1154,10 +1189,30 @@ export function createUploadsMcpTools(opts) {
|
|
|
1154
1189
|
failures,
|
|
1155
1190
|
});
|
|
1156
1191
|
}
|
|
1192
|
+
// Auto-promote (CLI `attach --pr` parity, issue #920): before the
|
|
1193
|
+
// comment sync, best-effort promote this workspace's branch-staged
|
|
1194
|
+
// files into the PR, following any `git branch -m` rename. Never for
|
|
1195
|
+
// issues, never with noPromote, and silently skipped when the current
|
|
1196
|
+
// branch can't be resolved — this must not fail the attach.
|
|
1197
|
+
let promotion;
|
|
1198
|
+
let promoteError;
|
|
1199
|
+
if (target.kind === "pull" && !optBool(args, "noPromote")) {
|
|
1200
|
+
const branch = fromBranch ?? resolveCurrentBranchSafe(run);
|
|
1201
|
+
if (branch !== undefined) {
|
|
1202
|
+
await registerRenamesBestEffort(client, run, target.repo, branch, {
|
|
1203
|
+
explicit: fromBranch !== undefined,
|
|
1204
|
+
});
|
|
1205
|
+
({ promotion, promoteError } = await attemptPromote(client, target, branch));
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
const promoteFields = {
|
|
1209
|
+
...(promotion ? { promotion } : {}),
|
|
1210
|
+
...(promoteError ? { promoteError } : {}),
|
|
1211
|
+
};
|
|
1157
1212
|
if (optBool(args, "noComment"))
|
|
1158
|
-
return { target, uploads, failures };
|
|
1213
|
+
return { target, uploads, failures, ...promoteFields };
|
|
1159
1214
|
const { comment, commentError } = await syncComment(client, target, config.workspace);
|
|
1160
|
-
return { target, uploads, failures, comment, commentError };
|
|
1215
|
+
return { target, uploads, failures, ...promoteFields, comment, commentError };
|
|
1161
1216
|
},
|
|
1162
1217
|
},
|
|
1163
1218
|
{
|