@dmsdc-ai/aigentry-devkit 0.1.8 → 0.1.9
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/mcp-servers/deliberation/browser-control-port.js +9 -0
- package/mcp-servers/deliberation/index.js +59 -6
- package/mcp-servers/deliberation/selectors/chatgpt-extension.json +21 -0
- package/mcp-servers/deliberation/selectors/claude-extension.json +21 -0
- package/mcp-servers/deliberation/selectors/extension-providers.json +24 -0
- package/mcp-servers/deliberation/selectors/gemini-extension.json +21 -0
- package/package.json +1 -1
|
@@ -138,6 +138,15 @@ class DevToolsMcpAdapter extends BrowserControlPort {
|
|
|
138
138
|
foundTab = { ...tab, endpoint };
|
|
139
139
|
break;
|
|
140
140
|
}
|
|
141
|
+
// Strategy 3: Extension title match
|
|
142
|
+
if (selectorConfig.isExtension && tabUrl.startsWith("chrome-extension://")) {
|
|
143
|
+
const titlePatterns = selectorConfig.titlePatterns || [];
|
|
144
|
+
const lowerTitle = String(tab.title || "").toLowerCase();
|
|
145
|
+
if (titlePatterns.some(p => lowerTitle.includes(p.toLowerCase()))) {
|
|
146
|
+
foundTab = { ...tab, endpoint };
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
if (foundTab) break;
|
|
143
152
|
} catch {
|
|
@@ -87,6 +87,29 @@ const DEFAULT_LLM_DOMAINS = [
|
|
|
87
87
|
"notebooklm.google.com",
|
|
88
88
|
];
|
|
89
89
|
|
|
90
|
+
let _extensionProviderRegistry = null;
|
|
91
|
+
function loadExtensionProviderRegistry() {
|
|
92
|
+
if (_extensionProviderRegistry) return _extensionProviderRegistry;
|
|
93
|
+
try {
|
|
94
|
+
const registryPath = require("path").join(__dirname, "selectors", "extension-providers.json");
|
|
95
|
+
_extensionProviderRegistry = JSON.parse(require("fs").readFileSync(registryPath, "utf-8"));
|
|
96
|
+
return _extensionProviderRegistry;
|
|
97
|
+
} catch {
|
|
98
|
+
_extensionProviderRegistry = { providers: [] };
|
|
99
|
+
return _extensionProviderRegistry;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function isExtensionLlmTab(url = "", title = "") {
|
|
104
|
+
if (!String(url).startsWith("chrome-extension://")) return false;
|
|
105
|
+
const registry = loadExtensionProviderRegistry();
|
|
106
|
+
const lowerTitle = String(title || "").toLowerCase();
|
|
107
|
+
if (!lowerTitle) return false;
|
|
108
|
+
return registry.providers.some(p =>
|
|
109
|
+
p.titlePatterns.some(pattern => lowerTitle.includes(pattern.toLowerCase()))
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
90
113
|
const PRODUCT_DISCLAIMER = "ℹ️ 이 도구는 외부 웹사이트를 영구 수정하지 않습니다. 브라우저 문맥을 읽기 전용으로 참조하여 발화자를 라우팅합니다.";
|
|
91
114
|
const LOCKS_SUBDIR = ".locks";
|
|
92
115
|
const LOCK_RETRY_MS = 25;
|
|
@@ -435,7 +458,7 @@ function dedupeBrowserTabs(tabs = []) {
|
|
|
435
458
|
const browser = String(tab?.browser || "").trim();
|
|
436
459
|
const title = String(tab?.title || "").trim();
|
|
437
460
|
const url = String(tab?.url || "").trim();
|
|
438
|
-
if (!url || !isLlmUrl(url)) continue;
|
|
461
|
+
if (!url || (!isLlmUrl(url) && !isExtensionLlmTab(url, title))) continue;
|
|
439
462
|
const key = `${browser}\t${title}\t${url}`;
|
|
440
463
|
if (seen.has(key)) continue;
|
|
441
464
|
seen.add(key);
|
|
@@ -571,10 +594,11 @@ async function collectBrowserLlmTabsViaCdp() {
|
|
|
571
594
|
for (const item of payload) {
|
|
572
595
|
if (!item || String(item.type) !== "page") continue;
|
|
573
596
|
const url = String(item.url || "").trim();
|
|
574
|
-
|
|
597
|
+
const title = String(item.title || "").trim();
|
|
598
|
+
if (!isLlmUrl(url) && !isExtensionLlmTab(url, title)) continue;
|
|
575
599
|
tabs.push({
|
|
576
600
|
browser,
|
|
577
|
-
title:
|
|
601
|
+
title: title || "(untitled)",
|
|
578
602
|
url,
|
|
579
603
|
});
|
|
580
604
|
}
|
|
@@ -792,8 +816,19 @@ async function collectBrowserLlmTabs() {
|
|
|
792
816
|
};
|
|
793
817
|
}
|
|
794
818
|
|
|
795
|
-
function inferLlmProvider(url = "") {
|
|
819
|
+
function inferLlmProvider(url = "", title = "") {
|
|
796
820
|
const value = String(url).toLowerCase();
|
|
821
|
+
// Extension side panel: infer from title via registry
|
|
822
|
+
if (value.startsWith("chrome-extension://") && title) {
|
|
823
|
+
const registry = loadExtensionProviderRegistry();
|
|
824
|
+
const lowerTitle = String(title).toLowerCase();
|
|
825
|
+
for (const entry of registry.providers) {
|
|
826
|
+
if (entry.titlePatterns.some(p => lowerTitle.includes(p.toLowerCase()))) {
|
|
827
|
+
return entry.provider;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return "extension-llm";
|
|
831
|
+
}
|
|
797
832
|
if (value.includes("claude.ai") || value.includes("anthropic.com")) return "claude";
|
|
798
833
|
if (value.includes("chatgpt.com") || value.includes("openai.com")) return "chatgpt";
|
|
799
834
|
if (value.includes("gemini.google.com") || value.includes("notebooklm.google.com")) return "gemini";
|
|
@@ -841,7 +876,7 @@ async function collectSpeakerCandidates({ include_cli = true, include_browser =
|
|
|
841
876
|
browserNote = browserNote ? `${browserNote} | ${note || ""}`.replace(/ \| $/, "") : (note || null);
|
|
842
877
|
const providerCounts = new Map();
|
|
843
878
|
for (const tab of tabs) {
|
|
844
|
-
const provider = inferLlmProvider(tab.url);
|
|
879
|
+
const provider = inferLlmProvider(tab.url, tab.title);
|
|
845
880
|
const count = (providerCounts.get(provider) || 0) + 1;
|
|
846
881
|
providerCounts.set(provider, count);
|
|
847
882
|
add({
|
|
@@ -871,6 +906,23 @@ async function collectSpeakerCandidates({ include_cli = true, include_browser =
|
|
|
871
906
|
// Match CDP tabs with discovered browser candidates
|
|
872
907
|
for (const candidate of candidates) {
|
|
873
908
|
if (candidate.type !== "browser") continue;
|
|
909
|
+
// For extension candidates, match by title instead of hostname
|
|
910
|
+
const candidateUrl = String(candidate.url || "");
|
|
911
|
+
if (candidateUrl.startsWith("chrome-extension://")) {
|
|
912
|
+
const candidateTitle = String(candidate.title || "").toLowerCase();
|
|
913
|
+
if (candidateTitle) {
|
|
914
|
+
const matches = cdpTabs.filter(t =>
|
|
915
|
+
String(t.url || "").startsWith("chrome-extension://") &&
|
|
916
|
+
String(t.title || "").toLowerCase().includes(candidateTitle)
|
|
917
|
+
);
|
|
918
|
+
if (matches.length === 1) {
|
|
919
|
+
candidate.cdp_available = true;
|
|
920
|
+
candidate.cdp_tab_id = matches[0].id;
|
|
921
|
+
candidate.cdp_ws_url = matches[0].webSocketDebuggerUrl;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
874
926
|
let candidateHost = "";
|
|
875
927
|
try {
|
|
876
928
|
candidateHost = new URL(candidate.url).hostname.toLowerCase();
|
|
@@ -910,7 +962,8 @@ function formatSpeakerCandidatesReport({ candidates, browserNote }) {
|
|
|
910
962
|
} else {
|
|
911
963
|
out += `${browser.map(c => {
|
|
912
964
|
const icon = c.cdp_available ? "⚡자동" : "📋클립보드";
|
|
913
|
-
|
|
965
|
+
const extTag = String(c.url || "").startsWith("chrome-extension://") ? " [Extension]" : "";
|
|
966
|
+
return `- \`${c.speaker}\` [${icon}]${extTag} [${c.browser}] ${c.title}\n ${c.url}`;
|
|
914
967
|
}).join("\n")}\n`;
|
|
915
968
|
}
|
|
916
969
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "chatgpt-extension",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"domains": [],
|
|
5
|
+
"titlePatterns": ["ChatGPT"],
|
|
6
|
+
"isExtension": true,
|
|
7
|
+
"selectors": {
|
|
8
|
+
"inputSelector": "#prompt-textarea",
|
|
9
|
+
"sendButton": "button[data-testid='send-button']",
|
|
10
|
+
"responseSelector": ".markdown.prose",
|
|
11
|
+
"responseContainer": "[data-message-author-role='assistant']",
|
|
12
|
+
"streamingIndicator": ".result-streaming"
|
|
13
|
+
},
|
|
14
|
+
"timing": {
|
|
15
|
+
"inputDelayMs": 100,
|
|
16
|
+
"sendDelayMs": 300,
|
|
17
|
+
"pollIntervalMs": 500,
|
|
18
|
+
"streamingTimeoutMs": 45000
|
|
19
|
+
},
|
|
20
|
+
"notes": "ChatGPT extension side panel - placeholder selectors, update after DOM inspection"
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "claude-extension",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"domains": [],
|
|
5
|
+
"titlePatterns": ["Claude"],
|
|
6
|
+
"isExtension": true,
|
|
7
|
+
"selectors": {
|
|
8
|
+
"inputSelector": "[contenteditable='true']",
|
|
9
|
+
"sendButton": "button[aria-label='Send']",
|
|
10
|
+
"responseSelector": ".prose",
|
|
11
|
+
"responseContainer": "[data-is-streaming]",
|
|
12
|
+
"streamingIndicator": "[data-is-streaming='true']"
|
|
13
|
+
},
|
|
14
|
+
"timing": {
|
|
15
|
+
"inputDelayMs": 100,
|
|
16
|
+
"sendDelayMs": 300,
|
|
17
|
+
"pollIntervalMs": 500,
|
|
18
|
+
"streamingTimeoutMs": 45000
|
|
19
|
+
},
|
|
20
|
+
"notes": "Claude extension side panel - placeholder selectors, update after DOM inspection"
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"description": "Maps Chrome extension side panel titles to LLM providers for deliberation tab detection",
|
|
4
|
+
"providers": [
|
|
5
|
+
{
|
|
6
|
+
"provider": "claude-extension",
|
|
7
|
+
"selectorConfig": "claude-extension",
|
|
8
|
+
"titlePatterns": ["Claude"],
|
|
9
|
+
"notes": "Anthropic Claude Chrome extension side panel"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"provider": "chatgpt-extension",
|
|
13
|
+
"selectorConfig": "chatgpt-extension",
|
|
14
|
+
"titlePatterns": ["ChatGPT"],
|
|
15
|
+
"notes": "OpenAI ChatGPT Chrome extension side panel"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"provider": "gemini-extension",
|
|
19
|
+
"selectorConfig": "gemini-extension",
|
|
20
|
+
"titlePatterns": ["Gemini"],
|
|
21
|
+
"notes": "Google Gemini Chrome extension side panel"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": "gemini-extension",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"domains": [],
|
|
5
|
+
"titlePatterns": ["Gemini"],
|
|
6
|
+
"isExtension": true,
|
|
7
|
+
"selectors": {
|
|
8
|
+
"inputSelector": ".ql-editor",
|
|
9
|
+
"sendButton": "button[aria-label='Send message']",
|
|
10
|
+
"responseSelector": ".response-content",
|
|
11
|
+
"responseContainer": ".model-response-text",
|
|
12
|
+
"streamingIndicator": ".loading-indicator"
|
|
13
|
+
},
|
|
14
|
+
"timing": {
|
|
15
|
+
"inputDelayMs": 100,
|
|
16
|
+
"sendDelayMs": 300,
|
|
17
|
+
"pollIntervalMs": 500,
|
|
18
|
+
"streamingTimeoutMs": 45000
|
|
19
|
+
},
|
|
20
|
+
"notes": "Gemini extension side panel - placeholder selectors, update after DOM inspection"
|
|
21
|
+
}
|