@getmonoceros/workbench 1.15.0 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -20
- package/dist/bin.js +389 -27
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
- package/templates/components/README.md +13 -13
package/dist/bin.js
CHANGED
|
@@ -74,12 +74,13 @@ ${issues}`);
|
|
|
74
74
|
}
|
|
75
75
|
return result.data;
|
|
76
76
|
}
|
|
77
|
-
var SOLUTION_NAME_RE, APT_PACKAGE_NAME_RE, FEATURE_REF_RE, INSTALL_URL_RE, REPO_URL_RE, REPO_PATH_RE, POSTGRES_URL_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, ExternalServicesSchema, SolutionConfigSchema;
|
|
77
|
+
var SOLUTION_NAME_RE, APT_PACKAGE_NAME_RE, RUNTIME_VERSION_RE, FEATURE_REF_RE, INSTALL_URL_RE, REPO_URL_RE, REPO_PATH_RE, POSTGRES_URL_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, ExternalServicesSchema, SolutionConfigSchema;
|
|
78
78
|
var init_schema = __esm({
|
|
79
79
|
"src/config/schema.ts"() {
|
|
80
80
|
"use strict";
|
|
81
81
|
SOLUTION_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
82
82
|
APT_PACKAGE_NAME_RE = /^[a-z0-9][a-z0-9.+-]*$/;
|
|
83
|
+
RUNTIME_VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
83
84
|
FEATURE_REF_RE = /^[a-z0-9.-]+(\/[a-z0-9._-]+)+:[a-z0-9._-]+$/;
|
|
84
85
|
INSTALL_URL_RE = /^https:\/\/[A-Za-z0-9.\-_~/:?#[\]@!&'()*+,;=%]+$/;
|
|
85
86
|
REPO_URL_RE = /^https:\/\/[A-Za-z0-9@:/+_~.#=&?-]+$/;
|
|
@@ -216,6 +217,15 @@ var init_schema = __esm({
|
|
|
216
217
|
SOLUTION_NAME_RE,
|
|
217
218
|
"Invalid solution name. Use letters, digits, '.', '_' or '-'."
|
|
218
219
|
),
|
|
220
|
+
// Pinned runtime-image version (ADR 0017). Written by `init`, reused
|
|
221
|
+
// verbatim by every subsequent `apply` (never auto-bumped), changed
|
|
222
|
+
// only by `monoceros upgrade`. Optional in the schema so a
|
|
223
|
+
// pre-pinning yml still parses — `apply` is what enforces its
|
|
224
|
+
// presence with an actionable hint.
|
|
225
|
+
runtimeVersion: z.string().regex(
|
|
226
|
+
RUNTIME_VERSION_RE,
|
|
227
|
+
"Invalid runtimeVersion. Expected an exact version like '1.1.0'."
|
|
228
|
+
).optional(),
|
|
219
229
|
languages: z.array(z.string().min(1)).default([]),
|
|
220
230
|
aptPackages: z.array(
|
|
221
231
|
z.string().regex(
|
|
@@ -1821,6 +1831,27 @@ var init_port_check = __esm({
|
|
|
1821
1831
|
});
|
|
1822
1832
|
|
|
1823
1833
|
// src/create/catalog.ts
|
|
1834
|
+
import "fs";
|
|
1835
|
+
import "url";
|
|
1836
|
+
function resolveRuntimeImage(version) {
|
|
1837
|
+
const ov = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();
|
|
1838
|
+
if (ov && ov.length > 0) return ov;
|
|
1839
|
+
if (!version) return `${RUNTIME_IMAGE_REPO}:1`;
|
|
1840
|
+
return `${RUNTIME_IMAGE_REPO}:${version}`;
|
|
1841
|
+
}
|
|
1842
|
+
function compareRuntimeVersions(a, b) {
|
|
1843
|
+
const pa = a.split(".").map(Number);
|
|
1844
|
+
const pb = b.split(".").map(Number);
|
|
1845
|
+
for (let i = 0; i < 3; i++) {
|
|
1846
|
+
const d = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1847
|
+
if (d !== 0) return d < 0 ? -1 : 1;
|
|
1848
|
+
}
|
|
1849
|
+
return 0;
|
|
1850
|
+
}
|
|
1851
|
+
function runtimeSupportsIdeVolumes(version) {
|
|
1852
|
+
if (!version) return false;
|
|
1853
|
+
return compareRuntimeVersions(version, MIN_RUNTIME_FOR_IDE_VOLUMES) >= 0;
|
|
1854
|
+
}
|
|
1824
1855
|
function parseLanguageSpec(spec) {
|
|
1825
1856
|
const m = LANGUAGE_SPEC_RE.exec(spec);
|
|
1826
1857
|
if (!m) return null;
|
|
@@ -1877,13 +1908,21 @@ function deriveServiceName(image) {
|
|
|
1877
1908
|
const noTag = lastSegment.split("@")[0].split(":")[0];
|
|
1878
1909
|
return noTag.toLowerCase().replace(/[^a-z0-9_-]/g, "-");
|
|
1879
1910
|
}
|
|
1880
|
-
var DEFAULT_BASE_IMAGE, override, BASE_IMAGE, BUILTIN_LANGUAGES, LANGUAGE_CATALOG, LANGUAGE_SPEC_RE, SERVICE_CATALOG;
|
|
1911
|
+
var DEFAULT_BASE_IMAGE, override, BASE_IMAGE, RUNTIME_IMAGE_REPO, DEFAULT_RUNTIME_VERSION, MIN_RUNTIME_FOR_IDE_VOLUMES, BUILTIN_LANGUAGES, LANGUAGE_CATALOG, LANGUAGE_SPEC_RE, SERVICE_CATALOG;
|
|
1881
1912
|
var init_catalog = __esm({
|
|
1882
1913
|
"src/create/catalog.ts"() {
|
|
1883
1914
|
"use strict";
|
|
1884
1915
|
DEFAULT_BASE_IMAGE = "ghcr.io/getmonoceros/monoceros-runtime:1";
|
|
1885
1916
|
override = process.env.MONOCEROS_BASE_IMAGE_OVERRIDE?.trim();
|
|
1886
1917
|
BASE_IMAGE = override && override.length > 0 ? override : DEFAULT_BASE_IMAGE;
|
|
1918
|
+
RUNTIME_IMAGE_REPO = "ghcr.io/getmonoceros/monoceros-runtime";
|
|
1919
|
+
DEFAULT_RUNTIME_VERSION = true ? "1.1.0" : readFileSync3(
|
|
1920
|
+
fileURLToPath2(
|
|
1921
|
+
new URL("../../../../images/runtime/VERSION", import.meta.url)
|
|
1922
|
+
),
|
|
1923
|
+
"utf8"
|
|
1924
|
+
).trim();
|
|
1925
|
+
MIN_RUNTIME_FOR_IDE_VOLUMES = "1.1.0";
|
|
1887
1926
|
BUILTIN_LANGUAGES = /* @__PURE__ */ new Set(["node"]);
|
|
1888
1927
|
LANGUAGE_CATALOG = {
|
|
1889
1928
|
node: { id: "node", feature: "ghcr.io/devcontainers/features/node:1" },
|
|
@@ -1921,7 +1960,8 @@ var init_catalog = __esm({
|
|
|
1921
1960
|
// /var/lib/postgresql/data directly. See
|
|
1922
1961
|
// https://github.com/docker-library/postgres/pull/1259.
|
|
1923
1962
|
dataMount: "/var/lib/postgresql",
|
|
1924
|
-
defaultPort: 5432
|
|
1963
|
+
defaultPort: 5432,
|
|
1964
|
+
vscodeExtensions: ["cweijan.vscode-database-client2"]
|
|
1925
1965
|
},
|
|
1926
1966
|
mysql: {
|
|
1927
1967
|
id: "mysql",
|
|
@@ -1946,7 +1986,8 @@ var init_catalog = __esm({
|
|
|
1946
1986
|
retries: 5
|
|
1947
1987
|
},
|
|
1948
1988
|
dataMount: "/var/lib/mysql",
|
|
1949
|
-
defaultPort: 3306
|
|
1989
|
+
defaultPort: 3306,
|
|
1990
|
+
vscodeExtensions: ["cweijan.vscode-database-client2"]
|
|
1950
1991
|
},
|
|
1951
1992
|
redis: {
|
|
1952
1993
|
id: "redis",
|
|
@@ -1958,7 +1999,8 @@ var init_catalog = __esm({
|
|
|
1958
1999
|
retries: 5
|
|
1959
2000
|
},
|
|
1960
2001
|
dataMount: "/data",
|
|
1961
|
-
defaultPort: 6379
|
|
2002
|
+
defaultPort: 6379,
|
|
2003
|
+
vscodeExtensions: ["cweijan.vscode-database-client2"]
|
|
1962
2004
|
}
|
|
1963
2005
|
};
|
|
1964
2006
|
}
|
|
@@ -2022,7 +2064,7 @@ var init_service_doc = __esm({
|
|
|
2022
2064
|
});
|
|
2023
2065
|
|
|
2024
2066
|
// src/create/scaffold.ts
|
|
2025
|
-
import { existsSync as existsSync5, readFileSync as
|
|
2067
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, promises as fs7 } from "fs";
|
|
2026
2068
|
import path8 from "path";
|
|
2027
2069
|
function deriveRepoName(url) {
|
|
2028
2070
|
const lastSep = Math.max(url.lastIndexOf("/"), url.lastIndexOf(":"));
|
|
@@ -2134,6 +2176,7 @@ function normalizeOptions(opts) {
|
|
|
2134
2176
|
const ports = opts.ports ? [...new Set(opts.ports)] : void 0;
|
|
2135
2177
|
return {
|
|
2136
2178
|
name: opts.name,
|
|
2179
|
+
...opts.runtimeVersion !== void 0 ? { runtimeVersion: opts.runtimeVersion } : {},
|
|
2137
2180
|
languages,
|
|
2138
2181
|
services,
|
|
2139
2182
|
postgresUrl: opts.postgresUrl,
|
|
@@ -2208,7 +2251,7 @@ function resolveFeatures(opts) {
|
|
|
2208
2251
|
function readPersistentHomeEntries(localSourceDir) {
|
|
2209
2252
|
const manifestPath = path8.join(localSourceDir, "devcontainer-feature.json");
|
|
2210
2253
|
try {
|
|
2211
|
-
const text =
|
|
2254
|
+
const text = readFileSync4(manifestPath, "utf8");
|
|
2212
2255
|
const parsed = JSON.parse(text);
|
|
2213
2256
|
return {
|
|
2214
2257
|
paths: filterSubpaths(parsed["x-monoceros"]?.persistentHomePaths),
|
|
@@ -2246,6 +2289,18 @@ function filterFileEntries(raw) {
|
|
|
2246
2289
|
function isValidHomeSubpath(p) {
|
|
2247
2290
|
return p.length > 0 && !p.startsWith("/") && !p.includes("..") && HOME_SUBPATH_RE.test(p);
|
|
2248
2291
|
}
|
|
2292
|
+
function ideStateVolumes(name) {
|
|
2293
|
+
return [
|
|
2294
|
+
{
|
|
2295
|
+
volume: `monoceros-${name}-vscode-extensions`,
|
|
2296
|
+
target: "/home/node/.vscode-server/extensions"
|
|
2297
|
+
},
|
|
2298
|
+
{
|
|
2299
|
+
volume: `monoceros-${name}-vscode-userdata`,
|
|
2300
|
+
target: "/home/node/.vscode-server/data/User"
|
|
2301
|
+
}
|
|
2302
|
+
];
|
|
2303
|
+
}
|
|
2249
2304
|
function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
2250
2305
|
const resolvedFeatures = resolveFeatures(opts);
|
|
2251
2306
|
const features = {};
|
|
@@ -2291,7 +2346,10 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
2291
2346
|
...customizationsField ?? {}
|
|
2292
2347
|
};
|
|
2293
2348
|
}
|
|
2294
|
-
const
|
|
2349
|
+
const ideMounts = runtimeSupportsIdeVolumes(opts.runtimeVersion) ? ideStateVolumes(opts.name).map(
|
|
2350
|
+
(v) => `source=${v.volume},target=${v.target},type=volume`
|
|
2351
|
+
) : [];
|
|
2352
|
+
const mounts = [...homeMounts, ...ideMounts];
|
|
2295
2353
|
const mountsField = mounts.length > 0 ? { mounts } : {};
|
|
2296
2354
|
const workspaceMountField = {
|
|
2297
2355
|
workspaceMount: `source=\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind,consistency=cached`,
|
|
@@ -2304,7 +2362,7 @@ function buildDevcontainerJson(opts, dockerMode = "rootful") {
|
|
|
2304
2362
|
}
|
|
2305
2363
|
return {
|
|
2306
2364
|
name: opts.name,
|
|
2307
|
-
image:
|
|
2365
|
+
image: resolveRuntimeImage(opts.runtimeVersion),
|
|
2308
2366
|
remoteUser: "node",
|
|
2309
2367
|
...workspaceMountField,
|
|
2310
2368
|
...mountsField,
|
|
@@ -2331,8 +2389,9 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
2331
2389
|
void dockerMode;
|
|
2332
2390
|
const hasPorts = (opts.ports?.length ?? 0) > 0;
|
|
2333
2391
|
const lines = ["services:"];
|
|
2392
|
+
const ideVolumes = runtimeSupportsIdeVolumes(opts.runtimeVersion) ? ideStateVolumes(opts.name) : [];
|
|
2334
2393
|
lines.push(" workspace:");
|
|
2335
|
-
lines.push(` image: ${
|
|
2394
|
+
lines.push(` image: ${resolveRuntimeImage(opts.runtimeVersion)}`);
|
|
2336
2395
|
lines.push(" command: 'sleep infinity'");
|
|
2337
2396
|
lines.push(" cap_add:");
|
|
2338
2397
|
lines.push(" - NET_ADMIN");
|
|
@@ -2355,6 +2414,9 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
2355
2414
|
lines.push(` - ../home/${sub}:/home/node/${sub}`);
|
|
2356
2415
|
}
|
|
2357
2416
|
}
|
|
2417
|
+
for (const v of ideVolumes) {
|
|
2418
|
+
lines.push(` - ${v.volume}:${v.target}`);
|
|
2419
|
+
}
|
|
2358
2420
|
for (const svc of opts.services) {
|
|
2359
2421
|
lines.push(` ${svc.name}:`);
|
|
2360
2422
|
lines.push(` image: ${svc.image}`);
|
|
@@ -2393,6 +2455,13 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
2393
2455
|
}
|
|
2394
2456
|
}
|
|
2395
2457
|
}
|
|
2458
|
+
if (ideVolumes.length > 0) {
|
|
2459
|
+
lines.push("volumes:");
|
|
2460
|
+
for (const v of ideVolumes) {
|
|
2461
|
+
lines.push(` ${v.volume}:`);
|
|
2462
|
+
lines.push(` name: ${v.volume}`);
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2396
2465
|
if (hasPorts) {
|
|
2397
2466
|
lines.push("networks:");
|
|
2398
2467
|
lines.push(" monoceros-proxy:");
|
|
@@ -2400,8 +2469,34 @@ function buildComposeYaml(opts, dockerMode = "rootful") {
|
|
|
2400
2469
|
}
|
|
2401
2470
|
return lines.join("\n") + "\n";
|
|
2402
2471
|
}
|
|
2472
|
+
function extractRepoHost(url) {
|
|
2473
|
+
const schemeMatch = /^[a-z][a-z0-9+.-]*:\/\/(?:[^@/]+@)?([^/:]+)/i.exec(url);
|
|
2474
|
+
if (schemeMatch) return schemeMatch[1].toLowerCase();
|
|
2475
|
+
const scpMatch = /^(?:[^@/]+@)?([^/:]+):/.exec(url);
|
|
2476
|
+
if (scpMatch) return scpMatch[1].toLowerCase();
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
function computeExtensionRecommendations(opts) {
|
|
2480
|
+
const recs = /* @__PURE__ */ new Set();
|
|
2481
|
+
for (const svc of opts.services) {
|
|
2482
|
+
const catalogEntry = SERVICE_CATALOG[svc.name];
|
|
2483
|
+
for (const ext of catalogEntry?.vscodeExtensions ?? []) {
|
|
2484
|
+
recs.add(ext);
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
for (const repo of opts.repos ?? []) {
|
|
2488
|
+
const host = extractRepoHost(repo.url);
|
|
2489
|
+
if (!host) continue;
|
|
2490
|
+
for (const ext of REPO_HOST_EXTENSIONS[host] ?? []) {
|
|
2491
|
+
recs.add(ext);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
return [...recs].sort((a, b) => a.localeCompare(b));
|
|
2495
|
+
}
|
|
2403
2496
|
function buildCodeWorkspaceJson(opts) {
|
|
2404
|
-
const folders = [
|
|
2497
|
+
const folders = [
|
|
2498
|
+
{ path: ".", name: WORKSPACE_ROOT_LABEL }
|
|
2499
|
+
];
|
|
2405
2500
|
const sortedRepos = [...opts.repos ?? []].sort(
|
|
2406
2501
|
(a, b) => a.path.localeCompare(b.path)
|
|
2407
2502
|
);
|
|
@@ -2409,7 +2504,11 @@ function buildCodeWorkspaceJson(opts) {
|
|
|
2409
2504
|
const label = repo.path.split("/").pop() ?? repo.path;
|
|
2410
2505
|
folders.push({ path: `projects/${repo.path}`, name: label });
|
|
2411
2506
|
}
|
|
2412
|
-
|
|
2507
|
+
const recommendations = computeExtensionRecommendations(opts);
|
|
2508
|
+
return {
|
|
2509
|
+
folders,
|
|
2510
|
+
...recommendations.length > 0 ? { extensions: { recommendations } } : {}
|
|
2511
|
+
};
|
|
2413
2512
|
}
|
|
2414
2513
|
function mergeCodeWorkspace(existing, generated) {
|
|
2415
2514
|
if (!existing || typeof existing !== "object" || Array.isArray(existing) || !Array.isArray(existing.folders)) {
|
|
@@ -2424,10 +2523,41 @@ function mergeCodeWorkspace(existing, generated) {
|
|
|
2424
2523
|
for (const g of generated.folders) {
|
|
2425
2524
|
if (!existingPaths.has(g.path)) merged.push(g);
|
|
2426
2525
|
}
|
|
2526
|
+
const generatedRootName = generated.folders.find((f) => f.path === ".")?.name;
|
|
2527
|
+
if (generatedRootName) {
|
|
2528
|
+
const idx = merged.findIndex(
|
|
2529
|
+
(f) => f && typeof f === "object" && f.path === "."
|
|
2530
|
+
);
|
|
2531
|
+
if (idx >= 0 && !merged[idx].name) {
|
|
2532
|
+
merged[idx] = { ...merged[idx], name: generatedRootName };
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2427
2535
|
const out = { ...existingObj };
|
|
2428
2536
|
out.folders = merged;
|
|
2537
|
+
const generatedRecs = generated.extensions?.recommendations ?? [];
|
|
2538
|
+
if (generatedRecs.length > 0) {
|
|
2539
|
+
const existingExtRaw = existingObj.extensions;
|
|
2540
|
+
const existingExt = existingExtRaw && typeof existingExtRaw === "object" && !Array.isArray(existingExtRaw) ? existingExtRaw : {};
|
|
2541
|
+
const existingRecs = Array.isArray(existingExt.recommendations) ? existingExt.recommendations.filter(
|
|
2542
|
+
(r) => typeof r === "string"
|
|
2543
|
+
) : [];
|
|
2544
|
+
const existingRecSet = new Set(existingRecs);
|
|
2545
|
+
const mergedRecs = [...existingRecs];
|
|
2546
|
+
for (const r of generatedRecs) {
|
|
2547
|
+
if (!existingRecSet.has(r)) mergedRecs.push(r);
|
|
2548
|
+
}
|
|
2549
|
+
out.extensions = { ...existingExt, recommendations: mergedRecs };
|
|
2550
|
+
}
|
|
2429
2551
|
return out;
|
|
2430
2552
|
}
|
|
2553
|
+
function mergeVscodeSettings(existing) {
|
|
2554
|
+
const base = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
2555
|
+
const existingExclude = base["files.exclude"] && typeof base["files.exclude"] === "object" && !Array.isArray(base["files.exclude"]) ? base["files.exclude"] : {};
|
|
2556
|
+
return {
|
|
2557
|
+
...base,
|
|
2558
|
+
"files.exclude": { ...existingExclude, ...ROOT_DENOISE_EXCLUDES }
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2431
2561
|
function buildPostCreateScript(opts) {
|
|
2432
2562
|
const lines = [
|
|
2433
2563
|
"#!/usr/bin/env bash",
|
|
@@ -2528,6 +2658,14 @@ async function writePostCreateScript(devcontainerDir, opts) {
|
|
|
2528
2658
|
await fs7.writeFile(dest, buildPostCreateScript(opts));
|
|
2529
2659
|
await fs7.chmod(dest, 493);
|
|
2530
2660
|
}
|
|
2661
|
+
async function writeIfChanged(filePath, content) {
|
|
2662
|
+
try {
|
|
2663
|
+
if (await fs7.readFile(filePath, "utf8") === content) return false;
|
|
2664
|
+
} catch {
|
|
2665
|
+
}
|
|
2666
|
+
await fs7.writeFile(filePath, content);
|
|
2667
|
+
return true;
|
|
2668
|
+
}
|
|
2531
2669
|
async function writeScaffold(opts, targetDir, scaffoldOpts = {}) {
|
|
2532
2670
|
const dockerMode = scaffoldOpts.dockerMode ?? "rootful";
|
|
2533
2671
|
const devcontainerDir = path8.join(targetDir, ".devcontainer");
|
|
@@ -2562,7 +2700,7 @@ async function writeScaffold(opts, targetDir, scaffoldOpts = {}) {
|
|
|
2562
2700
|
"git-credentials*\ngitconfig\n"
|
|
2563
2701
|
);
|
|
2564
2702
|
const devcontainerJson = buildDevcontainerJson(opts, dockerMode);
|
|
2565
|
-
await
|
|
2703
|
+
await writeIfChanged(
|
|
2566
2704
|
path8.join(devcontainerDir, "devcontainer.json"),
|
|
2567
2705
|
JSON.stringify(devcontainerJson, null, 2) + "\n"
|
|
2568
2706
|
);
|
|
@@ -2592,7 +2730,7 @@ async function writeScaffold(opts, targetDir, scaffoldOpts = {}) {
|
|
|
2592
2730
|
await writePostCreateScript(devcontainerDir, opts);
|
|
2593
2731
|
const composePath = path8.join(devcontainerDir, "compose.yaml");
|
|
2594
2732
|
if (needsCompose(opts)) {
|
|
2595
|
-
await
|
|
2733
|
+
await writeIfChanged(composePath, buildComposeYaml(opts, dockerMode));
|
|
2596
2734
|
} else if (existsSync5(composePath)) {
|
|
2597
2735
|
await fs7.rm(composePath);
|
|
2598
2736
|
}
|
|
@@ -2607,8 +2745,21 @@ async function writeScaffold(opts, targetDir, scaffoldOpts = {}) {
|
|
|
2607
2745
|
const generated = buildCodeWorkspaceJson(opts);
|
|
2608
2746
|
const merged = mergeCodeWorkspace(existingWorkspace, generated);
|
|
2609
2747
|
await fs7.writeFile(workspacePath, JSON.stringify(merged, null, 2) + "\n");
|
|
2748
|
+
const vscodeDir = path8.join(targetDir, ".vscode");
|
|
2749
|
+
const settingsPath = path8.join(vscodeDir, "settings.json");
|
|
2750
|
+
let existingSettings;
|
|
2751
|
+
try {
|
|
2752
|
+
existingSettings = JSON.parse(await fs7.readFile(settingsPath, "utf8"));
|
|
2753
|
+
} catch {
|
|
2754
|
+
existingSettings = void 0;
|
|
2755
|
+
}
|
|
2756
|
+
await fs7.mkdir(vscodeDir, { recursive: true });
|
|
2757
|
+
await fs7.writeFile(
|
|
2758
|
+
settingsPath,
|
|
2759
|
+
JSON.stringify(mergeVscodeSettings(existingSettings), null, 2) + "\n"
|
|
2760
|
+
);
|
|
2610
2761
|
}
|
|
2611
|
-
var APT_PACKAGE_NAME_RE2, FEATURE_REF_RE2, INSTALL_URL_RE2, REPO_URL_RE2, REPO_PATH_RE2, HOME_SUBPATH_RE;
|
|
2762
|
+
var APT_PACKAGE_NAME_RE2, FEATURE_REF_RE2, INSTALL_URL_RE2, REPO_URL_RE2, REPO_PATH_RE2, HOME_SUBPATH_RE, WORKSPACE_ROOT_LABEL, REPO_HOST_EXTENSIONS, ROOT_DENOISE_EXCLUDES;
|
|
2612
2763
|
var init_scaffold = __esm({
|
|
2613
2764
|
"src/create/scaffold.ts"() {
|
|
2614
2765
|
"use strict";
|
|
@@ -2621,6 +2772,23 @@ var init_scaffold = __esm({
|
|
|
2621
2772
|
REPO_URL_RE2 = /^[A-Za-z0-9@:/+_~.#=&?-]+$/;
|
|
2622
2773
|
REPO_PATH_RE2 = /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/;
|
|
2623
2774
|
HOME_SUBPATH_RE = /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/;
|
|
2775
|
+
WORKSPACE_ROOT_LABEL = "\u{1F984} Monoceros";
|
|
2776
|
+
REPO_HOST_EXTENSIONS = {
|
|
2777
|
+
"github.com": [
|
|
2778
|
+
"github.vscode-pull-request-github",
|
|
2779
|
+
"GitHub.vscode-github-actions"
|
|
2780
|
+
],
|
|
2781
|
+
"gitlab.com": ["GitLab.gitlab-workflow"]
|
|
2782
|
+
};
|
|
2783
|
+
ROOT_DENOISE_EXCLUDES = {
|
|
2784
|
+
".devcontainer": true,
|
|
2785
|
+
".monoceros": true,
|
|
2786
|
+
".vscode": true,
|
|
2787
|
+
".gitignore": true,
|
|
2788
|
+
data: true,
|
|
2789
|
+
projects: true,
|
|
2790
|
+
"*.code-workspace": true
|
|
2791
|
+
};
|
|
2624
2792
|
}
|
|
2625
2793
|
});
|
|
2626
2794
|
|
|
@@ -4097,7 +4265,8 @@ function buildStateFile(opts) {
|
|
|
4097
4265
|
schemaVersion: CONFIG_SCHEMA_VERSION,
|
|
4098
4266
|
origin: opts.origin,
|
|
4099
4267
|
monocerosCliVersion: opts.cliVersion,
|
|
4100
|
-
materializedAt: (opts.now ?? /* @__PURE__ */ new Date()).toISOString()
|
|
4268
|
+
materializedAt: (opts.now ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
4269
|
+
...opts.runtimeImage ? { runtimeImage: opts.runtimeImage } : {}
|
|
4101
4270
|
};
|
|
4102
4271
|
}
|
|
4103
4272
|
function stateFilePath(targetDir) {
|
|
@@ -4138,6 +4307,7 @@ function solutionConfigToCreateOptions(config, featureDefaults = {}) {
|
|
|
4138
4307
|
}
|
|
4139
4308
|
const result = {
|
|
4140
4309
|
name: config.name,
|
|
4310
|
+
...config.runtimeVersion !== void 0 ? { runtimeVersion: config.runtimeVersion } : {},
|
|
4141
4311
|
languages: [...config.languages],
|
|
4142
4312
|
// Normalize every services[] entry (curated string or explicit
|
|
4143
4313
|
// object) to the canonical ResolvedService shape. `${VAR}` values
|
|
@@ -4435,12 +4605,22 @@ var init_apply_progress = __esm({
|
|
|
4435
4605
|
FRAME_INTERVAL_MS = 80;
|
|
4436
4606
|
TAIL_LINES = 15;
|
|
4437
4607
|
PHASE_TRIGGERS = [
|
|
4438
|
-
//
|
|
4439
|
-
//
|
|
4440
|
-
|
|
4441
|
-
//
|
|
4442
|
-
//
|
|
4443
|
-
|
|
4608
|
+
// Feature/layer build — distinct phase, often the longest single
|
|
4609
|
+
// step. Image mode runs `docker build`; compose mode runs
|
|
4610
|
+
// `docker compose … build <services>`. We match the build *subcommand*
|
|
4611
|
+
// (a space-delimited ` build`), NOT the substring — the compose `up`
|
|
4612
|
+
// line below carries `-f …devcontainer.build-<n>.yml` and would
|
|
4613
|
+
// otherwise false-match here and swallow the "starting" phase.
|
|
4614
|
+
{
|
|
4615
|
+
pattern: /Start: Run: docker (?:build\b|compose\b.* build\b)/i,
|
|
4616
|
+
label: "building feature layers\u2026"
|
|
4617
|
+
},
|
|
4618
|
+
// Container create/start. Image mode: `docker run` (pulls if needed,
|
|
4619
|
+
// creates, starts). Compose mode: `docker compose … up -d <services>`.
|
|
4620
|
+
{
|
|
4621
|
+
pattern: /Start: Run: docker (?:run\b|compose\b.* up\b)/i,
|
|
4622
|
+
label: "starting container\u2026"
|
|
4623
|
+
},
|
|
4444
4624
|
{ pattern: /Running the postCreateCommand/i, label: "running postCreate\u2026" }
|
|
4445
4625
|
];
|
|
4446
4626
|
}
|
|
@@ -5148,13 +5328,13 @@ var init_runtime_pull_hint = __esm({
|
|
|
5148
5328
|
|
|
5149
5329
|
// src/devcontainer/cli.ts
|
|
5150
5330
|
import { spawn as spawn4 } from "child_process";
|
|
5151
|
-
import { readFileSync as
|
|
5331
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
5152
5332
|
import { createRequire } from "module";
|
|
5153
5333
|
import path13 from "path";
|
|
5154
5334
|
function devcontainerCliPath() {
|
|
5155
5335
|
if (cachedBinaryPath) return cachedBinaryPath;
|
|
5156
5336
|
const pkgJsonPath = require_.resolve("@devcontainers/cli/package.json");
|
|
5157
|
-
const pkg = JSON.parse(
|
|
5337
|
+
const pkg = JSON.parse(readFileSync5(pkgJsonPath, "utf8"));
|
|
5158
5338
|
const binEntry = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.devcontainer ?? "";
|
|
5159
5339
|
if (!binEntry) {
|
|
5160
5340
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
@@ -5726,6 +5906,11 @@ ${sectionLine(label)}
|
|
|
5726
5906
|
await assertSafeTargetDir(targetDir, opts.name);
|
|
5727
5907
|
section("Configuration");
|
|
5728
5908
|
const parsed = await readConfig(ymlPath);
|
|
5909
|
+
if (!parsed.config.runtimeVersion) {
|
|
5910
|
+
throw new Error(
|
|
5911
|
+
`No runtime pinned for '${opts.name}': the yml has no 'runtimeVersion'. Pin it with \`monoceros upgrade ${opts.name} <version>\` (or add \`runtimeVersion: <version>\` to the yml), then re-apply.`
|
|
5912
|
+
);
|
|
5913
|
+
}
|
|
5729
5914
|
const globalConfig = await readMonocerosConfig({ monocerosHome: home });
|
|
5730
5915
|
warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);
|
|
5731
5916
|
const envPath = containerEnvPath(opts.name, home);
|
|
@@ -5837,6 +6022,7 @@ Fix the value in the env file (or the yml).`
|
|
|
5837
6022
|
buildStateFile({
|
|
5838
6023
|
origin: opts.name,
|
|
5839
6024
|
cliVersion: opts.cliVersion,
|
|
6025
|
+
runtimeImage: resolveRuntimeImage(createOpts.runtimeVersion),
|
|
5840
6026
|
...opts.now ? { now: opts.now } : {}
|
|
5841
6027
|
})
|
|
5842
6028
|
);
|
|
@@ -6059,6 +6245,7 @@ var init_apply = __esm({
|
|
|
6059
6245
|
init_schema();
|
|
6060
6246
|
init_state();
|
|
6061
6247
|
init_transform();
|
|
6248
|
+
init_catalog();
|
|
6062
6249
|
init_scaffold();
|
|
6063
6250
|
init_format();
|
|
6064
6251
|
init_ref();
|
|
@@ -6085,7 +6272,7 @@ var CLI_VERSION;
|
|
|
6085
6272
|
var init_version = __esm({
|
|
6086
6273
|
"src/version.ts"() {
|
|
6087
6274
|
"use strict";
|
|
6088
|
-
CLI_VERSION = true ? "1.
|
|
6275
|
+
CLI_VERSION = true ? "1.16.1" : "dev";
|
|
6089
6276
|
}
|
|
6090
6277
|
});
|
|
6091
6278
|
|
|
@@ -6510,6 +6697,7 @@ var init_resolve = __esm({
|
|
|
6510
6697
|
"stop",
|
|
6511
6698
|
"status",
|
|
6512
6699
|
"apply",
|
|
6700
|
+
"upgrade",
|
|
6513
6701
|
"remove",
|
|
6514
6702
|
"restore",
|
|
6515
6703
|
"add-service",
|
|
@@ -6554,6 +6742,12 @@ var init_resolve = __esm({
|
|
|
6554
6742
|
positionals: [containerName],
|
|
6555
6743
|
flags: { "--yes": { type: "boolean", aliases: ["-y"] } }
|
|
6556
6744
|
},
|
|
6745
|
+
upgrade: {
|
|
6746
|
+
// First positional is a container name; the second is a version
|
|
6747
|
+
// string (no suggestions — versions live in the registry).
|
|
6748
|
+
positionals: [containerName, () => []],
|
|
6749
|
+
flags: { "--list": { type: "boolean" } }
|
|
6750
|
+
},
|
|
6557
6751
|
remove: {
|
|
6558
6752
|
positionals: [containerName],
|
|
6559
6753
|
flags: {
|
|
@@ -6704,6 +6898,11 @@ function generateComposedYml(name, composed, lookupManifest, repoUrls = [], port
|
|
|
6704
6898
|
lines.push("");
|
|
6705
6899
|
lines.push("schemaVersion: 1");
|
|
6706
6900
|
lines.push(`name: ${name}`);
|
|
6901
|
+
lines.push(
|
|
6902
|
+
"# Pinned runtime image version. Reused on every apply; change it with"
|
|
6903
|
+
);
|
|
6904
|
+
lines.push("# `monoceros upgrade <name> [version]`.");
|
|
6905
|
+
lines.push(`runtimeVersion: ${DEFAULT_RUNTIME_VERSION}`);
|
|
6707
6906
|
lines.push("");
|
|
6708
6907
|
if (composed.languages.length > 0) {
|
|
6709
6908
|
pushSectionHeader(
|
|
@@ -6802,6 +7001,11 @@ function generateDocumentedYml(name, catalog, lookupManifest, repoUrls = [], por
|
|
|
6802
7001
|
lines.push("");
|
|
6803
7002
|
lines.push("schemaVersion: 1");
|
|
6804
7003
|
lines.push(`name: ${name}`);
|
|
7004
|
+
lines.push(
|
|
7005
|
+
"# Pinned runtime image version. Reused on every apply; change it with"
|
|
7006
|
+
);
|
|
7007
|
+
lines.push("# `monoceros upgrade <name> [version]`.");
|
|
7008
|
+
lines.push(`runtimeVersion: ${DEFAULT_RUNTIME_VERSION}`);
|
|
6805
7009
|
lines.push("");
|
|
6806
7010
|
if (byCategory.language.length > 0) {
|
|
6807
7011
|
pushSectionHeader(
|
|
@@ -7802,6 +8006,8 @@ async function runRemove(opts) {
|
|
|
7802
8006
|
logger,
|
|
7803
8007
|
exec: dockerExec
|
|
7804
8008
|
});
|
|
8009
|
+
const ideVolumes = ideStateVolumes(opts.name).map((v) => v.volume);
|
|
8010
|
+
await dockerExec(["volume", "rm", "-f", ...ideVolumes]);
|
|
7805
8011
|
let backupPath = null;
|
|
7806
8012
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
7807
8013
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -7896,6 +8102,7 @@ var init_remove = __esm({
|
|
|
7896
8102
|
init_paths();
|
|
7897
8103
|
init_schema();
|
|
7898
8104
|
init_compose();
|
|
8105
|
+
init_scaffold();
|
|
7899
8106
|
init_proxy();
|
|
7900
8107
|
init_dynamic();
|
|
7901
8108
|
}
|
|
@@ -9063,12 +9270,165 @@ var init_tunnel = __esm({
|
|
|
9063
9270
|
}
|
|
9064
9271
|
});
|
|
9065
9272
|
|
|
9273
|
+
// src/upgrade/index.ts
|
|
9274
|
+
import { existsSync as existsSync14, promises as fs17 } from "fs";
|
|
9275
|
+
import { consola as consola34 } from "consola";
|
|
9276
|
+
async function fetchRuntimeVersions() {
|
|
9277
|
+
const tokenUrl = `https://ghcr.io/token?service=ghcr.io&scope=repository:${RUNTIME_REPO}:pull`;
|
|
9278
|
+
const tokenRes = await fetch(tokenUrl);
|
|
9279
|
+
if (!tokenRes.ok) {
|
|
9280
|
+
throw new Error(
|
|
9281
|
+
`Could not get a GHCR token (HTTP ${tokenRes.status}). Pass an explicit version to skip the lookup: \`monoceros upgrade <name> <version>\`.`
|
|
9282
|
+
);
|
|
9283
|
+
}
|
|
9284
|
+
const { token } = await tokenRes.json();
|
|
9285
|
+
if (!token) throw new Error("GHCR token response contained no token.");
|
|
9286
|
+
const tagsRes = await fetch(`https://ghcr.io/v2/${RUNTIME_REPO}/tags/list`, {
|
|
9287
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
9288
|
+
});
|
|
9289
|
+
if (!tagsRes.ok) {
|
|
9290
|
+
throw new Error(
|
|
9291
|
+
`Could not list runtime versions (HTTP ${tagsRes.status}). Pass an explicit version: \`monoceros upgrade <name> <version>\`.`
|
|
9292
|
+
);
|
|
9293
|
+
}
|
|
9294
|
+
const { tags } = await tagsRes.json();
|
|
9295
|
+
return (tags ?? []).filter((t) => VERSION_RE.test(t)).sort(compareRuntimeVersions);
|
|
9296
|
+
}
|
|
9297
|
+
function setRuntimeVersion(yml, version) {
|
|
9298
|
+
if (/^runtimeVersion:.*$/m.test(yml)) {
|
|
9299
|
+
return yml.replace(/^runtimeVersion:.*$/m, `runtimeVersion: ${version}`);
|
|
9300
|
+
}
|
|
9301
|
+
if (/^schemaVersion:.*$/m.test(yml)) {
|
|
9302
|
+
return yml.replace(
|
|
9303
|
+
/^(schemaVersion:.*)$/m,
|
|
9304
|
+
`$1
|
|
9305
|
+
runtimeVersion: ${version}`
|
|
9306
|
+
);
|
|
9307
|
+
}
|
|
9308
|
+
return `runtimeVersion: ${version}
|
|
9309
|
+
${yml}`;
|
|
9310
|
+
}
|
|
9311
|
+
async function runUpgrade(opts) {
|
|
9312
|
+
const home = opts.monocerosHome ?? monocerosHome();
|
|
9313
|
+
const logger = opts.logger ?? consola34;
|
|
9314
|
+
const fetchVersions = opts.fetchVersions ?? fetchRuntimeVersions;
|
|
9315
|
+
if (opts.list) {
|
|
9316
|
+
const versions = await fetchVersions();
|
|
9317
|
+
if (versions.length === 0) {
|
|
9318
|
+
logger.info("No published runtime versions found.");
|
|
9319
|
+
return 0;
|
|
9320
|
+
}
|
|
9321
|
+
logger.info(
|
|
9322
|
+
`Available runtime versions (latest last):
|
|
9323
|
+
${versions.join("\n ")}`
|
|
9324
|
+
);
|
|
9325
|
+
return 0;
|
|
9326
|
+
}
|
|
9327
|
+
if (!opts.name) {
|
|
9328
|
+
throw new Error(
|
|
9329
|
+
"Usage: `monoceros upgrade <name> [version]` (or `monoceros upgrade --list`)."
|
|
9330
|
+
);
|
|
9331
|
+
}
|
|
9332
|
+
const ymlPath = containerConfigPath(opts.name, home);
|
|
9333
|
+
if (!existsSync14(ymlPath)) {
|
|
9334
|
+
throw new Error(
|
|
9335
|
+
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
9336
|
+
);
|
|
9337
|
+
}
|
|
9338
|
+
let target = opts.version;
|
|
9339
|
+
if (target !== void 0 && !VERSION_RE.test(target)) {
|
|
9340
|
+
throw new Error(
|
|
9341
|
+
`Invalid version ${JSON.stringify(target)}. Expected an exact version like '1.1.0'.`
|
|
9342
|
+
);
|
|
9343
|
+
}
|
|
9344
|
+
if (target === void 0) {
|
|
9345
|
+
const versions = await fetchVersions();
|
|
9346
|
+
target = versions[versions.length - 1];
|
|
9347
|
+
if (!target) {
|
|
9348
|
+
throw new Error("Could not determine the latest runtime version.");
|
|
9349
|
+
}
|
|
9350
|
+
logger.info(`Latest published runtime version: ${target}`);
|
|
9351
|
+
}
|
|
9352
|
+
const raw = await fs17.readFile(ymlPath, "utf8");
|
|
9353
|
+
const updated = setRuntimeVersion(raw, target);
|
|
9354
|
+
if (updated === raw) {
|
|
9355
|
+
logger.info(`'${opts.name}' is already pinned to runtime ${target}.`);
|
|
9356
|
+
} else {
|
|
9357
|
+
await fs17.writeFile(ymlPath, updated);
|
|
9358
|
+
logger.success(`Pinned '${opts.name}' to runtime ${target}. Re-applying\u2026`);
|
|
9359
|
+
}
|
|
9360
|
+
const apply = opts.applyRunner ?? runApply;
|
|
9361
|
+
const result = await apply({
|
|
9362
|
+
name: opts.name,
|
|
9363
|
+
cliVersion: opts.cliVersion,
|
|
9364
|
+
monocerosHome: home
|
|
9365
|
+
});
|
|
9366
|
+
return result.containerExitCode;
|
|
9367
|
+
}
|
|
9368
|
+
var RUNTIME_REPO, VERSION_RE;
|
|
9369
|
+
var init_upgrade = __esm({
|
|
9370
|
+
"src/upgrade/index.ts"() {
|
|
9371
|
+
"use strict";
|
|
9372
|
+
init_paths();
|
|
9373
|
+
init_catalog();
|
|
9374
|
+
init_apply();
|
|
9375
|
+
RUNTIME_REPO = "getmonoceros/monoceros-runtime";
|
|
9376
|
+
VERSION_RE = /^\d+\.\d+\.\d+$/;
|
|
9377
|
+
}
|
|
9378
|
+
});
|
|
9379
|
+
|
|
9380
|
+
// src/commands/upgrade.ts
|
|
9381
|
+
import { defineCommand as defineCommand30 } from "citty";
|
|
9382
|
+
var upgradeCommand;
|
|
9383
|
+
var init_upgrade2 = __esm({
|
|
9384
|
+
"src/commands/upgrade.ts"() {
|
|
9385
|
+
"use strict";
|
|
9386
|
+
init_upgrade();
|
|
9387
|
+
init_version();
|
|
9388
|
+
init_dispatch();
|
|
9389
|
+
upgradeCommand = defineCommand30({
|
|
9390
|
+
meta: {
|
|
9391
|
+
name: "upgrade",
|
|
9392
|
+
group: "lifecycle",
|
|
9393
|
+
description: "Pin a container to a newer runtime image version and re-apply. `monoceros upgrade <name>` pins to the latest published version; `monoceros upgrade <name> <version>` pins to an exact one; `monoceros upgrade --list` lists available versions."
|
|
9394
|
+
},
|
|
9395
|
+
args: {
|
|
9396
|
+
name: {
|
|
9397
|
+
type: "positional",
|
|
9398
|
+
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
9399
|
+
required: false
|
|
9400
|
+
},
|
|
9401
|
+
version: {
|
|
9402
|
+
type: "positional",
|
|
9403
|
+
description: "Exact runtime version to pin (e.g. 1.1.0). Omit to use the latest published version.",
|
|
9404
|
+
required: false
|
|
9405
|
+
},
|
|
9406
|
+
list: {
|
|
9407
|
+
type: "boolean",
|
|
9408
|
+
description: "List available runtime versions and exit, changing nothing.",
|
|
9409
|
+
default: false
|
|
9410
|
+
}
|
|
9411
|
+
},
|
|
9412
|
+
run({ args }) {
|
|
9413
|
+
return dispatch(
|
|
9414
|
+
() => runUpgrade({
|
|
9415
|
+
...args.name ? { name: args.name } : {},
|
|
9416
|
+
...args.version ? { version: args.version } : {},
|
|
9417
|
+
list: args.list,
|
|
9418
|
+
cliVersion: CLI_VERSION
|
|
9419
|
+
})
|
|
9420
|
+
);
|
|
9421
|
+
}
|
|
9422
|
+
});
|
|
9423
|
+
}
|
|
9424
|
+
});
|
|
9425
|
+
|
|
9066
9426
|
// src/main.ts
|
|
9067
9427
|
var main_exports = {};
|
|
9068
9428
|
__export(main_exports, {
|
|
9069
9429
|
main: () => main
|
|
9070
9430
|
});
|
|
9071
|
-
import { defineCommand as
|
|
9431
|
+
import { defineCommand as defineCommand31 } from "citty";
|
|
9072
9432
|
var main;
|
|
9073
9433
|
var init_main = __esm({
|
|
9074
9434
|
"src/main.ts"() {
|
|
@@ -9102,8 +9462,9 @@ var init_main = __esm({
|
|
|
9102
9462
|
init_status();
|
|
9103
9463
|
init_stop();
|
|
9104
9464
|
init_tunnel();
|
|
9465
|
+
init_upgrade2();
|
|
9105
9466
|
init_version();
|
|
9106
|
-
main =
|
|
9467
|
+
main = defineCommand31({
|
|
9107
9468
|
meta: {
|
|
9108
9469
|
name: "monoceros",
|
|
9109
9470
|
version: CLI_VERSION,
|
|
@@ -9119,6 +9480,7 @@ var init_main = __esm({
|
|
|
9119
9480
|
stop: stopCommand,
|
|
9120
9481
|
status: statusCommand,
|
|
9121
9482
|
apply: applyCommand,
|
|
9483
|
+
upgrade: upgradeCommand,
|
|
9122
9484
|
remove: removeCommand,
|
|
9123
9485
|
restore: restoreCommand,
|
|
9124
9486
|
"add-service": addServiceCommand,
|