adhdev 0.1.34 → 0.1.36

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 (2) hide show
  1. package/dist/index.js +2174 -265
  2. package/package.json +55 -55
package/dist/index.js CHANGED
@@ -32,18 +32,473 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
32
32
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
33
33
  mod
34
34
  ));
35
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
36
 
36
37
  // ../core/dist/chunk-PP6RFM75.js
38
+ function generateSecureToken(type) {
39
+ const prefix = TOKEN_PREFIX[type];
40
+ const randomBytes = new Uint8Array(32);
41
+ if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
42
+ crypto.getRandomValues(randomBytes);
43
+ } else {
44
+ for (let i = 0; i < 32; i++) {
45
+ randomBytes[i] = Math.floor(Math.random() * 256);
46
+ }
47
+ }
48
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
49
+ let token = prefix;
50
+ for (const byte of randomBytes) {
51
+ token += chars[byte % chars.length];
52
+ }
53
+ return token;
54
+ }
55
+ var TOKEN_PREFIX, AuthClient, FileTokenStorage;
37
56
  var init_chunk_PP6RFM75 = __esm({
38
57
  "../core/dist/chunk-PP6RFM75.js"() {
39
58
  "use strict";
59
+ TOKEN_PREFIX = {
60
+ connection: "adc_",
61
+ apiKey: "adk_",
62
+ refresh: "adr_"
63
+ };
64
+ AuthClient = class {
65
+ options;
66
+ constructor(options) {
67
+ this.options = options;
68
+ }
69
+ /**
70
+ * OAuth PKCE 로그인 URL 생성
71
+ */
72
+ async getLoginUrl(provider) {
73
+ const codeVerifier = this.generateCodeVerifier();
74
+ const codeChallenge = await this.generateCodeChallenge(codeVerifier);
75
+ const state = this.generateRandomString(32);
76
+ const params = new URLSearchParams({
77
+ provider,
78
+ code_challenge: codeChallenge,
79
+ code_challenge_method: "S256",
80
+ state,
81
+ redirect_uri: `${this.options.serverUrl}/auth/callback`
82
+ });
83
+ return {
84
+ url: `${this.options.serverUrl}/auth/login?${params}`,
85
+ codeVerifier,
86
+ state
87
+ };
88
+ }
89
+ /**
90
+ * OAuth 콜백 처리 → 토큰 저장
91
+ */
92
+ async handleCallback(code, codeVerifier) {
93
+ const response = await fetch(`${this.options.serverUrl}/auth/token`, {
94
+ method: "POST",
95
+ headers: { "Content-Type": "application/json" },
96
+ body: JSON.stringify({ code, code_verifier: codeVerifier })
97
+ });
98
+ if (!response.ok) {
99
+ throw new Error(`Auth failed: ${response.statusText}`);
100
+ }
101
+ const tokens = await response.json();
102
+ await this.options.storage.set("access_token", tokens.accessToken);
103
+ await this.options.storage.set("refresh_token", tokens.refreshToken);
104
+ return tokens;
105
+ }
106
+ /**
107
+ * Access Token 갱신
108
+ */
109
+ async refreshAccessToken() {
110
+ const refreshToken = await this.options.storage.get("refresh_token");
111
+ if (!refreshToken) return null;
112
+ try {
113
+ const response = await fetch(`${this.options.serverUrl}/auth/refresh`, {
114
+ method: "POST",
115
+ headers: { "Content-Type": "application/json" },
116
+ body: JSON.stringify({ refresh_token: refreshToken })
117
+ });
118
+ if (!response.ok) {
119
+ await this.logout();
120
+ return null;
121
+ }
122
+ const { accessToken, refreshToken: newRefreshToken } = await response.json();
123
+ await this.options.storage.set("access_token", accessToken);
124
+ if (newRefreshToken) {
125
+ await this.options.storage.set("refresh_token", newRefreshToken);
126
+ }
127
+ return accessToken;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+ /**
133
+ * 현재 유효한 Access Token 가져오기 (필요시 자동 갱신)
134
+ */
135
+ async getValidAccessToken() {
136
+ const token = await this.options.storage.get("access_token");
137
+ if (!token) return this.refreshAccessToken();
138
+ if (this.isTokenExpired(token)) {
139
+ return this.refreshAccessToken();
140
+ }
141
+ return token;
142
+ }
143
+ /**
144
+ * Connection Token 생성 (IDE 브리지용)
145
+ */
146
+ async createConnectionToken() {
147
+ const accessToken = await this.getValidAccessToken();
148
+ if (!accessToken) throw new Error("Not authenticated");
149
+ const response = await fetch(
150
+ `${this.options.serverUrl}/auth/connection-token`,
151
+ {
152
+ method: "POST",
153
+ headers: {
154
+ Authorization: `Bearer ${accessToken}`,
155
+ "Content-Type": "application/json"
156
+ }
157
+ }
158
+ );
159
+ if (!response.ok) throw new Error("Failed to create connection token");
160
+ const { connectionToken } = await response.json();
161
+ await this.options.storage.set("connection_token", connectionToken);
162
+ return connectionToken;
163
+ }
164
+ /**
165
+ * API Key 생성
166
+ */
167
+ async createApiKey(name, scopes) {
168
+ const accessToken = await this.getValidAccessToken();
169
+ if (!accessToken) throw new Error("Not authenticated");
170
+ const response = await fetch(`${this.options.serverUrl}/api/v1/api-keys`, {
171
+ method: "POST",
172
+ headers: {
173
+ Authorization: `Bearer ${accessToken}`,
174
+ "Content-Type": "application/json"
175
+ },
176
+ body: JSON.stringify({ name, scopes })
177
+ });
178
+ if (!response.ok) throw new Error("Failed to create API key");
179
+ return response.json();
180
+ }
181
+ /**
182
+ * 로그아웃
183
+ */
184
+ async logout() {
185
+ await this.options.storage.delete("access_token");
186
+ await this.options.storage.delete("refresh_token");
187
+ await this.options.storage.delete("connection_token");
188
+ }
189
+ /**
190
+ * 로그인 여부 확인
191
+ */
192
+ async isAuthenticated() {
193
+ const token = await this.getValidAccessToken();
194
+ return token !== null;
195
+ }
196
+ // ─── Crypto Helpers ───────────────────────────────────────
197
+ generateCodeVerifier() {
198
+ return this.generateRandomString(64);
199
+ }
200
+ async generateCodeChallenge(verifier) {
201
+ if (typeof globalThis.crypto?.subtle !== "undefined") {
202
+ const encoder = new TextEncoder();
203
+ const data = encoder.encode(verifier);
204
+ const hash2 = await crypto.subtle.digest("SHA-256", data);
205
+ return this.base64url(new Uint8Array(hash2));
206
+ }
207
+ const { createHash: createHash3 } = await import("crypto");
208
+ const hash = createHash3("sha256").update(verifier).digest();
209
+ return this.base64url(new Uint8Array(hash));
210
+ }
211
+ generateRandomString(length) {
212
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
213
+ let result = "";
214
+ const randomValues = new Uint8Array(length);
215
+ if (typeof globalThis.crypto?.getRandomValues !== "undefined") {
216
+ crypto.getRandomValues(randomValues);
217
+ } else {
218
+ for (let i = 0; i < length; i++) {
219
+ randomValues[i] = Math.floor(Math.random() * 256);
220
+ }
221
+ }
222
+ for (let i = 0; i < length; i++) {
223
+ result += chars[randomValues[i] % chars.length];
224
+ }
225
+ return result;
226
+ }
227
+ base64url(bytes) {
228
+ let binary = "";
229
+ for (const byte of bytes) {
230
+ binary += String.fromCharCode(byte);
231
+ }
232
+ if (typeof btoa !== "undefined") {
233
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
234
+ }
235
+ return Buffer.from(bytes).toString("base64url");
236
+ }
237
+ isTokenExpired(jwt) {
238
+ try {
239
+ const parts = jwt.split(".");
240
+ if (parts.length !== 3) return true;
241
+ const payload = JSON.parse(
242
+ typeof atob !== "undefined" ? atob(parts[1]) : Buffer.from(parts[1], "base64").toString()
243
+ );
244
+ if (!payload.exp) return false;
245
+ return Date.now() >= payload.exp * 1e3;
246
+ } catch {
247
+ return true;
248
+ }
249
+ }
250
+ };
251
+ FileTokenStorage = class {
252
+ configDir;
253
+ constructor(configDir) {
254
+ this.configDir = configDir;
255
+ }
256
+ async get(key) {
257
+ const { existsSync: existsSync7, readFileSync: readFileSync7 } = await import("fs");
258
+ const { join: join9 } = await import("path");
259
+ const filePath = join9(this.configDir, `${key}.secret`);
260
+ if (!existsSync7(filePath)) return null;
261
+ try {
262
+ return readFileSync7(filePath, "utf-8").trim();
263
+ } catch {
264
+ return null;
265
+ }
266
+ }
267
+ async set(key, value) {
268
+ const { mkdirSync: mkdirSync7, writeFileSync: writeFileSync5, chmodSync } = await import("fs");
269
+ const { join: join9 } = await import("path");
270
+ const { existsSync: existsSync7 } = await import("fs");
271
+ if (!existsSync7(this.configDir)) {
272
+ mkdirSync7(this.configDir, { recursive: true });
273
+ }
274
+ const filePath = join9(this.configDir, `${key}.secret`);
275
+ writeFileSync5(filePath, value, "utf-8");
276
+ try {
277
+ chmodSync(filePath, 384);
278
+ } catch {
279
+ }
280
+ }
281
+ async delete(key) {
282
+ const { unlinkSync: unlinkSync4, existsSync: existsSync7 } = await import("fs");
283
+ const { join: join9 } = await import("path");
284
+ const filePath = join9(this.configDir, `${key}.secret`);
285
+ if (existsSync7(filePath)) {
286
+ unlinkSync4(filePath);
287
+ }
288
+ }
289
+ };
40
290
  }
41
291
  });
42
292
 
43
293
  // ../core/dist/chunk-ZZGGFYGY.js
294
+ function getCompatibilityManager(matrixUrl) {
295
+ if (!_instance) {
296
+ _instance = new CompatibilityManager(matrixUrl);
297
+ }
298
+ return _instance;
299
+ }
300
+ var DEFAULT_MATRIX, CompatibilityManager, _instance;
44
301
  var init_chunk_ZZGGFYGY = __esm({
45
302
  "../core/dist/chunk-ZZGGFYGY.js"() {
46
303
  "use strict";
304
+ DEFAULT_MATRIX = {
305
+ version: "2026-02-25-default",
306
+ ides: {
307
+ vscode: {
308
+ minVersion: "1.85.0",
309
+ testedVersions: ["1.85.0", "1.90.0", "1.95.0", "2.0.0", "2.5.0"]
310
+ },
311
+ cursor: {
312
+ minVersion: "0.40.0",
313
+ testedVersions: ["0.40.0", "0.42.0", "0.45.0"],
314
+ notes: "Forked VS Code, some command IDs may differ"
315
+ },
316
+ vscodium: {
317
+ minVersion: "1.85.0",
318
+ testedVersions: ["1.85.0", "1.90.0"]
319
+ },
320
+ windsurf: {
321
+ minVersion: "1.0.0",
322
+ testedVersions: ["1.0.0"]
323
+ }
324
+ },
325
+ agents: {
326
+ "roo-code": {
327
+ extensionId: "rooveterinaryinc.roo-cline",
328
+ versions: {
329
+ "*": {
330
+ send: "roo-cline.sendMessage",
331
+ approve: "roo-cline.approveAction",
332
+ reject: "roo-cline.rejectAction",
333
+ status: "roo-cline.getStatus",
334
+ open: "roo-cline.focus"
335
+ }
336
+ }
337
+ },
338
+ copilot: {
339
+ extensionId: "github.copilot",
340
+ versions: {
341
+ "*": {
342
+ send: "github.copilot.chat.sendMessage",
343
+ accept: "editor.action.inlineSuggest.commit",
344
+ reject: "editor.action.inlineSuggest.hide",
345
+ open: "github.copilot.chat.focus"
346
+ }
347
+ }
348
+ },
349
+ cline: {
350
+ extensionId: "saoudrizwan.claude-dev",
351
+ versions: {
352
+ "*": {
353
+ send: "cline.sendMessage",
354
+ approve: "cline.approveAction",
355
+ reject: "cline.rejectAction",
356
+ open: "cline.focus"
357
+ }
358
+ }
359
+ },
360
+ continue: {
361
+ extensionId: "continue.continue",
362
+ versions: {
363
+ "*": {
364
+ send: "continue.sendToChat",
365
+ open: "continue.focusChat"
366
+ }
367
+ }
368
+ }
369
+ }
370
+ };
371
+ CompatibilityManager = class {
372
+ matrix;
373
+ matrixUrl;
374
+ lastFetch = 0;
375
+ fetchIntervalMs = 36e5;
376
+ // 1시간마다 갱신
377
+ constructor(matrixUrl) {
378
+ this.matrix = { ...DEFAULT_MATRIX };
379
+ this.matrixUrl = matrixUrl || "https://api.adhdev.io/compatibility/matrix.json";
380
+ }
381
+ /**
382
+ * 원격에서 최신 호환성 매트릭스 가져오기
383
+ */
384
+ async fetchMatrix() {
385
+ const now = Date.now();
386
+ if (now - this.lastFetch < this.fetchIntervalMs) return;
387
+ try {
388
+ const response = await fetch(this.matrixUrl);
389
+ if (response.ok) {
390
+ const remote = await response.json();
391
+ this.matrix = remote;
392
+ this.lastFetch = now;
393
+ console.log(`[Compat] Matrix updated: ${remote.version}`);
394
+ }
395
+ } catch (error) {
396
+ console.warn("[Compat] Failed to fetch remote matrix, using local fallback");
397
+ }
398
+ }
399
+ /**
400
+ * 특정 IDE가 지원되는지 확인
401
+ */
402
+ isIDESupported(ideType, ideVersion) {
403
+ const entry = this.matrix.ides[ideType];
404
+ if (!entry) {
405
+ return {
406
+ supported: false,
407
+ tested: false,
408
+ warnings: [`IDE '${ideType}' is not in the compatibility matrix`]
409
+ };
410
+ }
411
+ const warnings = [];
412
+ const supported = this.compareVersions(ideVersion, entry.minVersion) >= 0;
413
+ const tested = entry.testedVersions.includes(ideVersion);
414
+ if (!tested && supported) {
415
+ warnings.push(
416
+ `IDE version ${ideVersion} has not been tested. Tested versions: ${entry.testedVersions.join(", ")}`
417
+ );
418
+ }
419
+ if (entry.knownIssues?.[ideVersion]) {
420
+ warnings.push(`Known issue for v${ideVersion}: ${entry.knownIssues[ideVersion]}`);
421
+ }
422
+ return { supported, tested, warnings };
423
+ }
424
+ /**
425
+ * AI 에이전트의 명령어 확인
426
+ * 버전에 맞는 정확한 VS Code 명령어 ID를 반환
427
+ */
428
+ getAgentCommand(agentType, action, agentVersion) {
429
+ const agent = this.matrix.agents[agentType];
430
+ if (!agent) {
431
+ return { command: null, unsupported: true };
432
+ }
433
+ let commands = null;
434
+ if (agentVersion) {
435
+ const majorMinor = agentVersion.split(".").slice(0, 2).join(".");
436
+ const major = agentVersion.split(".")[0] + ".x";
437
+ commands = agent.versions[agentVersion] || agent.versions[majorMinor] || agent.versions[major] || agent.versions["*"] || null;
438
+ } else {
439
+ commands = agent.versions["*"] || Object.values(agent.versions)[0] || null;
440
+ }
441
+ if (!commands) {
442
+ return { command: null, unsupported: true };
443
+ }
444
+ const command = commands[action];
445
+ if (!command) {
446
+ const availableActions = Object.keys(commands);
447
+ return {
448
+ command: null,
449
+ unsupported: true,
450
+ fallback: `Available actions for ${agentType}: ${availableActions.join(", ")}`
451
+ };
452
+ }
453
+ return { command, unsupported: false };
454
+ }
455
+ /**
456
+ * 현재 매트릭스 버전 정보
457
+ */
458
+ getMatrixVersion() {
459
+ return this.matrix.version;
460
+ }
461
+ /**
462
+ * 매트릭스를 수동으로 업데이트 (테스트/디버그용)
463
+ */
464
+ updateMatrix(matrix) {
465
+ this.matrix = matrix;
466
+ }
467
+ /**
468
+ * 지원되는 모든 에이전트 목록
469
+ */
470
+ getSupportedAgents() {
471
+ return Object.keys(this.matrix.agents);
472
+ }
473
+ /**
474
+ * 특정 에이전트의 지원 액션 목록
475
+ */
476
+ getAgentActions(agentType) {
477
+ const agent = this.matrix.agents[agentType];
478
+ if (!agent) return [];
479
+ const actions = /* @__PURE__ */ new Set();
480
+ for (const commands of Object.values(agent.versions)) {
481
+ for (const action of Object.keys(commands)) {
482
+ actions.add(action);
483
+ }
484
+ }
485
+ return Array.from(actions);
486
+ }
487
+ // ─── Private Helpers ──────────────────────────────────────
488
+ compareVersions(a, b) {
489
+ const pa = a.split(".").map(Number);
490
+ const pb = b.split(".").map(Number);
491
+ const len = Math.max(pa.length, pb.length);
492
+ for (let i = 0; i < len; i++) {
493
+ const va = pa[i] || 0;
494
+ const vb = pb[i] || 0;
495
+ if (va > vb) return 1;
496
+ if (va < vb) return -1;
497
+ }
498
+ return 0;
499
+ }
500
+ };
501
+ _instance = null;
47
502
  }
48
503
  });
49
504
 
@@ -87,18 +542,18 @@ function checkPathExists(paths) {
87
542
  return null;
88
543
  }
89
544
  async function detectIDEs() {
90
- const os10 = (0, import_os.platform)();
545
+ const os11 = (0, import_os.platform)();
91
546
  const results = [];
92
547
  for (const def of IDE_DEFINITIONS) {
93
548
  const cliPath = findCliCommand(def.cli);
94
- const appPath = checkPathExists(def.paths[os10] || []);
549
+ const appPath = checkPathExists(def.paths[os11] || []);
95
550
  const installed = !!(cliPath || appPath);
96
551
  let resolvedCli = cliPath;
97
- if (!resolvedCli && appPath && os10 === "darwin") {
552
+ if (!resolvedCli && appPath && os11 === "darwin") {
98
553
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
99
554
  if ((0, import_fs.existsSync)(bundledCli)) resolvedCli = bundledCli;
100
555
  }
101
- if (!resolvedCli && appPath && os10 === "win32") {
556
+ if (!resolvedCli && appPath && os11 === "win32") {
102
557
  const { dirname: dirname4 } = await import("path");
103
558
  const appDir = dirname4(appPath);
104
559
  const candidates = [
@@ -130,6 +585,18 @@ async function detectIDEs() {
130
585
  }
131
586
  return results;
132
587
  }
588
+ async function getInstalledIDEs() {
589
+ const all = await detectIDEs();
590
+ return all.filter((ide) => ide.installed);
591
+ }
592
+ async function isIdeInstalled(ideId) {
593
+ const all = await detectIDEs();
594
+ const ide = all.find((i) => i.id === ideId);
595
+ return ide?.installed ?? false;
596
+ }
597
+ function getIdeDefinitions() {
598
+ return IDE_DEFINITIONS;
599
+ }
133
600
  var import_child_process, import_fs, import_os, IDE_DEFINITIONS;
134
601
  var init_chunk_BFUPGBW2 = __esm({
135
602
  "../core/dist/chunk-BFUPGBW2.js"() {
@@ -230,6 +697,20 @@ var init_chunk_BFUPGBW2 = __esm({
230
697
  });
231
698
 
232
699
  // ../core/dist/index.js
700
+ var dist_exports = {};
701
+ __export(dist_exports, {
702
+ AuthClient: () => AuthClient,
703
+ CompatibilityManager: () => CompatibilityManager,
704
+ DAEMON_WS_PATH: () => DAEMON_WS_PATH,
705
+ DEFAULT_DAEMON_PORT: () => DEFAULT_DAEMON_PORT,
706
+ FileTokenStorage: () => FileTokenStorage,
707
+ detectIDEs: () => detectIDEs,
708
+ generateSecureToken: () => generateSecureToken,
709
+ getCompatibilityManager: () => getCompatibilityManager,
710
+ getIdeDefinitions: () => getIdeDefinitions,
711
+ getInstalledIDEs: () => getInstalledIDEs,
712
+ isIdeInstalled: () => isIdeInstalled
713
+ });
233
714
  var DEFAULT_DAEMON_PORT, DAEMON_WS_PATH;
234
715
  var init_dist = __esm({
235
716
  "../core/dist/index.js"() {
@@ -364,8 +845,9 @@ async function detectCLIs() {
364
845
  return results;
365
846
  }
366
847
  async function detectCLI(cliId) {
848
+ const normalizedId = cliId === "claude-cli" ? "claude-code" : cliId;
367
849
  const all = await detectCLIs();
368
- return all.find((c) => c.id === cliId && c.installed) || null;
850
+ return all.find((c) => c.id === normalizedId && c.installed) || null;
369
851
  }
370
852
  var import_child_process3, os, KNOWN_CLIS;
371
853
  var init_cli_detector = __esm({
@@ -538,7 +1020,7 @@ function detectCurrentWorkspace(ideId) {
538
1020
  }
539
1021
  } else if (plat === "win32") {
540
1022
  try {
541
- const fs7 = require("fs");
1023
+ const fs6 = require("fs");
542
1024
  const appNameMap = {
543
1025
  vscode: "Code",
544
1026
  cursor: "Cursor",
@@ -553,8 +1035,8 @@ function detectCurrentWorkspace(ideId) {
553
1035
  appName,
554
1036
  "storage.json"
555
1037
  );
556
- if (fs7.existsSync(storagePath)) {
557
- const data = JSON.parse(fs7.readFileSync(storagePath, "utf-8"));
1038
+ if (fs6.existsSync(storagePath)) {
1039
+ const data = JSON.parse(fs6.readFileSync(storagePath, "utf-8"));
558
1040
  const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
559
1041
  if (workspaces.length > 0) {
560
1042
  const recent = workspaces[0];
@@ -933,6 +1415,47 @@ var init_cli_bridge = __esm({
933
1415
  function stripAnsi(str) {
934
1416
  return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
935
1417
  }
1418
+ function cleanGeminiResponse(raw, lastUserInput) {
1419
+ const lines = raw.split("\n");
1420
+ const cleaned = [];
1421
+ for (const line of lines) {
1422
+ const trimmed = line.trim();
1423
+ if (!trimmed) {
1424
+ cleaned.push("");
1425
+ continue;
1426
+ }
1427
+ if (/^[\u256d\u256e\u2570\u256f\u2502\u251c\u2524\u252c\u2534\u253c\u2500\u2501\u2550\u2554\u2557\u255a\u255d\u2551]+$/.test(trimmed)) continue;
1428
+ if (/^[\u256d\u2570]\u2500\u2500/.test(trimmed) || /\u2500\u2500[\u256e\u256f]$/.test(trimmed)) continue;
1429
+ if (/^\u2502.*\u2502$/.test(trimmed)) continue;
1430
+ if (/[\u2500\u2501\u2550\u2580\u2584]{4,}/.test(trimmed)) continue;
1431
+ if (/^YOLO\s+ctrl\+y/i.test(trimmed)) continue;
1432
+ if (/^\? for shortcuts/.test(trimmed)) continue;
1433
+ if (/^\/model\s/i.test(trimmed)) continue;
1434
+ if (/^~\s.*no sandbox/i.test(trimmed)) continue;
1435
+ if (/^Type your message/i.test(trimmed)) continue;
1436
+ if (/ctrl\+[a-z]/i.test(trimmed) && trimmed.length < 50) continue;
1437
+ if (/Shortcuts?\s*\(for more/i.test(trimmed)) continue;
1438
+ if (/shell mode|cycle mode|paste images|select file or folder|reverse-search|clear prompt|rewind|raw markdown|open external editor/i.test(trimmed)) continue;
1439
+ if (/Shift\+Tab|Option\+M|Ctrl\+X|Ctrl\+R|Ctrl\+Y/i.test(trimmed) && /mode|prompt|editor|search/i.test(trimmed)) continue;
1440
+ if (/^[\u276f>]\s*$/.test(trimmed)) continue;
1441
+ if (/^[*\u2022]\s*$/.test(trimmed)) continue;
1442
+ if (lastUserInput && /^[*\u2022]\s/.test(trimmed)) {
1443
+ const bulletContent = trimmed.replace(/^[*\u2022]\s*/, "").trim();
1444
+ if (bulletContent === lastUserInput.trim() || lastUserInput.trim().startsWith(bulletContent.slice(0, 15))) {
1445
+ continue;
1446
+ }
1447
+ }
1448
+ if (/Gemini CLI update available/i.test(trimmed)) continue;
1449
+ if (/brew upgrade gemini-cli/i.test(trimmed)) continue;
1450
+ if (/Installed via .+ Please update/i.test(trimmed)) continue;
1451
+ if (/Waiting for auth/i.test(trimmed)) continue;
1452
+ if (/ℹ Gemini CLI/i.test(trimmed)) continue;
1453
+ if (/^[\u280b\u2819\u2839\u2838\u283c\u2834\u2826\u2827\u2807\u280f\s]+$/.test(trimmed)) continue;
1454
+ if (trimmed.length <= 2 && /^[!@#$%^&*()_+=\-<>?/\\|~`]+$/.test(trimmed)) continue;
1455
+ cleaned.push(line);
1456
+ }
1457
+ return cleaned.join("\n").replace(/\n{3,}/g, "\n\n").trim();
1458
+ }
936
1459
  function findGeminiBinary() {
937
1460
  const isWin = os3.platform() === "win32";
938
1461
  const cmd = isWin ? "where gemini" : "which gemini";
@@ -983,12 +1506,20 @@ var init_gemini_cli = __esm({
983
1506
  ready = false;
984
1507
  startupBuffer = "";
985
1508
  startupDialogDismissed = false;
1509
+ // Raw PTY I/O (터미널 뷰용)
1510
+ onPtyData = null;
1511
+ ptyOutputBuffer = "";
1512
+ ptyOutputFlushTimer = null;
986
1513
  constructor(workingDir) {
987
1514
  this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os3.homedir()) : workingDir;
988
1515
  }
989
1516
  setOnStatusChange(callback) {
990
1517
  this.onStatusChange = callback;
991
1518
  }
1519
+ /** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
1520
+ setOnPtyData(callback) {
1521
+ this.onPtyData = callback;
1522
+ }
992
1523
  /**
993
1524
  * PTY로 gemini 프로세스 시작 (인터랙티브 모드)
994
1525
  */
@@ -1000,7 +1531,7 @@ var init_gemini_cli = __esm({
1000
1531
  console.log(`[GeminiAdapter] Spawning interactive session in ${this.workingDir}`);
1001
1532
  console.log(`[GeminiAdapter] Binary: ${geminiBin}`);
1002
1533
  const shell = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
1003
- const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c", `"${geminiBin}" --yolo`];
1534
+ const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c", `${geminiBin} --yolo`];
1004
1535
  console.log(`[GeminiAdapter] Shell: ${shell} ${shellArgs.join(" ")}`);
1005
1536
  this.ptyProcess = pty.spawn(shell, shellArgs, {
1006
1537
  name: "xterm-256color",
@@ -1013,6 +1544,18 @@ var init_gemini_cli = __esm({
1013
1544
  });
1014
1545
  this.ptyProcess.onData((data) => {
1015
1546
  this.handleOutput(data);
1547
+ if (this.onPtyData) {
1548
+ this.ptyOutputBuffer += data;
1549
+ if (!this.ptyOutputFlushTimer) {
1550
+ this.ptyOutputFlushTimer = setTimeout(() => {
1551
+ if (this.ptyOutputBuffer && this.onPtyData) {
1552
+ this.onPtyData(this.ptyOutputBuffer);
1553
+ }
1554
+ this.ptyOutputBuffer = "";
1555
+ this.ptyOutputFlushTimer = null;
1556
+ }, 50);
1557
+ }
1558
+ }
1016
1559
  });
1017
1560
  this.ptyProcess.onExit(({ exitCode }) => {
1018
1561
  console.log(`[GeminiAdapter] Process exited with code ${exitCode}`);
@@ -1091,6 +1634,8 @@ var init_gemini_cli = __esm({
1091
1634
  }
1092
1635
  }
1093
1636
  response = lines.join("\n").trim();
1637
+ const lastUserText = this.messages.length > 0 && this.messages[this.messages.length - 1].role === "user" ? this.messages[this.messages.length - 1].content : void 0;
1638
+ response = cleanGeminiResponse(response, lastUserText);
1094
1639
  if (response) {
1095
1640
  this.messages.push({
1096
1641
  role: "assistant",
@@ -1185,6 +1730,21 @@ var init_gemini_cli = __esm({
1185
1730
  isReady() {
1186
1731
  return this.ready;
1187
1732
  }
1733
+ /** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
1734
+ writeRaw(data) {
1735
+ if (this.ptyProcess) {
1736
+ this.ptyProcess.write(data);
1737
+ }
1738
+ }
1739
+ /** PTY 터미널 사이즈 변경 */
1740
+ resize(cols, rows) {
1741
+ if (this.ptyProcess) {
1742
+ try {
1743
+ this.ptyProcess.resize(cols, rows);
1744
+ } catch {
1745
+ }
1746
+ }
1747
+ }
1188
1748
  /** 작업 디렉토리 변경 (세션 재시작) */
1189
1749
  async changeWorkingDir(newDir) {
1190
1750
  this.cancel();
@@ -1239,6 +1799,7 @@ var init_claude_cli = __esm({
1239
1799
  startupBuffer = "";
1240
1800
  bridge = null;
1241
1801
  logBuffer = [];
1802
+ onPtyDataCallback = null;
1242
1803
  // Patterns for Claude Code
1243
1804
  PROMPT_PATTERNS = [
1244
1805
  /❯\s*$/,
@@ -1282,6 +1843,7 @@ var init_claude_cli = __esm({
1282
1843
  env: { ...process.env }
1283
1844
  });
1284
1845
  this.ptyProcess.onData((data) => {
1846
+ this.onPtyDataCallback?.(data);
1285
1847
  this.handleOutput(data);
1286
1848
  });
1287
1849
  this.ptyProcess.onExit(({ exitCode }) => {
@@ -1407,6 +1969,25 @@ var init_claude_cli = __esm({
1407
1969
  await new Promise((r) => setTimeout(r, 1e3));
1408
1970
  await this.spawn();
1409
1971
  }
1972
+ /** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
1973
+ setOnPtyData(callback) {
1974
+ this.onPtyDataCallback = callback;
1975
+ }
1976
+ /** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
1977
+ writeRaw(data) {
1978
+ if (this.ptyProcess) {
1979
+ this.ptyProcess.write(data);
1980
+ }
1981
+ }
1982
+ /** PTY 터미널 사이즈 변경 */
1983
+ resize(cols, rows) {
1984
+ if (this.ptyProcess) {
1985
+ try {
1986
+ this.ptyProcess.resize(cols, rows);
1987
+ } catch {
1988
+ }
1989
+ }
1990
+ }
1410
1991
  };
1411
1992
  }
1412
1993
  });
@@ -1924,17 +2505,16 @@ var init_local_server = __esm({
1924
2505
  });
1925
2506
 
1926
2507
  // src/daemon-cdp.ts
1927
- var import_ws3, http, fs, KNOWN_AGENTS, DaemonCdpManager;
2508
+ var import_ws3, http, KNOWN_AGENTS, DaemonCdpManager;
1928
2509
  var init_daemon_cdp = __esm({
1929
2510
  "src/daemon-cdp.ts"() {
1930
2511
  "use strict";
1931
2512
  import_ws3 = __toESM(require("ws"));
1932
2513
  http = __toESM(require("http"));
1933
- fs = __toESM(require("fs"));
1934
2514
  KNOWN_AGENTS = [
1935
- { agentType: "cline", extensionId: "saoudrizwan.claude-dev", extensionIdPattern: /saoudrizwan\.claude-dev/ },
1936
- { agentType: "roo-code", extensionId: "rooveterinaryinc.roo-cline", extensionIdPattern: /rooveterinaryinc\.roo-cline/ },
1937
- { agentType: "continue", extensionId: "continue.continue", extensionIdPattern: /continue\.continue/ }
2515
+ { agentType: "cline", extensionId: "saoudrizwan.claude-dev", extensionIdPattern: /saoudrizwan\.claude-dev/i },
2516
+ { agentType: "roo-code", extensionId: "rooveterinaryinc.roo-cline", extensionIdPattern: /rooveterinaryinc\.roo-cline/i },
2517
+ { agentType: "continue", extensionId: "continue.continue", extensionIdPattern: /continue\.continue/i }
1938
2518
  ];
1939
2519
  DaemonCdpManager = class {
1940
2520
  ws = null;
@@ -1961,10 +2541,6 @@ var init_daemon_cdp = __esm({
1961
2541
  this.disableFallback = disableFallback;
1962
2542
  this.logFn = logFn || ((msg) => {
1963
2543
  console.log(msg);
1964
- try {
1965
- fs.appendFileSync("/tmp/adhdev_daemon_cdp.log", msg + "\n");
1966
- } catch {
1967
- }
1968
2544
  });
1969
2545
  }
1970
2546
  setPort(port) {
@@ -2273,6 +2849,62 @@ var init_daemon_cdp = __esm({
2273
2849
  })()
2274
2850
  `);
2275
2851
  }
2852
+ /**
2853
+ * CDP 프로토콜로 텍스트 입력 후 Enter 전송
2854
+ * Lexical 등 execCommand가 작동하지 않는 에디터에서 사용.
2855
+ *
2856
+ * 1. selector로 에디터 찾아서 focus + click
2857
+ * 2. Input.insertText로 텍스트 삽입
2858
+ * 3. Input.dispatchKeyEvent로 Enter 전송
2859
+ */
2860
+ async typeAndSend(selector, text) {
2861
+ if (!this.isConnected) return false;
2862
+ const focusResult = await this.evaluate(`(() => {
2863
+ const e = document.querySelector(${JSON.stringify(selector)});
2864
+ if (!e) return null;
2865
+ e.focus();
2866
+ const r = e.getBoundingClientRect();
2867
+ return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
2868
+ })()`);
2869
+ if (!focusResult) {
2870
+ this.log("[CDP] typeAndSend: selector not found");
2871
+ return false;
2872
+ }
2873
+ const pos = JSON.parse(focusResult);
2874
+ await this.sendInternal("Input.dispatchMouseEvent", {
2875
+ type: "mousePressed",
2876
+ x: Math.round(pos.x),
2877
+ y: Math.round(pos.y),
2878
+ button: "left",
2879
+ clickCount: 1
2880
+ });
2881
+ await this.sendInternal("Input.dispatchMouseEvent", {
2882
+ type: "mouseReleased",
2883
+ x: Math.round(pos.x),
2884
+ y: Math.round(pos.y),
2885
+ button: "left",
2886
+ clickCount: 1
2887
+ });
2888
+ await new Promise((r) => setTimeout(r, 150));
2889
+ await this.sendInternal("Input.insertText", { text });
2890
+ await new Promise((r) => setTimeout(r, 200));
2891
+ await this.sendInternal("Input.dispatchKeyEvent", {
2892
+ type: "rawKeyDown",
2893
+ key: "Enter",
2894
+ code: "Enter",
2895
+ windowsVirtualKeyCode: 13,
2896
+ nativeVirtualKeyCode: 13
2897
+ });
2898
+ await this.sendInternal("Input.dispatchKeyEvent", {
2899
+ type: "keyUp",
2900
+ key: "Enter",
2901
+ code: "Enter",
2902
+ windowsVirtualKeyCode: 13,
2903
+ nativeVirtualKeyCode: 13
2904
+ });
2905
+ this.log(`[CDP] typeAndSend: sent "${text.substring(0, 50)}..."`);
2906
+ return true;
2907
+ }
2276
2908
  // ─── Agent Webview Multi-Session ─────────────────────────
2277
2909
  async discoverAgentWebviews() {
2278
2910
  if (!this.isConnected) return [];
@@ -2705,21 +3337,17 @@ var require_lib = __commonJS({
2705
3337
  });
2706
3338
 
2707
3339
  // src/daemon-p2p.ts
2708
- var fs2, path2, os6, logFile, log, DaemonP2PSender;
3340
+ var fs, path2, os6, logFile, log, DaemonP2PSender;
2709
3341
  var init_daemon_p2p = __esm({
2710
3342
  "src/daemon-p2p.ts"() {
2711
3343
  "use strict";
2712
- fs2 = __toESM(require("fs"));
3344
+ fs = __toESM(require("fs"));
2713
3345
  path2 = __toESM(require("path"));
2714
3346
  os6 = __toESM(require("os"));
2715
3347
  logFile = path2.join(os6.tmpdir(), "adhdev_daemon_p2p.log");
2716
3348
  log = (msg) => {
2717
3349
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [P2P] ${msg}`;
2718
3350
  console.log(line);
2719
- try {
2720
- fs2.appendFileSync(logFile, line + "\n");
2721
- } catch {
2722
- }
2723
3351
  };
2724
3352
  DaemonP2PSender = class {
2725
3353
  bridge;
@@ -2729,7 +3357,13 @@ var init_daemon_p2p = __esm({
2729
3357
  fileRequestHandler = null;
2730
3358
  inputHandler = null;
2731
3359
  commandHandler = null;
3360
+ ptyInputHandler = null;
3361
+ ptyResizeHandler = null;
2732
3362
  _ssDebugDone = false;
3363
+ // PTY scrollback buffer per cliType (재연결 시 최근 출력 전송)
3364
+ ptyScrollback = /* @__PURE__ */ new Map();
3365
+ PTY_SCROLLBACK_MAX = 64 * 1024;
3366
+ // 64KB per CLI
2733
3367
  get screenshotActive() {
2734
3368
  for (const peer of this.peers.values()) {
2735
3369
  if (peer.screenshotActive && peer.state === "connected") return true;
@@ -2770,11 +3404,11 @@ var init_daemon_p2p = __esm({
2770
3404
  ];
2771
3405
  for (const candidate of candidates) {
2772
3406
  const prebuildPath = path2.join(candidate, "prebuilds", prebuildKey, "node_datachannel.node");
2773
- if (fs2.existsSync(prebuildPath)) {
3407
+ if (fs.existsSync(prebuildPath)) {
2774
3408
  const targetDir = path2.join(candidate, "build", "Release");
2775
3409
  const targetPath = path2.join(targetDir, "node_datachannel.node");
2776
- fs2.mkdirSync(targetDir, { recursive: true });
2777
- fs2.copyFileSync(prebuildPath, targetPath);
3410
+ fs.mkdirSync(targetDir, { recursive: true });
3411
+ fs.copyFileSync(prebuildPath, targetPath);
2778
3412
  try {
2779
3413
  delete require.cache[require.resolve("node-datachannel")];
2780
3414
  } catch {
@@ -2835,7 +3469,7 @@ var init_daemon_p2p = __esm({
2835
3469
  const configPath = path2.join(os6.homedir(), ".adhdev", "config.json");
2836
3470
  let token = "";
2837
3471
  try {
2838
- const config = JSON.parse(fs2.readFileSync(configPath, "utf-8"));
3472
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
2839
3473
  token = config.connectionToken || "";
2840
3474
  } catch {
2841
3475
  }
@@ -2973,7 +3607,10 @@ var init_daemon_p2p = __esm({
2973
3607
  screenshotCh.onError((err) => log(`Screenshots error: ${err}`));
2974
3608
  const filesCh = pc.createDataChannel("files");
2975
3609
  entry.filesChannel = filesCh;
2976
- filesCh.onOpen(() => log(`Files channel OPEN for peer ${pid}`));
3610
+ filesCh.onOpen(() => {
3611
+ log(`Files channel OPEN for peer ${pid}`);
3612
+ setTimeout(() => this.sendPtyScrollback(pid), 100);
3613
+ });
2977
3614
  filesCh.onClosed(() => {
2978
3615
  const peer = this.peers.get(pid);
2979
3616
  if (peer?.filesChannel === filesCh) peer.filesChannel = null;
@@ -3021,6 +3658,18 @@ var init_daemon_p2p = __esm({
3021
3658
  this.handleP2PCommand(peerId, parsed);
3022
3659
  return;
3023
3660
  }
3661
+ if (parsed.type === "pty_input") {
3662
+ if (this.ptyInputHandler && parsed.data) {
3663
+ this.ptyInputHandler(parsed.cliType || "gemini-cli", parsed.data);
3664
+ }
3665
+ return;
3666
+ }
3667
+ if (parsed.type === "pty_resize") {
3668
+ if (this.ptyResizeHandler && parsed.cols && parsed.rows) {
3669
+ this.ptyResizeHandler(parsed.cliType || "gemini-cli", parsed.cols, parsed.rows);
3670
+ }
3671
+ return;
3672
+ }
3024
3673
  this.handleFileRequest(peerId, parsed);
3025
3674
  } catch (e) {
3026
3675
  log(`Parse error from peer ${peerId}: ${e?.message}`);
@@ -3044,6 +3693,44 @@ var init_daemon_p2p = __esm({
3044
3693
  }
3045
3694
  return sentAny;
3046
3695
  }
3696
+ /** PTY 출력을 모든 connected peer에게 브로드캐스트 */
3697
+ broadcastPtyOutput(cliType, data) {
3698
+ const prev = this.ptyScrollback.get(cliType) || "";
3699
+ const updated = prev + data;
3700
+ this.ptyScrollback.set(
3701
+ cliType,
3702
+ updated.length > this.PTY_SCROLLBACK_MAX ? updated.slice(-this.PTY_SCROLLBACK_MAX) : updated
3703
+ );
3704
+ const msg = JSON.stringify({ type: "pty_output", cliType, data });
3705
+ let sentAny = false;
3706
+ for (const peer of this.peers.values()) {
3707
+ if (peer.state !== "connected" || !peer.filesChannel) continue;
3708
+ try {
3709
+ peer.filesChannel.sendMessage(msg);
3710
+ sentAny = true;
3711
+ } catch {
3712
+ }
3713
+ }
3714
+ return sentAny;
3715
+ }
3716
+ /** Peer 연결 시 scrollback 전송 */
3717
+ sendPtyScrollback(peerId) {
3718
+ const peer = this.peers.get(peerId);
3719
+ if (!peer?.filesChannel) return;
3720
+ for (const [cliType, buffer] of this.ptyScrollback) {
3721
+ if (!buffer) continue;
3722
+ try {
3723
+ peer.filesChannel.sendMessage(JSON.stringify({
3724
+ type: "pty_output",
3725
+ cliType,
3726
+ data: buffer,
3727
+ scrollback: true
3728
+ }));
3729
+ log(`Sent PTY scrollback to peer ${peerId}: ${cliType} (${buffer.length} bytes)`);
3730
+ } catch {
3731
+ }
3732
+ }
3733
+ }
3047
3734
  sendScreenshot(base64Data) {
3048
3735
  let sentAny = false;
3049
3736
  const buffer = Buffer.from(base64Data, "base64");
@@ -3081,6 +3768,12 @@ var init_daemon_p2p = __esm({
3081
3768
  onCommand(handler) {
3082
3769
  this.commandHandler = handler;
3083
3770
  }
3771
+ onPtyInput(handler) {
3772
+ this.ptyInputHandler = handler;
3773
+ }
3774
+ onPtyResize(handler) {
3775
+ this.ptyResizeHandler = handler;
3776
+ }
3084
3777
  // ─── P2P 명령/입력/파일 처리 ────────────────
3085
3778
  async handleP2PCommand(peerId, msg) {
3086
3779
  const { id, commandType, data } = msg;
@@ -3189,17 +3882,12 @@ var init_daemon_p2p = __esm({
3189
3882
 
3190
3883
  // src/daemon-script-loader.ts
3191
3884
  function log2(msg) {
3192
- try {
3193
- fs3.appendFileSync(`${require("os").tmpdir()}/adhdev_daemon_scripts.log`, `${msg}
3194
- `);
3195
- } catch {
3196
- }
3885
+ console.log(msg);
3197
3886
  }
3198
- var fs3, http2, https, DaemonScriptLoader;
3887
+ var http2, https, DaemonScriptLoader;
3199
3888
  var init_daemon_script_loader = __esm({
3200
3889
  "src/daemon-script-loader.ts"() {
3201
3890
  "use strict";
3202
- fs3 = __toESM(require("fs"));
3203
3891
  http2 = __toESM(require("http"));
3204
3892
  https = __toESM(require("https"));
3205
3893
  DaemonScriptLoader = class {
@@ -3209,9 +3897,13 @@ var init_daemon_script_loader = __esm({
3209
3897
  // name → code
3210
3898
  fallbacks = /* @__PURE__ */ new Map();
3211
3899
  // name → bundled fallback code
3900
+ capabilityMap = /* @__PURE__ */ new Map();
3901
+ // capability → { script, params }
3212
3902
  manifestVersion = "0";
3213
3903
  refreshTimer = null;
3214
3904
  token = "";
3905
+ platform = "";
3906
+ // darwin, win32, linux
3215
3907
  constructor(baseUrl, ideType) {
3216
3908
  this.baseUrl = baseUrl.replace(/\/$/, "");
3217
3909
  this.ideType = ideType;
@@ -3219,6 +3911,9 @@ var init_daemon_script_loader = __esm({
3219
3911
  setToken(token) {
3220
3912
  this.token = token;
3221
3913
  }
3914
+ setPlatform(platform9) {
3915
+ this.platform = platform9;
3916
+ }
3222
3917
  registerFallback(name, code) {
3223
3918
  this.fallbacks.set(name, code);
3224
3919
  }
@@ -3235,6 +3930,46 @@ var init_daemon_script_loader = __esm({
3235
3930
  has(name) {
3236
3931
  return this.cache.has(name) || this.fallbacks.has(name);
3237
3932
  }
3933
+ /** Resolve a logical capability to its IDE-specific script */
3934
+ getCapability(capability) {
3935
+ const entry = this.capabilityMap.get(capability);
3936
+ if (entry?.script) {
3937
+ return this.get(entry.script);
3938
+ }
3939
+ return this.get(capability);
3940
+ }
3941
+ /** Resolve capability with template params */
3942
+ getCapabilityWithParams(capability, params) {
3943
+ const entry = this.capabilityMap.get(capability);
3944
+ if (entry?.script) {
3945
+ return this.getWithParams(entry.script, params);
3946
+ }
3947
+ return this.getWithParams(capability, params);
3948
+ }
3949
+ /** Check if a capability is supported (implemented) */
3950
+ hasCapability(capability) {
3951
+ const entry = this.capabilityMap.get(capability);
3952
+ if (!entry) return this.has(capability);
3953
+ if (entry.status === "not_implemented" || entry.status === "not_needed") return false;
3954
+ return !!entry.script && this.has(entry.script);
3955
+ }
3956
+ /** Get the capability map for status reporting */
3957
+ getCapabilities() {
3958
+ const result = {};
3959
+ for (const [k, v] of this.capabilityMap) result[k] = v;
3960
+ return result;
3961
+ }
3962
+ /** Manually set capability map (for testing or static config) */
3963
+ setCapabilityMap(map) {
3964
+ this.capabilityMap.clear();
3965
+ for (const [cap, entry] of Object.entries(map)) {
3966
+ if (entry && typeof entry === "object" && "script" in entry) {
3967
+ this.capabilityMap.set(cap, entry);
3968
+ } else if (entry && typeof entry === "object" && "status" in entry) {
3969
+ this.capabilityMap.set(cap, entry);
3970
+ }
3971
+ }
3972
+ }
3238
3973
  async start() {
3239
3974
  log2(`[DaemonScriptLoader] Starting: baseUrl=${this.baseUrl} ide=${this.ideType}`);
3240
3975
  await this.refresh();
@@ -3315,6 +4050,22 @@ var init_daemon_script_loader = __esm({
3315
4050
  log2(`[DaemonScriptLoader] Downloading ${scriptNames.length} scripts for ${this.ideType}/${version}`);
3316
4051
  const downloads = scriptNames.map((name) => this.downloadScript(version, name));
3317
4052
  await Promise.allSettled(downloads);
4053
+ const capabilities = manifest.capabilities?.[this.ideType]?.[version];
4054
+ if (capabilities && typeof capabilities === "object") {
4055
+ this.capabilityMap.clear();
4056
+ for (const [cap, entry] of Object.entries(capabilities)) {
4057
+ if (entry && typeof entry === "object") {
4058
+ const osFilter = entry.os;
4059
+ if (osFilter && Array.isArray(osFilter) && this.platform && !osFilter.includes(this.platform)) {
4060
+ continue;
4061
+ }
4062
+ this.capabilityMap.set(cap, entry);
4063
+ }
4064
+ }
4065
+ log2(`[DaemonScriptLoader] Loaded ${this.capabilityMap.size} capabilities for ${this.ideType}/${version}`);
4066
+ } else {
4067
+ this.buildDefaultCapabilityMap(scriptNames);
4068
+ }
3318
4069
  log2(`[DaemonScriptLoader] Loaded ${this.cache.size} scripts`);
3319
4070
  } catch (e) {
3320
4071
  log2(`[DaemonScriptLoader] Refresh error: ${e?.message}`);
@@ -3338,28 +4089,47 @@ var init_daemon_script_loader = __esm({
3338
4089
  log2(`[DaemonScriptLoader] Download error ${name}: ${e?.message}`);
3339
4090
  }
3340
4091
  }
4092
+ /** Build a default capability map from script names when no explicit map exists */
4093
+ buildDefaultCapabilityMap(scriptNames) {
4094
+ this.capabilityMap.clear();
4095
+ for (const name of scriptNames) {
4096
+ this.capabilityMap.set(name, { script: name });
4097
+ }
4098
+ if (scriptNames.includes("resolve_modal")) {
4099
+ this.capabilityMap.set("resolve", { script: "resolve_modal", params: ["BUTTON_TEXT"] });
4100
+ } else if (scriptNames.includes("resolve_action")) {
4101
+ this.capabilityMap.set("resolve", { script: "resolve_action", params: ["ACTION"] });
4102
+ }
4103
+ log2(`[DaemonScriptLoader] Built default capability map: ${this.capabilityMap.size} entries`);
4104
+ }
3341
4105
  };
3342
4106
  }
3343
4107
  });
3344
4108
 
3345
4109
  // src/daemon-commands.ts
3346
- var fs4, path3, os7, DaemonCommandHandler;
4110
+ var fs2, path3, os7, DaemonCommandHandler;
3347
4111
  var init_daemon_commands = __esm({
3348
4112
  "src/daemon-commands.ts"() {
3349
4113
  "use strict";
3350
- fs4 = __toESM(require("fs"));
4114
+ fs2 = __toESM(require("fs"));
3351
4115
  path3 = __toESM(require("path"));
3352
4116
  os7 = __toESM(require("os"));
3353
4117
  init_config();
3354
4118
  DaemonCommandHandler = class {
3355
4119
  ctx;
3356
4120
  agentStream = null;
3357
- /** Get CDP manager for a specific ideType, or first connected one */
4121
+ /** Get CDP manager for a specific ideType.
4122
+ * When ideType is explicitly specified (via arg or _currentIdeType),
4123
+ * returns null if that IDE's CDP is unavailable — never falls back to another IDE.
4124
+ * Fallback to first connected manager only when no ideType is known at all.
4125
+ */
3358
4126
  getCdp(ideType) {
3359
4127
  const key = ideType || this._currentIdeType;
3360
4128
  if (key) {
3361
4129
  const m = this.ctx.cdpManagers.get(key.toLowerCase());
3362
4130
  if (m?.isConnected) return m;
4131
+ console.log(`[getCdp] \u26A0 Target IDE "${key}" CDP not available, refusing fallback`);
4132
+ return null;
3363
4133
  }
3364
4134
  for (const m of this.ctx.cdpManagers.values()) {
3365
4135
  if (m.isConnected) return m;
@@ -3368,10 +4138,17 @@ var init_daemon_commands = __esm({
3368
4138
  }
3369
4139
  /** Current IDE type extracted from command args (per-request) */
3370
4140
  _currentIdeType;
3371
- /** Extract ideType from _targetInstance (e.g. 'vscode_abc123' → 'vscode') */
4141
+ /** Extract ideType from _targetInstance
4142
+ * Handles both simple ('vscode_abc123') and composite ('doId:ide:vscode_abc123') formats.
4143
+ */
3372
4144
  extractIdeType(args) {
3373
4145
  if (args?._targetInstance) {
3374
- const parts = args._targetInstance.split("_");
4146
+ let raw = args._targetInstance;
4147
+ const ideMatch = raw.match(/:ide:(.+)$/);
4148
+ const cliMatch = raw.match(/:cli:(.+)$/);
4149
+ if (ideMatch) raw = ideMatch[1];
4150
+ else if (cliMatch) raw = cliMatch[1];
4151
+ const parts = raw.split("_");
3375
4152
  if (parts.length >= 2) return parts[0];
3376
4153
  }
3377
4154
  return void 0;
@@ -3394,6 +4171,7 @@ var init_daemon_commands = __esm({
3394
4171
  }
3395
4172
  async handle(cmd, args) {
3396
4173
  this._currentIdeType = this.extractIdeType(args);
4174
+ console.log(`[Cmd] ${cmd} \u2192 ideType=${this._currentIdeType || "none"} _targetInstance=${args?._targetInstance || "none"}`);
3397
4175
  switch (cmd) {
3398
4176
  // ─── CDP 직접 처리 ───────────────────
3399
4177
  case "read_chat":
@@ -3425,6 +4203,12 @@ var init_daemon_commands = __esm({
3425
4203
  return this.handleCdpRemoteAction(args);
3426
4204
  case "cdp_discover_agents":
3427
4205
  return this.handleDiscoverAgents(args);
4206
+ case "cdp_dom_dump":
4207
+ return this.handleCdpDomDump(args);
4208
+ case "cdp_dom_query":
4209
+ return this.handleCdpDomQuery(args);
4210
+ case "cdp_dom_debug":
4211
+ return this.handleCdpDomDebug(args);
3428
4212
  // ─── 파일 직접 처리 ──────────────────
3429
4213
  case "file_read":
3430
4214
  return this.handleFileRead(args);
@@ -3479,35 +4263,77 @@ var init_daemon_commands = __esm({
3479
4263
  return this.handleAgentStreamSwitchSession(args);
3480
4264
  case "agent_stream_focus":
3481
4265
  return this.handleAgentStreamFocus(args);
4266
+ // ─── PTY Raw I/O (터미널 뷰) ─────────
4267
+ case "pty_input":
4268
+ return this.handlePtyInput(args);
4269
+ case "pty_resize":
4270
+ return this.handlePtyResize(args);
3482
4271
  default:
3483
4272
  return { success: false, error: `Unknown command: ${cmd}` };
3484
4273
  }
3485
4274
  }
3486
4275
  // ─── CDP 기반 채팅 명령 ──────────────────────
3487
4276
  async handleReadChat(args) {
3488
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
4277
+ const cdp2 = this.getCdp();
4278
+ if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
3489
4279
  const script = this.getScriptLoader()?.get("read_chat");
3490
- if (!script) return { success: false, error: "read_chat script not loaded" };
4280
+ if (script) {
4281
+ try {
4282
+ const result = await cdp2.evaluate(script, 5e4);
4283
+ if (result && typeof result === "object" && result.messages?.length > 0) {
4284
+ console.log(`[read_chat] Server script OK: ${result.messages?.length} msgs`);
4285
+ return { success: true, ...result };
4286
+ }
4287
+ if (result && typeof result === "object") {
4288
+ console.log(`[read_chat] Server script returned ${result.messages?.length || 0} msgs, trying inline`);
4289
+ }
4290
+ } catch (e) {
4291
+ console.log(`[read_chat] Server script error: ${e.message}`);
4292
+ }
4293
+ }
3491
4294
  try {
3492
- const result = await this.getCdp().evaluate(script, 5e4);
3493
- return { success: true, ...result };
4295
+ const inlineScript = `(() => {
4296
+ const container = document.querySelector('.flex.flex-col.gap-2\\\\.5');
4297
+ if (!container) return { messages: [], status: 'idle' };
4298
+ const msgs = [];
4299
+ for (const child of container.children) {
4300
+ if ((child.className || '').includes('mark-js-ignore')) continue;
4301
+ if (child.offsetHeight < 10) continue;
4302
+ const inner = child.children[0];
4303
+ if (!inner) continue;
4304
+ const isUser = inner.className?.includes('flex-row') || !!inner.querySelector('.justify-end');
4305
+ const role = isUser ? 'user' : 'assistant';
4306
+ const text = child.textContent?.trim() || '';
4307
+ if (text.length < 1) continue;
4308
+ msgs.push({ id: 'msg_' + msgs.length, role, content: text.substring(0, 6000), index: msgs.length });
4309
+ }
4310
+ return { messages: msgs, status: 'idle', id: 'cascade' };
4311
+ })()`;
4312
+ const result = await cdp2.evaluate(inlineScript, 1e4);
4313
+ if (result && typeof result === "object") {
4314
+ const msgs = result.messages || [];
4315
+ console.log(`[read_chat] Inline fallback: ${msgs.length} msgs`);
4316
+ return { success: true, ...result };
4317
+ }
3494
4318
  } catch (e) {
3495
- return { success: false, error: e.message };
4319
+ console.log(`[read_chat] Inline fallback error: ${e.message}`);
3496
4320
  }
4321
+ return { success: true, messages: [], status: "idle" };
3497
4322
  }
3498
4323
  async handleSendChat(args) {
3499
4324
  const text = args?.text || args?.message;
3500
4325
  if (!text) return { success: false, error: "text required" };
4326
+ const _log = (msg) => console.log(`[send_chat] ${msg}`);
3501
4327
  const targetAgent = args?.agentType || args?.cliType || this._currentIdeType;
3502
4328
  if (targetAgent) {
3503
4329
  for (const [key, adapter] of this.ctx.adapters.entries()) {
3504
4330
  if (adapter.cliType === targetAgent || key.startsWith(targetAgent)) {
3505
- console.log(`[send_chat] Routing to CLI adapter: ${adapter.cliType} (${key})`);
4331
+ _log(`Routing to CLI adapter: ${adapter.cliType} (${key})`);
3506
4332
  try {
3507
4333
  await adapter.sendMessage(text);
3508
4334
  return { success: true, sent: true, targetAgent: adapter.cliType };
3509
4335
  } catch (e) {
3510
- console.log(`[send_chat] CLI adapter failed: ${e.message}`);
4336
+ _log(`CLI adapter failed: ${e.message}`);
3511
4337
  return { success: false, error: `CLI send failed: ${e.message}` };
3512
4338
  }
3513
4339
  }
@@ -3515,49 +4341,86 @@ var init_daemon_commands = __esm({
3515
4341
  }
3516
4342
  const targetCdp = this.getCdp();
3517
4343
  const targetLoader = this.getScriptLoader();
3518
- if (targetCdp?.isConnected && targetLoader) {
3519
- const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3520
- if (script) {
3521
- console.log(`[send_chat] Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
3522
- try {
3523
- await targetCdp.evaluate(script, 3e4);
3524
- return { success: true, sent: true };
3525
- } catch (e) {
3526
- console.log(`[send_chat] Failed on ${this._currentIdeType}: ${e.message}`);
4344
+ if (targetCdp?.isConnected) {
4345
+ _log(`Targeting IDE: ${this._currentIdeType || "fallback-first"}`);
4346
+ const inputSelector = '[data-lexical-editor="true"]';
4347
+ try {
4348
+ const sent = await targetCdp.typeAndSend(inputSelector, text);
4349
+ if (sent) {
4350
+ _log(`typeAndSend success`);
4351
+ return { success: true, sent: true, method: "typeAndSend" };
3527
4352
  }
4353
+ } catch (e) {
4354
+ _log(`typeAndSend failed: ${e.message}`);
3528
4355
  }
3529
- }
3530
- console.log(`[send_chat] No specific target, trying all CDPs...`);
3531
- for (const [ideType, cdp] of this.ctx.cdpManagers) {
3532
- if (!cdp.isConnected) continue;
3533
- const loader = this.ctx.scriptLoaders.get(ideType);
3534
- if (!loader) continue;
3535
- const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
4356
+ const ceSelector = '[contenteditable="true"][role="textbox"]';
4357
+ try {
4358
+ const sent = await targetCdp.typeAndSend(ceSelector, text);
4359
+ if (sent) {
4360
+ _log(`typeAndSend(ce) success`);
4361
+ return { success: true, sent: true, method: "typeAndSend-ce" };
4362
+ }
4363
+ } catch (e) {
4364
+ _log(`typeAndSend(ce) failed: ${e.message}`);
4365
+ }
4366
+ if (targetLoader) {
4367
+ const script = targetLoader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
4368
+ if (script) {
4369
+ try {
4370
+ const result = await targetCdp.evaluate(script, 3e4);
4371
+ _log(`script result: ${JSON.stringify(result)}`);
4372
+ return { success: true, sent: true, method: "script" };
4373
+ } catch (e) {
4374
+ _log(`script failed on ${this._currentIdeType}: ${e.message}`);
4375
+ }
4376
+ }
4377
+ }
4378
+ } else {
4379
+ _log(`No CDP (connected=${targetCdp?.isConnected}) for ${this._currentIdeType}`);
4380
+ }
4381
+ if (this._currentIdeType) {
4382
+ _log(`Target IDE "${this._currentIdeType}" CDP not available \u2014 refusing cross-IDE fallback`);
4383
+ return { success: false, error: `CDP for ${this._currentIdeType} not connected. Ensure the IDE is running with --remote-debugging-port.` };
4384
+ }
4385
+ _log(`No target IDE specified, trying all ${this.ctx.cdpManagers.size} CDPs...`);
4386
+ for (const [ideType, cdp2] of this.ctx.cdpManagers) {
4387
+ if (!cdp2.isConnected) continue;
4388
+ try {
4389
+ const sent = await cdp2.typeAndSend('[data-lexical-editor="true"]', text);
4390
+ if (sent) {
4391
+ _log(`fallback typeAndSend success on ${ideType}`);
4392
+ return { success: true, sent: true, targetIde: ideType, method: "typeAndSend" };
4393
+ }
4394
+ } catch {
4395
+ }
4396
+ const loader = this.ctx.scriptLoaders.get(ideType);
4397
+ if (!loader) continue;
4398
+ const script = loader.getWithParams("send_message", { MESSAGE: JSON.stringify(text) });
3536
4399
  if (!script) continue;
3537
- console.log(`[send_chat] Trying IDE: ${ideType}`);
4400
+ _log(`Trying script on ${ideType}`);
3538
4401
  try {
3539
- await cdp.evaluate(script, 3e4);
3540
- return { success: true, sent: true, targetIde: ideType };
4402
+ const result = await cdp2.evaluate(script, 3e4);
4403
+ _log(`Success on ${ideType}: ${JSON.stringify(result)}`);
4404
+ return { success: true, sent: true, targetIde: ideType, method: "script" };
3541
4405
  } catch (e) {
3542
- console.log(`[send_chat] Failed on ${ideType}: ${e.message}`);
4406
+ _log(`Failed on ${ideType}: ${e.message}`);
3543
4407
  }
3544
4408
  }
4409
+ _log("All methods failed");
3545
4410
  return { success: false, error: "No CDP or CLI adapter could send the message" };
3546
4411
  }
3547
4412
  async handleListChats(args) {
3548
- const cdp = this.getCdp();
3549
- if (!cdp?.isConnected) {
4413
+ const cdp2 = this.getCdp();
4414
+ if (!cdp2?.isConnected) {
3550
4415
  console.log(`[list_chats] CDP not connected, ideType=${this._currentIdeType}`);
3551
4416
  return { success: false, error: "CDP not connected" };
3552
4417
  }
3553
- const script = this.getScriptLoader()?.get("list_chats");
3554
- if (!script) {
3555
- console.log(`[list_chats] script not loaded`);
3556
- return { success: false, error: "list_chats script not loaded" };
3557
- }
4418
+ const loader = this.getScriptLoader();
4419
+ if (!loader) return { success: false, error: "Script loader not initialized" };
4420
+ const script = loader.getCapability("list_chats");
4421
+ if (!script) return { success: false, error: "list_chats script not available" };
3558
4422
  try {
3559
- console.log(`[list_chats] Evaluating on ideType=${this._currentIdeType}`);
3560
- const result = await cdp.evaluate(script, 3e4);
4423
+ const result = await cdp2.evaluate(script, 3e4);
3561
4424
  let parsed = result;
3562
4425
  if (typeof result === "string") {
3563
4426
  try {
@@ -3565,10 +4428,13 @@ var init_daemon_commands = __esm({
3565
4428
  } catch {
3566
4429
  }
3567
4430
  }
3568
- console.log(`[list_chats] Result type=${typeof parsed}, isArray=${Array.isArray(parsed)}, len=${Array.isArray(parsed) ? parsed.length : "N/A"}`);
3569
- return { success: true, chats: parsed };
4431
+ if (Array.isArray(parsed)) {
4432
+ console.log(`[list_chats] OK: ${parsed.length} chats (ideType=${this._currentIdeType})`);
4433
+ return { success: true, chats: parsed };
4434
+ }
4435
+ return { success: true, chats: [] };
3570
4436
  } catch (e) {
3571
- console.log(`[list_chats] Error: ${e.message}`);
4437
+ console.log(`[list_chats] error: ${e.message}`);
3572
4438
  return { success: false, error: e.message };
3573
4439
  }
3574
4440
  }
@@ -3584,15 +4450,22 @@ var init_daemon_commands = __esm({
3584
4450
  }
3585
4451
  }
3586
4452
  async handleSwitchChat(args) {
3587
- if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
4453
+ const ideType = this._currentIdeType;
4454
+ const cdp2 = this.getCdp(ideType);
4455
+ if (!cdp2?.isConnected) return { success: false, error: "CDP not connected" };
3588
4456
  const sessionId = args?.sessionId || args?.id || args?.chatId;
3589
4457
  if (!sessionId) return { success: false, error: "sessionId required" };
3590
- const script = this.getScriptLoader()?.getWithParams("switch_session", { SESSION_ID: sessionId });
3591
- if (!script) return { success: false, error: "switch_session script not loaded" };
4458
+ console.log(`[switch_chat] sessionId=${sessionId}, ideType=${ideType}`);
4459
+ const loader = this.getScriptLoader();
4460
+ if (!loader) return { success: false, error: "Script loader not initialized" };
4461
+ const script = loader.getCapabilityWithParams("switch_session", { SESSION_ID: JSON.stringify(sessionId) });
4462
+ if (!script) return { success: false, error: "switch_session script not available" };
3592
4463
  try {
3593
- await this.getCdp().evaluate(script, 15e3);
3594
- return { success: true };
4464
+ const result = await cdp2.evaluate(script, 15e3);
4465
+ console.log(`[switch_chat] result:`, result);
4466
+ return { success: true, result };
3595
4467
  } catch (e) {
4468
+ console.error(`[switch_chat] error:`, e.message);
3596
4469
  return { success: false, error: e.message };
3597
4470
  }
3598
4471
  }
@@ -3780,11 +4653,304 @@ var init_daemon_commands = __esm({
3780
4653
  const agents = await this.getCdp().discoverAgentWebviews();
3781
4654
  return { success: true, agents };
3782
4655
  }
4656
+ // ─── CDP DOM Analysis Tools ─────────────────
4657
+ /**
4658
+ * CDP DOM Dump — IDE의 DOM 트리를 가져옴
4659
+ *
4660
+ * args:
4661
+ * selector?: string — CSS 셀렉터로 특정 영역만 덤프 (default: 전체)
4662
+ * depth?: number — 덤프 깊이 제한 (default: 10)
4663
+ * attrs?: boolean — 속성 포함 여부 (default: true)
4664
+ * maxLength?: number — 최대 문자 수 (default: 200000)
4665
+ * format?: 'html' | 'tree' | 'summary' — 출력 형식 (default: 'html')
4666
+ * sessionId?: string — agent webview 세션 ID (있으면 해당 webview DOM)
4667
+ */
4668
+ async handleCdpDomDump(args) {
4669
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
4670
+ const selector = args?.selector || "body";
4671
+ const depth = args?.depth || 10;
4672
+ const maxLength = args?.maxLength || 2e5;
4673
+ const format = args?.format || "html";
4674
+ const attrs = args?.attrs !== false;
4675
+ const sessionId = args?.sessionId;
4676
+ try {
4677
+ let expression;
4678
+ if (format === "summary") {
4679
+ expression = `(() => {
4680
+ const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
4681
+ if (!root) return JSON.stringify({ error: 'Selector not found: ${selector}' });
4682
+
4683
+ function summarize(el, depth, maxD) {
4684
+ if (depth > maxD) return { tag: '...', note: 'max depth' };
4685
+ const node = {
4686
+ tag: el.tagName?.toLowerCase(),
4687
+ id: el.id || undefined,
4688
+ class: el.className && typeof el.className === 'string' ? el.className.split(' ').filter(c => c).slice(0, 5).join(' ') : undefined,
4689
+ role: el.getAttribute?.('role') || undefined,
4690
+ 'data-testid': el.getAttribute?.('data-testid') || undefined,
4691
+ childCount: el.children?.length || 0,
4692
+ };
4693
+ if (el.children?.length > 0 && depth < maxD) {
4694
+ node.children = Array.from(el.children).slice(0, 30).map(c => summarize(c, depth + 1, maxD));
4695
+ }
4696
+ return node;
4697
+ }
4698
+ return JSON.stringify(summarize(root, 0, ${depth}));
4699
+ })()`;
4700
+ } else if (format === "tree") {
4701
+ expression = `(() => {
4702
+ const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
4703
+ if (!root) return 'Selector not found: ${selector}';
4704
+
4705
+ function tree(el, indent, depth, maxD) {
4706
+ if (depth > maxD) return indent + '...\\n';
4707
+ let line = indent + '<' + (el.tagName?.toLowerCase() || '?');
4708
+ if (el.id) line += ' #' + el.id;
4709
+ if (el.className && typeof el.className === 'string') {
4710
+ const cls = el.className.trim().split(' ').filter(c => c).slice(0, 3).join('.');
4711
+ if (cls) line += ' .' + cls;
4712
+ }
4713
+ const role = el.getAttribute?.('role');
4714
+ if (role) line += ' [role=' + role + ']';
4715
+ const testId = el.getAttribute?.('data-testid');
4716
+ if (testId) line += ' [data-testid=' + testId + ']';
4717
+ line += '> (' + (el.children?.length || 0) + ')\\n';
4718
+
4719
+ let result = line;
4720
+ if (el.children?.length > 0 && depth < maxD) {
4721
+ for (let i = 0; i < Math.min(el.children.length, 30); i++) {
4722
+ result += tree(el.children[i], indent + ' ', depth + 1, maxD);
4723
+ }
4724
+ if (el.children.length > 30) result += indent + ' ... +' + (el.children.length - 30) + ' more\\n';
4725
+ }
4726
+ return result;
4727
+ }
4728
+ return tree(root, '', 0, ${depth});
4729
+ })()`;
4730
+ } else {
4731
+ expression = `(() => {
4732
+ const root = document.querySelector('${selector.replace(/'/g, "\\'")}');
4733
+ if (!root) return 'Selector not found: ${selector}';
4734
+ let html = root.outerHTML;
4735
+ if (html.length > ${maxLength}) {
4736
+ html = html.slice(0, ${maxLength}) + '\\n<!-- TRUNCATED at ${maxLength} chars -->';
4737
+ }
4738
+ return html;
4739
+ })()`;
4740
+ }
4741
+ let result;
4742
+ if (sessionId) {
4743
+ result = await this.getCdp().evaluateInSession(sessionId, expression);
4744
+ } else {
4745
+ result = await this.getCdp().evaluate(expression, 3e4);
4746
+ }
4747
+ if (format === "summary" && typeof result === "string") {
4748
+ try {
4749
+ result = JSON.parse(result);
4750
+ } catch {
4751
+ }
4752
+ }
4753
+ const size = typeof result === "string" ? result.length : JSON.stringify(result).length;
4754
+ return { success: true, result, format, selector, size };
4755
+ } catch (e) {
4756
+ return { success: false, error: e.message };
4757
+ }
4758
+ }
4759
+ /**
4760
+ * CDP DOM Query — CSS 셀렉터 테스트
4761
+ * 셀렉터가 몇 개 요소에 매칭되는지, 어떤 요소인지 확인
4762
+ *
4763
+ * args:
4764
+ * selector: string — CSS 셀렉터
4765
+ * limit?: number — 반환할 최대 요소 수 (default: 20)
4766
+ * content?: boolean — 텍스트 내용 포함 여부 (default: true)
4767
+ * sessionId?: string — agent webview 세션 ID
4768
+ */
4769
+ async handleCdpDomQuery(args) {
4770
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
4771
+ const selector = args?.selector;
4772
+ if (!selector) return { success: false, error: "selector required" };
4773
+ const limit = args?.limit || 20;
4774
+ const content = args?.content !== false;
4775
+ const sessionId = args?.sessionId;
4776
+ const expression = `(() => {
4777
+ try {
4778
+ const els = document.querySelectorAll('${selector.replace(/'/g, "\\'")}');
4779
+ const results = [];
4780
+ for (let i = 0; i < Math.min(els.length, ${limit}); i++) {
4781
+ const el = els[i];
4782
+ const item = {
4783
+ index: i,
4784
+ tag: el.tagName?.toLowerCase(),
4785
+ id: el.id || null,
4786
+ class: el.className && typeof el.className === 'string' ? el.className.trim().slice(0, 200) : null,
4787
+ role: el.getAttribute?.('role') || null,
4788
+ 'data-testid': el.getAttribute?.('data-testid') || null,
4789
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4790
+ visible: el.offsetParent !== null || el.offsetWidth > 0,
4791
+ };
4792
+ ${content ? `item.text = (el.textContent || '').trim().slice(0, 200);` : ""}
4793
+ ${content ? `item.value = el.value !== undefined ? String(el.value).slice(0, 200) : undefined;` : ""}
4794
+ results.push(item);
4795
+ }
4796
+ return JSON.stringify({ total: els.length, results });
4797
+ } catch(e) {
4798
+ return JSON.stringify({ error: e.message });
4799
+ }
4800
+ })()`;
4801
+ try {
4802
+ let raw;
4803
+ if (sessionId) {
4804
+ raw = await this.getCdp().evaluateInSession(sessionId, expression);
4805
+ } else {
4806
+ raw = await this.getCdp().evaluate(expression, 15e3);
4807
+ }
4808
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
4809
+ return { success: true, ...parsed, selector };
4810
+ } catch (e) {
4811
+ return { success: false, error: e.message };
4812
+ }
4813
+ }
4814
+ /**
4815
+ * CDP DOM Debug — IDE AI 패널 특화 분석
4816
+ * 새 IDE 지원 시 필요한 핵심 정보를 한 번에 수집
4817
+ *
4818
+ * args:
4819
+ * ideType?: string — IDE 유형 힌트
4820
+ * sessionId?: string — agent webview 세션 ID
4821
+ */
4822
+ async handleCdpDomDebug(args) {
4823
+ if (!this.getCdp()?.isConnected) return { success: false, error: "CDP not connected" };
4824
+ const sessionId = args?.sessionId;
4825
+ const expression = `(() => {
4826
+ const result = {
4827
+ url: location.href,
4828
+ title: document.title,
4829
+ viewport: { w: window.innerWidth, h: window.innerHeight },
4830
+
4831
+ // \uC785\uB825 \uD544\uB4DC \uD6C4\uBCF4
4832
+ inputs: [],
4833
+ // textarea \uD6C4\uBCF4
4834
+ textareas: [],
4835
+ // contenteditable \uD6C4\uBCF4
4836
+ editables: [],
4837
+ // \uBC84\uD2BC (send, submit \uB4F1)
4838
+ buttons: [],
4839
+ // iframes (agent webviews)
4840
+ iframes: [],
4841
+ // role="textbox" \uD6C4\uBCF4
4842
+ textboxes: [],
4843
+ };
4844
+
4845
+ // \uC785\uB825 \uD544\uB4DC
4846
+ document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => {
4847
+ if (i >= 10) return;
4848
+ result.inputs.push({
4849
+ tag: 'input',
4850
+ id: el.id || null,
4851
+ class: (el.className || '').toString().slice(0, 150),
4852
+ placeholder: el.getAttribute('placeholder') || null,
4853
+ name: el.name || null,
4854
+ value: el.value?.slice(0, 100) || null,
4855
+ visible: el.offsetParent !== null,
4856
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4857
+ });
4858
+ });
4859
+
4860
+ // textarea
4861
+ document.querySelectorAll('textarea').forEach((el, i) => {
4862
+ if (i >= 10) return;
4863
+ result.textareas.push({
4864
+ id: el.id || null,
4865
+ class: (el.className || '').toString().slice(0, 150),
4866
+ placeholder: el.getAttribute('placeholder') || null,
4867
+ rows: el.rows,
4868
+ value: el.value?.slice(0, 100) || null,
4869
+ visible: el.offsetParent !== null,
4870
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4871
+ });
4872
+ });
4873
+
4874
+ // contenteditable
4875
+ document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => {
4876
+ if (i >= 10) return;
4877
+ result.editables.push({
4878
+ tag: el.tagName?.toLowerCase(),
4879
+ id: el.id || null,
4880
+ class: (el.className || '').toString().slice(0, 150),
4881
+ role: el.getAttribute('role') || null,
4882
+ text: (el.textContent || '').trim().slice(0, 100),
4883
+ visible: el.offsetParent !== null,
4884
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4885
+ });
4886
+ });
4887
+
4888
+ // role="textbox"
4889
+ document.querySelectorAll('[role="textbox"]').forEach((el, i) => {
4890
+ if (i >= 10) return;
4891
+ result.textboxes.push({
4892
+ tag: el.tagName?.toLowerCase(),
4893
+ id: el.id || null,
4894
+ class: (el.className || '').toString().slice(0, 150),
4895
+ 'aria-label': el.getAttribute('aria-label') || null,
4896
+ text: (el.textContent || '').trim().slice(0, 100),
4897
+ visible: el.offsetParent !== null,
4898
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4899
+ });
4900
+ });
4901
+
4902
+ // \uBC84\uD2BC (send, submit, accept, reject, approve \uB4F1)
4903
+ const btnKeywords = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
4904
+ document.querySelectorAll('button, [role="button"], input[type="submit"]').forEach((el, i) => {
4905
+ const text = (el.textContent || el.getAttribute('aria-label') || '').trim();
4906
+ if (i < 30 && (text.length < 30 || btnKeywords.test(text))) {
4907
+ result.buttons.push({
4908
+ tag: el.tagName?.toLowerCase(),
4909
+ id: el.id || null,
4910
+ class: (el.className || '').toString().slice(0, 150),
4911
+ text: text.slice(0, 80),
4912
+ 'aria-label': el.getAttribute('aria-label') || null,
4913
+ disabled: el.disabled || el.getAttribute('disabled') !== null,
4914
+ visible: el.offsetParent !== null,
4915
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4916
+ });
4917
+ }
4918
+ });
4919
+
4920
+ // iframes
4921
+ document.querySelectorAll('iframe, webview').forEach((el, i) => {
4922
+ if (i >= 20) return;
4923
+ result.iframes.push({
4924
+ tag: el.tagName?.toLowerCase(),
4925
+ id: el.id || null,
4926
+ class: (el.className || '').toString().slice(0, 150),
4927
+ src: el.getAttribute('src')?.slice(0, 200) || null,
4928
+ title: el.getAttribute('title') || null,
4929
+ visible: el.offsetParent !== null,
4930
+ rect: (() => { try { const r = el.getBoundingClientRect(); return { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }; } catch { return null; } })(),
4931
+ });
4932
+ });
4933
+
4934
+ return JSON.stringify(result);
4935
+ })()`;
4936
+ try {
4937
+ let raw;
4938
+ if (sessionId) {
4939
+ raw = await this.getCdp().evaluateInSession(sessionId, expression);
4940
+ } else {
4941
+ raw = await this.getCdp().evaluate(expression, 3e4);
4942
+ }
4943
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
4944
+ return { success: true, ...parsed };
4945
+ } catch (e) {
4946
+ return { success: false, error: e.message };
4947
+ }
4948
+ }
3783
4949
  // ─── 파일 직접 처리 ─────────────────────────
3784
4950
  async handleFileRead(args) {
3785
4951
  try {
3786
4952
  const filePath = this.resolveSafePath(args?.path);
3787
- const content = fs4.readFileSync(filePath, "utf-8");
4953
+ const content = fs2.readFileSync(filePath, "utf-8");
3788
4954
  return { success: true, content, path: filePath };
3789
4955
  } catch (e) {
3790
4956
  return { success: false, error: e.message };
@@ -3793,8 +4959,8 @@ var init_daemon_commands = __esm({
3793
4959
  async handleFileWrite(args) {
3794
4960
  try {
3795
4961
  const filePath = this.resolveSafePath(args?.path);
3796
- fs4.mkdirSync(path3.dirname(filePath), { recursive: true });
3797
- fs4.writeFileSync(filePath, args?.content || "", "utf-8");
4962
+ fs2.mkdirSync(path3.dirname(filePath), { recursive: true });
4963
+ fs2.writeFileSync(filePath, args?.content || "", "utf-8");
3798
4964
  return { success: true, path: filePath };
3799
4965
  } catch (e) {
3800
4966
  return { success: false, error: e.message };
@@ -3803,11 +4969,11 @@ var init_daemon_commands = __esm({
3803
4969
  async handleFileList(args) {
3804
4970
  try {
3805
4971
  const dirPath = this.resolveSafePath(args?.path || ".");
3806
- const entries = fs4.readdirSync(dirPath, { withFileTypes: true });
4972
+ const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
3807
4973
  const files = entries.map((e) => ({
3808
4974
  name: e.name,
3809
4975
  type: e.isDirectory() ? "directory" : "file",
3810
- size: e.isFile() ? fs4.statSync(path3.join(dirPath, e.name)).size : void 0
4976
+ size: e.isFile() ? fs2.statSync(path3.join(dirPath, e.name)).size : void 0
3811
4977
  }));
3812
4978
  return { success: true, files, path: dirPath };
3813
4979
  } catch (e) {
@@ -3845,7 +5011,7 @@ var init_daemon_commands = __esm({
3845
5011
  const candidates = ["Cursor", "Code", "VSCodium"];
3846
5012
  for (const app of candidates) {
3847
5013
  const stateFile = path3.join(storageDir, app, "User", "globalStorage", "state.vscdb");
3848
- if (fs4.existsSync(stateFile)) {
5014
+ if (fs2.existsSync(stateFile)) {
3849
5015
  const result = await this.delegateToExtension("adhdev.getRecentWorkspaces", []);
3850
5016
  if (result.success && Array.isArray(result.result)) {
3851
5017
  const merged = Array.from(/* @__PURE__ */ new Set([...cliRecent, ...result.result])).slice(0, 20);
@@ -3938,6 +5104,40 @@ var init_daemon_commands = __esm({
3938
5104
  const ok = await this.agentStream.focusAgentEditor(this.getCdp(), agentType);
3939
5105
  return { success: ok };
3940
5106
  }
5107
+ // ─── PTY Raw I/O (터미널 뷰) ──────────────────────
5108
+ handlePtyInput(args) {
5109
+ const { cliType, data } = args || {};
5110
+ if (!data) return { success: false, error: "data required" };
5111
+ if (this.ctx.adapters) {
5112
+ const targetCli = cliType || "gemini-cli";
5113
+ for (const [, adapter] of this.ctx.adapters) {
5114
+ if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
5115
+ adapter.writeRaw(data);
5116
+ return { success: true };
5117
+ }
5118
+ }
5119
+ }
5120
+ return { success: false, error: `CLI adapter not found: ${cliType}` };
5121
+ }
5122
+ handlePtyResize(args) {
5123
+ const { cliType, cols, rows, force } = args || {};
5124
+ if (!cols || !rows) return { success: false, error: "cols and rows required" };
5125
+ if (this.ctx.adapters) {
5126
+ const targetCli = cliType || "gemini-cli";
5127
+ for (const [, adapter] of this.ctx.adapters) {
5128
+ if (adapter.cliType === targetCli && typeof adapter.resize === "function") {
5129
+ if (force) {
5130
+ adapter.resize(cols - 1, rows);
5131
+ setTimeout(() => adapter.resize(cols, rows), 50);
5132
+ } else {
5133
+ adapter.resize(cols, rows);
5134
+ }
5135
+ return { success: true };
5136
+ }
5137
+ }
5138
+ }
5139
+ return { success: false, error: `CLI adapter not found: ${cliType}` };
5140
+ }
3941
5141
  };
3942
5142
  }
3943
5143
  });
@@ -4236,13 +5436,13 @@ var init_manager = __esm({
4236
5436
  }
4237
5437
  }
4238
5438
  }
4239
- async switchActiveAgent(cdp, agentType) {
5439
+ async switchActiveAgent(cdp2, agentType) {
4240
5440
  if (this._activeAgentType === agentType) return;
4241
5441
  if (this._activeAgentType) {
4242
5442
  const prev = this.managed.get(this._activeAgentType);
4243
5443
  if (prev) {
4244
5444
  try {
4245
- await cdp.detachAgent(prev.sessionId);
5445
+ await cdp2.detachAgent(prev.sessionId);
4246
5446
  } catch {
4247
5447
  }
4248
5448
  this.managed.delete(this._activeAgentType);
@@ -4254,7 +5454,7 @@ var init_manager = __esm({
4254
5454
  this.logFn(`[AgentStream] Active agent: ${agentType || "none"}`);
4255
5455
  }
4256
5456
  /** Agent webview 발견 + 세션 연결 */
4257
- async syncAgentSessions(cdp) {
5457
+ async syncAgentSessions(cdp2) {
4258
5458
  if (!this.enabled || !this._activeAgentType) return;
4259
5459
  const now = Date.now();
4260
5460
  if (this.managed.has(this._activeAgentType) && now - this.lastDiscoveryTime < this.discoveryIntervalMs) {
@@ -4262,12 +5462,12 @@ var init_manager = __esm({
4262
5462
  }
4263
5463
  this.lastDiscoveryTime = now;
4264
5464
  try {
4265
- const targets = await cdp.discoverAgentWebviews();
5465
+ const targets = await cdp2.discoverAgentWebviews();
4266
5466
  const activeTarget = targets.find((t) => t.agentType === this._activeAgentType);
4267
5467
  if (activeTarget && !this.managed.has(this._activeAgentType)) {
4268
5468
  const adapter = this.allAdapters.find((a) => a.agentType === this._activeAgentType);
4269
5469
  if (adapter) {
4270
- const sessionId = await cdp.attachToAgent(activeTarget);
5470
+ const sessionId = await cdp2.attachToAgent(activeTarget);
4271
5471
  if (sessionId) {
4272
5472
  this.managed.set(this._activeAgentType, {
4273
5473
  adapter,
@@ -4283,7 +5483,7 @@ var init_manager = __esm({
4283
5483
  }
4284
5484
  for (const [type, agent] of this.managed) {
4285
5485
  if (type !== this._activeAgentType) {
4286
- await cdp.detachAgent(agent.sessionId);
5486
+ await cdp2.detachAgent(agent.sessionId);
4287
5487
  this.managed.delete(type);
4288
5488
  }
4289
5489
  }
@@ -4293,7 +5493,7 @@ var init_manager = __esm({
4293
5493
  }
4294
5494
  }
4295
5495
  /** 활성 에이전트 상태 수집 */
4296
- async collectAgentStreams(cdp) {
5496
+ async collectAgentStreams(cdp2) {
4297
5497
  if (!this.enabled) return [];
4298
5498
  const results = [];
4299
5499
  if (this._activeAgentType && this.managed.has(this._activeAgentType)) {
@@ -4305,7 +5505,7 @@ var init_manager = __esm({
4305
5505
  results.push(agent.lastState);
4306
5506
  } else {
4307
5507
  try {
4308
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5508
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4309
5509
  const state = await agent.adapter.readChat(evaluate);
4310
5510
  agent.lastState = state;
4311
5511
  agent.lastError = null;
@@ -4327,7 +5527,7 @@ var init_manager = __esm({
4327
5527
  });
4328
5528
  if (errorMsg.includes("timeout") || errorMsg.includes("not connected") || errorMsg.includes("Session")) {
4329
5529
  try {
4330
- await cdp.detachAgent(agent.sessionId);
5530
+ await cdp2.detachAgent(agent.sessionId);
4331
5531
  } catch {
4332
5532
  }
4333
5533
  this.managed.delete(type);
@@ -4338,12 +5538,12 @@ var init_manager = __esm({
4338
5538
  }
4339
5539
  return results;
4340
5540
  }
4341
- async sendToAgent(cdp, agentType, text, targetIdeType) {
5541
+ async sendToAgent(cdp2, agentType, text, targetIdeType) {
4342
5542
  await this.ensureAgentPanelOpen(agentType, targetIdeType);
4343
5543
  const agent = this.managed.get(agentType);
4344
5544
  if (!agent) return false;
4345
5545
  try {
4346
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5546
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4347
5547
  await agent.adapter.sendMessage(evaluate, text);
4348
5548
  return true;
4349
5549
  } catch (e) {
@@ -4351,24 +5551,24 @@ var init_manager = __esm({
4351
5551
  return false;
4352
5552
  }
4353
5553
  }
4354
- async resolveAgentAction(cdp, agentType, action, targetIdeType) {
5554
+ async resolveAgentAction(cdp2, agentType, action, targetIdeType) {
4355
5555
  await this.ensureAgentPanelOpen(agentType, targetIdeType);
4356
5556
  const agent = this.managed.get(agentType);
4357
5557
  if (!agent) return false;
4358
5558
  try {
4359
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5559
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4360
5560
  return await agent.adapter.resolveAction(evaluate, action);
4361
5561
  } catch (e) {
4362
5562
  this.logFn(`[AgentStream] resolveAction(${agentType}) error: ${e.message}`);
4363
5563
  return false;
4364
5564
  }
4365
5565
  }
4366
- async newAgentSession(cdp, agentType, targetIdeType) {
5566
+ async newAgentSession(cdp2, agentType, targetIdeType) {
4367
5567
  await this.ensureAgentPanelOpen(agentType, targetIdeType);
4368
5568
  const agent = this.managed.get(agentType);
4369
5569
  if (!agent) return false;
4370
5570
  try {
4371
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5571
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4372
5572
  await agent.adapter.newSession(evaluate);
4373
5573
  return true;
4374
5574
  } catch (e) {
@@ -4376,33 +5576,45 @@ var init_manager = __esm({
4376
5576
  return false;
4377
5577
  }
4378
5578
  }
4379
- async listAgentChats(cdp, agentType) {
4380
- const agent = this.managed.get(agentType);
5579
+ async listAgentChats(cdp2, agentType) {
5580
+ let agent = this.managed.get(agentType);
5581
+ if (!agent) {
5582
+ this.logFn(`[AgentStream] listChats: ${agentType} not managed, trying on-demand activation`);
5583
+ await this.switchActiveAgent(cdp2, agentType);
5584
+ await this.syncAgentSessions(cdp2);
5585
+ agent = this.managed.get(agentType);
5586
+ }
4381
5587
  if (!agent || typeof agent.adapter.listChats !== "function") return [];
4382
5588
  try {
4383
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5589
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4384
5590
  return await agent.adapter.listChats(evaluate);
4385
5591
  } catch (e) {
4386
5592
  this.logFn(`[AgentStream] listChats(${agentType}) error: ${e.message}`);
4387
5593
  return [];
4388
5594
  }
4389
5595
  }
4390
- async switchAgentSession(cdp, agentType, sessionId) {
4391
- const agent = this.managed.get(agentType);
5596
+ async switchAgentSession(cdp2, agentType, sessionId) {
5597
+ let agent = this.managed.get(agentType);
5598
+ if (!agent) {
5599
+ this.logFn(`[AgentStream] switchSession: ${agentType} not managed, trying on-demand activation`);
5600
+ await this.switchActiveAgent(cdp2, agentType);
5601
+ await this.syncAgentSessions(cdp2);
5602
+ agent = this.managed.get(agentType);
5603
+ }
4392
5604
  if (!agent || typeof agent.adapter.switchSession !== "function") return false;
4393
5605
  try {
4394
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5606
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4395
5607
  return await agent.adapter.switchSession(evaluate, sessionId);
4396
5608
  } catch (e) {
4397
5609
  this.logFn(`[AgentStream] switchSession(${agentType}) error: ${e.message}`);
4398
5610
  return false;
4399
5611
  }
4400
5612
  }
4401
- async focusAgentEditor(cdp, agentType) {
5613
+ async focusAgentEditor(cdp2, agentType) {
4402
5614
  const agent = this.managed.get(agentType);
4403
5615
  if (!agent || typeof agent.adapter.focusEditor !== "function") return false;
4404
5616
  try {
4405
- const evaluate = (expr, timeout) => cdp.evaluateInSession(agent.sessionId, expr, timeout);
5617
+ const evaluate = (expr, timeout) => cdp2.evaluateInSession(agent.sessionId, expr, timeout);
4406
5618
  await agent.adapter.focusEditor(evaluate);
4407
5619
  return true;
4408
5620
  } catch (e) {
@@ -4416,12 +5628,12 @@ var init_manager = __esm({
4416
5628
  getManagedAgent(agentType) {
4417
5629
  return this.managed.get(agentType);
4418
5630
  }
4419
- async dispose(cdp) {
5631
+ async dispose(cdp2) {
4420
5632
  for (const loader of this.scriptLoaders.values()) loader.stop();
4421
5633
  this.scriptLoaders.clear();
4422
5634
  for (const [, agent] of this.managed) {
4423
5635
  try {
4424
- await cdp.detachAgent(agent.sessionId);
5636
+ await cdp2.detachAgent(agent.sessionId);
4425
5637
  } catch {
4426
5638
  }
4427
5639
  }
@@ -4450,6 +5662,107 @@ var init_agent_stream = __esm({
4450
5662
  }
4451
5663
  });
4452
5664
 
5665
+ // src/daemon-logger.ts
5666
+ var daemon_logger_exports = {};
5667
+ __export(daemon_logger_exports, {
5668
+ LOG_DIR_PATH: () => LOG_DIR_PATH,
5669
+ LOG_PATH: () => LOG_PATH,
5670
+ cdpLogFn: () => cdpLogFn,
5671
+ daemonLog: () => daemonLog,
5672
+ installGlobalInterceptor: () => installGlobalInterceptor
5673
+ });
5674
+ function rotateIfNeeded() {
5675
+ try {
5676
+ const stat = fs3.statSync(LOG_FILE);
5677
+ if (stat.size > MAX_LOG_SIZE) {
5678
+ const backup = LOG_FILE + ".old";
5679
+ try {
5680
+ fs3.unlinkSync(backup);
5681
+ } catch {
5682
+ }
5683
+ fs3.renameSync(LOG_FILE, backup);
5684
+ }
5685
+ } catch {
5686
+ }
5687
+ }
5688
+ function writeToFile(line) {
5689
+ try {
5690
+ fs3.appendFileSync(LOG_FILE, line + "\n");
5691
+ } catch {
5692
+ }
5693
+ }
5694
+ function daemonLog(category, msg) {
5695
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${category}] ${msg}`;
5696
+ origConsoleLog(line);
5697
+ writeToFile(line);
5698
+ }
5699
+ function cdpLogFn(ideType) {
5700
+ return (msg) => {
5701
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [CDP:${ideType}] ${msg}`;
5702
+ origConsoleLog(line);
5703
+ writeToFile(line);
5704
+ };
5705
+ }
5706
+ function installGlobalInterceptor() {
5707
+ if (interceptorInstalled) return;
5708
+ interceptorInstalled = true;
5709
+ const stripAnsi4 = (str) => str.replace(/\x1B\[[0-9;]*m/g, "");
5710
+ console.log = (...args) => {
5711
+ origConsoleLog(...args);
5712
+ try {
5713
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5714
+ const clean = stripAnsi4(msg);
5715
+ const line = clean.startsWith("[20") ? clean : `[${(/* @__PURE__ */ new Date()).toISOString()}] ${clean}`;
5716
+ writeToFile(line);
5717
+ } catch {
5718
+ }
5719
+ };
5720
+ console.error = (...args) => {
5721
+ origConsoleError(...args);
5722
+ try {
5723
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5724
+ const clean = stripAnsi4(msg);
5725
+ writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [ERROR] ${clean}`);
5726
+ } catch {
5727
+ }
5728
+ };
5729
+ console.warn = (...args) => {
5730
+ origConsoleWarn(...args);
5731
+ try {
5732
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
5733
+ const clean = stripAnsi4(msg);
5734
+ writeToFile(`[${(/* @__PURE__ */ new Date()).toISOString()}] [WARN] ${clean}`);
5735
+ } catch {
5736
+ }
5737
+ };
5738
+ writeToFile(`
5739
+ === ADHDev Daemon started at ${(/* @__PURE__ */ new Date()).toISOString()} ===`);
5740
+ writeToFile(`Log file: ${LOG_FILE}`);
5741
+ }
5742
+ var fs3, path4, os8, LOG_DIR, LOG_FILE, MAX_LOG_SIZE, origConsoleLog, origConsoleError, origConsoleWarn, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
5743
+ var init_daemon_logger = __esm({
5744
+ "src/daemon-logger.ts"() {
5745
+ "use strict";
5746
+ fs3 = __toESM(require("fs"));
5747
+ path4 = __toESM(require("path"));
5748
+ os8 = __toESM(require("os"));
5749
+ LOG_DIR = process.platform === "darwin" ? path4.join(os8.homedir(), "Library", "Logs", "adhdev") : path4.join(os8.homedir(), ".local", "share", "adhdev", "logs");
5750
+ LOG_FILE = path4.join(LOG_DIR, "daemon.log");
5751
+ MAX_LOG_SIZE = 10 * 1024 * 1024;
5752
+ try {
5753
+ fs3.mkdirSync(LOG_DIR, { recursive: true });
5754
+ } catch {
5755
+ }
5756
+ rotateIfNeeded();
5757
+ origConsoleLog = console.log.bind(console);
5758
+ origConsoleError = console.error.bind(console);
5759
+ origConsoleWarn = console.warn.bind(console);
5760
+ interceptorInstalled = false;
5761
+ LOG_PATH = LOG_FILE;
5762
+ LOG_DIR_PATH = LOG_DIR;
5763
+ }
5764
+ });
5765
+
4453
5766
  // src/adhdev-daemon.ts
4454
5767
  var adhdev_daemon_exports = {};
4455
5768
  __export(adhdev_daemon_exports, {
@@ -4458,24 +5771,24 @@ __export(adhdev_daemon_exports, {
4458
5771
  stopDaemon: () => stopDaemon
4459
5772
  });
4460
5773
  function getDaemonPidFile() {
4461
- const dir = path4.join(os8.homedir(), ".adhdev");
4462
- if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
4463
- return path4.join(dir, "daemon.pid");
5774
+ const dir = path5.join(os9.homedir(), ".adhdev");
5775
+ if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
5776
+ return path5.join(dir, "daemon.pid");
4464
5777
  }
4465
5778
  function writeDaemonPid(pid) {
4466
- fs5.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
5779
+ fs4.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
4467
5780
  }
4468
5781
  function removeDaemonPid() {
4469
5782
  try {
4470
- fs5.unlinkSync(getDaemonPidFile());
5783
+ fs4.unlinkSync(getDaemonPidFile());
4471
5784
  } catch {
4472
5785
  }
4473
5786
  }
4474
5787
  function isDaemonRunning() {
4475
5788
  const pidFile = getDaemonPidFile();
4476
5789
  try {
4477
- if (!fs5.existsSync(pidFile)) return false;
4478
- const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
5790
+ if (!fs4.existsSync(pidFile)) return false;
5791
+ const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
4479
5792
  process.kill(pid, 0);
4480
5793
  return true;
4481
5794
  } catch {
@@ -4486,8 +5799,8 @@ function isDaemonRunning() {
4486
5799
  function stopDaemon() {
4487
5800
  const pidFile = getDaemonPidFile();
4488
5801
  try {
4489
- if (!fs5.existsSync(pidFile)) return false;
4490
- const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
5802
+ if (!fs4.existsSync(pidFile)) return false;
5803
+ const pid = parseInt(fs4.readFileSync(pidFile, "utf-8").trim());
4491
5804
  process.kill(pid, "SIGTERM");
4492
5805
  removeDaemonPid();
4493
5806
  return true;
@@ -4496,7 +5809,7 @@ function stopDaemon() {
4496
5809
  return false;
4497
5810
  }
4498
5811
  }
4499
- var os8, fs5, path4, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
5812
+ var os9, fs4, path5, crypto2, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
4500
5813
  var init_adhdev_daemon = __esm({
4501
5814
  "src/adhdev-daemon.ts"() {
4502
5815
  "use strict";
@@ -4515,9 +5828,9 @@ var init_adhdev_daemon = __esm({
4515
5828
  init_daemon_commands();
4516
5829
  init_agent_stream();
4517
5830
  init_dist();
4518
- os8 = __toESM(require("os"));
4519
- fs5 = __toESM(require("fs"));
4520
- path4 = __toESM(require("path"));
5831
+ os9 = __toESM(require("os"));
5832
+ fs4 = __toESM(require("fs"));
5833
+ path5 = __toESM(require("path"));
4521
5834
  crypto2 = __toESM(require("crypto"));
4522
5835
  import_chalk2 = __toESM(require("chalk"));
4523
5836
  DANGEROUS_PATTERNS = [
@@ -4542,6 +5855,11 @@ var init_adhdev_daemon = __esm({
4542
5855
  scriptLoaders = /* @__PURE__ */ new Map();
4543
5856
  commandHandler = null;
4544
5857
  screenshotTimer = null;
5858
+ lastStatusSentFull = false;
5859
+ lastStatusSentAt = 0;
5860
+ statusPendingThrottle = false;
5861
+ lastP2PStatusHash = "";
5862
+ statusReportCount = 0;
4545
5863
  agentStreamManager = null;
4546
5864
  agentStreamTimer = null;
4547
5865
  running = false;
@@ -4562,6 +5880,11 @@ var init_adhdev_daemon = __esm({
4562
5880
  this.localPort = DEFAULT_DAEMON_PORT;
4563
5881
  }
4564
5882
  async start(options = {}) {
5883
+ try {
5884
+ const { installGlobalInterceptor: installGlobalInterceptor2 } = (init_daemon_logger(), __toCommonJS(daemon_logger_exports));
5885
+ installGlobalInterceptor2();
5886
+ } catch {
5887
+ }
4565
5888
  this.localPort = options.localPort || DEFAULT_DAEMON_PORT;
4566
5889
  const workingDir = options.workingDir || process.cwd();
4567
5890
  if (isDaemonRunning()) {
@@ -4592,7 +5915,7 @@ var init_adhdev_daemon = __esm({
4592
5915
  setTimeout(() => this.sendUnifiedStatusReport(), 500);
4593
5916
  },
4594
5917
  onExtensionStatus: () => {
4595
- this.sendUnifiedStatusReport();
5918
+ this.throttledStatusReport();
4596
5919
  },
4597
5920
  onExtensionEvent: (ext, event, data) => {
4598
5921
  if (event === "token_updated" && data.token) {
@@ -4635,6 +5958,7 @@ var init_adhdev_daemon = __esm({
4635
5958
  for (const ideType of this.cdpManagers.keys()) {
4636
5959
  const loader = new DaemonScriptLoader(serverUrl, ideType);
4637
5960
  loader.setToken(config.connectionToken);
5961
+ loader.setPlatform(os9.platform());
4638
5962
  await loader.start().catch((e) => {
4639
5963
  console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${ideType}: ${e.message}`));
4640
5964
  });
@@ -4644,6 +5968,7 @@ var init_adhdev_daemon = __esm({
4644
5968
  const fallbackType = this.detectedIdes.find((ide) => ide.installed)?.id || "cursor";
4645
5969
  const loader = new DaemonScriptLoader(serverUrl, fallbackType);
4646
5970
  loader.setToken(config.connectionToken);
5971
+ loader.setPlatform(os9.platform());
4647
5972
  await loader.start().catch((e) => {
4648
5973
  console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed: ${e.message}`));
4649
5974
  });
@@ -4669,8 +5994,8 @@ var init_adhdev_daemon = __esm({
4669
5994
  console.log(import_chalk2.default.yellow(` \u26A0 Failed to start CLI ${options.cliType}: ${e.message}`));
4670
5995
  });
4671
5996
  }
4672
- const machineId = os8.hostname().replace(/[^a-zA-Z0-9]/g, "_");
4673
- const machineHash = crypto2.createHash("md5").update(os8.hostname() + os8.homedir()).digest("hex").slice(0, 8);
5997
+ const machineId = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
5998
+ const machineHash = crypto2.createHash("md5").update(os9.hostname() + os9.homedir()).digest("hex").slice(0, 8);
4674
5999
  const instanceId = `daemon_${machineId}_${machineHash}`;
4675
6000
  this.bridge = new CliBridgeConnection({
4676
6001
  serverUrl: options.serverUrl || config.serverUrl,
@@ -4678,7 +6003,7 @@ var init_adhdev_daemon = __esm({
4678
6003
  cliInfo: {
4679
6004
  type: "adhdev-daemon",
4680
6005
  version: "0.2.0",
4681
- platform: os8.platform(),
6006
+ platform: os9.platform(),
4682
6007
  instanceId
4683
6008
  }
4684
6009
  });
@@ -4690,8 +6015,7 @@ var init_adhdev_daemon = __esm({
4690
6015
  return this.commandHandler.handle("cdp_remote_action", event);
4691
6016
  });
4692
6017
  this.p2p.onCommand(async (cmdType, data, id) => {
4693
- if (!this.commandHandler) return { success: false, error: "No handler" };
4694
- return this.commandHandler.handle(cmdType, data);
6018
+ return this.handleP2PCommand(cmdType, data);
4695
6019
  });
4696
6020
  this.p2p.onFileRequest(async (req) => {
4697
6021
  if (req.type === "read") {
@@ -4705,18 +6029,34 @@ var init_adhdev_daemon = __esm({
4705
6029
  return { id: req.id, success: result.success, entries: result.files, error: result.error };
4706
6030
  }
4707
6031
  });
6032
+ this.p2p.onPtyInput((cliType, data) => {
6033
+ for (const [, adapter] of this.adapters) {
6034
+ if (adapter.cliType === cliType && typeof adapter.writeRaw === "function") {
6035
+ adapter.writeRaw(data);
6036
+ return;
6037
+ }
6038
+ }
6039
+ });
6040
+ this.p2p.onPtyResize((cliType, cols, rows) => {
6041
+ for (const [, adapter] of this.adapters) {
6042
+ if (adapter.cliType === cliType && typeof adapter.resize === "function") {
6043
+ adapter.resize(cols, rows);
6044
+ return;
6045
+ }
6046
+ }
6047
+ });
4708
6048
  let ssDebugCount = 0;
4709
6049
  this.screenshotTimer = setInterval(async () => {
4710
6050
  const active = this.p2p?.screenshotActive;
4711
6051
  const ssIdeType = this.p2p?.screenshotIdeType;
4712
- const cdp = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
4713
- if (!active || !cdp) return;
6052
+ const cdp2 = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
6053
+ if (!active || !cdp2) return;
4714
6054
  ssDebugCount++;
4715
6055
  if (ssDebugCount <= 3 || ssDebugCount % 25 === 0) {
4716
6056
  console.log(`[SS] Capturing screenshot... (tick ${ssDebugCount}, active=${active}, cdp=true)`);
4717
6057
  }
4718
6058
  try {
4719
- const buf = await cdp.captureScreenshot();
6059
+ const buf = await cdp2.captureScreenshot();
4720
6060
  if (buf) {
4721
6061
  const sent = this.p2p.sendScreenshot(buf.toString("base64"));
4722
6062
  if (ssDebugCount <= 3) console.log(`[SS] Screenshot sent: ${buf.length} bytes, delivered=${sent}`);
@@ -4842,7 +6182,10 @@ var init_adhdev_daemon = __esm({
4842
6182
  "agent_stream_new",
4843
6183
  "agent_stream_list_chats",
4844
6184
  "agent_stream_switch_session",
4845
- "agent_stream_focus"
6185
+ "agent_stream_focus",
6186
+ // PTY I/O commands (터미널 뷰)
6187
+ "pty_input",
6188
+ "pty_resize"
4846
6189
  ];
4847
6190
  for (const cmdType of directCdpCommands) {
4848
6191
  this.bridge.on(cmdType, async (msg) => {
@@ -4911,7 +6254,19 @@ var init_adhdev_daemon = __esm({
4911
6254
  const dir = args?.dir || process.cwd();
4912
6255
  if (!cliType) throw new Error("cliType required");
4913
6256
  const key = this.getCliKey(cliType, dir);
4914
- await this.stopCliSession(key);
6257
+ if (this.adapters.has(key)) {
6258
+ await this.stopCliSession(key);
6259
+ } else {
6260
+ let found = false;
6261
+ for (const [k, adapter] of this.adapters) {
6262
+ if (adapter.cliType === cliType) {
6263
+ await this.stopCliSession(k);
6264
+ found = true;
6265
+ break;
6266
+ }
6267
+ }
6268
+ if (!found) console.log(import_chalk2.default.yellow(` \u26A0 No adapter found for ${cliType} (key: ${key})`));
6269
+ }
4915
6270
  this.sendResult(msg, true, { cliType, dir });
4916
6271
  return;
4917
6272
  }
@@ -4953,7 +6308,7 @@ var init_adhdev_daemon = __esm({
4953
6308
  if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
4954
6309
  console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
4955
6310
  const manager = new DaemonCdpManager(result.port, (msg2) => {
4956
- console.log(import_chalk2.default.gray(msg2));
6311
+ console.log(`[CDP:${result.ideId}] ${msg2}`);
4957
6312
  }, true);
4958
6313
  const connected = await manager.connect();
4959
6314
  if (connected) {
@@ -4965,6 +6320,7 @@ var init_adhdev_daemon = __esm({
4965
6320
  const serverUrl = config.serverUrl || "https://api.adhf.dev";
4966
6321
  const loader = new DaemonScriptLoader(serverUrl, result.ideId);
4967
6322
  loader.setToken(config.connectionToken || "");
6323
+ loader.setPlatform(os9.platform());
4968
6324
  await loader.start().catch((e) => {
4969
6325
  console.log(import_chalk2.default.yellow(` \u26A0 ScriptLoader start failed for ${result.ideId}: ${e?.message}`));
4970
6326
  });
@@ -4999,6 +6355,13 @@ var init_adhdev_daemon = __esm({
4999
6355
  if (this.commandHandler) {
5000
6356
  const result = await this.commandHandler.handle(cmd, args);
5001
6357
  this.sendResult(msg, result.success, result);
6358
+ const CHAT_COMMANDS = ["send_chat", "new_chat", "switch_chat", "set_mode", "change_model", "agent_stream_send"];
6359
+ if (CHAT_COMMANDS.includes(cmd)) {
6360
+ setTimeout(() => this.sendUnifiedStatusReport().catch(() => {
6361
+ }), 1e3);
6362
+ setTimeout(() => this.sendUnifiedStatusReport().catch(() => {
6363
+ }), 3e3);
6364
+ }
5002
6365
  } else {
5003
6366
  this.sendResult(msg, false, { error: `Command handler not initialized: ${cmd}` });
5004
6367
  }
@@ -5007,6 +6370,87 @@ var init_adhdev_daemon = __esm({
5007
6370
  this.sendResult(msg, false, { error: e.message });
5008
6371
  }
5009
6372
  }
6373
+ /** P2P 명령 처리 — DaemonCommandHandler + daemon-level 명령 */
6374
+ async handleP2PCommand(cmdType, data) {
6375
+ try {
6376
+ switch (cmdType) {
6377
+ case "launch_ide": {
6378
+ const ideKey = data.ideId || data.ideType || "";
6379
+ const launchArgs = { ...data, ideId: ideKey };
6380
+ console.log(`[launch_ide] (P2P) target=${ideKey || "auto"}`);
6381
+ const result = await launchWithCdp(launchArgs);
6382
+ if (result.success && result.port && result.ideId && !this.cdpManagers.has(result.ideId)) {
6383
+ console.log(import_chalk2.default.cyan(`[launch_ide] Connecting CDP for ${result.ideId} on port ${result.port}...`));
6384
+ const manager = new DaemonCdpManager(result.port, (msg) => {
6385
+ console.log(`[CDP:${result.ideId}] ${msg}`);
6386
+ }, true);
6387
+ const connected = await manager.connect();
6388
+ if (connected) {
6389
+ this.cdpManagers.set(result.ideId, manager);
6390
+ console.log(import_chalk2.default.green(` \u{1F50D} CDP connected: ${result.ideId} (port ${result.port})`));
6391
+ if (!this.scriptLoaders.has(result.ideId)) {
6392
+ const config = loadConfig();
6393
+ const serverUrl = config.serverUrl || "https://api.adhf.dev";
6394
+ const loader = new DaemonScriptLoader(serverUrl, result.ideId);
6395
+ loader.setToken(config.connectionToken || "");
6396
+ loader.setPlatform(os9.platform());
6397
+ await loader.start().catch(() => {
6398
+ });
6399
+ this.scriptLoaders.set(result.ideId, loader);
6400
+ }
6401
+ }
6402
+ }
6403
+ this.startAgentStreamPolling();
6404
+ return { success: result.success, ...result };
6405
+ }
6406
+ case "detect_ides": {
6407
+ this.detectedIdes = await detectIDEs();
6408
+ return { success: true, ides: this.detectedIdes };
6409
+ }
6410
+ case "launch_cli": {
6411
+ const cliType = data.cliType || "gemini-cli";
6412
+ const dir = data.dir || process.cwd();
6413
+ await this.startCliSession(cliType, dir);
6414
+ return { success: true, started: true };
6415
+ }
6416
+ case "stop_cli": {
6417
+ const cliType = data.cliType || "gemini-cli";
6418
+ const dir = data.dir || "";
6419
+ const key = `${cliType}:${dir}`;
6420
+ const adapter = this.adapters.get(key);
6421
+ if (adapter) {
6422
+ try {
6423
+ adapter.destroy?.();
6424
+ } catch {
6425
+ }
6426
+ this.adapters.delete(key);
6427
+ }
6428
+ return { success: true, stopped: true };
6429
+ }
6430
+ case "restart_session": {
6431
+ const cliType = data.cliType || data.ideType || "gemini-cli";
6432
+ const dir = data.dir || process.cwd();
6433
+ for (const [key, adapter] of this.adapters) {
6434
+ if (key.startsWith(cliType)) {
6435
+ try {
6436
+ adapter.destroy?.();
6437
+ } catch {
6438
+ }
6439
+ this.adapters.delete(key);
6440
+ }
6441
+ }
6442
+ await this.startCliSession(cliType, dir);
6443
+ return { success: true, restarted: true };
6444
+ }
6445
+ }
6446
+ if (this.commandHandler) {
6447
+ return this.commandHandler.handle(cmdType, data);
6448
+ }
6449
+ return { success: false, error: `Unknown command: ${cmdType}` };
6450
+ } catch (e) {
6451
+ return { success: false, error: e.message };
6452
+ }
6453
+ }
5010
6454
  // ─── CLI 세션 관리 ──────────────────────────────
5011
6455
  createAdapter(cliType, workingDir) {
5012
6456
  if (cliType === "claude-cli" || cliType === "claude-code") {
@@ -5018,7 +6462,7 @@ var init_adhdev_daemon = __esm({
5018
6462
  }
5019
6463
  }
5020
6464
  async startCliSession(cliType, workingDir) {
5021
- const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os8.homedir()) : workingDir;
6465
+ const resolvedDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os9.homedir()) : workingDir;
5022
6466
  const cliInfo = await detectCLI(cliType);
5023
6467
  if (!cliInfo) throw new Error(`${cliType} not found`);
5024
6468
  const key = this.getCliKey(cliType, resolvedDir);
@@ -5033,6 +6477,17 @@ var init_adhdev_daemon = __esm({
5033
6477
  adapter.setBridge(this.bridge);
5034
6478
  }
5035
6479
  adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
6480
+ if (typeof adapter.setOnPtyData === "function") {
6481
+ adapter.setOnPtyData((data) => {
6482
+ const sentViaP2P = this.p2p?.broadcastPtyOutput(adapter.cliType, data);
6483
+ if (!sentViaP2P && this.bridge) {
6484
+ this.bridge.sendMessage("pty_output", {
6485
+ cliType: adapter.cliType,
6486
+ data
6487
+ });
6488
+ }
6489
+ });
6490
+ }
5036
6491
  this.adapters.set(key, adapter);
5037
6492
  this.lastAgentStatus.set(key, "idle");
5038
6493
  console.log(import_chalk2.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
@@ -5052,28 +6507,44 @@ var init_adhdev_daemon = __esm({
5052
6507
  // ─── 통합 상태 보고 ─────────────────────────────
5053
6508
  startStatusReporting() {
5054
6509
  const scheduleNext = () => {
5055
- const isProcessing = Array.from(this.adapters.values()).some((a) => a.isProcessing());
5056
- const interval = isProcessing ? 2e3 : 15e3;
5057
6510
  this.statusTimer = setTimeout(() => {
5058
6511
  this.sendUnifiedStatusReport().catch(() => {
5059
6512
  });
5060
6513
  scheduleNext();
5061
- }, interval);
6514
+ }, 5e3);
5062
6515
  };
5063
6516
  scheduleNext();
5064
6517
  }
6518
+ /** Throttled status report: 최소 3초 간격 보장 */
6519
+ throttledStatusReport() {
6520
+ const now = Date.now();
6521
+ const elapsed = now - this.lastStatusSentAt;
6522
+ if (elapsed >= 3e3) {
6523
+ this.sendUnifiedStatusReport().catch(() => {
6524
+ });
6525
+ } else if (!this.statusPendingThrottle) {
6526
+ this.statusPendingThrottle = true;
6527
+ setTimeout(() => {
6528
+ this.statusPendingThrottle = false;
6529
+ this.sendUnifiedStatusReport().catch(() => {
6530
+ });
6531
+ }, 3e3 - elapsed);
6532
+ }
6533
+ }
5065
6534
  async sendUnifiedStatusReport() {
5066
6535
  if (!this.bridge?.isConnected()) return;
6536
+ this.lastStatusSentAt = Date.now();
6537
+ console.log(`[StatusReport] Starting... cdp=${this.cdpManagers.size} scripts=${this.scriptLoaders.size} busy=${this._cdpChatBusy}`);
5067
6538
  if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
5068
6539
  this._cdpChatBusy = true;
5069
- for (const [ideType, cdp] of this.cdpManagers) {
5070
- if (!cdp.isConnected) continue;
6540
+ for (const [ideType, cdp2] of this.cdpManagers) {
6541
+ if (!cdp2.isConnected) continue;
5071
6542
  const loader = this.scriptLoaders.get(ideType);
5072
6543
  if (!loader) continue;
5073
6544
  const readChatScript = loader.get("read_chat");
5074
6545
  if (!readChatScript) continue;
5075
6546
  try {
5076
- const raw = await cdp.evaluate(readChatScript, 3e4);
6547
+ const raw = await cdp2.evaluate(readChatScript, 3e4);
5077
6548
  if (raw && typeof raw === "object") {
5078
6549
  let { activeModal } = raw;
5079
6550
  if (activeModal) {
@@ -5085,9 +6556,54 @@ var init_adhdev_daemon = __esm({
5085
6556
  buttons: (activeModal.buttons ?? []).filter((t) => t.length < 30)
5086
6557
  };
5087
6558
  }
5088
- this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
6559
+ const msgCount = raw.messages?.length || 0;
6560
+ console.log(`[StatusReport] ${ideType} read_chat: ${msgCount} msgs, status=${raw.status}, id=${raw.id?.substring(0, 15)}`);
6561
+ if (msgCount > 0) {
6562
+ const prevChat = this._cachedActiveChatMap.get(ideType);
6563
+ const prevMsgs = prevChat?.messages || [];
6564
+ const prevByHash = /* @__PURE__ */ new Map();
6565
+ for (const pm of prevMsgs) {
6566
+ const h = `${pm.role}:${(pm.content || "").slice(0, 100)}`;
6567
+ if (pm.receivedAt) prevByHash.set(h, pm.receivedAt);
6568
+ }
6569
+ const now2 = Date.now();
6570
+ for (const msg of raw.messages) {
6571
+ const h = `${msg.role}:${(msg.content || "").slice(0, 100)}`;
6572
+ msg.receivedAt = prevByHash.get(h) || now2;
6573
+ }
6574
+ this._cachedActiveChatMap.set(ideType, { ...raw, activeModal });
6575
+ } else {
6576
+ try {
6577
+ const inlineResult = await cdp2.evaluate(`(() => {
6578
+ const container = document.querySelector('.flex.flex-col.gap-2\\\\.5');
6579
+ if (!container) return { messages: [], status: 'idle', id: 'cascade' };
6580
+ const msgs = [];
6581
+ for (const child of container.children) {
6582
+ if ((child.className || '').includes('mark-js-ignore')) continue;
6583
+ if (child.offsetHeight < 10) continue;
6584
+ const inner = child.children[0];
6585
+ if (!inner) continue;
6586
+ const isUser = inner.className?.includes('flex-row') || !!inner.querySelector('.justify-end');
6587
+ const role = isUser ? 'user' : 'assistant';
6588
+ const text = child.textContent?.trim() || '';
6589
+ if (text.length < 1) continue;
6590
+ msgs.push({ id: 'msg_' + msgs.length, role, content: text.substring(0, 6000), index: msgs.length });
6591
+ }
6592
+ return { messages: msgs, status: 'idle', id: 'cascade' };
6593
+ })()`, 1e4);
6594
+ console.log(`[StatusReport] ${ideType} inline fallback: ${inlineResult?.messages?.length || 0} msgs`);
6595
+ if (inlineResult?.messages?.length > 0) {
6596
+ this._cachedActiveChatMap.set(ideType, { ...inlineResult, activeModal });
6597
+ }
6598
+ } catch (e2) {
6599
+ console.log(`[StatusReport] ${ideType} inline error: ${e2.message}`);
6600
+ }
6601
+ }
6602
+ } else {
6603
+ console.log(`[StatusReport] ${ideType} read_chat: null/non-object result`);
5089
6604
  }
5090
- } catch {
6605
+ } catch (e1) {
6606
+ console.log(`[StatusReport] ${ideType} read_chat error: ${e1.message}`);
5091
6607
  }
5092
6608
  }
5093
6609
  this._cdpChatBusy = false;
@@ -5138,6 +6654,29 @@ var init_adhdev_daemon = __esm({
5138
6654
  cdpConnected: isMainCdpIde ? this.cdpManagers.get(ext.ideType.toLowerCase())?.isConnected || false : false
5139
6655
  };
5140
6656
  });
6657
+ const managedIdeTypes = new Set(managedIdes.map((ide) => ide.ideType.toLowerCase()));
6658
+ for (const [ideType, cdp2] of this.cdpManagers) {
6659
+ if (!cdp2.isConnected) continue;
6660
+ if (managedIdeTypes.has(ideType)) continue;
6661
+ if (enabledIdesFilter.length > 0 && !enabledIdesFilter.includes(ideType)) continue;
6662
+ const cachedChat = this._cachedActiveChatMap.get(ideType);
6663
+ const cdpStreams = this._cachedAgentStreamsMap.get(ideType) || [];
6664
+ const machineHash = require("crypto").createHash("md5").update(os9.hostname() + ideType).digest("hex").slice(0, 8);
6665
+ console.log(`[StatusReport] Adding CDP-only IDE: ${ideType} (no bridge ext, chat=${cachedChat?.messages?.length || 0} msgs)`);
6666
+ managedIdes.push({
6667
+ ideType,
6668
+ ideVersion: "",
6669
+ instanceId: `${ideType}_${machineHash}`,
6670
+ workspaceFolders: [],
6671
+ activeFile: null,
6672
+ terminals: 0,
6673
+ aiAgents: [],
6674
+ activeChat: cachedChat || null,
6675
+ chats: [],
6676
+ agentStreams: cdpStreams,
6677
+ cdpConnected: true
6678
+ });
6679
+ }
5141
6680
  const managedClis = [];
5142
6681
  for (const [key, adapter] of this.adapters.entries()) {
5143
6682
  const adapterStatus = adapter.getStatus();
@@ -5148,7 +6687,7 @@ var init_adhdev_daemon = __esm({
5148
6687
  this.generatingStartedAt.set(key, now);
5149
6688
  this.bridge?.sendMessage("status_event", {
5150
6689
  event: "agent:generating_started",
5151
- chatTitle: `${adapter.cliName} Session`,
6690
+ chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
5152
6691
  timestamp: now
5153
6692
  });
5154
6693
  } else if (lastStatus === "generating" && cliStatus === "idle") {
@@ -5156,7 +6695,7 @@ var init_adhdev_daemon = __esm({
5156
6695
  const duration = startedAt ? Math.round((now - startedAt) / 1e3) : 0;
5157
6696
  this.bridge?.sendMessage("status_event", {
5158
6697
  event: "agent:generating_completed",
5159
- chatTitle: `${adapter.cliName} Session`,
6698
+ chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
5160
6699
  duration,
5161
6700
  timestamp: now
5162
6701
  });
@@ -5182,7 +6721,7 @@ var init_adhdev_daemon = __esm({
5182
6721
  activeChat: {
5183
6722
  id: key,
5184
6723
  status: cliStatus,
5185
- title: `${adapter.cliName} Session`,
6724
+ title: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
5186
6725
  messages: cliMessages,
5187
6726
  inputContent: "",
5188
6727
  activeModal: adapterStatus.activeModal
@@ -5196,15 +6735,15 @@ var init_adhdev_daemon = __esm({
5196
6735
  // Machine 정보
5197
6736
  daemonMode: true,
5198
6737
  machine: {
5199
- hostname: os8.hostname(),
5200
- platform: os8.platform(),
5201
- release: os8.release(),
5202
- arch: os8.arch(),
5203
- cpus: os8.cpus().length,
5204
- totalMem: os8.totalmem(),
5205
- freeMem: os8.freemem(),
5206
- loadavg: os8.loadavg(),
5207
- uptime: os8.uptime()
6738
+ hostname: os9.hostname(),
6739
+ platform: os9.platform(),
6740
+ release: os9.release(),
6741
+ arch: os9.arch(),
6742
+ cpus: os9.cpus().length,
6743
+ totalMem: os9.totalmem(),
6744
+ freeMem: os9.freemem(),
6745
+ loadavg: os9.loadavg(),
6746
+ uptime: os9.uptime()
5208
6747
  },
5209
6748
  // 관리 중인 IDE/CLI (계층 데이터)
5210
6749
  managedIdes,
@@ -5227,7 +6766,7 @@ var init_adhdev_daemon = __esm({
5227
6766
  timestamp: now,
5228
6767
  // ─── Legacy compat (서버 기존 로직용, 점진적 제거 예정) ───
5229
6768
  activeFile: extSummary.activeFile,
5230
- workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : Array.from(this.adapters.values()).map((a) => ({ name: path4.basename(a.workingDir), path: a.workingDir })),
6769
+ workspaceFolders: extSummary.workspaceFolders.length > 0 ? extSummary.workspaceFolders : Array.from(this.adapters.values()).map((a) => ({ name: path5.basename(a.workingDir), path: a.workingDir })),
5231
6770
  terminals: extSummary.terminals,
5232
6771
  // Legacy aiAgents: managedIdes에서 각 IDE의 aiAgents를 통합
5233
6772
  aiAgents: [
@@ -5246,19 +6785,49 @@ var init_adhdev_daemon = __esm({
5246
6785
  ],
5247
6786
  connectedExtensions: extSummary.connectedIdes,
5248
6787
  system: {
5249
- platform: os8.platform(),
5250
- release: os8.release(),
5251
- arch: os8.arch(),
5252
- cpus: os8.cpus().length,
5253
- totalMem: os8.totalmem(),
5254
- freeMem: os8.freemem(),
5255
- loadavg: os8.loadavg(),
5256
- uptime: os8.uptime(),
5257
- hostname: os8.hostname()
6788
+ platform: os9.platform(),
6789
+ release: os9.release(),
6790
+ arch: os9.arch(),
6791
+ cpus: os9.cpus().length,
6792
+ totalMem: os9.totalmem(),
6793
+ freeMem: os9.freemem(),
6794
+ loadavg: os9.loadavg(),
6795
+ uptime: os9.uptime(),
6796
+ hostname: os9.hostname()
5258
6797
  }
5259
6798
  };
5260
- this.bridge.sendMessage("status_report", payload);
5261
- this.p2p?.sendStatus(payload);
6799
+ const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
6800
+ if (hashTarget.machine) {
6801
+ const { freeMem: _f, loadavg: _l, uptime: _u, ...stableMachine } = hashTarget.machine;
6802
+ hashTarget.machine = stableMachine;
6803
+ }
6804
+ const statusHash = this.simpleHash(JSON.stringify(hashTarget));
6805
+ if (statusHash !== this.lastP2PStatusHash) {
6806
+ this.lastP2PStatusHash = statusHash;
6807
+ this.p2p?.sendStatus(payload);
6808
+ }
6809
+ const p2pConnected = (this.p2p?.connectedPeerCount || 0) > 0;
6810
+ this.statusReportCount = (this.statusReportCount || 0) + 1;
6811
+ const forceFullSync = this.statusReportCount % 12 === 0;
6812
+ if (p2pConnected && this.lastStatusSentFull && !forceFullSync) {
6813
+ this.bridge.sendMessage("status_heartbeat", {
6814
+ timestamp: now,
6815
+ p2pConnected: true,
6816
+ p2pPeers: this.p2p?.connectedPeerCount || 0
6817
+ });
6818
+ } else {
6819
+ this.bridge.sendMessage("status_report", payload);
6820
+ this.lastStatusSentFull = true;
6821
+ }
6822
+ }
6823
+ /** Fast string hash (FNV-1a 32-bit) */
6824
+ simpleHash(s) {
6825
+ let h = 2166136261;
6826
+ for (let i = 0; i < s.length; i++) {
6827
+ h ^= s.charCodeAt(i);
6828
+ h = h * 16777619 >>> 0;
6829
+ }
6830
+ return h.toString(36);
5262
6831
  }
5263
6832
  sendResult(msg, success, extra) {
5264
6833
  if (!msg.id) return;
@@ -5320,17 +6889,41 @@ var init_adhdev_daemon = __esm({
5320
6889
  return this.cdpManagers.get(ideType.toLowerCase()) || null;
5321
6890
  }
5322
6891
  /** Agent stream polling 시작 (idempotent — 이미 시작됐으면 무시) */
6892
+ _agentStreamCdpIdeType = null;
6893
+ // agent가 연결된 IDE
5323
6894
  startAgentStreamPolling() {
5324
6895
  if (this.agentStreamTimer) return;
5325
6896
  this.agentStreamTimer = setInterval(async () => {
5326
6897
  if (!this.agentStreamManager || this.cdpManagers.size === 0) return;
5327
- for (const [ideType, cdp] of this.cdpManagers) {
5328
- if (!cdp.isConnected) continue;
5329
- try {
5330
- await this.agentStreamManager.syncAgentSessions(cdp);
5331
- const streams = await this.agentStreamManager.collectAgentStreams(cdp);
5332
- this._cachedAgentStreamsMap.set(ideType, streams);
5333
- } catch {
6898
+ if (this._agentStreamCdpIdeType) {
6899
+ const cdp2 = this.cdpManagers.get(this._agentStreamCdpIdeType);
6900
+ if (cdp2?.isConnected) {
6901
+ try {
6902
+ await this.agentStreamManager.syncAgentSessions(cdp2);
6903
+ const streams = await this.agentStreamManager.collectAgentStreams(cdp2);
6904
+ this._cachedAgentStreamsMap.set(this._agentStreamCdpIdeType, streams);
6905
+ } catch {
6906
+ }
6907
+ return;
6908
+ }
6909
+ this._agentStreamCdpIdeType = null;
6910
+ }
6911
+ if (!this.agentStreamManager.activeAgentType) {
6912
+ for (const [ideType, cdp2] of this.cdpManagers) {
6913
+ if (!cdp2.isConnected) continue;
6914
+ try {
6915
+ const discovered = await cdp2.discoverAgentWebviews();
6916
+ if (discovered.length > 0) {
6917
+ this._agentStreamCdpIdeType = ideType;
6918
+ await this.agentStreamManager.switchActiveAgent(cdp2, discovered[0].agentType);
6919
+ console.log(`[AgentStream] Auto-activated: ${discovered[0].agentType} (${ideType})`);
6920
+ await this.agentStreamManager.syncAgentSessions(cdp2);
6921
+ const streams = await this.agentStreamManager.collectAgentStreams(cdp2);
6922
+ this._cachedAgentStreamsMap.set(ideType, streams);
6923
+ return;
6924
+ }
6925
+ } catch {
6926
+ }
5334
6927
  }
5335
6928
  }
5336
6929
  }, 5e3);
@@ -5357,7 +6950,7 @@ var init_adhdev_daemon = __esm({
5357
6950
  const filteredPorts = enabledIdes.length > 0 ? portsToTry.filter((p) => enabledIdes.includes(p.ide)) : portsToTry;
5358
6951
  for (const { port, ide } of filteredPorts) {
5359
6952
  const manager = new DaemonCdpManager(port, (msg) => {
5360
- console.log(import_chalk2.default.gray(msg));
6953
+ console.log(`[CDP:${ide}] ${msg}`);
5361
6954
  }, true);
5362
6955
  const connected = await manager.connect();
5363
6956
  if (connected) {
@@ -5400,27 +6993,27 @@ __export(cli_daemon_exports, {
5400
6993
  uninstallService: () => uninstallService
5401
6994
  });
5402
6995
  function getPidFile(workingDir, cliType) {
5403
- const absPath = path5.resolve(workingDir);
6996
+ const absPath = path6.resolve(workingDir);
5404
6997
  const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
5405
- const dir = path5.join(os9.homedir(), ".adhdev");
5406
- if (!fs6.existsSync(dir)) fs6.mkdirSync(dir, { recursive: true });
5407
- return path5.join(dir, `bridge-${cliType}-${hash}.pid`);
6998
+ const dir = path6.join(os10.homedir(), ".adhdev");
6999
+ if (!fs5.existsSync(dir)) fs5.mkdirSync(dir, { recursive: true });
7000
+ return path6.join(dir, `bridge-${cliType}-${hash}.pid`);
5408
7001
  }
5409
7002
  function writePidFile(pid, workingDir, cliType) {
5410
7003
  const pidFile = getPidFile(workingDir, cliType);
5411
- fs6.writeFileSync(pidFile, String(pid), "utf-8");
7004
+ fs5.writeFileSync(pidFile, String(pid), "utf-8");
5412
7005
  }
5413
7006
  function removePidFile(workingDir, cliType) {
5414
7007
  try {
5415
- fs6.unlinkSync(getPidFile(workingDir, cliType));
7008
+ fs5.unlinkSync(getPidFile(workingDir, cliType));
5416
7009
  } catch {
5417
7010
  }
5418
7011
  }
5419
7012
  function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
5420
7013
  const pidFile = getPidFile(workingDir, cliType);
5421
7014
  try {
5422
- if (!fs6.existsSync(pidFile)) return false;
5423
- const pid = parseInt(fs6.readFileSync(pidFile, "utf-8").trim());
7015
+ if (!fs5.existsSync(pidFile)) return false;
7016
+ const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
5424
7017
  process.kill(pid, 0);
5425
7018
  return true;
5426
7019
  } catch {
@@ -5431,8 +7024,8 @@ function isDaemonRunning2(workingDir = process.cwd(), cliType = "gemini-cli") {
5431
7024
  function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
5432
7025
  const pidFile = getPidFile(workingDir, cliType);
5433
7026
  try {
5434
- if (!fs6.existsSync(pidFile)) return false;
5435
- const pid = parseInt(fs6.readFileSync(pidFile, "utf-8").trim());
7027
+ if (!fs5.existsSync(pidFile)) return false;
7028
+ const pid = parseInt(fs5.readFileSync(pidFile, "utf-8").trim());
5436
7029
  process.kill(pid, "SIGTERM");
5437
7030
  removePidFile(workingDir, cliType);
5438
7031
  return true;
@@ -5442,40 +7035,40 @@ function stopDaemon2(workingDir = process.cwd(), cliType = "gemini-cli") {
5442
7035
  }
5443
7036
  }
5444
7037
  function getServiceLabel(workingDir, cliType) {
5445
- const absPath = path5.resolve(workingDir);
7038
+ const absPath = path6.resolve(workingDir);
5446
7039
  const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
5447
7040
  return `dev.adhf.cli-bridge.${cliType}.${hash}`;
5448
7041
  }
5449
7042
  function getLogFile(workingDir, cliType, isErr = false) {
5450
- const absPath = path5.resolve(workingDir);
7043
+ const absPath = path6.resolve(workingDir);
5451
7044
  const hash = crypto3.createHash("md5").update(absPath).digest("hex").slice(0, 8);
5452
- if (!fs6.existsSync(LOG_DIR)) fs6.mkdirSync(LOG_DIR, { recursive: true });
5453
- return path5.join(LOG_DIR, `bridge-${cliType}-${hash}${isErr ? ".err" : ""}.log`);
7045
+ if (!fs5.existsSync(LOG_DIR2)) fs5.mkdirSync(LOG_DIR2, { recursive: true });
7046
+ return path6.join(LOG_DIR2, `bridge-${cliType}-${hash}${isErr ? ".err" : ""}.log`);
5454
7047
  }
5455
7048
  function getServicePaths(label) {
5456
- const platform9 = os9.platform();
7049
+ const platform9 = os10.platform();
5457
7050
  if (platform9 === "darwin") {
5458
7051
  return {
5459
- plist: path5.join(os9.homedir(), "Library", "LaunchAgents", `${label}.plist`)
7052
+ plist: path6.join(os10.homedir(), "Library", "LaunchAgents", `${label}.plist`)
5460
7053
  };
5461
7054
  } else if (platform9 === "linux") {
5462
7055
  return {
5463
- systemd: path5.join(os9.homedir(), ".config", "systemd", "user", `${label}.service`)
7056
+ systemd: path6.join(os10.homedir(), ".config", "systemd", "user", `${label}.service`)
5464
7057
  };
5465
7058
  } else if (platform9 === "win32") {
5466
7059
  return {
5467
- startup: path5.join(os9.homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", `${label}.vbs`)
7060
+ startup: path6.join(os10.homedir(), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup", `${label}.vbs`)
5468
7061
  };
5469
7062
  }
5470
7063
  return {};
5471
7064
  }
5472
7065
  function installService(options = {}) {
5473
- const platform9 = os9.platform();
7066
+ const platform9 = os10.platform();
5474
7067
  const cliType = options.cliType || "gemini-cli";
5475
- const workingDir = options.workingDir || os9.homedir();
7068
+ const workingDir = options.workingDir || os10.homedir();
5476
7069
  const adhdevBin = process.argv[1];
5477
7070
  const nodeBin = process.execPath;
5478
- if (!fs6.existsSync(LOG_DIR)) fs6.mkdirSync(LOG_DIR, { recursive: true });
7071
+ if (!fs5.existsSync(LOG_DIR2)) fs5.mkdirSync(LOG_DIR2, { recursive: true });
5479
7072
  const label = getServiceLabel(workingDir, cliType);
5480
7073
  const logFile2 = getLogFile(workingDir, cliType);
5481
7074
  const errLogFile = getLogFile(workingDir, cliType, true);
@@ -5530,17 +7123,17 @@ function installMacService(nodeBin, adhdevBin, cliType, workingDir, label, logFi
5530
7123
  <key>PATH</key>
5531
7124
  <string>${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}</string>
5532
7125
  <key>HOME</key>
5533
- <string>${os9.homedir()}</string>
7126
+ <string>${os10.homedir()}</string>
5534
7127
  </dict>
5535
7128
  </dict>
5536
7129
  </plist>`;
5537
- const agentsDir = path5.dirname(plistPath);
5538
- if (!fs6.existsSync(agentsDir)) fs6.mkdirSync(agentsDir, { recursive: true });
7130
+ const agentsDir = path6.dirname(plistPath);
7131
+ if (!fs5.existsSync(agentsDir)) fs5.mkdirSync(agentsDir, { recursive: true });
5539
7132
  try {
5540
7133
  execSync8(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "pipe" });
5541
7134
  } catch {
5542
7135
  }
5543
- fs6.writeFileSync(plistPath, plistContent, "utf-8");
7136
+ fs5.writeFileSync(plistPath, plistContent, "utf-8");
5544
7137
  try {
5545
7138
  execSync8(`launchctl load "${plistPath}"`, { stdio: "pipe" });
5546
7139
  } catch (e) {
@@ -5566,14 +7159,14 @@ RestartSec=10
5566
7159
  StandardOutput=append:${logFile2}
5567
7160
  StandardError=append:${errLogFile}
5568
7161
  Environment=PATH=${process.env.PATH || "/usr/local/bin:/usr/bin:/bin"}
5569
- Environment=HOME=${os9.homedir()}
7162
+ Environment=HOME=${os10.homedir()}
5570
7163
 
5571
7164
  [Install]
5572
7165
  WantedBy=default.target
5573
7166
  `;
5574
- const serviceDir = path5.dirname(servicePath);
5575
- if (!fs6.existsSync(serviceDir)) fs6.mkdirSync(serviceDir, { recursive: true });
5576
- fs6.writeFileSync(servicePath, serviceContent, "utf-8");
7167
+ const serviceDir = path6.dirname(servicePath);
7168
+ if (!fs5.existsSync(serviceDir)) fs5.mkdirSync(serviceDir, { recursive: true });
7169
+ fs5.writeFileSync(servicePath, serviceContent, "utf-8");
5577
7170
  try {
5578
7171
  execSync8("systemctl --user daemon-reload", { stdio: "pipe" });
5579
7172
  execSync8(`systemctl --user enable ${label}.service`, { stdio: "pipe" });
@@ -5590,9 +7183,9 @@ function installWindowsService(nodeBin, adhdevBin, cliType, workingDir, label) {
5590
7183
  const vbsContent = `Set WshShell = CreateObject("WScript.Shell")
5591
7184
  WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliType} --dir ""${workingDir}""", 0, False
5592
7185
  `;
5593
- const startupDir = path5.dirname(startupPath);
5594
- if (!fs6.existsSync(startupDir)) fs6.mkdirSync(startupDir, { recursive: true });
5595
- fs6.writeFileSync(startupPath, vbsContent, "utf-8");
7186
+ const startupDir = path6.dirname(startupPath);
7187
+ if (!fs5.existsSync(startupDir)) fs5.mkdirSync(startupDir, { recursive: true });
7188
+ fs5.writeFileSync(startupPath, vbsContent, "utf-8");
5596
7189
  try {
5597
7190
  const { execSync: execSync8 } = require("child_process");
5598
7191
  execSync8(`wscript "${startupPath}"`, { stdio: "pipe" });
@@ -5601,9 +7194,9 @@ WshShell.Run """${nodeBin}"" ""${adhdevBin}"" bridge --foreground --cli ${cliTyp
5601
7194
  return true;
5602
7195
  }
5603
7196
  function uninstallService(options = {}) {
5604
- const platform9 = os9.platform();
7197
+ const platform9 = os10.platform();
5605
7198
  const cliType = options.cliType || "gemini-cli";
5606
- const workingDir = options.workingDir || os9.homedir();
7199
+ const workingDir = options.workingDir || os10.homedir();
5607
7200
  const label = getServiceLabel(workingDir, cliType);
5608
7201
  const paths = getServicePaths(label);
5609
7202
  const { execSync: execSync8 } = require("child_process");
@@ -5613,7 +7206,7 @@ function uninstallService(options = {}) {
5613
7206
  } catch {
5614
7207
  }
5615
7208
  try {
5616
- if (fs6.existsSync(paths.plist)) fs6.unlinkSync(paths.plist);
7209
+ if (fs5.existsSync(paths.plist)) fs5.unlinkSync(paths.plist);
5617
7210
  } catch {
5618
7211
  }
5619
7212
  } else if (platform9 === "linux" && paths.systemd) {
@@ -5623,7 +7216,7 @@ function uninstallService(options = {}) {
5623
7216
  } catch {
5624
7217
  }
5625
7218
  try {
5626
- if (fs6.existsSync(paths.systemd)) fs6.unlinkSync(paths.systemd);
7219
+ if (fs5.existsSync(paths.systemd)) fs5.unlinkSync(paths.systemd);
5627
7220
  } catch {
5628
7221
  }
5629
7222
  try {
@@ -5632,25 +7225,25 @@ function uninstallService(options = {}) {
5632
7225
  }
5633
7226
  } else if (platform9 === "win32" && paths.startup) {
5634
7227
  try {
5635
- if (fs6.existsSync(paths.startup)) fs6.unlinkSync(paths.startup);
7228
+ if (fs5.existsSync(paths.startup)) fs5.unlinkSync(paths.startup);
5636
7229
  } catch {
5637
7230
  }
5638
7231
  }
5639
7232
  removePidFile(workingDir, cliType);
5640
7233
  return true;
5641
7234
  }
5642
- function isServiceInstalled(workingDir = os9.homedir(), cliType = "gemini-cli") {
7235
+ function isServiceInstalled(workingDir = os10.homedir(), cliType = "gemini-cli") {
5643
7236
  const label = getServiceLabel(workingDir, cliType);
5644
7237
  const paths = getServicePaths(label);
5645
- if (paths.plist) return fs6.existsSync(paths.plist);
5646
- if (paths.systemd) return fs6.existsSync(paths.systemd);
5647
- if (paths.startup) return fs6.existsSync(paths.startup);
7238
+ if (paths.plist) return fs5.existsSync(paths.plist);
7239
+ if (paths.systemd) return fs5.existsSync(paths.systemd);
7240
+ if (paths.startup) return fs5.existsSync(paths.startup);
5648
7241
  return false;
5649
7242
  }
5650
- function getLogPath(workingDir = os9.homedir(), cliType = "gemini-cli") {
7243
+ function getLogPath(workingDir = os10.homedir(), cliType = "gemini-cli") {
5651
7244
  return getLogFile(workingDir, cliType);
5652
7245
  }
5653
- var os9, fs6, path5, crypto3, import_chalk3, DANGEROUS_PATTERNS2, CliDaemon, LOG_DIR;
7246
+ var os10, fs5, path6, crypto3, import_chalk3, DANGEROUS_PATTERNS2, CliDaemon, LOG_DIR2;
5654
7247
  var init_cli_daemon = __esm({
5655
7248
  "src/cli-daemon.ts"() {
5656
7249
  "use strict";
@@ -5660,9 +7253,9 @@ var init_cli_daemon = __esm({
5660
7253
  init_codex_cli();
5661
7254
  init_cli_detector();
5662
7255
  init_config();
5663
- os9 = __toESM(require("os"));
5664
- fs6 = __toESM(require("fs"));
5665
- path5 = __toESM(require("path"));
7256
+ os10 = __toESM(require("os"));
7257
+ fs5 = __toESM(require("fs"));
7258
+ path6 = __toESM(require("path"));
5666
7259
  crypto3 = __toESM(require("crypto"));
5667
7260
  import_chalk3 = __toESM(require("chalk"));
5668
7261
  init_detector();
@@ -5731,8 +7324,8 @@ var init_cli_daemon = __esm({
5731
7324
  this.adapter.setOnStatusChange(() => {
5732
7325
  this.sendStatusReport();
5733
7326
  });
5734
- const machineId = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
5735
- const dirHash = crypto3.createHash("md5").update(path5.resolve(workingDir)).digest("hex").slice(0, 4);
7327
+ const machineId = os10.hostname().replace(/[^a-zA-Z0-9]/g, "_");
7328
+ const dirHash = crypto3.createHash("md5").update(path6.resolve(workingDir)).digest("hex").slice(0, 4);
5736
7329
  const instanceId = `${cliType}_${machineId}_${dirHash}`;
5737
7330
  this.bridge = new CliBridgeConnection({
5738
7331
  serverUrl: options.serverUrl || config.serverUrl,
@@ -5740,7 +7333,7 @@ var init_cli_daemon = __esm({
5740
7333
  cliInfo: {
5741
7334
  type: cliType,
5742
7335
  version: cliInfo.version || "unknown",
5743
- platform: os9.platform(),
7336
+ platform: os10.platform(),
5744
7337
  instanceId
5745
7338
  }
5746
7339
  });
@@ -5811,11 +7404,11 @@ var init_cli_daemon = __esm({
5811
7404
  }
5812
7405
  case "list_files": {
5813
7406
  const targetDir = args?.dir || this.adapter?.workingDir || process.cwd();
5814
- const entries = fs6.readdirSync(targetDir).map((f) => {
7407
+ const entries = fs5.readdirSync(targetDir).map((f) => {
5815
7408
  try {
5816
7409
  return {
5817
7410
  name: f,
5818
- isDirectory: fs6.statSync(path5.join(targetDir, f)).isDirectory()
7411
+ isDirectory: fs5.statSync(path6.join(targetDir, f)).isDirectory()
5819
7412
  };
5820
7413
  } catch {
5821
7414
  return null;
@@ -5927,14 +7520,14 @@ var init_cli_daemon = __esm({
5927
7520
  this.sendStatusReport();
5928
7521
  });
5929
7522
  if (this.bridge) {
5930
- const machineId = os9.hostname().replace(/[^a-zA-Z0-9]/g, "_");
5931
- const dirHash = crypto3.createHash("md5").update(path5.resolve(workingDir)).digest("hex").slice(0, 4);
7523
+ const machineId = os10.hostname().replace(/[^a-zA-Z0-9]/g, "_");
7524
+ const dirHash = crypto3.createHash("md5").update(path6.resolve(workingDir)).digest("hex").slice(0, 4);
5932
7525
  const config2 = this.bridge.options;
5933
7526
  config2.cliInfo = {
5934
7527
  ...config2.cliInfo,
5935
7528
  type: cliType,
5936
7529
  version: cliInfo.version || "unknown",
5937
- platform: os9.platform(),
7530
+ platform: os10.platform(),
5938
7531
  instanceId: `${cliType}_${machineId}_${dirHash}`
5939
7532
  };
5940
7533
  }
@@ -6001,13 +7594,13 @@ var init_cli_daemon = __esm({
6001
7594
  const payload = {
6002
7595
  activeFile: null,
6003
7596
  workspaceFolders: [{
6004
- name: path5.basename(adapterStatus.workingDir),
7597
+ name: path6.basename(adapterStatus.workingDir),
6005
7598
  path: adapterStatus.workingDir
6006
7599
  }],
6007
7600
  currentConfig: {
6008
7601
  cli: this.bridge?.getCliInfo()?.type || "unknown",
6009
7602
  dir: adapterStatus.workingDir,
6010
- homeDir: os9.homedir()
7603
+ homeDir: os10.homedir()
6011
7604
  },
6012
7605
  terminals: 1,
6013
7606
  aiAgents: [{
@@ -6026,19 +7619,19 @@ var init_cli_daemon = __esm({
6026
7619
  currentConfig: {
6027
7620
  cli: this.bridge?.getCliInfo()?.type || "unknown",
6028
7621
  dir: adapterStatus.workingDir,
6029
- homeDir: os9.homedir()
7622
+ homeDir: os10.homedir()
6030
7623
  }
6031
7624
  }],
6032
7625
  system: {
6033
- platform: os9.platform(),
6034
- release: os9.release(),
6035
- arch: os9.arch(),
6036
- cpus: os9.cpus().length,
6037
- totalMem: os9.totalmem(),
6038
- freeMem: os9.freemem(),
6039
- loadavg: os9.loadavg(),
6040
- uptime: os9.uptime(),
6041
- hostname: os9.hostname()
7626
+ platform: os10.platform(),
7627
+ release: os10.release(),
7628
+ arch: os10.arch(),
7629
+ cpus: os10.cpus().length,
7630
+ totalMem: os10.totalmem(),
7631
+ freeMem: os10.freemem(),
7632
+ loadavg: os10.loadavg(),
7633
+ uptime: os10.uptime(),
7634
+ hostname: os10.hostname()
6042
7635
  },
6043
7636
  detectedIdes: this.detectedIdes,
6044
7637
  activeChat: {
@@ -6077,7 +7670,7 @@ var init_cli_daemon = __esm({
6077
7670
  process.exit(0);
6078
7671
  }
6079
7672
  };
6080
- LOG_DIR = path5.join(os9.homedir(), ".adhdev", "logs");
7673
+ LOG_DIR2 = path6.join(os10.homedir(), ".adhdev", "logs");
6081
7674
  }
6082
7675
  });
6083
7676
 
@@ -6220,8 +7813,8 @@ async function installExtension(ide, extension) {
6220
7813
  const res = await fetch(extension.vsixUrl);
6221
7814
  if (res.ok) {
6222
7815
  const buffer = Buffer.from(await res.arrayBuffer());
6223
- const fs7 = await import("fs");
6224
- fs7.writeFileSync(vsixPath, buffer);
7816
+ const fs6 = await import("fs");
7817
+ fs6.writeFileSync(vsixPath, buffer);
6225
7818
  return new Promise((resolve3) => {
6226
7819
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
6227
7820
  (0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
@@ -6634,18 +8227,18 @@ async function loginFlow() {
6634
8227
  async function injectTokenToIDE(ide, connectionToken) {
6635
8228
  if (!ide.cliCommand) return;
6636
8229
  try {
6637
- const os10 = await import("os");
6638
- const fs7 = await import("fs");
6639
- const path6 = await import("path");
6640
- const platform9 = os10.platform();
6641
- const home = os10.homedir();
8230
+ const os11 = await import("os");
8231
+ const fs6 = await import("fs");
8232
+ const path7 = await import("path");
8233
+ const platform9 = os11.platform();
8234
+ const home = os11.homedir();
6642
8235
  const getSettingsPath = (appName2) => {
6643
8236
  if (platform9 === "darwin") {
6644
- return path6.join(home, "Library", "Application Support", appName2, "User", "settings.json");
8237
+ return path7.join(home, "Library", "Application Support", appName2, "User", "settings.json");
6645
8238
  } else if (platform9 === "win32") {
6646
- return path6.join(process.env.APPDATA || path6.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
8239
+ return path7.join(process.env.APPDATA || path7.join(home, "AppData", "Roaming"), appName2, "User", "settings.json");
6647
8240
  } else {
6648
- return path6.join(home, ".config", appName2, "User", "settings.json");
8241
+ return path7.join(home, ".config", appName2, "User", "settings.json");
6649
8242
  }
6650
8243
  };
6651
8244
  const appNameMap = {
@@ -6660,18 +8253,18 @@ async function injectTokenToIDE(ide, connectionToken) {
6660
8253
  if (!appName) return;
6661
8254
  const settingsPath = getSettingsPath(appName);
6662
8255
  let settings = {};
6663
- if (fs7.existsSync(settingsPath)) {
8256
+ if (fs6.existsSync(settingsPath)) {
6664
8257
  try {
6665
- settings = JSON.parse(fs7.readFileSync(settingsPath, "utf-8"));
8258
+ settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
6666
8259
  } catch {
6667
8260
  settings = {};
6668
8261
  }
6669
8262
  } else {
6670
- fs7.mkdirSync(path6.dirname(settingsPath), { recursive: true });
8263
+ fs6.mkdirSync(path7.dirname(settingsPath), { recursive: true });
6671
8264
  }
6672
8265
  settings["adhdev.connectionToken"] = connectionToken;
6673
8266
  settings["adhdev.autoConnect"] = true;
6674
- fs7.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
8267
+ fs6.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
6675
8268
  console.log(import_chalk.default.green(" \u2713 Connection token saved to IDE settings"));
6676
8269
  } catch (e) {
6677
8270
  console.log(import_chalk.default.yellow(` \u26A0 Could not inject token: ${e.message}`));
@@ -6993,6 +8586,322 @@ program.command("daemon:stop").description("Stop ADHDev Daemon").action(async ()
6993
8586
  `));
6994
8587
  }
6995
8588
  });
8589
+ async function sendDaemonCommand(cmd, args = {}, port = 19222) {
8590
+ const WebSocket4 = (await import("ws")).default;
8591
+ const { DAEMON_WS_PATH: DAEMON_WS_PATH2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
8592
+ return new Promise((resolve3, reject) => {
8593
+ const wsUrl = `ws://127.0.0.1:${port}${DAEMON_WS_PATH2 || "/daemon"}`;
8594
+ const ws = new WebSocket4(wsUrl);
8595
+ const timeout = setTimeout(() => {
8596
+ ws.close();
8597
+ reject(new Error("Timeout: no response from daemon after 15s"));
8598
+ }, 15e3);
8599
+ ws.on("open", () => {
8600
+ ws.send(JSON.stringify({
8601
+ type: "ext:register",
8602
+ payload: {
8603
+ ideType: "cli-debug",
8604
+ ideVersion: "1.0.0",
8605
+ extensionVersion: "1.0.0",
8606
+ instanceId: `cli-debug-${Date.now()}`,
8607
+ machineId: "cli"
8608
+ }
8609
+ }));
8610
+ setTimeout(() => {
8611
+ ws.send(JSON.stringify({
8612
+ type: "ext:command",
8613
+ payload: { command: cmd, args, messageId: `cli-${Date.now()}` }
8614
+ }));
8615
+ }, 200);
8616
+ });
8617
+ ws.on("message", (data) => {
8618
+ try {
8619
+ const msg = JSON.parse(data.toString());
8620
+ if (msg.type === "daemon:command_result" || msg.type === "command_result") {
8621
+ clearTimeout(timeout);
8622
+ ws.close();
8623
+ resolve3(msg.payload?.result || msg.payload || msg);
8624
+ }
8625
+ } catch {
8626
+ }
8627
+ });
8628
+ ws.on("error", (e) => {
8629
+ clearTimeout(timeout);
8630
+ reject(new Error(`Cannot connect to daemon at port ${port}: ${e.message}
8631
+ Is 'adhdev daemon' running?`));
8632
+ });
8633
+ ws.on("close", () => {
8634
+ clearTimeout(timeout);
8635
+ });
8636
+ });
8637
+ }
8638
+ async function directCdpEval(expression, port = 9222) {
8639
+ const http3 = await import("http");
8640
+ const targets = await new Promise((resolve3, reject) => {
8641
+ http3.get(`http://127.0.0.1:${port}/json`, (res) => {
8642
+ let data = "";
8643
+ res.on("data", (c) => data += c);
8644
+ res.on("end", () => {
8645
+ try {
8646
+ resolve3(JSON.parse(data));
8647
+ } catch {
8648
+ reject(new Error("Invalid JSON"));
8649
+ }
8650
+ });
8651
+ }).on("error", (e) => reject(new Error(`Cannot reach CDP at port ${port}: ${e.message}`)));
8652
+ });
8653
+ const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
8654
+ const pages = targets.filter((t) => (t.type === "page" || t.type === "Page") && t.webSocketDebuggerUrl);
8655
+ const mainPages = pages.filter((t) => !isNonMain(t.title || ""));
8656
+ const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
8657
+ if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target found");
8658
+ const WebSocket4 = (await import("ws")).default;
8659
+ return new Promise((resolve3, reject) => {
8660
+ const ws = new WebSocket4(target.webSocketDebuggerUrl);
8661
+ const timeout = setTimeout(() => {
8662
+ ws.close();
8663
+ reject(new Error("CDP timeout"));
8664
+ }, 15e3);
8665
+ let id = 1;
8666
+ ws.on("open", () => {
8667
+ const stripped = expression.replace(/\/\*[\s\S]*?\*\//g, "").trimStart();
8668
+ const isAsync = stripped.startsWith("(async");
8669
+ ws.send(JSON.stringify({
8670
+ id: id++,
8671
+ method: "Runtime.evaluate",
8672
+ params: { expression, returnByValue: true, awaitPromise: isAsync }
8673
+ }));
8674
+ });
8675
+ ws.on("message", (data) => {
8676
+ const msg = JSON.parse(data.toString());
8677
+ if (msg.id) {
8678
+ clearTimeout(timeout);
8679
+ ws.close();
8680
+ if (msg.result?.result?.value !== void 0) {
8681
+ resolve3(msg.result.result.value);
8682
+ } else if (msg.result?.exceptionDetails) {
8683
+ reject(new Error(msg.result.exceptionDetails.text));
8684
+ } else {
8685
+ resolve3(msg.result);
8686
+ }
8687
+ }
8688
+ });
8689
+ ws.on("error", (e) => {
8690
+ clearTimeout(timeout);
8691
+ reject(new Error(`CDP connection failed: ${e.message}`));
8692
+ });
8693
+ });
8694
+ }
8695
+ var cdp = program.command("cdp").description("\u{1F50D} CDP debugging tools \u2014 analyze IDE DOM for script development");
8696
+ cdp.command("debug").description("Analyze IDE AI panel \u2014 find inputs, buttons, textareas, iframes").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon instead of direct CDP").option("--session <id>", "Agent webview session ID").option("-j, --json", "Output raw JSON").action(async (options) => {
8697
+ try {
8698
+ let result;
8699
+ if (options.daemon) {
8700
+ result = await sendDaemonCommand("cdp_dom_debug", { sessionId: options.session });
8701
+ } else {
8702
+ const expression = `(() => {
8703
+ const result = { url: location.href, title: document.title, viewport: { w: window.innerWidth, h: window.innerHeight }, inputs: [], textareas: [], editables: [], buttons: [], iframes: [], textboxes: [] };
8704
+ document.querySelectorAll('input[type="text"], input:not([type])').forEach((el, i) => { if (i >= 10) return; result.inputs.push({ id: el.id||null, class: (el.className||'').toString().slice(0,150), placeholder: el.getAttribute('placeholder')||null, visible: el.offsetParent!==null, rect: (() => { try { const r=el.getBoundingClientRect(); return {x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)}; } catch{return null;} })() }); });
8705
+ document.querySelectorAll('textarea').forEach((el, i) => { if (i >= 10) return; result.textareas.push({ id: el.id||null, class: (el.className||'').toString().slice(0,150), placeholder: el.getAttribute('placeholder')||null, rows: el.rows, visible: el.offsetParent!==null }); });
8706
+ document.querySelectorAll('[contenteditable="true"]').forEach((el, i) => { if (i >= 10) return; result.editables.push({ tag: el.tagName?.toLowerCase(), id: el.id||null, class: (el.className||'').toString().slice(0,150), role: el.getAttribute('role')||null, text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null }); });
8707
+ document.querySelectorAll('[role="textbox"]').forEach((el, i) => { if (i >= 10) return; result.textboxes.push({ tag: el.tagName?.toLowerCase(), id: el.id||null, class: (el.className||'').toString().slice(0,150), text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null }); });
8708
+ const btnKw = /send|submit|accept|reject|approve|deny|cancel|confirm|run|execute|apply/i;
8709
+ document.querySelectorAll('button, [role="button"]').forEach((el, i) => { const text=(el.textContent||el.getAttribute('aria-label')||'').trim(); if(i<30&&(text.length<30||btnKw.test(text))) result.buttons.push({ tag: el.tagName?.toLowerCase(), text: text.slice(0,80), disabled: el.disabled||el.getAttribute('disabled')!==null, visible: el.offsetParent!==null }); });
8710
+ document.querySelectorAll('iframe, webview').forEach((el, i) => { if (i >= 20) return; result.iframes.push({ tag: el.tagName?.toLowerCase(), src: el.getAttribute('src')?.slice(0,200)||null, title: el.getAttribute('title')||null }); });
8711
+ return JSON.stringify(result);
8712
+ })()`;
8713
+ const raw = await directCdpEval(expression, parseInt(options.port));
8714
+ result = typeof raw === "string" ? JSON.parse(raw) : raw;
8715
+ }
8716
+ if (options.json) {
8717
+ console.log(JSON.stringify(result, null, 2));
8718
+ return;
8719
+ }
8720
+ console.log(import_chalk4.default.bold("\n\u{1F50D} CDP DOM Debug\n"));
8721
+ console.log(` ${import_chalk4.default.bold("URL:")} ${result.url || "N/A"}`);
8722
+ console.log(` ${import_chalk4.default.bold("Title:")} ${result.title || "N/A"}`);
8723
+ if (result.viewport) console.log(` ${import_chalk4.default.bold("Viewport:")} ${result.viewport.w}\xD7${result.viewport.h}`);
8724
+ const sections = [
8725
+ { key: "inputs", label: "\u{1F4DD} Inputs", fields: ["id", "placeholder", "class"] },
8726
+ { key: "textareas", label: "\u{1F4DD} Textareas", fields: ["id", "placeholder", "rows"] },
8727
+ { key: "editables", label: "\u270F\uFE0F ContentEditable", fields: ["tag", "role", "class", "text"] },
8728
+ { key: "textboxes", label: '\u{1F4E6} role="textbox"', fields: ["tag", "class", "text"] },
8729
+ { key: "buttons", label: "\u{1F518} Buttons", fields: ["text", "disabled"] },
8730
+ { key: "iframes", label: "\u{1F5BC} Iframes/Webviews", fields: ["tag", "src", "title"] }
8731
+ ];
8732
+ for (const { key, label, fields } of sections) {
8733
+ const items = result[key] || [];
8734
+ if (items.length === 0) continue;
8735
+ console.log(`
8736
+ ${import_chalk4.default.bold(label)} (${items.length})`);
8737
+ for (const item of items) {
8738
+ const vis = item.visible === false ? import_chalk4.default.red(" [hidden]") : "";
8739
+ const parts = fields.map((f) => item[f] ? `${f}=${import_chalk4.default.cyan(String(item[f]).slice(0, 60))}` : null).filter(Boolean);
8740
+ console.log(` ${import_chalk4.default.gray("\u2022")} ${parts.join(" ")}${vis}`);
8741
+ }
8742
+ }
8743
+ console.log();
8744
+ } catch (e) {
8745
+ console.error(import_chalk4.default.red(`
8746
+ \u2717 ${e.message}
8747
+ `));
8748
+ process.exit(1);
8749
+ }
8750
+ });
8751
+ cdp.command("dump [selector]").description("Dump DOM tree (default: body)").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon").option("-f, --format <fmt>", "Format: html, tree, summary", "tree").option("--depth <n>", "Max depth", "6").option("--session <id>", "Agent webview session ID").option("-o, --output <file>", "Save to file").action(async (selector, options) => {
8752
+ try {
8753
+ const sel = selector || "body";
8754
+ const depth = parseInt(options.depth) || 6;
8755
+ let result;
8756
+ if (options.daemon) {
8757
+ const resp = await sendDaemonCommand("cdp_dom_dump", {
8758
+ selector: sel,
8759
+ format: options.format,
8760
+ depth,
8761
+ sessionId: options.session
8762
+ });
8763
+ result = resp?.result || resp;
8764
+ } else {
8765
+ let expr;
8766
+ if (options.format === "tree") {
8767
+ expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return 'Not found: ${sel}'; function tree(el, indent, d, maxD) { if (d > maxD) return indent + '...\\n'; let line = indent + '<' + (el.tagName?.toLowerCase()||'?'); if (el.id) line += ' #' + el.id; if (el.className && typeof el.className === 'string') { const cls = el.className.trim().split(' ').filter(c=>c).slice(0,3).join('.'); if(cls) line += ' .' + cls; } const role = el.getAttribute?.('role'); if(role) line += ' [role=' + role + ']'; line += '> (' + (el.children?.length||0) + ')\\n'; let r = line; if (el.children?.length > 0 && d < maxD) { for (let i = 0; i < Math.min(el.children.length, 30); i++) { r += tree(el.children[i], indent + ' ', d+1, maxD); } if (el.children.length > 30) r += indent + ' ... +' + (el.children.length-30) + ' more\\n'; } return r; } return tree(root, '', 0, ${depth}); })()`;
8768
+ } else if (options.format === "summary") {
8769
+ expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return JSON.stringify({error:'Not found'}); function s(el, d, maxD) { if (d > maxD) return {tag:'...',note:'max depth'}; const n = {tag:el.tagName?.toLowerCase(),id:el.id||undefined,class:el.className&&typeof el.className==='string'?el.className.split(' ').filter(c=>c).slice(0,5).join(' '):undefined,role:el.getAttribute?.('role')||undefined,childCount:el.children?.length||0}; if (el.children?.length > 0 && d < maxD) { n.children = Array.from(el.children).slice(0,30).map(c=>s(c,d+1,maxD)); } return n; } return JSON.stringify(s(root,0,${depth})); })()`;
8770
+ } else {
8771
+ expr = `(() => { const root = document.querySelector('${sel.replace(/'/g, "\\'")}'); if (!root) return 'Not found: ${sel}'; let html = root.outerHTML; if (html.length > 200000) html = html.slice(0, 200000) + '\\n<!-- TRUNCATED -->'; return html; })()`;
8772
+ }
8773
+ result = await directCdpEval(expr, parseInt(options.port));
8774
+ if (options.format === "summary" && typeof result === "string") {
8775
+ try {
8776
+ result = JSON.parse(result);
8777
+ } catch {
8778
+ }
8779
+ }
8780
+ }
8781
+ const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
8782
+ if (options.output) {
8783
+ const fs6 = await import("fs");
8784
+ fs6.writeFileSync(options.output, output, "utf-8");
8785
+ console.log(import_chalk4.default.green(`
8786
+ \u2713 Saved to ${options.output} (${output.length} chars)
8787
+ `));
8788
+ } else {
8789
+ console.log(output);
8790
+ }
8791
+ } catch (e) {
8792
+ console.error(import_chalk4.default.red(`
8793
+ \u2717 ${e.message}
8794
+ `));
8795
+ process.exit(1);
8796
+ }
8797
+ });
8798
+ cdp.command("query <selector>").description("Test a CSS selector \u2014 count matches, show elements").option("-p, --port <port>", "CDP port (direct mode)", "9222").option("-d, --daemon", "Use running daemon").option("-l, --limit <n>", "Max results", "10").option("--session <id>", "Agent webview session ID").option("-j, --json", "Output raw JSON").action(async (selector, options) => {
8799
+ try {
8800
+ const limit = parseInt(options.limit) || 10;
8801
+ let result;
8802
+ if (options.daemon) {
8803
+ result = await sendDaemonCommand("cdp_dom_query", {
8804
+ selector,
8805
+ limit,
8806
+ sessionId: options.session
8807
+ });
8808
+ } else {
8809
+ const expr = `(() => { try { const els = document.querySelectorAll('${selector.replace(/'/g, "\\'")}'); const results = []; for (let i = 0; i < Math.min(els.length, ${limit}); i++) { const el = els[i]; results.push({ index: i, tag: el.tagName?.toLowerCase(), id: el.id||null, class: el.className&&typeof el.className==='string'?el.className.trim().slice(0,200):null, role: el.getAttribute?.('role')||null, text: (el.textContent||'').trim().slice(0,100), visible: el.offsetParent!==null||el.offsetWidth>0, rect: (()=>{try{const r=el.getBoundingClientRect();return{x:Math.round(r.x),y:Math.round(r.y),w:Math.round(r.width),h:Math.round(r.height)};}catch{return null;}})() }); } return JSON.stringify({ total: els.length, results }); } catch(e) { return JSON.stringify({ error: e.message }); } })()`;
8810
+ const raw = await directCdpEval(expr, parseInt(options.port));
8811
+ result = typeof raw === "string" ? JSON.parse(raw) : raw;
8812
+ }
8813
+ if (options.json) {
8814
+ console.log(JSON.stringify(result, null, 2));
8815
+ return;
8816
+ }
8817
+ const total = result.total || 0;
8818
+ console.log(import_chalk4.default.bold(`
8819
+ \u{1F50D} Selector: ${import_chalk4.default.cyan(selector)}`));
8820
+ console.log(` Matches: ${total === 0 ? import_chalk4.default.red("0") : import_chalk4.default.green(total)}
8821
+ `);
8822
+ for (const item of result.results || []) {
8823
+ const vis = item.visible === false ? import_chalk4.default.red(" [hidden]") : import_chalk4.default.green(" [visible]");
8824
+ const rect = item.rect ? import_chalk4.default.gray(` ${item.rect.x},${item.rect.y} ${item.rect.w}\xD7${item.rect.h}`) : "";
8825
+ console.log(` ${import_chalk4.default.yellow(`[${item.index}]`)} <${import_chalk4.default.bold(item.tag)}>${vis}${rect}`);
8826
+ if (item.id) console.log(` id: ${import_chalk4.default.cyan(item.id)}`);
8827
+ if (item.class) console.log(` class: ${import_chalk4.default.gray(item.class.slice(0, 80))}`);
8828
+ if (item.text) console.log(` text: "${import_chalk4.default.white(item.text.slice(0, 80))}"`);
8829
+ if (item.role) console.log(` role: ${import_chalk4.default.magenta(item.role)}`);
8830
+ }
8831
+ console.log();
8832
+ } catch (e) {
8833
+ console.error(import_chalk4.default.red(`
8834
+ \u2717 ${e.message}
8835
+ `));
8836
+ process.exit(1);
8837
+ }
8838
+ });
8839
+ cdp.command("eval <expression>").description("Execute JavaScript expression via CDP").option("-p, --port <port>", "CDP port", "9222").option("-d, --daemon", "Use running daemon").action(async (expression, options) => {
8840
+ try {
8841
+ let result;
8842
+ if (options.daemon) {
8843
+ const resp = await sendDaemonCommand("cdp_eval", { expression });
8844
+ result = resp?.result !== void 0 ? resp.result : resp;
8845
+ } else {
8846
+ result = await directCdpEval(expression, parseInt(options.port));
8847
+ }
8848
+ const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
8849
+ console.log(output);
8850
+ } catch (e) {
8851
+ console.error(import_chalk4.default.red(`
8852
+ \u2717 ${e.message}
8853
+ `));
8854
+ process.exit(1);
8855
+ }
8856
+ });
8857
+ cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --port <port>", "CDP port", "9222").option("-o, --output <file>", "Output file path", "/tmp/cdp_screenshot.jpg").action(async (options) => {
8858
+ try {
8859
+ const http3 = await import("http");
8860
+ const targets = await new Promise((resolve3, reject) => {
8861
+ http3.get(`http://127.0.0.1:${options.port}/json`, (res) => {
8862
+ let data = "";
8863
+ res.on("data", (c) => data += c);
8864
+ res.on("end", () => {
8865
+ try {
8866
+ resolve3(JSON.parse(data));
8867
+ } catch {
8868
+ reject(new Error("Invalid JSON"));
8869
+ }
8870
+ });
8871
+ }).on("error", (e) => reject(e));
8872
+ });
8873
+ const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
8874
+ const pages = targets.filter((t) => (t.type === "page" || t.type === "Page") && t.webSocketDebuggerUrl);
8875
+ const mainPages = pages.filter((t) => !isNonMain(t.title || ""));
8876
+ const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
8877
+ if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target");
8878
+ const WebSocket4 = (await import("ws")).default;
8879
+ const ws = new WebSocket4(target.webSocketDebuggerUrl);
8880
+ await new Promise((resolve3, reject) => {
8881
+ ws.on("open", () => {
8882
+ ws.send(JSON.stringify({ id: 1, method: "Page.captureScreenshot", params: { format: "jpeg", quality: 50 } }));
8883
+ });
8884
+ ws.on("message", async (data) => {
8885
+ const msg = JSON.parse(data.toString());
8886
+ if (msg.id === 1 && msg.result?.data) {
8887
+ const fs6 = await import("fs");
8888
+ fs6.writeFileSync(options.output, Buffer.from(msg.result.data, "base64"));
8889
+ console.log(import_chalk4.default.green(`
8890
+ \u2713 Screenshot saved to ${options.output}
8891
+ `));
8892
+ ws.close();
8893
+ resolve3();
8894
+ }
8895
+ });
8896
+ ws.on("error", (e) => reject(e));
8897
+ });
8898
+ } catch (e) {
8899
+ console.error(import_chalk4.default.red(`
8900
+ \u2717 ${e.message}
8901
+ `));
8902
+ process.exit(1);
8903
+ }
8904
+ });
6996
8905
  program.command("bridge").description('[Legacy] Start CLI bridge (use "adhdev daemon" instead)').option("--foreground", "Run in foreground mode").option("-d, --dir <path>", "Working directory for CLI sessions", process.cwd()).option("--cli <type>", "CLI type to use", "gemini-cli").option("--server <url>", "Override server URL for testing").action(async (options) => {
6997
8906
  console.log(import_chalk4.default.yellow(' \u26A0 "adhdev bridge" is deprecated. Use "adhdev daemon --cli <type>" instead.\n'));
6998
8907
  const { CliDaemon: CliDaemon2 } = await Promise.resolve().then(() => (init_cli_daemon(), cli_daemon_exports));
@@ -7057,8 +8966,8 @@ program.command("bridge:logs").description("[Legacy] Show CLI bridge service log
7057
8966
  const logPath = getLogPath2();
7058
8967
  const { execSync: execSync8, spawn: spawnProc } = await import("child_process");
7059
8968
  try {
7060
- const fs7 = await import("fs");
7061
- if (!fs7.existsSync(logPath)) {
8969
+ const fs6 = await import("fs");
8970
+ if (!fs6.existsSync(logPath)) {
7062
8971
  console.log(import_chalk4.default.gray("\n No logs yet. Start the bridge first.\n"));
7063
8972
  return;
7064
8973
  }