@adhdev/daemon-core 0.6.77 → 0.7.0
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.mts +2342 -0
- package/dist/index.d.ts +86 -932
- package/dist/index.js +879 -664
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14702 -0
- package/dist/index.mjs.map +1 -0
- package/dist/normalize-S2PmiRgB.d.mts +843 -0
- package/dist/normalize-S2PmiRgB.d.ts +843 -0
- package/dist/status/normalize.d.mts +1 -0
- package/dist/status/normalize.d.ts +1 -0
- package/dist/status/normalize.js +73 -0
- package/dist/status/normalize.js.map +1 -0
- package/dist/status/normalize.mjs +45 -0
- package/dist/status/normalize.mjs.map +1 -0
- package/package.json +8 -1
- package/src/agent-stream/manager.ts +213 -150
- package/src/agent-stream/poller.ts +57 -45
- package/src/boot/daemon-lifecycle.ts +30 -12
- package/src/cdp/initializer.ts +47 -0
- package/src/cdp/manager.ts +45 -4
- package/src/cdp/setup.ts +26 -11
- package/src/commands/chat-commands.ts +136 -88
- package/src/commands/cli-manager.ts +31 -6
- package/src/commands/handler.ts +71 -109
- package/src/commands/router.ts +4 -20
- package/src/commands/stream-commands.ts +34 -156
- package/src/daemon-core.ts +3 -9
- package/src/index.ts +8 -5
- package/src/logging/command-log.ts +1 -1
- package/src/providers/acp-provider-instance.ts +4 -0
- package/src/providers/provider-instance-manager.ts +1 -0
- package/src/sessions/registry.ts +76 -0
- package/src/shared-types.ts +45 -54
- package/src/status/builders.ts +157 -120
- package/src/status/normalize.ts +64 -0
- package/src/status/reporter.ts +16 -15
- package/src/status/snapshot.ts +3 -11
|
@@ -15,6 +15,7 @@ import type { DaemonAgentStreamManager } from './manager.js';
|
|
|
15
15
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
16
16
|
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
17
17
|
import { registerExtensionProviders } from '../cdp/setup.js';
|
|
18
|
+
import type { SessionRegistry } from '../sessions/registry.js';
|
|
18
19
|
import { LOG } from '../logging/logger.js';
|
|
19
20
|
import type { AgentStreamState } from './types.js';
|
|
20
21
|
|
|
@@ -25,13 +26,13 @@ export interface AgentStreamPollerDeps {
|
|
|
25
26
|
providerLoader: ProviderLoader;
|
|
26
27
|
instanceManager: ProviderInstanceManager;
|
|
27
28
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
29
|
+
sessionRegistry: SessionRegistry;
|
|
28
30
|
/** Callback when agent streams are updated */
|
|
29
31
|
onStreamsUpdated?: (ideType: string, streams: AgentStreamState[]) => void;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export class AgentStreamPoller {
|
|
33
35
|
private deps: AgentStreamPollerDeps;
|
|
34
|
-
private _activeIdeType: string | null = null;
|
|
35
36
|
private timer: NodeJS.Timeout | null = null;
|
|
36
37
|
|
|
37
38
|
constructor(deps: AgentStreamPollerDeps) {
|
|
@@ -40,14 +41,12 @@ export class AgentStreamPoller {
|
|
|
40
41
|
|
|
41
42
|
/** Currently active IDE type for agent streaming */
|
|
42
43
|
get activeIde(): string | null {
|
|
43
|
-
return
|
|
44
|
+
return null;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/** Reset active IDE tracking (e.g., when IDE is stopped) */
|
|
47
|
-
resetActiveIde(
|
|
48
|
-
|
|
49
|
-
this._activeIdeType = null;
|
|
50
|
-
}
|
|
48
|
+
resetActiveIde(parentSessionId: string): void {
|
|
49
|
+
this.deps.agentStreamManager.resetParentSession(parentSessionId);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
/** Start polling (idempotent — ignored if already started) */
|
|
@@ -74,6 +73,7 @@ export class AgentStreamPoller {
|
|
|
74
73
|
providerLoader,
|
|
75
74
|
instanceManager,
|
|
76
75
|
cdpManagers,
|
|
76
|
+
sessionRegistry,
|
|
77
77
|
} = this.deps;
|
|
78
78
|
|
|
79
79
|
if (!agentStreamManager || cdpManagers.size === 0) return;
|
|
@@ -85,6 +85,7 @@ export class AgentStreamPoller {
|
|
|
85
85
|
|
|
86
86
|
// 1b. Dynamically add/remove IDE instance extensions
|
|
87
87
|
const ideInstance = instanceManager.getInstance(`ide:${ideType}`) as any;
|
|
88
|
+
const parentSessionId = ideInstance?.getInstanceId?.();
|
|
88
89
|
if (ideInstance?.getExtensionTypes && ideInstance?.addExtension && ideInstance?.removeExtension) {
|
|
89
90
|
const currentExtTypes = new Set(ideInstance.getExtensionTypes() as string[]);
|
|
90
91
|
const enabledExtTypes = new Set(
|
|
@@ -94,6 +95,10 @@ export class AgentStreamPoller {
|
|
|
94
95
|
// Remove disabled extensions
|
|
95
96
|
for (const extType of currentExtTypes) {
|
|
96
97
|
if (!enabledExtTypes.has(extType)) {
|
|
98
|
+
const extInstance = ideInstance.getExtension?.(extType);
|
|
99
|
+
if (extInstance?.getInstanceId) {
|
|
100
|
+
sessionRegistry.unregister(extInstance.getInstanceId());
|
|
101
|
+
}
|
|
97
102
|
ideInstance.removeExtension(extType);
|
|
98
103
|
LOG.info('AgentStream', `Extension removed: ${extType} (disabled for ${ideType})`);
|
|
99
104
|
}
|
|
@@ -106,6 +111,18 @@ export class AgentStreamPoller {
|
|
|
106
111
|
if (extProvider) {
|
|
107
112
|
const extSettings = providerLoader.getSettings(extType);
|
|
108
113
|
ideInstance.addExtension(extProvider, extSettings);
|
|
114
|
+
const extInstance = ideInstance.getExtension?.(extType);
|
|
115
|
+
if (parentSessionId && extInstance?.getInstanceId) {
|
|
116
|
+
sessionRegistry.register({
|
|
117
|
+
sessionId: extInstance.getInstanceId(),
|
|
118
|
+
parentSessionId,
|
|
119
|
+
providerType: extType,
|
|
120
|
+
providerCategory: 'extension',
|
|
121
|
+
transport: 'cdp-webview',
|
|
122
|
+
cdpManagerKey: ideType,
|
|
123
|
+
instanceKey: `ide:${ideType}`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
109
126
|
LOG.info('AgentStream', `Extension added: ${extType} (enabled for ${ideType})`);
|
|
110
127
|
}
|
|
111
128
|
}
|
|
@@ -113,56 +130,51 @@ export class AgentStreamPoller {
|
|
|
113
130
|
}
|
|
114
131
|
|
|
115
132
|
// 1c. If the active agent stream belongs to a now-disabled extension, detach it
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
await agentStreamManager.switchActiveAgent(cdp, null);
|
|
124
|
-
this._activeIdeType = null;
|
|
133
|
+
const activeSessionId = parentSessionId ? agentStreamManager.getActiveSessionId(parentSessionId) : null;
|
|
134
|
+
if (activeSessionId) {
|
|
135
|
+
const activeTarget = sessionRegistry.get(activeSessionId);
|
|
136
|
+
const enabledExtTypes = new Set(providerLoader.getEnabledExtensionProviders(ideType).map((p: any) => p.type));
|
|
137
|
+
if (!activeTarget || !enabledExtTypes.has(activeTarget.providerType)) {
|
|
138
|
+
LOG.info('AgentStream', `Active agent ${activeTarget?.providerType || activeSessionId} was disabled for ${ideType} — detaching`);
|
|
139
|
+
await agentStreamManager.setActiveSession(cdp, parentSessionId!, null);
|
|
125
140
|
// Report empty streams so dashboard removes the tab
|
|
126
141
|
this.deps.onStreamsUpdated?.(ideType, []);
|
|
127
142
|
}
|
|
128
143
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const cdp = cdpManagers.get(this._activeIdeType);
|
|
136
|
-
if (cdp?.isConnected) {
|
|
137
|
-
try {
|
|
138
|
-
await agentStreamManager.syncAgentSessions(cdp);
|
|
139
|
-
const streams = await agentStreamManager.collectAgentStreams(cdp);
|
|
140
|
-
this.deps.onStreamsUpdated?.(this._activeIdeType, streams);
|
|
141
|
-
} catch { }
|
|
142
|
-
return;
|
|
144
|
+
if (!cdp.isConnected) {
|
|
145
|
+
if (parentSessionId && activeSessionId) {
|
|
146
|
+
agentStreamManager.resetParentSession(parentSessionId);
|
|
147
|
+
this.deps.onStreamsUpdated?.(ideType, []);
|
|
148
|
+
}
|
|
149
|
+
continue;
|
|
143
150
|
}
|
|
144
|
-
// CDP lost — reset
|
|
145
|
-
this._activeIdeType = null;
|
|
146
|
-
}
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
+
// ─── Phase 2: Agent session sync + collect ───
|
|
153
|
+
let resolvedActiveSessionId = activeSessionId;
|
|
154
|
+
|
|
155
|
+
// ─── Phase 3: Auto-discover agents ───
|
|
156
|
+
if (!resolvedActiveSessionId && parentSessionId) {
|
|
152
157
|
try {
|
|
153
158
|
const discovered = await cdp.discoverAgentWebviews();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
return;
|
|
159
|
+
for (const target of discovered) {
|
|
160
|
+
const sessionId = agentStreamManager.resolveSessionForAgent(parentSessionId, target.agentType);
|
|
161
|
+
if (sessionId) {
|
|
162
|
+
resolvedActiveSessionId = sessionId;
|
|
163
|
+
await agentStreamManager.setActiveSession(cdp, parentSessionId, sessionId);
|
|
164
|
+
LOG.info('AgentStream', `Auto-activated: ${target.agentType} (${ideType})`);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
163
167
|
}
|
|
164
168
|
} catch { }
|
|
165
169
|
}
|
|
170
|
+
|
|
171
|
+
if (!resolvedActiveSessionId || !parentSessionId) continue;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await agentStreamManager.syncActiveSession(cdp, parentSessionId);
|
|
175
|
+
const stream = await agentStreamManager.collectActiveSession(cdp, parentSessionId);
|
|
176
|
+
this.deps.onStreamsUpdated?.(ideType, stream ? [stream] : []);
|
|
177
|
+
} catch { }
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
}
|
|
@@ -20,6 +20,7 @@ import { VersionArchive, detectAllVersions } from '../providers/version-archive.
|
|
|
20
20
|
import { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
21
21
|
import { DevServer } from '../daemon/dev-server.js';
|
|
22
22
|
import { detectIDEs } from '../detection/ide-detector.js';
|
|
23
|
+
import { SessionRegistry } from '../sessions/registry.js';
|
|
23
24
|
import { installGlobalInterceptor, LOG } from '../logging/logger.js';
|
|
24
25
|
import { loadConfig } from '../config/config.js';
|
|
25
26
|
|
|
@@ -70,7 +71,7 @@ export interface DaemonComponents {
|
|
|
70
71
|
poller: AgentStreamPoller;
|
|
71
72
|
cdpInitializer: DaemonCdpInitializer;
|
|
72
73
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
73
|
-
|
|
74
|
+
sessionRegistry: SessionRegistry;
|
|
74
75
|
detectedIdes: { value: any[] };
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -141,13 +142,16 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
141
142
|
// 3. Shared state
|
|
142
143
|
const instanceManager = new ProviderInstanceManager();
|
|
143
144
|
const cdpManagers = new Map<string, DaemonCdpManager>();
|
|
144
|
-
const
|
|
145
|
+
const sessionRegistry = new SessionRegistry();
|
|
145
146
|
const detectedIdesRef = { value: [] as any[] };
|
|
147
|
+
let agentStreamManager: DaemonAgentStreamManager | null = null;
|
|
148
|
+
let poller: AgentStreamPoller | null = null;
|
|
146
149
|
|
|
147
150
|
// 4. CLI Manager
|
|
148
151
|
const cliManager = new DaemonCliManager({
|
|
149
152
|
...config.cliManagerDeps,
|
|
150
153
|
getInstanceManager: () => instanceManager,
|
|
154
|
+
getSessionRegistry: () => sessionRegistry,
|
|
151
155
|
}, providerLoader);
|
|
152
156
|
|
|
153
157
|
// 5. Detect IDEs
|
|
@@ -161,7 +165,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
161
165
|
providerLoader,
|
|
162
166
|
instanceManager,
|
|
163
167
|
cdpManagers,
|
|
164
|
-
|
|
168
|
+
sessionRegistry,
|
|
165
169
|
};
|
|
166
170
|
|
|
167
171
|
const cdpInitializer = new DaemonCdpInitializer({
|
|
@@ -174,6 +178,21 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
174
178
|
// Transport-specific extras
|
|
175
179
|
await config.onCdpManagerSetup?.(ideType, manager, managerKey);
|
|
176
180
|
},
|
|
181
|
+
onDisconnected: async (_ideType, _manager, managerKey) => {
|
|
182
|
+
sessionRegistry.unregisterByManagerKey(managerKey);
|
|
183
|
+
const instanceKey = `ide:${managerKey}`;
|
|
184
|
+
const ideInstance = instanceManager.getInstance(instanceKey) as any;
|
|
185
|
+
|
|
186
|
+
if (ideInstance) {
|
|
187
|
+
instanceManager.removeInstance(instanceKey);
|
|
188
|
+
LOG.info('CDP', `Instance removed after disconnect: ${instanceKey}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (ideInstance?.getInstanceId) {
|
|
192
|
+
agentStreamManager?.resetParentSession(ideInstance.getInstanceId());
|
|
193
|
+
}
|
|
194
|
+
config.onStatusChange?.();
|
|
195
|
+
},
|
|
177
196
|
});
|
|
178
197
|
await cdpInitializer.connectAll(detectedIdesRef.value);
|
|
179
198
|
cdpInitializer.startPeriodicScan(config.cdpScanIntervalMs ?? 30_000);
|
|
@@ -186,20 +205,19 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
186
205
|
adapters: cliManager.adapters,
|
|
187
206
|
providerLoader,
|
|
188
207
|
instanceManager,
|
|
189
|
-
|
|
208
|
+
sessionRegistry,
|
|
190
209
|
});
|
|
191
210
|
|
|
192
211
|
// 8. AgentStreamManager
|
|
193
|
-
|
|
212
|
+
agentStreamManager = new DaemonAgentStreamManager(
|
|
194
213
|
LOG.forComponent('AgentStream').asLogFn(),
|
|
195
214
|
providerLoader,
|
|
215
|
+
sessionRegistry,
|
|
196
216
|
);
|
|
197
217
|
commandHandler.setAgentStreamManager(agentStreamManager);
|
|
198
218
|
|
|
199
219
|
// 9. Router + Poller (with internal cross-wiring)
|
|
200
220
|
// Note: poller is declared first so router's onIdeConnected closure captures it
|
|
201
|
-
let poller: AgentStreamPoller;
|
|
202
|
-
|
|
203
221
|
const router = new DaemonCommandRouter({
|
|
204
222
|
commandHandler,
|
|
205
223
|
cliManager,
|
|
@@ -207,7 +225,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
207
225
|
providerLoader,
|
|
208
226
|
instanceManager,
|
|
209
227
|
detectedIdes: detectedIdesRef,
|
|
210
|
-
|
|
228
|
+
sessionRegistry,
|
|
211
229
|
onCdpManagerCreated: async (ideType: string, manager: DaemonCdpManager) => {
|
|
212
230
|
// For launch_ide: register instance + extension providers
|
|
213
231
|
await setupIdeInstance(cdpSetupContext, { ideType, manager });
|
|
@@ -224,6 +242,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
224
242
|
providerLoader,
|
|
225
243
|
instanceManager,
|
|
226
244
|
cdpManagers,
|
|
245
|
+
sessionRegistry,
|
|
227
246
|
onStreamsUpdated: config.onStreamsUpdated,
|
|
228
247
|
});
|
|
229
248
|
poller.start();
|
|
@@ -241,7 +260,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
241
260
|
poller,
|
|
242
261
|
cdpInitializer,
|
|
243
262
|
cdpManagers,
|
|
244
|
-
|
|
263
|
+
sessionRegistry,
|
|
245
264
|
detectedIdes: detectedIdesRef,
|
|
246
265
|
};
|
|
247
266
|
}
|
|
@@ -288,9 +307,8 @@ export async function shutdownDaemonComponents(components: DaemonComponents): Pr
|
|
|
288
307
|
|
|
289
308
|
// 2. Dispose agent stream
|
|
290
309
|
try {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
await agentStreamManager.dispose(anyCdp);
|
|
310
|
+
if (agentStreamManager) {
|
|
311
|
+
await agentStreamManager.dispose(cdpManagers);
|
|
294
312
|
}
|
|
295
313
|
} catch (e: any) { LOG.warn('Shutdown', `AgentStream dispose: ${e?.message}`); }
|
|
296
314
|
|
package/src/cdp/initializer.ts
CHANGED
|
@@ -24,6 +24,13 @@ export interface CdpInitializerConfig {
|
|
|
24
24
|
enabledIdes?: string[];
|
|
25
25
|
/** Callback when a new CDP manager is connected */
|
|
26
26
|
onConnected?: (ideType: string, manager: DaemonCdpManager, managerKey: string) => void | Promise<void>;
|
|
27
|
+
/** Callback when a stale/disconnected CDP manager is removed */
|
|
28
|
+
onDisconnected?: (
|
|
29
|
+
ideType: string,
|
|
30
|
+
manager: DaemonCdpManager,
|
|
31
|
+
managerKey: string,
|
|
32
|
+
reason: 'ide_closed' | 'target_closed' | 'target_rekeyed',
|
|
33
|
+
) => void | Promise<void>;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
export class DaemonCdpInitializer {
|
|
@@ -88,6 +95,7 @@ export class DaemonCdpInitializer {
|
|
|
88
95
|
|
|
89
96
|
// 1. Try multi-window: list all workbench pages on this port
|
|
90
97
|
const targets = await DaemonCdpManager.listAllTargets(port);
|
|
98
|
+
await this.pruneStaleManagers(port, ide, targets);
|
|
91
99
|
|
|
92
100
|
if (targets.length === 0) {
|
|
93
101
|
// Prevent duplicate fallback connection
|
|
@@ -153,6 +161,45 @@ export class DaemonCdpInitializer {
|
|
|
153
161
|
}
|
|
154
162
|
}
|
|
155
163
|
|
|
164
|
+
private async pruneStaleManagers(
|
|
165
|
+
port: number,
|
|
166
|
+
ide: string,
|
|
167
|
+
targets: Array<{ id: string }>,
|
|
168
|
+
): Promise<void> {
|
|
169
|
+
const trackedTargetIds = new Set(targets.map((target) => target.id));
|
|
170
|
+
const removals: Array<{
|
|
171
|
+
key: string;
|
|
172
|
+
manager: DaemonCdpManager;
|
|
173
|
+
reason: 'ide_closed' | 'target_closed' | 'target_rekeyed';
|
|
174
|
+
}> = [];
|
|
175
|
+
|
|
176
|
+
for (const [key, manager] of this.config.cdpManagers.entries()) {
|
|
177
|
+
if (!(key === ide || key.startsWith(`${ide}_`))) continue;
|
|
178
|
+
if (manager.getPort() !== port) continue;
|
|
179
|
+
|
|
180
|
+
if (targets.length === 0) {
|
|
181
|
+
removals.push({ key, manager, reason: 'ide_closed' });
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (manager.targetId && !trackedTargetIds.has(manager.targetId)) {
|
|
186
|
+
removals.push({ key, manager, reason: 'target_closed' });
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (key === ide && !manager.targetId && targets.length > 1) {
|
|
191
|
+
removals.push({ key, manager, reason: 'target_rekeyed' });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const { key, manager, reason } of removals) {
|
|
196
|
+
try { manager.disconnect(); } catch { /* noop */ }
|
|
197
|
+
this.config.cdpManagers.delete(key);
|
|
198
|
+
LOG.info('CDP', `Removed stale manager: ${key} (${reason})`);
|
|
199
|
+
await this.config.onDisconnected?.(ide, manager, key, reason);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
156
203
|
// ─── Periodic scanning ───
|
|
157
204
|
|
|
158
205
|
/**
|
package/src/cdp/manager.ts
CHANGED
|
@@ -660,9 +660,12 @@ export class DaemonCdpManager {
|
|
|
660
660
|
try {
|
|
661
661
|
// 1. Find webview iframe targets
|
|
662
662
|
const { targetInfos } = await sendWs('Target.getTargets');
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
663
|
+
const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
|
|
664
|
+
const webviewIframes = (targetInfos || []).filter((t: any) => {
|
|
665
|
+
if (t.type !== 'iframe' || !(t.url || '').includes('vscode-webview')) return false;
|
|
666
|
+
if (pageWebviewUrls.size === 0) return true;
|
|
667
|
+
return pageWebviewUrls.has(t.url || '');
|
|
668
|
+
});
|
|
666
669
|
|
|
667
670
|
if (webviewIframes.length === 0) {
|
|
668
671
|
this.log('[CDP] evaluateInWebviewFrame: no webview iframes found');
|
|
@@ -766,6 +769,8 @@ export class DaemonCdpManager {
|
|
|
766
769
|
allTargets = result?.targetInfos || [];
|
|
767
770
|
}
|
|
768
771
|
|
|
772
|
+
const pageWebviewUrls = await this.getCurrentPageWebviewUrls();
|
|
773
|
+
|
|
769
774
|
const iframes = allTargets.filter((t: any) => t.type === 'iframe');
|
|
770
775
|
const typeMap = new Map<string, number>();
|
|
771
776
|
for (const t of allTargets) {
|
|
@@ -794,6 +799,7 @@ export class DaemonCdpManager {
|
|
|
794
799
|
const url = target.url || '';
|
|
795
800
|
const hasWebview = url.includes('vscode-webview');
|
|
796
801
|
if (!hasWebview) continue;
|
|
802
|
+
if (pageWebviewUrls.size > 0 && !pageWebviewUrls.has(url)) continue;
|
|
797
803
|
|
|
798
804
|
for (const known of this.extensionProviders) {
|
|
799
805
|
if (known.extensionIdPattern.test(url)) {
|
|
@@ -822,7 +828,10 @@ export class DaemonCdpManager {
|
|
|
822
828
|
async attachToAgent(target: AgentWebviewTarget): Promise<string | null> {
|
|
823
829
|
if (!this.isConnected) return null;
|
|
824
830
|
for (const [sid, t] of this.agentSessions) {
|
|
825
|
-
if (t.
|
|
831
|
+
if (t.targetId === target.targetId) return sid;
|
|
832
|
+
if (t.agentType === target.agentType && t.targetId !== target.targetId) {
|
|
833
|
+
await this.detachAgent(sid).catch(() => { });
|
|
834
|
+
}
|
|
826
835
|
}
|
|
827
836
|
try {
|
|
828
837
|
// Attach via Browser WS (iframes can only be attached from browser-level)
|
|
@@ -963,6 +972,38 @@ export class DaemonCdpManager {
|
|
|
963
972
|
return this.agentSessions;
|
|
964
973
|
}
|
|
965
974
|
|
|
975
|
+
private async getCurrentPageWebviewUrls(): Promise<Set<string>> {
|
|
976
|
+
if (!this.isConnected) return new Set();
|
|
977
|
+
|
|
978
|
+
try {
|
|
979
|
+
const urls = new Set<string>();
|
|
980
|
+
const { frameTree } = await this.sendInternal('Page.getFrameTree', {}, 5000);
|
|
981
|
+
const visit = (node: any) => {
|
|
982
|
+
const url = node?.frame?.url;
|
|
983
|
+
if (typeof url === 'string' && url.includes('vscode-webview')) {
|
|
984
|
+
urls.add(url);
|
|
985
|
+
}
|
|
986
|
+
for (const child of node?.childFrames || []) visit(child);
|
|
987
|
+
};
|
|
988
|
+
if (frameTree) visit(frameTree);
|
|
989
|
+
if (urls.size > 0) return urls;
|
|
990
|
+
} catch { /* fall through to DOM scan */ }
|
|
991
|
+
|
|
992
|
+
try {
|
|
993
|
+
const raw = await this.evaluate(
|
|
994
|
+
`JSON.stringify(Array.from(document.querySelectorAll('iframe,webview'))
|
|
995
|
+
.map((el) => el.src || el.getAttribute('src') || '')
|
|
996
|
+
.filter((src) => typeof src === 'string' && src.includes('vscode-webview')))`,
|
|
997
|
+
5000,
|
|
998
|
+
);
|
|
999
|
+
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
1000
|
+
if (!Array.isArray(parsed)) return new Set();
|
|
1001
|
+
return new Set(parsed.filter((src: unknown): src is string => typeof src === 'string' && src.length > 0));
|
|
1002
|
+
} catch {
|
|
1003
|
+
return new Set();
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
966
1007
|
// ─── Screenshot ──────────────────────────────────────────
|
|
967
1008
|
|
|
968
1009
|
async captureScreenshot(opts?: { quality?: number }): Promise<Buffer | null> {
|
package/src/cdp/setup.ts
CHANGED
|
@@ -9,14 +9,13 @@ import { DaemonCdpManager } from './manager.js';
|
|
|
9
9
|
import { ProviderLoader } from '../providers/provider-loader.js';
|
|
10
10
|
import { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
11
11
|
import { IdeProviderInstance } from '../providers/ide-provider-instance.js';
|
|
12
|
-
import
|
|
12
|
+
import { SessionRegistry } from '../sessions/registry.js';
|
|
13
13
|
|
|
14
14
|
export interface CdpSetupContext {
|
|
15
15
|
providerLoader: ProviderLoader;
|
|
16
16
|
instanceManager: ProviderInstanceManager;
|
|
17
17
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
18
|
-
|
|
19
|
-
instanceIdMap: Map<string, string>;
|
|
18
|
+
sessionRegistry: SessionRegistry;
|
|
20
19
|
/** Server connection (optional) */
|
|
21
20
|
serverConn?: any;
|
|
22
21
|
}
|
|
@@ -58,7 +57,7 @@ export function registerExtensionProviders(
|
|
|
58
57
|
* 2. Create IdeProviderInstance
|
|
59
58
|
* 3. Register in InstanceManager
|
|
60
59
|
* 4. Register enabled extensions
|
|
61
|
-
* 5.
|
|
60
|
+
* 5. Register runtime sessions (workspace + extension children)
|
|
62
61
|
*
|
|
63
62
|
* @returns The created IdeProviderInstance, or null if provider not found
|
|
64
63
|
*/
|
|
@@ -66,7 +65,7 @@ export async function setupIdeInstance(
|
|
|
66
65
|
ctx: CdpSetupContext,
|
|
67
66
|
opts: SetupIdeInstanceOptions,
|
|
68
67
|
): Promise<IdeProviderInstance | null> {
|
|
69
|
-
const { providerLoader, instanceManager,
|
|
68
|
+
const { providerLoader, instanceManager, sessionRegistry } = ctx;
|
|
70
69
|
const { ideType, manager, settings } = opts;
|
|
71
70
|
const managerKey = opts.managerKey || ideType;
|
|
72
71
|
|
|
@@ -91,18 +90,34 @@ export async function setupIdeInstance(
|
|
|
91
90
|
settings: resolvedSettings,
|
|
92
91
|
});
|
|
93
92
|
|
|
94
|
-
// 5.
|
|
95
|
-
|
|
93
|
+
// 5. Register workspace session
|
|
94
|
+
sessionRegistry.register({
|
|
95
|
+
sessionId: ideInstance.getInstanceId(),
|
|
96
|
+
parentSessionId: null,
|
|
97
|
+
providerType: ideType,
|
|
98
|
+
providerCategory: 'ide',
|
|
99
|
+
transport: 'cdp-page',
|
|
100
|
+
cdpManagerKey: managerKey,
|
|
101
|
+
instanceKey: `ide:${managerKey}`,
|
|
102
|
+
});
|
|
96
103
|
|
|
97
104
|
// 6. Register enabled extensions
|
|
98
105
|
const extensionProviders = providerLoader.getEnabledByCategory('extension', ideType);
|
|
99
106
|
for (const extProvider of extensionProviders) {
|
|
100
107
|
const extSettings = providerLoader.getSettings(extProvider.type);
|
|
101
108
|
await ideInstance.addExtension(extProvider, extSettings);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const ext of ideInstance.getExtensionInstances()) {
|
|
112
|
+
sessionRegistry.register({
|
|
113
|
+
sessionId: ext.getInstanceId(),
|
|
114
|
+
parentSessionId: ideInstance.getInstanceId(),
|
|
115
|
+
providerType: ext.type,
|
|
116
|
+
providerCategory: 'extension',
|
|
117
|
+
transport: 'cdp-webview',
|
|
118
|
+
cdpManagerKey: managerKey,
|
|
119
|
+
instanceKey: `ide:${managerKey}`,
|
|
120
|
+
});
|
|
106
121
|
}
|
|
107
122
|
|
|
108
123
|
return ideInstance;
|