@getmonoceros/workbench 1.26.5 → 1.28.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 +963 -494
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -2146,9 +2146,9 @@ function compareRuntimeVersions(a, b) {
|
|
|
2146
2146
|
}
|
|
2147
2147
|
return 0;
|
|
2148
2148
|
}
|
|
2149
|
-
function
|
|
2149
|
+
function runtimeSupportsSshAttach(version) {
|
|
2150
2150
|
if (!version) return false;
|
|
2151
|
-
return compareRuntimeVersions(version,
|
|
2151
|
+
return compareRuntimeVersions(version, MIN_RUNTIME_FOR_SSH_ATTACH) >= 0;
|
|
2152
2152
|
}
|
|
2153
2153
|
function descriptorOptionDefaults(options) {
|
|
2154
2154
|
const out = {};
|
|
@@ -2269,7 +2269,7 @@ function deriveServiceName(image) {
|
|
|
2269
2269
|
const noTag = lastSegment.split("@")[0].split(":")[0];
|
|
2270
2270
|
return noTag.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
2271
2271
|
}
|
|
2272
|
-
var DEFAULT_BASE_IMAGE, override, BASE_IMAGE, RUNTIME_IMAGE_REPO, DEFAULT_RUNTIME_VERSION,
|
|
2272
|
+
var DEFAULT_BASE_IMAGE, override, BASE_IMAGE, RUNTIME_IMAGE_REPO, DEFAULT_RUNTIME_VERSION, MIN_RUNTIME_FOR_SSH_ATTACH, DESCRIPTORS, BUILTIN_LANGUAGES, LANGUAGE_CATALOG, LANGUAGE_SPEC_RE, SERVICE_CATALOG;
|
|
2273
2273
|
var init_catalog = __esm({
|
|
2274
2274
|
"src/create/catalog.ts"() {
|
|
2275
2275
|
"use strict";
|
|
@@ -2278,13 +2278,13 @@ 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.
|
|
2281
|
+
DEFAULT_RUNTIME_VERSION = true ? "1.3.2" : readFileSync3(
|
|
2282
2282
|
fileURLToPath2(
|
|
2283
2283
|
new URL("../../../../images/runtime/VERSION", import.meta.url)
|
|
2284
2284
|
),
|
|
2285
2285
|
"utf8"
|
|
2286
2286
|
).trim();
|
|
2287
|
-
|
|
2287
|
+
MIN_RUNTIME_FOR_SSH_ATTACH = "1.2.0";
|
|
2288
2288
|
DESCRIPTORS = loadDescriptorCatalogSync();
|
|
2289
2289
|
BUILTIN_LANGUAGES = new Set(
|
|
2290
2290
|
[...DESCRIPTORS.values()].filter((c) => c.category === "language" && c.descriptor.language?.builtin).map(descriptorSelector)
|
|
@@ -2947,14 +2947,61 @@ function ideStateVolumes(name) {
|
|
|
2947
2947
|
return [
|
|
2948
2948
|
{
|
|
2949
2949
|
volume: `monoceros-${name}-vscode-extensions`,
|
|
2950
|
-
target: "/home/node/.vscode-server/extensions"
|
|
2950
|
+
target: "/home/node/.vscode-server/extensions",
|
|
2951
|
+
minRuntime: "1.1.0"
|
|
2951
2952
|
},
|
|
2952
2953
|
{
|
|
2953
2954
|
volume: `monoceros-${name}-vscode-userdata`,
|
|
2954
|
-
target: "/home/node/.vscode-server/data/User"
|
|
2955
|
+
target: "/home/node/.vscode-server/data/User",
|
|
2956
|
+
minRuntime: "1.1.0"
|
|
2957
|
+
},
|
|
2958
|
+
{
|
|
2959
|
+
volume: `monoceros-${name}-vscodium-extensions`,
|
|
2960
|
+
target: "/home/node/.vscodium-server/extensions",
|
|
2961
|
+
minRuntime: "1.2.0"
|
|
2962
|
+
},
|
|
2963
|
+
{
|
|
2964
|
+
volume: `monoceros-${name}-vscodium-userdata`,
|
|
2965
|
+
target: "/home/node/.vscodium-server/data/User",
|
|
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"
|
|
2955
2996
|
}
|
|
2956
2997
|
];
|
|
2957
2998
|
}
|
|
2999
|
+
function ideStateVolumesForRuntime(name, version) {
|
|
3000
|
+
if (!version) return [];
|
|
3001
|
+
return ideStateVolumes(name).filter(
|
|
3002
|
+
(v) => compareRuntimeVersions(version, v.minRuntime) >= 0
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
2958
3005
|
function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
2959
3006
|
const resolvedFeatures = resolveFeatures(opts);
|
|
2960
3007
|
const features = {};
|
|
@@ -2986,6 +3033,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
2986
3033
|
}
|
|
2987
3034
|
}
|
|
2988
3035
|
} : void 0;
|
|
3036
|
+
const sshPostStart = runtimeSupportsSshAttach(opts.runtimeVersion) ? { postStartCommand: "sudo /usr/local/bin/monoceros-sshd-up.sh" } : {};
|
|
2989
3037
|
if (needsCompose(opts)) {
|
|
2990
3038
|
return {
|
|
2991
3039
|
name: opts.name,
|
|
@@ -2996,20 +3044,22 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
2996
3044
|
remoteUser: "node",
|
|
2997
3045
|
forwardPorts: ports,
|
|
2998
3046
|
postCreateCommand: ".devcontainer/post-create.sh",
|
|
3047
|
+
...sshPostStart,
|
|
2999
3048
|
...featuresField ?? {},
|
|
3000
3049
|
...customizationsField ?? {}
|
|
3001
3050
|
};
|
|
3002
3051
|
}
|
|
3003
|
-
const ideMounts =
|
|
3004
|
-
|
|
3005
|
-
|
|
3052
|
+
const ideMounts = ideStateVolumesForRuntime(
|
|
3053
|
+
opts.name,
|
|
3054
|
+
opts.runtimeVersion
|
|
3055
|
+
).map((v) => `source=${v.volume},target=${v.target},type=volume`);
|
|
3006
3056
|
const mounts = [...homeMounts, ...ideMounts];
|
|
3007
3057
|
const mountsField = mounts.length > 0 ? { mounts } : {};
|
|
3008
3058
|
const workspaceMountField = {
|
|
3009
3059
|
workspaceMount: `source=\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind,consistency=cached`,
|
|
3010
3060
|
workspaceFolder: `/workspaces/${opts.name}`
|
|
3011
3061
|
};
|
|
3012
|
-
const runArgs = ["--cap-add=NET_ADMIN"];
|
|
3062
|
+
const runArgs = ["--cap-add=NET_ADMIN", `--name=monoceros-${opts.name}`];
|
|
3013
3063
|
if (ports.length > 0) {
|
|
3014
3064
|
runArgs.push("--network=monoceros-proxy");
|
|
3015
3065
|
runArgs.push(`--network-alias=${opts.name}`);
|
|
@@ -3023,6 +3073,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
3023
3073
|
runArgs,
|
|
3024
3074
|
forwardPorts: ports,
|
|
3025
3075
|
postCreateCommand: ".devcontainer/post-create.sh",
|
|
3076
|
+
...sshPostStart,
|
|
3026
3077
|
...featuresField ?? {},
|
|
3027
3078
|
...customizationsField ?? {}
|
|
3028
3079
|
};
|
|
@@ -3043,9 +3094,10 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
3043
3094
|
void dockerMode;
|
|
3044
3095
|
const hasPorts = (opts.ports?.length ?? 0) > 0;
|
|
3045
3096
|
const lines = ["services:"];
|
|
3046
|
-
const ideVolumes =
|
|
3097
|
+
const ideVolumes = ideStateVolumesForRuntime(opts.name, opts.runtimeVersion);
|
|
3047
3098
|
lines.push(" workspace:");
|
|
3048
3099
|
lines.push(` image: ${resolveRuntimeImage(opts.runtimeVersion)}`);
|
|
3100
|
+
lines.push(` container_name: monoceros-${opts.name}`);
|
|
3049
3101
|
lines.push(" command: 'sleep infinity'");
|
|
3050
3102
|
lines.push(" cap_add:");
|
|
3051
3103
|
lines.push(" - NET_ADMIN");
|
|
@@ -3917,8 +3969,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
|
|
|
3917
3969
|
if (!isMap2(item)) return false;
|
|
3918
3970
|
const url = item.get("url");
|
|
3919
3971
|
if (url === urlOrPath) return true;
|
|
3920
|
-
const
|
|
3921
|
-
const effectivePath = typeof
|
|
3972
|
+
const path30 = item.get("path");
|
|
3973
|
+
const effectivePath = typeof path30 === "string" ? path30 : typeof url === "string" ? deriveRepoName(url) : void 0;
|
|
3922
3974
|
return effectivePath === urlOrPath;
|
|
3923
3975
|
});
|
|
3924
3976
|
if (idx < 0) return false;
|
|
@@ -4040,7 +4092,7 @@ async function runAddRepo(input) {
|
|
|
4040
4092
|
"Missing repo URL. Usage: monoceros add-repo <containername> <url>."
|
|
4041
4093
|
);
|
|
4042
4094
|
}
|
|
4043
|
-
const
|
|
4095
|
+
const path30 = (input.path ?? deriveRepoName(url)).trim();
|
|
4044
4096
|
const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
|
|
4045
4097
|
const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
|
|
4046
4098
|
if (hasName !== hasEmail) {
|
|
@@ -4077,7 +4129,7 @@ async function runAddRepo(input) {
|
|
|
4077
4129
|
const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
|
|
4078
4130
|
const entry2 = {
|
|
4079
4131
|
url,
|
|
4080
|
-
path:
|
|
4132
|
+
path: path30,
|
|
4081
4133
|
...hasName && hasEmail ? {
|
|
4082
4134
|
gitUser: {
|
|
4083
4135
|
name: input.gitName.trim(),
|
|
@@ -5120,9 +5172,163 @@ var init_transform = __esm({
|
|
|
5120
5172
|
}
|
|
5121
5173
|
});
|
|
5122
5174
|
|
|
5175
|
+
// src/devcontainer/ssh-attach.ts
|
|
5176
|
+
import { spawn as spawn4 } from "child_process";
|
|
5177
|
+
import { promises as fs10 } from "fs";
|
|
5178
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5179
|
+
import os2 from "os";
|
|
5180
|
+
import path13 from "path";
|
|
5181
|
+
function sshHomeDir(home) {
|
|
5182
|
+
return path13.join(home, "ssh");
|
|
5183
|
+
}
|
|
5184
|
+
function sshConfigEntryPath(home, name) {
|
|
5185
|
+
return path13.join(sshHomeDir(home), "config.d", name);
|
|
5186
|
+
}
|
|
5187
|
+
function sshProxyScriptPath(home, name) {
|
|
5188
|
+
return path13.join(sshHomeDir(home), `exec-${name}.sh`);
|
|
5189
|
+
}
|
|
5190
|
+
function privateKeyPath(targetDir) {
|
|
5191
|
+
return path13.join(targetDir, ".monoceros", "ssh", "id_ed25519");
|
|
5192
|
+
}
|
|
5193
|
+
async function ensureKeypair(targetDir, name, keygen, logger) {
|
|
5194
|
+
const privateKey = privateKeyPath(targetDir);
|
|
5195
|
+
const publicKey = `${privateKey}.pub`;
|
|
5196
|
+
if (existsSync8(privateKey) && existsSync8(publicKey)) {
|
|
5197
|
+
return { privateKey, publicKey };
|
|
5198
|
+
}
|
|
5199
|
+
await fs10.mkdir(path13.dirname(privateKey), { recursive: true });
|
|
5200
|
+
try {
|
|
5201
|
+
const res = await keygen([
|
|
5202
|
+
"-t",
|
|
5203
|
+
"ed25519",
|
|
5204
|
+
"-N",
|
|
5205
|
+
"",
|
|
5206
|
+
"-f",
|
|
5207
|
+
privateKey,
|
|
5208
|
+
"-C",
|
|
5209
|
+
`monoceros-${name}`,
|
|
5210
|
+
"-q"
|
|
5211
|
+
]);
|
|
5212
|
+
if (res.exitCode !== 0) {
|
|
5213
|
+
logger.warn(
|
|
5214
|
+
`ssh-keygen failed (exit ${res.exitCode})${res.stderr ? `: ${res.stderr.trim()}` : ""}; IDE SSH attach not set up.`
|
|
5215
|
+
);
|
|
5216
|
+
return null;
|
|
5217
|
+
}
|
|
5218
|
+
} catch (err) {
|
|
5219
|
+
logger.warn(
|
|
5220
|
+
`ssh-keygen not runnable (${err instanceof Error ? err.message : String(err)}); IDE SSH attach not set up.`
|
|
5221
|
+
);
|
|
5222
|
+
return null;
|
|
5223
|
+
}
|
|
5224
|
+
return { privateKey, publicKey };
|
|
5225
|
+
}
|
|
5226
|
+
function proxyScriptContent(name, targetDir) {
|
|
5227
|
+
return `#!/bin/sh
|
|
5228
|
+
# Generated by Monoceros (ADR 0022) - do not edit.
|
|
5229
|
+
# Portless SSH transport into the running dev container '${name}'.
|
|
5230
|
+
# Resolves the container by the devcontainer.local_folder label (the
|
|
5231
|
+
# same handle 'monoceros shell' uses), so it follows rebuilds.
|
|
5232
|
+
set -e
|
|
5233
|
+
cid=$(docker ps -q \\
|
|
5234
|
+
--filter "label=devcontainer.local_folder=${targetDir}" \\
|
|
5235
|
+
--filter status=running | head -n1)
|
|
5236
|
+
if [ -z "$cid" ]; then
|
|
5237
|
+
echo "monoceros: dev container '${name}' is not running. Start it with: monoceros apply ${name}" >&2
|
|
5238
|
+
exit 1
|
|
5239
|
+
fi
|
|
5240
|
+
exec docker exec -i "$cid" socat - TCP:127.0.0.1:22
|
|
5241
|
+
`;
|
|
5242
|
+
}
|
|
5243
|
+
function configEntryContent(name, hostAlias, privateKey, proxyScript) {
|
|
5244
|
+
return `# Generated by Monoceros (ADR 0022) - do not edit.
|
|
5245
|
+
# Attach an SSH-capable IDE (Codium, IntelliJ, Zed) to host
|
|
5246
|
+
# '${hostAlias}', or run: ssh ${hostAlias}
|
|
5247
|
+
Host ${hostAlias}
|
|
5248
|
+
User node
|
|
5249
|
+
IdentityFile "${privateKey}"
|
|
5250
|
+
IdentitiesOnly yes
|
|
5251
|
+
StrictHostKeyChecking no
|
|
5252
|
+
UserKnownHostsFile /dev/null
|
|
5253
|
+
ProxyCommand "${proxyScript}"
|
|
5254
|
+
`;
|
|
5255
|
+
}
|
|
5256
|
+
async function ensureInclude(userSshDir, home) {
|
|
5257
|
+
const configPath = path13.join(userSshDir, "config");
|
|
5258
|
+
const includeTarget = path13.join(sshHomeDir(home), "config.d", "*");
|
|
5259
|
+
const includeLine = `Include "${includeTarget}"`;
|
|
5260
|
+
await fs10.mkdir(userSshDir, { recursive: true });
|
|
5261
|
+
let existing = "";
|
|
5262
|
+
try {
|
|
5263
|
+
existing = await fs10.readFile(configPath, "utf8");
|
|
5264
|
+
} catch {
|
|
5265
|
+
existing = "";
|
|
5266
|
+
}
|
|
5267
|
+
if (existing.includes(includeTarget)) return;
|
|
5268
|
+
const banner = "# Added by Monoceros (ADR 0022): per-container SSH attach entries.";
|
|
5269
|
+
const prefix = `${banner}
|
|
5270
|
+
${includeLine}
|
|
5271
|
+
`;
|
|
5272
|
+
const next = existing.length > 0 ? `${prefix}
|
|
5273
|
+
${existing}` : prefix;
|
|
5274
|
+
await fs10.writeFile(configPath, next, { mode: 384 });
|
|
5275
|
+
await fs10.chmod(configPath, 384).catch(() => {
|
|
5276
|
+
});
|
|
5277
|
+
}
|
|
5278
|
+
async function setupSshAttach(opts) {
|
|
5279
|
+
const hostAlias = `monoceros-${opts.name}`;
|
|
5280
|
+
const logger = opts.logger ?? { info: () => {
|
|
5281
|
+
}, warn: () => {
|
|
5282
|
+
} };
|
|
5283
|
+
const keygen = opts.keygen ?? realKeygen;
|
|
5284
|
+
const userSshDir = opts.userSshDir ?? path13.join(os2.homedir(), ".ssh");
|
|
5285
|
+
const keys = await ensureKeypair(opts.targetDir, opts.name, keygen, logger);
|
|
5286
|
+
if (!keys) return { hostAlias, configured: false };
|
|
5287
|
+
const proxyScript = sshProxyScriptPath(opts.home, opts.name);
|
|
5288
|
+
const configEntry = sshConfigEntryPath(opts.home, opts.name);
|
|
5289
|
+
await fs10.mkdir(path13.dirname(proxyScript), { recursive: true });
|
|
5290
|
+
await fs10.mkdir(path13.dirname(configEntry), { recursive: true });
|
|
5291
|
+
await fs10.writeFile(
|
|
5292
|
+
proxyScript,
|
|
5293
|
+
proxyScriptContent(opts.name, opts.targetDir),
|
|
5294
|
+
{ mode: 493 }
|
|
5295
|
+
);
|
|
5296
|
+
await fs10.chmod(proxyScript, 493).catch(() => {
|
|
5297
|
+
});
|
|
5298
|
+
await fs10.writeFile(
|
|
5299
|
+
configEntry,
|
|
5300
|
+
configEntryContent(opts.name, hostAlias, keys.privateKey, proxyScript)
|
|
5301
|
+
);
|
|
5302
|
+
await ensureInclude(userSshDir, opts.home);
|
|
5303
|
+
return { hostAlias, configured: true };
|
|
5304
|
+
}
|
|
5305
|
+
async function removeSshAttach(home, name) {
|
|
5306
|
+
await fs10.rm(sshProxyScriptPath(home, name), { force: true });
|
|
5307
|
+
await fs10.rm(sshConfigEntryPath(home, name), { force: true });
|
|
5308
|
+
}
|
|
5309
|
+
var realKeygen;
|
|
5310
|
+
var init_ssh_attach = __esm({
|
|
5311
|
+
"src/devcontainer/ssh-attach.ts"() {
|
|
5312
|
+
"use strict";
|
|
5313
|
+
realKeygen = (args) => {
|
|
5314
|
+
return new Promise((resolve, reject) => {
|
|
5315
|
+
const child = spawn4("ssh-keygen", args, {
|
|
5316
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
5317
|
+
});
|
|
5318
|
+
let stderr = "";
|
|
5319
|
+
child.stderr.on("data", (chunk) => {
|
|
5320
|
+
stderr += chunk.toString();
|
|
5321
|
+
});
|
|
5322
|
+
child.on("error", reject);
|
|
5323
|
+
child.on("exit", (code) => resolve({ exitCode: code ?? 0, stderr }));
|
|
5324
|
+
});
|
|
5325
|
+
};
|
|
5326
|
+
}
|
|
5327
|
+
});
|
|
5328
|
+
|
|
5123
5329
|
// src/apply/apply-log.ts
|
|
5124
5330
|
import { createWriteStream, mkdirSync } from "fs";
|
|
5125
|
-
import
|
|
5331
|
+
import path14 from "path";
|
|
5126
5332
|
import { Writable } from "stream";
|
|
5127
5333
|
function safeIsoStamp(d) {
|
|
5128
5334
|
return d.toISOString().replace(/[:.]/g, "-");
|
|
@@ -5132,7 +5338,7 @@ function createApplyLog(opts) {
|
|
|
5132
5338
|
const dir = containerLogsDir(opts.name, opts.home);
|
|
5133
5339
|
mkdirSync(dir, { recursive: true });
|
|
5134
5340
|
const file = `apply-${opts.name}-${safeIsoStamp(now)}.log`;
|
|
5135
|
-
const fullPath =
|
|
5341
|
+
const fullPath = path14.join(dir, file);
|
|
5136
5342
|
const stream = createWriteStream(fullPath, { flags: "w" });
|
|
5137
5343
|
const header = [
|
|
5138
5344
|
`# monoceros apply log`,
|
|
@@ -5999,8 +6205,8 @@ var init_markers = __esm({
|
|
|
5999
6205
|
});
|
|
6000
6206
|
|
|
6001
6207
|
// src/briefing/index.ts
|
|
6002
|
-
import { promises as
|
|
6003
|
-
import
|
|
6208
|
+
import { promises as fs11 } from "fs";
|
|
6209
|
+
import path15 from "path";
|
|
6004
6210
|
async function writeBriefing(input) {
|
|
6005
6211
|
const subCommands = input.subCommands ?? await loadSubCommandsDynamic();
|
|
6006
6212
|
const manifestLoader = input.manifestLoader ?? ((ref) => loadFeatureManifestSummary(ref));
|
|
@@ -6013,12 +6219,12 @@ async function writeBriefing(input) {
|
|
|
6013
6219
|
);
|
|
6014
6220
|
const claudeBody = generateClaudeMd();
|
|
6015
6221
|
const commandsBody = generateCommandsMd(subCommands);
|
|
6016
|
-
await writeMarkerAware(
|
|
6017
|
-
await writeMarkerAware(
|
|
6018
|
-
const monocerosDir =
|
|
6019
|
-
await
|
|
6020
|
-
await
|
|
6021
|
-
|
|
6222
|
+
await writeMarkerAware(path15.join(input.targetDir, "AGENTS.md"), agentsBody);
|
|
6223
|
+
await writeMarkerAware(path15.join(input.targetDir, "CLAUDE.md"), claudeBody);
|
|
6224
|
+
const monocerosDir = path15.join(input.targetDir, ".monoceros");
|
|
6225
|
+
await fs11.mkdir(monocerosDir, { recursive: true });
|
|
6226
|
+
await fs11.writeFile(
|
|
6227
|
+
path15.join(monocerosDir, "commands.md"),
|
|
6022
6228
|
commandsBody,
|
|
6023
6229
|
"utf8"
|
|
6024
6230
|
);
|
|
@@ -6038,18 +6244,18 @@ function featureDisplayMap(components) {
|
|
|
6038
6244
|
async function writeMarkerAware(filePath, body) {
|
|
6039
6245
|
let existing = null;
|
|
6040
6246
|
try {
|
|
6041
|
-
existing = await
|
|
6247
|
+
existing = await fs11.readFile(filePath, "utf8");
|
|
6042
6248
|
} catch (err) {
|
|
6043
6249
|
if (err.code !== "ENOENT") throw err;
|
|
6044
6250
|
}
|
|
6045
6251
|
if (existing) {
|
|
6046
6252
|
const updated = replaceMarkerBlock(existing, body);
|
|
6047
6253
|
if (updated !== null) {
|
|
6048
|
-
await
|
|
6254
|
+
await fs11.writeFile(filePath, updated, "utf8");
|
|
6049
6255
|
return;
|
|
6050
6256
|
}
|
|
6051
6257
|
}
|
|
6052
|
-
await
|
|
6258
|
+
await fs11.writeFile(filePath, wrapWithMarkers(body), "utf8");
|
|
6053
6259
|
}
|
|
6054
6260
|
async function loadSubCommandsDynamic() {
|
|
6055
6261
|
const mod = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
@@ -6174,10 +6380,10 @@ var init_runtime_pull_hint = __esm({
|
|
|
6174
6380
|
});
|
|
6175
6381
|
|
|
6176
6382
|
// src/devcontainer/cli.ts
|
|
6177
|
-
import { spawn as
|
|
6383
|
+
import { spawn as spawn5 } from "child_process";
|
|
6178
6384
|
import { readFileSync as readFileSync4 } from "fs";
|
|
6179
6385
|
import { createRequire } from "module";
|
|
6180
|
-
import
|
|
6386
|
+
import path16 from "path";
|
|
6181
6387
|
function devcontainerCliPath() {
|
|
6182
6388
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
6183
6389
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
@@ -6186,7 +6392,7 @@ function devcontainerCliPath() {
|
|
|
6186
6392
|
if (!binEntry) {
|
|
6187
6393
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
6188
6394
|
}
|
|
6189
|
-
cachedBinaryPath =
|
|
6395
|
+
cachedBinaryPath = path16.resolve(path16.dirname(pkgJsonPath), binEntry);
|
|
6190
6396
|
return cachedBinaryPath;
|
|
6191
6397
|
}
|
|
6192
6398
|
var require_, cachedBinaryPath, spawnDevcontainer;
|
|
@@ -6202,7 +6408,7 @@ var init_cli = __esm({
|
|
|
6202
6408
|
const env = { ...process.env, DOCKER_CLI_HINTS: "false" };
|
|
6203
6409
|
return new Promise((resolve, reject) => {
|
|
6204
6410
|
if (options.interactive) {
|
|
6205
|
-
const child2 =
|
|
6411
|
+
const child2 = spawn5(process.execPath, [binPath, ...args], {
|
|
6206
6412
|
cwd,
|
|
6207
6413
|
env,
|
|
6208
6414
|
stdio: "inherit"
|
|
@@ -6211,7 +6417,7 @@ var init_cli = __esm({
|
|
|
6211
6417
|
child2.on("exit", (code) => resolve(code ?? 0));
|
|
6212
6418
|
return;
|
|
6213
6419
|
}
|
|
6214
|
-
const child =
|
|
6420
|
+
const child = spawn5(process.execPath, [binPath, ...args], {
|
|
6215
6421
|
cwd,
|
|
6216
6422
|
env,
|
|
6217
6423
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6259,9 +6465,9 @@ var init_cli = __esm({
|
|
|
6259
6465
|
});
|
|
6260
6466
|
|
|
6261
6467
|
// src/devcontainer/compose.ts
|
|
6262
|
-
import { spawn as
|
|
6263
|
-
import { existsSync as
|
|
6264
|
-
import
|
|
6468
|
+
import { spawn as spawn6 } from "child_process";
|
|
6469
|
+
import { existsSync as existsSync9 } from "fs";
|
|
6470
|
+
import path17 from "path";
|
|
6265
6471
|
import { Writable as Writable3 } from "stream";
|
|
6266
6472
|
import { consola as consola10 } from "consola";
|
|
6267
6473
|
async function findContainerIds(filters, exec = spawnDocker) {
|
|
@@ -6311,18 +6517,24 @@ async function cleanupDockerObjects(opts) {
|
|
|
6311
6517
|
return { exitCode: rmExit, removedIds: ids };
|
|
6312
6518
|
}
|
|
6313
6519
|
function composeProjectName(root) {
|
|
6314
|
-
return `${
|
|
6520
|
+
return `${path17.basename(root)}_devcontainer`;
|
|
6315
6521
|
}
|
|
6316
|
-
function
|
|
6317
|
-
if (!
|
|
6522
|
+
function assertDevcontainer(root) {
|
|
6523
|
+
if (!existsSync9(path17.join(root, ".devcontainer"))) {
|
|
6318
6524
|
throw new Error(
|
|
6319
6525
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
6320
6526
|
);
|
|
6321
6527
|
}
|
|
6322
|
-
|
|
6323
|
-
|
|
6528
|
+
}
|
|
6529
|
+
function isComposeMode(root) {
|
|
6530
|
+
return existsSync9(path17.join(root, ".devcontainer", "compose.yaml"));
|
|
6531
|
+
}
|
|
6532
|
+
function resolveCompose(root) {
|
|
6533
|
+
assertDevcontainer(root);
|
|
6534
|
+
const composeFile = path17.join(root, ".devcontainer", "compose.yaml");
|
|
6535
|
+
if (!existsSync9(composeFile)) {
|
|
6324
6536
|
throw new Error(
|
|
6325
|
-
`No compose.yaml at ${composeFile}. \`
|
|
6537
|
+
`No compose.yaml at ${composeFile}. \`monoceros logs\` tails compose service logs, which require services configured via \`monoceros add-service <name> <svc>\`. For a bare container, use \`monoceros shell <name>\` or read logs/<app>.log.`
|
|
6326
6538
|
);
|
|
6327
6539
|
}
|
|
6328
6540
|
return { composeFile, projectName: composeProjectName(root) };
|
|
@@ -6334,7 +6546,7 @@ async function runComposeAction(buildSubArgs, opts) {
|
|
|
6334
6546
|
return spawnFn(["-f", composeFile, "-p", projectName, ...subArgs], opts.root);
|
|
6335
6547
|
}
|
|
6336
6548
|
async function runStart(opts) {
|
|
6337
|
-
|
|
6549
|
+
assertDevcontainer(opts.root);
|
|
6338
6550
|
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6339
6551
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
6340
6552
|
logger.info(`Bringing devcontainer up at ${opts.root}\u2026`);
|
|
@@ -6477,16 +6689,71 @@ and retry \`monoceros apply\`.`
|
|
|
6477
6689
|
);
|
|
6478
6690
|
}
|
|
6479
6691
|
function runStop(opts) {
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6692
|
+
assertDevcontainer(opts.root);
|
|
6693
|
+
if (isComposeMode(opts.root)) {
|
|
6694
|
+
return runComposeAction(
|
|
6695
|
+
(service) => ["stop", ...service ? [service] : []],
|
|
6696
|
+
opts
|
|
6697
|
+
);
|
|
6698
|
+
}
|
|
6699
|
+
return stopImageContainer(opts);
|
|
6484
6700
|
}
|
|
6485
6701
|
function runStatus(opts) {
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6702
|
+
assertDevcontainer(opts.root);
|
|
6703
|
+
if (isComposeMode(opts.root)) {
|
|
6704
|
+
return runComposeAction(
|
|
6705
|
+
(service) => ["ps", ...service ? [service] : []],
|
|
6706
|
+
opts
|
|
6707
|
+
);
|
|
6708
|
+
}
|
|
6709
|
+
return statusImageContainer(opts);
|
|
6710
|
+
}
|
|
6711
|
+
function imageContainerFilter(root) {
|
|
6712
|
+
return `label=devcontainer.local_folder=${root}`;
|
|
6713
|
+
}
|
|
6714
|
+
async function stopImageContainer(opts) {
|
|
6715
|
+
const exec = opts.dockerExec ?? spawnDocker;
|
|
6716
|
+
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6717
|
+
const name = path17.basename(opts.root);
|
|
6718
|
+
const ps = await exec([
|
|
6719
|
+
"ps",
|
|
6720
|
+
"-q",
|
|
6721
|
+
"--filter",
|
|
6722
|
+
imageContainerFilter(opts.root),
|
|
6723
|
+
"--filter",
|
|
6724
|
+
"status=running"
|
|
6725
|
+
]);
|
|
6726
|
+
const id = ps.stdout.split("\n").map((s) => s.trim()).find((s) => s.length > 0);
|
|
6727
|
+
if (!id) {
|
|
6728
|
+
logger.info(`Container '${name}' is not running.`);
|
|
6729
|
+
return 0;
|
|
6730
|
+
}
|
|
6731
|
+
const res = await exec(["stop", id]);
|
|
6732
|
+
if (res.exitCode === 0) logger.info(`Stopped '${name}'.`);
|
|
6733
|
+
return res.exitCode;
|
|
6734
|
+
}
|
|
6735
|
+
async function statusImageContainer(opts) {
|
|
6736
|
+
const exec = opts.dockerExec ?? spawnDocker;
|
|
6737
|
+
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6738
|
+
const name = path17.basename(opts.root);
|
|
6739
|
+
const res = await exec([
|
|
6740
|
+
"ps",
|
|
6741
|
+
"-a",
|
|
6742
|
+
"--filter",
|
|
6743
|
+
imageContainerFilter(opts.root)
|
|
6744
|
+
]);
|
|
6745
|
+
const rows = res.stdout.split("\n").filter((l) => l.trim().length > 0);
|
|
6746
|
+
if (rows.length <= 1) {
|
|
6747
|
+
logger.info(
|
|
6748
|
+
`Container '${name}' does not exist. Run \`monoceros apply ${name}\`.`
|
|
6749
|
+
);
|
|
6750
|
+
return 0;
|
|
6751
|
+
}
|
|
6752
|
+
process.stdout.write(
|
|
6753
|
+
res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}
|
|
6754
|
+
`
|
|
6489
6755
|
);
|
|
6756
|
+
return res.exitCode;
|
|
6490
6757
|
}
|
|
6491
6758
|
function runLogs(opts) {
|
|
6492
6759
|
const follow = opts.follow ?? true;
|
|
@@ -6509,7 +6776,7 @@ var init_compose = __esm({
|
|
|
6509
6776
|
init_proxy();
|
|
6510
6777
|
spawnDockerCompose = (args, cwd) => {
|
|
6511
6778
|
return new Promise((resolve, reject) => {
|
|
6512
|
-
const child =
|
|
6779
|
+
const child = spawn6("docker", ["compose", ...args], {
|
|
6513
6780
|
cwd,
|
|
6514
6781
|
stdio: ["inherit", "pipe", "pipe"]
|
|
6515
6782
|
});
|
|
@@ -6521,7 +6788,7 @@ var init_compose = __esm({
|
|
|
6521
6788
|
};
|
|
6522
6789
|
spawnDocker = (args) => {
|
|
6523
6790
|
return new Promise((resolve, reject) => {
|
|
6524
|
-
const child =
|
|
6791
|
+
const child = spawn6("docker", args, {
|
|
6525
6792
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6526
6793
|
});
|
|
6527
6794
|
let stdout = "";
|
|
@@ -6582,9 +6849,9 @@ var init_images = __esm({
|
|
|
6582
6849
|
|
|
6583
6850
|
// src/config/machine-state.ts
|
|
6584
6851
|
import { promises as fsp4 } from "fs";
|
|
6585
|
-
import
|
|
6852
|
+
import path18 from "path";
|
|
6586
6853
|
function machineStatePath(home = monocerosHome()) {
|
|
6587
|
-
return
|
|
6854
|
+
return path18.join(home, ".machine-state.json");
|
|
6588
6855
|
}
|
|
6589
6856
|
async function readMachineState(home = monocerosHome()) {
|
|
6590
6857
|
try {
|
|
@@ -6639,7 +6906,7 @@ var init_machine_state = __esm({
|
|
|
6639
6906
|
});
|
|
6640
6907
|
|
|
6641
6908
|
// src/devcontainer/docker-mode.ts
|
|
6642
|
-
import { spawn as
|
|
6909
|
+
import { spawn as spawn7 } from "child_process";
|
|
6643
6910
|
async function detectDockerMode(options = {}) {
|
|
6644
6911
|
const spawnFn = options.spawn ?? realDockerInfo;
|
|
6645
6912
|
try {
|
|
@@ -6689,7 +6956,7 @@ var init_docker_mode = __esm({
|
|
|
6689
6956
|
init_format();
|
|
6690
6957
|
realDockerInfo = () => {
|
|
6691
6958
|
return new Promise((resolve, reject) => {
|
|
6692
|
-
const child =
|
|
6959
|
+
const child = spawn7(
|
|
6693
6960
|
"docker",
|
|
6694
6961
|
["info", "--format", "{{json .SecurityOptions}}"],
|
|
6695
6962
|
{
|
|
@@ -6708,9 +6975,9 @@ var init_docker_mode = __esm({
|
|
|
6708
6975
|
});
|
|
6709
6976
|
|
|
6710
6977
|
// src/devcontainer/identity.ts
|
|
6711
|
-
import { spawn as
|
|
6712
|
-
import { promises as
|
|
6713
|
-
import
|
|
6978
|
+
import { spawn as spawn8 } from "child_process";
|
|
6979
|
+
import { promises as fs12 } from "fs";
|
|
6980
|
+
import path19 from "path";
|
|
6714
6981
|
import { consola as consola11 } from "consola";
|
|
6715
6982
|
async function resolveIdentityWithPrompt(options = {}) {
|
|
6716
6983
|
const spawnFn = options.spawn ?? realGitConfigGet;
|
|
@@ -6766,8 +7033,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
6766
7033
|
};
|
|
6767
7034
|
}
|
|
6768
7035
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
6769
|
-
const gitconfigDir =
|
|
6770
|
-
const gitconfigPath =
|
|
7036
|
+
const gitconfigDir = path19.join(devContainerRoot, ".monoceros");
|
|
7037
|
+
const gitconfigPath = path19.join(gitconfigDir, "gitconfig");
|
|
6771
7038
|
const logger = options.logger ?? { info: () => {
|
|
6772
7039
|
}, warn: () => {
|
|
6773
7040
|
} };
|
|
@@ -6780,8 +7047,8 @@ async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
|
6780
7047
|
const lines = ["[user]"];
|
|
6781
7048
|
if (resolved.name !== void 0) lines.push(` name = ${resolved.name}`);
|
|
6782
7049
|
if (resolved.email !== void 0) lines.push(` email = ${resolved.email}`);
|
|
6783
|
-
await
|
|
6784
|
-
await
|
|
7050
|
+
await fs12.mkdir(gitconfigDir, { recursive: true });
|
|
7051
|
+
await fs12.writeFile(gitconfigPath, lines.join("\n") + "\n");
|
|
6785
7052
|
return {
|
|
6786
7053
|
...resolved.name !== void 0 ? { name: resolved.name } : {},
|
|
6787
7054
|
...resolved.email !== void 0 ? { email: resolved.email } : {},
|
|
@@ -6824,7 +7091,7 @@ async function readKeyFromHost(spawnFn, key, logger) {
|
|
|
6824
7091
|
}
|
|
6825
7092
|
async function readExistingGitconfig(filePath) {
|
|
6826
7093
|
try {
|
|
6827
|
-
const content = await
|
|
7094
|
+
const content = await fs12.readFile(filePath, "utf8");
|
|
6828
7095
|
const result = {};
|
|
6829
7096
|
const nameMatch = /^\s*name\s*=\s*(.+?)\s*$/m.exec(content);
|
|
6830
7097
|
const emailMatch = /^\s*email\s*=\s*(.+?)\s*$/m.exec(content);
|
|
@@ -6841,7 +7108,7 @@ var init_identity = __esm({
|
|
|
6841
7108
|
"use strict";
|
|
6842
7109
|
realGitConfigGet = (key) => {
|
|
6843
7110
|
return new Promise((resolve, reject) => {
|
|
6844
|
-
const child =
|
|
7111
|
+
const child = spawn8("git", ["config", "--global", "--get", key], {
|
|
6845
7112
|
stdio: ["ignore", "pipe", "inherit"]
|
|
6846
7113
|
});
|
|
6847
7114
|
let stdout = "";
|
|
@@ -6901,7 +7168,7 @@ var init_identity = __esm({
|
|
|
6901
7168
|
});
|
|
6902
7169
|
|
|
6903
7170
|
// src/apply/index.ts
|
|
6904
|
-
import { existsSync as
|
|
7171
|
+
import { existsSync as existsSync10, promises as fs13 } from "fs";
|
|
6905
7172
|
import { consola as consola12 } from "consola";
|
|
6906
7173
|
async function runApply(opts) {
|
|
6907
7174
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -6923,7 +7190,7 @@ ${sectionLine(label)}
|
|
|
6923
7190
|
);
|
|
6924
7191
|
}
|
|
6925
7192
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
6926
|
-
if (!
|
|
7193
|
+
if (!existsSync10(ymlPath)) {
|
|
6927
7194
|
throw new Error(
|
|
6928
7195
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
6929
7196
|
);
|
|
@@ -7041,7 +7308,7 @@ Fix the value in the env file (or the yml).`
|
|
|
7041
7308
|
if (dockerMode === "rootless") {
|
|
7042
7309
|
throw new Error(formatRootlessNotSupportedError());
|
|
7043
7310
|
}
|
|
7044
|
-
await
|
|
7311
|
+
await fs13.mkdir(targetDir, { recursive: true });
|
|
7045
7312
|
await writeScaffold(createOpts, targetDir, { dockerMode });
|
|
7046
7313
|
await writeStateFile(
|
|
7047
7314
|
targetDir,
|
|
@@ -7052,6 +7319,27 @@ Fix the value in the env file (or the yml).`
|
|
|
7052
7319
|
...opts.now ? { now: opts.now } : {}
|
|
7053
7320
|
})
|
|
7054
7321
|
);
|
|
7322
|
+
if (runtimeSupportsSshAttach(createOpts.runtimeVersion)) {
|
|
7323
|
+
try {
|
|
7324
|
+
const ssh = await setupSshAttach({
|
|
7325
|
+
name: opts.name,
|
|
7326
|
+
targetDir,
|
|
7327
|
+
home,
|
|
7328
|
+
...opts.sshKeygen ? { keygen: opts.sshKeygen } : {},
|
|
7329
|
+
...opts.sshUserSshDir ? { userSshDir: opts.sshUserSshDir } : {},
|
|
7330
|
+
logger: idLogger
|
|
7331
|
+
});
|
|
7332
|
+
if (ssh.configured) {
|
|
7333
|
+
logger.info(
|
|
7334
|
+
`SSH attach: \`ssh ${ssh.hostAlias}\` (or pick it in your IDE)`
|
|
7335
|
+
);
|
|
7336
|
+
}
|
|
7337
|
+
} catch (err) {
|
|
7338
|
+
(logger.warn ?? logger.info)(
|
|
7339
|
+
`SSH attach setup skipped: ${err instanceof Error ? err.message : String(err)}`
|
|
7340
|
+
);
|
|
7341
|
+
}
|
|
7342
|
+
}
|
|
7055
7343
|
try {
|
|
7056
7344
|
const components = await loadComponentCatalog();
|
|
7057
7345
|
await writeBriefing({ targetDir, createOpts, components });
|
|
@@ -7196,8 +7484,8 @@ ${stripAnsi(formatted)}
|
|
|
7196
7484
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
7197
7485
|
}
|
|
7198
7486
|
async function assertSafeTargetDir(targetDir, expectedOrigin) {
|
|
7199
|
-
if (!
|
|
7200
|
-
const entries = await
|
|
7487
|
+
if (!existsSync10(targetDir)) return;
|
|
7488
|
+
const entries = await fs13.readdir(targetDir);
|
|
7201
7489
|
if (entries.length === 0) return;
|
|
7202
7490
|
const state = await readStateFile(targetDir);
|
|
7203
7491
|
if (state) {
|
|
@@ -7267,7 +7555,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
7267
7555
|
}
|
|
7268
7556
|
if (wantContainer) {
|
|
7269
7557
|
try {
|
|
7270
|
-
const text = await
|
|
7558
|
+
const text = await fs13.readFile(ymlPath, "utf8");
|
|
7271
7559
|
const parsed = parseConfig(text, ymlPath);
|
|
7272
7560
|
const changed = setContainerGitUserInDoc(parsed.doc, {
|
|
7273
7561
|
name: prompted.name,
|
|
@@ -7275,7 +7563,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
7275
7563
|
});
|
|
7276
7564
|
if (changed) {
|
|
7277
7565
|
const out = stringifyConfig(parsed.doc);
|
|
7278
|
-
await
|
|
7566
|
+
await fs13.writeFile(ymlPath, out, "utf8");
|
|
7279
7567
|
logger.info(
|
|
7280
7568
|
`Saved identity in this container \u2014 wrote git.user into ${prettyPath(ymlPath)}.`
|
|
7281
7569
|
);
|
|
@@ -7298,6 +7586,7 @@ var init_apply = __esm({
|
|
|
7298
7586
|
init_state();
|
|
7299
7587
|
init_transform();
|
|
7300
7588
|
init_catalog();
|
|
7589
|
+
init_ssh_attach();
|
|
7301
7590
|
init_scaffold();
|
|
7302
7591
|
init_format();
|
|
7303
7592
|
init_ref();
|
|
@@ -7321,82 +7610,378 @@ var init_apply = __esm({
|
|
|
7321
7610
|
}
|
|
7322
7611
|
});
|
|
7323
7612
|
|
|
7324
|
-
// src/
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
}
|
|
7331
|
-
});
|
|
7332
|
-
|
|
7333
|
-
// src/commands/_dispatch.ts
|
|
7334
|
-
import { consola as consola13 } from "consola";
|
|
7335
|
-
async function dispatch(runner) {
|
|
7613
|
+
// src/devcontainer/browser-bridge.ts
|
|
7614
|
+
import { spawn as spawn9 } from "child_process";
|
|
7615
|
+
import { existsSync as existsSync11, promises as fsp5, readFileSync as readFileSync5 } from "fs";
|
|
7616
|
+
import http from "http";
|
|
7617
|
+
import path20 from "path";
|
|
7618
|
+
function parseCallbackTarget(authUrl) {
|
|
7336
7619
|
try {
|
|
7337
|
-
const
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7620
|
+
const u = new URL(authUrl);
|
|
7621
|
+
const redirect = u.searchParams.get("redirect_uri");
|
|
7622
|
+
if (!redirect) return null;
|
|
7623
|
+
const r = new URL(redirect);
|
|
7624
|
+
if (r.hostname !== "localhost" && r.hostname !== "127.0.0.1") return null;
|
|
7625
|
+
const port = Number(r.port);
|
|
7626
|
+
if (!Number.isInteger(port) || port <= 0) return null;
|
|
7627
|
+
return { port, pathname: r.pathname };
|
|
7628
|
+
} catch {
|
|
7629
|
+
return null;
|
|
7342
7630
|
}
|
|
7343
7631
|
}
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7632
|
+
function nextRelayUrl(content, lastOpened) {
|
|
7633
|
+
const url = content.trim();
|
|
7634
|
+
return url && url !== lastOpened ? url : null;
|
|
7635
|
+
}
|
|
7636
|
+
function wrapExec(command, opts) {
|
|
7637
|
+
const leading = [];
|
|
7638
|
+
const stmts = [];
|
|
7639
|
+
if (opts.pathPrepend) {
|
|
7640
|
+
leading.push(opts.pathPrepend);
|
|
7641
|
+
const i = leading.length;
|
|
7642
|
+
stmts.push(`export PATH="$${i}:$PATH"`);
|
|
7643
|
+
stmts.push(`export BROWSER="$${i}/xdg-open"`);
|
|
7347
7644
|
}
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
name: {
|
|
7367
|
-
type: "positional",
|
|
7368
|
-
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
7369
|
-
required: true
|
|
7370
|
-
},
|
|
7371
|
-
verbose: {
|
|
7372
|
-
type: "boolean",
|
|
7373
|
-
description: "Stream the raw @devcontainers/cli output to stderr instead of showing a phase spinner. Auto-enabled when stderr is not a TTY.",
|
|
7374
|
-
default: false
|
|
7375
|
-
}
|
|
7376
|
-
},
|
|
7377
|
-
run({ args }) {
|
|
7378
|
-
return dispatch(async () => {
|
|
7379
|
-
const result = await runApply({
|
|
7380
|
-
name: args.name,
|
|
7381
|
-
cliVersion: CLI_VERSION,
|
|
7382
|
-
verbose: args.verbose
|
|
7383
|
-
});
|
|
7384
|
-
return result.containerExitCode;
|
|
7385
|
-
});
|
|
7386
|
-
}
|
|
7645
|
+
if (opts.cwd) {
|
|
7646
|
+
leading.push(opts.cwd);
|
|
7647
|
+
stmts.push(`cd -- "$${leading.length}"`);
|
|
7648
|
+
}
|
|
7649
|
+
if (leading.length === 0) return command;
|
|
7650
|
+
const shift = leading.length === 1 ? "shift" : `shift ${leading.length}`;
|
|
7651
|
+
const script = `${stmts.join(" && ")} && ${shift} && exec "$@"`;
|
|
7652
|
+
return ["bash", "-lc", script, "bash", ...leading, ...command];
|
|
7653
|
+
}
|
|
7654
|
+
function openInBrowser(url) {
|
|
7655
|
+
const platform = process.platform;
|
|
7656
|
+
const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
7657
|
+
try {
|
|
7658
|
+
const child = spawn9(cmd, args, {
|
|
7659
|
+
stdio: "ignore",
|
|
7660
|
+
detached: true
|
|
7661
|
+
});
|
|
7662
|
+
child.on("error", () => {
|
|
7387
7663
|
});
|
|
7664
|
+
child.unref();
|
|
7665
|
+
} catch {
|
|
7388
7666
|
}
|
|
7389
|
-
}
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7667
|
+
}
|
|
7668
|
+
async function startBrowserBridge(opts) {
|
|
7669
|
+
const relayDir = path20.join(opts.root, RELAY_DIRNAME);
|
|
7670
|
+
const relayScript = path20.join(relayDir, "xdg-open");
|
|
7671
|
+
const urlFile = path20.join(relayDir, "url");
|
|
7672
|
+
await fsp5.mkdir(relayDir, { recursive: true });
|
|
7673
|
+
await fsp5.rm(urlFile, { force: true });
|
|
7674
|
+
await fsp5.writeFile(
|
|
7675
|
+
relayScript,
|
|
7676
|
+
`#!/bin/sh
|
|
7677
|
+
printf '%s\\n' "$1" > "$(dirname "$0")/url"
|
|
7678
|
+
exit 0
|
|
7679
|
+
`,
|
|
7680
|
+
{ mode: 493 }
|
|
7681
|
+
);
|
|
7682
|
+
await fsp5.chmod(relayScript, 493);
|
|
7683
|
+
const servers = [];
|
|
7684
|
+
let lastOpened = null;
|
|
7685
|
+
const onUrl = (url) => {
|
|
7686
|
+
openInBrowser(url);
|
|
7687
|
+
const target = parseCallbackTarget(url);
|
|
7688
|
+
if (!target) return;
|
|
7689
|
+
const server = http.createServer((req, res) => {
|
|
7690
|
+
const reqUrl = req.url ?? target.pathname;
|
|
7691
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
7692
|
+
res.end(
|
|
7693
|
+
'<html><body style="font-family:sans-serif;padding:3rem">You are signed in. You can close this tab and return to the terminal.</body></html>'
|
|
7694
|
+
);
|
|
7695
|
+
void opts.spawn(
|
|
7696
|
+
[
|
|
7697
|
+
"exec",
|
|
7698
|
+
"--workspace-folder",
|
|
7699
|
+
opts.root,
|
|
7700
|
+
"--mount-workspace-git-root=false",
|
|
7701
|
+
"curl",
|
|
7702
|
+
"-fsS",
|
|
7703
|
+
`http://localhost:${target.port}${reqUrl}`
|
|
7704
|
+
],
|
|
7705
|
+
opts.root,
|
|
7706
|
+
{ quiet: true }
|
|
7707
|
+
);
|
|
7708
|
+
});
|
|
7709
|
+
server.on("error", () => {
|
|
7710
|
+
});
|
|
7711
|
+
server.listen(target.port, "127.0.0.1");
|
|
7712
|
+
servers.push(server);
|
|
7713
|
+
};
|
|
7714
|
+
const poll = setInterval(() => {
|
|
7715
|
+
if (!existsSync11(urlFile)) return;
|
|
7716
|
+
let content = "";
|
|
7717
|
+
try {
|
|
7718
|
+
content = readFileSync5(urlFile, "utf8");
|
|
7719
|
+
} catch {
|
|
7720
|
+
return;
|
|
7721
|
+
}
|
|
7722
|
+
const url = nextRelayUrl(content, lastOpened);
|
|
7723
|
+
if (!url) return;
|
|
7724
|
+
lastOpened = url;
|
|
7725
|
+
onUrl(url);
|
|
7726
|
+
}, 250);
|
|
7727
|
+
return {
|
|
7728
|
+
relayDirInContainer: `/workspaces/${opts.name}/${RELAY_DIRNAME}`,
|
|
7729
|
+
async dispose() {
|
|
7730
|
+
clearInterval(poll);
|
|
7731
|
+
for (const s of servers) s.close();
|
|
7732
|
+
await fsp5.rm(relayDir, { recursive: true, force: true });
|
|
7733
|
+
}
|
|
7734
|
+
};
|
|
7735
|
+
}
|
|
7736
|
+
var RELAY_DIRNAME;
|
|
7737
|
+
var init_browser_bridge = __esm({
|
|
7738
|
+
"src/devcontainer/browser-bridge.ts"() {
|
|
7739
|
+
"use strict";
|
|
7740
|
+
RELAY_DIRNAME = ".monoceros-bridge";
|
|
7741
|
+
}
|
|
7742
|
+
});
|
|
7743
|
+
|
|
7744
|
+
// src/devcontainer/shell.ts
|
|
7745
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7746
|
+
import path21 from "path";
|
|
7747
|
+
async function runShell(opts) {
|
|
7748
|
+
assertContainerExists(opts.root);
|
|
7749
|
+
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
7750
|
+
const upCode = await spawnFn(
|
|
7751
|
+
["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
|
|
7752
|
+
opts.root,
|
|
7753
|
+
{ quiet: true }
|
|
7754
|
+
);
|
|
7755
|
+
if (upCode !== 0) return upCode;
|
|
7756
|
+
const name = opts.name ?? path21.basename(opts.root);
|
|
7757
|
+
const isTty2 = opts.isTty ?? process.stdout.isTTY;
|
|
7758
|
+
const bridge = isTty2 ? await startBrowserBridge({ name, root: opts.root, spawn: spawnFn }) : null;
|
|
7759
|
+
try {
|
|
7760
|
+
const innerExec = wrapExec(
|
|
7761
|
+
["bash"],
|
|
7762
|
+
bridge ? { pathPrepend: bridge.relayDirInContainer } : {}
|
|
7763
|
+
);
|
|
7764
|
+
return await spawnFn(
|
|
7765
|
+
[
|
|
7766
|
+
"exec",
|
|
7767
|
+
"--workspace-folder",
|
|
7768
|
+
opts.root,
|
|
7769
|
+
"--mount-workspace-git-root=false",
|
|
7770
|
+
...innerExec
|
|
7771
|
+
],
|
|
7772
|
+
opts.root,
|
|
7773
|
+
{ interactive: true }
|
|
7774
|
+
);
|
|
7775
|
+
} finally {
|
|
7776
|
+
if (bridge) await bridge.dispose();
|
|
7777
|
+
}
|
|
7778
|
+
}
|
|
7779
|
+
function assertContainerExists(root) {
|
|
7780
|
+
if (!existsSync12(path21.join(root, ".devcontainer"))) {
|
|
7781
|
+
throw new Error(
|
|
7782
|
+
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
7783
|
+
);
|
|
7784
|
+
}
|
|
7785
|
+
}
|
|
7786
|
+
var init_shell = __esm({
|
|
7787
|
+
"src/devcontainer/shell.ts"() {
|
|
7788
|
+
"use strict";
|
|
7789
|
+
init_browser_bridge();
|
|
7790
|
+
init_cli();
|
|
7791
|
+
}
|
|
7792
|
+
});
|
|
7793
|
+
|
|
7794
|
+
// src/open/index.ts
|
|
7795
|
+
import { spawn as spawn10 } from "child_process";
|
|
7796
|
+
import { accessSync, constants, existsSync as existsSync13 } from "fs";
|
|
7797
|
+
import path22 from "path";
|
|
7798
|
+
import { consola as consola13 } from "consola";
|
|
7799
|
+
function resolveOnPath(bin) {
|
|
7800
|
+
const dirs = (process.env.PATH ?? "").split(path22.delimiter).filter(Boolean);
|
|
7801
|
+
for (const dir of dirs) {
|
|
7802
|
+
const candidate = path22.join(dir, bin);
|
|
7803
|
+
try {
|
|
7804
|
+
accessSync(candidate, constants.X_OK);
|
|
7805
|
+
return candidate;
|
|
7806
|
+
} catch {
|
|
7807
|
+
}
|
|
7808
|
+
}
|
|
7809
|
+
return null;
|
|
7810
|
+
}
|
|
7811
|
+
function resolveEditorBinary(tool) {
|
|
7812
|
+
if (resolveOnPath(tool.bin)) return tool.bin;
|
|
7813
|
+
if (process.platform === "darwin" && existsSync13(tool.macAppBin)) {
|
|
7814
|
+
return tool.macAppBin;
|
|
7815
|
+
}
|
|
7816
|
+
return null;
|
|
7817
|
+
}
|
|
7818
|
+
function realLaunch(bin, args) {
|
|
7819
|
+
const child = spawn10(bin, args, {
|
|
7820
|
+
detached: true,
|
|
7821
|
+
stdio: "ignore"
|
|
7822
|
+
});
|
|
7823
|
+
child.unref();
|
|
7824
|
+
}
|
|
7825
|
+
async function runOpen(opts) {
|
|
7826
|
+
const home = opts.monocerosHome ?? monocerosHome();
|
|
7827
|
+
const logger = opts.logger ?? {
|
|
7828
|
+
info: (m) => consola13.info(m),
|
|
7829
|
+
warn: (m) => consola13.warn(m)
|
|
7830
|
+
};
|
|
7831
|
+
const root = containerDir(opts.name, home);
|
|
7832
|
+
if (opts.tool === "shell") {
|
|
7833
|
+
const shellRunner = opts.shellRunner ?? runShell;
|
|
7834
|
+
return shellRunner({ root, name: opts.name });
|
|
7835
|
+
}
|
|
7836
|
+
const editor = EDITORS[opts.tool];
|
|
7837
|
+
if (!editor) {
|
|
7838
|
+
throw new Error(
|
|
7839
|
+
`Unknown tool '${opts.tool}'. Supported: ${OPEN_TOOLS.join(", ")}.`
|
|
7840
|
+
);
|
|
7841
|
+
}
|
|
7842
|
+
if (!existsSync13(sshConfigEntryPath(home, opts.name))) {
|
|
7843
|
+
throw new Error(
|
|
7844
|
+
`SSH attach isn't set up for '${opts.name}'. Run \`monoceros apply ${opts.name}\` first (needs a runtime >= 1.2.0).`
|
|
7845
|
+
);
|
|
7846
|
+
}
|
|
7847
|
+
const running = await findRunningContainerByLocalFolder(
|
|
7848
|
+
root,
|
|
7849
|
+
opts.dockerLookup ? { docker: opts.dockerLookup } : {}
|
|
7850
|
+
);
|
|
7851
|
+
if (!running) {
|
|
7852
|
+
throw new Error(
|
|
7853
|
+
`Container '${opts.name}' isn't running. Bring it up with \`monoceros apply ${opts.name}\`, then retry.`
|
|
7854
|
+
);
|
|
7855
|
+
}
|
|
7856
|
+
const resolve = opts.binResolver ?? resolveEditorBinary;
|
|
7857
|
+
const bin = resolve(editor);
|
|
7858
|
+
if (!bin) {
|
|
7859
|
+
const where = process.platform === "darwin" ? " on PATH or in /Applications" : " on PATH";
|
|
7860
|
+
throw new Error(
|
|
7861
|
+
`${editor.label} ('${editor.bin}') not found${where}. In ${editor.label}, ${editor.setupHint}.`
|
|
7862
|
+
);
|
|
7863
|
+
}
|
|
7864
|
+
const uri = `vscode-remote://ssh-remote+monoceros-${opts.name}/workspaces/${opts.name}/${opts.name}.code-workspace`;
|
|
7865
|
+
const launch = opts.launch ?? realLaunch;
|
|
7866
|
+
launch(bin, ["--file-uri", uri]);
|
|
7867
|
+
logger.info(`Opening '${opts.name}' in ${editor.label}...`);
|
|
7868
|
+
return 0;
|
|
7869
|
+
}
|
|
7870
|
+
var EDITORS, OPEN_TOOLS;
|
|
7871
|
+
var init_open = __esm({
|
|
7872
|
+
"src/open/index.ts"() {
|
|
7873
|
+
"use strict";
|
|
7874
|
+
init_paths();
|
|
7875
|
+
init_locate_running();
|
|
7876
|
+
init_shell();
|
|
7877
|
+
init_ssh_attach();
|
|
7878
|
+
EDITORS = {
|
|
7879
|
+
code: {
|
|
7880
|
+
label: "VS Code",
|
|
7881
|
+
bin: "code",
|
|
7882
|
+
macAppBin: "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
7883
|
+
setupHint: `install the Remote-SSH extension and run "Shell Command: Install 'code' command in PATH"`
|
|
7884
|
+
},
|
|
7885
|
+
codium: {
|
|
7886
|
+
label: "VS Codium",
|
|
7887
|
+
bin: "codium",
|
|
7888
|
+
macAppBin: "/Applications/VSCodium.app/Contents/Resources/app/bin/codium",
|
|
7889
|
+
setupHint: 'install the "Open Remote - SSH" extension (the codium CLI ships with the app)'
|
|
7890
|
+
}
|
|
7891
|
+
};
|
|
7892
|
+
OPEN_TOOLS = [...Object.keys(EDITORS), "shell"];
|
|
7893
|
+
}
|
|
7894
|
+
});
|
|
7895
|
+
|
|
7896
|
+
// src/version.ts
|
|
7897
|
+
var CLI_VERSION;
|
|
7898
|
+
var init_version = __esm({
|
|
7899
|
+
"src/version.ts"() {
|
|
7900
|
+
"use strict";
|
|
7901
|
+
CLI_VERSION = true ? "1.28.0" : "dev";
|
|
7902
|
+
}
|
|
7903
|
+
});
|
|
7904
|
+
|
|
7905
|
+
// src/commands/_dispatch.ts
|
|
7906
|
+
import { consola as consola14 } from "consola";
|
|
7907
|
+
async function dispatch(runner) {
|
|
7908
|
+
try {
|
|
7909
|
+
const exitCode = await runner();
|
|
7910
|
+
process.exit(exitCode);
|
|
7911
|
+
} catch (err) {
|
|
7912
|
+
consola14.error(err instanceof Error ? err.message : String(err));
|
|
7913
|
+
process.exit(1);
|
|
7914
|
+
}
|
|
7915
|
+
}
|
|
7916
|
+
var init_dispatch = __esm({
|
|
7917
|
+
"src/commands/_dispatch.ts"() {
|
|
7918
|
+
"use strict";
|
|
7919
|
+
}
|
|
7920
|
+
});
|
|
7921
|
+
|
|
7922
|
+
// src/commands/apply.ts
|
|
7923
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
7924
|
+
import { consola as consola15 } from "consola";
|
|
7925
|
+
var applyCommand;
|
|
7926
|
+
var init_apply2 = __esm({
|
|
7927
|
+
"src/commands/apply.ts"() {
|
|
7928
|
+
"use strict";
|
|
7929
|
+
init_apply();
|
|
7930
|
+
init_open();
|
|
7931
|
+
init_version();
|
|
7932
|
+
init_dispatch();
|
|
7933
|
+
applyCommand = defineCommand8({
|
|
7934
|
+
meta: {
|
|
7935
|
+
name: "apply",
|
|
7936
|
+
group: "lifecycle",
|
|
7937
|
+
description: "Materialize a container config into $MONOCEROS_HOME/container/<name>/ and bring the dev-container up. Close any VS Code Remote Containers session for the target first \u2014 the extension auto-recreates and races with apply."
|
|
7938
|
+
},
|
|
7939
|
+
args: {
|
|
7940
|
+
name: {
|
|
7941
|
+
type: "positional",
|
|
7942
|
+
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
7943
|
+
required: true
|
|
7944
|
+
},
|
|
7945
|
+
verbose: {
|
|
7946
|
+
type: "boolean",
|
|
7947
|
+
description: "Stream the raw @devcontainers/cli output to stderr instead of showing a phase spinner. Auto-enabled when stderr is not a TTY.",
|
|
7948
|
+
default: false
|
|
7949
|
+
},
|
|
7950
|
+
open: {
|
|
7951
|
+
type: "string",
|
|
7952
|
+
description: `After a successful apply, open the container in this tool (${OPEN_TOOLS.join("|")}).`
|
|
7953
|
+
}
|
|
7954
|
+
},
|
|
7955
|
+
run({ args }) {
|
|
7956
|
+
return dispatch(async () => {
|
|
7957
|
+
const result = await runApply({
|
|
7958
|
+
name: args.name,
|
|
7959
|
+
cliVersion: CLI_VERSION,
|
|
7960
|
+
verbose: args.verbose
|
|
7961
|
+
});
|
|
7962
|
+
if (args.open && result.containerExitCode === 0) {
|
|
7963
|
+
try {
|
|
7964
|
+
await runOpen({ name: args.name, tool: args.open });
|
|
7965
|
+
} catch (err) {
|
|
7966
|
+
consola15.warn(err instanceof Error ? err.message : String(err));
|
|
7967
|
+
}
|
|
7968
|
+
}
|
|
7969
|
+
return result.containerExitCode;
|
|
7970
|
+
});
|
|
7971
|
+
}
|
|
7972
|
+
});
|
|
7973
|
+
}
|
|
7974
|
+
});
|
|
7975
|
+
|
|
7976
|
+
// src/commands/completion.ts
|
|
7977
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
7978
|
+
function renderCompletionScript(shell) {
|
|
7979
|
+
if (shell === "bash") {
|
|
7980
|
+
return [
|
|
7981
|
+
"# bash completion for monoceros",
|
|
7982
|
+
"# install: source this file from .bashrc, e.g.",
|
|
7983
|
+
"# monoceros completion bash > ~/.bash_completion.d/monoceros",
|
|
7984
|
+
'# echo "source ~/.bash_completion.d/monoceros" >> ~/.bashrc',
|
|
7400
7985
|
"#",
|
|
7401
7986
|
"# The work is done by `monoceros __complete --line --point`; this",
|
|
7402
7987
|
"# shell wrapper only forwards the cursor view.",
|
|
@@ -7516,8 +8101,8 @@ var init_completion = __esm({
|
|
|
7516
8101
|
});
|
|
7517
8102
|
|
|
7518
8103
|
// src/completion/resolve.ts
|
|
7519
|
-
import { existsSync as
|
|
7520
|
-
import
|
|
8104
|
+
import { existsSync as existsSync14, promises as fs14 } from "fs";
|
|
8105
|
+
import path23 from "path";
|
|
7521
8106
|
async function resolveCompletions(line, point, opts = {}) {
|
|
7522
8107
|
const { prev, current } = parseCompletionLine(line, point);
|
|
7523
8108
|
const ctx = { prev, current, opts };
|
|
@@ -7666,9 +8251,9 @@ function filterPrefix(values, fragment) {
|
|
|
7666
8251
|
}
|
|
7667
8252
|
async function listContainerNames(ctx) {
|
|
7668
8253
|
const home = ctx.opts.monocerosHome ?? monocerosHome();
|
|
7669
|
-
const dir =
|
|
7670
|
-
if (!
|
|
7671
|
-
const entries = await
|
|
8254
|
+
const dir = path23.join(home, "container-configs");
|
|
8255
|
+
if (!existsSync14(dir)) return [];
|
|
8256
|
+
const entries = await fs14.readdir(dir);
|
|
7672
8257
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length)).sort();
|
|
7673
8258
|
}
|
|
7674
8259
|
async function listFeatureComponents() {
|
|
@@ -7742,10 +8327,12 @@ var init_resolve = __esm({
|
|
|
7742
8327
|
init_manifest();
|
|
7743
8328
|
init_catalog();
|
|
7744
8329
|
init_schema();
|
|
8330
|
+
init_open();
|
|
7745
8331
|
ALL_COMMANDS = [
|
|
7746
8332
|
"init",
|
|
7747
8333
|
"list-components",
|
|
7748
8334
|
"shell",
|
|
8335
|
+
"open",
|
|
7749
8336
|
"run",
|
|
7750
8337
|
"logs",
|
|
7751
8338
|
"start",
|
|
@@ -7795,7 +8382,10 @@ var init_resolve = __esm({
|
|
|
7795
8382
|
},
|
|
7796
8383
|
apply: {
|
|
7797
8384
|
positionals: [containerName],
|
|
7798
|
-
flags: {
|
|
8385
|
+
flags: {
|
|
8386
|
+
"--yes": { type: "boolean", aliases: ["-y"] },
|
|
8387
|
+
"--open": { type: "value", values: () => [...OPEN_TOOLS] }
|
|
8388
|
+
}
|
|
7799
8389
|
},
|
|
7800
8390
|
upgrade: {
|
|
7801
8391
|
// First positional is a container name; the second is a version
|
|
@@ -7811,12 +8401,16 @@ var init_resolve = __esm({
|
|
|
7811
8401
|
}
|
|
7812
8402
|
},
|
|
7813
8403
|
shell: { positionals: [containerName] },
|
|
8404
|
+
open: { positionals: [containerName, () => [...OPEN_TOOLS]] },
|
|
7814
8405
|
run: {
|
|
7815
8406
|
positionals: [containerName],
|
|
7816
8407
|
flags: { "--in": { type: "value" } }
|
|
7817
8408
|
},
|
|
7818
8409
|
logs: { positionals: [containerName] },
|
|
7819
|
-
start: {
|
|
8410
|
+
start: {
|
|
8411
|
+
positionals: [containerName],
|
|
8412
|
+
flags: { "--open": { type: "value", values: () => [...OPEN_TOOLS] } }
|
|
8413
|
+
},
|
|
7820
8414
|
stop: { positionals: [containerName] },
|
|
7821
8415
|
status: { positionals: [containerName] },
|
|
7822
8416
|
"add-language": {
|
|
@@ -8335,14 +8929,14 @@ var init_generator = __esm({
|
|
|
8335
8929
|
});
|
|
8336
8930
|
|
|
8337
8931
|
// src/init/index.ts
|
|
8338
|
-
import { existsSync as
|
|
8339
|
-
import
|
|
8340
|
-
import { consola as
|
|
8932
|
+
import { existsSync as existsSync15, promises as fs15 } from "fs";
|
|
8933
|
+
import path24 from "path";
|
|
8934
|
+
import { consola as consola16 } from "consola";
|
|
8341
8935
|
async function runInit(opts) {
|
|
8342
8936
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
8343
8937
|
const logger = opts.logger ?? {
|
|
8344
|
-
success: (msg) =>
|
|
8345
|
-
info: (msg) =>
|
|
8938
|
+
success: (msg) => consola16.success(msg),
|
|
8939
|
+
info: (msg) => consola16.info(msg)
|
|
8346
8940
|
};
|
|
8347
8941
|
if (!REGEX.solutionName.test(opts.name)) {
|
|
8348
8942
|
throw new Error(
|
|
@@ -8350,12 +8944,12 @@ async function runInit(opts) {
|
|
|
8350
8944
|
);
|
|
8351
8945
|
}
|
|
8352
8946
|
const dest = containerConfigPath(opts.name, home);
|
|
8353
|
-
if (
|
|
8947
|
+
if (existsSync15(dest)) {
|
|
8354
8948
|
throw new Error(
|
|
8355
8949
|
`Config already exists: ${dest}. Delete it manually before re-running \`monoceros init\` \u2014 this protects any hand-edits.`
|
|
8356
8950
|
);
|
|
8357
8951
|
}
|
|
8358
|
-
const componentsRoot = opts.workbenchRoot ?
|
|
8952
|
+
const componentsRoot = opts.workbenchRoot ? path24.join(opts.workbenchRoot, "components") : componentsRootDir();
|
|
8359
8953
|
const catalog = await loadComponentCatalog(componentsRoot);
|
|
8360
8954
|
if (catalog.size === 0) {
|
|
8361
8955
|
throw new Error(
|
|
@@ -8421,9 +9015,9 @@ async function runInit(opts) {
|
|
|
8421
9015
|
} else {
|
|
8422
9016
|
text = generateComposedYml(opts.name, composed, lookup, repos, ports);
|
|
8423
9017
|
}
|
|
8424
|
-
await
|
|
9018
|
+
await fs15.mkdir(containerConfigsDir(home), { recursive: true });
|
|
8425
9019
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
8426
|
-
await
|
|
9020
|
+
await fs15.writeFile(dest, text, "utf8");
|
|
8427
9021
|
const envPath = containerEnvPath(opts.name, home);
|
|
8428
9022
|
const seedVars = {};
|
|
8429
9023
|
for (const f of composed.features) {
|
|
@@ -8446,8 +9040,8 @@ async function runInit(opts) {
|
|
|
8446
9040
|
}
|
|
8447
9041
|
await ensureEnvVars(envPath, opts.name, seedVars);
|
|
8448
9042
|
const documented = !anyComposed;
|
|
8449
|
-
const ymlRel =
|
|
8450
|
-
const envRel =
|
|
9043
|
+
const ymlRel = path24.relative(home, dest);
|
|
9044
|
+
const envRel = path24.relative(home, envPath);
|
|
8451
9045
|
if (documented) {
|
|
8452
9046
|
logger.success(`Wrote documented default to ${ymlRel} and ${envRel}.`);
|
|
8453
9047
|
logger.info(
|
|
@@ -8591,7 +9185,7 @@ var init_init = __esm({
|
|
|
8591
9185
|
|
|
8592
9186
|
// src/commands/init.ts
|
|
8593
9187
|
import { defineCommand as defineCommand11 } from "citty";
|
|
8594
|
-
import { consola as
|
|
9188
|
+
import { consola as consola17 } from "consola";
|
|
8595
9189
|
function collectListFlag(flag, rawArgs) {
|
|
8596
9190
|
const eq = `${flag}=`;
|
|
8597
9191
|
const pieces = [];
|
|
@@ -8717,7 +9311,7 @@ var init_init2 = __esm({
|
|
|
8717
9311
|
...ports && ports.length > 0 ? { withPorts: ports } : {}
|
|
8718
9312
|
});
|
|
8719
9313
|
} catch (err) {
|
|
8720
|
-
|
|
9314
|
+
consola17.error(err instanceof Error ? err.message : String(err));
|
|
8721
9315
|
process.exit(1);
|
|
8722
9316
|
}
|
|
8723
9317
|
}
|
|
@@ -8766,7 +9360,7 @@ var init_expand = __esm({
|
|
|
8766
9360
|
|
|
8767
9361
|
// src/commands/list-components.ts
|
|
8768
9362
|
import { defineCommand as defineCommand12 } from "citty";
|
|
8769
|
-
import { consola as
|
|
9363
|
+
import { consola as consola18 } from "consola";
|
|
8770
9364
|
var CATEGORY_LABELS, CATEGORY_ORDER, listComponentsCommand;
|
|
8771
9365
|
var init_list_components = __esm({
|
|
8772
9366
|
"src/commands/list-components.ts"() {
|
|
@@ -8795,7 +9389,7 @@ var init_list_components = __esm({
|
|
|
8795
9389
|
try {
|
|
8796
9390
|
const catalog = expandSelectable(await loadDescriptorCatalog());
|
|
8797
9391
|
if (catalog.size === 0) {
|
|
8798
|
-
|
|
9392
|
+
consola18.warn(
|
|
8799
9393
|
"No components found. The workbench checkout looks incomplete."
|
|
8800
9394
|
);
|
|
8801
9395
|
process.exit(0);
|
|
@@ -8846,7 +9440,7 @@ var init_list_components = __esm({
|
|
|
8846
9440
|
}
|
|
8847
9441
|
process.exit(0);
|
|
8848
9442
|
} catch (err) {
|
|
8849
|
-
|
|
9443
|
+
consola18.error(err instanceof Error ? err.message : String(err));
|
|
8850
9444
|
process.exit(1);
|
|
8851
9445
|
}
|
|
8852
9446
|
}
|
|
@@ -8855,14 +9449,14 @@ var init_list_components = __esm({
|
|
|
8855
9449
|
});
|
|
8856
9450
|
|
|
8857
9451
|
// src/commands/logs.ts
|
|
8858
|
-
import { spawn as
|
|
8859
|
-
import { existsSync as
|
|
8860
|
-
import
|
|
9452
|
+
import { spawn as spawn11 } from "child_process";
|
|
9453
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9454
|
+
import path25 from "path";
|
|
8861
9455
|
import { defineCommand as defineCommand13 } from "citty";
|
|
8862
9456
|
function tailLogFile(file, follow) {
|
|
8863
9457
|
const [cmd, args] = follow ? ["tail", ["-F", file]] : ["cat", [file]];
|
|
8864
9458
|
return new Promise((resolve, reject) => {
|
|
8865
|
-
const child =
|
|
9459
|
+
const child = spawn11(cmd, args, { stdio: "inherit" });
|
|
8866
9460
|
child.on("error", reject);
|
|
8867
9461
|
child.on("exit", (code) => resolve(code ?? 0));
|
|
8868
9462
|
});
|
|
@@ -8900,8 +9494,8 @@ var init_logs = __esm({
|
|
|
8900
9494
|
run({ args }) {
|
|
8901
9495
|
const service = typeof args.service === "string" ? args.service : void 0;
|
|
8902
9496
|
if (service) {
|
|
8903
|
-
const logFile =
|
|
8904
|
-
if (
|
|
9497
|
+
const logFile = path25.join(containerLogsDir(args.name), `${service}.log`);
|
|
9498
|
+
if (existsSync16(logFile)) {
|
|
8905
9499
|
return dispatch(() => tailLogFile(logFile, args.follow));
|
|
8906
9500
|
}
|
|
8907
9501
|
}
|
|
@@ -8917,12 +9511,45 @@ var init_logs = __esm({
|
|
|
8917
9511
|
}
|
|
8918
9512
|
});
|
|
8919
9513
|
|
|
8920
|
-
// src/commands/
|
|
9514
|
+
// src/commands/open.ts
|
|
8921
9515
|
import { defineCommand as defineCommand14 } from "citty";
|
|
8922
|
-
|
|
9516
|
+
var openCommand;
|
|
9517
|
+
var init_open2 = __esm({
|
|
9518
|
+
"src/commands/open.ts"() {
|
|
9519
|
+
"use strict";
|
|
9520
|
+
init_open();
|
|
9521
|
+
init_dispatch();
|
|
9522
|
+
openCommand = defineCommand14({
|
|
9523
|
+
meta: {
|
|
9524
|
+
name: "open",
|
|
9525
|
+
group: "run",
|
|
9526
|
+
description: `Attach an editor to the named dev-container over SSH, or drop into a shell. Tools: ${OPEN_TOOLS.join(", ")}. The container must be applied (runtime >= 1.2.0) and running.`
|
|
9527
|
+
},
|
|
9528
|
+
args: {
|
|
9529
|
+
name: {
|
|
9530
|
+
type: "positional",
|
|
9531
|
+
description: "Container name (yml in $MONOCEROS_HOME/container-configs/).",
|
|
9532
|
+
required: true
|
|
9533
|
+
},
|
|
9534
|
+
tool: {
|
|
9535
|
+
type: "positional",
|
|
9536
|
+
description: `What to open it in: ${OPEN_TOOLS.join(", ")}.`,
|
|
9537
|
+
required: true
|
|
9538
|
+
}
|
|
9539
|
+
},
|
|
9540
|
+
run({ args }) {
|
|
9541
|
+
return dispatch(() => runOpen({ name: args.name, tool: args.tool }));
|
|
9542
|
+
}
|
|
9543
|
+
});
|
|
9544
|
+
}
|
|
9545
|
+
});
|
|
9546
|
+
|
|
9547
|
+
// src/commands/port.ts
|
|
9548
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
9549
|
+
import { consola as consola19 } from "consola";
|
|
8923
9550
|
async function runPortListing(opts) {
|
|
8924
9551
|
const out = opts.out ?? process.stdout;
|
|
8925
|
-
const info = opts.info ?? ((m) =>
|
|
9552
|
+
const info = opts.info ?? ((m) => consola19.info(m));
|
|
8926
9553
|
const parsed = await readConfig(
|
|
8927
9554
|
containerConfigPath(opts.name, opts.monocerosHome)
|
|
8928
9555
|
);
|
|
@@ -8980,7 +9607,7 @@ var init_port = __esm({
|
|
|
8980
9607
|
init_schema();
|
|
8981
9608
|
init_dynamic();
|
|
8982
9609
|
init_format();
|
|
8983
|
-
portCommand =
|
|
9610
|
+
portCommand = defineCommand15({
|
|
8984
9611
|
meta: {
|
|
8985
9612
|
name: "port",
|
|
8986
9613
|
group: "discovery",
|
|
@@ -8998,7 +9625,7 @@ var init_port = __esm({
|
|
|
8998
9625
|
const code = await runPortListing({ name: args.name });
|
|
8999
9626
|
process.exit(code);
|
|
9000
9627
|
} catch (err) {
|
|
9001
|
-
|
|
9628
|
+
consola19.error(err instanceof Error ? err.message : String(err));
|
|
9002
9629
|
process.exit(1);
|
|
9003
9630
|
}
|
|
9004
9631
|
}
|
|
@@ -9007,15 +9634,15 @@ var init_port = __esm({
|
|
|
9007
9634
|
});
|
|
9008
9635
|
|
|
9009
9636
|
// src/commands/remove-apt-packages.ts
|
|
9010
|
-
import { defineCommand as
|
|
9011
|
-
import { consola as
|
|
9637
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
9638
|
+
import { consola as consola20 } from "consola";
|
|
9012
9639
|
var removeAptPackagesCommand;
|
|
9013
9640
|
var init_remove_apt_packages = __esm({
|
|
9014
9641
|
"src/commands/remove-apt-packages.ts"() {
|
|
9015
9642
|
"use strict";
|
|
9016
9643
|
init_inner_args();
|
|
9017
9644
|
init_modify();
|
|
9018
|
-
removeAptPackagesCommand =
|
|
9645
|
+
removeAptPackagesCommand = defineCommand16({
|
|
9019
9646
|
meta: {
|
|
9020
9647
|
name: "remove-apt-packages",
|
|
9021
9648
|
group: "edit",
|
|
@@ -9037,7 +9664,7 @@ var init_remove_apt_packages = __esm({
|
|
|
9037
9664
|
async run({ args }) {
|
|
9038
9665
|
const packages = [...getInnerArgs()];
|
|
9039
9666
|
if (packages.length === 0) {
|
|
9040
|
-
|
|
9667
|
+
consola20.error(
|
|
9041
9668
|
"No package names given. Usage: `monoceros remove-apt-packages <containername> [--yes] -- <pkg> [<pkg> \u2026]`."
|
|
9042
9669
|
);
|
|
9043
9670
|
process.exit(1);
|
|
@@ -9050,7 +9677,7 @@ var init_remove_apt_packages = __esm({
|
|
|
9050
9677
|
});
|
|
9051
9678
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9052
9679
|
} catch (err) {
|
|
9053
|
-
|
|
9680
|
+
consola20.error(err instanceof Error ? err.message : String(err));
|
|
9054
9681
|
process.exit(1);
|
|
9055
9682
|
}
|
|
9056
9683
|
}
|
|
@@ -9059,14 +9686,14 @@ var init_remove_apt_packages = __esm({
|
|
|
9059
9686
|
});
|
|
9060
9687
|
|
|
9061
9688
|
// src/commands/remove-feature.ts
|
|
9062
|
-
import { defineCommand as
|
|
9063
|
-
import { consola as
|
|
9689
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
9690
|
+
import { consola as consola21 } from "consola";
|
|
9064
9691
|
var removeFeatureCommand;
|
|
9065
9692
|
var init_remove_feature = __esm({
|
|
9066
9693
|
"src/commands/remove-feature.ts"() {
|
|
9067
9694
|
"use strict";
|
|
9068
9695
|
init_modify();
|
|
9069
|
-
removeFeatureCommand =
|
|
9696
|
+
removeFeatureCommand = defineCommand17({
|
|
9070
9697
|
meta: {
|
|
9071
9698
|
name: "remove-feature",
|
|
9072
9699
|
group: "edit",
|
|
@@ -9099,7 +9726,7 @@ var init_remove_feature = __esm({
|
|
|
9099
9726
|
});
|
|
9100
9727
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9101
9728
|
} catch (err) {
|
|
9102
|
-
|
|
9729
|
+
consola21.error(err instanceof Error ? err.message : String(err));
|
|
9103
9730
|
process.exit(1);
|
|
9104
9731
|
}
|
|
9105
9732
|
}
|
|
@@ -9108,15 +9735,15 @@ var init_remove_feature = __esm({
|
|
|
9108
9735
|
});
|
|
9109
9736
|
|
|
9110
9737
|
// src/remove/index.ts
|
|
9111
|
-
import { existsSync as
|
|
9112
|
-
import
|
|
9113
|
-
import { consola as
|
|
9738
|
+
import { existsSync as existsSync17, promises as fs16 } from "fs";
|
|
9739
|
+
import path26 from "path";
|
|
9740
|
+
import { consola as consola22 } from "consola";
|
|
9114
9741
|
async function runRemove(opts) {
|
|
9115
9742
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
9116
9743
|
const logger = opts.logger ?? {
|
|
9117
|
-
info: (msg) =>
|
|
9118
|
-
success: (msg) =>
|
|
9119
|
-
warn: (msg) =>
|
|
9744
|
+
info: (msg) => consola22.info(msg),
|
|
9745
|
+
success: (msg) => consola22.success(msg),
|
|
9746
|
+
warn: (msg) => consola22.warn(msg)
|
|
9120
9747
|
};
|
|
9121
9748
|
if (!REGEX.solutionName.test(opts.name)) {
|
|
9122
9749
|
throw new Error(
|
|
@@ -9126,9 +9753,9 @@ async function runRemove(opts) {
|
|
|
9126
9753
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
9127
9754
|
const envPath = containerEnvPath(opts.name, home);
|
|
9128
9755
|
const containerPath = containerDir(opts.name, home);
|
|
9129
|
-
const hasYml =
|
|
9130
|
-
const hasEnv =
|
|
9131
|
-
const hasContainer =
|
|
9756
|
+
const hasYml = existsSync17(ymlPath);
|
|
9757
|
+
const hasEnv = existsSync17(envPath);
|
|
9758
|
+
const hasContainer = existsSync17(containerPath);
|
|
9132
9759
|
if (!hasYml && !hasContainer) {
|
|
9133
9760
|
throw new Error(
|
|
9134
9761
|
`Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
|
|
@@ -9149,35 +9776,35 @@ async function runRemove(opts) {
|
|
|
9149
9776
|
logger,
|
|
9150
9777
|
exec: dockerExec
|
|
9151
9778
|
});
|
|
9152
|
-
const ideVolumes = ideStateVolumes(opts.name).map((v) => v.volume);
|
|
9779
|
+
const ideVolumes = ideStateVolumes(opts.name).filter((v) => !v.shared).map((v) => v.volume);
|
|
9153
9780
|
await dockerExec(["volume", "rm", "-f", ...ideVolumes]);
|
|
9154
9781
|
let backupPath = null;
|
|
9155
9782
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
9156
9783
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9157
|
-
backupPath =
|
|
9158
|
-
await
|
|
9784
|
+
backupPath = path26.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
9785
|
+
await fs16.mkdir(backupPath, { recursive: true });
|
|
9159
9786
|
if (hasYml) {
|
|
9160
|
-
await
|
|
9787
|
+
await fs16.copyFile(ymlPath, path26.join(backupPath, `${opts.name}.yml`));
|
|
9161
9788
|
}
|
|
9162
9789
|
if (hasEnv) {
|
|
9163
|
-
await
|
|
9790
|
+
await fs16.copyFile(envPath, path26.join(backupPath, `${opts.name}.env`));
|
|
9164
9791
|
}
|
|
9165
9792
|
if (hasContainer) {
|
|
9166
|
-
await
|
|
9793
|
+
await fs16.cp(containerPath, path26.join(backupPath, "container"), {
|
|
9167
9794
|
recursive: true
|
|
9168
9795
|
});
|
|
9169
9796
|
}
|
|
9170
9797
|
logger.info(`Backup written to ${prettyPath(backupPath)}.`);
|
|
9171
9798
|
}
|
|
9172
9799
|
if (hasYml) {
|
|
9173
|
-
await
|
|
9800
|
+
await fs16.rm(ymlPath, { force: true });
|
|
9174
9801
|
}
|
|
9175
9802
|
if (hasEnv) {
|
|
9176
|
-
await
|
|
9803
|
+
await fs16.rm(envPath, { force: true });
|
|
9177
9804
|
}
|
|
9178
9805
|
if (hasContainer) {
|
|
9179
9806
|
try {
|
|
9180
|
-
await
|
|
9807
|
+
await fs16.rm(containerPath, { recursive: true, force: true });
|
|
9181
9808
|
} catch (err) {
|
|
9182
9809
|
const code = err.code;
|
|
9183
9810
|
if (code !== "EACCES" && code !== "EPERM") {
|
|
@@ -9199,7 +9826,7 @@ async function runRemove(opts) {
|
|
|
9199
9826
|
"-delete"
|
|
9200
9827
|
]);
|
|
9201
9828
|
try {
|
|
9202
|
-
await
|
|
9829
|
+
await fs16.rm(containerPath, { recursive: true, force: true });
|
|
9203
9830
|
} catch (err2) {
|
|
9204
9831
|
const code2 = err2.code;
|
|
9205
9832
|
throw new Error(
|
|
@@ -9213,7 +9840,14 @@ async function runRemove(opts) {
|
|
|
9213
9840
|
);
|
|
9214
9841
|
if (!backupPath) {
|
|
9215
9842
|
logger.warn?.(
|
|
9216
|
-
"No backup created (--no-backup). The host-side state is gone for good."
|
|
9843
|
+
"No backup created (--no-backup). The host-side state is gone for good."
|
|
9844
|
+
);
|
|
9845
|
+
}
|
|
9846
|
+
try {
|
|
9847
|
+
await removeSshAttach(home, opts.name);
|
|
9848
|
+
} catch (err) {
|
|
9849
|
+
logger.warn?.(
|
|
9850
|
+
`Could not remove SSH attach config for ${opts.name}: ${err instanceof Error ? err.message : String(err)}. Ignored.`
|
|
9217
9851
|
);
|
|
9218
9852
|
}
|
|
9219
9853
|
try {
|
|
@@ -9248,21 +9882,22 @@ var init_remove = __esm({
|
|
|
9248
9882
|
init_schema();
|
|
9249
9883
|
init_compose();
|
|
9250
9884
|
init_scaffold();
|
|
9885
|
+
init_ssh_attach();
|
|
9251
9886
|
init_proxy();
|
|
9252
9887
|
init_dynamic();
|
|
9253
9888
|
}
|
|
9254
9889
|
});
|
|
9255
9890
|
|
|
9256
9891
|
// src/commands/remove.ts
|
|
9257
|
-
import { defineCommand as
|
|
9258
|
-
import { consola as
|
|
9892
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
9893
|
+
import { consola as consola23 } from "consola";
|
|
9259
9894
|
import { createInterface } from "readline/promises";
|
|
9260
9895
|
var removeCommand;
|
|
9261
9896
|
var init_remove2 = __esm({
|
|
9262
9897
|
"src/commands/remove.ts"() {
|
|
9263
9898
|
"use strict";
|
|
9264
9899
|
init_remove();
|
|
9265
|
-
removeCommand =
|
|
9900
|
+
removeCommand = defineCommand18({
|
|
9266
9901
|
meta: {
|
|
9267
9902
|
name: "remove",
|
|
9268
9903
|
group: "lifecycle",
|
|
@@ -9299,7 +9934,7 @@ var init_remove2 = __esm({
|
|
|
9299
9934
|
const skipPrompt = args.yes === true;
|
|
9300
9935
|
if (!skipPrompt) {
|
|
9301
9936
|
const warning = noBackup ? `About to remove '${args.name}' WITHOUT a backup. Docker objects, container-configs entry, and container directory will all be deleted.` : `About to remove '${args.name}'. A backup will be written to container-backups/ first, then docker objects, container-configs entry, and container directory will all be deleted.`;
|
|
9302
|
-
|
|
9937
|
+
consola23.warn(warning);
|
|
9303
9938
|
const rl = createInterface({
|
|
9304
9939
|
input: process.stdin,
|
|
9305
9940
|
output: process.stdout
|
|
@@ -9307,7 +9942,7 @@ var init_remove2 = __esm({
|
|
|
9307
9942
|
const answer = await rl.question("Continue? [y/N] ");
|
|
9308
9943
|
rl.close();
|
|
9309
9944
|
if (!/^y(es)?$/i.test(answer.trim())) {
|
|
9310
|
-
|
|
9945
|
+
consola23.info("Aborted. Nothing changed.");
|
|
9311
9946
|
process.exit(0);
|
|
9312
9947
|
}
|
|
9313
9948
|
}
|
|
@@ -9316,7 +9951,7 @@ var init_remove2 = __esm({
|
|
|
9316
9951
|
...noBackup ? { noBackup: true } : {}
|
|
9317
9952
|
});
|
|
9318
9953
|
} catch (err) {
|
|
9319
|
-
|
|
9954
|
+
consola23.error(err instanceof Error ? err.message : String(err));
|
|
9320
9955
|
process.exit(1);
|
|
9321
9956
|
}
|
|
9322
9957
|
}
|
|
@@ -9325,24 +9960,24 @@ var init_remove2 = __esm({
|
|
|
9325
9960
|
});
|
|
9326
9961
|
|
|
9327
9962
|
// src/restore/index.ts
|
|
9328
|
-
import { existsSync as
|
|
9329
|
-
import
|
|
9330
|
-
import { consola as
|
|
9963
|
+
import { existsSync as existsSync18, promises as fs17 } from "fs";
|
|
9964
|
+
import path27 from "path";
|
|
9965
|
+
import { consola as consola24 } from "consola";
|
|
9331
9966
|
async function runRestore(opts) {
|
|
9332
9967
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
9333
9968
|
const logger = opts.logger ?? {
|
|
9334
|
-
info: (msg) =>
|
|
9335
|
-
success: (msg) =>
|
|
9969
|
+
info: (msg) => consola24.info(msg),
|
|
9970
|
+
success: (msg) => consola24.success(msg)
|
|
9336
9971
|
};
|
|
9337
|
-
const backup =
|
|
9338
|
-
if (!
|
|
9972
|
+
const backup = path27.resolve(opts.backupPath);
|
|
9973
|
+
if (!existsSync18(backup)) {
|
|
9339
9974
|
throw new Error(`Backup not found: ${backup}.`);
|
|
9340
9975
|
}
|
|
9341
|
-
const stat = await
|
|
9976
|
+
const stat = await fs17.stat(backup);
|
|
9342
9977
|
if (!stat.isDirectory()) {
|
|
9343
9978
|
throw new Error(`Backup path is not a directory: ${backup}.`);
|
|
9344
9979
|
}
|
|
9345
|
-
const entries = await
|
|
9980
|
+
const entries = await fs17.readdir(backup);
|
|
9346
9981
|
const ymlFiles = entries.filter((f) => f.endsWith(".yml"));
|
|
9347
9982
|
if (ymlFiles.length === 0) {
|
|
9348
9983
|
throw new Error(
|
|
@@ -9356,29 +9991,29 @@ async function runRestore(opts) {
|
|
|
9356
9991
|
}
|
|
9357
9992
|
const ymlFile = ymlFiles[0];
|
|
9358
9993
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
9359
|
-
const containerInBackup =
|
|
9360
|
-
const hasContainer =
|
|
9361
|
-
const envInBackup =
|
|
9362
|
-
const hasEnv =
|
|
9994
|
+
const containerInBackup = path27.join(backup, "container");
|
|
9995
|
+
const hasContainer = existsSync18(containerInBackup);
|
|
9996
|
+
const envInBackup = path27.join(backup, `${name}.env`);
|
|
9997
|
+
const hasEnv = existsSync18(envInBackup);
|
|
9363
9998
|
const destYml = containerConfigPath(name, home);
|
|
9364
9999
|
const destContainer = containerDir(name, home);
|
|
9365
|
-
if (
|
|
10000
|
+
if (existsSync18(destYml)) {
|
|
9366
10001
|
throw new Error(
|
|
9367
10002
|
`Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
|
|
9368
10003
|
);
|
|
9369
10004
|
}
|
|
9370
|
-
if (hasContainer &&
|
|
10005
|
+
if (hasContainer && existsSync18(destContainer)) {
|
|
9371
10006
|
throw new Error(
|
|
9372
10007
|
`Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
|
|
9373
10008
|
);
|
|
9374
10009
|
}
|
|
9375
|
-
await
|
|
9376
|
-
await
|
|
10010
|
+
await fs17.mkdir(containerConfigsDir(home), { recursive: true });
|
|
10011
|
+
await fs17.copyFile(path27.join(backup, ymlFile), destYml);
|
|
9377
10012
|
if (hasEnv) {
|
|
9378
|
-
await
|
|
10013
|
+
await fs17.copyFile(envInBackup, containerEnvPath(name, home));
|
|
9379
10014
|
}
|
|
9380
10015
|
if (hasContainer) {
|
|
9381
|
-
await
|
|
10016
|
+
await fs17.cp(containerInBackup, destContainer, { recursive: true });
|
|
9382
10017
|
}
|
|
9383
10018
|
logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);
|
|
9384
10019
|
logger.info(
|
|
@@ -9398,14 +10033,14 @@ var init_restore = __esm({
|
|
|
9398
10033
|
});
|
|
9399
10034
|
|
|
9400
10035
|
// src/commands/restore.ts
|
|
9401
|
-
import { defineCommand as
|
|
9402
|
-
import { consola as
|
|
10036
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
10037
|
+
import { consola as consola25 } from "consola";
|
|
9403
10038
|
var restoreCommand;
|
|
9404
10039
|
var init_restore2 = __esm({
|
|
9405
10040
|
"src/commands/restore.ts"() {
|
|
9406
10041
|
"use strict";
|
|
9407
10042
|
init_restore();
|
|
9408
|
-
restoreCommand =
|
|
10043
|
+
restoreCommand = defineCommand19({
|
|
9409
10044
|
meta: {
|
|
9410
10045
|
name: "restore",
|
|
9411
10046
|
group: "lifecycle",
|
|
@@ -9422,7 +10057,7 @@ var init_restore2 = __esm({
|
|
|
9422
10057
|
try {
|
|
9423
10058
|
await runRestore({ backupPath: args["backup-path"] });
|
|
9424
10059
|
} catch (err) {
|
|
9425
|
-
|
|
10060
|
+
consola25.error(err instanceof Error ? err.message : String(err));
|
|
9426
10061
|
process.exit(1);
|
|
9427
10062
|
}
|
|
9428
10063
|
}
|
|
@@ -9431,14 +10066,14 @@ var init_restore2 = __esm({
|
|
|
9431
10066
|
});
|
|
9432
10067
|
|
|
9433
10068
|
// src/commands/remove-from-url.ts
|
|
9434
|
-
import { defineCommand as
|
|
9435
|
-
import { consola as
|
|
10069
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
10070
|
+
import { consola as consola26 } from "consola";
|
|
9436
10071
|
var removeFromUrlCommand;
|
|
9437
10072
|
var init_remove_from_url = __esm({
|
|
9438
10073
|
"src/commands/remove-from-url.ts"() {
|
|
9439
10074
|
"use strict";
|
|
9440
10075
|
init_modify();
|
|
9441
|
-
removeFromUrlCommand =
|
|
10076
|
+
removeFromUrlCommand = defineCommand20({
|
|
9442
10077
|
meta: {
|
|
9443
10078
|
name: "remove-from-url",
|
|
9444
10079
|
group: "edit",
|
|
@@ -9471,7 +10106,7 @@ var init_remove_from_url = __esm({
|
|
|
9471
10106
|
});
|
|
9472
10107
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9473
10108
|
} catch (err) {
|
|
9474
|
-
|
|
10109
|
+
consola26.error(err instanceof Error ? err.message : String(err));
|
|
9475
10110
|
process.exit(1);
|
|
9476
10111
|
}
|
|
9477
10112
|
}
|
|
@@ -9480,14 +10115,14 @@ var init_remove_from_url = __esm({
|
|
|
9480
10115
|
});
|
|
9481
10116
|
|
|
9482
10117
|
// src/commands/remove-language.ts
|
|
9483
|
-
import { defineCommand as
|
|
9484
|
-
import { consola as
|
|
10118
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
10119
|
+
import { consola as consola27 } from "consola";
|
|
9485
10120
|
var removeLanguageCommand;
|
|
9486
10121
|
var init_remove_language = __esm({
|
|
9487
10122
|
"src/commands/remove-language.ts"() {
|
|
9488
10123
|
"use strict";
|
|
9489
10124
|
init_modify();
|
|
9490
|
-
removeLanguageCommand =
|
|
10125
|
+
removeLanguageCommand = defineCommand21({
|
|
9491
10126
|
meta: {
|
|
9492
10127
|
name: "remove-language",
|
|
9493
10128
|
group: "edit",
|
|
@@ -9520,7 +10155,7 @@ var init_remove_language = __esm({
|
|
|
9520
10155
|
});
|
|
9521
10156
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9522
10157
|
} catch (err) {
|
|
9523
|
-
|
|
10158
|
+
consola27.error(err instanceof Error ? err.message : String(err));
|
|
9524
10159
|
process.exit(1);
|
|
9525
10160
|
}
|
|
9526
10161
|
}
|
|
@@ -9529,8 +10164,8 @@ var init_remove_language = __esm({
|
|
|
9529
10164
|
});
|
|
9530
10165
|
|
|
9531
10166
|
// src/commands/remove-port.ts
|
|
9532
|
-
import { defineCommand as
|
|
9533
|
-
import { consola as
|
|
10167
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
10168
|
+
import { consola as consola28 } from "consola";
|
|
9534
10169
|
function coerceToken2(raw) {
|
|
9535
10170
|
const n = Number(raw);
|
|
9536
10171
|
return Number.isFinite(n) ? n : raw;
|
|
@@ -9541,7 +10176,7 @@ var init_remove_port = __esm({
|
|
|
9541
10176
|
"use strict";
|
|
9542
10177
|
init_inner_args();
|
|
9543
10178
|
init_modify();
|
|
9544
|
-
removePortCommand =
|
|
10179
|
+
removePortCommand = defineCommand22({
|
|
9545
10180
|
meta: {
|
|
9546
10181
|
name: "remove-port",
|
|
9547
10182
|
group: "edit",
|
|
@@ -9563,7 +10198,7 @@ var init_remove_port = __esm({
|
|
|
9563
10198
|
async run({ args }) {
|
|
9564
10199
|
const tokens = [...getInnerArgs()];
|
|
9565
10200
|
if (tokens.length === 0) {
|
|
9566
|
-
|
|
10201
|
+
consola28.error(
|
|
9567
10202
|
"No ports given. Usage: `monoceros remove-port <containername> [--yes] -- <port> [<port> \u2026]`."
|
|
9568
10203
|
);
|
|
9569
10204
|
process.exit(1);
|
|
@@ -9576,7 +10211,7 @@ var init_remove_port = __esm({
|
|
|
9576
10211
|
});
|
|
9577
10212
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9578
10213
|
} catch (err) {
|
|
9579
|
-
|
|
10214
|
+
consola28.error(err instanceof Error ? err.message : String(err));
|
|
9580
10215
|
process.exit(1);
|
|
9581
10216
|
}
|
|
9582
10217
|
}
|
|
@@ -9585,14 +10220,14 @@ var init_remove_port = __esm({
|
|
|
9585
10220
|
});
|
|
9586
10221
|
|
|
9587
10222
|
// src/commands/remove-repo.ts
|
|
9588
|
-
import { defineCommand as
|
|
9589
|
-
import { consola as
|
|
10223
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
10224
|
+
import { consola as consola29 } from "consola";
|
|
9590
10225
|
var removeRepoCommand;
|
|
9591
10226
|
var init_remove_repo = __esm({
|
|
9592
10227
|
"src/commands/remove-repo.ts"() {
|
|
9593
10228
|
"use strict";
|
|
9594
10229
|
init_modify();
|
|
9595
|
-
removeRepoCommand =
|
|
10230
|
+
removeRepoCommand = defineCommand23({
|
|
9596
10231
|
meta: {
|
|
9597
10232
|
name: "remove-repo",
|
|
9598
10233
|
group: "edit",
|
|
@@ -9625,7 +10260,7 @@ var init_remove_repo = __esm({
|
|
|
9625
10260
|
});
|
|
9626
10261
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9627
10262
|
} catch (err) {
|
|
9628
|
-
|
|
10263
|
+
consola29.error(err instanceof Error ? err.message : String(err));
|
|
9629
10264
|
process.exit(1);
|
|
9630
10265
|
}
|
|
9631
10266
|
}
|
|
@@ -9634,14 +10269,14 @@ var init_remove_repo = __esm({
|
|
|
9634
10269
|
});
|
|
9635
10270
|
|
|
9636
10271
|
// src/commands/remove-service.ts
|
|
9637
|
-
import { defineCommand as
|
|
9638
|
-
import { consola as
|
|
10272
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
10273
|
+
import { consola as consola30 } from "consola";
|
|
9639
10274
|
var removeServiceCommand;
|
|
9640
10275
|
var init_remove_service = __esm({
|
|
9641
10276
|
"src/commands/remove-service.ts"() {
|
|
9642
10277
|
"use strict";
|
|
9643
10278
|
init_modify();
|
|
9644
|
-
removeServiceCommand =
|
|
10279
|
+
removeServiceCommand = defineCommand24({
|
|
9645
10280
|
meta: {
|
|
9646
10281
|
name: "remove-service",
|
|
9647
10282
|
group: "edit",
|
|
@@ -9674,7 +10309,7 @@ var init_remove_service = __esm({
|
|
|
9674
10309
|
});
|
|
9675
10310
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9676
10311
|
} catch (err) {
|
|
9677
|
-
|
|
10312
|
+
consola30.error(err instanceof Error ? err.message : String(err));
|
|
9678
10313
|
process.exit(1);
|
|
9679
10314
|
}
|
|
9680
10315
|
}
|
|
@@ -9682,148 +10317,17 @@ var init_remove_service = __esm({
|
|
|
9682
10317
|
}
|
|
9683
10318
|
});
|
|
9684
10319
|
|
|
9685
|
-
// src/devcontainer/browser-bridge.ts
|
|
9686
|
-
import { spawn as spawn9 } from "child_process";
|
|
9687
|
-
import { existsSync as existsSync15, promises as fsp5, readFileSync as readFileSync5 } from "fs";
|
|
9688
|
-
import http from "http";
|
|
9689
|
-
import path24 from "path";
|
|
9690
|
-
function parseCallbackTarget(authUrl) {
|
|
9691
|
-
try {
|
|
9692
|
-
const u = new URL(authUrl);
|
|
9693
|
-
const redirect = u.searchParams.get("redirect_uri");
|
|
9694
|
-
if (!redirect) return null;
|
|
9695
|
-
const r = new URL(redirect);
|
|
9696
|
-
if (r.hostname !== "localhost" && r.hostname !== "127.0.0.1") return null;
|
|
9697
|
-
const port = Number(r.port);
|
|
9698
|
-
if (!Number.isInteger(port) || port <= 0) return null;
|
|
9699
|
-
return { port, pathname: r.pathname };
|
|
9700
|
-
} catch {
|
|
9701
|
-
return null;
|
|
9702
|
-
}
|
|
9703
|
-
}
|
|
9704
|
-
function nextRelayUrl(content, lastOpened) {
|
|
9705
|
-
const url = content.trim();
|
|
9706
|
-
return url && url !== lastOpened ? url : null;
|
|
9707
|
-
}
|
|
9708
|
-
function wrapExec(command, opts) {
|
|
9709
|
-
const leading = [];
|
|
9710
|
-
const stmts = [];
|
|
9711
|
-
if (opts.pathPrepend) {
|
|
9712
|
-
leading.push(opts.pathPrepend);
|
|
9713
|
-
const i = leading.length;
|
|
9714
|
-
stmts.push(`export PATH="$${i}:$PATH"`);
|
|
9715
|
-
stmts.push(`export BROWSER="$${i}/xdg-open"`);
|
|
9716
|
-
}
|
|
9717
|
-
if (opts.cwd) {
|
|
9718
|
-
leading.push(opts.cwd);
|
|
9719
|
-
stmts.push(`cd -- "$${leading.length}"`);
|
|
9720
|
-
}
|
|
9721
|
-
if (leading.length === 0) return command;
|
|
9722
|
-
const shift = leading.length === 1 ? "shift" : `shift ${leading.length}`;
|
|
9723
|
-
const script = `${stmts.join(" && ")} && ${shift} && exec "$@"`;
|
|
9724
|
-
return ["bash", "-lc", script, "bash", ...leading, ...command];
|
|
9725
|
-
}
|
|
9726
|
-
function openInBrowser(url) {
|
|
9727
|
-
const platform = process.platform;
|
|
9728
|
-
const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
9729
|
-
try {
|
|
9730
|
-
const child = spawn9(cmd, args, {
|
|
9731
|
-
stdio: "ignore",
|
|
9732
|
-
detached: true
|
|
9733
|
-
});
|
|
9734
|
-
child.on("error", () => {
|
|
9735
|
-
});
|
|
9736
|
-
child.unref();
|
|
9737
|
-
} catch {
|
|
9738
|
-
}
|
|
9739
|
-
}
|
|
9740
|
-
async function startBrowserBridge(opts) {
|
|
9741
|
-
const relayDir = path24.join(opts.root, RELAY_DIRNAME);
|
|
9742
|
-
const relayScript = path24.join(relayDir, "xdg-open");
|
|
9743
|
-
const urlFile = path24.join(relayDir, "url");
|
|
9744
|
-
await fsp5.mkdir(relayDir, { recursive: true });
|
|
9745
|
-
await fsp5.rm(urlFile, { force: true });
|
|
9746
|
-
await fsp5.writeFile(
|
|
9747
|
-
relayScript,
|
|
9748
|
-
`#!/bin/sh
|
|
9749
|
-
printf '%s\\n' "$1" > "$(dirname "$0")/url"
|
|
9750
|
-
exit 0
|
|
9751
|
-
`,
|
|
9752
|
-
{ mode: 493 }
|
|
9753
|
-
);
|
|
9754
|
-
await fsp5.chmod(relayScript, 493);
|
|
9755
|
-
const servers = [];
|
|
9756
|
-
let lastOpened = null;
|
|
9757
|
-
const onUrl = (url) => {
|
|
9758
|
-
openInBrowser(url);
|
|
9759
|
-
const target = parseCallbackTarget(url);
|
|
9760
|
-
if (!target) return;
|
|
9761
|
-
const server = http.createServer((req, res) => {
|
|
9762
|
-
const reqUrl = req.url ?? target.pathname;
|
|
9763
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
9764
|
-
res.end(
|
|
9765
|
-
'<html><body style="font-family:sans-serif;padding:3rem">You are signed in. You can close this tab and return to the terminal.</body></html>'
|
|
9766
|
-
);
|
|
9767
|
-
void opts.spawn(
|
|
9768
|
-
[
|
|
9769
|
-
"exec",
|
|
9770
|
-
"--workspace-folder",
|
|
9771
|
-
opts.root,
|
|
9772
|
-
"--mount-workspace-git-root=false",
|
|
9773
|
-
"curl",
|
|
9774
|
-
"-fsS",
|
|
9775
|
-
`http://localhost:${target.port}${reqUrl}`
|
|
9776
|
-
],
|
|
9777
|
-
opts.root,
|
|
9778
|
-
{ quiet: true }
|
|
9779
|
-
);
|
|
9780
|
-
});
|
|
9781
|
-
server.on("error", () => {
|
|
9782
|
-
});
|
|
9783
|
-
server.listen(target.port, "127.0.0.1");
|
|
9784
|
-
servers.push(server);
|
|
9785
|
-
};
|
|
9786
|
-
const poll = setInterval(() => {
|
|
9787
|
-
if (!existsSync15(urlFile)) return;
|
|
9788
|
-
let content = "";
|
|
9789
|
-
try {
|
|
9790
|
-
content = readFileSync5(urlFile, "utf8");
|
|
9791
|
-
} catch {
|
|
9792
|
-
return;
|
|
9793
|
-
}
|
|
9794
|
-
const url = nextRelayUrl(content, lastOpened);
|
|
9795
|
-
if (!url) return;
|
|
9796
|
-
lastOpened = url;
|
|
9797
|
-
onUrl(url);
|
|
9798
|
-
}, 250);
|
|
9799
|
-
return {
|
|
9800
|
-
relayDirInContainer: `/workspaces/${opts.name}/${RELAY_DIRNAME}`,
|
|
9801
|
-
async dispose() {
|
|
9802
|
-
clearInterval(poll);
|
|
9803
|
-
for (const s of servers) s.close();
|
|
9804
|
-
await fsp5.rm(relayDir, { recursive: true, force: true });
|
|
9805
|
-
}
|
|
9806
|
-
};
|
|
9807
|
-
}
|
|
9808
|
-
var RELAY_DIRNAME;
|
|
9809
|
-
var init_browser_bridge = __esm({
|
|
9810
|
-
"src/devcontainer/browser-bridge.ts"() {
|
|
9811
|
-
"use strict";
|
|
9812
|
-
RELAY_DIRNAME = ".monoceros-bridge";
|
|
9813
|
-
}
|
|
9814
|
-
});
|
|
9815
|
-
|
|
9816
10320
|
// src/devcontainer/claude-trust.ts
|
|
9817
|
-
import { existsSync as
|
|
9818
|
-
import
|
|
10321
|
+
import { existsSync as existsSync19, promises as fsp6 } from "fs";
|
|
10322
|
+
import path28 from "path";
|
|
9819
10323
|
function resolveContainerCwd(name, cwd) {
|
|
9820
10324
|
const workspace = `/workspaces/${name}`;
|
|
9821
10325
|
if (!cwd) return workspace;
|
|
9822
|
-
return
|
|
10326
|
+
return path28.posix.isAbsolute(cwd) ? cwd : path28.posix.join(workspace, cwd);
|
|
9823
10327
|
}
|
|
9824
10328
|
async function preApproveClaudeProject(opts) {
|
|
9825
|
-
const file =
|
|
9826
|
-
if (!
|
|
10329
|
+
const file = path28.join(opts.root, "home", ".claude.json");
|
|
10330
|
+
if (!existsSync19(file)) return;
|
|
9827
10331
|
try {
|
|
9828
10332
|
const raw = await fsp6.readFile(file, "utf8");
|
|
9829
10333
|
const config = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -9856,56 +10360,6 @@ var init_claude_trust = __esm({
|
|
|
9856
10360
|
}
|
|
9857
10361
|
});
|
|
9858
10362
|
|
|
9859
|
-
// src/devcontainer/shell.ts
|
|
9860
|
-
import { existsSync as existsSync17 } from "fs";
|
|
9861
|
-
import path26 from "path";
|
|
9862
|
-
async function runShell(opts) {
|
|
9863
|
-
assertContainerExists(opts.root);
|
|
9864
|
-
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
9865
|
-
const upCode = await spawnFn(
|
|
9866
|
-
["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
|
|
9867
|
-
opts.root,
|
|
9868
|
-
{ quiet: true }
|
|
9869
|
-
);
|
|
9870
|
-
if (upCode !== 0) return upCode;
|
|
9871
|
-
const name = opts.name ?? path26.basename(opts.root);
|
|
9872
|
-
const isTty2 = opts.isTty ?? process.stdout.isTTY;
|
|
9873
|
-
const bridge = isTty2 ? await startBrowserBridge({ name, root: opts.root, spawn: spawnFn }) : null;
|
|
9874
|
-
try {
|
|
9875
|
-
const innerExec = wrapExec(
|
|
9876
|
-
["bash"],
|
|
9877
|
-
bridge ? { pathPrepend: bridge.relayDirInContainer } : {}
|
|
9878
|
-
);
|
|
9879
|
-
return await spawnFn(
|
|
9880
|
-
[
|
|
9881
|
-
"exec",
|
|
9882
|
-
"--workspace-folder",
|
|
9883
|
-
opts.root,
|
|
9884
|
-
"--mount-workspace-git-root=false",
|
|
9885
|
-
...innerExec
|
|
9886
|
-
],
|
|
9887
|
-
opts.root,
|
|
9888
|
-
{ interactive: true }
|
|
9889
|
-
);
|
|
9890
|
-
} finally {
|
|
9891
|
-
if (bridge) await bridge.dispose();
|
|
9892
|
-
}
|
|
9893
|
-
}
|
|
9894
|
-
function assertContainerExists(root) {
|
|
9895
|
-
if (!existsSync17(path26.join(root, ".devcontainer"))) {
|
|
9896
|
-
throw new Error(
|
|
9897
|
-
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
9898
|
-
);
|
|
9899
|
-
}
|
|
9900
|
-
}
|
|
9901
|
-
var init_shell = __esm({
|
|
9902
|
-
"src/devcontainer/shell.ts"() {
|
|
9903
|
-
"use strict";
|
|
9904
|
-
init_browser_bridge();
|
|
9905
|
-
init_cli();
|
|
9906
|
-
}
|
|
9907
|
-
});
|
|
9908
|
-
|
|
9909
10363
|
// src/devcontainer/run.ts
|
|
9910
10364
|
async function runInContainer(opts) {
|
|
9911
10365
|
if (opts.command.length === 0) {
|
|
@@ -9964,8 +10418,8 @@ var init_run = __esm({
|
|
|
9964
10418
|
});
|
|
9965
10419
|
|
|
9966
10420
|
// src/commands/run.ts
|
|
9967
|
-
import { defineCommand as
|
|
9968
|
-
import { consola as
|
|
10421
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
10422
|
+
import { consola as consola31 } from "consola";
|
|
9969
10423
|
var runCommand;
|
|
9970
10424
|
var init_run2 = __esm({
|
|
9971
10425
|
"src/commands/run.ts"() {
|
|
@@ -9973,7 +10427,7 @@ var init_run2 = __esm({
|
|
|
9973
10427
|
init_paths();
|
|
9974
10428
|
init_run();
|
|
9975
10429
|
init_inner_args();
|
|
9976
|
-
runCommand =
|
|
10430
|
+
runCommand = defineCommand25({
|
|
9977
10431
|
meta: {
|
|
9978
10432
|
name: "run",
|
|
9979
10433
|
group: "run",
|
|
@@ -9993,7 +10447,7 @@ var init_run2 = __esm({
|
|
|
9993
10447
|
async run({ args }) {
|
|
9994
10448
|
const command = [...getInnerArgs()];
|
|
9995
10449
|
if (command.length === 0) {
|
|
9996
|
-
|
|
10450
|
+
consola31.error(
|
|
9997
10451
|
"No command provided. Usage: `monoceros run <containername> -- <cmd> [args\u2026]`."
|
|
9998
10452
|
);
|
|
9999
10453
|
process.exit(1);
|
|
@@ -10007,7 +10461,7 @@ var init_run2 = __esm({
|
|
|
10007
10461
|
});
|
|
10008
10462
|
process.exit(exitCode);
|
|
10009
10463
|
} catch (err) {
|
|
10010
|
-
|
|
10464
|
+
consola31.error(err instanceof Error ? err.message : String(err));
|
|
10011
10465
|
process.exit(1);
|
|
10012
10466
|
}
|
|
10013
10467
|
}
|
|
@@ -10016,15 +10470,15 @@ var init_run2 = __esm({
|
|
|
10016
10470
|
});
|
|
10017
10471
|
|
|
10018
10472
|
// src/commands/shell.ts
|
|
10019
|
-
import { defineCommand as
|
|
10020
|
-
import { consola as
|
|
10473
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
10474
|
+
import { consola as consola32 } from "consola";
|
|
10021
10475
|
var shellCommand;
|
|
10022
10476
|
var init_shell2 = __esm({
|
|
10023
10477
|
"src/commands/shell.ts"() {
|
|
10024
10478
|
"use strict";
|
|
10025
10479
|
init_paths();
|
|
10026
10480
|
init_shell();
|
|
10027
|
-
shellCommand =
|
|
10481
|
+
shellCommand = defineCommand26({
|
|
10028
10482
|
meta: {
|
|
10029
10483
|
name: "shell",
|
|
10030
10484
|
group: "run",
|
|
@@ -10045,7 +10499,7 @@ var init_shell2 = __esm({
|
|
|
10045
10499
|
});
|
|
10046
10500
|
process.exit(exitCode);
|
|
10047
10501
|
} catch (err) {
|
|
10048
|
-
|
|
10502
|
+
consola32.error(err instanceof Error ? err.message : String(err));
|
|
10049
10503
|
process.exit(1);
|
|
10050
10504
|
}
|
|
10051
10505
|
}
|
|
@@ -10054,8 +10508,8 @@ var init_shell2 = __esm({
|
|
|
10054
10508
|
});
|
|
10055
10509
|
|
|
10056
10510
|
// src/commands/start.ts
|
|
10057
|
-
import { defineCommand as
|
|
10058
|
-
import { consola as
|
|
10511
|
+
import { defineCommand as defineCommand27 } from "citty";
|
|
10512
|
+
import { consola as consola33 } from "consola";
|
|
10059
10513
|
var startCommand;
|
|
10060
10514
|
var init_start = __esm({
|
|
10061
10515
|
"src/commands/start.ts"() {
|
|
@@ -10064,10 +10518,11 @@ var init_start = __esm({
|
|
|
10064
10518
|
init_io();
|
|
10065
10519
|
init_paths();
|
|
10066
10520
|
init_compose();
|
|
10521
|
+
init_open();
|
|
10067
10522
|
init_proxy();
|
|
10068
10523
|
init_port_check();
|
|
10069
10524
|
init_dispatch();
|
|
10070
|
-
startCommand =
|
|
10525
|
+
startCommand = defineCommand27({
|
|
10071
10526
|
meta: {
|
|
10072
10527
|
name: "start",
|
|
10073
10528
|
group: "run",
|
|
@@ -10078,6 +10533,10 @@ var init_start = __esm({
|
|
|
10078
10533
|
type: "positional",
|
|
10079
10534
|
description: "Container name (yml in $MONOCEROS_HOME/container-configs/).",
|
|
10080
10535
|
required: true
|
|
10536
|
+
},
|
|
10537
|
+
open: {
|
|
10538
|
+
type: "string",
|
|
10539
|
+
description: `After a successful start, open the container in this tool (${OPEN_TOOLS.join("|")}).`
|
|
10081
10540
|
}
|
|
10082
10541
|
},
|
|
10083
10542
|
run({ args }) {
|
|
@@ -10092,7 +10551,7 @@ var init_start = __esm({
|
|
|
10092
10551
|
hostPort = proxyHostPort(global);
|
|
10093
10552
|
}
|
|
10094
10553
|
} catch (err) {
|
|
10095
|
-
|
|
10554
|
+
consola33.warn(
|
|
10096
10555
|
`Could not read container yml ahead of start: ${err instanceof Error ? err.message : String(err)}. Skipping Traefik pre-flight.`
|
|
10097
10556
|
);
|
|
10098
10557
|
}
|
|
@@ -10100,7 +10559,15 @@ var init_start = __esm({
|
|
|
10100
10559
|
await preflightHostPort(hostPort);
|
|
10101
10560
|
await ensureProxy({ hostPort });
|
|
10102
10561
|
}
|
|
10103
|
-
|
|
10562
|
+
const exitCode = await runStart({ root: containerDir(args.name) });
|
|
10563
|
+
if (args.open && exitCode === 0) {
|
|
10564
|
+
try {
|
|
10565
|
+
await runOpen({ name: args.name, tool: args.open });
|
|
10566
|
+
} catch (err) {
|
|
10567
|
+
consola33.warn(err instanceof Error ? err.message : String(err));
|
|
10568
|
+
}
|
|
10569
|
+
}
|
|
10570
|
+
return exitCode;
|
|
10104
10571
|
});
|
|
10105
10572
|
}
|
|
10106
10573
|
});
|
|
@@ -10108,7 +10575,7 @@ var init_start = __esm({
|
|
|
10108
10575
|
});
|
|
10109
10576
|
|
|
10110
10577
|
// src/commands/status.ts
|
|
10111
|
-
import { defineCommand as
|
|
10578
|
+
import { defineCommand as defineCommand28 } from "citty";
|
|
10112
10579
|
var statusCommand;
|
|
10113
10580
|
var init_status = __esm({
|
|
10114
10581
|
"src/commands/status.ts"() {
|
|
@@ -10116,7 +10583,7 @@ var init_status = __esm({
|
|
|
10116
10583
|
init_paths();
|
|
10117
10584
|
init_compose();
|
|
10118
10585
|
init_dispatch();
|
|
10119
|
-
statusCommand =
|
|
10586
|
+
statusCommand = defineCommand28({
|
|
10120
10587
|
meta: {
|
|
10121
10588
|
name: "status",
|
|
10122
10589
|
group: "run",
|
|
@@ -10146,8 +10613,8 @@ var init_status = __esm({
|
|
|
10146
10613
|
});
|
|
10147
10614
|
|
|
10148
10615
|
// src/commands/stop.ts
|
|
10149
|
-
import { defineCommand as
|
|
10150
|
-
import { consola as
|
|
10616
|
+
import { defineCommand as defineCommand29 } from "citty";
|
|
10617
|
+
import { consola as consola34 } from "consola";
|
|
10151
10618
|
var stopCommand;
|
|
10152
10619
|
var init_stop = __esm({
|
|
10153
10620
|
"src/commands/stop.ts"() {
|
|
@@ -10156,7 +10623,7 @@ var init_stop = __esm({
|
|
|
10156
10623
|
init_compose();
|
|
10157
10624
|
init_proxy();
|
|
10158
10625
|
init_dispatch();
|
|
10159
|
-
stopCommand =
|
|
10626
|
+
stopCommand = defineCommand29({
|
|
10160
10627
|
meta: {
|
|
10161
10628
|
name: "stop",
|
|
10162
10629
|
group: "run",
|
|
@@ -10181,10 +10648,10 @@ var init_stop = __esm({
|
|
|
10181
10648
|
});
|
|
10182
10649
|
try {
|
|
10183
10650
|
await maybeStopProxy({
|
|
10184
|
-
logger: { info: (msg) =>
|
|
10651
|
+
logger: { info: (msg) => consola34.info(msg) }
|
|
10185
10652
|
});
|
|
10186
10653
|
} catch (err) {
|
|
10187
|
-
|
|
10654
|
+
consola34.warn(
|
|
10188
10655
|
`Could not tear down the Traefik proxy: ${err instanceof Error ? err.message : String(err)}. Ignored.`
|
|
10189
10656
|
);
|
|
10190
10657
|
}
|
|
@@ -10196,11 +10663,11 @@ var init_stop = __esm({
|
|
|
10196
10663
|
});
|
|
10197
10664
|
|
|
10198
10665
|
// src/tunnel/resolve.ts
|
|
10199
|
-
import { existsSync as
|
|
10200
|
-
import
|
|
10666
|
+
import { existsSync as existsSync20 } from "fs";
|
|
10667
|
+
import path29 from "path";
|
|
10201
10668
|
async function resolveTunnelTarget(opts) {
|
|
10202
10669
|
const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
|
|
10203
|
-
if (!
|
|
10670
|
+
if (!existsSync20(ymlPath)) {
|
|
10204
10671
|
throw new Error(
|
|
10205
10672
|
`No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
|
|
10206
10673
|
);
|
|
@@ -10208,13 +10675,13 @@ async function resolveTunnelTarget(opts) {
|
|
|
10208
10675
|
const parsed = await readConfig(ymlPath);
|
|
10209
10676
|
const config = parsed.config;
|
|
10210
10677
|
const containerRoot = containerDir(opts.name, opts.monocerosHome);
|
|
10211
|
-
if (!
|
|
10678
|
+
if (!existsSync20(containerRoot)) {
|
|
10212
10679
|
throw new Error(
|
|
10213
10680
|
`Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
|
|
10214
10681
|
);
|
|
10215
10682
|
}
|
|
10216
|
-
const composePath =
|
|
10217
|
-
const isCompose =
|
|
10683
|
+
const composePath = path29.join(containerRoot, ".devcontainer", "compose.yaml");
|
|
10684
|
+
const isCompose = existsSync20(composePath);
|
|
10218
10685
|
const parsedTarget = parseTargetArg(opts.target, config);
|
|
10219
10686
|
const docker = opts.docker ?? defaultDockerExec;
|
|
10220
10687
|
if (isCompose) {
|
|
@@ -10449,8 +10916,8 @@ var init_port_check2 = __esm({
|
|
|
10449
10916
|
});
|
|
10450
10917
|
|
|
10451
10918
|
// src/tunnel/run.ts
|
|
10452
|
-
import { spawn as
|
|
10453
|
-
import { consola as
|
|
10919
|
+
import { spawn as spawn12 } from "child_process";
|
|
10920
|
+
import { consola as consola35 } from "consola";
|
|
10454
10921
|
function signalNumber(signal) {
|
|
10455
10922
|
switch (signal) {
|
|
10456
10923
|
case "SIGINT":
|
|
@@ -10463,8 +10930,8 @@ function signalNumber(signal) {
|
|
|
10463
10930
|
}
|
|
10464
10931
|
async function runTunnel(opts) {
|
|
10465
10932
|
const log = opts.logger ?? {
|
|
10466
|
-
info: (m) =>
|
|
10467
|
-
warn: (m) =>
|
|
10933
|
+
info: (m) => consola35.info(m),
|
|
10934
|
+
warn: (m) => consola35.warn(m)
|
|
10468
10935
|
};
|
|
10469
10936
|
const resolve = opts.resolve ?? resolveTunnelTarget;
|
|
10470
10937
|
const resolveArgs3 = {
|
|
@@ -10539,7 +11006,7 @@ var init_run3 = __esm({
|
|
|
10539
11006
|
init_port_check2();
|
|
10540
11007
|
SOCAT_IMAGE = "alpine/socat:1.8.0.3";
|
|
10541
11008
|
defaultDockerSpawn = (args) => {
|
|
10542
|
-
const child =
|
|
11009
|
+
const child = spawn12("docker", args, {
|
|
10543
11010
|
stdio: "inherit"
|
|
10544
11011
|
});
|
|
10545
11012
|
const exited = new Promise((resolve, reject) => {
|
|
@@ -10570,8 +11037,8 @@ var init_run3 = __esm({
|
|
|
10570
11037
|
});
|
|
10571
11038
|
|
|
10572
11039
|
// src/commands/tunnel.ts
|
|
10573
|
-
import { defineCommand as
|
|
10574
|
-
import { consola as
|
|
11040
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
11041
|
+
import { consola as consola36 } from "consola";
|
|
10575
11042
|
function parseLocalPort(raw) {
|
|
10576
11043
|
if (raw === void 0) return void 0;
|
|
10577
11044
|
const n = Number(raw);
|
|
@@ -10587,7 +11054,7 @@ var init_tunnel = __esm({
|
|
|
10587
11054
|
"src/commands/tunnel.ts"() {
|
|
10588
11055
|
"use strict";
|
|
10589
11056
|
init_run3();
|
|
10590
|
-
tunnelCommand =
|
|
11057
|
+
tunnelCommand = defineCommand30({
|
|
10591
11058
|
meta: {
|
|
10592
11059
|
name: "tunnel",
|
|
10593
11060
|
group: "discovery",
|
|
@@ -10624,7 +11091,7 @@ var init_tunnel = __esm({
|
|
|
10624
11091
|
});
|
|
10625
11092
|
process.exit(exitCode);
|
|
10626
11093
|
} catch (err) {
|
|
10627
|
-
|
|
11094
|
+
consola36.error(err instanceof Error ? err.message : String(err));
|
|
10628
11095
|
process.exit(1);
|
|
10629
11096
|
}
|
|
10630
11097
|
}
|
|
@@ -10694,8 +11161,8 @@ var init_prune = __esm({
|
|
|
10694
11161
|
});
|
|
10695
11162
|
|
|
10696
11163
|
// src/upgrade/index.ts
|
|
10697
|
-
import { existsSync as
|
|
10698
|
-
import { consola as
|
|
11164
|
+
import { existsSync as existsSync21, promises as fs18 } from "fs";
|
|
11165
|
+
import { consola as consola37 } from "consola";
|
|
10699
11166
|
async function fetchRuntimeVersions() {
|
|
10700
11167
|
const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
|
|
10701
11168
|
const tokenRes = await fetch(tokenUrl);
|
|
@@ -10733,7 +11200,7 @@ ${yml}`;
|
|
|
10733
11200
|
}
|
|
10734
11201
|
async function runUpgrade(opts) {
|
|
10735
11202
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
10736
|
-
const logger = opts.logger ??
|
|
11203
|
+
const logger = opts.logger ?? consola37;
|
|
10737
11204
|
const fetchVersions = opts.fetchVersions ?? fetchRuntimeVersions;
|
|
10738
11205
|
if (opts.list) {
|
|
10739
11206
|
const versions = await fetchVersions();
|
|
@@ -10759,7 +11226,7 @@ async function runUpgrade(opts) {
|
|
|
10759
11226
|
);
|
|
10760
11227
|
}
|
|
10761
11228
|
}
|
|
10762
|
-
if (opts.name && !
|
|
11229
|
+
if (opts.name && !existsSync21(containerConfigPath(opts.name, home))) {
|
|
10763
11230
|
throw new Error(
|
|
10764
11231
|
`No such config: ${containerConfigPath(opts.name, home)}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
10765
11232
|
);
|
|
@@ -10810,11 +11277,11 @@ async function runUpgrade(opts) {
|
|
|
10810
11277
|
let bumped = 0;
|
|
10811
11278
|
for (const name of targets) {
|
|
10812
11279
|
const ymlPath = containerConfigPath(name, home);
|
|
10813
|
-
if (!
|
|
10814
|
-
const raw = await
|
|
11280
|
+
if (!existsSync21(ymlPath)) continue;
|
|
11281
|
+
const raw = await fs18.readFile(ymlPath, "utf8");
|
|
10815
11282
|
const updated = setRuntimeVersion(raw, pinVersion);
|
|
10816
11283
|
if (updated !== raw) {
|
|
10817
|
-
await
|
|
11284
|
+
await fs18.writeFile(ymlPath, updated);
|
|
10818
11285
|
bumped += 1;
|
|
10819
11286
|
logger.info(`Pinned '${name}' to runtime ${pinVersion}.`);
|
|
10820
11287
|
}
|
|
@@ -10856,7 +11323,7 @@ function formatPruneLine(prune) {
|
|
|
10856
11323
|
}
|
|
10857
11324
|
async function listContainerNames2(home) {
|
|
10858
11325
|
try {
|
|
10859
|
-
const entries = await
|
|
11326
|
+
const entries = await fs18.readdir(containerConfigsDir(home));
|
|
10860
11327
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length));
|
|
10861
11328
|
} catch {
|
|
10862
11329
|
return [];
|
|
@@ -10879,7 +11346,7 @@ var init_upgrade = __esm({
|
|
|
10879
11346
|
});
|
|
10880
11347
|
|
|
10881
11348
|
// src/commands/upgrade.ts
|
|
10882
|
-
import { defineCommand as
|
|
11349
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
10883
11350
|
var upgradeCommand;
|
|
10884
11351
|
var init_upgrade2 = __esm({
|
|
10885
11352
|
"src/commands/upgrade.ts"() {
|
|
@@ -10887,7 +11354,7 @@ var init_upgrade2 = __esm({
|
|
|
10887
11354
|
init_upgrade();
|
|
10888
11355
|
init_version();
|
|
10889
11356
|
init_dispatch();
|
|
10890
|
-
upgradeCommand =
|
|
11357
|
+
upgradeCommand = defineCommand31({
|
|
10891
11358
|
meta: {
|
|
10892
11359
|
name: "upgrade",
|
|
10893
11360
|
group: "lifecycle",
|
|
@@ -10929,7 +11396,7 @@ var main_exports = {};
|
|
|
10929
11396
|
__export(main_exports, {
|
|
10930
11397
|
main: () => main
|
|
10931
11398
|
});
|
|
10932
|
-
import { defineCommand as
|
|
11399
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
10933
11400
|
var main;
|
|
10934
11401
|
var init_main = __esm({
|
|
10935
11402
|
"src/main.ts"() {
|
|
@@ -10947,6 +11414,7 @@ var init_main = __esm({
|
|
|
10947
11414
|
init_init2();
|
|
10948
11415
|
init_list_components();
|
|
10949
11416
|
init_logs();
|
|
11417
|
+
init_open2();
|
|
10950
11418
|
init_port();
|
|
10951
11419
|
init_remove_apt_packages();
|
|
10952
11420
|
init_remove_feature();
|
|
@@ -10965,7 +11433,7 @@ var init_main = __esm({
|
|
|
10965
11433
|
init_tunnel();
|
|
10966
11434
|
init_upgrade2();
|
|
10967
11435
|
init_version();
|
|
10968
|
-
main =
|
|
11436
|
+
main = defineCommand32({
|
|
10969
11437
|
meta: {
|
|
10970
11438
|
name: "monoceros",
|
|
10971
11439
|
version: CLI_VERSION,
|
|
@@ -10975,6 +11443,7 @@ var init_main = __esm({
|
|
|
10975
11443
|
init: initCommand,
|
|
10976
11444
|
"list-components": listComponentsCommand,
|
|
10977
11445
|
shell: shellCommand,
|
|
11446
|
+
open: openCommand,
|
|
10978
11447
|
run: runCommand,
|
|
10979
11448
|
logs: logsCommand,
|
|
10980
11449
|
start: startCommand,
|
|
@@ -11261,25 +11730,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
11261
11730
|
const separatorIdx = argv.indexOf("--");
|
|
11262
11731
|
if (helpIdx === -1) return null;
|
|
11263
11732
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
11264
|
-
const
|
|
11733
|
+
const path30 = [];
|
|
11265
11734
|
const tokens = argv.slice(
|
|
11266
11735
|
0,
|
|
11267
11736
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
11268
11737
|
);
|
|
11269
11738
|
let cursor = main2;
|
|
11270
11739
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
11271
|
-
|
|
11740
|
+
path30.push(mainName);
|
|
11272
11741
|
for (const tok of tokens) {
|
|
11273
11742
|
if (tok.startsWith("-")) continue;
|
|
11274
11743
|
const subs = cursor.subCommands ?? {};
|
|
11275
11744
|
if (tok in subs) {
|
|
11276
11745
|
cursor = subs[tok];
|
|
11277
|
-
|
|
11746
|
+
path30.push(tok);
|
|
11278
11747
|
continue;
|
|
11279
11748
|
}
|
|
11280
11749
|
break;
|
|
11281
11750
|
}
|
|
11282
|
-
return { path:
|
|
11751
|
+
return { path: path30, cmd: cursor };
|
|
11283
11752
|
}
|
|
11284
11753
|
async function maybeRenderHelp(argv, main2) {
|
|
11285
11754
|
const hit = detectHelpRequest(argv, main2);
|