@leemour/cli-core 0.2.0 → 0.4.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/README.md +26 -1
- package/dist/commands/index.js +4 -2
- package/dist/commands/index.js.map +1 -1
- package/dist/completion/index.d.ts +37 -0
- package/dist/completion/index.d.ts.map +1 -0
- package/dist/completion/index.js +87 -0
- package/dist/completion/index.js.map +1 -0
- package/dist/update/index.d.ts +51 -0
- package/dist/update/index.d.ts.map +1 -0
- package/dist/update/index.js +101 -0
- package/dist/update/index.js.map +1 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ keyring behind a testable seam, and injectable clocks.
|
|
|
7
7
|
Extracted from [`brazecli`](https://github.com/leemour/brazecli), where each piece earned its
|
|
8
8
|
shape, and shared with [`max-cli`](https://github.com/leemour/max-cli).
|
|
9
9
|
|
|
10
|
-
**Status: 0.
|
|
10
|
+
**Status: 0.4.0.** Published on npm, used by `max-cli`. Both extraction steps have landed: the
|
|
11
11
|
files that move unchanged, and the ones that needed a parameter threaded through. 75 tests.
|
|
12
12
|
|
|
13
13
|
## The rule this package exists to keep
|
|
@@ -44,6 +44,8 @@ streams.stderr // []
|
|
|
44
44
|
| `retry` | full-jitter backoff, and the distinction between "no answer came" and "safe to repeat" |
|
|
45
45
|
| `/testing` | `captureStreams`, `memoryKeyring`, `brokenKeyring`, `fakeClock` |
|
|
46
46
|
| `/commands` | the command registry: `describeProgram`, `annotate`, `flatten` — see below |
|
|
47
|
+
| `/completion` | shell completion over the registry: `suggest`, `formatSuggestions` — see below |
|
|
48
|
+
| `/update` | keeping an install current: `installerOf`, `updateCommand`, `latestVersion`, `mayNotify` — see below |
|
|
47
49
|
|
|
48
50
|
**Nothing in the root export is HTTP.** Status classification, `Retry-After` parsing and the fetch
|
|
49
51
|
seam live in `@leemour/cli-core/http`, so a CLI that speaks a socket never depends on a stack it
|
|
@@ -72,6 +74,21 @@ Each option says whether it `takesValue` and, separately, whether it is `mandato
|
|
|
72
74
|
Commander's `required`, which means "takes a value when given" — plus its choices, default,
|
|
73
75
|
environment variable, the options it `conflicts` with and the values it `implies`.
|
|
74
76
|
|
|
77
|
+
**Shell completion is `@leemour/cli-core/completion`**, built on the registry. `suggest` takes the
|
|
78
|
+
words typed so far and answers what may come next: a command, an action, an option, one of its
|
|
79
|
+
values, or an argument's values from a source the CLI hands in — a local cache, never the network,
|
|
80
|
+
because a shell calls it on every Tab. `formatSuggestions` writes the answer in the protocol of the
|
|
81
|
+
shell scripts [`@bomb.sh/tab`](https://github.com/bombshell-dev/tab) generates, so the CLI prints
|
|
82
|
+
those scripts with tab and answers `<cli> complete -- <words>` with this; cli-core itself does not
|
|
83
|
+
depend on tab.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { formatSuggestions, suggest } from "@leemour/cli-core/completion"
|
|
87
|
+
|
|
88
|
+
const words = argv.slice(argv.indexOf("--") + 1)
|
|
89
|
+
streams.data(formatSuggestions(suggest({ commands, globalOptions, words, sources: { arguments: { chat: chatNames } } })))
|
|
90
|
+
```
|
|
91
|
+
|
|
75
92
|
**Two traps worth knowing before you use the credential store.** The OS keyring is global: an entry
|
|
76
93
|
is addressed by service and account and knows nothing about which config directory asked for it, so
|
|
77
94
|
a throwaway config directory silently overwrites the real secret unless you pass `isolated: true`.
|
|
@@ -82,6 +99,14 @@ second line of defence, not the first.
|
|
|
82
99
|
no ambient clock. That is what makes a timeout test finish instantly and a keyring test incapable
|
|
83
100
|
of reaching a real keychain.
|
|
84
101
|
|
|
102
|
+
**Keeping an install current is `@leemour/cli-core/update`.** `installerOf(realpath(script))` says
|
|
103
|
+
which package manager put the CLI there — pnpm, npm or bun, measured on real installs — and
|
|
104
|
+
`updateCommand` gives the argv that updates it; a checkout or `npx` gets none. `latestVersion` asks
|
|
105
|
+
npm with a timeout and answers `undefined` on any failure. `mayNotify` says whether a person is at a
|
|
106
|
+
terminal to be told: never in JSON, a pipe, `--quiet`, `CI` or with `NO_UPDATE_NOTIFIER`. Nothing
|
|
107
|
+
here updates or prints by itself — a CLI holding somebody's credentials must not change itself
|
|
108
|
+
unasked.
|
|
109
|
+
|
|
85
110
|
## Where secrets actually go, per platform
|
|
86
111
|
|
|
87
112
|
The keyring is real on every desktop and absent on most servers, so the fallback is not an edge
|
package/dist/commands/index.js
CHANGED
|
@@ -5,7 +5,7 @@ export const annotate = (command, meta) => {
|
|
|
5
5
|
return command;
|
|
6
6
|
};
|
|
7
7
|
export const metaOf = (command) => labels.get(command) ?? {};
|
|
8
|
-
export const describeProgram = (root) => root.
|
|
8
|
+
export const describeProgram = (root) => visible(root).map((child) => describe(child, root.name(), []));
|
|
9
9
|
export const describeOptions = (command) => command.options.filter((option) => !option.hidden).map(describeOption);
|
|
10
10
|
export const flatten = (commands) => commands.flatMap((command) => [command, ...flatten(command.commands)]);
|
|
11
11
|
const describe = (command, cli, parents) => {
|
|
@@ -31,9 +31,11 @@ const describe = (command, cli, parents) => {
|
|
|
31
31
|
...meta,
|
|
32
32
|
arguments: args,
|
|
33
33
|
options,
|
|
34
|
-
commands: command.
|
|
34
|
+
commands: visible(command).map((child) => describe(child, cli, path)),
|
|
35
35
|
};
|
|
36
36
|
};
|
|
37
|
+
// `addCommand(command, { hidden: true })` sets only this; Commander has no public getter for it.
|
|
38
|
+
const visible = (command) => command.commands.filter((child) => !child._hidden);
|
|
37
39
|
const describeArgument = (argument) => ({
|
|
38
40
|
name: argument.name(),
|
|
39
41
|
required: argument.required,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAuEA,MAAM,MAAM,GAAG,IAAI,OAAO,EAAwB,CAAA;AAElD,qGAAqG;AACrG,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAoB,OAAU,EAAE,IAAiB,EAAK,EAAE;IAC9E,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;IACxD,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAe,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;AAElF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,IAAa,EAAiB,EAAE,CAC9D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell completion over the command registry: which word may come next, as data.
|
|
3
|
+
*
|
|
4
|
+
* It speaks the protocol of the shell scripts `@bomb.sh/tab` generates — the shell runs
|
|
5
|
+
* `<cli> complete -- <words>` and reads `value<TAB>description` lines ending in `:<directive>` —
|
|
6
|
+
* so a CLI prints those scripts with tab and answers the requests with this. Nothing here reads a
|
|
7
|
+
* terminal, a file or the network: the words come in, the suggestions go out, and where values come
|
|
8
|
+
* from (a local cache, a config file) is the CLI's business, handed in as `sources`.
|
|
9
|
+
*/
|
|
10
|
+
import type { CommandInfo, OptionInfo } from "../commands/index.js";
|
|
11
|
+
export interface Suggestion {
|
|
12
|
+
value: string;
|
|
13
|
+
description: string;
|
|
14
|
+
}
|
|
15
|
+
/** Values for one argument or option. Called on every Tab, so it must be cheap and must not connect anywhere. */
|
|
16
|
+
export type Values = () => readonly (string | Suggestion)[];
|
|
17
|
+
export interface CompletionSources {
|
|
18
|
+
/** By argument name, as registered: `chat`, `person`. */
|
|
19
|
+
arguments?: Readonly<Record<string, Values>>;
|
|
20
|
+
/** By long option name without dashes: `chat` for `--chat <id>`. */
|
|
21
|
+
options?: Readonly<Record<string, Values>>;
|
|
22
|
+
/** Offered alongside the top-level commands — a profile name that may come first, say. */
|
|
23
|
+
firstWord?: Values;
|
|
24
|
+
}
|
|
25
|
+
export interface CompletionRequest {
|
|
26
|
+
commands: readonly CommandInfo[];
|
|
27
|
+
globalOptions?: readonly OptionInfo[];
|
|
28
|
+
/** Everything after `complete --`; the last word is the one being typed, `""` for a fresh one. */
|
|
29
|
+
words: readonly string[];
|
|
30
|
+
sources?: CompletionSources;
|
|
31
|
+
}
|
|
32
|
+
/** Tells the shell not to fall back to file names when nothing matched. */
|
|
33
|
+
export declare const NO_FILE_COMPLETION = 4;
|
|
34
|
+
export declare const suggest: ({ commands, globalOptions, words, sources }: CompletionRequest) => Suggestion[];
|
|
35
|
+
/** The answer in the form the shell script reads. */
|
|
36
|
+
export declare const formatSuggestions: (suggestions: readonly Suggestion[]) => string;
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/completion/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAgB,WAAW,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEjF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,iHAAiH;AACjH,MAAM,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,GAAG,UAAU,CAAC,EAAE,CAAA;AAE3D,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,SAAS,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5C,oEAAoE;IACpE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1C,0FAA0F;IAC1F,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAA;IAChC,aAAa,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC,kGAAkG;IAClG,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B;AAED,2EAA2E;AAC3E,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,eAAO,MAAM,OAAO,GAAI,6CAAuD,iBAAiB,KAAG,UAAU,EA2C5G,CAAA;AAED,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,GAAI,aAAa,SAAS,UAAU,EAAE,KAAG,MAC4C,CAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Tells the shell not to fall back to file names when nothing matched. */
|
|
2
|
+
export const NO_FILE_COMPLETION = 4;
|
|
3
|
+
export const suggest = ({ commands, globalOptions = [], words, sources = {} }) => {
|
|
4
|
+
const typed = words.at(-1) ?? "";
|
|
5
|
+
const before = words.slice(0, -1);
|
|
6
|
+
let level = commands;
|
|
7
|
+
let current;
|
|
8
|
+
let positional = 0;
|
|
9
|
+
let pending;
|
|
10
|
+
for (const word of before) {
|
|
11
|
+
if (pending) {
|
|
12
|
+
pending = undefined;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (word.startsWith("-")) {
|
|
16
|
+
const option = findOption(word, current, globalOptions);
|
|
17
|
+
if (option?.takesValue && !word.includes("="))
|
|
18
|
+
pending = option;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const child = level.find((command) => command.name === word);
|
|
22
|
+
if (child) {
|
|
23
|
+
current = child;
|
|
24
|
+
level = child.commands;
|
|
25
|
+
positional = 0;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
positional += 1;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const candidates = pending
|
|
32
|
+
? optionValues(pending, sources)
|
|
33
|
+
: typed.startsWith("-")
|
|
34
|
+
? typed.includes("=")
|
|
35
|
+
? valuesAfterEquals(typed, current, globalOptions, sources)
|
|
36
|
+
: optionNames(current, globalOptions)
|
|
37
|
+
: [
|
|
38
|
+
...level.filter((command) => command.state !== "deprecated").map(asSuggestion),
|
|
39
|
+
...(current ? argumentValues(current.arguments, positional, sources) : []),
|
|
40
|
+
...(current ? [] : resolve(sources.firstWord)),
|
|
41
|
+
];
|
|
42
|
+
const unique = new Map(candidates.map((candidate) => [candidate.value, candidate]));
|
|
43
|
+
return [...unique.values()].filter(({ value }) => value.startsWith(typed));
|
|
44
|
+
};
|
|
45
|
+
/** The answer in the form the shell script reads. */
|
|
46
|
+
export const formatSuggestions = (suggestions) => [...suggestions.map(({ value, description }) => `${value}\t${description}`), `:${NO_FILE_COMPLETION}`].join("\n");
|
|
47
|
+
const asSuggestion = (command) => ({
|
|
48
|
+
value: command.name,
|
|
49
|
+
description: command.summary ?? command.description,
|
|
50
|
+
});
|
|
51
|
+
const longName = (option) => option.flags.match(/--([\w-]+)/)?.[1];
|
|
52
|
+
const findOption = (word, current, globals) => {
|
|
53
|
+
const name = word.replace(/^-+/, "").split("=")[0];
|
|
54
|
+
const matches = (option) => longName(option) === name || option.flags.split(/[ ,]+/).includes(`-${name}`);
|
|
55
|
+
return [...(current?.options ?? []), ...globals].find(matches);
|
|
56
|
+
};
|
|
57
|
+
const optionNames = (current, globals) => [...(current?.options ?? []), ...globals].flatMap((option) => {
|
|
58
|
+
const name = longName(option);
|
|
59
|
+
return name ? [{ value: `--${name}`, description: option.description }] : [];
|
|
60
|
+
});
|
|
61
|
+
/** `--kind <dialog|group|channel>`: Commander does not know these as choices, but the flags say them. */
|
|
62
|
+
const inlineChoices = (flags) => flags.match(/<([^>]*\|[^>]*)>/)?.[1]?.split("|") ?? [];
|
|
63
|
+
const optionValues = (option, sources) => {
|
|
64
|
+
const name = longName(option);
|
|
65
|
+
return [
|
|
66
|
+
...[...(option.choices ?? []), ...inlineChoices(option.flags)].map((value) => ({ value, description: "" })),
|
|
67
|
+
...resolve(name ? sources.options?.[name] : undefined),
|
|
68
|
+
];
|
|
69
|
+
};
|
|
70
|
+
const valuesAfterEquals = (typed, current, globals, sources) => {
|
|
71
|
+
const [flag] = typed.split("=");
|
|
72
|
+
const option = findOption(flag ?? "", current, globals);
|
|
73
|
+
if (!option?.takesValue)
|
|
74
|
+
return [];
|
|
75
|
+
return optionValues(option, sources).map(({ value, description }) => ({ value: `${flag}=${value}`, description }));
|
|
76
|
+
};
|
|
77
|
+
const argumentValues = (args, position, sources) => {
|
|
78
|
+
const argument = args[position] ?? (args.at(-1)?.variadic ? args.at(-1) : undefined);
|
|
79
|
+
if (!argument)
|
|
80
|
+
return [];
|
|
81
|
+
return [
|
|
82
|
+
...(argument.choices ?? []).map((value) => ({ value, description: "" })),
|
|
83
|
+
...resolve(sources.arguments?.[argument.name]),
|
|
84
|
+
];
|
|
85
|
+
};
|
|
86
|
+
const resolve = (values) => (values?.() ?? []).map((value) => (typeof value === "string" ? { value, description: "" } : value));
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/completion/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping an installed CLI current: which package manager put it here, what command updates it,
|
|
3
|
+
* whether npm has something newer, and whether a person should be told.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here updates anything by itself, and nothing here decides to print: a CLI that holds
|
|
6
|
+
* somebody's credentials must not change itself unasked, and a line on the terminal is for a
|
|
7
|
+
* person, never for a script or an agent reading JSON. The CLI asks and decides.
|
|
8
|
+
*/
|
|
9
|
+
import * as v from "valibot";
|
|
10
|
+
import type { FetchLike } from "../http/index.js";
|
|
11
|
+
export type Installer = "pnpm" | "npm" | "bun" | "npx" | "checkout" | "unknown";
|
|
12
|
+
/**
|
|
13
|
+
* Read from where the running file really lives — `realpath` of the script, not `which`: the
|
|
14
|
+
* command on `PATH` is a shim or a link, and only its target says who installed it. Paths measured
|
|
15
|
+
* on 2026-09-24 by installing into throwaway prefixes.
|
|
16
|
+
*/
|
|
17
|
+
export declare const installerOf: (scriptPath: string) => Installer;
|
|
18
|
+
/** The argv that updates a global install, or `undefined` where there is nothing to run. */
|
|
19
|
+
export declare const updateCommand: (installer: Installer, packageName: string) => string[] | undefined;
|
|
20
|
+
/** Plain `x.y.z` only: a pre-release is never offered, so it never counts as newer. */
|
|
21
|
+
export declare const isNewer: (candidate: string, current: string) => boolean;
|
|
22
|
+
/** The `latest` version on npm, or `undefined` on any failure — a hint may never fail a command. */
|
|
23
|
+
export declare const latestVersion: (packageName: string, fetch: FetchLike, { timeoutMs }?: {
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}) => Promise<string | undefined>;
|
|
26
|
+
export declare const CHECK_EVERY_MS: number;
|
|
27
|
+
export interface NotifyContext {
|
|
28
|
+
/** `pretty`, `json` or `jsonl` — the line exists only for the first. */
|
|
29
|
+
format: string;
|
|
30
|
+
stderrIsTTY: boolean;
|
|
31
|
+
quiet: boolean;
|
|
32
|
+
/** The setting; `false` turns the check off. */
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
installer: Installer;
|
|
35
|
+
env: Readonly<Record<string, string | undefined>>;
|
|
36
|
+
/** Extra variables that turn it off, for the CLI's own prefix: `MAX_NO_UPDATE_CHECK`. */
|
|
37
|
+
offVariables?: readonly string[];
|
|
38
|
+
}
|
|
39
|
+
/** Whether a person is at a terminal who could be told — the only case a check is worth making. */
|
|
40
|
+
export declare const mayNotify: ({ format, stderrIsTTY, quiet, enabled, installer, env, offVariables, }: NotifyContext) => boolean;
|
|
41
|
+
declare const updateState: v.ObjectSchema<{
|
|
42
|
+
readonly checkedAt: v.NumberSchema<undefined>;
|
|
43
|
+
readonly latest: v.OptionalSchema<v.StringSchema<undefined>, undefined>;
|
|
44
|
+
}, undefined>;
|
|
45
|
+
export type UpdateState = v.InferOutput<typeof updateState>;
|
|
46
|
+
/** What the last check found. A missing or unreadable file is "never checked", not an error. */
|
|
47
|
+
export declare const readUpdateState: (path: string) => UpdateState | undefined;
|
|
48
|
+
export declare const writeUpdateState: (path: string, state: UpdateState) => void;
|
|
49
|
+
export declare const checkIsDue: (state: UpdateState | undefined, now: number) => boolean;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/update/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAA;AAE/E;;;;GAIG;AACH,eAAO,MAAM,WAAW,GAAI,YAAY,MAAM,KAAG,SAOhD,CAAA;AAED,4FAA4F;AAC5F,eAAO,MAAM,aAAa,GAAI,WAAW,SAAS,EAAE,aAAa,MAAM,KAAG,MAAM,EAAE,GAAG,SAYpF,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,OAAO,GAAI,WAAW,MAAM,EAAE,SAAS,MAAM,KAAG,OAS5D,CAAA;AAED,oGAAoG;AACpG,eAAO,MAAM,aAAa,GACxB,aAAa,MAAM,EACnB,OAAO,SAAS,EAChB,gBAAsB;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,KAChD,OAAO,CAAC,MAAM,GAAG,SAAS,CAY5B,CAAA;AAED,eAAO,MAAM,cAAc,QAAsB,CAAA;AAEjD,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,OAAO,CAAA;IACd,gDAAgD;IAChD,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,SAAS,CAAA;IACpB,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAA;IACjD,yFAAyF;IACzF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACjC;AAED,mGAAmG;AACnG,eAAO,MAAM,SAAS,GAAI,wEAQvB,aAAa,KAAG,OAQuB,CAAA;AAE1C,QAAA,MAAM,WAAW;;;aAAsE,CAAA;AACvF,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,WAAW,CAAC,CAAA;AAE3D,gGAAgG;AAChG,eAAO,MAAM,eAAe,GAAI,MAAM,MAAM,KAAG,WAAW,GAAG,SAO5D,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,MAAM,MAAM,EAAE,OAAO,WAAW,KAAG,IAMnE,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,OAAO,WAAW,GAAG,SAAS,EAAE,KAAK,MAAM,KAAG,OACT,CAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeping an installed CLI current: which package manager put it here, what command updates it,
|
|
3
|
+
* whether npm has something newer, and whether a person should be told.
|
|
4
|
+
*
|
|
5
|
+
* Nothing here updates anything by itself, and nothing here decides to print: a CLI that holds
|
|
6
|
+
* somebody's credentials must not change itself unasked, and a line on the terminal is for a
|
|
7
|
+
* person, never for a script or an agent reading JSON. The CLI asks and decides.
|
|
8
|
+
*/
|
|
9
|
+
import * as v from "valibot";
|
|
10
|
+
import { loadConfigFile, saveConfigFile } from "../config.js";
|
|
11
|
+
/**
|
|
12
|
+
* Read from where the running file really lives — `realpath` of the script, not `which`: the
|
|
13
|
+
* command on `PATH` is a shim or a link, and only its target says who installed it. Paths measured
|
|
14
|
+
* on 2026-09-24 by installing into throwaway prefixes.
|
|
15
|
+
*/
|
|
16
|
+
export const installerOf = (scriptPath) => {
|
|
17
|
+
const path = scriptPath.replaceAll("\\", "/");
|
|
18
|
+
if (path.includes("/_npx/"))
|
|
19
|
+
return "npx";
|
|
20
|
+
if (path.includes("/install/global/node_modules/"))
|
|
21
|
+
return "bun";
|
|
22
|
+
if (/\/\.?pnpm\//.test(path))
|
|
23
|
+
return "pnpm";
|
|
24
|
+
if (path.includes("/node_modules/"))
|
|
25
|
+
return "npm";
|
|
26
|
+
return path.includes("/dist/") ? "checkout" : "unknown";
|
|
27
|
+
};
|
|
28
|
+
/** The argv that updates a global install, or `undefined` where there is nothing to run. */
|
|
29
|
+
export const updateCommand = (installer, packageName) => {
|
|
30
|
+
const target = `${packageName}@latest`;
|
|
31
|
+
switch (installer) {
|
|
32
|
+
case "pnpm":
|
|
33
|
+
return ["pnpm", "add", "-g", target];
|
|
34
|
+
case "npm":
|
|
35
|
+
return ["npm", "install", "-g", target];
|
|
36
|
+
case "bun":
|
|
37
|
+
return ["bun", "add", "-g", target];
|
|
38
|
+
default:
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/** Plain `x.y.z` only: a pre-release is never offered, so it never counts as newer. */
|
|
43
|
+
export const isNewer = (candidate, current) => {
|
|
44
|
+
const parse = (version) => (/^\d+\.\d+\.\d+$/.test(version) ? version.split(".").map(Number) : undefined);
|
|
45
|
+
const a = parse(candidate);
|
|
46
|
+
const b = parse(current);
|
|
47
|
+
if (!a || !b)
|
|
48
|
+
return false;
|
|
49
|
+
for (let i = 0; i < 3; i++) {
|
|
50
|
+
if (a[i] !== b[i])
|
|
51
|
+
return a[i] > b[i];
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
/** The `latest` version on npm, or `undefined` on any failure — a hint may never fail a command. */
|
|
56
|
+
export const latestVersion = async (packageName, fetch, { timeoutMs = 1000 } = {}) => {
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
59
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
60
|
+
headers: { accept: "application/json" },
|
|
61
|
+
});
|
|
62
|
+
if (!response.ok)
|
|
63
|
+
return undefined;
|
|
64
|
+
const { version } = (await response.json());
|
|
65
|
+
return typeof version === "string" ? version : undefined;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
export const CHECK_EVERY_MS = 24 * 60 * 60 * 1000;
|
|
72
|
+
/** Whether a person is at a terminal who could be told — the only case a check is worth making. */
|
|
73
|
+
export const mayNotify = ({ format, stderrIsTTY, quiet, enabled, installer, env, offVariables = [], }) => enabled &&
|
|
74
|
+
format === "pretty" &&
|
|
75
|
+
stderrIsTTY &&
|
|
76
|
+
!quiet &&
|
|
77
|
+
updateCommand(installer, "any") !== undefined &&
|
|
78
|
+
!env.CI &&
|
|
79
|
+
!env.NO_UPDATE_NOTIFIER &&
|
|
80
|
+
offVariables.every((name) => !env[name]);
|
|
81
|
+
const updateState = v.object({ checkedAt: v.number(), latest: v.optional(v.string()) });
|
|
82
|
+
/** What the last check found. A missing or unreadable file is "never checked", not an error. */
|
|
83
|
+
export const readUpdateState = (path) => {
|
|
84
|
+
try {
|
|
85
|
+
const state = loadConfigFile(path, updateState, () => ({ checkedAt: 0 }));
|
|
86
|
+
return state.checkedAt > 0 ? state : undefined;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
export const writeUpdateState = (path, state) => {
|
|
93
|
+
try {
|
|
94
|
+
saveConfigFile(path, state);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// A state file that cannot be written means asking npm again tomorrow, nothing worse.
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export const checkIsDue = (state, now) => state === undefined || now - state.checkedAt >= CHECK_EVERY_MS;
|
|
101
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/update/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leemour/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -34,6 +34,14 @@
|
|
|
34
34
|
"./commands": {
|
|
35
35
|
"types": "./dist/commands/index.d.ts",
|
|
36
36
|
"default": "./dist/commands/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./completion": {
|
|
39
|
+
"types": "./dist/completion/index.d.ts",
|
|
40
|
+
"default": "./dist/completion/index.js"
|
|
41
|
+
},
|
|
42
|
+
"./update": {
|
|
43
|
+
"types": "./dist/update/index.d.ts",
|
|
44
|
+
"default": "./dist/update/index.js"
|
|
37
45
|
}
|
|
38
46
|
},
|
|
39
47
|
"scripts": {
|