@getmonoceros/workbench 1.27.0 → 1.29.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/dist/bin.js +208 -9
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2278,7 +2278,7 @@ var init_catalog = __esm({
|
|
|
2278
2278
|
override = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();
|
|
2279
2279
|
BASE_IMAGE = override && override.length > 0 ? override : DEFAULT_BASE_IMAGE;
|
|
2280
2280
|
RUNTIME_IMAGE_REPO = "ghcr.io/getmonoceros/monoceros-runtime";
|
|
2281
|
-
DEFAULT_RUNTIME_VERSION = true ? "1.2
|
|
2281
|
+
DEFAULT_RUNTIME_VERSION = true ? "1.3.2" : readFileSync3(
|
|
2282
2282
|
fileURLToPath2(
|
|
2283
2283
|
new URL("../../../../images/runtime/VERSION", import.meta.url)
|
|
2284
2284
|
),
|
|
@@ -2964,6 +2964,35 @@ function ideStateVolumes(name) {
|
|
|
2964
2964
|
volume: `monoceros-${name}-vscodium-userdata`,
|
|
2965
2965
|
target: "/home/node/.vscodium-server/data/User",
|
|
2966
2966
|
minRuntime: "1.2.0"
|
|
2967
|
+
},
|
|
2968
|
+
// JetBrains (ADR 0022). Only the backend DISTRIBUTION
|
|
2969
|
+
// (`~/.cache/JetBrains/RemoteDev/dist`, ~3 GB, identical across
|
|
2970
|
+
// containers) is shared machine-wide - downloaded once. The sibling
|
|
2971
|
+
// RemoteDev state (`active/`, `recent/`, `remote-dev-worker/`) is
|
|
2972
|
+
// per-user session state and stays per-container, inside the
|
|
2973
|
+
// per-container `~/.cache/JetBrains` volume; sharing it pooled every
|
|
2974
|
+
// container's recent-projects/active-sessions (the "wild" Gateway
|
|
2975
|
+
// list). The shared `dist` mount nests two levels into that volume.
|
|
2976
|
+
{
|
|
2977
|
+
volume: "monoceros-jetbrains-dist",
|
|
2978
|
+
target: "/home/node/.cache/JetBrains/RemoteDev/dist",
|
|
2979
|
+
minRuntime: "1.3.2",
|
|
2980
|
+
shared: true
|
|
2981
|
+
},
|
|
2982
|
+
{
|
|
2983
|
+
volume: `monoceros-${name}-jetbrains-cache`,
|
|
2984
|
+
target: "/home/node/.cache/JetBrains",
|
|
2985
|
+
minRuntime: "1.3.0"
|
|
2986
|
+
},
|
|
2987
|
+
{
|
|
2988
|
+
volume: `monoceros-${name}-jetbrains-config`,
|
|
2989
|
+
target: "/home/node/.config/JetBrains",
|
|
2990
|
+
minRuntime: "1.3.0"
|
|
2991
|
+
},
|
|
2992
|
+
{
|
|
2993
|
+
volume: `monoceros-${name}-jetbrains-data`,
|
|
2994
|
+
target: "/home/node/.local/share/JetBrains",
|
|
2995
|
+
minRuntime: "1.3.0"
|
|
2967
2996
|
}
|
|
2968
2997
|
];
|
|
2969
2998
|
}
|
|
@@ -5146,7 +5175,7 @@ var init_transform = __esm({
|
|
|
5146
5175
|
// src/devcontainer/ssh-attach.ts
|
|
5147
5176
|
import { spawn as spawn4 } from "child_process";
|
|
5148
5177
|
import { promises as fs10 } from "fs";
|
|
5149
|
-
import { existsSync as existsSync8 } from "fs";
|
|
5178
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
|
|
5150
5179
|
import os2 from "os";
|
|
5151
5180
|
import path13 from "path";
|
|
5152
5181
|
function sshHomeDir(home) {
|
|
@@ -5246,6 +5275,155 @@ ${existing}` : prefix;
|
|
|
5246
5275
|
await fs10.chmod(configPath, 384).catch(() => {
|
|
5247
5276
|
});
|
|
5248
5277
|
}
|
|
5278
|
+
function runCapture(cmd, args) {
|
|
5279
|
+
return new Promise((resolve, reject) => {
|
|
5280
|
+
const child = spawn4(cmd, args, {
|
|
5281
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
5282
|
+
});
|
|
5283
|
+
let stdout = "";
|
|
5284
|
+
child.stdout.on("data", (c) => {
|
|
5285
|
+
stdout += c.toString();
|
|
5286
|
+
});
|
|
5287
|
+
child.on("error", reject);
|
|
5288
|
+
child.on("exit", (code) => resolve({ stdout, exitCode: code ?? 0 }));
|
|
5289
|
+
});
|
|
5290
|
+
}
|
|
5291
|
+
function realIsWsl() {
|
|
5292
|
+
try {
|
|
5293
|
+
if (process.env.WSL_DISTRO_NAME) return true;
|
|
5294
|
+
const rel = readFileSync4(
|
|
5295
|
+
"/proc/sys/kernel/osrelease",
|
|
5296
|
+
"utf8"
|
|
5297
|
+
).toLowerCase();
|
|
5298
|
+
return rel.includes("microsoft") || rel.includes("wsl");
|
|
5299
|
+
} catch {
|
|
5300
|
+
return false;
|
|
5301
|
+
}
|
|
5302
|
+
}
|
|
5303
|
+
async function realResolveWindowsProfile() {
|
|
5304
|
+
try {
|
|
5305
|
+
const r = await runCapture("cmd.exe", ["/c", "echo %USERPROFILE%"]);
|
|
5306
|
+
const homeWin = r.stdout.replace(/\r/g, "").trim();
|
|
5307
|
+
if (r.exitCode !== 0 || !homeWin || homeWin.includes("%USERPROFILE%")) {
|
|
5308
|
+
return null;
|
|
5309
|
+
}
|
|
5310
|
+
const w = await runCapture("wslpath", ["-u", homeWin]);
|
|
5311
|
+
const homeWsl = w.stdout.trim();
|
|
5312
|
+
const user = homeWin.split("\\").pop() ?? "";
|
|
5313
|
+
if (w.exitCode !== 0 || !homeWsl || !user) return null;
|
|
5314
|
+
return { homeWsl, homeWin, user };
|
|
5315
|
+
} catch {
|
|
5316
|
+
return null;
|
|
5317
|
+
}
|
|
5318
|
+
}
|
|
5319
|
+
async function realLockWindowsKey(winKeyPath, user) {
|
|
5320
|
+
await runCapture("icacls.exe", [
|
|
5321
|
+
winKeyPath,
|
|
5322
|
+
"/inheritance:r",
|
|
5323
|
+
"/grant:r",
|
|
5324
|
+
`${user}:R`
|
|
5325
|
+
]);
|
|
5326
|
+
}
|
|
5327
|
+
function resolveWindowsDeps(d) {
|
|
5328
|
+
return {
|
|
5329
|
+
isWsl: d?.isWsl ?? realIsWsl,
|
|
5330
|
+
resolveProfile: d?.resolveProfile ?? realResolveWindowsProfile,
|
|
5331
|
+
lockKey: d?.lockKey ?? realLockWindowsKey
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
function windowsHostBlock(hostAlias, keyWin) {
|
|
5335
|
+
return [
|
|
5336
|
+
`Host ${hostAlias}`,
|
|
5337
|
+
` User node`,
|
|
5338
|
+
` IdentityFile ${keyWin}`,
|
|
5339
|
+
` IdentitiesOnly yes`,
|
|
5340
|
+
` StrictHostKeyChecking no`,
|
|
5341
|
+
` UserKnownHostsFile NUL`,
|
|
5342
|
+
` ProxyCommand docker exec -i ${hostAlias} socat - TCP:127.0.0.1:22`
|
|
5343
|
+
].join("\n");
|
|
5344
|
+
}
|
|
5345
|
+
function escapeRegExp2(s) {
|
|
5346
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5347
|
+
}
|
|
5348
|
+
function blockMarkers(hostAlias) {
|
|
5349
|
+
return {
|
|
5350
|
+
begin: `# >>> monoceros ${hostAlias} >>>`,
|
|
5351
|
+
end: `# <<< monoceros ${hostAlias} <<<`
|
|
5352
|
+
};
|
|
5353
|
+
}
|
|
5354
|
+
async function upsertMarkedBlock(configPath, hostAlias, body) {
|
|
5355
|
+
const { begin, end } = blockMarkers(hostAlias);
|
|
5356
|
+
const section = `${begin}
|
|
5357
|
+
${body}
|
|
5358
|
+
${end}`;
|
|
5359
|
+
await fs10.mkdir(path13.dirname(configPath), { recursive: true });
|
|
5360
|
+
let existing = "";
|
|
5361
|
+
try {
|
|
5362
|
+
existing = await fs10.readFile(configPath, "utf8");
|
|
5363
|
+
} catch {
|
|
5364
|
+
existing = "";
|
|
5365
|
+
}
|
|
5366
|
+
const re = new RegExp(`${escapeRegExp2(begin)}[\\s\\S]*?${escapeRegExp2(end)}`);
|
|
5367
|
+
if (re.test(existing)) {
|
|
5368
|
+
await fs10.writeFile(configPath, existing.replace(re, section));
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
const sep = existing.length === 0 || existing.endsWith("\n") ? "" : "\n";
|
|
5372
|
+
await fs10.writeFile(configPath, `${existing}${sep}${section}
|
|
5373
|
+
`);
|
|
5374
|
+
}
|
|
5375
|
+
async function removeMarkedBlock(configPath, hostAlias) {
|
|
5376
|
+
let existing = "";
|
|
5377
|
+
try {
|
|
5378
|
+
existing = await fs10.readFile(configPath, "utf8");
|
|
5379
|
+
} catch {
|
|
5380
|
+
return;
|
|
5381
|
+
}
|
|
5382
|
+
const { begin, end } = blockMarkers(hostAlias);
|
|
5383
|
+
const re = new RegExp(
|
|
5384
|
+
`\\n?${escapeRegExp2(begin)}[\\s\\S]*?${escapeRegExp2(end)}\\n?`,
|
|
5385
|
+
"g"
|
|
5386
|
+
);
|
|
5387
|
+
await fs10.writeFile(
|
|
5388
|
+
configPath,
|
|
5389
|
+
existing.replace(re, "\n").replace(/\n{3,}/g, "\n\n")
|
|
5390
|
+
);
|
|
5391
|
+
}
|
|
5392
|
+
async function setupWindowsBridge(name, hostAlias, privateKey, deps, logger) {
|
|
5393
|
+
if (!deps.isWsl()) return;
|
|
5394
|
+
const profile = await deps.resolveProfile();
|
|
5395
|
+
if (!profile) {
|
|
5396
|
+
logger.warn(
|
|
5397
|
+
"WSL detected but the Windows user profile could not be resolved; skipping the Windows SSH bridge."
|
|
5398
|
+
);
|
|
5399
|
+
return;
|
|
5400
|
+
}
|
|
5401
|
+
const monoDir = path13.join(profile.homeWsl, ".ssh", "monoceros");
|
|
5402
|
+
await fs10.mkdir(monoDir, { recursive: true });
|
|
5403
|
+
await fs10.copyFile(privateKey, path13.join(monoDir, name));
|
|
5404
|
+
const keyWin = `${profile.homeWin}\\.ssh\\monoceros\\${name}`;
|
|
5405
|
+
await upsertMarkedBlock(
|
|
5406
|
+
path13.join(profile.homeWsl, ".ssh", "config"),
|
|
5407
|
+
hostAlias,
|
|
5408
|
+
windowsHostBlock(hostAlias, keyWin)
|
|
5409
|
+
);
|
|
5410
|
+
await deps.lockKey(keyWin, profile.user);
|
|
5411
|
+
logger.info(
|
|
5412
|
+
`Windows SSH bridge ready: \`ssh ${hostAlias}\` (Codium/Gateway too).`
|
|
5413
|
+
);
|
|
5414
|
+
}
|
|
5415
|
+
async function removeWindowsBridge(name, hostAlias, deps) {
|
|
5416
|
+
if (!deps.isWsl()) return;
|
|
5417
|
+
const profile = await deps.resolveProfile();
|
|
5418
|
+
if (!profile) return;
|
|
5419
|
+
await fs10.rm(path13.join(profile.homeWsl, ".ssh", "monoceros", name), {
|
|
5420
|
+
force: true
|
|
5421
|
+
});
|
|
5422
|
+
await removeMarkedBlock(
|
|
5423
|
+
path13.join(profile.homeWsl, ".ssh", "config"),
|
|
5424
|
+
hostAlias
|
|
5425
|
+
);
|
|
5426
|
+
}
|
|
5249
5427
|
async function setupSshAttach(opts) {
|
|
5250
5428
|
const hostAlias = `monoceros-${opts.name}`;
|
|
5251
5429
|
const logger = opts.logger ?? { info: () => {
|
|
@@ -5271,11 +5449,32 @@ async function setupSshAttach(opts) {
|
|
|
5271
5449
|
configEntryContent(opts.name, hostAlias, keys.privateKey, proxyScript)
|
|
5272
5450
|
);
|
|
5273
5451
|
await ensureInclude(userSshDir, opts.home);
|
|
5452
|
+
try {
|
|
5453
|
+
await setupWindowsBridge(
|
|
5454
|
+
opts.name,
|
|
5455
|
+
hostAlias,
|
|
5456
|
+
keys.privateKey,
|
|
5457
|
+
resolveWindowsDeps(opts.windows),
|
|
5458
|
+
logger
|
|
5459
|
+
);
|
|
5460
|
+
} catch (err) {
|
|
5461
|
+
logger.warn(
|
|
5462
|
+
`Windows SSH bridge skipped: ${err instanceof Error ? err.message : String(err)}`
|
|
5463
|
+
);
|
|
5464
|
+
}
|
|
5274
5465
|
return { hostAlias, configured: true };
|
|
5275
5466
|
}
|
|
5276
|
-
async function removeSshAttach(home, name) {
|
|
5467
|
+
async function removeSshAttach(home, name, windows) {
|
|
5277
5468
|
await fs10.rm(sshProxyScriptPath(home, name), { force: true });
|
|
5278
5469
|
await fs10.rm(sshConfigEntryPath(home, name), { force: true });
|
|
5470
|
+
try {
|
|
5471
|
+
await removeWindowsBridge(
|
|
5472
|
+
name,
|
|
5473
|
+
`monoceros-${name}`,
|
|
5474
|
+
resolveWindowsDeps(windows)
|
|
5475
|
+
);
|
|
5476
|
+
} catch {
|
|
5477
|
+
}
|
|
5279
5478
|
}
|
|
5280
5479
|
var realKeygen;
|
|
5281
5480
|
var init_ssh_attach = __esm({
|
|
@@ -6352,13 +6551,13 @@ var init_runtime_pull_hint = __esm({
|
|
|
6352
6551
|
|
|
6353
6552
|
// src/devcontainer/cli.ts
|
|
6354
6553
|
import { spawn as spawn5 } from "child_process";
|
|
6355
|
-
import { readFileSync as
|
|
6554
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
6356
6555
|
import { createRequire } from "module";
|
|
6357
6556
|
import path16 from "path";
|
|
6358
6557
|
function devcontainerCliPath() {
|
|
6359
6558
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
6360
6559
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
6361
|
-
const pkg = JSON.parse(
|
|
6560
|
+
const pkg = JSON.parse(readFileSync5(pkgJsonPath, "utf8"));
|
|
6362
6561
|
const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.devcontainer ?? "";
|
|
6363
6562
|
if (!binEntry) {
|
|
6364
6563
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
@@ -7583,7 +7782,7 @@ var init_apply = __esm({
|
|
|
7583
7782
|
|
|
7584
7783
|
// src/devcontainer/browser-bridge.ts
|
|
7585
7784
|
import { spawn as spawn9 } from "child_process";
|
|
7586
|
-
import { existsSync as existsSync11, promises as fsp5, readFileSync as
|
|
7785
|
+
import { existsSync as existsSync11, promises as fsp5, readFileSync as readFileSync6 } from "fs";
|
|
7587
7786
|
import http from "http";
|
|
7588
7787
|
import path20 from "path";
|
|
7589
7788
|
function parseCallbackTarget(authUrl) {
|
|
@@ -7686,7 +7885,7 @@ exit 0
|
|
|
7686
7885
|
if (!existsSync11(urlFile)) return;
|
|
7687
7886
|
let content = "";
|
|
7688
7887
|
try {
|
|
7689
|
-
content =
|
|
7888
|
+
content = readFileSync6(urlFile, "utf8");
|
|
7690
7889
|
} catch {
|
|
7691
7890
|
return;
|
|
7692
7891
|
}
|
|
@@ -7869,7 +8068,7 @@ var CLI_VERSION;
|
|
|
7869
8068
|
var init_version = __esm({
|
|
7870
8069
|
"src/version.ts"() {
|
|
7871
8070
|
"use strict";
|
|
7872
|
-
CLI_VERSION = true ? "1.
|
|
8071
|
+
CLI_VERSION = true ? "1.29.0" : "dev";
|
|
7873
8072
|
}
|
|
7874
8073
|
});
|
|
7875
8074
|
|
|
@@ -9747,7 +9946,7 @@ async function runRemove(opts) {
|
|
|
9747
9946
|
logger,
|
|
9748
9947
|
exec: dockerExec
|
|
9749
9948
|
});
|
|
9750
|
-
const ideVolumes = ideStateVolumes(opts.name).map((v) => v.volume);
|
|
9949
|
+
const ideVolumes = ideStateVolumes(opts.name).filter((v) => !v.shared).map((v) => v.volume);
|
|
9751
9950
|
await dockerExec(["volume", "rm", "-f", ...ideVolumes]);
|
|
9752
9951
|
let backupPath = null;
|
|
9753
9952
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|