@dev.fast/whiteboard 0.0.0-stage → 0.2.1-preview.20261005.98

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.
Files changed (247) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -2
  3. package/THIRD_PARTY_NOTICES.md +93 -0
  4. package/dist/account-alias-DOHN1RSH.js +973 -0
  5. package/dist/agent-cli-ChOq0ZuO.js +117 -0
  6. package/dist/agent-cli-DzWfZr6G.js +2 -0
  7. package/dist/agent-client-CPQo7iTI.js +305 -0
  8. package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
  9. package/dist/build-info.json +1 -0
  10. package/dist/cli-hVgHcdsv.js +47 -0
  11. package/dist/cli-runner-D8y2_luU.js +5095 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +128 -0
  14. package/dist/client-DFoh50W3.js +950 -0
  15. package/dist/desktop-discovery-DBO-PM2V.js +164 -0
  16. package/dist/error-message-OtiDonty.js +6 -0
  17. package/dist/fs-utils-BMPLt0cr.js +22 -0
  18. package/dist/fuzzy-match-BoAmcyak.js +30 -0
  19. package/dist/headless-host-BJxf4dUU.js +94 -0
  20. package/dist/input-error-OLgB_h31.js +11 -0
  21. package/dist/local-data-CxLk75rx.js +4491 -0
  22. package/dist/mcp-QeF8vkdf.js +140 -0
  23. package/dist/package-paths-B6-zxvIO.js +47 -0
  24. package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
  25. package/dist/profile-4ry2f3AM.js +76 -0
  26. package/dist/profile-Bh5FIrmf.js +2 -0
  27. package/dist/request-origin-D_4QIdoN.js +811 -0
  28. package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
  29. package/dist/review-home-paths-6zZH-1c9.js +2560 -0
  30. package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
  31. package/dist/runtime-CKiWhGrf.js +52 -0
  32. package/dist/runtime.d.ts +9 -0
  33. package/dist/runtime.js +2 -0
  34. package/dist/s3-SzLpeCG6.js +345 -0
  35. package/dist/s3-config-BgxkSoOy.js +2 -0
  36. package/dist/s3-config-CAxhO9u_.js +676 -0
  37. package/dist/server/desktop-host.d.ts +4 -0
  38. package/dist/server/desktop-host.js +1702 -0
  39. package/dist/server-discovery-Dbzn3w6Z.js +66 -0
  40. package/dist/sharing/index.d.ts +1977 -0
  41. package/dist/sharing/index.js +2 -0
  42. package/dist/src-CGP5ytbV.js +283 -0
  43. package/dist/src-CmdiBL20.js +3676 -0
  44. package/dist/src-DnwdaQ2r.js +865 -0
  45. package/dist/src-X9phtB2j.js +68 -0
  46. package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
  47. package/dist/tool-failure-C8zB73HV.js +98 -0
  48. package/dist/tutorial-trace-DrBvkaeL.js +43 -0
  49. package/instructions/authoring.md +36 -0
  50. package/instructions/file-lenses.md +13 -0
  51. package/instructions/scratchpad.md +23 -0
  52. package/instructions/trace-archaeology.md +113 -0
  53. package/onboarding.md +8 -0
  54. package/package.json +106 -3
  55. package/src/agent-selection.ts +100 -0
  56. package/src/agent-session-ref.ts +80 -0
  57. package/src/ask/agents.ts +351 -0
  58. package/src/ask/checkout-files.ts +47 -0
  59. package/src/ask/file-refs.ts +111 -0
  60. package/src/ask/pi-mcp.ts +154 -0
  61. package/src/ask/protocol.ts +244 -0
  62. package/src/ask/thread-state.ts +350 -0
  63. package/src/ask/thread.ts +1400 -0
  64. package/src/ask/threads.ts +219 -0
  65. package/src/ask/watch.ts +79 -0
  66. package/src/cli-install.ts +1041 -0
  67. package/src/cli-runner.ts +1461 -0
  68. package/src/cli-runtime-info.ts +35 -0
  69. package/src/cli.ts +222 -0
  70. package/src/connect-prompts.ts +248 -0
  71. package/src/cursor-deeplink.ts +11 -0
  72. package/src/desktop-discovery.ts +355 -0
  73. package/src/diff-selection-migration.ts +63 -0
  74. package/src/embedded-posthog-key.ts +6 -0
  75. package/src/error-telemetry.ts +247 -0
  76. package/src/evidence.ts +14 -0
  77. package/src/exception-telemetry.ts +126 -0
  78. package/src/fixtures/blocks/call_stack_diff.json +27 -0
  79. package/src/fixtures/blocks/callout.json +11 -0
  80. package/src/fixtures/blocks/code.json +9 -0
  81. package/src/fixtures/blocks/code_peek.json +8 -0
  82. package/src/fixtures/blocks/database_lens.json +51 -0
  83. package/src/fixtures/blocks/divider.json +1 -0
  84. package/src/fixtures/blocks/fixtures.ts +33 -0
  85. package/src/fixtures/blocks/flow_diagram.json +36 -0
  86. package/src/fixtures/blocks/ids.ts +8 -0
  87. package/src/fixtures/blocks/image.json +9 -0
  88. package/src/fixtures/blocks/markdown.json +7 -0
  89. package/src/fixtures/blocks/section.json +11 -0
  90. package/src/fixtures/blocks/sequence.json +31 -0
  91. package/src/fixtures/blocks/software_map.json +7 -0
  92. package/src/fixtures/blocks/trace_quote.json +9 -0
  93. package/src/fixtures/blocks/tutorial.json +51 -0
  94. package/src/fs-utils.ts +32 -0
  95. package/src/fuzzy-match.ts +94 -0
  96. package/src/install.ts +29 -0
  97. package/src/legacy-skills.ts +133 -0
  98. package/src/lens-selection.ts +230 -0
  99. package/src/markdown-latex-math.ts +230 -0
  100. package/src/markdown.ts +81 -0
  101. package/src/package-paths.ts +53 -0
  102. package/src/posthog-capture-client.ts +610 -0
  103. package/src/review-api/README.md +295 -0
  104. package/src/review-api/activity.ts +340 -0
  105. package/src/review-api/agent-cli.ts +219 -0
  106. package/src/review-api/agent-client.ts +188 -0
  107. package/src/review-api/anchor-quotes.ts +88 -0
  108. package/src/review-api/ask-history.ts +242 -0
  109. package/src/review-api/authoring-tools.ts +211 -0
  110. package/src/review-api/blocks/call_stack_diff.ts +75 -0
  111. package/src/review-api/blocks/callout.ts +24 -0
  112. package/src/review-api/blocks/code.ts +9 -0
  113. package/src/review-api/blocks/code_peek.ts +13 -0
  114. package/src/review-api/blocks/database_lens.ts +150 -0
  115. package/src/review-api/blocks/definition.ts +40 -0
  116. package/src/review-api/blocks/divider.ts +6 -0
  117. package/src/review-api/blocks/flow_diagram.ts +111 -0
  118. package/src/review-api/blocks/image.ts +10 -0
  119. package/src/review-api/blocks/index.ts +90 -0
  120. package/src/review-api/blocks/markdown.ts +17 -0
  121. package/src/review-api/blocks/section.ts +24 -0
  122. package/src/review-api/blocks/sequence.ts +56 -0
  123. package/src/review-api/blocks/software_map.ts +9 -0
  124. package/src/review-api/blocks/trace_quote.ts +10 -0
  125. package/src/review-api/blocks/tutorial.ts +39 -0
  126. package/src/review-api/checkout-fs.ts +14 -0
  127. package/src/review-api/client.ts +1 -0
  128. package/src/review-api/comparison-coverage.ts +304 -0
  129. package/src/review-api/component-reference.ts +30 -0
  130. package/src/review-api/diff-lenses.ts +175 -0
  131. package/src/review-api/document-headings.ts +51 -0
  132. package/src/review-api/document-text.ts +200 -0
  133. package/src/review-api/document.ts +906 -0
  134. package/src/review-api/file-lenses.ts +100 -0
  135. package/src/review-api/http.ts +1877 -0
  136. package/src/review-api/image-decode.ts +29 -0
  137. package/src/review-api/input-error.ts +9 -0
  138. package/src/review-api/instructions.ts +89 -0
  139. package/src/review-api/lens-alignment.ts +52 -0
  140. package/src/review-api/local-data.ts +1837 -0
  141. package/src/review-api/map-input.ts +154 -0
  142. package/src/review-api/mcp-client-agent.ts +32 -0
  143. package/src/review-api/mcp.ts +240 -0
  144. package/src/review-api/origin.ts +43 -0
  145. package/src/review-api/profile.ts +146 -0
  146. package/src/review-api/public-tools.ts +102 -0
  147. package/src/review-api/pull-request.ts +389 -0
  148. package/src/review-api/read-schemas.ts +85 -0
  149. package/src/review-api/recovery.ts +2 -0
  150. package/src/review-api/request-origin.ts +33 -0
  151. package/src/review-api/review-progress.ts +382 -0
  152. package/src/review-api/status-tool.ts +10 -0
  153. package/src/review-api/store-schema.ts +22 -0
  154. package/src/review-api/store.ts +1647 -0
  155. package/src/review-api/tool-failure.ts +64 -0
  156. package/src/review-api/trace-schema.ts +13 -0
  157. package/src/review-api/traces.ts +119 -0
  158. package/src/review-api/unsupported-files.integration.ts +91 -0
  159. package/src/review-api/workspaces.ts +692 -0
  160. package/src/review-api/worktree-source.ts +170 -0
  161. package/src/review-api/worktree-structural.integration.ts +320 -0
  162. package/src/review-app-launcher.ts +432 -0
  163. package/src/review-app-picker.ts +168 -0
  164. package/src/review-app.ts +134 -0
  165. package/src/review-bundled-tools.ts +249 -0
  166. package/src/review-checkout-paths.ts +37 -0
  167. package/src/review-diff-files.ts +89 -0
  168. package/src/review-head-checkout.ts +300 -0
  169. package/src/review-home-paths.ts +73 -0
  170. package/src/review-info.ts +59 -0
  171. package/src/review-instances.ts +113 -0
  172. package/src/review-logger.ts +183 -0
  173. package/src/review-preferences.ts +88 -0
  174. package/src/review-prepare.ts +291 -0
  175. package/src/review-stack.ts +112 -0
  176. package/src/review-telemetry.ts +1066 -0
  177. package/src/runtime.ts +74 -0
  178. package/src/server/account-alias.ts +30 -0
  179. package/src/server/bounded-stream.ts +34 -0
  180. package/src/server/bug-report.ts +301 -0
  181. package/src/server/client-error-budget.ts +45 -0
  182. package/src/server/crash-report.ts +273 -0
  183. package/src/server/desktop-host-shutdown.ts +64 -0
  184. package/src/server/desktop-host.ts +190 -0
  185. package/src/server/desktop-server.ts +703 -0
  186. package/src/server/diffr-config.ts +509 -0
  187. package/src/server/diffr-languages.ts +97 -0
  188. package/src/server/global-verb-relay.ts +190 -0
  189. package/src/server/headless-host.ts +149 -0
  190. package/src/server/hono-http.ts +162 -0
  191. package/src/server/http-json.ts +24 -0
  192. package/src/server/json-review-reporting.ts +174 -0
  193. package/src/server/process-error-telemetry.ts +174 -0
  194. package/src/server/review-api-parsers.ts +90 -0
  195. package/src/server/review-lifecycle-telemetry.ts +73 -0
  196. package/src/server/review-open-watchdog.ts +46 -0
  197. package/src/server/review-server-core.ts +290 -0
  198. package/src/server/structural-comparisons.ts +153 -0
  199. package/src/server/structural-diff.ts +186 -0
  200. package/src/server/tutorial-service.ts +241 -0
  201. package/src/server/ui-telemetry.ts +198 -0
  202. package/src/server-discovery.ts +95 -0
  203. package/src/session-markers.ts +132 -0
  204. package/src/sharing/auth.ts +41 -0
  205. package/src/sharing/cli.ts +83 -0
  206. package/src/sharing/client.ts +334 -0
  207. package/src/sharing/export.ts +195 -0
  208. package/src/sharing/host.ts +316 -0
  209. package/src/sharing/import.ts +787 -0
  210. package/src/sharing/index.ts +16 -0
  211. package/src/sharing/repository.ts +145 -0
  212. package/src/sharing/routes.ts +34 -0
  213. package/src/slug.ts +23 -0
  214. package/src/software-map-diff-counts.ts +517 -0
  215. package/src/software-map-model.ts +1147 -0
  216. package/src/software-map-topology-diff.ts +260 -0
  217. package/src/source.ts +92 -0
  218. package/src/startup-trace.ts +232 -0
  219. package/src/stored-document-migration.ts +151 -0
  220. package/src/telemetry-clean-text.ts +257 -0
  221. package/src/telemetry-config.ts +298 -0
  222. package/src/telemetry-debug-sink.ts +38 -0
  223. package/src/telemetry.ts +13 -0
  224. package/src/trace-cli.ts +156 -0
  225. package/src/trace-storage-cli.ts +509 -0
  226. package/src/tutorial-conversation.ts +18 -0
  227. package/src/ui-telemetry-events.ts +765 -0
  228. package/src/unified-diff.ts +71 -0
  229. package/src/viewed-coverage.ts +259 -0
  230. package/src/windows-cli.ts +125 -0
  231. package/tutorial/document.json +275 -0
  232. package/tutorial/runtime-manifest.json +12 -0
  233. package/tutorial/sample-service/package.json +9 -0
  234. package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
  235. package/tutorial/sample-service/src/app.ts +26 -0
  236. package/tutorial/sample-service/src/database/schema.ts +10 -0
  237. package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
  238. package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
  239. package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
  240. package/tutorial/sample-service/src/orders/order-service.ts +34 -0
  241. package/tutorial/sample-service/src/orders/order.ts +21 -0
  242. package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
  243. package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
  244. package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
  245. package/tutorial/sample-service/tsconfig.json +12 -0
  246. package/tutorial/software-map.json +139 -0
  247. package/tutorial/trace.json +20 -0
