@bussolabs/closeyourit-cli 0.17.0 → 0.18.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.
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ // Genera un documento OpenCLI (https://opencli.org) a partire dal manifest oclif.
3
+ //
4
+ // Perché: `opencli.json` scritto a mano driftava — dichiarava una versione e un elenco di comandi
5
+ // fermi a mesi prima. Qui la fonte è il manifest oclif, che è sempre allineato ai comandi reali,
6
+ // così il file non può più restare indietro. La parte di I/O (leggere il manifest, scrivere il
7
+ // file) vive in `scripts/generate-opencli.mjs`; qui c'è solo la trasformazione pura, testabile.
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.buildOpenCliDocument = buildOpenCliDocument;
10
+ // Versione della specifica OpenCLI a cui il documento si conforma.
11
+ const OPENCLI_VERSION = '0.1';
12
+ const SCHEMA_URL = 'https://opencli.org/draft.json';
13
+ // oclif separa i segmenti dell'id con ':' nel manifest (indipendentemente dal topicSeparator).
14
+ const ID_SEPARATOR = ':';
15
+ // Opzioni globali: iniettate da oclif su ogni comando, quindi dichiarate una volta sola sul
16
+ // comando radice come `recursive`. Sono invarianti strutturali del runtime, non comandi che
17
+ // driftano, perciò vivono qui come costante e non nel manifest.
18
+ const GLOBAL_OPTIONS = [
19
+ { name: '--help', aliases: ['-h'], description: 'Show help.', recursive: true },
20
+ { name: '--version', aliases: ['-V'], description: 'Show CLI version.' },
21
+ { name: '--json', description: 'Format output as json.', recursive: true },
22
+ ];
23
+ // Codici di uscita del CLI: convenzione stabile, condivisa da tutti i comandi.
24
+ const EXIT_CODES = [
25
+ { code: 0, description: 'Success' },
26
+ { code: 1, description: 'Runtime or API error' },
27
+ { code: 2, description: 'Invalid usage (bad flags/arguments)' },
28
+ ];
29
+ const upper = (name) => name.toUpperCase();
30
+ const mapArgument = (arg) => {
31
+ const out = { name: upper(arg.name) };
32
+ if (arg.required)
33
+ out.required = true;
34
+ if (arg.description)
35
+ out.description = arg.description;
36
+ if (arg.options && arg.options.length > 0)
37
+ out.acceptedValues = arg.options;
38
+ if (arg.hidden)
39
+ out.hidden = true;
40
+ return out;
41
+ };
42
+ const mapFlag = (flag) => {
43
+ const out = { name: `--${flag.name}` };
44
+ if (flag.char)
45
+ out.aliases = [`-${flag.char}`];
46
+ if (flag.description)
47
+ out.description = flag.description;
48
+ if (flag.required)
49
+ out.required = true;
50
+ if (flag.hidden)
51
+ out.hidden = true;
52
+ if (flag.type === 'option') {
53
+ const argument = { name: upper(flag.name), required: true };
54
+ if (flag.options && flag.options.length > 0)
55
+ argument.acceptedValues = flag.options;
56
+ out.arguments = [argument];
57
+ }
58
+ return out;
59
+ };
60
+ const substituteBin = (text, bin) => text.replaceAll('<%= config.bin %>', bin);
61
+ const mapExamples = (examples, bin) => {
62
+ if (!examples || examples.length === 0)
63
+ return undefined;
64
+ return examples.map((ex) => substituteBin(typeof ex === 'string' ? ex : ex.command, bin));
65
+ };
66
+ // Applica al nodo-comando i dati del suo comando oclif. Il flag globale --json (helpGroup GLOBAL)
67
+ // non viene ripetuto: è già dichiarato ricorsivo sul comando radice.
68
+ const applyCommand = (node, cmd, bin) => {
69
+ node.description = cmd.summary ?? cmd.description;
70
+ if (cmd.hidden)
71
+ node.hidden = true;
72
+ if (cmd.aliases && cmd.aliases.length > 0)
73
+ node.aliases = cmd.aliases;
74
+ const flags = Object.values(cmd.flags ?? {}).filter((f) => f.helpGroup !== 'GLOBAL');
75
+ if (flags.length > 0)
76
+ node.options = flags.map((f) => mapFlag(f));
77
+ const args = Object.values(cmd.args ?? {});
78
+ if (args.length > 0)
79
+ node.arguments = args.map((a) => mapArgument(a));
80
+ const examples = mapExamples(cmd.examples, bin);
81
+ if (examples)
82
+ node.examples = examples;
83
+ };
84
+ const buildTree = (manifest) => {
85
+ const root = { name: '', path: [], children: new Map() };
86
+ for (const id of Object.keys(manifest.commands)) {
87
+ const segments = id.split(ID_SEPARATOR);
88
+ let node = root;
89
+ const path = [];
90
+ for (const segment of segments) {
91
+ path.push(segment);
92
+ let child = node.children.get(segment);
93
+ if (!child) {
94
+ child = { name: segment, path: [...path], children: new Map() };
95
+ node.children.set(segment, child);
96
+ }
97
+ node = child;
98
+ }
99
+ node.command = manifest.commands[id];
100
+ }
101
+ return root;
102
+ };
103
+ const toOpenCliCommand = (node, bin, topics) => {
104
+ const out = { name: node.name };
105
+ if (node.command) {
106
+ applyCommand(out, node.command, bin);
107
+ }
108
+ else {
109
+ // Topic contenitore: nessun comando proprio, la descrizione arriva da package.json (se c'è).
110
+ const topic = topics[node.path.join(ID_SEPARATOR)];
111
+ if (topic?.description)
112
+ out.description = topic.description;
113
+ }
114
+ if (node.children.size > 0) {
115
+ out.commands = [...node.children.values()]
116
+ .sort((a, b) => a.name.localeCompare(b.name))
117
+ .map((child) => toOpenCliCommand(child, bin, topics));
118
+ }
119
+ return out;
120
+ };
121
+ /** Costruisce il documento OpenCLI completo dal manifest oclif e dai metadati di package.json. */
122
+ function buildOpenCliDocument(input) {
123
+ const { manifest, bin, topics = {} } = input;
124
+ const info = { title: bin, version: manifest.version };
125
+ if (input.summary)
126
+ info.summary = input.summary;
127
+ if (input.description)
128
+ info.description = input.description;
129
+ if (input.license && (input.license.name || input.license.identifier || input.license.url)) {
130
+ info.license = input.license;
131
+ }
132
+ const tree = buildTree(manifest);
133
+ const command = {
134
+ name: bin,
135
+ ...(input.description ? { description: input.description } : {}),
136
+ options: GLOBAL_OPTIONS,
137
+ exitCodes: EXIT_CODES,
138
+ commands: [...tree.children.values()]
139
+ .sort((a, b) => a.name.localeCompare(b.name))
140
+ .map((child) => toOpenCliCommand(child, bin, topics)),
141
+ };
142
+ return {
143
+ $schema: SCHEMA_URL,
144
+ opencli: OPENCLI_VERSION,
145
+ info,
146
+ conventions: { groupOptions: true, optionSeparator: ' ' },
147
+ command,
148
+ };
149
+ }
@@ -142,6 +142,16 @@ const STATUS_TONE = {
142
142
  active: 'ok',
143
143
  expired: 'neutral',
144
144
  revoked: 'neutral',
145
+ // vulnerability advisory severity (GHSA scale). `unknown` is already neutral above — which is the
146
+ // right reading: an advisory OSV didn't classify is not a severe advisory.
147
+ critical: 'down',
148
+ high: 'down',
149
+ moderate: 'warn',
150
+ low: 'neutral',
151
+ // runtime support state
152
+ ending_soon: 'warn',
153
+ eol: 'down',
154
+ supported: 'ok',
145
155
  };
146
156
  /**
147
157
  * Badge colour (Tailwind family name) → tone. Ticket status/priority labels are org-customisable,