adhdev 0.1.53 → 0.1.54

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.
Files changed (68) hide show
  1. package/dist/index.js +1870 -819
  2. package/package.json +1 -1
  3. package/providers/_builtin/CONTRIBUTING.md +141 -0
  4. package/providers/_builtin/README.md +51 -0
  5. package/providers/_builtin/_helpers/index.js +188 -0
  6. package/providers/_builtin/acp/agentpool/provider.js +59 -0
  7. package/providers/_builtin/acp/amp/provider.js +61 -0
  8. package/providers/_builtin/acp/auggie/provider.js +60 -0
  9. package/providers/_builtin/acp/autodev/provider.js +59 -0
  10. package/providers/_builtin/acp/autohand/provider.js +59 -0
  11. package/providers/_builtin/acp/blackbox-ai/provider.js +59 -0
  12. package/providers/_builtin/acp/claude-agent/provider.js +61 -0
  13. package/providers/_builtin/acp/cline-acp/provider.js +62 -0
  14. package/providers/_builtin/acp/code-assistant/provider.js +59 -0
  15. package/providers/_builtin/acp/codebuddy/provider.js +59 -0
  16. package/providers/_builtin/acp/codex-cli/provider.js +11 -1
  17. package/providers/_builtin/acp/corust-agent/provider.js +59 -0
  18. package/providers/_builtin/acp/crow-cli/provider.js +59 -0
  19. package/providers/_builtin/acp/cursor-acp/provider.js +59 -0
  20. package/providers/_builtin/acp/deepagents/provider.js +59 -0
  21. package/providers/_builtin/acp/dimcode/provider.js +58 -0
  22. package/providers/_builtin/acp/docker-cagent/provider.js +59 -0
  23. package/providers/_builtin/acp/factory-droid/provider.js +59 -0
  24. package/providers/_builtin/acp/fast-agent/provider.js +59 -0
  25. package/providers/_builtin/acp/fount/provider.js +59 -0
  26. package/providers/_builtin/acp/gemini-cli/provider.js +104 -0
  27. package/providers/_builtin/acp/github-copilot/provider.js +60 -0
  28. package/providers/_builtin/acp/goose/provider.js +37 -5
  29. package/providers/_builtin/acp/junie/provider.js +62 -0
  30. package/providers/_builtin/acp/kilo/provider.js +59 -0
  31. package/providers/_builtin/acp/kimi-cli/provider.js +63 -0
  32. package/providers/_builtin/acp/kiro-cli/provider.js +59 -0
  33. package/providers/_builtin/acp/minion-code/provider.js +59 -0
  34. package/providers/_builtin/acp/mistral-vibe/provider.js +63 -0
  35. package/providers/_builtin/acp/nova/provider.js +59 -0
  36. package/providers/_builtin/acp/openclaw/provider.js +59 -0
  37. package/providers/_builtin/acp/opencode/provider.js +34 -6
  38. package/providers/_builtin/acp/openhands/provider.js +59 -0
  39. package/providers/_builtin/acp/pi-acp/provider.js +59 -0
  40. package/providers/_builtin/acp/qoder/provider.js +58 -0
  41. package/providers/_builtin/acp/qwen-code/provider.js +61 -0
  42. package/providers/_builtin/acp/stakpak/provider.js +59 -0
  43. package/providers/_builtin/acp/vtcode/provider.js +59 -0
  44. package/providers/_builtin/cli/claude-cli/provider.js +3 -0
  45. package/providers/_builtin/cli/codex-cli/provider.js +3 -0
  46. package/providers/_builtin/cli/gemini-cli/provider.js +3 -0
  47. package/providers/_builtin/ide/kiro/provider.js +6 -2
  48. package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
  49. package/providers/_builtin/ide/pearai/provider.js +12 -0
  50. package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
  51. package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
  52. package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
  53. package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +32 -4
  54. package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
  55. package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
  56. package/providers/_builtin/ide/trae/scripts/send_message.js +53 -3
  57. package/providers/_builtin/validate.js +156 -0
  58. package/dist/node_datachannel-LPY6EJH5.node +0 -0
  59. package/providers/_builtin/ide/cursor/provider.js.backup +0 -116
  60. package/providers/_builtin/ide/cursor/provider.js.bak +0 -127
  61. package/providers/_builtin/ide/cursor/scripts_backup/focus_editor.js +0 -20
  62. package/providers/_builtin/ide/cursor/scripts_backup/list_chats.js +0 -111
  63. package/providers/_builtin/ide/cursor/scripts_backup/new_session.js +0 -62
  64. package/providers/_builtin/ide/cursor/scripts_backup/open_panel.js +0 -31
  65. package/providers/_builtin/ide/cursor/scripts_backup/read_chat.js +0 -433
  66. package/providers/_builtin/ide/cursor/scripts_backup/resolve_action.js +0 -90
  67. package/providers/_builtin/ide/cursor/scripts_backup/send_message.js +0 -86
  68. package/providers/_builtin/ide/cursor/scripts_backup/switch_session.js +0 -63