@@ -0,0 +1,316 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { Writable } from "node:stream";
3
+
4
+ import { parseShareLink, shareIdSchema } from "@dev.fast/review-share-protocol";
5
+ import {
6
+ DEFAULT_STORE_ORIGIN,
7
+ clearStoreAuth,
8
+ openUrlInBrowser,
9
+ readStoreAuth,
10
+ runStoreLogin,
11
+ } from "@dev.fast/trace-core";
12
+ import { ReviewInputError } from "@review/review-api/document.js";
13
+ import type { LocalReviewData } from "@review/review-api/local-data.js";
14
+ import type { ReviewStore } from "@review/review-api/store.js";
15
+ import { readBoundedRequestJson } from "@review/server/hono-http.js";
16
+ import type { Hono } from "hono";
17
+ import { z } from "zod";
18
+
19
+ import { readSharingAuth } from "./auth.js";
20
+ import { ShareAuthError, ShareClient, SharePreflightError } from "./client.js";
21
+ import { exportShare, validateShareSources } from "./export.js";
22
+ import { SharedReviewStore, sharedReviewId } from "./import.js";
23
+ import { readShareRepository, verifyShareRepository } from "./repository.js";
24
+
25
+ interface LoginState {
26
+ pending: boolean;
27
+ url?: string;
28
+ error?: string;
29
+ }
30
+
31
+ const publishSchema = z.strictObject({
32
+ reviewId: z.string().min(1),
33
+ version: z.number().int().nonnegative().optional(),
34
+ requestId: z.uuid().optional(),
35
+ });
36
+
37
+ /** Where the host reports sharing and sign-in outcomes, for telemetry. */
38
+ export interface SharingHostEvents {
39
+ onPublished?: (event: { reviewId: string; version: number }) => void;
40
+ onRevoked?: (event: { shareId: string }) => void;
41
+ onLogin?: (
42
+ outcome: "started" | "succeeded" | "failed",
43
+ reason?: "did_not_finish" | "error",
44
+ ) => void;
45
+ }
46
+
47
+ interface SharingHostOptions extends SharingHostEvents {
48
+ verifyRepository?: typeof verifyShareRepository;
49
+ readRepository?: typeof readShareRepository;
50
+ login?: typeof runStoreLogin;
51
+ openUrl?: typeof openUrlInBrowser;
52
+ fetch?: typeof fetch;
53
+ }
54
+
55
+ /** Mounted behind local host authentication. Account credentials never enter the renderer. */
56
+ export function mountSharingHost(
57
+ app: Hono,
58
+ store: ReviewStore,
59
+ data: LocalReviewData,
60
+ shared: SharedReviewStore,
61
+ options: SharingHostOptions = {},
62
+ ) {
63
+ const startLogin = options.login ?? runStoreLogin;
64
+ const openUrl = options.openUrl ?? openUrlInBrowser;
65
+
66
+ let login: LoginState = {
67
+ pending: false,
68
+ };
69
+
70
+ app.get("/sharing/account", async (context) => {
71
+ const account = await readStoreAuth();
72
+
73
+ return context.json({
74
+ account: account
75
+ ? { login: account.login, origin: account.origin }
76
+ : null,
77
+ ...login,
78
+ });
79
+ });
80
+ app.post("/sharing/login", async (context) => {
81
+ if (!login.pending) {
82
+ login = { pending: true };
83
+ options.onLogin?.("started");
84
+
85
+ const discard = new Writable({
86
+ write(_chunk, _encoding, done) {
87
+ done();
88
+ },
89
+ });
90
+
91
+ void startLogin({
92
+ traces: false,
93
+ stdout: discard,
94
+ stderr: discard,
95
+ openUrl: async (url) => {
96
+ login.url = url;
97
+ await openUrl(url);
98
+ },
99
+ })
100
+ .then((code) => {
101
+ login = { pending: false };
102
+
103
+ if (code) login.error = "Sign-in did not finish. Try again.";
104
+ options.onLogin?.(
105
+ code ? "failed" : "succeeded",
106
+ code ? "did_not_finish" : undefined,
107
+ );
108
+ })
109
+ .catch(() => {
110
+ login = { pending: false, error: "Sign-in failed. Try again." };
111
+ options.onLogin?.("failed", "error");
112
+ });
113
+ }
114
+
115
+ return context.json(login);
116
+ });
117
+ mountSharingPublisher(app, store, data, options);
118
+ const imports = new Map<string, Promise<void>>();
119
+ app.get("/sharing/import/:id", (context) => {
120
+ const id = context.req.param("id");
121
+ const status = shared.status(id);
122
+
123
+ const result = { reviewId: id, ...status };
124
+
125
+ if (status.stage === "ready")
126
+ return context.json({ ...result, title: shared.get(id).snapshot.title });
127
+
128
+ return context.json(result);
129
+ });
130
+ app.post("/sharing/import", async (context) => {
131
+ const { url } = z
132
+ .strictObject({ url: z.string().max(2048) })
133
+ .parse(await readBoundedRequestJson(context.req.raw));
134
+
135
+ const parsed = parseShareLink(url);
136
+ const account = await readStoreAuth();
137
+
138
+ if (
139
+ parsed.origin !== DEFAULT_STORE_ORIGIN &&
140
+ parsed.origin !== account?.origin
141
+ )
142
+ throw new ReviewInputError(
143
+ "This share uses an untrusted service. Sign in to that service before opening its links.",
144
+ 400,
145
+ );
146
+ const id = sharedReviewId(parsed.origin, parsed.shareId);
147
+
148
+ if (!imports.has(id)) {
149
+ shared.setStatus(id, "downloading");
150
+
151
+ const job = (async () => {
152
+ if (shared.has(id)) {
153
+ // Retained bytes survive a failed fetch and let retries work offline.
154
+ await shared.prepare(id);
155
+
156
+ return;
157
+ }
158
+
159
+ const bundle = await new ShareClient(parsed.origin).download(
160
+ parsed.shareId,
161
+ parsed.capability,
162
+ );
163
+
164
+ await shared.import(parsed.origin, parsed.shareId, bundle);
165
+ })()
166
+ .catch((error) => {
167
+ shared.setStatus(
168
+ id,
169
+ "error",
170
+ error instanceof ReviewInputError
171
+ ? error.message
172
+ : "This share is unavailable, revoked, or needs a newer Whiteboard version.",
173
+ );
174
+ })
175
+ .finally(() => imports.delete(id));
176
+
177
+ imports.set(id, job);
178
+ shared.trackImport(job);
179
+ }
180
+
181
+ return context.json({ reviewId: id, ...shared.status(id) }, 202);
182
+ });
183
+ }
184
+
185
+ /** Publishing needs only an authored store, never Desktop or recipient workspaces. */
186
+ export function mountSharingPublisher(
187
+ app: Hono,
188
+ store: ReviewStore,
189
+ data: LocalReviewData,
190
+ options: Pick<
191
+ SharingHostOptions,
192
+ | "verifyRepository"
193
+ | "readRepository"
194
+ | "fetch"
195
+ | "onPublished"
196
+ | "onRevoked"
197
+ > = {},
198
+ ) {
199
+ const verifyRepository = options.verifyRepository ?? verifyShareRepository;
200
+ app.post("/sharing/publish", async (context) => {
201
+ const input = publishSchema.parse(
202
+ await readBoundedRequestJson(context.req.raw),
203
+ );
204
+
205
+ if (input.reviewId.startsWith("shared-"))
206
+ throw new ReviewInputError(
207
+ "Only the authoring review can be shared.",
208
+ 409,
209
+ );
210
+ const snapshot = store.read(input.reviewId, input.version);
211
+
212
+ if (snapshot.target?.kind === "worktree")
213
+ throw new ReviewInputError(
214
+ "Pin this review to commits before sharing it.",
215
+ );
216
+
217
+ validateShareSources(snapshot);
218
+ const account = await readSharingAuth();
219
+
220
+ if (!account)
221
+ throw new ReviewInputError(
222
+ "Set DEV_REVIEW_SHARE_TOKEN for CI, or run whiteboard login before sharing.",
223
+ 409,
224
+ );
225
+
226
+ const root = snapshot.pins
227
+ ? store.repositoryPath(snapshot.pins.repositoryId)
228
+ : undefined;
229
+
230
+ const repository = root
231
+ ? await (options.readRepository ?? readShareRepository)(root)
232
+ : undefined;
233
+
234
+ const bundle = await exportShare({
235
+ store,
236
+ data,
237
+ reviewId: input.reviewId,
238
+ version: snapshot.version,
239
+ repository,
240
+ });
241
+
242
+ const verification =
243
+ root && snapshot.pins && repository
244
+ ? verifyRepository(root, snapshot.pins, repository).then(
245
+ () => ({ ok: true as const }),
246
+ (error: Error) => ({ ok: false as const, error }),
247
+ )
248
+ : Promise.resolve({ ok: true as const });
249
+
250
+ try {
251
+ const result = await new ShareClient(
252
+ account.origin,
253
+ account.token,
254
+ options.fetch,
255
+ ).create(bundle, input.requestId ?? randomUUID(), async () => {
256
+ const result = await verification;
257
+
258
+ if (!result.ok) throw result.error;
259
+ });
260
+
261
+ const url = new URL(result.url);
262
+
263
+ if (
264
+ process.env.DEV_FAST_REVIEW_APP_URL_PROTOCOL ===
265
+ "dev-fast-review-preview"
266
+ )
267
+ url.searchParams.set("app", "preview");
268
+
269
+ options.onPublished?.({
270
+ reviewId: input.reviewId,
271
+ version: snapshot.version,
272
+ });
273
+
274
+ return context.json({
275
+ ...result,
276
+ url: url.href,
277
+ version: snapshot.version,
278
+ });
279
+ } catch (error) {
280
+ if (error instanceof SharePreflightError)
281
+ return context.json({ error: error.message }, 422);
282
+
283
+ if (error instanceof ShareAuthError) {
284
+ // A CI token lives in the environment; only the saved login can go stale.
285
+ if (process.env.DEV_REVIEW_SHARE_TOKEN === undefined)
286
+ await clearStoreAuth();
287
+ throw new ReviewInputError(
288
+ "Your sign-in has expired. Sign in again to share.",
289
+ 401,
290
+ );
291
+ }
292
+
293
+ throw new ReviewInputError(
294
+ "Sharing failed. Check your connection and login, then retry.",
295
+ 409,
296
+ );
297
+ }
298
+ });
299
+ app.post("/sharing/revoke", async (context) => {
300
+ const { shareId } = z
301
+ .strictObject({ shareId: shareIdSchema })
302
+ .parse(await readBoundedRequestJson(context.req.raw));
303
+
304
+ const account = await readSharingAuth();
305
+
306
+ if (!account)
307
+ throw new ReviewInputError(
308
+ "Set DEV_REVIEW_SHARE_TOKEN for CI, or run whiteboard login first.",
309
+ 409,
310
+ );
311
+ await new ShareClient(account.origin, account.token).revoke(shareId);
312
+ options.onRevoked?.({ shareId });
313
+
314
+ return context.json({ shareId, revoked: true });
315
+ });
316
+ }