@dev.fast/whiteboard 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +146 -2
- package/THIRD_PARTY_NOTICES.md +93 -0
- package/dist/account-alias-DOHN1RSH.js +973 -0
- package/dist/agent-cli-ChOq0ZuO.js +117 -0
- package/dist/agent-cli-DzWfZr6G.js +2 -0
- package/dist/agent-client-CPQo7iTI.js +305 -0
- package/dist/authoring-tools-Cr6Kpjsx.js +234 -0
- package/dist/build-info.json +1 -0
- package/dist/cli-hVgHcdsv.js +47 -0
- package/dist/cli-runner-D8y2_luU.js +5095 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +128 -0
- package/dist/client-DFoh50W3.js +950 -0
- package/dist/desktop-discovery-DBO-PM2V.js +164 -0
- package/dist/error-message-OtiDonty.js +6 -0
- package/dist/fs-utils-BMPLt0cr.js +22 -0
- package/dist/fuzzy-match-BoAmcyak.js +30 -0
- package/dist/headless-host-BJxf4dUU.js +94 -0
- package/dist/input-error-OLgB_h31.js +11 -0
- package/dist/local-data-CxLk75rx.js +4491 -0
- package/dist/mcp-QeF8vkdf.js +140 -0
- package/dist/package-paths-B6-zxvIO.js +47 -0
- package/dist/process-error-telemetry-v9e5h6D7.js +3952 -0
- package/dist/profile-4ry2f3AM.js +76 -0
- package/dist/profile-Bh5FIrmf.js +2 -0
- package/dist/request-origin-D_4QIdoN.js +811 -0
- package/dist/review-agent-traces-DNJFYzc8.js +3866 -0
- package/dist/review-home-paths-6zZH-1c9.js +2560 -0
- package/dist/review-telemetry-DEFnQsQ2.js +1195 -0
- package/dist/runtime-CKiWhGrf.js +52 -0
- package/dist/runtime.d.ts +9 -0
- package/dist/runtime.js +2 -0
- package/dist/s3-SzLpeCG6.js +345 -0
- package/dist/s3-config-BgxkSoOy.js +2 -0
- package/dist/s3-config-CAxhO9u_.js +676 -0
- package/dist/server/desktop-host.d.ts +4 -0
- package/dist/server/desktop-host.js +1702 -0
- package/dist/server-discovery-Dbzn3w6Z.js +66 -0
- package/dist/sharing/index.d.ts +1977 -0
- package/dist/sharing/index.js +2 -0
- package/dist/src-CGP5ytbV.js +283 -0
- package/dist/src-CmdiBL20.js +3676 -0
- package/dist/src-DnwdaQ2r.js +865 -0
- package/dist/src-X9phtB2j.js +68 -0
- package/dist/stored-document-migration-DHeMpLMb.js +2671 -0
- package/dist/tool-failure-C8zB73HV.js +98 -0
- package/dist/tutorial-trace-DrBvkaeL.js +43 -0
- package/instructions/authoring.md +36 -0
- package/instructions/file-lenses.md +13 -0
- package/instructions/scratchpad.md +23 -0
- package/instructions/trace-archaeology.md +113 -0
- package/onboarding.md +8 -0
- package/package.json +106 -3
- package/src/agent-selection.ts +100 -0
- package/src/agent-session-ref.ts +80 -0
- package/src/ask/agents.ts +351 -0
- package/src/ask/checkout-files.ts +47 -0
- package/src/ask/file-refs.ts +111 -0
- package/src/ask/pi-mcp.ts +154 -0
- package/src/ask/protocol.ts +244 -0
- package/src/ask/thread-state.ts +350 -0
- package/src/ask/thread.ts +1400 -0
- package/src/ask/threads.ts +219 -0
- package/src/ask/watch.ts +79 -0
- package/src/cli-install.ts +1041 -0
- package/src/cli-runner.ts +1461 -0
- package/src/cli-runtime-info.ts +35 -0
- package/src/cli.ts +222 -0
- package/src/connect-prompts.ts +248 -0
- package/src/cursor-deeplink.ts +11 -0
- package/src/desktop-discovery.ts +355 -0
- package/src/diff-selection-migration.ts +63 -0
- package/src/embedded-posthog-key.ts +6 -0
- package/src/error-telemetry.ts +247 -0
- package/src/evidence.ts +14 -0
- package/src/exception-telemetry.ts +126 -0
- package/src/fixtures/blocks/call_stack_diff.json +27 -0
- package/src/fixtures/blocks/callout.json +11 -0
- package/src/fixtures/blocks/code.json +9 -0
- package/src/fixtures/blocks/code_peek.json +8 -0
- package/src/fixtures/blocks/database_lens.json +51 -0
- package/src/fixtures/blocks/divider.json +1 -0
- package/src/fixtures/blocks/fixtures.ts +33 -0
- package/src/fixtures/blocks/flow_diagram.json +36 -0
- package/src/fixtures/blocks/ids.ts +8 -0
- package/src/fixtures/blocks/image.json +9 -0
- package/src/fixtures/blocks/markdown.json +7 -0
- package/src/fixtures/blocks/section.json +11 -0
- package/src/fixtures/blocks/sequence.json +31 -0
- package/src/fixtures/blocks/software_map.json +7 -0
- package/src/fixtures/blocks/trace_quote.json +9 -0
- package/src/fixtures/blocks/tutorial.json +51 -0
- package/src/fs-utils.ts +32 -0
- package/src/fuzzy-match.ts +94 -0
- package/src/install.ts +29 -0
- package/src/legacy-skills.ts +133 -0
- package/src/lens-selection.ts +230 -0
- package/src/markdown-latex-math.ts +230 -0
- package/src/markdown.ts +81 -0
- package/src/package-paths.ts +53 -0
- package/src/posthog-capture-client.ts +610 -0
- package/src/review-api/README.md +295 -0
- package/src/review-api/activity.ts +340 -0
- package/src/review-api/agent-cli.ts +219 -0
- package/src/review-api/agent-client.ts +188 -0
- package/src/review-api/anchor-quotes.ts +88 -0
- package/src/review-api/ask-history.ts +242 -0
- package/src/review-api/authoring-tools.ts +211 -0
- package/src/review-api/blocks/call_stack_diff.ts +75 -0
- package/src/review-api/blocks/callout.ts +24 -0
- package/src/review-api/blocks/code.ts +9 -0
- package/src/review-api/blocks/code_peek.ts +13 -0
- package/src/review-api/blocks/database_lens.ts +150 -0
- package/src/review-api/blocks/definition.ts +40 -0
- package/src/review-api/blocks/divider.ts +6 -0
- package/src/review-api/blocks/flow_diagram.ts +111 -0
- package/src/review-api/blocks/image.ts +10 -0
- package/src/review-api/blocks/index.ts +90 -0
- package/src/review-api/blocks/markdown.ts +17 -0
- package/src/review-api/blocks/section.ts +24 -0
- package/src/review-api/blocks/sequence.ts +56 -0
- package/src/review-api/blocks/software_map.ts +9 -0
- package/src/review-api/blocks/trace_quote.ts +10 -0
- package/src/review-api/blocks/tutorial.ts +39 -0
- package/src/review-api/checkout-fs.ts +14 -0
- package/src/review-api/client.ts +1 -0
- package/src/review-api/comparison-coverage.ts +304 -0
- package/src/review-api/component-reference.ts +30 -0
- package/src/review-api/diff-lenses.ts +175 -0
- package/src/review-api/document-headings.ts +51 -0
- package/src/review-api/document-text.ts +200 -0
- package/src/review-api/document.ts +906 -0
- package/src/review-api/file-lenses.ts +100 -0
- package/src/review-api/http.ts +1877 -0
- package/src/review-api/image-decode.ts +29 -0
- package/src/review-api/input-error.ts +9 -0
- package/src/review-api/instructions.ts +89 -0
- package/src/review-api/lens-alignment.ts +52 -0
- package/src/review-api/local-data.ts +1837 -0
- package/src/review-api/map-input.ts +154 -0
- package/src/review-api/mcp-client-agent.ts +32 -0
- package/src/review-api/mcp.ts +240 -0
- package/src/review-api/origin.ts +43 -0
- package/src/review-api/profile.ts +146 -0
- package/src/review-api/public-tools.ts +102 -0
- package/src/review-api/pull-request.ts +389 -0
- package/src/review-api/read-schemas.ts +85 -0
- package/src/review-api/recovery.ts +2 -0
- package/src/review-api/request-origin.ts +33 -0
- package/src/review-api/review-progress.ts +382 -0
- package/src/review-api/status-tool.ts +10 -0
- package/src/review-api/store-schema.ts +22 -0
- package/src/review-api/store.ts +1647 -0
- package/src/review-api/tool-failure.ts +64 -0
- package/src/review-api/trace-schema.ts +13 -0
- package/src/review-api/traces.ts +119 -0
- package/src/review-api/unsupported-files.integration.ts +91 -0
- package/src/review-api/workspaces.ts +692 -0
- package/src/review-api/worktree-source.ts +170 -0
- package/src/review-api/worktree-structural.integration.ts +320 -0
- package/src/review-app-launcher.ts +432 -0
- package/src/review-app-picker.ts +168 -0
- package/src/review-app.ts +134 -0
- package/src/review-bundled-tools.ts +249 -0
- package/src/review-checkout-paths.ts +37 -0
- package/src/review-diff-files.ts +89 -0
- package/src/review-head-checkout.ts +300 -0
- package/src/review-home-paths.ts +73 -0
- package/src/review-info.ts +59 -0
- package/src/review-instances.ts +113 -0
- package/src/review-logger.ts +183 -0
- package/src/review-preferences.ts +88 -0
- package/src/review-prepare.ts +291 -0
- package/src/review-stack.ts +112 -0
- package/src/review-telemetry.ts +1066 -0
- package/src/runtime.ts +74 -0
- package/src/server/account-alias.ts +30 -0
- package/src/server/bounded-stream.ts +34 -0
- package/src/server/bug-report.ts +301 -0
- package/src/server/client-error-budget.ts +45 -0
- package/src/server/crash-report.ts +273 -0
- package/src/server/desktop-host-shutdown.ts +64 -0
- package/src/server/desktop-host.ts +190 -0
- package/src/server/desktop-server.ts +703 -0
- package/src/server/diffr-config.ts +509 -0
- package/src/server/diffr-languages.ts +97 -0
- package/src/server/global-verb-relay.ts +190 -0
- package/src/server/headless-host.ts +149 -0
- package/src/server/hono-http.ts +162 -0
- package/src/server/http-json.ts +24 -0
- package/src/server/json-review-reporting.ts +174 -0
- package/src/server/process-error-telemetry.ts +174 -0
- package/src/server/review-api-parsers.ts +90 -0
- package/src/server/review-lifecycle-telemetry.ts +73 -0
- package/src/server/review-open-watchdog.ts +46 -0
- package/src/server/review-server-core.ts +290 -0
- package/src/server/structural-comparisons.ts +153 -0
- package/src/server/structural-diff.ts +186 -0
- package/src/server/tutorial-service.ts +241 -0
- package/src/server/ui-telemetry.ts +198 -0
- package/src/server-discovery.ts +95 -0
- package/src/session-markers.ts +132 -0
- package/src/sharing/auth.ts +41 -0
- package/src/sharing/cli.ts +83 -0
- package/src/sharing/client.ts +334 -0
- package/src/sharing/export.ts +195 -0
- package/src/sharing/host.ts +316 -0
- package/src/sharing/import.ts +787 -0
- package/src/sharing/index.ts +16 -0
- package/src/sharing/repository.ts +145 -0
- package/src/sharing/routes.ts +34 -0
- package/src/slug.ts +23 -0
- package/src/software-map-diff-counts.ts +517 -0
- package/src/software-map-model.ts +1147 -0
- package/src/software-map-topology-diff.ts +260 -0
- package/src/source.ts +92 -0
- package/src/startup-trace.ts +232 -0
- package/src/stored-document-migration.ts +151 -0
- package/src/telemetry-clean-text.ts +257 -0
- package/src/telemetry-config.ts +298 -0
- package/src/telemetry-debug-sink.ts +38 -0
- package/src/telemetry.ts +13 -0
- package/src/trace-cli.ts +156 -0
- package/src/trace-storage-cli.ts +509 -0
- package/src/tutorial-conversation.ts +18 -0
- package/src/ui-telemetry-events.ts +765 -0
- package/src/unified-diff.ts +71 -0
- package/src/viewed-coverage.ts +259 -0
- package/src/windows-cli.ts +125 -0
- package/tutorial/document.json +275 -0
- package/tutorial/runtime-manifest.json +12 -0
- package/tutorial/sample-service/package.json +9 -0
- package/tutorial/sample-service/src/api/checkout-api.ts +17 -0
- package/tutorial/sample-service/src/app.ts +26 -0
- package/tutorial/sample-service/src/database/schema.ts +10 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +15 -0
- package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +24 -0
- package/tutorial/sample-service/src/inventory/inventory-service.ts +11 -0
- package/tutorial/sample-service/src/orders/order-service.ts +34 -0
- package/tutorial/sample-service/src/orders/order.ts +21 -0
- package/tutorial/sample-service/src/orders/orders-repository.ts +24 -0
- package/tutorial/sample-service/src/payments/payment-gateway.ts +12 -0
- package/tutorial/sample-service/src/shipping/shipping-gateway.ts +15 -0
- package/tutorial/sample-service/tsconfig.json +12 -0
- package/tutorial/software-map.json +139 -0
- package/tutorial/trace.json +20 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { a as jsonNumber, n as isJsonObject, s as jsonProperty, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
2
|
+
import { n as parseReviewDesktopDiscovery } from "./src-DnwdaQ2r.js";
|
|
3
|
+
import { a as reviewInstancesDir, o as reviewLegacyDiscoveryPath, r as reviewDevInstanceKey, t as reviewDefaultInstancePath } from "./review-home-paths-6zZH-1c9.js";
|
|
4
|
+
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
//#region src/desktop-discovery.ts
|
|
7
|
+
var ReviewDesktopProtocolMismatchError = class extends Error {
|
|
8
|
+
actualVersion;
|
|
9
|
+
expectedVersion;
|
|
10
|
+
name = "ReviewDesktopProtocolMismatchError";
|
|
11
|
+
constructor(actualVersion, expectedVersion = 3) {
|
|
12
|
+
super(`Whiteboard Desktop uses protocol ${actualVersion}, but this Whiteboard CLI needs protocol ${expectedVersion}. Update the Whiteboard CLI and Whiteboard Desktop to compatible versions, then try again.`);
|
|
13
|
+
this.actualVersion = actualVersion;
|
|
14
|
+
this.expectedVersion = expectedVersion;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var ReviewDesktopDiscoveryUnreadableError = class extends Error {
|
|
18
|
+
name = "ReviewDesktopDiscoveryUnreadableError";
|
|
19
|
+
constructor(filePath, detail) {
|
|
20
|
+
super(`Whiteboard Desktop discovery is unreadable at ${filePath}. Restart Whiteboard Desktop and try again.${detail ? ` ${detail}` : ""}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
/** One record, or null when the file does not exist. */
|
|
24
|
+
async function readReviewDesktopDiscoveryFile(filePath) {
|
|
25
|
+
let source;
|
|
26
|
+
try {
|
|
27
|
+
source = await readFile(filePath, "utf8");
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
|
|
30
|
+
throw new ReviewDesktopDiscoveryUnreadableError(filePath, String(error));
|
|
31
|
+
}
|
|
32
|
+
let value;
|
|
33
|
+
try {
|
|
34
|
+
value = parseJsonText(source);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new ReviewDesktopDiscoveryUnreadableError(filePath, String(error));
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return parseReviewDesktopDiscovery(value);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
const version = isJsonObject(value) ? jsonNumber(jsonProperty(value, "version")) : void 0;
|
|
42
|
+
if (version !== void 0 && Number.isInteger(version) && version !== 3) throw new ReviewDesktopProtocolMismatchError(version);
|
|
43
|
+
throw new ReviewDesktopDiscoveryUnreadableError(filePath, String(error));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function isHealthyReviewDesktop(discovery, fetch = globalThis.fetch) {
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch(`${discovery.url}/health`, { signal: AbortSignal.timeout(1500) });
|
|
49
|
+
if (!response.ok) return false;
|
|
50
|
+
const health = await response.json();
|
|
51
|
+
return isJsonObject(health) && health.ok === true && health.instanceId === discovery.instanceId && health.desktopAttached === true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** The selected Desktop's record while it is running, else null. */
|
|
57
|
+
function healthyReviewInstance(selection) {
|
|
58
|
+
return selection.instance?.healthy ? selection.instance.discovery : null;
|
|
59
|
+
}
|
|
60
|
+
async function requireHealthyReviewDesktop(fetch = globalThis.fetch) {
|
|
61
|
+
const selection = await selectReviewInstance({ fetch });
|
|
62
|
+
const discovery = healthyReviewInstance(selection);
|
|
63
|
+
if (!discovery) throw reviewInstanceUnavailable(selection);
|
|
64
|
+
return discovery;
|
|
65
|
+
}
|
|
66
|
+
/** Shell-session override; agents inherit it through the bare shim. */
|
|
67
|
+
const REVIEW_INSTANCE_ENV = "DEV_REVIEW_INSTANCE";
|
|
68
|
+
function checkedInstanceKey(key, origin = "") {
|
|
69
|
+
if (/^(?:stable|preview|dev-[A-Za-z0-9_.-]+)$/.test(key)) return key;
|
|
70
|
+
throw new Error(`Unknown Whiteboard instance ${JSON.stringify(key)}${origin}. Use stable, preview, or a dev-… key from \`whiteboard instances\`.`);
|
|
71
|
+
}
|
|
72
|
+
/** Identity of the Desktop hosting this process, from the env electron-main passes. */
|
|
73
|
+
function reviewInstanceIdentity(env) {
|
|
74
|
+
const checkout = env.DEV_FAST_REVIEW_CHECKOUT?.trim();
|
|
75
|
+
const channel = checkout ? "dev" : env.DEV_FAST_REVIEW_RELEASE_CHANNEL === "preview" ? "preview" : "stable";
|
|
76
|
+
const identity = {
|
|
77
|
+
key: checkout ? reviewDevInstanceKey(checkout) : channel,
|
|
78
|
+
channel
|
|
79
|
+
};
|
|
80
|
+
if (checkout) identity.checkout = path.resolve(checkout);
|
|
81
|
+
const appPath = env.DEV_FAST_REVIEW_APP_PATH?.trim();
|
|
82
|
+
if (appPath) identity.appPath = appPath;
|
|
83
|
+
const appVersion = env.DEV_FAST_REVIEW_APP_VERSION?.trim();
|
|
84
|
+
if (appVersion) identity.appVersion = appVersion;
|
|
85
|
+
return identity;
|
|
86
|
+
}
|
|
87
|
+
/** Records by key, plus the files that could not be read, by key. */
|
|
88
|
+
async function readReviewInstances(dependencies) {
|
|
89
|
+
const env = dependencies.env ?? process.env;
|
|
90
|
+
const directory = reviewInstancesDir(env);
|
|
91
|
+
const names = await readdir(directory).catch(() => []);
|
|
92
|
+
const files = names.filter((name) => name.endsWith(".json")).map((name) => [path.basename(name, ".json"), path.join(directory, name)]);
|
|
93
|
+
if (!names.includes("stable.json")) files.push(["stable", reviewLegacyDiscoveryPath(env)]);
|
|
94
|
+
const records = [];
|
|
95
|
+
const broken = /* @__PURE__ */ new Map();
|
|
96
|
+
for (const [key, filePath] of files) try {
|
|
97
|
+
const discovery = await readReviewDesktopDiscoveryFile(filePath);
|
|
98
|
+
if (discovery) records.push({
|
|
99
|
+
key,
|
|
100
|
+
filePath,
|
|
101
|
+
discovery
|
|
102
|
+
});
|
|
103
|
+
} catch (error) {
|
|
104
|
+
const problem = error instanceof Error ? error : new Error(String(error));
|
|
105
|
+
broken.set(key, problem);
|
|
106
|
+
dependencies.warn?.(`Skipping ${filePath}: ${problem.message}`);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
instances: await Promise.all(records.map(async (record) => ({
|
|
110
|
+
...record,
|
|
111
|
+
healthy: await isHealthyReviewDesktop(record.discovery, dependencies.fetch)
|
|
112
|
+
}))),
|
|
113
|
+
broken
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function readDefaultReviewInstance(env = process.env) {
|
|
117
|
+
return await readFile(reviewDefaultInstancePath(env), "utf8").then((text) => text.trim()).catch(() => "") || void 0;
|
|
118
|
+
}
|
|
119
|
+
async function writeDefaultReviewInstance(key, env = process.env) {
|
|
120
|
+
const filePath = reviewDefaultInstancePath(env);
|
|
121
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
122
|
+
await writeFile(filePath, `${checkedInstanceKey(key)}\n`);
|
|
123
|
+
}
|
|
124
|
+
async function clearDefaultReviewInstance(env = process.env) {
|
|
125
|
+
await rm(reviewDefaultInstancePath(env), { force: true });
|
|
126
|
+
}
|
|
127
|
+
/** DEV_REVIEW_INSTANCE, then the machine default, then the only running Desktop, then stable. */
|
|
128
|
+
async function selectReviewInstance(dependencies = {}) {
|
|
129
|
+
const env = dependencies.env ?? process.env;
|
|
130
|
+
const { instances, broken } = await readReviewInstances(dependencies);
|
|
131
|
+
const fromEnv = env[REVIEW_INSTANCE_ENV]?.trim();
|
|
132
|
+
const fromDefault = fromEnv ? void 0 : await readDefaultReviewInstance(env);
|
|
133
|
+
const running = instances.filter((instance) => instance.healthy);
|
|
134
|
+
const [key, source] = fromEnv ? [checkedInstanceKey(fromEnv, ` from ${REVIEW_INSTANCE_ENV}`), "env"] : fromDefault ? [checkedInstanceKey(fromDefault, " as the machine default (`whiteboard instances clear` removes it)"), "default"] : running.length === 1 ? [running[0].key, "only-running"] : ["stable", "fallback"];
|
|
135
|
+
const selection = {
|
|
136
|
+
key,
|
|
137
|
+
source,
|
|
138
|
+
instances
|
|
139
|
+
};
|
|
140
|
+
const instance = instances.find((instance) => instance.key === key);
|
|
141
|
+
const problem = instance ? void 0 : fromEnv || fromDefault ? broken.get(key) : broken.values().next().value;
|
|
142
|
+
if (instance) selection.instance = instance;
|
|
143
|
+
if (problem) selection.problem = problem;
|
|
144
|
+
return selection;
|
|
145
|
+
}
|
|
146
|
+
/** Never a redirect: names what is running and how to start or pick one. */
|
|
147
|
+
function reviewInstanceUnavailable(selection) {
|
|
148
|
+
if (selection.problem) return selection.problem;
|
|
149
|
+
const running = selection.instances.filter((instance) => instance.healthy).map((instance) => instance.key);
|
|
150
|
+
const others = running.length ? ` Running: ${running.join(", ")}.` : " No Whiteboard is running.";
|
|
151
|
+
if (selection.source === "fallback" && running.length > 1) return new ReviewInstanceUnavailableError(`Several Whiteboard instances are running and none is selected.${others} Choose one with \`whiteboard instances use <key>\`, or \`export ${REVIEW_INSTANCE_ENV}=<key>\` for this shell.`);
|
|
152
|
+
return new ReviewInstanceUnavailableError(`Whiteboard \`${selection.key}\` is not running. ${reviewInstanceStartHint(selection)}, or pick another instance with \`whiteboard instances\`.${others}`);
|
|
153
|
+
}
|
|
154
|
+
/** A command could not reach the selected Desktop instance. */
|
|
155
|
+
var ReviewInstanceUnavailableError = class extends Error {
|
|
156
|
+
name = "ReviewInstanceUnavailableError";
|
|
157
|
+
};
|
|
158
|
+
function reviewInstanceStartHint(selection) {
|
|
159
|
+
if (!selection.key.startsWith("dev-")) return "Start it with `whiteboard app launch`";
|
|
160
|
+
const checkout = selection.instance?.discovery.checkout;
|
|
161
|
+
return checkout ? `Start it with \`pnpm dev\` in ${checkout}` : "Start it with `pnpm dev` in its checkout";
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
export { readDefaultReviewInstance as a, reviewInstanceIdentity as c, selectReviewInstance as d, writeDefaultReviewInstance as f, healthyReviewInstance as i, reviewInstanceStartHint as l, ReviewInstanceUnavailableError as n, readReviewInstances as o, clearDefaultReviewInstance as r, requireHealthyReviewDesktop as s, REVIEW_INSTANCE_ENV as t, reviewInstanceUnavailable as u };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
//#region src/fs-utils.ts
|
|
3
|
+
/** Whether a thrown filesystem error reports a missing file (ENOENT). */
|
|
4
|
+
function isMissingFileError(cause) {
|
|
5
|
+
return cause instanceof Error && "code" in cause && cause.code === "ENOENT";
|
|
6
|
+
}
|
|
7
|
+
async function isDirectory(target) {
|
|
8
|
+
try {
|
|
9
|
+
return (await stat(target)).isDirectory();
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async function isFile(target) {
|
|
15
|
+
try {
|
|
16
|
+
return (await stat(target)).isFile();
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { isFile as n, isMissingFileError as r, isDirectory as t };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import fuzzysort from "fuzzysort";
|
|
2
|
+
//#region src/fuzzy-match.ts
|
|
3
|
+
const SCORE_THRESHOLD = .5;
|
|
4
|
+
/**
|
|
5
|
+
* The single fuzzy matcher for this package. Each label is scored on its own,
|
|
6
|
+
* never joined: a match across a join could pair a letter from one label with
|
|
7
|
+
* a letter from the next and keep an item that shows neither word.
|
|
8
|
+
*/
|
|
9
|
+
function bestScore(needle, labels) {
|
|
10
|
+
let best = 0;
|
|
11
|
+
for (const label of labels) {
|
|
12
|
+
const score = fuzzysort.single(needle, label)?.score ?? 0;
|
|
13
|
+
if (score > best) best = score;
|
|
14
|
+
}
|
|
15
|
+
return best;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The items whose labels hit the query, best match first. An empty query keeps
|
|
19
|
+
* every item in its original order.
|
|
20
|
+
*/
|
|
21
|
+
function fuzzyRank(query, items, labelsOf) {
|
|
22
|
+
const needle = query.trim();
|
|
23
|
+
if (!needle) return [...items];
|
|
24
|
+
return items.values().map((item) => ({
|
|
25
|
+
item,
|
|
26
|
+
score: bestScore(needle, labelsOf(item))
|
|
27
|
+
})).filter((hit) => hit.score >= SCORE_THRESHOLD).toArray().sort((left, right) => right.score - left.score).map((hit) => hit.item);
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { fuzzyRank as t };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { p as isObjectValue } from "./src-X9phtB2j.js";
|
|
2
|
+
import { O as withFileLock, P as writePrivateJsonAtomic } from "./s3-config-CAxhO9u_.js";
|
|
3
|
+
import "./review-home-paths-6zZH-1c9.js";
|
|
4
|
+
import { t as openReviewProfile } from "./profile-4ry2f3AM.js";
|
|
5
|
+
import { i as createWhiteboardCore, l as createNodeRequestListener, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-v9e5h6D7.js";
|
|
6
|
+
import { i as reviewServerDiscoveryPath, t as headlessServerLockPath } from "./server-discovery-Dbzn3w6Z.js";
|
|
7
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
8
|
+
import { mkdir, realpath, rm } from "node:fs/promises";
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { once } from "node:events";
|
|
11
|
+
//#region src/server/headless-host.ts
|
|
12
|
+
/** One foreground headless endpoint per profile; Desktop shares its database. */
|
|
13
|
+
async function runHeadlessServer(input) {
|
|
14
|
+
await mkdir(input.stateDir, {
|
|
15
|
+
recursive: true,
|
|
16
|
+
mode: 448
|
|
17
|
+
});
|
|
18
|
+
const stateDir = await realpath(input.stateDir);
|
|
19
|
+
const stopErrorTelemetry = input.telemetry && installProcessErrorTelemetry(input.telemetry);
|
|
20
|
+
if (!(await withHeadlessServerLock(stateDir, () => serve({
|
|
21
|
+
...input,
|
|
22
|
+
stateDir
|
|
23
|
+
})).finally(() => stopErrorTelemetry?.())).acquired) throw new Error(`A Whiteboard server already owns ${stateDir}. Stop it first, or choose another --state-dir.`);
|
|
24
|
+
}
|
|
25
|
+
/** Held by a running server, so also by anything that must not run beside one. */
|
|
26
|
+
function withHeadlessServerLock(stateDir, operation) {
|
|
27
|
+
return withFileLock(headlessServerLockPath(stateDir), {
|
|
28
|
+
timeoutMs: 0,
|
|
29
|
+
retryMs: 20,
|
|
30
|
+
staleMs: Infinity,
|
|
31
|
+
unownedGraceMs: 1e3,
|
|
32
|
+
identifyOwner: true
|
|
33
|
+
}, operation);
|
|
34
|
+
}
|
|
35
|
+
async function serve(input) {
|
|
36
|
+
if (input.signal.aborted) return;
|
|
37
|
+
if (input.telemetry) await drainServerCrashReport(input.telemetry);
|
|
38
|
+
const local = await openReviewProfile(input.stateDir, { manageWorkspaces: false });
|
|
39
|
+
const discovery = {
|
|
40
|
+
version: 1,
|
|
41
|
+
instanceId: randomUUID(),
|
|
42
|
+
url: "http://127.0.0.1:0",
|
|
43
|
+
serverPid: process.pid,
|
|
44
|
+
token: randomBytes(32).toString("base64url")
|
|
45
|
+
};
|
|
46
|
+
const relay = new GlobalReviewDesktopVerbRelay();
|
|
47
|
+
const { app, api } = createWhiteboardCore({
|
|
48
|
+
profile: local,
|
|
49
|
+
relay,
|
|
50
|
+
token: discovery.token,
|
|
51
|
+
instanceId: discovery.instanceId,
|
|
52
|
+
softwareMapEnabled: input.softwareMapEnabled,
|
|
53
|
+
scratchpad: () => false,
|
|
54
|
+
status: () => ({
|
|
55
|
+
key: "headless",
|
|
56
|
+
home: input.stateDir
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
app.route("/reviews-api", api);
|
|
60
|
+
const server = createServer(createNodeRequestListener(app));
|
|
61
|
+
let published = false;
|
|
62
|
+
try {
|
|
63
|
+
const listening = once(server, "listening");
|
|
64
|
+
server.listen(input.port ?? 0, "127.0.0.1");
|
|
65
|
+
await listening;
|
|
66
|
+
const address = server.address();
|
|
67
|
+
if (!isObjectValue(address)) throw new Error("Whiteboard server did not bind a TCP port.");
|
|
68
|
+
discovery.url = `http://127.0.0.1:${address.port}`;
|
|
69
|
+
await writePrivateJsonAtomic(reviewServerDiscoveryPath(input.stateDir), discovery);
|
|
70
|
+
published = true;
|
|
71
|
+
input.onReady(discovery);
|
|
72
|
+
await new Promise((resolve) => {
|
|
73
|
+
if (input.signal.aborted) resolve();
|
|
74
|
+
else input.signal.addEventListener("abort", () => resolve(), { once: true });
|
|
75
|
+
});
|
|
76
|
+
} finally {
|
|
77
|
+
const forceClose = setTimeout(() => server.closeAllConnections(), 5e3);
|
|
78
|
+
forceClose.unref();
|
|
79
|
+
try {
|
|
80
|
+
if (published) await rm(reviewServerDiscoveryPath(input.stateDir), { force: true });
|
|
81
|
+
} finally {
|
|
82
|
+
relay.close();
|
|
83
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
84
|
+
clearTimeout(forceClose);
|
|
85
|
+
try {
|
|
86
|
+
await local.data.close();
|
|
87
|
+
} finally {
|
|
88
|
+
await local.store.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { runHeadlessServer, withHeadlessServerLock };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/review-api/input-error.ts
|
|
2
|
+
/** Deliberately safe to show to API clients, unlike filesystem/provider errors. */
|
|
3
|
+
var ReviewInputError = class extends Error {
|
|
4
|
+
status;
|
|
5
|
+
constructor(message, status = 400) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { ReviewInputError as t };
|