@@ -1,116 +0,0 @@
1
- /**
2
- * Cursor — IDE Provider
3
- *
4
- * Category: ide (workbench CDP session — iframe 없음)
5
- *
6
- * 특이 사항:
7
- * - .composer-view 컨테이너 (data-composer-id, data-composer-status)
8
- * - contenteditable[role="textbox"] 또는 textarea.native-input
9
- * - Fiber summaries 기반 제목 추출
10
- * - 6단계 모달 감지 계층 (overlay → run-command-review → ancestor → dialog → inline → quickInput → AI msg)
11
- * - resolve_modal 사용
12
- *
13
- * @type {import('../../../src/providers/contracts').ProviderModule}
14
- */
15
- const fs = require('fs');
16
- const path = require('path');
17
-
18
- const SCRIPTS_DIR = path.join(__dirname, 'scripts');
19
-
20
- function loadScript(name) {
21
- try {
22
- return fs.readFileSync(path.join(SCRIPTS_DIR, name), 'utf8');
23
- } catch {
24
- return null;
25
- }
26
- }
27
-
28
- module.exports = {
29
- type: 'cursor',
30
- name: 'Cursor',
31
- category: 'ide',
32
-
33
- // ─── IDE 인프라 ───
34
- displayName: 'Cursor',
35
- icon: '⚡',
36
- cli: 'cursor',
37
- cdpPorts: [9333, 9334],
38
- processNames: {
39
- darwin: 'Cursor',
40
- win32: ['Cursor.exe'],
41
- },
42
- paths: {
43
- darwin: ['/Applications/Cursor.app'],
44
- win32: ['C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe'],
45
- linux: ['/opt/Cursor', '/usr/share/cursor'],
46
- },
47
-
48
- inputMethod: 'cdp-type-and-send',
49
- inputSelector: '[contenteditable="true"][role="textbox"]',
50
-
51
- scripts: {
52
- readChat() {
53
- return loadScript('read_chat.js');
54
- },
55
-
56
- sendMessage(text) {
57
- const script = loadScript('send_message.js');
58
- if (!script) return null;
59
- return script.replace(/\$\{\s*MESSAGE\s*\}/g, JSON.stringify(text));
60
- },
61
-
62
- listSessions() {
63
- return loadScript('list_chats.js');
64
- },
65
-
66
- switchSession(sessionId) {
67
- const script = loadScript('switch_session.js');
68
- if (!script) return null;
69
- return script.replace(/\$\{\s*SESSION_ID\s*\}/g, JSON.stringify(sessionId));
70
- },
71
-
72
- newSession() {
73
- return loadScript('new_session.js');
74
- },
75
-
76
- resolveModal(buttonText) {
77
- const script = loadScript('resolve_modal.js');
78
- if (!script) return null;
79
- return script.replace(/\$\{\s*BUTTON_TEXT\s*\}/g, JSON.stringify(buttonText));
80
- },
81
-
82
- focusEditor() {
83
- return loadScript('focus_editor.js');
84
- },
85
-
86
- openPanel() {
87
- return loadScript('open_panel.js');
88
- },
89
-
90
- probe() {
91
- return `(() => {
92
- const checks = [];
93
- try {
94
- const composer = document.querySelector('.composer-view, .chat-view, [data-composer-id]');
95
- checks.push({ name: 'composer_view', passed: !!composer });
96
-
97
- const editor = document.querySelector('[contenteditable="true"][role="textbox"]')
98
- || document.querySelector('textarea.native-input');
99
- checks.push({ name: 'editor', passed: !!editor });
100
-
101
- const composerId = composer?.getAttribute('data-composer-id');
102
- checks.push({ name: 'composer_id', passed: !!composerId });
103
-
104
- const msgEls = document.querySelectorAll('[data-message-id]');
105
- checks.push({ name: 'message_elements', passed: msgEls.length > 0, message: msgEls.length + ' messages' });
106
-
107
- const healthy = checks.filter(c => c.passed).length >= 2;
108
- return JSON.stringify({ healthy, checks });
109
- } catch (e) {
110
- checks.push({ name: 'error', passed: false, message: e.message });
111
- return JSON.stringify({ healthy: false, checks });
112
- }
113
- })()`;
114
- },
115
- },
116
- };
@@ -1,127 +0,0 @@
1
- /**
2
- * Cursor — IDE Provider
3
- * @type {import('../../../src/providers/contracts').ProviderModule}
4
- */
5
- module.exports = {
6
- type: 'cursor',
7
- name: 'Cursor',
8
- category: 'ide',
9
- displayName: 'Cursor',
10
- icon: '⚡',
11
- cli: 'cursor',
12
- cdpPorts: [9333, 9334],
13
- processNames: { darwin: 'Cursor', win32: ['Cursor.exe'] },
14
- paths: {
15
- darwin: ['/Applications/Cursor.app'],
16
- win32: ['C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe'],
17
- linux: ['/opt/Cursor', '/usr/share/cursor'],
18
- },
19
- inputMethod: 'cdp-type-and-send',
20
- inputSelector: '.aislash-editor-input[contenteditable="true"]',
21
-
22
- scripts: {
23
- readChat() {
24
- return `(() => {
25
- try {
26
- const c = document.querySelector('[data-composer-id]');
27
- const id = c?.getAttribute('data-composer-id') || 'active';
28
- const rawStatus = c?.getAttribute('data-composer-status') || 'idle';
29
- const status = rawStatus === 'thinking' ? 'generating' : rawStatus;
30
- const msgs = [];
31
- document.querySelectorAll('.composer-human-ai-pair-container').forEach((p, i) => {
32
- const h = p.querySelector('.composer-human-message');
33
- if (h) msgs.push({ role: 'user', content: h.textContent.trim().substring(0, 6000), index: msgs.length });
34
- p.querySelectorAll('.composer-rendered-message').forEach(a => {
35
- if (a.closest('.composer-human-message')) return;
36
- const t = a.textContent.trim();
37
- if (t) msgs.push({ role: 'assistant', content: t.substring(0, 6000), index: msgs.length });
38
- });
39
- });
40
- const inputEl = document.querySelector('.aislash-editor-input[contenteditable="true"]');
41
- const inputContent = inputEl?.textContent?.trim() || '';
42
- return JSON.stringify({ id, status, title: document.title.split(' — ')[0], messages: msgs, inputContent });
43
- } catch(e) {
44
- return JSON.stringify({ id: '', status: 'error', messages: [] });
45
- }
46
- })()`;
47
- },
48
-
49
- sendMessage(text) {
50
- return `(() => {
51
- try {
52
- const input = document.querySelector('.aislash-editor-input[contenteditable="true"]');
53
- if (!input) return JSON.stringify({ sent: false, error: 'Input box not found' });
54
- return JSON.stringify({
55
- sent: false,
56
- needsTypeAndSend: true,
57
- selector: '.aislash-editor-input[contenteditable="true"]',
58
- });
59
- } catch(e) {
60
- return JSON.stringify({ sent: false, error: e.message });
61
- }
62
- })()`;
63
- },
64
-
65
- listSessions() {
66
- return `(() => {
67
- try {
68
- const sessions = [];
69
- const composer = document.querySelector('[data-composer-id]');
70
- if (composer) {
71
- sessions.push({
72
- id: composer.getAttribute('data-composer-id'),
73
- title: document.title.split(' — ')[0],
74
- active: true,
75
- status: composer.getAttribute('data-composer-status') || 'idle',
76
- });
77
- }
78
- return JSON.stringify(sessions);
79
- } catch(e) {
80
- return JSON.stringify([]);
81
- }
82
- })()`;
83
- },
84
-
85
- newSession() {
86
- return `(() => {
87
- try {
88
- const newBtn = [...document.querySelectorAll('a.action-label.codicon-add-two')]
89
- .find(a => (a.getAttribute('aria-label') || '').startsWith('New Chat'));
90
- if (newBtn) { newBtn.click(); return JSON.stringify({ created: true }); }
91
- return JSON.stringify({ created: false, error: 'New Chat button not found' });
92
- } catch(e) {
93
- return JSON.stringify({ created: false, error: e.message });
94
- }
95
- })()`;
96
- },
97
-
98
- focusEditor() {
99
- return `(() => {
100
- try {
101
- const input = document.querySelector('.aislash-editor-input[contenteditable="true"]');
102
- if (input) { input.focus(); return 'focused'; }
103
- return 'not_found';
104
- } catch(e) { return 'error'; }
105
- })()`;
106
- },
107
-
108
- openPanel() {
109
- return `(() => {
110
- try {
111
- const sidebar = document.getElementById('workbench.parts.auxiliarybar');
112
- if (sidebar && sidebar.offsetWidth > 0) {
113
- const chatView = document.querySelector('[data-composer-id]');
114
- if (chatView) return 'visible';
115
- }
116
- const btns = [...document.querySelectorAll('li.action-item a, button, [role="tab"]')];
117
- const toggle = btns.find(b => {
118
- const label = (b.textContent || b.getAttribute('aria-label') || '').toLowerCase();
119
- return /agent|chat|composer|cursor tab/i.test(label);
120
- });
121
- if (toggle) { toggle.click(); return 'opened'; }
122
- return 'not_found';
123
- } catch (e) { return 'error'; }
124
- })()`;
125
- },
126
- },
127
- };
@@ -1,20 +0,0 @@
1
- /**
2
- * Cursor v1 — focus_editor
3
- *
4
- * CURSOR.md 4-5: 셀렉터 우선순위
5
- * [contenteditable="true"][role="textbox"]
6
- * → .chat-input textarea
7
- * → .composer-input
8
- * → textarea
9
- *
10
- * 최종 확인: 2026-03-06
11
- */
12
- (() => {
13
- const editor = document.querySelector('[contenteditable="true"][role="textbox"]')
14
- || document.querySelector('.chat-input textarea')
15
- || document.querySelector('.composer-input')
16
- || document.querySelector('textarea.native-input')
17
- || document.querySelector('textarea');
18
- if (editor) { editor.focus(); return 'focused'; }
19
- return 'no editor found';
20
- })()
@@ -1,111 +0,0 @@
1
- /**
2
- * Cursor v1 — list_chats
3
- *
4
- * CURSOR.md 4-2: React Fiber 주 수단, DOM 폴백
5
- * Fiber keys: summaries, recentConversations, chatHistory, composers
6
- * "More" 항목 제외, modSec 정렬, 중복 제거, 100자 truncate
7
- *
8
- * 최종 확인: 2026-03-06
9
- */
10
- (async () => {
11
- // ─── 1. React Fiber에서 히스토리 추출 ───
12
- const getFiberHistory = function () {
13
- // CURSOR.md: Fiber 엔트리 포인트
14
- const entryPoints = ['.composer-view', '.chat-view', '.agent-sidebar-cell', '[data-past-conversations-toggle="true"]', '.history-toggle-button', '#workbench.parts.auxiliarybar'];
15
- for (var i = 0; i < entryPoints.length; i++) {
16
- var el = document.querySelector(entryPoints[i]);
17
- if (!el) continue;
18
- var fiberKey = Object.keys(el).find(function (k) { return k.startsWith('__reactFiber'); });
19
- if (!fiberKey) continue;
20
- var fiber = el[fiberKey];
21
- var summaries = null;
22
- var currentWsUris = null;
23
- // CURSOR.md: Fiber 순회 (최대 200)
24
- for (var d = 0; d < 200 && fiber; d++) {
25
- if (fiber.memoizedState) {
26
- var s = fiber.memoizedState;
27
- while (s) {
28
- try {
29
- var ms = s.memoizedState;
30
- if (ms && typeof ms === 'object') {
31
- // CURSOR.md Fiber keys
32
- if (ms.summaries) summaries = ms.summaries;
33
- else if (ms.recentConversations) summaries = ms.recentConversations;
34
- else if (ms.chatHistory) summaries = ms.chatHistory;
35
- else if (ms.composers && Array.isArray(ms.composers)) {
36
- summaries = {};
37
- ms.composers.forEach(c => summaries[c.id || c.composerId] = c);
38
- }
39
- }
40
- // CURSOR.md: 워크스페이스 URI 추출
41
- var lrs = s.queue && s.queue.lastRenderedState;
42
- if (lrs && lrs.workspaceUris && Array.isArray(lrs.workspaceUris)) currentWsUris = lrs.workspaceUris;
43
- if (summaries) break;
44
- } catch (e) { }
45
- s = s.next;
46
- }
47
- }
48
- if (summaries) break;
49
- fiber = fiber.return;
50
- }
51
- if (summaries) {
52
- var wsUris = currentWsUris ? new Set(currentWsUris) : null;
53
- var res = [];
54
- var entries = Object.entries(summaries);
55
- for (var j = 0; j < entries.length; j++) {
56
- var pair = entries[j];
57
- var id = pair[0];
58
- var info = pair[1];
59
- if (!id || !info) continue;
60
- // CURSOR.md: 히스토리 항목 필드 (summary → title → name → historyItemName)
61
- var title = info.summary || info.title || info.name || info.historyItemName || 'New Chat';
62
- var status = info.mode || info.unifiedMode || 'standard';
63
- var lastMod = (info.lastModifiedTime && info.lastModifiedTime.seconds) || info.lastUpdatedAt || info.createdAt || 0;
64
- // 워크스페이스 필터링
65
- if (wsUris && info.workspaces) {
66
- var match = false;
67
- for (var k = 0; k < info.workspaces.length; k++) {
68
- if (wsUris.has(info.workspaces[k].workspaceFolderAbsoluteUri || '')) { match = true; break; }
69
- }
70
- if (!match) continue;
71
- }
72
- // CURSOR.md: "More" 항목 제외 필수
73
- if (id === 'More' || (title || '').toLowerCase() === 'more' || id === 'history.more') continue;
74
- res.push({ id: id, title: title, status: status, modSec: lastMod });
75
- }
76
- // modSec 내림차순 정렬
77
- res.sort(function (a, b) { return b.modSec - a.modSec; });
78
- return res;
79
- }
80
- }
81
- return null;
82
- };
83
-
84
- // ─── 2. DOM 폴백 (CURSOR.md 셀렉터 참조표) ───
85
- const getDomHistory = function () {
86
- const selectors = ['.composer-below-chat-history-item', '.agent-sidebar-cell', '.chat-history-item', '.composer-history-item', '.history-item-container', '.monaco-list-row[aria-label*="Chat"]', '.monaco-list-row[aria-label*="Composer"]', '.monaco-list-row[aria-label*="Conversation"]'];
87
- const items = document.querySelectorAll(selectors.join(', '));
88
- return Array.from(items).map(function (el) {
89
- // .agent-sidebar-cell-text, .auxiliary-bar-chat-title (CURSOR.md)
90
- const titleEl = el.querySelector('.agent-sidebar-cell-text, .auxiliary-bar-chat-title, [class*="title"], [class*="label"], .composer-history-item-title') || el;
91
- let title = titleEl && titleEl.textContent ? titleEl.textContent.trim() : (el.getAttribute('title') || el.getAttribute('aria-label'));
92
- if (title) title = title.replace(/\s+\d+[hdmyws]$/, '').replace(/^\d+[hdmyws]\s+/, '').trim();
93
- return { id: el.getAttribute('data-composer-id') || el.getAttribute('data-id') || el.id || title, status: el.getAttribute('data-composer-status') || '', title: title || 'New Chat' };
94
- });
95
- };
96
-
97
- // ─── 3. 결과 조합 ───
98
- let history = getFiberHistory();
99
- if (!history || history.length === 0) history = getDomHistory();
100
- if (history && history.length > 0) {
101
- // 중복 제거 + 100자 truncate
102
- const seen = new Set();
103
- return history.filter(item => {
104
- const key = item.id || item.title;
105
- if (seen.has(key)) return false;
106
- seen.add(key);
107
- return true;
108
- }).map(item => ({ id: item.id, title: item.title && item.title.length > 100 ? item.title.substring(0, 100) + '...' : item.title, status: item.status }));
109
- }
110
- return [];
111
- })()
@@ -1,62 +0,0 @@
1
- /**
2
- * Cursor v1 — new_session
3
- *
4
- * Cursor는 workbench DOM 직접 접근.
5
- * "New Chat" / "New Composer" 버튼 찾기.
6
- *
7
- * 전략:
8
- * 1. 키보드 단축키 Ctrl+L (새 composer)
9
- * 2. aria-label 기반
10
- * 3. 텍스트 기반
11
- *
12
- * 최종 확인: 2026-03-07
13
- */
14
- (() => {
15
- try {
16
- // ─── 1. aria-label 기반 ───
17
- const allBtns = Array.from(document.querySelectorAll('button, [role="button"], .action-item'))
18
- .filter(b => b.offsetWidth > 0);
19
-
20
- for (const btn of allBtns) {
21
- const label = (btn.getAttribute('aria-label') || '').toLowerCase();
22
- if (label.includes('new chat') || label.includes('new composer') ||
23
- label.includes('new conversation') || label.includes('start new')) {
24
- btn.click();
25
- return 'clicked (aria)';
26
- }
27
- }
28
-
29
- // ─── 2. 텍스트 기반 ───
30
- for (const btn of allBtns) {
31
- const text = (btn.textContent || '').trim();
32
- if (text === '+' || text === 'New Chat' || text === 'New Composer' ||
33
- text === 'Start New Chat') {
34
- btn.click();
35
- return 'clicked (text)';
36
- }
37
- }
38
-
39
- // ─── 3. Codicon 아이콘 기반 (Cursor 스타일) ───
40
- for (const btn of allBtns) {
41
- const hasPlus = btn.querySelector('.codicon-plus, .codicon-add, [class*="plus"]');
42
- if (hasPlus) {
43
- const label = (btn.getAttribute('aria-label') || btn.getAttribute('title') || '').toLowerCase();
44
- if (label.includes('chat') || label.includes('composer') || label.includes('new') || label === '') {
45
- btn.click();
46
- return 'clicked (icon)';
47
- }
48
- }
49
- }
50
-
51
- // ─── 4. 키보드 단축키 Ctrl+L (Cursor 기본 바인딩) ───
52
- document.dispatchEvent(new KeyboardEvent('keydown', {
53
- key: 'l', code: 'KeyL', keyCode: 76,
54
- ctrlKey: true, metaKey: false,
55
- bubbles: true, cancelable: true,
56
- }));
57
-
58
- return 'sent Ctrl+L';
59
- } catch (e) {
60
- return 'error: ' + e.message;
61
- }
62
- })()
@@ -1,31 +0,0 @@
1
- /**
2
- * Cursor v1 — open_panel
3
- *
4
- * Cursor Agent 패널이 숨겨져 있을 때 패널 열기.
5
- * Cursor 워크벤치 페이지에서 실행 (type=page, workbench URL)
6
- *
7
- * 반환: 'visible' | 'opened' | 'error: ...'
8
- * 최종 확인: 2026-03-07
9
- */
10
- (() => {
11
- try {
12
- // Cursor Agent 패널 확인
13
- const sidebar = document.getElementById('workbench.parts.auxiliarybar') ||
14
- document.getElementById('workbench.parts.unifiedsidebar');
15
- if (sidebar && sidebar.offsetWidth > 0 && sidebar.offsetHeight > 0) {
16
- return 'visible';
17
- }
18
-
19
- // Toggle 버튼 클릭
20
- const toggleBtns = Array.from(document.querySelectorAll('li.action-item a'));
21
- for (const btn of toggleBtns) {
22
- const label = (btn.getAttribute('aria-label') || '').toLowerCase();
23
- if (label.includes('toggle agents') || label.includes('toggle primary')) {
24
- btn.click();
25
- return 'opened (toggle)';
26
- }
27
- }
28
-
29
- return 'error: no toggle button found';
30
- } catch (e) { return 'error: ' + e.message; }
31
- })()