@dev.fast/whiteboard 0.2.5-preview.20261013.113 → 0.2.5-preview.20261015.115
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/THIRD_PARTY_NOTICES.md +29 -0
- package/dist/{account-alias-D0hF0sKk.js → account-alias-ChZR3RIU.js} +94 -8
- package/dist/{agent-cli-04xbDj23.js → agent-cli-CR2PnA9G.js} +9 -12
- package/dist/agent-cli-CRPPmS3l.js +2 -0
- package/dist/{agent-client-P3J4y8Zn.js → agent-client-DVd-prfp.js} +31 -14
- package/dist/{authoring-tools-fdA0RPIT.js → authoring-tools-BXKEwhfi.js} +5 -5
- package/dist/background-server-Cw7CcfaZ.js +156 -0
- package/dist/background-server-D2Rrr_e2.js +2 -0
- package/dist/build-info.json +1 -1
- package/dist/{cli-C0ziP6hg.js → cli-CSrz_wGk.js} +1 -1
- package/dist/{cli-runner-C3men22A.js → cli-runner-PlGqBeMw.js} +368 -252
- package/dist/cli.js +3 -2
- package/dist/{client-DzcSt6pV.js → client-DhZzPePs.js} +5 -3
- package/dist/{process-error-telemetry-BCBYp2t4.js → headless-host-CVXTSIlx.js} +3285 -2585
- package/dist/headless-host-Cp37C-v0.js +2 -0
- package/dist/input-error-OLgB_h31.js +11 -0
- package/dist/{local-data-Dy1pNtLF.js → local-data-yfLp5PiJ.js} +113 -62
- package/dist/{mcp-B8kuRSXM.js → mcp-DBbDwG4y.js} +4 -4
- package/dist/{package-paths-B9PDoAl6.js → package-paths-BsuI4WUf.js} +1 -1
- package/dist/profile-BSOkA6A-.js +2 -0
- package/dist/{profile-tbGWuWHu.js → profile-pAzg7fxC.js} +3 -3
- package/dist/remote-attach-BAt2eZx3.js +137 -0
- package/dist/remote-extensions-2Cxf3hMo.js +2 -0
- package/dist/remote-extensions-BiNx6NQK.js +323 -0
- package/dist/remote-language-server-DQgWUNeV.js +252 -0
- package/dist/remote-uninstall-DLtDG0o7.js +153 -0
- package/dist/{request-origin-DA4K1qWY.js → request-origin-DaRIV3cB.js} +5 -2
- package/dist/review-app-launcher-D7I4DGoH.js +222 -0
- package/dist/{review-home-paths-Y9mYWkCI.js → review-home-paths-zwevIq4I.js} +7 -7
- package/dist/{review-telemetry-CmM6tCbj.js → review-telemetry-BqB4CiTD.js} +5 -5
- package/dist/{s3-SzLpeCG6.js → s3-77-qkEhs.js} +1 -1
- package/dist/{s3-config-BgxkSoOy.js → s3-config-0xuCh-F_.js} +1 -1
- package/dist/{s3-config-CAxhO9u_.js → s3-config-DDWWMh5x.js} +5 -1
- package/dist/server/desktop-host.js +1663 -266
- package/dist/{desktop-discovery-Ba3dD5Cn.js → server-discovery-CifQ3yQS.js} +88 -3
- package/dist/sharing/index.d.ts +88 -32
- package/dist/sharing/index.js +1 -1
- package/dist/{src-DiHP11l3.js → src-BMPuxWv4.js} +6 -11
- package/dist/{src-WCbabvdS.js → src-Bgvf1sEN.js} +128 -8
- package/dist/{stored-document-migration-u4L_1VZa.js → stored-document-migration-CFOvjRwW.js} +5 -18
- package/dist/{tool-failure-CJHBCHMh.js → tool-failure-BXWzQG8T.js} +2 -2
- package/instructions/authoring.md +16 -12
- package/instructions/file-lenses.md +4 -2
- package/package.json +9 -14
- package/src/agent-connect.ts +507 -0
- package/src/agent-selection.ts +4 -0
- package/src/ask/agents.ts +59 -13
- package/src/ask/pi-mcp.ts +7 -3
- package/src/ask/thread-state.ts +9 -4
- package/src/ask/thread.ts +88 -50
- package/src/ask/threads.ts +53 -7
- package/src/cli-install.ts +3 -1
- package/src/cli-runner.ts +472 -81
- package/src/cli.ts +1 -1
- package/src/connect-agents.ts +285 -0
- package/src/connect-prompts.ts +60 -6
- package/src/desktop-discovery.ts +24 -0
- package/src/pinned-download.ts +118 -0
- package/src/remote-attach.ts +130 -0
- package/src/remote-extensions.ts +359 -0
- package/src/remote-language-server.ts +428 -0
- package/src/remote-toolchains.ts +107 -0
- package/src/remote-uninstall.ts +280 -0
- package/src/review-api/README.md +1 -3
- package/src/review-api/agent-cli.ts +4 -4
- package/src/review-api/agent-client.ts +23 -0
- package/src/review-api/ask-history.ts +31 -16
- package/src/review-api/authoring-tools.ts +1 -1
- package/src/review-api/blocks/index.ts +2 -4
- package/src/review-api/document.ts +1 -4
- package/src/review-api/http.ts +265 -237
- package/src/review-api/local-data.ts +18 -3
- package/src/review-api/onboarding.ts +478 -0
- package/src/review-api/origin.ts +7 -0
- package/src/review-api/recent-folders.ts +173 -0
- package/src/review-api/store.ts +56 -10
- package/src/review-api/workspaces.ts +56 -11
- package/src/review-app-launcher.ts +61 -3
- package/src/review-stack.ts +162 -36
- package/src/server/background-server.ts +250 -0
- package/src/server/desktop-host-shutdown.ts +24 -0
- package/src/server/desktop-host.ts +39 -24
- package/src/server/desktop-server.ts +68 -118
- package/src/server/global-verb-relay.ts +16 -0
- package/src/server/headless-host.ts +128 -3
- package/src/server/hono-http.ts +6 -1
- package/src/server/legacy-tutorial-cleanup.ts +38 -0
- package/src/server/review-gateway-hosts.ts +379 -0
- package/src/server/review-gateway-list.ts +109 -0
- package/src/server/review-gateway-memory.ts +130 -0
- package/src/server/review-gateway-pushes.ts +142 -0
- package/src/server/review-gateway-streams.ts +415 -0
- package/src/server/review-gateway-transport.ts +204 -0
- package/src/server/review-gateway.ts +433 -0
- package/src/server/review-server-core.ts +36 -6
- package/src/server-discovery.ts +11 -0
- package/src/sharing/import.ts +1 -0
- package/src/ui-telemetry-events.ts +5 -1
- package/src/vsix.ts +122 -0
- package/dist/agent-cli-BR_Jw5BL.js +0 -2
- package/dist/headless-host-BLLJ7Dyu.js +0 -95
- package/dist/profile-aMSw1oCM.js +0 -2
- package/dist/server-discovery-ZVi8Ay8J.js +0 -66
- package/src/fixtures/blocks/tutorial.json +0 -14
- package/src/review-api/blocks/tutorial.ts +0 -23
- package/src/server/tutorial-service.ts +0 -217
- package/tutorial/document.json +0 -216
- package/tutorial/lenses.json +0 -10
- package/tutorial/runtime-manifest.json +0 -11
- package/tutorial/sample-service/package.json +0 -9
- package/tutorial/sample-service/src/api/checkout-api.ts +0 -17
- package/tutorial/sample-service/src/app.ts +0 -26
- package/tutorial/sample-service/src/database/drizzle-orm.d.ts +0 -16
- package/tutorial/sample-service/src/database/schema.ts +0 -9
- package/tutorial/sample-service/src/fulfillment/fulfillment-queue.ts +0 -15
- package/tutorial/sample-service/src/fulfillment/fulfillment-worker.ts +0 -24
- package/tutorial/sample-service/src/inventory/forklift-mood.ts +0 -4
- package/tutorial/sample-service/src/inventory/inventory-service.ts +0 -28
- package/tutorial/sample-service/src/inventory/warehouse-lore.ts +0 -11
- package/tutorial/sample-service/src/orders/order-service.ts +0 -35
- package/tutorial/sample-service/src/orders/order.ts +0 -43
- package/tutorial/sample-service/src/orders/orders-repository.ts +0 -24
- package/tutorial/sample-service/src/payments/fraud-vibes.ts +0 -4
- package/tutorial/sample-service/src/payments/payment-gateway.ts +0 -29
- package/tutorial/sample-service/src/payments/raccoon-ledger.ts +0 -9
- package/tutorial/sample-service/src/shipping/shipping-gateway.ts +0 -15
- package/tutorial/sample-service/tsconfig.json +0 -12
|
@@ -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 };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as parseGitRemote, a as diff, b as resolveRepoContextSync, c as diffFileSummariesWorkingTree, d as git, g as listTrackedFilesAtCommit, h as listCommitRange, i as detectLocalVcs, l as diffTrees, m as jjRevisionIsConflicted, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as readFileAtCommit, x as resolveRevision, y as resolveRepoContext } from "./src-BMPuxWv4.js";
|
|
2
2
|
import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
3
|
-
import {
|
|
4
|
-
import { s as safeStorageSegment } from "./review-home-paths-
|
|
5
|
-
import { $ as unionIntervals, A as applyLensEdit, C as resourceReferences, D as summarizeEdit, E as sourceReferences, F as checkSourcePath, I as fileLineRangeSchema, K as sourceAnchors, L as requireVisibleSource, N as lensSelections, O as checkReferences, P as SourceRangeError, Q as subtractIntervals, R as sliceSourceRange, S as resourceReference, X as intersectIntervals, Y as emptyCoverage, a as defineSoftwareMap, b as pinsSchema, c as traceSchema, d as applyEdit, et as updateCoverage, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, j as lensEditSchema, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, p as documentSchema, q as coverageSchema, r as migrateStoredLenses, t as liftFileLenses, u as anchorPins, v as isUnit, w as reviewTargetSchema } from "./stored-document-migration-
|
|
6
|
-
import {
|
|
3
|
+
import { C as ReviewApiSelectionSourceSchema, M as structuralChangeCounts, O as ReviewSelectedDiffSchema, j as decodeStructuralDiffEvent, k as SCRATCHPAD_REVIEW_ID } from "./src-Bgvf1sEN.js";
|
|
4
|
+
import { s as safeStorageSegment } from "./review-home-paths-zwevIq4I.js";
|
|
5
|
+
import { $ as unionIntervals, A as applyLensEdit, C as resourceReferences, D as summarizeEdit, E as sourceReferences, F as checkSourcePath, I as fileLineRangeSchema, K as sourceAnchors, L as requireVisibleSource, N as lensSelections, O as checkReferences, P as SourceRangeError, Q as subtractIntervals, R as sliceSourceRange, S as resourceReference, X as intersectIntervals, Y as emptyCoverage, a as defineSoftwareMap, b as pinsSchema, c as traceSchema, d as applyEdit, et as updateCoverage, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, j as lensEditSchema, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, p as documentSchema, q as coverageSchema, r as migrateStoredLenses, t as liftFileLenses, u as anchorPins, v as isUnit, w as reviewTargetSchema } from "./stored-document-migration-CFOvjRwW.js";
|
|
6
|
+
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
7
|
+
import { A as withFileLock, I as writePrivateJsonAtomic, O as processIsAlive } from "./s3-config-DDWWMh5x.js";
|
|
7
8
|
import { t as errorMessage } from "./error-message-OtiDonty.js";
|
|
8
9
|
import { r as isMissingFileError } from "./fs-utils-BMPLt0cr.js";
|
|
9
10
|
import { createRequire } from "node:module";
|
|
@@ -1665,26 +1666,30 @@ var ReviewActivity = class {
|
|
|
1665
1666
|
};
|
|
1666
1667
|
/** A semantic selection, independent of comment/thread creation. */
|
|
1667
1668
|
const AgentSelectionSchema = z.strictObject({
|
|
1668
|
-
target: z.discriminatedUnion("kind", [
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1669
|
+
target: z.discriminatedUnion("kind", [
|
|
1670
|
+
z.strictObject({
|
|
1671
|
+
kind: z.literal("text"),
|
|
1672
|
+
quote: z.string(),
|
|
1673
|
+
/** Where the quote is: characters `start` to `end` of the text of the
|
|
1674
|
+
* review block `blockId`, which was `text` then. The block keeps its
|
|
1675
|
+
* id across versions; comparing its text then and now finds the quote
|
|
1676
|
+
* again, or tells that it changed. */
|
|
1677
|
+
anchor: z.strictObject({
|
|
1678
|
+
blockId: z.string().min(1).max(200),
|
|
1679
|
+
start: z.number().int().nonnegative(),
|
|
1680
|
+
end: z.number().int().positive(),
|
|
1681
|
+
text: z.string().max(2e4)
|
|
1682
|
+
}).refine((anchor) => anchor.start < anchor.end, "an empty anchor").optional()
|
|
1683
|
+
}),
|
|
1684
|
+
z.strictObject({
|
|
1685
|
+
kind: z.literal("code"),
|
|
1686
|
+
path: z.string(),
|
|
1687
|
+
side: z.enum(["base", "head"]),
|
|
1688
|
+
startLine: z.number().int().positive(),
|
|
1689
|
+
endLine: z.number().int().positive()
|
|
1690
|
+
}),
|
|
1691
|
+
z.strictObject({ kind: z.literal("review") })
|
|
1692
|
+
]),
|
|
1688
1693
|
title: z.string().max(500),
|
|
1689
1694
|
detail: z.string().max(24e3).optional(),
|
|
1690
1695
|
revision: z.string().max(200).optional(),
|
|
@@ -1694,6 +1699,7 @@ const AgentSelectionSchema = z.strictObject({
|
|
|
1694
1699
|
function selectionMarkdown(selection, sourceExcerpt = "", diffPaths) {
|
|
1695
1700
|
const target = selection.target;
|
|
1696
1701
|
if (selection.selectedDiff) return selectedDiffMarkdown(selection.selectedDiff, diffPaths);
|
|
1702
|
+
if (target.kind === "review") return "";
|
|
1697
1703
|
if (target.kind === "text") return target.quote.split(/\r?\n/).map((line) => `> ${line}`).join("\n");
|
|
1698
1704
|
return [
|
|
1699
1705
|
`## ${selection.title}`,
|
|
@@ -1872,6 +1878,13 @@ const statusSchema = z.enum([
|
|
|
1872
1878
|
"idle",
|
|
1873
1879
|
"failed"
|
|
1874
1880
|
]);
|
|
1881
|
+
/** What an agent may do without asking: nothing that changes the checkout,
|
|
1882
|
+
* what its own judgment calls safe, or everything. */
|
|
1883
|
+
const askPermissionsSchema = z.enum([
|
|
1884
|
+
"read-only",
|
|
1885
|
+
"auto",
|
|
1886
|
+
"bypass"
|
|
1887
|
+
]);
|
|
1875
1888
|
/** Everything the Ask panel renders. A watcher gets it whole once, then
|
|
1876
1889
|
* follows it through `AskChange`s. */
|
|
1877
1890
|
const askThreadStateSchema = z.object({
|
|
@@ -1884,8 +1897,7 @@ const askThreadStateSchema = z.object({
|
|
|
1884
1897
|
signIn: z.string().optional(),
|
|
1885
1898
|
/** The mode that keeps the agent from changing files was accepted. */
|
|
1886
1899
|
readOnly: z.boolean(),
|
|
1887
|
-
|
|
1888
|
-
bypass: z.boolean(),
|
|
1900
|
+
permissions: askPermissionsSchema,
|
|
1889
1901
|
head: z.string(),
|
|
1890
1902
|
/** The checkout the agent works in. */
|
|
1891
1903
|
cwd: z.string(),
|
|
@@ -1913,7 +1925,7 @@ const askChangeSchema = z.discriminatedUnion("type", [
|
|
|
1913
1925
|
type: z.literal("set"),
|
|
1914
1926
|
status: statusSchema.optional(),
|
|
1915
1927
|
readOnly: z.boolean().optional(),
|
|
1916
|
-
|
|
1928
|
+
permissions: askPermissionsSchema.optional(),
|
|
1917
1929
|
choices: askChoicesSchema.optional(),
|
|
1918
1930
|
commands: z.array(askCommandSchema).optional(),
|
|
1919
1931
|
accepts: askAcceptsSchema.optional(),
|
|
@@ -1969,7 +1981,7 @@ function applyAskChange(state, change) {
|
|
|
1969
1981
|
...rest,
|
|
1970
1982
|
status: change.status ?? state.status,
|
|
1971
1983
|
readOnly: change.readOnly ?? state.readOnly,
|
|
1972
|
-
|
|
1984
|
+
permissions: change.permissions ?? state.permissions
|
|
1973
1985
|
};
|
|
1974
1986
|
const choices = change.choices ?? state.choices;
|
|
1975
1987
|
if (choices) next.choices = choices;
|
|
@@ -2037,8 +2049,8 @@ const askRecordSchema = z.object({
|
|
|
2037
2049
|
cwd: z.string(),
|
|
2038
2050
|
/** What the panel showed last; absent until a turn ends or it closes. */
|
|
2039
2051
|
entries: z.array(askEntrySchema).optional(),
|
|
2040
|
-
/**
|
|
2041
|
-
|
|
2052
|
+
/** What the agent may do without asking, when it reopens. */
|
|
2053
|
+
permissions: askPermissionsSchema.optional()
|
|
2042
2054
|
});
|
|
2043
2055
|
const rowSchema = z.object({
|
|
2044
2056
|
id: z.string(),
|
|
@@ -2053,7 +2065,8 @@ const rowSchema = z.object({
|
|
|
2053
2065
|
created_at: z.string(),
|
|
2054
2066
|
updated_at: z.string(),
|
|
2055
2067
|
entries: z.string().nullable(),
|
|
2056
|
-
bypass: z.number()
|
|
2068
|
+
bypass: z.number(),
|
|
2069
|
+
permissions: z.string().nullable()
|
|
2057
2070
|
}).transform((row) => askRecordSchema.parse({
|
|
2058
2071
|
id: row.id,
|
|
2059
2072
|
reviewId: row.review_id,
|
|
@@ -2067,7 +2080,7 @@ const rowSchema = z.object({
|
|
|
2067
2080
|
createdAt: row.created_at,
|
|
2068
2081
|
updatedAt: row.updated_at,
|
|
2069
2082
|
entries: row.entries === null ? void 0 : JSON.parse(row.entries),
|
|
2070
|
-
|
|
2083
|
+
permissions: row.permissions ?? (row.bypass === 1 ? "bypass" : "read-only")
|
|
2071
2084
|
}));
|
|
2072
2085
|
/** Saved Ask conversations, per review. Shared reviews are not in
|
|
2073
2086
|
* `reviews`, so there is no foreign key; deleting a review deletes its rows. */
|
|
@@ -2093,9 +2106,12 @@ var AskHistory = class {
|
|
|
2093
2106
|
updated_at TEXT NOT NULL,
|
|
2094
2107
|
entries TEXT,
|
|
2095
2108
|
bypass INTEGER NOT NULL DEFAULT 0,
|
|
2109
|
+
permissions TEXT,
|
|
2096
2110
|
UNIQUE(agent, session_id));
|
|
2097
2111
|
CREATE INDEX IF NOT EXISTS ask_conversations_review ON ask_conversations(review_id, updated_at);`);
|
|
2098
|
-
|
|
2112
|
+
const columns = new Set(db.prepare("PRAGMA table_info(ask_conversations)").all().map((column) => String(column.name)));
|
|
2113
|
+
if (!columns.has("bypass")) db.exec("ALTER TABLE ask_conversations ADD COLUMN bypass INTEGER NOT NULL DEFAULT 0");
|
|
2114
|
+
if (!columns.has("permissions")) db.exec("ALTER TABLE ask_conversations ADD COLUMN permissions TEXT");
|
|
2099
2115
|
db.exec("CREATE TABLE IF NOT EXISTS ask_agent_offers(agent TEXT PRIMARY KEY, offer TEXT NOT NULL)");
|
|
2100
2116
|
db.exec("CREATE TABLE IF NOT EXISTS ask_agent_model_offers(agent TEXT NOT NULL, model TEXT NOT NULL, offer TEXT NOT NULL, PRIMARY KEY(agent, model))");
|
|
2101
2117
|
}
|
|
@@ -2113,13 +2129,13 @@ var AskHistory = class {
|
|
|
2113
2129
|
}
|
|
2114
2130
|
save(record) {
|
|
2115
2131
|
this.assertWritable(record.reviewId);
|
|
2116
|
-
this.db.prepare(`INSERT OR REPLACE INTO ask_conversations(id,review_id,agent,session_id,version,head,cwd,selection,title,created_at,updated_at,bypass)
|
|
2117
|
-
VALUES(
|
|
2132
|
+
this.db.prepare(`INSERT OR REPLACE INTO ask_conversations(id,review_id,agent,session_id,version,head,cwd,selection,title,created_at,updated_at,bypass,permissions)
|
|
2133
|
+
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(record.id, record.reviewId, record.agent, record.sessionId, record.version, record.head, record.cwd, JSON.stringify(record.selection), record.title, record.createdAt, record.updatedAt, record.permissions === "bypass" ? 1 : 0, record.permissions ?? null);
|
|
2118
2134
|
}
|
|
2119
|
-
/**
|
|
2120
|
-
|
|
2135
|
+
/** What the agent may do without asking when the conversation reopens. */
|
|
2136
|
+
setPermissions(id, permissions) {
|
|
2121
2137
|
this.assertThreadWritable(id);
|
|
2122
|
-
this.db.prepare("UPDATE ask_conversations SET bypass=? WHERE id=?").run(bypass ? 1 : 0, id);
|
|
2138
|
+
this.db.prepare("UPDATE ask_conversations SET bypass=?, permissions=? WHERE id=?").run(permissions === "bypass" ? 1 : 0, permissions, id);
|
|
2123
2139
|
}
|
|
2124
2140
|
/** Points a conversation at a new session, when its agent could not
|
|
2125
2141
|
* reopen the one it had. */
|
|
@@ -2160,7 +2176,7 @@ var AskHistory = class {
|
|
|
2160
2176
|
/** Newest first. */
|
|
2161
2177
|
list(reviewId) {
|
|
2162
2178
|
return this.db.prepare("SELECT * FROM ask_conversations WHERE review_id=? ORDER BY updated_at DESC").all(reviewId).map((row) => {
|
|
2163
|
-
const { reviewId: _reviewId, sessionId: _sessionId, version: _version, cwd: _cwd, entries,
|
|
2179
|
+
const { reviewId: _reviewId, sessionId: _sessionId, version: _version, cwd: _cwd, entries, permissions: _permissions, ...entry } = rowSchema.parse(row);
|
|
2164
2180
|
const question = entries?.find((entry) => entry.kind === "user")?.text;
|
|
2165
2181
|
if (question?.trim()) entry.question = question;
|
|
2166
2182
|
return entry;
|
|
@@ -2187,19 +2203,21 @@ function pullRequestKey(url) {
|
|
|
2187
2203
|
return url.toLowerCase();
|
|
2188
2204
|
}
|
|
2189
2205
|
/** Omission preserves identity; null detaches it without changing import metadata. */
|
|
2190
|
-
function setPullRequest(snapshot, url) {
|
|
2206
|
+
function setPullRequest(snapshot, url, title) {
|
|
2191
2207
|
if (url === void 0) return;
|
|
2192
2208
|
if (url === null) {
|
|
2193
2209
|
if (snapshot.origin) {
|
|
2194
2210
|
delete snapshot.origin.pullRequestUrl;
|
|
2195
2211
|
delete snapshot.origin.pullRequestNumber;
|
|
2212
|
+
delete snapshot.origin.pullRequestTitle;
|
|
2196
2213
|
}
|
|
2197
2214
|
return;
|
|
2198
2215
|
}
|
|
2199
2216
|
snapshot.origin = {
|
|
2200
2217
|
...snapshot.origin,
|
|
2201
2218
|
pullRequestUrl: url,
|
|
2202
|
-
pullRequestNumber: Number(url.split("/").at(-1))
|
|
2219
|
+
pullRequestNumber: Number(url.split("/").at(-1)),
|
|
2220
|
+
pullRequestTitle: title ?? (snapshot.origin?.pullRequestUrl === url ? snapshot.origin.pullRequestTitle : void 0)
|
|
2203
2221
|
};
|
|
2204
2222
|
}
|
|
2205
2223
|
//#endregion
|
|
@@ -2677,8 +2695,8 @@ var ReviewStore = class {
|
|
|
2677
2695
|
listener();
|
|
2678
2696
|
} catch {}
|
|
2679
2697
|
}
|
|
2680
|
-
/**
|
|
2681
|
-
|
|
2698
|
+
/** Reviews of the tutorial earlier versions shipped, for their removal. */
|
|
2699
|
+
legacyTutorialIds() {
|
|
2682
2700
|
return this.db.prepare(`SELECT reviews.id FROM reviews JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version WHERE json_extract(versions.snapshot,'$.origin.tutorial') = 1`).all().map((row) => String(row.id));
|
|
2683
2701
|
}
|
|
2684
2702
|
list(mode = "structural") {
|
|
@@ -2696,8 +2714,7 @@ var ReviewStore = class {
|
|
|
2696
2714
|
JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
|
|
2697
2715
|
LEFT JOIN review_attention ON review_attention.review_id=reviews.id
|
|
2698
2716
|
LEFT JOIN repositories ON repositories.id=json_extract(versions.snapshot,'$.pins.repositoryId')
|
|
2699
|
-
|
|
2700
|
-
${id === void 0 ? "" : "AND reviews.id=?"}
|
|
2717
|
+
${id === void 0 ? "" : "WHERE reviews.id=?"}
|
|
2701
2718
|
ORDER BY reviews.rowid`).all(...id === void 0 ? [] : [id]).map((row) => {
|
|
2702
2719
|
const summary = JSON.parse(String(row.summary));
|
|
2703
2720
|
if (summary.pins) summary.target ??= {
|
|
@@ -2763,13 +2780,14 @@ var ReviewStore = class {
|
|
|
2763
2780
|
inspect(id, targetId, version) {
|
|
2764
2781
|
return inspectSnapshot(this.read(id, version), targetId);
|
|
2765
2782
|
}
|
|
2766
|
-
/** The host can seed a
|
|
2783
|
+
/** The host can seed a document; transport callers only supply a command. */
|
|
2767
2784
|
execute(input, initial) {
|
|
2768
2785
|
if (this.closing) return Promise.reject(/* @__PURE__ */ new Error("Review store is closing."));
|
|
2769
2786
|
const command = commandSchema.parse(input);
|
|
2770
2787
|
if (initial && command.operation.type !== "create") throw new ReviewInputError("Initial content requires a create command.");
|
|
2771
2788
|
const pullRequest = this.startPullRequest(command);
|
|
2772
2789
|
pullRequest?.catch(() => {});
|
|
2790
|
+
const pullRequestTitle = pullRequest ? pullRequest.then(({ title }) => title || void 0, () => void 0) : this.startPullRequestTitle(command);
|
|
2773
2791
|
const run = this.pending.then(async () => {
|
|
2774
2792
|
const op = command.operation;
|
|
2775
2793
|
this.purgeExpired();
|
|
@@ -2821,7 +2839,7 @@ var ReviewStore = class {
|
|
|
2821
2839
|
}
|
|
2822
2840
|
const id = op.type !== "create" ? op.reviewId : op.kind === "scratchpad" ? SCRATCHPAD_ID : randomUUID();
|
|
2823
2841
|
const previous = op.type === "create" ? void 0 : this.read(id);
|
|
2824
|
-
let snapshot = op.type === "create" ? createdSnapshot(id, op, resolvedTarget, fromPullRequest
|
|
2842
|
+
let snapshot = op.type === "create" ? createdSnapshot(id, op, resolvedTarget, fromPullRequest && (fromPullRequest.title || `PR #${op.pullRequestUrl.split("/").at(-1)}`)) : structuredClone(previous);
|
|
2825
2843
|
delete snapshot.trashedAt;
|
|
2826
2844
|
delete snapshot.sourceUnavailable;
|
|
2827
2845
|
delete snapshot.lastEdit;
|
|
@@ -2839,6 +2857,10 @@ var ReviewStore = class {
|
|
|
2839
2857
|
};
|
|
2840
2858
|
}
|
|
2841
2859
|
}
|
|
2860
|
+
if (requestedTarget) snapshot.origin = {
|
|
2861
|
+
...snapshot.origin,
|
|
2862
|
+
baseBranch: requestedTarget.base === resolvedTarget?.pins.base ? void 0 : requestedTarget.base
|
|
2863
|
+
};
|
|
2842
2864
|
switch (op.type) {
|
|
2843
2865
|
case "create":
|
|
2844
2866
|
if (initial) {
|
|
@@ -2849,13 +2871,13 @@ var ReviewStore = class {
|
|
|
2849
2871
|
};
|
|
2850
2872
|
for (const block of snapshot.document) assignFreshIds(block, (prefix) => `${prefix}-${++nextId}`);
|
|
2851
2873
|
}
|
|
2852
|
-
setPullRequest(snapshot, op.pullRequestUrl);
|
|
2874
|
+
setPullRequest(snapshot, op.pullRequestUrl, await pullRequestTitle);
|
|
2853
2875
|
break;
|
|
2854
2876
|
case "rename":
|
|
2855
2877
|
snapshot.title = op.title;
|
|
2856
2878
|
break;
|
|
2857
2879
|
case "set_target":
|
|
2858
|
-
setPullRequest(snapshot, op.pullRequestUrl ?? (op.pullRequestUrl === null || snapshot.pins?.repositoryId !== resolvedTarget.pins.repositoryId ? null : void 0));
|
|
2880
|
+
setPullRequest(snapshot, op.pullRequestUrl ?? (op.pullRequestUrl === null || snapshot.pins?.repositoryId !== resolvedTarget.pins.repositoryId ? null : void 0), await pullRequestTitle);
|
|
2859
2881
|
snapshot.staleSources = [];
|
|
2860
2882
|
snapshot.target = resolvedTarget.target;
|
|
2861
2883
|
snapshot.pins = resolvedTarget.pins;
|
|
@@ -2942,12 +2964,16 @@ var ReviewStore = class {
|
|
|
2942
2964
|
preferred: existing && this.read(existing).pins?.repositoryId
|
|
2943
2965
|
});
|
|
2944
2966
|
}
|
|
2967
|
+
startPullRequestTitle(command) {
|
|
2968
|
+
const op = command.operation;
|
|
2969
|
+
if (op.type !== "create" && op.type !== "set_target" || !op.pullRequestUrl) return Promise.resolve(void 0);
|
|
2970
|
+
return this.providers.pullRequestTitle?.(op.pullRequestUrl) ?? Promise.resolve(void 0);
|
|
2971
|
+
}
|
|
2945
2972
|
/** Reviews whose PR is this one, newest version first. Summaries only. */
|
|
2946
2973
|
reviewsForPullRequest(url) {
|
|
2947
2974
|
return this.db.prepare(`SELECT reviews.id FROM reviews
|
|
2948
2975
|
JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
|
|
2949
2976
|
WHERE lower(json_extract(versions.snapshot,'$.origin.pullRequestUrl'))=?
|
|
2950
|
-
AND COALESCE(json_extract(versions.snapshot,'$.origin.tutorial'), 0) = 0
|
|
2951
2977
|
ORDER BY json_extract(versions.snapshot,'$.createdAt') DESC, reviews.rowid DESC`).all(pullRequestKey(url)).map((row) => String(row.id));
|
|
2952
2978
|
}
|
|
2953
2979
|
assertReusableReview(reviewId) {
|
|
@@ -3114,7 +3140,7 @@ function inspectSnapshot(snapshot, targetId) {
|
|
|
3114
3140
|
}
|
|
3115
3141
|
//#endregion
|
|
3116
3142
|
//#region src/review-api/workspaces.ts
|
|
3117
|
-
const OWNED_ELSEWHERE = "Another
|
|
3143
|
+
const OWNED_ELSEWHERE = "Another Whiteboard server is managing this review's language workspaces. Retry once it stops.";
|
|
3118
3144
|
/** Local lifecycle only: source and authored history never depend on preparation.
|
|
3119
3145
|
* Status/queue/process lifecycle follows #334, retaining the legacy prepare config
|
|
3120
3146
|
* and per-review managed checkout layout instead of a new settings system.
|
|
@@ -3147,10 +3173,20 @@ var ReviewWorkspaces = class {
|
|
|
3147
3173
|
this.db.close();
|
|
3148
3174
|
throw error;
|
|
3149
3175
|
}
|
|
3150
|
-
|
|
3176
|
+
const stopCatalog = store.subscribeCatalog(() => {
|
|
3151
3177
|
this.collect();
|
|
3152
3178
|
this.releaseTrashed();
|
|
3153
3179
|
});
|
|
3180
|
+
const stopDeleted = store.subscribe(({ reviewId, deleted }) => {
|
|
3181
|
+
if (!deleted) return;
|
|
3182
|
+
const repositories = this.repositoryDirs();
|
|
3183
|
+
repositories.catch(() => void 0);
|
|
3184
|
+
this.release(reviewId, repositories, () => !this.hasReview(reviewId));
|
|
3185
|
+
});
|
|
3186
|
+
this.stop = () => {
|
|
3187
|
+
stopCatalog();
|
|
3188
|
+
stopDeleted();
|
|
3189
|
+
};
|
|
3154
3190
|
this.collect();
|
|
3155
3191
|
this.releaseTrashed();
|
|
3156
3192
|
}
|
|
@@ -3169,7 +3205,11 @@ var ReviewWorkspaces = class {
|
|
|
3169
3205
|
if (!released.length) return;
|
|
3170
3206
|
const repositories = this.repositoryDirs();
|
|
3171
3207
|
repositories.catch(() => void 0);
|
|
3172
|
-
for (const reviewId of released) this.release(reviewId, repositories);
|
|
3208
|
+
for (const reviewId of released) this.release(reviewId, repositories, () => this.stillTrashed(reviewId));
|
|
3209
|
+
}
|
|
3210
|
+
/** Restored since the move to Trash: its checkouts may be in use. */
|
|
3211
|
+
stillTrashed(reviewId) {
|
|
3212
|
+
return this.store.trashedIds().includes(reviewId);
|
|
3173
3213
|
}
|
|
3174
3214
|
async repositoryDirs() {
|
|
3175
3215
|
const dirs = new Set(this.all().map((item) => item.repository));
|
|
@@ -3183,21 +3223,21 @@ var ReviewWorkspaces = class {
|
|
|
3183
3223
|
released(reviewId) {
|
|
3184
3224
|
return this.releasing.get(reviewId) ?? Promise.resolve();
|
|
3185
3225
|
}
|
|
3186
|
-
release(reviewId, repositories
|
|
3226
|
+
release(reviewId, repositories, stillReleased) {
|
|
3187
3227
|
const requests = [...this.requests.values()];
|
|
3188
3228
|
const done = this.cleanup.then(async () => {
|
|
3189
3229
|
await Promise.allSettled(requests);
|
|
3190
|
-
await this.releaseCheckouts(reviewId, await repositories);
|
|
3230
|
+
await this.releaseCheckouts(reviewId, await repositories, stillReleased);
|
|
3191
3231
|
}).catch((error) => {
|
|
3192
|
-
console.error(`Could not free the pinned checkouts of
|
|
3232
|
+
console.error(`Could not free the pinned checkouts of review ${reviewId}:`, error);
|
|
3193
3233
|
}).finally(() => {
|
|
3194
3234
|
if (this.releasing.get(reviewId) === done) this.releasing.delete(reviewId);
|
|
3195
3235
|
});
|
|
3196
3236
|
this.releasing.set(reviewId, done);
|
|
3197
3237
|
this.cleanup = done;
|
|
3198
3238
|
}
|
|
3199
|
-
async releaseCheckouts(reviewId, repositories) {
|
|
3200
|
-
if (!
|
|
3239
|
+
async releaseCheckouts(reviewId, repositories, stillReleased) {
|
|
3240
|
+
if (!stillReleased()) return;
|
|
3201
3241
|
const environments = this.all().filter((item) => item.reviewId === reviewId);
|
|
3202
3242
|
const checkouts = repositories.filter((repository) => existsSync(reviewManagedCheckoutRoot(repository, reviewId)));
|
|
3203
3243
|
if (!environments.length && !checkouts.length || !this.claim(reviewId)) return;
|
|
@@ -3292,7 +3332,7 @@ var ReviewWorkspaces = class {
|
|
|
3292
3332
|
async retryCleanup(id) {
|
|
3293
3333
|
const environment = this.get(id);
|
|
3294
3334
|
if (!environment || environment.state !== "cleanup-failed") throw new ReviewInputError("Cleanup failure not found.", 404);
|
|
3295
|
-
if (this.store.has(environment.reviewId)) this.release(environment.reviewId);
|
|
3335
|
+
if (this.store.has(environment.reviewId)) this.release(environment.reviewId, this.repositoryDirs(), () => this.stillTrashed(environment.reviewId));
|
|
3296
3336
|
else this.collect(id);
|
|
3297
3337
|
await this.cleanup;
|
|
3298
3338
|
}
|
|
@@ -3466,6 +3506,7 @@ var ReviewWorkspaces = class {
|
|
|
3466
3506
|
]);
|
|
3467
3507
|
}
|
|
3468
3508
|
if (environment.rootPath) await removeReviewPrepareArtifacts(environment.rootPath);
|
|
3509
|
+
if (environment.repository && !this.all().some((other) => other.id !== environment.id && other.reviewId === environment.reviewId && other.repository === environment.repository)) await removeReviewManagedCheckouts(environment.repository, environment.reviewId);
|
|
3469
3510
|
this.db.prepare("DELETE FROM pinned_environments WHERE id=?").run(environment.id);
|
|
3470
3511
|
this.db.prepare("DELETE FROM workspace_leases WHERE review_id=? AND owner=? AND NOT EXISTS(SELECT 1 FROM pinned_environments WHERE value->>'reviewId'=?)").run(environment.reviewId, this.ownerId, environment.reviewId);
|
|
3471
3512
|
} catch (error) {
|
|
@@ -4263,9 +4304,18 @@ var LocalReviewData = class {
|
|
|
4263
4304
|
base,
|
|
4264
4305
|
head
|
|
4265
4306
|
},
|
|
4266
|
-
title: pullRequest.title.trim()
|
|
4307
|
+
title: pullRequest.title.trim()
|
|
4267
4308
|
};
|
|
4268
4309
|
}
|
|
4310
|
+
async pullRequestTitle(url) {
|
|
4311
|
+
const { provider, address } = resolvePullRequest(url);
|
|
4312
|
+
try {
|
|
4313
|
+
const { title } = await provider.readPullRequest(address, this.options.pullRequests ?? defaultPullRequestDeps);
|
|
4314
|
+
return title.trim() || void 0;
|
|
4315
|
+
} catch {
|
|
4316
|
+
return;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4269
4319
|
/** A registered checkout with a remote for host/owner/repo, and that remote. */
|
|
4270
4320
|
async pullRequestCheckout(provider, address, repository, deps) {
|
|
4271
4321
|
const registered = this.store.repositories();
|
|
@@ -4685,6 +4735,7 @@ function openLocalReviewStore(databasePath, options = {}) {
|
|
|
4685
4735
|
projectSource: (snapshot, pins) => data.projectSource(snapshot, pins),
|
|
4686
4736
|
resolveTarget: (target, pinned) => data.resolveTarget(target, pinned),
|
|
4687
4737
|
resolvePullRequest: (url, repository) => data.resolvePullRequest(url, repository),
|
|
4738
|
+
pullRequestTitle: (url) => data.pullRequestTitle(url),
|
|
4688
4739
|
headBranch: (pins, headRef) => data.headBranch(pins, headRef),
|
|
4689
4740
|
sourcePins: (snapshot) => data.sourcePins(snapshot),
|
|
4690
4741
|
unavailableAnchors: (snapshot) => data.unavailableAnchors(snapshot),
|
|
@@ -4729,4 +4780,4 @@ async function readWorkspace(file) {
|
|
|
4729
4780
|
}
|
|
4730
4781
|
}
|
|
4731
4782
|
//#endregion
|
|
4732
|
-
export {
|
|
4783
|
+
export { activityUpdateSchema as _, inspectSnapshot as a, diffrMissingError as b, askAgentIds as c, askPicksSchema as d, askQuestionSchema as f, activityEndSchema as g, activityBeginSchema as h, commandSchema as i, askChoiceKinds as l, selectionMarkdown as m, uploadSchema as n, initializeReviewStoreSchema as o, AgentSelectionSchema as p, SCRATCHPAD_ID as r, applyAskChange as s, openLocalReviewStore as t, askPermissionsSchema as u, invalidateStructuralComparisons as v, diffrExecutable as y };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-
|
|
4
|
-
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-
|
|
1
|
+
import { a as toolResultText, s as ReviewApiError } from "./agent-client-DVd-prfp.js";
|
|
2
|
+
import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
|
|
3
|
+
import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-BXKEwhfi.js";
|
|
4
|
+
import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-BXWzQG8T.js";
|
|
5
5
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
|
|
2
|
-
import "./review-home-paths-
|
|
2
|
+
import "./review-home-paths-zwevIq4I.js";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-
|
|
2
|
-
import "./review-home-paths-
|
|
3
|
-
import {
|
|
1
|
+
import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-yfLp5PiJ.js";
|
|
2
|
+
import "./review-home-paths-zwevIq4I.js";
|
|
3
|
+
import { A as withFileLock } from "./s3-config-DDWWMh5x.js";
|
|
4
4
|
import { access, mkdir } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { DatabaseSync } from "node:sqlite";
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { i as readReviewPackageVersion } from "./package-paths-BsuI4WUf.js";
|
|
2
|
+
import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-CifQ3yQS.js";
|
|
3
|
+
import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-Cw7CcfaZ.js";
|
|
4
|
+
import { t as ensureRemoteLanguageServer } from "./remote-language-server-DQgWUNeV.js";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { gt, valid } from "semver";
|
|
8
|
+
//#region src/remote-toolchains.ts
|
|
9
|
+
const PROBE_TIMEOUT_MS = 1e4;
|
|
10
|
+
const TOOLCHAINS = {
|
|
11
|
+
rust: [["cargo", "--version"], ["rustc", "--version"]],
|
|
12
|
+
swift: [["swift", "--version"]],
|
|
13
|
+
csharp: [["dotnet", "--version"]],
|
|
14
|
+
go: [["go", "version"]]
|
|
15
|
+
};
|
|
16
|
+
function probe(command, env, timeoutMs) {
|
|
17
|
+
const shell = env.SHELL || "/bin/sh";
|
|
18
|
+
const flags = ["csh", "tcsh"].includes(path.basename(shell)) ? ["-ic"] : [
|
|
19
|
+
"-i",
|
|
20
|
+
"-l",
|
|
21
|
+
"-c"
|
|
22
|
+
];
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const child = spawn(shell, [...flags, command.join(" ")], {
|
|
25
|
+
env,
|
|
26
|
+
detached: true,
|
|
27
|
+
stdio: "ignore"
|
|
28
|
+
});
|
|
29
|
+
const timer = setTimeout(() => {
|
|
30
|
+
child.removeAllListeners("exit");
|
|
31
|
+
try {
|
|
32
|
+
process.kill(-child.pid, "SIGKILL");
|
|
33
|
+
} catch {}
|
|
34
|
+
resolve(`${command.join(" ")} did not answer within ${timeoutMs / 1e3} s`);
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
child.once("error", (error) => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
resolve(`the login shell ${shell} did not run: ${error.message}`);
|
|
39
|
+
});
|
|
40
|
+
child.once("exit", (code) => {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
resolve(code === 0 ? "found" : code === 127 ? "missing" : `${command.join(" ")} failed with exit code ${code ?? "none"}`);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function missingToolchains(groups, env, { timeoutMs = PROBE_TIMEOUT_MS, toolchains = TOOLCHAINS } = {}) {
|
|
47
|
+
const entries = await Promise.all(groups.map(async (group) => {
|
|
48
|
+
const commands = Object.hasOwn(toolchains, group) ? toolchains[group] : [];
|
|
49
|
+
const outcomes = await Promise.all(commands.map((command) => probe(command, env, timeoutMs)));
|
|
50
|
+
const missing = commands.filter((_, i) => outcomes[i] === "missing").map(([tool]) => tool);
|
|
51
|
+
const failures = outcomes.filter((outcome) => outcome !== "found" && outcome !== "missing");
|
|
52
|
+
const details = [...missing.length > 0 ? [`${missing.join(" and ")} ${missing.length > 1 ? "were" : "was"} not found on the login shell's PATH`] : [], ...failures];
|
|
53
|
+
return details.length > 0 ? [[group, details.join("; ")]] : [];
|
|
54
|
+
}));
|
|
55
|
+
return new Map(entries.flat());
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/remote-attach.ts
|
|
59
|
+
async function remoteAttach(input) {
|
|
60
|
+
const extensions = new AbortController();
|
|
61
|
+
const language = ensureRemoteLanguageServer({
|
|
62
|
+
env: input.env,
|
|
63
|
+
packageRoot: input.packageRoot,
|
|
64
|
+
groups: input.groups,
|
|
65
|
+
signal: extensions.signal,
|
|
66
|
+
ensure: input.ensureExtensions,
|
|
67
|
+
installTimeoutMs: input.installTimeoutMs,
|
|
68
|
+
cli: input.cli
|
|
69
|
+
});
|
|
70
|
+
const toolchains = missingToolchains(input.groups ?? [], input.env);
|
|
71
|
+
let server;
|
|
72
|
+
let previousVersion;
|
|
73
|
+
let incompatibleRunning;
|
|
74
|
+
try {
|
|
75
|
+
const version = input.version ?? readReviewPackageVersion(import.meta.url);
|
|
76
|
+
const other = input.replace ? await otherVersionRunning(input.stateDir, version) : void 0;
|
|
77
|
+
if (other && (other.startedBy === "user" || newer(other.version, version))) incompatibleRunning = other;
|
|
78
|
+
else if (other) {
|
|
79
|
+
await stopBackgroundServer({ serverPid: other.pid });
|
|
80
|
+
previousVersion = other.version;
|
|
81
|
+
}
|
|
82
|
+
server = await ensureBackgroundServer({
|
|
83
|
+
stateDir: input.stateDir,
|
|
84
|
+
env: input.env,
|
|
85
|
+
startedBy: "desktop",
|
|
86
|
+
cli: input.cli
|
|
87
|
+
});
|
|
88
|
+
} catch (error) {
|
|
89
|
+
extensions.abort();
|
|
90
|
+
await Promise.all([language, toolchains]);
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
const { discovery, started } = server;
|
|
94
|
+
const { languageServer, languageServerDetail, languageServerPending, languageGroups } = await language;
|
|
95
|
+
const missing = await toolchains;
|
|
96
|
+
const health = await readReviewServerHealth(discovery);
|
|
97
|
+
if (!health) throw serverNotReady(input.stateDir);
|
|
98
|
+
return {
|
|
99
|
+
event: "remote.attach",
|
|
100
|
+
version: health.version ?? null,
|
|
101
|
+
commit: health.commit ?? null,
|
|
102
|
+
serverId: health.serverId ?? null,
|
|
103
|
+
url: discovery.url,
|
|
104
|
+
token: discovery.token,
|
|
105
|
+
startedServer: started,
|
|
106
|
+
languageServer,
|
|
107
|
+
...languageServerDetail !== void 0 && { languageServerDetail },
|
|
108
|
+
...languageServerPending && { languageServerPending },
|
|
109
|
+
languageGroups: languageGroups.map(({ group, installed, detail }) => {
|
|
110
|
+
const details = [detail, missing.get(group)].filter(Boolean).join("; ");
|
|
111
|
+
return {
|
|
112
|
+
group,
|
|
113
|
+
installed,
|
|
114
|
+
...details && { detail: details }
|
|
115
|
+
};
|
|
116
|
+
}),
|
|
117
|
+
...previousVersion !== void 0 && {
|
|
118
|
+
replaced: true,
|
|
119
|
+
previousVersion
|
|
120
|
+
},
|
|
121
|
+
...incompatibleRunning && { incompatibleRunning }
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const newer = (running, own) => valid(running) !== null && valid(own) !== null && gt(running, own);
|
|
125
|
+
async function otherVersionRunning(stateDir, version) {
|
|
126
|
+
const recorded = await recordedBackgroundServer(stateDir);
|
|
127
|
+
if (!recorded) return void 0;
|
|
128
|
+
const { discovery, health } = recorded;
|
|
129
|
+
const running = health.version ?? "unknown";
|
|
130
|
+
return running === version ? void 0 : {
|
|
131
|
+
version: running,
|
|
132
|
+
pid: discovery.serverPid,
|
|
133
|
+
startedBy: discovery.startedBy
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { remoteAttach };
|