@plexus-ms/cli 0.5.0 → 0.7.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/plx.js +277 -71
- package/package.json +2 -2
package/dist/plx.js
CHANGED
|
@@ -7754,7 +7754,7 @@ function realIo(prompt) {
|
|
|
7754
7754
|
stdout: process.stdout,
|
|
7755
7755
|
stderr: process.stderr,
|
|
7756
7756
|
prompt: prompt && isInteractive() ? prompt : void 0,
|
|
7757
|
-
exec: (command, args, options = {}) => new Promise((
|
|
7757
|
+
exec: (command, args, options = {}) => new Promise((resolve10, reject) => {
|
|
7758
7758
|
const child = spawn(command, args, {
|
|
7759
7759
|
cwd: options.cwd,
|
|
7760
7760
|
env: options.env,
|
|
@@ -7777,7 +7777,7 @@ function realIo(prompt) {
|
|
|
7777
7777
|
child.on("error", reject);
|
|
7778
7778
|
child.on("close", (code) => {
|
|
7779
7779
|
if (code === 0)
|
|
7780
|
-
|
|
7780
|
+
resolve10(stdout);
|
|
7781
7781
|
else
|
|
7782
7782
|
reject(new ExecError(render(command, args), code, stderr));
|
|
7783
7783
|
});
|
|
@@ -7808,8 +7808,6 @@ function describe(step) {
|
|
|
7808
7808
|
switch (step.kind) {
|
|
7809
7809
|
case "run":
|
|
7810
7810
|
return `$ ${step.cwd ? `(cd ${step.cwd} && ` : ""}${render(step.command, step.args)}${step.cwd ? ")" : ""}`;
|
|
7811
|
-
case "ssh":
|
|
7812
|
-
return `$ ${render("ssh", [step.host, "bash", "-s"])} <<'EOF'`;
|
|
7813
7811
|
case "write":
|
|
7814
7812
|
return `> ${step.path} (${Buffer.byteLength(step.content)} bytes)`;
|
|
7815
7813
|
}
|
|
@@ -7818,9 +7816,6 @@ function body(step) {
|
|
|
7818
7816
|
switch (step.kind) {
|
|
7819
7817
|
case "run":
|
|
7820
7818
|
return step.input;
|
|
7821
|
-
case "ssh":
|
|
7822
|
-
return `${step.script.trimEnd()}
|
|
7823
|
-
EOF`;
|
|
7824
7819
|
case "write":
|
|
7825
7820
|
return step.content;
|
|
7826
7821
|
}
|
|
@@ -8110,8 +8105,6 @@ var Context = class {
|
|
|
8110
8105
|
switch (step.kind) {
|
|
8111
8106
|
case "run":
|
|
8112
8107
|
return this.io.exec(step.command, step.args, { cwd: step.cwd, input: step.input, inherit });
|
|
8113
|
-
case "ssh":
|
|
8114
|
-
return this.io.exec("ssh", [step.host, "bash", "-s"], { input: step.script, inherit });
|
|
8115
8108
|
case "write":
|
|
8116
8109
|
await this.io.writeFile(step.path, step.content);
|
|
8117
8110
|
return void 0;
|
|
@@ -8133,6 +8126,8 @@ function indent(text) {
|
|
|
8133
8126
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
8134
8127
|
var ANSWERS_FILE = ".copier-answers.yml";
|
|
8135
8128
|
var TEMPLATE_FILES = ["copier.yml", "copier.yaml"];
|
|
8129
|
+
var COPIER_VERSION = "9.19.1";
|
|
8130
|
+
var copierVia = (args) => ["exec", `copier@${COPIER_VERSION}`, "--", "copier", ...args];
|
|
8136
8131
|
function parseAnswers(text) {
|
|
8137
8132
|
const data = (0, import_yaml3.parse)(text);
|
|
8138
8133
|
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
@@ -8145,8 +8140,9 @@ function parseAnswers(text) {
|
|
|
8145
8140
|
};
|
|
8146
8141
|
}
|
|
8147
8142
|
|
|
8148
|
-
// ../lib/dist/
|
|
8149
|
-
|
|
8143
|
+
// ../lib/dist/deploy.js
|
|
8144
|
+
import { randomBytes } from "node:crypto";
|
|
8145
|
+
import { tmpdir } from "node:os";
|
|
8150
8146
|
import { join as join5 } from "node:path";
|
|
8151
8147
|
|
|
8152
8148
|
// ../lib/dist/train.js
|
|
@@ -8289,7 +8285,174 @@ pass the version explicitly (--train ${train.last}) to finish the half-tagged tr
|
|
|
8289
8285
|
var UsageError = class extends Error {
|
|
8290
8286
|
};
|
|
8291
8287
|
|
|
8288
|
+
// ../lib/dist/deploy.js
|
|
8289
|
+
var PLACEHOLDER = /\$\{PLEXUS_DEPLOYMENT_IMAGE(:[^}]*)?\}/g;
|
|
8290
|
+
var INCOMING = ".incoming";
|
|
8291
|
+
function renderCompose(text, image, path3) {
|
|
8292
|
+
const has = PLACEHOLDER.test(text);
|
|
8293
|
+
PLACEHOLDER.lastIndex = 0;
|
|
8294
|
+
if (image) {
|
|
8295
|
+
if (!has)
|
|
8296
|
+
throw new UsageError(`${path3} does not reference \${PLEXUS_DEPLOYMENT_IMAGE}; an image ref was given but nothing would use it (\xA7 3 PLX-App)`);
|
|
8297
|
+
return text.replace(PLACEHOLDER, image);
|
|
8298
|
+
}
|
|
8299
|
+
if (has)
|
|
8300
|
+
throw new UsageError(`${path3} references \${PLEXUS_DEPLOYMENT_IMAGE} but no image ref was given`);
|
|
8301
|
+
return text;
|
|
8302
|
+
}
|
|
8303
|
+
var q = (word) => `'${word.replaceAll("'", "'\\''")}'`;
|
|
8304
|
+
var Remote = class {
|
|
8305
|
+
socket = join5(tmpdir(), `plx-deploy-${randomBytes(4).toString("hex")}.sock`);
|
|
8306
|
+
ctx;
|
|
8307
|
+
host;
|
|
8308
|
+
constructor(ctx, host) {
|
|
8309
|
+
this.ctx = ctx;
|
|
8310
|
+
this.host = host;
|
|
8311
|
+
}
|
|
8312
|
+
args(command) {
|
|
8313
|
+
return [
|
|
8314
|
+
"-o",
|
|
8315
|
+
"ControlMaster=auto",
|
|
8316
|
+
"-o",
|
|
8317
|
+
`ControlPath=${this.socket}`,
|
|
8318
|
+
"-o",
|
|
8319
|
+
"ControlPersist=60",
|
|
8320
|
+
"-o",
|
|
8321
|
+
"StrictHostKeyChecking=accept-new",
|
|
8322
|
+
this.host,
|
|
8323
|
+
"--",
|
|
8324
|
+
command
|
|
8325
|
+
];
|
|
8326
|
+
}
|
|
8327
|
+
/** An effect on the host: narrated, performed unless --dry-run. */
|
|
8328
|
+
exec(command, input) {
|
|
8329
|
+
return this.ctx.apply([run("ssh", this.args(command), input === void 0 ? {} : { input })]);
|
|
8330
|
+
}
|
|
8331
|
+
/**
|
|
8332
|
+
* A read from the host: its output, or undefined under --dry-run, where no
|
|
8333
|
+
* host is reached. A read that is a step of the procedure (`show`) is
|
|
8334
|
+
* printed by the dry run like an effect, so the operator copies it too.
|
|
8335
|
+
*/
|
|
8336
|
+
async read(command, show = false) {
|
|
8337
|
+
if (this.ctx.dryRun) {
|
|
8338
|
+
this.ctx.say(`${show ? "[dry-run] $" : "?"} ssh ${this.host} -- ${q(command)}`, show ? 0 : 1);
|
|
8339
|
+
return void 0;
|
|
8340
|
+
}
|
|
8341
|
+
return (await this.ctx.read("ssh", this.args(command))).trimEnd();
|
|
8342
|
+
}
|
|
8343
|
+
/** A check on the host: whether the command succeeds, or undefined under --dry-run. */
|
|
8344
|
+
async test(command, show = false) {
|
|
8345
|
+
try {
|
|
8346
|
+
return await this.read(command, show) === void 0 ? void 0 : true;
|
|
8347
|
+
} catch (error) {
|
|
8348
|
+
if (error instanceof ExecError)
|
|
8349
|
+
return false;
|
|
8350
|
+
throw error;
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
/** Close the shared connection; nothing to close when no call was made. */
|
|
8354
|
+
close() {
|
|
8355
|
+
return this.ctx.apply([run("ssh", ["-o", `ControlPath=${this.socket}`, "-O", "exit", this.host])]);
|
|
8356
|
+
}
|
|
8357
|
+
};
|
|
8358
|
+
async function deploy(ctx, options) {
|
|
8359
|
+
const { host, tenant, app } = options;
|
|
8360
|
+
const dir = options.dir ?? ".";
|
|
8361
|
+
const image = options.image || void 0;
|
|
8362
|
+
const appDir = options.appDir || `/opt/stacks/${tenant}/${app}`;
|
|
8363
|
+
const source = options.source || `apps/${app}`;
|
|
8364
|
+
const retries = options.retries ?? 30;
|
|
8365
|
+
let compose;
|
|
8366
|
+
let text = "";
|
|
8367
|
+
for (const candidate of [`${source}/docker/compose.yaml`, `${source}/compose.yaml`]) {
|
|
8368
|
+
try {
|
|
8369
|
+
text = await ctx.readFile(join5(dir, candidate));
|
|
8370
|
+
compose = candidate;
|
|
8371
|
+
break;
|
|
8372
|
+
} catch {
|
|
8373
|
+
}
|
|
8374
|
+
}
|
|
8375
|
+
if (compose === void 0)
|
|
8376
|
+
throw new UsageError(`no compose.yaml under ${source}; run from the app's source repo (\xA7 2 PLX-App)`);
|
|
8377
|
+
const rendered = renderCompose(text, image, compose);
|
|
8378
|
+
ctx.say(`\u2192 deploying ${image ?? "<image pinned in compose.yaml>"}`);
|
|
8379
|
+
ctx.say(` host: ${host}`);
|
|
8380
|
+
ctx.say(` dir: ${appDir}`);
|
|
8381
|
+
ctx.say(` compose: ${compose}`);
|
|
8382
|
+
ctx.say(` health: ${options.healthUrl || "<resolved on host>"}`);
|
|
8383
|
+
const remote = new Remote(ctx, host);
|
|
8384
|
+
const D = q(appDir);
|
|
8385
|
+
const dc = `cd ${D} && docker compose`;
|
|
8386
|
+
const dci = `${dc} --project-directory . -f ${INCOMING}/compose.yaml`;
|
|
8387
|
+
const liveImage = `${dc} ps -q web 2>/dev/null | head -1 | xargs -r docker inspect --format '{{.Config.Image}}' 2>/dev/null || true`;
|
|
8388
|
+
const result = { host, appDir, compose, image, healthy: false, rolledBack: false };
|
|
8389
|
+
let staged = false;
|
|
8390
|
+
try {
|
|
8391
|
+
await remote.exec(`rm -rf ${D}/${INCOMING} && mkdir -p ${D}/${INCOMING} && cat > ${D}/${INCOMING}/compose.yaml`, rendered);
|
|
8392
|
+
staged = true;
|
|
8393
|
+
result.previous = await remote.read(liveImage) || void 0;
|
|
8394
|
+
ctx.say(` previous: ${result.previous ?? (ctx.dryRun ? "<read on host>" : "<none>")}`);
|
|
8395
|
+
await remote.exec(`${dci} pull`);
|
|
8396
|
+
const migrate = await remote.test(`${dci} --profile migrate config --services 2>/dev/null | grep -qx migrate`);
|
|
8397
|
+
if (migrate === void 0)
|
|
8398
|
+
ctx.say(" (if compose.yaml declares a migrate service:)");
|
|
8399
|
+
if (migrate !== false)
|
|
8400
|
+
await remote.exec(`${dci} run --rm migrate </dev/null`);
|
|
8401
|
+
const hadPrev = await remote.test(`test -f ${D}/compose.yaml`);
|
|
8402
|
+
if (hadPrev !== false)
|
|
8403
|
+
await remote.exec(`cd ${D} && cp -p compose.yaml ${INCOMING}/compose.yaml.prev`);
|
|
8404
|
+
await remote.exec(`cd ${D} && mv ${INCOMING}/compose.yaml compose.yaml`);
|
|
8405
|
+
await remote.exec(`${dc} up -d`);
|
|
8406
|
+
const url = options.healthUrl || await resolveHealthUrl(remote, D, dc, ctx);
|
|
8407
|
+
const poll = `cd ${D} && for _ in $(seq 1 ${retries}); do curl -fsS --max-time 5 -o /dev/null ${q(url)} && exit 0; sleep 2; done; exit 1`;
|
|
8408
|
+
const started = Date.now();
|
|
8409
|
+
const healthy = await remote.test(poll, true);
|
|
8410
|
+
if (healthy !== false) {
|
|
8411
|
+
result.live = await remote.read(liveImage) || image;
|
|
8412
|
+
result.healthy = true;
|
|
8413
|
+
if (ctx.dryRun)
|
|
8414
|
+
ctx.say(" (on failure: restore the previous compose.yaml, up -d, poll again)");
|
|
8415
|
+
else
|
|
8416
|
+
ctx.done(`${result.live ?? "the app"} is live and healthy`);
|
|
8417
|
+
return result;
|
|
8418
|
+
}
|
|
8419
|
+
ctx.say(`\u2717 healthcheck failed after ${Math.round((Date.now() - started) / 1e3)}s; last lines from the app:`);
|
|
8420
|
+
const logs = await remote.read(`${dc} logs --no-color --tail 50 web 2>&1 || true`);
|
|
8421
|
+
if (logs)
|
|
8422
|
+
ctx.say(logs.replace(/^/gm, " "));
|
|
8423
|
+
const differs = hadPrev && await remote.test(`cd ${D} && ! cmp -s compose.yaml ${INCOMING}/compose.yaml.prev`);
|
|
8424
|
+
if (!differs) {
|
|
8425
|
+
ctx.done("nothing to roll back to (first deploy, or the same run shape re-deployed); manual intervention needed", false);
|
|
8426
|
+
return result;
|
|
8427
|
+
}
|
|
8428
|
+
await remote.exec(`cd ${D} && cp -p ${INCOMING}/compose.yaml.prev compose.yaml`);
|
|
8429
|
+
ctx.say(`\u21A9 rolling back to ${result.previous ?? "the previous compose.yaml"}`);
|
|
8430
|
+
await remote.exec(`${dc} up -d`);
|
|
8431
|
+
result.rolledBack = true;
|
|
8432
|
+
if (await remote.test(poll)) {
|
|
8433
|
+
result.live = result.previous;
|
|
8434
|
+
ctx.done(`rolled back to ${result.previous ?? "the previous compose.yaml"}`);
|
|
8435
|
+
} else {
|
|
8436
|
+
ctx.done("rollback also unhealthy; manual intervention needed", false);
|
|
8437
|
+
}
|
|
8438
|
+
return result;
|
|
8439
|
+
} finally {
|
|
8440
|
+
if (staged)
|
|
8441
|
+
await remote.exec(`rm -rf ${D}/${INCOMING}`).catch(() => void 0);
|
|
8442
|
+
await remote.close().catch(() => void 0);
|
|
8443
|
+
}
|
|
8444
|
+
}
|
|
8445
|
+
async function resolveHealthUrl(remote, D, dc, ctx) {
|
|
8446
|
+
const port = await remote.read(`sed -n 's/^PLEXUS_LOOPBACK_PORT=//p' ${D}/.env 2>/dev/null | tail -1`);
|
|
8447
|
+
const path3 = await remote.read(`${dc} ps -q web 2>/dev/null | head -1 | xargs -r docker inspect --format '{{ index .Config.Labels "plexus.healthz" }}' 2>/dev/null || true`);
|
|
8448
|
+
const url = ctx.dryRun ? "http://127.0.0.1:${PLEXUS_LOOPBACK_PORT:-3000}${plexus.healthz:-/healthz}" : `http://127.0.0.1:${port || "3000"}${path3 || "/healthz"}`;
|
|
8449
|
+
ctx.say(` health: ${url}`);
|
|
8450
|
+
return url;
|
|
8451
|
+
}
|
|
8452
|
+
|
|
8292
8453
|
// ../lib/dist/deployTargets.js
|
|
8454
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
8455
|
+
import { join as join6 } from "node:path";
|
|
8293
8456
|
var VARS_DIRS = ["group_vars", "host_vars"];
|
|
8294
8457
|
function parseInventoryFile(path3, text) {
|
|
8295
8458
|
const data = (0, import_yaml4.parse)(text);
|
|
@@ -8318,7 +8481,7 @@ async function readDeployTargets(ctx, inventory) {
|
|
|
8318
8481
|
const files = [];
|
|
8319
8482
|
let found = false;
|
|
8320
8483
|
for (const sub of VARS_DIRS) {
|
|
8321
|
-
const dir =
|
|
8484
|
+
const dir = join6(inventory, sub);
|
|
8322
8485
|
const entries = await ctx.readDir(dir).catch(() => void 0);
|
|
8323
8486
|
if (entries === void 0)
|
|
8324
8487
|
continue;
|
|
@@ -8326,7 +8489,7 @@ async function readDeployTargets(ctx, inventory) {
|
|
|
8326
8489
|
for (const e of entries) {
|
|
8327
8490
|
if (e.directory || !/\.ya?ml$/.test(e.name))
|
|
8328
8491
|
continue;
|
|
8329
|
-
const path3 =
|
|
8492
|
+
const path3 = join6(dir, e.name);
|
|
8330
8493
|
files.push(parseInventoryFile(path3, await ctx.readFile(path3)));
|
|
8331
8494
|
}
|
|
8332
8495
|
}
|
|
@@ -8356,7 +8519,7 @@ function isNotable(dir) {
|
|
|
8356
8519
|
}
|
|
8357
8520
|
|
|
8358
8521
|
// ../lib/dist/overview.js
|
|
8359
|
-
import { join as
|
|
8522
|
+
import { join as join7 } from "node:path";
|
|
8360
8523
|
var PRUNED = /* @__PURE__ */ new Set([
|
|
8361
8524
|
"node_modules",
|
|
8362
8525
|
".hg",
|
|
@@ -8404,7 +8567,7 @@ async function walk(ctx, path3, name, options) {
|
|
|
8404
8567
|
}
|
|
8405
8568
|
const subdirs = entries.filter((e) => e.directory && e.name !== ".git" && !PRUNED.has(e.name)).map((e) => e.name).sort();
|
|
8406
8569
|
for (const sub of subdirs) {
|
|
8407
|
-
const child = await walk(ctx,
|
|
8570
|
+
const child = await walk(ctx, join7(path3, sub), sub, options);
|
|
8408
8571
|
if (isNotable(child) || child.children.length > 0)
|
|
8409
8572
|
dir.children.push(child);
|
|
8410
8573
|
}
|
|
@@ -8412,7 +8575,7 @@ async function walk(ctx, path3, name, options) {
|
|
|
8412
8575
|
}
|
|
8413
8576
|
async function facet(ctx, dir, file, read) {
|
|
8414
8577
|
try {
|
|
8415
|
-
read(await ctx.readFile(
|
|
8578
|
+
read(await ctx.readFile(join7(dir.path, file)));
|
|
8416
8579
|
} catch (error) {
|
|
8417
8580
|
dir.problems ??= [];
|
|
8418
8581
|
dir.problems.push(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -8451,12 +8614,12 @@ async function templateFacet(ctx, cwd) {
|
|
|
8451
8614
|
}
|
|
8452
8615
|
|
|
8453
8616
|
// ../lib/dist/propagate.js
|
|
8454
|
-
import { join as
|
|
8617
|
+
import { join as join8, relative as relative3 } from "node:path";
|
|
8455
8618
|
var isClean = (s) => s.conflicted.length === 0 && s.failed.length === 0 && s.skipped.length === 0;
|
|
8456
8619
|
var PREVIEW_BASE = "ship-base";
|
|
8457
8620
|
async function propagate(ctx, workspace, train) {
|
|
8458
8621
|
const summary = { train, preview: ctx.dryRun, clean: [], conflicted: [], failed: [], skipped: [] };
|
|
8459
|
-
const upstream =
|
|
8622
|
+
const upstream = join8(workspace, UPSTREAM);
|
|
8460
8623
|
const tree = await overview(ctx, workspace);
|
|
8461
8624
|
const upstreamDir = tree.children.find((c) => c.path === upstream);
|
|
8462
8625
|
const presets = TRAIN_REPOS.filter((r) => upstreamDir?.children.some((c) => c.name === r && c.template));
|
|
@@ -8473,7 +8636,7 @@ async function propagate(ctx, workspace, train) {
|
|
|
8473
8636
|
const cloneOf = async (consumer) => {
|
|
8474
8637
|
let clone = clones.get(consumer.path);
|
|
8475
8638
|
if (!clone) {
|
|
8476
|
-
clone =
|
|
8639
|
+
clone = join8(scratch ?? "", consumer.rel);
|
|
8477
8640
|
await ctx.stage(run("git", ["clone", "--quiet", consumer.path, clone]));
|
|
8478
8641
|
await ctx.stage(run("git", ["tag", PREVIEW_BASE], { cwd: clone }));
|
|
8479
8642
|
clones.set(consumer.path, clone);
|
|
@@ -8500,7 +8663,7 @@ async function propagate(ctx, workspace, train) {
|
|
|
8500
8663
|
if (sites.length === 0)
|
|
8501
8664
|
ctx.say(`\u2022 no consumers of ${presetSource(preset)} found under ${workspace}`);
|
|
8502
8665
|
for (const [consumer, site] of sites)
|
|
8503
|
-
await updateSite(ctx, consumer, site,
|
|
8666
|
+
await updateSite(ctx, consumer, site, join8(upstream, preset), summary, cloneOf, previewCommit);
|
|
8504
8667
|
}
|
|
8505
8668
|
await refreshLockfiles(ctx, active, summary, clones);
|
|
8506
8669
|
await bumpConsumerRefs(ctx, consumers, train);
|
|
@@ -8517,17 +8680,15 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8517
8680
|
if (ctx.dryRun) {
|
|
8518
8681
|
ctx.say(`\u2192 ${site.rel}: copier update (preview from ${localPreset}@main)`);
|
|
8519
8682
|
repo = await cloneOf(consumer);
|
|
8520
|
-
target =
|
|
8521
|
-
const answers = await ctx.readFile(
|
|
8522
|
-
await ctx.stage(write(
|
|
8683
|
+
target = join8(repo, relative3(consumer.path, site.path));
|
|
8684
|
+
const answers = await ctx.readFile(join8(site.path, ANSWERS_FILE));
|
|
8685
|
+
await ctx.stage(write(join8(target, ANSWERS_FILE), answers.replace(/^_src_path: .*$/m, `_src_path: ${localPreset}`)));
|
|
8523
8686
|
await previewCommit(repo, `plx preview: ${site.rel} reads ${localPreset} from the local checkout`);
|
|
8524
|
-
await ctx.stage(run("
|
|
8525
|
-
cwd: target
|
|
8526
|
-
}));
|
|
8687
|
+
await ctx.stage(run("mise", copierVia(["update", "--skip-answered", "--trust", "--defaults", "--skip-tasks", "--vcs-ref", "main"]), { cwd: target }));
|
|
8527
8688
|
} else {
|
|
8528
8689
|
ctx.say(`\u2192 ${site.rel}: copier update`);
|
|
8529
8690
|
const args = ["update", "--skip-answered", "--trust", ...ctx.yes ? ["--defaults"] : []];
|
|
8530
|
-
await ctx.apply([run("
|
|
8691
|
+
await ctx.apply([run("mise", copierVia(args), { cwd: target, interactive: true })]);
|
|
8531
8692
|
}
|
|
8532
8693
|
} catch (error) {
|
|
8533
8694
|
ctx.say(` \u2717 FAILED \u2014 copier update exited nonzero${detail(error)}`);
|
|
@@ -8538,7 +8699,7 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8538
8699
|
let conflict = hasUnmergedEntries(status);
|
|
8539
8700
|
const changed = (await ctx.read("git", ["diff", "--name-only"], { cwd: repo })).split("\n").filter(Boolean);
|
|
8540
8701
|
for (const f of changed) {
|
|
8541
|
-
const text = await ctx.readFile(
|
|
8702
|
+
const text = await ctx.readFile(join8(repo, f)).catch(() => "");
|
|
8542
8703
|
if (hasConflictMarkers(text))
|
|
8543
8704
|
conflict = true;
|
|
8544
8705
|
}
|
|
@@ -8556,17 +8717,17 @@ async function updateSite(ctx, consumer, site, localPreset, summary, cloneOf, pr
|
|
|
8556
8717
|
await previewCommit(repo, `plx preview: ${site.rel} updated`);
|
|
8557
8718
|
}
|
|
8558
8719
|
async function syncSiteMarker(ctx, site, target) {
|
|
8559
|
-
const marker = await ctx.readFile(
|
|
8720
|
+
const marker = await ctx.readFile(join8(target, MARKER_FILE)).catch(() => void 0);
|
|
8560
8721
|
if (marker === void 0)
|
|
8561
8722
|
return;
|
|
8562
|
-
const { commit } = parseAnswers(await ctx.readFile(
|
|
8723
|
+
const { commit } = parseAnswers(await ctx.readFile(join8(target, ANSWERS_FILE)));
|
|
8563
8724
|
if (!commit)
|
|
8564
8725
|
return;
|
|
8565
8726
|
const synced = syncMarker(marker, commit);
|
|
8566
8727
|
if (synced === marker)
|
|
8567
8728
|
return;
|
|
8568
8729
|
ctx.say(` marker: plexus \u2192 ${commit} in ${site.rel}/${MARKER_FILE}`);
|
|
8569
|
-
const step = write(
|
|
8730
|
+
const step = write(join8(target, MARKER_FILE), synced);
|
|
8570
8731
|
if (ctx.dryRun)
|
|
8571
8732
|
await ctx.stage(step);
|
|
8572
8733
|
else
|
|
@@ -8575,7 +8736,7 @@ async function syncSiteMarker(ctx, site, target) {
|
|
|
8575
8736
|
async function refreshLockfiles(ctx, consumers, summary, clones) {
|
|
8576
8737
|
const manifests = ["*package.json", "pnpm-workspace.yaml"];
|
|
8577
8738
|
for (const c of consumers) {
|
|
8578
|
-
if (await ctx.readFile(
|
|
8739
|
+
if (await ctx.readFile(join8(c.path, "pnpm-workspace.yaml")).catch(() => void 0) === void 0)
|
|
8579
8740
|
continue;
|
|
8580
8741
|
const repo = ctx.dryRun ? clones.get(c.path) : c.path;
|
|
8581
8742
|
if (!repo)
|
|
@@ -8595,7 +8756,7 @@ async function refreshLockfiles(ctx, consumers, summary, clones) {
|
|
|
8595
8756
|
}
|
|
8596
8757
|
async function bumpConsumerRefs(ctx, consumers, train) {
|
|
8597
8758
|
for (const c of consumers) {
|
|
8598
|
-
const dir =
|
|
8759
|
+
const dir = join8(c.path, ".github", "workflows");
|
|
8599
8760
|
const entries = await ctx.readDir(dir).catch(() => []);
|
|
8600
8761
|
const files = entries.filter((e) => !e.directory && /\.ya?ml$/.test(e.name)).map((e) => e.name).sort();
|
|
8601
8762
|
if (files.length === 0)
|
|
@@ -8605,14 +8766,14 @@ async function bumpConsumerRefs(ctx, consumers, train) {
|
|
|
8605
8766
|
continue;
|
|
8606
8767
|
}
|
|
8607
8768
|
for (const f of files) {
|
|
8608
|
-
const text = await ctx.readFile(
|
|
8769
|
+
const text = await ctx.readFile(join8(dir, f));
|
|
8609
8770
|
if (!text.includes(WORKFLOW_REF))
|
|
8610
8771
|
continue;
|
|
8611
8772
|
const bumped = bumpWorkflowRefs(text, train);
|
|
8612
8773
|
if (bumped === text)
|
|
8613
8774
|
continue;
|
|
8614
8775
|
ctx.say(`\u2192 ${c.rel}: ${f} ci-cd refs \u2192 @${train}`);
|
|
8615
|
-
await ctx.apply([write(
|
|
8776
|
+
await ctx.apply([write(join8(dir, f), bumped)]);
|
|
8616
8777
|
}
|
|
8617
8778
|
}
|
|
8618
8779
|
}
|
|
@@ -8623,7 +8784,7 @@ var detail = (error) => {
|
|
|
8623
8784
|
};
|
|
8624
8785
|
|
|
8625
8786
|
// ../lib/dist/requirements.js
|
|
8626
|
-
import { join as
|
|
8787
|
+
import { join as join9 } from "node:path";
|
|
8627
8788
|
var KEYWORD = /MUST|SHOULD|MAY/;
|
|
8628
8789
|
var hasKeyword = (line) => KEYWORD.test(line.replace(/MUSTs|SHOULDs|MAYs/g, ""));
|
|
8629
8790
|
var list = (value) => value.replace(/^\s*\[/, "").replace(/\].*$/, "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -8716,7 +8877,7 @@ var MAP_PAGE = "essentials/standard.md";
|
|
|
8716
8877
|
async function modulePages(ctx, compendium) {
|
|
8717
8878
|
const pages = [];
|
|
8718
8879
|
for (const dir of Object.keys(CATALOG_DIRS)) {
|
|
8719
|
-
const entries = await ctx.readDir(
|
|
8880
|
+
const entries = await ctx.readDir(join9(compendium, dir));
|
|
8720
8881
|
for (const e of entries.filter((e2) => !e2.directory && e2.name.endsWith(".md") && e2.name !== "index.md").sort())
|
|
8721
8882
|
pages.push(`${dir}/${e.name}`);
|
|
8722
8883
|
}
|
|
@@ -8724,11 +8885,11 @@ async function modulePages(ctx, compendium) {
|
|
|
8724
8885
|
}
|
|
8725
8886
|
async function checkCompendium(ctx, compendium) {
|
|
8726
8887
|
const defects = [];
|
|
8727
|
-
defects.push(...scanPage(await ctx.readFile(
|
|
8888
|
+
defects.push(...scanPage(await ctx.readFile(join9(compendium, MAP_PAGE)), MAP_PAGE, "map").defects);
|
|
8728
8889
|
const pages = await modulePages(ctx, compendium);
|
|
8729
8890
|
const scans = /* @__PURE__ */ new Map();
|
|
8730
8891
|
for (const page of pages) {
|
|
8731
|
-
const scan = scanPage(await ctx.readFile(
|
|
8892
|
+
const scan = scanPage(await ctx.readFile(join9(compendium, page)), page, "module");
|
|
8732
8893
|
defects.push(...scan.defects);
|
|
8733
8894
|
scans.set(page, scan);
|
|
8734
8895
|
}
|
|
@@ -8774,11 +8935,25 @@ async function checkCompendium(ctx, compendium) {
|
|
|
8774
8935
|
};
|
|
8775
8936
|
}
|
|
8776
8937
|
|
|
8938
|
+
// ../lib/dist/self.js
|
|
8939
|
+
var SELF_PACKAGE = "@plexus-ms/cli";
|
|
8940
|
+
async function preflightSelf(ctx, running) {
|
|
8941
|
+
const latest = (await ctx.read("npm", ["view", SELF_PACKAGE, "version"]).catch(() => "")).trim();
|
|
8942
|
+
if (!latest) {
|
|
8943
|
+
ctx.say(`\u26A0 could not ask the registry for the newest ${SELF_PACKAGE}; going on with plx ${running}`);
|
|
8944
|
+
return;
|
|
8945
|
+
}
|
|
8946
|
+
if (compareTrains(running, latest) < 0)
|
|
8947
|
+
throw new Error(`plx ${running} is behind ${latest}, the newest release; update it first: mise cache clear && mise up npm:${SELF_PACKAGE}`);
|
|
8948
|
+
}
|
|
8949
|
+
|
|
8777
8950
|
// ../lib/dist/ship.js
|
|
8778
|
-
import { join as
|
|
8951
|
+
import { join as join10 } from "node:path";
|
|
8779
8952
|
async function ship(ctx, options) {
|
|
8780
|
-
const upstream =
|
|
8953
|
+
const upstream = join10(options.workspace, UPSTREAM);
|
|
8781
8954
|
const propagateOnly = options.propagateOnly ?? false;
|
|
8955
|
+
if (options.plx)
|
|
8956
|
+
await preflightSelf(ctx, options.plx);
|
|
8782
8957
|
const train = await readTrain(ctx, upstream);
|
|
8783
8958
|
if (!propagateOnly)
|
|
8784
8959
|
await preflightRelease(ctx, train);
|
|
@@ -8828,7 +9003,7 @@ async function planBookkeeping(ctx, train, next, today2) {
|
|
|
8828
9003
|
return r;
|
|
8829
9004
|
};
|
|
8830
9005
|
const edit = async (r, rel, what, transform) => {
|
|
8831
|
-
const path3 =
|
|
9006
|
+
const path3 = join10(r.path, rel);
|
|
8832
9007
|
const text = await ctx.readFile(path3).catch(() => void 0);
|
|
8833
9008
|
if (text === void 0)
|
|
8834
9009
|
return;
|
|
@@ -8849,7 +9024,7 @@ async function planBookkeeping(ctx, train, next, today2) {
|
|
|
8849
9024
|
const comp = repo("compendium");
|
|
8850
9025
|
await edit(comp, MAP_PAGE, `version \u2192 ${next}, timestamp \u2192 ${today2}`, (t) => stampStandard(t, next, today2));
|
|
8851
9026
|
for (const page of await modulePages(ctx, comp.path)) {
|
|
8852
|
-
const text = await ctx.readFile(
|
|
9027
|
+
const text = await ctx.readFile(join10(comp.path, page));
|
|
8853
9028
|
if (frontmatterField(text, "maturity") !== "standard")
|
|
8854
9029
|
continue;
|
|
8855
9030
|
const previous = comp.tag ? await ctx.read("git", ["show", `${comp.tag}:${page}`], { cwd: comp.path }).catch(() => "") : "";
|
|
@@ -9812,13 +9987,13 @@ import { AsyncResource as AsyncResource3 } from "node:async_hooks";
|
|
|
9812
9987
|
|
|
9813
9988
|
// ../../node_modules/.pnpm/@inquirer+type@4.2.0_@types+node@26.6.5/node_modules/@inquirer/type/dist/utils.js
|
|
9814
9989
|
function withResolver() {
|
|
9815
|
-
let
|
|
9990
|
+
let resolve10;
|
|
9816
9991
|
let reject;
|
|
9817
9992
|
const promise = new Promise((res, rej) => {
|
|
9818
|
-
|
|
9993
|
+
resolve10 = res;
|
|
9819
9994
|
reject = rej;
|
|
9820
9995
|
});
|
|
9821
|
-
return { promise, resolve:
|
|
9996
|
+
return { promise, resolve: resolve10, reject };
|
|
9822
9997
|
}
|
|
9823
9998
|
|
|
9824
9999
|
// ../../node_modules/.pnpm/@inquirer+core@12.0.5_@types+node@26.6.5/node_modules/@inquirer/core/dist/lib/create-prompt.js
|
|
@@ -10197,7 +10372,7 @@ function createPrompt(view) {
|
|
|
10197
10372
|
});
|
|
10198
10373
|
output.mute();
|
|
10199
10374
|
const screen = new ScreenManager(rl);
|
|
10200
|
-
const { promise, resolve:
|
|
10375
|
+
const { promise, resolve: resolve10, reject } = withResolver();
|
|
10201
10376
|
return withHooks(rl, (cycle) => {
|
|
10202
10377
|
const clearEffects = AsyncResource3.bind(() => effectScheduler.clearAll());
|
|
10203
10378
|
const settlePrompt = (settle) => {
|
|
@@ -10208,7 +10383,7 @@ function createPrompt(view) {
|
|
|
10208
10383
|
reject(error);
|
|
10209
10384
|
}
|
|
10210
10385
|
};
|
|
10211
|
-
const resolvePrompt = (value) => settlePrompt(() =>
|
|
10386
|
+
const resolvePrompt = (value) => settlePrompt(() => resolve10(value));
|
|
10212
10387
|
const rejectPrompt = (error) => settlePrompt(() => reject(error));
|
|
10213
10388
|
const promptPromise = Object.assign(promise.finally(() => {
|
|
10214
10389
|
cleanups.forEach((cleanup) => cleanup());
|
|
@@ -13753,19 +13928,49 @@ function renderChangeset(c) {
|
|
|
13753
13928
|
return [`\u2713 ${c.path}`, ...releases.map(([name, bump]) => ` ${name}: ${bump}`)].join("\n");
|
|
13754
13929
|
}
|
|
13755
13930
|
|
|
13756
|
-
// src/commands/deploy
|
|
13931
|
+
// src/commands/deploy.ts
|
|
13757
13932
|
import { resolve as resolve3 } from "node:path";
|
|
13758
13933
|
function register3(program2) {
|
|
13934
|
+
program2.command("deploy").description(
|
|
13935
|
+
"render the app\u2019s compose file with the image pinned, stage it on the host, pull, migrate, swap, up, poll readiness, roll back on failure"
|
|
13936
|
+
).argument("<host>", "ssh target, deploy@host").argument("<tenant>", "tenant slug, the first segment of the app dir on the host").argument("<app>", "the app, apps/<app> in the checkout").option("--image <ref>", "the image ref to pin; absent for a third-party app whose compose.yaml pins its own").option(
|
|
13937
|
+
"--health-url <url>",
|
|
13938
|
+
"readiness URL polled on the host (default: /healthz, or the plexus.healthz label, on the loopback port)"
|
|
13939
|
+
).option("--retries <n>", "poll attempts, two seconds apart", "30").option("--app-dir <path>", "the app dir on the host (default: /opt/stacks/<tenant>/<app>)").option("--source <path>", "the app\u2019s dir in the checkout (default: apps/<app>)").option("-C, --dir <dir>", "the checkout to read", ".").action(async (host, tenant, app, opts, command) => {
|
|
13940
|
+
const ctx = contextOf2(command);
|
|
13941
|
+
const result = await deploy(ctx, {
|
|
13942
|
+
host,
|
|
13943
|
+
tenant,
|
|
13944
|
+
app,
|
|
13945
|
+
image: opts.image,
|
|
13946
|
+
healthUrl: opts.healthUrl,
|
|
13947
|
+
retries: Number(opts.retries),
|
|
13948
|
+
appDir: opts.appDir,
|
|
13949
|
+
source: opts.source,
|
|
13950
|
+
dir: resolve3(opts.dir)
|
|
13951
|
+
});
|
|
13952
|
+
ctx.result(result, renderDeploy);
|
|
13953
|
+
if (!result.healthy) process.exitCode = 1;
|
|
13954
|
+
});
|
|
13955
|
+
}
|
|
13956
|
+
function renderDeploy(d) {
|
|
13957
|
+
if (d.healthy) return d.live ?? "";
|
|
13958
|
+
return d.rolledBack ? `rolled back to ${d.live ?? "the previous compose.yaml"}` : "not healthy";
|
|
13959
|
+
}
|
|
13960
|
+
|
|
13961
|
+
// src/commands/deploy-targets.ts
|
|
13962
|
+
import { resolve as resolve4 } from "node:path";
|
|
13963
|
+
function register4(program2) {
|
|
13759
13964
|
program2.command("deploy-targets").description("map each app of a platform repo to the deploy target of the inventory file that declares it").argument("[inventory]", "the inventory directory", "inventory").action(async (inventory, _opts, command) => {
|
|
13760
13965
|
const ctx = contextOf2(command);
|
|
13761
|
-
const targets = await readDeployTargets(ctx,
|
|
13966
|
+
const targets = await readDeployTargets(ctx, resolve4(inventory));
|
|
13762
13967
|
ctx.result(targets, (t) => JSON.stringify(t));
|
|
13763
13968
|
});
|
|
13764
13969
|
}
|
|
13765
13970
|
|
|
13766
13971
|
// src/commands/doctor.ts
|
|
13767
13972
|
import { existsSync } from "node:fs";
|
|
13768
|
-
import { join as
|
|
13973
|
+
import { join as join11, resolve as resolve5 } from "node:path";
|
|
13769
13974
|
|
|
13770
13975
|
// src/style.ts
|
|
13771
13976
|
function style(enabled) {
|
|
@@ -13776,15 +13981,15 @@ var colorByDefault = () => process.stdout.isTTY === true && !process.env.NO_COLO
|
|
|
13776
13981
|
|
|
13777
13982
|
// src/commands/doctor.ts
|
|
13778
13983
|
var WORKSPACE_COMPENDIUM = "plexus-ms/compendium";
|
|
13779
|
-
function
|
|
13984
|
+
function register5(program2) {
|
|
13780
13985
|
program2.command("doctor").description("check every PLEXUS.md under a directory for modules that changed since and dependencies not declared").argument("[dir]", "directory to check", ".").option(
|
|
13781
13986
|
"--compendium <path>",
|
|
13782
13987
|
`compendium checkout to read the modules from (default: <dir>/${WORKSPACE_COMPENDIUM})`
|
|
13783
13988
|
).option("--color", "colour the report even when piped").option("--no-color", "never colour the report").action(async (dir, opts, command) => {
|
|
13784
13989
|
const ctx = contextOf2(command);
|
|
13785
|
-
const root =
|
|
13786
|
-
const compendium =
|
|
13787
|
-
if (!existsSync(
|
|
13990
|
+
const root = resolve5(dir);
|
|
13991
|
+
const compendium = resolve5(opts.compendium ?? join11(root, WORKSPACE_COMPENDIUM));
|
|
13992
|
+
if (!existsSync(join11(compendium, "contracts")))
|
|
13788
13993
|
throw new Error(
|
|
13789
13994
|
`no compendium at ${compendium}; pass --compendium <path to a checkout of plexus-ms/compendium>`
|
|
13790
13995
|
);
|
|
@@ -13812,11 +14017,11 @@ function renderReport(report, s, showCurrent) {
|
|
|
13812
14017
|
}
|
|
13813
14018
|
|
|
13814
14019
|
// src/commands/overview.ts
|
|
13815
|
-
import { resolve as
|
|
13816
|
-
function
|
|
14020
|
+
import { resolve as resolve6 } from "node:path";
|
|
14021
|
+
function register6(program2) {
|
|
13817
14022
|
program2.command("overview").description("map the git repos, copier templates and projects, and Plexus units under a directory").argument("[dir]", "directory to map", ".").option("--color", "colour the tree even when piped").option("--no-color", "never colour the tree").action(async (dir, opts, command) => {
|
|
13818
14023
|
const ctx = contextOf2(command);
|
|
13819
|
-
const tree = await overview(ctx,
|
|
14024
|
+
const tree = await overview(ctx, resolve6(dir));
|
|
13820
14025
|
const s = style(opts.color ?? colorByDefault());
|
|
13821
14026
|
ctx.result(tree, (t) => renderTree(t, s, ctx.verbose >= 1));
|
|
13822
14027
|
});
|
|
@@ -13859,14 +14064,14 @@ function annotate(dir, s, modules) {
|
|
|
13859
14064
|
}
|
|
13860
14065
|
|
|
13861
14066
|
// src/commands/propagate.ts
|
|
13862
|
-
import { resolve as
|
|
14067
|
+
import { resolve as resolve8 } from "node:path";
|
|
13863
14068
|
|
|
13864
14069
|
// src/commands/ship.ts
|
|
13865
|
-
import { resolve as
|
|
13866
|
-
function
|
|
14070
|
+
import { resolve as resolve7 } from "node:path";
|
|
14071
|
+
function register7(program2) {
|
|
13867
14072
|
program2.command("ship").description("release the train: tag every train repo identically, then copier-update every consumer").argument("[dir]", "the workspace: plexus-ms/ and the consumers sit under it", ".").option("--train <version>", "the train version, plain semver (default: minor+1 over the latest train)").action(async (dir, opts, command) => {
|
|
13868
14073
|
const ctx = contextOf2(command);
|
|
13869
|
-
const result = await ship(ctx, { workspace:
|
|
14074
|
+
const result = await ship(ctx, { workspace: resolve7(dir), train: opts.train, plx: version });
|
|
13870
14075
|
ctx.result(result, (r) => renderSummary(r, `plx -y ship --train ${r.train} ${dir}`));
|
|
13871
14076
|
if (!isClean(result)) process.exitCode = 1;
|
|
13872
14077
|
});
|
|
@@ -13889,23 +14094,23 @@ function renderSummary(s, realCommand) {
|
|
|
13889
14094
|
}
|
|
13890
14095
|
|
|
13891
14096
|
// src/commands/propagate.ts
|
|
13892
|
-
function
|
|
14097
|
+
function register8(program2) {
|
|
13893
14098
|
program2.command("propagate").description("copier-update every consumer of the presets to the current train, and pin their ci-cd workflow refs").argument("[dir]", "the workspace: plexus-ms/ and the consumers sit under it", ".").option("--train <version>", "the train to propagate (default: the current one)").action(async (dir, opts, command) => {
|
|
13894
14099
|
const ctx = contextOf2(command);
|
|
13895
|
-
const result = await ship(ctx, { workspace:
|
|
14100
|
+
const result = await ship(ctx, { workspace: resolve8(dir), train: opts.train, propagateOnly: true, plx: version });
|
|
13896
14101
|
ctx.result(result, (r) => renderSummary(r, `plx -y propagate --train ${r.train} ${dir}`));
|
|
13897
14102
|
if (!isClean(result)) process.exitCode = 1;
|
|
13898
14103
|
});
|
|
13899
14104
|
}
|
|
13900
14105
|
|
|
13901
14106
|
// src/commands/requirements.ts
|
|
13902
|
-
import { join as
|
|
13903
|
-
function
|
|
14107
|
+
import { join as join12, resolve as resolve9 } from "node:path";
|
|
14108
|
+
function register9(program2) {
|
|
13904
14109
|
program2.command("requirements").description("lint the standard's map and module pages, or extract one module's requirement lines").argument("[dir]", "a checkout of the compendium", ".").option("--extract <page>", "print a module page's requirement lines instead of linting (path relative to dir)").action(async (dir, opts, command) => {
|
|
13905
14110
|
const ctx = contextOf2(command);
|
|
13906
|
-
const compendium =
|
|
14111
|
+
const compendium = resolve9(dir);
|
|
13907
14112
|
if (opts.extract) {
|
|
13908
|
-
ctx.result(extractRequirements(await ctx.readFile(
|
|
14113
|
+
ctx.result(extractRequirements(await ctx.readFile(join12(compendium, opts.extract))));
|
|
13909
14114
|
return;
|
|
13910
14115
|
}
|
|
13911
14116
|
const check = await checkCompendium(ctx, compendium);
|
|
@@ -13920,13 +14125,14 @@ function renderCheck(c) {
|
|
|
13920
14125
|
|
|
13921
14126
|
// src/commands/index.ts
|
|
13922
14127
|
var commands = [
|
|
13923
|
-
register5,
|
|
13924
|
-
register4,
|
|
13925
14128
|
register6,
|
|
14129
|
+
register5,
|
|
13926
14130
|
register7,
|
|
13927
14131
|
register8,
|
|
14132
|
+
register9,
|
|
13928
14133
|
register2,
|
|
13929
14134
|
register,
|
|
14135
|
+
register4,
|
|
13930
14136
|
register3
|
|
13931
14137
|
];
|
|
13932
14138
|
|
|
@@ -13937,7 +14143,7 @@ function buildProgram() {
|
|
|
13937
14143
|
program2.addOption(new Option("-n, --dry-run", "print every effect this run would have, perform none").env("PLX_DRY_RUN")).addOption(
|
|
13938
14144
|
new Option("-v, --verbose", "say more: once for reads and inputs, twice for command output").argParser((_value, previous) => previous + 1).default(0)
|
|
13939
14145
|
).addOption(new Option("-q, --quiet", "say nothing but results and errors").env("PLX_QUIET")).addOption(new Option("-y, --yes", "answer every confirmation with yes (needed off a terminal)").env("PLX_YES")).addOption(new Option("--json", "print results as JSON").env("PLX_JSON"));
|
|
13940
|
-
for (const
|
|
14146
|
+
for (const register10 of commands) register10(program2);
|
|
13941
14147
|
return program2;
|
|
13942
14148
|
}
|
|
13943
14149
|
function contextOf2(command) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plexus-ms/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "plx, the Plexus command-line tool: a single-script adapter over @plexus-ms/lib",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@inquirer/prompts": "^8.7.4",
|
|
27
|
-
"@plexus-ms/lib": "0.
|
|
27
|
+
"@plexus-ms/lib": "0.7.0",
|
|
28
28
|
"commander": "^15.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|