@getmonoceros/workbench 1.18.0 → 1.19.1
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 +192 -59
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1903,6 +1903,41 @@ function curatedServiceEnvDefaults(name) {
|
|
|
1903
1903
|
const def = SERVICE_CATALOG[name];
|
|
1904
1904
|
return def?.env ? { ...def.env } : {};
|
|
1905
1905
|
}
|
|
1906
|
+
function serviceConnectionEnv(services) {
|
|
1907
|
+
const env = {};
|
|
1908
|
+
for (const svc of services) {
|
|
1909
|
+
if (!isCuratedService(svc.name)) continue;
|
|
1910
|
+
const host = svc.name;
|
|
1911
|
+
if (svc.name === "postgres") {
|
|
1912
|
+
const user = svc.env.POSTGRES_USER ?? "postgres";
|
|
1913
|
+
const pass = svc.env.POSTGRES_PASSWORD ?? "";
|
|
1914
|
+
const db = svc.env.POSTGRES_DB ?? user;
|
|
1915
|
+
const port = svc.port ?? 5432;
|
|
1916
|
+
env.PGHOST = host;
|
|
1917
|
+
env.PGPORT = String(port);
|
|
1918
|
+
env.PGUSER = user;
|
|
1919
|
+
env.PGPASSWORD = pass;
|
|
1920
|
+
env.PGDATABASE = db;
|
|
1921
|
+
env.DATABASE_URL = `postgresql://${user}:${pass}@${host}:${port}/${db}`;
|
|
1922
|
+
} else if (svc.name === "mysql") {
|
|
1923
|
+
const pass = svc.env.MYSQL_ROOT_PASSWORD ?? "";
|
|
1924
|
+
const db = svc.env.MYSQL_DATABASE ?? "";
|
|
1925
|
+
const port = svc.port ?? 3306;
|
|
1926
|
+
env.MYSQL_HOST = host;
|
|
1927
|
+
env.MYSQL_PORT = String(port);
|
|
1928
|
+
env.MYSQL_USER = "root";
|
|
1929
|
+
env.MYSQL_PASSWORD = pass;
|
|
1930
|
+
env.MYSQL_DATABASE = db;
|
|
1931
|
+
if (env.DATABASE_URL === void 0) {
|
|
1932
|
+
env.DATABASE_URL = `mysql://root:${pass}@${host}:${port}/${db}`;
|
|
1933
|
+
}
|
|
1934
|
+
} else if (svc.name === "redis") {
|
|
1935
|
+
const port = svc.port ?? 6379;
|
|
1936
|
+
env.REDIS_URL = `redis://${host}:${port}`;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
return env;
|
|
1940
|
+
}
|
|
1906
1941
|
function deriveServiceName(image) {
|
|
1907
1942
|
const lastSegment = image.split("/").pop() ?? image;
|
|
1908
1943
|
const noTag = lastSegment.split("@")[0].split(":")[0];
|
|
@@ -2438,6 +2473,14 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
2438
2473
|
for (const v of ideVolumes) {
|
|
2439
2474
|
lines.push(` - ${v.volume}:${v.target}`);
|
|
2440
2475
|
}
|
|
2476
|
+
const wsEnv = serviceConnectionEnv(opts.services);
|
|
2477
|
+
const wsEnvKeys = Object.keys(wsEnv);
|
|
2478
|
+
if (wsEnvKeys.length > 0) {
|
|
2479
|
+
lines.push(" environment:");
|
|
2480
|
+
for (const k of wsEnvKeys) {
|
|
2481
|
+
lines.push(` ${k}: ${composeScalar(wsEnv[k])}`);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2441
2484
|
for (const svc of opts.services) {
|
|
2442
2485
|
lines.push(` ${svc.name}:`);
|
|
2443
2486
|
lines.push(` image: ${svc.image}`);
|
|
@@ -3216,8 +3259,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
|
|
|
3216
3259
|
if (!isMap2(item)) return false;
|
|
3217
3260
|
const url = item.get("url");
|
|
3218
3261
|
if (url === urlOrPath) return true;
|
|
3219
|
-
const
|
|
3220
|
-
const effectivePath = typeof
|
|
3262
|
+
const path24 = item.get("path");
|
|
3263
|
+
const effectivePath = typeof path24 === "string" ? path24 : typeof url === "string" ? deriveRepoName(url) : void 0;
|
|
3221
3264
|
return effectivePath === urlOrPath;
|
|
3222
3265
|
});
|
|
3223
3266
|
if (idx < 0) return false;
|
|
@@ -3329,7 +3372,7 @@ async function runAddRepo(input) {
|
|
|
3329
3372
|
"Missing repo URL. Usage: monoceros add-repo <containername> <url>."
|
|
3330
3373
|
);
|
|
3331
3374
|
}
|
|
3332
|
-
const
|
|
3375
|
+
const path24 = (input.path ?? deriveRepoName(url)).trim();
|
|
3333
3376
|
const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
|
|
3334
3377
|
const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
|
|
3335
3378
|
if (hasName !== hasEmail) {
|
|
@@ -3366,7 +3409,7 @@ async function runAddRepo(input) {
|
|
|
3366
3409
|
const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
|
|
3367
3410
|
const entry2 = {
|
|
3368
3411
|
url,
|
|
3369
|
-
path:
|
|
3412
|
+
path: path24,
|
|
3370
3413
|
...hasName && hasEmail ? {
|
|
3371
3414
|
gitUser: {
|
|
3372
3415
|
name: input.gitName.trim(),
|
|
@@ -4767,22 +4810,42 @@ function generateAgentsMd(input) {
|
|
|
4767
4810
|
lines.push(formatServiceLine(svc));
|
|
4768
4811
|
}
|
|
4769
4812
|
lines.push("");
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4813
|
+
const connEnv = serviceConnectionEnv(input.services);
|
|
4814
|
+
if (Object.keys(connEnv).length > 0) {
|
|
4815
|
+
lines.push(
|
|
4816
|
+
"Connection details for the curated services above are already set as",
|
|
4817
|
+
"environment variables in this container. Read them from the",
|
|
4818
|
+
"environment \u2014 do not ask the user for credentials, and do not",
|
|
4819
|
+
"hardcode them:"
|
|
4820
|
+
);
|
|
4821
|
+
lines.push("");
|
|
4822
|
+
if (connEnv.DATABASE_URL !== void 0) {
|
|
4823
|
+
lines.push(
|
|
4824
|
+
"- `DATABASE_URL` \u2014 the SQL database. Engine-specific variables are",
|
|
4825
|
+
" set too (`PGHOST`/`PGPORT`/`PGUSER`/`PGPASSWORD`/`PGDATABASE` for",
|
|
4826
|
+
" Postgres, `MYSQL_*` for MySQL)."
|
|
4827
|
+
);
|
|
4828
|
+
}
|
|
4829
|
+
if (connEnv.REDIS_URL !== void 0) {
|
|
4830
|
+
lines.push("- `REDIS_URL` \u2014 Redis.");
|
|
4831
|
+
}
|
|
4832
|
+
lines.push("");
|
|
4833
|
+
lines.push(
|
|
4834
|
+
"These are dev-only defaults for the local container, fine to use",
|
|
4835
|
+
"directly. Prefer reading the variable (e.g. `process.env.DATABASE_URL`)",
|
|
4836
|
+
"over copying its value into code."
|
|
4837
|
+
);
|
|
4838
|
+
}
|
|
4778
4839
|
const hasCustom = input.services.some((s) => !isCuratedService(s.name));
|
|
4779
4840
|
if (hasCustom) {
|
|
4780
4841
|
lines.push("");
|
|
4781
4842
|
lines.push(
|
|
4782
4843
|
"For custom-image services, Monoceros does not know the service's",
|
|
4783
|
-
"configuration (env vars, ports beyond the primary one,
|
|
4784
|
-
"volumes). Treat such a service as a black box reachable at
|
|
4785
|
-
"listed address; if you need
|
|
4844
|
+
"configuration or credentials (env vars, ports beyond the primary one,",
|
|
4845
|
+
"required volumes). Treat such a service as a black box reachable at",
|
|
4846
|
+
"the listed address; if you need to connect, ask the user in the",
|
|
4847
|
+
"current chat. Do not commit credentials into the repo \u2014 they belong",
|
|
4848
|
+
"in the user's `.env` on the host."
|
|
4786
4849
|
);
|
|
4787
4850
|
}
|
|
4788
4851
|
lines.push("");
|
|
@@ -4866,6 +4929,54 @@ function generateAgentsMd(input) {
|
|
|
4866
4929
|
" DataGrip) to one of the services."
|
|
4867
4930
|
);
|
|
4868
4931
|
lines.push("");
|
|
4932
|
+
if (input.ports.length > 0) {
|
|
4933
|
+
lines.push("## Running a long-running server");
|
|
4934
|
+
lines.push("");
|
|
4935
|
+
lines.push(
|
|
4936
|
+
"When you build something that serves on a port (a web app, an API),",
|
|
4937
|
+
"start it as a **detached** background process so it keeps running after",
|
|
4938
|
+
"this session ends. A plain `npm start` (or any foreground start) dies",
|
|
4939
|
+
"the moment the user exits you or closes the terminal \u2014 and then",
|
|
4940
|
+
`\`${input.containerName}.localhost\` returns 502 Bad Gateway.`
|
|
4941
|
+
);
|
|
4942
|
+
lines.push("");
|
|
4943
|
+
lines.push(
|
|
4944
|
+
"Launch it in a new session with `setsid`, using the project's own",
|
|
4945
|
+
"start command, and record the process-group PID + log under the",
|
|
4946
|
+
"container's logs directory:"
|
|
4947
|
+
);
|
|
4948
|
+
lines.push("");
|
|
4949
|
+
lines.push("```");
|
|
4950
|
+
lines.push(
|
|
4951
|
+
`setsid sh -c 'echo $$ >/workspaces/${input.containerName}/logs/<app>.pid; \\`
|
|
4952
|
+
);
|
|
4953
|
+
lines.push(
|
|
4954
|
+
` exec <the project's start command> >/workspaces/${input.containerName}/logs/<app>.log 2>&1' </dev/null &`
|
|
4955
|
+
);
|
|
4956
|
+
lines.push("```");
|
|
4957
|
+
lines.push("");
|
|
4958
|
+
lines.push(
|
|
4959
|
+
"Use whatever start command the project actually uses (`npm start`,",
|
|
4960
|
+
"`./mvnw spring-boot:run`, `python app.py`, `go run .`, \u2026) \u2014 do not force",
|
|
4961
|
+
"a language-specific one. `<app>` is a short name you choose."
|
|
4962
|
+
);
|
|
4963
|
+
lines.push("");
|
|
4964
|
+
lines.push("To stop it, kill the whole process group (also stops children");
|
|
4965
|
+
lines.push("like node under npm or java under maven):");
|
|
4966
|
+
lines.push("");
|
|
4967
|
+
lines.push("```");
|
|
4968
|
+
lines.push(
|
|
4969
|
+
`kill -TERM -$(cat /workspaces/${input.containerName}/logs/<app>.pid)`
|
|
4970
|
+
);
|
|
4971
|
+
lines.push("```");
|
|
4972
|
+
lines.push("");
|
|
4973
|
+
lines.push(
|
|
4974
|
+
`The user can follow the output with \`monoceros logs ${input.containerName} <app>\``,
|
|
4975
|
+
"on the host. The server must listen on `0.0.0.0` (not `127.0.0.1`) on",
|
|
4976
|
+
"the exposed port, or Traefik cannot reach it."
|
|
4977
|
+
);
|
|
4978
|
+
lines.push("");
|
|
4979
|
+
}
|
|
4869
4980
|
lines.push("## Command reference");
|
|
4870
4981
|
lines.push("");
|
|
4871
4982
|
lines.push(
|
|
@@ -5373,10 +5484,12 @@ var init_cli = __esm({
|
|
|
5373
5484
|
cachedBinaryPath = null;
|
|
5374
5485
|
spawnDevcontainer = (args, cwd, options = {}) => {
|
|
5375
5486
|
const binPath = devcontainerCliPath();
|
|
5487
|
+
const env = { ...process.env, DOCKER_CLI_HINTS: "false" };
|
|
5376
5488
|
return new Promise((resolve, reject) => {
|
|
5377
5489
|
if (options.interactive) {
|
|
5378
5490
|
const child2 = spawn4(process.execPath, [binPath, ...args], {
|
|
5379
5491
|
cwd,
|
|
5492
|
+
env,
|
|
5380
5493
|
stdio: "inherit"
|
|
5381
5494
|
});
|
|
5382
5495
|
child2.on("error", reject);
|
|
@@ -5385,6 +5498,7 @@ var init_cli = __esm({
|
|
|
5385
5498
|
}
|
|
5386
5499
|
const child = spawn4(process.execPath, [binPath, ...args], {
|
|
5387
5500
|
cwd,
|
|
5501
|
+
env,
|
|
5388
5502
|
stdio: ["ignore", "pipe", "pipe"]
|
|
5389
5503
|
});
|
|
5390
5504
|
if (options.quiet) {
|
|
@@ -6293,7 +6407,7 @@ var CLI_VERSION;
|
|
|
6293
6407
|
var init_version = __esm({
|
|
6294
6408
|
"src/version.ts"() {
|
|
6295
6409
|
"use strict";
|
|
6296
|
-
CLI_VERSION = true ? "1.
|
|
6410
|
+
CLI_VERSION = true ? "1.19.1" : "dev";
|
|
6297
6411
|
}
|
|
6298
6412
|
});
|
|
6299
6413
|
|
|
@@ -7931,7 +8045,6 @@ exit 0
|
|
|
7931
8045
|
const servers = [];
|
|
7932
8046
|
let handledUrl = false;
|
|
7933
8047
|
const onAuthUrl = (authUrl) => {
|
|
7934
|
-
consola16.info("Opening the Claude sign-in page in your browser\u2026");
|
|
7935
8048
|
openInBrowser(authUrl);
|
|
7936
8049
|
const target = parseCallbackTarget(authUrl);
|
|
7937
8050
|
if (!target) {
|
|
@@ -7977,9 +8090,7 @@ exit 0
|
|
|
7977
8090
|
handledUrl = true;
|
|
7978
8091
|
onAuthUrl(content.trim());
|
|
7979
8092
|
}, 250);
|
|
7980
|
-
consola16.info(
|
|
7981
|
-
"Starting Claude login. Pick your account in Claude's menu \u2014 the browser opens for you, no copying."
|
|
7982
|
-
);
|
|
8093
|
+
consola16.info("Logging in to Claude \u2014 a browser window will open for you.");
|
|
7983
8094
|
const child = spawn8(
|
|
7984
8095
|
process.execPath,
|
|
7985
8096
|
[
|
|
@@ -7990,9 +8101,13 @@ exit 0
|
|
|
7990
8101
|
"--mount-workspace-git-root=false",
|
|
7991
8102
|
"bash",
|
|
7992
8103
|
"-lc",
|
|
7993
|
-
`export PATH="/workspaces/${name}/${RELAY_DIRNAME}:$PATH"; exec claude`
|
|
8104
|
+
`export PATH="/workspaces/${name}/${RELAY_DIRNAME}:$PATH"; exec claude auth login`
|
|
7994
8105
|
],
|
|
7995
|
-
{
|
|
8106
|
+
{
|
|
8107
|
+
cwd: root,
|
|
8108
|
+
env: { ...process.env, DOCKER_CLI_HINTS: "false" },
|
|
8109
|
+
stdio: "inherit"
|
|
8110
|
+
}
|
|
7996
8111
|
);
|
|
7997
8112
|
const code = await new Promise((resolve) => {
|
|
7998
8113
|
child.on("error", () => resolve(1));
|
|
@@ -8064,7 +8179,18 @@ var init_login2 = __esm({
|
|
|
8064
8179
|
});
|
|
8065
8180
|
|
|
8066
8181
|
// src/commands/logs.ts
|
|
8182
|
+
import { spawn as spawn9 } from "child_process";
|
|
8183
|
+
import { existsSync as existsSync12 } from "fs";
|
|
8184
|
+
import path20 from "path";
|
|
8067
8185
|
import { defineCommand as defineCommand14 } from "citty";
|
|
8186
|
+
function tailLogFile(file, follow) {
|
|
8187
|
+
const [cmd, args] = follow ? ["tail", ["-F", file]] : ["cat", [file]];
|
|
8188
|
+
return new Promise((resolve, reject) => {
|
|
8189
|
+
const child = spawn9(cmd, args, { stdio: "inherit" });
|
|
8190
|
+
child.on("error", reject);
|
|
8191
|
+
child.on("exit", (code) => resolve(code ?? 0));
|
|
8192
|
+
});
|
|
8193
|
+
}
|
|
8068
8194
|
var logsCommand;
|
|
8069
8195
|
var init_logs = __esm({
|
|
8070
8196
|
"src/commands/logs.ts"() {
|
|
@@ -8076,7 +8202,7 @@ var init_logs = __esm({
|
|
|
8076
8202
|
meta: {
|
|
8077
8203
|
name: "logs",
|
|
8078
8204
|
group: "run",
|
|
8079
|
-
description: "Tail logs from
|
|
8205
|
+
description: "Tail logs from a compose service of the named dev-container, or from a long-running app started inside it (logs/<app>.log). Pass --no-follow for a one-shot dump."
|
|
8080
8206
|
},
|
|
8081
8207
|
args: {
|
|
8082
8208
|
name: {
|
|
@@ -8086,7 +8212,7 @@ var init_logs = __esm({
|
|
|
8086
8212
|
},
|
|
8087
8213
|
service: {
|
|
8088
8214
|
type: "string",
|
|
8089
|
-
description: "
|
|
8215
|
+
description: "A compose service (e.g. postgres) or an app whose log is at logs/<service>.log. Defaults to all compose services."
|
|
8090
8216
|
},
|
|
8091
8217
|
follow: {
|
|
8092
8218
|
type: "boolean",
|
|
@@ -8096,10 +8222,17 @@ var init_logs = __esm({
|
|
|
8096
8222
|
}
|
|
8097
8223
|
},
|
|
8098
8224
|
run({ args }) {
|
|
8225
|
+
const service = typeof args.service === "string" ? args.service : void 0;
|
|
8226
|
+
if (service) {
|
|
8227
|
+
const logFile = path20.join(containerLogsDir(args.name), `${service}.log`);
|
|
8228
|
+
if (existsSync12(logFile)) {
|
|
8229
|
+
return dispatch(() => tailLogFile(logFile, args.follow));
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8099
8232
|
return dispatch(
|
|
8100
8233
|
() => runLogs({
|
|
8101
8234
|
root: containerDir(args.name),
|
|
8102
|
-
...
|
|
8235
|
+
...service ? { service } : {},
|
|
8103
8236
|
follow: args.follow
|
|
8104
8237
|
})
|
|
8105
8238
|
);
|
|
@@ -8299,8 +8432,8 @@ var init_remove_feature = __esm({
|
|
|
8299
8432
|
});
|
|
8300
8433
|
|
|
8301
8434
|
// src/remove/index.ts
|
|
8302
|
-
import { existsSync as
|
|
8303
|
-
import
|
|
8435
|
+
import { existsSync as existsSync13, promises as fs15 } from "fs";
|
|
8436
|
+
import path21 from "path";
|
|
8304
8437
|
import { consola as consola21 } from "consola";
|
|
8305
8438
|
async function runRemove(opts) {
|
|
8306
8439
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -8317,9 +8450,9 @@ async function runRemove(opts) {
|
|
|
8317
8450
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
8318
8451
|
const envPath = containerEnvPath(opts.name, home);
|
|
8319
8452
|
const containerPath = containerDir(opts.name, home);
|
|
8320
|
-
const hasYml =
|
|
8321
|
-
const hasEnv =
|
|
8322
|
-
const hasContainer =
|
|
8453
|
+
const hasYml = existsSync13(ymlPath);
|
|
8454
|
+
const hasEnv = existsSync13(envPath);
|
|
8455
|
+
const hasContainer = existsSync13(containerPath);
|
|
8323
8456
|
if (!hasYml && !hasContainer) {
|
|
8324
8457
|
throw new Error(
|
|
8325
8458
|
`Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
|
|
@@ -8345,16 +8478,16 @@ async function runRemove(opts) {
|
|
|
8345
8478
|
let backupPath = null;
|
|
8346
8479
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
8347
8480
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8348
|
-
backupPath =
|
|
8481
|
+
backupPath = path21.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
8349
8482
|
await fs15.mkdir(backupPath, { recursive: true });
|
|
8350
8483
|
if (hasYml) {
|
|
8351
|
-
await fs15.copyFile(ymlPath,
|
|
8484
|
+
await fs15.copyFile(ymlPath, path21.join(backupPath, `${opts.name}.yml`));
|
|
8352
8485
|
}
|
|
8353
8486
|
if (hasEnv) {
|
|
8354
|
-
await fs15.copyFile(envPath,
|
|
8487
|
+
await fs15.copyFile(envPath, path21.join(backupPath, `${opts.name}.env`));
|
|
8355
8488
|
}
|
|
8356
8489
|
if (hasContainer) {
|
|
8357
|
-
await fs15.cp(containerPath,
|
|
8490
|
+
await fs15.cp(containerPath, path21.join(backupPath, "container"), {
|
|
8358
8491
|
recursive: true
|
|
8359
8492
|
});
|
|
8360
8493
|
}
|
|
@@ -8514,8 +8647,8 @@ var init_remove2 = __esm({
|
|
|
8514
8647
|
});
|
|
8515
8648
|
|
|
8516
8649
|
// src/restore/index.ts
|
|
8517
|
-
import { existsSync as
|
|
8518
|
-
import
|
|
8650
|
+
import { existsSync as existsSync14, promises as fs16 } from "fs";
|
|
8651
|
+
import path22 from "path";
|
|
8519
8652
|
import { consola as consola23 } from "consola";
|
|
8520
8653
|
async function runRestore(opts) {
|
|
8521
8654
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -8523,8 +8656,8 @@ async function runRestore(opts) {
|
|
|
8523
8656
|
info: (msg) => consola23.info(msg),
|
|
8524
8657
|
success: (msg) => consola23.success(msg)
|
|
8525
8658
|
};
|
|
8526
|
-
const backup =
|
|
8527
|
-
if (!
|
|
8659
|
+
const backup = path22.resolve(opts.backupPath);
|
|
8660
|
+
if (!existsSync14(backup)) {
|
|
8528
8661
|
throw new Error(`Backup not found: ${backup}.`);
|
|
8529
8662
|
}
|
|
8530
8663
|
const stat = await fs16.stat(backup);
|
|
@@ -8545,24 +8678,24 @@ async function runRestore(opts) {
|
|
|
8545
8678
|
}
|
|
8546
8679
|
const ymlFile = ymlFiles[0];
|
|
8547
8680
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
8548
|
-
const containerInBackup =
|
|
8549
|
-
const hasContainer =
|
|
8550
|
-
const envInBackup =
|
|
8551
|
-
const hasEnv =
|
|
8681
|
+
const containerInBackup = path22.join(backup, "container");
|
|
8682
|
+
const hasContainer = existsSync14(containerInBackup);
|
|
8683
|
+
const envInBackup = path22.join(backup, `${name}.env`);
|
|
8684
|
+
const hasEnv = existsSync14(envInBackup);
|
|
8552
8685
|
const destYml = containerConfigPath(name, home);
|
|
8553
8686
|
const destContainer = containerDir(name, home);
|
|
8554
|
-
if (
|
|
8687
|
+
if (existsSync14(destYml)) {
|
|
8555
8688
|
throw new Error(
|
|
8556
8689
|
`Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
|
|
8557
8690
|
);
|
|
8558
8691
|
}
|
|
8559
|
-
if (hasContainer &&
|
|
8692
|
+
if (hasContainer && existsSync14(destContainer)) {
|
|
8560
8693
|
throw new Error(
|
|
8561
8694
|
`Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
|
|
8562
8695
|
);
|
|
8563
8696
|
}
|
|
8564
8697
|
await fs16.mkdir(containerConfigsDir(home), { recursive: true });
|
|
8565
|
-
await fs16.copyFile(
|
|
8698
|
+
await fs16.copyFile(path22.join(backup, ymlFile), destYml);
|
|
8566
8699
|
if (hasEnv) {
|
|
8567
8700
|
await fs16.copyFile(envInBackup, containerEnvPath(name, home));
|
|
8568
8701
|
}
|
|
@@ -9143,11 +9276,11 @@ var init_stop = __esm({
|
|
|
9143
9276
|
});
|
|
9144
9277
|
|
|
9145
9278
|
// src/tunnel/resolve.ts
|
|
9146
|
-
import { existsSync as
|
|
9147
|
-
import
|
|
9279
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9280
|
+
import path23 from "path";
|
|
9148
9281
|
async function resolveTunnelTarget(opts) {
|
|
9149
9282
|
const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
|
|
9150
|
-
if (!
|
|
9283
|
+
if (!existsSync15(ymlPath)) {
|
|
9151
9284
|
throw new Error(
|
|
9152
9285
|
`No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
|
|
9153
9286
|
);
|
|
@@ -9155,13 +9288,13 @@ async function resolveTunnelTarget(opts) {
|
|
|
9155
9288
|
const parsed = await readConfig(ymlPath);
|
|
9156
9289
|
const config = parsed.config;
|
|
9157
9290
|
const containerRoot = containerDir(opts.name, opts.monocerosHome);
|
|
9158
|
-
if (!
|
|
9291
|
+
if (!existsSync15(containerRoot)) {
|
|
9159
9292
|
throw new Error(
|
|
9160
9293
|
`Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
|
|
9161
9294
|
);
|
|
9162
9295
|
}
|
|
9163
|
-
const composePath =
|
|
9164
|
-
const isCompose =
|
|
9296
|
+
const composePath = path23.join(containerRoot, ".devcontainer", "compose.yaml");
|
|
9297
|
+
const isCompose = existsSync15(composePath);
|
|
9165
9298
|
const parsedTarget = parseTargetArg(opts.target, config);
|
|
9166
9299
|
const docker = opts.docker ?? defaultDockerExec;
|
|
9167
9300
|
if (isCompose) {
|
|
@@ -9396,7 +9529,7 @@ var init_port_check2 = __esm({
|
|
|
9396
9529
|
});
|
|
9397
9530
|
|
|
9398
9531
|
// src/tunnel/run.ts
|
|
9399
|
-
import { spawn as
|
|
9532
|
+
import { spawn as spawn10 } from "child_process";
|
|
9400
9533
|
import { consola as consola34 } from "consola";
|
|
9401
9534
|
function signalNumber(signal) {
|
|
9402
9535
|
switch (signal) {
|
|
@@ -9486,7 +9619,7 @@ var init_run3 = __esm({
|
|
|
9486
9619
|
init_port_check2();
|
|
9487
9620
|
SOCAT_IMAGE = "alpine/socat:1.8.0.3";
|
|
9488
9621
|
defaultDockerSpawn = (args) => {
|
|
9489
|
-
const child =
|
|
9622
|
+
const child = spawn10("docker", args, {
|
|
9490
9623
|
stdio: "inherit"
|
|
9491
9624
|
});
|
|
9492
9625
|
const exited = new Promise((resolve, reject) => {
|
|
@@ -9580,7 +9713,7 @@ var init_tunnel = __esm({
|
|
|
9580
9713
|
});
|
|
9581
9714
|
|
|
9582
9715
|
// src/upgrade/index.ts
|
|
9583
|
-
import { existsSync as
|
|
9716
|
+
import { existsSync as existsSync16, promises as fs17 } from "fs";
|
|
9584
9717
|
import { consola as consola36 } from "consola";
|
|
9585
9718
|
async function fetchRuntimeVersions() {
|
|
9586
9719
|
const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
|
|
@@ -9639,7 +9772,7 @@ async function runUpgrade(opts) {
|
|
|
9639
9772
|
);
|
|
9640
9773
|
}
|
|
9641
9774
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
9642
|
-
if (!
|
|
9775
|
+
if (!existsSync16(ymlPath)) {
|
|
9643
9776
|
throw new Error(
|
|
9644
9777
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
9645
9778
|
);
|
|
@@ -10071,25 +10204,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
10071
10204
|
const separatorIdx = argv.indexOf("--");
|
|
10072
10205
|
if (helpIdx === -1) return null;
|
|
10073
10206
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
10074
|
-
const
|
|
10207
|
+
const path24 = [];
|
|
10075
10208
|
const tokens = argv.slice(
|
|
10076
10209
|
0,
|
|
10077
10210
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
10078
10211
|
);
|
|
10079
10212
|
let cursor = main2;
|
|
10080
10213
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
10081
|
-
|
|
10214
|
+
path24.push(mainName);
|
|
10082
10215
|
for (const tok of tokens) {
|
|
10083
10216
|
if (tok.startsWith("-")) continue;
|
|
10084
10217
|
const subs = cursor.subCommands ?? {};
|
|
10085
10218
|
if (tok in subs) {
|
|
10086
10219
|
cursor = subs[tok];
|
|
10087
|
-
|
|
10220
|
+
path24.push(tok);
|
|
10088
10221
|
continue;
|
|
10089
10222
|
}
|
|
10090
10223
|
break;
|
|
10091
10224
|
}
|
|
10092
|
-
return { path:
|
|
10225
|
+
return { path: path24, cmd: cursor };
|
|
10093
10226
|
}
|
|
10094
10227
|
async function maybeRenderHelp(argv, main2) {
|
|
10095
10228
|
const hit = detectHelpRequest(argv, main2);
|