@getmonoceros/workbench 1.26.4 → 1.27.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 +940 -514
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -74,7 +74,7 @@ ${issues}`);
|
|
|
74
74
|
}
|
|
75
75
|
return result.data;
|
|
76
76
|
}
|
|
77
|
-
var SOLUTION_NAME_RE, APT_PACKAGE_NAME_RE, RUNTIME_VERSION_RE, FEATURE_REF_RE, INSTALL_URL_RE, REPO_URL_RE, REPO_PATH_RE,
|
|
77
|
+
var SOLUTION_NAME_RE, APT_PACKAGE_NAME_RE, RUNTIME_VERSION_RE, FEATURE_REF_RE, INSTALL_URL_RE, REPO_URL_RE, REPO_PATH_RE, REGEX, PROVIDER_VALUES, KNOWN_PROVIDER_HOSTS, CONFIG_SCHEMA_VERSION, FeatureOptionValueSchema, FeatureEntrySchema, EMAIL_RE, GitUserSchema, RepoEntrySchema, PortEntrySchema, RoutingSchema, SERVICE_NAME_RE, ServiceEnvValueSchema, ServiceHealthcheckSchema, SERVICE_RESTART_VALUES, ServiceObjectSchema, LanguageOptionValueSchema, LanguageEntrySchema, SolutionConfigSchema;
|
|
78
78
|
var init_schema = __esm({
|
|
79
79
|
"src/config/schema.ts"() {
|
|
80
80
|
"use strict";
|
|
@@ -85,15 +85,13 @@ var init_schema = __esm({
|
|
|
85
85
|
INSTALL_URL_RE = /^https:\/\/[A-Za-z0-9.\-_~/:?#[\]@!&'()*+,;=%]+$/;
|
|
86
86
|
REPO_URL_RE = /^https:\/\/[A-Za-z0-9@:/+_~.#=&?-]+$/;
|
|
87
87
|
REPO_PATH_RE = /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/;
|
|
88
|
-
POSTGRES_URL_RE = /^postgres(ql)?:\/\//;
|
|
89
88
|
REGEX = {
|
|
90
89
|
solutionName: SOLUTION_NAME_RE,
|
|
91
90
|
aptPackage: APT_PACKAGE_NAME_RE,
|
|
92
91
|
featureRef: FEATURE_REF_RE,
|
|
93
92
|
installUrl: INSTALL_URL_RE,
|
|
94
93
|
repoUrl: REPO_URL_RE,
|
|
95
|
-
repoPath: REPO_PATH_RE
|
|
96
|
-
postgresUrl: POSTGRES_URL_RE
|
|
94
|
+
repoPath: REPO_PATH_RE
|
|
97
95
|
};
|
|
98
96
|
PROVIDER_VALUES = [
|
|
99
97
|
"github",
|
|
@@ -217,12 +215,6 @@ var init_schema = __esm({
|
|
|
217
215
|
// are left untouched by `interpolateServices`.
|
|
218
216
|
connectionEnv: z.record(z.string(), z.string()).optional()
|
|
219
217
|
});
|
|
220
|
-
ExternalServicesSchema = z.object({
|
|
221
|
-
postgres: z.string().regex(
|
|
222
|
-
POSTGRES_URL_RE,
|
|
223
|
-
"Postgres URL must start with 'postgres://' or 'postgresql://'"
|
|
224
|
-
).optional()
|
|
225
|
-
});
|
|
226
218
|
LanguageOptionValueSchema = z.union([
|
|
227
219
|
z.string(),
|
|
228
220
|
z.number(),
|
|
@@ -269,7 +261,6 @@ var init_schema = __esm({
|
|
|
269
261
|
services: z.array(ServiceObjectSchema).default([]),
|
|
270
262
|
repos: z.array(RepoEntrySchema).default([]),
|
|
271
263
|
routing: RoutingSchema.optional(),
|
|
272
|
-
externalServices: ExternalServicesSchema.default({}),
|
|
273
264
|
git: z.object({
|
|
274
265
|
user: GitUserSchema.optional()
|
|
275
266
|
}).optional()
|
|
@@ -2155,9 +2146,9 @@ function compareRuntimeVersions(a, b) {
|
|
|
2155
2146
|
}
|
|
2156
2147
|
return 0;
|
|
2157
2148
|
}
|
|
2158
|
-
function
|
|
2149
|
+
function runtimeSupportsSshAttach(version) {
|
|
2159
2150
|
if (!version) return false;
|
|
2160
|
-
return compareRuntimeVersions(version,
|
|
2151
|
+
return compareRuntimeVersions(version, MIN_RUNTIME_FOR_SSH_ATTACH) >= 0;
|
|
2161
2152
|
}
|
|
2162
2153
|
function descriptorOptionDefaults(options) {
|
|
2163
2154
|
const out = {};
|
|
@@ -2278,7 +2269,7 @@ function deriveServiceName(image) {
|
|
|
2278
2269
|
const noTag = lastSegment.split("@")[0].split(":")[0];
|
|
2279
2270
|
return noTag.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
2280
2271
|
}
|
|
2281
|
-
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;
|
|
2282
2273
|
var init_catalog = __esm({
|
|
2283
2274
|
"src/create/catalog.ts"() {
|
|
2284
2275
|
"use strict";
|
|
@@ -2287,13 +2278,13 @@ var init_catalog = __esm({
|
|
|
2287
2278
|
override = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();
|
|
2288
2279
|
BASE_IMAGE = override && override.length > 0 ? override : DEFAULT_BASE_IMAGE;
|
|
2289
2280
|
RUNTIME_IMAGE_REPO = "ghcr.io/getmonoceros/monoceros-runtime";
|
|
2290
|
-
DEFAULT_RUNTIME_VERSION = true ? "1.
|
|
2281
|
+
DEFAULT_RUNTIME_VERSION = true ? "1.2.0" : readFileSync3(
|
|
2291
2282
|
fileURLToPath2(
|
|
2292
2283
|
new URL("../../../../images/runtime/VERSION", import.meta.url)
|
|
2293
2284
|
),
|
|
2294
2285
|
"utf8"
|
|
2295
2286
|
).trim();
|
|
2296
|
-
|
|
2287
|
+
MIN_RUNTIME_FOR_SSH_ATTACH = "1.2.0";
|
|
2297
2288
|
DESCRIPTORS = loadDescriptorCatalogSync();
|
|
2298
2289
|
BUILTIN_LANGUAGES = new Set(
|
|
2299
2290
|
[...DESCRIPTORS.values()].filter((c) => c.category === "language" && c.descriptor.language?.builtin).map(descriptorSelector)
|
|
@@ -2798,7 +2789,6 @@ function normalizeOptions(opts) {
|
|
|
2798
2789
|
const languages = [...new Set(opts.languages)].sort();
|
|
2799
2790
|
const serviceByName = /* @__PURE__ */ new Map();
|
|
2800
2791
|
for (const svc of opts.services) {
|
|
2801
|
-
if (opts.postgresUrl && svc.name === "postgres") continue;
|
|
2802
2792
|
serviceByName.set(svc.name, svc);
|
|
2803
2793
|
}
|
|
2804
2794
|
const services = [...serviceByName.values()].sort(
|
|
@@ -2819,7 +2809,6 @@ function normalizeOptions(opts) {
|
|
|
2819
2809
|
languages,
|
|
2820
2810
|
...opts.languageOptions && Object.keys(opts.languageOptions).length > 0 ? { languageOptions: opts.languageOptions } : {},
|
|
2821
2811
|
services,
|
|
2822
|
-
postgresUrl: opts.postgresUrl,
|
|
2823
2812
|
...aptPackages.length > 0 ? { aptPackages } : {},
|
|
2824
2813
|
...features && Object.keys(features).length > 0 ? { features } : {},
|
|
2825
2814
|
...installUrls && installUrls.length > 0 ? { installUrls } : {},
|
|
@@ -2958,14 +2947,32 @@ function ideStateVolumes(name) {
|
|
|
2958
2947
|
return [
|
|
2959
2948
|
{
|
|
2960
2949
|
volume: `monoceros-${name}-vscode-extensions`,
|
|
2961
|
-
target: "/home/node/.vscode-server/extensions"
|
|
2950
|
+
target: "/home/node/.vscode-server/extensions",
|
|
2951
|
+
minRuntime: "1.1.0"
|
|
2962
2952
|
},
|
|
2963
2953
|
{
|
|
2964
2954
|
volume: `monoceros-${name}-vscode-userdata`,
|
|
2965
|
-
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"
|
|
2966
2967
|
}
|
|
2967
2968
|
];
|
|
2968
2969
|
}
|
|
2970
|
+
function ideStateVolumesForRuntime(name, version) {
|
|
2971
|
+
if (!version) return [];
|
|
2972
|
+
return ideStateVolumes(name).filter(
|
|
2973
|
+
(v) => compareRuntimeVersions(version, v.minRuntime) >= 0
|
|
2974
|
+
);
|
|
2975
|
+
}
|
|
2969
2976
|
function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
2970
2977
|
const resolvedFeatures = resolveFeatures(opts);
|
|
2971
2978
|
const features = {};
|
|
@@ -2997,6 +3004,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
2997
3004
|
}
|
|
2998
3005
|
}
|
|
2999
3006
|
} : void 0;
|
|
3007
|
+
const sshPostStart = runtimeSupportsSshAttach(opts.runtimeVersion) ? { postStartCommand: "sudo /usr/local/bin/monoceros-sshd-up.sh" } : {};
|
|
3000
3008
|
if (needsCompose(opts)) {
|
|
3001
3009
|
return {
|
|
3002
3010
|
name: opts.name,
|
|
@@ -3007,20 +3015,22 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
3007
3015
|
remoteUser: "node",
|
|
3008
3016
|
forwardPorts: ports,
|
|
3009
3017
|
postCreateCommand: ".devcontainer/post-create.sh",
|
|
3018
|
+
...sshPostStart,
|
|
3010
3019
|
...featuresField ?? {},
|
|
3011
3020
|
...customizationsField ?? {}
|
|
3012
3021
|
};
|
|
3013
3022
|
}
|
|
3014
|
-
const ideMounts =
|
|
3015
|
-
|
|
3016
|
-
|
|
3023
|
+
const ideMounts = ideStateVolumesForRuntime(
|
|
3024
|
+
opts.name,
|
|
3025
|
+
opts.runtimeVersion
|
|
3026
|
+
).map((v) => `source=${v.volume},target=${v.target},type=volume`);
|
|
3017
3027
|
const mounts = [...homeMounts, ...ideMounts];
|
|
3018
3028
|
const mountsField = mounts.length > 0 ? { mounts } : {};
|
|
3019
3029
|
const workspaceMountField = {
|
|
3020
3030
|
workspaceMount: `source=\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind,consistency=cached`,
|
|
3021
3031
|
workspaceFolder: `/workspaces/${opts.name}`
|
|
3022
3032
|
};
|
|
3023
|
-
const runArgs = ["--cap-add=NET_ADMIN"];
|
|
3033
|
+
const runArgs = ["--cap-add=NET_ADMIN", `--name=monoceros-${opts.name}`];
|
|
3024
3034
|
if (ports.length > 0) {
|
|
3025
3035
|
runArgs.push("--network=monoceros-proxy");
|
|
3026
3036
|
runArgs.push(`--network-alias=${opts.name}`);
|
|
@@ -3034,6 +3044,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
3034
3044
|
runArgs,
|
|
3035
3045
|
forwardPorts: ports,
|
|
3036
3046
|
postCreateCommand: ".devcontainer/post-create.sh",
|
|
3047
|
+
...sshPostStart,
|
|
3037
3048
|
...featuresField ?? {},
|
|
3038
3049
|
...customizationsField ?? {}
|
|
3039
3050
|
};
|
|
@@ -3054,9 +3065,10 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
3054
3065
|
void dockerMode;
|
|
3055
3066
|
const hasPorts = (opts.ports?.length ?? 0) > 0;
|
|
3056
3067
|
const lines = ["services:"];
|
|
3057
|
-
const ideVolumes =
|
|
3068
|
+
const ideVolumes = ideStateVolumesForRuntime(opts.name, opts.runtimeVersion);
|
|
3058
3069
|
lines.push(" workspace:");
|
|
3059
3070
|
lines.push(` image: ${resolveRuntimeImage(opts.runtimeVersion)}`);
|
|
3071
|
+
lines.push(` container_name: monoceros-${opts.name}`);
|
|
3060
3072
|
lines.push(" command: 'sleep infinity'");
|
|
3061
3073
|
lines.push(" cap_add:");
|
|
3062
3074
|
lines.push(" - NET_ADMIN");
|
|
@@ -3928,8 +3940,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
|
|
|
3928
3940
|
if (!isMap2(item)) return false;
|
|
3929
3941
|
const url = item.get("url");
|
|
3930
3942
|
if (url === urlOrPath) return true;
|
|
3931
|
-
const
|
|
3932
|
-
const effectivePath = typeof
|
|
3943
|
+
const path30 = item.get("path");
|
|
3944
|
+
const effectivePath = typeof path30 === "string" ? path30 : typeof url === "string" ? deriveRepoName(url) : void 0;
|
|
3933
3945
|
return effectivePath === urlOrPath;
|
|
3934
3946
|
});
|
|
3935
3947
|
if (idx < 0) return false;
|
|
@@ -4051,7 +4063,7 @@ async function runAddRepo(input) {
|
|
|
4051
4063
|
"Missing repo URL. Usage: monoceros add-repo <containername> <url>."
|
|
4052
4064
|
);
|
|
4053
4065
|
}
|
|
4054
|
-
const
|
|
4066
|
+
const path30 = (input.path ?? deriveRepoName(url)).trim();
|
|
4055
4067
|
const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
|
|
4056
4068
|
const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
|
|
4057
4069
|
if (hasName !== hasEmail) {
|
|
@@ -4088,7 +4100,7 @@ async function runAddRepo(input) {
|
|
|
4088
4100
|
const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
|
|
4089
4101
|
const entry2 = {
|
|
4090
4102
|
url,
|
|
4091
|
-
path:
|
|
4103
|
+
path: path30,
|
|
4092
4104
|
...hasName && hasEmail ? {
|
|
4093
4105
|
gitUser: {
|
|
4094
4106
|
name: input.gitName.trim(),
|
|
@@ -5079,9 +5091,6 @@ function solutionConfigToCreateOptions(config, featureDefaults = {}) {
|
|
|
5079
5091
|
if (Object.keys(languageOptions).length > 0) {
|
|
5080
5092
|
result.languageOptions = languageOptions;
|
|
5081
5093
|
}
|
|
5082
|
-
if (config.externalServices.postgres !== void 0) {
|
|
5083
|
-
result.postgresUrl = config.externalServices.postgres;
|
|
5084
|
-
}
|
|
5085
5094
|
if (config.aptPackages.length > 0) {
|
|
5086
5095
|
result.aptPackages = [...config.aptPackages];
|
|
5087
5096
|
}
|
|
@@ -5134,9 +5143,163 @@ var init_transform = __esm({
|
|
|
5134
5143
|
}
|
|
5135
5144
|
});
|
|
5136
5145
|
|
|
5146
|
+
// src/devcontainer/ssh-attach.ts
|
|
5147
|
+
import { spawn as spawn4 } from "child_process";
|
|
5148
|
+
import { promises as fs10 } from "fs";
|
|
5149
|
+
import { existsSync as existsSync8 } from "fs";
|
|
5150
|
+
import os2 from "os";
|
|
5151
|
+
import path13 from "path";
|
|
5152
|
+
function sshHomeDir(home) {
|
|
5153
|
+
return path13.join(home, "ssh");
|
|
5154
|
+
}
|
|
5155
|
+
function sshConfigEntryPath(home, name) {
|
|
5156
|
+
return path13.join(sshHomeDir(home), "config.d", name);
|
|
5157
|
+
}
|
|
5158
|
+
function sshProxyScriptPath(home, name) {
|
|
5159
|
+
return path13.join(sshHomeDir(home), `exec-${name}.sh`);
|
|
5160
|
+
}
|
|
5161
|
+
function privateKeyPath(targetDir) {
|
|
5162
|
+
return path13.join(targetDir, ".monoceros", "ssh", "id_ed25519");
|
|
5163
|
+
}
|
|
5164
|
+
async function ensureKeypair(targetDir, name, keygen, logger) {
|
|
5165
|
+
const privateKey = privateKeyPath(targetDir);
|
|
5166
|
+
const publicKey = `${privateKey}.pub`;
|
|
5167
|
+
if (existsSync8(privateKey) && existsSync8(publicKey)) {
|
|
5168
|
+
return { privateKey, publicKey };
|
|
5169
|
+
}
|
|
5170
|
+
await fs10.mkdir(path13.dirname(privateKey), { recursive: true });
|
|
5171
|
+
try {
|
|
5172
|
+
const res = await keygen([
|
|
5173
|
+
"-t",
|
|
5174
|
+
"ed25519",
|
|
5175
|
+
"-N",
|
|
5176
|
+
"",
|
|
5177
|
+
"-f",
|
|
5178
|
+
privateKey,
|
|
5179
|
+
"-C",
|
|
5180
|
+
`monoceros-${name}`,
|
|
5181
|
+
"-q"
|
|
5182
|
+
]);
|
|
5183
|
+
if (res.exitCode !== 0) {
|
|
5184
|
+
logger.warn(
|
|
5185
|
+
`ssh-keygen failed (exit ${res.exitCode})${res.stderr ? `: ${res.stderr.trim()}` : ""}; IDE SSH attach not set up.`
|
|
5186
|
+
);
|
|
5187
|
+
return null;
|
|
5188
|
+
}
|
|
5189
|
+
} catch (err) {
|
|
5190
|
+
logger.warn(
|
|
5191
|
+
`ssh-keygen not runnable (${err instanceof Error ? err.message : String(err)}); IDE SSH attach not set up.`
|
|
5192
|
+
);
|
|
5193
|
+
return null;
|
|
5194
|
+
}
|
|
5195
|
+
return { privateKey, publicKey };
|
|
5196
|
+
}
|
|
5197
|
+
function proxyScriptContent(name, targetDir) {
|
|
5198
|
+
return `#!/bin/sh
|
|
5199
|
+
# Generated by Monoceros (ADR 0022) - do not edit.
|
|
5200
|
+
# Portless SSH transport into the running dev container '${name}'.
|
|
5201
|
+
# Resolves the container by the devcontainer.local_folder label (the
|
|
5202
|
+
# same handle 'monoceros shell' uses), so it follows rebuilds.
|
|
5203
|
+
set -e
|
|
5204
|
+
cid=$(docker ps -q \\
|
|
5205
|
+
--filter "label=devcontainer.local_folder=${targetDir}" \\
|
|
5206
|
+
--filter status=running | head -n1)
|
|
5207
|
+
if [ -z "$cid" ]; then
|
|
5208
|
+
echo "monoceros: dev container '${name}' is not running. Start it with: monoceros apply ${name}" >&2
|
|
5209
|
+
exit 1
|
|
5210
|
+
fi
|
|
5211
|
+
exec docker exec -i "$cid" socat - TCP:127.0.0.1:22
|
|
5212
|
+
`;
|
|
5213
|
+
}
|
|
5214
|
+
function configEntryContent(name, hostAlias, privateKey, proxyScript) {
|
|
5215
|
+
return `# Generated by Monoceros (ADR 0022) - do not edit.
|
|
5216
|
+
# Attach an SSH-capable IDE (Codium, IntelliJ, Zed) to host
|
|
5217
|
+
# '${hostAlias}', or run: ssh ${hostAlias}
|
|
5218
|
+
Host ${hostAlias}
|
|
5219
|
+
User node
|
|
5220
|
+
IdentityFile "${privateKey}"
|
|
5221
|
+
IdentitiesOnly yes
|
|
5222
|
+
StrictHostKeyChecking no
|
|
5223
|
+
UserKnownHostsFile /dev/null
|
|
5224
|
+
ProxyCommand "${proxyScript}"
|
|
5225
|
+
`;
|
|
5226
|
+
}
|
|
5227
|
+
async function ensureInclude(userSshDir, home) {
|
|
5228
|
+
const configPath = path13.join(userSshDir, "config");
|
|
5229
|
+
const includeTarget = path13.join(sshHomeDir(home), "config.d", "*");
|
|
5230
|
+
const includeLine = `Include "${includeTarget}"`;
|
|
5231
|
+
await fs10.mkdir(userSshDir, { recursive: true });
|
|
5232
|
+
let existing = "";
|
|
5233
|
+
try {
|
|
5234
|
+
existing = await fs10.readFile(configPath, "utf8");
|
|
5235
|
+
} catch {
|
|
5236
|
+
existing = "";
|
|
5237
|
+
}
|
|
5238
|
+
if (existing.includes(includeTarget)) return;
|
|
5239
|
+
const banner = "# Added by Monoceros (ADR 0022): per-container SSH attach entries.";
|
|
5240
|
+
const prefix = `${banner}
|
|
5241
|
+
${includeLine}
|
|
5242
|
+
`;
|
|
5243
|
+
const next = existing.length > 0 ? `${prefix}
|
|
5244
|
+
${existing}` : prefix;
|
|
5245
|
+
await fs10.writeFile(configPath, next, { mode: 384 });
|
|
5246
|
+
await fs10.chmod(configPath, 384).catch(() => {
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
async function setupSshAttach(opts) {
|
|
5250
|
+
const hostAlias = `monoceros-${opts.name}`;
|
|
5251
|
+
const logger = opts.logger ?? { info: () => {
|
|
5252
|
+
}, warn: () => {
|
|
5253
|
+
} };
|
|
5254
|
+
const keygen = opts.keygen ?? realKeygen;
|
|
5255
|
+
const userSshDir = opts.userSshDir ?? path13.join(os2.homedir(), ".ssh");
|
|
5256
|
+
const keys = await ensureKeypair(opts.targetDir, opts.name, keygen, logger);
|
|
5257
|
+
if (!keys) return { hostAlias, configured: false };
|
|
5258
|
+
const proxyScript = sshProxyScriptPath(opts.home, opts.name);
|
|
5259
|
+
const configEntry = sshConfigEntryPath(opts.home, opts.name);
|
|
5260
|
+
await fs10.mkdir(path13.dirname(proxyScript), { recursive: true });
|
|
5261
|
+
await fs10.mkdir(path13.dirname(configEntry), { recursive: true });
|
|
5262
|
+
await fs10.writeFile(
|
|
5263
|
+
proxyScript,
|
|
5264
|
+
proxyScriptContent(opts.name, opts.targetDir),
|
|
5265
|
+
{ mode: 493 }
|
|
5266
|
+
);
|
|
5267
|
+
await fs10.chmod(proxyScript, 493).catch(() => {
|
|
5268
|
+
});
|
|
5269
|
+
await fs10.writeFile(
|
|
5270
|
+
configEntry,
|
|
5271
|
+
configEntryContent(opts.name, hostAlias, keys.privateKey, proxyScript)
|
|
5272
|
+
);
|
|
5273
|
+
await ensureInclude(userSshDir, opts.home);
|
|
5274
|
+
return { hostAlias, configured: true };
|
|
5275
|
+
}
|
|
5276
|
+
async function removeSshAttach(home, name) {
|
|
5277
|
+
await fs10.rm(sshProxyScriptPath(home, name), { force: true });
|
|
5278
|
+
await fs10.rm(sshConfigEntryPath(home, name), { force: true });
|
|
5279
|
+
}
|
|
5280
|
+
var realKeygen;
|
|
5281
|
+
var init_ssh_attach = __esm({
|
|
5282
|
+
"src/devcontainer/ssh-attach.ts"() {
|
|
5283
|
+
"use strict";
|
|
5284
|
+
realKeygen = (args) => {
|
|
5285
|
+
return new Promise((resolve, reject) => {
|
|
5286
|
+
const child = spawn4("ssh-keygen", args, {
|
|
5287
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
5288
|
+
});
|
|
5289
|
+
let stderr = "";
|
|
5290
|
+
child.stderr.on("data", (chunk) => {
|
|
5291
|
+
stderr += chunk.toString();
|
|
5292
|
+
});
|
|
5293
|
+
child.on("error", reject);
|
|
5294
|
+
child.on("exit", (code) => resolve({ exitCode: code ?? 0, stderr }));
|
|
5295
|
+
});
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
5298
|
+
});
|
|
5299
|
+
|
|
5137
5300
|
// src/apply/apply-log.ts
|
|
5138
5301
|
import { createWriteStream, mkdirSync } from "fs";
|
|
5139
|
-
import
|
|
5302
|
+
import path14 from "path";
|
|
5140
5303
|
import { Writable } from "stream";
|
|
5141
5304
|
function safeIsoStamp(d) {
|
|
5142
5305
|
return d.toISOString().replace(/[:.]/g, "-");
|
|
@@ -5146,7 +5309,7 @@ function createApplyLog(opts) {
|
|
|
5146
5309
|
const dir = containerLogsDir(opts.name, opts.home);
|
|
5147
5310
|
mkdirSync(dir, { recursive: true });
|
|
5148
5311
|
const file = `apply-${opts.name}-${safeIsoStamp(now)}.log`;
|
|
5149
|
-
const fullPath =
|
|
5312
|
+
const fullPath = path14.join(dir, file);
|
|
5150
5313
|
const stream = createWriteStream(fullPath, { flags: "w" });
|
|
5151
5314
|
const header = [
|
|
5152
5315
|
`# monoceros apply log`,
|
|
@@ -6013,8 +6176,8 @@ var init_markers = __esm({
|
|
|
6013
6176
|
});
|
|
6014
6177
|
|
|
6015
6178
|
// src/briefing/index.ts
|
|
6016
|
-
import { promises as
|
|
6017
|
-
import
|
|
6179
|
+
import { promises as fs11 } from "fs";
|
|
6180
|
+
import path15 from "path";
|
|
6018
6181
|
async function writeBriefing(input) {
|
|
6019
6182
|
const subCommands = input.subCommands ?? await loadSubCommandsDynamic();
|
|
6020
6183
|
const manifestLoader = input.manifestLoader ?? ((ref) => loadFeatureManifestSummary(ref));
|
|
@@ -6027,12 +6190,12 @@ async function writeBriefing(input) {
|
|
|
6027
6190
|
);
|
|
6028
6191
|
const claudeBody = generateClaudeMd();
|
|
6029
6192
|
const commandsBody = generateCommandsMd(subCommands);
|
|
6030
|
-
await writeMarkerAware(
|
|
6031
|
-
await writeMarkerAware(
|
|
6032
|
-
const monocerosDir =
|
|
6033
|
-
await
|
|
6034
|
-
await
|
|
6035
|
-
|
|
6193
|
+
await writeMarkerAware(path15.join(input.targetDir, "AGENTS.md"), agentsBody);
|
|
6194
|
+
await writeMarkerAware(path15.join(input.targetDir, "CLAUDE.md"), claudeBody);
|
|
6195
|
+
const monocerosDir = path15.join(input.targetDir, ".monoceros");
|
|
6196
|
+
await fs11.mkdir(monocerosDir, { recursive: true });
|
|
6197
|
+
await fs11.writeFile(
|
|
6198
|
+
path15.join(monocerosDir, "commands.md"),
|
|
6036
6199
|
commandsBody,
|
|
6037
6200
|
"utf8"
|
|
6038
6201
|
);
|
|
@@ -6052,18 +6215,18 @@ function featureDisplayMap(components) {
|
|
|
6052
6215
|
async function writeMarkerAware(filePath, body) {
|
|
6053
6216
|
let existing = null;
|
|
6054
6217
|
try {
|
|
6055
|
-
existing = await
|
|
6218
|
+
existing = await fs11.readFile(filePath, "utf8");
|
|
6056
6219
|
} catch (err) {
|
|
6057
6220
|
if (err.code !== "ENOENT") throw err;
|
|
6058
6221
|
}
|
|
6059
6222
|
if (existing) {
|
|
6060
6223
|
const updated = replaceMarkerBlock(existing, body);
|
|
6061
6224
|
if (updated !== null) {
|
|
6062
|
-
await
|
|
6225
|
+
await fs11.writeFile(filePath, updated, "utf8");
|
|
6063
6226
|
return;
|
|
6064
6227
|
}
|
|
6065
6228
|
}
|
|
6066
|
-
await
|
|
6229
|
+
await fs11.writeFile(filePath, wrapWithMarkers(body), "utf8");
|
|
6067
6230
|
}
|
|
6068
6231
|
async function loadSubCommandsDynamic() {
|
|
6069
6232
|
const mod = await Promise.resolve().then(() => (init_main(), main_exports));
|
|
@@ -6188,10 +6351,10 @@ var init_runtime_pull_hint = __esm({
|
|
|
6188
6351
|
});
|
|
6189
6352
|
|
|
6190
6353
|
// src/devcontainer/cli.ts
|
|
6191
|
-
import { spawn as
|
|
6354
|
+
import { spawn as spawn5 } from "child_process";
|
|
6192
6355
|
import { readFileSync as readFileSync4 } from "fs";
|
|
6193
6356
|
import { createRequire } from "module";
|
|
6194
|
-
import
|
|
6357
|
+
import path16 from "path";
|
|
6195
6358
|
function devcontainerCliPath() {
|
|
6196
6359
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
6197
6360
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
@@ -6200,7 +6363,7 @@ function devcontainerCliPath() {
|
|
|
6200
6363
|
if (!binEntry) {
|
|
6201
6364
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
6202
6365
|
}
|
|
6203
|
-
cachedBinaryPath =
|
|
6366
|
+
cachedBinaryPath = path16.resolve(path16.dirname(pkgJsonPath), binEntry);
|
|
6204
6367
|
return cachedBinaryPath;
|
|
6205
6368
|
}
|
|
6206
6369
|
var require_, cachedBinaryPath, spawnDevcontainer;
|
|
@@ -6216,7 +6379,7 @@ var init_cli = __esm({
|
|
|
6216
6379
|
const env = { ...process.env, DOCKER_CLI_HINTS: "false" };
|
|
6217
6380
|
return new Promise((resolve, reject) => {
|
|
6218
6381
|
if (options.interactive) {
|
|
6219
|
-
const child2 =
|
|
6382
|
+
const child2 = spawn5(process.execPath, [binPath, ...args], {
|
|
6220
6383
|
cwd,
|
|
6221
6384
|
env,
|
|
6222
6385
|
stdio: "inherit"
|
|
@@ -6225,7 +6388,7 @@ var init_cli = __esm({
|
|
|
6225
6388
|
child2.on("exit", (code) => resolve(code ?? 0));
|
|
6226
6389
|
return;
|
|
6227
6390
|
}
|
|
6228
|
-
const child =
|
|
6391
|
+
const child = spawn5(process.execPath, [binPath, ...args], {
|
|
6229
6392
|
cwd,
|
|
6230
6393
|
env,
|
|
6231
6394
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -6273,9 +6436,9 @@ var init_cli = __esm({
|
|
|
6273
6436
|
});
|
|
6274
6437
|
|
|
6275
6438
|
// src/devcontainer/compose.ts
|
|
6276
|
-
import { spawn as
|
|
6277
|
-
import { existsSync as
|
|
6278
|
-
import
|
|
6439
|
+
import { spawn as spawn6 } from "child_process";
|
|
6440
|
+
import { existsSync as existsSync9 } from "fs";
|
|
6441
|
+
import path17 from "path";
|
|
6279
6442
|
import { Writable as Writable3 } from "stream";
|
|
6280
6443
|
import { consola as consola10 } from "consola";
|
|
6281
6444
|
async function findContainerIds(filters, exec = spawnDocker) {
|
|
@@ -6325,18 +6488,24 @@ async function cleanupDockerObjects(opts) {
|
|
|
6325
6488
|
return { exitCode: rmExit, removedIds: ids };
|
|
6326
6489
|
}
|
|
6327
6490
|
function composeProjectName(root) {
|
|
6328
|
-
return `${
|
|
6491
|
+
return `${path17.basename(root)}_devcontainer`;
|
|
6329
6492
|
}
|
|
6330
|
-
function
|
|
6331
|
-
if (!
|
|
6493
|
+
function assertDevcontainer(root) {
|
|
6494
|
+
if (!existsSync9(path17.join(root, ".devcontainer"))) {
|
|
6332
6495
|
throw new Error(
|
|
6333
6496
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
6334
6497
|
);
|
|
6335
6498
|
}
|
|
6336
|
-
|
|
6337
|
-
|
|
6499
|
+
}
|
|
6500
|
+
function isComposeMode(root) {
|
|
6501
|
+
return existsSync9(path17.join(root, ".devcontainer", "compose.yaml"));
|
|
6502
|
+
}
|
|
6503
|
+
function resolveCompose(root) {
|
|
6504
|
+
assertDevcontainer(root);
|
|
6505
|
+
const composeFile = path17.join(root, ".devcontainer", "compose.yaml");
|
|
6506
|
+
if (!existsSync9(composeFile)) {
|
|
6338
6507
|
throw new Error(
|
|
6339
|
-
`No compose.yaml at ${composeFile}. \`
|
|
6508
|
+
`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.`
|
|
6340
6509
|
);
|
|
6341
6510
|
}
|
|
6342
6511
|
return { composeFile, projectName: composeProjectName(root) };
|
|
@@ -6348,7 +6517,7 @@ async function runComposeAction(buildSubArgs, opts) {
|
|
|
6348
6517
|
return spawnFn(["-f", composeFile, "-p", projectName, ...subArgs], opts.root);
|
|
6349
6518
|
}
|
|
6350
6519
|
async function runStart(opts) {
|
|
6351
|
-
|
|
6520
|
+
assertDevcontainer(opts.root);
|
|
6352
6521
|
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6353
6522
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
6354
6523
|
logger.info(`Bringing devcontainer up at ${opts.root}\u2026`);
|
|
@@ -6491,16 +6660,71 @@ and retry \`monoceros apply\`.`
|
|
|
6491
6660
|
);
|
|
6492
6661
|
}
|
|
6493
6662
|
function runStop(opts) {
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6663
|
+
assertDevcontainer(opts.root);
|
|
6664
|
+
if (isComposeMode(opts.root)) {
|
|
6665
|
+
return runComposeAction(
|
|
6666
|
+
(service) => ["stop", ...service ? [service] : []],
|
|
6667
|
+
opts
|
|
6668
|
+
);
|
|
6669
|
+
}
|
|
6670
|
+
return stopImageContainer(opts);
|
|
6498
6671
|
}
|
|
6499
6672
|
function runStatus(opts) {
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6673
|
+
assertDevcontainer(opts.root);
|
|
6674
|
+
if (isComposeMode(opts.root)) {
|
|
6675
|
+
return runComposeAction(
|
|
6676
|
+
(service) => ["ps", ...service ? [service] : []],
|
|
6677
|
+
opts
|
|
6678
|
+
);
|
|
6679
|
+
}
|
|
6680
|
+
return statusImageContainer(opts);
|
|
6681
|
+
}
|
|
6682
|
+
function imageContainerFilter(root) {
|
|
6683
|
+
return `label=devcontainer.local_folder=${root}`;
|
|
6684
|
+
}
|
|
6685
|
+
async function stopImageContainer(opts) {
|
|
6686
|
+
const exec = opts.dockerExec ?? spawnDocker;
|
|
6687
|
+
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6688
|
+
const name = path17.basename(opts.root);
|
|
6689
|
+
const ps = await exec([
|
|
6690
|
+
"ps",
|
|
6691
|
+
"-q",
|
|
6692
|
+
"--filter",
|
|
6693
|
+
imageContainerFilter(opts.root),
|
|
6694
|
+
"--filter",
|
|
6695
|
+
"status=running"
|
|
6696
|
+
]);
|
|
6697
|
+
const id = ps.stdout.split("\n").map((s) => s.trim()).find((s) => s.length > 0);
|
|
6698
|
+
if (!id) {
|
|
6699
|
+
logger.info(`Container '${name}' is not running.`);
|
|
6700
|
+
return 0;
|
|
6701
|
+
}
|
|
6702
|
+
const res = await exec(["stop", id]);
|
|
6703
|
+
if (res.exitCode === 0) logger.info(`Stopped '${name}'.`);
|
|
6704
|
+
return res.exitCode;
|
|
6705
|
+
}
|
|
6706
|
+
async function statusImageContainer(opts) {
|
|
6707
|
+
const exec = opts.dockerExec ?? spawnDocker;
|
|
6708
|
+
const logger = opts.logger ?? { info: (msg) => consola10.info(msg) };
|
|
6709
|
+
const name = path17.basename(opts.root);
|
|
6710
|
+
const res = await exec([
|
|
6711
|
+
"ps",
|
|
6712
|
+
"-a",
|
|
6713
|
+
"--filter",
|
|
6714
|
+
imageContainerFilter(opts.root)
|
|
6715
|
+
]);
|
|
6716
|
+
const rows = res.stdout.split("\n").filter((l) => l.trim().length > 0);
|
|
6717
|
+
if (rows.length <= 1) {
|
|
6718
|
+
logger.info(
|
|
6719
|
+
`Container '${name}' does not exist. Run \`monoceros apply ${name}\`.`
|
|
6720
|
+
);
|
|
6721
|
+
return 0;
|
|
6722
|
+
}
|
|
6723
|
+
process.stdout.write(
|
|
6724
|
+
res.stdout.endsWith("\n") ? res.stdout : `${res.stdout}
|
|
6725
|
+
`
|
|
6503
6726
|
);
|
|
6727
|
+
return res.exitCode;
|
|
6504
6728
|
}
|
|
6505
6729
|
function runLogs(opts) {
|
|
6506
6730
|
const follow = opts.follow ?? true;
|
|
@@ -6523,7 +6747,7 @@ var init_compose = __esm({
|
|
|
6523
6747
|
init_proxy();
|
|
6524
6748
|
spawnDockerCompose = (args, cwd) => {
|
|
6525
6749
|
return new Promise((resolve, reject) => {
|
|
6526
|
-
const child =
|
|
6750
|
+
const child = spawn6("docker", ["compose", ...args], {
|
|
6527
6751
|
cwd,
|
|
6528
6752
|
stdio: ["inherit", "pipe", "pipe"]
|
|
6529
6753
|
});
|
|
@@ -6535,7 +6759,7 @@ var init_compose = __esm({
|
|
|
6535
6759
|
};
|
|
6536
6760
|
spawnDocker = (args) => {
|
|
6537
6761
|
return new Promise((resolve, reject) => {
|
|
6538
|
-
const child =
|
|
6762
|
+
const child = spawn6("docker", args, {
|
|
6539
6763
|
stdio: ["ignore", "pipe", "pipe"]
|
|
6540
6764
|
});
|
|
6541
6765
|
let stdout = "";
|
|
@@ -6596,9 +6820,9 @@ var init_images = __esm({
|
|
|
6596
6820
|
|
|
6597
6821
|
// src/config/machine-state.ts
|
|
6598
6822
|
import { promises as fsp4 } from "fs";
|
|
6599
|
-
import
|
|
6823
|
+
import path18 from "path";
|
|
6600
6824
|
function machineStatePath(home = monocerosHome()) {
|
|
6601
|
-
return
|
|
6825
|
+
return path18.join(home, ".machine-state.json");
|
|
6602
6826
|
}
|
|
6603
6827
|
async function readMachineState(home = monocerosHome()) {
|
|
6604
6828
|
try {
|
|
@@ -6653,7 +6877,7 @@ var init_machine_state = __esm({
|
|
|
6653
6877
|
});
|
|
6654
6878
|
|
|
6655
6879
|
// src/devcontainer/docker-mode.ts
|
|
6656
|
-
import { spawn as
|
|
6880
|
+
import { spawn as spawn7 } from "child_process";
|
|
6657
6881
|
async function detectDockerMode(options = {}) {
|
|
6658
6882
|
const spawnFn = options.spawn ?? realDockerInfo;
|
|
6659
6883
|
try {
|
|
@@ -6703,7 +6927,7 @@ var init_docker_mode = __esm({
|
|
|
6703
6927
|
init_format();
|
|
6704
6928
|
realDockerInfo = () => {
|
|
6705
6929
|
return new Promise((resolve, reject) => {
|
|
6706
|
-
const child =
|
|
6930
|
+
const child = spawn7(
|
|
6707
6931
|
"docker",
|
|
6708
6932
|
["info", "--format", "{{json .SecurityOptions}}"],
|
|
6709
6933
|
{
|
|
@@ -6722,9 +6946,9 @@ var init_docker_mode = __esm({
|
|
|
6722
6946
|
});
|
|
6723
6947
|
|
|
6724
6948
|
// src/devcontainer/identity.ts
|
|
6725
|
-
import { spawn as
|
|
6726
|
-
import { promises as
|
|
6727
|
-
import
|
|
6949
|
+
import { spawn as spawn8 } from "child_process";
|
|
6950
|
+
import { promises as fs12 } from "fs";
|
|
6951
|
+
import path19 from "path";
|
|
6728
6952
|
import { consola as consola11 } from "consola";
|
|
6729
6953
|
async function resolveIdentityWithPrompt(options = {}) {
|
|
6730
6954
|
const spawnFn = options.spawn ?? realGitConfigGet;
|
|
@@ -6780,8 +7004,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
6780
7004
|
};
|
|
6781
7005
|
}
|
|
6782
7006
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
6783
|
-
const gitconfigDir =
|
|
6784
|
-
const gitconfigPath =
|
|
7007
|
+
const gitconfigDir = path19.join(devContainerRoot, ".monoceros");
|
|
7008
|
+
const gitconfigPath = path19.join(gitconfigDir, "gitconfig");
|
|
6785
7009
|
const logger = options.logger ?? { info: () => {
|
|
6786
7010
|
}, warn: () => {
|
|
6787
7011
|
} };
|
|
@@ -6794,8 +7018,8 @@ async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
|
6794
7018
|
const lines = ["[user]"];
|
|
6795
7019
|
if (resolved.name !== void 0) lines.push(` name = ${resolved.name}`);
|
|
6796
7020
|
if (resolved.email !== void 0) lines.push(` email = ${resolved.email}`);
|
|
6797
|
-
await
|
|
6798
|
-
await
|
|
7021
|
+
await fs12.mkdir(gitconfigDir, { recursive: true });
|
|
7022
|
+
await fs12.writeFile(gitconfigPath, lines.join("\n") + "\n");
|
|
6799
7023
|
return {
|
|
6800
7024
|
...resolved.name !== void 0 ? { name: resolved.name } : {},
|
|
6801
7025
|
...resolved.email !== void 0 ? { email: resolved.email } : {},
|
|
@@ -6838,7 +7062,7 @@ async function readKeyFromHost(spawnFn, key, logger) {
|
|
|
6838
7062
|
}
|
|
6839
7063
|
async function readExistingGitconfig(filePath) {
|
|
6840
7064
|
try {
|
|
6841
|
-
const content = await
|
|
7065
|
+
const content = await fs12.readFile(filePath, "utf8");
|
|
6842
7066
|
const result = {};
|
|
6843
7067
|
const nameMatch = /^\s*name\s*=\s*(.+?)\s*$/m.exec(content);
|
|
6844
7068
|
const emailMatch = /^\s*email\s*=\s*(.+?)\s*$/m.exec(content);
|
|
@@ -6855,7 +7079,7 @@ var init_identity = __esm({
|
|
|
6855
7079
|
"use strict";
|
|
6856
7080
|
realGitConfigGet = (key) => {
|
|
6857
7081
|
return new Promise((resolve, reject) => {
|
|
6858
|
-
const child =
|
|
7082
|
+
const child = spawn8("git", ["config", "--global", "--get", key], {
|
|
6859
7083
|
stdio: ["ignore", "pipe", "inherit"]
|
|
6860
7084
|
});
|
|
6861
7085
|
let stdout = "";
|
|
@@ -6915,7 +7139,7 @@ var init_identity = __esm({
|
|
|
6915
7139
|
});
|
|
6916
7140
|
|
|
6917
7141
|
// src/apply/index.ts
|
|
6918
|
-
import { existsSync as
|
|
7142
|
+
import { existsSync as existsSync10, promises as fs13 } from "fs";
|
|
6919
7143
|
import { consola as consola12 } from "consola";
|
|
6920
7144
|
async function runApply(opts) {
|
|
6921
7145
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -6937,7 +7161,7 @@ ${sectionLine(label)}
|
|
|
6937
7161
|
);
|
|
6938
7162
|
}
|
|
6939
7163
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
6940
|
-
if (!
|
|
7164
|
+
if (!existsSync10(ymlPath)) {
|
|
6941
7165
|
throw new Error(
|
|
6942
7166
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
6943
7167
|
);
|
|
@@ -7055,7 +7279,7 @@ Fix the value in the env file (or the yml).`
|
|
|
7055
7279
|
if (dockerMode === "rootless") {
|
|
7056
7280
|
throw new Error(formatRootlessNotSupportedError());
|
|
7057
7281
|
}
|
|
7058
|
-
await
|
|
7282
|
+
await fs13.mkdir(targetDir, { recursive: true });
|
|
7059
7283
|
await writeScaffold(createOpts, targetDir, { dockerMode });
|
|
7060
7284
|
await writeStateFile(
|
|
7061
7285
|
targetDir,
|
|
@@ -7066,6 +7290,27 @@ Fix the value in the env file (or the yml).`
|
|
|
7066
7290
|
...opts.now ? { now: opts.now } : {}
|
|
7067
7291
|
})
|
|
7068
7292
|
);
|
|
7293
|
+
if (runtimeSupportsSshAttach(createOpts.runtimeVersion)) {
|
|
7294
|
+
try {
|
|
7295
|
+
const ssh = await setupSshAttach({
|
|
7296
|
+
name: opts.name,
|
|
7297
|
+
targetDir,
|
|
7298
|
+
home,
|
|
7299
|
+
...opts.sshKeygen ? { keygen: opts.sshKeygen } : {},
|
|
7300
|
+
...opts.sshUserSshDir ? { userSshDir: opts.sshUserSshDir } : {},
|
|
7301
|
+
logger: idLogger
|
|
7302
|
+
});
|
|
7303
|
+
if (ssh.configured) {
|
|
7304
|
+
logger.info(
|
|
7305
|
+
`SSH attach: \`ssh ${ssh.hostAlias}\` (or pick it in your IDE)`
|
|
7306
|
+
);
|
|
7307
|
+
}
|
|
7308
|
+
} catch (err) {
|
|
7309
|
+
(logger.warn ?? logger.info)(
|
|
7310
|
+
`SSH attach setup skipped: ${err instanceof Error ? err.message : String(err)}`
|
|
7311
|
+
);
|
|
7312
|
+
}
|
|
7313
|
+
}
|
|
7069
7314
|
try {
|
|
7070
7315
|
const components = await loadComponentCatalog();
|
|
7071
7316
|
await writeBriefing({ targetDir, createOpts, components });
|
|
@@ -7210,8 +7455,8 @@ ${stripAnsi(formatted)}
|
|
|
7210
7455
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
7211
7456
|
}
|
|
7212
7457
|
async function assertSafeTargetDir(targetDir, expectedOrigin) {
|
|
7213
|
-
if (!
|
|
7214
|
-
const entries = await
|
|
7458
|
+
if (!existsSync10(targetDir)) return;
|
|
7459
|
+
const entries = await fs13.readdir(targetDir);
|
|
7215
7460
|
if (entries.length === 0) return;
|
|
7216
7461
|
const state = await readStateFile(targetDir);
|
|
7217
7462
|
if (state) {
|
|
@@ -7281,7 +7526,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
7281
7526
|
}
|
|
7282
7527
|
if (wantContainer) {
|
|
7283
7528
|
try {
|
|
7284
|
-
const text = await
|
|
7529
|
+
const text = await fs13.readFile(ymlPath, "utf8");
|
|
7285
7530
|
const parsed = parseConfig(text, ymlPath);
|
|
7286
7531
|
const changed = setContainerGitUserInDoc(parsed.doc, {
|
|
7287
7532
|
name: prompted.name,
|
|
@@ -7289,7 +7534,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
7289
7534
|
});
|
|
7290
7535
|
if (changed) {
|
|
7291
7536
|
const out = stringifyConfig(parsed.doc);
|
|
7292
|
-
await
|
|
7537
|
+
await fs13.writeFile(ymlPath, out, "utf8");
|
|
7293
7538
|
logger.info(
|
|
7294
7539
|
`Saved identity in this container \u2014 wrote git.user into ${prettyPath(ymlPath)}.`
|
|
7295
7540
|
);
|
|
@@ -7312,6 +7557,7 @@ var init_apply = __esm({
|
|
|
7312
7557
|
init_state();
|
|
7313
7558
|
init_transform();
|
|
7314
7559
|
init_catalog();
|
|
7560
|
+
init_ssh_attach();
|
|
7315
7561
|
init_scaffold();
|
|
7316
7562
|
init_format();
|
|
7317
7563
|
init_ref();
|
|
@@ -7335,87 +7581,383 @@ var init_apply = __esm({
|
|
|
7335
7581
|
}
|
|
7336
7582
|
});
|
|
7337
7583
|
|
|
7338
|
-
// src/
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
}
|
|
7345
|
-
});
|
|
7346
|
-
|
|
7347
|
-
// src/commands/_dispatch.ts
|
|
7348
|
-
import { consola as consola13 } from "consola";
|
|
7349
|
-
async function dispatch(runner) {
|
|
7584
|
+
// src/devcontainer/browser-bridge.ts
|
|
7585
|
+
import { spawn as spawn9 } from "child_process";
|
|
7586
|
+
import { existsSync as existsSync11, promises as fsp5, readFileSync as readFileSync5 } from "fs";
|
|
7587
|
+
import http from "http";
|
|
7588
|
+
import path20 from "path";
|
|
7589
|
+
function parseCallbackTarget(authUrl) {
|
|
7350
7590
|
try {
|
|
7351
|
-
const
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7591
|
+
const u = new URL(authUrl);
|
|
7592
|
+
const redirect = u.searchParams.get("redirect_uri");
|
|
7593
|
+
if (!redirect) return null;
|
|
7594
|
+
const r = new URL(redirect);
|
|
7595
|
+
if (r.hostname !== "localhost" && r.hostname !== "127.0.0.1") return null;
|
|
7596
|
+
const port = Number(r.port);
|
|
7597
|
+
if (!Number.isInteger(port) || port <= 0) return null;
|
|
7598
|
+
return { port, pathname: r.pathname };
|
|
7599
|
+
} catch {
|
|
7600
|
+
return null;
|
|
7356
7601
|
}
|
|
7357
7602
|
}
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7603
|
+
function nextRelayUrl(content, lastOpened) {
|
|
7604
|
+
const url = content.trim();
|
|
7605
|
+
return url && url !== lastOpened ? url : null;
|
|
7606
|
+
}
|
|
7607
|
+
function wrapExec(command, opts) {
|
|
7608
|
+
const leading = [];
|
|
7609
|
+
const stmts = [];
|
|
7610
|
+
if (opts.pathPrepend) {
|
|
7611
|
+
leading.push(opts.pathPrepend);
|
|
7612
|
+
const i = leading.length;
|
|
7613
|
+
stmts.push(`export PATH="$${i}:$PATH"`);
|
|
7614
|
+
stmts.push(`export BROWSER="$${i}/xdg-open"`);
|
|
7361
7615
|
}
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
},
|
|
7379
|
-
args: {
|
|
7380
|
-
name: {
|
|
7381
|
-
type: "positional",
|
|
7382
|
-
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
7383
|
-
required: true
|
|
7384
|
-
},
|
|
7385
|
-
verbose: {
|
|
7386
|
-
type: "boolean",
|
|
7387
|
-
description: "Stream the raw @devcontainers/cli output to stderr instead of showing a phase spinner. Auto-enabled when stderr is not a TTY.",
|
|
7388
|
-
default: false
|
|
7389
|
-
}
|
|
7390
|
-
},
|
|
7391
|
-
run({ args }) {
|
|
7392
|
-
return dispatch(async () => {
|
|
7393
|
-
const result = await runApply({
|
|
7394
|
-
name: args.name,
|
|
7395
|
-
cliVersion: CLI_VERSION,
|
|
7396
|
-
verbose: args.verbose
|
|
7397
|
-
});
|
|
7398
|
-
return result.containerExitCode;
|
|
7399
|
-
});
|
|
7400
|
-
}
|
|
7616
|
+
if (opts.cwd) {
|
|
7617
|
+
leading.push(opts.cwd);
|
|
7618
|
+
stmts.push(`cd -- "$${leading.length}"`);
|
|
7619
|
+
}
|
|
7620
|
+
if (leading.length === 0) return command;
|
|
7621
|
+
const shift = leading.length === 1 ? "shift" : `shift ${leading.length}`;
|
|
7622
|
+
const script = `${stmts.join(" && ")} && ${shift} && exec "$@"`;
|
|
7623
|
+
return ["bash", "-lc", script, "bash", ...leading, ...command];
|
|
7624
|
+
}
|
|
7625
|
+
function openInBrowser(url) {
|
|
7626
|
+
const platform = process.platform;
|
|
7627
|
+
const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
7628
|
+
try {
|
|
7629
|
+
const child = spawn9(cmd, args, {
|
|
7630
|
+
stdio: "ignore",
|
|
7631
|
+
detached: true
|
|
7401
7632
|
});
|
|
7633
|
+
child.on("error", () => {
|
|
7634
|
+
});
|
|
7635
|
+
child.unref();
|
|
7636
|
+
} catch {
|
|
7402
7637
|
}
|
|
7403
|
-
}
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7638
|
+
}
|
|
7639
|
+
async function startBrowserBridge(opts) {
|
|
7640
|
+
const relayDir = path20.join(opts.root, RELAY_DIRNAME);
|
|
7641
|
+
const relayScript = path20.join(relayDir, "xdg-open");
|
|
7642
|
+
const urlFile = path20.join(relayDir, "url");
|
|
7643
|
+
await fsp5.mkdir(relayDir, { recursive: true });
|
|
7644
|
+
await fsp5.rm(urlFile, { force: true });
|
|
7645
|
+
await fsp5.writeFile(
|
|
7646
|
+
relayScript,
|
|
7647
|
+
`#!/bin/sh
|
|
7648
|
+
printf '%s\\n' "$1" > "$(dirname "$0")/url"
|
|
7649
|
+
exit 0
|
|
7650
|
+
`,
|
|
7651
|
+
{ mode: 493 }
|
|
7652
|
+
);
|
|
7653
|
+
await fsp5.chmod(relayScript, 493);
|
|
7654
|
+
const servers = [];
|
|
7655
|
+
let lastOpened = null;
|
|
7656
|
+
const onUrl = (url) => {
|
|
7657
|
+
openInBrowser(url);
|
|
7658
|
+
const target = parseCallbackTarget(url);
|
|
7659
|
+
if (!target) return;
|
|
7660
|
+
const server = http.createServer((req, res) => {
|
|
7661
|
+
const reqUrl = req.url ?? target.pathname;
|
|
7662
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
7663
|
+
res.end(
|
|
7664
|
+
'<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>'
|
|
7665
|
+
);
|
|
7666
|
+
void opts.spawn(
|
|
7667
|
+
[
|
|
7668
|
+
"exec",
|
|
7669
|
+
"--workspace-folder",
|
|
7670
|
+
opts.root,
|
|
7671
|
+
"--mount-workspace-git-root=false",
|
|
7672
|
+
"curl",
|
|
7673
|
+
"-fsS",
|
|
7674
|
+
`http://localhost:${target.port}${reqUrl}`
|
|
7675
|
+
],
|
|
7676
|
+
opts.root,
|
|
7677
|
+
{ quiet: true }
|
|
7678
|
+
);
|
|
7679
|
+
});
|
|
7680
|
+
server.on("error", () => {
|
|
7681
|
+
});
|
|
7682
|
+
server.listen(target.port, "127.0.0.1");
|
|
7683
|
+
servers.push(server);
|
|
7684
|
+
};
|
|
7685
|
+
const poll = setInterval(() => {
|
|
7686
|
+
if (!existsSync11(urlFile)) return;
|
|
7687
|
+
let content = "";
|
|
7688
|
+
try {
|
|
7689
|
+
content = readFileSync5(urlFile, "utf8");
|
|
7690
|
+
} catch {
|
|
7691
|
+
return;
|
|
7692
|
+
}
|
|
7693
|
+
const url = nextRelayUrl(content, lastOpened);
|
|
7694
|
+
if (!url) return;
|
|
7695
|
+
lastOpened = url;
|
|
7696
|
+
onUrl(url);
|
|
7697
|
+
}, 250);
|
|
7698
|
+
return {
|
|
7699
|
+
relayDirInContainer: `/workspaces/${opts.name}/${RELAY_DIRNAME}`,
|
|
7700
|
+
async dispose() {
|
|
7701
|
+
clearInterval(poll);
|
|
7702
|
+
for (const s of servers) s.close();
|
|
7703
|
+
await fsp5.rm(relayDir, { recursive: true, force: true });
|
|
7704
|
+
}
|
|
7705
|
+
};
|
|
7706
|
+
}
|
|
7707
|
+
var RELAY_DIRNAME;
|
|
7708
|
+
var init_browser_bridge = __esm({
|
|
7709
|
+
"src/devcontainer/browser-bridge.ts"() {
|
|
7710
|
+
"use strict";
|
|
7711
|
+
RELAY_DIRNAME = ".monoceros-bridge";
|
|
7712
|
+
}
|
|
7713
|
+
});
|
|
7714
|
+
|
|
7715
|
+
// src/devcontainer/shell.ts
|
|
7716
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7717
|
+
import path21 from "path";
|
|
7718
|
+
async function runShell(opts) {
|
|
7719
|
+
assertContainerExists(opts.root);
|
|
7720
|
+
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
7721
|
+
const upCode = await spawnFn(
|
|
7722
|
+
["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
|
|
7723
|
+
opts.root,
|
|
7724
|
+
{ quiet: true }
|
|
7725
|
+
);
|
|
7726
|
+
if (upCode !== 0) return upCode;
|
|
7727
|
+
const name = opts.name ?? path21.basename(opts.root);
|
|
7728
|
+
const isTty2 = opts.isTty ?? process.stdout.isTTY;
|
|
7729
|
+
const bridge = isTty2 ? await startBrowserBridge({ name, root: opts.root, spawn: spawnFn }) : null;
|
|
7730
|
+
try {
|
|
7731
|
+
const innerExec = wrapExec(
|
|
7732
|
+
["bash"],
|
|
7733
|
+
bridge ? { pathPrepend: bridge.relayDirInContainer } : {}
|
|
7734
|
+
);
|
|
7735
|
+
return await spawnFn(
|
|
7736
|
+
[
|
|
7737
|
+
"exec",
|
|
7738
|
+
"--workspace-folder",
|
|
7739
|
+
opts.root,
|
|
7740
|
+
"--mount-workspace-git-root=false",
|
|
7741
|
+
...innerExec
|
|
7742
|
+
],
|
|
7743
|
+
opts.root,
|
|
7744
|
+
{ interactive: true }
|
|
7745
|
+
);
|
|
7746
|
+
} finally {
|
|
7747
|
+
if (bridge) await bridge.dispose();
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
function assertContainerExists(root) {
|
|
7751
|
+
if (!existsSync12(path21.join(root, ".devcontainer"))) {
|
|
7752
|
+
throw new Error(
|
|
7753
|
+
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
7754
|
+
);
|
|
7755
|
+
}
|
|
7756
|
+
}
|
|
7757
|
+
var init_shell = __esm({
|
|
7758
|
+
"src/devcontainer/shell.ts"() {
|
|
7759
|
+
"use strict";
|
|
7760
|
+
init_browser_bridge();
|
|
7761
|
+
init_cli();
|
|
7762
|
+
}
|
|
7763
|
+
});
|
|
7764
|
+
|
|
7765
|
+
// src/open/index.ts
|
|
7766
|
+
import { spawn as spawn10 } from "child_process";
|
|
7767
|
+
import { accessSync, constants, existsSync as existsSync13 } from "fs";
|
|
7768
|
+
import path22 from "path";
|
|
7769
|
+
import { consola as consola13 } from "consola";
|
|
7770
|
+
function resolveOnPath(bin) {
|
|
7771
|
+
const dirs = (process.env.PATH ?? "").split(path22.delimiter).filter(Boolean);
|
|
7772
|
+
for (const dir of dirs) {
|
|
7773
|
+
const candidate = path22.join(dir, bin);
|
|
7774
|
+
try {
|
|
7775
|
+
accessSync(candidate, constants.X_OK);
|
|
7776
|
+
return candidate;
|
|
7777
|
+
} catch {
|
|
7778
|
+
}
|
|
7779
|
+
}
|
|
7780
|
+
return null;
|
|
7781
|
+
}
|
|
7782
|
+
function resolveEditorBinary(tool) {
|
|
7783
|
+
if (resolveOnPath(tool.bin)) return tool.bin;
|
|
7784
|
+
if (process.platform === "darwin" && existsSync13(tool.macAppBin)) {
|
|
7785
|
+
return tool.macAppBin;
|
|
7786
|
+
}
|
|
7787
|
+
return null;
|
|
7788
|
+
}
|
|
7789
|
+
function realLaunch(bin, args) {
|
|
7790
|
+
const child = spawn10(bin, args, {
|
|
7791
|
+
detached: true,
|
|
7792
|
+
stdio: "ignore"
|
|
7793
|
+
});
|
|
7794
|
+
child.unref();
|
|
7795
|
+
}
|
|
7796
|
+
async function runOpen(opts) {
|
|
7797
|
+
const home = opts.monocerosHome ?? monocerosHome();
|
|
7798
|
+
const logger = opts.logger ?? {
|
|
7799
|
+
info: (m) => consola13.info(m),
|
|
7800
|
+
warn: (m) => consola13.warn(m)
|
|
7801
|
+
};
|
|
7802
|
+
const root = containerDir(opts.name, home);
|
|
7803
|
+
if (opts.tool === "shell") {
|
|
7804
|
+
const shellRunner = opts.shellRunner ?? runShell;
|
|
7805
|
+
return shellRunner({ root, name: opts.name });
|
|
7806
|
+
}
|
|
7807
|
+
const editor = EDITORS[opts.tool];
|
|
7808
|
+
if (!editor) {
|
|
7809
|
+
throw new Error(
|
|
7810
|
+
`Unknown tool '${opts.tool}'. Supported: ${OPEN_TOOLS.join(", ")}.`
|
|
7811
|
+
);
|
|
7812
|
+
}
|
|
7813
|
+
if (!existsSync13(sshConfigEntryPath(home, opts.name))) {
|
|
7814
|
+
throw new Error(
|
|
7815
|
+
`SSH attach isn't set up for '${opts.name}'. Run \`monoceros apply ${opts.name}\` first (needs a runtime >= 1.2.0).`
|
|
7816
|
+
);
|
|
7817
|
+
}
|
|
7818
|
+
const running = await findRunningContainerByLocalFolder(
|
|
7819
|
+
root,
|
|
7820
|
+
opts.dockerLookup ? { docker: opts.dockerLookup } : {}
|
|
7821
|
+
);
|
|
7822
|
+
if (!running) {
|
|
7823
|
+
throw new Error(
|
|
7824
|
+
`Container '${opts.name}' isn't running. Bring it up with \`monoceros apply ${opts.name}\`, then retry.`
|
|
7825
|
+
);
|
|
7826
|
+
}
|
|
7827
|
+
const resolve = opts.binResolver ?? resolveEditorBinary;
|
|
7828
|
+
const bin = resolve(editor);
|
|
7829
|
+
if (!bin) {
|
|
7830
|
+
const where = process.platform === "darwin" ? " on PATH or in /Applications" : " on PATH";
|
|
7831
|
+
throw new Error(
|
|
7832
|
+
`${editor.label} ('${editor.bin}') not found${where}. In ${editor.label}, ${editor.setupHint}.`
|
|
7833
|
+
);
|
|
7834
|
+
}
|
|
7835
|
+
const uri = `vscode-remote://ssh-remote+monoceros-${opts.name}/workspaces/${opts.name}/${opts.name}.code-workspace`;
|
|
7836
|
+
const launch = opts.launch ?? realLaunch;
|
|
7837
|
+
launch(bin, ["--file-uri", uri]);
|
|
7838
|
+
logger.info(`Opening '${opts.name}' in ${editor.label}...`);
|
|
7839
|
+
return 0;
|
|
7840
|
+
}
|
|
7841
|
+
var EDITORS, OPEN_TOOLS;
|
|
7842
|
+
var init_open = __esm({
|
|
7843
|
+
"src/open/index.ts"() {
|
|
7844
|
+
"use strict";
|
|
7845
|
+
init_paths();
|
|
7846
|
+
init_locate_running();
|
|
7847
|
+
init_shell();
|
|
7848
|
+
init_ssh_attach();
|
|
7849
|
+
EDITORS = {
|
|
7850
|
+
code: {
|
|
7851
|
+
label: "VS Code",
|
|
7852
|
+
bin: "code",
|
|
7853
|
+
macAppBin: "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
7854
|
+
setupHint: `install the Remote-SSH extension and run "Shell Command: Install 'code' command in PATH"`
|
|
7855
|
+
},
|
|
7856
|
+
codium: {
|
|
7857
|
+
label: "VS Codium",
|
|
7858
|
+
bin: "codium",
|
|
7859
|
+
macAppBin: "/Applications/VSCodium.app/Contents/Resources/app/bin/codium",
|
|
7860
|
+
setupHint: 'install the "Open Remote - SSH" extension (the codium CLI ships with the app)'
|
|
7861
|
+
}
|
|
7862
|
+
};
|
|
7863
|
+
OPEN_TOOLS = [...Object.keys(EDITORS), "shell"];
|
|
7864
|
+
}
|
|
7865
|
+
});
|
|
7866
|
+
|
|
7867
|
+
// src/version.ts
|
|
7868
|
+
var CLI_VERSION;
|
|
7869
|
+
var init_version = __esm({
|
|
7870
|
+
"src/version.ts"() {
|
|
7871
|
+
"use strict";
|
|
7872
|
+
CLI_VERSION = true ? "1.27.0" : "dev";
|
|
7873
|
+
}
|
|
7874
|
+
});
|
|
7875
|
+
|
|
7876
|
+
// src/commands/_dispatch.ts
|
|
7877
|
+
import { consola as consola14 } from "consola";
|
|
7878
|
+
async function dispatch(runner) {
|
|
7879
|
+
try {
|
|
7880
|
+
const exitCode = await runner();
|
|
7881
|
+
process.exit(exitCode);
|
|
7882
|
+
} catch (err) {
|
|
7883
|
+
consola14.error(err instanceof Error ? err.message : String(err));
|
|
7884
|
+
process.exit(1);
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
var init_dispatch = __esm({
|
|
7888
|
+
"src/commands/_dispatch.ts"() {
|
|
7889
|
+
"use strict";
|
|
7890
|
+
}
|
|
7891
|
+
});
|
|
7892
|
+
|
|
7893
|
+
// src/commands/apply.ts
|
|
7894
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
7895
|
+
import { consola as consola15 } from "consola";
|
|
7896
|
+
var applyCommand;
|
|
7897
|
+
var init_apply2 = __esm({
|
|
7898
|
+
"src/commands/apply.ts"() {
|
|
7899
|
+
"use strict";
|
|
7900
|
+
init_apply();
|
|
7901
|
+
init_open();
|
|
7902
|
+
init_version();
|
|
7903
|
+
init_dispatch();
|
|
7904
|
+
applyCommand = defineCommand8({
|
|
7905
|
+
meta: {
|
|
7906
|
+
name: "apply",
|
|
7907
|
+
group: "lifecycle",
|
|
7908
|
+
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."
|
|
7909
|
+
},
|
|
7910
|
+
args: {
|
|
7911
|
+
name: {
|
|
7912
|
+
type: "positional",
|
|
7913
|
+
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
7914
|
+
required: true
|
|
7915
|
+
},
|
|
7916
|
+
verbose: {
|
|
7917
|
+
type: "boolean",
|
|
7918
|
+
description: "Stream the raw @devcontainers/cli output to stderr instead of showing a phase spinner. Auto-enabled when stderr is not a TTY.",
|
|
7919
|
+
default: false
|
|
7920
|
+
},
|
|
7921
|
+
open: {
|
|
7922
|
+
type: "string",
|
|
7923
|
+
description: `After a successful apply, open the container in this tool (${OPEN_TOOLS.join("|")}).`
|
|
7924
|
+
}
|
|
7925
|
+
},
|
|
7926
|
+
run({ args }) {
|
|
7927
|
+
return dispatch(async () => {
|
|
7928
|
+
const result = await runApply({
|
|
7929
|
+
name: args.name,
|
|
7930
|
+
cliVersion: CLI_VERSION,
|
|
7931
|
+
verbose: args.verbose
|
|
7932
|
+
});
|
|
7933
|
+
if (args.open && result.containerExitCode === 0) {
|
|
7934
|
+
try {
|
|
7935
|
+
await runOpen({ name: args.name, tool: args.open });
|
|
7936
|
+
} catch (err) {
|
|
7937
|
+
consola15.warn(err instanceof Error ? err.message : String(err));
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7940
|
+
return result.containerExitCode;
|
|
7941
|
+
});
|
|
7942
|
+
}
|
|
7943
|
+
});
|
|
7944
|
+
}
|
|
7945
|
+
});
|
|
7946
|
+
|
|
7947
|
+
// src/commands/completion.ts
|
|
7948
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
7949
|
+
function renderCompletionScript(shell) {
|
|
7950
|
+
if (shell === "bash") {
|
|
7951
|
+
return [
|
|
7952
|
+
"# bash completion for monoceros",
|
|
7953
|
+
"# install: source this file from .bashrc, e.g.",
|
|
7954
|
+
"# monoceros completion bash > ~/.bash_completion.d/monoceros",
|
|
7955
|
+
'# echo "source ~/.bash_completion.d/monoceros" >> ~/.bashrc',
|
|
7956
|
+
"#",
|
|
7957
|
+
"# The work is done by `monoceros __complete --line --point`; this",
|
|
7958
|
+
"# shell wrapper only forwards the cursor view.",
|
|
7959
|
+
"",
|
|
7960
|
+
"_monoceros() {",
|
|
7419
7961
|
" local IFS=$'\\n'",
|
|
7420
7962
|
" local candidates",
|
|
7421
7963
|
' candidates=$(monoceros __complete --line "$COMP_LINE" --point "$COMP_POINT" 2>/dev/null)',
|
|
@@ -7530,8 +8072,8 @@ var init_completion = __esm({
|
|
|
7530
8072
|
});
|
|
7531
8073
|
|
|
7532
8074
|
// src/completion/resolve.ts
|
|
7533
|
-
import { existsSync as
|
|
7534
|
-
import
|
|
8075
|
+
import { existsSync as existsSync14, promises as fs14 } from "fs";
|
|
8076
|
+
import path23 from "path";
|
|
7535
8077
|
async function resolveCompletions(line, point, opts = {}) {
|
|
7536
8078
|
const { prev, current } = parseCompletionLine(line, point);
|
|
7537
8079
|
const ctx = { prev, current, opts };
|
|
@@ -7680,9 +8222,9 @@ function filterPrefix(values, fragment) {
|
|
|
7680
8222
|
}
|
|
7681
8223
|
async function listContainerNames(ctx) {
|
|
7682
8224
|
const home = ctx.opts.monocerosHome ?? monocerosHome();
|
|
7683
|
-
const dir =
|
|
7684
|
-
if (!
|
|
7685
|
-
const entries = await
|
|
8225
|
+
const dir = path23.join(home, "container-configs");
|
|
8226
|
+
if (!existsSync14(dir)) return [];
|
|
8227
|
+
const entries = await fs14.readdir(dir);
|
|
7686
8228
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length)).sort();
|
|
7687
8229
|
}
|
|
7688
8230
|
async function listFeatureComponents() {
|
|
@@ -7756,10 +8298,12 @@ var init_resolve = __esm({
|
|
|
7756
8298
|
init_manifest();
|
|
7757
8299
|
init_catalog();
|
|
7758
8300
|
init_schema();
|
|
8301
|
+
init_open();
|
|
7759
8302
|
ALL_COMMANDS = [
|
|
7760
8303
|
"init",
|
|
7761
8304
|
"list-components",
|
|
7762
8305
|
"shell",
|
|
8306
|
+
"open",
|
|
7763
8307
|
"run",
|
|
7764
8308
|
"logs",
|
|
7765
8309
|
"start",
|
|
@@ -7809,7 +8353,10 @@ var init_resolve = __esm({
|
|
|
7809
8353
|
},
|
|
7810
8354
|
apply: {
|
|
7811
8355
|
positionals: [containerName],
|
|
7812
|
-
flags: {
|
|
8356
|
+
flags: {
|
|
8357
|
+
"--yes": { type: "boolean", aliases: ["-y"] },
|
|
8358
|
+
"--open": { type: "value", values: () => [...OPEN_TOOLS] }
|
|
8359
|
+
}
|
|
7813
8360
|
},
|
|
7814
8361
|
upgrade: {
|
|
7815
8362
|
// First positional is a container name; the second is a version
|
|
@@ -7825,12 +8372,16 @@ var init_resolve = __esm({
|
|
|
7825
8372
|
}
|
|
7826
8373
|
},
|
|
7827
8374
|
shell: { positionals: [containerName] },
|
|
8375
|
+
open: { positionals: [containerName, () => [...OPEN_TOOLS]] },
|
|
7828
8376
|
run: {
|
|
7829
8377
|
positionals: [containerName],
|
|
7830
8378
|
flags: { "--in": { type: "value" } }
|
|
7831
8379
|
},
|
|
7832
8380
|
logs: { positionals: [containerName] },
|
|
7833
|
-
start: {
|
|
8381
|
+
start: {
|
|
8382
|
+
positionals: [containerName],
|
|
8383
|
+
flags: { "--open": { type: "value", values: () => [...OPEN_TOOLS] } }
|
|
8384
|
+
},
|
|
7834
8385
|
stop: { positionals: [containerName] },
|
|
7835
8386
|
status: { positionals: [containerName] },
|
|
7836
8387
|
"add-language": {
|
|
@@ -8349,14 +8900,14 @@ var init_generator = __esm({
|
|
|
8349
8900
|
});
|
|
8350
8901
|
|
|
8351
8902
|
// src/init/index.ts
|
|
8352
|
-
import { existsSync as
|
|
8353
|
-
import
|
|
8354
|
-
import { consola as
|
|
8903
|
+
import { existsSync as existsSync15, promises as fs15 } from "fs";
|
|
8904
|
+
import path24 from "path";
|
|
8905
|
+
import { consola as consola16 } from "consola";
|
|
8355
8906
|
async function runInit(opts) {
|
|
8356
8907
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
8357
8908
|
const logger = opts.logger ?? {
|
|
8358
|
-
success: (msg) =>
|
|
8359
|
-
info: (msg) =>
|
|
8909
|
+
success: (msg) => consola16.success(msg),
|
|
8910
|
+
info: (msg) => consola16.info(msg)
|
|
8360
8911
|
};
|
|
8361
8912
|
if (!REGEX.solutionName.test(opts.name)) {
|
|
8362
8913
|
throw new Error(
|
|
@@ -8364,12 +8915,12 @@ async function runInit(opts) {
|
|
|
8364
8915
|
);
|
|
8365
8916
|
}
|
|
8366
8917
|
const dest = containerConfigPath(opts.name, home);
|
|
8367
|
-
if (
|
|
8918
|
+
if (existsSync15(dest)) {
|
|
8368
8919
|
throw new Error(
|
|
8369
8920
|
`Config already exists: ${dest}. Delete it manually before re-running \`monoceros init\` \u2014 this protects any hand-edits.`
|
|
8370
8921
|
);
|
|
8371
8922
|
}
|
|
8372
|
-
const componentsRoot = opts.workbenchRoot ?
|
|
8923
|
+
const componentsRoot = opts.workbenchRoot ? path24.join(opts.workbenchRoot, "components") : componentsRootDir();
|
|
8373
8924
|
const catalog = await loadComponentCatalog(componentsRoot);
|
|
8374
8925
|
if (catalog.size === 0) {
|
|
8375
8926
|
throw new Error(
|
|
@@ -8435,9 +8986,9 @@ async function runInit(opts) {
|
|
|
8435
8986
|
} else {
|
|
8436
8987
|
text = generateComposedYml(opts.name, composed, lookup, repos, ports);
|
|
8437
8988
|
}
|
|
8438
|
-
await
|
|
8989
|
+
await fs15.mkdir(containerConfigsDir(home), { recursive: true });
|
|
8439
8990
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
8440
|
-
await
|
|
8991
|
+
await fs15.writeFile(dest, text, "utf8");
|
|
8441
8992
|
const envPath = containerEnvPath(opts.name, home);
|
|
8442
8993
|
const seedVars = {};
|
|
8443
8994
|
for (const f of composed.features) {
|
|
@@ -8460,8 +9011,8 @@ async function runInit(opts) {
|
|
|
8460
9011
|
}
|
|
8461
9012
|
await ensureEnvVars(envPath, opts.name, seedVars);
|
|
8462
9013
|
const documented = !anyComposed;
|
|
8463
|
-
const ymlRel =
|
|
8464
|
-
const envRel =
|
|
9014
|
+
const ymlRel = path24.relative(home, dest);
|
|
9015
|
+
const envRel = path24.relative(home, envPath);
|
|
8465
9016
|
if (documented) {
|
|
8466
9017
|
logger.success(`Wrote documented default to ${ymlRel} and ${envRel}.`);
|
|
8467
9018
|
logger.info(
|
|
@@ -8605,7 +9156,7 @@ var init_init = __esm({
|
|
|
8605
9156
|
|
|
8606
9157
|
// src/commands/init.ts
|
|
8607
9158
|
import { defineCommand as defineCommand11 } from "citty";
|
|
8608
|
-
import { consola as
|
|
9159
|
+
import { consola as consola17 } from "consola";
|
|
8609
9160
|
function collectListFlag(flag, rawArgs) {
|
|
8610
9161
|
const eq = `${flag}=`;
|
|
8611
9162
|
const pieces = [];
|
|
@@ -8731,7 +9282,7 @@ var init_init2 = __esm({
|
|
|
8731
9282
|
...ports && ports.length > 0 ? { withPorts: ports } : {}
|
|
8732
9283
|
});
|
|
8733
9284
|
} catch (err) {
|
|
8734
|
-
|
|
9285
|
+
consola17.error(err instanceof Error ? err.message : String(err));
|
|
8735
9286
|
process.exit(1);
|
|
8736
9287
|
}
|
|
8737
9288
|
}
|
|
@@ -8780,7 +9331,7 @@ var init_expand = __esm({
|
|
|
8780
9331
|
|
|
8781
9332
|
// src/commands/list-components.ts
|
|
8782
9333
|
import { defineCommand as defineCommand12 } from "citty";
|
|
8783
|
-
import { consola as
|
|
9334
|
+
import { consola as consola18 } from "consola";
|
|
8784
9335
|
var CATEGORY_LABELS, CATEGORY_ORDER, listComponentsCommand;
|
|
8785
9336
|
var init_list_components = __esm({
|
|
8786
9337
|
"src/commands/list-components.ts"() {
|
|
@@ -8809,7 +9360,7 @@ var init_list_components = __esm({
|
|
|
8809
9360
|
try {
|
|
8810
9361
|
const catalog = expandSelectable(await loadDescriptorCatalog());
|
|
8811
9362
|
if (catalog.size === 0) {
|
|
8812
|
-
|
|
9363
|
+
consola18.warn(
|
|
8813
9364
|
"No components found. The workbench checkout looks incomplete."
|
|
8814
9365
|
);
|
|
8815
9366
|
process.exit(0);
|
|
@@ -8860,7 +9411,7 @@ var init_list_components = __esm({
|
|
|
8860
9411
|
}
|
|
8861
9412
|
process.exit(0);
|
|
8862
9413
|
} catch (err) {
|
|
8863
|
-
|
|
9414
|
+
consola18.error(err instanceof Error ? err.message : String(err));
|
|
8864
9415
|
process.exit(1);
|
|
8865
9416
|
}
|
|
8866
9417
|
}
|
|
@@ -8869,14 +9420,14 @@ var init_list_components = __esm({
|
|
|
8869
9420
|
});
|
|
8870
9421
|
|
|
8871
9422
|
// src/commands/logs.ts
|
|
8872
|
-
import { spawn as
|
|
8873
|
-
import { existsSync as
|
|
8874
|
-
import
|
|
9423
|
+
import { spawn as spawn11 } from "child_process";
|
|
9424
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9425
|
+
import path25 from "path";
|
|
8875
9426
|
import { defineCommand as defineCommand13 } from "citty";
|
|
8876
9427
|
function tailLogFile(file, follow) {
|
|
8877
9428
|
const [cmd, args] = follow ? ["tail", ["-F", file]] : ["cat", [file]];
|
|
8878
9429
|
return new Promise((resolve, reject) => {
|
|
8879
|
-
const child =
|
|
9430
|
+
const child = spawn11(cmd, args, { stdio: "inherit" });
|
|
8880
9431
|
child.on("error", reject);
|
|
8881
9432
|
child.on("exit", (code) => resolve(code ?? 0));
|
|
8882
9433
|
});
|
|
@@ -8914,8 +9465,8 @@ var init_logs = __esm({
|
|
|
8914
9465
|
run({ args }) {
|
|
8915
9466
|
const service = typeof args.service === "string" ? args.service : void 0;
|
|
8916
9467
|
if (service) {
|
|
8917
|
-
const logFile =
|
|
8918
|
-
if (
|
|
9468
|
+
const logFile = path25.join(containerLogsDir(args.name), `${service}.log`);
|
|
9469
|
+
if (existsSync16(logFile)) {
|
|
8919
9470
|
return dispatch(() => tailLogFile(logFile, args.follow));
|
|
8920
9471
|
}
|
|
8921
9472
|
}
|
|
@@ -8931,12 +9482,45 @@ var init_logs = __esm({
|
|
|
8931
9482
|
}
|
|
8932
9483
|
});
|
|
8933
9484
|
|
|
8934
|
-
// src/commands/
|
|
9485
|
+
// src/commands/open.ts
|
|
8935
9486
|
import { defineCommand as defineCommand14 } from "citty";
|
|
8936
|
-
|
|
9487
|
+
var openCommand;
|
|
9488
|
+
var init_open2 = __esm({
|
|
9489
|
+
"src/commands/open.ts"() {
|
|
9490
|
+
"use strict";
|
|
9491
|
+
init_open();
|
|
9492
|
+
init_dispatch();
|
|
9493
|
+
openCommand = defineCommand14({
|
|
9494
|
+
meta: {
|
|
9495
|
+
name: "open",
|
|
9496
|
+
group: "run",
|
|
9497
|
+
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.`
|
|
9498
|
+
},
|
|
9499
|
+
args: {
|
|
9500
|
+
name: {
|
|
9501
|
+
type: "positional",
|
|
9502
|
+
description: "Container name (yml in $MONOCEROS_HOME/container-configs/).",
|
|
9503
|
+
required: true
|
|
9504
|
+
},
|
|
9505
|
+
tool: {
|
|
9506
|
+
type: "positional",
|
|
9507
|
+
description: `What to open it in: ${OPEN_TOOLS.join(", ")}.`,
|
|
9508
|
+
required: true
|
|
9509
|
+
}
|
|
9510
|
+
},
|
|
9511
|
+
run({ args }) {
|
|
9512
|
+
return dispatch(() => runOpen({ name: args.name, tool: args.tool }));
|
|
9513
|
+
}
|
|
9514
|
+
});
|
|
9515
|
+
}
|
|
9516
|
+
});
|
|
9517
|
+
|
|
9518
|
+
// src/commands/port.ts
|
|
9519
|
+
import { defineCommand as defineCommand15 } from "citty";
|
|
9520
|
+
import { consola as consola19 } from "consola";
|
|
8937
9521
|
async function runPortListing(opts) {
|
|
8938
9522
|
const out = opts.out ?? process.stdout;
|
|
8939
|
-
const info = opts.info ?? ((m) =>
|
|
9523
|
+
const info = opts.info ?? ((m) => consola19.info(m));
|
|
8940
9524
|
const parsed = await readConfig(
|
|
8941
9525
|
containerConfigPath(opts.name, opts.monocerosHome)
|
|
8942
9526
|
);
|
|
@@ -8994,7 +9578,7 @@ var init_port = __esm({
|
|
|
8994
9578
|
init_schema();
|
|
8995
9579
|
init_dynamic();
|
|
8996
9580
|
init_format();
|
|
8997
|
-
portCommand =
|
|
9581
|
+
portCommand = defineCommand15({
|
|
8998
9582
|
meta: {
|
|
8999
9583
|
name: "port",
|
|
9000
9584
|
group: "discovery",
|
|
@@ -9012,7 +9596,7 @@ var init_port = __esm({
|
|
|
9012
9596
|
const code = await runPortListing({ name: args.name });
|
|
9013
9597
|
process.exit(code);
|
|
9014
9598
|
} catch (err) {
|
|
9015
|
-
|
|
9599
|
+
consola19.error(err instanceof Error ? err.message : String(err));
|
|
9016
9600
|
process.exit(1);
|
|
9017
9601
|
}
|
|
9018
9602
|
}
|
|
@@ -9021,15 +9605,15 @@ var init_port = __esm({
|
|
|
9021
9605
|
});
|
|
9022
9606
|
|
|
9023
9607
|
// src/commands/remove-apt-packages.ts
|
|
9024
|
-
import { defineCommand as
|
|
9025
|
-
import { consola as
|
|
9608
|
+
import { defineCommand as defineCommand16 } from "citty";
|
|
9609
|
+
import { consola as consola20 } from "consola";
|
|
9026
9610
|
var removeAptPackagesCommand;
|
|
9027
9611
|
var init_remove_apt_packages = __esm({
|
|
9028
9612
|
"src/commands/remove-apt-packages.ts"() {
|
|
9029
9613
|
"use strict";
|
|
9030
9614
|
init_inner_args();
|
|
9031
9615
|
init_modify();
|
|
9032
|
-
removeAptPackagesCommand =
|
|
9616
|
+
removeAptPackagesCommand = defineCommand16({
|
|
9033
9617
|
meta: {
|
|
9034
9618
|
name: "remove-apt-packages",
|
|
9035
9619
|
group: "edit",
|
|
@@ -9051,7 +9635,7 @@ var init_remove_apt_packages = __esm({
|
|
|
9051
9635
|
async run({ args }) {
|
|
9052
9636
|
const packages = [...getInnerArgs()];
|
|
9053
9637
|
if (packages.length === 0) {
|
|
9054
|
-
|
|
9638
|
+
consola20.error(
|
|
9055
9639
|
"No package names given. Usage: `monoceros remove-apt-packages <containername> [--yes] -- <pkg> [<pkg> \u2026]`."
|
|
9056
9640
|
);
|
|
9057
9641
|
process.exit(1);
|
|
@@ -9064,7 +9648,7 @@ var init_remove_apt_packages = __esm({
|
|
|
9064
9648
|
});
|
|
9065
9649
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9066
9650
|
} catch (err) {
|
|
9067
|
-
|
|
9651
|
+
consola20.error(err instanceof Error ? err.message : String(err));
|
|
9068
9652
|
process.exit(1);
|
|
9069
9653
|
}
|
|
9070
9654
|
}
|
|
@@ -9073,14 +9657,14 @@ var init_remove_apt_packages = __esm({
|
|
|
9073
9657
|
});
|
|
9074
9658
|
|
|
9075
9659
|
// src/commands/remove-feature.ts
|
|
9076
|
-
import { defineCommand as
|
|
9077
|
-
import { consola as
|
|
9660
|
+
import { defineCommand as defineCommand17 } from "citty";
|
|
9661
|
+
import { consola as consola21 } from "consola";
|
|
9078
9662
|
var removeFeatureCommand;
|
|
9079
9663
|
var init_remove_feature = __esm({
|
|
9080
9664
|
"src/commands/remove-feature.ts"() {
|
|
9081
9665
|
"use strict";
|
|
9082
9666
|
init_modify();
|
|
9083
|
-
removeFeatureCommand =
|
|
9667
|
+
removeFeatureCommand = defineCommand17({
|
|
9084
9668
|
meta: {
|
|
9085
9669
|
name: "remove-feature",
|
|
9086
9670
|
group: "edit",
|
|
@@ -9113,7 +9697,7 @@ var init_remove_feature = __esm({
|
|
|
9113
9697
|
});
|
|
9114
9698
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9115
9699
|
} catch (err) {
|
|
9116
|
-
|
|
9700
|
+
consola21.error(err instanceof Error ? err.message : String(err));
|
|
9117
9701
|
process.exit(1);
|
|
9118
9702
|
}
|
|
9119
9703
|
}
|
|
@@ -9122,15 +9706,15 @@ var init_remove_feature = __esm({
|
|
|
9122
9706
|
});
|
|
9123
9707
|
|
|
9124
9708
|
// src/remove/index.ts
|
|
9125
|
-
import { existsSync as
|
|
9126
|
-
import
|
|
9127
|
-
import { consola as
|
|
9709
|
+
import { existsSync as existsSync17, promises as fs16 } from "fs";
|
|
9710
|
+
import path26 from "path";
|
|
9711
|
+
import { consola as consola22 } from "consola";
|
|
9128
9712
|
async function runRemove(opts) {
|
|
9129
9713
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
9130
9714
|
const logger = opts.logger ?? {
|
|
9131
|
-
info: (msg) =>
|
|
9132
|
-
success: (msg) =>
|
|
9133
|
-
warn: (msg) =>
|
|
9715
|
+
info: (msg) => consola22.info(msg),
|
|
9716
|
+
success: (msg) => consola22.success(msg),
|
|
9717
|
+
warn: (msg) => consola22.warn(msg)
|
|
9134
9718
|
};
|
|
9135
9719
|
if (!REGEX.solutionName.test(opts.name)) {
|
|
9136
9720
|
throw new Error(
|
|
@@ -9140,9 +9724,9 @@ async function runRemove(opts) {
|
|
|
9140
9724
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
9141
9725
|
const envPath = containerEnvPath(opts.name, home);
|
|
9142
9726
|
const containerPath = containerDir(opts.name, home);
|
|
9143
|
-
const hasYml =
|
|
9144
|
-
const hasEnv =
|
|
9145
|
-
const hasContainer =
|
|
9727
|
+
const hasYml = existsSync17(ymlPath);
|
|
9728
|
+
const hasEnv = existsSync17(envPath);
|
|
9729
|
+
const hasContainer = existsSync17(containerPath);
|
|
9146
9730
|
if (!hasYml && !hasContainer) {
|
|
9147
9731
|
throw new Error(
|
|
9148
9732
|
`Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
|
|
@@ -9168,30 +9752,30 @@ async function runRemove(opts) {
|
|
|
9168
9752
|
let backupPath = null;
|
|
9169
9753
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
9170
9754
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
9171
|
-
backupPath =
|
|
9172
|
-
await
|
|
9755
|
+
backupPath = path26.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
9756
|
+
await fs16.mkdir(backupPath, { recursive: true });
|
|
9173
9757
|
if (hasYml) {
|
|
9174
|
-
await
|
|
9758
|
+
await fs16.copyFile(ymlPath, path26.join(backupPath, `${opts.name}.yml`));
|
|
9175
9759
|
}
|
|
9176
9760
|
if (hasEnv) {
|
|
9177
|
-
await
|
|
9761
|
+
await fs16.copyFile(envPath, path26.join(backupPath, `${opts.name}.env`));
|
|
9178
9762
|
}
|
|
9179
9763
|
if (hasContainer) {
|
|
9180
|
-
await
|
|
9764
|
+
await fs16.cp(containerPath, path26.join(backupPath, "container"), {
|
|
9181
9765
|
recursive: true
|
|
9182
9766
|
});
|
|
9183
9767
|
}
|
|
9184
9768
|
logger.info(`Backup written to ${prettyPath(backupPath)}.`);
|
|
9185
9769
|
}
|
|
9186
9770
|
if (hasYml) {
|
|
9187
|
-
await
|
|
9771
|
+
await fs16.rm(ymlPath, { force: true });
|
|
9188
9772
|
}
|
|
9189
9773
|
if (hasEnv) {
|
|
9190
|
-
await
|
|
9774
|
+
await fs16.rm(envPath, { force: true });
|
|
9191
9775
|
}
|
|
9192
9776
|
if (hasContainer) {
|
|
9193
9777
|
try {
|
|
9194
|
-
await
|
|
9778
|
+
await fs16.rm(containerPath, { recursive: true, force: true });
|
|
9195
9779
|
} catch (err) {
|
|
9196
9780
|
const code = err.code;
|
|
9197
9781
|
if (code !== "EACCES" && code !== "EPERM") {
|
|
@@ -9213,7 +9797,7 @@ async function runRemove(opts) {
|
|
|
9213
9797
|
"-delete"
|
|
9214
9798
|
]);
|
|
9215
9799
|
try {
|
|
9216
|
-
await
|
|
9800
|
+
await fs16.rm(containerPath, { recursive: true, force: true });
|
|
9217
9801
|
} catch (err2) {
|
|
9218
9802
|
const code2 = err2.code;
|
|
9219
9803
|
throw new Error(
|
|
@@ -9227,7 +9811,14 @@ async function runRemove(opts) {
|
|
|
9227
9811
|
);
|
|
9228
9812
|
if (!backupPath) {
|
|
9229
9813
|
logger.warn?.(
|
|
9230
|
-
"No backup created (--no-backup). The host-side state is gone for good."
|
|
9814
|
+
"No backup created (--no-backup). The host-side state is gone for good."
|
|
9815
|
+
);
|
|
9816
|
+
}
|
|
9817
|
+
try {
|
|
9818
|
+
await removeSshAttach(home, opts.name);
|
|
9819
|
+
} catch (err) {
|
|
9820
|
+
logger.warn?.(
|
|
9821
|
+
`Could not remove SSH attach config for ${opts.name}: ${err instanceof Error ? err.message : String(err)}. Ignored.`
|
|
9231
9822
|
);
|
|
9232
9823
|
}
|
|
9233
9824
|
try {
|
|
@@ -9262,21 +9853,22 @@ var init_remove = __esm({
|
|
|
9262
9853
|
init_schema();
|
|
9263
9854
|
init_compose();
|
|
9264
9855
|
init_scaffold();
|
|
9856
|
+
init_ssh_attach();
|
|
9265
9857
|
init_proxy();
|
|
9266
9858
|
init_dynamic();
|
|
9267
9859
|
}
|
|
9268
9860
|
});
|
|
9269
9861
|
|
|
9270
9862
|
// src/commands/remove.ts
|
|
9271
|
-
import { defineCommand as
|
|
9272
|
-
import { consola as
|
|
9863
|
+
import { defineCommand as defineCommand18 } from "citty";
|
|
9864
|
+
import { consola as consola23 } from "consola";
|
|
9273
9865
|
import { createInterface } from "readline/promises";
|
|
9274
9866
|
var removeCommand;
|
|
9275
9867
|
var init_remove2 = __esm({
|
|
9276
9868
|
"src/commands/remove.ts"() {
|
|
9277
9869
|
"use strict";
|
|
9278
9870
|
init_remove();
|
|
9279
|
-
removeCommand =
|
|
9871
|
+
removeCommand = defineCommand18({
|
|
9280
9872
|
meta: {
|
|
9281
9873
|
name: "remove",
|
|
9282
9874
|
group: "lifecycle",
|
|
@@ -9313,7 +9905,7 @@ var init_remove2 = __esm({
|
|
|
9313
9905
|
const skipPrompt = args.yes === true;
|
|
9314
9906
|
if (!skipPrompt) {
|
|
9315
9907
|
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.`;
|
|
9316
|
-
|
|
9908
|
+
consola23.warn(warning);
|
|
9317
9909
|
const rl = createInterface({
|
|
9318
9910
|
input: process.stdin,
|
|
9319
9911
|
output: process.stdout
|
|
@@ -9321,7 +9913,7 @@ var init_remove2 = __esm({
|
|
|
9321
9913
|
const answer = await rl.question("Continue? [y/N] ");
|
|
9322
9914
|
rl.close();
|
|
9323
9915
|
if (!/^y(es)?$/i.test(answer.trim())) {
|
|
9324
|
-
|
|
9916
|
+
consola23.info("Aborted. Nothing changed.");
|
|
9325
9917
|
process.exit(0);
|
|
9326
9918
|
}
|
|
9327
9919
|
}
|
|
@@ -9330,7 +9922,7 @@ var init_remove2 = __esm({
|
|
|
9330
9922
|
...noBackup ? { noBackup: true } : {}
|
|
9331
9923
|
});
|
|
9332
9924
|
} catch (err) {
|
|
9333
|
-
|
|
9925
|
+
consola23.error(err instanceof Error ? err.message : String(err));
|
|
9334
9926
|
process.exit(1);
|
|
9335
9927
|
}
|
|
9336
9928
|
}
|
|
@@ -9339,24 +9931,24 @@ var init_remove2 = __esm({
|
|
|
9339
9931
|
});
|
|
9340
9932
|
|
|
9341
9933
|
// src/restore/index.ts
|
|
9342
|
-
import { existsSync as
|
|
9343
|
-
import
|
|
9344
|
-
import { consola as
|
|
9934
|
+
import { existsSync as existsSync18, promises as fs17 } from "fs";
|
|
9935
|
+
import path27 from "path";
|
|
9936
|
+
import { consola as consola24 } from "consola";
|
|
9345
9937
|
async function runRestore(opts) {
|
|
9346
9938
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
9347
9939
|
const logger = opts.logger ?? {
|
|
9348
|
-
info: (msg) =>
|
|
9349
|
-
success: (msg) =>
|
|
9940
|
+
info: (msg) => consola24.info(msg),
|
|
9941
|
+
success: (msg) => consola24.success(msg)
|
|
9350
9942
|
};
|
|
9351
|
-
const backup =
|
|
9352
|
-
if (!
|
|
9943
|
+
const backup = path27.resolve(opts.backupPath);
|
|
9944
|
+
if (!existsSync18(backup)) {
|
|
9353
9945
|
throw new Error(`Backup not found: ${backup}.`);
|
|
9354
9946
|
}
|
|
9355
|
-
const stat = await
|
|
9947
|
+
const stat = await fs17.stat(backup);
|
|
9356
9948
|
if (!stat.isDirectory()) {
|
|
9357
9949
|
throw new Error(`Backup path is not a directory: ${backup}.`);
|
|
9358
9950
|
}
|
|
9359
|
-
const entries = await
|
|
9951
|
+
const entries = await fs17.readdir(backup);
|
|
9360
9952
|
const ymlFiles = entries.filter((f) => f.endsWith(".yml"));
|
|
9361
9953
|
if (ymlFiles.length === 0) {
|
|
9362
9954
|
throw new Error(
|
|
@@ -9370,29 +9962,29 @@ async function runRestore(opts) {
|
|
|
9370
9962
|
}
|
|
9371
9963
|
const ymlFile = ymlFiles[0];
|
|
9372
9964
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
9373
|
-
const containerInBackup =
|
|
9374
|
-
const hasContainer =
|
|
9375
|
-
const envInBackup =
|
|
9376
|
-
const hasEnv =
|
|
9965
|
+
const containerInBackup = path27.join(backup, "container");
|
|
9966
|
+
const hasContainer = existsSync18(containerInBackup);
|
|
9967
|
+
const envInBackup = path27.join(backup, `${name}.env`);
|
|
9968
|
+
const hasEnv = existsSync18(envInBackup);
|
|
9377
9969
|
const destYml = containerConfigPath(name, home);
|
|
9378
9970
|
const destContainer = containerDir(name, home);
|
|
9379
|
-
if (
|
|
9971
|
+
if (existsSync18(destYml)) {
|
|
9380
9972
|
throw new Error(
|
|
9381
9973
|
`Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
|
|
9382
9974
|
);
|
|
9383
9975
|
}
|
|
9384
|
-
if (hasContainer &&
|
|
9976
|
+
if (hasContainer && existsSync18(destContainer)) {
|
|
9385
9977
|
throw new Error(
|
|
9386
9978
|
`Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
|
|
9387
9979
|
);
|
|
9388
9980
|
}
|
|
9389
|
-
await
|
|
9390
|
-
await
|
|
9981
|
+
await fs17.mkdir(containerConfigsDir(home), { recursive: true });
|
|
9982
|
+
await fs17.copyFile(path27.join(backup, ymlFile), destYml);
|
|
9391
9983
|
if (hasEnv) {
|
|
9392
|
-
await
|
|
9984
|
+
await fs17.copyFile(envInBackup, containerEnvPath(name, home));
|
|
9393
9985
|
}
|
|
9394
9986
|
if (hasContainer) {
|
|
9395
|
-
await
|
|
9987
|
+
await fs17.cp(containerInBackup, destContainer, { recursive: true });
|
|
9396
9988
|
}
|
|
9397
9989
|
logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);
|
|
9398
9990
|
logger.info(
|
|
@@ -9412,14 +10004,14 @@ var init_restore = __esm({
|
|
|
9412
10004
|
});
|
|
9413
10005
|
|
|
9414
10006
|
// src/commands/restore.ts
|
|
9415
|
-
import { defineCommand as
|
|
9416
|
-
import { consola as
|
|
10007
|
+
import { defineCommand as defineCommand19 } from "citty";
|
|
10008
|
+
import { consola as consola25 } from "consola";
|
|
9417
10009
|
var restoreCommand;
|
|
9418
10010
|
var init_restore2 = __esm({
|
|
9419
10011
|
"src/commands/restore.ts"() {
|
|
9420
10012
|
"use strict";
|
|
9421
10013
|
init_restore();
|
|
9422
|
-
restoreCommand =
|
|
10014
|
+
restoreCommand = defineCommand19({
|
|
9423
10015
|
meta: {
|
|
9424
10016
|
name: "restore",
|
|
9425
10017
|
group: "lifecycle",
|
|
@@ -9436,7 +10028,7 @@ var init_restore2 = __esm({
|
|
|
9436
10028
|
try {
|
|
9437
10029
|
await runRestore({ backupPath: args["backup-path"] });
|
|
9438
10030
|
} catch (err) {
|
|
9439
|
-
|
|
10031
|
+
consola25.error(err instanceof Error ? err.message : String(err));
|
|
9440
10032
|
process.exit(1);
|
|
9441
10033
|
}
|
|
9442
10034
|
}
|
|
@@ -9445,14 +10037,14 @@ var init_restore2 = __esm({
|
|
|
9445
10037
|
});
|
|
9446
10038
|
|
|
9447
10039
|
// src/commands/remove-from-url.ts
|
|
9448
|
-
import { defineCommand as
|
|
9449
|
-
import { consola as
|
|
10040
|
+
import { defineCommand as defineCommand20 } from "citty";
|
|
10041
|
+
import { consola as consola26 } from "consola";
|
|
9450
10042
|
var removeFromUrlCommand;
|
|
9451
10043
|
var init_remove_from_url = __esm({
|
|
9452
10044
|
"src/commands/remove-from-url.ts"() {
|
|
9453
10045
|
"use strict";
|
|
9454
10046
|
init_modify();
|
|
9455
|
-
removeFromUrlCommand =
|
|
10047
|
+
removeFromUrlCommand = defineCommand20({
|
|
9456
10048
|
meta: {
|
|
9457
10049
|
name: "remove-from-url",
|
|
9458
10050
|
group: "edit",
|
|
@@ -9485,7 +10077,7 @@ var init_remove_from_url = __esm({
|
|
|
9485
10077
|
});
|
|
9486
10078
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9487
10079
|
} catch (err) {
|
|
9488
|
-
|
|
10080
|
+
consola26.error(err instanceof Error ? err.message : String(err));
|
|
9489
10081
|
process.exit(1);
|
|
9490
10082
|
}
|
|
9491
10083
|
}
|
|
@@ -9494,14 +10086,14 @@ var init_remove_from_url = __esm({
|
|
|
9494
10086
|
});
|
|
9495
10087
|
|
|
9496
10088
|
// src/commands/remove-language.ts
|
|
9497
|
-
import { defineCommand as
|
|
9498
|
-
import { consola as
|
|
10089
|
+
import { defineCommand as defineCommand21 } from "citty";
|
|
10090
|
+
import { consola as consola27 } from "consola";
|
|
9499
10091
|
var removeLanguageCommand;
|
|
9500
10092
|
var init_remove_language = __esm({
|
|
9501
10093
|
"src/commands/remove-language.ts"() {
|
|
9502
10094
|
"use strict";
|
|
9503
10095
|
init_modify();
|
|
9504
|
-
removeLanguageCommand =
|
|
10096
|
+
removeLanguageCommand = defineCommand21({
|
|
9505
10097
|
meta: {
|
|
9506
10098
|
name: "remove-language",
|
|
9507
10099
|
group: "edit",
|
|
@@ -9534,7 +10126,7 @@ var init_remove_language = __esm({
|
|
|
9534
10126
|
});
|
|
9535
10127
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9536
10128
|
} catch (err) {
|
|
9537
|
-
|
|
10129
|
+
consola27.error(err instanceof Error ? err.message : String(err));
|
|
9538
10130
|
process.exit(1);
|
|
9539
10131
|
}
|
|
9540
10132
|
}
|
|
@@ -9543,8 +10135,8 @@ var init_remove_language = __esm({
|
|
|
9543
10135
|
});
|
|
9544
10136
|
|
|
9545
10137
|
// src/commands/remove-port.ts
|
|
9546
|
-
import { defineCommand as
|
|
9547
|
-
import { consola as
|
|
10138
|
+
import { defineCommand as defineCommand22 } from "citty";
|
|
10139
|
+
import { consola as consola28 } from "consola";
|
|
9548
10140
|
function coerceToken2(raw) {
|
|
9549
10141
|
const n = Number(raw);
|
|
9550
10142
|
return Number.isFinite(n) ? n : raw;
|
|
@@ -9555,7 +10147,7 @@ var init_remove_port = __esm({
|
|
|
9555
10147
|
"use strict";
|
|
9556
10148
|
init_inner_args();
|
|
9557
10149
|
init_modify();
|
|
9558
|
-
removePortCommand =
|
|
10150
|
+
removePortCommand = defineCommand22({
|
|
9559
10151
|
meta: {
|
|
9560
10152
|
name: "remove-port",
|
|
9561
10153
|
group: "edit",
|
|
@@ -9577,7 +10169,7 @@ var init_remove_port = __esm({
|
|
|
9577
10169
|
async run({ args }) {
|
|
9578
10170
|
const tokens = [...getInnerArgs()];
|
|
9579
10171
|
if (tokens.length === 0) {
|
|
9580
|
-
|
|
10172
|
+
consola28.error(
|
|
9581
10173
|
"No ports given. Usage: `monoceros remove-port <containername> [--yes] -- <port> [<port> \u2026]`."
|
|
9582
10174
|
);
|
|
9583
10175
|
process.exit(1);
|
|
@@ -9590,7 +10182,7 @@ var init_remove_port = __esm({
|
|
|
9590
10182
|
});
|
|
9591
10183
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9592
10184
|
} catch (err) {
|
|
9593
|
-
|
|
10185
|
+
consola28.error(err instanceof Error ? err.message : String(err));
|
|
9594
10186
|
process.exit(1);
|
|
9595
10187
|
}
|
|
9596
10188
|
}
|
|
@@ -9599,14 +10191,14 @@ var init_remove_port = __esm({
|
|
|
9599
10191
|
});
|
|
9600
10192
|
|
|
9601
10193
|
// src/commands/remove-repo.ts
|
|
9602
|
-
import { defineCommand as
|
|
9603
|
-
import { consola as
|
|
10194
|
+
import { defineCommand as defineCommand23 } from "citty";
|
|
10195
|
+
import { consola as consola29 } from "consola";
|
|
9604
10196
|
var removeRepoCommand;
|
|
9605
10197
|
var init_remove_repo = __esm({
|
|
9606
10198
|
"src/commands/remove-repo.ts"() {
|
|
9607
10199
|
"use strict";
|
|
9608
10200
|
init_modify();
|
|
9609
|
-
removeRepoCommand =
|
|
10201
|
+
removeRepoCommand = defineCommand23({
|
|
9610
10202
|
meta: {
|
|
9611
10203
|
name: "remove-repo",
|
|
9612
10204
|
group: "edit",
|
|
@@ -9639,7 +10231,7 @@ var init_remove_repo = __esm({
|
|
|
9639
10231
|
});
|
|
9640
10232
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9641
10233
|
} catch (err) {
|
|
9642
|
-
|
|
10234
|
+
consola29.error(err instanceof Error ? err.message : String(err));
|
|
9643
10235
|
process.exit(1);
|
|
9644
10236
|
}
|
|
9645
10237
|
}
|
|
@@ -9648,14 +10240,14 @@ var init_remove_repo = __esm({
|
|
|
9648
10240
|
});
|
|
9649
10241
|
|
|
9650
10242
|
// src/commands/remove-service.ts
|
|
9651
|
-
import { defineCommand as
|
|
9652
|
-
import { consola as
|
|
10243
|
+
import { defineCommand as defineCommand24 } from "citty";
|
|
10244
|
+
import { consola as consola30 } from "consola";
|
|
9653
10245
|
var removeServiceCommand;
|
|
9654
10246
|
var init_remove_service = __esm({
|
|
9655
10247
|
"src/commands/remove-service.ts"() {
|
|
9656
10248
|
"use strict";
|
|
9657
10249
|
init_modify();
|
|
9658
|
-
removeServiceCommand =
|
|
10250
|
+
removeServiceCommand = defineCommand24({
|
|
9659
10251
|
meta: {
|
|
9660
10252
|
name: "remove-service",
|
|
9661
10253
|
group: "edit",
|
|
@@ -9688,7 +10280,7 @@ var init_remove_service = __esm({
|
|
|
9688
10280
|
});
|
|
9689
10281
|
process.exit(result.status === "aborted" ? 1 : 0);
|
|
9690
10282
|
} catch (err) {
|
|
9691
|
-
|
|
10283
|
+
consola30.error(err instanceof Error ? err.message : String(err));
|
|
9692
10284
|
process.exit(1);
|
|
9693
10285
|
}
|
|
9694
10286
|
}
|
|
@@ -9696,148 +10288,17 @@ var init_remove_service = __esm({
|
|
|
9696
10288
|
}
|
|
9697
10289
|
});
|
|
9698
10290
|
|
|
9699
|
-
// src/devcontainer/browser-bridge.ts
|
|
9700
|
-
import { spawn as spawn9 } from "child_process";
|
|
9701
|
-
import { existsSync as existsSync15, promises as fsp5, readFileSync as readFileSync5 } from "fs";
|
|
9702
|
-
import http from "http";
|
|
9703
|
-
import path24 from "path";
|
|
9704
|
-
function parseCallbackTarget(authUrl) {
|
|
9705
|
-
try {
|
|
9706
|
-
const u = new URL(authUrl);
|
|
9707
|
-
const redirect = u.searchParams.get("redirect_uri");
|
|
9708
|
-
if (!redirect) return null;
|
|
9709
|
-
const r = new URL(redirect);
|
|
9710
|
-
if (r.hostname !== "localhost" && r.hostname !== "127.0.0.1") return null;
|
|
9711
|
-
const port = Number(r.port);
|
|
9712
|
-
if (!Number.isInteger(port) || port <= 0) return null;
|
|
9713
|
-
return { port, pathname: r.pathname };
|
|
9714
|
-
} catch {
|
|
9715
|
-
return null;
|
|
9716
|
-
}
|
|
9717
|
-
}
|
|
9718
|
-
function nextRelayUrl(content, lastOpened) {
|
|
9719
|
-
const url = content.trim();
|
|
9720
|
-
return url && url !== lastOpened ? url : null;
|
|
9721
|
-
}
|
|
9722
|
-
function wrapExec(command, opts) {
|
|
9723
|
-
const leading = [];
|
|
9724
|
-
const stmts = [];
|
|
9725
|
-
if (opts.pathPrepend) {
|
|
9726
|
-
leading.push(opts.pathPrepend);
|
|
9727
|
-
const i = leading.length;
|
|
9728
|
-
stmts.push(`export PATH="$${i}:$PATH"`);
|
|
9729
|
-
stmts.push(`export BROWSER="$${i}/xdg-open"`);
|
|
9730
|
-
}
|
|
9731
|
-
if (opts.cwd) {
|
|
9732
|
-
leading.push(opts.cwd);
|
|
9733
|
-
stmts.push(`cd -- "$${leading.length}"`);
|
|
9734
|
-
}
|
|
9735
|
-
if (leading.length === 0) return command;
|
|
9736
|
-
const shift = leading.length === 1 ? "shift" : `shift ${leading.length}`;
|
|
9737
|
-
const script = `${stmts.join(" && ")} && ${shift} && exec "$@"`;
|
|
9738
|
-
return ["bash", "-lc", script, "bash", ...leading, ...command];
|
|
9739
|
-
}
|
|
9740
|
-
function openInBrowser(url) {
|
|
9741
|
-
const platform = process.platform;
|
|
9742
|
-
const [cmd, args] = platform === "darwin" ? ["open", [url]] : platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
9743
|
-
try {
|
|
9744
|
-
const child = spawn9(cmd, args, {
|
|
9745
|
-
stdio: "ignore",
|
|
9746
|
-
detached: true
|
|
9747
|
-
});
|
|
9748
|
-
child.on("error", () => {
|
|
9749
|
-
});
|
|
9750
|
-
child.unref();
|
|
9751
|
-
} catch {
|
|
9752
|
-
}
|
|
9753
|
-
}
|
|
9754
|
-
async function startBrowserBridge(opts) {
|
|
9755
|
-
const relayDir = path24.join(opts.root, RELAY_DIRNAME);
|
|
9756
|
-
const relayScript = path24.join(relayDir, "xdg-open");
|
|
9757
|
-
const urlFile = path24.join(relayDir, "url");
|
|
9758
|
-
await fsp5.mkdir(relayDir, { recursive: true });
|
|
9759
|
-
await fsp5.rm(urlFile, { force: true });
|
|
9760
|
-
await fsp5.writeFile(
|
|
9761
|
-
relayScript,
|
|
9762
|
-
`#!/bin/sh
|
|
9763
|
-
printf '%s\\n' "$1" > "$(dirname "$0")/url"
|
|
9764
|
-
exit 0
|
|
9765
|
-
`,
|
|
9766
|
-
{ mode: 493 }
|
|
9767
|
-
);
|
|
9768
|
-
await fsp5.chmod(relayScript, 493);
|
|
9769
|
-
const servers = [];
|
|
9770
|
-
let lastOpened = null;
|
|
9771
|
-
const onUrl = (url) => {
|
|
9772
|
-
openInBrowser(url);
|
|
9773
|
-
const target = parseCallbackTarget(url);
|
|
9774
|
-
if (!target) return;
|
|
9775
|
-
const server = http.createServer((req, res) => {
|
|
9776
|
-
const reqUrl = req.url ?? target.pathname;
|
|
9777
|
-
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
9778
|
-
res.end(
|
|
9779
|
-
'<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>'
|
|
9780
|
-
);
|
|
9781
|
-
void opts.spawn(
|
|
9782
|
-
[
|
|
9783
|
-
"exec",
|
|
9784
|
-
"--workspace-folder",
|
|
9785
|
-
opts.root,
|
|
9786
|
-
"--mount-workspace-git-root=false",
|
|
9787
|
-
"curl",
|
|
9788
|
-
"-fsS",
|
|
9789
|
-
`http://localhost:${target.port}${reqUrl}`
|
|
9790
|
-
],
|
|
9791
|
-
opts.root,
|
|
9792
|
-
{ quiet: true }
|
|
9793
|
-
);
|
|
9794
|
-
});
|
|
9795
|
-
server.on("error", () => {
|
|
9796
|
-
});
|
|
9797
|
-
server.listen(target.port, "127.0.0.1");
|
|
9798
|
-
servers.push(server);
|
|
9799
|
-
};
|
|
9800
|
-
const poll = setInterval(() => {
|
|
9801
|
-
if (!existsSync15(urlFile)) return;
|
|
9802
|
-
let content = "";
|
|
9803
|
-
try {
|
|
9804
|
-
content = readFileSync5(urlFile, "utf8");
|
|
9805
|
-
} catch {
|
|
9806
|
-
return;
|
|
9807
|
-
}
|
|
9808
|
-
const url = nextRelayUrl(content, lastOpened);
|
|
9809
|
-
if (!url) return;
|
|
9810
|
-
lastOpened = url;
|
|
9811
|
-
onUrl(url);
|
|
9812
|
-
}, 250);
|
|
9813
|
-
return {
|
|
9814
|
-
relayDirInContainer: `/workspaces/${opts.name}/${RELAY_DIRNAME}`,
|
|
9815
|
-
async dispose() {
|
|
9816
|
-
clearInterval(poll);
|
|
9817
|
-
for (const s of servers) s.close();
|
|
9818
|
-
await fsp5.rm(relayDir, { recursive: true, force: true });
|
|
9819
|
-
}
|
|
9820
|
-
};
|
|
9821
|
-
}
|
|
9822
|
-
var RELAY_DIRNAME;
|
|
9823
|
-
var init_browser_bridge = __esm({
|
|
9824
|
-
"src/devcontainer/browser-bridge.ts"() {
|
|
9825
|
-
"use strict";
|
|
9826
|
-
RELAY_DIRNAME = ".monoceros-bridge";
|
|
9827
|
-
}
|
|
9828
|
-
});
|
|
9829
|
-
|
|
9830
10291
|
// src/devcontainer/claude-trust.ts
|
|
9831
|
-
import { existsSync as
|
|
9832
|
-
import
|
|
10292
|
+
import { existsSync as existsSync19, promises as fsp6 } from "fs";
|
|
10293
|
+
import path28 from "path";
|
|
9833
10294
|
function resolveContainerCwd(name, cwd) {
|
|
9834
10295
|
const workspace = `/workspaces/${name}`;
|
|
9835
10296
|
if (!cwd) return workspace;
|
|
9836
|
-
return
|
|
10297
|
+
return path28.posix.isAbsolute(cwd) ? cwd : path28.posix.join(workspace, cwd);
|
|
9837
10298
|
}
|
|
9838
10299
|
async function preApproveClaudeProject(opts) {
|
|
9839
|
-
const file =
|
|
9840
|
-
if (!
|
|
10300
|
+
const file = path28.join(opts.root, "home", ".claude.json");
|
|
10301
|
+
if (!existsSync19(file)) return;
|
|
9841
10302
|
try {
|
|
9842
10303
|
const raw = await fsp6.readFile(file, "utf8");
|
|
9843
10304
|
const config = raw.trim() ? JSON.parse(raw) : {};
|
|
@@ -9870,56 +10331,6 @@ var init_claude_trust = __esm({
|
|
|
9870
10331
|
}
|
|
9871
10332
|
});
|
|
9872
10333
|
|
|
9873
|
-
// src/devcontainer/shell.ts
|
|
9874
|
-
import { existsSync as existsSync17 } from "fs";
|
|
9875
|
-
import path26 from "path";
|
|
9876
|
-
async function runShell(opts) {
|
|
9877
|
-
assertContainerExists(opts.root);
|
|
9878
|
-
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
9879
|
-
const upCode = await spawnFn(
|
|
9880
|
-
["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
|
|
9881
|
-
opts.root,
|
|
9882
|
-
{ quiet: true }
|
|
9883
|
-
);
|
|
9884
|
-
if (upCode !== 0) return upCode;
|
|
9885
|
-
const name = opts.name ?? path26.basename(opts.root);
|
|
9886
|
-
const isTty2 = opts.isTty ?? process.stdout.isTTY;
|
|
9887
|
-
const bridge = isTty2 ? await startBrowserBridge({ name, root: opts.root, spawn: spawnFn }) : null;
|
|
9888
|
-
try {
|
|
9889
|
-
const innerExec = wrapExec(
|
|
9890
|
-
["bash"],
|
|
9891
|
-
bridge ? { pathPrepend: bridge.relayDirInContainer } : {}
|
|
9892
|
-
);
|
|
9893
|
-
return await spawnFn(
|
|
9894
|
-
[
|
|
9895
|
-
"exec",
|
|
9896
|
-
"--workspace-folder",
|
|
9897
|
-
opts.root,
|
|
9898
|
-
"--mount-workspace-git-root=false",
|
|
9899
|
-
...innerExec
|
|
9900
|
-
],
|
|
9901
|
-
opts.root,
|
|
9902
|
-
{ interactive: true }
|
|
9903
|
-
);
|
|
9904
|
-
} finally {
|
|
9905
|
-
if (bridge) await bridge.dispose();
|
|
9906
|
-
}
|
|
9907
|
-
}
|
|
9908
|
-
function assertContainerExists(root) {
|
|
9909
|
-
if (!existsSync17(path26.join(root, ".devcontainer"))) {
|
|
9910
|
-
throw new Error(
|
|
9911
|
-
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
9912
|
-
);
|
|
9913
|
-
}
|
|
9914
|
-
}
|
|
9915
|
-
var init_shell = __esm({
|
|
9916
|
-
"src/devcontainer/shell.ts"() {
|
|
9917
|
-
"use strict";
|
|
9918
|
-
init_browser_bridge();
|
|
9919
|
-
init_cli();
|
|
9920
|
-
}
|
|
9921
|
-
});
|
|
9922
|
-
|
|
9923
10334
|
// src/devcontainer/run.ts
|
|
9924
10335
|
async function runInContainer(opts) {
|
|
9925
10336
|
if (opts.command.length === 0) {
|
|
@@ -9978,8 +10389,8 @@ var init_run = __esm({
|
|
|
9978
10389
|
});
|
|
9979
10390
|
|
|
9980
10391
|
// src/commands/run.ts
|
|
9981
|
-
import { defineCommand as
|
|
9982
|
-
import { consola as
|
|
10392
|
+
import { defineCommand as defineCommand25 } from "citty";
|
|
10393
|
+
import { consola as consola31 } from "consola";
|
|
9983
10394
|
var runCommand;
|
|
9984
10395
|
var init_run2 = __esm({
|
|
9985
10396
|
"src/commands/run.ts"() {
|
|
@@ -9987,7 +10398,7 @@ var init_run2 = __esm({
|
|
|
9987
10398
|
init_paths();
|
|
9988
10399
|
init_run();
|
|
9989
10400
|
init_inner_args();
|
|
9990
|
-
runCommand =
|
|
10401
|
+
runCommand = defineCommand25({
|
|
9991
10402
|
meta: {
|
|
9992
10403
|
name: "run",
|
|
9993
10404
|
group: "run",
|
|
@@ -10007,7 +10418,7 @@ var init_run2 = __esm({
|
|
|
10007
10418
|
async run({ args }) {
|
|
10008
10419
|
const command = [...getInnerArgs()];
|
|
10009
10420
|
if (command.length === 0) {
|
|
10010
|
-
|
|
10421
|
+
consola31.error(
|
|
10011
10422
|
"No command provided. Usage: `monoceros run <containername> -- <cmd> [args\u2026]`."
|
|
10012
10423
|
);
|
|
10013
10424
|
process.exit(1);
|
|
@@ -10021,7 +10432,7 @@ var init_run2 = __esm({
|
|
|
10021
10432
|
});
|
|
10022
10433
|
process.exit(exitCode);
|
|
10023
10434
|
} catch (err) {
|
|
10024
|
-
|
|
10435
|
+
consola31.error(err instanceof Error ? err.message : String(err));
|
|
10025
10436
|
process.exit(1);
|
|
10026
10437
|
}
|
|
10027
10438
|
}
|
|
@@ -10030,15 +10441,15 @@ var init_run2 = __esm({
|
|
|
10030
10441
|
});
|
|
10031
10442
|
|
|
10032
10443
|
// src/commands/shell.ts
|
|
10033
|
-
import { defineCommand as
|
|
10034
|
-
import { consola as
|
|
10444
|
+
import { defineCommand as defineCommand26 } from "citty";
|
|
10445
|
+
import { consola as consola32 } from "consola";
|
|
10035
10446
|
var shellCommand;
|
|
10036
10447
|
var init_shell2 = __esm({
|
|
10037
10448
|
"src/commands/shell.ts"() {
|
|
10038
10449
|
"use strict";
|
|
10039
10450
|
init_paths();
|
|
10040
10451
|
init_shell();
|
|
10041
|
-
shellCommand =
|
|
10452
|
+
shellCommand = defineCommand26({
|
|
10042
10453
|
meta: {
|
|
10043
10454
|
name: "shell",
|
|
10044
10455
|
group: "run",
|
|
@@ -10059,7 +10470,7 @@ var init_shell2 = __esm({
|
|
|
10059
10470
|
});
|
|
10060
10471
|
process.exit(exitCode);
|
|
10061
10472
|
} catch (err) {
|
|
10062
|
-
|
|
10473
|
+
consola32.error(err instanceof Error ? err.message : String(err));
|
|
10063
10474
|
process.exit(1);
|
|
10064
10475
|
}
|
|
10065
10476
|
}
|
|
@@ -10068,8 +10479,8 @@ var init_shell2 = __esm({
|
|
|
10068
10479
|
});
|
|
10069
10480
|
|
|
10070
10481
|
// src/commands/start.ts
|
|
10071
|
-
import { defineCommand as
|
|
10072
|
-
import { consola as
|
|
10482
|
+
import { defineCommand as defineCommand27 } from "citty";
|
|
10483
|
+
import { consola as consola33 } from "consola";
|
|
10073
10484
|
var startCommand;
|
|
10074
10485
|
var init_start = __esm({
|
|
10075
10486
|
"src/commands/start.ts"() {
|
|
@@ -10078,10 +10489,11 @@ var init_start = __esm({
|
|
|
10078
10489
|
init_io();
|
|
10079
10490
|
init_paths();
|
|
10080
10491
|
init_compose();
|
|
10492
|
+
init_open();
|
|
10081
10493
|
init_proxy();
|
|
10082
10494
|
init_port_check();
|
|
10083
10495
|
init_dispatch();
|
|
10084
|
-
startCommand =
|
|
10496
|
+
startCommand = defineCommand27({
|
|
10085
10497
|
meta: {
|
|
10086
10498
|
name: "start",
|
|
10087
10499
|
group: "run",
|
|
@@ -10092,6 +10504,10 @@ var init_start = __esm({
|
|
|
10092
10504
|
type: "positional",
|
|
10093
10505
|
description: "Container name (yml in $MONOCEROS_HOME/container-configs/).",
|
|
10094
10506
|
required: true
|
|
10507
|
+
},
|
|
10508
|
+
open: {
|
|
10509
|
+
type: "string",
|
|
10510
|
+
description: `After a successful start, open the container in this tool (${OPEN_TOOLS.join("|")}).`
|
|
10095
10511
|
}
|
|
10096
10512
|
},
|
|
10097
10513
|
run({ args }) {
|
|
@@ -10106,7 +10522,7 @@ var init_start = __esm({
|
|
|
10106
10522
|
hostPort = proxyHostPort(global);
|
|
10107
10523
|
}
|
|
10108
10524
|
} catch (err) {
|
|
10109
|
-
|
|
10525
|
+
consola33.warn(
|
|
10110
10526
|
`Could not read container yml ahead of start: ${err instanceof Error ? err.message : String(err)}. Skipping Traefik pre-flight.`
|
|
10111
10527
|
);
|
|
10112
10528
|
}
|
|
@@ -10114,7 +10530,15 @@ var init_start = __esm({
|
|
|
10114
10530
|
await preflightHostPort(hostPort);
|
|
10115
10531
|
await ensureProxy({ hostPort });
|
|
10116
10532
|
}
|
|
10117
|
-
|
|
10533
|
+
const exitCode = await runStart({ root: containerDir(args.name) });
|
|
10534
|
+
if (args.open && exitCode === 0) {
|
|
10535
|
+
try {
|
|
10536
|
+
await runOpen({ name: args.name, tool: args.open });
|
|
10537
|
+
} catch (err) {
|
|
10538
|
+
consola33.warn(err instanceof Error ? err.message : String(err));
|
|
10539
|
+
}
|
|
10540
|
+
}
|
|
10541
|
+
return exitCode;
|
|
10118
10542
|
});
|
|
10119
10543
|
}
|
|
10120
10544
|
});
|
|
@@ -10122,7 +10546,7 @@ var init_start = __esm({
|
|
|
10122
10546
|
});
|
|
10123
10547
|
|
|
10124
10548
|
// src/commands/status.ts
|
|
10125
|
-
import { defineCommand as
|
|
10549
|
+
import { defineCommand as defineCommand28 } from "citty";
|
|
10126
10550
|
var statusCommand;
|
|
10127
10551
|
var init_status = __esm({
|
|
10128
10552
|
"src/commands/status.ts"() {
|
|
@@ -10130,7 +10554,7 @@ var init_status = __esm({
|
|
|
10130
10554
|
init_paths();
|
|
10131
10555
|
init_compose();
|
|
10132
10556
|
init_dispatch();
|
|
10133
|
-
statusCommand =
|
|
10557
|
+
statusCommand = defineCommand28({
|
|
10134
10558
|
meta: {
|
|
10135
10559
|
name: "status",
|
|
10136
10560
|
group: "run",
|
|
@@ -10160,8 +10584,8 @@ var init_status = __esm({
|
|
|
10160
10584
|
});
|
|
10161
10585
|
|
|
10162
10586
|
// src/commands/stop.ts
|
|
10163
|
-
import { defineCommand as
|
|
10164
|
-
import { consola as
|
|
10587
|
+
import { defineCommand as defineCommand29 } from "citty";
|
|
10588
|
+
import { consola as consola34 } from "consola";
|
|
10165
10589
|
var stopCommand;
|
|
10166
10590
|
var init_stop = __esm({
|
|
10167
10591
|
"src/commands/stop.ts"() {
|
|
@@ -10170,7 +10594,7 @@ var init_stop = __esm({
|
|
|
10170
10594
|
init_compose();
|
|
10171
10595
|
init_proxy();
|
|
10172
10596
|
init_dispatch();
|
|
10173
|
-
stopCommand =
|
|
10597
|
+
stopCommand = defineCommand29({
|
|
10174
10598
|
meta: {
|
|
10175
10599
|
name: "stop",
|
|
10176
10600
|
group: "run",
|
|
@@ -10195,10 +10619,10 @@ var init_stop = __esm({
|
|
|
10195
10619
|
});
|
|
10196
10620
|
try {
|
|
10197
10621
|
await maybeStopProxy({
|
|
10198
|
-
logger: { info: (msg) =>
|
|
10622
|
+
logger: { info: (msg) => consola34.info(msg) }
|
|
10199
10623
|
});
|
|
10200
10624
|
} catch (err) {
|
|
10201
|
-
|
|
10625
|
+
consola34.warn(
|
|
10202
10626
|
`Could not tear down the Traefik proxy: ${err instanceof Error ? err.message : String(err)}. Ignored.`
|
|
10203
10627
|
);
|
|
10204
10628
|
}
|
|
@@ -10210,11 +10634,11 @@ var init_stop = __esm({
|
|
|
10210
10634
|
});
|
|
10211
10635
|
|
|
10212
10636
|
// src/tunnel/resolve.ts
|
|
10213
|
-
import { existsSync as
|
|
10214
|
-
import
|
|
10637
|
+
import { existsSync as existsSync20 } from "fs";
|
|
10638
|
+
import path29 from "path";
|
|
10215
10639
|
async function resolveTunnelTarget(opts) {
|
|
10216
10640
|
const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
|
|
10217
|
-
if (!
|
|
10641
|
+
if (!existsSync20(ymlPath)) {
|
|
10218
10642
|
throw new Error(
|
|
10219
10643
|
`No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
|
|
10220
10644
|
);
|
|
@@ -10222,13 +10646,13 @@ async function resolveTunnelTarget(opts) {
|
|
|
10222
10646
|
const parsed = await readConfig(ymlPath);
|
|
10223
10647
|
const config = parsed.config;
|
|
10224
10648
|
const containerRoot = containerDir(opts.name, opts.monocerosHome);
|
|
10225
|
-
if (!
|
|
10649
|
+
if (!existsSync20(containerRoot)) {
|
|
10226
10650
|
throw new Error(
|
|
10227
10651
|
`Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
|
|
10228
10652
|
);
|
|
10229
10653
|
}
|
|
10230
|
-
const composePath =
|
|
10231
|
-
const isCompose =
|
|
10654
|
+
const composePath = path29.join(containerRoot, ".devcontainer", "compose.yaml");
|
|
10655
|
+
const isCompose = existsSync20(composePath);
|
|
10232
10656
|
const parsedTarget = parseTargetArg(opts.target, config);
|
|
10233
10657
|
const docker = opts.docker ?? defaultDockerExec;
|
|
10234
10658
|
if (isCompose) {
|
|
@@ -10463,8 +10887,8 @@ var init_port_check2 = __esm({
|
|
|
10463
10887
|
});
|
|
10464
10888
|
|
|
10465
10889
|
// src/tunnel/run.ts
|
|
10466
|
-
import { spawn as
|
|
10467
|
-
import { consola as
|
|
10890
|
+
import { spawn as spawn12 } from "child_process";
|
|
10891
|
+
import { consola as consola35 } from "consola";
|
|
10468
10892
|
function signalNumber(signal) {
|
|
10469
10893
|
switch (signal) {
|
|
10470
10894
|
case "SIGINT":
|
|
@@ -10477,8 +10901,8 @@ function signalNumber(signal) {
|
|
|
10477
10901
|
}
|
|
10478
10902
|
async function runTunnel(opts) {
|
|
10479
10903
|
const log = opts.logger ?? {
|
|
10480
|
-
info: (m) =>
|
|
10481
|
-
warn: (m) =>
|
|
10904
|
+
info: (m) => consola35.info(m),
|
|
10905
|
+
warn: (m) => consola35.warn(m)
|
|
10482
10906
|
};
|
|
10483
10907
|
const resolve = opts.resolve ?? resolveTunnelTarget;
|
|
10484
10908
|
const resolveArgs3 = {
|
|
@@ -10553,7 +10977,7 @@ var init_run3 = __esm({
|
|
|
10553
10977
|
init_port_check2();
|
|
10554
10978
|
SOCAT_IMAGE = "alpine/socat:1.8.0.3";
|
|
10555
10979
|
defaultDockerSpawn = (args) => {
|
|
10556
|
-
const child =
|
|
10980
|
+
const child = spawn12("docker", args, {
|
|
10557
10981
|
stdio: "inherit"
|
|
10558
10982
|
});
|
|
10559
10983
|
const exited = new Promise((resolve, reject) => {
|
|
@@ -10584,8 +11008,8 @@ var init_run3 = __esm({
|
|
|
10584
11008
|
});
|
|
10585
11009
|
|
|
10586
11010
|
// src/commands/tunnel.ts
|
|
10587
|
-
import { defineCommand as
|
|
10588
|
-
import { consola as
|
|
11011
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
11012
|
+
import { consola as consola36 } from "consola";
|
|
10589
11013
|
function parseLocalPort(raw) {
|
|
10590
11014
|
if (raw === void 0) return void 0;
|
|
10591
11015
|
const n = Number(raw);
|
|
@@ -10601,7 +11025,7 @@ var init_tunnel = __esm({
|
|
|
10601
11025
|
"src/commands/tunnel.ts"() {
|
|
10602
11026
|
"use strict";
|
|
10603
11027
|
init_run3();
|
|
10604
|
-
tunnelCommand =
|
|
11028
|
+
tunnelCommand = defineCommand30({
|
|
10605
11029
|
meta: {
|
|
10606
11030
|
name: "tunnel",
|
|
10607
11031
|
group: "discovery",
|
|
@@ -10638,7 +11062,7 @@ var init_tunnel = __esm({
|
|
|
10638
11062
|
});
|
|
10639
11063
|
process.exit(exitCode);
|
|
10640
11064
|
} catch (err) {
|
|
10641
|
-
|
|
11065
|
+
consola36.error(err instanceof Error ? err.message : String(err));
|
|
10642
11066
|
process.exit(1);
|
|
10643
11067
|
}
|
|
10644
11068
|
}
|
|
@@ -10708,8 +11132,8 @@ var init_prune = __esm({
|
|
|
10708
11132
|
});
|
|
10709
11133
|
|
|
10710
11134
|
// src/upgrade/index.ts
|
|
10711
|
-
import { existsSync as
|
|
10712
|
-
import { consola as
|
|
11135
|
+
import { existsSync as existsSync21, promises as fs18 } from "fs";
|
|
11136
|
+
import { consola as consola37 } from "consola";
|
|
10713
11137
|
async function fetchRuntimeVersions() {
|
|
10714
11138
|
const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
|
|
10715
11139
|
const tokenRes = await fetch(tokenUrl);
|
|
@@ -10747,7 +11171,7 @@ ${yml}`;
|
|
|
10747
11171
|
}
|
|
10748
11172
|
async function runUpgrade(opts) {
|
|
10749
11173
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
10750
|
-
const logger = opts.logger ??
|
|
11174
|
+
const logger = opts.logger ?? consola37;
|
|
10751
11175
|
const fetchVersions = opts.fetchVersions ?? fetchRuntimeVersions;
|
|
10752
11176
|
if (opts.list) {
|
|
10753
11177
|
const versions = await fetchVersions();
|
|
@@ -10773,7 +11197,7 @@ async function runUpgrade(opts) {
|
|
|
10773
11197
|
);
|
|
10774
11198
|
}
|
|
10775
11199
|
}
|
|
10776
|
-
if (opts.name && !
|
|
11200
|
+
if (opts.name && !existsSync21(containerConfigPath(opts.name, home))) {
|
|
10777
11201
|
throw new Error(
|
|
10778
11202
|
`No such config: ${containerConfigPath(opts.name, home)}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
10779
11203
|
);
|
|
@@ -10824,11 +11248,11 @@ async function runUpgrade(opts) {
|
|
|
10824
11248
|
let bumped = 0;
|
|
10825
11249
|
for (const name of targets) {
|
|
10826
11250
|
const ymlPath = containerConfigPath(name, home);
|
|
10827
|
-
if (!
|
|
10828
|
-
const raw = await
|
|
11251
|
+
if (!existsSync21(ymlPath)) continue;
|
|
11252
|
+
const raw = await fs18.readFile(ymlPath, "utf8");
|
|
10829
11253
|
const updated = setRuntimeVersion(raw, pinVersion);
|
|
10830
11254
|
if (updated !== raw) {
|
|
10831
|
-
await
|
|
11255
|
+
await fs18.writeFile(ymlPath, updated);
|
|
10832
11256
|
bumped += 1;
|
|
10833
11257
|
logger.info(`Pinned '${name}' to runtime ${pinVersion}.`);
|
|
10834
11258
|
}
|
|
@@ -10870,7 +11294,7 @@ function formatPruneLine(prune) {
|
|
|
10870
11294
|
}
|
|
10871
11295
|
async function listContainerNames2(home) {
|
|
10872
11296
|
try {
|
|
10873
|
-
const entries = await
|
|
11297
|
+
const entries = await fs18.readdir(containerConfigsDir(home));
|
|
10874
11298
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length));
|
|
10875
11299
|
} catch {
|
|
10876
11300
|
return [];
|
|
@@ -10893,7 +11317,7 @@ var init_upgrade = __esm({
|
|
|
10893
11317
|
});
|
|
10894
11318
|
|
|
10895
11319
|
// src/commands/upgrade.ts
|
|
10896
|
-
import { defineCommand as
|
|
11320
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
10897
11321
|
var upgradeCommand;
|
|
10898
11322
|
var init_upgrade2 = __esm({
|
|
10899
11323
|
"src/commands/upgrade.ts"() {
|
|
@@ -10901,7 +11325,7 @@ var init_upgrade2 = __esm({
|
|
|
10901
11325
|
init_upgrade();
|
|
10902
11326
|
init_version();
|
|
10903
11327
|
init_dispatch();
|
|
10904
|
-
upgradeCommand =
|
|
11328
|
+
upgradeCommand = defineCommand31({
|
|
10905
11329
|
meta: {
|
|
10906
11330
|
name: "upgrade",
|
|
10907
11331
|
group: "lifecycle",
|
|
@@ -10943,7 +11367,7 @@ var main_exports = {};
|
|
|
10943
11367
|
__export(main_exports, {
|
|
10944
11368
|
main: () => main
|
|
10945
11369
|
});
|
|
10946
|
-
import { defineCommand as
|
|
11370
|
+
import { defineCommand as defineCommand32 } from "citty";
|
|
10947
11371
|
var main;
|
|
10948
11372
|
var init_main = __esm({
|
|
10949
11373
|
"src/main.ts"() {
|
|
@@ -10961,6 +11385,7 @@ var init_main = __esm({
|
|
|
10961
11385
|
init_init2();
|
|
10962
11386
|
init_list_components();
|
|
10963
11387
|
init_logs();
|
|
11388
|
+
init_open2();
|
|
10964
11389
|
init_port();
|
|
10965
11390
|
init_remove_apt_packages();
|
|
10966
11391
|
init_remove_feature();
|
|
@@ -10979,7 +11404,7 @@ var init_main = __esm({
|
|
|
10979
11404
|
init_tunnel();
|
|
10980
11405
|
init_upgrade2();
|
|
10981
11406
|
init_version();
|
|
10982
|
-
main =
|
|
11407
|
+
main = defineCommand32({
|
|
10983
11408
|
meta: {
|
|
10984
11409
|
name: "monoceros",
|
|
10985
11410
|
version: CLI_VERSION,
|
|
@@ -10989,6 +11414,7 @@ var init_main = __esm({
|
|
|
10989
11414
|
init: initCommand,
|
|
10990
11415
|
"list-components": listComponentsCommand,
|
|
10991
11416
|
shell: shellCommand,
|
|
11417
|
+
open: openCommand,
|
|
10992
11418
|
run: runCommand,
|
|
10993
11419
|
logs: logsCommand,
|
|
10994
11420
|
start: startCommand,
|
|
@@ -11275,25 +11701,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
11275
11701
|
const separatorIdx = argv.indexOf("--");
|
|
11276
11702
|
if (helpIdx === -1) return null;
|
|
11277
11703
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
11278
|
-
const
|
|
11704
|
+
const path30 = [];
|
|
11279
11705
|
const tokens = argv.slice(
|
|
11280
11706
|
0,
|
|
11281
11707
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
11282
11708
|
);
|
|
11283
11709
|
let cursor = main2;
|
|
11284
11710
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
11285
|
-
|
|
11711
|
+
path30.push(mainName);
|
|
11286
11712
|
for (const tok of tokens) {
|
|
11287
11713
|
if (tok.startsWith("-")) continue;
|
|
11288
11714
|
const subs = cursor.subCommands ?? {};
|
|
11289
11715
|
if (tok in subs) {
|
|
11290
11716
|
cursor = subs[tok];
|
|
11291
|
-
|
|
11717
|
+
path30.push(tok);
|
|
11292
11718
|
continue;
|
|
11293
11719
|
}
|
|
11294
11720
|
break;
|
|
11295
11721
|
}
|
|
11296
|
-
return { path:
|
|
11722
|
+
return { path: path30, cmd: cursor };
|
|
11297
11723
|
}
|
|
11298
11724
|
async function maybeRenderHelp(argv, main2) {
|
|
11299
11725
|
const hit = detectHelpRequest(argv, main2);
|