@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.
@@ -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
- if (!isLlmUrl(url)) continue;
597
+ const title = String(item.title || "").trim();
598
+ if (!isLlmUrl(url) && !isExtensionLlmTab(url, title)) continue;
575
599
  tabs.push({
576
600
  browser,
577
- title: String(item.title || "").trim() || "(untitled)",
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
- return `- \`${c.speaker}\` [${icon}] [${c.browser}] ${c.title}\n ${c.url}`;
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-devkit",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Cross-platform installer and tooling bundle for aigentry-devkit",
5
5
  "license": "MIT",
6
6
  "repository": {