@bussolabs/closeyourit-cli 0.22.0 → 0.23.1

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.
Files changed (116) hide show
  1. package/README.md +44 -3
  2. package/dist/base.d.ts +23 -0
  3. package/dist/base.js +64 -4
  4. package/dist/commands/assistant/ask.d.ts +24 -0
  5. package/dist/commands/assistant/ask.js +112 -0
  6. package/dist/commands/assistant/conversations/create.d.ts +9 -0
  7. package/dist/commands/assistant/conversations/create.js +25 -0
  8. package/dist/commands/assistant/conversations/delete.d.ts +12 -0
  9. package/dist/commands/assistant/conversations/delete.js +25 -0
  10. package/dist/commands/assistant/conversations/list.d.ts +10 -0
  11. package/dist/commands/assistant/conversations/list.js +30 -0
  12. package/dist/commands/assistant/conversations/show.d.ts +9 -0
  13. package/dist/commands/assistant/conversations/show.js +25 -0
  14. package/dist/commands/errors/assign.d.ts +14 -0
  15. package/dist/commands/errors/assign.js +52 -0
  16. package/dist/commands/errors/bulk-triage.d.ts +11 -0
  17. package/dist/commands/errors/bulk-triage.js +41 -0
  18. package/dist/commands/errors/delete.d.ts +13 -0
  19. package/dist/commands/errors/delete.js +28 -0
  20. package/dist/commands/errors/grouping-rules/create.d.ts +15 -0
  21. package/dist/commands/errors/grouping-rules/create.js +44 -0
  22. package/dist/commands/errors/grouping-rules/delete.d.ts +13 -0
  23. package/dist/commands/errors/grouping-rules/delete.js +27 -0
  24. package/dist/commands/errors/grouping-rules/list.d.ts +10 -0
  25. package/dist/commands/errors/grouping-rules/list.js +28 -0
  26. package/dist/commands/errors/grouping-rules/update.d.ts +18 -0
  27. package/dist/commands/errors/grouping-rules/update.js +43 -0
  28. package/dist/commands/errors/merge.d.ts +14 -0
  29. package/dist/commands/errors/merge.js +39 -0
  30. package/dist/commands/errors/split.d.ts +13 -0
  31. package/dist/commands/errors/split.js +34 -0
  32. package/dist/commands/invitations/create.js +9 -2
  33. package/dist/commands/invitations/resend.js +8 -2
  34. package/dist/commands/kb/attach.d.ts +12 -0
  35. package/dist/commands/kb/attach.js +49 -0
  36. package/dist/commands/kb/attachment-download.d.ts +14 -0
  37. package/dist/commands/kb/attachment-download.js +38 -0
  38. package/dist/commands/kb/attachments.d.ts +9 -0
  39. package/dist/commands/kb/attachments.js +36 -0
  40. package/dist/commands/kb/create.d.ts +4 -1
  41. package/dist/commands/kb/create.js +41 -5
  42. package/dist/commands/kb/detach.d.ts +13 -0
  43. package/dist/commands/kb/detach.js +26 -0
  44. package/dist/commands/kb/show.js +3 -0
  45. package/dist/commands/kb/update.d.ts +4 -0
  46. package/dist/commands/kb/update.js +69 -21
  47. package/dist/commands/metrics/bulk-triage.d.ts +11 -0
  48. package/dist/commands/metrics/bulk-triage.js +44 -0
  49. package/dist/commands/service-accounts/show.d.ts +9 -0
  50. package/dist/commands/service-accounts/show.js +26 -0
  51. package/dist/commands/service-accounts/tokens/list.d.ts +12 -0
  52. package/dist/commands/service-accounts/tokens/list.js +40 -0
  53. package/dist/commands/service-accounts/update.d.ts +31 -0
  54. package/dist/commands/service-accounts/update.js +105 -0
  55. package/dist/commands/shared/delete.d.ts +12 -0
  56. package/dist/commands/shared/delete.js +30 -0
  57. package/dist/commands/shared/list.d.ts +6 -0
  58. package/dist/commands/shared/list.js +45 -0
  59. package/dist/commands/shared/set.d.ts +14 -0
  60. package/dist/commands/shared/set.js +41 -0
  61. package/dist/commands/tickets/ask.d.ts +13 -0
  62. package/dist/commands/tickets/ask.js +81 -0
  63. package/dist/commands/tickets/dependencies/add.d.ts +15 -0
  64. package/dist/commands/tickets/dependencies/add.js +62 -0
  65. package/dist/commands/tickets/dependencies/list.d.ts +12 -0
  66. package/dist/commands/tickets/dependencies/list.js +37 -0
  67. package/dist/commands/tickets/dependencies/remove.d.ts +14 -0
  68. package/dist/commands/tickets/dependencies/remove.js +59 -0
  69. package/dist/commands/tickets/work-context.d.ts +15 -0
  70. package/dist/commands/tickets/work-context.js +130 -0
  71. package/dist/commands/vault/requests/approve.d.ts +9 -0
  72. package/dist/commands/vault/requests/approve.js +40 -0
  73. package/dist/commands/vault/requests/list.d.ts +6 -0
  74. package/dist/commands/vault/requests/list.js +29 -0
  75. package/dist/commands/vault/requests/reject.d.ts +12 -0
  76. package/dist/commands/vault/requests/reject.js +52 -0
  77. package/dist/commands/workload/create.d.ts +15 -0
  78. package/dist/commands/workload/create.js +45 -0
  79. package/dist/commands/workload/delete.d.ts +12 -0
  80. package/dist/commands/workload/delete.js +26 -0
  81. package/dist/commands/workload/list.d.ts +9 -0
  82. package/dist/commands/workload/list.js +29 -0
  83. package/dist/commands/workload/participant/add.d.ts +10 -0
  84. package/dist/commands/workload/participant/add.js +27 -0
  85. package/dist/commands/workload/participant/remove.d.ts +10 -0
  86. package/dist/commands/workload/participant/remove.js +23 -0
  87. package/dist/commands/workload/promote.d.ts +17 -0
  88. package/dist/commands/workload/promote.js +58 -0
  89. package/dist/commands/workload/show.d.ts +9 -0
  90. package/dist/commands/workload/show.js +24 -0
  91. package/dist/commands/workload/update.d.ts +17 -0
  92. package/dist/commands/workload/update.js +45 -0
  93. package/dist/errors/error-codes.d.ts +17 -0
  94. package/dist/errors/error-codes.js +36 -0
  95. package/dist/lib/api.d.ts +7 -1
  96. package/dist/lib/api.js +8 -0
  97. package/dist/lib/assistant.d.ts +29 -0
  98. package/dist/lib/assistant.js +76 -0
  99. package/dist/lib/bulk-triage.d.ts +35 -0
  100. package/dist/lib/bulk-triage.js +55 -0
  101. package/dist/lib/grouping-rules.d.ts +28 -0
  102. package/dist/lib/grouping-rules.js +39 -0
  103. package/dist/lib/knowledge.d.ts +15 -0
  104. package/dist/lib/knowledge.js +36 -0
  105. package/dist/lib/output.js +10 -0
  106. package/dist/lib/poll.d.ts +51 -0
  107. package/dist/lib/poll.js +59 -0
  108. package/dist/lib/service-accounts.d.ts +65 -0
  109. package/dist/lib/service-accounts.js +132 -0
  110. package/dist/lib/vault-requests.d.ts +38 -0
  111. package/dist/lib/vault-requests.js +125 -0
  112. package/dist/lib/workload.d.ts +37 -0
  113. package/dist/lib/workload.js +64 -0
  114. package/oclif.manifest.json +6290 -4001
  115. package/opencli.json +2164 -549
  116. package/package.json +27 -3
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.environmentsLine = environmentsLine;
4
+ exports.normalizeEnvironmentCodes = normalizeEnvironmentCodes;
5
+ exports.accountSummary = accountSummary;
6
+ exports.withoutSecret = withoutSecret;
7
+ exports.tokenStatus = tokenStatus;
8
+ exports.unknownEnvironmentCodes = unknownEnvironmentCodes;
9
+ exports.assertBoundaryApplied = assertBoundaryApplied;
10
+ const api_1 = require("./api");
11
+ const error_codes_1 = require("../errors/error-codes");
12
+ /**
13
+ * Reading and correcting a service account without recreating it (CYCL-46).
14
+ *
15
+ * One rule runs through everything here: on the secret boundary of a service account, an EMPTY list
16
+ * of environment codes does not mean "no access", it means "every environment". So the empty list is
17
+ * never a neutral value to fall into by accident — it is a decision, and it has to be printed with
18
+ * words that cannot be misread.
19
+ */
20
+ /** The boundary in words: the codes, or the fact that there is no boundary at all. */
21
+ function environmentsLine(codes) {
22
+ const list = normalizeEnvironmentCodes(Array.isArray(codes) ? codes : []);
23
+ return `secret environments: ${list.length > 0 ? list.join(', ') : 'all (no restriction)'}`;
24
+ }
25
+ /**
26
+ * Environment codes the way the server stores them: trimmed, lowercase, each one once, blanks
27
+ * dropped — the same normalisation `Connections::SetSecretAccess#sanitize` applies before saving.
28
+ * Sending them already in that shape is what makes "what I asked for" and "what came back"
29
+ * comparable at all: otherwise ` Staging ` would look dropped every single time.
30
+ */
31
+ function normalizeEnvironmentCodes(values) {
32
+ const seen = new Set();
33
+ const codes = [];
34
+ for (const value of values) {
35
+ const code = String(value ?? '').trim().toLowerCase();
36
+ if (code === '' || seen.has(code))
37
+ continue;
38
+ seen.add(code);
39
+ codes.push(code);
40
+ }
41
+ return codes;
42
+ }
43
+ /**
44
+ * The fields of a service account worth printing, as an explicit allow-list — the same discipline as
45
+ * `changeRequestSummary`: `renderRecord` prints every scalar it is handed, so handing it the raw
46
+ * payload would print whatever the server adds next, up to and including a field carrying a token.
47
+ * `secret_environment_codes` is left out on purpose: it is an array (renderRecord would swallow it)
48
+ * and it deserves the sentence `environmentsLine` builds, not a bare key.
49
+ */
50
+ function accountSummary(account) {
51
+ const summary = {
52
+ id: account.id,
53
+ name: account.name,
54
+ handle: account.handle,
55
+ created_at: account.created_at,
56
+ active_tokens_count: account.active_tokens_count,
57
+ };
58
+ return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== undefined && value !== null));
59
+ }
60
+ /**
61
+ * A copy of a token row without the secret, on both the human and the `--json` path.
62
+ *
63
+ * `UserApiTokenSerializer` never ships it — the secret exists only in the response of `tokens
64
+ * create`, reveal-once, and in the database there is just a prefix. This strips it anyway, for the
65
+ * same reason `withoutValue` does it on the vault queue: the CLI decides what leaves the terminal,
66
+ * so a payload that one day carried the secret (a serializer edit, a proxy, a hand-rolled backend)
67
+ * cannot turn a plain listing into a second reveal.
68
+ */
69
+ function withoutSecret(row) {
70
+ if (row === null || typeof row !== 'object')
71
+ return row;
72
+ const { secret: _secret, ...rest } = row;
73
+ return rest;
74
+ }
75
+ /** `active` while the token works, `revoked` once it has been withdrawn — what the reader is after. */
76
+ function tokenStatus(token) {
77
+ return token.revoked_at ? 'revoked' : 'active';
78
+ }
79
+ /**
80
+ * The requested codes an organization does not declare — checked BEFORE writing.
81
+ *
82
+ * `SetSecretAccess` intersects what it receives with the codes the organization really has and
83
+ * saves what is left, answering 200 either way. On a typo that intersection is empty, and an empty
84
+ * allow-list is not "no environments", it is EVERY environment: `--secret-environment stagin` would
85
+ * hand the account the production secrets it was being restricted away from. Catching it here means
86
+ * the mistake costs an error message instead of an open door.
87
+ *
88
+ * `known` empty is treated as "could not read them" by the caller, not as "nothing is valid": a
89
+ * lookup that came back empty (no permission, a serializer without `code`) must not block a command
90
+ * the caller is entitled to run.
91
+ */
92
+ function unknownEnvironmentCodes(requested, known) {
93
+ const declared = new Set(normalizeEnvironmentCodes(known));
94
+ return requested.filter((code) => !declared.has(code));
95
+ }
96
+ /**
97
+ * Refuse an environment boundary the server did not actually put in place.
98
+ *
99
+ * The pre-flight check above catches the typo before it is written, but it cannot be the only
100
+ * guard: reading the environments needs `environments.view`, a permission managing members does not
101
+ * imply, and an environment can be removed between the two calls. So the request and the response
102
+ * are compared here too, and any difference — a code dropped OR a code appearing that nobody asked
103
+ * for, which would mean a wider boundary than requested — is an error, not a footnote.
104
+ *
105
+ * Whatever the server did save stays saved (the boundary is one call away from being fixed, and
106
+ * rolling it back blindly could lock out an agent mid-run), but the command says so and exits
107
+ * non-zero — the same shape as `assertNothingDropped` on a partial bulk triage.
108
+ */
109
+ function assertBoundaryApplied(requested, applied) {
110
+ const inForce = applied.length > 0 ? applied.join(', ') : 'all environments (no restriction)';
111
+ // --all-environments: the removal is the whole point, so a boundary still standing means the
112
+ // command did nothing while looking like it worked.
113
+ if (requested.length === 0) {
114
+ if (applied.length === 0)
115
+ return;
116
+ throw new api_1.ApiRequestError(500, error_codes_1.ErrorCodes.ServiceAccount.restrictionNotApplied, `The restriction was not removed: the account can still read secrets only in ${inForce}. ` +
117
+ 'Nothing else changed — run the command again, or check the account from the web vault.');
118
+ }
119
+ const kept = new Set(applied.map((code) => code.toLowerCase()));
120
+ const asked = new Set(requested);
121
+ const dropped = requested.filter((code) => !kept.has(code));
122
+ const extra = applied.filter((code) => !asked.has(code.toLowerCase()));
123
+ if (dropped.length === 0 && extra.length === 0)
124
+ return;
125
+ const what = dropped.length > 0
126
+ ? `Not applied: ${dropped.join(', ')} — the server kept a different boundary ` +
127
+ '(an environment removed in the meantime, or a code this organization does not have).'
128
+ : `Applied without being asked for: ${extra.join(', ')} — the boundary is wider than requested ` +
129
+ `(${requested.join(', ')}).`;
130
+ throw new api_1.ApiRequestError(404, error_codes_1.ErrorCodes.ServiceAccount.boundaryMismatch, `${what} In force now: ${inForce}. ` +
131
+ 'Run "environments list" to see the real codes, then set the boundary again.');
132
+ }
@@ -0,0 +1,38 @@
1
+ /** Org-level collection of the secret changes waiting for a second approval (CYCL-40). */
2
+ export declare const CHANGE_REQUESTS_PATH = "/cli/v1/vault/change_requests";
3
+ /**
4
+ * A copy of a change-request row without the proposed value.
5
+ *
6
+ * `SecretChangeRequestSerializer` never ships `value` — it is encrypted at rest and a change
7
+ * request is only ever allowed to reveal the NAME and the intent. This strips it anyway, on both
8
+ * the human and the `--json` path: the CLI decides what leaves the terminal, so a payload that one
9
+ * day carried the secret (a serializer edit, a proxy, a hand-rolled backend) cannot leak it through
10
+ * a command whose whole point is reviewing secrets without seeing them.
11
+ */
12
+ export declare function withoutValue<T>(row: T): T;
13
+ /** One row of `vault requests list`: never the value, only what identifies the pending change. */
14
+ export declare function changeRequestRow(row: Record<string, unknown>): string[];
15
+ /** Column headers of `vault requests list`, in the order `changeRequestRow` fills them. */
16
+ export declare const CHANGE_REQUEST_HEADERS: string[];
17
+ /**
18
+ * Short human wording of what a request is about — `STRIPE_KEY (set) on ACME/production`. Empty when
19
+ * the payload names nothing, so the caller can fall back to a bare confirmation instead of printing
20
+ * a line full of question marks.
21
+ */
22
+ export declare function changeRequestLabel(row: Record<string, unknown>): string;
23
+ /**
24
+ * The fields of a decided request worth printing, as an explicit allow-list — the same discipline as
25
+ * `leaseSummary` in `tickets lease claim`: `renderRecord` prints every scalar it is handed, so
26
+ * handing it the raw payload would print whatever the server adds next. Absent/null fields are
27
+ * dropped rather than shown as `-`: an approval has no rejection reason, and a row of dashes reads
28
+ * like missing data instead of "not applicable".
29
+ */
30
+ export declare function changeRequestSummary(row: Record<string, unknown>): Record<string, unknown>;
31
+ /**
32
+ * Append the way out to a refused decision, keeping the server's own message and code intact — the
33
+ * pattern `fetchLookupRows` already uses on a 403 in `BaseCommand`. Anything else (a 404 on a
34
+ * request outside the visible projects, a plain "Permesso negato" from the RBAC gate, a network
35
+ * failure) passes through untouched: inventing an explanation for an error we have not identified
36
+ * would be worse than the bare code.
37
+ */
38
+ export declare function describeDecisionError(error: unknown): unknown;
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CHANGE_REQUEST_HEADERS = exports.CHANGE_REQUESTS_PATH = void 0;
4
+ exports.withoutValue = withoutValue;
5
+ exports.changeRequestRow = changeRequestRow;
6
+ exports.changeRequestLabel = changeRequestLabel;
7
+ exports.changeRequestSummary = changeRequestSummary;
8
+ exports.describeDecisionError = describeDecisionError;
9
+ const api_1 = require("./api");
10
+ /** Org-level collection of the secret changes waiting for a second approval (CYCL-40). */
11
+ exports.CHANGE_REQUESTS_PATH = '/cli/v1/vault/change_requests';
12
+ /**
13
+ * A copy of a change-request row without the proposed value.
14
+ *
15
+ * `SecretChangeRequestSerializer` never ships `value` — it is encrypted at rest and a change
16
+ * request is only ever allowed to reveal the NAME and the intent. This strips it anyway, on both
17
+ * the human and the `--json` path: the CLI decides what leaves the terminal, so a payload that one
18
+ * day carried the secret (a serializer edit, a proxy, a hand-rolled backend) cannot leak it through
19
+ * a command whose whole point is reviewing secrets without seeing them.
20
+ */
21
+ function withoutValue(row) {
22
+ if (row === null || typeof row !== 'object')
23
+ return row;
24
+ const { value: _value, ...rest } = row;
25
+ return rest;
26
+ }
27
+ /** `key` of the project the change belongs to (its name as a fallback), or '' when the payload is silent. */
28
+ function projectLabel(row) {
29
+ const project = (row.project ?? {});
30
+ return String(project.key ?? project.name ?? '');
31
+ }
32
+ /** `code` of the target environment (its label as a fallback), or '' when the payload is silent. */
33
+ function environmentLabel(row) {
34
+ const environment = (row.environment ?? {});
35
+ return String(environment.code ?? environment.label ?? '');
36
+ }
37
+ /**
38
+ * Who asked for the change: name, email as a fallback, '' when nobody is attached. The requester FK
39
+ * is nullified when the account is deleted (the audit outlives the person), so this is a real case,
40
+ * not a defensive flourish.
41
+ */
42
+ function requesterLabel(row) {
43
+ const requester = (row.requested_by ?? {});
44
+ return String(requester.name ?? requester.email ?? '');
45
+ }
46
+ /** One row of `vault requests list`: never the value, only what identifies the pending change. */
47
+ function changeRequestRow(row) {
48
+ return [
49
+ String(row.id ?? ''),
50
+ projectLabel(row),
51
+ environmentLabel(row),
52
+ String(row.action ?? ''),
53
+ String(row.name ?? ''),
54
+ requesterLabel(row),
55
+ String(row.created_at ?? ''),
56
+ ];
57
+ }
58
+ /** Column headers of `vault requests list`, in the order `changeRequestRow` fills them. */
59
+ exports.CHANGE_REQUEST_HEADERS = ['ID', 'PROJECT', 'ENVIRONMENT', 'ACTION', 'NAME', 'REQUESTED BY', 'ASKED AT'];
60
+ /**
61
+ * Short human wording of what a request is about — `STRIPE_KEY (set) on ACME/production`. Empty when
62
+ * the payload names nothing, so the caller can fall back to a bare confirmation instead of printing
63
+ * a line full of question marks.
64
+ */
65
+ function changeRequestLabel(row) {
66
+ const name = String(row.name ?? '').trim();
67
+ if (name === '')
68
+ return '';
69
+ const action = String(row.action ?? '').trim();
70
+ const where = [projectLabel(row), environmentLabel(row)].filter(Boolean).join('/');
71
+ return [name, action && `(${action})`, where && `on ${where}`].filter(Boolean).join(' ');
72
+ }
73
+ /**
74
+ * The fields of a decided request worth printing, as an explicit allow-list — the same discipline as
75
+ * `leaseSummary` in `tickets lease claim`: `renderRecord` prints every scalar it is handed, so
76
+ * handing it the raw payload would print whatever the server adds next. Absent/null fields are
77
+ * dropped rather than shown as `-`: an approval has no rejection reason, and a row of dashes reads
78
+ * like missing data instead of "not applicable".
79
+ */
80
+ function changeRequestSummary(row) {
81
+ const summary = {
82
+ id: row.id,
83
+ project: projectLabel(row) || undefined,
84
+ environment: environmentLabel(row) || undefined,
85
+ name: row.name,
86
+ action: row.action,
87
+ status: row.status,
88
+ requested_by: requesterLabel(row) || undefined,
89
+ requested_at: row.created_at,
90
+ decided_at: row.decided_at,
91
+ reason: row.reason,
92
+ };
93
+ return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== undefined && value !== null));
94
+ }
95
+ /**
96
+ * The four ways a decision is refused, each with the way out. The backend codes are stable and its
97
+ * messages are already clear about WHAT happened; what a refusal alone does not say is what to do
98
+ * next — and all four look identical from the outside ("it did not work"), while they call for
99
+ * opposite reactions: hand the request to a colleague, log in as yourself, refresh the queue, write
100
+ * a real reason.
101
+ */
102
+ const DECISION_HINTS = {
103
+ // Four-eyes separation: whoever decides is never whoever asked (Secrets::ChangeRequests::DecisionGuard).
104
+ 'R403-CHANGEREQUEST-001': 'The four-eyes rule holds here: this one has to be decided by someone else with secrets.manage on that project.',
105
+ // CYRA-640: the second approval is a person's, never a machine's — a service-account token
106
+ // authenticates like a personal one, so the refusal has to name the difference.
107
+ 'R403-CHANGEREQUEST-003': 'Only a person can decide a secret change request: a service account cannot, so run this with your own login.',
108
+ 'R409-CHANGEREQUEST-001': 'It was already decided (applied, rejected or withdrawn) — run "vault requests list" to see what is still waiting.',
109
+ 'R422-CHANGEREQUEST-001': 'Pass a --reason that says why: a rejection freezes the request, and whoever asked needs to know what to fix.',
110
+ };
111
+ /**
112
+ * Append the way out to a refused decision, keeping the server's own message and code intact — the
113
+ * pattern `fetchLookupRows` already uses on a 403 in `BaseCommand`. Anything else (a 404 on a
114
+ * request outside the visible projects, a plain "Permesso negato" from the RBAC gate, a network
115
+ * failure) passes through untouched: inventing an explanation for an error we have not identified
116
+ * would be worse than the bare code.
117
+ */
118
+ function describeDecisionError(error) {
119
+ if (!(error instanceof api_1.ApiRequestError))
120
+ return error;
121
+ const hint = DECISION_HINTS[error.code];
122
+ if (!hint)
123
+ return error;
124
+ return new api_1.ApiRequestError(error.status, error.code, `${error.message}. ${hint}`, error.details);
125
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * La board del carico di lavoro non-dev (fiere, materiale cartaceo, incontri, chiamate): una sola
3
+ * raccolta d'organizzazione, con radice di tenancy il TEAM e non il progetto — per questo l'endpoint
4
+ * non è annidato sotto `/projects/:id` come quasi tutto il resto della CLI.
5
+ */
6
+ export declare const WORKLOAD_ACTIONS_PATH = "/cli/v1/workload/actions";
7
+ /** Gli stati dell'enum del server, nell'ordine in cui un'attività li attraversa. */
8
+ export declare const WORKLOAD_STATUSES: string[];
9
+ /**
10
+ * I campi che `workload create` e `workload update` scrivono allo stesso modo. Il titolo resta
11
+ * fuori: nasce obbligatorio sulla creazione e facoltativo sulla modifica. Il team pure — è
12
+ * immutabile lato server (spostare un'attività cambierebbe chi la vede), quindi si indica solo
13
+ * quando l'attività nasce.
14
+ */
15
+ export declare const actionFieldFlags: {
16
+ description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
+ 'scheduled-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
+ 'due-at': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ ticket: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ };
22
+ type ActionFlags = Record<string, unknown>;
23
+ /** Popola `body` coi campi scalari presenti nei flag (chiavi snake_case attese dall'API CLI). */
24
+ export declare function applyActionBody(body: ActionFlags, flags: ActionFlags): void;
25
+ /**
26
+ * Vero quando il ticket indicato NON è finito sull'attività: il server sostituisce in silenzio con
27
+ * «nessun ticket» un id che chi scrive non può vedere (anti-BOLA), e senza dirlo l'attività
28
+ * risulterebbe collegata a chi l'ha appena salvata — mentre non lo è. Falso quando non c'era niente
29
+ * da collegare (flag assente, o stringa vuota che scollega apposta) o quando il legame c'è davvero.
30
+ *
31
+ * Sulla modifica la conseguenza è più pesante che sulla creazione — quel silenzio toglie anche il
32
+ * ticket che c'era prima — quindi l'avviso vero e proprio lo scrive ciascun comando.
33
+ */
34
+ export declare function ticketLinkMissing(reference: string | undefined, resolvedId: string | undefined, data: Record<string, unknown>): boolean;
35
+ /** La riga dei partecipanti: `renderRecord` stampa solo i campi scalari, e i nomi sono un elenco. */
36
+ export declare function participantsLine(data: Record<string, unknown>): string;
37
+ export {};
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.actionFieldFlags = exports.WORKLOAD_STATUSES = exports.WORKLOAD_ACTIONS_PATH = void 0;
4
+ exports.applyActionBody = applyActionBody;
5
+ exports.ticketLinkMissing = ticketLinkMissing;
6
+ exports.participantsLine = participantsLine;
7
+ const core_1 = require("@oclif/core");
8
+ const output_1 = require("./output");
9
+ /**
10
+ * La board del carico di lavoro non-dev (fiere, materiale cartaceo, incontri, chiamate): una sola
11
+ * raccolta d'organizzazione, con radice di tenancy il TEAM e non il progetto — per questo l'endpoint
12
+ * non è annidato sotto `/projects/:id` come quasi tutto il resto della CLI.
13
+ */
14
+ exports.WORKLOAD_ACTIONS_PATH = '/cli/v1/workload/actions';
15
+ /** Gli stati dell'enum del server, nell'ordine in cui un'attività li attraversa. */
16
+ exports.WORKLOAD_STATUSES = ['planned', 'in_progress', 'done', 'cancelled'];
17
+ /**
18
+ * I campi che `workload create` e `workload update` scrivono allo stesso modo. Il titolo resta
19
+ * fuori: nasce obbligatorio sulla creazione e facoltativo sulla modifica. Il team pure — è
20
+ * immutabile lato server (spostare un'attività cambierebbe chi la vede), quindi si indica solo
21
+ * quando l'attività nasce.
22
+ */
23
+ exports.actionFieldFlags = {
24
+ description: core_1.Flags.string({ description: 'What the activity is about (free text)' }),
25
+ status: core_1.Flags.string({
26
+ description: `Status (${exports.WORKLOAD_STATUSES.join(', ')})`,
27
+ options: exports.WORKLOAD_STATUSES,
28
+ }),
29
+ 'scheduled-at': core_1.Flags.string({ description: 'When it takes place (ISO 8601)' }),
30
+ 'due-at': core_1.Flags.string({ description: 'Deadline (ISO 8601)' }),
31
+ ticket: core_1.Flags.string({
32
+ description: 'Link an existing ticket by code (e.g. DRFL-3) or id — pass an empty string to unlink',
33
+ }),
34
+ };
35
+ /** Popola `body` coi campi scalari presenti nei flag (chiavi snake_case attese dall'API CLI). */
36
+ function applyActionBody(body, flags) {
37
+ if (flags.description !== undefined)
38
+ body.description = flags.description;
39
+ if (flags.status !== undefined)
40
+ body.status = flags.status;
41
+ if (flags['scheduled-at'] !== undefined)
42
+ body.scheduled_at = flags['scheduled-at'];
43
+ if (flags['due-at'] !== undefined)
44
+ body.due_at = flags['due-at'];
45
+ }
46
+ /**
47
+ * Vero quando il ticket indicato NON è finito sull'attività: il server sostituisce in silenzio con
48
+ * «nessun ticket» un id che chi scrive non può vedere (anti-BOLA), e senza dirlo l'attività
49
+ * risulterebbe collegata a chi l'ha appena salvata — mentre non lo è. Falso quando non c'era niente
50
+ * da collegare (flag assente, o stringa vuota che scollega apposta) o quando il legame c'è davvero.
51
+ *
52
+ * Sulla modifica la conseguenza è più pesante che sulla creazione — quel silenzio toglie anche il
53
+ * ticket che c'era prima — quindi l'avviso vero e proprio lo scrive ciascun comando.
54
+ */
55
+ function ticketLinkMissing(reference, resolvedId, data) {
56
+ if (!reference || !resolvedId)
57
+ return false;
58
+ return data.ticket_id !== resolvedId;
59
+ }
60
+ /** La riga dei partecipanti: `renderRecord` stampa solo i campi scalari, e i nomi sono un elenco. */
61
+ function participantsLine(data) {
62
+ const names = (Array.isArray(data.participants) ? data.participants : []).map((name) => (0, output_1.sanitize)(name));
63
+ return `participants: ${names.length > 0 ? names.join(', ') : '-'}`;
64
+ }