@f5xc-salesdemos/xcsh 19.48.1 → 19.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/xcsh",
4
- "version": "19.48.1",
4
+ "version": "19.51.1",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5xc-salesdemos/xcsh",
7
7
  "author": "Can Boluk",
@@ -54,13 +54,13 @@
54
54
  "dependencies": {
55
55
  "@agentclientprotocol/sdk": "0.16.1",
56
56
  "@mozilla/readability": "^0.6",
57
- "@f5xc-salesdemos/xcsh-stats": "19.48.1",
58
- "@f5xc-salesdemos/pi-agent-core": "19.48.1",
59
- "@f5xc-salesdemos/pi-ai": "19.48.1",
60
- "@f5xc-salesdemos/pi-natives": "19.48.1",
61
- "@f5xc-salesdemos/pi-resource-management": "19.48.1",
62
- "@f5xc-salesdemos/pi-tui": "19.48.1",
63
- "@f5xc-salesdemos/pi-utils": "19.48.1",
57
+ "@f5xc-salesdemos/xcsh-stats": "19.51.1",
58
+ "@f5xc-salesdemos/pi-agent-core": "19.51.1",
59
+ "@f5xc-salesdemos/pi-ai": "19.51.1",
60
+ "@f5xc-salesdemos/pi-natives": "19.51.1",
61
+ "@f5xc-salesdemos/pi-resource-management": "19.51.1",
62
+ "@f5xc-salesdemos/pi-tui": "19.51.1",
63
+ "@f5xc-salesdemos/pi-utils": "19.51.1",
64
64
  "@sinclair/typebox": "^0.34",
65
65
  "@xterm/headless": "^6.0",
66
66
  "ajv": "^8.20",
@@ -32,11 +32,14 @@ const CONSOLE_DOMAIN_RE = /\.(volterra\.us|console\.ves\.volterra\.io)$/;
32
32
  * coords. Runs entirely via `javascript_tool` — one small returnByValue, no
33
33
  * full AX serialization.
34
34
  */
35
- function buildResolverScript(selector: string): string {
36
- // Escape for JSON-safe embedding. The script returns JSON {x,y,found,tag,txt} or {found:false,error}.
37
- const sel = JSON.stringify(selector);
38
- return `(()=>{
39
- const sel=${sel};
35
+ /**
36
+ * Shared element-matching JS for the catalogue selector grammar (text('…'),
37
+ * role:text('…'), role[name='…'], bare role, and "row:has-text('…') >> sub").
38
+ * Both buildResolverScript (returns coords) and buildElementResolverScript
39
+ * (returns the live element for the deterministic click path) embed this verbatim
40
+ * so they resolve identically — only their tails differ.
41
+ */
42
+ const RESOLVER_HELPERS = `
40
43
  function roleToSelector(role){
41
44
  switch(role){
42
45
  case'button':return'button,input[type=button],input[type=submit],[role=button]';
@@ -112,6 +115,14 @@ function findInScope(s){
112
115
  if(bareRoleM){const c=[...document.querySelectorAll(roleToSelector(s))].filter(isVisible);return c[0]||document.querySelector(roleToSelector(s));}
113
116
  {const c=[...document.querySelectorAll(s)].filter(isVisible);return c[0]||document.querySelector(s);}
114
117
  }
118
+ `;
119
+
120
+ /**
121
+ * The element-finding block, shared. `__FAIL__('msg')` sentinels are substituted
122
+ * per builder: buildResolverScript → a {found:false,error} JSON return;
123
+ * buildElementResolverScript → `return null`. Sets `el` and scrolls it into view.
124
+ */
125
+ const RESOLVER_FIND = `
115
126
  let el=null;
116
127
  if(sel.includes('>>')){
117
128
  const parts=sel.split('>>').map(p=>p.trim());
@@ -119,23 +130,45 @@ if(sel.includes('>>')){
119
130
  const want=rowM?rowM[1].replace(/\\u0421/g,'C'):'';
120
131
  const rows=[...document.querySelectorAll('tr,[role=row],datatable-body-row')];
121
132
  const row=rows.find(r=>(r.textContent||'').replace(/\\u0421/g,'C').includes(want));
122
- if(!row)return JSON.stringify({found:false,error:'no row matching '+parts[0]});
123
- // resolve the sub-selector within the row
133
+ if(!row)__FAIL__('no row matching '+parts[0]);
124
134
  const subTextM=parts[1].match(/^([a-z]+):text\\('([^']*)'\\)$/);
125
135
  if(subTextM){const want2=subTextM[2];const cs=[...row.querySelectorAll(roleToSelector(subTextM[1]))].filter(isVisible);const nm=t=>t.replace(/\\s+/g,' ').trim();el=cs.find(e=>nm(e.textContent||'')===want2)||cs.find(e=>nm(e.textContent||'').includes(want2));}
126
136
  else{const cs=[...row.querySelectorAll(parts[1])].filter(isVisible);el=cs[0]||row.querySelector(parts[1]);}
127
- if(!el)return JSON.stringify({found:false,error:'sub-selector '+parts[1]+' not found in row'});
137
+ if(!el)__FAIL__('sub-selector '+parts[1]+' not found in row');
128
138
  }else{
129
139
  el=findInScope(sel);
130
140
  }
131
- if(!el)return JSON.stringify({found:false,error:'no match for '+sel});
141
+ if(!el)__FAIL__('no match for '+sel);
132
142
  el.scrollIntoView({block:'center',inline:'center'});
143
+ `;
144
+
145
+ /** Resolve a selector to viewport-center coords (returns JSON). */
146
+ export function buildResolverScript(selector: string): string {
147
+ const sel = JSON.stringify(selector);
148
+ const find = RESOLVER_FIND.replace(/__FAIL__\(([^;]*?)\);/g, "return JSON.stringify({found:false,error:($1)});");
149
+ return `(()=>{
150
+ const sel=${sel};
151
+ ${RESOLVER_HELPERS}
152
+ ${find}
133
153
  const r=el.getBoundingClientRect();
134
154
  const inGrid=!!el.closest&&!!el.closest('ngx-datatable,datatable-body-cell,datatable-body-row,[class*=datatable]');
135
155
  return JSON.stringify({found:true,x:Math.round(r.x+r.width/2),y:Math.round(r.y+r.height/2),tag:el.tagName,txt:(el.textContent||'').trim().slice(0,30),inGrid:inGrid,val:(el.value!==undefined&&el.value!==null?String(el.value):'')});
136
156
  })()`;
137
157
  }
138
158
 
159
+ /** Resolve a selector to the live ELEMENT (returns the element or null) — the
160
+ * input to the deterministic click path (clickElement → getContentQuads + hit-test). */
161
+ export function buildElementResolverScript(selector: string): string {
162
+ const sel = JSON.stringify(selector);
163
+ const find = RESOLVER_FIND.replace(/__FAIL__\([^;]*?\);/g, "return null;");
164
+ return `(()=>{
165
+ const sel=${sel};
166
+ ${RESOLVER_HELPERS}
167
+ ${find}
168
+ return el;
169
+ })()`;
170
+ }
171
+
139
172
  /**
140
173
  * Run a `javascript_tool` snippet whose body returns a JSON string, and parse it.
141
174
  * The bridge's `javascript_tool` wraps the evaluated value as `{ result: <value> }`,
@@ -217,14 +250,13 @@ export class ExtensionPageActions implements PageActions {
217
250
  }
218
251
 
219
252
  async click(selector: string, _context?: string): Promise<void> {
220
- // Brief settle before the trusted click: dialogs/overlays/menus animate in,
221
- // and a click landing mid-transition misses. resolveCoords already scrolled
222
- // the element into view; a short pause lets the transition finish. (The
223
- // deterministic driver slept between steps, which is why it clicked reliably
224
- // where the back-to-back runner missed the confirm button.)
225
- const { x, y } = await resolveCoords(this.#ext, selector);
226
- await new Promise(r => setTimeout(r, 300));
227
- await this.#ext.clickXy(x, y);
253
+ // Deterministic click: resolve the selector to the live element, then let the
254
+ // extension derive geometry from the renderer (DOM.getContentQuads) and
255
+ // hit-test the point (document.elementFromPoint) before dispatching. This
256
+ // replaces the JS getBoundingClientRect coords + 300ms settle heuristic
257
+ // the hit-test is the gate now (it fails loudly on occlusion instead of
258
+ // landing mid-transition or on an overlay).
259
+ await this.#ext.clickElement(buildElementResolverScript(selector));
228
260
  }
229
261
 
230
262
  async fill(selector: string, value: string, _context?: string): Promise<void> {
@@ -247,7 +279,8 @@ export class ExtensionPageActions implements PageActions {
247
279
  // For textareas we must NOT press Enter (it inserts a newline); Tab blurs.
248
280
  const isTextarea = probe.tag === "TEXTAREA";
249
281
  if (probe.inGrid || isTextarea) {
250
- await this.#ext.clickXy(probe.x as number, probe.y as number);
282
+ // Focus the cell/textarea via the deterministic path (hit-tested).
283
+ await this.#ext.clickElement(buildElementResolverScript(selector));
251
284
  await new Promise(r => setTimeout(r, 200));
252
285
  // Clear any pre-existing text (Backspace ×N) so re-fills are clean —
253
286
  // fresh Add-Item rows are empty, so this is a no-op for create.
@@ -287,18 +320,19 @@ export class ExtensionPageActions implements PageActions {
287
320
  // All options appear on click; the option is clicked directly below.
288
321
  const resolved = await withTimeout(evalJson(this.#ext, buildResolverScript(selector)), 10_000, "resolve-listbox");
289
322
  if (!resolved?.found) throw new Error(`selectOption: selector "${selector}" not found`);
290
- await withTimeout(this.#ext.clickXy(resolved.x, resolved.y), 10_000, "click-listbox");
323
+ await withTimeout(this.#ext.clickElement(buildElementResolverScript(selector)), 10_000, "click-listbox");
291
324
  await new Promise(r => setTimeout(r, 1200));
292
325
  // Click the option whose text matches value (exact-first via the resolver).
293
326
  // Best-effort: if it never renders, fall through — the default value usually
294
327
  // already applies, and a hard failure here would block create flows.
295
328
  try {
296
- const optCoords = await withTimeout(
297
- resolveCoords(this.#ext, `option:text('${value}')`),
298
- 10_000,
299
- "resolve-option",
329
+ // Deterministic option click — polls for the option to render (async panel)
330
+ // and hit-tests it. Best-effort; on no-match it throws and we Escape below.
331
+ await withTimeout(
332
+ this.#ext.clickElement(buildElementResolverScript(`option:text('${value}')`), 6_000),
333
+ 12_000,
334
+ "click-option",
300
335
  );
301
- await withTimeout(this.#ext.clickXy(optCoords.x, optCoords.y), 10_000, "click-option");
302
336
  await new Promise(r => setTimeout(r, 600));
303
337
  } catch {
304
338
  // Option not selectable (already default, or non-standard widget) — press
@@ -49,8 +49,15 @@ export interface ExtensionPage {
49
49
  browserBatch(
50
50
  actions: Array<{ tool: string; params: unknown }>,
51
51
  ): Promise<Array<{ tool: string; content: unknown; is_error: boolean }>>;
52
- /** Trusted CDP click at viewport coordinates (no ref / read_ax needed). */
52
+ /** Trusted CDP click at viewport coordinates. Last-resort primitive for points
53
+ * with no backing element (e.g. a computed portal point); prefer clickElement. */
53
54
  clickXy(x: number, y: number): Promise<void>;
55
+ /** Deterministic click: `js` is an expression returning an Element (or null).
56
+ * The extension derives the clickable point from the renderer's layout
57
+ * (DOM.getContentQuads) and hit-tests it (document.elementFromPoint) before
58
+ * dispatching — no JS getBoundingClientRect, no silent mis-clicks. `waitMs`
59
+ * polls for the element (async portals). Throws loudly on occlusion. */
60
+ clickElement(js: string, waitMs?: number): Promise<{ x: number; y: number; hit: boolean }>;
54
61
  /** CDP Input.insertText into the focused element (genuine trusted input events). */
55
62
  typeText(text: string): Promise<void>;
56
63
  /** Atomically type into a CDK-portal typeahead + click the matching option.
@@ -214,6 +221,15 @@ class BridgeExtensionPage implements ExtensionPage {
214
221
  unwrap(await this.#server.request("click_xy", { x, y }), "click_xy");
215
222
  }
216
223
 
224
+ async clickElement(js: string, waitMs?: number): Promise<{ x: number; y: number; hit: boolean }> {
225
+ const timeout = Math.max(30_000, (waitMs ?? 0) + 5_000);
226
+ return unwrap(await this.#server.request("click_element", { js, wait_ms: waitMs }, timeout), "click_element") as {
227
+ x: number;
228
+ y: number;
229
+ hit: boolean;
230
+ };
231
+ }
232
+
217
233
  async typeText(text: string): Promise<void> {
218
234
  unwrap(await this.#server.request("type_text", { text }), "type_text");
219
235
  }
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "19.48.1",
21
- "commit": "631f2894864056abcc7f4d985ad5d4bc4b12806e",
22
- "shortCommit": "631f289",
20
+ "version": "19.51.1",
21
+ "commit": "671ad2b869a09652fe8e49dc726d754f80086d9b",
22
+ "shortCommit": "671ad2b",
23
23
  "branch": "main",
24
- "tag": "v19.48.1",
25
- "commitDate": "2026-06-25T20:15:07Z",
26
- "buildDate": "2026-06-25T20:36:30.697Z",
24
+ "tag": "v19.51.1",
25
+ "commitDate": "2026-06-26T06:28:05Z",
26
+ "buildDate": "2026-06-26T06:51:11.727Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5xc-salesdemos/xcsh",
30
30
  "repoSlug": "f5xc-salesdemos/xcsh",
31
- "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/631f2894864056abcc7f4d985ad5d4bc4b12806e",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.48.1"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/671ad2b869a09652fe8e49dc726d754f80086d9b",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.51.1"
33
33
  };
@@ -216,7 +216,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
216
216
  "cluster/update":
217
217
  "schema: urn:f5xc:console:workflow:v1\nid: cluster-update\nlabel: Update Clusters\nresource: cluster\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-cluster\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/virtual_host/clusters\n wait_for: text('Clusters')\n description: Navigate to Clusters list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('Clusters')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
218
218
  "code-base-integration/create":
219
- "schema: urn:f5xc:console:workflow:v1\nid: code-base-integration-create\nlabel: Create Code Base Integration\nresource: code-base-integration\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Code Base Integration name (lowercase alphanumeric and hyphens)\n example: example-code-base-integration\n code_base:\n required: false\n description: \"'Field Code Base in Integration Data is required'\"\n default: \"\"\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/api_security/code_base_integration\n wait_for: text('Code Base Integration')\n description: Navigate to Code Base Integration list page\n - id: click-add-tab\n action: click\n selector: text('Add Code Base Integration')\n wait_for: textbox[name='Name']\n description: Click Add Code Base Integration to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: configure-code_base\n action: click\n selector: text('Configure')\n context: Code Base section\n description: Open the Code Base configuration ('Configure' is a link, not a button)\n - id: fill-code_base-value\n action: fill\n selector: textbox\n value: \"{code_base}\"\n description: Enter the Code Base value via the 'code_base' parameter\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
219
+ "schema: urn:f5xc:console:workflow:v1\nid: code-base-integration-create\nlabel: Create Code Base Integration\nresource: code-base-integration\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Code Base Integration name (lowercase alphanumeric and hyphens)\n example: example-code-base-integration\n code_base:\n required: true\n description: A vsui \"Select item\" listbox (not a configurable). Selects an existing code-base integration. Verified from\n live form screenshot.\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/api_security/code_base_integration\n wait_for: text('Code Base Integration')\n description: Navigate to Code Base Integration list page\n - id: click-add-tab\n action: click\n selector: text('Add Code Base Integration')\n wait_for: textbox[name='Name']\n description: Click Add Code Base Integration to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: select-code_base\n action: select\n selector: listbox[name='Code Base']\n context: Code Base section\n value: \"{code_base}\"\n description: Select Code Base (references an existing code_base — dependency)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
220
220
  "code-base-integration/delete":
221
221
  "schema: urn:f5xc:console:workflow:v1\nid: code-base-integration-delete\nlabel: Delete Code Base Integration\nresource: code-base-integration\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-code-base-integration\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/api_security/code_base_integration\n wait_for: text('Code Base Integration')\n description: Navigate to Code Base Integration list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('Code Base Integration')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
222
222
  "code-base-integration/read":
@@ -320,7 +320,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
320
320
  "dns-zone/update":
321
321
  "schema: urn:f5xc:console:workflow:v1\nid: dns-zone-update\nlabel: Update DNS Zones\nresource: dns-zone\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-dns-zone\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/dns-management/manage/zone_management\n wait_for: text('DNS Zones')\n description: Navigate to DNS Zones list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('DNS Zones')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
322
322
  "endpoint/create":
323
- "schema: urn:f5xc:console:workflow:v1\nid: endpoint-create\nlabel: Create Endpoints\nresource: endpoint\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Endpoints name (lowercase alphanumeric and hyphens)\n example: example-endpoint\n protocol:\n required: false\n description: \"Server-required: Must be TCP or UDP\"\n default: TCP\n virtual_site_or_site_or_network:\n required: false\n description: Virtual-Site or Site or Network\n default: Virtual Site\n reference:\n required: true\n description: \"Feedback loop: 'Field Reference is required'\"\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/virtual_host/endpoints\n wait_for: text('Endpoints')\n description: Navigate to Endpoints list page\n - id: click-add-tab\n action: click\n selector: text('Add Endpoint')\n wait_for: textbox[name='Name']\n description: Click Add Endpoint to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: select-protocol\n action: select\n selector: listbox\n context: Protocol section\n value: \"{protocol}\"\n description: Select Protocol\n condition: params.protocol is set\n - id: select-virtual_site_or_site_or_network\n action: select\n selector: listbox\n context: Virtual-Site or Site or Network section\n value: \"{virtual_site_or_site_or_network}\"\n description: Select Virtual-Site or Site or Network\n condition: params.virtual_site_or_site_or_network is set\n - id: configure-reference\n action: click\n selector: text('Configure')\n context: Reference section\n description: Open the Reference configuration ('Configure' is a link, not a button)\n - id: fill-reference-value\n action: fill\n selector: textbox\n value: \"{reference}\"\n description: Enter the Reference value via the 'reference' parameter\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
323
+ 'schema: urn:f5xc:console:workflow:v1\nid: endpoint-create\nlabel: Create Endpoints\nresource: endpoint\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - "role_minimum: admin"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Endpoints name (lowercase alphanumeric and hyphens)\n example: example-endpoint\n protocol:\n required: false\n description: "Server-required: Must be TCP or UDP"\n default: TCP\n virtual_site_or_site_or_network:\n required: false\n description: Virtual-Site or Site or Network\n default: Virtual Site\n reference:\n required: true\n description: vsui autocomplete dropdown (VSUI-AUTOCOMPLETE-DROPDOWN) selecting a virtual-site, site, or network.\n Dependency resource.\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/virtual_host/endpoints\n wait_for: text(\'Endpoints\')\n description: Navigate to Endpoints list page\n - id: click-add-tab\n action: click\n selector: text(\'Add Endpoint\')\n wait_for: textbox[name=\'Name\']\n description: Click Add Endpoint to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name=\'Name\']\n value: "{name}"\n description: Enter Name\n - id: select-protocol\n action: select\n selector: listbox\n context: Protocol section\n value: "{protocol}"\n description: Select Protocol\n condition: params.protocol is set\n - id: select-virtual_site_or_site_or_network\n action: select\n selector: listbox\n context: Virtual-Site or Site or Network section\n value: "{virtual_site_or_site_or_network}"\n description: Select Virtual-Site or Site or Network\n condition: params.virtual_site_or_site_or_network is set\n - id: select-reference\n action: select\n selector: listbox[name=\'Reference\']\n context: Reference section\n value: "{reference}"\n description: Select Reference (references an existing virtual_site — dependency)\n - id: save\n action: click\n selector: "[class*=\'save-bt\'],[class*=\'submit-button\']"\n context: footer\n wait_for: text(\'{name}\')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - "resource_name_in_list: {name}"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: "2025.06"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n',
324
324
  "endpoint/delete":
325
325
  "schema: urn:f5xc:console:workflow:v1\nid: endpoint-delete\nlabel: Delete Endpoints\nresource: endpoint\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-endpoint\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/virtual_host/endpoints\n wait_for: text('Endpoints')\n description: Navigate to Endpoints list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('Endpoints')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
326
326
  "endpoint/read":
@@ -462,7 +462,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
462
462
  "mcn-secret-policy-rule/update":
463
463
  "schema: urn:f5xc:console:workflow:v1\nid: mcn-secret-policy-rule-update\nlabel: Update Secret Policy Rules\nresource: mcn-secret-policy-rule\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-mcn-secret-policy-rule\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-network-connect/manage/secrets/secret_policy_rules\n wait_for: text('Secret Policy Rules')\n description: Navigate to Secret Policy Rules list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('Secret Policy Rules')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
464
464
  "namespace/create":
465
- "schema: urn:f5xc:console:workflow:v1\nid: namespace-create\nlabel: Create My Namespaces\nresource: namespace\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: My Namespaces name (lowercase alphanumeric and hyphens)\n example: example-namespace\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/administration/personal-management/namespaces\n wait_for: text('My Namespaces')\n description: Navigate to My Namespaces list page\n - id: click-add-tab\n action: click\n selector: text('Add Namespace')\n wait_for: textbox[name='Name']\n description: Click Add Namespace to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
465
+ "schema: urn:f5xc:console:workflow:v1\nid: namespace-create\nlabel: Create My Namespaces\nresource: namespace\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: My Namespaces name (lowercase alphanumeric and hyphens)\n example: example-namespace\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/administration/personal-management/namespaces\n wait_for: text('My Namespaces')\n description: Navigate to My Namespaces list page\n - id: click-add-tab\n action: click\n selector: text('Add Namespace')\n wait_for: textbox[name='Namespace Name']\n description: Click Add Namespace to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Namespace Name']\n value: \"{name}\"\n description: Enter Namespace Name\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
466
466
  "namespace/delete":
467
467
  "schema: urn:f5xc:console:workflow:v1\nid: namespace-delete\nlabel: Delete My Namespaces\nresource: namespace\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-namespace\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/administration/personal-management/namespaces\n wait_for: text('My Namespaces')\n description: Navigate to My Namespaces list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('My Namespaces')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
468
468
  "namespace/read":
@@ -534,7 +534,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
534
534
  "openapi-file/update":
535
535
  "schema: urn:f5xc:console:workflow:v1\nid: openapi-file-update\nlabel: Update OpenAPI Files\nresource: openapi-file\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-openapi-file\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/files/openapi\n wait_for: text('OpenAPI Files')\n description: Navigate to OpenAPI Files list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('OpenAPI Files')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
536
536
  "origin-pool/create":
537
- 'schema: "urn:f5xc:console:workflow:v1"\nid: "origin-pool-create"\nlabel: "Create Origin Pool"\nresource: "origin-pool"\noperation: "create"\n\npreconditions:\n - "user_logged_in"\n - "namespace_selected"\n - "role_minimum: admin"\n\nparams:\n namespace:\n required: true\n description: "Target namespace"\n example: "demo"\n name:\n required: true\n description: "Origin pool name (lowercase alphanumeric and hyphens)"\n example: "example-origin-pool"\n origin_server:\n required: true\n description: "DNS hostname of the origin server (must be hostname format, not IP)"\n example: "app.example.com"\n port:\n required: true\n type: "number"\n description: "Port number for the origin server"\n example: 8080\n health_check:\n required: false\n description: "Existing health check name to attach"\n example: "example-health-check"\n\nsteps:\n - id: "navigate-to-list"\n action: "navigate"\n url: "/web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/load_balancers/origin_pools"\n wait_for: "text(\'Origin Pools\')"\n description: "Navigate to Origin Pools list page within Web App & API Protection workspace"\n\n - id: "click-add-tab"\n action: "click"\n selector: "tab:text(\'Add Origin Pool\')"\n wait_for: "textbox[name=\'Name\']"\n description: "Click the Add Origin Pool tab to open the inline create form"\n note: "This is a tab element, not a button"\n\n - id: "fill-name"\n action: "fill"\n selector: "textbox[name=\'Name\']"\n value: "{name}"\n description: "Enter the origin pool name in the Name textbox"\n\n - id: "add-origin-server"\n action: "click"\n selector: "button:text(\'Add Item\')"\n context: "Origin Servers section"\n wait_for: "text(\'DNS Name\')"\n description: "Click Add Item to open the Origin Server sub-form (Type defaults to \'Public DNS Name of Origin Server\')"\n then:\n - id: "fill-origin-server"\n action: "fill"\n selector: "textbox[name=\'DNS Name\']"\n context: "Origin Server sub-form"\n value: "{origin_server}"\n description: "Enter the origin server DNS hostname in the *DNS Name field. Bare \'textbox\' is ambiguous (the sub-form has 18 inputs incl. search boxes) — target the DNS Name field by label."\n\n - id: "apply-origin-server"\n action: "click"\n selector: "button:text(\'Apply\')"\n description: "Confirm origin server entry (closes the sub-form, returns to the pool form)"\n\n - id: "fill-port"\n action: "fill"\n selector: "textbox[name=\'Port\']"\n value: "{port}"\n description: "Enter the origin pool *Port (required, pool-level field on the main form — distinct from the origin-server sub-form). Without this, save fails validation."\n\n - id: "attach-health-check"\n condition: "params.health_check is set"\n action: "click"\n selector: "button:text(\'Add Item\')"\n context: "Health Checks section"\n wait_for: "listbox"\n description: "Click Add Item in the Health Checks section to add a health check"\n then:\n - id: "select-health-check"\n action: "select"\n selector: "listbox"\n context: "Health Check selector"\n value: "{health_check}"\n description: "Select the named health check from the dropdown"\n\n - id: "apply-health-check"\n action: "click"\n selector: "button:text(\'Apply\')"\n description: "Confirm health check selection"\n\n - id: "save"\n action: "click"\n selector: "[class*=\'save-bt\']"\n context: "footer"\n wait_for: "text(\'{name}\')"\n wait_timeout_ms: 30000\n description: "Click the footer save button. NOTE: \'Add Origin Pool\' text matches TWO buttons (the header tab + the footer save) — target the footer save button by its \'save-bt\'/\'save-btn\' class to avoid clicking the tab."\n\n - id: "verify-created"\n action: "assert"\n selector: "text(\'{name}\')"\n description: "Verify the resource was created by checking the name appears in the list"\n\npostconditions:\n - "resource_list_page_visible"\n - "resource_name_in_list: {name}"\n\nmetadata:\n confidence: "validated"\n discovered_at: "2026-06-15"\n validated_at: "2026-06-15"\n console_version: "2025.06"\n estimated_duration_seconds: 30\n notes: "Selectors use aria/text-based patterns validated against live console. No data-testid attributes exist."\n',
537
+ "schema: urn:f5xc:console:workflow:v1\nid: origin-pool-create\nlabel: Create Origin Pools\nresource: origin-pool\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Origin Pools name (lowercase alphanumeric and hyphens)\n example: example-origin-pool\n origin_servers:\n required: true\n description: 'Default type: Public DNS Name. The sub-form requires \"DNS Name\" (FQDN of the origin).'\n port:\n required: false\n description: Port\n default: 443\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/load_balancers/origin_pools\n wait_for: text('Origin Pools')\n description: Navigate to Origin Pools list page\n - id: click-add-tab\n action: click\n selector: text('Add Origin Pool')\n wait_for: textbox[name='Name']\n description: Click Add Origin Pool to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: \"Enter Name (pattern: ^[a-z][a-z0-9-]*$), max 64 chars\"\n - id: add-origin_servers\n action: click\n selector: button:text('Add Item')\n context: Origin Servers section\n description: \"Add Origin Servers entry (default type: Public DNS Name)\"\n then:\n - id: fill-origin_servers-value\n action: fill\n selector: textbox[name='DNS Name']\n value: \"{origin_servers}\"\n description: Enter DNS Name for the Origin Servers entry\n - id: apply-origin_servers\n action: click\n selector: button:text('Apply')\n description: Confirm Origin Servers entry\n - id: fill-port\n action: fill\n selector: spinbutton[name='Port']\n value: \"{port}\"\n description: \"Set Port (default: 443)\"\n condition: params.port is set\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
538
538
  "origin-pool/delete":
539
539
  "schema: urn:f5xc:console:workflow:v1\nid: origin-pool-delete\nlabel: Delete Origin Pools\nresource: origin-pool\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-origin-pool\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/web-app-and-api-protection/namespaces/{namespace}/manage/load_balancers/origin_pools\n wait_for: text('Origin Pools')\n description: Navigate to Origin Pools list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('Origin Pools')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
540
540
  "origin-pool/read":
@@ -702,7 +702,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
702
702
  "subnet/update":
703
703
  "schema: urn:f5xc:console:workflow:v1\nid: subnet-update\nlabel: Update Subnets\nresource: subnet\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-subnet\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-network-connect/manage/networking/legacy_network_configuration/subnets\n wait_for: text('Subnets')\n description: Navigate to Subnets list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('Subnets')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
704
704
  "tcp-load-balancer/create":
705
- 'schema: "urn:f5xc:console:workflow:v1"\nid: "tcp-load-balancer-create"\nlabel: "Create TCP Load Balancer"\nresource: "tcp-load-balancer"\noperation: "create"\n\npreconditions:\n - "user_logged_in"\n - "namespace_selected"\n - "role_minimum: admin"\n\nparams:\n namespace:\n required: true\n description: "Target namespace"\n example: "demo"\n name:\n required: true\n description: "TCP load balancer name (lowercase alphanumeric and hyphens)"\n example: "example-tcp-lb"\n listen_port:\n required: false\n type: "number"\n description: "TCP port to listen on (default 0)"\n example: 443\n origin_pool:\n required: false\n description: "Existing origin pool name to attach"\n example: "example-origin-pool"\n\nsteps:\n - id: "navigate-to-list"\n action: "navigate"\n url: "/web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/load_balancers/tcp_loadbalancers"\n wait_for: "text(\'TCP Load Balancers\')"\n description: "Navigate to TCP Load Balancers list page within Multi-Cloud App Connect workspace"\n\n - id: "click-add-tab"\n action: "click"\n selector: "tab:text(\'Add TCP Load Balancer\')"\n wait_for: "textbox[name=\'Name\']"\n description: "Click the Add TCP Load Balancer tab to open the inline create form"\n\n - id: "fill-name"\n action: "fill"\n selector: "textbox[name=\'Name\']"\n value: "{name}"\n description: "Enter the TCP load balancer name in the Name textbox"\n\n - id: "attach-origin-pool"\n condition: "params.origin_pool is set"\n action: "click"\n selector: "button:text(\'Add Item\')"\n context: "Origin Pools section"\n wait_for: "listbox"\n description: "Click Add Item in the Origin Pools section to add an origin pool"\n then:\n - id: "select-pool"\n action: "select"\n selector: "listbox"\n context: "Origin Pool selector"\n value: "{origin_pool}"\n description: "Select the named origin pool from the dropdown"\n\n - id: "apply-pool"\n action: "click"\n selector: "button:text(\'Apply\')"\n description: "Confirm origin pool selection"\n\n - id: "save"\n action: "click"\n selector: "button:text(\'Add TCP Load Balancer\')"\n context: "footer"\n wait_for: "text(\'{name}\')"\n wait_timeout_ms: 30000\n description: "Click the Add TCP Load Balancer button in the form footer to save"\n\n - id: "verify-created"\n action: "assert"\n selector: "text(\'{name}\')"\n description: "Verify the resource was created by checking the name appears in the list"\n\npostconditions:\n - "resource_list_page_visible"\n - "resource_name_in_list: {name}"\n\nmetadata:\n confidence: "validated"\n discovered_at: "2026-06-15"\n validated_at: "2026-06-15"\n console_version: "2025.06"\n estimated_duration_seconds: 30\n notes: "Selectors use aria/text-based patterns validated against live console. No data-testid attributes exist."\n',
705
+ "schema: urn:f5xc:console:workflow:v1\nid: tcp-load-balancer-create\nlabel: Create TCP Load Balancers\nresource: tcp-load-balancer\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: TCP Load Balancers name (lowercase alphanumeric and hyphens)\n example: example-tcp-load-balancer\n domains:\n required: true\n description: TCP LB Domains table starts EMPTY (0 items) — needs Add Item click before filling. Fills via ngx-datatable\n grid real-typing after row creation.\n listen_port:\n required: false\n description: vsui listbox dropdown. Verified from live form screenshot.\n default: Listen Port\n example: Listen Port\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/load_balancers/tcp_loadbalancers\n wait_for: text('TCP Load Balancers')\n description: Navigate to TCP Load Balancers list page\n - id: click-add-tab\n action: click\n selector: text('Add TCP Load Balancer')\n wait_for: textbox[name='Name']\n description: Click Add TCP Load Balancer to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: add-item-domains\n action: click\n selector: button:text('Add Item')\n context: Domains table\n description: Add a row to the Domains table (starts empty unlike most tables)\n - id: fill-domains\n action: fill\n selector: ngx-datatable input.form-control\n context: Domains table\n value: \"{domains}\"\n description: Enter Domains in the newly-added table row (Add Item clicked above)\n - id: fill-listen_port\n action: fill\n selector: spinbutton[name='Listen Port']\n value: \"{listen_port}\"\n description: Set Listen Port\n - id: select-listen_port\n action: select\n selector: listbox\n context: Listen Port section\n value: \"{listen_port}\"\n description: Select Listen Port (Listen Port | Port Ranges)\n condition: params.listen_port is set\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
706
706
  "tcp-load-balancer/delete":
707
707
  "schema: urn:f5xc:console:workflow:v1\nid: tcp-load-balancer-delete\nlabel: Delete TCP Load Balancers\nresource: tcp-load-balancer\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-tcp-load-balancer\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-app-connect/namespaces/{namespace}/manage/load_balancers/tcp_loadbalancers\n wait_for: text('TCP Load Balancers')\n description: Navigate to TCP Load Balancers list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('TCP Load Balancers')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
708
708
  "tcp-load-balancer/read":
@@ -790,7 +790,7 @@ export const CONSOLE_CATALOG_DATA: ConsoleCatalogData = {
790
790
  "virtual-network/update":
791
791
  "schema: urn:f5xc:console:workflow:v1\nid: virtual-network-update\nlabel: Update Virtual Networks\nresource: virtual-network\noperation: update\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-virtual-network\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/multi-cloud-network-connect/manage/networking/legacy_network_configuration/virtual_networks\n wait_for: text('Virtual Networks')\n description: Navigate to Virtual Networks list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Manage Configuration')\n description: Open the row kebab menu\n - id: open-configuration\n action: click\n selector: option:text('Manage Configuration')\n wait_for: button:text('Edit Configuration')\n wait_timeout_ms: 15000\n description: Open the read-only Manage Configuration view\n - id: enter-edit-mode\n action: click\n selector: button:text('Edit Configuration')\n wait_for: \"[class*='save-bt'],[class*='submit-button']\"\n wait_timeout_ms: 20000\n description: Click 'Edit Configuration' to switch from read-only view into the editable form\n - id: modify-fields\n action: wait\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n description: The agent fills the specific fields the user asked to modify (dynamic, not pre-generated)\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('Virtual Networks')\n wait_timeout_ms: 30000\n description: Save/submit changes (union selector matches save-bt OR submit-button)\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
792
792
  "virtual-site/create":
793
- "schema: urn:f5xc:console:workflow:v1\nid: virtual-site-create\nlabel: Create Virtual Sites\nresource: virtual-site\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Virtual Sites name (lowercase alphanumeric and hyphens)\n example: example-virtual-site\n site_selector:\n required: true\n description: \"Server-required: Field should be not nil\"\n site_type:\n required: false\n description: \"Required: Site Type (RE = Regional Edge, CE = Customer Edge). Select from the listbox.\"\n default: RE\n example: RE\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/shared-configuration/manage/virtual_sites\n wait_for: text('Virtual Sites')\n description: Navigate to Virtual Sites list page\n - id: click-add-tab\n action: click\n selector: text('Add Virtual Site')\n wait_for: textbox[name='Name']\n description: Click Add Virtual Site to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: configure-site_selector\n action: click\n selector: text('Configure')\n context: Site Selector section\n description: Open the Site Selector configuration ('Configure' is a link, not a button)\n - id: fill-site_selector-value\n action: fill\n selector: textbox\n value: \"{site_selector}\"\n description: Enter the Site Selector value via the 'site_selector' parameter\n - id: select-site_type\n action: select\n selector: listbox\n context: Site Type section\n value: \"{site_type}\"\n description: Select Site Type (RE | CE)\n condition: params.site_type is set\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
793
+ "schema: urn:f5xc:console:workflow:v1\nid: virtual-site-create\nlabel: Create Virtual Sites\nresource: virtual-site\noperation: create\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\nparams:\n namespace:\n required: true\n description: Target namespace\n example: demo\n name:\n required: true\n description: Virtual Sites name (lowercase alphanumeric and hyphens)\n example: example-virtual-site\n site_selector_expression:\n required: true\n description: 'CDK-portal label-selector: Add Label type key prefix (e.g. \"ves\") → select from portal → operator →\n value. Verified live via label_select tool.'\n site_type:\n required: false\n description: \"Required: Site Type (RE = Regional Edge, CE = Customer Edge). Select from the listbox.\"\n default: RE\n example: RE\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/shared-configuration/manage/virtual_sites\n wait_for: text('Virtual Sites')\n description: Navigate to Virtual Sites list page\n - id: click-add-tab\n action: click\n selector: text('Add Virtual Site')\n wait_for: textbox[name='Name']\n description: Click Add Virtual Site to open the create form\n - id: fill-name\n action: fill\n selector: textbox[name='Name']\n value: \"{name}\"\n description: Enter Name\n - id: add-label-site_selector_expression\n action: click\n selector: text('Add Label')\n context: Site Selector Expression section\n description: Open the label-selector typeahead for Site Selector Expression\n - id: select-label-key-site_selector_expression\n action: selectLabel\n selector: input[placeholder='Type to search']\n value: \"{site_selector_expression}\"\n description: Select label key for Site Selector Expression from the CDK portal (type prefix → pick option)\n - id: select-site_type\n action: select\n selector: listbox\n context: Site Type section\n value: \"{site_type}\"\n description: Select Site Type (RE | CE)\n condition: params.site_type is set\n - id: save\n action: click\n selector: \"[class*='save-bt'],[class*='submit-button']\"\n context: footer\n wait_for: text('{name}')\n wait_timeout_ms: 30000\n description: Save/submit the form (union selector matches save-bt OR submit-button)\npostconditions:\n - resource_list_page_visible\n - \"resource_name_in_list: {name}\"\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
794
794
  "virtual-site/delete":
795
795
  "schema: urn:f5xc:console:workflow:v1\nid: virtual-site-delete\nlabel: Delete Virtual Sites\nresource: virtual-site\noperation: delete\npreconditions:\n - user_logged_in\n - namespace_selected\n - \"role_minimum: admin\"\n - \"resource_exists: {name}\"\nparams:\n namespace:\n required: true\n example: demo\n name:\n required: true\n example: example-virtual-site\nsteps:\n - id: navigate-to-list\n action: navigate\n url: /web/workspaces/shared-configuration/manage/virtual_sites\n wait_for: text('Virtual Sites')\n description: Navigate to Virtual Sites list page\n - id: scroll-actions-into-view\n action: scroll\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n description: Scroll the Actions column into view for the target row\n - id: open-row-actions\n action: click\n selector: row:has-text('{name}') >> [ves-e2e-test='row-action-dropdown']\n wait_for: option:text('Delete')\n description: Open the row kebab menu\n - id: click-delete\n action: click\n selector: option:text('Delete')\n wait_for: button:text('Delete')\n description: Click Delete option\n - id: confirm-delete\n action: click\n selector: button:text('Delete')\n wait_for: text('Virtual Sites')\n wait_timeout_ms: 15000\n description: Confirm deletion\npostconditions:\n - resource_removed_from_list\n - list_page_visible\nmetadata:\n confidence: inferred\n discovered_at: 2026-06-24\n console_version: \"2025.06\"\n notes: Auto-generated by scripts/generate-workflows.ts from api-specs-enriched field metadata.\n",
796
796
  "virtual-site/read":
@@ -745,6 +745,8 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
745
745
  required: true,
746
746
  add_action: "Add Item",
747
747
  form_section: "origin-servers",
748
+ sub_field_label: "DNS Name",
749
+ description: 'Default type: Public DNS Name. The sub-form requires "DNS Name" (FQDN of the origin).',
748
750
  item_types: {
749
751
  public_name: {
750
752
  label: "Public DNS Name",
@@ -871,16 +873,25 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
871
873
  },
872
874
  },
873
875
  tcp_loadbalancer: {
876
+ "metadata.name": {
877
+ widget_type: "textbox",
878
+ label: "Name",
879
+ required: true,
880
+ form_section: "metadata",
881
+ },
874
882
  domains: {
875
883
  widget_type: "table",
876
884
  label: "Domains",
877
- add_action: "Add Item",
885
+ required: true,
886
+ add_item_first: true,
878
887
  form_section: "basic-configuration",
888
+ description:
889
+ "TCP LB Domains table starts EMPTY (0 items) — needs Add Item click before filling. Fills via ngx-datatable grid real-typing after row creation.",
879
890
  },
880
891
  listen_port: {
881
892
  widget_type: "spinbutton",
882
893
  label: "Listen Port",
883
- default: 0,
894
+ required: true,
884
895
  form_section: "basic-configuration",
885
896
  },
886
897
  no_sni: {
@@ -921,10 +932,12 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
921
932
  },
922
933
  "spec.port_choice": {
923
934
  required: true,
924
- label: "Port Choice",
935
+ label: "Listen Port",
925
936
  form_section: "basic-configuration",
926
- description: "Server-required: Field should be not nil",
927
- widget_type: "configurable",
937
+ default: "Listen Port",
938
+ options: ["Listen Port", "Port Ranges"],
939
+ description: "vsui listbox dropdown. Verified from live form screenshot.",
940
+ widget_type: "listbox",
928
941
  },
929
942
  },
930
943
  healthcheck: {
@@ -1089,10 +1102,11 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
1089
1102
  namespace: {
1090
1103
  "metadata.name": {
1091
1104
  widget_type: "textbox",
1092
- label: "Name",
1105
+ label: "Namespace Name",
1093
1106
  required: true,
1094
1107
  form_section: "metadata",
1095
1108
  notes: "System-level resource — no namespace scope",
1109
+ description: 'Live form uses aria-label "Namespace Name" (not standard "Name")',
1096
1110
  },
1097
1111
  "metadata.description": {
1098
1112
  widget_type: "textbox",
@@ -1707,11 +1721,13 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
1707
1721
  form_section: "metadata",
1708
1722
  },
1709
1723
  "spec.site_selector": {
1710
- widget_type: "configurable",
1711
- label: "Site Selector",
1724
+ widget_type: "label-selector",
1725
+ label: "Site Selector Expression",
1712
1726
  form_section: "selector",
1713
1727
  required: true,
1714
- description: "Server-required: Field should be not nil",
1728
+ add_action: "Add Label",
1729
+ description:
1730
+ 'CDK-portal label-selector: Add Label → type key prefix (e.g. "ves") → select from portal → operator → value. Verified live via label_select tool.',
1715
1731
  },
1716
1732
  "spec.site_type": {
1717
1733
  widget_type: "listbox",
@@ -2669,12 +2685,13 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
2669
2685
  form_section: "metadata",
2670
2686
  },
2671
2687
  "spec.code_base": {
2672
- widget_type: "configurable",
2688
+ widget_type: "listbox",
2673
2689
  label: "Code Base",
2674
2690
  required: true,
2675
- default: "",
2676
2691
  form_section: "integration-data",
2677
- description: "'Field Code Base in Integration Data is required'",
2692
+ resource_type: "code_base",
2693
+ description:
2694
+ 'A vsui "Select item" listbox (not a configurable). Selects an existing code-base integration. Verified from live form screenshot.',
2678
2695
  },
2679
2696
  },
2680
2697
  container_registry: {
@@ -3037,11 +3054,13 @@ export const CONSOLE_FIELD_METADATA: ConsoleFieldMetadataData = {
3037
3054
  form_section: "origin-server",
3038
3055
  },
3039
3056
  "spec.reference": {
3040
- widget_type: "configurable",
3057
+ widget_type: "listbox",
3041
3058
  label: "Reference",
3042
3059
  required: true,
3060
+ resource_type: "virtual_site",
3043
3061
  form_section: "spec",
3044
- description: "Feedback loop: 'Field Reference is required'",
3062
+ description:
3063
+ "vsui autocomplete dropdown (VSUI-AUTOCOMPLETE-DROPDOWN) selecting a virtual-site, site, or network. Dependency resource.",
3045
3064
  },
3046
3065
  "spec.network_type": {
3047
3066
  widget_type: "listbox",
@@ -0,0 +1,147 @@
1
+ # xcsh Chrome Extension Bridge — Tool API Reference
2
+
3
+ > Surfaced via `xcsh://extension`. The xcsh assistant drives the browser through
4
+ > the Chrome extension bridge (native messaging → service worker → CDP). Knowing
5
+ > which tool to use — and which to avoid — for each interaction is essential for
6
+ > deterministic, non-fragile automation.
7
+
8
+ ---
9
+
10
+ ## Deterministic click path (PREFERRED)
11
+
12
+ ### `click_element { js, wait_ms? }`
13
+ **Use for:** clicking any element that exists in the page's main document.
14
+
15
+ `js` is a JavaScript expression that returns the target **Element** (or null). The
16
+ extension resolves geometry from the renderer (`DOM.getContentQuads` — CSS viewport
17
+ px with transforms/zoom/DPR baked in) and **hit-tests** the point
18
+ (`document.elementFromPoint`) before dispatching the trusted click. On occlusion it
19
+ re-scrolls once, then **fails loudly** naming the occluder — never a silent mis-click.
20
+ `wait_ms` polls for async elements (CDK portals, lazy content).
21
+
22
+ **Guarantees:**
23
+ - Deterministic across window sizes and zoom levels (layout-engine coords, not JS rects).
24
+ - Catches overlays (CDK portals, dialogs) and tells you what's in the way.
25
+ - Holds the element handle atomically across scroll → measure → verify → click.
26
+
27
+ **Build the `js` arg via** `buildElementResolverScript(selector)` (the same catalogue
28
+ selector grammar: `text('…')`, `role:text('…')`, `role[name='…']`, CSS, row-scoped `>>`)
29
+ so the click resolves the same element the workflow YAML names.
30
+
31
+ ---
32
+
33
+ ### `click { ref }`
34
+ Use when you already have an AX `ref` handle (from `read_ax`). Routes through
35
+ `click_element` internally — same determinism guarantees.
36
+
37
+ ### `click_xy { x, y }`
38
+ **Last-resort.** Only for viewport points with no backing element (e.g. a computed
39
+ portal-overlay coordinate, or testing a specific pixel). Skips hit-testing.
40
+
41
+ ---
42
+
43
+ ## CDK-portal / typeahead (REQUIRED for CDK portals)
44
+
45
+ ### `label_select { selector, value, label_value?, wait_ms? }`
46
+ **Use for:** any CDK-portal typeahead (`.cdk-overlay-container`): label-selector
47
+ key/operator/value steps, vsui "Type to search" inputs.
48
+
49
+ The extension keeps the input **focused throughout** (uses plain `Runtime.evaluate`,
50
+ NOT `evaluateWithRecovery` which detaches the debugger and kills focus). It:
51
+ 1. Clicks the input (focus).
52
+ 2. Types `value` via `Input.insertText` (keeps focus).
53
+ 3. Polls the CDK portal for a matching option (`span` in `.cdk-overlay-container`).
54
+ 4. Clicks the option via trusted CDP.
55
+ 5. If `label_value` is provided, enters it into the value field that appears (uses the
56
+ secret-textarea real-typing path if it's a `<textarea>`).
57
+
58
+ **Why mandatory for CDK portals:** `javascript_tool` routes through `evaluateWithRecovery`
59
+ which DETACHES the CDP debugger on timeout — killing input focus and closing the portal.
60
+ `label_select` uses plain `Runtime.evaluate` and holds focus end-to-end.
61
+
62
+ ---
63
+
64
+ ## Input (trusted)
65
+
66
+ ### `type_text { text }`
67
+ Inserts text via `Input.insertText` into the currently-focused element. Fires genuine
68
+ trusted input events that Angular's ControlValueAccessor and vsui pick up. Use this
69
+ (or `fill` via page-actions) for text fields, not programmatic value setting.
70
+
71
+ ### `key_press { key }`
72
+ Dispatches a trusted `Input.dispatchKeyEvent` (keyDown + keyUp) for the named key
73
+ (e.g. `"Enter"`, `"Tab"`, `"Escape"`, `"Backspace"`).
74
+
75
+ ### `form_input { ref, value }`
76
+ Sets a form field's value via `Runtime.callFunctionOn` — the Angular-compat path that
77
+ bypasses vsui's value-descriptor patching. Use when `fill` isn't available.
78
+
79
+ ---
80
+
81
+ ## DOM inspection (caution: defocuses active input)
82
+
83
+ ### `javascript_tool { code }`
84
+ Evaluates arbitrary JavaScript and returns the result. Routes through
85
+ `evaluateWithRecovery` — **IMPORTANT: this defocuses the currently-focused input and
86
+ closes any open CDK portal / vsui dropdown.** Never call it between a typeahead open
87
+ and a portal selection. Safe for reading DOM state BEFORE opening a typeahead.
88
+
89
+ ### `read_ax { }` / `find { selector }`
90
+ Reads the accessibility tree or finds AX nodes by selector. Both freeze the MV3
91
+ service worker on heavy F5 XC SPA pages (30s+) — prefer `javascript_tool` for quick
92
+ DOM queries. `read_ax` is usable on lighter pages.
93
+
94
+ ### `get_page_text { }`
95
+ Returns the visible text content of the current page.
96
+
97
+ ---
98
+
99
+ ## Navigation
100
+
101
+ ### `navigate { url }`
102
+ Navigates to a URL. Auto-accepts `beforeunload` ("Leave site?") dialogs — so a dirty
103
+ form's dialog does NOT block navigation. Wait for the target page to settle before
104
+ querying the DOM.
105
+
106
+ ### `wait_for { selector, context?, timeoutMs? }` / `assert_text { selector, expected }`
107
+ Wait for an element to appear / assert text. Use these as post-navigation settle points.
108
+
109
+ ---
110
+
111
+ ## Tab management
112
+
113
+ ### `tabs_list`, `tabs_create`, `tabs_close`
114
+ Enumerate, open, and close browser tabs. Useful for multi-tab flows or checking that a
115
+ navigation landed on the intended page.
116
+
117
+ ---
118
+
119
+ ## Screenshot (avoid in automation)
120
+
121
+ ### `screenshot { }` → base64 JPEG
122
+ Captures a scaled canvas screenshot. **Avoid in automation loops** — `captureVisibleTab`
123
+ freezes the MV3 service worker event loop on retina Mac displays, blocking all
124
+ subsequent bridge requests for several seconds.
125
+
126
+ ---
127
+
128
+ ## Debugging tools
129
+
130
+ ### `read_console { pattern? }` / `read_network { pattern? }`
131
+ Read Chrome DevTools console logs or network requests. Use for debugging — not required
132
+ in deterministic create/read/update/delete flows.
133
+
134
+ ---
135
+
136
+ ## Summary: which tool for which job
137
+
138
+ | Situation | Tool |
139
+ |-----------|------|
140
+ | Click a button/tab/link in the page | `click_element` (buildElementResolverScript) |
141
+ | Click inside a CDK portal option | `label_select` (the ONLY reliable path) |
142
+ | Fill a text input | page-actions `fill` → `type_text` under the hood |
143
+ | Select from a vsui listbox | page-actions `selectOption` → `click_element` + poll |
144
+ | Need current DOM state | `javascript_tool` (but check there's no open typeahead first) |
145
+ | Navigate between pages | `navigate` |
146
+ | Verify something appeared | `wait_for` / `assert_text` |
147
+ | Raw coordinates only | `click_xy` (last resort) |
@@ -374,6 +374,11 @@ export class CatalogWorkflowRunnerTool
374
374
  await page.selectOption(resolvedSelector, resolvedValue ?? "", resolvedContext);
375
375
  break;
376
376
  }
377
+ case "selectLabel": {
378
+ if (!resolvedSelector) throw new ToolError(`Step "${step.id}": selectLabel requires selector`);
379
+ await (page as any).selectLabel(resolvedSelector, resolvedValue ?? "", resolvedContext);
380
+ break;
381
+ }
377
382
  case "assert": {
378
383
  if (!resolvedSelector) throw new ToolError(`Step "${step.id}": assert requires selector`);
379
384
  if (!resolvedExpected) throw new ToolError(`Step "${step.id}": assert requires expected_text`);