@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,359 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
mkdir,
|
|
4
|
+
readFile,
|
|
5
|
+
readdir,
|
|
6
|
+
rename,
|
|
7
|
+
rm,
|
|
8
|
+
writeFile,
|
|
9
|
+
} from "node:fs/promises";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { promisify } from "node:util";
|
|
12
|
+
|
|
13
|
+
import { parseJsonText } from "@dev.fast/json";
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
16
|
+
import { findReviewPackageRoot } from "./package-paths";
|
|
17
|
+
import { downloadPinned } from "./pinned-download";
|
|
18
|
+
import { devReviewHome } from "./review-home-paths";
|
|
19
|
+
import { extractVsix, sanitizeVsixManifest } from "./vsix";
|
|
20
|
+
|
|
21
|
+
const EXECUTABLE_TIMEOUT_MS = 15_000;
|
|
22
|
+
|
|
23
|
+
const STAMP_FILE = ".curated.json";
|
|
24
|
+
|
|
25
|
+
type RemoteTarget = "linux-x64" | "linux-arm64";
|
|
26
|
+
|
|
27
|
+
const curatedDownloadSchema = z.object({
|
|
28
|
+
universal: z.boolean(),
|
|
29
|
+
url: z.string(),
|
|
30
|
+
sha256: z.string(),
|
|
31
|
+
size: z.number(),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
type CuratedDownload = z.infer<typeof curatedDownloadSchema>;
|
|
35
|
+
|
|
36
|
+
const curatedExtensionSchema = z.object({
|
|
37
|
+
id: z.string(),
|
|
38
|
+
version: z.string(),
|
|
39
|
+
tier: z.enum(["bundled", "optional"]),
|
|
40
|
+
group: z.string(),
|
|
41
|
+
executables: z.array(z.string()),
|
|
42
|
+
stripExtensionPack: z.boolean(),
|
|
43
|
+
targets: z.object({
|
|
44
|
+
"linux-x64": curatedDownloadSchema,
|
|
45
|
+
"linux-arm64": curatedDownloadSchema,
|
|
46
|
+
}),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export type CuratedRemoteExtension = z.infer<typeof curatedExtensionSchema>;
|
|
50
|
+
|
|
51
|
+
const stampSchema = z.object({
|
|
52
|
+
id: z.string(),
|
|
53
|
+
version: z.string(),
|
|
54
|
+
target: z.string(),
|
|
55
|
+
sha256: z.string(),
|
|
56
|
+
installedTimestamp: z.number(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
type Stamp = z.infer<typeof stampSchema>;
|
|
60
|
+
|
|
61
|
+
export function whiteboardRemoteHome(env: NodeJS.ProcessEnv = process.env) {
|
|
62
|
+
return path.join(devReviewHome(env), "whiteboard-remote");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function remoteServerPaths(env: NodeJS.ProcessEnv = process.env) {
|
|
66
|
+
const home = whiteboardRemoteHome(env);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
extensionsDir: path.join(home, "extensions"),
|
|
70
|
+
serverDataDir: path.join(home, "server"),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function remoteExtensionTarget(
|
|
75
|
+
platform: string = process.platform,
|
|
76
|
+
arch: string = process.arch,
|
|
77
|
+
): RemoteTarget {
|
|
78
|
+
if (platform === "linux" && (arch === "x64" || arch === "arm64"))
|
|
79
|
+
return `linux-${arch}`;
|
|
80
|
+
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Language features on a remote need Linux on x64 or arm64; this machine is ${platform}-${arch}.`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface EnsureRemoteExtensionsInput {
|
|
87
|
+
env?: NodeJS.ProcessEnv;
|
|
88
|
+
curated?: CuratedRemoteExtension[];
|
|
89
|
+
groups?: string[];
|
|
90
|
+
target?: RemoteTarget;
|
|
91
|
+
timeoutMs?: number;
|
|
92
|
+
signal?: AbortSignal;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function ensureRemoteExtensions(
|
|
96
|
+
input: EnsureRemoteExtensionsInput = {},
|
|
97
|
+
) {
|
|
98
|
+
const target = input.target ?? remoteExtensionTarget();
|
|
99
|
+
const curated = input.curated ?? (await readPackagedCurated());
|
|
100
|
+
const { extensionsDir, serverDataDir } = remoteServerPaths(input.env);
|
|
101
|
+
const installed: string[] = [];
|
|
102
|
+
const skipped: { id: string; reason: string }[] = [];
|
|
103
|
+
const failed: { id: string; error: string }[] = [];
|
|
104
|
+
const listed: StoredExtension[] = [];
|
|
105
|
+
|
|
106
|
+
await mkdir(extensionsDir, { recursive: true });
|
|
107
|
+
|
|
108
|
+
for (const extension of curated) {
|
|
109
|
+
if (
|
|
110
|
+
extension.tier === "optional" &&
|
|
111
|
+
!input.groups?.includes(extension.group)
|
|
112
|
+
) {
|
|
113
|
+
skipped.push({
|
|
114
|
+
id: extension.id,
|
|
115
|
+
reason: `optional group "${extension.group}" not requested`,
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const download = extension.targets[target];
|
|
121
|
+
const folder = `${extension.id}-${extension.version}`;
|
|
122
|
+
const directory = path.join(extensionsDir, folder);
|
|
123
|
+
|
|
124
|
+
const stamp = {
|
|
125
|
+
id: extension.id,
|
|
126
|
+
version: extension.version,
|
|
127
|
+
target,
|
|
128
|
+
sha256: download.sha256,
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const current = await readStamp(directory);
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
current &&
|
|
136
|
+
sameStamp(current, stamp) &&
|
|
137
|
+
(await checkExecutables(directory, extension).then(
|
|
138
|
+
() => true,
|
|
139
|
+
() => false,
|
|
140
|
+
))
|
|
141
|
+
) {
|
|
142
|
+
skipped.push({ id: extension.id, reason: "up to date" });
|
|
143
|
+
listed.push(
|
|
144
|
+
storedExtension(
|
|
145
|
+
extension,
|
|
146
|
+
directory,
|
|
147
|
+
folder,
|
|
148
|
+
target,
|
|
149
|
+
current.installedTimestamp,
|
|
150
|
+
),
|
|
151
|
+
);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const installedTimestamp = Date.now();
|
|
156
|
+
await install(
|
|
157
|
+
extension,
|
|
158
|
+
download,
|
|
159
|
+
directory,
|
|
160
|
+
{ ...stamp, installedTimestamp },
|
|
161
|
+
input.timeoutMs,
|
|
162
|
+
input.signal,
|
|
163
|
+
);
|
|
164
|
+
installed.push(extension.id);
|
|
165
|
+
listed.push(
|
|
166
|
+
storedExtension(
|
|
167
|
+
extension,
|
|
168
|
+
directory,
|
|
169
|
+
folder,
|
|
170
|
+
target,
|
|
171
|
+
installedTimestamp,
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
failed.push({
|
|
176
|
+
id: extension.id,
|
|
177
|
+
error: error instanceof Error ? error.message : String(error),
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// The scanner caches its last scan; a changed list is read only once the cache is gone.
|
|
183
|
+
const list = `${JSON.stringify(listed)}\n`;
|
|
184
|
+
const listFile = path.join(extensionsDir, "extensions.json");
|
|
185
|
+
|
|
186
|
+
if ((await readFile(listFile, "utf8").catch(() => undefined)) !== list) {
|
|
187
|
+
const temporary = `${listFile}.${process.pid}.tmp`;
|
|
188
|
+
|
|
189
|
+
await writeFile(temporary, list);
|
|
190
|
+
await rename(temporary, listFile);
|
|
191
|
+
await rm(path.join(serverDataDir, "data", "CachedProfilesData"), {
|
|
192
|
+
recursive: true,
|
|
193
|
+
force: true,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const kept = new Set(listed.map((entry) => entry.relativeLocation));
|
|
198
|
+
|
|
199
|
+
for (const folder of await readdir(extensionsDir))
|
|
200
|
+
if (
|
|
201
|
+
!kept.has(folder) &&
|
|
202
|
+
curated.some(
|
|
203
|
+
({ id }) =>
|
|
204
|
+
folder.startsWith(`${id}-`) &&
|
|
205
|
+
/^\d/.test(folder.slice(id.length + 1)),
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
await rm(path.join(extensionsDir, folder), {
|
|
209
|
+
recursive: true,
|
|
210
|
+
force: true,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
event: "remote.extensions" as const,
|
|
215
|
+
target,
|
|
216
|
+
installed,
|
|
217
|
+
skipped,
|
|
218
|
+
failed,
|
|
219
|
+
groups: requestedGroups(curated, input.groups ?? [], failed),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function requestedGroups(
|
|
224
|
+
curated: CuratedRemoteExtension[],
|
|
225
|
+
groups: string[],
|
|
226
|
+
failed: { id: string; error: string }[],
|
|
227
|
+
) {
|
|
228
|
+
return groups.flatMap((group) => {
|
|
229
|
+
const members = curated.filter(
|
|
230
|
+
(extension) => extension.tier === "optional" && extension.group === group,
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
if (members.length === 0) return [];
|
|
234
|
+
|
|
235
|
+
const errors = failed.filter(({ id }) =>
|
|
236
|
+
members.some((extension) => extension.id === id),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
return [
|
|
240
|
+
errors.length === 0
|
|
241
|
+
? { group, installed: true }
|
|
242
|
+
: {
|
|
243
|
+
group,
|
|
244
|
+
installed: false,
|
|
245
|
+
detail: errors.map(({ id, error }) => `${id}: ${error}`).join("; "),
|
|
246
|
+
},
|
|
247
|
+
];
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function readPackagedCurated(): Promise<CuratedRemoteExtension[]> {
|
|
252
|
+
const file = path.join(
|
|
253
|
+
findReviewPackageRoot(import.meta.url),
|
|
254
|
+
"vscode-server",
|
|
255
|
+
"curated.json",
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const text = await readFile(file, "utf8").catch(() => {
|
|
259
|
+
throw new Error(`This package has no VS Code server (${file} is missing).`);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return z
|
|
263
|
+
.object({ extensions: z.array(curatedExtensionSchema) })
|
|
264
|
+
.parse(JSON.parse(text)).extensions;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async function readStamp(directory: string): Promise<Stamp | undefined> {
|
|
268
|
+
return readFile(path.join(directory, STAMP_FILE), "utf8")
|
|
269
|
+
.then((text) => stampSchema.safeParse(parseJsonText(text)).data)
|
|
270
|
+
.catch(() => undefined);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function sameStamp(
|
|
274
|
+
current: Stamp,
|
|
275
|
+
expected: Omit<Stamp, "installedTimestamp">,
|
|
276
|
+
) {
|
|
277
|
+
return (
|
|
278
|
+
current.id === expected.id &&
|
|
279
|
+
current.version === expected.version &&
|
|
280
|
+
current.target === expected.target &&
|
|
281
|
+
current.sha256 === expected.sha256
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function install(
|
|
286
|
+
extension: CuratedRemoteExtension,
|
|
287
|
+
download: CuratedDownload,
|
|
288
|
+
directory: string,
|
|
289
|
+
stamp: Stamp,
|
|
290
|
+
timeoutMs?: number,
|
|
291
|
+
signal?: AbortSignal,
|
|
292
|
+
) {
|
|
293
|
+
const part = `${directory}.${process.pid}.vsix`;
|
|
294
|
+
const staging = `${directory}.${process.pid}.staging`;
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
await downloadPinned(download, part, { timeoutMs, signal });
|
|
298
|
+
await rm(staging, { recursive: true, force: true });
|
|
299
|
+
await extractVsix(part, staging, download.size * 8);
|
|
300
|
+
await sanitizeVsixManifest(staging, extension);
|
|
301
|
+
|
|
302
|
+
await checkExecutables(staging, extension);
|
|
303
|
+
|
|
304
|
+
await writeFile(
|
|
305
|
+
path.join(staging, STAMP_FILE),
|
|
306
|
+
`${JSON.stringify(stamp)}\n`,
|
|
307
|
+
);
|
|
308
|
+
await rm(directory, { recursive: true, force: true });
|
|
309
|
+
await rename(staging, directory);
|
|
310
|
+
} finally {
|
|
311
|
+
await rm(part, { force: true });
|
|
312
|
+
await rm(staging, { recursive: true, force: true });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function checkExecutables(
|
|
317
|
+
directory: string,
|
|
318
|
+
extension: CuratedRemoteExtension,
|
|
319
|
+
) {
|
|
320
|
+
for (const relative of extension.executables) {
|
|
321
|
+
await promisify(execFile)(path.join(directory, relative), ["--version"], {
|
|
322
|
+
timeout: EXECUTABLE_TIMEOUT_MS,
|
|
323
|
+
killSignal: "SIGKILL",
|
|
324
|
+
}).catch((error: Error) => {
|
|
325
|
+
throw new Error(
|
|
326
|
+
`${extension.id}: ${relative} does not run here: ${error.message}`,
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
interface StoredExtension {
|
|
333
|
+
identifier: { id: string };
|
|
334
|
+
version: string;
|
|
335
|
+
location: { $mid: 1; scheme: "file"; path: string };
|
|
336
|
+
relativeLocation: string;
|
|
337
|
+
metadata: { installedTimestamp: number; targetPlatform: string };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function storedExtension(
|
|
341
|
+
extension: CuratedRemoteExtension,
|
|
342
|
+
directory: string,
|
|
343
|
+
folder: string,
|
|
344
|
+
target: RemoteTarget,
|
|
345
|
+
installedTimestamp: number,
|
|
346
|
+
): StoredExtension {
|
|
347
|
+
return {
|
|
348
|
+
identifier: { id: extension.id },
|
|
349
|
+
version: extension.version,
|
|
350
|
+
location: { $mid: 1, scheme: "file", path: directory },
|
|
351
|
+
relativeLocation: folder,
|
|
352
|
+
metadata: {
|
|
353
|
+
installedTimestamp,
|
|
354
|
+
targetPlatform: extension.targets[target].universal
|
|
355
|
+
? "universal"
|
|
356
|
+
: target,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|