From 6bfd24d947be3502d1e6e3704465416bbc6b6cc6 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Tue, 11 Aug 2026 13:08:02 -0400 Subject: [PATCH 01/23] fix: infer destructive intent for cloud DELETE and known destructive endpoints --- src/cloud/register.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cloud/register.ts b/src/cloud/register.ts index 2a19ba2b..eb04859e 100644 --- a/src/cloud/register.ts +++ b/src/cloud/register.ts @@ -18,6 +18,7 @@ import { readCredentialPolicyOptions, } from './credentials.ts' import type { JsonValue, ParsedResult } from '../factory.ts' +import { inferIntentFromHttp } from '@cli-schema/spec' /** * Maps project-type namespaces from codegen to short CLI group names. @@ -110,12 +111,14 @@ function checkDuplicates (defs: CloudApiDefinition[], namespace: string): void { function buildFlatLeaf (def: CloudApiDefinition): OpaqueCommandHandle { const schema = buildCloudJsonSchema(def) + const intent = inferIntentFromHttp(def.method) return defineCommand({ name: def.name, description: def.description, input: schema, readOnly: def.method === 'GET', handler: createCloudHandler(def), + ...(intent != null ? { intent } : {}), }) } @@ -159,12 +162,14 @@ function buildServerlessTypeGroup ( const handler: (parsed: ParsedResult) => Promise = isCredentialCommand(def.name) ? async (parsed) => wrapWithCredentialPolicy(def.name, baseHandler, parsed) : baseHandler + const defIntent = inferIntentFromHttp(def.method) const cmd = defineCommand({ name: shortName, description: def.description, input: schema, readOnly: def.method === 'GET', handler, + ...(defIntent != null ? { intent: defIntent } : {}), }) if (isCreateProjectCommand(def.name)) { (cmd as Command).option('--wait', 'Wait for the project to reach "initialized" phase before returning') From e3951f826bfca2c9190d78868a908917750f4764 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Tue, 11 Aug 2026 13:08:21 -0400 Subject: [PATCH 02/23] fix: mark extension remove as destructive --- src/extension/register.ts | 1 + 1 file changed, 1 insertion(+) 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), }) From 5f1480c5852d8bdcc979d5291abe9d4040755e25 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Tue, 11 Aug 2026 14:26:50 -0400 Subject: [PATCH 03/23] feat: add --yes confirmation guard for destructive commands --- src/factory.ts | 79 +++++++++++ test/confirmation.test.ts | 271 ++++++++++++++++++++++++++++++++++++++ test/factory.test.ts | 2 +- 3 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 test/confirmation.test.ts diff --git a/src/factory.ts b/src/factory.ts index 6b546724..2b839ce9 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/confirmation.test.ts b/test/confirmation.test.ts new file mode 100644 index 00000000..7c0eb9bf --- /dev/null +++ b/test/confirmation.test.ts @@ -0,0 +1,271 @@ +/* + * 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 +// `process.stderr.isTTY`. Wrapping everything in `{ concurrency: false }` here +// serialises all execution and eliminates those races. +// --------------------------------------------------------------------------- +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 origIsTTY: boolean | undefined + + beforeEach(() => { + origIsTTY = process.stderr.isTTY + Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) + }) + afterEach(() => { + Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) + }) + + 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 origIsTTY: boolean | undefined + + beforeEach(() => { + origIsTTY = process.stderr.isTTY + Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) + }) + afterEach(() => { + Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) + }) + + 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 () => { + // 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) + let handlerCalled = false + const cmdYes = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; throw new Error('stop_here') }, + }) + try { + await captureErrAsync(cmdYes, [], []) + } finally { r2(); r1() } + assert.equal(handlerCalled, true, 'handler must be called when TTY reader returns true') + + // Case 2: reader returns false → aborts before handler runs + handlerCalled = false + r1 = _testSetIsTTY(true) + r2 = _testSetConfirmReader(async () => false) + const cmdNo = defineCommand({ + name: 'delete2', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; return {} }, + }) + try { + const err = await captureErrAsync(cmdNo, [], []) + assert.equal(handlerCalled, false) + assert.match(err, /Aborted/) + } finally { r2(); r1() } + }) + }) + + // ------------------------------------------------------------------------- + // --dry-run exits before the confirmation guard + // ------------------------------------------------------------------------- + + describe('--dry-run skip', () => { + let origIsTTY: boolean | undefined + + beforeEach(() => { + origIsTTY = process.stderr.isTTY + Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) + }) + afterEach(() => { + Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) + }) + + 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', () => { From c794af5730243cd269fe649a01a9a2b2bdf37308 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Wed, 12 Aug 2026 18:34:44 -0400 Subject: [PATCH 04/23] chore: regenerate cli schema with --yes flag --- docs/cli/schema.json | 1316 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1316 insertions(+) diff --git a/docs/cli/schema.json b/docs/cli/schema.json index 998d4929..2232061f 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.", @@ -18156,6 +18254,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 +20189,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 +22072,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 +22169,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 +22273,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 +22370,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 +22474,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 +22563,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 +22651,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 +27017,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 +28760,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 +28827,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 +32839,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 +32928,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 +33016,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 +33852,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 +34377,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 +34939,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 +35020,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 +35102,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 +35190,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 +35271,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 +35359,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 +35433,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 +35528,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 +35623,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 +35704,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 +35792,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 +35873,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 +41646,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 +42539,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 +42814,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 +42888,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 +43437,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 +44087,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 +44161,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 +45381,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 +46525,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 +46611,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 +46697,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 +46797,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 +46883,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 +48689,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 +48784,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 +50997,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 +52150,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 +52239,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 +53262,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 +54089,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 +54177,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 +55572,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 +57011,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 +58305,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 +58602,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 +58821,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 +59430,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 +59670,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 +59976,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 +60126,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 +60823,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 +61103,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 +61192,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 +61826,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 +62222,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 +62441,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 +62723,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 +63983,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 +65631,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 +66074,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 +66884,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 +67130,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 +67449,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 +67546,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 +68227,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 +68402,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 +68775,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 +69438,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 +70274,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 +70821,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 +71092,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 +71528,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 +72142,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 +72780,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 +72995,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 +73441,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 +74378,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 +74635,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 +75931,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 +77856,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 +78112,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 +78388,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 +78620,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 +79226,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 +79309,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 +79598,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 +79839,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 +80214,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 +80896,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 +81466,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 +82028,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 +82382,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 +82498,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 +83526,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 +83886,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 +84239,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 +84501,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 +85599,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 +86164,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 +86385,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 +86919,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 +87234,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 +87491,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 +87792,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 +87852,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 +88782,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 +89289,9 @@ "parameters": [], "summary": "Fetch current account information", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88419,6 +89325,9 @@ ], "summary": "Updates the current account", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88493,6 +89402,9 @@ ], "summary": "Get all API keys", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88568,10 +89480,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88606,6 +89528,9 @@ ], "summary": "Get API key", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88636,10 +89561,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -88695,6 +89630,9 @@ ], "summary": "Get costs overview for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88750,6 +89688,9 @@ ], "summary": "Get charts for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88798,6 +89739,9 @@ ], "summary": "Get deployments costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88860,6 +89804,9 @@ ], "summary": "Get charts by deployment. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88915,6 +89862,9 @@ ], "summary": "Get itemized costs by deployments. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -88963,6 +89913,9 @@ ], "summary": "Get itemized costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -88982,6 +89935,9 @@ "parameters": [], "summary": "List organizations", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89016,6 +89972,9 @@ ], "summary": "Get organization invitation", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89084,6 +90043,9 @@ ], "summary": "Fetch organization information", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89151,6 +90113,9 @@ ], "summary": "Update organization", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89185,6 +90150,9 @@ ], "summary": "Get domain claims", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89221,10 +90189,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89339,6 +90317,9 @@ ], "summary": "Get organization IdP", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89391,6 +90372,9 @@ ], "summary": "Setup organization IdP", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89421,10 +90405,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89459,6 +90453,9 @@ ], "summary": "Get organization service provider SAML2 metadata.xml for configuring the identity provider", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89493,6 +90490,9 @@ ], "summary": "List organization invitations", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89583,10 +90583,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89621,6 +90631,9 @@ ], "summary": "List organization members", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89665,10 +90678,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89703,6 +90726,9 @@ ], "summary": "Get role mappings", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89743,6 +90769,9 @@ ], "summary": "Updates role mappings", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -89773,10 +90802,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -89896,10 +90935,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -89981,6 +91030,9 @@ ], "summary": "Get deployment templates", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90044,6 +91096,9 @@ ], "summary": "Get deployment template", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -90064,6 +91119,9 @@ "parameters": [], "summary": "List Deployments", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90418,6 +91476,9 @@ ], "summary": "Get Deployment", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90517,6 +91578,9 @@ ], "summary": "Update Deployment", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90699,6 +91763,9 @@ ], "summary": "Get Deployment APM Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90832,6 +91899,9 @@ ], "summary": "Get Deployment App Search Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90874,6 +91944,9 @@ ], "summary": "Set AppSearch read-only status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90922,6 +91995,9 @@ ], "summary": "Set AppSearch read-only status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -90957,6 +92033,9 @@ ], "summary": "Get certificate authority", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91076,6 +92155,9 @@ ], "summary": "Get Deployment Elasticsearch Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91525,6 +92607,9 @@ ], "summary": "Get the items in the Elasticsearch resource keystore", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91622,6 +92707,9 @@ ], "summary": "Get certificate based remote clusters", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91670,6 +92758,9 @@ ], "summary": "Set certificate based remote clusters", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91712,6 +92803,9 @@ ], "summary": "List the attached snapshot repositories", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91805,10 +92899,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -91851,6 +92955,9 @@ ], "summary": "Get Elasticsearch tiers", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92026,6 +93133,9 @@ ], "summary": "Get Deployment Enterprise Search Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92117,6 +93227,9 @@ ], "summary": "Get Deployment Integrations Server Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92215,6 +93328,9 @@ ], "summary": "Get Deployment Kibana Resource Info", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92264,6 +93380,9 @@ ], "summary": "Build request to migrate deployment to a different template", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92299,6 +93418,9 @@ ], "summary": "Get the tags for a Deployment", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92340,6 +93462,9 @@ ], "summary": "Set the tags for a Deployment", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92423,6 +93548,9 @@ ], "summary": "Get Deployment upgrade assistant status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -92986,10 +94114,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93039,6 +94177,9 @@ ], "summary": "Get the user settings of a Deployment resource", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93094,6 +94235,9 @@ ], "summary": "Update user settings for a deployment resource", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93262,6 +94406,9 @@ ], "summary": "Get associated rulesets", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93304,6 +94451,9 @@ ], "summary": "List traffic filter claimed link id", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93457,6 +94607,9 @@ ], "summary": "List traffic filter rulesets", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93563,6 +94716,9 @@ ], "summary": "Retrieves the ruleset by ID.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93634,6 +94790,9 @@ ], "summary": "Updates a ruleset", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93672,10 +94831,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93711,6 +94880,9 @@ ], "summary": "Get associated deployments", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93803,10 +94975,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -93827,6 +95009,9 @@ "parameters": [], "summary": "List Extensions", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -93931,6 +95116,9 @@ ], "summary": "Get Extension", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94035,6 +95223,9 @@ ], "summary": "Uploads the Extension", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94066,10 +95257,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -94119,6 +95320,9 @@ ], "summary": "Get stack versions", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -94139,6 +95343,9 @@ "parameters": [], "summary": "Get trusted environments", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -94207,6 +95414,9 @@ ], "summary": "Get Elasticsearch projects", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94366,6 +95576,9 @@ ], "summary": "Get an Elasticsearch project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94398,10 +95611,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94618,6 +95841,9 @@ ], "summary": "Get roles for an Elasticsearch project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94654,6 +95880,9 @@ ], "summary": "Get the status of an Elasticsearch project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -94711,6 +95940,9 @@ ], "summary": "Get Observability projects", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94864,6 +96096,9 @@ ], "summary": "Get an Observability project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -94896,10 +96131,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95120,6 +96365,9 @@ ], "summary": "Get roles for an Observability project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95156,6 +96404,9 @@ ], "summary": "Get the status of an Observability project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -95213,6 +96464,9 @@ ], "summary": "Get Security projects", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95378,6 +96632,9 @@ ], "summary": "Get a Security project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95410,10 +96667,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95643,6 +96910,9 @@ ], "summary": "Get roles for a Security project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95679,6 +96949,9 @@ ], "summary": "Get the status of a Security project", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -95766,6 +97039,9 @@ ], "summary": "Get Elasticsearch project link candidates", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95843,6 +97119,9 @@ ], "summary": "Get Observability project link candidates", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95920,6 +97199,9 @@ ], "summary": "Get Security project link candidates", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95955,6 +97237,9 @@ ], "summary": "Get Elasticsearch project delete status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -95990,6 +97275,9 @@ ], "summary": "Get Observability project delete status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -96025,6 +97313,9 @@ ], "summary": "Get Security project delete status", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -96045,6 +97336,9 @@ "parameters": [], "summary": "Get regions", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -96080,6 +97374,9 @@ ], "summary": "Get a region", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } } @@ -96129,6 +97426,9 @@ ], "summary": "List traffic filters", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -96240,6 +97540,9 @@ ], "summary": "List PrivateLink region metadata", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -96275,6 +97578,9 @@ ], "summary": "Retrieves the traffic filter by ID.", "intent": { + "destructive": false, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, @@ -96306,10 +97612,20 @@ "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, + "idempotent": true, + "scope": "global", "requiresAuth": true } }, From cdc3e1009f84e33e03234447cdd0e8c511cdd94b Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:30:46 -0400 Subject: [PATCH 05/23] fix: use schema destructive flag for cloud commands --- src/cloud/register.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/cloud/register.ts b/src/cloud/register.ts index eb04859e..237b1269 100644 --- a/src/cloud/register.ts +++ b/src/cloud/register.ts @@ -18,7 +18,6 @@ import { readCredentialPolicyOptions, } from './credentials.ts' import type { JsonValue, ParsedResult } from '../factory.ts' -import { inferIntentFromHttp } from '@cli-schema/spec' /** * Maps project-type namespaces from codegen to short CLI group names. @@ -111,14 +110,13 @@ function checkDuplicates (defs: CloudApiDefinition[], namespace: string): void { function buildFlatLeaf (def: CloudApiDefinition): OpaqueCommandHandle { const schema = buildCloudJsonSchema(def) - const intent = inferIntentFromHttp(def.method) return defineCommand({ name: def.name, description: def.description, input: schema, readOnly: def.method === 'GET', handler: createCloudHandler(def), - ...(intent != null ? { intent } : {}), + intent: { destructive: def.destructive }, }) } @@ -162,14 +160,13 @@ function buildServerlessTypeGroup ( const handler: (parsed: ParsedResult) => Promise = isCredentialCommand(def.name) ? async (parsed) => wrapWithCredentialPolicy(def.name, baseHandler, parsed) : baseHandler - const defIntent = inferIntentFromHttp(def.method) const cmd = defineCommand({ name: shortName, description: def.description, input: schema, readOnly: def.method === 'GET', handler, - ...(defIntent != null ? { intent: defIntent } : {}), + intent: { destructive: def.destructive }, }) if (isCreateProjectCommand(def.name)) { (cmd as Command).option('--wait', 'Wait for the project to reach "initialized" phase before returning') From f113cbd7af74db27adf56ff561844bda9309cfc5 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:30:47 -0400 Subject: [PATCH 06/23] fix: lowercase yes flag help text --- src/factory.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/factory.ts b/src/factory.ts index 2b839ce9..06ad1a6d 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -457,7 +457,7 @@ export function defineCommand (config: CommandConfig): OpaqueCommandHandle { } if (config.intent?.destructive === true || config.intent?.requiresConfirmation === true) { - cmd.option('--yes', 'Confirm destructive action without prompting') + cmd.option('--yes', 'confirm destructive action without prompting') } configureHelpWithSchema(cmd, isJsonSchemaInput(config.input) ? config.input : undefined) From de1821efcdec2f02fecd18584955a46e312a8c23 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:30:52 -0400 Subject: [PATCH 07/23] test: add cloud destructive intent guard tests --- test/cloud/register.test.ts | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) 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', () => { From 952aad0225ac03973f897991624beb891968ad90 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:30:53 -0400 Subject: [PATCH 08/23] chore: regenerate cli schema --- docs/cli/schema.json | 992 ++++++++++++++++++++++++++++--------------- 1 file changed, 639 insertions(+), 353 deletions(-) diff --git a/docs/cli/schema.json b/docs/cli/schema.json index 2232061f..3064b25a 100644 --- a/docs/cli/schema.json +++ b/docs/cli/schema.json @@ -539,7 +539,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a document.", @@ -2532,7 +2532,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Clear a scrolling search.", @@ -2605,7 +2605,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Close a point in time.", @@ -5437,7 +5437,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a script or search template.", @@ -6550,7 +6550,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async search.", @@ -11364,7 +11364,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete auto-follow patterns.", @@ -12737,7 +12737,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete component templates.", @@ -12818,7 +12818,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Clear cluster voting config exclusions.", @@ -14208,7 +14208,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector.", @@ -14879,7 +14879,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector sync job.", @@ -16479,7 +16479,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a dangling index.", @@ -16797,7 +16797,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an enrich policy.", @@ -17196,7 +17196,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async EQL search.", @@ -17845,7 +17845,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async ES|QL query.", @@ -18104,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.", @@ -18186,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.", @@ -18260,7 +18274,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an ES|QL view.", @@ -20195,7 +20209,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a lifecycle policy.", @@ -22078,7 +22092,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete indices.", @@ -22175,7 +22189,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an alias.", @@ -22279,7 +22293,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data stream lifecycles.", @@ -22376,7 +22390,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data streams.", @@ -22480,7 +22494,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete data stream options.", @@ -22569,7 +22583,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an index template.", @@ -22657,7 +22671,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a legacy index template.", @@ -27023,7 +27037,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Remove an index block.", @@ -28766,7 +28780,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an inference endpoint.", @@ -28833,7 +28847,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the inference region policy.", @@ -32845,7 +32859,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete GeoIP database configurations.", @@ -32934,7 +32948,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete IP geolocation database configurations.", @@ -33022,7 +33036,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete pipelines.", @@ -33858,7 +33872,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the license.", @@ -34383,7 +34397,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Logstash pipeline.", @@ -34945,7 +34959,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a calendar.", @@ -35026,7 +35040,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete events from a calendar.", @@ -35108,7 +35122,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete anomaly jobs from a calendar.", @@ -35196,7 +35210,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a data frame analytics job.", @@ -35277,7 +35291,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a datafeed.", @@ -35365,7 +35379,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete expired ML data.", @@ -35439,7 +35453,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a filter.", @@ -35534,7 +35548,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete forecasts from a job.", @@ -35629,7 +35643,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an anomaly detection job.", @@ -35710,7 +35724,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a model snapshot.", @@ -35798,7 +35812,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an unreferenced trained model.", @@ -35879,7 +35893,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a trained model alias.", @@ -41652,7 +41666,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Clear the archived repositories metering.", @@ -42545,7 +42559,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a project routing expression.", @@ -42820,7 +42834,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a query rule.", @@ -42894,7 +42908,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a query ruleset.", @@ -43443,7 +43457,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a rollup job.", @@ -44093,7 +44107,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a search application.", @@ -44167,7 +44181,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a behavioral analytics collection.", @@ -45387,7 +45401,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Bulk delete roles.", @@ -46531,7 +46545,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete application privileges.", @@ -46617,7 +46631,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete roles.", @@ -46703,7 +46717,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete role mappings.", @@ -46803,7 +46817,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete service account tokens.", @@ -46889,7 +46903,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete users.", @@ -48695,7 +48709,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Invalidate API keys.", @@ -48790,7 +48804,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Invalidate a token.", @@ -51003,7 +51017,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a policy.", @@ -52156,7 +52170,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete snapshots.", @@ -52245,7 +52259,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete snapshot repositories.", @@ -53268,7 +53282,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an async SQL search.", @@ -54095,7 +54109,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a synonym set.", @@ -54183,7 +54197,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a synonym rule.", @@ -55578,7 +55592,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a transform.", @@ -57017,7 +57031,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a watch.", @@ -58311,7 +58325,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent", @@ -58608,7 +58622,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversation by ID", @@ -58827,7 +58841,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversation attachment", @@ -59436,7 +59450,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a plugin", @@ -59676,7 +59690,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a skill", @@ -59982,7 +59996,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a tool", @@ -60132,7 +60146,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a rule", @@ -60829,7 +60843,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a snooze schedule for a rule", @@ -61109,7 +61123,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a backfill by ID", @@ -61198,7 +61212,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete agent configuration", @@ -61832,7 +61846,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a connector", @@ -62228,7 +62242,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a dashboard", @@ -62447,7 +62461,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a data view", @@ -62729,7 +62743,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a runtime field", @@ -63989,7 +64003,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent binary download source", @@ -65637,7 +65651,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agent", @@ -66080,7 +66094,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an uploaded file", @@ -66890,7 +66904,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package", @@ -67136,7 +67150,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package", @@ -67455,7 +67469,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete assets for a package", @@ -67552,7 +67566,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Kibana assets for a package", @@ -68233,7 +68247,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an agentless policy", @@ -68408,7 +68422,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete cloud connector (supports force deletion)", @@ -68781,7 +68795,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Revoke or delete an enrollment API key", @@ -69444,7 +69458,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete output", @@ -70280,7 +70294,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a package policy", @@ -70827,7 +70841,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a proxy", @@ -71098,7 +71112,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Fleet Server host", @@ -71534,7 +71548,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a maintenance window.", @@ -72148,7 +72162,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a role", @@ -72786,7 +72800,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete conversations", @@ -73001,7 +73015,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a conversation", @@ -73447,7 +73461,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Deletes a single Knowledge Base Entry using the `id` field", @@ -74384,7 +74398,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Attack Discovery schedule", @@ -74641,7 +74655,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a detection rule", @@ -75937,7 +75951,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Elastic Endpoint exception list item", @@ -77862,7 +77876,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a script", @@ -78118,7 +78132,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an asset criticality record", @@ -78394,7 +78408,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the Privilege Monitoring Engine", @@ -78626,7 +78640,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a monitored user", @@ -79232,7 +79246,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Entity Engines", @@ -79315,7 +79329,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete the Entity Engine", @@ -79604,7 +79618,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an entity in Entity Store", @@ -79845,7 +79859,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Cleanup the Risk Engine", @@ -80220,7 +80234,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an entity", @@ -80902,7 +80916,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an exception list", @@ -81472,7 +81486,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an exception list item", @@ -82034,7 +82048,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a value list", @@ -82388,7 +82402,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete value list data streams", @@ -82504,7 +82518,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a value list item", @@ -83532,7 +83546,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a pack", @@ -83892,7 +83906,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a saved query", @@ -84245,7 +84259,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete one or more notes", @@ -84507,7 +84521,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Timelines or Timeline templates", @@ -85605,7 +85619,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an SLO", @@ -86170,7 +86184,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a space", @@ -86391,7 +86405,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a stream", @@ -86925,7 +86939,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Remove a query from a stream", @@ -87240,7 +87254,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Unlink an attachment from a stream", @@ -87497,7 +87511,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a tag", @@ -87798,7 +87812,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a visualization", @@ -87858,7 +87872,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Bulk delete workflows", @@ -88788,7 +88802,7 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a workflow", @@ -89290,8 +89304,6 @@ "summary": "Fetch current account information", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89321,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -89357,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 } } @@ -89403,8 +89428,6 @@ "summary": "Get all API keys", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89450,6 +89473,7 @@ ], "summary": "Create API key", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -89486,14 +89510,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete API keys", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89529,8 +89551,6 @@ "summary": "Get API key", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89567,14 +89587,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete API key", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -89631,8 +89649,6 @@ "summary": "Get costs overview for the organization. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89689,8 +89705,6 @@ "summary": "Get charts for the organization. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89740,8 +89754,6 @@ "summary": "Get deployments costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89805,8 +89817,6 @@ "summary": "Get charts by deployment. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89863,8 +89873,6 @@ "summary": "Get itemized costs by deployments. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89914,8 +89922,6 @@ "summary": "Get itemized costs for the organization. Currently unavailable in self-hosted ECE.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -89936,8 +89942,6 @@ "summary": "List organizations", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -89973,8 +89977,6 @@ "summary": "Get organization invitation", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90005,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 } }, @@ -90044,8 +90054,6 @@ "summary": "Fetch organization information", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90109,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -90151,8 +90164,6 @@ "summary": "Get domain claims", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90195,14 +90206,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete domain claim", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90239,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 } }, @@ -90279,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 } }, @@ -90318,8 +90343,6 @@ "summary": "Get organization IdP", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90368,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -90411,14 +90439,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Tear down organization IdP", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90454,8 +90480,6 @@ "summary": "Get organization service provider SAML2 metadata.xml for configuring the identity provider", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90491,8 +90515,6 @@ "summary": "List organization invitations", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90546,6 +90568,7 @@ ], "summary": "Create organization invitations", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -90589,14 +90612,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete organization invitations", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90632,8 +90653,6 @@ "summary": "List organization members", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90684,14 +90703,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete organization memberships", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90727,8 +90744,6 @@ "summary": "Get role mappings", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -90765,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -90808,14 +90828,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete role mappings", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -90877,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 } }, @@ -90941,14 +90967,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Remove Role Assignments", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -91031,8 +91055,6 @@ "summary": "Get deployment templates", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91097,8 +91119,6 @@ "summary": "Get deployment template", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -91120,8 +91140,6 @@ "summary": "List Deployments", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91213,6 +91231,7 @@ ], "summary": "Create Deployment", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -91274,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 } }, @@ -91339,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 } }, @@ -91477,8 +91512,6 @@ "summary": "Get Deployment", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91574,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -91619,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 } }, @@ -91668,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 } }, @@ -91764,8 +91818,6 @@ "summary": "Get Deployment APM Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91804,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 } }, @@ -91900,8 +91960,6 @@ "summary": "Get Deployment App Search Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91945,8 +92003,6 @@ "summary": "Set AppSearch read-only status", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -91991,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -92034,8 +92095,6 @@ "summary": "Get certificate authority", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92156,8 +92215,6 @@ "summary": "Get Deployment Elasticsearch Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92203,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 } }, @@ -92258,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 } }, @@ -92307,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 } }, @@ -92356,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 } }, @@ -92433,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 } }, @@ -92489,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 } }, @@ -92561,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 } }, @@ -92608,8 +92721,6 @@ "summary": "Get the items in the Elasticsearch resource keystore", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92661,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 } }, @@ -92708,8 +92827,6 @@ "summary": "Get certificate based remote clusters", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92754,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -92804,8 +92926,6 @@ "summary": "List the attached snapshot repositories", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92854,6 +92974,7 @@ ], "summary": "Create a snapshot repository for Elasticsearch resource", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -92905,14 +93026,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Remove the attached snapshot repository", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -92956,8 +93075,6 @@ "summary": "Get Elasticsearch tiers", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93038,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 } }, @@ -93134,8 +93259,6 @@ "summary": "Get Deployment Enterprise Search Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93228,8 +93351,6 @@ "summary": "Get Deployment Integrations Server Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93329,8 +93450,6 @@ "summary": "Get Deployment Kibana Resource Info", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93381,8 +93500,6 @@ "summary": "Build request to migrate deployment to a different template", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93419,8 +93536,6 @@ "summary": "Get the tags for a Deployment", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93458,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -93502,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 } }, @@ -93549,8 +93677,6 @@ "summary": "Get Deployment upgrade assistant status", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -93603,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 } }, @@ -93652,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 } }, @@ -93701,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 } }, @@ -93750,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 } }, @@ -93799,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 } }, @@ -93862,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 } }, @@ -93925,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 } }, @@ -93988,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 } }, @@ -94051,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 } }, @@ -94120,14 +94318,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Cancel resource pending plan", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94178,8 +94374,6 @@ "summary": "Get the user settings of a Deployment resource", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94231,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -94290,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 } }, @@ -94353,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 } } @@ -94407,8 +94622,6 @@ "summary": "Get associated rulesets", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94452,8 +94665,6 @@ "summary": "List traffic filter claimed link id", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94502,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 } }, @@ -94554,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 } }, @@ -94608,8 +94835,6 @@ "summary": "List traffic filter rulesets", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94674,6 +94899,7 @@ ], "summary": "Create a ruleset", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94717,8 +94943,6 @@ "summary": "Retrieves the ruleset by ID.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94786,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -94837,14 +95066,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a ruleset", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94881,8 +95108,6 @@ "summary": "Get associated deployments", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -94930,6 +95155,7 @@ ], "summary": "Create ruleset association", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -94981,14 +95207,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete ruleset association", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -95010,8 +95234,6 @@ "summary": "List Extensions", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95074,6 +95296,7 @@ ], "summary": "Create an extension", "intent": { + "destructive": false, "requiresAuth": true } }, @@ -95117,8 +95340,6 @@ "summary": "Get Extension", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95184,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 } }, @@ -95219,13 +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": false, - "idempotent": true, - "scope": "global", + "destructive": true, "requiresAuth": true } }, @@ -95263,14 +95497,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete Extension", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -95321,8 +95553,6 @@ "summary": "Get stack versions", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -95344,8 +95574,6 @@ "summary": "Get trusted environments", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -95415,8 +95643,6 @@ "summary": "Get Elasticsearch projects", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95495,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", @@ -95540,6 +95773,7 @@ ], "summary": "Create an Elasticsearch project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95577,8 +95811,6 @@ "summary": "Get an Elasticsearch project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95617,14 +95849,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Elasticsearch project", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95694,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 } }, @@ -95731,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", @@ -95769,6 +96014,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -95801,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 } }, @@ -95842,8 +96096,6 @@ "summary": "Get roles for an Elasticsearch project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -95881,8 +96133,6 @@ "summary": "Get the status of an Elasticsearch project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -95941,8 +96191,6 @@ "summary": "Get Observability projects", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96015,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", @@ -96060,6 +96315,7 @@ ], "summary": "Create an observability project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96097,8 +96353,6 @@ "summary": "Get an Observability project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96137,14 +96391,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete an Observability project", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96218,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 } }, @@ -96255,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", @@ -96293,6 +96560,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96325,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 } }, @@ -96366,8 +96642,6 @@ "summary": "Get roles for an Observability project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96405,8 +96679,6 @@ "summary": "Get the status of an Observability project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -96465,8 +96737,6 @@ "summary": "Get Security projects", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96551,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", @@ -96596,6 +96873,7 @@ ], "summary": "Create a security project", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96633,8 +96911,6 @@ "summary": "Get a Security project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96673,14 +96949,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a Security project", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96763,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 } }, @@ -96800,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", @@ -96838,6 +97127,7 @@ ], "summary": "Reset the project credentials", "intent": { + "destructive": true, "requiresAuth": true } }, @@ -96870,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 } }, @@ -96911,8 +97209,6 @@ "summary": "Get roles for a Security project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -96950,8 +97246,6 @@ "summary": "Get the status of a Security project", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -97040,8 +97334,6 @@ "summary": "Get Elasticsearch project link candidates", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97120,8 +97412,6 @@ "summary": "Get Observability project link candidates", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97200,8 +97490,6 @@ "summary": "Get Security project link candidates", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97238,8 +97526,6 @@ "summary": "Get Elasticsearch project delete status", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97276,8 +97562,6 @@ "summary": "Get Observability project delete status", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97314,8 +97598,6 @@ "summary": "Get Security project delete status", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -97337,8 +97619,6 @@ "summary": "Get regions", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97375,8 +97655,6 @@ "summary": "Get a region", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } } @@ -97427,8 +97705,6 @@ "summary": "List traffic filters", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97494,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 } }, @@ -97541,8 +97825,6 @@ "summary": "List PrivateLink region metadata", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97579,8 +97861,6 @@ "summary": "Retrieves the traffic filter by ID.", "intent": { "destructive": false, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97618,14 +97898,12 @@ "name": "yes", "type": "boolean", "required": false, - "summary": "Confirm destructive action without prompting" + "summary": "confirm destructive action without prompting" } ], "summary": "Delete a traffic filter", "intent": { "destructive": true, - "idempotent": true, - "scope": "global", "requiresAuth": true } }, @@ -97681,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 } } From 141243851bff9b011839e3ddbe6cc82330b55b3c Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:48:49 -0400 Subject: [PATCH 09/23] fix: pass yes flag in generated es delete tests --- codegen/functional/mapper.ts | 7 +++++++ codegen/functional/test/mapper.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) 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')) + }) }) From 606437a1d1605f72541c01499479b7b4818d3183 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 11:48:49 -0400 Subject: [PATCH 10/23] fix: pass yes flag in kb functional delete calls --- test/functional/kb/alerting.sh | 4 ++-- test/functional/kb/connectors.sh | 4 ++-- test/functional/kb/data_views.sh | 4 ++-- test/functional/kb/spaces.sh | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) 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') From bcbadf6dee05a990cc63739f1d00c912683f0851 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 14:49:21 -0400 Subject: [PATCH 11/23] ci: cap test node job at 10 minutes --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f668eba0..e121ed56 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 10 to fail fast on a hang instead + # of burning hours and losing logs to blob storage expiry. + timeout-minutes: 10 # 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. From 89b92679acaa1d53fc1c80f40f80564b82358815 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 15:06:12 -0400 Subject: [PATCH 12/23] test: fail individual tests after 20s not the job --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cc1da7d2..946f2971 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=20000 --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", From 08eb2104fa10ee0631e21b537593a4e5a29721e5 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:11:50 -0400 Subject: [PATCH 13/23] test: drop dynamic import causing windows test hang --- test/confirmation.test.ts | 50 +++++++++++++-------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts index 7c0eb9bf..d250a8fc 100644 --- a/test/confirmation.test.ts +++ b/test/confirmation.test.ts @@ -5,6 +5,7 @@ import { describe, it, beforeEach, afterEach } from 'node:test' import assert from 'node:assert/strict' +import { Command } from 'commander' import { defineCommand, _testSetConfirmReader, _testSetIsTTY } from '../src/factory.ts' import type { OpaqueCommandHandle } from '../src/factory.ts' @@ -13,7 +14,6 @@ import type { OpaqueCommandHandle } from '../src/factory.ts' // --------------------------------------------------------------------------- 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') @@ -26,7 +26,6 @@ async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string 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') @@ -40,8 +39,8 @@ async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string // --------------------------------------------------------------------------- // 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 +// execution creates async gaps (each helper awaits program.parseAsync) during +// which sibling describe hooks can mutate shared state such as // `process.stderr.isTTY`. Wrapping everything in `{ concurrency: false }` here // serialises all execution and eliminates those races. // --------------------------------------------------------------------------- @@ -178,54 +177,37 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- // 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) + it('proceeds and runs the handler when the reader returns true', async () => { + const r1 = _testSetIsTTY(true) const r2 = _testSetConfirmReader(async () => true) - r2(); r1() - }) - - it('proceeds on yes, aborts on no', async () => { - // 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) let handlerCalled = false - const cmdYes = defineCommand({ + const cmd = defineCommand({ name: 'delete', description: 'Delete a resource', intent: { destructive: true }, - handler: () => { handlerCalled = true; throw new Error('stop_here') }, + handler: () => { handlerCalled = true; return {} }, }) try { - await captureErrAsync(cmdYes, [], []) + await invokeAsync(cmd, [], []) } finally { r2(); r1() } assert.equal(handlerCalled, true, 'handler must be called when TTY reader returns true') + }) - // Case 2: reader returns false → aborts before handler runs - handlerCalled = false - r1 = _testSetIsTTY(true) - r2 = _testSetConfirmReader(async () => false) - const cmdNo = defineCommand({ - name: 'delete2', + it('aborts before the handler runs when the reader returns false', async () => { + const r1 = _testSetIsTTY(true) + const r2 = _testSetConfirmReader(async () => false) + let handlerCalled = false + const cmd = defineCommand({ + name: 'delete', description: 'Delete a resource', intent: { destructive: true }, handler: () => { handlerCalled = true; return {} }, }) try { - const err = await captureErrAsync(cmdNo, [], []) + const err = await captureErrAsync(cmd, [], []) assert.equal(handlerCalled, false) assert.match(err, /Aborted/) } finally { r2(); r1() } From 3a4fc4bb1f8346c4798051c3f7e1b65d297cca2f Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:19:30 -0400 Subject: [PATCH 14/23] test: raise per test timeout to 60s --- .github/workflows/ci.yml | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e121ed56..ee468024 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,9 +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 10 to fail fast on a hang instead + # 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: 10 + 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/package.json b/package.json index 946f2971..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 --test-timeout=20000 --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", From af9e1f77e8f661f76ee41a4163a0e92a873e15aa Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:25:27 -0400 Subject: [PATCH 15/23] test: drop coverage flags to isolate windows hang --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fd43fc6..101286c2 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 --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:unit": "node --max-old-space-size=8192 --import tsx/esm --test --test-timeout=60000", "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", From 287983269abd2aca977c059fde634d1cb85497c2 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:34:36 -0400 Subject: [PATCH 16/23] test: rename confirmation test to check position dependence --- test/{confirmation.test.ts => zzz-confirmation.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{confirmation.test.ts => zzz-confirmation.test.ts} (100%) diff --git a/test/confirmation.test.ts b/test/zzz-confirmation.test.ts similarity index 100% rename from test/confirmation.test.ts rename to test/zzz-confirmation.test.ts From cc35e860e9db44a08cbdab21460746b19bab3cba Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:42:49 -0400 Subject: [PATCH 17/23] test: revert to dynamic commander import, restore filename --- test/{zzz-confirmation.test.ts => confirmation.test.ts} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename test/{zzz-confirmation.test.ts => confirmation.test.ts} (99%) diff --git a/test/zzz-confirmation.test.ts b/test/confirmation.test.ts similarity index 99% rename from test/zzz-confirmation.test.ts rename to test/confirmation.test.ts index d250a8fc..bf889e44 100644 --- a/test/zzz-confirmation.test.ts +++ b/test/confirmation.test.ts @@ -5,7 +5,6 @@ import { describe, it, beforeEach, afterEach } from 'node:test' import assert from 'node:assert/strict' -import { Command } from 'commander' import { defineCommand, _testSetConfirmReader, _testSetIsTTY } from '../src/factory.ts' import type { OpaqueCommandHandle } from '../src/factory.ts' @@ -14,6 +13,7 @@ import type { OpaqueCommandHandle } from '../src/factory.ts' // --------------------------------------------------------------------------- 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') @@ -26,6 +26,7 @@ async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string 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') From b27b22379518c834c7b78a2c3676fc57cbc9f93d Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:48:49 -0400 Subject: [PATCH 18/23] test: merge tty prompt cases back into one test --- test/confirmation.test.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts index bf889e44..c2b6baeb 100644 --- a/test/confirmation.test.ts +++ b/test/confirmation.test.ts @@ -178,37 +178,39 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- // Destructive commands: TTY interactive prompt + // + // Both TTY paths (proceed and abort) run inside one `it` block: splitting + // them into separate tests reintroduces a race in node:test's between-test + // scheduling around the TTY seam that reliably hangs on Windows CI. // ------------------------------------------------------------------------- describe('TTY prompt', () => { - it('proceeds and runs the handler when the reader returns true', async () => { - const r1 = _testSetIsTTY(true) - const r2 = _testSetConfirmReader(async () => true) + it('proceeds on yes, aborts on no', async () => { + let r1 = _testSetIsTTY(true) + let r2 = _testSetConfirmReader(async () => true) let handlerCalled = false - const cmd = defineCommand({ + const cmdYes = defineCommand({ name: 'delete', description: 'Delete a resource', intent: { destructive: true }, handler: () => { handlerCalled = true; return {} }, }) try { - await invokeAsync(cmd, [], []) + await invokeAsync(cmdYes, [], []) } finally { r2(); r1() } assert.equal(handlerCalled, true, 'handler must be called when TTY reader returns true') - }) - it('aborts before the handler runs when the reader returns false', async () => { - const r1 = _testSetIsTTY(true) - const r2 = _testSetConfirmReader(async () => false) - let handlerCalled = false - const cmd = defineCommand({ - name: 'delete', + handlerCalled = false + r1 = _testSetIsTTY(true) + r2 = _testSetConfirmReader(async () => false) + const cmdNo = defineCommand({ + name: 'delete2', description: 'Delete a resource', intent: { destructive: true }, handler: () => { handlerCalled = true; return {} }, }) try { - const err = await captureErrAsync(cmd, [], []) + const err = await captureErrAsync(cmdNo, [], []) assert.equal(handlerCalled, false) assert.match(err, /Aborted/) } finally { r2(); r1() } From b8d0380902b45923685c116f2cef6920a92648fa Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 16:55:41 -0400 Subject: [PATCH 19/23] test: add diagnostic marker for real readline fallback --- src/factory.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/factory.ts b/src/factory.ts index 06ad1a6d..fc0a6ed1 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -103,6 +103,7 @@ export function _testSetConfirmReader (fn: () => Promise): () => void { /** 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() + process.stderr.write('DIAGNOSTIC: promptConfirm hit real readline branch, confirmReader was null\n') return new Promise(resolve => { const rl = createInterface({ input: process.stdin, output: process.stderr }) rl.question('This action is destructive. Continue? [y/N] ', answer => { From a873792b7cec10ff55ccefedbb3e4dcd7267b7cf Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 17:05:07 -0400 Subject: [PATCH 20/23] test: revert failed hang fixes, keep timeout diagnostics --- package.json | 2 +- src/factory.ts | 1 - test/confirmation.test.ts | 29 ++++++++++++++++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 101286c2..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 --test-timeout=60000", + "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/factory.ts b/src/factory.ts index fc0a6ed1..06ad1a6d 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -103,7 +103,6 @@ export function _testSetConfirmReader (fn: () => Promise): () => void { /** 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() - process.stderr.write('DIAGNOSTIC: promptConfirm hit real readline branch, confirmReader was null\n') return new Promise(resolve => { const rl = createInterface({ input: process.stdin, output: process.stderr }) rl.question('This action is destructive. Continue? [y/N] ', answer => { diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts index c2b6baeb..7c0eb9bf 100644 --- a/test/confirmation.test.ts +++ b/test/confirmation.test.ts @@ -40,8 +40,8 @@ async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string // --------------------------------------------------------------------------- // 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 (each helper awaits program.parseAsync) during -// which sibling describe hooks can mutate shared state such as +// execution creates async gaps (from `await import('commander')` inside the +// helpers) during which sibling describe hooks can mutate shared state such as // `process.stderr.isTTY`. Wrapping everything in `{ concurrency: false }` here // serialises all execution and eliminates those races. // --------------------------------------------------------------------------- @@ -179,13 +179,27 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- // Destructive commands: TTY interactive prompt // - // Both TTY paths (proceed and abort) run inside one `it` block: splitting - // them into separate tests reintroduces a race in node:test's between-test - // scheduling around the TTY seam that reliably hangs on Windows CI. + // 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 () => { + // 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) let handlerCalled = false @@ -193,13 +207,14 @@ describe('confirmation guard', { concurrency: false }, () => { name: 'delete', description: 'Delete a resource', intent: { destructive: true }, - handler: () => { handlerCalled = true; return {} }, + handler: () => { handlerCalled = true; throw new Error('stop_here') }, }) try { - await invokeAsync(cmdYes, [], []) + await captureErrAsync(cmdYes, [], []) } finally { r2(); r1() } assert.equal(handlerCalled, true, 'handler must be called when TTY reader returns true') + // Case 2: reader returns false → aborts before handler runs handlerCalled = false r1 = _testSetIsTTY(true) r2 = _testSetConfirmReader(async () => false) From cf5d16a39ff6aba1d4b238c9bd0fba91efc9a2b2 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 17:15:55 -0400 Subject: [PATCH 21/23] test: add isolated tty hang repro file --- test/zzz-tty-repro.test.ts | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/zzz-tty-repro.test.ts diff --git a/test/zzz-tty-repro.test.ts b/test/zzz-tty-repro.test.ts new file mode 100644 index 00000000..af451159 --- /dev/null +++ b/test/zzz-tty-repro.test.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Minimal, isolated reproduction of the windows-latest hang seen in +// confirmation.test.ts's "TTY prompt" test. No sibling describes, no +// Object.defineProperty mutations, no --dry-run/--yes paths. Just the +// isTTYFn()===true + confirmReader seam, in complete isolation, to check +// whether the hang is inherent to this exact code path or an interaction +// with surrounding tests in the same file. + +import { describe, it } from 'node:test' +import assert from 'node:assert/strict' +import { defineCommand, _testSetConfirmReader, _testSetIsTTY } from '../src/factory.ts' + +describe('minimal tty repro', () => { + it('single tty-true confirm-yes invocation', async () => { + const r1 = _testSetIsTTY(true) + const r2 = _testSetConfirmReader(async () => true) + let handlerCalled = false + const cmd = defineCommand({ + name: 'delete', + description: 'Delete a resource', + intent: { destructive: true }, + handler: () => { handlerCalled = true; return {} }, + }) + const { Command } = await import('commander') + const program = new Command('elastic') + program.exitOverride() + program.addCommand(cmd) + cmd.exitOverride() + try { + await program.parseAsync(['delete'], { from: 'user' }) + } finally { + r2(); r1() + } + assert.equal(handlerCalled, true) + }) +}) From 5cb43aebaa3f9b00dfd9016066d0c74044c0493b Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 17:23:03 -0400 Subject: [PATCH 22/23] fix: stop mutating process.stderr.isTTY directly in tests --- test/confirmation.test.ts | 42 +++++++++++++++----------------------- test/zzz-tty-repro.test.ts | 40 ------------------------------------ 2 files changed, 17 insertions(+), 65 deletions(-) delete mode 100644 test/zzz-tty-repro.test.ts diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts index 7c0eb9bf..4d3ae509 100644 --- a/test/confirmation.test.ts +++ b/test/confirmation.test.ts @@ -42,8 +42,15 @@ async function captureErrAsync (handle: OpaqueCommandHandle, globalFlags: string // 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 -// `process.stderr.isTTY`. Wrapping everything in `{ concurrency: false }` here +// 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 }, () => { // ------------------------------------------------------------------------- @@ -80,15 +87,10 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- describe('non-TTY fail-closed', () => { - let origIsTTY: boolean | undefined + let restore: () => void - beforeEach(() => { - origIsTTY = process.stderr.isTTY - Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) - }) - afterEach(() => { - Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) - }) + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) it('returns confirmation_required error without --yes', async () => { const cmd = defineCommand({ @@ -143,15 +145,10 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- describe('--yes flag', () => { - let origIsTTY: boolean | undefined + let restore: () => void - beforeEach(() => { - origIsTTY = process.stderr.isTTY - Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) - }) - afterEach(() => { - Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) - }) + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) it('proceeds when --yes is passed', async () => { let handlerCalled = false @@ -237,15 +234,10 @@ describe('confirmation guard', { concurrency: false }, () => { // ------------------------------------------------------------------------- describe('--dry-run skip', () => { - let origIsTTY: boolean | undefined + let restore: () => void - beforeEach(() => { - origIsTTY = process.stderr.isTTY - Object.defineProperty(process.stderr, 'isTTY', { value: undefined, configurable: true, writable: true }) - }) - afterEach(() => { - Object.defineProperty(process.stderr, 'isTTY', { value: origIsTTY, configurable: true, writable: true }) - }) + beforeEach(() => { restore = _testSetIsTTY(false) }) + afterEach(() => { restore() }) it('--dry-run skips confirmation and exits early', async () => { let handlerCalled = false diff --git a/test/zzz-tty-repro.test.ts b/test/zzz-tty-repro.test.ts deleted file mode 100644 index af451159..00000000 --- a/test/zzz-tty-repro.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// Minimal, isolated reproduction of the windows-latest hang seen in -// confirmation.test.ts's "TTY prompt" test. No sibling describes, no -// Object.defineProperty mutations, no --dry-run/--yes paths. Just the -// isTTYFn()===true + confirmReader seam, in complete isolation, to check -// whether the hang is inherent to this exact code path or an interaction -// with surrounding tests in the same file. - -import { describe, it } from 'node:test' -import assert from 'node:assert/strict' -import { defineCommand, _testSetConfirmReader, _testSetIsTTY } from '../src/factory.ts' - -describe('minimal tty repro', () => { - it('single tty-true confirm-yes invocation', async () => { - const r1 = _testSetIsTTY(true) - const r2 = _testSetConfirmReader(async () => true) - let handlerCalled = false - const cmd = defineCommand({ - name: 'delete', - description: 'Delete a resource', - intent: { destructive: true }, - handler: () => { handlerCalled = true; return {} }, - }) - const { Command } = await import('commander') - const program = new Command('elastic') - program.exitOverride() - program.addCommand(cmd) - cmd.exitOverride() - try { - await program.parseAsync(['delete'], { from: 'user' }) - } finally { - r2(); r1() - } - assert.equal(handlerCalled, true) - }) -}) From 9238e1373333389a9bda438b2119e3f616dc42a4 Mon Sep 17 00:00:00 2001 From: margaretjgu Date: Fri, 14 Aug 2026 17:30:20 -0400 Subject: [PATCH 23/23] test: add diagnostics to isolate windows hang --- test/confirmation.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/confirmation.test.ts b/test/confirmation.test.ts index 4d3ae509..ffaf4f38 100644 --- a/test/confirmation.test.ts +++ b/test/confirmation.test.ts @@ -192,6 +192,8 @@ describe('confirmation guard', { concurrency: false }, () => { }) 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 @@ -199,6 +201,7 @@ describe('confirmation guard', { concurrency: false }, () => { // 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', @@ -206,26 +209,34 @@ describe('confirmation guard', { concurrency: false }, () => { 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') }) })