@adhdev/daemon-core 0.5.26 → 0.5.28
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/dist/index.d.ts +8 -4
- package/dist/index.js +26 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/providers/_builtin/extension/codex/scripts/1.0/send_message.js +1 -1
- package/providers/_builtin/ide/antigravity/scripts/1.106/read_chat.js +2 -1
- package/providers/_builtin/ide/antigravity/scripts/1.107/read_chat.js +2 -1
- package/providers/_builtin/ide/cursor/provider.json +70 -1
- package/providers/_builtin/ide/cursor/scripts/0.49/list_sessions.js +1 -1
- package/providers/_builtin/ide/cursor/scripts/0.49/read_chat.js +1 -1
- package/providers/_builtin/ide/windsurf/scripts/1.0/read_chat.js +2 -1
- package/src/boot/daemon-lifecycle.ts +11 -0
- package/src/providers/provider-loader.ts +20 -22
- package/providers/_builtin/extension/codex/scripts/list_modes.js +0 -138
- package/providers/_builtin/extension/codex/scripts/set_mode.js +0 -165
- package/providers/_builtin/ide/cursor/provider.json.bak +0 -70
package/package.json
CHANGED
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
editor.dispatchEvent(enterEvent);
|
|
56
56
|
}, 100);
|
|
57
57
|
|
|
58
|
-
return JSON.stringify({ success: true, message: message.substring(0, 100) });
|
|
58
|
+
return JSON.stringify({ sent: true, success: true, message: message.substring(0, 100) });
|
|
59
59
|
} catch (e) {
|
|
60
60
|
return JSON.stringify({ error: e.message || String(e) });
|
|
61
61
|
}
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
if (animMarkdown && animMarkdown.offsetWidth > 0) status = 'generating';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const titleParts = document.title.split(' \u2014 ');
|
|
34
|
+
const title = (titleParts.length >= 2 ? titleParts[titleParts.length - 1] : titleParts[0] || '').trim() || 'Active Session';
|
|
34
35
|
|
|
35
36
|
// ─── HTML → Markdown 변환기 (대시보드가 ReactMarkdown+remarkGfm 사용) ───
|
|
36
37
|
// extractCodeText: layout-independent code text extraction
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
if (animMarkdown && animMarkdown.offsetWidth > 0) status = 'generating';
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const
|
|
33
|
+
const titleParts = document.title.split(' \u2014 ');
|
|
34
|
+
const title = (titleParts.length >= 2 ? titleParts[titleParts.length - 1] : titleParts[0] || '').trim() || 'Active Session';
|
|
34
35
|
|
|
35
36
|
// ─── HTML → Markdown 변환기 (대시보드가 ReactMarkdown+remarkGfm 사용) ───
|
|
36
37
|
// extractCodeText: layout-independent code text extraction
|
|
@@ -1 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
{
|
|
2
|
+
"type": "cursor",
|
|
3
|
+
"name": "Cursor",
|
|
4
|
+
"category": "ide",
|
|
5
|
+
"displayName": "Cursor",
|
|
6
|
+
"icon": "⚡",
|
|
7
|
+
"cli": "cursor",
|
|
8
|
+
"cdpPorts": [
|
|
9
|
+
9333,
|
|
10
|
+
9334
|
|
11
|
+
],
|
|
12
|
+
"targetFilter": {
|
|
13
|
+
"urlIncludes": "workbench.html",
|
|
14
|
+
"urlExcludes": ["agent"],
|
|
15
|
+
"titleExcludes": "extension-output|ADHDev CDP|Debug Console|Output\\s*$|Launchpad"
|
|
16
|
+
},
|
|
17
|
+
"processNames": {
|
|
18
|
+
"darwin": "Cursor",
|
|
19
|
+
"win32": [
|
|
20
|
+
"Cursor.exe"
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
"paths": {
|
|
24
|
+
"darwin": [
|
|
25
|
+
"/Applications/Cursor.app"
|
|
26
|
+
],
|
|
27
|
+
"win32": [
|
|
28
|
+
"C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe"
|
|
29
|
+
],
|
|
30
|
+
"linux": [
|
|
31
|
+
"/opt/Cursor",
|
|
32
|
+
"/usr/share/cursor"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"inputMethod": "cdp-type-and-send",
|
|
36
|
+
"inputSelector": ".aislash-editor-input[contenteditable=\"true\"]",
|
|
37
|
+
"versionCommand": "cursor --version",
|
|
38
|
+
"providerVersion": "1.0.0",
|
|
39
|
+
"compatibility": [
|
|
40
|
+
{ "ideVersion": ">=0.49.0", "scriptDir": "scripts/0.49" }
|
|
41
|
+
],
|
|
42
|
+
"defaultScriptDir": "scripts/0.49",
|
|
43
|
+
"vscodeCommands": {
|
|
44
|
+
"changeModel": "cursor.model"
|
|
45
|
+
},
|
|
46
|
+
"settings": {
|
|
47
|
+
"approvalAlert": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"default": true,
|
|
50
|
+
"public": true,
|
|
51
|
+
"label": "Approval Notifications",
|
|
52
|
+
"description": "Show notification when approval is needed"
|
|
53
|
+
},
|
|
54
|
+
"longGeneratingAlert": {
|
|
55
|
+
"type": "boolean",
|
|
56
|
+
"default": true,
|
|
57
|
+
"public": true,
|
|
58
|
+
"label": "Long Generation Alert",
|
|
59
|
+
"description": "Alert when generation takes too long"
|
|
60
|
+
},
|
|
61
|
+
"longGeneratingThresholdSec": {
|
|
62
|
+
"type": "number",
|
|
63
|
+
"default": 180,
|
|
64
|
+
"public": true,
|
|
65
|
+
"label": "Long Generation Threshold (sec)",
|
|
66
|
+
"min": 30,
|
|
67
|
+
"max": 600
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
if (sessions.length === 0 && activeComposer) {
|
|
31
31
|
sessions.push({
|
|
32
32
|
id: activeId,
|
|
33
|
-
title: document.title.split(' — ')[0],
|
|
33
|
+
title: (() => { const p = document.title.split(' — '); return p.length >= 2 ? p[p.length - 1] : p[0]; })(),
|
|
34
34
|
active: true,
|
|
35
35
|
index: 0,
|
|
36
36
|
});
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
|
|
310
310
|
// ─── Title ───
|
|
311
311
|
const titleParts = document.title.split(' — ');
|
|
312
|
-
const projectTitle = (titleParts.length >= 2 ? titleParts[titleParts.length -
|
|
312
|
+
const projectTitle = (titleParts.length >= 2 ? titleParts[titleParts.length - 1] : titleParts[0] || '').trim();
|
|
313
313
|
|
|
314
314
|
return JSON.stringify({ id, status, title: projectTitle, messages, inputContent, activeModal });
|
|
315
315
|
} catch(e) {
|
|
@@ -83,7 +83,8 @@
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const
|
|
86
|
+
const titleParts = document.title.split(' \u2014 ');
|
|
87
|
+
const title = (titleParts.length >= 2 ? titleParts[titleParts.length - 1] : titleParts[0] || '').trim() || 'Cascade';
|
|
87
88
|
|
|
88
89
|
// ─── 4. HTML → Markdown 변환기 ───
|
|
89
90
|
const BLOCK_TAGS = new Set(['DIV', 'P', 'BR', 'LI', 'TR', 'SECTION', 'ARTICLE', 'HEADER', 'FOOTER']);
|
|
@@ -96,6 +96,17 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
96
96
|
const providerLoader = new ProviderLoader({
|
|
97
97
|
logFn: config.providerLogFn,
|
|
98
98
|
});
|
|
99
|
+
|
|
100
|
+
// If no upstream providers exist, fetch them first (blocking — critical for new users)
|
|
101
|
+
if (!providerLoader.hasUpstream()) {
|
|
102
|
+
LOG.info('Provider', 'No upstream providers found — downloading from GitHub...');
|
|
103
|
+
try {
|
|
104
|
+
await providerLoader.fetchLatest();
|
|
105
|
+
} catch (e: any) {
|
|
106
|
+
LOG.warn('Provider', `⚠ Failed to fetch providers: ${e?.message}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
providerLoader.loadAll();
|
|
100
111
|
providerLoader.registerToDetector();
|
|
101
112
|
|
|
@@ -72,24 +72,15 @@ export class ProviderLoader {
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* Load all providers (3-tier priority)
|
|
75
|
-
* 1.
|
|
76
|
-
* 2.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
75
|
+
* 1. .upstream/ (GitHub auto-download — primary source)
|
|
76
|
+
* 2. User custom (~/.adhdev/providers/ excluding .upstream)
|
|
77
|
+
* User custom always wins (highest priority).
|
|
78
|
+
* If .upstream/ is empty, call fetchLatest() before loadAll().
|
|
79
79
|
*/
|
|
80
80
|
loadAll(): void {
|
|
81
81
|
this.providers.clear();
|
|
82
82
|
|
|
83
|
-
// 1. Load
|
|
84
|
-
let builtinCount = 0;
|
|
85
|
-
for (const dir of this.builtinDirs) {
|
|
86
|
-
if (fs.existsSync(dir)) {
|
|
87
|
-
builtinCount += this.loadDir(dir);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
this.log(`Loaded ${builtinCount} builtin providers`);
|
|
91
|
-
|
|
92
|
-
// 2. Load upstream (GitHub auto-download — overrides builtin)
|
|
83
|
+
// 1. Load upstream (GitHub auto-download — primary source)
|
|
93
84
|
let upstreamCount = 0;
|
|
94
85
|
if (fs.existsSync(this.upstreamDir)) {
|
|
95
86
|
upstreamCount = this.loadDir(this.upstreamDir);
|
|
@@ -98,7 +89,7 @@ export class ProviderLoader {
|
|
|
98
89
|
}
|
|
99
90
|
}
|
|
100
91
|
|
|
101
|
-
//
|
|
92
|
+
// 2. Load user custom (excluding .upstream — highest priority, never auto-updated)
|
|
102
93
|
if (fs.existsSync(this.userDir)) {
|
|
103
94
|
const userCount = this.loadDir(this.userDir, ['.upstream']);
|
|
104
95
|
if (userCount > 0) {
|
|
@@ -108,17 +99,24 @@ export class ProviderLoader {
|
|
|
108
99
|
|
|
109
100
|
this.log(`Total: ${this.providers.size} providers [${[...this.providers.keys()].join(', ')}]`);
|
|
110
101
|
|
|
111
|
-
//
|
|
112
|
-
if (upstreamCount === 0 && builtinCount > 0) {
|
|
113
|
-
this.log(`⚠ Using bundled providers only (upstream not available). Run 'adhdev daemon' with internet to auto-update.`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ❌ Error: no providers found anywhere
|
|
102
|
+
// ❌ Error: no providers found
|
|
117
103
|
if (this.providers.size === 0) {
|
|
118
|
-
this.log(`❌ No providers loaded!
|
|
104
|
+
this.log(`❌ No providers loaded! Run 'adhdev daemon' with internet to download providers.`);
|
|
119
105
|
}
|
|
120
106
|
}
|
|
121
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Check if upstream directory exists and has providers.
|
|
110
|
+
*/
|
|
111
|
+
hasUpstream(): boolean {
|
|
112
|
+
if (!fs.existsSync(this.upstreamDir)) return false;
|
|
113
|
+
try {
|
|
114
|
+
return fs.readdirSync(this.upstreamDir).some(d =>
|
|
115
|
+
fs.statSync(path.join(this.upstreamDir, d)).isDirectory()
|
|
116
|
+
);
|
|
117
|
+
} catch { return false; }
|
|
118
|
+
}
|
|
119
|
+
|
|
122
120
|
/**
|
|
123
121
|
* Get raw provider metadata by type (NO scripts loaded).
|
|
124
122
|
* Use resolve() when you need scripts (readChat, listModels, etc).
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex Extension — list_modes
|
|
3
|
-
*
|
|
4
|
-
* Finds the mode / autonomy dropdown next to the model chip in the composer footer,
|
|
5
|
-
* opens it (Radix), reads options, closes. UI expects `modes` + `current` (see ModelModeBar).
|
|
6
|
-
*/
|
|
7
|
-
(() => {
|
|
8
|
-
try {
|
|
9
|
-
function resolveDoc() {
|
|
10
|
-
let doc = document;
|
|
11
|
-
let root = doc.getElementById('root');
|
|
12
|
-
if (!root) {
|
|
13
|
-
const iframes = doc.querySelectorAll('iframe');
|
|
14
|
-
for (const iframe of iframes) {
|
|
15
|
-
try {
|
|
16
|
-
const innerDoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
17
|
-
if (innerDoc?.getElementById('root')) {
|
|
18
|
-
doc = innerDoc;
|
|
19
|
-
root = innerDoc.getElementById('root');
|
|
20
|
-
break;
|
|
21
|
-
}
|
|
22
|
-
} catch (e) { /* cross-origin */ }
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return { doc, root };
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function isModelMenuButton(b) {
|
|
29
|
-
const text = (b.textContent || '').trim();
|
|
30
|
-
if (b.getAttribute('aria-haspopup') !== 'menu') return false;
|
|
31
|
-
return /^(GPT-|gpt-|o\d|claude-|sonnet|opus)/i.test(text);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Mode chip: menu trigger in composer that is not the model selector. */
|
|
35
|
-
function findModeMenuButton(doc) {
|
|
36
|
-
const composer =
|
|
37
|
-
doc.querySelector('[class*="thread-composer-max-width"]') ||
|
|
38
|
-
doc.querySelector('[class*="thread-composer"]') ||
|
|
39
|
-
doc.getElementById('root') ||
|
|
40
|
-
doc.body;
|
|
41
|
-
|
|
42
|
-
const buttons = Array.from(composer.querySelectorAll('button')).filter(
|
|
43
|
-
(b) => b.offsetWidth > 0 && b.offsetHeight > 0,
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const menuTriggers = buttons.filter(
|
|
47
|
-
(b) => b.getAttribute('aria-haspopup') === 'menu' && !isModelMenuButton(b),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
if (menuTriggers.length === 0) return null;
|
|
51
|
-
|
|
52
|
-
const byAria = menuTriggers.find((b) => {
|
|
53
|
-
const al = (b.getAttribute('aria-label') || '').toLowerCase();
|
|
54
|
-
return /mode|agent|ask|plan|autonomy|codex|모드|에이전트|플랜/i.test(al);
|
|
55
|
-
});
|
|
56
|
-
if (byAria) return byAria;
|
|
57
|
-
|
|
58
|
-
return menuTriggers[0];
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function openMenu(btn) {
|
|
62
|
-
const rect = btn.getBoundingClientRect();
|
|
63
|
-
const cx = rect.left + rect.width / 2;
|
|
64
|
-
const cy = rect.top + rect.height / 2;
|
|
65
|
-
btn.dispatchEvent(
|
|
66
|
-
new PointerEvent('pointerdown', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
|
|
67
|
-
);
|
|
68
|
-
btn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: cx, clientY: cy }));
|
|
69
|
-
btn.dispatchEvent(
|
|
70
|
-
new PointerEvent('pointerup', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
|
|
71
|
-
);
|
|
72
|
-
btn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: cx, clientY: cy }));
|
|
73
|
-
btn.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: cx, clientY: cy }));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const { doc, root } = resolveDoc();
|
|
77
|
-
if (!root) return JSON.stringify({ modes: [], current: '', currentMode: '', error: 'no root' });
|
|
78
|
-
|
|
79
|
-
const modeBtn = findModeMenuButton(doc);
|
|
80
|
-
if (!modeBtn) {
|
|
81
|
-
return JSON.stringify({
|
|
82
|
-
modes: [],
|
|
83
|
-
current: '',
|
|
84
|
-
currentMode: '',
|
|
85
|
-
error: 'mode menu button not found',
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const currentLabel = (modeBtn.textContent || '').trim();
|
|
90
|
-
openMenu(modeBtn);
|
|
91
|
-
|
|
92
|
-
return new Promise((resolve) => {
|
|
93
|
-
setTimeout(() => {
|
|
94
|
-
let menu = doc.querySelector('[role="menu"][data-state="open"]');
|
|
95
|
-
if (!menu) {
|
|
96
|
-
menu = doc.querySelector('[role="menu"]');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const collected = [];
|
|
100
|
-
if (menu) {
|
|
101
|
-
const items = menu.querySelectorAll(
|
|
102
|
-
'[role="menuitem"], [role="menuitemradio"], [role="option"], div[class*="cursor-interaction"]',
|
|
103
|
-
);
|
|
104
|
-
for (const item of items) {
|
|
105
|
-
const text = (item.textContent || '').trim();
|
|
106
|
-
if (
|
|
107
|
-
text &&
|
|
108
|
-
text.length > 0 &&
|
|
109
|
-
text.length < 80 &&
|
|
110
|
-
!/^모델|^model\b|^select\b/i.test(text)
|
|
111
|
-
) {
|
|
112
|
-
collected.push(text);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
doc.dispatchEvent(
|
|
118
|
-
new KeyboardEvent('keydown', {
|
|
119
|
-
key: 'Escape',
|
|
120
|
-
code: 'Escape',
|
|
121
|
-
keyCode: 27,
|
|
122
|
-
bubbles: true,
|
|
123
|
-
}),
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
const modes = [...new Set(collected)];
|
|
127
|
-
const out = {
|
|
128
|
-
modes: modes.length > 0 ? modes : currentLabel ? [currentLabel] : [],
|
|
129
|
-
current: currentLabel,
|
|
130
|
-
currentMode: currentLabel,
|
|
131
|
-
};
|
|
132
|
-
resolve(JSON.stringify(out));
|
|
133
|
-
}, 550);
|
|
134
|
-
});
|
|
135
|
-
} catch (e) {
|
|
136
|
-
return JSON.stringify({ error: e.message || String(e), modes: [], current: '', currentMode: '' });
|
|
137
|
-
}
|
|
138
|
-
})();
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codex Extension — set_mode
|
|
3
|
-
*
|
|
4
|
-
* Opens the mode dropdown (same discovery as list_modes), selects an item matching ${MODE}.
|
|
5
|
-
*
|
|
6
|
-
* Placeholder: ${MODE}
|
|
7
|
-
*/
|
|
8
|
-
(() => {
|
|
9
|
-
try {
|
|
10
|
-
const targetMode = ${MODE};
|
|
11
|
-
|
|
12
|
-
function resolveDoc() {
|
|
13
|
-
let doc = document;
|
|
14
|
-
let root = doc.getElementById('root');
|
|
15
|
-
if (!root) {
|
|
16
|
-
const iframes = doc.querySelectorAll('iframe');
|
|
17
|
-
for (const iframe of iframes) {
|
|
18
|
-
try {
|
|
19
|
-
const innerDoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
20
|
-
if (innerDoc?.getElementById('root')) {
|
|
21
|
-
doc = innerDoc;
|
|
22
|
-
root = innerDoc.getElementById('root');
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
} catch (e) { /* cross-origin */ }
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return { doc, root };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function isModelMenuButton(b) {
|
|
32
|
-
const text = (b.textContent || '').trim();
|
|
33
|
-
if (b.getAttribute('aria-haspopup') !== 'menu') return false;
|
|
34
|
-
return /^(GPT-|gpt-|o\d|claude-|sonnet|opus)/i.test(text);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function findModeMenuButton(doc) {
|
|
38
|
-
const composer =
|
|
39
|
-
doc.querySelector('[class*="thread-composer-max-width"]') ||
|
|
40
|
-
doc.querySelector('[class*="thread-composer"]') ||
|
|
41
|
-
doc.getElementById('root') ||
|
|
42
|
-
doc.body;
|
|
43
|
-
|
|
44
|
-
const buttons = Array.from(composer.querySelectorAll('button')).filter(
|
|
45
|
-
(b) => b.offsetWidth > 0 && b.offsetHeight > 0,
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
const menuTriggers = buttons.filter(
|
|
49
|
-
(b) => b.getAttribute('aria-haspopup') === 'menu' && !isModelMenuButton(b),
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
if (menuTriggers.length === 0) return null;
|
|
53
|
-
|
|
54
|
-
const byAria = menuTriggers.find((b) => {
|
|
55
|
-
const al = (b.getAttribute('aria-label') || '').toLowerCase();
|
|
56
|
-
return /mode|agent|ask|plan|autonomy|codex|모드|에이전트|플랜/i.test(al);
|
|
57
|
-
});
|
|
58
|
-
if (byAria) return byAria;
|
|
59
|
-
|
|
60
|
-
return menuTriggers[0];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function openMenu(btn) {
|
|
64
|
-
const rect = btn.getBoundingClientRect();
|
|
65
|
-
const cx = rect.left + rect.width / 2;
|
|
66
|
-
const cy = rect.top + rect.height / 2;
|
|
67
|
-
btn.dispatchEvent(
|
|
68
|
-
new PointerEvent('pointerdown', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
|
|
69
|
-
);
|
|
70
|
-
btn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: cx, clientY: cy }));
|
|
71
|
-
btn.dispatchEvent(
|
|
72
|
-
new PointerEvent('pointerup', { bubbles: true, clientX: cx, clientY: cy, pointerId: 1 }),
|
|
73
|
-
);
|
|
74
|
-
btn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: cx, clientY: cy }));
|
|
75
|
-
btn.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: cx, clientY: cy }));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function clickItem(el) {
|
|
79
|
-
const ir = el.getBoundingClientRect();
|
|
80
|
-
const ix = ir.left + ir.width / 2;
|
|
81
|
-
const iy = ir.top + ir.height / 2;
|
|
82
|
-
el.dispatchEvent(
|
|
83
|
-
new PointerEvent('pointerdown', { bubbles: true, clientX: ix, clientY: iy }),
|
|
84
|
-
);
|
|
85
|
-
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: ix, clientY: iy }));
|
|
86
|
-
el.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, clientX: ix, clientY: iy }));
|
|
87
|
-
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: ix, clientY: iy }));
|
|
88
|
-
el.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: ix, clientY: iy }));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const { doc, root } = resolveDoc();
|
|
92
|
-
if (!root) return JSON.stringify({ success: false, error: 'no root' });
|
|
93
|
-
|
|
94
|
-
const want =
|
|
95
|
-
typeof targetMode === 'string'
|
|
96
|
-
? targetMode.trim()
|
|
97
|
-
: targetMode != null
|
|
98
|
-
? String(targetMode).trim()
|
|
99
|
-
: '';
|
|
100
|
-
if (!want) return JSON.stringify({ success: false, error: 'empty mode' });
|
|
101
|
-
|
|
102
|
-
const modeBtn = findModeMenuButton(doc);
|
|
103
|
-
if (!modeBtn) return JSON.stringify({ success: false, error: 'mode menu button not found' });
|
|
104
|
-
|
|
105
|
-
const currentLabel = (modeBtn.textContent || '').trim();
|
|
106
|
-
if (currentLabel.toLowerCase() === want.toLowerCase()) {
|
|
107
|
-
return JSON.stringify({ success: true, mode: currentLabel, changed: false });
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
openMenu(modeBtn);
|
|
111
|
-
|
|
112
|
-
return new Promise((resolve) => {
|
|
113
|
-
setTimeout(() => {
|
|
114
|
-
let menu = doc.querySelector('[role="menu"][data-state="open"]');
|
|
115
|
-
if (!menu) menu = doc.querySelector('[role="menu"]');
|
|
116
|
-
|
|
117
|
-
if (!menu) {
|
|
118
|
-
doc.dispatchEvent(
|
|
119
|
-
new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, bubbles: true }),
|
|
120
|
-
);
|
|
121
|
-
return resolve(JSON.stringify({ success: false, error: 'mode menu did not open' }));
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const items = Array.from(
|
|
125
|
-
menu.querySelectorAll(
|
|
126
|
-
'[role="menuitem"], [role="menuitemradio"], [role="option"], div[class*="cursor-interaction"]',
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const norm = (s) => (s || '').trim().toLowerCase();
|
|
131
|
-
const wantN = norm(want);
|
|
132
|
-
|
|
133
|
-
let match = items.find((el) => norm(el.textContent) === wantN);
|
|
134
|
-
if (!match) {
|
|
135
|
-
match = items.find((el) => norm(el.textContent).includes(wantN) || wantN.includes(norm(el.textContent)));
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!match) {
|
|
139
|
-
doc.dispatchEvent(
|
|
140
|
-
new KeyboardEvent('keydown', { key: 'Escape', code: 'Escape', keyCode: 27, bubbles: true }),
|
|
141
|
-
);
|
|
142
|
-
const available = items
|
|
143
|
-
.map((el) => (el.textContent || '').trim())
|
|
144
|
-
.filter((t) => t.length > 0 && t.length < 80);
|
|
145
|
-
return resolve(
|
|
146
|
-
JSON.stringify({
|
|
147
|
-
success: false,
|
|
148
|
-
error: `mode "${want}" not found`,
|
|
149
|
-
available,
|
|
150
|
-
}),
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const picked = (match.textContent || '').trim();
|
|
155
|
-
clickItem(match);
|
|
156
|
-
|
|
157
|
-
setTimeout(() => {
|
|
158
|
-
resolve(JSON.stringify({ success: true, mode: picked, changed: true }));
|
|
159
|
-
}, 350);
|
|
160
|
-
}, 550);
|
|
161
|
-
});
|
|
162
|
-
} catch (e) {
|
|
163
|
-
return JSON.stringify({ success: false, error: e.message || String(e) });
|
|
164
|
-
}
|
|
165
|
-
})();
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"type": "cursor",
|
|
3
|
-
"name": "Cursor",
|
|
4
|
-
"category": "ide",
|
|
5
|
-
"displayName": "Cursor",
|
|
6
|
-
"icon": "⚡",
|
|
7
|
-
"cli": "cursor",
|
|
8
|
-
"cdpPorts": [
|
|
9
|
-
9333,
|
|
10
|
-
9334
|
|
11
|
-
],
|
|
12
|
-
"targetFilter": {
|
|
13
|
-
"urlIncludes": "workbench.html",
|
|
14
|
-
"urlExcludes": ["agent"],
|
|
15
|
-
"titleExcludes": "extension-output|ADHDev CDP|Debug Console|Output\\s*$|Launchpad"
|
|
16
|
-
},
|
|
17
|
-
"processNames": {
|
|
18
|
-
"darwin": "Cursor",
|
|
19
|
-
"win32": [
|
|
20
|
-
"Cursor.exe"
|
|
21
|
-
]
|
|
22
|
-
},
|
|
23
|
-
"paths": {
|
|
24
|
-
"darwin": [
|
|
25
|
-
"/Applications/Cursor.app"
|
|
26
|
-
],
|
|
27
|
-
"win32": [
|
|
28
|
-
"C:\\Users\\*\\AppData\\Local\\Programs\\cursor\\Cursor.exe"
|
|
29
|
-
],
|
|
30
|
-
"linux": [
|
|
31
|
-
"/opt/Cursor",
|
|
32
|
-
"/usr/share/cursor"
|
|
33
|
-
]
|
|
34
|
-
},
|
|
35
|
-
"inputMethod": "cdp-type-and-send",
|
|
36
|
-
"inputSelector": ".aislash-editor-input[contenteditable=\"true\"]",
|
|
37
|
-
"versionCommand": "cursor --version",
|
|
38
|
-
"providerVersion": "1.0.0",
|
|
39
|
-
"compatibility": [
|
|
40
|
-
{ "ideVersion": ">=0.49.0", "scriptDir": "scripts/0.49" }
|
|
41
|
-
],
|
|
42
|
-
"defaultScriptDir": "scripts/0.49",
|
|
43
|
-
"vscodeCommands": {
|
|
44
|
-
"changeModel": "cursor.model"
|
|
45
|
-
},
|
|
46
|
-
"settings": {
|
|
47
|
-
"approvalAlert": {
|
|
48
|
-
"type": "boolean",
|
|
49
|
-
"default": true,
|
|
50
|
-
"public": true,
|
|
51
|
-
"label": "Approval Notifications",
|
|
52
|
-
"description": "Show notification when approval is needed"
|
|
53
|
-
},
|
|
54
|
-
"longGeneratingAlert": {
|
|
55
|
-
"type": "boolean",
|
|
56
|
-
"default": true,
|
|
57
|
-
"public": true,
|
|
58
|
-
"label": "Long Generation Alert",
|
|
59
|
-
"description": "Alert when generation takes too long"
|
|
60
|
-
},
|
|
61
|
-
"longGeneratingThresholdSec": {
|
|
62
|
-
"type": "number",
|
|
63
|
-
"default": 180,
|
|
64
|
-
"public": true,
|
|
65
|
-
"label": "Long Generation Threshold (sec)",
|
|
66
|
-
"min": 30,
|
|
67
|
-
"max": 600
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|