瀏覽代碼

Support showing the Ad-hoc command `note`

Updates #3155
JC Brand 1 年之前
父節點
當前提交
55c0c306ac

+ 1 - 0
CHANGES.md

@@ -5,6 +5,7 @@
 - #1195: Add actions to quote and copy messages
 - #2716: Fix issue with chat display when opening via URL
 - #3033: Add the `muc_grouped_by_domain` option to display MUCs on the same domain in collapsible groups
+- #3155: Some ad-hoc commands not working
 - #3300: Adding the maxWait option for `debouncedPruneHistory`
 - #3302: debounce MUC sidebar rendering
 - #3305: New config option [muc_search_service](https://conversejs.org/docs/html/configuration.html#muc-search-service)

+ 13 - 10
src/headless/plugins/adhoc/api.js

@@ -1,15 +1,18 @@
 /**
  * @typedef {import('./utils').AdHocCommand} AdHocCommand
- * @typedef {import('./utils').AdHocCommandFields} AdHocCommandFields
+ * @typedef {import('./utils').AdHocCommandResult} AdHocCommandResult
  */
 import log from '../../log.js';
 import _converse from '../../shared/_converse.js';
 import api from '../../shared/api/index.js';
 import converse from '../../shared/api/public.js';
-import { getCommandFields, parseForCommands } from './utils.js';
+import { parseCommandResult, parseForCommands } from './utils.js';
 
 const { Strophe, $iq, u, stx } = converse.env;
 
