@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/README.md
CHANGED
|
@@ -85,6 +85,16 @@ CLOSEYOURIT_TOKEN=cyi_u_… CLOSEYOURIT_API_URL=https://www.closeyour.it \
|
|
|
85
85
|
| `vulnerabilities promote <id>` | Open a ticket from it — automatic promotion only fires on high/critical. |
|
|
86
86
|
| `vulnerabilities rescan --project <id\|key>` | Rescan now, without waiting for the nightly run. |
|
|
87
87
|
| `vulnerabilities runtimes [--project <id\|key>] [--page]` | Support state of the declared runtimes (Ruby, Node, Flutter…). |
|
|
88
|
+
| `seo list [--project <id\|key>] [--severity] [--status] [--area] [--page]` | List the SEO findings of your sites, worst first. |
|
|
89
|
+
| `seo show <id>` | Show one, with the evidence behind it. |
|
|
90
|
+
| `seo pages [--project <id\|key>] [--url] [--page]` | The pages the check visited: status, title, h1, words. |
|
|
91
|
+
| `seo ignore <id> [--note]` / `reopen <id>` | Accept living with a finding, or change your mind (requires seo.triage). |
|
|
92
|
+
| `seo promote <id>` | Open a ticket from it, evidence included. |
|
|
93
|
+
| `seo rescan --site <id>` | Check a site now, without waiting for the scheduled run (requires seo.manage). |
|
|
94
|
+
| `seo-sites list [--project <id\|key>] [--page]` | The sites under SEO check, with cadence and open findings. |
|
|
95
|
+
| `seo-sites create --project <id\|key> --url <url> --environment <id> [--frequency] [--max-pages] [--no-sitemap]` | Declare a site to watch; the first check starts right away. |
|
|
96
|
+
| `seo-sites update <id> [--url] [--frequency] [--max-pages] [--sitemap\|--no-sitemap] [--enable\|--disable]` | Change how a site is checked. |
|
|
97
|
+
| `seo-sites delete <id>` | Stop watching a site and remove its pages and findings. |
|
|
88
98
|
| `tokens list --project <id\|key>` | List ingest tokens. |
|
|
89
99
|
| `tokens create --project <id\|key> --name <name> --environment-id <id>` | Create a token (secret shown once). |
|
|
90
100
|
| `tokens provision --project <id\|key> --name <name> --environment-id <code\|id> --to-project <id\|key> --to-environment-id <code\|id> --secret-name <NAME>` | Generate and deliver a token without revealing its value. GitHub sync is enabled by default; use `--no-sync-github` for vault-only delivery. |
|
|
@@ -99,9 +109,12 @@ CLOSEYOURIT_TOKEN=cyi_u_… CLOSEYOURIT_API_URL=https://www.closeyour.it \
|
|
|
99
109
|
| `secrets assets shared-list\|shared-upload\|delegate\|undelegate …` | Manage one organization-level ciphertext and delegate it to authorized projects. |
|
|
100
110
|
| `tickets list --project <id\|key> [--page]` | List tickets. |
|
|
101
111
|
| `tickets show <id> --project <id\|key>` | Show one ticket. |
|
|
102
|
-
| `tickets create --project <id\|key> --title <t> [--kind bug\|story\|task\|epic] [--parent-id] [--step-*] [--description] [--status
|
|
103
|
-
| `tickets statuses` | List the org ticket statuses (id, code, label) — the
|
|
104
|
-
| `tickets priorities` | List the org ticket priorities (id, code, label) — the
|
|
112
|
+
| `tickets create --project <id\|key> --title <t> [--kind bug\|story\|task\|epic] [--parent-id] [--step-*] [--description] [--status <code\|label\|id>] [--priority <code\|label\|id>]` | Create a ticket. `--status` defaults to `open` and `--priority` to `medium`, and both take a code or a label (`in_progress`, `"In Progress"`), so no id lookup is needed. Length caps: title 255, description 4000, technical analysis 1500 characters — every field over the cap is listed at once, before the call. |
|
|
113
|
+
| `tickets statuses` | List the org ticket statuses (id, code, label) — any of the three works as `--status`. |
|
|
114
|
+
| `tickets priorities` | List the org ticket priorities (id, code, label) — any of the three works as `--priority`. |
|
|
115
|
+
| `tickets report <id\|code> --project <id\|key> (--body <text> \| --body-file <path>)` | Write the work report — the long text a comment cannot hold (max 20000 characters). Each write adds a version, it never overwrites the previous one. |
|
|
116
|
+
| `tickets report-show <id\|code> --project <id\|key> [--version <n>]` | Show the current work report, or a past version. |
|
|
117
|
+
| `tickets report-versions <id\|code> --project <id\|key> [--page]` | List the versions of the work report (newest first). |
|
|
105
118
|
| `tickets reject <id> --project <id\|key> --reason <text>` | Reject the review: back to work, the reason becomes a comment. |
|
|
106
119
|
| `tickets approve <id> --project <id\|key>` | Approve the review: move to the first done status. |
|
|
107
120
|
| `tickets links <id> --project <id\|key>` | List the ticket-to-ticket links (duplicate/related) involving a ticket. |
|
|
@@ -128,9 +141,9 @@ CLOSEYOURIT_TOKEN=cyi_u_… CLOSEYOURIT_API_URL=https://www.closeyour.it \
|
|
|
128
141
|
| `kb search <query…> [--project <id\|key>] [--kind] [--per] [--page] [--with-related]` | Search knowledge pages (semantic when available, title ILIKE fallback). |
|
|
129
142
|
| `kb show <id> [--related] [--question <q>]` | Show one knowledge page: metadata header, raw markdown body, then the technical section. |
|
|
130
143
|
| `kb related <id> [--question <q>] [--links-only]` | Pages to read next: linked with `[[wiki links]]` plus close matches by meaning. |
|
|
131
|
-
| `kb create --project <id\|key> --title <t> [--kind note\|decision\|guide] (--body <md> \| --body-file <path>) [--tech-spec <md> \| --tech-spec-file <path>] [--in-review] [--review-note <line>]` | Create a knowledge page (optional technical section, kept separate from the body). With `--in-review` the page waits for a human and stays out of search, answers and related panels. |
|
|
144
|
+
| `kb create --project <id\|key> --title <t> [--kind note\|decision\|guide] (--body <md> \| --body-file <path>) [--tech-spec <md> \| --tech-spec-file <path>] [--in-review] [--review-note <line>]` | Create a knowledge page (optional technical section, kept separate from the body). Length caps: title 255, body 4000, technical section 1500, review note 240 characters — every field over the cap is listed at once, before the call. With `--in-review` the page waits for a human and stays out of search, answers and related panels. |
|
|
132
145
|
| `kb consolidate <id> --path <doc-path>` | Mark an accepted page as written to the versioned docs, recording where the document lives. |
|
|
133
|
-
| `kb update <id> [--title] [--kind] [--body \| --body-file] [--tech-spec \| --tech-spec-file]` | Update a page (unpassed fields keep their current value). |
|
|
146
|
+
| `kb update <id> [--title] [--kind] [--body \| --body-file] [--tech-spec \| --tech-spec-file]` | Update a page (unpassed fields keep their current value). Same length caps as `kb create`; text already over the cap is refused only if it grows. |
|
|
134
147
|
| `kb delete <id> --confirm` | Delete a knowledge page (irreversible). |
|
|
135
148
|
| `kb ask <question…>` | Ask a question; the AI answers from the knowledge base and cites the pages it used. |
|
|
136
149
|
| `servers list [--status] [--page]` | List the monitored servers (fleet snapshot: status, cpu/mem/disk, last seen). |
|
|
@@ -219,6 +232,17 @@ CLOSEYOURIT_TOKEN=cyi_u_… CLOSEYOURIT_API_URL=https://www.closeyour.it \
|
|
|
219
232
|
Every command accepts `--json` for machine-readable output and `--help` for usage details.
|
|
220
233
|
`--project` accepts either a project UUID or its key (matched case-insensitively).
|
|
221
234
|
|
|
235
|
+
When a command fails in `--json` mode it exits non-zero and prints an error envelope on stdout —
|
|
236
|
+
never a silent success:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{ "error": { "code": "C400-INP-001", "message": "Missing required flag project" } }
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`C400-INP-001` means the command line itself needs fixing (missing flag or argument, unknown flag,
|
|
243
|
+
missing `--confirm`) and `message` says what; `C500-SYS-001` is reserved for unexpected failures.
|
|
244
|
+
Codes starting with `R`/`G` come straight from the backend.
|
|
245
|
+
|
|
222
246
|
## Configuration
|
|
223
247
|
|
|
224
248
|
The CLI stores its state in:
|
package/dist/base.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, type Interfaces } from '@oclif/core';
|
|
2
2
|
import { type CliConfig } from './lib/config';
|
|
3
3
|
import { CliApi } from './lib/api';
|
|
4
|
+
import { type TicketLookup } from './lib/ticket-lookup';
|
|
4
5
|
export declare const UUID_RE: RegExp;
|
|
5
6
|
/** Shared `--project <id|key>` flag (required). */
|
|
6
7
|
export declare const projectFlag: {
|
|
@@ -33,6 +34,16 @@ export declare abstract class BaseCommand extends Command {
|
|
|
33
34
|
protected resolveTeamId(value: string): Promise<string>;
|
|
34
35
|
/** Resolve a member reference (UUID account id passes through; otherwise matched by email). */
|
|
35
36
|
protected resolveMemberId(value: string): Promise<string>;
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a ticket status/priority reference: a UUID passes through, anything else is matched
|
|
39
|
+
* against the organization lookup by code first, then by label (case-insensitive).
|
|
40
|
+
*
|
|
41
|
+
* `undefined` means the flag was not passed, so the lookup's own default applies (`open`,
|
|
42
|
+
* `medium`): the server demands both fields on a new ticket while the help gave them as optional,
|
|
43
|
+
* and getting through used to take two id hunts (`tickets statuses`, `tickets priorities`) and a
|
|
44
|
+
* pair of UUIDs pasted by hand (CYCL-34).
|
|
45
|
+
*/
|
|
46
|
+
protected resolveTicketLookupId(lookup: TicketLookup, value?: string): Promise<string>;
|
|
36
47
|
/** Resolve each value of a list to an id, preserving order. */
|
|
37
48
|
protected resolveAll(values: string[], resolve: (value: string) => Promise<string>): Promise<string[]>;
|
|
38
49
|
/** Resolve an `{id, name}` reference by name (UUID passes through). */
|
package/dist/base.js
CHANGED
|
@@ -6,6 +6,8 @@ const config_1 = require("./lib/config");
|
|
|
6
6
|
const api_1 = require("./lib/api");
|
|
7
7
|
const output_1 = require("./lib/output");
|
|
8
8
|
const error_codes_1 = require("./errors/error-codes");
|
|
9
|
+
const input_error_1 = require("./errors/input-error");
|
|
10
|
+
const ticket_lookup_1 = require("./lib/ticket-lookup");
|
|
9
11
|
exports.UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
12
|
/** Shared `--project <id|key>` flag (required). */
|
|
11
13
|
exports.projectFlag = {
|
|
@@ -72,6 +74,25 @@ class BaseCommand extends core_1.Command {
|
|
|
72
74
|
}
|
|
73
75
|
return match.account_id;
|
|
74
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolve a ticket status/priority reference: a UUID passes through, anything else is matched
|
|
79
|
+
* against the organization lookup by code first, then by label (case-insensitive).
|
|
80
|
+
*
|
|
81
|
+
* `undefined` means the flag was not passed, so the lookup's own default applies (`open`,
|
|
82
|
+
* `medium`): the server demands both fields on a new ticket while the help gave them as optional,
|
|
83
|
+
* and getting through used to take two id hunts (`tickets statuses`, `tickets priorities`) and a
|
|
84
|
+
* pair of UUIDs pasted by hand (CYCL-34).
|
|
85
|
+
*/
|
|
86
|
+
async resolveTicketLookupId(lookup, value) {
|
|
87
|
+
const wanted = value ?? lookup.fallback;
|
|
88
|
+
if (exports.UUID_RE.test(wanted))
|
|
89
|
+
return wanted;
|
|
90
|
+
const rows = (await this.api.get(lookup.path)).data;
|
|
91
|
+
const matches = (0, ticket_lookup_1.matchTicketLookup)(rows, wanted);
|
|
92
|
+
if (matches.length === 1)
|
|
93
|
+
return matches[0].id;
|
|
94
|
+
throw new api_1.ApiRequestError(404, lookup.errorCode, (0, ticket_lookup_1.ticketLookupErrorMessage)(lookup, wanted, rows, { matched: matches.length, isDefault: value === undefined }));
|
|
95
|
+
}
|
|
75
96
|
/** Resolve each value of a list to an id, preserving order. */
|
|
76
97
|
resolveAll(values, resolve) {
|
|
77
98
|
return Promise.all(values.map((value) => resolve(value)));
|
|
@@ -102,6 +123,16 @@ class BaseCommand extends core_1.Command {
|
|
|
102
123
|
}
|
|
103
124
|
return this.exit(1);
|
|
104
125
|
}
|
|
126
|
+
// A rejected command line is not a server failure: oclif refuses it before the command body runs
|
|
127
|
+
// (missing required flag/arg, nonexistent flag, value outside the options) and the `this.error(...)`
|
|
128
|
+
// guards refuse it inside. Both used to collapse into the generic C500-SYS-001 "Command failed" in
|
|
129
|
+
// --json, so a script could not tell a forgotten flag from an outage. Emit the same message the
|
|
130
|
+
// human mode prints, under a dedicated code (CYCL-33). Human mode is untouched: oclif keeps
|
|
131
|
+
// rendering its own error plus the usage help.
|
|
132
|
+
if ((0, input_error_1.isInputError)(error) && this.jsonEnabled()) {
|
|
133
|
+
this.logJson({ error: { code: error_codes_1.ErrorCodes.Input.invalid, message: (0, input_error_1.inputErrorMessage)(error) } });
|
|
134
|
+
return this.exit(1);
|
|
135
|
+
}
|
|
105
136
|
// Oclif's default JSON formatter serializes the complete CommandError. That object references
|
|
106
137
|
// the command instance and therefore cfg/api.config, including the bearer token. Never pass an
|
|
107
138
|
// unknown error to that formatter in JSON mode: emit a deliberately small allow-listed envelope.
|
|
@@ -5,6 +5,9 @@ class AlertsNotificationsReadAll extends base_1.BaseCommand {
|
|
|
5
5
|
static description = 'Mark all your unread notifications as read';
|
|
6
6
|
static examples = ['<%= config.bin %> alerts notifications read-all'];
|
|
7
7
|
async run() {
|
|
8
|
+
// Validate the command line before marking anything read: a mistyped flag must stop the
|
|
9
|
+
// command instead of being ignored (CYCL-33).
|
|
10
|
+
await this.parse(AlertsNotificationsReadAll);
|
|
8
11
|
const res = await this.api.put('/cli/v1/alert_notifications/read_all');
|
|
9
12
|
if (!this.jsonEnabled()) {
|
|
10
13
|
this.log(`Marked ${res.data?.marked_read ?? 0} notification(s) read`);
|
|
@@ -6,6 +6,8 @@ class AlertsPreferencesShow extends base_1.BaseCommand {
|
|
|
6
6
|
static description = 'Show your personal alert preferences (per organization)';
|
|
7
7
|
static examples = ['<%= config.bin %> alerts preferences show'];
|
|
8
8
|
async run() {
|
|
9
|
+
// No flags of its own, but the parser is what rejects a mistyped one (CYCL-33).
|
|
10
|
+
await this.parse(AlertsPreferencesShow);
|
|
9
11
|
const res = await this.api.get('/cli/v1/alert_preferences');
|
|
10
12
|
if (!this.jsonEnabled()) {
|
|
11
13
|
this.log((0, output_1.renderRecord)(res.data ?? {}));
|
|
@@ -6,11 +6,11 @@ export default class IdeasConvert extends BaseCommand {
|
|
|
6
6
|
static description: string;
|
|
7
7
|
static examples: string[];
|
|
8
8
|
static flags: {
|
|
9
|
+
status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
11
|
title: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
12
|
description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
13
|
kind: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
-
'status-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
-
'priority-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
14
14
|
project: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
15
15
|
};
|
|
16
16
|
run(): Promise<unknown>;
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const base_1 = require("../../base");
|
|
5
|
+
const limits_1 = require("../../lib/limits");
|
|
5
6
|
const output_1 = require("../../lib/output");
|
|
7
|
+
const ticket_lookup_1 = require("../../lib/ticket-lookup");
|
|
6
8
|
class IdeasConvert extends base_1.BaseCommand {
|
|
7
9
|
static args = {
|
|
8
10
|
id: core_1.Args.string({ description: 'Idea id', required: true }),
|
|
@@ -14,14 +16,28 @@ class IdeasConvert extends base_1.BaseCommand {
|
|
|
14
16
|
];
|
|
15
17
|
static flags = {
|
|
16
18
|
...base_1.projectFlag,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
// Il ticket nasce qui, quindi valgono i tetti di `tickets create` (CYCL-32).
|
|
20
|
+
title: core_1.Flags.string({
|
|
21
|
+
description: `Ticket title (skips AI when set together with --description, max ${limits_1.LENGTH_LIMITS.title} characters)`,
|
|
22
|
+
}),
|
|
23
|
+
description: core_1.Flags.string({
|
|
24
|
+
description: `Ticket description (skips AI when set together with --title, max ${limits_1.LENGTH_LIMITS.description} characters)`,
|
|
25
|
+
}),
|
|
19
26
|
kind: core_1.Flags.string({ description: 'Ticket kind (default: story)', options: ['bug', 'story', 'task', 'epic'] }),
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
// Il ticket nasce qui: stato e priorità si indicano per code o etichetta, e omettendoli vale il
|
|
28
|
+
// predefinito che il server applica alla conversione (CYCL-34).
|
|
29
|
+
...ticket_lookup_1.newTicketLookupFlags,
|
|
22
30
|
};
|
|
23
31
|
async run() {
|
|
24
32
|
const { args, flags } = await this.parse(IdeasConvert);
|
|
33
|
+
// Il ticket è nuovo: niente da salvaguardare, tetto pieno, e i campi troppo lunghi si leggono
|
|
34
|
+
// tutti in questo unico rifiuto — prima dei ~30 secondi di sintesi AI, non dopo.
|
|
35
|
+
const tooLong = (0, limits_1.tooLongMessage)([
|
|
36
|
+
{ flag: '--title', value: flags.title, max: limits_1.LENGTH_LIMITS.title, orthography: true },
|
|
37
|
+
{ flag: '--description', value: flags.description, max: limits_1.LENGTH_LIMITS.description, orthography: true },
|
|
38
|
+
]);
|
|
39
|
+
if (tooLong)
|
|
40
|
+
this.error(tooLong, { exit: 2 });
|
|
25
41
|
const projectId = await this.resolveProjectId(flags.project);
|
|
26
42
|
const body = {};
|
|
27
43
|
if (flags.title !== undefined)
|
|
@@ -30,10 +46,11 @@ class IdeasConvert extends base_1.BaseCommand {
|
|
|
30
46
|
body.description = flags.description;
|
|
31
47
|
if (flags.kind !== undefined)
|
|
32
48
|
body.kind = flags.kind;
|
|
33
|
-
if (flags
|
|
34
|
-
body.status_id = flags
|
|
35
|
-
if (flags
|
|
36
|
-
body.priority_id = flags
|
|
49
|
+
if (flags.status !== undefined)
|
|
50
|
+
body.status_id = await this.resolveTicketLookupId(ticket_lookup_1.TICKET_STATUS, flags.status);
|
|
51
|
+
if (flags.priority !== undefined) {
|
|
52
|
+
body.priority_id = await this.resolveTicketLookupId(ticket_lookup_1.TICKET_PRIORITY, flags.priority);
|
|
53
|
+
}
|
|
37
54
|
if (!this.jsonEnabled() && (flags.title === undefined || flags.description === undefined)) {
|
|
38
55
|
this.log('Synthesizing the ticket draft with AI (idea + comments), this may take ~30s…');
|
|
39
56
|
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const promises_1 = require("node:fs/promises");
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const base_1 = require("../../base");
|
|
6
|
+
const limits_1 = require("../../lib/limits");
|
|
6
7
|
const output_1 = require("../../lib/output");
|
|
7
8
|
class KbCreate extends base_1.BaseCommand {
|
|
8
9
|
static description = 'Create a knowledge page in a project';
|
|
@@ -15,21 +16,31 @@ class KbCreate extends base_1.BaseCommand {
|
|
|
15
16
|
static flags = {
|
|
16
17
|
// Required — passed straight to the server as `project` (key or UUID), resolved there.
|
|
17
18
|
...base_1.projectFlag,
|
|
18
|
-
title: core_1.Flags.string({ description:
|
|
19
|
+
title: core_1.Flags.string({ description: `Page title (max ${limits_1.LENGTH_LIMITS.title} characters)`, required: true }),
|
|
19
20
|
kind: core_1.Flags.string({ description: 'Page kind: note|decision|guide', default: 'note' }),
|
|
20
|
-
body: core_1.Flags.string({ description:
|
|
21
|
-
'body-file': core_1.Flags.string({
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
body: core_1.Flags.string({ description: `Page body (markdown, max ${limits_1.LENGTH_LIMITS.pageBody} characters)` }),
|
|
22
|
+
'body-file': core_1.Flags.string({
|
|
23
|
+
description: `Read the body (markdown, max ${limits_1.LENGTH_LIMITS.pageBody} characters) from a local file`,
|
|
24
|
+
}),
|
|
25
|
+
'tech-spec': core_1.Flags.string({
|
|
26
|
+
description: `Technical section, kept separate from the simple body (max ${limits_1.LENGTH_LIMITS.techSpec} characters)`,
|
|
27
|
+
}),
|
|
28
|
+
'tech-spec-file': core_1.Flags.string({
|
|
29
|
+
description: `Read the technical section (max ${limits_1.LENGTH_LIMITS.techSpec} characters) from a local file`,
|
|
30
|
+
}),
|
|
24
31
|
// Propose instead of publish: the page waits for a human in the review queue and stays out of
|
|
25
32
|
// search, answers and related panels until it is accepted (see `kb list --status in_review`).
|
|
26
33
|
'in-review': core_1.Flags.boolean({ description: 'Create the page in review instead of publishing it' }),
|
|
27
|
-
'review-note': core_1.Flags.string({
|
|
34
|
+
'review-note': core_1.Flags.string({
|
|
35
|
+
description: `One line explaining why the page is worth keeping (review queue, max ${limits_1.LENGTH_LIMITS.reviewNote} characters)`,
|
|
36
|
+
}),
|
|
28
37
|
};
|
|
29
38
|
async run() {
|
|
30
39
|
const { flags } = await this.parse(KbCreate);
|
|
31
40
|
let body = flags.body;
|
|
41
|
+
let bodyFlag = '--body';
|
|
32
42
|
if (body === undefined && flags['body-file'] !== undefined) {
|
|
43
|
+
bodyFlag = '--body-file';
|
|
33
44
|
try {
|
|
34
45
|
body = await (0, promises_1.readFile)(flags['body-file'], 'utf8');
|
|
35
46
|
}
|
|
@@ -41,7 +52,9 @@ class KbCreate extends base_1.BaseCommand {
|
|
|
41
52
|
this.error('Provide the page body via --body or --body-file.', { exit: 2 });
|
|
42
53
|
}
|
|
43
54
|
let techSpec = flags['tech-spec'];
|
|
55
|
+
let techSpecFlag = '--tech-spec';
|
|
44
56
|
if (techSpec === undefined && flags['tech-spec-file'] !== undefined) {
|
|
57
|
+
techSpecFlag = '--tech-spec-file';
|
|
45
58
|
try {
|
|
46
59
|
techSpec = await (0, promises_1.readFile)(flags['tech-spec-file'], 'utf8');
|
|
47
60
|
}
|
|
@@ -52,6 +65,16 @@ class KbCreate extends base_1.BaseCommand {
|
|
|
52
65
|
if (flags['review-note'] !== undefined && !flags['in-review']) {
|
|
53
66
|
this.error('--review-note only applies to a page created with --in-review.', { exit: 2 });
|
|
54
67
|
}
|
|
68
|
+
// Una pagina nuova non ha niente da salvaguardare: il tetto vale pieno su ogni campo, e i campi
|
|
69
|
+
// troppo lunghi si leggono tutti in questo unico rifiuto invece che uno per tentativo (CYCL-32).
|
|
70
|
+
const tooLong = (0, limits_1.tooLongMessage)([
|
|
71
|
+
{ flag: '--title', value: flags.title, max: limits_1.LENGTH_LIMITS.title },
|
|
72
|
+
{ flag: bodyFlag, value: body, max: limits_1.LENGTH_LIMITS.pageBody },
|
|
73
|
+
{ flag: techSpecFlag, value: techSpec, max: limits_1.LENGTH_LIMITS.techSpec },
|
|
74
|
+
{ flag: '--review-note', value: flags['review-note'], max: limits_1.LENGTH_LIMITS.reviewNote },
|
|
75
|
+
]);
|
|
76
|
+
if (tooLong)
|
|
77
|
+
this.error(tooLong, { exit: 2 });
|
|
55
78
|
const res = await this.api.post('/cli/v1/knowledge/pages', {
|
|
56
79
|
project: flags.project,
|
|
57
80
|
title: flags.title,
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const promises_1 = require("node:fs/promises");
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const base_1 = require("../../base");
|
|
6
|
+
const limits_1 = require("../../lib/limits");
|
|
6
7
|
const output_1 = require("../../lib/output");
|
|
7
8
|
class KbUpdate extends base_1.BaseCommand {
|
|
8
9
|
static description = 'Update a knowledge page (only the fields you pass; the others keep their current value)';
|
|
@@ -15,17 +16,25 @@ class KbUpdate extends base_1.BaseCommand {
|
|
|
15
16
|
id: core_1.Args.string({ description: 'Knowledge page id', required: true }),
|
|
16
17
|
};
|
|
17
18
|
static flags = {
|
|
18
|
-
title: core_1.Flags.string({ description:
|
|
19
|
+
title: core_1.Flags.string({ description: `New title (max ${limits_1.LENGTH_LIMITS.title} characters)` }),
|
|
19
20
|
kind: core_1.Flags.string({ description: 'New kind: note|decision|guide' }),
|
|
20
|
-
body: core_1.Flags.string({ description:
|
|
21
|
-
'body-file': core_1.Flags.string({
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
body: core_1.Flags.string({ description: `New body (markdown, max ${limits_1.LENGTH_LIMITS.pageBody} characters)` }),
|
|
22
|
+
'body-file': core_1.Flags.string({
|
|
23
|
+
description: `Read the new body (markdown, max ${limits_1.LENGTH_LIMITS.pageBody} characters) from a local file`,
|
|
24
|
+
}),
|
|
25
|
+
'tech-spec': core_1.Flags.string({
|
|
26
|
+
description: `New technical section, kept separate from the simple body (max ${limits_1.LENGTH_LIMITS.techSpec} characters)`,
|
|
27
|
+
}),
|
|
28
|
+
'tech-spec-file': core_1.Flags.string({
|
|
29
|
+
description: `Read the new technical section (max ${limits_1.LENGTH_LIMITS.techSpec} characters) from a local file`,
|
|
30
|
+
}),
|
|
24
31
|
};
|
|
25
32
|
async run() {
|
|
26
33
|
const { args, flags } = await this.parse(KbUpdate);
|
|
27
34
|
let body = flags.body;
|
|
35
|
+
let bodyFlag = '--body';
|
|
28
36
|
if (body === undefined && flags['body-file'] !== undefined) {
|
|
37
|
+
bodyFlag = '--body-file';
|
|
29
38
|
try {
|
|
30
39
|
body = await (0, promises_1.readFile)(flags['body-file'], 'utf8');
|
|
31
40
|
}
|
|
@@ -34,7 +43,9 @@ class KbUpdate extends base_1.BaseCommand {
|
|
|
34
43
|
}
|
|
35
44
|
}
|
|
36
45
|
let techSpec = flags['tech-spec'];
|
|
46
|
+
let techSpecFlag = '--tech-spec';
|
|
37
47
|
if (techSpec === undefined && flags['tech-spec-file'] !== undefined) {
|
|
48
|
+
techSpecFlag = '--tech-spec-file';
|
|
38
49
|
try {
|
|
39
50
|
techSpec = await (0, promises_1.readFile)(flags['tech-spec-file'], 'utf8');
|
|
40
51
|
}
|
|
@@ -53,6 +64,16 @@ class KbUpdate extends base_1.BaseCommand {
|
|
|
53
64
|
const base = `/cli/v1/knowledge/pages/${encodeURIComponent(args.id)}`;
|
|
54
65
|
const current = await this.api.get(base);
|
|
55
66
|
const page = current.data ?? {};
|
|
67
|
+
// I campi troppo lunghi si scoprono qui, tutti insieme, invece che uno per rifiuto (CYCL-32). Il
|
|
68
|
+
// valore attuale fa da salvaguardia: una pagina scritta prima che il tetto esistesse si può
|
|
69
|
+
// ancora correggere e accorciare per gradi, com'è dal server.
|
|
70
|
+
const tooLong = (0, limits_1.tooLongMessage)([
|
|
71
|
+
{ flag: '--title', value: flags.title, max: limits_1.LENGTH_LIMITS.title, previous: page.title },
|
|
72
|
+
{ flag: bodyFlag, value: body, max: limits_1.LENGTH_LIMITS.pageBody, previous: page.body },
|
|
73
|
+
{ flag: techSpecFlag, value: techSpec, max: limits_1.LENGTH_LIMITS.techSpec, previous: page.tech_spec },
|
|
74
|
+
]);
|
|
75
|
+
if (tooLong)
|
|
76
|
+
this.error(tooLong, { exit: 2 });
|
|
56
77
|
const res = await this.api.put(base, {
|
|
57
78
|
title: flags.title ?? page.title,
|
|
58
79
|
kind: flags.kind ?? page.kind,
|
package/dist/commands/logout.js
CHANGED
|
@@ -6,6 +6,9 @@ class Logout extends base_1.BaseCommand {
|
|
|
6
6
|
static description = 'Clear the locally stored token and identity';
|
|
7
7
|
static examples = ['<%= config.bin %> logout'];
|
|
8
8
|
async run() {
|
|
9
|
+
// Validate the command line before touching the stored credentials: a mistyped flag must stop
|
|
10
|
+
// the command, not be ignored while it logs you out anyway (CYCL-33).
|
|
11
|
+
await this.parse(Logout);
|
|
9
12
|
(0, config_1.clearToken)();
|
|
10
13
|
if (!this.jsonEnabled()) {
|
|
11
14
|
this.log('Logged out. Local credentials cleared.');
|
|
@@ -6,6 +6,8 @@ class OrgShow extends base_1.BaseCommand {
|
|
|
6
6
|
static description = 'Show your organization settings';
|
|
7
7
|
static examples = ['<%= config.bin %> org show', '<%= config.bin %> org show --json'];
|
|
8
8
|
async run() {
|
|
9
|
+
// No flags of its own, but the parser is what rejects a mistyped one (CYCL-33).
|
|
10
|
+
await this.parse(OrgShow);
|
|
9
11
|
const res = await this.api.get('/cli/v1/organization');
|
|
10
12
|
if (!this.jsonEnabled()) {
|
|
11
13
|
this.log((0, output_1.renderRecord)(res.data ?? {}));
|
|
@@ -11,7 +11,7 @@ class PersonalImport extends base_1.BaseCommand {
|
|
|
11
11
|
static examples = [
|
|
12
12
|
'<%= config.bin %> personal import --from-file .env',
|
|
13
13
|
'<%= config.bin %> personal import --format json --from-file secrets.json',
|
|
14
|
-
'
|
|
14
|
+
'printf \'{"API_KEY":"value"}\' | <%= config.bin %> personal import --format json',
|
|
15
15
|
];
|
|
16
16
|
static flags = {
|
|
17
17
|
'from-file': core_1.Flags.string({ description: 'Path to a .env or JSON file (default: read from stdin)' }),
|
|
@@ -25,7 +25,7 @@ class PersonalImport extends base_1.BaseCommand {
|
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
27
27
|
if (process.stdin.isTTY) {
|
|
28
|
-
this.error('Provide --from-file or pipe
|
|
28
|
+
this.error('Provide --from-file or pipe JSON/ENV input via stdin (e.g. `printf \'{"API_KEY":"value"}\' | cyi personal import --format json`)', { exit: 1 });
|
|
29
29
|
}
|
|
30
30
|
text = await (0, stdin_1.readStream)();
|
|
31
31
|
}
|
|
@@ -7,7 +7,7 @@ class PersonalRun extends base_1.BaseCommand {
|
|
|
7
7
|
static strict = false;
|
|
8
8
|
// The output belongs to the child process, not to us.
|
|
9
9
|
static enableJsonFlag = false;
|
|
10
|
-
static description = 'Run a command with your personal secrets injected as env vars
|
|
10
|
+
static description = 'Run a command with your personal secrets injected as env vars. Secrets never touch disk.';
|
|
11
11
|
static examples = [
|
|
12
12
|
'<%= config.bin %> personal run -- bundle exec rails server',
|
|
13
13
|
'<%= config.bin %> personal run -- printenv OPENAI_API_KEY',
|
package/dist/commands/run.js
CHANGED
|
@@ -7,7 +7,7 @@ class Run extends base_1.BaseCommand {
|
|
|
7
7
|
static strict = false;
|
|
8
8
|
// The output belongs to the child process, not to us.
|
|
9
9
|
static enableJsonFlag = false;
|
|
10
|
-
static description = 'Run a command with
|
|
10
|
+
static description = 'Run a command with project/environment secrets injected as env vars. Secrets never touch disk.';
|
|
11
11
|
static examples = [
|
|
12
12
|
'<%= config.bin %> run -p acme-api -e production -- bundle exec rails server',
|
|
13
13
|
'<%= config.bin %> run -p acme-api -e production -- printenv DATABASE_URL',
|
|
@@ -36,7 +36,7 @@ class SecretsImport extends base_1.BaseCommand {
|
|
|
36
36
|
static examples = [
|
|
37
37
|
'<%= config.bin %> secrets import -p acme-api -e production --from-file .env',
|
|
38
38
|
'<%= config.bin %> secrets import -p acme-api -e production --format json --from-file secrets.json',
|
|
39
|
-
'
|
|
39
|
+
'printf \'{"API_KEY":"value"}\' | <%= config.bin %> secrets import -p acme-api -e development --format json',
|
|
40
40
|
];
|
|
41
41
|
static flags = {
|
|
42
42
|
...base_1.projectFlag,
|
|
@@ -53,7 +53,7 @@ class SecretsImport extends base_1.BaseCommand {
|
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
55
|
if (process.stdin.isTTY) {
|
|
56
|
-
this.error('Provide --from-file or pipe
|
|
56
|
+
this.error('Provide --from-file or pipe JSON/ENV input via stdin (e.g. `printf \'{"API_KEY":"value"}\' | cyi secrets import -p acme-api -e development --format json`)', { exit: 1 });
|
|
57
57
|
}
|
|
58
58
|
text = await (0, stdin_1.readStream)();
|
|
59
59
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SeoIgnore extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
note: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<unknown>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
class SeoIgnore extends base_1.BaseCommand {
|
|
6
|
+
static description = 'Ignore an SEO finding: you accept living with it';
|
|
7
|
+
static examples = [
|
|
8
|
+
'<%= config.bin %> seo ignore <issue-id>',
|
|
9
|
+
'<%= config.bin %> seo ignore <issue-id> --note "Service page, not meant to be found."',
|
|
10
|
+
];
|
|
11
|
+
static args = {
|
|
12
|
+
id: core_1.Args.string({ description: 'SEO finding id', required: true }),
|
|
13
|
+
};
|
|
14
|
+
static flags = {
|
|
15
|
+
note: core_1.Flags.string({ description: 'Why you are keeping it (shown alongside the ignored row)' }),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(SeoIgnore);
|
|
19
|
+
const res = await this.api.put(`/cli/v1/seo/${args.id}/ignore`, { triage_note: flags.note });
|
|
20
|
+
if (!this.jsonEnabled()) {
|
|
21
|
+
// An ignored finding never reopens on its own: the next check won't undo a decision taken.
|
|
22
|
+
this.log(`SEO finding ${args.id} ignored.`);
|
|
23
|
+
}
|
|
24
|
+
return res;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.default = SeoIgnore;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SeoList extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
page: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
severity: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
area: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
run(): Promise<unknown>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const base_1 = require("../../base");
|
|
5
|
+
const output_1 = require("../../lib/output");
|
|
6
|
+
class SeoList extends base_1.BaseCommand {
|
|
7
|
+
static description = 'List the SEO findings of your sites (cross-project)';
|
|
8
|
+
static examples = [
|
|
9
|
+
'<%= config.bin %> seo list',
|
|
10
|
+
'<%= config.bin %> seo list --severity critical',
|
|
11
|
+
'<%= config.bin %> seo list --project acme-site --status open',
|
|
12
|
+
'<%= config.bin %> seo list --area indexability --json',
|
|
13
|
+
];
|
|
14
|
+
static flags = {
|
|
15
|
+
// Optional here (the endpoint is cross-project): without it, the list spans every visible project.
|
|
16
|
+
project: core_1.Flags.string({ char: 'p', description: 'Filter by project id (UUID) or key' }),
|
|
17
|
+
severity: core_1.Flags.string({ description: 'Filter by severity (critical, high, medium, low)' }),
|
|
18
|
+
status: core_1.Flags.string({ description: 'Filter by status (open, resolved, ignored)' }),
|
|
19
|
+
area: core_1.Flags.string({ description: 'Filter by area (indexability, structure, content, i18n, performance)' }),
|
|
20
|
+
...base_1.pageFlag,
|
|
21
|
+
};
|
|
22
|
+
async run() {
|
|
23
|
+
const { flags } = await this.parse(SeoList);
|
|
24
|
+
const query = new URLSearchParams({ page: String(flags.page) });
|
|
25
|
+
if (flags.project)
|
|
26
|
+
query.set('project_id', await this.resolveProjectId(flags.project));
|
|
27
|
+
if (flags.severity)
|
|
28
|
+
query.set('severity', flags.severity);
|
|
29
|
+
if (flags.status)
|
|
30
|
+
query.set('status', flags.status);
|
|
31
|
+
if (flags.area)
|
|
32
|
+
query.set('area', flags.area);
|
|
33
|
+
const res = await this.api.get(`/cli/v1/seo?${query.toString()}`);
|
|
34
|
+
const issues = res.data ?? [];
|
|
35
|
+
if (!this.jsonEnabled()) {
|
|
36
|
+
this.log((0, output_1.renderTable)(
|
|
37
|
+
// ID first, like `vulnerabilities list`: every other subcommand takes it as its argument,
|
|
38
|
+
// so a table without it forces `--json` just to triage what you are already looking at.
|
|
39
|
+
['ID', 'PROJECT', 'CHECK', 'URL', 'SEVERITY', 'STATUS'], issues.map((issue) => [
|
|
40
|
+
String(issue.id ?? ''),
|
|
41
|
+
String(issue.project?.key ?? ''),
|
|
42
|
+
String(issue.check_key ?? ''),
|
|
43
|
+
// A site-wide finding (duplicate titles, unreachable sitemap) has no single URL.
|
|
44
|
+
String(issue.url ?? '— whole site'),
|
|
45
|
+
(0, output_1.statusCell)(issue.severity),
|
|
46
|
+
(0, output_1.statusCell)(issue.status),
|
|
47
|
+
])));
|
|
48
|
+
}
|
|
49
|
+
return res;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.default = SeoList;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base';
|
|
2
|
+
export default class SeoPages extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
page: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
7
|
+
project: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
url: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<unknown>;
|
|
11
|
+
}
|