@ctxo/cli 0.8.2 → 0.9.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.
|
@@ -31,8 +31,8 @@ import {
|
|
|
31
31
|
} from "./chunk-PSKDDLSF.js";
|
|
32
32
|
|
|
33
33
|
// src/cli/cli-router.ts
|
|
34
|
-
import { readFileSync as
|
|
35
|
-
import { dirname as dirname8, join as
|
|
34
|
+
import { readFileSync as readFileSync15, existsSync as existsSync23 } from "fs";
|
|
35
|
+
import { dirname as dirname8, join as join25 } from "path";
|
|
36
36
|
|
|
37
37
|
// src/cli/index-command.ts
|
|
38
38
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -264,7 +264,6 @@ async function loadPlugins(projectRoot, options = {}) {
|
|
|
264
264
|
|
|
265
265
|
// src/cli/install-command.ts
|
|
266
266
|
import { existsSync as existsSync5 } from "fs";
|
|
267
|
-
import { spawn } from "child_process";
|
|
268
267
|
import { createRequire as createRequire2 } from "module";
|
|
269
268
|
import { join as join4 } from "path";
|
|
270
269
|
|
|
@@ -523,6 +522,16 @@ function buildInstallCommand(manager, packages, options = {}) {
|
|
|
523
522
|
}
|
|
524
523
|
}
|
|
525
524
|
|
|
525
|
+
// src/core/install/run-package-manager.ts
|
|
526
|
+
import { spawn } from "child_process";
|
|
527
|
+
function runPackageManager(command, args, cwd) {
|
|
528
|
+
return new Promise((resolve4, reject) => {
|
|
529
|
+
const child = spawn(command, [...args], { cwd, stdio: "inherit", shell: process.platform === "win32" });
|
|
530
|
+
child.on("exit", (code) => resolve4(code ?? 0));
|
|
531
|
+
child.on("error", (err) => reject(err));
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
|
|
526
535
|
// src/cli/install-command.ts
|
|
527
536
|
var InstallCommand = class {
|
|
528
537
|
constructor(projectRoot) {
|
|
@@ -579,7 +588,7 @@ var InstallCommand = class {
|
|
|
579
588
|
console.error("[ctxo] --dry-run set; not executing.");
|
|
580
589
|
return;
|
|
581
590
|
}
|
|
582
|
-
const code = await
|
|
591
|
+
const code = await runPackageManager(plan.command, plan.args, this.projectRoot);
|
|
583
592
|
if (code !== 0) {
|
|
584
593
|
console.error(`[ctxo] ${plan.command} exited with code ${code}`);
|
|
585
594
|
process.exitCode = code;
|
|
@@ -648,13 +657,6 @@ function isLockedCI(env) {
|
|
|
648
657
|
if (env["CI"] !== "true" && env["CI"] !== "1") return false;
|
|
649
658
|
return true;
|
|
650
659
|
}
|
|
651
|
-
function spawnInstall(command, args, cwd) {
|
|
652
|
-
return new Promise((resolve4, reject) => {
|
|
653
|
-
const child = spawn(command, [...args], { cwd, stdio: "inherit", shell: process.platform === "win32" });
|
|
654
|
-
child.on("exit", (code) => resolve4(code ?? 0));
|
|
655
|
-
child.on("error", (err) => reject(err));
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
660
|
|
|
659
661
|
// src/adapters/storage/json-index-writer.ts
|
|
660
662
|
import { mkdirSync, unlinkSync, existsSync as existsSync6 } from "fs";
|
|
@@ -3606,7 +3608,7 @@ var DoctorCommand = class {
|
|
|
3606
3608
|
this.ctxoRoot = join22(projectRoot, ".ctxo");
|
|
3607
3609
|
}
|
|
3608
3610
|
async run(options = {}) {
|
|
3609
|
-
const { getVersion: getVersion2 } = await import("./cli-router-
|
|
3611
|
+
const { getVersion: getVersion2 } = await import("./cli-router-L34BRMXQ.js");
|
|
3610
3612
|
console.error(`ctxo v${getVersion2()} \u2014 health check
|
|
3611
3613
|
`);
|
|
3612
3614
|
const checks = [
|
|
@@ -3867,20 +3869,363 @@ var VisualizeCommand = class {
|
|
|
3867
3869
|
}
|
|
3868
3870
|
};
|
|
3869
3871
|
|
|
3872
|
+
// src/cli/update-command.ts
|
|
3873
|
+
import { existsSync as existsSync22, readFileSync as readFileSync14 } from "fs";
|
|
3874
|
+
import { join as join24 } from "path";
|
|
3875
|
+
|
|
3876
|
+
// src/core/update/registry-client.ts
|
|
3877
|
+
import { request } from "https";
|
|
3878
|
+
var log5 = createLogger("ctxo:update:registry");
|
|
3879
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
3880
|
+
async function fetchDistTags(packageName, options = {}) {
|
|
3881
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
3882
|
+
const fetcher = options.fetcher ?? defaultHttpsFetcher;
|
|
3883
|
+
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}`;
|
|
3884
|
+
let response;
|
|
3885
|
+
try {
|
|
3886
|
+
response = await fetcher(url, timeoutMs);
|
|
3887
|
+
} catch (err) {
|
|
3888
|
+
return classifyFetchError(packageName, err);
|
|
3889
|
+
}
|
|
3890
|
+
if (response.status === 404) {
|
|
3891
|
+
return { name: packageName, reason: "registry-404", status: 404 };
|
|
3892
|
+
}
|
|
3893
|
+
if (response.status < 200 || response.status >= 300) {
|
|
3894
|
+
return { name: packageName, reason: "registry-error", status: response.status };
|
|
3895
|
+
}
|
|
3896
|
+
let parsed;
|
|
3897
|
+
try {
|
|
3898
|
+
parsed = JSON.parse(response.body);
|
|
3899
|
+
} catch {
|
|
3900
|
+
return { name: packageName, reason: "registry-error", status: response.status, message: "invalid JSON" };
|
|
3901
|
+
}
|
|
3902
|
+
const distTags = parsed["dist-tags"];
|
|
3903
|
+
if (!distTags || typeof distTags !== "object") {
|
|
3904
|
+
return { name: packageName, reason: "registry-error", status: response.status, message: "missing dist-tags" };
|
|
3905
|
+
}
|
|
3906
|
+
return { name: packageName, distTags };
|
|
3907
|
+
}
|
|
3908
|
+
async function fetchDistTagsBatch(packageNames, options = {}) {
|
|
3909
|
+
return Promise.all(packageNames.map((name) => fetchDistTags(name, options)));
|
|
3910
|
+
}
|
|
3911
|
+
function classifyFetchError(name, err) {
|
|
3912
|
+
const code = err?.code;
|
|
3913
|
+
const message = err?.message ?? String(err);
|
|
3914
|
+
if (code === "ETIMEDOUT" || /timeout/i.test(message)) {
|
|
3915
|
+
return { name, reason: "timeout", message };
|
|
3916
|
+
}
|
|
3917
|
+
if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
3918
|
+
return { name, reason: "network", message };
|
|
3919
|
+
}
|
|
3920
|
+
log5.error(`unexpected fetch error for ${name}: ${message}`);
|
|
3921
|
+
return { name, reason: "network", message };
|
|
3922
|
+
}
|
|
3923
|
+
var defaultHttpsFetcher = (url, timeoutMs) => new Promise((resolve4, reject) => {
|
|
3924
|
+
const req = request(url, { method: "GET", headers: { "user-agent": "ctxo-update", accept: "application/json" } }, (res) => {
|
|
3925
|
+
const chunks = [];
|
|
3926
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
3927
|
+
res.on("end", () => resolve4({ status: res.statusCode ?? 0, body: Buffer.concat(chunks).toString("utf-8") }));
|
|
3928
|
+
res.on("error", reject);
|
|
3929
|
+
});
|
|
3930
|
+
req.setTimeout(timeoutMs, () => {
|
|
3931
|
+
req.destroy(Object.assign(new Error(`timeout after ${timeoutMs}ms`), { code: "ETIMEDOUT" }));
|
|
3932
|
+
});
|
|
3933
|
+
req.on("error", reject);
|
|
3934
|
+
req.end();
|
|
3935
|
+
});
|
|
3936
|
+
|
|
3937
|
+
// src/core/update/update-plan.ts
|
|
3938
|
+
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
3939
|
+
function parseSemver(version) {
|
|
3940
|
+
const match = SEMVER_RE.exec(version.trim());
|
|
3941
|
+
if (!match) return null;
|
|
3942
|
+
const prereleaseRaw = match[4] ?? "";
|
|
3943
|
+
return {
|
|
3944
|
+
major: Number(match[1]),
|
|
3945
|
+
minor: Number(match[2]),
|
|
3946
|
+
patch: Number(match[3]),
|
|
3947
|
+
prerelease: prereleaseRaw ? prereleaseRaw.split(".") : []
|
|
3948
|
+
};
|
|
3949
|
+
}
|
|
3950
|
+
function detectChannel(version) {
|
|
3951
|
+
const parsed = parseSemver(version);
|
|
3952
|
+
if (!parsed || parsed.prerelease.length === 0) return "latest";
|
|
3953
|
+
const first = parsed.prerelease[0];
|
|
3954
|
+
if (/^\d+$/.test(first)) return "latest";
|
|
3955
|
+
return first;
|
|
3956
|
+
}
|
|
3957
|
+
function compareSemver(a, b) {
|
|
3958
|
+
const pa = parseSemver(a);
|
|
3959
|
+
const pb = parseSemver(b);
|
|
3960
|
+
if (!pa && !pb) return 0;
|
|
3961
|
+
if (!pa) return -1;
|
|
3962
|
+
if (!pb) return 1;
|
|
3963
|
+
if (pa.major !== pb.major) return pa.major < pb.major ? -1 : 1;
|
|
3964
|
+
if (pa.minor !== pb.minor) return pa.minor < pb.minor ? -1 : 1;
|
|
3965
|
+
if (pa.patch !== pb.patch) return pa.patch < pb.patch ? -1 : 1;
|
|
3966
|
+
if (pa.prerelease.length === 0 && pb.prerelease.length === 0) return 0;
|
|
3967
|
+
if (pa.prerelease.length === 0) return 1;
|
|
3968
|
+
if (pb.prerelease.length === 0) return -1;
|
|
3969
|
+
const len = Math.max(pa.prerelease.length, pb.prerelease.length);
|
|
3970
|
+
for (let i = 0; i < len; i++) {
|
|
3971
|
+
const ai = pa.prerelease[i];
|
|
3972
|
+
const bi = pb.prerelease[i];
|
|
3973
|
+
if (ai === void 0) return -1;
|
|
3974
|
+
if (bi === void 0) return 1;
|
|
3975
|
+
const aNum = /^\d+$/.test(ai);
|
|
3976
|
+
const bNum = /^\d+$/.test(bi);
|
|
3977
|
+
if (aNum && bNum) {
|
|
3978
|
+
const an = Number(ai);
|
|
3979
|
+
const bn = Number(bi);
|
|
3980
|
+
if (an !== bn) return an < bn ? -1 : 1;
|
|
3981
|
+
} else if (aNum && !bNum) {
|
|
3982
|
+
return -1;
|
|
3983
|
+
} else if (!aNum && bNum) {
|
|
3984
|
+
return 1;
|
|
3985
|
+
} else if (ai !== bi) {
|
|
3986
|
+
return ai < bi ? -1 : 1;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
return 0;
|
|
3990
|
+
}
|
|
3991
|
+
function computePackageStates(installed, results) {
|
|
3992
|
+
const byName = /* @__PURE__ */ new Map();
|
|
3993
|
+
for (const r of results) byName.set(r.name, r);
|
|
3994
|
+
return installed.map((pkg) => {
|
|
3995
|
+
const installedChannel = detectChannel(pkg.version);
|
|
3996
|
+
const result = byName.get(pkg.name);
|
|
3997
|
+
if (!result) {
|
|
3998
|
+
return { name: pkg.name, current: pkg.version, latest: null, channel: installedChannel, status: "unknown" };
|
|
3999
|
+
}
|
|
4000
|
+
if (!("distTags" in result)) {
|
|
4001
|
+
return { name: pkg.name, current: pkg.version, latest: null, channel: installedChannel, status: "unknown", reason: result.reason };
|
|
4002
|
+
}
|
|
4003
|
+
const hasInstalledChannel = typeof result.distTags[installedChannel] === "string";
|
|
4004
|
+
const targetChannel = hasInstalledChannel ? installedChannel : "latest";
|
|
4005
|
+
const target = result.distTags[targetChannel] ?? null;
|
|
4006
|
+
if (!target) {
|
|
4007
|
+
return { name: pkg.name, current: pkg.version, latest: null, channel: installedChannel, status: "unknown" };
|
|
4008
|
+
}
|
|
4009
|
+
const cmp = compareSemver(pkg.version, target);
|
|
4010
|
+
const status = cmp === 0 ? "current" : cmp < 0 ? "update" : "ahead";
|
|
4011
|
+
return { name: pkg.name, current: pkg.version, latest: target, channel: targetChannel, status };
|
|
4012
|
+
});
|
|
4013
|
+
}
|
|
4014
|
+
function selectInstallTargets(states) {
|
|
4015
|
+
return states.filter((s) => s.status === "update" && typeof s.latest === "string").filter((s) => isSafeVersionSpecifier(s.latest)).map((s) => ({ name: s.name, version: s.latest }));
|
|
4016
|
+
}
|
|
4017
|
+
var SAFE_SPECIFIER_RE = /^[0-9A-Za-z.+-]+$/;
|
|
4018
|
+
function isSafeVersionSpecifier(version) {
|
|
4019
|
+
return SAFE_SPECIFIER_RE.test(version);
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
// src/cli/update-command.ts
|
|
4023
|
+
var log6 = createLogger("ctxo:update");
|
|
4024
|
+
var CTXO_CLI_PACKAGE = "@ctxo/cli";
|
|
4025
|
+
function formatText(report) {
|
|
4026
|
+
const lines = [];
|
|
4027
|
+
lines.push("ctxo update \u2014 checking registry for updates...");
|
|
4028
|
+
lines.push("");
|
|
4029
|
+
const updatesExist = report.packages.some((p) => p.status === "update");
|
|
4030
|
+
if (!updatesExist && report.strategy !== "print") {
|
|
4031
|
+
const total = report.packages.length;
|
|
4032
|
+
lines.push(`All ${total} package${total === 1 ? "" : "s"} are up to date.`);
|
|
4033
|
+
return lines.join("\n");
|
|
4034
|
+
}
|
|
4035
|
+
const nameCol = Math.max(7, ...report.packages.map((p) => p.name.length));
|
|
4036
|
+
const curCol = Math.max(7, ...report.packages.map((p) => p.current.length));
|
|
4037
|
+
const latestCol = Math.max(15, ...report.packages.map((p) => (p.latest ?? "(not found)").length));
|
|
4038
|
+
lines.push(
|
|
4039
|
+
`${"PACKAGE".padEnd(nameCol)} ${"CURRENT".padEnd(curCol)} ${`LATEST (${report.channel})`.padEnd(latestCol)} STATUS`
|
|
4040
|
+
);
|
|
4041
|
+
for (const pkg of report.packages) {
|
|
4042
|
+
const latestText = pkg.latest ?? "(not found)";
|
|
4043
|
+
const statusText = renderStatus(pkg, report.channel);
|
|
4044
|
+
lines.push(
|
|
4045
|
+
`${pkg.name.padEnd(nameCol)} ${pkg.current.padEnd(curCol)} ${latestText.padEnd(latestCol)} ${statusText}`
|
|
4046
|
+
);
|
|
4047
|
+
}
|
|
4048
|
+
if (report.plan && report.strategy === "execute") {
|
|
4049
|
+
lines.push("");
|
|
4050
|
+
lines.push(`Plan: ${report.plan.command} ${report.plan.args.join(" ")}`);
|
|
4051
|
+
lines.push(`Using ${report.plan.manager} (${formatManagerSource(report.plan)})`);
|
|
4052
|
+
lines.push("");
|
|
4053
|
+
lines.push("Running...");
|
|
4054
|
+
} else if (report.plan && report.strategy === "print") {
|
|
4055
|
+
lines.push("");
|
|
4056
|
+
lines.push("To update, run:");
|
|
4057
|
+
lines.push(` ${report.plan.command} ${report.plan.args.join(" ")}`);
|
|
4058
|
+
}
|
|
4059
|
+
return lines.join("\n");
|
|
4060
|
+
}
|
|
4061
|
+
function renderStatus(pkg, reportChannel) {
|
|
4062
|
+
switch (pkg.status) {
|
|
4063
|
+
case "current":
|
|
4064
|
+
return "up to date";
|
|
4065
|
+
case "ahead":
|
|
4066
|
+
return "ahead of registry";
|
|
4067
|
+
case "unknown":
|
|
4068
|
+
return pkg.reason === "registry-404" ? "skipped" : `error${pkg.reason ? ` (${pkg.reason})` : ""}`;
|
|
4069
|
+
case "update":
|
|
4070
|
+
return pkg.channel === reportChannel ? "update" : `update (${pkg.channel})`;
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
function formatManagerSource(plan) {
|
|
4074
|
+
return plan.managerDetail ? `${plan.managerSource}: ${plan.managerDetail}` : plan.managerSource;
|
|
4075
|
+
}
|
|
4076
|
+
function formatJson2(report) {
|
|
4077
|
+
return JSON.stringify(
|
|
4078
|
+
{
|
|
4079
|
+
ctxo: report.ctxo,
|
|
4080
|
+
channel: report.channel,
|
|
4081
|
+
packages: report.packages,
|
|
4082
|
+
plan: report.plan,
|
|
4083
|
+
executed: report.executed,
|
|
4084
|
+
...report.exitCode !== void 0 ? { exitCode: report.exitCode } : {}
|
|
4085
|
+
},
|
|
4086
|
+
null,
|
|
4087
|
+
2
|
|
4088
|
+
);
|
|
4089
|
+
}
|
|
4090
|
+
var UpdateCommand = class {
|
|
4091
|
+
constructor(projectRoot, deps = {}) {
|
|
4092
|
+
this.projectRoot = projectRoot;
|
|
4093
|
+
this.deps = deps;
|
|
4094
|
+
}
|
|
4095
|
+
projectRoot;
|
|
4096
|
+
deps;
|
|
4097
|
+
async run(options = {}) {
|
|
4098
|
+
if (options.pm && !isPackageManager(options.pm)) {
|
|
4099
|
+
this.emitError(`Unknown --pm value "${options.pm}".`);
|
|
4100
|
+
this.deps.setExitCode?.(1);
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
const discover = this.deps.discoverInstalled ?? defaultDiscoverInstalled;
|
|
4104
|
+
const installed = await discover(this.projectRoot);
|
|
4105
|
+
if (installed.length === 0) {
|
|
4106
|
+
this.emit(options.json ? "{}\n" : "ctxo update \u2014 no @ctxo/* packages discovered.\n");
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
const names = installed.map((p) => p.name);
|
|
4110
|
+
const results = await fetchDistTagsBatch(names, { fetcher: this.deps.fetcher });
|
|
4111
|
+
const allMissed = results.every((r) => !("distTags" in r));
|
|
4112
|
+
if (allMissed) {
|
|
4113
|
+
this.emitError("registry unreachable for every package; aborting.");
|
|
4114
|
+
this.deps.setExitCode?.(1);
|
|
4115
|
+
return;
|
|
4116
|
+
}
|
|
4117
|
+
const states = computePackageStates(installed, results);
|
|
4118
|
+
const targets = selectInstallTargets(states);
|
|
4119
|
+
const cliVersion = getVersion();
|
|
4120
|
+
const channel = detectChannel(cliVersion);
|
|
4121
|
+
const plan = targets.length === 0 ? null : this.buildPlan(targets, options);
|
|
4122
|
+
const strategy = this.pickStrategy(targets, options);
|
|
4123
|
+
const baseReport = {
|
|
4124
|
+
ctxo: cliVersion,
|
|
4125
|
+
channel,
|
|
4126
|
+
packages: states,
|
|
4127
|
+
plan,
|
|
4128
|
+
strategy,
|
|
4129
|
+
executed: false
|
|
4130
|
+
};
|
|
4131
|
+
if (options.check) {
|
|
4132
|
+
const checkStrategy = plan ? "print" : strategy;
|
|
4133
|
+
this.emitReport({ ...baseReport, strategy: checkStrategy }, options.json ?? false);
|
|
4134
|
+
return;
|
|
4135
|
+
}
|
|
4136
|
+
if (strategy === "none" || strategy === "print" || !plan) {
|
|
4137
|
+
this.emitReport(baseReport, options.json ?? false);
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
if (!options.force && !options.global && isLockedCI2(this.deps.env ?? process.env)) {
|
|
4141
|
+
this.emitReport({ ...baseReport, strategy: "print" }, options.json ?? false);
|
|
4142
|
+
this.emitError("CI environment with frozen lockfile detected. Refusing to mutate. Use --force or --global.");
|
|
4143
|
+
this.deps.setExitCode?.(1);
|
|
4144
|
+
return;
|
|
4145
|
+
}
|
|
4146
|
+
if (!options.json) this.emitReport(baseReport, false);
|
|
4147
|
+
const run2 = this.deps.runner ?? runPackageManager;
|
|
4148
|
+
const code = await run2(plan.command, plan.args, this.projectRoot);
|
|
4149
|
+
const finalReport = { ...baseReport, executed: true, exitCode: code };
|
|
4150
|
+
if (options.json) this.emit(formatJson2(finalReport) + "\n");
|
|
4151
|
+
if (code !== 0) this.deps.setExitCode?.(code);
|
|
4152
|
+
}
|
|
4153
|
+
buildPlan(targets, options) {
|
|
4154
|
+
const resolution = resolvePackageManager({
|
|
4155
|
+
flag: options.pm,
|
|
4156
|
+
projectRoot: this.projectRoot,
|
|
4157
|
+
env: this.deps.env
|
|
4158
|
+
});
|
|
4159
|
+
const specifiers = targets.map((t) => `${t.name}@${t.version}`);
|
|
4160
|
+
const useGlobal = options.global || !this.projectHasCtxoDep();
|
|
4161
|
+
const invocation = buildInstallCommand(resolution.manager, specifiers, { global: useGlobal });
|
|
4162
|
+
return {
|
|
4163
|
+
manager: resolution.manager,
|
|
4164
|
+
managerSource: resolution.source,
|
|
4165
|
+
managerDetail: resolution.detail,
|
|
4166
|
+
global: useGlobal,
|
|
4167
|
+
command: invocation.command,
|
|
4168
|
+
args: invocation.args
|
|
4169
|
+
};
|
|
4170
|
+
}
|
|
4171
|
+
pickStrategy(targets, options) {
|
|
4172
|
+
if (targets.length === 0) return "none";
|
|
4173
|
+
if (options.print) return "print";
|
|
4174
|
+
if (options.global) return "execute";
|
|
4175
|
+
return this.projectHasCtxoDep() ? "execute" : "print";
|
|
4176
|
+
}
|
|
4177
|
+
projectHasCtxoDep() {
|
|
4178
|
+
const pkg = join24(this.projectRoot, "package.json");
|
|
4179
|
+
if (!existsSync22(pkg)) return false;
|
|
4180
|
+
try {
|
|
4181
|
+
const json = JSON.parse(readFileSync14(pkg, "utf-8"));
|
|
4182
|
+
const merged = { ...json.dependencies ?? {}, ...json.devDependencies ?? {} };
|
|
4183
|
+
return Object.keys(merged).some(
|
|
4184
|
+
(name) => name === CTXO_CLI_PACKAGE || name.startsWith("@ctxo/lang-") || name.startsWith("ctxo-lang-")
|
|
4185
|
+
);
|
|
4186
|
+
} catch (err) {
|
|
4187
|
+
log6.error(`failed to read project package.json: ${err.message}`);
|
|
4188
|
+
return false;
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
emit(text) {
|
|
4192
|
+
process.stdout.write(text);
|
|
4193
|
+
}
|
|
4194
|
+
emitError(text) {
|
|
4195
|
+
process.stderr.write(`[ctxo] ${text}
|
|
4196
|
+
`);
|
|
4197
|
+
}
|
|
4198
|
+
emitReport(report, json) {
|
|
4199
|
+
if (json) this.emit(formatJson2(report) + "\n");
|
|
4200
|
+
else this.emit(formatText(report) + "\n");
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
async function defaultDiscoverInstalled(projectRoot) {
|
|
4204
|
+
const out = [{ name: CTXO_CLI_PACKAGE, version: getVersion() }];
|
|
4205
|
+
const manifestPath = loadManifestPath(projectRoot);
|
|
4206
|
+
if (!manifestPath) return out;
|
|
4207
|
+
const { plugins } = await discoverPlugins({ manifestPath });
|
|
4208
|
+
for (const p of plugins) out.push({ name: p.specifier, version: p.plugin.version });
|
|
4209
|
+
return out;
|
|
4210
|
+
}
|
|
4211
|
+
function isLockedCI2(env) {
|
|
4212
|
+
return env["CI"] === "true" || env["CI"] === "1";
|
|
4213
|
+
}
|
|
4214
|
+
|
|
3870
4215
|
// src/cli/cli-router.ts
|
|
3871
4216
|
function getVersion() {
|
|
3872
4217
|
let dir = import.meta.dirname;
|
|
3873
4218
|
for (let i = 0; i < 10; i++) {
|
|
3874
|
-
const pkg =
|
|
3875
|
-
if (
|
|
4219
|
+
const pkg = join25(dir, "package.json");
|
|
4220
|
+
if (existsSync23(pkg)) {
|
|
3876
4221
|
try {
|
|
3877
|
-
const json = JSON.parse(
|
|
4222
|
+
const json = JSON.parse(readFileSync15(pkg, "utf-8"));
|
|
3878
4223
|
return json.version ?? "unknown";
|
|
3879
4224
|
} catch {
|
|
3880
4225
|
break;
|
|
3881
4226
|
}
|
|
3882
4227
|
}
|
|
3883
|
-
const parent =
|
|
4228
|
+
const parent = join25(dir, "..");
|
|
3884
4229
|
if (parent === dir) break;
|
|
3885
4230
|
dir = parent;
|
|
3886
4231
|
}
|
|
@@ -3889,7 +4234,7 @@ function getVersion() {
|
|
|
3889
4234
|
function locateGitRoot(startDir) {
|
|
3890
4235
|
let dir = startDir;
|
|
3891
4236
|
for (let i = 0; i < 12; i++) {
|
|
3892
|
-
if (
|
|
4237
|
+
if (existsSync23(join25(dir, ".git"))) return dir;
|
|
3893
4238
|
const parent = dirname8(dir);
|
|
3894
4239
|
if (parent === dir) break;
|
|
3895
4240
|
dir = parent;
|
|
@@ -4042,6 +4387,43 @@ var CliRouter = class {
|
|
|
4042
4387
|
});
|
|
4043
4388
|
break;
|
|
4044
4389
|
}
|
|
4390
|
+
case "update": {
|
|
4391
|
+
const flagValues = {};
|
|
4392
|
+
for (let i = 1; i < args.length; i++) {
|
|
4393
|
+
const a = args[i];
|
|
4394
|
+
if (a === "--check") flagValues["check"] = true;
|
|
4395
|
+
else if (a === "--print") flagValues["print"] = true;
|
|
4396
|
+
else if (a === "--global" || a === "-g") flagValues["global"] = true;
|
|
4397
|
+
else if (a === "--json") flagValues["json"] = true;
|
|
4398
|
+
else if (a === "--force") flagValues["force"] = true;
|
|
4399
|
+
else if (a === "--pm") {
|
|
4400
|
+
const next = args[++i];
|
|
4401
|
+
if (!next) {
|
|
4402
|
+
console.error("[ctxo] --pm requires a value");
|
|
4403
|
+
process.exit(1);
|
|
4404
|
+
return;
|
|
4405
|
+
}
|
|
4406
|
+
flagValues["pm"] = next;
|
|
4407
|
+
} else {
|
|
4408
|
+
console.error(`[ctxo] Unknown update flag: ${a}`);
|
|
4409
|
+
process.exit(1);
|
|
4410
|
+
return;
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
await new UpdateCommand(this.projectRoot, {
|
|
4414
|
+
setExitCode: (code) => {
|
|
4415
|
+
process.exitCode = code;
|
|
4416
|
+
}
|
|
4417
|
+
}).run({
|
|
4418
|
+
check: flagValues["check"] === true,
|
|
4419
|
+
print: flagValues["print"] === true,
|
|
4420
|
+
global: flagValues["global"] === true,
|
|
4421
|
+
json: flagValues["json"] === true,
|
|
4422
|
+
force: flagValues["force"] === true,
|
|
4423
|
+
pm: typeof flagValues["pm"] === "string" ? flagValues["pm"] : void 0
|
|
4424
|
+
});
|
|
4425
|
+
break;
|
|
4426
|
+
}
|
|
4045
4427
|
case "visualize": {
|
|
4046
4428
|
const outputIdx = args.indexOf("--output");
|
|
4047
4429
|
const outputArg = outputIdx !== -1 ? args[outputIdx + 1] : void 0;
|
|
@@ -4089,6 +4471,13 @@ Usage:
|
|
|
4089
4471
|
ctxo install python -y Batch install, no prompt
|
|
4090
4472
|
ctxo install ts --global Install globally
|
|
4091
4473
|
ctxo install py --dry-run Show command without running
|
|
4474
|
+
ctxo update Check + apply updates for ctxo + plugins
|
|
4475
|
+
ctxo update --check Check only, no install
|
|
4476
|
+
ctxo update --print Print install command, never execute
|
|
4477
|
+
ctxo update --global Force a global install
|
|
4478
|
+
ctxo update --json Machine-readable output
|
|
4479
|
+
ctxo update --pm <pm> Override package manager (npm|pnpm|yarn|bun)
|
|
4480
|
+
ctxo update --force Bypass CI lockfile guard
|
|
4092
4481
|
ctxo --version Print core version
|
|
4093
4482
|
ctxo --version --verbose Print core + plugins + runtime
|
|
4094
4483
|
ctxo --version --json Machine-readable version payload
|
|
@@ -4102,4 +4491,4 @@ export {
|
|
|
4102
4491
|
getVersion,
|
|
4103
4492
|
locateGitRoot
|
|
4104
4493
|
};
|
|
4105
|
-
//# sourceMappingURL=cli-router-
|
|
4494
|
+
//# sourceMappingURL=cli-router-L34BRMXQ.js.map
|