+/**
+ * @typedef {'execute'| 'cancel' |'prev'|'next'|'complete'} AdHocCommandAction
+ */
 
 export default {
     /**
@@ -43,7 +46,7 @@ export default {
          * @method api.adhoc.fetchCommandForm
          * @param {string} jid
          * @param {string} node
-         * @returns {Promise<AdHocCommandFields>}
+         * @returns {Promise<AdHocCommandResult>}
          */
         async fetchCommandForm (jid, node) {
             const stanza = $iq({
@@ -54,16 +57,16 @@ export default {
                 action: 'execute',
                 node,
             });
-            return getCommandFields(await api.sendIQ(stanza), jid);
+            return parseCommandResult(await api.sendIQ(stanza), jid);
         },
 
         /**
          * @method api.adhoc.runCommand
-         * @param { String } jid
-         * @param { String } sessionid
-         * @param { 'execute' | 'cancel' | 'prev' | 'next' | 'complete' } action
-         * @param { String } node
-         * @param { Array<{ [k:string]: string }> } inputs
+         * @param {String} jid
+         * @param {String} sessionid
+         * @param {AdHocCommandAction} action
+         * @param {String} node
+         * @param {Array<{ [k:string]: string }>} inputs
          */
         async runCommand (jid, sessionid, node, action, inputs) {
             const iq =
@@ -93,7 +96,7 @@ export default {
             const status = command?.getAttribute('status');
             return {
                 status,
-                ...(status === 'executing' ? getCommandFields(result) : {}),
+                ...(status === 'executing' ? parseCommandResult(result) : {}),
                 note: result.querySelector('note')?.textContent
             }
         }

+ 23 - 6
src/headless/plugins/adhoc/utils.js

@@ -15,36 +15,53 @@ const { Strophe, u } = converse.env;
  */
 
 /**
+ * Given a "result" IQ stanza that contains a list of ad-hoc commands, parse it
+ * and return the list of commands as JSON objects.
  * @param {Element} stanza
  * @returns {AdHocCommand[]}
  */
-export function parseForCommands (stanza) {
+export function parseForCommands(stanza) {
     const items = sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"][node="${Strophe.NS.ADHOC}"] item`, stanza);
     return items.map(u.getAttributes);
 }
 
 /**
- * @typedef {Object} AdHocCommandFields
+ * @typedef {Object} AdHocCommandResultNote
+ * @property {string} text
+ * @property {'info'|'warn'|'error'} type
+ *
+ * @typedef {Object} AdHocCommandResult
  * @property {string} sessionid
  * @property {string} [instructions]
  * @property {TemplateResult[]} [fields]
  * @property {string[]} [actions]
+ * @property {AdHocCommandResultNote} [note]
  */
 
 /**
+ * Given a "result" IQ stanza containing the outcome of an Ad-hoc command that
+ * was executed, parse it and return the values as a JSON object.
  * @param {Element} iq
  * @param {string} [jid]
- * @returns {AdHocCommandFields}
+ * @returns {AdHocCommandResult}
  */
-export function getCommandFields (iq, jid) {
+export function parseCommandResult(iq, jid) {
     const cmd_el = sizzle(`command[xmlns="${Strophe.NS.ADHOC}"]`, iq).pop();
+    const note = cmd_el.querySelector('note');
+
     const data = {
         sessionid: cmd_el.getAttribute('sessionid'),
         instructions: sizzle('x[type="form"][xmlns="jabber:x:data"] instructions', cmd_el).pop()?.textContent,
-        fields: sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el).map((f) =>
-            u.xForm2TemplateResult(f, cmd_el, { domain: jid })
+        fields: sizzle('x[type="form"][xmlns="jabber:x:data"] field', cmd_el).map(
+            /** @param {Element} f */ (f) => u.xForm2TemplateResult(f, cmd_el, { domain: jid })
         ),
         actions: Array.from(cmd_el.querySelector('actions')?.children ?? []).map((a) => a.nodeName.toLowerCase()),
+        note: note
+            ? {
+                  text: note.textContent,
+                  type: note.getAttribute('type'),
+              }
+            : null,
     };
     return data;
 }

+ 10 - 9
src/headless/types/plugins/adhoc/api.d.ts

@@ -9,18 +9,18 @@ declare namespace _default {
          * @method api.adhoc.fetchCommandForm
          * @param {string} jid
          * @param {string} node
-         * @returns {Promise<AdHocCommandFields>}
+         * @returns {Promise<AdHocCommandResult>}
          */
-        function fetchCommandForm(jid: string, node: string): Promise<import("./utils.js").AdHocCommandFields>;
+        function fetchCommandForm(jid: string, node: string): Promise<import("./utils.js").AdHocCommandResult>;
         /**
          * @method api.adhoc.runCommand
-         * @param { String } jid
-         * @param { String } sessionid
-         * @param { 'execute' | 'cancel' | 'prev' | 'next' | 'complete' } action
-         * @param { String } node
-         * @param { Array<{ [k:string]: string }> } inputs
+         * @param {String} jid
+         * @param {String} sessionid
+         * @param {AdHocCommandAction} action
+         * @param {String} node
+         * @param {Array<{ [k:string]: string }>} inputs
          */
-        function runCommand(jid: string, sessionid: string, node: string, action: "cancel" | "execute" | "prev" | "next" | "complete", inputs: {
+        function runCommand(jid: string, sessionid: string, node: string, action: AdHocCommandAction, inputs: {
             [k: string]: string;
         }[]): Promise<{
             note: any;
@@ -34,5 +34,6 @@ declare namespace _default {
 }
 export default _default;
 export type AdHocCommand = import('./utils').AdHocCommand;
-export type AdHocCommandFields = import('./utils').AdHocCommandFields;
+export type AdHocCommandResult = import('./utils').AdHocCommandResult;
+export type AdHocCommandAction = 'execute' | 'cancel' | 'prev' | 'next' | 'complete';
 //# sourceMappingURL=api.d.ts.map

+ 18 - 4
src/headless/types/plugins/adhoc/utils.d.ts

@@ -6,34 +6,48 @@
  * @property {string} status
  */
 /**
+ * Given a "result" IQ stanza that contains a list of ad-hoc commands, parse it
+ * and return the list of commands as JSON objects.
  * @param {Element} stanza
  * @returns {AdHocCommand[]}
  */
 export function parseForCommands(stanza: Element): AdHocCommand[];
 /**
- * @typedef {Object} AdHocCommandFields
+ * @typedef {Object} AdHocCommandResultNote
+ * @property {string} text
+ * @property {'info'|'warn'|'error'} type
+ *
+ * @typedef {Object} AdHocCommandResult
  * @property {string} sessionid
  * @property {string} [instructions]
  * @property {TemplateResult[]} [fields]
  * @property {string[]} [actions]
+ * @property {AdHocCommandResultNote} [note]
  */
 /**
+ * Given a "result" IQ stanza containing the outcome of an Ad-hoc command that
+ * was executed, parse it and return the values as a JSON object.
  * @param {Element} iq
  * @param {string} [jid]
- * @returns {AdHocCommandFields}
+ * @returns {AdHocCommandResult}
  */
-export function getCommandFields(iq: Element, jid?: string): AdHocCommandFields;
+export function parseCommandResult(iq: Element, jid?: string): AdHocCommandResult;
 export type AdHocCommand = {
     action: string;
     node: string;
     sessionid: string;
     status: string;
 };
-export type AdHocCommandFields = {
+export type AdHocCommandResultNote = {
+    text: string;
+    type: 'info' | 'warn' | 'error';
+};
+export type AdHocCommandResult = {
     sessionid: string;
     instructions?: string;
     fields?: TemplateResult[];
     actions?: string[];
+    note?: AdHocCommandResultNote;
 };
 export type TemplateResult = import('lit').TemplateResult;
 //# sourceMappingURL=utils.d.ts.map

+ 39 - 32
src/plugins/adhoc-views/adhoc-commands.js

@@ -1,12 +1,13 @@
 /**
  * @typedef {import('@converse/headless/types/plugins/adhoc/utils').AdHocCommand} AdHocCommand
- * @typedef {import('@converse/headless/types/plugins/adhoc/utils').AdHocCommandFields} AdHocCommandFields
+ * @typedef {import('@converse/headless/types/plugins/adhoc/utils').AdHocCommandResult} AdHocCommandResult
+ * @typedef {import('@converse/headless/types/plugins/adhoc/api').AdHocCommandAction} AdHocCommandAction
  */
+import { api, converse, log } from '@converse/headless';
 import 'shared/autocomplete/index.js';
 import tplAdhoc from './templates/ad-hoc.js';
 import { CustomElement } from 'shared/components/element.js';
 import { __ } from 'i18n';
-import { api, converse, log } from '@converse/headless';
 import { getNameAndValue } from 'utils/html.js';
 
 const { Strophe, sizzle } = converse.env;
@@ -19,10 +20,9 @@ const { Strophe, sizzle } = converse.env;
  * @property {'danger'|'primary'} [alert_type]
  * @property {'cancel'|'complete'|'execute'|'next'|'prev'} name
  *
- * @typedef {AdHocCommand & AdHocCommandFields & UIProps} AdHocCommandUIProps
+ * @typedef {AdHocCommand & AdHocCommandResult & UIProps} AdHocCommandUIProps
  */
 
-
 export default class AdHocCommands extends CustomElement {
     static get properties () {
         return {
@@ -40,11 +40,11 @@ export default class AdHocCommands extends CustomElement {
         this.view = 'choose-service';
         this.fetching = false;
         this.showform = '';
-        this.commands = /** @type {AdHocCommandUIProps[]} */([]);
+        this.commands = /** @type {AdHocCommandUIProps[]} */ ([]);
     }
 
-    render () {
-        return tplAdhoc(this)
+    render() {
+        return tplAdhoc(this);
     }
 
     /**
@@ -64,7 +64,7 @@ export default class AdHocCommands extends CustomElement {
         delete this.alert;
 
         const form_data = new FormData(ev.target);
-        const jid = /** @type {string} */(form_data.get('jid')).trim();
+        const jid = /** @type {string} */ (form_data.get('jid')).trim();
         let supported;
         try {
             supported = await api.disco.supports(Strophe.NS.ADHOC, jid);
@@ -76,17 +76,18 @@ export default class AdHocCommands extends CustomElement {
 
         if (supported) {
             try {
-                this.commands = /** @type {AdHocCommandUIProps[]} */(await api.adhoc.getCommands(jid));
+                this.commands = /** @type {AdHocCommandUIProps[]} */ (await api.adhoc.getCommands(jid));
                 this.view = 'list-commands';
             } catch (e) {
                 log.error(e);
                 this.alert_type = 'danger';
                 this.alert = __('Sorry, an error occurred while looking for commands on that entity.');
-                this.commands = /** @type {AdHocCommandUIProps[]} */([]);
+                this.commands = /** @type {AdHocCommandUIProps[]} */ ([]);
                 log.error(e);
                 return;
             }
         } else {
+            this.commands = [];
             this.alert_type = 'danger';
             this.alert = __("The specified entity doesn't support ad-hoc commands");
         }
@@ -95,7 +96,7 @@ export default class AdHocCommands extends CustomElement {
     async toggleCommandForm (ev) {
         ev.preventDefault();
         const node = ev.target.getAttribute('data-command-node');
-        const cmd = this.commands.filter(c => c.node === node)[0];
+        const cmd = this.commands.filter((c) => c.node === node)[0];
         const { jid } = cmd;
 
         if (this.showform === node) {
@@ -103,11 +104,11 @@ export default class AdHocCommands extends CustomElement {
             this.requestUpdate();
         } else {
             try {
-                const form = await api.adhoc.fetchCommandForm(jid, node);
-                cmd.sessionid = form.sessionid;
-                cmd.instructions = form.instructions;
-                cmd.fields = form.fields;
-                cmd.actions = form.actions;
+                const { sessionid, instrucions, fields, actions, note, status } = await api.adhoc.fetchCommandForm(
+                    jid,
+                    node
+                );
+                Object.assign(cmd, { sessionid, instrucions, fields, actions, note, status });
             } catch (e) {
                 if (e === null) {
                     log.error(`Error: timeout while trying to execute command for ${jid}`);
@@ -115,7 +116,8 @@ export default class AdHocCommands extends CustomElement {
                     log.error(`Error while trying to execute command for ${jid}`);
                     log.error(e);
                 }
-                cmd.instructions = __('An error occurred while trying to fetch the command form');
+                cmd.alert = __('An error occurred while trying to fetch the command form');
+                cmd.alert_type = 'danger';
             }
             this.showform = node;
         }
@@ -123,7 +125,6 @@ export default class AdHocCommands extends CustomElement {
 
     executeAction (ev) {
         ev.preventDefault();
-
         const action = ev.target.getAttribute('data-action');
 
         if (['execute', 'next', 'prev', 'complete'].includes(action)) {
@@ -138,31 +139,39 @@ export default class AdHocCommands extends CustomElement {
         delete cmd.instructions;
         delete cmd.sessionid;
         delete cmd.alert_type;
+        delete cmd.status;
         cmd.fields = [];
         cmd.acions = [];
         this.showform = '';
     }
 
+    /**
+     * @param {HTMLFormElement} form
+     * @param {AdHocCommandAction} action
+     */
     async runCommand (form, action) {
         const form_data = new FormData(form);
-        const jid = /** @type {string} */(form_data.get('command_jid')).trim();
-        const node = /** @type {string} */(form_data.get('command_node')).trim();
+        const jid = /** @type {string} */ (form_data.get('command_jid')).trim();
+        const node = /** @type {string} */ (form_data.get('command_node')).trim();
 
-        const cmd = this.commands.filter(c => c.node === node)[0];
+        const cmd = this.commands.filter((c) => c.node === node)[0];
         delete cmd.alert;
         this.requestUpdate();
 
-        const inputs = action === 'prev' ? [] :
-            sizzle(':input:not([type=button]):not([type=submit])', form)
-                .filter(i => !['command_jid', 'command_node'].includes(i.getAttribute('name')))
-                .map(getNameAndValue)
-                .filter(n => n);
+        const inputs =
+            action === 'prev'
+                ? []
+                : sizzle(':input:not([type=button]):not([type=submit])', form)
+                      .filter((i) => !['command_jid', 'command_node'].includes(i.getAttribute('name')))
+                      .map(getNameAndValue)
+                      .filter((n) => n);
 
         const response = await api.adhoc.runCommand(jid, cmd.sessionid, cmd.node, action, inputs);
 
         const { fields, status, note, instructions, actions } = response;
 
         if (status === 'error') {
+            cmd.status = status;
             cmd.alert_type = 'danger';
             cmd.alert = __(
                 'Sorry, an error occurred while trying to execute the command. See the developer console for details'
@@ -171,11 +180,9 @@ export default class AdHocCommands extends CustomElement {
         }
 
         if (status === 'executing') {
+            Object.assign(cmd, { fields, instructions, actions, status });
             cmd.alert = __('Executing');
-            cmd.fields = fields;
-            cmd.instructions = instructions;
             cmd.alert_type = 'primary';
-            cmd.actions = actions;
         } else if (status === 'completed') {
             this.alert_type = 'primary';
             this.alert = __('Completed');
@@ -195,10 +202,10 @@ export default class AdHocCommands extends CustomElement {
         this.requestUpdate();
 
         const form_data = new FormData(ev.target.form);
-        const jid = /** @type {string} */(form_data.get('command_jid')).trim();
-        const node = /** @type {string} */(form_data.get('command_node')).trim();
+        const jid = /** @type {string} */ (form_data.get('command_jid')).trim();
+        const node = /** @type {string} */ (form_data.get('command_node')).trim();
 
-        const cmd = this.commands.filter(c => c.node === node)[0];
+        const cmd = this.commands.filter((c) => c.node === node)[0];
         delete cmd.alert;
         this.requestUpdate();
 

+ 35 - 20
src/plugins/adhoc-views/templates/ad-hoc-command-form.js

@@ -5,13 +5,19 @@
 import { html } from 'lit';
 import { __ } from 'i18n';
 
-const action_map = {
+const ACTION_MAP = {
     execute: __('Execute'),
     prev: __('Previous'),
     next: __('Next'),
     complete: __('Complete'),
 };
 
+const NOTE_ALERT_MAP = {
+    'info': 'primary',
+    'warn': 'secondary',
+    'error': 'danger',
+};
+
 /**
  * @param {AdHocCommands} el
  * @param {AdHocCommandUIProps} command
@@ -24,34 +30,43 @@ export default (el, command) => {
             <!-- Don't remove this <span>,
                  this is a workaround for a lit bug where a <form> cannot be removed
                  if it contains an <input> with name "remove" -->
-            <form>
+            <form class="converse-form">
                 ${command.alert
                     ? html`<div class="alert alert-${command.alert_type}" role="alert">${command.alert}</div>`
                     : ''}
+                ${command.note
+                    ? html`<div class="alert alert-${NOTE_ALERT_MAP[command.note.type || 'info']}" role="alert">
+                          ${command.note.text}
+                      </div>`
+                    : ''}
+
                 <fieldset class="form-group">
                     <input type="hidden" name="command_node" value="${command.node}" />
                     <input type="hidden" name="command_jid" value="${command.jid}" />
 
-                    <p class="form-instructions">${command.instructions}</p>
+                    ${command.instructions ? html`<p class="form-instructions">${command.instructions}</p>` : ''}
                     ${command.fields ?? []}
                 </fieldset>
-                <fieldset>
-                    ${command.actions?.map(
-                        (action) =>
-                            html`<input
-                                data-action="${action}"
-                                @click=${(ev) => el.executeAction(ev)}
-                                type="button"
-                                class="btn btn-primary"
-                                value="${action_map[action]}"
-                            />`
-                    )}<input
-                        type="button"
-                        class="btn btn-secondary button-cancel"
-                        value="${i18n_cancel}"
-                        @click=${(ev) => el.cancel(ev)}
-                    />
-                </fieldset>
+                ${command.actions?.length
+                    ? html` <fieldset>
+                          ${command.actions?.map(
+                              (action) =>
+                                  html`<input
+                                      data-action="${action}"
+                                      @click=${(ev) => el.executeAction(ev)}
+                                      type="button"
+                                      class="btn btn-primary"
+                                      value="${ACTION_MAP[action]}"
+                                  />`
+                          )}
+                          <input
+                              type="button"
+                              class="btn btn-secondary button-cancel"
+                              value="${i18n_cancel}"
+                              @click=${(ev) => el.cancel(ev)}
+                          />
+                      </fieldset>`
+                    : ''}
             </form>
         </span>
     `;

+ 80 - 0
src/plugins/adhoc-views/tests/adhoc.js

@@ -131,6 +131,86 @@ describe("Ad-hoc commands", function () {
         inputs[6].click();
         await u.waitUntil(() => !u.isVisible(form));
     }));
+
+    it("can immediately return with a result, instead of a form", mock.initConverse([], {}, async (_converse) => {
+        const { api } = _converse;
+        const entity_jid = 'muc.montague.lit';
+        const { IQ_stanzas } = _converse.api.connection.get();
+
+        const modal = await api.modal.show('converse-user-settings-modal');
+        await u.waitUntil(() => u.isVisible(modal));
+        modal.querySelector('#commands-tab').click();
+
+        const adhoc_form = modal.querySelector('converse-adhoc-commands');
+        await u.waitUntil(() => u.isVisible(adhoc_form));
+
+        adhoc_form.querySelector('input[name="jid"]').value = entity_jid;
+        adhoc_form.querySelector('input[type="submit"]').click();
+
+        await mock.waitUntilDiscoConfirmed(_converse, entity_jid, [], ['http://jabber.org/protocol/commands'], [], 'info');
+
+        let sel = `iq[to="${entity_jid}"] query[xmlns="http://jabber.org/protocol/disco#items"]`;
+        let iq = await u.waitUntil(() => IQ_stanzas.filter(iq => sizzle(sel, iq).length).pop());
+
+        _converse.api.connection.get()._dataRecv(mock.createRequest(stx`
+            <iq type="result"
+                id="${iq.getAttribute("id")}"
+                to="${_converse.jid}"
+                from="${entity_jid}"
+                xmlns="jabber:client">
+            <query xmlns="http://jabber.org/protocol/disco#items"
+                    node="http://jabber.org/protocol/commands">
+                <item node="uptime" name="Get uptime" jid="${entity_jid}"/>
+            </query>
+        </iq>`));
+
+        const heading = await u.waitUntil(() => adhoc_form.querySelector('.list-group-item.active'));
+        expect(heading.textContent).toBe('Commands found:');
+
+        const items = adhoc_form.querySelectorAll('.list-group-item:not(.active)');
+        expect(items.length).toBe(1);
+        expect(items[0].textContent.trim()).toBe('Get uptime');
+
+        items[0].querySelector('a').click();
+
+        sel = `iq[to="${entity_jid}"][type="set"] command`;
+        iq = await u.waitUntil(() => IQ_stanzas.filter(iq => sizzle(sel, iq).length).pop());
+
+        expect(Strophe.serialize(iq)).toBe(
+            `<iq id="${iq.getAttribute("id")}" to="${entity_jid}" type="set" xmlns="jabber:client">`+
+                `<command action="execute" node="uptime" xmlns="http://jabber.org/protocol/commands"/>`+
+            `</iq>`
+        );
+
+        const uptime_text = 'This service has been running for 143 days, '+
+                            '11 hours and 15 minutes (since Sun Nov 12 21:22:22: 2023)';
+
+        _converse.api.connection.get()._dataRecv(
+            mock.createRequest(stx`
+                <iq to="${_converse.jid}"
+                    xmlns="jabber:client"
+                    type="result"
+                    xml:lang="en"
+                    id="${iq.getAttribute('id')}"
+                    from="${entity_jid}">
+
+                    <command status="completed"
+                             node="uptime"
+                             sessionid="1653988890a"
+                             xmlns="http://jabber.org/protocol/commands">
+                        <note type="info">${uptime_text}</note>
+                    </command>
+                </iq>`)
+        );
+
+        const form = await u.waitUntil(() => adhoc_form.querySelector('form form'));
+        const alert = form.querySelector('div.alert');
+        expect(u.isVisible(alert)).toBe(true);
+        expect(alert.textContent).toBe(uptime_text);
+
+        const inputs = form.querySelectorAll('input[type="button"]');
+        expect(inputs.length).toBe(0);
+    }));
 });
 
 describe("Ad-hoc commands consisting of multiple steps", function () {

+ 9 - 4
src/types/plugins/adhoc-views/adhoc-commands.d.ts

@@ -6,7 +6,7 @@
  * @property {'danger'|'primary'} [alert_type]
  * @property {'cancel'|'complete'|'execute'|'next'|'prev'} name
  *
- * @typedef {AdHocCommand & AdHocCommandFields & UIProps} AdHocCommandUIProps
+ * @typedef {AdHocCommand & AdHocCommandResult & UIProps} AdHocCommandUIProps
  */
 export default class AdHocCommands extends CustomElement {
     static get properties(): {
@@ -43,12 +43,17 @@ export default class AdHocCommands extends CustomElement {
     toggleCommandForm(ev: any): Promise<void>;
     executeAction(ev: any): void;
     clearCommand(cmd: any): void;
-    runCommand(form: any, action: any): Promise<void>;
+    /**
+     * @param {HTMLFormElement} form
+     * @param {AdHocCommandAction} action
+     */
+    runCommand(form: HTMLFormElement, action: AdHocCommandAction): Promise<void>;
     note: any;
     cancel(ev: any): Promise<void>;
 }
 export type AdHocCommand = import('@converse/headless/types/plugins/adhoc/utils').AdHocCommand;
-export type AdHocCommandFields = import('@converse/headless/types/plugins/adhoc/utils').AdHocCommandFields;
+export type AdHocCommandResult = import('@converse/headless/types/plugins/adhoc/utils').AdHocCommandResult;
+export type AdHocCommandAction = import('@converse/headless/types/plugins/adhoc/api').AdHocCommandAction;
 export type UIProps = {
     instructions: string;
     jid: string;
@@ -56,6 +61,6 @@ export type UIProps = {
     alert_type?: 'danger' | 'primary';
     name: 'cancel' | 'complete' | 'execute' | 'next' | 'prev';
 };
-export type AdHocCommandUIProps = AdHocCommand & AdHocCommandFields & UIProps;
+export type AdHocCommandUIProps = AdHocCommand & AdHocCommandResult & UIProps;
 import { CustomElement } from "shared/components/element.js";
 //# sourceMappingURL=adhoc-commands.d.ts.map