@bussolabs/closeyourit-cli 0.18.0 → 0.20.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 +29 -5
- package/dist/base.d.ts +11 -0
- package/dist/base.js +31 -0
- package/dist/commands/alerts/notifications/read-all.js +3 -0
- package/dist/commands/alerts/preferences/show.js +2 -0
- package/dist/commands/ideas/convert.d.ts +2 -2
- package/dist/commands/ideas/convert.js +25 -8
- package/dist/commands/kb/create.js +29 -6
- package/dist/commands/kb/update.js +26 -5
- package/dist/commands/logout.js +3 -0
- package/dist/commands/org/show.js +2 -0
- package/dist/commands/personal/import.js +2 -2
- package/dist/commands/personal/run.js +1 -1
- package/dist/commands/run.js +1 -1
- package/dist/commands/secrets/import.js +2 -2
- package/dist/commands/seo/ignore.d.ts +12 -0
- package/dist/commands/seo/ignore.js +27 -0
- package/dist/commands/seo/list.d.ts +13 -0
- package/dist/commands/seo/list.js +52 -0
- package/dist/commands/seo/pages.d.ts +11 -0
- package/dist/commands/seo/pages.js +44 -0
- package/dist/commands/seo/promote.d.ts +9 -0
- package/dist/commands/seo/promote.js +22 -0
- package/dist/commands/seo/reopen.d.ts +9 -0
- package/dist/commands/seo/reopen.js +21 -0
- package/dist/commands/seo/rescan.d.ts +9 -0
- package/dist/commands/seo/rescan.js +21 -0
- package/dist/commands/seo/show.d.ts +9 -0
- package/dist/commands/seo/show.js +35 -0
- package/dist/commands/seo-sites/create.d.ts +14 -0
- package/dist/commands/seo-sites/create.js +43 -0
- package/dist/commands/seo-sites/delete.d.ts +9 -0
- package/dist/commands/seo-sites/delete.js +21 -0
- package/dist/commands/seo-sites/list.d.ts +10 -0
- package/dist/commands/seo-sites/list.js +38 -0
- package/dist/commands/seo-sites/update.d.ts +17 -0
- package/dist/commands/seo-sites/update.js +45 -0
- package/dist/commands/servers/ignored-containers.d.ts +15 -0
- package/dist/commands/servers/ignored-containers.js +51 -0
- package/dist/commands/tickets/comment.d.ts +14 -3
- package/dist/commands/tickets/comment.js +29 -1
- package/dist/commands/tickets/create.d.ts +6 -6
- package/dist/commands/tickets/create.js +21 -2
- package/dist/commands/tickets/report-show.d.ts +20 -0
- package/dist/commands/tickets/report-show.js +38 -0
- package/dist/commands/tickets/report-versions.d.ts +19 -0
- package/dist/commands/tickets/report-versions.js +40 -0
- package/dist/commands/tickets/report.d.ts +24 -0
- package/dist/commands/tickets/report.js +69 -0
- package/dist/commands/tickets/status.d.ts +1 -1
- package/dist/commands/tickets/status.js +14 -4
- package/dist/commands/tickets/update.d.ts +25 -6
- package/dist/commands/tickets/update.js +144 -9
- package/dist/commands/whoami.js +3 -0
- package/dist/errors/error-codes.d.ts +9 -0
- package/dist/errors/error-codes.js +14 -0
- package/dist/errors/input-error.d.ts +16 -0
- package/dist/errors/input-error.js +42 -0
- package/dist/lib/alert-events.d.ts +8 -1
- package/dist/lib/alert-events.js +37 -1
- package/dist/lib/limits.d.ts +59 -0
- package/dist/lib/limits.js +92 -0
- package/dist/lib/stdin.d.ts +1 -2
- package/dist/lib/stdin.js +1 -2
- package/dist/lib/subprocess.d.ts +1 -1
- package/dist/lib/subprocess.js +1 -1
- package/dist/lib/ticket-body.d.ts +22 -6
- package/dist/lib/ticket-body.js +71 -9
- package/dist/lib/ticket-lookup.d.ts +53 -0
- package/dist/lib/ticket-lookup.js +78 -0
- package/dist/lib/ticket-report.d.ts +7 -0
- package/dist/lib/ticket-report.js +14 -0
- package/oclif.manifest.json +3596 -2674
- package/opencli.json +698 -54
- package/package.json +8 -8
package/dist/lib/alert-events.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ALERT_EVENT_TYPES = void 0;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Event types accepted by alert rules — mirror of the backend enum (Alerting::Rule).
|
|
6
|
+
*
|
|
7
|
+
* CYRA-519: the list had stopped at the first fifteen, so `--event-type` rejected every type added
|
|
8
|
+
* since (containers, replication, inodes, automation, vulnerabilities…) — the flag looked broken
|
|
9
|
+
* while the backend accepted them. Keep this in the same order as the backend enum: appending there
|
|
10
|
+
* means appending here.
|
|
11
|
+
*/
|
|
5
12
|
exports.ALERT_EVENT_TYPES = [
|
|
6
13
|
'error_new',
|
|
7
14
|
'error_regression',
|
|
@@ -18,4 +25,33 @@ exports.ALERT_EVENT_TYPES = [
|
|
|
18
25
|
'server_temp',
|
|
19
26
|
'server_service_failed',
|
|
20
27
|
'server_smart_failing',
|
|
28
|
+
'error_spike',
|
|
29
|
+
'log_alert',
|
|
30
|
+
'server_db_down',
|
|
31
|
+
'server_db_connections',
|
|
32
|
+
'server_replication_lag',
|
|
33
|
+
'agents_stalled',
|
|
34
|
+
'server_container_down',
|
|
35
|
+
'agents_host_failing',
|
|
36
|
+
'uptime_slow',
|
|
37
|
+
'analytics_traffic_drop',
|
|
38
|
+
'analytics_traffic_spike',
|
|
39
|
+
'idea_created',
|
|
40
|
+
'idea_commented',
|
|
41
|
+
'workload_due_soon',
|
|
42
|
+
'dataset_training_completed',
|
|
43
|
+
'dataset_training_failed',
|
|
44
|
+
'agents_host_stale',
|
|
45
|
+
'vulnerability_new',
|
|
46
|
+
'runtime_eol',
|
|
47
|
+
'embedding_down',
|
|
48
|
+
'server_container_up',
|
|
49
|
+
'server_db_connection_usage',
|
|
50
|
+
'server_data_volume_disk',
|
|
51
|
+
'server_inode',
|
|
52
|
+
'server_replication_down',
|
|
53
|
+
'server_replication_up',
|
|
54
|
+
'server_container_restart_loop',
|
|
55
|
+
'server_container_stable',
|
|
56
|
+
'seo_issue_new',
|
|
21
57
|
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tetti di lunghezza dei campi di testo, come li applica il server (CYCL-32).
|
|
3
|
+
*
|
|
4
|
+
* Prima erano scoperti solo sbagliando: l'aiuto non li nominava e il rifiuto arrivava dopo aver
|
|
5
|
+
* scritto tutto. Qui vivono i numeri — copia dichiarata di `Knowledge::Constants`,
|
|
6
|
+
* `Ticketing::Constants` e `App::Constants::COMMENT_MAX_CHARS` — che le descrizioni dei flag
|
|
7
|
+
* mostrano e che i comandi verificano prima di spedire, così i campi troppo lunghi si scoprono
|
|
8
|
+
* tutti insieme e senza chiamare il server.
|
|
9
|
+
*/
|
|
10
|
+
export declare const LENGTH_LIMITS: {
|
|
11
|
+
/** Titolo di un ticket e di una pagina di conoscenza. */
|
|
12
|
+
readonly title: 255;
|
|
13
|
+
readonly description: 4000;
|
|
14
|
+
readonly technicalAnalysis: 1500;
|
|
15
|
+
readonly comment: 240;
|
|
16
|
+
readonly report: 20000;
|
|
17
|
+
readonly pageBody: 4000;
|
|
18
|
+
readonly techSpec: 1500;
|
|
19
|
+
readonly reviewNote: 240;
|
|
20
|
+
};
|
|
21
|
+
/** Un campo da misurare: il flag come lo scrive chi usa il comando, il valore e il suo tetto. */
|
|
22
|
+
export interface LengthCheck {
|
|
23
|
+
flag: string;
|
|
24
|
+
value: unknown;
|
|
25
|
+
max: number;
|
|
26
|
+
/** Valore attualmente sul server, quando il comando lo ha letto (vedi `tooLongMessage`). */
|
|
27
|
+
previous?: unknown;
|
|
28
|
+
/** Campo che il server corregge in ortografia italiana prima di misurarlo (vedi `exceeds`). */
|
|
29
|
+
orthography?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Caratteri di un testo contati come li conta il server (Ruby `String#length`, cioè per code point).
|
|
33
|
+
* La `.length` di JS conta unità UTF-16: un'emoji o qualunque carattere fuori dal BMP ne vale due, e
|
|
34
|
+
* la lunghezza mostrata divergerebbe da quella su cui il server misura il suo limite.
|
|
35
|
+
*/
|
|
36
|
+
export declare function charCount(value: unknown): number;
|
|
37
|
+
/**
|
|
38
|
+
* Lunghezza sulla quale il server decide: il testo passa dalle sue normalizzazioni prima di essere
|
|
39
|
+
* misurato — CRLF ridotti a un solo a capo (`LengthBudget.normalize_newlines`) e spazi ai bordi via
|
|
40
|
+
* (`strip`). Senza questo un file con 100 righe scritto su Windows "peserebbe" 100 caratteri in più
|
|
41
|
+
* qui che là, e il comando rifiuterebbe un testo che il server accetta.
|
|
42
|
+
*
|
|
43
|
+
* `trim()` non è `String#strip`: toglie anche gli spazi Unicode che Ruby lascia (lo spazio unificatore
|
|
44
|
+
* in testa a una riga, per dire). Va nella direzione innocua — misura al più quanto il server, mai di
|
|
45
|
+
* più — quindi al massimo lascia partire una chiamata che il server rifiuterà, mai il contrario.
|
|
46
|
+
*/
|
|
47
|
+
export declare function serverLength(value: unknown): number;
|
|
48
|
+
/**
|
|
49
|
+
* Il messaggio che elenca TUTTI i campi oltre il tetto, in un colpo solo — il punto della
|
|
50
|
+
* lavorazione: sistemarne uno e scoprire il successivo al tentativo dopo è esattamente ciò che
|
|
51
|
+
* questa funzione toglie di mezzo. `undefined` quando è tutto in regola.
|
|
52
|
+
*
|
|
53
|
+
* `previous` porta la clausola di salvaguardia del server (`LengthBudget`): un testo già oltre il
|
|
54
|
+
* tetto resta salvabile finché non si allunga, così chi ha in archivio una pagina o un ticket
|
|
55
|
+
* scritti prima del limite può ancora correggerli e accorciarli per gradi. I comandi che leggono lo
|
|
56
|
+
* stato attuale prima di scrivere lo passano; chi non lo conosce (una creazione, o `--replace`) lo
|
|
57
|
+
* lascia fuori e il tetto vale pieno.
|
|
58
|
+
*/
|
|
59
|
+
export declare function tooLongMessage(checks: LengthCheck[]): string | undefined;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LENGTH_LIMITS = void 0;
|
|
4
|
+
exports.charCount = charCount;
|
|
5
|
+
exports.serverLength = serverLength;
|
|
6
|
+
exports.tooLongMessage = tooLongMessage;
|
|
7
|
+
/**
|
|
8
|
+
* Tetti di lunghezza dei campi di testo, come li applica il server (CYCL-32).
|
|
9
|
+
*
|
|
10
|
+
* Prima erano scoperti solo sbagliando: l'aiuto non li nominava e il rifiuto arrivava dopo aver
|
|
11
|
+
* scritto tutto. Qui vivono i numeri — copia dichiarata di `Knowledge::Constants`,
|
|
12
|
+
* `Ticketing::Constants` e `App::Constants::COMMENT_MAX_CHARS` — che le descrizioni dei flag
|
|
13
|
+
* mostrano e che i comandi verificano prima di spedire, così i campi troppo lunghi si scoprono
|
|
14
|
+
* tutti insieme e senza chiamare il server.
|
|
15
|
+
*/
|
|
16
|
+
exports.LENGTH_LIMITS = {
|
|
17
|
+
/** Titolo di un ticket e di una pagina di conoscenza. */
|
|
18
|
+
title: 255,
|
|
19
|
+
description: 4_000,
|
|
20
|
+
technicalAnalysis: 1_500,
|
|
21
|
+
comment: 240,
|
|
22
|
+
report: 20_000,
|
|
23
|
+
pageBody: 4_000,
|
|
24
|
+
techSpec: 1_500,
|
|
25
|
+
reviewNote: 240,
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Caratteri di un testo contati come li conta il server (Ruby `String#length`, cioè per code point).
|
|
29
|
+
* La `.length` di JS conta unità UTF-16: un'emoji o qualunque carattere fuori dal BMP ne vale due, e
|
|
30
|
+
* la lunghezza mostrata divergerebbe da quella su cui il server misura il suo limite.
|
|
31
|
+
*/
|
|
32
|
+
function charCount(value) {
|
|
33
|
+
return [...String(value ?? '')].length;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lunghezza sulla quale il server decide: il testo passa dalle sue normalizzazioni prima di essere
|
|
37
|
+
* misurato — CRLF ridotti a un solo a capo (`LengthBudget.normalize_newlines`) e spazi ai bordi via
|
|
38
|
+
* (`strip`). Senza questo un file con 100 righe scritto su Windows "peserebbe" 100 caratteri in più
|
|
39
|
+
* qui che là, e il comando rifiuterebbe un testo che il server accetta.
|
|
40
|
+
*
|
|
41
|
+
* `trim()` non è `String#strip`: toglie anche gli spazi Unicode che Ruby lascia (lo spazio unificatore
|
|
42
|
+
* in testa a una riga, per dire). Va nella direzione innocua — misura al più quanto il server, mai di
|
|
43
|
+
* più — quindi al massimo lascia partire una chiamata che il server rifiuterà, mai il contrario.
|
|
44
|
+
*/
|
|
45
|
+
function serverLength(value) {
|
|
46
|
+
return charCount(String(value ?? '').replaceAll('\r\n', '\n').trim());
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Quanto la correzione ortografica italiana del server può ancora accorciare il testo (CYCL-32).
|
|
50
|
+
*
|
|
51
|
+
* `Text::ItalianOrthography.correct` gira sui campi dei ticket e sul resoconto prima della misura, e
|
|
52
|
+
* sostituisce forme note con la loro versione accentata: «gia» → «già» non cambia la lunghezza,
|
|
53
|
+
* «perche'» → «perché» ne toglie una, perché assorbe l'apostrofo. Il dizionario non vive qui e non
|
|
54
|
+
* deve: il taglio massimo possibile è comunque UN carattere per apostrofo presente nel testo.
|
|
55
|
+
*
|
|
56
|
+
* Serve a non rifiutare in locale ciò che il server accetterebbe — un testo appena sopra il tetto e
|
|
57
|
+
* pieno di apostrofi da correggere ci rientra da solo. Sotto quel margine la misura resta al server.
|
|
58
|
+
*/
|
|
59
|
+
function orthographyMargin(value) {
|
|
60
|
+
return (value.match(/['’]/g) ?? []).length;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Il messaggio che elenca TUTTI i campi oltre il tetto, in un colpo solo — il punto della
|
|
64
|
+
* lavorazione: sistemarne uno e scoprire il successivo al tentativo dopo è esattamente ciò che
|
|
65
|
+
* questa funzione toglie di mezzo. `undefined` quando è tutto in regola.
|
|
66
|
+
*
|
|
67
|
+
* `previous` porta la clausola di salvaguardia del server (`LengthBudget`): un testo già oltre il
|
|
68
|
+
* tetto resta salvabile finché non si allunga, così chi ha in archivio una pagina o un ticket
|
|
69
|
+
* scritti prima del limite può ancora correggerli e accorciarli per gradi. I comandi che leggono lo
|
|
70
|
+
* stato attuale prima di scrivere lo passano; chi non lo conosce (una creazione, o `--replace`) lo
|
|
71
|
+
* lascia fuori e il tetto vale pieno.
|
|
72
|
+
*/
|
|
73
|
+
function tooLongMessage(checks) {
|
|
74
|
+
const over = checks.filter((check) => exceeds(check));
|
|
75
|
+
if (over.length === 0)
|
|
76
|
+
return undefined;
|
|
77
|
+
const list = over.map((check) => `${check.flag} is ${serverLength(check.value)} characters (max ${check.max})`);
|
|
78
|
+
return `Too long for the server: ${list.join('; ')}. Shorten ${over.length === 1 ? 'it' : 'them'} and run the command again.`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Vero solo quando il rifiuto è CERTO: si misura la lunghezza più corta che il testo può avere una
|
|
82
|
+
* volta arrivato al server (correzione ortografica inclusa, dove il server la applica) e si tiene
|
|
83
|
+
* conto della salvaguardia sul valore attuale. Nel dubbio si tace e decide il server.
|
|
84
|
+
*/
|
|
85
|
+
function exceeds(check) {
|
|
86
|
+
const length = serverLength(check.value);
|
|
87
|
+
const shortest = check.orthography ? length - orthographyMargin(String(check.value ?? '')) : length;
|
|
88
|
+
if (shortest <= check.max)
|
|
89
|
+
return false;
|
|
90
|
+
const previous = serverLength(check.previous);
|
|
91
|
+
return !(previous > check.max && length <= previous);
|
|
92
|
+
}
|
package/dist/lib/stdin.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
/** Read a whole stream (default: process.stdin) into a UTF-8 string
|
|
2
|
-
* piped input, e.g. `doppler secrets download --format json | cyi secrets import --format json`. */
|
|
1
|
+
/** Read a whole stream (default: process.stdin) into a UTF-8 string for `secrets import`. */
|
|
3
2
|
export declare function readStream(stream?: AsyncIterable<Buffer | string>): Promise<string>;
|
package/dist/lib/stdin.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readStream = readStream;
|
|
4
|
-
/** Read a whole stream (default: process.stdin) into a UTF-8 string
|
|
5
|
-
* piped input, e.g. `doppler secrets download --format json | cyi secrets import --format json`. */
|
|
4
|
+
/** Read a whole stream (default: process.stdin) into a UTF-8 string for `secrets import`. */
|
|
6
5
|
async function readStream(stream = process.stdin) {
|
|
7
6
|
const chunks = [];
|
|
8
7
|
for await (const chunk of stream)
|
package/dist/lib/subprocess.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Run a child process inheriting stdio, resolving with its exit code. Unlike `openUrl` (browser.ts)
|
|
3
3
|
* this is NOT detached/unref'd: `cyi run` must stay attached and propagate the child's exit code
|
|
4
|
-
*
|
|
4
|
+
* and propagate the child's exit code. A terminating signal maps to a non-zero code.
|
|
5
5
|
*/
|
|
6
6
|
export declare function runCommand(command: string, args: string[], env: NodeJS.ProcessEnv): Promise<number>;
|
package/dist/lib/subprocess.js
CHANGED
|
@@ -5,7 +5,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
5
5
|
/**
|
|
6
6
|
* Run a child process inheriting stdio, resolving with its exit code. Unlike `openUrl` (browser.ts)
|
|
7
7
|
* this is NOT detached/unref'd: `cyi run` must stay attached and propagate the child's exit code
|
|
8
|
-
*
|
|
8
|
+
* and propagate the child's exit code. A terminating signal maps to a non-zero code.
|
|
9
9
|
*/
|
|
10
10
|
function runCommand(command, args, env) {
|
|
11
11
|
return new Promise((resolve, reject) => {
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import { type LengthCheck } from './limits';
|
|
1
2
|
export declare const ticketBodyFlags: {
|
|
3
|
+
'assignee-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
|
+
'milestone-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
5
|
+
'parent-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
6
|
+
'platform-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
2
9
|
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
3
10
|
'technical-analysis': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
4
11
|
scenarios: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -9,12 +16,10 @@ export declare const ticketBodyFlags: {
|
|
|
9
16
|
condition: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
17
|
weight: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
18
|
'due-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'parent-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
17
|
-
'platform-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
19
|
+
};
|
|
20
|
+
export declare const CLEARABLE_FIELDS: Record<string, string>;
|
|
21
|
+
export declare const clearFlag: {
|
|
22
|
+
clear: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
18
23
|
};
|
|
19
24
|
type BodyFlags = Record<string, unknown>;
|
|
20
25
|
export declare function scenariosFromFlags(flags: BodyFlags): Array<Record<string, unknown>> | undefined;
|
|
@@ -22,4 +27,15 @@ export declare function conditionsFromFlags(flags: BodyFlags): Array<{
|
|
|
22
27
|
text: string;
|
|
23
28
|
}> | undefined;
|
|
24
29
|
export declare function applyTicketBody(body: BodyFlags, flags: BodyFlags): void;
|
|
30
|
+
/**
|
|
31
|
+
* I campi del corpo che il server misura, pronti per `tooLongMessage` (CYCL-32): titolo, descrizione
|
|
32
|
+
* e analisi tecnica. Scenari e condizioni non ci sono — non hanno tetto.
|
|
33
|
+
*
|
|
34
|
+
* `current` è il ticket com'è adesso, per i comandi che lo leggono prima di scrivere: fa da
|
|
35
|
+
* salvaguardia, così un testo scritto prima che il tetto esistesse resta correggibile finché non si
|
|
36
|
+
* allunga. Chi non lo conosce lo omette e il tetto vale pieno.
|
|
37
|
+
*/
|
|
38
|
+
export declare function ticketLengthChecks(flags: BodyFlags, current?: BodyFlags): LengthCheck[];
|
|
39
|
+
/** Marca come vuoti i campi elencati da `--clear`: null, o lista vuota per le piattaforme. */
|
|
40
|
+
export declare function applyTicketClears(body: BodyFlags, fields: string[] | undefined): void;
|
|
25
41
|
export {};
|
package/dist/lib/ticket-body.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ticketBodyFlags = void 0;
|
|
3
|
+
exports.clearFlag = exports.CLEARABLE_FIELDS = exports.ticketBodyFlags = void 0;
|
|
4
4
|
exports.scenariosFromFlags = scenariosFromFlags;
|
|
5
5
|
exports.conditionsFromFlags = conditionsFromFlags;
|
|
6
6
|
exports.applyTicketBody = applyTicketBody;
|
|
7
|
+
exports.ticketLengthChecks = ticketLengthChecks;
|
|
8
|
+
exports.applyTicketClears = applyTicketClears;
|
|
7
9
|
const core_1 = require("@oclif/core");
|
|
10
|
+
const limits_1 = require("./limits");
|
|
11
|
+
const ticket_lookup_1 = require("./ticket-lookup");
|
|
8
12
|
// Flag del corpo ticket condivisi da `tickets create` e `tickets update`: descrizione (sommario
|
|
9
13
|
// human-simple) + N scenari BDD + N condizioni DoD + analisi tecnica (registro tecnico separato) +
|
|
10
14
|
// metadati. Gli scenari si passano come JSON (--scenarios) oppure, per un SINGOLO scenario, con le
|
|
11
15
|
// scorciatoie --step-*. Le condizioni DoD sono --condition (ripetibile). Parità piena col form web.
|
|
12
16
|
exports.ticketBodyFlags = {
|
|
13
|
-
description: core_1.Flags.string({
|
|
14
|
-
|
|
17
|
+
description: core_1.Flags.string({
|
|
18
|
+
description: `Free-form description (human-simple summary, max ${limits_1.LENGTH_LIMITS.description} characters)`,
|
|
19
|
+
}),
|
|
20
|
+
'technical-analysis': core_1.Flags.string({
|
|
21
|
+
description: `Technical analysis (stack, cause) — kept out of the human-simple body (max ${limits_1.LENGTH_LIMITS.technicalAnalysis} characters)`,
|
|
22
|
+
}),
|
|
15
23
|
scenarios: core_1.Flags.string({
|
|
16
24
|
description: 'BDD scenarios as a JSON array: [{"title","step_given","step_when","step_then","step_expected"}]',
|
|
17
25
|
}),
|
|
@@ -22,13 +30,34 @@ exports.ticketBodyFlags = {
|
|
|
22
30
|
condition: core_1.Flags.string({ description: 'Definition-of-Done line (repeatable)', multiple: true }),
|
|
23
31
|
weight: core_1.Flags.string({ description: 'Story points (positive integer)' }),
|
|
24
32
|
'due-at': core_1.Flags.string({ description: 'Due date (ISO 8601)' }),
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
// Stato e priorità si indicano per code, etichetta o id, e il comando li risolve (CYCL-34).
|
|
34
|
+
...ticket_lookup_1.ticketLookupFlags,
|
|
27
35
|
'assignee-id': core_1.Flags.string({ description: 'Assignee account id' }),
|
|
28
36
|
'milestone-id': core_1.Flags.string({ description: 'Milestone id (of this project)' }),
|
|
29
37
|
'parent-id': core_1.Flags.string({ description: 'Parent epic id (of this project) — pass an empty string to detach' }),
|
|
30
38
|
'platform-id': core_1.Flags.string({ description: 'Affected platform id (repeatable)', multiple: true }),
|
|
31
39
|
};
|
|
40
|
+
// Campi che `tickets update` svuota solo se glielo si chiede (`--clear <campo>`): nome del flag che
|
|
41
|
+
// li imposta → chiave del corpo. Svuotarli è una richiesta a sé, mai l'effetto collaterale di un
|
|
42
|
+
// campo omesso (CYCL-30). Scenari e condizioni non ci sono: il backend li sostituisce quando il
|
|
43
|
+
// corpo ne porta di nuovi, li lascia intatti quando li omette, e un array vuoto non li cancella.
|
|
44
|
+
exports.CLEARABLE_FIELDS = {
|
|
45
|
+
description: 'description',
|
|
46
|
+
'technical-analysis': 'technical_analysis',
|
|
47
|
+
weight: 'weight',
|
|
48
|
+
'due-at': 'due_at',
|
|
49
|
+
assignee: 'assignee_id',
|
|
50
|
+
milestone: 'milestone_id',
|
|
51
|
+
parent: 'parent_id',
|
|
52
|
+
platforms: 'platform_ids',
|
|
53
|
+
};
|
|
54
|
+
exports.clearFlag = {
|
|
55
|
+
clear: core_1.Flags.string({
|
|
56
|
+
description: `Empty a field on purpose (repeatable): ${Object.keys(exports.CLEARABLE_FIELDS).join(', ')}`,
|
|
57
|
+
multiple: true,
|
|
58
|
+
options: Object.keys(exports.CLEARABLE_FIELDS),
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
32
61
|
const STEP_KEYS = [
|
|
33
62
|
['step-given', 'step_given'],
|
|
34
63
|
['step-when', 'step_when'],
|
|
@@ -64,6 +93,8 @@ function conditionsFromFlags(flags) {
|
|
|
64
93
|
return conditions.map((text) => ({ text }));
|
|
65
94
|
}
|
|
66
95
|
// Popola `body` coi campi del corpo presenti nei flag (chiavi snake_case attese dall'API CLI).
|
|
96
|
+
// Stato e priorità restano fuori: vanno risolti in id contro le lookup dell'organizzazione, cioè
|
|
97
|
+
// con una chiamata, e ogni comando li aggiunge dopo — con o senza predefinito (CYCL-34).
|
|
67
98
|
function applyTicketBody(body, flags) {
|
|
68
99
|
if (flags.description !== undefined)
|
|
69
100
|
body.description = flags.description;
|
|
@@ -73,10 +104,6 @@ function applyTicketBody(body, flags) {
|
|
|
73
104
|
body.weight = flags.weight;
|
|
74
105
|
if (flags['due-at'] !== undefined)
|
|
75
106
|
body.due_at = flags['due-at'];
|
|
76
|
-
if (flags['status-id'] !== undefined)
|
|
77
|
-
body.status_id = flags['status-id'];
|
|
78
|
-
if (flags['priority-id'] !== undefined)
|
|
79
|
-
body.priority_id = flags['priority-id'];
|
|
80
107
|
if (flags['assignee-id'] !== undefined)
|
|
81
108
|
body.assignee_id = flags['assignee-id'];
|
|
82
109
|
if (flags['milestone-id'] !== undefined)
|
|
@@ -92,3 +119,38 @@ function applyTicketBody(body, flags) {
|
|
|
92
119
|
if (conditions !== undefined)
|
|
93
120
|
body.conditions_attributes = conditions;
|
|
94
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* I campi del corpo che il server misura, pronti per `tooLongMessage` (CYCL-32): titolo, descrizione
|
|
124
|
+
* e analisi tecnica. Scenari e condizioni non ci sono — non hanno tetto.
|
|
125
|
+
*
|
|
126
|
+
* `current` è il ticket com'è adesso, per i comandi che lo leggono prima di scrivere: fa da
|
|
127
|
+
* salvaguardia, così un testo scritto prima che il tetto esistesse resta correggibile finché non si
|
|
128
|
+
* allunga. Chi non lo conosce lo omette e il tetto vale pieno.
|
|
129
|
+
*/
|
|
130
|
+
function ticketLengthChecks(flags, current = {}) {
|
|
131
|
+
// `orthography`: il server corregge l'ortografia italiana di questi tre campi prima di misurarli.
|
|
132
|
+
return [
|
|
133
|
+
{ flag: '--title', value: flags.title, max: limits_1.LENGTH_LIMITS.title, previous: current.title, orthography: true },
|
|
134
|
+
{
|
|
135
|
+
flag: '--description',
|
|
136
|
+
value: flags.description,
|
|
137
|
+
max: limits_1.LENGTH_LIMITS.description,
|
|
138
|
+
previous: current.description,
|
|
139
|
+
orthography: true,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
flag: '--technical-analysis',
|
|
143
|
+
value: flags['technical-analysis'],
|
|
144
|
+
max: limits_1.LENGTH_LIMITS.technicalAnalysis,
|
|
145
|
+
previous: current.technical_analysis,
|
|
146
|
+
orthography: true,
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
/** Marca come vuoti i campi elencati da `--clear`: null, o lista vuota per le piattaforme. */
|
|
151
|
+
function applyTicketClears(body, fields) {
|
|
152
|
+
for (const field of fields ?? []) {
|
|
153
|
+
const key = exports.CLEARABLE_FIELDS[field];
|
|
154
|
+
body[key] = key === 'platform_ids' ? [] : null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Le due lookup d'organizzazione che ogni ticket si porta addosso: stato e priorità (CYCL-34).
|
|
3
|
+
*
|
|
4
|
+
* Il server le pretende entrambe alla creazione (R422-TICKET-001 "Status must exist and Priority
|
|
5
|
+
* must exist") mentre l'aiuto della CLI le dava per opzionali, e i loro id andavano cercati prima
|
|
6
|
+
* con `tickets statuses`/`tickets priorities` e incollati come UUID. Qui vivono l'endpoint da
|
|
7
|
+
* leggere, il code che vale quando il ticket nasce senza che li si sia indicati, e come si chiama
|
|
8
|
+
* il campo nei messaggi.
|
|
9
|
+
*/
|
|
10
|
+
export interface TicketLookup {
|
|
11
|
+
/** Lookup d'organizzazione (non di progetto): gli id valgono per un ticket di qualunque progetto. */
|
|
12
|
+
path: string;
|
|
13
|
+
/** Nome del campo nei messaggi d'errore. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Il flag che lo imposta. */
|
|
16
|
+
flag: string;
|
|
17
|
+
/** Il code che vale quando il ticket nasce e non lo si è indicato. */
|
|
18
|
+
fallback: string;
|
|
19
|
+
errorCode: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const TICKET_STATUS: TicketLookup;
|
|
22
|
+
export declare const TICKET_PRIORITY: TicketLookup;
|
|
23
|
+
/**
|
|
24
|
+
* `--status`/`--priority` di un comando che modifica un ticket già esistente. `--status-id` e
|
|
25
|
+
* `--priority-id` restano come alias: erano l'unico modo di indicarli, e gli script che li usano
|
|
26
|
+
* continuano a funzionare senza riscritture.
|
|
27
|
+
*/
|
|
28
|
+
export declare const ticketLookupFlags: {
|
|
29
|
+
status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
30
|
+
priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
31
|
+
};
|
|
32
|
+
/** Gli stessi due flag su un comando che crea il ticket, dove ometterli ha un valore predefinito. */
|
|
33
|
+
export declare const newTicketLookupFlags: {
|
|
34
|
+
status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
35
|
+
priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Le voci che corrispondono al riferimento scritto a mano: prima il code, poi l'etichetta, senza
|
|
39
|
+
* badare alle maiuscole. Il code ha la precedenza perché è il nome esatto della voce: senza,
|
|
40
|
+
* l'etichetta di un'altra ("Urgent" su `high`) farebbe concorrenza al code di quella cercata
|
|
41
|
+
* (`urgent`) e una scelta legittima diventerebbe un'ambiguità da rifiutare.
|
|
42
|
+
*/
|
|
43
|
+
export declare function matchTicketLookup(rows: unknown, value: string): Array<{
|
|
44
|
+
id: string;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Il rifiuto di un riferimento che non si risolve: dice cos'è stato cercato, come si scrive il flag
|
|
48
|
+
* e quali voci esistono davvero — così scoprirle non costa un secondo comando.
|
|
49
|
+
*/
|
|
50
|
+
export declare function ticketLookupErrorMessage(lookup: TicketLookup, wanted: string, rows: unknown, { matched, isDefault }: {
|
|
51
|
+
matched: number;
|
|
52
|
+
isDefault: boolean;
|
|
53
|
+
}): string;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.newTicketLookupFlags = exports.ticketLookupFlags = exports.TICKET_PRIORITY = exports.TICKET_STATUS = void 0;
|
|
4
|
+
exports.matchTicketLookup = matchTicketLookup;
|
|
5
|
+
exports.ticketLookupErrorMessage = ticketLookupErrorMessage;
|
|
6
|
+
const core_1 = require("@oclif/core");
|
|
7
|
+
const error_codes_1 = require("../errors/error-codes");
|
|
8
|
+
exports.TICKET_STATUS = {
|
|
9
|
+
path: '/cli/v1/types/ticket_statuses',
|
|
10
|
+
name: 'Status',
|
|
11
|
+
flag: '--status',
|
|
12
|
+
fallback: 'open',
|
|
13
|
+
errorCode: error_codes_1.ErrorCodes.TicketStatus.notFound,
|
|
14
|
+
};
|
|
15
|
+
exports.TICKET_PRIORITY = {
|
|
16
|
+
path: '/cli/v1/types/ticket_priorities',
|
|
17
|
+
name: 'Priority',
|
|
18
|
+
flag: '--priority',
|
|
19
|
+
fallback: 'medium',
|
|
20
|
+
errorCode: error_codes_1.ErrorCodes.TicketPriority.notFound,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `--status`/`--priority` di un comando che modifica un ticket già esistente. `--status-id` e
|
|
24
|
+
* `--priority-id` restano come alias: erano l'unico modo di indicarli, e gli script che li usano
|
|
25
|
+
* continuano a funzionare senza riscritture.
|
|
26
|
+
*/
|
|
27
|
+
exports.ticketLookupFlags = {
|
|
28
|
+
status: core_1.Flags.string({
|
|
29
|
+
aliases: ['status-id'],
|
|
30
|
+
description: 'Status code, label or id (e.g. open, "In Progress") — unchanged when omitted',
|
|
31
|
+
}),
|
|
32
|
+
priority: core_1.Flags.string({
|
|
33
|
+
aliases: ['priority-id'],
|
|
34
|
+
description: 'Priority code, label or id (e.g. medium, High) — unchanged when omitted',
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
/** Gli stessi due flag su un comando che crea il ticket, dove ometterli ha un valore predefinito. */
|
|
38
|
+
exports.newTicketLookupFlags = {
|
|
39
|
+
status: core_1.Flags.string({
|
|
40
|
+
aliases: ['status-id'],
|
|
41
|
+
description: `Status code, label or id (e.g. open, "In Progress") — default: ${exports.TICKET_STATUS.fallback}`,
|
|
42
|
+
}),
|
|
43
|
+
priority: core_1.Flags.string({
|
|
44
|
+
aliases: ['priority-id'],
|
|
45
|
+
description: `Priority code, label or id (e.g. medium, High) — default: ${exports.TICKET_PRIORITY.fallback}`,
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Le voci che corrispondono al riferimento scritto a mano: prima il code, poi l'etichetta, senza
|
|
50
|
+
* badare alle maiuscole. Il code ha la precedenza perché è il nome esatto della voce: senza,
|
|
51
|
+
* l'etichetta di un'altra ("Urgent" su `high`) farebbe concorrenza al code di quella cercata
|
|
52
|
+
* (`urgent`) e una scelta legittima diventerebbe un'ambiguità da rifiutare.
|
|
53
|
+
*/
|
|
54
|
+
function matchTicketLookup(rows, value) {
|
|
55
|
+
const wanted = value.trim().toLowerCase();
|
|
56
|
+
const list = (Array.isArray(rows) ? rows : []);
|
|
57
|
+
const identified = list.filter((row) => typeof row.id === 'string' && row.id !== '');
|
|
58
|
+
const sameAs = (field) => typeof field === 'string' && field.trim().toLowerCase() === wanted;
|
|
59
|
+
const byCode = identified.filter((row) => sameAs(row.code));
|
|
60
|
+
return byCode.length > 0 ? byCode : identified.filter((row) => sameAs(row.label));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Il rifiuto di un riferimento che non si risolve: dice cos'è stato cercato, come si scrive il flag
|
|
64
|
+
* e quali voci esistono davvero — così scoprirle non costa un secondo comando.
|
|
65
|
+
*/
|
|
66
|
+
function ticketLookupErrorMessage(lookup, wanted, rows, { matched, isDefault }) {
|
|
67
|
+
const known = (Array.isArray(rows) ? rows : [])
|
|
68
|
+
.map((row) => (typeof row.code === 'string' && row.code !== '' ? row.code : String(row.label ?? '')))
|
|
69
|
+
.filter((name) => name !== '')
|
|
70
|
+
.join(', ');
|
|
71
|
+
const hint = `Pass ${lookup.flag} with a code, a label or an id${known === '' ? '' : ` — known: ${known}`}.`;
|
|
72
|
+
if (matched > 1)
|
|
73
|
+
return `${lookup.name} "${wanted}" matches ${matched} entries. ${hint}`;
|
|
74
|
+
if (isDefault) {
|
|
75
|
+
return `This organization has no ${lookup.name.toLowerCase()} "${wanted}", the default for a new ticket. ${hint}`;
|
|
76
|
+
}
|
|
77
|
+
return `${lookup.name} not found: "${wanted}". ${hint}`;
|
|
78
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Una versione del resoconto di lavorazione: intestazione coi metadati + il testo per intero
|
|
3
|
+
* (CYCL-31). Il corpo passa da `sanitizeMultiline` e non da `renderRecord`: quest'ultimo appiattisce
|
|
4
|
+
* ogni valore con `sanitize`, che trasforma gli a capo in spazi — su un resoconto di migliaia di
|
|
5
|
+
* caratteri significherebbe stamparlo come un muro di testo su una riga sola.
|
|
6
|
+
*/
|
|
7
|
+
export declare function renderReport(report: Record<string, unknown>): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderReport = renderReport;
|
|
4
|
+
const output_1 = require("./output");
|
|
5
|
+
/**
|
|
6
|
+
* Una versione del resoconto di lavorazione: intestazione coi metadati + il testo per intero
|
|
7
|
+
* (CYCL-31). Il corpo passa da `sanitizeMultiline` e non da `renderRecord`: quest'ultimo appiattisce
|
|
8
|
+
* ogni valore con `sanitize`, che trasforma gli a capo in spazi — su un resoconto di migliaia di
|
|
9
|
+
* caratteri significherebbe stamparlo come un muro di testo su una riga sola.
|
|
10
|
+
*/
|
|
11
|
+
function renderReport(report) {
|
|
12
|
+
const header = `── version ${(0, output_1.sanitize)(report.version ?? '?')} · ${(0, output_1.sanitize)(report.author ?? '?')} · ${(0, output_1.sanitize)(report.source ?? '-')} · ${(0, output_1.sanitize)(report.created_at ?? '-')}`;
|
|
13
|
+
return `${header}\n${(0, output_1.sanitizeMultiline)(report.body ?? '')}`;
|
|
14
|
+
}
|