@linzumi/cli 0.0.15-beta → 0.0.17-beta
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/README.md +6 -1
- package/package.json +1 -1
- package/src/index.ts +2 -2
- package/src/localEditor.ts +115 -17
- package/src/localEditorRuntime.ts +148 -34
package/README.md
CHANGED
|
@@ -107,6 +107,11 @@ workspace/channel scope from the approval flow, trusts only the selected
|
|
|
107
107
|
folder by default, and listens only to the approving human unless
|
|
108
108
|
`--listen-user` is explicitly passed.
|
|
109
109
|
|
|
110
|
+
By default, the runner downloads the Kandan-approved `code-server`
|
|
111
|
+
runtime for your platform and verifies its checksum before enabling the
|
|
112
|
+
browser editor. Linux editor launches are wrapped with `bubblewrap`
|
|
113
|
+
(`bwrap`) for filesystem confinement.
|
|
114
|
+
|
|
110
115
|
`linzumi claim` also prints `support_channel_url`. That channel is the
|
|
111
116
|
shared onboarding room for the approving human, the claimed agent identity,
|
|
112
117
|
and Linzumi support. Keep repository work in task threads; use the support
|
|
@@ -229,7 +234,7 @@ privileges as your shell. Every action is auditable from the thread.
|
|
|
229
234
|
## Pinning a version
|
|
230
235
|
|
|
231
236
|
```bash
|
|
232
|
-
npm install -g @linzumi/cli@0.0.
|
|
237
|
+
npm install -g @linzumi/cli@0.0.17-beta
|
|
233
238
|
linzumi --version
|
|
234
239
|
```
|
|
235
240
|
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -121,7 +121,7 @@ async function main(args: readonly string[]): Promise<void> {
|
|
|
121
121
|
process.stdout.write(connectGuideText());
|
|
122
122
|
return;
|
|
123
123
|
case "version":
|
|
124
|
-
process.stdout.write("linzumi 0.0.
|
|
124
|
+
process.stdout.write("linzumi 0.0.17-beta\n");
|
|
125
125
|
return;
|
|
126
126
|
case "auth":
|
|
127
127
|
await runAuthCommand(parsed.args);
|
|
@@ -614,7 +614,7 @@ export async function parseRunnerArgs(
|
|
|
614
614
|
}
|
|
615
615
|
|
|
616
616
|
if (values.get("version") === true) {
|
|
617
|
-
process.stdout.write("linzumi 0.0.
|
|
617
|
+
process.stdout.write("linzumi 0.0.17-beta\n");
|
|
618
618
|
process.exit(0);
|
|
619
619
|
}
|
|
620
620
|
|
package/src/localEditor.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
writeFileSync,
|
|
15
15
|
} from "node:fs";
|
|
16
16
|
import { tmpdir } from "node:os";
|
|
17
|
-
import { delimiter, dirname, join } from "node:path";
|
|
17
|
+
import { basename, delimiter, dirname, join } from "node:path";
|
|
18
18
|
import { chooseLoopbackPort } from "./codexAppServer";
|
|
19
19
|
import { resolveAllowedCwd } from "./localCapabilities";
|
|
20
20
|
import type { InstalledEditorRuntime } from "./localEditorRuntime";
|
|
@@ -91,6 +91,7 @@ export type PrepareCodeServerLaunchOptions = {
|
|
|
91
91
|
readonly extensionsDir?: string | undefined;
|
|
92
92
|
readonly platform?: NodeJS.Platform | undefined;
|
|
93
93
|
readonly sandboxExecBin?: string | undefined;
|
|
94
|
+
readonly bubblewrapBin?: string | undefined;
|
|
94
95
|
readonly envPath?: string | undefined;
|
|
95
96
|
};
|
|
96
97
|
|
|
@@ -365,8 +366,6 @@ export function codeServerArgs(
|
|
|
365
366
|
"--disable-update-check",
|
|
366
367
|
"--disable-workspace-trust",
|
|
367
368
|
"--disable-getting-started-override",
|
|
368
|
-
"--reconnection-grace-time",
|
|
369
|
-
"10",
|
|
370
369
|
"--app-name",
|
|
371
370
|
"Kandan",
|
|
372
371
|
"--user-data-dir",
|
|
@@ -414,20 +413,18 @@ export function prepareCodeServerLaunch(
|
|
|
414
413
|
): PrepareCodeServerLaunchResult {
|
|
415
414
|
const platform = options.platform ?? process.platform;
|
|
416
415
|
|
|
416
|
+
if (platform === "linux") {
|
|
417
|
+
return prepareLinuxCodeServerLaunch(options);
|
|
418
|
+
}
|
|
419
|
+
|
|
417
420
|
if (platform !== "darwin") {
|
|
418
|
-
return
|
|
419
|
-
ok: false,
|
|
420
|
-
reason: "local_editor_filesystem_sandbox_unavailable",
|
|
421
|
-
};
|
|
421
|
+
return filesystemSandboxUnavailable();
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
const sandboxExecBin = options.sandboxExecBin ?? "/usr/bin/sandbox-exec";
|
|
425
425
|
|
|
426
426
|
if (!existsSync(sandboxExecBin)) {
|
|
427
|
-
return
|
|
428
|
-
ok: false,
|
|
429
|
-
reason: "local_editor_filesystem_sandbox_unavailable",
|
|
430
|
-
};
|
|
427
|
+
return filesystemSandboxUnavailable();
|
|
431
428
|
}
|
|
432
429
|
|
|
433
430
|
const codeServerExecutable = resolveCodeServerExecutable(
|
|
@@ -436,10 +433,7 @@ export function prepareCodeServerLaunch(
|
|
|
436
433
|
);
|
|
437
434
|
|
|
438
435
|
if (!codeServerExecutable.ok) {
|
|
439
|
-
return
|
|
440
|
-
ok: false,
|
|
441
|
-
reason: "local_editor_filesystem_sandbox_unavailable",
|
|
442
|
-
};
|
|
436
|
+
return filesystemSandboxUnavailable();
|
|
443
437
|
}
|
|
444
438
|
|
|
445
439
|
return {
|
|
@@ -460,6 +454,96 @@ export function prepareCodeServerLaunch(
|
|
|
460
454
|
};
|
|
461
455
|
}
|
|
462
456
|
|
|
457
|
+
function prepareLinuxCodeServerLaunch(
|
|
458
|
+
options: PrepareCodeServerLaunchOptions,
|
|
459
|
+
): PrepareCodeServerLaunchResult {
|
|
460
|
+
const bubblewrapExecutable = resolveCodeServerExecutable(
|
|
461
|
+
options.bubblewrapBin ?? "bwrap",
|
|
462
|
+
options.envPath ?? process.env.PATH ?? "",
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
if (!bubblewrapExecutable.ok) {
|
|
466
|
+
return filesystemSandboxUnavailable();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const codeServerExecutable = resolveCodeServerExecutable(
|
|
470
|
+
options.codeServerBin,
|
|
471
|
+
options.envPath ?? process.env.PATH ?? "",
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (!codeServerExecutable.ok) {
|
|
475
|
+
return filesystemSandboxUnavailable();
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const readOnlyRoots = uniquePaths([
|
|
479
|
+
"/usr",
|
|
480
|
+
"/bin",
|
|
481
|
+
"/sbin",
|
|
482
|
+
"/lib",
|
|
483
|
+
"/lib64",
|
|
484
|
+
"/etc",
|
|
485
|
+
"/nix",
|
|
486
|
+
"/opt",
|
|
487
|
+
...(options.codeServerRuntimeRoot === undefined
|
|
488
|
+
? []
|
|
489
|
+
: sandboxPathAliases(options.codeServerRuntimeRoot)),
|
|
490
|
+
codeServerExecutable.directory,
|
|
491
|
+
]);
|
|
492
|
+
|
|
493
|
+
const args = [
|
|
494
|
+
"--die-with-parent",
|
|
495
|
+
"--proc",
|
|
496
|
+
"/proc",
|
|
497
|
+
"--dev-bind",
|
|
498
|
+
"/dev",
|
|
499
|
+
"/dev",
|
|
500
|
+
"--tmpfs",
|
|
501
|
+
"/tmp",
|
|
502
|
+
"--setenv",
|
|
503
|
+
"HOME",
|
|
504
|
+
options.userDataDir,
|
|
505
|
+
"--setenv",
|
|
506
|
+
"XDG_DATA_HOME",
|
|
507
|
+
join(options.userDataDir, "data"),
|
|
508
|
+
"--setenv",
|
|
509
|
+
"XDG_CONFIG_HOME",
|
|
510
|
+
join(options.userDataDir, "config"),
|
|
511
|
+
...readOnlyRoots.flatMap((path) => ["--ro-bind-try", path, path]),
|
|
512
|
+
"--bind",
|
|
513
|
+
options.cwd,
|
|
514
|
+
options.cwd,
|
|
515
|
+
"--bind",
|
|
516
|
+
options.userDataDir,
|
|
517
|
+
options.userDataDir,
|
|
518
|
+
...(options.extensionsDir === undefined
|
|
519
|
+
? []
|
|
520
|
+
: ["--bind", options.extensionsDir, options.extensionsDir]),
|
|
521
|
+
"--chdir",
|
|
522
|
+
options.cwd,
|
|
523
|
+
"--",
|
|
524
|
+
codeServerExecutable.command,
|
|
525
|
+
...codeServerArgs(
|
|
526
|
+
options.port,
|
|
527
|
+
options.cwd,
|
|
528
|
+
options.userDataDir,
|
|
529
|
+
options.extensionsDir,
|
|
530
|
+
),
|
|
531
|
+
];
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
ok: true,
|
|
535
|
+
command: bubblewrapExecutable.command,
|
|
536
|
+
args,
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function filesystemSandboxUnavailable(): PrepareCodeServerLaunchResult {
|
|
541
|
+
return {
|
|
542
|
+
ok: false,
|
|
543
|
+
reason: "local_editor_filesystem_sandbox_unavailable",
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
463
547
|
function codeServerSandboxProfile(
|
|
464
548
|
options: PrepareCodeServerLaunchOptions,
|
|
465
549
|
codeServerBinDir: string,
|
|
@@ -656,7 +740,7 @@ async function startCollaborationSidecar(
|
|
|
656
740
|
]);
|
|
657
741
|
|
|
658
742
|
const child = spawn(
|
|
659
|
-
|
|
743
|
+
nodeRuntimeExecutable(),
|
|
660
744
|
[
|
|
661
745
|
join(
|
|
662
746
|
profile.collaborationServerDir,
|
|
@@ -710,6 +794,19 @@ async function startCollaborationSidecar(
|
|
|
710
794
|
}
|
|
711
795
|
}
|
|
712
796
|
|
|
797
|
+
export function nodeRuntimeExecutable(
|
|
798
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
799
|
+
execPath = process.execPath,
|
|
800
|
+
): string {
|
|
801
|
+
const configured = env.LINZUMI_NODE_BIN?.trim();
|
|
802
|
+
|
|
803
|
+
if (configured !== undefined && configured !== "") {
|
|
804
|
+
return configured;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
return basename(execPath).toLowerCase().includes("bun") ? "node" : execPath;
|
|
808
|
+
}
|
|
809
|
+
|
|
713
810
|
async function installLocalTarball(
|
|
714
811
|
archivePath: string,
|
|
715
812
|
destinationDir: string,
|
|
@@ -728,8 +825,9 @@ function codeServerEnv(
|
|
|
728
825
|
userDataDir: string,
|
|
729
826
|
collaboration?: RunningLocalEditorCollaboration | undefined,
|
|
730
827
|
): NodeJS.ProcessEnv {
|
|
828
|
+
const { PORT: _port, ...hostEnv } = env;
|
|
731
829
|
const base = {
|
|
732
|
-
...
|
|
830
|
+
...hostEnv,
|
|
733
831
|
HOME: userDataDir,
|
|
734
832
|
XDG_CACHE_HOME: join(userDataDir, "xdg-cache"),
|
|
735
833
|
XDG_CONFIG_HOME: join(userDataDir, "xdg-config"),
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
rmSync,
|
|
19
19
|
writeFileSync,
|
|
20
20
|
} from "node:fs";
|
|
21
|
-
import { homedir
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
22
|
import { dirname, join, resolve } from "node:path";
|
|
23
23
|
import { Readable } from "node:stream";
|
|
24
24
|
import { pipeline } from "node:stream/promises";
|
|
@@ -33,7 +33,14 @@ export type EditorRuntimeManifest = {
|
|
|
33
33
|
readonly codeServerVersion: string;
|
|
34
34
|
readonly codeServerBinPath: string;
|
|
35
35
|
readonly manifestPath: string;
|
|
36
|
-
readonly assets: readonly
|
|
36
|
+
readonly assets: readonly EditorRuntimeAsset[];
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type EditorRuntimeAsset = {
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly sha256: string;
|
|
42
|
+
readonly url?: string | undefined;
|
|
43
|
+
readonly contentBase64?: string | undefined;
|
|
37
44
|
};
|
|
38
45
|
|
|
39
46
|
export type EditorRuntimeStatus =
|
|
@@ -186,6 +193,8 @@ export function editorRuntimePlatform(
|
|
|
186
193
|
return "darwin-arm64";
|
|
187
194
|
case "darwin-x64":
|
|
188
195
|
return "darwin-x64";
|
|
196
|
+
case "linux-x64":
|
|
197
|
+
return "linux-x64";
|
|
189
198
|
default:
|
|
190
199
|
return undefined;
|
|
191
200
|
}
|
|
@@ -266,16 +275,15 @@ function normalizeManifest(value: JsonObject):
|
|
|
266
275
|
const codeServerVersion = nonEmptyString(value.codeServerVersion);
|
|
267
276
|
const codeServerBinPath = nonEmptyString(value.codeServerBinPath) ?? "bin/code-server";
|
|
268
277
|
const manifestPath = nonEmptyString(value.manifestPath) ?? "linzumi-editor-runtime.json";
|
|
269
|
-
const assets =
|
|
270
|
-
? value.assets
|
|
271
|
-
: [];
|
|
278
|
+
const assets = normalizeRuntimeAssets(value.assets);
|
|
272
279
|
|
|
273
280
|
if (
|
|
274
281
|
version === undefined ||
|
|
275
282
|
platform === undefined ||
|
|
276
283
|
archiveUrl === undefined ||
|
|
277
284
|
archiveSha256 === undefined ||
|
|
278
|
-
codeServerVersion === undefined
|
|
285
|
+
codeServerVersion === undefined ||
|
|
286
|
+
assets === undefined
|
|
279
287
|
) {
|
|
280
288
|
return { ok: false };
|
|
281
289
|
}
|
|
@@ -295,6 +303,38 @@ function normalizeManifest(value: JsonObject):
|
|
|
295
303
|
};
|
|
296
304
|
}
|
|
297
305
|
|
|
306
|
+
function normalizeRuntimeAssets(value: unknown): readonly EditorRuntimeAsset[] | undefined {
|
|
307
|
+
if (!Array.isArray(value)) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const assets: EditorRuntimeAsset[] = [];
|
|
312
|
+
|
|
313
|
+
for (const asset of value) {
|
|
314
|
+
if (!isJsonObject(asset)) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const path = nonEmptyString(asset.path);
|
|
319
|
+
const sha256 = sha256String(asset.sha256);
|
|
320
|
+
const url = nonEmptyString(asset.url);
|
|
321
|
+
const contentBase64 = nonEmptyString(asset.contentBase64);
|
|
322
|
+
|
|
323
|
+
if (path === undefined || sha256 === undefined) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
assets.push({
|
|
328
|
+
path,
|
|
329
|
+
sha256,
|
|
330
|
+
...(url === undefined ? {} : { url }),
|
|
331
|
+
...(contentBase64 === undefined ? {} : { contentBase64 }),
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return assets;
|
|
336
|
+
}
|
|
337
|
+
|
|
298
338
|
function installedRuntime(
|
|
299
339
|
cacheRoot: string,
|
|
300
340
|
manifest: EditorRuntimeManifest,
|
|
@@ -348,7 +388,7 @@ async function installRuntime(args: {
|
|
|
348
388
|
> {
|
|
349
389
|
mkdirSync(args.cacheRoot, { recursive: true });
|
|
350
390
|
|
|
351
|
-
const tempRoot = mkdtempSync(join(
|
|
391
|
+
const tempRoot = mkdtempSync(join(args.cacheRoot, ".install-"));
|
|
352
392
|
const archivePath = join(tempRoot, "runtime.tar.gz");
|
|
353
393
|
const extractRoot = join(tempRoot, "runtime");
|
|
354
394
|
|
|
@@ -371,33 +411,37 @@ async function installRuntime(args: {
|
|
|
371
411
|
return { ok: false, reason: "archive_extract_failed" };
|
|
372
412
|
}
|
|
373
413
|
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
414
|
+
const assetsInstalled = await materializeRuntimeAssets({
|
|
415
|
+
kandanUrl: args.kandanUrl,
|
|
416
|
+
manifest: args.manifest,
|
|
417
|
+
runtimeRoot: extractRoot,
|
|
418
|
+
fetchImpl: args.fetchImpl,
|
|
419
|
+
});
|
|
377
420
|
|
|
378
|
-
if (!
|
|
379
|
-
return { ok: false, reason: "
|
|
421
|
+
if (!assetsInstalled) {
|
|
422
|
+
return { ok: false, reason: "install_failed" };
|
|
380
423
|
}
|
|
381
424
|
|
|
382
|
-
const
|
|
425
|
+
const manifestPath = join(extractRoot, args.manifest.manifestPath);
|
|
426
|
+
const codeServerBin = join(extractRoot, args.manifest.codeServerBinPath);
|
|
427
|
+
const assets = verifiedRuntimeAssetPaths(extractRoot, args.manifest);
|
|
383
428
|
|
|
384
|
-
if (
|
|
385
|
-
!isJsonObject(installed) ||
|
|
386
|
-
installed.version !== args.manifest.version ||
|
|
387
|
-
installed.platform !== args.manifest.platform ||
|
|
388
|
-
(installed.archiveSha256 !== undefined &&
|
|
389
|
-
installed.archiveSha256 !== args.manifest.archiveSha256)
|
|
390
|
-
) {
|
|
429
|
+
if (!existsSync(codeServerBin) || assets === undefined) {
|
|
391
430
|
return { ok: false, reason: "invalid_archive" };
|
|
392
431
|
}
|
|
393
432
|
|
|
433
|
+
mkdirSync(dirname(manifestPath), { recursive: true });
|
|
394
434
|
writeFileSync(
|
|
395
435
|
manifestPath,
|
|
396
436
|
JSON.stringify(
|
|
397
437
|
{
|
|
398
|
-
|
|
438
|
+
version: args.manifest.version,
|
|
439
|
+
platform: args.manifest.platform,
|
|
399
440
|
archiveSha256: args.manifest.archiveSha256,
|
|
400
441
|
codeServerVersion: args.manifest.codeServerVersion,
|
|
442
|
+
codeServerBinPath: args.manifest.codeServerBinPath,
|
|
443
|
+
manifestPath: args.manifest.manifestPath,
|
|
444
|
+
assets: args.manifest.assets,
|
|
401
445
|
},
|
|
402
446
|
null,
|
|
403
447
|
2,
|
|
@@ -444,6 +488,59 @@ async function installRuntime(args: {
|
|
|
444
488
|
}
|
|
445
489
|
}
|
|
446
490
|
|
|
491
|
+
async function materializeRuntimeAssets(args: {
|
|
492
|
+
readonly kandanUrl: string;
|
|
493
|
+
readonly manifest: EditorRuntimeManifest;
|
|
494
|
+
readonly runtimeRoot: string;
|
|
495
|
+
readonly fetchImpl: typeof fetch;
|
|
496
|
+
}): Promise<boolean> {
|
|
497
|
+
for (const asset of args.manifest.assets) {
|
|
498
|
+
const targetPath = join(args.runtimeRoot, asset.path);
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
const bytes = await runtimeAssetBytes({
|
|
502
|
+
kandanUrl: args.kandanUrl,
|
|
503
|
+
asset,
|
|
504
|
+
fetchImpl: args.fetchImpl,
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
if (bytes === undefined) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
512
|
+
writeFileSync(targetPath, bytes);
|
|
513
|
+
} catch (_error) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async function runtimeAssetBytes(args: {
|
|
522
|
+
readonly kandanUrl: string;
|
|
523
|
+
readonly asset: EditorRuntimeAsset;
|
|
524
|
+
readonly fetchImpl: typeof fetch;
|
|
525
|
+
}): Promise<Buffer | undefined> {
|
|
526
|
+
if (args.asset.contentBase64 !== undefined) {
|
|
527
|
+
return Buffer.from(args.asset.contentBase64, "base64");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (args.asset.url !== undefined) {
|
|
531
|
+
const url = new URL(args.asset.url, kandanHttpBaseUrl(args.kandanUrl));
|
|
532
|
+
const response = await args.fetchImpl(url);
|
|
533
|
+
|
|
534
|
+
if (response.status !== 200) {
|
|
535
|
+
return undefined;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return Buffer.from(await response.arrayBuffer());
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return undefined;
|
|
542
|
+
}
|
|
543
|
+
|
|
447
544
|
async function downloadArchive(args: {
|
|
448
545
|
readonly kandanUrl: string;
|
|
449
546
|
readonly token: string;
|
|
@@ -454,9 +551,12 @@ async function downloadArchive(args: {
|
|
|
454
551
|
| { readonly ok: true }
|
|
455
552
|
| { readonly ok: false; readonly reason: "download_failed" | "checksum_mismatch" }
|
|
456
553
|
> {
|
|
457
|
-
const
|
|
554
|
+
const kandanBaseUrl = kandanHttpBaseUrl(args.kandanUrl);
|
|
555
|
+
const url = new URL(args.manifest.archiveUrl, kandanBaseUrl);
|
|
458
556
|
const response = await args.fetchImpl(url, {
|
|
459
|
-
|
|
557
|
+
...(sameOrigin(url, new URL(kandanBaseUrl))
|
|
558
|
+
? { headers: { authorization: `Bearer ${args.token}` } }
|
|
559
|
+
: {}),
|
|
460
560
|
});
|
|
461
561
|
|
|
462
562
|
if (response.status !== 200 || response.body === null) {
|
|
@@ -477,6 +577,10 @@ async function downloadArchive(args: {
|
|
|
477
577
|
return { ok: true };
|
|
478
578
|
}
|
|
479
579
|
|
|
580
|
+
function sameOrigin(left: URL, right: URL): boolean {
|
|
581
|
+
return left.protocol === right.protocol && left.host === right.host;
|
|
582
|
+
}
|
|
583
|
+
|
|
480
584
|
function extractTarGz(archivePath: string, destination: string): Promise<boolean> {
|
|
481
585
|
return new Promise((resolveExtract) => {
|
|
482
586
|
const child = spawn("tar", ["-xzf", archivePath, "-C", destination], {
|
|
@@ -529,10 +633,11 @@ function verifiedRuntimeAssetPaths(
|
|
|
529
633
|
"kandan.document-state-telemetry",
|
|
530
634
|
);
|
|
531
635
|
|
|
636
|
+
const codeServerRoot = codeServerRuntimeRoot(manifest.codeServerBinPath);
|
|
532
637
|
const requiredPaths = [
|
|
533
638
|
manifest.codeServerBinPath,
|
|
534
|
-
"lib
|
|
535
|
-
"lib
|
|
639
|
+
join(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda.js"),
|
|
640
|
+
join(codeServerRoot, "lib", "vscode", "node_modules", "vsda", "rust", "web", "vsda_bg.wasm"),
|
|
536
641
|
"kandan/editor_extensions/typefox.open-collaboration-tools.tar.gz",
|
|
537
642
|
"kandan/editor_servers/open-collaboration-server.tar.gz",
|
|
538
643
|
"kandan/editor_extensions/kandan.document-state-telemetry/package.json",
|
|
@@ -543,13 +648,17 @@ function verifiedRuntimeAssetPaths(
|
|
|
543
648
|
for (const relativePath of requiredPaths) {
|
|
544
649
|
const expectedSha256 = assetChecksums.get(relativePath);
|
|
545
650
|
|
|
546
|
-
if (expectedSha256 === undefined) {
|
|
651
|
+
if (expectedSha256 === undefined && relativePath !== manifest.codeServerBinPath) {
|
|
547
652
|
return undefined;
|
|
548
653
|
}
|
|
549
654
|
|
|
550
655
|
const absolutePath = join(runtimeRoot, relativePath);
|
|
551
656
|
|
|
552
|
-
if (!existsSync(absolutePath)
|
|
657
|
+
if (!existsSync(absolutePath)) {
|
|
658
|
+
return undefined;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (expectedSha256 !== undefined && fileSha256Sync(absolutePath) !== expectedSha256) {
|
|
553
662
|
return undefined;
|
|
554
663
|
}
|
|
555
664
|
}
|
|
@@ -565,16 +674,21 @@ function verifiedRuntimeAssetPaths(
|
|
|
565
674
|
};
|
|
566
675
|
}
|
|
567
676
|
|
|
568
|
-
function
|
|
677
|
+
function codeServerRuntimeRoot(codeServerBinPath: string): string {
|
|
678
|
+
const normalized = codeServerBinPath.replaceAll("\\", "/");
|
|
679
|
+
|
|
680
|
+
return normalized === "bin/code-server"
|
|
681
|
+
? "."
|
|
682
|
+
: normalized.endsWith("/bin/code-server")
|
|
683
|
+
? normalized.slice(0, -"/bin/code-server".length) || "."
|
|
684
|
+
: dirname(normalized);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function manifestAssetChecksums(assets: readonly EditorRuntimeAsset[]): Map<string, string> {
|
|
569
688
|
const checksums = new Map<string, string>();
|
|
570
689
|
|
|
571
690
|
for (const asset of assets) {
|
|
572
|
-
|
|
573
|
-
const sha256 = sha256String(asset.sha256);
|
|
574
|
-
|
|
575
|
-
if (path !== undefined && sha256 !== undefined) {
|
|
576
|
-
checksums.set(path, sha256);
|
|
577
|
-
}
|
|
691
|
+
checksums.set(asset.path, asset.sha256);
|
|
578
692
|
}
|
|
579
693
|
|
|
580
694
|
return checksums;
|