@dev.fast/whiteboard 0.5.0 → 0.5.2-preview.20261104.146
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/{account-alias-Bw3PT3Qo.js → account-alias-BlYamXAG.js} +5 -5
- package/dist/{agent-cli-kHRrwO4n.js → agent-cli-D7fF5xmm.js} +9 -9
- package/dist/agent-cli-DLibAL4u.js +2 -0
- package/dist/{agent-client-CMacGbMp.js → agent-client-BCEbSPWU.js} +4 -4
- package/dist/{authoring-tools-CiLy46kh.js → authoring-tools-CkfxTVJm.js} +12 -8
- package/dist/{background-server-eZWXoVcI.js → background-server-BraP8pJ9.js} +4 -4
- package/dist/{background-server-BLNHbksB.js → background-server-CwToVN1g.js} +1 -1
- package/dist/build-info.json +1 -1
- package/dist/{cli-Bxmiqutp.js → cli-BpadBaGU.js} +1 -1
- package/dist/{cli-runner-DQPuu7kJ.js → cli-runner-BebYF50a.js} +22 -22
- package/dist/cli.js +2 -2
- package/dist/{fuzzy-match-DZjeOaPY.js → fuzzy-match-CxcsLQGx.js} +3 -3
- package/dist/{headless-host-Dl2KZ9IT.js → headless-host-FAlbekFD.js} +561 -415
- package/dist/{headless-host-B4r0s1ks.js → headless-host-cLipIufH.js} +1 -1
- package/dist/{import-YAIU6rB0.js → import-CZ1958L4.js} +234 -62
- package/dist/{local-data-C58vzLZR.js → local-data-DdFkxFK_.js} +41 -16
- package/dist/{mcp-COYWiGVn.js → mcp-Claiy9xE.js} +9 -6
- package/dist/{package-paths-Cg6fnaGp.js → package-paths-Dk7MOXxN.js} +1 -1
- package/dist/{profile--BYIjPkc.js → profile-Bsgzi9PA.js} +2 -2
- package/dist/profile-BxdzBxHW.js +2 -0
- package/dist/{remote-attach-C1prLjmj.js → remote-attach-LGTpwpUv.js} +4 -4
- package/dist/{remote-extensions-Bw029iIS.js → remote-extensions-BvV29_gk.js} +1 -1
- package/dist/{remote-extensions-MUUgbLJn.js → remote-extensions-L95HNwIH.js} +2 -2
- package/dist/{remote-language-server-z3rdHcLL.js → remote-language-server-BAznkpeu.js} +4 -4
- package/dist/{remote-uninstall-B3Slwor1.js → remote-uninstall-CLP79Cuk.js} +5 -5
- package/dist/{request-origin-B3_XI2VA.js → request-origin-D6B-zrL8.js} +27 -5
- package/dist/{review-app-launcher-BZEbDKuK.js → review-app-launcher-tTe8bTwg.js} +2 -2
- package/dist/{review-home-paths-LdGS4SpR.js → review-home-paths-BmZX2qiz.js} +1 -1
- package/dist/{review-telemetry-x8u-9EZb.js → review-telemetry-DACDMfqq.js} +4 -4
- package/dist/server/desktop-host.js +26 -38
- package/dist/{server-discovery-WmuFl2S3.js → server-discovery-CV5qf1J8.js} +2 -2
- package/dist/sharing/index.d.ts +137 -14
- package/dist/sharing/index.js +1 -1
- package/dist/{src-B2b8cCoI.js → src-BQDlJXkG.js} +44 -5
- package/dist/{src-DLWol7GZ.js → src-CrrW7lcY.js} +16 -1
- package/dist/{stored-document-migration-iwIQ4l5G.js → stored-document-migration-D1wK4yID.js} +3 -3
- package/dist/{thread-state-B7L1mpN_.js → thread-state-WTqSborE.js} +1 -1
- package/dist/{tool-failure-RLR8m9hq.js → tool-failure-YOFGHJCZ.js} +319 -55
- package/instructions/animation.md +69 -5
- package/instructions/animations/before-after-structure/README.md +27 -0
- package/instructions/animations/before-after-structure/animation.html +89 -0
- package/instructions/animations/before-after-structure/block.json +10 -0
- package/instructions/animations/before-after-structure/frames.json +313 -0
- package/instructions/animations/cache-fill-evict/README.md +24 -0
- package/instructions/animations/cache-fill-evict/animation.html +81 -0
- package/instructions/animations/cache-fill-evict/block.json +13 -0
- package/instructions/animations/cache-fill-evict/frames.json +79 -0
- package/instructions/animations/cache-fill-evict/scenarios.json +17 -0
- package/instructions/animations/log-replay/README.md +25 -0
- package/instructions/animations/log-replay/animation.html +86 -0
- package/instructions/animations/log-replay/block.json +10 -0
- package/instructions/animations/log-replay/frames.json +143 -0
- package/instructions/animations/log-replay/scenarios.json +18 -0
- package/instructions/animations/queue-workers/README.md +23 -0
- package/instructions/animations/queue-workers/animation.html +107 -0
- package/instructions/animations/queue-workers/block.json +13 -0
- package/instructions/animations/queue-workers/frames.json +119 -0
- package/instructions/animations/queue-workers/scenarios.json +18 -0
- package/instructions/animations/race-two-lanes/README.md +22 -0
- package/instructions/animations/race-two-lanes/animation.html +97 -0
- package/instructions/animations/race-two-lanes/block.json +1 -0
- package/instructions/animations/race-two-lanes/frames.json +234 -0
- package/instructions/animations/race-two-lanes/scenarios.json +19 -0
- package/instructions/animations/request-across-layers/README.md +24 -0
- package/instructions/animations/request-across-layers/animation.html +85 -0
- package/instructions/animations/request-across-layers/block.json +17 -0
- package/instructions/animations/request-across-layers/frames.json +58 -0
- package/instructions/animations/request-across-layers/scenarios.json +12 -0
- package/instructions/animations/request-timing/README.md +22 -0
- package/instructions/animations/request-timing/animation.html +120 -0
- package/instructions/animations/request-timing/block.json +21 -0
- package/instructions/animations/request-timing/markers.json +32 -0
- package/instructions/animations/request-timing/scenarios.json +4 -0
- package/instructions/animations/retry-backoff/README.md +22 -0
- package/instructions/animations/retry-backoff/animation.html +122 -0
- package/instructions/animations/retry-backoff/block.json +10 -0
- package/instructions/animations/retry-backoff/frames.json +65 -0
- package/instructions/animations/retry-backoff/scenarios.json +12 -0
- package/instructions/animations/state-machine/README.md +24 -0
- package/instructions/animations/state-machine/animation.html +95 -0
- package/instructions/animations/state-machine/block.json +9 -0
- package/instructions/animations/state-machine/frames.json +79 -0
- package/instructions/animations/state-machine/scenarios.json +12 -0
- package/instructions/animations/video-platform-architecture/README.md +24 -0
- package/instructions/animations/video-platform-architecture/animation.html +76 -0
- package/instructions/animations/video-platform-architecture/block.json +17 -0
- package/instructions/animations/video-platform-architecture/frames.json +97 -0
- package/instructions/animations/video-platform-architecture/scenarios.json +28 -0
- package/instructions/whiteboard.d.ts +168 -14
- package/package.json +6 -6
- package/src/animation-stage-kit.ts +150 -10
- package/src/cli-runner.ts +4 -1
- package/src/fixtures/blocks/clip.json +19 -0
- package/src/review-api/README.md +1 -0
- package/src/review-api/agent-cli.ts +3 -2
- package/src/review-api/animation-drafts.ts +151 -47
- package/src/review-api/animation-references.ts +92 -0
- package/src/review-api/animation-render.ts +106 -0
- package/src/review-api/authoring-tools.ts +10 -3
- package/src/review-api/document-text.ts +27 -12
- package/src/review-api/http.ts +52 -6
- package/src/review-api/instructions.ts +1 -1
- package/src/review-api/mcp.ts +10 -5
- package/src/review-api/public-tools.ts +71 -2
- package/src/review-api/store.ts +89 -12
- package/src/server/desktop-server.ts +17 -5
- package/src/server/headless-host.ts +4 -0
- package/src/server/review-gateway.ts +1 -0
- package/src/server/review-server-core.ts +71 -12
- package/src/sharing/client.ts +129 -32
- package/src/sharing/export.ts +42 -5
- package/src/sharing/file.ts +8 -0
- package/src/sharing/host.ts +202 -73
- package/src/sharing/import.ts +125 -33
- package/src/ui-telemetry-events.ts +25 -1
- package/dist/agent-cli-B8KBHuh_.js +0 -2
- package/dist/profile-BjcXi-oH.js +0 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as withHeadlessServerLock, n as runHeadlessServer, r as takeOverServerHome, t as HeadlessServerBusyError } from "./headless-host-
|
|
1
|
+
import { i as withHeadlessServerLock, n as runHeadlessServer, r as takeOverServerHome, t as HeadlessServerBusyError } from "./headless-host-FAlbekFD.js";
|
|
2
2
|
export { HeadlessServerBusyError, runHeadlessServer, takeOverServerHome, withHeadlessServerLock };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { f as gitAt } from "./src-Y30gweiK.js";
|
|
2
2
|
import { u as parseJsonText } from "./src-X9phtB2j.js";
|
|
3
|
-
import { $ as comparisonKey, A as parseMarkdown, B as lensSchema, E as sourceReferences, L as UNCATEGORIZED_LENS_ID, M as checkReferences, O as markdownNodes, Q as anchorSelection, T as selectionReferences, V as lensSelections, Z as SelectionUnavailableError, _ as readsAtBoardPins, at as unionIntervals, c as documentSchema, h as pinsSchema, it as sourceAnchors, n as anchorPins, nt as resolveDiffSelection, o as boardRepositoryId, ot as ReviewInputError, q as fileLineRangeSchema, rt as selectionKey, u as elements, v as resourceReference, y as resourceReferences } from "./src-
|
|
4
|
-
import { s as safeStorageSegment } from "./review-home-paths-
|
|
5
|
-
import { D as scopedCoverage, O as sourcesForFile, T as emptyCoverage, b as textIncludesQuote, c as traceSchema, k as subtractIntervals, n as migrateStoredDocument, r as migrateStoredLenses, s as softwareModelDataSchema, t as liftFileLenses, u as pullRequestUrl, w as coverageSources, x as textualRows } from "./stored-document-migration-
|
|
3
|
+
import { $ as comparisonKey, A as parseMarkdown, B as lensSchema, E as sourceReferences, L as UNCATEGORIZED_LENS_ID, M as checkReferences, O as markdownNodes, Q as anchorSelection, T as selectionReferences, V as lensSelections, Z as SelectionUnavailableError, _ as readsAtBoardPins, at as unionIntervals, c as documentSchema, h as pinsSchema, it as sourceAnchors, n as anchorPins, nt as resolveDiffSelection, o as boardRepositoryId, ot as ReviewInputError, q as fileLineRangeSchema, rt as selectionKey, u as elements, v as resourceReference, y as resourceReferences } from "./src-BQDlJXkG.js";
|
|
4
|
+
import { s as safeStorageSegment } from "./review-home-paths-BmZX2qiz.js";
|
|
5
|
+
import { D as scopedCoverage, O as sourcesForFile, T as emptyCoverage, b as textIncludesQuote, c as traceSchema, k as subtractIntervals, n as migrateStoredDocument, r as migrateStoredLenses, s as softwareModelDataSchema, t as liftFileLenses, u as pullRequestUrl, w as coverageSources, x as textualRows } from "./stored-document-migration-D1wK4yID.js";
|
|
6
6
|
import { N as devReviewHome } from "./s3-config-CjcLyFV5.js";
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { z } from "zod";
|
|
@@ -36,51 +36,93 @@ const gitHubRepositoryUrlSchema = z.string().refine((value) => {
|
|
|
36
36
|
const commitSchema = z.string().regex(/^[a-f0-9]{40}([a-f0-9]{24})?$/);
|
|
37
37
|
const sideSchema = z.enum(["base", "head"]);
|
|
38
38
|
/** An excerpt is the lines a board references. */
|
|
39
|
-
const shareSourceSchema = z.discriminatedUnion("kind", [z.
|
|
39
|
+
const shareSourceSchema = z.discriminatedUnion("kind", [z.object({
|
|
40
40
|
kind: z.literal("excerpt"),
|
|
41
41
|
file: sharePathSchema,
|
|
42
42
|
commit: commitSchema,
|
|
43
43
|
fromLine: z.number().int().positive(),
|
|
44
44
|
lines: z.array(z.string()).min(1)
|
|
45
45
|
})]);
|
|
46
|
-
const lineRangeSchema = z.
|
|
46
|
+
const lineRangeSchema = z.object({
|
|
47
47
|
side: sideSchema,
|
|
48
48
|
file: sharePathSchema,
|
|
49
49
|
fromLine: z.number().int().positive(),
|
|
50
50
|
toLine: z.number().int().positive(),
|
|
51
|
-
pins: z.
|
|
51
|
+
pins: z.object({
|
|
52
52
|
repositoryId: z.string(),
|
|
53
53
|
head: z.string(),
|
|
54
54
|
base: z.string().optional()
|
|
55
55
|
}).optional()
|
|
56
56
|
});
|
|
57
|
-
|
|
57
|
+
/** 1-based base and head lines; null when a side lacks the line. */
|
|
58
|
+
const alignmentRowSchema = z.tuple([z.number().int().positive().nullable(), z.number().int().positive().nullable()]);
|
|
59
|
+
const shareAlignmentSchema = z.object({
|
|
60
|
+
file: sharePathSchema,
|
|
61
|
+
previousFile: sharePathSchema.optional(),
|
|
62
|
+
head: z.string(),
|
|
63
|
+
base: z.string().optional(),
|
|
64
|
+
rows: z.array(alignmentRowSchema).min(1)
|
|
65
|
+
});
|
|
66
|
+
/** Unknown fields drop, so a reader opens bundles from newer sharers. */
|
|
67
|
+
const shareBundleSchema = z.object({
|
|
58
68
|
format: z.literal(SHARE_FORMAT),
|
|
59
69
|
reviewId: z.string().min(1).max(256),
|
|
60
70
|
version: z.number().int().nonnegative(),
|
|
61
71
|
title: z.string().max(4096),
|
|
62
72
|
/** Validated against the document schema by the reader. */
|
|
63
73
|
snapshot: z.record(z.string(), z.unknown()),
|
|
64
|
-
images: z.record(z.string(), z.
|
|
74
|
+
images: z.record(z.string(), z.object({ png: z.base64() })),
|
|
65
75
|
traces: z.record(z.string(), z.unknown()),
|
|
66
76
|
maps: z.record(z.string(), z.unknown()),
|
|
67
77
|
commits: z.array(z.unknown()),
|
|
68
78
|
/** Each reference's lines, resolved where the board was shared. */
|
|
69
79
|
selections: z.record(z.string(), z.array(lineRangeSchema)),
|
|
70
80
|
sources: z.array(shareSourceSchema),
|
|
71
|
-
|
|
81
|
+
/** The sharer's diff over the selections, since 0.4. */
|
|
82
|
+
alignments: z.array(shareAlignmentSchema).optional(),
|
|
83
|
+
repository: z.object({ cloneUrl: gitHubRepositoryUrlSchema }).optional(),
|
|
84
|
+
/** Quotes uncommitted changes: view only, never imported. Since 0.6. */
|
|
85
|
+
frozen: z.literal(true).optional()
|
|
72
86
|
});
|
|
73
|
-
z.
|
|
87
|
+
z.object({
|
|
74
88
|
requestId: z.uuid(),
|
|
75
89
|
bundle: shareBundleSchema
|
|
76
90
|
});
|
|
91
|
+
const shareCreatedSchema = z.strictObject({
|
|
92
|
+
shareId: shareIdSchema,
|
|
93
|
+
url: z.url()
|
|
94
|
+
});
|
|
95
|
+
const shareSizeSchema = z.number().int().positive().max(MAX_SHARE_BYTES);
|
|
96
|
+
const shareRevisionRefSchema = z.object({
|
|
97
|
+
id: shareIdSchema,
|
|
98
|
+
version: z.number().int().nonnegative(),
|
|
99
|
+
createdAt: z.number()
|
|
100
|
+
});
|
|
101
|
+
const shareRevisionSchema = shareRevisionRefSchema.extend({
|
|
102
|
+
size: shareSizeSchema,
|
|
103
|
+
url: z.url()
|
|
104
|
+
});
|
|
105
|
+
const shareRevisionsSchema = z.object({
|
|
106
|
+
latest: shareIdSchema,
|
|
107
|
+
items: z.array(shareRevisionSchema)
|
|
108
|
+
});
|
|
109
|
+
const sharedMetaSchema = z.object({
|
|
110
|
+
sender: z.strictObject({ login: z.string().min(1).max(256) }),
|
|
111
|
+
sharedAt: z.number(),
|
|
112
|
+
size: shareSizeSchema,
|
|
113
|
+
revision: shareRevisionRefSchema,
|
|
114
|
+
latest: shareIdSchema
|
|
115
|
+
});
|
|
77
116
|
function parseShareLink(value) {
|
|
78
117
|
const url = new URL(value);
|
|
79
|
-
if (url.protocol !== "https:" || url.username || url.password || url.hash || [...url.searchParams.keys()].some((key) => key !== "app")) throw new Error("Use an HTTPS Whiteboard share link.");
|
|
80
|
-
|
|
118
|
+
if (url.protocol !== "https:" || url.username || url.password || url.hash || [...url.searchParams.keys()].some((key) => key !== "app" && key !== "rev")) throw new Error("Use an HTTPS Whiteboard share link.");
|
|
119
|
+
const link = {
|
|
81
120
|
origin: url.origin,
|
|
82
121
|
capability: capabilitySchema.parse(/^\/s\/([^/]+)$/.exec(url.pathname)?.[1])
|
|
83
122
|
};
|
|
123
|
+
const revision = url.searchParams.get("rev");
|
|
124
|
+
if (revision !== null) link.revision = shareIdSchema.parse(revision);
|
|
125
|
+
return link;
|
|
84
126
|
}
|
|
85
127
|
//#endregion
|
|
86
128
|
//#region src/server/bounded-stream.ts
|
|
@@ -425,7 +467,11 @@ async function exportShare(input) {
|
|
|
425
467
|
const stored = structuredClone(input.store.read(input.reviewId, input.version));
|
|
426
468
|
const repositoryId = boardRepositoryId(stored);
|
|
427
469
|
const { target, staleSources: _staleSources, sourceUnavailable: _sourceUnavailable, lastEdit: _lastEdit, trashedAt: _trashedAt, personEdit: _personEdit, ...snapshot } = stored;
|
|
428
|
-
|
|
470
|
+
const live = target?.kind === "worktree" ? await input.data.sourcePins(stored) : void 0;
|
|
471
|
+
if (live) {
|
|
472
|
+
const { worktreeRevision: _worktreeRevision, ...committed } = live;
|
|
473
|
+
snapshot.pins = committed;
|
|
474
|
+
}
|
|
429
475
|
const pullRequestUrl = snapshot.origin?.pullRequestUrl;
|
|
430
476
|
const baseBranch = pullRequestUrl && !snapshot.origin?.baseBranch && (snapshot.origin?.pullRequestBaseBranch ?? input.data.pullRequestBaseBranch(pullRequestUrl));
|
|
431
477
|
if (baseBranch) snapshot.origin = {
|
|
@@ -439,7 +485,7 @@ async function exportShare(input) {
|
|
|
439
485
|
if (block.type !== "markdown") continue;
|
|
440
486
|
for (const node of markdownNodes(parseMarkdown(block.markdown))) if (node.type === "image") throw new ReviewInputError("Convert Markdown images to managed image blocks before sharing.");
|
|
441
487
|
}
|
|
442
|
-
const pins = snapshot.pins;
|
|
488
|
+
const pins = live ?? snapshot.pins;
|
|
443
489
|
const references = [...sourceReferences(snapshot.document, { repositoryId }), ...lensSelections(snapshot.lenses ?? []).flatMap((selection) => sourceAnchors(selection.source).map((source) => ({ source })))];
|
|
444
490
|
const traces = /* @__PURE__ */ new Map();
|
|
445
491
|
const maps = /* @__PURE__ */ new Map();
|
|
@@ -480,7 +526,16 @@ async function exportShare(input) {
|
|
|
480
526
|
fromLine,
|
|
481
527
|
toLine
|
|
482
528
|
}));
|
|
483
|
-
|
|
529
|
+
let frozen = false;
|
|
530
|
+
const sources = await cutExcerpts({ async quote(at, range) {
|
|
531
|
+
const quote = await input.data.quote(at, range);
|
|
532
|
+
if (at.worktreeRevision && range.side === "head" && !frozen) {
|
|
533
|
+
const { worktreeRevision: _worktreeRevision, ...committed } = at;
|
|
534
|
+
if (await input.data.quote(committed, range).then((head) => head.text !== quote.text, () => true)) frozen = true;
|
|
535
|
+
}
|
|
536
|
+
return quote;
|
|
537
|
+
} }, excerpts);
|
|
538
|
+
const bundle = {
|
|
484
539
|
format: SHARE_FORMAT,
|
|
485
540
|
reviewId: snapshot.reviewId,
|
|
486
541
|
version: snapshot.version,
|
|
@@ -491,9 +546,49 @@ async function exportShare(input) {
|
|
|
491
546
|
maps: Object.fromEntries(maps),
|
|
492
547
|
commits: pins ? await input.data.commits((await input.data.resolveSource(stored)).pins) : [],
|
|
493
548
|
selections,
|
|
494
|
-
sources
|
|
549
|
+
sources,
|
|
495
550
|
...input.repository && { repository: input.repository }
|
|
496
551
|
};
|
|
552
|
+
if (frozen) bundle.frozen = true;
|
|
553
|
+
return bundle;
|
|
554
|
+
}
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region src/slug.ts
|
|
557
|
+
/** Lowercase ASCII slug: runs of anything but `a-z0-9` become one `-`, outer
|
|
558
|
+
* dashes go. Empty input, or input with no letters or digits, yields `""`;
|
|
559
|
+
* callers choose their own fallback (`"section"`, `"diagram"`, `"database"`).
|
|
560
|
+
* This is the rule sequence and lens ids were already published with, so it
|
|
561
|
+
* must not change. */
|
|
562
|
+
function slugify(text) {
|
|
563
|
+
return text.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
564
|
+
}
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region src/sharing/file.ts
|
|
567
|
+
const WHITEBOARD_FILE_EXTENSION = ".whiteboard";
|
|
568
|
+
const WHITEBOARD_UPGRADE_REQUIRED_MESSAGE = "This action requires a newer version of Whiteboard. Use Check for Updates, then try again.";
|
|
569
|
+
const headerSchema = z.looseObject({ format: z.string() });
|
|
570
|
+
function whiteboardFileName(title) {
|
|
571
|
+
return `${slugify(title) || "untitled"}${WHITEBOARD_FILE_EXTENSION}`;
|
|
572
|
+
}
|
|
573
|
+
function whiteboardFileText(bundle) {
|
|
574
|
+
return JSON.stringify(bundle, null, 2) + "\n";
|
|
575
|
+
}
|
|
576
|
+
function parseWhiteboardFile(text) {
|
|
577
|
+
let parsed;
|
|
578
|
+
try {
|
|
579
|
+
parsed = JSON.parse(text);
|
|
580
|
+
} catch {
|
|
581
|
+
parsed = void 0;
|
|
582
|
+
}
|
|
583
|
+
const header = headerSchema.safeParse(parsed);
|
|
584
|
+
if (!header.success) throw new ReviewInputError("This is not a Whiteboard file.", 400);
|
|
585
|
+
const version = /^review-share\/([1-9]\d*)$/.exec(header.data.format);
|
|
586
|
+
if (version && Number(version[1]) > Number("review-share/2".split("/")[1])) throw new ReviewInputError(WHITEBOARD_UPGRADE_REQUIRED_MESSAGE, 400);
|
|
587
|
+
if (header.data.format !== "review-share/2") throw new ReviewInputError("This file needs a different Whiteboard version.", 400);
|
|
588
|
+
return shareBundleSchema.parse(parsed);
|
|
589
|
+
}
|
|
590
|
+
function whiteboardFileKey(text) {
|
|
591
|
+
return createHash("sha256").update("whiteboard-file\0").update(text).digest("hex");
|
|
497
592
|
}
|
|
498
593
|
//#endregion
|
|
499
594
|
//#region src/sharing/client.ts
|
|
@@ -512,15 +607,6 @@ const pooledFetch = (input, init) => {
|
|
|
512
607
|
const jwtSchema = z.object({ token: z.string().min(1) });
|
|
513
608
|
const jwtPayloadSchema = z.object({ exp: z.number() });
|
|
514
609
|
const bearers = /* @__PURE__ */ new Map();
|
|
515
|
-
const linkSchema = z.strictObject({
|
|
516
|
-
shareId: z.uuid(),
|
|
517
|
-
url: z.url()
|
|
518
|
-
});
|
|
519
|
-
const receivedSchema = z.object({
|
|
520
|
-
sender: z.strictObject({ login: z.string().min(1).max(256) }),
|
|
521
|
-
sharedAt: z.number(),
|
|
522
|
-
size: z.number().int().positive().max(MAX_SHARE_BYTES)
|
|
523
|
-
});
|
|
524
610
|
/** Size is checked while reading, even if Content-Length is absent or false. */
|
|
525
611
|
async function readBoundedBytes(response, limit) {
|
|
526
612
|
if (!response.ok || !response.body) throw new Error(`Download failed (${response.status}).`);
|
|
@@ -543,6 +629,44 @@ var ShareAuthError = class extends Error {
|
|
|
543
629
|
this.name = "ShareAuthError";
|
|
544
630
|
}
|
|
545
631
|
};
|
|
632
|
+
var ShareRequestError = class extends Error {
|
|
633
|
+
status;
|
|
634
|
+
constructor(status, message) {
|
|
635
|
+
super(message);
|
|
636
|
+
this.status = status;
|
|
637
|
+
this.name = "ShareRequestError";
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
const errorSchema = z.object({ error: z.object({ code: z.string() }) });
|
|
641
|
+
async function errorCode(response) {
|
|
642
|
+
if (!response.body) return void 0;
|
|
643
|
+
if (!response.headers.get("content-type")?.includes("application/json")) {
|
|
644
|
+
await response.body.cancel();
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
const bytes = await readBoundedStream(response.body, 16384);
|
|
649
|
+
return errorSchema.safeParse(parseJsonText(bytes.toString())).data?.error.code;
|
|
650
|
+
} catch {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
function requestErrorMessage(response, code) {
|
|
655
|
+
const { status, headers } = response;
|
|
656
|
+
if (status === 426 || code === "upgrade_required") return WHITEBOARD_UPGRADE_REQUIRED_MESSAGE;
|
|
657
|
+
if (status === 403 && headers.get("content-type")?.includes("text/html") && headers.get("server")?.toLowerCase() === "cloudflare") return "The sharing service's security filter blocked this request. Please report this issue.";
|
|
658
|
+
if (status === 413) return "This review is too large to share. Remove large resources, then retry.";
|
|
659
|
+
if (status === 429) return code === "share_quota_exceeded" ? "Your sharing storage limit has been reached. Please contact support." : "Too many sharing requests. Wait a moment, then retry.";
|
|
660
|
+
if (status === 400 && code === "invalid_request") return "The sharing service could not accept this review. Update Whiteboard or report this issue.";
|
|
661
|
+
if (status >= 500) return `The sharing service is temporarily unavailable (HTTP ${status}). Try again shortly.`;
|
|
662
|
+
return `The sharing service refused this request (HTTP ${status}). Please report this issue.`;
|
|
663
|
+
}
|
|
664
|
+
async function requestError(response) {
|
|
665
|
+
const message = requestErrorMessage(response, await errorCode(response));
|
|
666
|
+
const ray = response.headers.get("cf-ray");
|
|
667
|
+
const reference = ray && /^[a-f0-9]{16}(?:-[a-z]{3})?$/i.test(ray) ? ` Reference: ${ray}.` : "";
|
|
668
|
+
return new ShareRequestError(response.status, message + reference);
|
|
669
|
+
}
|
|
546
670
|
/** Account tokens go only to the configured API origin. */
|
|
547
671
|
var ShareClient = class {
|
|
548
672
|
token;
|
|
@@ -570,9 +694,11 @@ var ShareClient = class {
|
|
|
570
694
|
if (body !== void 0) init.body = body;
|
|
571
695
|
const response = await this.send(this.origin + route, init);
|
|
572
696
|
if (!response.ok) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
697
|
+
if (response.status === 401 && !anonymous) {
|
|
698
|
+
await response.body?.cancel();
|
|
699
|
+
throw new ShareAuthError();
|
|
700
|
+
}
|
|
701
|
+
throw await requestError(response);
|
|
576
702
|
}
|
|
577
703
|
return readBoundedBytes(response, maxBytes);
|
|
578
704
|
}
|
|
@@ -595,13 +721,18 @@ var ShareClient = class {
|
|
|
595
721
|
headers: { authorization: `Bearer ${token}` }
|
|
596
722
|
});
|
|
597
723
|
if (!response.ok) {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
724
|
+
if (response.status === 404 || response.status === 405) {
|
|
725
|
+
await response.body?.cancel();
|
|
726
|
+
return {
|
|
727
|
+
bearer: token,
|
|
728
|
+
until: Date.now() + 6e5
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
if (response.status === 401) {
|
|
732
|
+
await response.body?.cancel();
|
|
733
|
+
throw new ShareAuthError();
|
|
734
|
+
}
|
|
735
|
+
throw await requestError(response);
|
|
605
736
|
}
|
|
606
737
|
const { token: jwt } = jwtSchema.parse(parseJsonText(Buffer.from(await readBoundedBytes(response, 65536)).toString()));
|
|
607
738
|
const { exp } = jwtPayloadSchema.parse(parseJsonText(Buffer.from(jwt.split(".")[1] ?? "", "base64url").toString()));
|
|
@@ -618,29 +749,38 @@ var ShareClient = class {
|
|
|
618
749
|
})).body?.cancel();
|
|
619
750
|
}
|
|
620
751
|
async create(bundle, requestId = randomUUID()) {
|
|
621
|
-
return
|
|
752
|
+
return shareCreatedSchema.parse(await this.api("/api/v2/shares", "POST", shareRequestBody(bundle, requestId)));
|
|
622
753
|
}
|
|
623
754
|
async list(cursor) {
|
|
624
755
|
const query = cursor ? `?cursor=${z.uuid().parse(cursor)}` : "";
|
|
625
756
|
return this.api(`/api/v2/shares${query}`);
|
|
626
757
|
}
|
|
627
758
|
async recoverLink(shareId) {
|
|
628
|
-
return
|
|
759
|
+
return shareCreatedSchema.parse(await this.api(`/api/v2/shares/${z.uuid().parse(shareId)}/link`));
|
|
629
760
|
}
|
|
630
761
|
async revoke(shareId) {
|
|
631
762
|
return this.api(`/api/v2/shares/${z.uuid().parse(shareId)}`, "DELETE");
|
|
632
763
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
764
|
+
async revisions(shareId) {
|
|
765
|
+
return shareRevisionsSchema.parse(await this.api(`/api/v2/shares/${shareIdSchema.parse(shareId)}/revisions`));
|
|
766
|
+
}
|
|
767
|
+
/** Who shared a link's revision, and which revision is its latest. */
|
|
768
|
+
async shared(capability, revision) {
|
|
769
|
+
const query = revision === void 0 ? "" : `?rev=${shareIdSchema.parse(revision)}`;
|
|
770
|
+
return sharedMetaSchema.parse(await this.api(`/api/v2/shared/${capabilitySchema.parse(capability)}${query}`, "GET", void 0, true));
|
|
771
|
+
}
|
|
772
|
+
/** The bundle of the revision the meta names, and who shared it. */
|
|
773
|
+
async download(capability, revision) {
|
|
774
|
+
const meta = await this.shared(capability, revision);
|
|
775
|
+
const bytes = await this.request(`/api/v2/shared/${capability}/bundle?rev=${meta.revision.id}`, "GET", void 0, true, MAX_SHARE_BYTES);
|
|
638
776
|
return {
|
|
639
|
-
bundle:
|
|
777
|
+
bundle: parseWhiteboardFile(new TextDecoder().decode(bytes)),
|
|
640
778
|
attribution: {
|
|
641
|
-
login:
|
|
642
|
-
sharedAt:
|
|
643
|
-
}
|
|
779
|
+
login: meta.sender.login,
|
|
780
|
+
sharedAt: meta.sharedAt
|
|
781
|
+
},
|
|
782
|
+
revision: meta.revision,
|
|
783
|
+
latest: meta.latest
|
|
644
784
|
};
|
|
645
785
|
}
|
|
646
786
|
};
|
|
@@ -847,7 +987,7 @@ async function readShareRepository(root) {
|
|
|
847
987
|
}
|
|
848
988
|
//#endregion
|
|
849
989
|
//#region src/sharing/import.ts
|
|
850
|
-
const sharedSnapshotSchema = z.
|
|
990
|
+
const sharedSnapshotSchema = z.object({
|
|
851
991
|
reviewId: z.string().min(1),
|
|
852
992
|
version: z.number().int().nonnegative(),
|
|
853
993
|
/** Empty for a board never named; it reads "Untitled". */
|
|
@@ -859,7 +999,7 @@ const sharedSnapshotSchema = z.strictObject({
|
|
|
859
999
|
document: documentSchema,
|
|
860
1000
|
lenses: z.array(lensSchema).optional(),
|
|
861
1001
|
createdAt: z.string(),
|
|
862
|
-
origin: z.
|
|
1002
|
+
origin: z.object({
|
|
863
1003
|
branch: z.string().optional(),
|
|
864
1004
|
baseRef: z.string().optional(),
|
|
865
1005
|
baseBranch: z.string().optional(),
|
|
@@ -871,7 +1011,7 @@ const sharedSnapshotSchema = z.strictObject({
|
|
|
871
1011
|
}).optional()
|
|
872
1012
|
});
|
|
873
1013
|
const count = z.number().int().nonnegative();
|
|
874
|
-
const counts = z.
|
|
1014
|
+
const counts = z.object({
|
|
875
1015
|
additions: count,
|
|
876
1016
|
deletions: count
|
|
877
1017
|
});
|
|
@@ -881,9 +1021,9 @@ const mapSchema = softwareModelDataSchema.extend({
|
|
|
881
1021
|
countsByElementPath: z.record(z.string(), counts),
|
|
882
1022
|
unmappedByElementPath: z.record(z.string(), counts.extend({ files: z.array(counts.extend({
|
|
883
1023
|
file: z.string(),
|
|
884
|
-
hunks: z.array(z.
|
|
1024
|
+
hunks: z.array(z.object({
|
|
885
1025
|
startLine: count,
|
|
886
|
-
lines: z.array(z.
|
|
1026
|
+
lines: z.array(z.object({
|
|
887
1027
|
kind: z.enum(["add", "remove"]),
|
|
888
1028
|
oldLine: count.nullable(),
|
|
889
1029
|
newLine: count.nullable(),
|
|
@@ -892,13 +1032,14 @@ const mapSchema = softwareModelDataSchema.extend({
|
|
|
892
1032
|
}))
|
|
893
1033
|
})) }))
|
|
894
1034
|
});
|
|
895
|
-
z.
|
|
1035
|
+
z.object({ maps: z.record(z.string(), mapSchema) });
|
|
896
1036
|
/** Commits and traces live in a checkout; a document alone needs none. */
|
|
897
1037
|
function needsRepository(bundle, snapshot) {
|
|
898
1038
|
return Boolean(snapshot.pins || referenceCommits(snapshot).length || Object.keys(bundle.traces).length);
|
|
899
1039
|
}
|
|
900
1040
|
function validateShareBundle(input) {
|
|
901
1041
|
const bundle = shareBundleSchema.parse(input);
|
|
1042
|
+
if (bundle.frozen) throw new ReviewInputError("This whiteboard is a read-only snapshot of uncommitted changes. View it in the browser.", 409);
|
|
902
1043
|
const stored = bundle.snapshot;
|
|
903
1044
|
const snapshot = sharedSnapshotSchema.parse(stored.document === void 0 ? stored : liftSharedLenses(stored, migrateStoredDocument(stored.document)));
|
|
904
1045
|
if (snapshot.reviewId !== bundle.reviewId || snapshot.version !== bundle.version || snapshot.title !== bundle.title) throw new Error("Shared snapshot does not match its bundle.");
|
|
@@ -948,12 +1089,41 @@ function shareKey(origin, capability) {
|
|
|
948
1089
|
capabilitySchema.parse(capability);
|
|
949
1090
|
return createHash("sha256").update(JSON.stringify([url.origin, capability])).digest("hex");
|
|
950
1091
|
}
|
|
951
|
-
/**
|
|
952
|
-
|
|
953
|
-
|
|
1092
|
+
/** Which copy a served revision opens or refreshes in place; otherwise a new copy. */
|
|
1093
|
+
function planShareCopy(copies, served, pinned = false) {
|
|
1094
|
+
const same = copies.find(({ shared }) => shared.revisionId === void 0 ? shared.sharedAt === served.revision.createdAt : shared.revisionId === served.revision.id);
|
|
1095
|
+
if (same) return {
|
|
1096
|
+
kind: "open",
|
|
1097
|
+
reviewId: same.reviewId
|
|
1098
|
+
};
|
|
1099
|
+
const unedited = !pinned && served.revision.id === served.latest && copies.find((copy) => !copy.shared.pinned && copy.version === (copy.shared.importedVersion ?? 0));
|
|
1100
|
+
return unedited ? {
|
|
1101
|
+
kind: "refresh",
|
|
1102
|
+
reviewId: unedited.reviewId,
|
|
1103
|
+
version: unedited.version
|
|
1104
|
+
} : { kind: "import" };
|
|
1105
|
+
}
|
|
1106
|
+
/** Opens, refreshes or copies a link's board by the revision it serves. */
|
|
1107
|
+
async function receiveShare(input, key, served, download, pinned = false) {
|
|
1108
|
+
const plan = planShareCopy(input.store.copiesOf(key), served, pinned);
|
|
1109
|
+
if (plan.kind === "open") return plan.reviewId;
|
|
1110
|
+
const { bundle, attribution } = await download();
|
|
1111
|
+
const provenance = {
|
|
1112
|
+
...attribution,
|
|
1113
|
+
revisionId: served.revision.id,
|
|
1114
|
+
...pinned && { pinned }
|
|
1115
|
+
};
|
|
1116
|
+
if (plan.kind === "import") return importShare(input, key, bundle, provenance);
|
|
1117
|
+
try {
|
|
1118
|
+
return await importShare(input, key, bundle, provenance, plan);
|
|
1119
|
+
} catch (error) {
|
|
1120
|
+
if (error instanceof ReviewInputError && (error.status === 409 || error.status === 404)) return importShare(input, key, bundle, provenance);
|
|
1121
|
+
throw error;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
/** Imports a share as an ordinary board, or into a copy of its link. */
|
|
1125
|
+
async function importShare(input, key, received, provenance, into) {
|
|
954
1126
|
const { store, data } = input;
|
|
955
|
-
const existing = store.copyOf(key);
|
|
956
|
-
if (existing) return existing;
|
|
957
1127
|
const { bundle, snapshot, maps } = validateShareBundle(received);
|
|
958
1128
|
const cloneUrl = bundle.repository?.cloneUrl;
|
|
959
1129
|
let repositoryId;
|
|
@@ -988,7 +1158,7 @@ async function importShare(input, key, received, attribution) {
|
|
|
988
1158
|
}
|
|
989
1159
|
let document = JSON.stringify(snapshot.document);
|
|
990
1160
|
for (const [id, copy] of ids) document = document.replaceAll(id, copy);
|
|
991
|
-
const
|
|
1161
|
+
const create = { operation: {
|
|
992
1162
|
type: "create",
|
|
993
1163
|
...snapshot.title && { title: snapshot.title },
|
|
994
1164
|
...pins ? { target: {
|
|
@@ -998,17 +1168,19 @@ async function importShare(input, key, received, attribution) {
|
|
|
998
1168
|
kind: "project",
|
|
999
1169
|
repositoryId
|
|
1000
1170
|
} }
|
|
1001
|
-
} }
|
|
1171
|
+
} };
|
|
1172
|
+
const seed = {
|
|
1002
1173
|
document: JSON.parse(document),
|
|
1003
1174
|
lenses: snapshot.lenses,
|
|
1004
1175
|
origin: snapshot.origin,
|
|
1005
1176
|
shared: {
|
|
1006
|
-
...
|
|
1177
|
+
...provenance,
|
|
1007
1178
|
cloneUrl,
|
|
1008
1179
|
key
|
|
1009
1180
|
},
|
|
1010
1181
|
copiedFrom: snapshot.reviewId
|
|
1011
|
-
}
|
|
1182
|
+
};
|
|
1183
|
+
const { reviewId } = into ? await store.refreshCopy(into.reviewId, into.version, create, seed) : await store.execute(create, seed, { kind: "person" });
|
|
1012
1184
|
return reviewId;
|
|
1013
1185
|
}
|
|
1014
1186
|
/** A checkout of the share's repository with every commit it reads at: the
|
|
@@ -1050,4 +1222,4 @@ async function shareRepository({ store, data, ...git }, cloneUrl, pins, commits,
|
|
|
1050
1222
|
return repositoryId;
|
|
1051
1223
|
}
|
|
1052
1224
|
//#endregion
|
|
1053
|
-
export {
|
|
1225
|
+
export { StreamLimitError as C, shareIdSchema as D, shareBundleSchema as E, uncategorizedReport as S, parseShareLink as T, validateShareSources as _, shareKey as a, progressUpdateSchema as b, readShareRepository as c, ShareRequestError as d, parseWhiteboardFile as f, exportShare as g, whiteboardFileText as h, receiveShare as i, ShareAuthError as l, whiteboardFileName as m, needsRepository as n, validateShareBundle as o, whiteboardFileKey as p, planShareCopy as r, forgetSharedCommits as s, importShare as t, ShareClient as u, legacyModeSchema as v, readBoundedStream as w, reviewProgress as x, lensReport as y };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { S as splitGitPatchFiles, T as runVcsSync, _ as listTrackedFilesAtCommit, a as diff, b as resolveRepoContext, c as diffFileSummariesWorkingTree, d as git, f as gitAt, g as listCommitRange, h as jjRevisionIsConflicted, i as detectLocalVcs, l as diffTrees, m as gitCommonDirSync, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as parseGitRemote, w as runVcs, x as resolveRevision, y as readFileAtCommit } from "./src-Y30gweiK.js";
|
|
2
2
|
import { c as jsonString, m as isStringValue, n as isJsonObject, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
3
|
-
import { A as parseMarkdown, B as lensSchema, D as summarizeEdit, E as sourceReferences, G as SourceRangeError, J as requireVisibleSource, K as checkSourcePath, M as checkReferences, O as markdownNodes, P as documentAdvice, R as applyLensEdit, S as respellEditLinks, V as lensSelections, Y as sliceSourceRange, a as boardLinks, at as unionIntervals, b as respellCommitLinks, c as documentSchema, ct as activityEndSchema, d as explicitPins, h as pinsSchema, i as assignMissingIds, it as sourceAnchors, k as markdownText, l as editSchema, lt as activityUpdateSchema, m as pinnedRepositoryIds, n as anchorPins, o as boardRepositoryId, ot as ReviewInputError, p as pinDocumentReferences, q as fileLineRangeSchema, r as applyEdit, s as commitLinkCommits, st as activityBeginSchema, t as adoptFlowUnits, tt as parseAnchor, u as elements, ut as focusSchema, v as resourceReference, w as reviewTargetSchema, x as respellDocumentLinks, y as resourceReferences, z as lensEditSchema } from "./src-
|
|
4
|
-
import {
|
|
5
|
-
import { s as safeStorageSegment } from "./review-home-paths-
|
|
6
|
-
import { A as updateCoverage, C as coverageSchema, E as intersectIntervals, S as parseUnifiedPatch, T as emptyCoverage, _ as fetchPullRequest, a as SoftwareModelValidationError, b as textIncludesQuote, c as traceSchema, d as setPullRequest, f as sourceBranchName, g as defaultPullRequestDeps, h as GitHubRefusedRead, i as readStoredDocument, k as subtractIntervals, l as pullRequestKey, m as resolvePullRequest, n as migrateStoredDocument, o as defineSoftwareMap, r as migrateStoredLenses, u as pullRequestUrl, v as remoteUrls, x as textualRows, y as azureRepositoryOf } from "./stored-document-migration-
|
|
3
|
+
import { A as parseMarkdown, B as lensSchema, D as summarizeEdit, E as sourceReferences, G as SourceRangeError, J as requireVisibleSource, K as checkSourcePath, M as checkReferences, O as markdownNodes, P as documentAdvice, R as applyLensEdit, S as respellEditLinks, V as lensSelections, Y as sliceSourceRange, a as boardLinks, at as unionIntervals, b as respellCommitLinks, c as documentSchema, ct as activityEndSchema, d as explicitPins, h as pinsSchema, i as assignMissingIds, it as sourceAnchors, k as markdownText, l as editSchema, lt as activityUpdateSchema, m as pinnedRepositoryIds, n as anchorPins, o as boardRepositoryId, ot as ReviewInputError, p as pinDocumentReferences, q as fileLineRangeSchema, r as applyEdit, s as commitLinkCommits, st as activityBeginSchema, t as adoptFlowUnits, tt as parseAnchor, u as elements, ut as focusSchema, v as resourceReference, w as reviewTargetSchema, x as respellDocumentLinks, y as resourceReferences, z as lensEditSchema } from "./src-BQDlJXkG.js";
|
|
4
|
+
import { I as applyStructuralUpdate, L as decodeStructuralDiffEvent, R as structuralChangeCounts, c as boardIconKind, l as boardTitle } from "./src-CrrW7lcY.js";
|
|
5
|
+
import { s as safeStorageSegment } from "./review-home-paths-BmZX2qiz.js";
|
|
6
|
+
import { A as updateCoverage, C as coverageSchema, E as intersectIntervals, S as parseUnifiedPatch, T as emptyCoverage, _ as fetchPullRequest, a as SoftwareModelValidationError, b as textIncludesQuote, c as traceSchema, d as setPullRequest, f as sourceBranchName, g as defaultPullRequestDeps, h as GitHubRefusedRead, i as readStoredDocument, k as subtractIntervals, l as pullRequestKey, m as resolvePullRequest, n as migrateStoredDocument, o as defineSoftwareMap, r as migrateStoredLenses, u as pullRequestUrl, v as remoteUrls, x as textualRows, y as azureRepositoryOf } from "./stored-document-migration-D1wK4yID.js";
|
|
7
7
|
import { A as processStartIdentity, L as writePrivateJsonAtomic, O as pidReused, j as withFileLock, k as processIsAlive } from "./s3-config-CjcLyFV5.js";
|
|
8
8
|
import { t as errorMessage } from "./error-message-OtiDonty.js";
|
|
9
|
-
import { a as askHistoryEntrySchema, i as askEntrySchema, m as isMissingFileError, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-
|
|
9
|
+
import { a as askHistoryEntrySchema, i as askEntrySchema, m as isMissingFileError, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-WTqSborE.js";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
12
12
|
import { z } from "zod";
|
|
@@ -3095,11 +3095,16 @@ var ReviewStore = class {
|
|
|
3095
3095
|
this.pending = run.catch(() => {});
|
|
3096
3096
|
return run;
|
|
3097
3097
|
}
|
|
3098
|
-
/** The
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3098
|
+
/** The boards copied from a share link, newest first, until each is deleted. */
|
|
3099
|
+
copiesOf(key) {
|
|
3100
|
+
return this.db.prepare(`SELECT reviews.id, reviews.version, json_extract(versions.snapshot,'$.shared') AS shared
|
|
3101
|
+
FROM reviews JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
|
|
3102
|
+
WHERE CASE WHEN json_valid(versions.snapshot) THEN json_extract(versions.snapshot,'$.shared.key') END=?
|
|
3103
|
+
ORDER BY reviews.rowid DESC`).all(key).map((row) => ({
|
|
3104
|
+
reviewId: String(row.id),
|
|
3105
|
+
version: Number(row.version),
|
|
3106
|
+
shared: JSON.parse(String(row.shared))
|
|
3107
|
+
}));
|
|
3103
3108
|
}
|
|
3104
3109
|
/** Checkouts whose remote is cloneUrl, most recently used first. */
|
|
3105
3110
|
checkoutsOf(cloneUrl) {
|
|
@@ -3361,6 +3366,21 @@ var ReviewStore = class {
|
|
|
3361
3366
|
pullRequest?.catch(() => {});
|
|
3362
3367
|
const pullRequestTitle = pullRequest ? pullRequest.then(({ title }) => title || void 0, () => void 0) : this.startPullRequestTitle(command);
|
|
3363
3368
|
const prepared = this.prepareCommand(command, pullRequest, pullRequestTitle, author).then((source) => this.applyPreparedCommand(source, initial, author));
|
|
3369
|
+
return this.track(prepared);
|
|
3370
|
+
}
|
|
3371
|
+
/** Replaces a share copy's content under the same board id; 409 once it moved past `baseVersion`. */
|
|
3372
|
+
refreshCopy(reviewId, baseVersion, create, seed) {
|
|
3373
|
+
if (this.closing) return Promise.reject(/* @__PURE__ */ new Error("Review store is closing."));
|
|
3374
|
+
const command = commandSchema.parse(create);
|
|
3375
|
+
const op = command.operation;
|
|
3376
|
+
if (op.type !== "create" || op.pullRequestUrl || op.parentId || op.dependsOn || op.projectId) throw new ReviewInputError("A refresh takes a create command with a title and target alone.");
|
|
3377
|
+
const person = { kind: "person" };
|
|
3378
|
+
return this.track(this.prepareCommand(command, void 0, Promise.resolve(void 0), person).then((source) => this.applyPreparedCommand(source, seed, person, {
|
|
3379
|
+
reviewId,
|
|
3380
|
+
baseVersion
|
|
3381
|
+
})));
|
|
3382
|
+
}
|
|
3383
|
+
track(prepared) {
|
|
3364
3384
|
this.preparing.add(prepared);
|
|
3365
3385
|
prepared.finally(() => this.preparing.delete(prepared)).catch(() => {});
|
|
3366
3386
|
return prepared;
|
|
@@ -3389,10 +3409,10 @@ var ReviewStore = class {
|
|
|
3389
3409
|
resolvedTitle: await pullRequestTitle
|
|
3390
3410
|
};
|
|
3391
3411
|
}
|
|
3392
|
-
applyPreparedCommand(prepared, initial, author) {
|
|
3412
|
+
applyPreparedCommand(prepared, initial, author, into) {
|
|
3393
3413
|
const { op, requestedTarget, fromPullRequest, resolvedTarget, resolvedBranch, discoveredPullRequest, resolvedTitle } = prepared;
|
|
3394
3414
|
return this.enqueue(async () => {
|
|
3395
|
-
if (op.type === "create" && (op.pullRequestUrl ? op.reuseExisting !== false : op.reuseExisting && resolvedTarget?.pins)) {
|
|
3415
|
+
if (!into && op.type === "create" && (op.pullRequestUrl ? op.reuseExisting !== false : op.reuseExisting && resolvedTarget?.pins)) {
|
|
3396
3416
|
const known = op.pullRequestUrl ? this.reviewsForPullRequest(op.pullRequestUrl) : [];
|
|
3397
3417
|
const branch = fromPullRequest?.branch ?? resolvedBranch;
|
|
3398
3418
|
const repository = resolvedTarget?.target.repositoryId;
|
|
@@ -3410,8 +3430,9 @@ var ReviewStore = class {
|
|
|
3410
3430
|
if (op.type === "lifecycle") return this.commitBoardFacts(op.reviewId, author, { state: op.state });
|
|
3411
3431
|
if (op.type === "attention") return this.commitBoardFacts(op.reviewId, author, { viewed: true });
|
|
3412
3432
|
if (op.type === "delete") return this.deleteBoard(this.read(op.reviewId), author, { fromTrash: true });
|
|
3413
|
-
const id = op.type === "create" ? randomUUID() : op.reviewId;
|
|
3414
|
-
const previous = op.type === "create" ? void 0 : this.read(id);
|
|
3433
|
+
const id = into?.reviewId ?? (op.type === "create" ? randomUUID() : op.reviewId);
|
|
3434
|
+
const previous = op.type === "create" && !into ? void 0 : this.read(id);
|
|
3435
|
+
if (into && (!previous?.shared?.key || previous.shared.key !== initial?.shared?.key)) throw new ReviewInputError("Only a copy of the same share link refreshes in place.");
|
|
3415
3436
|
let snapshot = op.type === "create" ? createdSnapshot(id, op, resolvedTarget, fromPullRequest && (fromPullRequest.title || `PR #${op.pullRequestUrl.split("/").at(-1)}`), author.kind === "person") : structuredClone(previous);
|
|
3416
3437
|
delete snapshot.trashedAt;
|
|
3417
3438
|
delete snapshot.sourceUnavailable;
|
|
@@ -3520,9 +3541,11 @@ var ReviewStore = class {
|
|
|
3520
3541
|
restore: op.type === "restore",
|
|
3521
3542
|
repin: op.type === "set_target",
|
|
3522
3543
|
copied: initial !== void 0,
|
|
3544
|
+
inPlace: into !== void 0,
|
|
3545
|
+
baseVersion: into?.baseVersion,
|
|
3523
3546
|
projectLive: op.type !== "restore" && !resolvedTarget,
|
|
3524
3547
|
result: {
|
|
3525
|
-
...op.type === "create" && { created: true },
|
|
3548
|
+
...op.type === "create" && !into && { created: true },
|
|
3526
3549
|
targetId: applied?.targetId ?? lensTarget?.targetId,
|
|
3527
3550
|
...applied && { type: applied.type },
|
|
3528
3551
|
...lensTarget && { type: lensTarget.type },
|
|
@@ -3590,6 +3613,7 @@ var ReviewStore = class {
|
|
|
3590
3613
|
person: author.kind === "person"
|
|
3591
3614
|
}));
|
|
3592
3615
|
warnings = [...change.warnings ?? [], ...warnings];
|
|
3616
|
+
if (change.copied && snapshot.shared) snapshot.shared.importedVersion = snapshot.version;
|
|
3593
3617
|
const result = {
|
|
3594
3618
|
...change.result,
|
|
3595
3619
|
reviewId: id,
|
|
@@ -3611,7 +3635,8 @@ var ReviewStore = class {
|
|
|
3611
3635
|
if (change.relations?.dependsOn) this.setDependencies(id, change.relations.dependsOn);
|
|
3612
3636
|
if (previous && projectId !== previousProject && this.dropRelationsOutside(id, projectId)) result.warnings = [...result.warnings ?? [], "Its parent, children and dependencies were in its old project, so they were dropped: whiteboards relate only within a project."];
|
|
3613
3637
|
}, previous ? () => {
|
|
3614
|
-
this.
|
|
3638
|
+
if (change.inPlace) this.assertExists(id);
|
|
3639
|
+
else this.assertWritable(id);
|
|
3615
3640
|
if (change.baseVersion !== void 0) assertBaseVersion(change.baseVersion, this.currentVersion(id));
|
|
3616
3641
|
else this.assertMutation(id, previous.version);
|
|
3617
3642
|
holds();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { n as isJsonObject } from "./src-X9phtB2j.js";
|
|
2
|
-
import { ot as ReviewInputError } from "./src-
|
|
3
|
-
import { a as toolResultText, s as ReviewApiError, t as ToolText } from "./agent-client-
|
|
4
|
-
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-
|
|
5
|
-
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-
|
|
2
|
+
import { ot as ReviewInputError } from "./src-BQDlJXkG.js";
|
|
3
|
+
import { a as toolResultText, s as ReviewApiError, t as ToolText } from "./agent-client-BCEbSPWU.js";
|
|
4
|
+
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-CkfxTVJm.js";
|
|
5
|
+
import { a as renderedImage, i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-YOFGHJCZ.js";
|
|
6
6
|
import { Server } from "@modelcontextprotocol/server";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
8
8
|
//#region src/review-api/mcp-client-agent.ts
|
|
@@ -118,10 +118,13 @@ async function serveReviewMcp(connect, stdin, stdout, stderr = process.stderr, t
|
|
|
118
118
|
phase = "call";
|
|
119
119
|
tool = tools.find((tool) => tool.name === request.params.name);
|
|
120
120
|
if (!tool) throw new ReviewInputError(`Unknown Whiteboard tool: ${request.params.name}`);
|
|
121
|
-
const result = await callPublicTool(client, tool, request.params.arguments ?? {}, ctx.mcpReq.signal);
|
|
121
|
+
const { image, result } = renderedImage(tool, await callPublicTool(client, tool, request.params.arguments ?? {}, ctx.mcpReq.signal));
|
|
122
122
|
const text = toolResultText(tool, result);
|
|
123
123
|
report();
|
|
124
|
-
const response = { content: [{
|
|
124
|
+
const response = { content: [...image ? [{
|
|
125
|
+
type: "image",
|
|
126
|
+
...image
|
|
127
|
+
}] : [], {
|
|
125
128
|
type: "text",
|
|
126
129
|
text: listedWhileDown && tool.name === instructionsTool.name ? `${RELOAD_TOOLS}\n\n${text}` : text
|
|
127
130
|
}] };
|