@duyquangnvx/create-agent-cli 0.22.0 → 0.23.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/CHANGELOG.md +6 -0
- package/dist/bin.js +151 -13
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ What changed in each release of `@duyquangnvx/agent-cli` (the Runtime) and `@duy
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## v0.23.0 (2026-11-03)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- A Global install of a CLI with several Commands runs a Release check: at most once a day a run asks the Install registry, in a detached process, for the latest release, and later runs give the Notice ``<cli> X.Y.Z is out, this is A.B.C; run `<cli> update` `` while the CLI is behind. No run waits on it. `CI`, or `<NAME>_NO_RELEASE_CHECK=1` for one CLI, turns it off; it keeps its answer in `$XDG_CACHE_HOME/<name>/release-check.json`, by default under `~/.cache`.
|
|
12
|
+
|
|
7
13
|
## v0.22.0 (2026-11-02)
|
|
8
14
|
|
|
9
15
|
### Added
|
package/dist/bin.js
CHANGED
|
@@ -5959,8 +5959,12 @@ async function orMissing(pending) {
|
|
|
5959
5959
|
function isNothingThere(error) {
|
|
5960
5960
|
return isMissingFile(error) || errnoOf(error) === "ENOTDIR";
|
|
5961
5961
|
}
|
|
5962
|
+
/** The CLI's own environment variable, such as `TOOL_DEBUG` for `cliVariable("tool", "DEBUG")`. */
|
|
5963
|
+
function cliVariable(cliName, suffix) {
|
|
5964
|
+
return `${cliName.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_${suffix}`;
|
|
5965
|
+
}
|
|
5962
5966
|
function debugVariable(cliName) {
|
|
5963
|
-
return
|
|
5967
|
+
return cliVariable(cliName, "DEBUG");
|
|
5964
5968
|
}
|
|
5965
5969
|
function messageOf(error) {
|
|
5966
5970
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -7081,9 +7085,9 @@ function secretRows(secrets, commands) {
|
|
|
7081
7085
|
/** The bundler's stamp, absent when the CLI runs unbundled. Read per call, so a test can define it. */
|
|
7082
7086
|
function buildStamp() {
|
|
7083
7087
|
return {
|
|
7084
|
-
"sha": "
|
|
7088
|
+
"sha": "71583a7",
|
|
7085
7089
|
"dirty": false,
|
|
7086
|
-
"builtAt": "2026-11-
|
|
7090
|
+
"builtAt": "2026-11-03T06:40:47.000Z"
|
|
7087
7091
|
};
|
|
7088
7092
|
}
|
|
7089
7093
|
/** The first line of `--version`: the version, and the commit and time of a stamped build. */
|
|
@@ -7361,15 +7365,18 @@ async function installGlobally(version, ctx) {
|
|
|
7361
7365
|
...ctx.registry.flags
|
|
7362
7366
|
], ctx, null);
|
|
7363
7367
|
}
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
const answer = await runNpm("view", [
|
|
7368
|
+
function latestViewArgs({ package: name, flags }) {
|
|
7369
|
+
return [
|
|
7367
7370
|
"view",
|
|
7368
7371
|
`${name}@latest`,
|
|
7369
7372
|
"version",
|
|
7370
7373
|
"--json",
|
|
7371
7374
|
...flags
|
|
7372
|
-
]
|
|
7375
|
+
];
|
|
7376
|
+
}
|
|
7377
|
+
async function latestVersion(ctx) {
|
|
7378
|
+
const { package: name, label: registry } = ctx.registry;
|
|
7379
|
+
const answer = await runNpm("view", latestViewArgs(ctx.registry), ctx);
|
|
7373
7380
|
const version = versionInViewAnswer(answer);
|
|
7374
7381
|
if (version === void 0) throw noLatest(ctx, `npm answered no version for ${name}@latest on ${registry}: ${answer.trim()}`);
|
|
7375
7382
|
return version;
|
|
@@ -7611,7 +7618,8 @@ function dependencyInstall(cli, install) {
|
|
|
7611
7618
|
runAs,
|
|
7612
7619
|
builtIns: () => ({}),
|
|
7613
7620
|
exitClasses: [],
|
|
7614
|
-
check: (launch) => checkOf$1(cli, install, runAs, launch)
|
|
7621
|
+
check: (launch) => checkOf$1(cli, install, runAs, launch),
|
|
7622
|
+
notice: async () => {}
|
|
7615
7623
|
};
|
|
7616
7624
|
}
|
|
7617
7625
|
//#endregion
|
|
@@ -8688,6 +8696,127 @@ function updateCommand(cli, install, skill, { bin, node }) {
|
|
|
8688
8696
|
});
|
|
8689
8697
|
}
|
|
8690
8698
|
//#endregion
|
|
8699
|
+
//#region ../agent-cli/dist/built-ins/install/release-check.js
|
|
8700
|
+
const CHECK_INTERVAL_MS = 864e5;
|
|
8701
|
+
const checkSchema = object({
|
|
8702
|
+
checkedAt: number(),
|
|
8703
|
+
latest: string().optional(),
|
|
8704
|
+
error: string().optional()
|
|
8705
|
+
});
|
|
8706
|
+
const CHECK_SCRIPT = `
|
|
8707
|
+
const { spawnSync } = require("node:child_process");
|
|
8708
|
+
const { mkdtempSync, renameSync, rmSync, writeFileSync } = require("node:fs");
|
|
8709
|
+
const { tmpdir } = require("node:os");
|
|
8710
|
+
const { join } = require("node:path");
|
|
8711
|
+
const [file, checkedAt, previous, npm, ...args] = process.argv.slice(1);
|
|
8712
|
+
const windows = process.platform === "win32";
|
|
8713
|
+
const cache = mkdtempSync(join(tmpdir(), "release-check-"));
|
|
8714
|
+
const write = (check) => {
|
|
8715
|
+
const temp = file + "." + process.pid + ".tmp";
|
|
8716
|
+
writeFileSync(temp, JSON.stringify({ checkedAt: Number(checkedAt), ...check }));
|
|
8717
|
+
renameSync(temp, file);
|
|
8718
|
+
};
|
|
8719
|
+
const latestOf = (stdout) => {
|
|
8720
|
+
try {
|
|
8721
|
+
const parsed = JSON.parse(stdout);
|
|
8722
|
+
const latest = Array.isArray(parsed) ? parsed.at(-1) : parsed;
|
|
8723
|
+
return typeof latest === "string" && /^\\d+\\.\\d+\\.\\d+/.test(latest) ? latest : undefined;
|
|
8724
|
+
} catch {
|
|
8725
|
+
return undefined;
|
|
8726
|
+
}
|
|
8727
|
+
};
|
|
8728
|
+
try {
|
|
8729
|
+
const answer = spawnSync(windows ? '"' + npm + '"' : npm, args, {
|
|
8730
|
+
encoding: "utf8",
|
|
8731
|
+
shell: windows,
|
|
8732
|
+
windowsHide: true,
|
|
8733
|
+
env: { ...process.env, npm_config_cache: join(cache, "cache"), npm_config_logs_max: "0" },
|
|
8734
|
+
});
|
|
8735
|
+
const latest = answer.status === 0 ? latestOf(answer.stdout) : undefined;
|
|
8736
|
+
if (latest !== undefined) {
|
|
8737
|
+
write({ latest });
|
|
8738
|
+
} else {
|
|
8739
|
+
const said = (answer.stderr || "").trim().split(/\\r?\\n/).slice(0, 5).join("; ");
|
|
8740
|
+
const error = answer.error ? answer.error.message : said || "npm answered " + answer.stdout.trim();
|
|
8741
|
+
write({ latest: previous || undefined, error });
|
|
8742
|
+
}
|
|
8743
|
+
} finally {
|
|
8744
|
+
rmSync(cache, { recursive: true, force: true });
|
|
8745
|
+
}
|
|
8746
|
+
`;
|
|
8747
|
+
function isOff(cliName, env) {
|
|
8748
|
+
return Boolean(env[cliVariable(cliName, "NO_RELEASE_CHECK")] || env.CI);
|
|
8749
|
+
}
|
|
8750
|
+
function cacheFileOf(cliName, env) {
|
|
8751
|
+
const cache = env.XDG_CACHE_HOME || join(env.HOME || env.USERPROFILE || homedir(), ".cache");
|
|
8752
|
+
return join(cache, cliName, "release-check.json");
|
|
8753
|
+
}
|
|
8754
|
+
function parsedCheck(text) {
|
|
8755
|
+
try {
|
|
8756
|
+
return checkSchema.safeParse(JSON.parse(text)).data;
|
|
8757
|
+
} catch {
|
|
8758
|
+
return;
|
|
8759
|
+
}
|
|
8760
|
+
}
|
|
8761
|
+
async function lastCheck(file) {
|
|
8762
|
+
const text = await orMissing(readFile(file, "utf8"));
|
|
8763
|
+
return text === void 0 ? void 0 : parsedCheck(text);
|
|
8764
|
+
}
|
|
8765
|
+
async function writeCheck(file, check) {
|
|
8766
|
+
await mkdir(dirname(file), { recursive: true });
|
|
8767
|
+
const temp = tempPathOf(file);
|
|
8768
|
+
await writeFile(temp, JSON.stringify(check));
|
|
8769
|
+
await rename(temp, file);
|
|
8770
|
+
}
|
|
8771
|
+
async function startCheck(cli, install, file, last, { node }, ctx) {
|
|
8772
|
+
const checkedAt = ctx.clock.now();
|
|
8773
|
+
const previous = last?.latest ?? "";
|
|
8774
|
+
await writeCheck(file, {
|
|
8775
|
+
checkedAt,
|
|
8776
|
+
latest: last?.latest
|
|
8777
|
+
});
|
|
8778
|
+
const npm = globalNpm(cli, install, ctx, node);
|
|
8779
|
+
const { path, env } = npmOf(npm, process.platform === "win32");
|
|
8780
|
+
await ctx.spawnDetached(process.execPath, [
|
|
8781
|
+
"-e",
|
|
8782
|
+
CHECK_SCRIPT,
|
|
8783
|
+
file,
|
|
8784
|
+
String(checkedAt),
|
|
8785
|
+
previous,
|
|
8786
|
+
path,
|
|
8787
|
+
...latestViewArgs(npm.registry)
|
|
8788
|
+
], { env });
|
|
8789
|
+
}
|
|
8790
|
+
function debugLine(cli, ctx, text) {
|
|
8791
|
+
if (isDebug({
|
|
8792
|
+
env: ctx.env,
|
|
8793
|
+
cliName: cli.name
|
|
8794
|
+
})) ctx.progress(`release check failed: ${text}`);
|
|
8795
|
+
}
|
|
8796
|
+
/**
|
|
8797
|
+
* Tells the caller of a newer release that an earlier Release check found, and starts the next
|
|
8798
|
+
* check once the last is a day old.
|
|
8799
|
+
*/
|
|
8800
|
+
async function releaseCheck(cli, install, runAs, launch, ctx) {
|
|
8801
|
+
if (launch.builtIn || launch.bin === void 0 || cli.commands === void 0) return;
|
|
8802
|
+
if (isOff(cli.name, ctx.env)) return;
|
|
8803
|
+
try {
|
|
8804
|
+
await checkAndTell(cli, install, runAs, launch, ctx);
|
|
8805
|
+
} catch (error) {
|
|
8806
|
+
if (errnoOf(error) === void 0 && !(error instanceof CliError)) throw error;
|
|
8807
|
+
debugLine(cli, ctx, messageOf(error));
|
|
8808
|
+
}
|
|
8809
|
+
}
|
|
8810
|
+
async function checkAndTell(cli, install, runAs, launch, ctx) {
|
|
8811
|
+
const file = cacheFileOf(cli.name, ctx.env);
|
|
8812
|
+
const last = await lastCheck(file);
|
|
8813
|
+
if (last?.error !== void 0) debugLine(cli, ctx, last.error);
|
|
8814
|
+
if (last === void 0 || ctx.clock.now() - last.checkedAt >= CHECK_INTERVAL_MS) await startCheck(cli, install, file, last, launch, ctx);
|
|
8815
|
+
const latest = last?.latest;
|
|
8816
|
+
if (latest === void 0 || !isVersion(latest) || compareVersions(latest, cli.version) <= 0) return;
|
|
8817
|
+
ctx.notice(`${cli.name} ${latest} is out, this is ${cli.version}; run \`${runAs} update\``);
|
|
8818
|
+
}
|
|
8819
|
+
//#endregion
|
|
8691
8820
|
//#region ../agent-cli/dist/built-ins/install/global.js
|
|
8692
8821
|
function checkOf(cli, install, { bin, node }) {
|
|
8693
8822
|
return {
|
|
@@ -8722,12 +8851,14 @@ function globalInstall(cli, install) {
|
|
|
8722
8851
|
runAs: runAsOf(cli),
|
|
8723
8852
|
builtIns: (launch) => ({ update: updateCommand(cli, install, skillOf$1(cli, adapter), launch) }),
|
|
8724
8853
|
exitClasses: ["source"],
|
|
8725
|
-
check: (launch) => checkOf(cli, install, launch)
|
|
8854
|
+
check: (launch) => checkOf(cli, install, launch),
|
|
8855
|
+
notice: (launch, ctx) => releaseCheck(cli, install, adapter.runAs, launch, ctx)
|
|
8726
8856
|
};
|
|
8727
8857
|
return adapter;
|
|
8728
8858
|
}
|
|
8729
8859
|
//#endregion
|
|
8730
8860
|
//#region ../agent-cli/dist/built-ins/install/install.js
|
|
8861
|
+
const noNotice = async () => {};
|
|
8731
8862
|
function noInstall(cli) {
|
|
8732
8863
|
const skipped = async () => ({
|
|
8733
8864
|
state: "skipped",
|
|
@@ -8740,7 +8871,8 @@ function noInstall(cli) {
|
|
|
8740
8871
|
check: () => ({
|
|
8741
8872
|
install: skipped,
|
|
8742
8873
|
release: async () => ({ skipped: await skipped() })
|
|
8743
|
-
})
|
|
8874
|
+
}),
|
|
8875
|
+
notice: noNotice
|
|
8744
8876
|
};
|
|
8745
8877
|
}
|
|
8746
8878
|
function installOf(cli) {
|
|
@@ -11178,9 +11310,10 @@ async function runCommand(cli, routed, options, write, baseFormat) {
|
|
|
11178
11310
|
let served = false;
|
|
11179
11311
|
let settled;
|
|
11180
11312
|
try {
|
|
11181
|
-
const outcome = await control.settle(
|
|
11313
|
+
const outcome = await control.settle(noticesOf(cli, {
|
|
11182
11314
|
builtIn,
|
|
11183
|
-
bin: options.bin
|
|
11315
|
+
bin: options.bin,
|
|
11316
|
+
node: options.node
|
|
11184
11317
|
}, ctx).then(() => execute(command, input, ctx, {
|
|
11185
11318
|
dryRun: flags.dryRun,
|
|
11186
11319
|
confirm: flags.confirm,
|
|
@@ -11240,6 +11373,11 @@ function once(build) {
|
|
|
11240
11373
|
throw outcome.error;
|
|
11241
11374
|
};
|
|
11242
11375
|
}
|
|
11376
|
+
async function noticesOf(cli, launch, ctx) {
|
|
11377
|
+
const install = installOf(cli);
|
|
11378
|
+
await skillOf$1(cli, install).notice(launch, ctx);
|
|
11379
|
+
await install.notice(launch, ctx);
|
|
11380
|
+
}
|
|
11243
11381
|
//#endregion
|
|
11244
11382
|
//#region ../agent-cli/dist/style.js
|
|
11245
11383
|
function colourEnabled(env, stdoutIsTTY) {
|
|
@@ -11394,7 +11532,7 @@ async function runProcess(cli) {
|
|
|
11394
11532
|
//#region package.json
|
|
11395
11533
|
var package_default = {
|
|
11396
11534
|
name: "@duyquangnvx/create-agent-cli",
|
|
11397
|
-
version: "0.
|
|
11535
|
+
version: "0.23.0",
|
|
11398
11536
|
description: "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
11399
11537
|
type: "module",
|
|
11400
11538
|
engines: { "node": ">=22.12.0" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@duyquangnvx/create-agent-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Creates a new Agent CLI repository wired to @duyquangnvx/agent-cli.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@duyquangnvx/agent-cli": "0.
|
|
24
|
+
"@duyquangnvx/agent-cli": "0.23.0",
|
|
25
25
|
"tsdown": "0.23.0",
|
|
26
26
|
"zod": "4.6.7"
|
|
27
27
|
},
|