@getmonoceros/workbench 1.28.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 CHANGED
@@ -5175,7 +5175,7 @@ var init_transform = __esm({
5175
5175
  // src/devcontainer/ssh-attach.ts
5176
5176
  import { spawn as spawn4 } from "child_process";
5177
5177
  import { promises as fs10 } from "fs";
5178
- import { existsSync as existsSync8 } from "fs";
5178
+ import { existsSync as existsSync8, readFileSync as readFileSync4 } from "fs";
5179
5179
  import os2 from "os";
5180
5180
  import path13 from "path";
5181
5181
  function sshHomeDir(home) {
@@ -5275,6 +5275,155 @@ ${existing}` : prefix;
5275
5275
  await fs10.chmod(configPath, 384).catch(() => {
5276
5276
  });
5277
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
+ }
5278
5427
  async function setupSshAttach(opts) {
5279
5428
  const hostAlias = `monoceros-${opts.name}`;
5280
5429
  const logger = opts.logger ?? { info: () => {
@@ -5300,11 +5449,32 @@ async function setupSshAttach(opts) {
5300
5449
  configEntryContent(opts.name, hostAlias, keys.privateKey, proxyScript)
5301
5450
  );
5302
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
+ }
5303
5465
  return { hostAlias, configured: true };
5304
5466
  }
5305
- async function removeSshAttach(home, name) {
5467
+ async function removeSshAttach(home, name, windows) {
5306
5468
  await fs10.rm(sshProxyScriptPath(home, name), { force: true });
5307
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
+ }
5308
5478
  }
5309
5479
  var realKeygen;
5310
5480
  var init_ssh_attach = __esm({
@@ -6381,13 +6551,13 @@ var init_runtime_pull_hint = __esm({
6381
6551
 
6382
6552
  // src/devcontainer/cli.ts
6383
6553
  import { spawn as spawn5 } from "child_process";
6384
- import { readFileSync as readFileSync4 } from "fs";
6554
+ import { readFileSync as readFileSync5 } from "fs";
6385
6555
  import { createRequire } from "module";
6386
6556
  import path16 from "path";
6387
6557
  function devcontainerCliPath() {
6388
6558
  if (cachedBinaryPath) return cachedBinaryPath;
6389
6559
  const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
6390
- const pkg = JSON.parse(readFileSync4(pkgJsonPath, "utf8"));
6560
+ const pkg = JSON.parse(readFileSync5(pkgJsonPath, "utf8"));
6391
6561
  const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.devcontainer ?? "";
6392
6562
  if (!binEntry) {
6393
6563
  throw new Error("Could not resolve @devcontainers/cli bin entry.");
@@ -7612,7 +7782,7 @@ var init_apply = __esm({
7612
7782
 
7613
7783
  // src/devcontainer/browser-bridge.ts
7614
7784
  import { spawn as spawn9 } from "child_process";
7615
- import { existsSync as existsSync11, promises as fsp5, readFileSync as readFileSync5 } from "fs";
7785
+ import { existsSync as existsSync11, promises as fsp5, readFileSync as readFileSync6 } from "fs";
7616
7786
  import http from "http";
7617
7787
  import path20 from "path";
7618
7788
  function parseCallbackTarget(authUrl) {
@@ -7715,7 +7885,7 @@ exit 0
7715
7885
  if (!existsSync11(urlFile)) return;
7716
7886
  let content = "";
7717
7887
  try {
7718
- content = readFileSync5(urlFile, "utf8");
7888
+ content = readFileSync6(urlFile, "utf8");
7719
7889
  } catch {
7720
7890
  return;
7721
7891
  }
@@ -7898,7 +8068,7 @@ var CLI_VERSION;
7898
8068
  var init_version = __esm({
7899
8069
  "src/version.ts"() {
7900
8070
  "use strict";
7901
- CLI_VERSION = true ? "1.28.0" : "dev";
8071
+ CLI_VERSION = true ? "1.29.0" : "dev";
7902
8072
  }
7903
8073
  });
7904
8074