diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f668eba0..ee468024 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,9 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] node-version: [22.x, 24.x, 25.x] runs-on: ${{ matrix.os }} + # Baseline job time is 1-3 min; cap at 15 to fail fast on a hang instead + # of burning hours and losing logs to blob storage expiry. + timeout-minutes: 15 # Per-endpoint Zod schemas (#171) that once bloated .d.ts emit are gone, # but tsc build still peaks around 3.7 GB locally; keep headroom above # the 2 GB Node default for slower/smaller CI runners. diff --git a/codegen/functional/mapper.ts b/codegen/functional/mapper.ts index 12b3a397..c76aa34b 100644 --- a/codegen/functional/mapper.ts +++ b/codegen/functional/mapper.ts @@ -6,6 +6,7 @@ import type { EsApiDefinition } from '../../src/es/types.ts' import { extractSchemaArgs } from '../../src/lib/json-schema-args.ts' import type { SchemaArgDefinition } from '../../src/lib/json-schema-args.ts' +import { inferIntentFromHttp } from '@cli-schema/spec' /** * Result of mapping a YAML dot-notation action to a CLI command. @@ -62,6 +63,12 @@ export function mapAction ( if (def.namespace != null) args.push(def.namespace) args.push(def.name) + // Destructive commands prompt for confirmation; test scripts run non-interactively. + const intent = def.intent ?? inferIntentFromHttp(def.method) + if (intent?.destructive === true || intent?.requiresConfirmation === true) { + args.push('--yes') + } + const schemaArgs = def.input != null ? extractSchemaArgs(def.input) : [] const bodyFields = new Set( diff --git a/codegen/functional/test/mapper.test.ts b/codegen/functional/test/mapper.test.ts index 40cc1b41..0e94ea7f 100644 --- a/codegen/functional/test/mapper.test.ts +++ b/codegen/functional/test/mapper.test.ts @@ -45,6 +45,20 @@ const testDefs: EsApiDefinition[] = [ description: 'Get cluster info', method: 'GET', path: '/' + }, + { + name: 'delete', + namespace: 'indices', + description: 'Delete an index', + method: 'DELETE', + path: '/{index}', + input: { + type: 'object', + properties: { + index: { type: 'string', 'x-found-in': 'path' }, + }, + required: ['index'], + } } ] @@ -107,4 +121,16 @@ describe('mapAction', () => { assert.ok(result) assert.ok(result.cliArgs.includes('--wait-for-active-shards')) }) + + it('appends --yes for a DELETE action so non-interactive test runs do not prompt', () => { + const result = mapAction('indices.delete', { index: 'test' }, actionMap) + assert.ok(result) + assert.deepStrictEqual(result.cliArgs, ['stack', 'es', 'indices', 'delete', '--yes', '--index', 'test']) + }) + + it('does not append --yes for a non-destructive action', () => { + const result = mapAction('indices.create', { index: 'test' }, actionMap) + assert.ok(result) + assert.ok(!result.cliArgs.includes('--yes')) + }) }) diff --git a/docs/cli/schema.json b/docs/cli/schema.json index 998d4929..3064b25a 100644 --- a/docs/cli/schema.json +++ b/docs/cli/schema.json @@ -533,6 +533,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a document.", @@ -2519,6 +2526,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Clear a scrolling search.", @@ -2585,6 +2599,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Close a point in time.", @@ -5410,6 +5431,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a script or search template.", @@ -6516,6 +6544,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async search.", @@ -11323,6 +11358,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete auto-follow patterns.", @@ -12689,6 +12731,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete component templates.", @@ -12763,6 +12812,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Clear cluster voting config exclusions.", @@ -14146,6 +14202,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector.", @@ -14810,6 +14873,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector sync job.", @@ -16403,6 +16473,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a dangling index.", @@ -16714,6 +16791,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an enrich policy.", @@ -17106,6 +17190,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async EQL search.", @@ -17748,6 +17839,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async ES|QL query.", @@ -18006,6 +18104,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete ES|QL data sources.", @@ -18088,6 +18193,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete ES|QL datasets.", @@ -18156,6 +18268,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an ES|QL view.", @@ -20084,6 +20203,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a lifecycle policy.", @@ -21960,6 +22086,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete indices.", @@ -22050,6 +22183,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an alias.", @@ -22147,6 +22287,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data stream lifecycles.", @@ -22237,6 +22384,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data streams.", @@ -22334,6 +22488,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data stream options.", @@ -22416,6 +22577,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an index template.", @@ -22497,6 +22665,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a legacy index template.", @@ -26856,6 +27031,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Remove an index block.", @@ -28592,6 +28774,13 @@ "type": "string", "required": false, "summary": "path to a JSON file to use as command input" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an inference endpoint.", @@ -28652,6 +28841,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the inference region policy.", @@ -32657,6 +32853,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete GeoIP database configurations.", @@ -32739,6 +32942,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete IP geolocation database configurations.", @@ -32820,6 +33030,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete pipelines.", @@ -33649,6 +33866,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the license.", @@ -34167,6 +34391,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Logstash pipeline.", @@ -34722,6 +34953,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a calendar.", @@ -34796,6 +35034,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete events from a calendar.", @@ -34871,6 +35116,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete anomaly jobs from a calendar.", @@ -34952,6 +35204,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a data frame analytics job.", @@ -35026,6 +35285,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a datafeed.", @@ -35107,6 +35373,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete expired ML data.", @@ -35174,6 +35447,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a filter.", @@ -35262,6 +35542,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete forecasts from a job.", @@ -35350,6 +35637,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an anomaly detection job.", @@ -35424,6 +35718,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a model snapshot.", @@ -35505,6 +35806,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an unreferenced trained model.", @@ -35579,6 +35887,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a trained model alias.", @@ -41345,6 +41660,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Clear the archived repositories metering.", @@ -42231,6 +42553,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a project routing expression.", @@ -42499,6 +42828,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a query rule.", @@ -42566,6 +42902,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a query ruleset.", @@ -43108,6 +43451,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a rollup job.", @@ -43751,6 +44101,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a search application.", @@ -43818,6 +44175,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a behavioral analytics collection.", @@ -45031,6 +45395,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Bulk delete roles.", @@ -46168,6 +46539,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete application privileges.", @@ -46247,6 +46625,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete roles.", @@ -46326,6 +46711,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete role mappings.", @@ -46419,6 +46811,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete service account tokens.", @@ -46498,6 +46897,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete users.", @@ -48297,6 +48703,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Invalidate API keys.", @@ -48385,6 +48798,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Invalidate a token.", @@ -50591,6 +51011,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a policy.", @@ -51737,6 +52164,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete snapshots.", @@ -51819,6 +52253,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete snapshot repositories.", @@ -52835,6 +53276,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async SQL search.", @@ -53655,6 +54103,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a synonym set.", @@ -53736,6 +54191,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a synonym rule.", @@ -55124,6 +55586,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a transform.", @@ -56556,6 +57025,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a watch.", @@ -57843,6 +58319,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent", @@ -58133,6 +58616,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversation by ID", @@ -58345,6 +58835,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversation attachment", @@ -58947,6 +59444,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a plugin", @@ -59180,6 +59684,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a skill", @@ -59479,6 +59990,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a tool", @@ -59622,6 +60140,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a rule", @@ -60312,6 +60837,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a snooze schedule for a rule", @@ -60585,6 +61117,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a backfill by ID", @@ -60667,6 +61206,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete agent configuration", @@ -61294,6 +61840,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector", @@ -61683,6 +62236,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a dashboard", @@ -61895,6 +62455,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a data view", @@ -62170,6 +62737,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a runtime field", @@ -63423,6 +63997,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent binary download source", @@ -65064,6 +65645,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent", @@ -65500,6 +66088,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an uploaded file", @@ -66303,6 +66898,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package", @@ -66542,6 +67144,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package", @@ -66854,6 +67463,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete assets for a package", @@ -66944,6 +67560,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Kibana assets for a package", @@ -67618,6 +68241,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agentless policy", @@ -67786,6 +68416,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete cloud connector (supports force deletion)", @@ -68152,6 +68789,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Revoke or delete an enrollment API key", @@ -68808,6 +69452,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete output", @@ -69637,6 +70288,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package policy", @@ -70177,6 +70835,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a proxy", @@ -70441,6 +71106,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Fleet Server host", @@ -70870,6 +71542,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a maintenance window.", @@ -71477,6 +72156,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a role", @@ -72108,6 +72794,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversations", @@ -72316,6 +73009,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a conversation", @@ -72755,6 +73455,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Deletes a single Knowledge Base Entry using the `id` field", @@ -73685,6 +74392,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Attack Discovery schedule", @@ -73935,6 +74649,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a detection rule", @@ -75224,6 +75945,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Elastic Endpoint exception list item", @@ -77142,6 +77870,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a script", @@ -77391,6 +78126,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an asset criticality record", @@ -77660,6 +78402,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the Privilege Monitoring Engine", @@ -77885,6 +78634,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a monitored user", @@ -78484,6 +79240,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Entity Engines", @@ -78560,6 +79323,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the Entity Engine", @@ -78842,6 +79612,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an entity in Entity Store", @@ -79076,6 +79853,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Cleanup the Risk Engine", @@ -79444,6 +80228,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an entity", @@ -80119,6 +80910,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an exception list", @@ -80682,6 +81480,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an exception list item", @@ -81237,6 +82042,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a value list", @@ -81584,6 +82396,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete value list data streams", @@ -81693,6 +82512,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a value list item", @@ -82714,6 +83540,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a pack", @@ -83067,6 +83900,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a saved query", @@ -83413,6 +84253,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete one or more notes", @@ -83668,6 +84515,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Timelines or Timeline templates", @@ -84759,6 +85613,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an SLO", @@ -85317,6 +86178,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a space", @@ -85531,6 +86399,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a stream", @@ -86058,6 +86933,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Remove a query from a stream", @@ -86366,6 +87248,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Unlink an attachment from a stream", @@ -86616,6 +87505,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a tag", @@ -86910,6 +87806,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a visualization", @@ -86963,6 +87866,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Bulk delete workflows", @@ -87886,6 +88796,13 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a workflow", @@ -88386,6 +89303,7 @@ "parameters": [], "summary": "Fetch current account information", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88415,10 +89333,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Updates the current account", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -88448,10 +89374,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Updates the current account", "intent": { + "destructive": true, "requiresAuth": true } } @@ -88493,6 +89427,7 @@ ], "summary": "Get all API keys", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88538,6 +89473,7 @@ ], "summary": "Create API key", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88568,10 +89504,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete API keys", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -88606,6 +89550,7 @@ ], "summary": "Get API key", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88636,10 +89581,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete API key", "intent": { + "destructive": true, "requiresAuth": true } } @@ -88695,6 +89648,7 @@ ], "summary": "Get costs overview for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88750,6 +89704,7 @@ ], "summary": "Get charts for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88798,6 +89753,7 @@ ], "summary": "Get deployments costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88860,6 +89816,7 @@ ], "summary": "Get charts by deployment. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88915,6 +89872,7 @@ ], "summary": "Get itemized costs by deployments. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -88963,6 +89921,7 @@ ], "summary": "Get itemized costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, "requiresAuth": true } } @@ -88982,6 +89941,7 @@ "parameters": [], "summary": "List organizations", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89016,6 +89976,7 @@ ], "summary": "Get organization invitation", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89046,10 +90007,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Accept an organization invitation", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89084,6 +90053,7 @@ ], "summary": "Fetch organization information", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89147,10 +90117,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update organization", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89185,6 +90163,7 @@ ], "summary": "Get domain claims", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89221,10 +90200,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete domain claim", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89261,10 +90248,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Generate verification code", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89301,10 +90296,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Verify domain claim", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89339,6 +90342,7 @@ ], "summary": "Get organization IdP", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89387,10 +90391,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Setup organization IdP", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89421,10 +90433,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Tear down organization IdP", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89459,6 +90479,7 @@ ], "summary": "Get organization service provider SAML2 metadata.xml for configuring the identity provider", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89493,6 +90514,7 @@ ], "summary": "List organization invitations", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89546,6 +90568,7 @@ ], "summary": "Create organization invitations", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89583,10 +90606,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete organization invitations", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89621,6 +90652,7 @@ ], "summary": "List organization members", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89665,10 +90697,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete organization memberships", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89703,6 +90743,7 @@ ], "summary": "Get role mappings", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89739,10 +90780,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Updates role mappings", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89773,10 +90822,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete role mappings", "intent": { + "destructive": true, "requiresAuth": true } } @@ -89838,10 +90895,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Add Role Assignments", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -89896,10 +90961,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Remove Role Assignments", "intent": { + "destructive": true, "requiresAuth": true } } @@ -89981,6 +91054,7 @@ ], "summary": "Get deployment templates", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90044,6 +91118,7 @@ ], "summary": "Get deployment template", "intent": { + "destructive": false, "requiresAuth": true } } @@ -90064,6 +91139,7 @@ "parameters": [], "summary": "List Deployments", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90155,6 +91231,7 @@ ], "summary": "Create Deployment", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90216,10 +91293,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Search Deployments", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90281,10 +91366,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Get eligible remote clusters", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90418,6 +91511,7 @@ ], "summary": "Get Deployment", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90513,10 +91607,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update Deployment", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90555,10 +91657,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Restores a shutdown Deployment", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90604,10 +91714,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Shuts down Deployment", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90699,6 +91817,7 @@ ], "summary": "Get Deployment APM Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90737,10 +91856,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Reset the secret token for an APM resource.", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90832,6 +91959,7 @@ ], "summary": "Get Deployment App Search Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90874,6 +92002,7 @@ ], "summary": "Set AppSearch read-only status", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90918,10 +92047,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Set AppSearch read-only status", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -90957,6 +92094,7 @@ ], "summary": "Get certificate authority", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91076,6 +92214,7 @@ ], "summary": "Get Deployment Elasticsearch Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91121,10 +92260,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Migrate Elasticsearch and associated Kibana resources to enable CCR", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91176,10 +92323,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Migrate Elasticsearch resource to use ILM", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91225,10 +92380,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Migrate Elasticsearch resource to use SLM", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91274,10 +92437,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Reset 'elastic' user password", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91351,10 +92522,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Restart Deployment Elasticsearch Resource", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91407,10 +92586,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Shutdown Deployment Elasticsearch Resource", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91479,10 +92666,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Get eligible remote clusters", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91525,6 +92720,7 @@ ], "summary": "Get the items in the Elasticsearch resource keystore", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91576,10 +92772,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Add or remove items from the Elasticsearch resource keystore", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91622,6 +92826,7 @@ ], "summary": "Get certificate based remote clusters", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91666,10 +92871,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Set certificate based remote clusters", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91712,6 +92925,7 @@ ], "summary": "List the attached snapshot repositories", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91760,6 +92974,7 @@ ], "summary": "Create a snapshot repository for Elasticsearch resource", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91805,10 +93020,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Remove the attached snapshot repository", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -91851,6 +93074,7 @@ ], "summary": "Get Elasticsearch tiers", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91931,10 +93155,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update Elasticsearch tiers", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92026,6 +93258,7 @@ ], "summary": "Get Deployment Enterprise Search Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92117,6 +93350,7 @@ ], "summary": "Get Deployment Integrations Server Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92215,6 +93449,7 @@ ], "summary": "Get Deployment Kibana Resource Info", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92264,6 +93499,7 @@ ], "summary": "Build request to migrate deployment to a different template", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92299,6 +93535,7 @@ ], "summary": "Get the tags for a Deployment", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92336,10 +93573,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Set the tags for a Deployment", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92377,10 +93622,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Upgrade a Deployment to a new Elastic Stack version", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92423,6 +93676,7 @@ ], "summary": "Get Deployment upgrade assistant status", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92475,10 +93729,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Restores a shutdown resource", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92524,10 +93786,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Start all instances", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92573,10 +93843,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Stop all instances", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92622,10 +93900,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Start maintenance mode (all instances)", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92671,10 +93957,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Stop maintenance mode (all instances)", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92734,10 +94028,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Start instances", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92797,10 +94099,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Stop instances", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92860,10 +94170,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Start maintenance mode", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92923,10 +94241,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Stop maintenance mode", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -92986,10 +94312,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Cancel resource pending plan", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93039,6 +94373,7 @@ ], "summary": "Get the user settings of a Deployment resource", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93090,10 +94425,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update user settings for a deployment resource", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93146,10 +94489,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Restart Deployment Stateless Resource", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93209,10 +94560,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Shutdown Deployment Stateless Resource", "intent": { + "destructive": true, "requiresAuth": true } } @@ -93262,6 +94621,7 @@ ], "summary": "Get associated rulesets", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93304,6 +94664,7 @@ ], "summary": "List traffic filter claimed link id", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93352,10 +94713,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Claim a link id", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93404,10 +94773,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Unclaims a link id", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93457,6 +94834,7 @@ ], "summary": "List traffic filter rulesets", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93521,6 +94899,7 @@ ], "summary": "Create a ruleset", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93563,6 +94942,7 @@ ], "summary": "Retrieves the ruleset by ID.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93630,10 +95010,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Updates a ruleset", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93672,10 +95060,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a ruleset", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -93711,6 +95107,7 @@ ], "summary": "Get associated deployments", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93758,6 +95155,7 @@ ], "summary": "Create ruleset association", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93803,10 +95201,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete ruleset association", "intent": { + "destructive": true, "requiresAuth": true } } @@ -93827,6 +95233,7 @@ "parameters": [], "summary": "List Extensions", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93889,6 +95296,7 @@ ], "summary": "Create an extension", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93931,6 +95339,7 @@ ], "summary": "Get Extension", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -93996,10 +95405,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update Extension", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94031,10 +95448,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Uploads the Extension", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94066,10 +95491,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Extension", "intent": { + "destructive": true, "requiresAuth": true } } @@ -94119,6 +95552,7 @@ ], "summary": "Get stack versions", "intent": { + "destructive": false, "requiresAuth": true } } @@ -94139,6 +95573,7 @@ "parameters": [], "summary": "Get trusted environments", "intent": { + "destructive": false, "requiresAuth": true } } @@ -94207,6 +95642,7 @@ ], "summary": "Get Elasticsearch projects", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94285,6 +95721,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "wait", @@ -94330,6 +95773,7 @@ ], "summary": "Create an Elasticsearch project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94366,6 +95810,7 @@ ], "summary": "Get an Elasticsearch project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94398,10 +95843,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Elasticsearch project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94471,10 +95924,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update an Elasticsearch project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94508,6 +95969,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "save-as", @@ -94546,6 +96014,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94578,10 +96047,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Resume Elasticsearch project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94618,6 +96095,7 @@ ], "summary": "Get roles for an Elasticsearch project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94654,6 +96132,7 @@ ], "summary": "Get the status of an Elasticsearch project", "intent": { + "destructive": false, "requiresAuth": true } } @@ -94711,6 +96190,7 @@ ], "summary": "Get Observability projects", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94783,6 +96263,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "wait", @@ -94828,6 +96315,7 @@ ], "summary": "Create an observability project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94864,6 +96352,7 @@ ], "summary": "Get an Observability project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94896,10 +96385,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Observability project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -94973,10 +96470,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update an Observability project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95010,6 +96515,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "save-as", @@ -95048,6 +96560,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95080,10 +96593,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Resume Observability project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95120,6 +96641,7 @@ ], "summary": "Get roles for an Observability project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95156,6 +96678,7 @@ ], "summary": "Get the status of an Observability project", "intent": { + "destructive": false, "requiresAuth": true } } @@ -95213,6 +96736,7 @@ ], "summary": "Get Security projects", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95297,6 +96821,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "wait", @@ -95342,6 +96873,7 @@ ], "summary": "Create a security project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95378,6 +96910,7 @@ ], "summary": "Get a Security project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95410,10 +96943,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Security project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95496,10 +97037,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Update a Security project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95533,6 +97082,13 @@ "required": false, "summary": "validate all inputs and exit without performing any action" }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" + }, { "role": "flag", "name": "save-as", @@ -95571,6 +97127,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95603,10 +97160,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Resume Security project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95643,6 +97208,7 @@ ], "summary": "Get roles for a Security project", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95679,6 +97245,7 @@ ], "summary": "Get the status of a Security project", "intent": { + "destructive": false, "requiresAuth": true } } @@ -95766,6 +97333,7 @@ ], "summary": "Get Elasticsearch project link candidates", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95843,6 +97411,7 @@ ], "summary": "Get Observability project link candidates", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95920,6 +97489,7 @@ ], "summary": "Get Security project link candidates", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95955,6 +97525,7 @@ ], "summary": "Get Elasticsearch project delete status", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95990,6 +97561,7 @@ ], "summary": "Get Observability project delete status", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -96025,6 +97597,7 @@ ], "summary": "Get Security project delete status", "intent": { + "destructive": false, "requiresAuth": true } } @@ -96045,6 +97618,7 @@ "parameters": [], "summary": "Get regions", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -96080,6 +97654,7 @@ ], "summary": "Get a region", "intent": { + "destructive": false, "requiresAuth": true } } @@ -96129,6 +97704,7 @@ ], "summary": "List traffic filters", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -96194,10 +97770,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Create a traffic filter", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96240,6 +97824,7 @@ ], "summary": "List PrivateLink region metadata", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -96275,6 +97860,7 @@ ], "summary": "Retrieves the traffic filter by ID.", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -96306,10 +97892,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a traffic filter", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96365,10 +97959,18 @@ "type": "boolean", "required": false, "summary": "validate all inputs and exit without performing any action" + }, + { + "role": "flag", + "name": "yes", + "type": "boolean", + "required": false, + "summary": "confirm destructive action without prompting" } ], "summary": "Updates a traffic filter", "intent": { + "destructive": true, "requiresAuth": true } } diff --git a/package.json b/package.json index cc1da7d2..7fd43fc6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "build": "node --max-old-space-size=8192 node_modules/typescript/bin/tsc -b", "postbuild": "node scripts/create-dist-package-jsons.mjs", "test": "npm run build && npm run test:unit && npm run test:license", - "test:unit": "node --max-old-space-size=8192 --import tsx/esm --test --experimental-test-coverage --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test-coverage-include='src/**/*.ts' --test-coverage-include='packages/*/src/**/*.ts' --test-coverage-exclude='src/es/api-manifest.ts' --test-coverage-exclude='src/cloud/serverless-apis.ts' --test-coverage-exclude='node_modules/**'", + "test:unit": "node --max-old-space-size=8192 --import tsx/esm --test --test-timeout=60000 --experimental-test-coverage --test-coverage-lines=90 --test-coverage-branches=90 --test-coverage-functions=90 --test-coverage-include='src/**/*.ts' --test-coverage-include='packages/*/src/**/*.ts' --test-coverage-exclude='src/es/api-manifest.ts' --test-coverage-exclude='src/cloud/serverless-apis.ts' --test-coverage-exclude='node_modules/**'", "test:lint": "eslint src packages test", "codegen:functional": "npx tsx codegen/functional/index.ts --tests-dir ../elasticsearch-clients-tests/tests", "test:functional:es": "bash test/functional/es/run.sh", diff --git a/src/cloud/register.ts b/src/cloud/register.ts index 2a19ba2b..237b1269 100644 --- a/src/cloud/register.ts +++ b/src/cloud/register.ts @@ -116,6 +116,7 @@ function buildFlatLeaf (def: CloudApiDefinition): OpaqueCommandHandle { input: schema, readOnly: def.method === 'GET', handler: createCloudHandler(def), + intent: { destructive: def.destructive }, }) } @@ -165,6 +166,7 @@ function buildServerlessTypeGroup ( input: schema, readOnly: def.method === 'GET', handler, + intent: { destructive: def.destructive }, }) if (isCreateProjectCommand(def.name)) { (cmd as Command).option('--wait', 'Wait for the project to reach "initialized" phase before returning') diff --git a/src/extension/register.ts b/src/extension/register.ts index c7205435..154d67ee 100644 --- a/src/extension/register.ts +++ b/src/extension/register.ts @@ -155,6 +155,7 @@ export function registerExtensionCommands (): OpaqueCommandHandle { description: 'Short extension name (e.g. "local" for elastic-local)', required: true, }, + intent: { destructive: true }, handler: async (parsed) => handleRemove(parsed), }) diff --git a/src/factory.ts b/src/factory.ts index 6b546724..06ad1a6d 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -5,6 +5,7 @@ import { Command } from 'commander' import { readFileSync, writeSync } from 'node:fs' +import { createInterface } from 'node:readline' import assert from 'node:assert/strict' import { getResolvedConfig } from './config/store.ts' import { extractSchemaArgs, validateSchemaArgs } from './lib/json-schema-args.ts' @@ -62,6 +63,55 @@ export function _testSetStdinReader (fn: () => string): () => void { return () => { stdinReader = prev } } +/** + * Module-level TTY detector - swappable in tests via {@link _testSetIsTTY}. + * Production default reads `process.stderr.isTTY`. + */ +let isTTYFn: () => boolean = () => process.stderr.isTTY === true + +/** + * Test-only seam: overrides the TTY detection function and returns a restore callback. + * Always call the returned function in a `finally` block to avoid test pollution. + * + * @internal not part of the public API + */ +export function _testSetIsTTY (value: boolean): () => void { + const prev = isTTYFn + isTTYFn = () => value + return () => { isTTYFn = prev } +} + +/** + * Module-level confirm reader - swappable in tests via {@link _testSetConfirmReader}. + * Production default prompts on stderr and reads a line from stdin via readline. + * `null` means use the real readline prompt. + */ +let confirmReader: (() => Promise) | null = null + +/** + * Test-only seam: replaces the confirm reader with `fn` and returns a restore callback. + * Always call the returned function in a `finally` block to avoid test pollution. + * + * @internal not part of the public API + */ +export function _testSetConfirmReader (fn: () => Promise): () => void { + const prev = confirmReader + confirmReader = fn + return () => { confirmReader = prev } +} + +/** Prompts the user on stderr and reads one line from stdin to confirm a destructive action. */ +async function promptConfirm (): Promise { + if (confirmReader != null) return confirmReader() + return new Promise(resolve => { + const rl = createInterface({ input: process.stdin, output: process.stderr }) + rl.question('This action is destructive. Continue? [y/N] ', answer => { + rl.close() + resolve(answer.trim().toLowerCase() === 'y') + }) + }) +} + /** converts a kebab-case option name to camelCase to match Commander's opts() keys */ function camelCase (s: string): string { return s.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) @@ -406,6 +456,10 @@ export function defineCommand (config: CommandConfig): OpaqueCommandHandle { cmd.option('--dry-run', 'validate all inputs and exit without performing any action') } + if (config.intent?.destructive === true || config.intent?.requiresConfirmation === true) { + cmd.option('--yes', 'confirm destructive action without prompting') + } + configureHelpWithSchema(cmd, isJsonSchemaInput(config.input) ? config.input : undefined) Object.defineProperty(cmd, '_commandConfig', { @@ -631,6 +685,31 @@ export function defineCommand (config: CommandConfig): OpaqueCommandHandle { return } + if (config.intent?.destructive === true || config.intent?.requiresConfirmation === true) { + if (allRaw['yes'] !== true) { + if (isTTYFn()) { + const confirmed = await promptConfirm() + if (!confirmed) { + const errObj = { error: { code: 'confirmation_required', message: 'Aborted.' } } + if (jsonFormat === true) { + writeErr(cmd, JSON.stringify(errObj) + '\n') + } else { + writeErr(cmd, 'Error: Aborted.\n') + } + throw Object.assign(new Error('confirmation_required'), { exitCode: 1 }) + } + } else { + const errObj = { error: { code: 'confirmation_required', message: 'Pass --yes to confirm this destructive action.' } } + if (jsonFormat === true) { + writeErr(cmd, JSON.stringify(errObj) + '\n') + } else { + writeErr(cmd, 'Error: Pass --yes to confirm this destructive action.\n') + } + throw Object.assign(new Error('confirmation_required'), { exitCode: 1 }) + } + } + } + const handlerResult = await config.handler(parsed) const { renderText, formatHandlerError } = await getOutput() diff --git a/test/cloud/register.test.ts b/test/cloud/register.test.ts index 41a485a1..068d6843 100644 --- a/test/cloud/register.test.ts +++ b/test/cloud/register.test.ts @@ -302,6 +302,60 @@ describe('registerCloudCommands', () => { assert.deepEqual(issues, []) }) }) + + describe('destructive intent', () => { + it('registers --yes for a PUT flat leaf marked destructive: true', async () => { + const defs: CloudApiDefinition[] = [ + { name: 'update-current-account', namespace: 'accounts', description: 'Update', method: 'PUT', path: '/api/v1/account', destructive: true }, + ] + const group = await registerCloudCommands(defs) + const cmd = group.commands.find((c) => c.name() === 'trust')! + .commands.find((c) => c.name() === 'update-current-account')! + assert.ok(cmd.options.map((o) => o.long).includes('--yes'), 'PUT def.destructive: true must expose --yes') + }) + + it('omits --yes for a PUT flat leaf marked destructive: false', async () => { + const defs: CloudApiDefinition[] = [ + { name: 'get-current-account', namespace: 'accounts', description: 'Idempotent replace', method: 'PUT', path: '/api/v1/account', destructive: false }, + ] + const group = await registerCloudCommands(defs) + const cmd = group.commands.find((c) => c.name() === 'trust')! + .commands.find((c) => c.name() === 'get-current-account')! + assert.ok(!cmd.options.map((o) => o.long).includes('--yes'), 'PUT def.destructive: false must not expose --yes') + }) + + it('registers --yes for a PATCH flat leaf marked destructive: true', async () => { + const defs: CloudApiDefinition[] = [ + { name: 'patch-current-account', namespace: 'accounts', description: 'Patch', method: 'PATCH', path: '/api/v1/account', destructive: true }, + ] + const group = await registerCloudCommands(defs) + const cmd = group.commands.find((c) => c.name() === 'trust')! + .commands.find((c) => c.name() === 'patch-current-account')! + assert.ok(cmd.options.map((o) => o.long).includes('--yes'), 'PATCH def.destructive: true must expose --yes') + }) + + it('registers --yes for a destructive PUT command inside a serverless project type group', async () => { + const defs: CloudApiDefinition[] = [ + { name: 'reset-elasticsearch-project-credentials', namespace: 'elasticsearch-projects', description: 'Reset', method: 'PUT', path: '/api/v1/serverless/projects/elasticsearch/{id}/_reset-credentials', destructive: true, input: { type: 'object', properties: { id: { type: 'string', description: 'ID', 'x-found-in': 'path' } }, required: ['id'] } }, + ] + const group = await registerCloudCommands(defs) + const cmd = group.commands.find((c) => c.name() === 'serverless')! + .commands.find((c) => c.name() === 'projects')! + .commands.find((c) => c.name() === 'search')! + .commands.find((c) => c.name() === 'reset-credentials')! + assert.ok(cmd.options.map((o) => o.long).includes('--yes'), 'serverless PUT def.destructive: true must expose --yes') + }) + + it('omits --yes for a non-destructive GET command', async () => { + const defs: CloudApiDefinition[] = [ + { name: 'get-current-account', namespace: 'accounts', description: 'Get', method: 'GET', path: '/api/v1/account', destructive: false }, + ] + const group = await registerCloudCommands(defs) + const cmd = group.commands.find((c) => c.name() === 'trust')! + .commands.find((c) => c.name() === 'get-current-account')! + assert.ok(!cmd.options.map((o) => o.long).includes('--yes'), 'GET def.destructive: false must not expose --yes') + }) + }) }) describe('simplifyProjectCommandName', () => { diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts new file mode 100644 index 00000000..ffaf4f38 --- /dev/null +++ b/test/confirmation.test.ts @@ -0,0 +1,274 @@ +/* + * Copyright Elasticsearch B.V. and contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, beforeEach, afterEach } from 'node:test' +import assert from 'node:assert/strict' +import { defineCommand, _testSetConfirmReader, _testSetIsTTY } from '../src/factory.ts' +import type { OpaqueCommandHandle } from '../src/factory.ts' + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +async function invokeAsync (handle: OpaqueCommandHandle, globalFlags: string[], argv: string[]): Promise { + const { Command } = await import('commander') + const program = new Command('elastic') + program.exitOverride() + program.option('--json', 'Output in JSON format') + program.addCommand(handle) + handle.exitOverride() + await program.parseAsync([...globalFlags, handle.name(), ...argv], { from: 'user' }) +} + +async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string[], argv: string[]): Promise { + let err = '' + handle.exitOverride() + handle.configureOutput({ writeErr: (s) => { err += s } }) + const { Command } = await import('commander') + const program = new Command('elastic') + program.exitOverride() + program.option('--json', 'Output in JSON format') + program.addCommand(handle) + try { + await program.parseAsync([...globalFlags, handle.name(), ...argv], { from: 'user' }) + } catch { /* exitOverride / confirmation throw */ } + return err +} + +// --------------------------------------------------------------------------- +// All confirmation guard tests run serially inside one outer describe. +// node:test runs top-level describes concurrently by default. That concurrent +// execution creates async gaps (from `await import('commander')` inside the +// helpers) during which sibling describe hooks can mutate shared state such as +// the `isTTYFn` seam. Wrapping everything in `{ concurrency: false }` here +// serialises all execution and eliminates those races. +// +// Note: TTY state is always overridden via the `_testSetIsTTY` seam, never by +// `Object.defineProperty(process.stderr, 'isTTY', ...)`. Redefining that +// property directly replaces the stream's inherited getter with an own data +// property; "restoring" it afterwards with another `Object.defineProperty` +// call does not return the stream to its original state, and reliably hangs +// later TTY-path tests in the same file on Windows CI. +// --------------------------------------------------------------------------- +describe('confirmation guard', { concurrency: false }, () => { + // ------------------------------------------------------------------------- + // Non-destructive commands + // ------------------------------------------------------------------------- + + describe('non-destructive commands', () => { + it('proceeds without --yes and without confirmation prompt', async () => { + const restore = _testSetIsTTY(false) + try { + let handlerCalled = false + const cmd = defineCommand({ + name: 'ping', + description: 'Ping the cluster', + handler: () => { handlerCalled = true; return {} }, + }) + await invokeAsync(cmd, [], []) + assert.equal(handlerCalled, true) + } finally { restore() } + }) + + it('does not register --yes flag', () => { + const cmd = defineCommand({ + name: 'ping', + description: 'Ping the cluster', + handler: () => ({}), + }) + assert.ok(!cmd.helpInformation().includes('--yes'), 'non-destructive command must not expose --yes') + }) + }) + + // ------------------------------------------------------------------------- + // Destructive commands: non-TTY (fail closed) + // ------------------------------------------------------------------------- + + describe('non-TTY fail-closed', () => { + let restore: () => void + + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) + + it('returns confirmation_required error without --yes', async () => { + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => ({}), + }) + const err = await captureErrAsync(cmd, [], []) + assert.match(err, /Pass --yes to confirm this destructive action/) + }) + + it('does not invoke handler when confirmation is missing', async () => { + let handlerCalled = false + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; return {} }, + }) + await captureErrAsync(cmd, [], []) + assert.equal(handlerCalled, false) + }) + + it('emits structured JSON error with --json flag', async () => { + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => ({}), + }) + const err = await captureErrAsync(cmd, ['--json'], []) + const parsed = JSON.parse(err) as { error: { code: string; message: string } } + assert.equal(parsed.error.code, 'confirmation_required') + assert.match(parsed.error.message, /--yes/) + }) + + it('requiresConfirmation intent also triggers the guard', async () => { + const cmd = defineCommand({ + name: 'reset', + description: 'Reset the resource', + intent: { requiresConfirmation: true }, + handler: () => ({}), + }) + const err = await captureErrAsync(cmd, [], []) + assert.match(err, /Pass --yes to confirm this destructive action/) + }) + }) + + // ------------------------------------------------------------------------- + // Destructive commands: --yes bypasses confirmation + // ------------------------------------------------------------------------- + + describe('--yes flag', () => { + let restore: () => void + + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) + + it('proceeds when --yes is passed', async () => { + let handlerCalled = false + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; return {} }, + }) + await invokeAsync(cmd, [], ['--yes']) + assert.equal(handlerCalled, true) + }) + + it('destructive commands register --yes flag in help', () => { + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => ({}), + }) + assert.ok(cmd.helpInformation().includes('--yes'), 'destructive command must expose --yes in help') + }) + }) + + // ------------------------------------------------------------------------- + // Destructive commands: TTY interactive prompt + // + // Both TTY paths (proceed and abort) run inside one `it` block so the two + // async phases can share state without racing against sibling suites. + // ------------------------------------------------------------------------- + + describe('TTY prompt', () => { + // node:test v22 / tsx bug: a suite with exactly one async test that yields + // early (via `await import(...)`) may be closed prematurely by the runner, + // causing the test to not appear in the TAP count. A second (instant) test + // keeps the suite registration alive while the real test is running. + it('confirm-reader seam restores state', () => { + const r1 = _testSetIsTTY(false) + const r2 = _testSetConfirmReader(async () => true) + r2(); r1() + }) + + it('proceeds on yes, aborts on no', async () => { + const diag = (msg: string): void => { process.stderr.write(`DIAG ${Date.now()} ${msg}\n`) } + diag('start') + // Case 1: reader returns true → confirmation guard passes, handler runs. + // The handler throws after setting the flag so the factory never reaches + // the process.stdout.write(renderText(...)) call and does not corrupt the + // TAP stream (node:test flushes suite TAP output asynchronously; any + // direct process.stdout.write from within a test can overwrite it). + let r1 = _testSetIsTTY(true) + let r2 = _testSetConfirmReader(async () => true) + diag('seams set for case 1') + let handlerCalled = false + const cmdYes = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; throw new Error('stop_here') }, + }) + diag('cmdYes defined') + try { + await captureErrAsync(cmdYes, [], []) + diag('case 1 captureErrAsync resolved') + } finally { r2(); r1() } + diag('case 1 seams restored') + assert.equal(handlerCalled, true, 'handler must be called when TTY reader returns true') + diag('case 1 assert passed') + + // Case 2: reader returns false → aborts before handler runs + handlerCalled = false + r1 = _testSetIsTTY(true) + r2 = _testSetConfirmReader(async () => false) + diag('seams set for case 2') + const cmdNo = defineCommand({ + name: 'delete2', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; return {} }, + }) + diag('cmdNo defined') + try { + const err = await captureErrAsync(cmdNo, [], []) + diag('case 2 captureErrAsync resolved') + assert.equal(handlerCalled, false) + assert.match(err, /Aborted/) + } finally { r2(); r1() } + diag('done') + }) + }) + + // ------------------------------------------------------------------------- + // --dry-run exits before the confirmation guard + // ------------------------------------------------------------------------- + + describe('--dry-run skip', () => { + let restore: () => void + + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) + + it('--dry-run skips confirmation and exits early', async () => { + let handlerCalled = false + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + input: { type: 'object', properties: {} }, + handler: () => { handlerCalled = true; return {} }, + }) + let stdout = '' + const origWrite = process.stdout.write.bind(process.stdout) + process.stdout.write = (s: string | Uint8Array) => { stdout += s; return true } + try { + await invokeAsync(cmd, [], ['--dry-run']) + } finally { + process.stdout.write = origWrite + } + assert.equal(handlerCalled, false) + assert.match(stdout, /dry run/) + }) + }) +}) diff --git a/test/factory.test.ts b/test/factory.test.ts index 7fc0b142..84057d5c 100644 --- a/test/factory.test.ts +++ b/test/factory.test.ts @@ -2927,7 +2927,7 @@ describe('no Commander API leaks', () => { it('factory module exports only public API and test seam at runtime', async () => { const factory = await import('../src/factory.ts') const exported = Object.keys(factory) - assert.deepEqual(exported.sort(), ['RawJsonValue', '_testSetStdinReader', 'commandPath', 'configureErrorOutput', 'configureJsonHelp', 'defineCommand', 'defineGroup', 'hideBlockedCommands', 'isCommandAllowed', 'isHidden', 'setHidden', 'stripTransportMeta', 'validateName']) + assert.deepEqual(exported.sort(), ['RawJsonValue', '_testSetConfirmReader', '_testSetIsTTY', '_testSetStdinReader', 'commandPath', 'configureErrorOutput', 'configureJsonHelp', 'defineCommand', 'defineGroup', 'hideBlockedCommands', 'isCommandAllowed', 'isHidden', 'setHidden', 'stripTransportMeta', 'validateName']) }) it('defineCommand return value requires no Commander import to use', () => { diff --git a/test/functional/kb/alerting.sh b/test/functional/kb/alerting.sh index 5a114be2..27dd6807 100755 --- a/test/functional/kb/alerting.sh +++ b/test/functional/kb/alerting.sh @@ -18,7 +18,7 @@ RULE_ID="cli-ft-rule" RULE_PARAMS='{"searchType":"esqlQuery","esqlQuery":{"esql":"FROM * | LIMIT 1"},"timeWindowSize":5,"timeWindowUnit":"m","threshold":[0],"thresholdComparator":">","size":0,"timeField":"@timestamp"}' teardown() { - $CLI stack kb alerting delete-alerting-rule-id --id "$RULE_ID" --json >/dev/null 2>&1 || true + $CLI stack kb alerting delete-alerting-rule-id --id "$RULE_ID" --yes --json >/dev/null 2>&1 || true } trap teardown EXIT @@ -56,7 +56,7 @@ count=$(echo "$output" | jq '[.data[] | select(.id == "'"$RULE_ID"'")] | length' # ── delete ──────────────────────────────────────────────────────────── -$CLI stack kb alerting delete-alerting-rule-id --id "$RULE_ID" --json >/dev/null 2>/dev/null +$CLI stack kb alerting delete-alerting-rule-id --id "$RULE_ID" --yes --json >/dev/null 2>/dev/null output=$($CLI stack kb alerting get-alerting-rules-find --json 2>/dev/null) count=$(echo "$output" | jq '[.data[] | select(.id == "'"$RULE_ID"'")] | length') diff --git a/test/functional/kb/connectors.sh b/test/functional/kb/connectors.sh index 70324650..2c74b09d 100755 --- a/test/functional/kb/connectors.sh +++ b/test/functional/kb/connectors.sh @@ -16,7 +16,7 @@ CONNECTOR_ID="" teardown() { if [ -n "$CONNECTOR_ID" ]; then - $CLI stack kb connectors delete-actions-connector-id --id "$CONNECTOR_ID" --json >/dev/null 2>&1 || true + $CLI stack kb connectors delete-actions-connector-id --id "$CONNECTOR_ID" --yes --json >/dev/null 2>&1 || true fi } trap teardown EXIT @@ -69,7 +69,7 @@ count=$(echo "$output" | jq '[.[] | select(.id == "'"$CONNECTOR_ID"'")] | length # ── delete ──────────────────────────────────────────────────────────── -$CLI stack kb connectors delete-actions-connector-id --id "$CONNECTOR_ID" --json >/dev/null 2>/dev/null +$CLI stack kb connectors delete-actions-connector-id --id "$CONNECTOR_ID" --yes --json >/dev/null 2>/dev/null CONNECTOR_ID="" output=$($CLI stack kb connectors get-actions-connectors --json 2>/dev/null) diff --git a/test/functional/kb/data_views.sh b/test/functional/kb/data_views.sh index 7950c8b9..925c1e20 100755 --- a/test/functional/kb/data_views.sh +++ b/test/functional/kb/data_views.sh @@ -16,7 +16,7 @@ VIEW_ID="" teardown() { if [ -n "$VIEW_ID" ]; then - $CLI stack kb data-views delete-data-view-default --view-id "$VIEW_ID" --json >/dev/null 2>&1 || true + $CLI stack kb data-views delete-data-view-default --view-id "$VIEW_ID" --yes --json >/dev/null 2>&1 || true fi } trap teardown EXIT @@ -48,7 +48,7 @@ count=$(echo "$output" | jq '[.data_view[] | select(.id == "'"$VIEW_ID"'")] | le # ── delete ──────────────────────────────────────────────────────────── -$CLI stack kb data-views delete-data-view-default --view-id "$VIEW_ID" --json >/dev/null 2>/dev/null +$CLI stack kb data-views delete-data-view-default --view-id "$VIEW_ID" --yes --json >/dev/null 2>/dev/null VIEW_ID="" output=$($CLI stack kb data-views get-all-data-views-default --json 2>/dev/null) diff --git a/test/functional/kb/spaces.sh b/test/functional/kb/spaces.sh index d676d24f..423170dc 100755 --- a/test/functional/kb/spaces.sh +++ b/test/functional/kb/spaces.sh @@ -15,7 +15,7 @@ CLI="node $REPO_ROOT/dist/cli.js" SPACE_ID="cli-ft-space" teardown() { - $CLI stack kb spaces delete-spaces-space-id --id "$SPACE_ID" --json >/dev/null 2>&1 || true + $CLI stack kb spaces delete-spaces-space-id --id "$SPACE_ID" --yes --json >/dev/null 2>&1 || true } trap teardown EXIT @@ -51,7 +51,7 @@ default_count=$(echo "$output" | jq '[.[] | select(.id == "default")] | length') # ── delete ──────────────────────────────────────────────────────────── -$CLI stack kb spaces delete-spaces-space-id --id "$SPACE_ID" --json >/dev/null 2>/dev/null +$CLI stack kb spaces delete-spaces-space-id --id "$SPACE_ID" --yes --json >/dev/null 2>/dev/null output=$($CLI stack kb spaces get-spaces-space --include-authorized-purposes false --json 2>/dev/null) count=$(echo "$output" | jq '[.[] | select(.id == "'"$SPACE_ID"'")] | length')