adhdev 0.1.53 → 0.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1870 -819
- package/package.json +1 -1
- package/providers/_builtin/CONTRIBUTING.md +141 -0
- package/providers/_builtin/README.md +51 -0
- package/providers/_builtin/_helpers/index.js +188 -0
- package/providers/_builtin/acp/agentpool/provider.js +59 -0
- package/providers/_builtin/acp/amp/provider.js +61 -0
- package/providers/_builtin/acp/auggie/provider.js +60 -0
- package/providers/_builtin/acp/autodev/provider.js +59 -0
- package/providers/_builtin/acp/autohand/provider.js +59 -0
- package/providers/_builtin/acp/blackbox-ai/provider.js +59 -0
- package/providers/_builtin/acp/claude-agent/provider.js +61 -0
- package/providers/_builtin/acp/cline-acp/provider.js +62 -0
- package/providers/_builtin/acp/code-assistant/provider.js +59 -0
- package/providers/_builtin/acp/codebuddy/provider.js +59 -0
- package/providers/_builtin/acp/codex-cli/provider.js +11 -1
- package/providers/_builtin/acp/corust-agent/provider.js +59 -0
- package/providers/_builtin/acp/crow-cli/provider.js +59 -0
- package/providers/_builtin/acp/cursor-acp/provider.js +59 -0
- package/providers/_builtin/acp/deepagents/provider.js +59 -0
- package/providers/_builtin/acp/dimcode/provider.js +58 -0
- package/providers/_builtin/acp/docker-cagent/provider.js +59 -0
- package/providers/_builtin/acp/factory-droid/provider.js +59 -0
- package/providers/_builtin/acp/fast-agent/provider.js +59 -0
- package/providers/_builtin/acp/fount/provider.js +59 -0
- package/providers/_builtin/acp/gemini-cli/provider.js +104 -0
- package/providers/_builtin/acp/github-copilot/provider.js +60 -0
- package/providers/_builtin/acp/goose/provider.js +37 -5
- package/providers/_builtin/acp/junie/provider.js +62 -0
- package/providers/_builtin/acp/kilo/provider.js +59 -0
- package/providers/_builtin/acp/kimi-cli/provider.js +63 -0
- package/providers/_builtin/acp/kiro-cli/provider.js +59 -0
- package/providers/_builtin/acp/minion-code/provider.js +59 -0
- package/providers/_builtin/acp/mistral-vibe/provider.js +63 -0
- package/providers/_builtin/acp/nova/provider.js +59 -0
- package/providers/_builtin/acp/openclaw/provider.js +59 -0
- package/providers/_builtin/acp/opencode/provider.js +34 -6
- package/providers/_builtin/acp/openhands/provider.js +59 -0
- package/providers/_builtin/acp/pi-acp/provider.js +59 -0
- package/providers/_builtin/acp/qoder/provider.js +58 -0
- package/providers/_builtin/acp/qwen-code/provider.js +61 -0
- package/providers/_builtin/acp/stakpak/provider.js +59 -0
- package/providers/_builtin/acp/vtcode/provider.js +59 -0
- package/providers/_builtin/cli/claude-cli/provider.js +3 -0
- package/providers/_builtin/cli/codex-cli/provider.js +3 -0
- package/providers/_builtin/cli/gemini-cli/provider.js +3 -0
- package/providers/_builtin/ide/kiro/provider.js +6 -2
- package/providers/_builtin/ide/kiro/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/provider.js +12 -0
- package/providers/_builtin/ide/pearai/scripts/list_sessions.js +38 -0
- package/providers/_builtin/ide/pearai/scripts/new_session.js +55 -0
- package/providers/_builtin/ide/pearai/scripts/webview_list_sessions.js +62 -0
- package/providers/_builtin/ide/pearai/scripts/webview_new_session.js +32 -4
- package/providers/_builtin/ide/pearai/scripts/webview_send_message.js +72 -0
- package/providers/_builtin/ide/pearai/scripts/webview_switch_session.js +34 -0
- package/providers/_builtin/ide/trae/scripts/send_message.js +53 -3
- package/providers/_builtin/validate.js +156 -0
- package/dist/node_datachannel-LPY6EJH5.node +0 -0
- package/providers/_builtin/ide/cursor/provider.js.backup +0 -116
- package/providers/_builtin/ide/cursor/provider.js.bak +0 -127
- package/providers/_builtin/ide/cursor/scripts_backup/focus_editor.js +0 -20
- package/providers/_builtin/ide/cursor/scripts_backup/list_chats.js +0 -111
- package/providers/_builtin/ide/cursor/scripts_backup/new_session.js +0 -62
- package/providers/_builtin/ide/cursor/scripts_backup/open_panel.js +0 -31
- package/providers/_builtin/ide/cursor/scripts_backup/read_chat.js +0 -433
- package/providers/_builtin/ide/cursor/scripts_backup/resolve_action.js +0 -90
- package/providers/_builtin/ide/cursor/scripts_backup/send_message.js +0 -86
- package/providers/_builtin/ide/cursor/scripts_backup/switch_session.js +0 -63
package/dist/index.js
CHANGED
|
@@ -9,9 +9,6 @@ var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
|
9
9
|
var __esm = (fn, res) => function __init() {
|
|
10
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
11
|
};
|
|
12
|
-
var __commonJS = (cb, mod) => function __require() {
|
|
13
|
-
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
14
|
-
};
|
|
15
12
|
var __export = (target, all) => {
|
|
16
13
|
for (var name in all)
|
|
17
14
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -99,8 +96,8 @@ async function detectIDEs() {
|
|
|
99
96
|
if ((0, import_fs.existsSync)(bundledCli)) resolvedCli = bundledCli;
|
|
100
97
|
}
|
|
101
98
|
if (!resolvedCli && appPath && os13 === "win32") {
|
|
102
|
-
const { dirname:
|
|
103
|
-
const appDir =
|
|
99
|
+
const { dirname: dirname5 } = await import("path");
|
|
100
|
+
const appDir = dirname5(appPath);
|
|
104
101
|
const candidates = [
|
|
105
102
|
`${appDir}\\\\bin\\\\${def.cli}.cmd`,
|
|
106
103
|
`${appDir}\\\\bin\\\\${def.cli}`,
|
|
@@ -263,7 +260,8 @@ var init_config = __esm({
|
|
|
263
260
|
machineNickname: null,
|
|
264
261
|
cliHistory: [],
|
|
265
262
|
providerSettings: {},
|
|
266
|
-
ideSettings: {}
|
|
263
|
+
ideSettings: {},
|
|
264
|
+
acpAuth: {}
|
|
267
265
|
};
|
|
268
266
|
}
|
|
269
267
|
});
|
|
@@ -281,15 +279,19 @@ var init_provider_loader = __esm({
|
|
|
281
279
|
path = __toESM(require("path"));
|
|
282
280
|
os = __toESM(require("os"));
|
|
283
281
|
init_detector();
|
|
284
|
-
ProviderLoader = class {
|
|
282
|
+
ProviderLoader = class _ProviderLoader {
|
|
285
283
|
providers = /* @__PURE__ */ new Map();
|
|
286
284
|
builtinDir;
|
|
287
285
|
userDir;
|
|
286
|
+
upstreamDir;
|
|
288
287
|
watchers = [];
|
|
289
288
|
logFn;
|
|
289
|
+
static GITHUB_TARBALL_URL = "https://github.com/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz";
|
|
290
|
+
static META_FILE = ".meta.json";
|
|
290
291
|
constructor(options) {
|
|
291
292
|
this.builtinDir = options?.builtinDir || path.resolve(__dirname, "../providers/_builtin");
|
|
292
293
|
this.userDir = options?.userDir || path.join(os.homedir(), ".adhdev", "providers");
|
|
294
|
+
this.upstreamDir = path.join(this.userDir, ".upstream");
|
|
293
295
|
this.logFn = options?.logFn || ((msg) => console.log(msg));
|
|
294
296
|
}
|
|
295
297
|
log(msg) {
|
|
@@ -297,16 +299,27 @@ var init_provider_loader = __esm({
|
|
|
297
299
|
}
|
|
298
300
|
// ─── Public API ────────────────────────────────
|
|
299
301
|
/**
|
|
300
|
-
* 모든 프로바이더 로드 (
|
|
301
|
-
*
|
|
302
|
+
* 모든 프로바이더 로드 (3단계 우선순위)
|
|
303
|
+
* 1. _builtin/ (번들 fallback)
|
|
304
|
+
* 2. _upstream/ (GitHub 자동 다운로드)
|
|
305
|
+
* 3. 유저 커스텀 (~/.adhdev/providers/ 에서 _upstream 제외)
|
|
306
|
+
* 나중 로드된 것이 이전을 덮어쓰므로 유저 커스텀이 항상 최종 우선.
|
|
302
307
|
*/
|
|
303
308
|
loadAll() {
|
|
304
309
|
this.providers.clear();
|
|
305
310
|
const builtinCount = this.loadDir(this.builtinDir);
|
|
306
|
-
this.log(`Loaded ${builtinCount} builtin providers
|
|
311
|
+
this.log(`Loaded ${builtinCount} builtin providers`);
|
|
312
|
+
if (fs.existsSync(this.upstreamDir)) {
|
|
313
|
+
const upstreamCount = this.loadDir(this.upstreamDir);
|
|
314
|
+
if (upstreamCount > 0) {
|
|
315
|
+
this.log(`Loaded ${upstreamCount} upstream providers (auto-updated)`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
307
318
|
if (fs.existsSync(this.userDir)) {
|
|
308
|
-
const userCount = this.loadDir(this.userDir);
|
|
309
|
-
|
|
319
|
+
const userCount = this.loadDir(this.userDir, [".upstream"]);
|
|
320
|
+
if (userCount > 0) {
|
|
321
|
+
this.log(`Loaded ${userCount} user custom providers (\uC808\uB300 \uC790\uB3D9\uAC31\uC2E0 \uC548 \uB428)`);
|
|
322
|
+
}
|
|
310
323
|
}
|
|
311
324
|
this.log(`Total: ${this.providers.size} providers [${[...this.providers.keys()].join(", ")}]`);
|
|
312
325
|
}
|
|
@@ -316,6 +329,43 @@ var init_provider_loader = __esm({
|
|
|
316
329
|
get(type) {
|
|
317
330
|
return this.providers.get(type);
|
|
318
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* 별칭(alias)으로 provider type 해석
|
|
334
|
+
* 'claude' → 'claude-cli', 'codex' → 'codex-cli' 등
|
|
335
|
+
* 매칭 실패 시 입력값 그대로 반환.
|
|
336
|
+
*/
|
|
337
|
+
resolveAlias(input) {
|
|
338
|
+
if (this.providers.has(input)) return input;
|
|
339
|
+
for (const p of this.providers.values()) {
|
|
340
|
+
if (p.aliases?.includes(input)) return p.type;
|
|
341
|
+
}
|
|
342
|
+
return input;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 별칭 포함 provider 조회 (get + alias fallback)
|
|
346
|
+
*/
|
|
347
|
+
getByAlias(input) {
|
|
348
|
+
return this.providers.get(this.resolveAlias(input));
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* CLI/ACP 설치 감지용 목록 생성 (cli-detector 대체)
|
|
352
|
+
* provider.js의 spawn.command에서 동적으로 생성.
|
|
353
|
+
*/
|
|
354
|
+
getCliDetectionList() {
|
|
355
|
+
const result = [];
|
|
356
|
+
for (const p of this.providers.values()) {
|
|
357
|
+
if ((p.category === "cli" || p.category === "acp") && p.spawn?.command) {
|
|
358
|
+
result.push({
|
|
359
|
+
id: p.type,
|
|
360
|
+
displayName: p.displayName || p.name,
|
|
361
|
+
icon: p.icon || "\u{1F527}",
|
|
362
|
+
command: p.spawn.command,
|
|
363
|
+
category: p.category
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return result;
|
|
368
|
+
}
|
|
319
369
|
/**
|
|
320
370
|
* 카테고리별 프로바이더 목록
|
|
321
371
|
*/
|
|
@@ -548,6 +598,201 @@ var init_provider_loader = __esm({
|
|
|
548
598
|
}
|
|
549
599
|
this.loadAll();
|
|
550
600
|
}
|
|
601
|
+
// ─── Upstream Auto-Update ─────────────────────────
|
|
602
|
+
/**
|
|
603
|
+
* GitHub에서 최신 providers tarball 다운로드 → .upstream/ 에 추출
|
|
604
|
+
* - ETag 기반 변경 감지 (변경 없으면 스킵)
|
|
605
|
+
* - ~/.adhdev/providers/ 의 유저 커스텀 파일은 절대 터치하지 않음
|
|
606
|
+
* - 백그라운드에서 실행, 실패해도 기존 providers 유지
|
|
607
|
+
*
|
|
608
|
+
* @returns 업데이트 여부
|
|
609
|
+
*/
|
|
610
|
+
async fetchLatest() {
|
|
611
|
+
const https = require("https");
|
|
612
|
+
const { execSync: execSync6 } = require("child_process");
|
|
613
|
+
const metaPath = path.join(this.upstreamDir, _ProviderLoader.META_FILE);
|
|
614
|
+
let prevEtag = "";
|
|
615
|
+
let prevTimestamp = 0;
|
|
616
|
+
try {
|
|
617
|
+
if (fs.existsSync(metaPath)) {
|
|
618
|
+
const meta = JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
619
|
+
prevEtag = meta.etag || "";
|
|
620
|
+
prevTimestamp = meta.timestamp || 0;
|
|
621
|
+
}
|
|
622
|
+
} catch {
|
|
623
|
+
}
|
|
624
|
+
const MIN_INTERVAL_MS = 30 * 60 * 1e3;
|
|
625
|
+
if (prevTimestamp && Date.now() - prevTimestamp < MIN_INTERVAL_MS) {
|
|
626
|
+
this.log("Upstream check skipped (last check < 30min ago)");
|
|
627
|
+
return { updated: false };
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
const etag = await new Promise((resolve5, reject) => {
|
|
631
|
+
const options = {
|
|
632
|
+
method: "HEAD",
|
|
633
|
+
hostname: "github.com",
|
|
634
|
+
path: "/vilmire/adhdev-providers/archive/refs/heads/main.tar.gz",
|
|
635
|
+
headers: { "User-Agent": "adhdev-launcher" },
|
|
636
|
+
timeout: 1e4
|
|
637
|
+
};
|
|
638
|
+
const req = https.request(options, (res) => {
|
|
639
|
+
if (res.statusCode === 302 && res.headers.location) {
|
|
640
|
+
const url = new URL(res.headers.location);
|
|
641
|
+
const req2 = https.request({
|
|
642
|
+
method: "HEAD",
|
|
643
|
+
hostname: url.hostname,
|
|
644
|
+
path: url.pathname + (url.search || ""),
|
|
645
|
+
headers: { "User-Agent": "adhdev-launcher" },
|
|
646
|
+
timeout: 1e4
|
|
647
|
+
}, (res2) => {
|
|
648
|
+
resolve5(res2.headers.etag || res2.headers["last-modified"] || "");
|
|
649
|
+
});
|
|
650
|
+
req2.on("error", reject);
|
|
651
|
+
req2.on("timeout", () => {
|
|
652
|
+
req2.destroy();
|
|
653
|
+
reject(new Error("timeout"));
|
|
654
|
+
});
|
|
655
|
+
req2.end();
|
|
656
|
+
} else {
|
|
657
|
+
resolve5(res.headers.etag || res.headers["last-modified"] || "");
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
req.on("error", reject);
|
|
661
|
+
req.on("timeout", () => {
|
|
662
|
+
req.destroy();
|
|
663
|
+
reject(new Error("timeout"));
|
|
664
|
+
});
|
|
665
|
+
req.end();
|
|
666
|
+
});
|
|
667
|
+
if (etag && etag === prevEtag) {
|
|
668
|
+
this.writeMeta(metaPath, prevEtag, Date.now());
|
|
669
|
+
this.log("Upstream unchanged (ETag match)");
|
|
670
|
+
return { updated: false };
|
|
671
|
+
}
|
|
672
|
+
this.log("Downloading latest providers from GitHub...");
|
|
673
|
+
const tmpTar = path.join(os.tmpdir(), `adhdev-providers-${Date.now()}.tar.gz`);
|
|
674
|
+
const tmpExtract = path.join(os.tmpdir(), `adhdev-providers-extract-${Date.now()}`);
|
|
675
|
+
await this.downloadFile(_ProviderLoader.GITHUB_TARBALL_URL, tmpTar);
|
|
676
|
+
fs.mkdirSync(tmpExtract, { recursive: true });
|
|
677
|
+
execSync6(`tar -xzf "${tmpTar}" -C "${tmpExtract}"`, { timeout: 3e4 });
|
|
678
|
+
const extracted = fs.readdirSync(tmpExtract);
|
|
679
|
+
const rootDir = extracted.find(
|
|
680
|
+
(d) => fs.statSync(path.join(tmpExtract, d)).isDirectory() && d.startsWith("adhdev-providers")
|
|
681
|
+
);
|
|
682
|
+
if (!rootDir) throw new Error("Unexpected tarball structure");
|
|
683
|
+
const sourceDir = path.join(tmpExtract, rootDir);
|
|
684
|
+
const backupDir = this.upstreamDir + ".bak";
|
|
685
|
+
if (fs.existsSync(this.upstreamDir)) {
|
|
686
|
+
if (fs.existsSync(backupDir)) fs.rmSync(backupDir, { recursive: true, force: true });
|
|
687
|
+
fs.renameSync(this.upstreamDir, backupDir);
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
this.copyDirRecursive(sourceDir, this.upstreamDir);
|
|
691
|
+
this.writeMeta(metaPath, etag || `ts-${Date.now()}`, Date.now());
|
|
692
|
+
if (fs.existsSync(backupDir)) fs.rmSync(backupDir, { recursive: true, force: true });
|
|
693
|
+
} catch (e) {
|
|
694
|
+
if (fs.existsSync(backupDir)) {
|
|
695
|
+
if (fs.existsSync(this.upstreamDir)) fs.rmSync(this.upstreamDir, { recursive: true, force: true });
|
|
696
|
+
fs.renameSync(backupDir, this.upstreamDir);
|
|
697
|
+
}
|
|
698
|
+
throw e;
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
fs.rmSync(tmpTar, { force: true });
|
|
702
|
+
} catch {
|
|
703
|
+
}
|
|
704
|
+
try {
|
|
705
|
+
fs.rmSync(tmpExtract, { recursive: true, force: true });
|
|
706
|
+
} catch {
|
|
707
|
+
}
|
|
708
|
+
const upstreamCount = this.countProviders(this.upstreamDir);
|
|
709
|
+
this.log(`\u2705 Upstream updated: ${upstreamCount} providers`);
|
|
710
|
+
return { updated: true };
|
|
711
|
+
} catch (e) {
|
|
712
|
+
this.log(`\u26A0 Upstream fetch failed (using existing): ${e?.message}`);
|
|
713
|
+
this.writeMeta(metaPath, prevEtag, Date.now());
|
|
714
|
+
return { updated: false, error: e?.message };
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
/** HTTP(S) 파일 다운로드 (redirect follow) */
|
|
718
|
+
downloadFile(url, destPath) {
|
|
719
|
+
const https = require("https");
|
|
720
|
+
const http3 = require("http");
|
|
721
|
+
return new Promise((resolve5, reject) => {
|
|
722
|
+
const doRequest = (reqUrl, redirectCount = 0) => {
|
|
723
|
+
if (redirectCount > 5) {
|
|
724
|
+
reject(new Error("Too many redirects"));
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const mod = reqUrl.startsWith("https") ? https : http3;
|
|
728
|
+
const req = mod.get(reqUrl, { headers: { "User-Agent": "adhdev-launcher" }, timeout: 6e4 }, (res) => {
|
|
729
|
+
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
730
|
+
doRequest(res.headers.location, redirectCount + 1);
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (res.statusCode !== 200) {
|
|
734
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
const ws = fs.createWriteStream(destPath);
|
|
738
|
+
res.pipe(ws);
|
|
739
|
+
ws.on("finish", () => {
|
|
740
|
+
ws.close();
|
|
741
|
+
resolve5();
|
|
742
|
+
});
|
|
743
|
+
ws.on("error", reject);
|
|
744
|
+
});
|
|
745
|
+
req.on("error", reject);
|
|
746
|
+
req.on("timeout", () => {
|
|
747
|
+
req.destroy();
|
|
748
|
+
reject(new Error("Download timeout"));
|
|
749
|
+
});
|
|
750
|
+
};
|
|
751
|
+
doRequest(url);
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
/** 디렉토리 재귀 복사 */
|
|
755
|
+
copyDirRecursive(src, dest) {
|
|
756
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
757
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
758
|
+
const srcPath = path.join(src, entry.name);
|
|
759
|
+
const destPath = path.join(dest, entry.name);
|
|
760
|
+
if (entry.isDirectory()) {
|
|
761
|
+
this.copyDirRecursive(srcPath, destPath);
|
|
762
|
+
} else {
|
|
763
|
+
fs.copyFileSync(srcPath, destPath);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
/** .meta.json 저장 */
|
|
768
|
+
writeMeta(metaPath, etag, timestamp) {
|
|
769
|
+
try {
|
|
770
|
+
fs.mkdirSync(path.dirname(metaPath), { recursive: true });
|
|
771
|
+
fs.writeFileSync(metaPath, JSON.stringify({
|
|
772
|
+
etag,
|
|
773
|
+
timestamp,
|
|
774
|
+
lastCheck: new Date(timestamp).toISOString(),
|
|
775
|
+
source: _ProviderLoader.GITHUB_TARBALL_URL
|
|
776
|
+
}, null, 2));
|
|
777
|
+
} catch {
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/** provider.js 개수 카운트 */
|
|
781
|
+
countProviders(dir) {
|
|
782
|
+
if (!fs.existsSync(dir)) return 0;
|
|
783
|
+
let count = 0;
|
|
784
|
+
const scan = (d) => {
|
|
785
|
+
try {
|
|
786
|
+
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
|
787
|
+
if (entry.isDirectory()) scan(path.join(d, entry.name));
|
|
788
|
+
else if (entry.name === "provider.js") count++;
|
|
789
|
+
}
|
|
790
|
+
} catch {
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
scan(dir);
|
|
794
|
+
return count;
|
|
795
|
+
}
|
|
551
796
|
// ─── Provider Settings API ─────────────────────────
|
|
552
797
|
/**
|
|
553
798
|
* Provider의 public settings 스키마 조회 (대시보드 UI 렌더링용)
|
|
@@ -630,7 +875,7 @@ var init_provider_loader = __esm({
|
|
|
630
875
|
* 디렉토리 재귀 스캔으로 provider.js 로드
|
|
631
876
|
* 구조: dir/category/agent-name/provider.js 또는 dir/agent-name/provider.js
|
|
632
877
|
*/
|
|
633
|
-
loadDir(dir) {
|
|
878
|
+
loadDir(dir, excludeDirs) {
|
|
634
879
|
if (!fs.existsSync(dir)) return 0;
|
|
635
880
|
let count = 0;
|
|
636
881
|
const scan = (d) => {
|
|
@@ -643,7 +888,8 @@ var init_provider_loader = __esm({
|
|
|
643
888
|
for (const entry of entries) {
|
|
644
889
|
const fullPath = path.join(d, entry.name);
|
|
645
890
|
if (entry.isDirectory()) {
|
|
646
|
-
if (entry.name.startsWith("_")) continue;
|
|
891
|
+
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
892
|
+
if (excludeDirs && d === dir && excludeDirs.includes(entry.name)) continue;
|
|
647
893
|
scan(fullPath);
|
|
648
894
|
} else if (entry.name === "provider.js") {
|
|
649
895
|
try {
|
|
@@ -707,13 +953,13 @@ var init_provider_loader = __esm({
|
|
|
707
953
|
}
|
|
708
954
|
});
|
|
709
955
|
|
|
710
|
-
// src/
|
|
711
|
-
var import_ws,
|
|
712
|
-
var
|
|
713
|
-
"src/
|
|
956
|
+
// src/server-connection.ts
|
|
957
|
+
var import_ws, ServerConnection;
|
|
958
|
+
var init_server_connection = __esm({
|
|
959
|
+
"src/server-connection.ts"() {
|
|
714
960
|
"use strict";
|
|
715
961
|
import_ws = __toESM(require("ws"));
|
|
716
|
-
|
|
962
|
+
ServerConnection = class {
|
|
717
963
|
ws = null;
|
|
718
964
|
options;
|
|
719
965
|
getCliInfo() {
|
|
@@ -722,6 +968,8 @@ var init_cli_bridge = __esm({
|
|
|
722
968
|
state = "disconnected";
|
|
723
969
|
reconnectAttempts = 0;
|
|
724
970
|
reconnectTimer = null;
|
|
971
|
+
pingTimer = null;
|
|
972
|
+
pongTimeout = null;
|
|
725
973
|
messageHandlers = /* @__PURE__ */ new Map();
|
|
726
974
|
stateChangeCallbacks = [];
|
|
727
975
|
userPlan = "free";
|
|
@@ -734,7 +982,7 @@ var init_cli_bridge = __esm({
|
|
|
734
982
|
try {
|
|
735
983
|
const wsUrl = this.options.serverUrl.replace(/^https:\/\//, "wss://").replace(/^http:\/\//, "ws://");
|
|
736
984
|
const fullUrl = wsUrl.endsWith("/ws") ? wsUrl : wsUrl + "/ws";
|
|
737
|
-
console.log(`[
|
|
985
|
+
console.log(`[ServerConn] Connecting to ${fullUrl}...`);
|
|
738
986
|
this.ws = new import_ws.default(fullUrl, {
|
|
739
987
|
headers: {
|
|
740
988
|
"X-ADHDev-Token": this.options.token,
|
|
@@ -745,8 +993,9 @@ var init_cli_bridge = __esm({
|
|
|
745
993
|
this.ws.on("message", (data) => this.onMessage(data.toString()));
|
|
746
994
|
this.ws.on("close", (code, reason) => this.onClose(code, reason.toString()));
|
|
747
995
|
this.ws.on("error", (err) => this.onError(err));
|
|
996
|
+
this.ws.on("pong", () => this.onPong());
|
|
748
997
|
} catch (error) {
|
|
749
|
-
console.error("[
|
|
998
|
+
console.error("[ServerConn] Connect failed:", error);
|
|
750
999
|
this.setState("error");
|
|
751
1000
|
this.scheduleReconnect();
|
|
752
1001
|
}
|
|
@@ -799,7 +1048,7 @@ var init_cli_bridge = __esm({
|
|
|
799
1048
|
}
|
|
800
1049
|
// --- Private ---
|
|
801
1050
|
onOpen() {
|
|
802
|
-
console.log(
|
|
1051
|
+
console.log(`[ServerConn] WebSocket open, sending auth...`);
|
|
803
1052
|
this.setState("authenticating");
|
|
804
1053
|
this.send({
|
|
805
1054
|
type: "auth",
|
|
@@ -817,9 +1066,10 @@ var init_cli_bridge = __esm({
|
|
|
817
1066
|
this.reconnectAttempts = 0;
|
|
818
1067
|
this.userPlan = message.payload.plan || "free";
|
|
819
1068
|
this.setState("connected");
|
|
820
|
-
console.log(`[
|
|
1069
|
+
console.log(`[ServerConn] \u2713 Authenticated (plan: ${this.userPlan})`);
|
|
1070
|
+
this.startHeartbeat();
|
|
821
1071
|
} else if (message.type === "auth_error") {
|
|
822
|
-
console.error("[
|
|
1072
|
+
console.error("[ServerConn] \u2717 Auth failed:", message.payload.reason);
|
|
823
1073
|
this.setState("error");
|
|
824
1074
|
return;
|
|
825
1075
|
}
|
|
@@ -827,14 +1077,14 @@ var init_cli_bridge = __esm({
|
|
|
827
1077
|
if (handlers) {
|
|
828
1078
|
handlers.forEach((h) => h(message));
|
|
829
1079
|
} else if (message.type !== "auth_ok") {
|
|
830
|
-
console.log(`[
|
|
1080
|
+
console.log(`[ServerConn] Unhandled message type: ${message.type}`);
|
|
831
1081
|
}
|
|
832
1082
|
} catch (error) {
|
|
833
|
-
console.error("[
|
|
1083
|
+
console.error("[ServerConn] Failed to parse message:", error);
|
|
834
1084
|
}
|
|
835
1085
|
}
|
|
836
1086
|
onClose(code, reason) {
|
|
837
|
-
console.log(`[
|
|
1087
|
+
console.log(`[ServerConn] WebSocket closed: ${code} ${reason}`);
|
|
838
1088
|
this.clearTimers();
|
|
839
1089
|
this.ws = null;
|
|
840
1090
|
if (code !== 1e3 && this.reconnectAttempts < 50) {
|
|
@@ -845,7 +1095,7 @@ var init_cli_bridge = __esm({
|
|
|
845
1095
|
}
|
|
846
1096
|
}
|
|
847
1097
|
onError(error) {
|
|
848
|
-
console.error("[
|
|
1098
|
+
console.error("[ServerConn] WebSocket error:", error.message);
|
|
849
1099
|
this.setState("error");
|
|
850
1100
|
}
|
|
851
1101
|
setState(state) {
|
|
@@ -857,7 +1107,7 @@ var init_cli_bridge = __esm({
|
|
|
857
1107
|
scheduleReconnect() {
|
|
858
1108
|
const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 6e4);
|
|
859
1109
|
this.reconnectAttempts++;
|
|
860
|
-
console.log(`[
|
|
1110
|
+
console.log(`[ServerConn] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})...`);
|
|
861
1111
|
this.reconnectTimer = setTimeout(() => this.connect(), delay);
|
|
862
1112
|
}
|
|
863
1113
|
clearTimers() {
|
|
@@ -865,6 +1115,40 @@ var init_cli_bridge = __esm({
|
|
|
865
1115
|
clearTimeout(this.reconnectTimer);
|
|
866
1116
|
this.reconnectTimer = null;
|
|
867
1117
|
}
|
|
1118
|
+
this.stopHeartbeat();
|
|
1119
|
+
}
|
|
1120
|
+
// ─── WS Heartbeat (ping/pong) ─────────────────────
|
|
1121
|
+
startHeartbeat() {
|
|
1122
|
+
this.stopHeartbeat();
|
|
1123
|
+
this.pingTimer = setInterval(() => {
|
|
1124
|
+
if (!this.ws || this.ws.readyState !== import_ws.default.OPEN) return;
|
|
1125
|
+
try {
|
|
1126
|
+
this.ws.ping();
|
|
1127
|
+
this.pongTimeout = setTimeout(() => {
|
|
1128
|
+
console.log("[ServerConn] \u26A0 Pong timeout (10s) \u2014 closing zombie WS");
|
|
1129
|
+
if (this.ws) {
|
|
1130
|
+
this.ws.terminate();
|
|
1131
|
+
}
|
|
1132
|
+
}, 1e4);
|
|
1133
|
+
} catch {
|
|
1134
|
+
}
|
|
1135
|
+
}, 3e4);
|
|
1136
|
+
}
|
|
1137
|
+
stopHeartbeat() {
|
|
1138
|
+
if (this.pingTimer) {
|
|
1139
|
+
clearInterval(this.pingTimer);
|
|
1140
|
+
this.pingTimer = null;
|
|
1141
|
+
}
|
|
1142
|
+
if (this.pongTimeout) {
|
|
1143
|
+
clearTimeout(this.pongTimeout);
|
|
1144
|
+
this.pongTimeout = null;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
onPong() {
|
|
1148
|
+
if (this.pongTimeout) {
|
|
1149
|
+
clearTimeout(this.pongTimeout);
|
|
1150
|
+
this.pongTimeout = null;
|
|
1151
|
+
}
|
|
868
1152
|
}
|
|
869
1153
|
};
|
|
870
1154
|
}
|
|
@@ -906,7 +1190,7 @@ var init_local_server = __esm({
|
|
|
906
1190
|
* 로컬 WS 서버 시작
|
|
907
1191
|
*/
|
|
908
1192
|
async start() {
|
|
909
|
-
return new Promise((
|
|
1193
|
+
return new Promise((resolve5, reject) => {
|
|
910
1194
|
try {
|
|
911
1195
|
this.wss = new import_ws2.WebSocketServer({
|
|
912
1196
|
port: this.port,
|
|
@@ -916,7 +1200,7 @@ var init_local_server = __esm({
|
|
|
916
1200
|
});
|
|
917
1201
|
this.wss.on("listening", () => {
|
|
918
1202
|
console.log(`[LocalServer] Listening on ws://127.0.0.1:${this.port}${DAEMON_WS_PATH}`);
|
|
919
|
-
|
|
1203
|
+
resolve5();
|
|
920
1204
|
});
|
|
921
1205
|
this.wss.on("connection", (ws) => {
|
|
922
1206
|
this.handleConnection(ws);
|
|
@@ -1095,14 +1379,14 @@ var init_local_server = __esm({
|
|
|
1095
1379
|
return { success: false, error: "No extension connected" };
|
|
1096
1380
|
}
|
|
1097
1381
|
const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
1098
|
-
return new Promise((
|
|
1382
|
+
return new Promise((resolve5) => {
|
|
1099
1383
|
const timeout = setTimeout(() => {
|
|
1100
1384
|
this.commandCallbacks.delete(requestId);
|
|
1101
|
-
|
|
1385
|
+
resolve5({ success: false, error: "Command execution timeout (10s)" });
|
|
1102
1386
|
}, 1e4);
|
|
1103
1387
|
this.commandCallbacks.set(requestId, (result) => {
|
|
1104
1388
|
clearTimeout(timeout);
|
|
1105
|
-
|
|
1389
|
+
resolve5(result);
|
|
1106
1390
|
});
|
|
1107
1391
|
this.sendToExtension(targetWs, {
|
|
1108
1392
|
type: "daemon:execute_vscode",
|
|
@@ -1226,17 +1510,17 @@ async function findFreePort(ports) {
|
|
|
1226
1510
|
throw new Error("No free port found");
|
|
1227
1511
|
}
|
|
1228
1512
|
function checkPortFree(port) {
|
|
1229
|
-
return new Promise((
|
|
1513
|
+
return new Promise((resolve5) => {
|
|
1230
1514
|
const server = net.createServer();
|
|
1231
1515
|
server.unref();
|
|
1232
|
-
server.on("error", () =>
|
|
1516
|
+
server.on("error", () => resolve5(false));
|
|
1233
1517
|
server.listen(port, "127.0.0.1", () => {
|
|
1234
|
-
server.close(() =>
|
|
1518
|
+
server.close(() => resolve5(true));
|
|
1235
1519
|
});
|
|
1236
1520
|
});
|
|
1237
1521
|
}
|
|
1238
1522
|
async function isCdpActive(port) {
|
|
1239
|
-
return new Promise((
|
|
1523
|
+
return new Promise((resolve5) => {
|
|
1240
1524
|
const req = require("http").get(`http://127.0.0.1:${port}/json/version`, {
|
|
1241
1525
|
timeout: 2e3
|
|
1242
1526
|
}, (res) => {
|
|
@@ -1245,16 +1529,16 @@ async function isCdpActive(port) {
|
|
|
1245
1529
|
res.on("end", () => {
|
|
1246
1530
|
try {
|
|
1247
1531
|
const info = JSON.parse(data);
|
|
1248
|
-
|
|
1532
|
+
resolve5(!!info["WebKit-Version"] || !!info["Browser"]);
|
|
1249
1533
|
} catch {
|
|
1250
|
-
|
|
1534
|
+
resolve5(false);
|
|
1251
1535
|
}
|
|
1252
1536
|
});
|
|
1253
1537
|
});
|
|
1254
|
-
req.on("error", () =>
|
|
1538
|
+
req.on("error", () => resolve5(false));
|
|
1255
1539
|
req.on("timeout", () => {
|
|
1256
1540
|
req.destroy();
|
|
1257
|
-
|
|
1541
|
+
resolve5(false);
|
|
1258
1542
|
});
|
|
1259
1543
|
});
|
|
1260
1544
|
}
|
|
@@ -1369,7 +1653,7 @@ function detectCurrentWorkspace(ideId) {
|
|
|
1369
1653
|
}
|
|
1370
1654
|
} else if (plat === "win32") {
|
|
1371
1655
|
try {
|
|
1372
|
-
const
|
|
1656
|
+
const fs8 = require("fs");
|
|
1373
1657
|
const appNameMap = getMacAppIdentifiers();
|
|
1374
1658
|
const appName = appNameMap[ideId];
|
|
1375
1659
|
if (appName) {
|
|
@@ -1378,8 +1662,8 @@ function detectCurrentWorkspace(ideId) {
|
|
|
1378
1662
|
appName,
|
|
1379
1663
|
"storage.json"
|
|
1380
1664
|
);
|
|
1381
|
-
if (
|
|
1382
|
-
const data = JSON.parse(
|
|
1665
|
+
if (fs8.existsSync(storagePath)) {
|
|
1666
|
+
const data = JSON.parse(fs8.readFileSync(storagePath, "utf-8"));
|
|
1383
1667
|
const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
|
|
1384
1668
|
if (workspaces.length > 0) {
|
|
1385
1669
|
const recent = workspaces[0];
|
|
@@ -1599,7 +1883,7 @@ var init_daemon_cdp = __esm({
|
|
|
1599
1883
|
* 같은 포트에 여러 IDE 창이 열려 있으면 여러 개 반환
|
|
1600
1884
|
*/
|
|
1601
1885
|
static listAllTargets(port) {
|
|
1602
|
-
return new Promise((
|
|
1886
|
+
return new Promise((resolve5) => {
|
|
1603
1887
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
1604
1888
|
let data = "";
|
|
1605
1889
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -1613,16 +1897,16 @@ var init_daemon_cdp = __esm({
|
|
|
1613
1897
|
const mainPages = pages.filter(
|
|
1614
1898
|
(t) => !isNonMain(t.title || "") && t.url?.includes("workbench.html") && !t.url?.includes("agent")
|
|
1615
1899
|
);
|
|
1616
|
-
|
|
1900
|
+
resolve5(mainPages.length > 0 ? mainPages : pages.filter((t) => !isNonMain(t.title || "")));
|
|
1617
1901
|
} catch {
|
|
1618
|
-
|
|
1902
|
+
resolve5([]);
|
|
1619
1903
|
}
|
|
1620
1904
|
});
|
|
1621
1905
|
});
|
|
1622
|
-
req.on("error", () =>
|
|
1906
|
+
req.on("error", () => resolve5([]));
|
|
1623
1907
|
req.setTimeout(2e3, () => {
|
|
1624
1908
|
req.destroy();
|
|
1625
|
-
|
|
1909
|
+
resolve5([]);
|
|
1626
1910
|
});
|
|
1627
1911
|
});
|
|
1628
1912
|
}
|
|
@@ -1662,7 +1946,7 @@ var init_daemon_cdp = __esm({
|
|
|
1662
1946
|
}
|
|
1663
1947
|
}
|
|
1664
1948
|
findTargetOnPort(port) {
|
|
1665
|
-
return new Promise((
|
|
1949
|
+
return new Promise((resolve5) => {
|
|
1666
1950
|
const req = http.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
1667
1951
|
let data = "";
|
|
1668
1952
|
res.on("data", (chunk) => data += chunk.toString());
|
|
@@ -1673,7 +1957,7 @@ var init_daemon_cdp = __esm({
|
|
|
1673
1957
|
(t) => (t.type === "page" || t.type === "browser" || t.type === "Page") && t.webSocketDebuggerUrl
|
|
1674
1958
|
);
|
|
1675
1959
|
if (pages.length === 0) {
|
|
1676
|
-
|
|
1960
|
+
resolve5(targets.find((t) => t.webSocketDebuggerUrl) || null);
|
|
1677
1961
|
return;
|
|
1678
1962
|
}
|
|
1679
1963
|
const isNonMain = (title) => !title || /extension-output|ADHDev CDP|Debug Console|Output\s*$|Launchpad/i.test(title);
|
|
@@ -1684,24 +1968,24 @@ var init_daemon_cdp = __esm({
|
|
|
1684
1968
|
const specific = list.find((t) => t.id === this._targetId);
|
|
1685
1969
|
if (specific) {
|
|
1686
1970
|
this._pageTitle = specific.title || "";
|
|
1687
|
-
|
|
1971
|
+
resolve5(specific);
|
|
1688
1972
|
} else {
|
|
1689
1973
|
this.log(`[CDP] Target ${this._targetId} not found in page list`);
|
|
1690
|
-
|
|
1974
|
+
resolve5(null);
|
|
1691
1975
|
}
|
|
1692
1976
|
return;
|
|
1693
1977
|
}
|
|
1694
1978
|
this._pageTitle = list[0]?.title || "";
|
|
1695
|
-
|
|
1979
|
+
resolve5(list[0]);
|
|
1696
1980
|
} catch {
|
|
1697
|
-
|
|
1981
|
+
resolve5(null);
|
|
1698
1982
|
}
|
|
1699
1983
|
});
|
|
1700
1984
|
});
|
|
1701
|
-
req.on("error", () =>
|
|
1985
|
+
req.on("error", () => resolve5(null));
|
|
1702
1986
|
req.setTimeout(2e3, () => {
|
|
1703
1987
|
req.destroy();
|
|
1704
|
-
|
|
1988
|
+
resolve5(null);
|
|
1705
1989
|
});
|
|
1706
1990
|
});
|
|
1707
1991
|
}
|
|
@@ -1712,7 +1996,7 @@ var init_daemon_cdp = __esm({
|
|
|
1712
1996
|
this.extensionProviders = providers;
|
|
1713
1997
|
}
|
|
1714
1998
|
connectToTarget(wsUrl) {
|
|
1715
|
-
return new Promise((
|
|
1999
|
+
return new Promise((resolve5) => {
|
|
1716
2000
|
this.ws = new import_ws3.default(wsUrl);
|
|
1717
2001
|
this.ws.on("open", async () => {
|
|
1718
2002
|
this._connected = true;
|
|
@@ -1722,17 +2006,17 @@ var init_daemon_cdp = __esm({
|
|
|
1722
2006
|
}
|
|
1723
2007
|
this.connectBrowserWs().catch(() => {
|
|
1724
2008
|
});
|
|
1725
|
-
|
|
2009
|
+
resolve5(true);
|
|
1726
2010
|
});
|
|
1727
2011
|
this.ws.on("message", (data) => {
|
|
1728
2012
|
try {
|
|
1729
2013
|
const msg = JSON.parse(data.toString());
|
|
1730
2014
|
if (msg.id && this.pending.has(msg.id)) {
|
|
1731
|
-
const { resolve:
|
|
2015
|
+
const { resolve: resolve6, reject } = this.pending.get(msg.id);
|
|
1732
2016
|
this.pending.delete(msg.id);
|
|
1733
2017
|
this.failureCount = 0;
|
|
1734
2018
|
if (msg.error) reject(new Error(msg.error.message));
|
|
1735
|
-
else
|
|
2019
|
+
else resolve6(msg.result);
|
|
1736
2020
|
} else if (msg.method === "Runtime.executionContextCreated") {
|
|
1737
2021
|
this.contexts.add(msg.params.context.id);
|
|
1738
2022
|
} else if (msg.method === "Runtime.executionContextDestroyed") {
|
|
@@ -1755,7 +2039,7 @@ var init_daemon_cdp = __esm({
|
|
|
1755
2039
|
this.ws.on("error", (err) => {
|
|
1756
2040
|
this.log(`[CDP] WebSocket error: ${err.message}`);
|
|
1757
2041
|
this._connected = false;
|
|
1758
|
-
|
|
2042
|
+
resolve5(false);
|
|
1759
2043
|
});
|
|
1760
2044
|
});
|
|
1761
2045
|
}
|
|
@@ -1769,7 +2053,7 @@ var init_daemon_cdp = __esm({
|
|
|
1769
2053
|
return;
|
|
1770
2054
|
}
|
|
1771
2055
|
this.log(`[CDP] Connecting browser WS for target discovery...`);
|
|
1772
|
-
await new Promise((
|
|
2056
|
+
await new Promise((resolve5, reject) => {
|
|
1773
2057
|
this.browserWs = new import_ws3.default(browserWsUrl);
|
|
1774
2058
|
this.browserWs.on("open", async () => {
|
|
1775
2059
|
this._browserConnected = true;
|
|
@@ -1779,16 +2063,16 @@ var init_daemon_cdp = __esm({
|
|
|
1779
2063
|
} catch (e) {
|
|
1780
2064
|
this.log(`[CDP] setDiscoverTargets failed: ${e.message}`);
|
|
1781
2065
|
}
|
|
1782
|
-
|
|
2066
|
+
resolve5();
|
|
1783
2067
|
});
|
|
1784
2068
|
this.browserWs.on("message", (data) => {
|
|
1785
2069
|
try {
|
|
1786
2070
|
const msg = JSON.parse(data.toString());
|
|
1787
2071
|
if (msg.id && this.browserPending.has(msg.id)) {
|
|
1788
|
-
const { resolve:
|
|
2072
|
+
const { resolve: resolve6, reject: reject2 } = this.browserPending.get(msg.id);
|
|
1789
2073
|
this.browserPending.delete(msg.id);
|
|
1790
2074
|
if (msg.error) reject2(new Error(msg.error.message));
|
|
1791
|
-
else
|
|
2075
|
+
else resolve6(msg.result);
|
|
1792
2076
|
}
|
|
1793
2077
|
} catch {
|
|
1794
2078
|
}
|
|
@@ -1808,31 +2092,31 @@ var init_daemon_cdp = __esm({
|
|
|
1808
2092
|
}
|
|
1809
2093
|
}
|
|
1810
2094
|
getBrowserWsUrl() {
|
|
1811
|
-
return new Promise((
|
|
2095
|
+
return new Promise((resolve5) => {
|
|
1812
2096
|
const req = http.get(`http://127.0.0.1:${this.port}/json/version`, (res) => {
|
|
1813
2097
|
let data = "";
|
|
1814
2098
|
res.on("data", (chunk) => data += chunk.toString());
|
|
1815
2099
|
res.on("end", () => {
|
|
1816
2100
|
try {
|
|
1817
2101
|
const info = JSON.parse(data);
|
|
1818
|
-
|
|
2102
|
+
resolve5(info.webSocketDebuggerUrl || null);
|
|
1819
2103
|
} catch {
|
|
1820
|
-
|
|
2104
|
+
resolve5(null);
|
|
1821
2105
|
}
|
|
1822
2106
|
});
|
|
1823
2107
|
});
|
|
1824
|
-
req.on("error", () =>
|
|
2108
|
+
req.on("error", () => resolve5(null));
|
|
1825
2109
|
req.setTimeout(3e3, () => {
|
|
1826
2110
|
req.destroy();
|
|
1827
|
-
|
|
2111
|
+
resolve5(null);
|
|
1828
2112
|
});
|
|
1829
2113
|
});
|
|
1830
2114
|
}
|
|
1831
2115
|
sendBrowser(method, params = {}, timeoutMs = 15e3) {
|
|
1832
|
-
return new Promise((
|
|
2116
|
+
return new Promise((resolve5, reject) => {
|
|
1833
2117
|
if (!this.browserWs || !this._browserConnected) return reject(new Error("Browser WS not connected"));
|
|
1834
2118
|
const id = this.browserMsgId++;
|
|
1835
|
-
this.browserPending.set(id, { resolve:
|
|
2119
|
+
this.browserPending.set(id, { resolve: resolve5, reject });
|
|
1836
2120
|
this.browserWs.send(JSON.stringify({ id, method, params }));
|
|
1837
2121
|
setTimeout(() => {
|
|
1838
2122
|
if (this.browserPending.has(id)) {
|
|
@@ -1872,11 +2156,11 @@ var init_daemon_cdp = __esm({
|
|
|
1872
2156
|
}
|
|
1873
2157
|
// ─── CDP Protocol ────────────────────────────────────────
|
|
1874
2158
|
sendInternal(method, params = {}, timeoutMs = 15e3) {
|
|
1875
|
-
return new Promise((
|
|
2159
|
+
return new Promise((resolve5, reject) => {
|
|
1876
2160
|
if (!this.ws || !this._connected) return reject(new Error("CDP not connected"));
|
|
1877
2161
|
if (this.ws.readyState !== import_ws3.default.OPEN) return reject(new Error("WebSocket not open"));
|
|
1878
2162
|
const id = this.msgId++;
|
|
1879
|
-
this.pending.set(id, { resolve:
|
|
2163
|
+
this.pending.set(id, { resolve: resolve5, reject });
|
|
1880
2164
|
this.ws.send(JSON.stringify({ id, method, params }));
|
|
1881
2165
|
setTimeout(() => {
|
|
1882
2166
|
if (this.pending.has(id)) {
|
|
@@ -2085,7 +2369,7 @@ var init_daemon_cdp = __esm({
|
|
|
2085
2369
|
const browserWs = this.browserWs;
|
|
2086
2370
|
let msgId = this.browserMsgId;
|
|
2087
2371
|
const sendWs = (method, params = {}, sessionId) => {
|
|
2088
|
-
return new Promise((
|
|
2372
|
+
return new Promise((resolve5, reject) => {
|
|
2089
2373
|
const mid = msgId++;
|
|
2090
2374
|
this.browserMsgId = msgId;
|
|
2091
2375
|
const handler = (raw) => {
|
|
@@ -2094,7 +2378,7 @@ var init_daemon_cdp = __esm({
|
|
|
2094
2378
|
if (msg.id === mid) {
|
|
2095
2379
|
browserWs.removeListener("message", handler);
|
|
2096
2380
|
if (msg.error) reject(new Error(msg.error.message || JSON.stringify(msg.error)));
|
|
2097
|
-
else
|
|
2381
|
+
else resolve5(msg.result);
|
|
2098
2382
|
}
|
|
2099
2383
|
} catch {
|
|
2100
2384
|
}
|
|
@@ -2119,7 +2403,7 @@ var init_daemon_cdp = __esm({
|
|
|
2119
2403
|
return null;
|
|
2120
2404
|
}
|
|
2121
2405
|
for (const iframe of webviewIframes) {
|
|
2122
|
-
let sessionId
|
|
2406
|
+
let sessionId;
|
|
2123
2407
|
try {
|
|
2124
2408
|
const attached = await sendWs("Target.attachToTarget", {
|
|
2125
2409
|
targetId: iframe.targetId,
|
|
@@ -2154,6 +2438,7 @@ var init_daemon_cdp = __esm({
|
|
|
2154
2438
|
const result = await sendWs("Runtime.evaluate", {
|
|
2155
2439
|
expression,
|
|
2156
2440
|
returnByValue: true,
|
|
2441
|
+
awaitPromise: true,
|
|
2157
2442
|
contextId: executionContextId
|
|
2158
2443
|
}, sessionId);
|
|
2159
2444
|
await sendWs("Target.detachFromTarget", { sessionId }).catch(() => {
|
|
@@ -2267,14 +2552,14 @@ var init_daemon_cdp = __esm({
|
|
|
2267
2552
|
if (!ws || ws.readyState !== import_ws3.default.OPEN) {
|
|
2268
2553
|
throw new Error("CDP not connected");
|
|
2269
2554
|
}
|
|
2270
|
-
return new Promise((
|
|
2555
|
+
return new Promise((resolve5, reject) => {
|
|
2271
2556
|
const id = getNextId();
|
|
2272
2557
|
pendingMap.set(id, {
|
|
2273
2558
|
resolve: (result) => {
|
|
2274
2559
|
if (result?.result?.subtype === "error") {
|
|
2275
2560
|
reject(new Error(result.result.description));
|
|
2276
2561
|
} else {
|
|
2277
|
-
|
|
2562
|
+
resolve5(result?.result?.value);
|
|
2278
2563
|
}
|
|
2279
2564
|
},
|
|
2280
2565
|
reject
|
|
@@ -2332,302 +2617,163 @@ var init_daemon_cdp = __esm({
|
|
|
2332
2617
|
}
|
|
2333
2618
|
});
|
|
2334
2619
|
|
|
2335
|
-
//
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2620
|
+
// src/chat-history.ts
|
|
2621
|
+
function readChatHistory(agentType, offset = 0, limit = 30, instanceId) {
|
|
2622
|
+
try {
|
|
2623
|
+
const sanitized = agentType.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2624
|
+
const dir = path3.join(HISTORY_DIR, sanitized);
|
|
2625
|
+
if (!fs2.existsSync(dir)) return { messages: [], hasMore: false };
|
|
2626
|
+
const sanitizedInstance = instanceId?.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2627
|
+
const files = fs2.readdirSync(dir).filter((f) => {
|
|
2628
|
+
if (!f.endsWith(".jsonl")) return false;
|
|
2629
|
+
if (sanitizedInstance) {
|
|
2630
|
+
return f.startsWith(`${sanitizedInstance}_`);
|
|
2631
|
+
}
|
|
2632
|
+
return !f.includes("_") || f.match(/^\d{4}-\d{2}-\d{2}\.jsonl$/);
|
|
2633
|
+
}).sort().reverse();
|
|
2634
|
+
const allMessages = [];
|
|
2635
|
+
const needed = offset + limit + 1;
|
|
2636
|
+
for (const file of files) {
|
|
2637
|
+
if (allMessages.length >= needed) break;
|
|
2638
|
+
const filePath = path3.join(dir, file);
|
|
2639
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
2640
|
+
const lines = content.trim().split("\n").filter(Boolean);
|
|
2641
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2642
|
+
if (allMessages.length >= needed) break;
|
|
2643
|
+
try {
|
|
2644
|
+
allMessages.push(JSON.parse(lines[i]));
|
|
2645
|
+
} catch {
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2351
2648
|
}
|
|
2649
|
+
const sliced = allMessages.slice(offset, offset + limit);
|
|
2650
|
+
const hasMore = allMessages.length > offset + limit;
|
|
2651
|
+
sliced.reverse();
|
|
2652
|
+
return { messages: sliced, hasMore };
|
|
2653
|
+
} catch {
|
|
2654
|
+
return { messages: [], hasMore: false };
|
|
2352
2655
|
}
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
"../../node_modules/node-datachannel/dist/cjs/lib/node-datachannel.cjs"(exports2) {
|
|
2358
|
-
"use strict";
|
|
2359
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2360
|
-
var nodeDataChannel = require_node_datachannel();
|
|
2361
|
-
exports2.default = nodeDataChannel;
|
|
2362
|
-
}
|
|
2363
|
-
});
|
|
2364
|
-
|
|
2365
|
-
// ../../node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs
|
|
2366
|
-
var require_datachannel_stream = __commonJS({
|
|
2367
|
-
"../../node_modules/node-datachannel/dist/cjs/lib/datachannel-stream.cjs"(exports2) {
|
|
2656
|
+
}
|
|
2657
|
+
var fs2, path3, os3, HISTORY_DIR, RETAIN_DAYS, ChatHistoryWriter;
|
|
2658
|
+
var init_chat_history = __esm({
|
|
2659
|
+
"src/chat-history.ts"() {
|
|
2368
2660
|
"use strict";
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2661
|
+
fs2 = __toESM(require("fs"));
|
|
2662
|
+
path3 = __toESM(require("path"));
|
|
2663
|
+
os3 = __toESM(require("os"));
|
|
2664
|
+
HISTORY_DIR = path3.join(os3.homedir(), ".adhdev", "history");
|
|
2665
|
+
RETAIN_DAYS = 30;
|
|
2666
|
+
ChatHistoryWriter = class {
|
|
2667
|
+
/** 에이전트별 마지막으로 본 메시지 개수 (중복 방지) */
|
|
2668
|
+
lastSeenCounts = /* @__PURE__ */ new Map();
|
|
2669
|
+
/** 에이전트별 마지막으로 본 메시지 해시 (중복 방지) */
|
|
2670
|
+
lastSeenHashes = /* @__PURE__ */ new Map();
|
|
2671
|
+
rotated = false;
|
|
2672
|
+
/**
|
|
2673
|
+
* 새 메시지를 히스토리에 추가
|
|
2674
|
+
*
|
|
2675
|
+
* @param agentType 에이전트 타입 (e.g. 'antigravity', 'cursor')
|
|
2676
|
+
* @param messages readChat에서 받은 메시지 배열
|
|
2677
|
+
* @param sessionTitle 현재 세션 제목
|
|
2678
|
+
* @param instanceId IDE instance UUID (같은 에이전트의 다른 창 구분)
|
|
2679
|
+
*/
|
|
2680
|
+
appendNewMessages(agentType, messages, sessionTitle, instanceId) {
|
|
2681
|
+
if (!messages || messages.length === 0) return;
|
|
2682
|
+
try {
|
|
2683
|
+
const dedupKey = instanceId ? `${agentType}:${instanceId}` : agentType;
|
|
2684
|
+
let seenHashes = this.lastSeenHashes.get(dedupKey);
|
|
2685
|
+
if (!seenHashes) {
|
|
2686
|
+
seenHashes = /* @__PURE__ */ new Set();
|
|
2687
|
+
this.lastSeenHashes.set(dedupKey, seenHashes);
|
|
2688
|
+
}
|
|
2689
|
+
const newMessages = [];
|
|
2690
|
+
for (const msg of messages) {
|
|
2691
|
+
const hash = `${msg.role}:${(msg.content || "").slice(0, 50)}`;
|
|
2692
|
+
if (seenHashes.has(hash)) continue;
|
|
2693
|
+
seenHashes.add(hash);
|
|
2694
|
+
newMessages.push({
|
|
2695
|
+
ts: new Date(msg.receivedAt || Date.now()).toISOString(),
|
|
2696
|
+
receivedAt: msg.receivedAt || Date.now(),
|
|
2697
|
+
role: msg.role,
|
|
2698
|
+
content: msg.content || "",
|
|
2699
|
+
agent: agentType,
|
|
2700
|
+
instanceId,
|
|
2701
|
+
sessionTitle
|
|
2382
2702
|
});
|
|
2383
2703
|
}
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
rawChannel.onMessage((msg) => {
|
|
2407
|
-
if (!this._readActive) return;
|
|
2408
|
-
this._readActive = this.push(msg);
|
|
2409
|
-
});
|
|
2410
|
-
rawChannel.onClosed(() => {
|
|
2411
|
-
this.push(null);
|
|
2412
|
-
this.destroy();
|
|
2413
|
-
});
|
|
2414
|
-
rawChannel.onError((errMsg) => {
|
|
2415
|
-
this.destroy(new Error(`DataChannel error: ${errMsg}`));
|
|
2416
|
-
});
|
|
2417
|
-
if (!rawChannel.isOpen()) {
|
|
2418
|
-
this.cork();
|
|
2419
|
-
rawChannel.onOpen(() => this.uncork());
|
|
2704
|
+
if (newMessages.length === 0) return;
|
|
2705
|
+
const dir = path3.join(HISTORY_DIR, this.sanitize(agentType));
|
|
2706
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
2707
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2708
|
+
const filePrefix = instanceId ? `${this.sanitize(instanceId)}_` : "";
|
|
2709
|
+
const filePath = path3.join(dir, `${filePrefix}${date}.jsonl`);
|
|
2710
|
+
const lines = newMessages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
2711
|
+
fs2.appendFileSync(filePath, lines, "utf-8");
|
|
2712
|
+
const prevCount = this.lastSeenCounts.get(dedupKey) || 0;
|
|
2713
|
+
if (messages.length < prevCount * 0.5 && prevCount > 3) {
|
|
2714
|
+
seenHashes.clear();
|
|
2715
|
+
for (const msg of messages) {
|
|
2716
|
+
seenHashes.add(`${msg.role}:${(msg.content || "").slice(0, 50)}`);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
this.lastSeenCounts.set(dedupKey, messages.length);
|
|
2720
|
+
if (!this.rotated) {
|
|
2721
|
+
this.rotated = true;
|
|
2722
|
+
this.rotateOldFiles().catch(() => {
|
|
2723
|
+
});
|
|
2724
|
+
}
|
|
2725
|
+
} catch {
|
|
2420
2726
|
}
|
|
2421
2727
|
}
|
|
2422
|
-
|
|
2423
|
-
|
|
2728
|
+
/** 에이전트 세션이 명시적으로 변경되었을 때 호출 */
|
|
2729
|
+
onSessionChange(agentType) {
|
|
2730
|
+
this.lastSeenHashes.delete(agentType);
|
|
2731
|
+
this.lastSeenCounts.delete(agentType);
|
|
2424
2732
|
}
|
|
2425
|
-
|
|
2426
|
-
|
|
2733
|
+
/** 30일 이상 된 히스토리 파일 삭제 */
|
|
2734
|
+
async rotateOldFiles() {
|
|
2427
2735
|
try {
|
|
2428
|
-
if (
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2736
|
+
if (!fs2.existsSync(HISTORY_DIR)) return;
|
|
2737
|
+
const cutoff = Date.now() - RETAIN_DAYS * 24 * 60 * 60 * 1e3;
|
|
2738
|
+
const agentDirs = fs2.readdirSync(HISTORY_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
2739
|
+
for (const dir of agentDirs) {
|
|
2740
|
+
const dirPath = path3.join(HISTORY_DIR, dir.name);
|
|
2741
|
+
const files = fs2.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
|
|
2742
|
+
for (const file of files) {
|
|
2743
|
+
const filePath = path3.join(dirPath, file);
|
|
2744
|
+
const stat = fs2.statSync(filePath);
|
|
2745
|
+
if (stat.mtimeMs < cutoff) {
|
|
2746
|
+
fs2.unlinkSync(filePath);
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2435
2749
|
}
|
|
2436
|
-
} catch
|
|
2437
|
-
return callback(err);
|
|
2438
|
-
}
|
|
2439
|
-
if (sentOk) {
|
|
2440
|
-
callback(null);
|
|
2441
|
-
} else {
|
|
2442
|
-
callback(new Error("Failed to write to DataChannel"));
|
|
2750
|
+
} catch {
|
|
2443
2751
|
}
|
|
2444
2752
|
}
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
}
|
|
2449
|
-
_destroy(maybeErr, callback) {
|
|
2450
|
-
this._rawChannel.close();
|
|
2451
|
-
callback(maybeErr);
|
|
2452
|
-
}
|
|
2453
|
-
get label() {
|
|
2454
|
-
return this._rawChannel.getLabel();
|
|
2455
|
-
}
|
|
2456
|
-
get id() {
|
|
2457
|
-
return this._rawChannel.getId();
|
|
2458
|
-
}
|
|
2459
|
-
get protocol() {
|
|
2460
|
-
return this._rawChannel.getProtocol();
|
|
2461
|
-
}
|
|
2462
|
-
};
|
|
2463
|
-
exports2.default = DataChannelStream;
|
|
2464
|
-
}
|
|
2465
|
-
});
|
|
2466
|
-
|
|
2467
|
-
// ../../node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs
|
|
2468
|
-
var require_websocket_server = __commonJS({
|
|
2469
|
-
"../../node_modules/node-datachannel/dist/cjs/lib/websocket-server.cjs"(exports2) {
|
|
2470
|
-
"use strict";
|
|
2471
|
-
var events = require("events");
|
|
2472
|
-
var nodeDatachannel = require_node_datachannel2();
|
|
2473
|
-
var __typeError = (msg) => {
|
|
2474
|
-
throw TypeError(msg);
|
|
2475
|
-
};
|
|
2476
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
2477
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
2478
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2479
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
2480
|
-
var _server;
|
|
2481
|
-
var _clients;
|
|
2482
|
-
var WebSocketServer2 = class extends events.EventEmitter {
|
|
2483
|
-
constructor(options) {
|
|
2484
|
-
super();
|
|
2485
|
-
__privateAdd(this, _server);
|
|
2486
|
-
__privateAdd(this, _clients, []);
|
|
2487
|
-
__privateSet(this, _server, new nodeDatachannel.default.WebSocketServer(options));
|
|
2488
|
-
__privateGet(this, _server).onClient((client) => {
|
|
2489
|
-
this.emit("client", client);
|
|
2490
|
-
__privateGet(this, _clients).push(client);
|
|
2491
|
-
});
|
|
2492
|
-
}
|
|
2493
|
-
port() {
|
|
2494
|
-
return __privateGet(this, _server)?.port() || 0;
|
|
2495
|
-
}
|
|
2496
|
-
stop() {
|
|
2497
|
-
__privateGet(this, _clients).forEach((client) => {
|
|
2498
|
-
client?.close();
|
|
2499
|
-
});
|
|
2500
|
-
__privateGet(this, _server)?.stop();
|
|
2501
|
-
__privateSet(this, _server, null);
|
|
2502
|
-
this.removeAllListeners();
|
|
2503
|
-
}
|
|
2504
|
-
onClient(cb) {
|
|
2505
|
-
if (__privateGet(this, _server)) this.on("client", cb);
|
|
2753
|
+
/** 파일명에 안전한 문자만 허용 */
|
|
2754
|
+
sanitize(name) {
|
|
2755
|
+
return name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
2506
2756
|
}
|
|
2507
2757
|
};
|
|
2508
|
-
_server = /* @__PURE__ */ new WeakMap();
|
|
2509
|
-
_clients = /* @__PURE__ */ new WeakMap();
|
|
2510
|
-
exports2.WebSocketServer = WebSocketServer2;
|
|
2511
|
-
}
|
|
2512
|
-
});
|
|
2513
|
-
|
|
2514
|
-
// ../../node_modules/node-datachannel/dist/cjs/lib/websocket.cjs
|
|
2515
|
-
var require_websocket = __commonJS({
|
|
2516
|
-
"../../node_modules/node-datachannel/dist/cjs/lib/websocket.cjs"(exports2) {
|
|
2517
|
-
"use strict";
|
|
2518
|
-
var nodeDatachannel = require_node_datachannel2();
|
|
2519
|
-
var WebSocket4 = nodeDatachannel.default.WebSocket;
|
|
2520
|
-
exports2.WebSocket = WebSocket4;
|
|
2521
|
-
}
|
|
2522
|
-
});
|
|
2523
|
-
|
|
2524
|
-
// ../../node_modules/node-datachannel/dist/cjs/lib/index.cjs
|
|
2525
|
-
var require_lib = __commonJS({
|
|
2526
|
-
"../../node_modules/node-datachannel/dist/cjs/lib/index.cjs"(exports2) {
|
|
2527
|
-
"use strict";
|
|
2528
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
2529
|
-
var nodeDatachannel = require_node_datachannel2();
|
|
2530
|
-
var datachannelStream = require_datachannel_stream();
|
|
2531
|
-
var websocketServer = require_websocket_server();
|
|
2532
|
-
var websocket = require_websocket();
|
|
2533
|
-
function preload() {
|
|
2534
|
-
nodeDatachannel.default.preload();
|
|
2535
|
-
}
|
|
2536
|
-
function initLogger(level) {
|
|
2537
|
-
nodeDatachannel.default.initLogger(level);
|
|
2538
|
-
}
|
|
2539
|
-
function cleanup() {
|
|
2540
|
-
nodeDatachannel.default.cleanup();
|
|
2541
|
-
}
|
|
2542
|
-
function setSctpSettings(settings) {
|
|
2543
|
-
nodeDatachannel.default.setSctpSettings(settings);
|
|
2544
|
-
}
|
|
2545
|
-
function getLibraryVersion() {
|
|
2546
|
-
return nodeDatachannel.default.getLibraryVersion();
|
|
2547
|
-
}
|
|
2548
|
-
var Audio = nodeDatachannel.default.Audio;
|
|
2549
|
-
var Video = nodeDatachannel.default.Video;
|
|
2550
|
-
var Track = nodeDatachannel.default.Track;
|
|
2551
|
-
var DataChannel = nodeDatachannel.default.DataChannel;
|
|
2552
|
-
var PeerConnection = nodeDatachannel.default.PeerConnection;
|
|
2553
|
-
var IceUdpMuxListener = nodeDatachannel.default.IceUdpMuxListener;
|
|
2554
|
-
var RtpPacketizationConfig = nodeDatachannel.default.RtpPacketizationConfig;
|
|
2555
|
-
var PacingHandler = nodeDatachannel.default.PacingHandler;
|
|
2556
|
-
var RtcpReceivingSession = nodeDatachannel.default.RtcpReceivingSession;
|
|
2557
|
-
var RtcpNackResponder = nodeDatachannel.default.RtcpNackResponder;
|
|
2558
|
-
var RtcpSrReporter = nodeDatachannel.default.RtcpSrReporter;
|
|
2559
|
-
var RtpPacketizer = nodeDatachannel.default.RtpPacketizer;
|
|
2560
|
-
var H264RtpPacketizer = nodeDatachannel.default.H264RtpPacketizer;
|
|
2561
|
-
var H265RtpPacketizer = nodeDatachannel.default.H265RtpPacketizer;
|
|
2562
|
-
var AV1RtpPacketizer = nodeDatachannel.default.AV1RtpPacketizer;
|
|
2563
|
-
var DataChannelStream = datachannelStream.default;
|
|
2564
|
-
var n = {
|
|
2565
|
-
initLogger,
|
|
2566
|
-
cleanup,
|
|
2567
|
-
preload,
|
|
2568
|
-
setSctpSettings,
|
|
2569
|
-
getLibraryVersion,
|
|
2570
|
-
PacingHandler,
|
|
2571
|
-
RtcpReceivingSession,
|
|
2572
|
-
RtcpNackResponder,
|
|
2573
|
-
RtcpSrReporter,
|
|
2574
|
-
RtpPacketizationConfig,
|
|
2575
|
-
RtpPacketizer,
|
|
2576
|
-
H264RtpPacketizer,
|
|
2577
|
-
H265RtpPacketizer,
|
|
2578
|
-
AV1RtpPacketizer,
|
|
2579
|
-
Track,
|
|
2580
|
-
Video,
|
|
2581
|
-
Audio,
|
|
2582
|
-
DataChannel,
|
|
2583
|
-
PeerConnection,
|
|
2584
|
-
WebSocket: websocket.WebSocket,
|
|
2585
|
-
WebSocketServer: websocketServer.WebSocketServer,
|
|
2586
|
-
DataChannelStream,
|
|
2587
|
-
IceUdpMuxListener
|
|
2588
|
-
};
|
|
2589
|
-
exports2.WebSocketServer = websocketServer.WebSocketServer;
|
|
2590
|
-
exports2.WebSocket = websocket.WebSocket;
|
|
2591
|
-
exports2.AV1RtpPacketizer = AV1RtpPacketizer;
|
|
2592
|
-
exports2.Audio = Audio;
|
|
2593
|
-
exports2.DataChannel = DataChannel;
|
|
2594
|
-
exports2.DataChannelStream = DataChannelStream;
|
|
2595
|
-
exports2.H264RtpPacketizer = H264RtpPacketizer;
|
|
2596
|
-
exports2.H265RtpPacketizer = H265RtpPacketizer;
|
|
2597
|
-
exports2.IceUdpMuxListener = IceUdpMuxListener;
|
|
2598
|
-
exports2.PacingHandler = PacingHandler;
|
|
2599
|
-
exports2.PeerConnection = PeerConnection;
|
|
2600
|
-
exports2.RtcpNackResponder = RtcpNackResponder;
|
|
2601
|
-
exports2.RtcpReceivingSession = RtcpReceivingSession;
|
|
2602
|
-
exports2.RtcpSrReporter = RtcpSrReporter;
|
|
2603
|
-
exports2.RtpPacketizationConfig = RtpPacketizationConfig;
|
|
2604
|
-
exports2.RtpPacketizer = RtpPacketizer;
|
|
2605
|
-
exports2.Track = Track;
|
|
2606
|
-
exports2.Video = Video;
|
|
2607
|
-
exports2.cleanup = cleanup;
|
|
2608
|
-
exports2.default = n;
|
|
2609
|
-
exports2.getLibraryVersion = getLibraryVersion;
|
|
2610
|
-
exports2.initLogger = initLogger;
|
|
2611
|
-
exports2.preload = preload;
|
|
2612
|
-
exports2.setSctpSettings = setSctpSettings;
|
|
2613
2758
|
}
|
|
2614
2759
|
});
|
|
2615
2760
|
|
|
2616
2761
|
// src/daemon-p2p.ts
|
|
2617
|
-
var
|
|
2762
|
+
var fs3, path4, os4, logFile, log, DaemonP2PSender;
|
|
2618
2763
|
var init_daemon_p2p = __esm({
|
|
2619
2764
|
"src/daemon-p2p.ts"() {
|
|
2620
2765
|
"use strict";
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2766
|
+
fs3 = __toESM(require("fs"));
|
|
2767
|
+
init_chat_history();
|
|
2768
|
+
path4 = __toESM(require("path"));
|
|
2769
|
+
os4 = __toESM(require("os"));
|
|
2770
|
+
logFile = path4.join(os4.tmpdir(), "adhdev_daemon_p2p.log");
|
|
2625
2771
|
log = (msg) => {
|
|
2626
2772
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [P2P] ${msg}`;
|
|
2627
2773
|
console.log(line);
|
|
2628
2774
|
};
|
|
2629
2775
|
DaemonP2PSender = class {
|
|
2630
|
-
|
|
2776
|
+
serverConn;
|
|
2631
2777
|
peers = /* @__PURE__ */ new Map();
|
|
2632
2778
|
nodeDatachannel = null;
|
|
2633
2779
|
stateListeners = [];
|
|
@@ -2656,14 +2802,14 @@ var init_daemon_p2p = __esm({
|
|
|
2656
2802
|
}
|
|
2657
2803
|
return void 0;
|
|
2658
2804
|
}
|
|
2659
|
-
constructor(
|
|
2660
|
-
this.
|
|
2805
|
+
constructor(serverConn) {
|
|
2806
|
+
this.serverConn = serverConn;
|
|
2661
2807
|
this.tryLoadNodeDatachannel();
|
|
2662
2808
|
}
|
|
2663
2809
|
/** node-datachannel 로드 */
|
|
2664
2810
|
tryLoadNodeDatachannel() {
|
|
2665
2811
|
try {
|
|
2666
|
-
this.nodeDatachannel =
|
|
2812
|
+
this.nodeDatachannel = require("node-datachannel");
|
|
2667
2813
|
const keys = Object.keys(this.nodeDatachannel).join(",");
|
|
2668
2814
|
log(`node-datachannel loaded \u2705 (keys: ${keys.substring(0, 100)})`);
|
|
2669
2815
|
return;
|
|
@@ -2675,22 +2821,22 @@ var init_daemon_p2p = __esm({
|
|
|
2675
2821
|
const prebuildKey = `${platform7}-${arch2}`;
|
|
2676
2822
|
try {
|
|
2677
2823
|
const candidates = [
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2824
|
+
path4.join(__dirname, "node_modules", "node-datachannel"),
|
|
2825
|
+
path4.join(__dirname, "..", "node_modules", "node-datachannel"),
|
|
2826
|
+
path4.join(__dirname, "..", "..", "node_modules", "node-datachannel")
|
|
2681
2827
|
];
|
|
2682
2828
|
for (const candidate of candidates) {
|
|
2683
|
-
const prebuildPath =
|
|
2684
|
-
if (
|
|
2685
|
-
const targetDir =
|
|
2686
|
-
const targetPath =
|
|
2687
|
-
|
|
2688
|
-
|
|
2829
|
+
const prebuildPath = path4.join(candidate, "prebuilds", prebuildKey, "node_datachannel.node");
|
|
2830
|
+
if (fs3.existsSync(prebuildPath)) {
|
|
2831
|
+
const targetDir = path4.join(candidate, "build", "Release");
|
|
2832
|
+
const targetPath = path4.join(targetDir, "node_datachannel.node");
|
|
2833
|
+
fs3.mkdirSync(targetDir, { recursive: true });
|
|
2834
|
+
fs3.copyFileSync(prebuildPath, targetPath);
|
|
2689
2835
|
try {
|
|
2690
2836
|
delete require.cache[require.resolve("node-datachannel")];
|
|
2691
2837
|
} catch {
|
|
2692
2838
|
}
|
|
2693
|
-
this.nodeDatachannel =
|
|
2839
|
+
this.nodeDatachannel = require("node-datachannel");
|
|
2694
2840
|
log(`node-datachannel loaded from prebuild (${prebuildKey}) \u2705`);
|
|
2695
2841
|
return;
|
|
2696
2842
|
}
|
|
@@ -2743,21 +2889,21 @@ var init_daemon_p2p = __esm({
|
|
|
2743
2889
|
async fetchTurnCredentials() {
|
|
2744
2890
|
try {
|
|
2745
2891
|
const serverUrl = "https://api.adhf.dev";
|
|
2746
|
-
const configPath =
|
|
2892
|
+
const configPath = path4.join(os4.homedir(), ".adhdev", "config.json");
|
|
2747
2893
|
let token = "";
|
|
2748
2894
|
try {
|
|
2749
|
-
const config = JSON.parse(
|
|
2895
|
+
const config = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
2750
2896
|
token = config.connectionToken || "";
|
|
2751
2897
|
} catch {
|
|
2752
2898
|
}
|
|
2753
2899
|
const http3 = require("https");
|
|
2754
|
-
const data = await new Promise((
|
|
2900
|
+
const data = await new Promise((resolve5, reject) => {
|
|
2755
2901
|
const req = http3.get(`${serverUrl}/api/v1/turn/credentials`, {
|
|
2756
2902
|
headers: { "Authorization": `Bearer ${token}` }
|
|
2757
2903
|
}, (res) => {
|
|
2758
2904
|
let d = "";
|
|
2759
2905
|
res.on("data", (c) => d += c);
|
|
2760
|
-
res.on("end", () =>
|
|
2906
|
+
res.on("end", () => resolve5(d));
|
|
2761
2907
|
});
|
|
2762
2908
|
req.on("error", reject);
|
|
2763
2909
|
req.setTimeout(5e3, () => {
|
|
@@ -2827,7 +2973,9 @@ var init_daemon_p2p = __esm({
|
|
|
2827
2973
|
filesChannel: null,
|
|
2828
2974
|
state: "connecting",
|
|
2829
2975
|
screenshotActive: false,
|
|
2830
|
-
connectedAt: Date.now()
|
|
2976
|
+
connectedAt: Date.now(),
|
|
2977
|
+
pendingCandidates: [],
|
|
2978
|
+
remoteDescriptionSet: false
|
|
2831
2979
|
};
|
|
2832
2980
|
this.peers.set(pid, entry);
|
|
2833
2981
|
this.notifyStateChange();
|
|
@@ -2842,10 +2990,10 @@ var init_daemon_p2p = __esm({
|
|
|
2842
2990
|
try {
|
|
2843
2991
|
pc.onLocalDescription((sdp, type) => {
|
|
2844
2992
|
log(`onLocalDescription for peer ${pid}: type=${type}`);
|
|
2845
|
-
this.
|
|
2993
|
+
this.serverConn.sendMessage("p2p_offer", { sdp, type, peerId: pid });
|
|
2846
2994
|
});
|
|
2847
2995
|
pc.onLocalCandidate((candidate, mid) => {
|
|
2848
|
-
this.
|
|
2996
|
+
this.serverConn.sendMessage("p2p_ice", { candidate, mid, peerId: pid });
|
|
2849
2997
|
});
|
|
2850
2998
|
pc.onStateChange((pcState) => {
|
|
2851
2999
|
log(`Peer ${pid} state: ${pcState}`);
|
|
@@ -2853,10 +3001,24 @@ var init_daemon_p2p = __esm({
|
|
|
2853
3001
|
if (!peer) return;
|
|
2854
3002
|
if (pcState === "connected") {
|
|
2855
3003
|
peer.state = "connected";
|
|
3004
|
+
if (peer.failedCleanupTimer) {
|
|
3005
|
+
clearTimeout(peer.failedCleanupTimer);
|
|
3006
|
+
peer.failedCleanupTimer = void 0;
|
|
3007
|
+
}
|
|
3008
|
+
this.startHeartbeat(pid);
|
|
2856
3009
|
this.notifyStateChange();
|
|
2857
3010
|
} else if (pcState === "failed" || pcState === "closed") {
|
|
2858
3011
|
peer.state = "failed";
|
|
2859
3012
|
this.notifyStateChange();
|
|
3013
|
+
if (!peer.failedCleanupTimer) {
|
|
3014
|
+
peer.failedCleanupTimer = setTimeout(() => {
|
|
3015
|
+
const p = this.peers.get(pid);
|
|
3016
|
+
if (p?.state === "failed") {
|
|
3017
|
+
log(`Auto-cleanup stale failed peer ${pid}`);
|
|
3018
|
+
this.disconnectPeer(pid);
|
|
3019
|
+
}
|
|
3020
|
+
}, 3e4);
|
|
3021
|
+
}
|
|
2860
3022
|
}
|
|
2861
3023
|
});
|
|
2862
3024
|
const screenshotCh = pc.createDataChannel("screenshots");
|
|
@@ -2907,9 +3069,20 @@ var init_daemon_p2p = __esm({
|
|
|
2907
3069
|
const text = typeof msg === "string" ? msg : msg.toString("utf-8");
|
|
2908
3070
|
try {
|
|
2909
3071
|
const parsed = JSON.parse(text);
|
|
2910
|
-
if (parsed.type !== "command" && parsed.type !== "pty_input" && parsed.type !== "pty_resize") {
|
|
3072
|
+
if (parsed.type !== "command" && parsed.type !== "pty_input" && parsed.type !== "pty_resize" && parsed.type !== "ping" && parsed.type !== "pong") {
|
|
2911
3073
|
log(`Files message from peer ${peerId}: type=${parsed.type}`);
|
|
2912
3074
|
}
|
|
3075
|
+
if (parsed.type === "ping") {
|
|
3076
|
+
const peer = this.peers.get(peerId);
|
|
3077
|
+
if (peer?.filesChannel?.isOpen()) {
|
|
3078
|
+
try {
|
|
3079
|
+
peer.filesChannel.sendMessage(JSON.stringify({ type: "pong", ts: Date.now() }));
|
|
3080
|
+
} catch {
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
return;
|
|
3084
|
+
}
|
|
3085
|
+
if (parsed.type === "pong") return;
|
|
2913
3086
|
if (parsed.type === "screenshot_start") {
|
|
2914
3087
|
const peer = this.peers.get(peerId);
|
|
2915
3088
|
if (peer) {
|
|
@@ -2939,16 +3112,22 @@ var init_daemon_p2p = __esm({
|
|
|
2939
3112
|
}
|
|
2940
3113
|
if (parsed.type === "pty_input") {
|
|
2941
3114
|
if (this.ptyInputHandler && parsed.data) {
|
|
2942
|
-
this.ptyInputHandler(parsed.cliType || "
|
|
3115
|
+
this.ptyInputHandler(parsed.cliType || "", parsed.data);
|
|
2943
3116
|
}
|
|
2944
3117
|
return;
|
|
2945
3118
|
}
|
|
2946
3119
|
if (parsed.type === "pty_resize") {
|
|
2947
3120
|
if (this.ptyResizeHandler && parsed.cols && parsed.rows) {
|
|
2948
|
-
this.ptyResizeHandler(parsed.cliType || "
|
|
3121
|
+
this.ptyResizeHandler(parsed.cliType || "", parsed.cols, parsed.rows);
|
|
2949
3122
|
}
|
|
2950
3123
|
return;
|
|
2951
3124
|
}
|
|
3125
|
+
if (parsed.type === "chat_history") {
|
|
3126
|
+
const { agent, offset, limit, id, instanceId } = parsed;
|
|
3127
|
+
const result = readChatHistory(agent || "", offset || 0, limit || 30, instanceId);
|
|
3128
|
+
this.sendToPeer(peerId, { type: "chat_history_result", id, ...result, agent });
|
|
3129
|
+
return;
|
|
3130
|
+
}
|
|
2952
3131
|
this.handleFileRequest(peerId, parsed);
|
|
2953
3132
|
} catch (e) {
|
|
2954
3133
|
log(`Parse error from peer ${peerId}: ${e?.message}`);
|
|
@@ -3029,8 +3208,12 @@ var init_daemon_p2p = __esm({
|
|
|
3029
3208
|
}
|
|
3030
3209
|
}
|
|
3031
3210
|
sendScreenshot(base64Data) {
|
|
3032
|
-
let sentAny = false;
|
|
3033
3211
|
const buffer = Buffer.from(base64Data, "base64");
|
|
3212
|
+
return this.sendScreenshotBuffer(buffer);
|
|
3213
|
+
}
|
|
3214
|
+
/** Send screenshot as raw Buffer (no base64 conversion overhead) */
|
|
3215
|
+
sendScreenshotBuffer(buffer) {
|
|
3216
|
+
let sentAny = false;
|
|
3034
3217
|
let debugOnce = !this._ssDebugDone;
|
|
3035
3218
|
const CHUNK_SIZE = 6e4;
|
|
3036
3219
|
for (const [pid, peer] of this.peers.entries()) {
|
|
@@ -3135,9 +3318,32 @@ var init_daemon_p2p = __esm({
|
|
|
3135
3318
|
return;
|
|
3136
3319
|
}
|
|
3137
3320
|
const peer = this.peers.get(peerId);
|
|
3138
|
-
if (peer?.pc) {
|
|
3139
|
-
log(`
|
|
3321
|
+
if (!peer?.pc) {
|
|
3322
|
+
log(`p2p_answer for unknown peer ${peerId} \u2014 ignoring`);
|
|
3323
|
+
return;
|
|
3324
|
+
}
|
|
3325
|
+
const pcState = peer.pc.state();
|
|
3326
|
+
if (pcState === "closed" || pcState === "failed") {
|
|
3327
|
+
log(`p2p_answer ignored: peer ${peerId} PC state is ${pcState}`);
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
try {
|
|
3331
|
+
log(`Applying SDP answer for peer ${peerId}`);
|
|
3140
3332
|
peer.pc.setRemoteDescription(payload.sdp, payload.type);
|
|
3333
|
+
peer.remoteDescriptionSet = true;
|
|
3334
|
+
if (peer.pendingCandidates.length > 0) {
|
|
3335
|
+
log(`Flushing ${peer.pendingCandidates.length} queued ICE candidates for peer ${peerId}`);
|
|
3336
|
+
for (const c of peer.pendingCandidates) {
|
|
3337
|
+
try {
|
|
3338
|
+
peer.pc.addRemoteCandidate(c.candidate, c.mid);
|
|
3339
|
+
} catch (e) {
|
|
3340
|
+
log(`Queued ICE candidate error: ${e?.message}`);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
peer.pendingCandidates = [];
|
|
3344
|
+
}
|
|
3345
|
+
} catch (e) {
|
|
3346
|
+
log(`p2p_answer setRemoteDescription error for peer ${peerId}: ${e?.message}`);
|
|
3141
3347
|
}
|
|
3142
3348
|
return;
|
|
3143
3349
|
}
|
|
@@ -3148,7 +3354,18 @@ var init_daemon_p2p = __esm({
|
|
|
3148
3354
|
}
|
|
3149
3355
|
const peer = this.peers.get(peerId);
|
|
3150
3356
|
if (peer?.pc && payload.candidate) {
|
|
3151
|
-
peer.
|
|
3357
|
+
if (!peer.remoteDescriptionSet) {
|
|
3358
|
+
peer.pendingCandidates.push({
|
|
3359
|
+
candidate: payload.candidate,
|
|
3360
|
+
mid: payload.mid || payload.sdpMid || "0"
|
|
3361
|
+
});
|
|
3362
|
+
} else {
|
|
3363
|
+
try {
|
|
3364
|
+
peer.pc.addRemoteCandidate(payload.candidate, payload.mid || payload.sdpMid || "0");
|
|
3365
|
+
} catch (e) {
|
|
3366
|
+
log(`ICE candidate error for peer ${peerId}: ${e?.message}`);
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3152
3369
|
}
|
|
3153
3370
|
return;
|
|
3154
3371
|
}
|
|
@@ -3158,6 +3375,8 @@ var init_daemon_p2p = __esm({
|
|
|
3158
3375
|
disconnectPeer(peerId) {
|
|
3159
3376
|
const peer = this.peers.get(peerId);
|
|
3160
3377
|
if (!peer) return;
|
|
3378
|
+
if (peer.failedCleanupTimer) clearTimeout(peer.failedCleanupTimer);
|
|
3379
|
+
if (peer.heartbeatTimer) clearInterval(peer.heartbeatTimer);
|
|
3161
3380
|
if (peer.screenshotChannel) try {
|
|
3162
3381
|
peer.screenshotChannel.close();
|
|
3163
3382
|
} catch {
|
|
@@ -3174,6 +3393,25 @@ var init_daemon_p2p = __esm({
|
|
|
3174
3393
|
this.notifyStateChange();
|
|
3175
3394
|
log(`Peer ${peerId} disconnected`);
|
|
3176
3395
|
}
|
|
3396
|
+
/** P2P keepalive — NAT 바인딩 만료 방지 (특히 TURN relay) */
|
|
3397
|
+
startHeartbeat(peerId) {
|
|
3398
|
+
const peer = this.peers.get(peerId);
|
|
3399
|
+
if (!peer) return;
|
|
3400
|
+
if (peer.heartbeatTimer) clearInterval(peer.heartbeatTimer);
|
|
3401
|
+
peer.heartbeatTimer = setInterval(() => {
|
|
3402
|
+
const p = this.peers.get(peerId);
|
|
3403
|
+
if (!p || p.state !== "connected") {
|
|
3404
|
+
if (p?.heartbeatTimer) clearInterval(p.heartbeatTimer);
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
try {
|
|
3408
|
+
if (p.filesChannel?.isOpen()) {
|
|
3409
|
+
p.filesChannel.sendMessage(JSON.stringify({ type: "ping", ts: Date.now() }));
|
|
3410
|
+
}
|
|
3411
|
+
} catch {
|
|
3412
|
+
}
|
|
3413
|
+
}, 15e3);
|
|
3414
|
+
}
|
|
3177
3415
|
disconnect() {
|
|
3178
3416
|
for (const peerId of Array.from(this.peers.keys())) {
|
|
3179
3417
|
this.disconnectPeer(peerId);
|
|
@@ -3330,14 +3568,14 @@ var init_scaffold_template = __esm({
|
|
|
3330
3568
|
});
|
|
3331
3569
|
|
|
3332
3570
|
// src/daemon/dev-server.ts
|
|
3333
|
-
var http2,
|
|
3571
|
+
var http2, fs4, path5, os5, DEV_SERVER_PORT, DevServer;
|
|
3334
3572
|
var init_dev_server = __esm({
|
|
3335
3573
|
"src/daemon/dev-server.ts"() {
|
|
3336
3574
|
"use strict";
|
|
3337
3575
|
http2 = __toESM(require("http"));
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3576
|
+
fs4 = __toESM(require("fs"));
|
|
3577
|
+
path5 = __toESM(require("path"));
|
|
3578
|
+
os5 = __toESM(require("os"));
|
|
3341
3579
|
init_scaffold_template();
|
|
3342
3580
|
DEV_SERVER_PORT = 19280;
|
|
3343
3581
|
DevServer = class _DevServer {
|
|
@@ -3438,15 +3676,15 @@ var init_dev_server = __esm({
|
|
|
3438
3676
|
this.json(res, 500, { error: e.message });
|
|
3439
3677
|
}
|
|
3440
3678
|
});
|
|
3441
|
-
return new Promise((
|
|
3679
|
+
return new Promise((resolve5, reject) => {
|
|
3442
3680
|
this.server.listen(port, "127.0.0.1", () => {
|
|
3443
3681
|
this.log(`Dev server listening on http://127.0.0.1:${port}`);
|
|
3444
|
-
|
|
3682
|
+
resolve5();
|
|
3445
3683
|
});
|
|
3446
3684
|
this.server.on("error", (e) => {
|
|
3447
3685
|
if (e.code === "EADDRINUSE") {
|
|
3448
3686
|
this.log(`Port ${port} in use, skipping dev server`);
|
|
3449
|
-
|
|
3687
|
+
resolve5();
|
|
3450
3688
|
} else {
|
|
3451
3689
|
reject(e);
|
|
3452
3690
|
}
|
|
@@ -3641,9 +3879,9 @@ var init_dev_server = __esm({
|
|
|
3641
3879
|
}
|
|
3642
3880
|
// ─── DevConsole HTML ───
|
|
3643
3881
|
async serveConsole(_req, res) {
|
|
3644
|
-
const htmlPath =
|
|
3882
|
+
const htmlPath = path5.join(__dirname, "dev-console.html");
|
|
3645
3883
|
try {
|
|
3646
|
-
const html =
|
|
3884
|
+
const html = fs4.readFileSync(htmlPath, "utf-8");
|
|
3647
3885
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
3648
3886
|
res.end(html);
|
|
3649
3887
|
} catch (e) {
|
|
@@ -3658,16 +3896,16 @@ var init_dev_server = __esm({
|
|
|
3658
3896
|
".svg": "image/svg+xml"
|
|
3659
3897
|
};
|
|
3660
3898
|
async serveStatic(pathname, res) {
|
|
3661
|
-
const filename =
|
|
3899
|
+
const filename = path5.basename(pathname);
|
|
3662
3900
|
const allowed = ["dev-console.css", "dev-console.js", "dev-console-monaco.js"];
|
|
3663
3901
|
if (!allowed.includes(filename)) {
|
|
3664
3902
|
this.json(res, 404, { error: "Not found" });
|
|
3665
3903
|
return;
|
|
3666
3904
|
}
|
|
3667
|
-
const filePath =
|
|
3905
|
+
const filePath = path5.join(__dirname, filename);
|
|
3668
3906
|
try {
|
|
3669
|
-
const content =
|
|
3670
|
-
const ext =
|
|
3907
|
+
const content = fs4.readFileSync(filePath, "utf-8");
|
|
3908
|
+
const ext = path5.extname(filename);
|
|
3671
3909
|
const contentType = _DevServer.MIME_MAP[ext] || "text/plain";
|
|
3672
3910
|
res.writeHead(200, { "Content-Type": contentType, "Cache-Control": "no-cache" });
|
|
3673
3911
|
res.end(content);
|
|
@@ -3767,17 +4005,17 @@ var init_dev_server = __esm({
|
|
|
3767
4005
|
this.json(res, 404, { error: `Provider '${type}' not found` });
|
|
3768
4006
|
return;
|
|
3769
4007
|
}
|
|
3770
|
-
const builtinDir =
|
|
3771
|
-
const userDir =
|
|
4008
|
+
const builtinDir = path5.resolve(__dirname, "../providers/_builtin");
|
|
4009
|
+
const userDir = path5.join(os5.homedir(), ".adhdev", "providers");
|
|
3772
4010
|
const possiblePaths = [
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
4011
|
+
path5.join(userDir, type, "provider.js"),
|
|
4012
|
+
path5.join(builtinDir, "ide", type, "provider.js"),
|
|
4013
|
+
path5.join(builtinDir, "extension", type, "provider.js"),
|
|
4014
|
+
path5.join(builtinDir, "cli", type, "provider.js")
|
|
3777
4015
|
];
|
|
3778
4016
|
for (const p of possiblePaths) {
|
|
3779
|
-
if (
|
|
3780
|
-
const source =
|
|
4017
|
+
if (fs4.existsSync(p)) {
|
|
4018
|
+
const source = fs4.readFileSync(p, "utf-8");
|
|
3781
4019
|
this.json(res, 200, { type, path: p, source, lines: source.split("\n").length });
|
|
3782
4020
|
return;
|
|
3783
4021
|
}
|
|
@@ -3792,25 +4030,25 @@ var init_dev_server = __esm({
|
|
|
3792
4030
|
this.json(res, 400, { error: "source (string) required" });
|
|
3793
4031
|
return;
|
|
3794
4032
|
}
|
|
3795
|
-
const builtinDir =
|
|
3796
|
-
const userDir =
|
|
4033
|
+
const builtinDir = path5.resolve(__dirname, "../providers/_builtin");
|
|
4034
|
+
const userDir = path5.join(os5.homedir(), ".adhdev", "providers");
|
|
3797
4035
|
const possiblePaths = [
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
4036
|
+
path5.join(userDir, type, "provider.js"),
|
|
4037
|
+
path5.join(builtinDir, "ide", type, "provider.js"),
|
|
4038
|
+
path5.join(builtinDir, "extension", type, "provider.js"),
|
|
4039
|
+
path5.join(builtinDir, "cli", type, "provider.js")
|
|
3802
4040
|
];
|
|
3803
|
-
let targetPath = possiblePaths.find((p) =>
|
|
4041
|
+
let targetPath = possiblePaths.find((p) => fs4.existsSync(p));
|
|
3804
4042
|
if (!targetPath) {
|
|
3805
|
-
targetPath =
|
|
3806
|
-
|
|
4043
|
+
targetPath = path5.join(userDir, type, "provider.js");
|
|
4044
|
+
fs4.mkdirSync(path5.dirname(targetPath), { recursive: true });
|
|
3807
4045
|
}
|
|
3808
4046
|
try {
|
|
3809
|
-
if (
|
|
4047
|
+
if (fs4.existsSync(targetPath)) {
|
|
3810
4048
|
const backupPath = targetPath + ".bak";
|
|
3811
|
-
|
|
4049
|
+
fs4.copyFileSync(targetPath, backupPath);
|
|
3812
4050
|
}
|
|
3813
|
-
|
|
4051
|
+
fs4.writeFileSync(targetPath, source, "utf-8");
|
|
3814
4052
|
this.log(`Saved provider: ${targetPath} (${source.length} chars)`);
|
|
3815
4053
|
this.providerLoader.reload();
|
|
3816
4054
|
this.json(res, 200, { saved: true, path: targetPath, chars: source.length });
|
|
@@ -3830,21 +4068,21 @@ var init_dev_server = __esm({
|
|
|
3830
4068
|
this.json(res, 400, { error: "script (string) and code (string) required" });
|
|
3831
4069
|
return;
|
|
3832
4070
|
}
|
|
3833
|
-
const builtinDir =
|
|
3834
|
-
const userDir =
|
|
4071
|
+
const builtinDir = path5.resolve(__dirname, "../providers/_builtin");
|
|
4072
|
+
const userDir = path5.join(os5.homedir(), ".adhdev", "providers");
|
|
3835
4073
|
const possiblePaths = [
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
4074
|
+
path5.join(userDir, type, "provider.js"),
|
|
4075
|
+
path5.join(builtinDir, "ide", type, "provider.js"),
|
|
4076
|
+
path5.join(builtinDir, "extension", type, "provider.js"),
|
|
4077
|
+
path5.join(builtinDir, "cli", type, "provider.js")
|
|
3840
4078
|
];
|
|
3841
|
-
const targetPath = possiblePaths.find((p) =>
|
|
4079
|
+
const targetPath = possiblePaths.find((p) => fs4.existsSync(p));
|
|
3842
4080
|
if (!targetPath) {
|
|
3843
4081
|
this.json(res, 404, { error: `Provider '${type}' file not found` });
|
|
3844
4082
|
return;
|
|
3845
4083
|
}
|
|
3846
4084
|
try {
|
|
3847
|
-
let source =
|
|
4085
|
+
let source = fs4.readFileSync(targetPath, "utf-8");
|
|
3848
4086
|
const escapedCode = code.replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
3849
4087
|
const paramsMap = {
|
|
3850
4088
|
sendMessage: "text",
|
|
@@ -3915,8 +4153,8 @@ var init_dev_server = __esm({
|
|
|
3915
4153
|
},`;
|
|
3916
4154
|
source = source.slice(0, scriptsEnd + 2) + newFn + source.slice(scriptsEnd + 2);
|
|
3917
4155
|
}
|
|
3918
|
-
|
|
3919
|
-
|
|
4156
|
+
fs4.copyFileSync(targetPath, targetPath + ".bak");
|
|
4157
|
+
fs4.writeFileSync(targetPath, source, "utf-8");
|
|
3920
4158
|
this.log(`Script saved: ${type}/${script} \u2192 ${targetPath}`);
|
|
3921
4159
|
this.providerLoader.reload();
|
|
3922
4160
|
this.json(res, 200, { saved: true, script, path: targetPath });
|
|
@@ -3954,19 +4192,19 @@ var init_dev_server = __esm({
|
|
|
3954
4192
|
const template = this.generateTemplate(type, name, category, { cdpPorts, cli, processName, installPath, binary, extensionId });
|
|
3955
4193
|
let targetDir;
|
|
3956
4194
|
if (location === "user") {
|
|
3957
|
-
targetDir =
|
|
4195
|
+
targetDir = path5.join(os5.homedir(), ".adhdev", "providers", type);
|
|
3958
4196
|
} else {
|
|
3959
|
-
const builtinDir =
|
|
3960
|
-
targetDir =
|
|
4197
|
+
const builtinDir = path5.resolve(__dirname, "../providers/_builtin");
|
|
4198
|
+
targetDir = path5.join(builtinDir, category, type);
|
|
3961
4199
|
}
|
|
3962
|
-
const targetFile =
|
|
3963
|
-
if (
|
|
4200
|
+
const targetFile = path5.join(targetDir, "provider.js");
|
|
4201
|
+
if (fs4.existsSync(targetFile)) {
|
|
3964
4202
|
this.json(res, 409, { error: `Provider already exists at ${targetFile}`, path: targetFile });
|
|
3965
4203
|
return;
|
|
3966
4204
|
}
|
|
3967
4205
|
try {
|
|
3968
|
-
|
|
3969
|
-
|
|
4206
|
+
fs4.mkdirSync(targetDir, { recursive: true });
|
|
4207
|
+
fs4.writeFileSync(targetFile, template, "utf-8");
|
|
3970
4208
|
this.log(`Scaffolded provider: ${targetFile}`);
|
|
3971
4209
|
this.json(res, 201, { created: true, path: targetFile, type, name, category });
|
|
3972
4210
|
} catch (e) {
|
|
@@ -4121,14 +4359,14 @@ var init_dev_server = __esm({
|
|
|
4121
4359
|
res.end(JSON.stringify(data, null, 2));
|
|
4122
4360
|
}
|
|
4123
4361
|
async readBody(req) {
|
|
4124
|
-
return new Promise((
|
|
4362
|
+
return new Promise((resolve5) => {
|
|
4125
4363
|
let body = "";
|
|
4126
4364
|
req.on("data", (chunk) => body += chunk);
|
|
4127
4365
|
req.on("end", () => {
|
|
4128
4366
|
try {
|
|
4129
|
-
|
|
4367
|
+
resolve5(JSON.parse(body));
|
|
4130
4368
|
} catch {
|
|
4131
|
-
|
|
4369
|
+
resolve5({});
|
|
4132
4370
|
}
|
|
4133
4371
|
});
|
|
4134
4372
|
});
|
|
@@ -4444,14 +4682,14 @@ var init_daemon_cdp_devtools = __esm({
|
|
|
4444
4682
|
});
|
|
4445
4683
|
|
|
4446
4684
|
// src/daemon-commands.ts
|
|
4447
|
-
var
|
|
4685
|
+
var fs5, path6, os6, DaemonCommandHandler;
|
|
4448
4686
|
var init_daemon_commands = __esm({
|
|
4449
4687
|
"src/daemon-commands.ts"() {
|
|
4450
4688
|
"use strict";
|
|
4451
4689
|
init_daemon_cdp_devtools();
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4690
|
+
fs5 = __toESM(require("fs"));
|
|
4691
|
+
path6 = __toESM(require("path"));
|
|
4692
|
+
os6 = __toESM(require("os"));
|
|
4455
4693
|
init_config();
|
|
4456
4694
|
DaemonCommandHandler = class {
|
|
4457
4695
|
ctx;
|
|
@@ -4474,7 +4712,8 @@ var init_daemon_commands = __esm({
|
|
|
4474
4712
|
/** Current provider type — agentType 우선, ideType 사용 */
|
|
4475
4713
|
_currentProviderType;
|
|
4476
4714
|
/** Extract ideType from _targetInstance
|
|
4477
|
-
*
|
|
4715
|
+
* UUID-based: instanceIdMap에서 직접 조회
|
|
4716
|
+
* Legacy: composite ID 파싱 ('doId:ide:uuid' → uuid → map lookup)
|
|
4478
4717
|
*/
|
|
4479
4718
|
extractIdeType(args) {
|
|
4480
4719
|
if (args?._targetInstance) {
|
|
@@ -4485,7 +4724,9 @@ var init_daemon_commands = __esm({
|
|
|
4485
4724
|
if (ideMatch) raw = ideMatch[1];
|
|
4486
4725
|
else if (cliMatch) raw = cliMatch[1];
|
|
4487
4726
|
else if (acpMatch) raw = acpMatch[1];
|
|
4488
|
-
if (
|
|
4727
|
+
if (this.ctx.instanceIdMap?.has(raw)) {
|
|
4728
|
+
return this.ctx.instanceIdMap.get(raw);
|
|
4729
|
+
}
|
|
4489
4730
|
const lastUnderscore = raw.lastIndexOf("_");
|
|
4490
4731
|
if (lastUnderscore > 0) return raw.substring(0, lastUnderscore);
|
|
4491
4732
|
}
|
|
@@ -4580,7 +4821,7 @@ var init_daemon_commands = __esm({
|
|
|
4580
4821
|
this._currentIdeType = this.extractIdeType(args);
|
|
4581
4822
|
this._currentProviderType = args?.agentType || args?.providerType || this._currentIdeType;
|
|
4582
4823
|
if (!this._currentIdeType && !this._currentProviderType) {
|
|
4583
|
-
const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "resolve_action"];
|
|
4824
|
+
const cdpCommands = ["send_chat", "read_chat", "list_chats", "new_chat", "switch_chat", "set_mode", "change_model", "set_thought_level", "resolve_action"];
|
|
4584
4825
|
if (cdpCommands.includes(cmd)) {
|
|
4585
4826
|
return { success: false, error: "No ideType specified \u2014 cannot route command" };
|
|
4586
4827
|
}
|
|
@@ -4601,6 +4842,8 @@ var init_daemon_commands = __esm({
|
|
|
4601
4842
|
return this.handleSetMode(args);
|
|
4602
4843
|
case "change_model":
|
|
4603
4844
|
return this.handleChangeModel(args);
|
|
4845
|
+
case "set_thought_level":
|
|
4846
|
+
return this.handleSetThoughtLevel(args);
|
|
4604
4847
|
case "resolve_action":
|
|
4605
4848
|
return this.handleResolveAction(args);
|
|
4606
4849
|
case "cdp_eval":
|
|
@@ -4852,6 +5095,30 @@ var init_daemon_commands = __esm({
|
|
|
4852
5095
|
return { success: false, error: `CDP for ${this._currentIdeType || "unknown"} not connected` };
|
|
4853
5096
|
}
|
|
4854
5097
|
_log(`Targeting IDE: ${this._currentIdeType}`);
|
|
5098
|
+
if (provider2?.webviewMatchText && provider2?.scripts?.webviewSendMessage) {
|
|
5099
|
+
try {
|
|
5100
|
+
const webviewScript = provider2.scripts.webviewSendMessage(text);
|
|
5101
|
+
if (webviewScript && targetCdp.evaluateInWebviewFrame) {
|
|
5102
|
+
const matchText = provider2.webviewMatchText;
|
|
5103
|
+
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
5104
|
+
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
5105
|
+
let wvParsed = wvResult;
|
|
5106
|
+
if (typeof wvResult === "string") {
|
|
5107
|
+
try {
|
|
5108
|
+
wvParsed = JSON.parse(wvResult);
|
|
5109
|
+
} catch {
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
if (wvParsed?.sent) {
|
|
5113
|
+
_log(`webviewSendMessage (priority) OK`);
|
|
5114
|
+
return { success: true, sent: true, method: "webview-script-priority" };
|
|
5115
|
+
}
|
|
5116
|
+
_log(`webviewSendMessage (priority) did not confirm sent, falling through`);
|
|
5117
|
+
}
|
|
5118
|
+
} catch (e) {
|
|
5119
|
+
_log(`webviewSendMessage (priority) failed: ${e.message}, falling through`);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
4855
5122
|
if (provider2?.inputMethod === "cdp-type-and-send" && provider2.inputSelector) {
|
|
4856
5123
|
try {
|
|
4857
5124
|
const sent = await targetCdp.typeAndSend(provider2.inputSelector, text);
|
|
@@ -4889,11 +5156,34 @@ var init_daemon_commands = __esm({
|
|
|
4889
5156
|
_log(`typeAndSend(script.selector) failed: ${e.message}`);
|
|
4890
5157
|
}
|
|
4891
5158
|
}
|
|
4892
|
-
if (parsed?.needsTypeAndSend &&
|
|
5159
|
+
if (parsed?.needsTypeAndSend && provider2?.scripts?.webviewSendMessage) {
|
|
4893
5160
|
try {
|
|
4894
|
-
const
|
|
4895
|
-
|
|
4896
|
-
|
|
5161
|
+
const webviewScript = provider2.scripts.webviewSendMessage(text);
|
|
5162
|
+
if (webviewScript && targetCdp.evaluateInWebviewFrame) {
|
|
5163
|
+
const matchText = provider2.webviewMatchText;
|
|
5164
|
+
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
5165
|
+
const wvResult = await targetCdp.evaluateInWebviewFrame(webviewScript, matchFn);
|
|
5166
|
+
let wvParsed = wvResult;
|
|
5167
|
+
if (typeof wvResult === "string") {
|
|
5168
|
+
try {
|
|
5169
|
+
wvParsed = JSON.parse(wvResult);
|
|
5170
|
+
} catch {
|
|
5171
|
+
}
|
|
5172
|
+
}
|
|
5173
|
+
if (wvParsed?.sent) {
|
|
5174
|
+
_log(`webviewSendMessage OK`);
|
|
5175
|
+
return { success: true, sent: true, method: "webview-script" };
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
} catch (e) {
|
|
5179
|
+
_log(`webviewSendMessage failed: ${e.message}`);
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
if (parsed?.needsTypeAndSend && parsed?.clickCoords) {
|
|
5183
|
+
try {
|
|
5184
|
+
const { x, y } = parsed.clickCoords;
|
|
5185
|
+
const sent = await targetCdp.typeAndSendAt(x, y, text);
|
|
5186
|
+
if (sent) {
|
|
4897
5187
|
_log(`typeAndSendAt(${x},${y}) success`);
|
|
4898
5188
|
return { success: true, sent: true, method: "typeAndSendAt-script" };
|
|
4899
5189
|
}
|
|
@@ -5082,17 +5372,142 @@ var init_daemon_commands = __esm({
|
|
|
5082
5372
|
}
|
|
5083
5373
|
}
|
|
5084
5374
|
async handleSetMode(args) {
|
|
5375
|
+
const provider2 = this.getProvider();
|
|
5085
5376
|
const mode = args?.mode || "agent";
|
|
5377
|
+
if (provider2?.category === "acp") {
|
|
5378
|
+
const adapter = this.getCliAdapter(provider2.type);
|
|
5379
|
+
if (adapter) {
|
|
5380
|
+
const acpInstance = adapter._acpInstance;
|
|
5381
|
+
if (acpInstance && typeof acpInstance.onEvent === "function") {
|
|
5382
|
+
acpInstance.onEvent("set_mode", { mode });
|
|
5383
|
+
return { success: true, mode };
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
return { success: false, error: "ACP adapter not found" };
|
|
5387
|
+
}
|
|
5388
|
+
const webviewScript = this.getProviderScript("webviewSetMode", { MODE: JSON.stringify(mode) });
|
|
5389
|
+
if (webviewScript) {
|
|
5390
|
+
const cdp2 = this.getCdp();
|
|
5391
|
+
if (cdp2?.isConnected) {
|
|
5392
|
+
try {
|
|
5393
|
+
const matchText = provider2?.webviewMatchText;
|
|
5394
|
+
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
5395
|
+
const raw = await cdp2.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
5396
|
+
let result = raw;
|
|
5397
|
+
if (typeof raw === "string") {
|
|
5398
|
+
try {
|
|
5399
|
+
result = JSON.parse(raw);
|
|
5400
|
+
} catch {
|
|
5401
|
+
}
|
|
5402
|
+
}
|
|
5403
|
+
if (result?.success) return { success: true, mode, method: "webview-script" };
|
|
5404
|
+
} catch (e) {
|
|
5405
|
+
console.log(`[set_mode] webview script error: ${e.message}`);
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
}
|
|
5409
|
+
const mainScript = this.getProviderScript("setMode", { MODE: JSON.stringify(mode) });
|
|
5410
|
+
if (mainScript) {
|
|
5411
|
+
try {
|
|
5412
|
+
const evalResult = await this.evaluateProviderScript("setMode", { MODE: JSON.stringify(mode) }, 15e3);
|
|
5413
|
+
if (evalResult?.result) {
|
|
5414
|
+
let parsed = evalResult.result;
|
|
5415
|
+
if (typeof parsed === "string") {
|
|
5416
|
+
try {
|
|
5417
|
+
parsed = JSON.parse(parsed);
|
|
5418
|
+
} catch {
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
if (parsed?.success) return { success: true, mode, method: "script" };
|
|
5422
|
+
}
|
|
5423
|
+
} catch (e) {
|
|
5424
|
+
console.log(`[set_mode] script error: ${e.message}`);
|
|
5425
|
+
}
|
|
5426
|
+
}
|
|
5086
5427
|
return this.delegateToExtension(`composerMode.${mode}`, []);
|
|
5087
5428
|
}
|
|
5088
5429
|
async handleChangeModel(args) {
|
|
5089
5430
|
const provider2 = this.getProvider();
|
|
5431
|
+
const model = args?.model;
|
|
5432
|
+
console.log(`[change_model] model=${model} provider=${provider2?.type} category=${provider2?.category} ideType=${this._currentIdeType} providerType=${this._currentProviderType}`);
|
|
5433
|
+
if (provider2?.category === "acp") {
|
|
5434
|
+
const adapter = this.getCliAdapter(provider2.type);
|
|
5435
|
+
console.log(`[change_model] ACP adapter found: ${!!adapter}, type=${adapter?.cliType}, hasAcpInstance=${!!adapter?._acpInstance}`);
|
|
5436
|
+
if (adapter) {
|
|
5437
|
+
const acpInstance = adapter._acpInstance;
|
|
5438
|
+
if (acpInstance && typeof acpInstance.onEvent === "function") {
|
|
5439
|
+
acpInstance.onEvent("change_model", { model });
|
|
5440
|
+
console.log(`[change_model] Dispatched change_model event to ACP instance`);
|
|
5441
|
+
return { success: true, model };
|
|
5442
|
+
}
|
|
5443
|
+
}
|
|
5444
|
+
return { success: false, error: "ACP adapter not found" };
|
|
5445
|
+
}
|
|
5446
|
+
const webviewScript = this.getProviderScript("webviewSetModel", { MODEL: JSON.stringify(model) });
|
|
5447
|
+
if (webviewScript) {
|
|
5448
|
+
const cdp2 = this.getCdp();
|
|
5449
|
+
if (cdp2?.isConnected) {
|
|
5450
|
+
try {
|
|
5451
|
+
const matchText = provider2?.webviewMatchText;
|
|
5452
|
+
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
5453
|
+
const raw = await cdp2.evaluateInWebviewFrame?.(webviewScript, matchFn);
|
|
5454
|
+
let result = raw;
|
|
5455
|
+
if (typeof raw === "string") {
|
|
5456
|
+
try {
|
|
5457
|
+
result = JSON.parse(raw);
|
|
5458
|
+
} catch {
|
|
5459
|
+
}
|
|
5460
|
+
}
|
|
5461
|
+
if (result?.success) return { success: true, model, method: "webview-script" };
|
|
5462
|
+
} catch (e) {
|
|
5463
|
+
console.log(`[change_model] webview script error: ${e.message}`);
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
const mainScript = this.getProviderScript("setModel", { MODEL: JSON.stringify(model) });
|
|
5468
|
+
if (mainScript) {
|
|
5469
|
+
try {
|
|
5470
|
+
const evalResult = await this.evaluateProviderScript("setModel", { MODEL: JSON.stringify(model) }, 15e3);
|
|
5471
|
+
if (evalResult?.result) {
|
|
5472
|
+
let parsed = evalResult.result;
|
|
5473
|
+
if (typeof parsed === "string") {
|
|
5474
|
+
try {
|
|
5475
|
+
parsed = JSON.parse(parsed);
|
|
5476
|
+
} catch {
|
|
5477
|
+
}
|
|
5478
|
+
}
|
|
5479
|
+
if (parsed?.success) return { success: true, model, method: "script" };
|
|
5480
|
+
}
|
|
5481
|
+
} catch (e) {
|
|
5482
|
+
console.log(`[change_model] script error: ${e.message}`);
|
|
5483
|
+
}
|
|
5484
|
+
}
|
|
5090
5485
|
const settingsKey = provider2?.vscodeCommands?.changeModel;
|
|
5091
5486
|
if (settingsKey) {
|
|
5092
5487
|
return this.delegateToExtension("workbench.action.openSettings", [settingsKey]);
|
|
5093
5488
|
}
|
|
5094
5489
|
return { success: false, error: "changeModel not supported by this IDE provider" };
|
|
5095
5490
|
}
|
|
5491
|
+
/** set_thought_level — ACP configOption 변경 */
|
|
5492
|
+
async handleSetThoughtLevel(args) {
|
|
5493
|
+
const configId = args?.configId;
|
|
5494
|
+
const value = args?.value;
|
|
5495
|
+
if (!configId || !value) return { success: false, error: "configId and value required" };
|
|
5496
|
+
const provider2 = this.getProvider();
|
|
5497
|
+
if (!provider2 || provider2.category !== "acp") {
|
|
5498
|
+
return { success: false, error: "set_thought_level only for ACP providers" };
|
|
5499
|
+
}
|
|
5500
|
+
const adapter = this.getCliAdapter(provider2.type);
|
|
5501
|
+
const acpInstance = adapter?._acpInstance;
|
|
5502
|
+
if (!acpInstance) return { success: false, error: "ACP instance not found" };
|
|
5503
|
+
try {
|
|
5504
|
+
await acpInstance.setConfigOption(configId, value);
|
|
5505
|
+
console.log(`[set_thought_level] ${configId}=${value} for ${provider2.type}`);
|
|
5506
|
+
return { success: true, configId, value };
|
|
5507
|
+
} catch (e) {
|
|
5508
|
+
return { success: false, error: e?.message };
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5096
5511
|
/** resolveAction — 통합 (IDE/Extension 공통) */
|
|
5097
5512
|
async handleResolveAction(args) {
|
|
5098
5513
|
const provider2 = this.getProvider();
|
|
@@ -5340,7 +5755,7 @@ var init_daemon_commands = __esm({
|
|
|
5340
5755
|
async handleFileRead(args) {
|
|
5341
5756
|
try {
|
|
5342
5757
|
const filePath = this.resolveSafePath(args?.path);
|
|
5343
|
-
const content =
|
|
5758
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
5344
5759
|
return { success: true, content, path: filePath };
|
|
5345
5760
|
} catch (e) {
|
|
5346
5761
|
return { success: false, error: e.message };
|
|
@@ -5349,8 +5764,8 @@ var init_daemon_commands = __esm({
|
|
|
5349
5764
|
async handleFileWrite(args) {
|
|
5350
5765
|
try {
|
|
5351
5766
|
const filePath = this.resolveSafePath(args?.path);
|
|
5352
|
-
|
|
5353
|
-
|
|
5767
|
+
fs5.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
5768
|
+
fs5.writeFileSync(filePath, args?.content || "", "utf-8");
|
|
5354
5769
|
return { success: true, path: filePath };
|
|
5355
5770
|
} catch (e) {
|
|
5356
5771
|
return { success: false, error: e.message };
|
|
@@ -5359,11 +5774,11 @@ var init_daemon_commands = __esm({
|
|
|
5359
5774
|
async handleFileList(args) {
|
|
5360
5775
|
try {
|
|
5361
5776
|
const dirPath = this.resolveSafePath(args?.path || ".");
|
|
5362
|
-
const entries =
|
|
5777
|
+
const entries = fs5.readdirSync(dirPath, { withFileTypes: true });
|
|
5363
5778
|
const files = entries.map((e) => ({
|
|
5364
5779
|
name: e.name,
|
|
5365
5780
|
type: e.isDirectory() ? "directory" : "file",
|
|
5366
|
-
size: e.isFile() ?
|
|
5781
|
+
size: e.isFile() ? fs5.statSync(path6.join(dirPath, e.name)).size : void 0
|
|
5367
5782
|
}));
|
|
5368
5783
|
return { success: true, files, path: dirPath };
|
|
5369
5784
|
} catch (e) {
|
|
@@ -5374,14 +5789,14 @@ var init_daemon_commands = __esm({
|
|
|
5374
5789
|
return this.handleFileList(args);
|
|
5375
5790
|
}
|
|
5376
5791
|
resolveSafePath(requestedPath) {
|
|
5377
|
-
const home =
|
|
5792
|
+
const home = os6.homedir();
|
|
5378
5793
|
let resolved;
|
|
5379
5794
|
if (requestedPath.startsWith("~")) {
|
|
5380
|
-
resolved =
|
|
5381
|
-
} else if (
|
|
5795
|
+
resolved = path6.join(home, requestedPath.slice(1));
|
|
5796
|
+
} else if (path6.isAbsolute(requestedPath)) {
|
|
5382
5797
|
resolved = requestedPath;
|
|
5383
5798
|
} else {
|
|
5384
|
-
resolved =
|
|
5799
|
+
resolved = path6.resolve(requestedPath);
|
|
5385
5800
|
}
|
|
5386
5801
|
return resolved;
|
|
5387
5802
|
}
|
|
@@ -5397,11 +5812,11 @@ var init_daemon_commands = __esm({
|
|
|
5397
5812
|
const config = loadConfig();
|
|
5398
5813
|
const cliRecent = config.recentCliWorkspaces || [];
|
|
5399
5814
|
try {
|
|
5400
|
-
const storageDir =
|
|
5815
|
+
const storageDir = path6.join(os6.homedir(), "Library", "Application Support");
|
|
5401
5816
|
const candidates = ["Cursor", "Code", "VSCodium"];
|
|
5402
5817
|
for (const app of candidates) {
|
|
5403
|
-
const stateFile =
|
|
5404
|
-
if (
|
|
5818
|
+
const stateFile = path6.join(storageDir, app, "User", "globalStorage", "state.vscdb");
|
|
5819
|
+
if (fs5.existsSync(stateFile)) {
|
|
5405
5820
|
const result = await this.delegateToExtension("adhdev.getRecentWorkspaces", []);
|
|
5406
5821
|
if (result.success && Array.isArray(result.result)) {
|
|
5407
5822
|
const merged = Array.from(/* @__PURE__ */ new Set([...cliRecent, ...result.result])).slice(0, 20);
|
|
@@ -5500,7 +5915,14 @@ var init_daemon_commands = __esm({
|
|
|
5500
5915
|
const { cliType, data } = args || {};
|
|
5501
5916
|
if (!data) return { success: false, error: "data required" };
|
|
5502
5917
|
if (this.ctx.adapters) {
|
|
5503
|
-
const targetCli = cliType || "
|
|
5918
|
+
const targetCli = cliType || "";
|
|
5919
|
+
if (!targetCli && this.ctx.adapters.size > 0) {
|
|
5920
|
+
const first = this.ctx.adapters.values().next().value;
|
|
5921
|
+
if (first && typeof first.writeRaw === "function") {
|
|
5922
|
+
first.writeRaw(data);
|
|
5923
|
+
return { success: true };
|
|
5924
|
+
}
|
|
5925
|
+
}
|
|
5504
5926
|
const directAdapter = this.ctx.adapters.get(targetCli);
|
|
5505
5927
|
if (directAdapter && typeof directAdapter.writeRaw === "function") {
|
|
5506
5928
|
directAdapter.writeRaw(data);
|
|
@@ -5525,7 +5947,19 @@ var init_daemon_commands = __esm({
|
|
|
5525
5947
|
const { cliType, cols, rows, force } = args || {};
|
|
5526
5948
|
if (!cols || !rows) return { success: false, error: "cols and rows required" };
|
|
5527
5949
|
if (this.ctx.adapters) {
|
|
5528
|
-
const targetCli = cliType || "
|
|
5950
|
+
const targetCli = cliType || "";
|
|
5951
|
+
if (!targetCli && this.ctx.adapters.size > 0) {
|
|
5952
|
+
const first = this.ctx.adapters.values().next().value;
|
|
5953
|
+
if (first && typeof first.resize === "function") {
|
|
5954
|
+
if (force) {
|
|
5955
|
+
first.resize(cols - 1, rows);
|
|
5956
|
+
setTimeout(() => first.resize(cols, rows), 50);
|
|
5957
|
+
} else {
|
|
5958
|
+
first.resize(cols, rows);
|
|
5959
|
+
}
|
|
5960
|
+
return { success: true };
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5529
5963
|
const directAdapter = this.ctx.adapters.get(targetCli);
|
|
5530
5964
|
if (directAdapter && typeof directAdapter.resize === "function") {
|
|
5531
5965
|
if (force) {
|
|
@@ -5590,8 +6024,9 @@ var init_daemon_commands = __esm({
|
|
|
5590
6024
|
if (!loader) return { success: false, error: "ProviderLoader not initialized" };
|
|
5591
6025
|
const provider2 = loader.get(agentType);
|
|
5592
6026
|
if (!provider2) return { success: false, error: `Provider not found: ${agentType}` };
|
|
5593
|
-
const
|
|
5594
|
-
const
|
|
6027
|
+
const webviewScriptName = `webview${scriptName.charAt(0).toUpperCase() + scriptName.slice(1)}`;
|
|
6028
|
+
const hasWebviewScript = provider2.category === "ide" && !!provider2.scripts?.[webviewScriptName];
|
|
6029
|
+
const actualScriptName = hasWebviewScript ? webviewScriptName : scriptName;
|
|
5595
6030
|
if (!provider2.scripts?.[actualScriptName]) {
|
|
5596
6031
|
return { success: false, error: `Script '${actualScriptName}' not available for ${agentType}` };
|
|
5597
6032
|
}
|
|
@@ -5617,7 +6052,7 @@ var init_daemon_commands = __esm({
|
|
|
5617
6052
|
return { success: false, error: `No active session found for ${agentType}` };
|
|
5618
6053
|
}
|
|
5619
6054
|
result = await cdp2.evaluateInSession(targetSessionId, scriptCode);
|
|
5620
|
-
} else if (
|
|
6055
|
+
} else if (hasWebviewScript && cdp2.evaluateInWebviewFrame) {
|
|
5621
6056
|
const matchText = provider2.webviewMatchText;
|
|
5622
6057
|
const matchFn = matchText ? (body) => body.includes(matchText) : void 0;
|
|
5623
6058
|
result = await cdp2.evaluateInWebviewFrame(scriptCode, matchFn);
|
|
@@ -6110,13 +6545,13 @@ function checkDateRotation() {
|
|
|
6110
6545
|
const today = getDateStr();
|
|
6111
6546
|
if (today !== currentDate) {
|
|
6112
6547
|
currentDate = today;
|
|
6113
|
-
currentLogFile =
|
|
6548
|
+
currentLogFile = path7.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
6114
6549
|
cleanOldLogs();
|
|
6115
6550
|
}
|
|
6116
6551
|
}
|
|
6117
6552
|
function cleanOldLogs() {
|
|
6118
6553
|
try {
|
|
6119
|
-
const files =
|
|
6554
|
+
const files = fs6.readdirSync(LOG_DIR).filter((f) => f.startsWith("daemon-") && f.endsWith(".log"));
|
|
6120
6555
|
const cutoff = /* @__PURE__ */ new Date();
|
|
6121
6556
|
cutoff.setDate(cutoff.getDate() - MAX_LOG_DAYS);
|
|
6122
6557
|
const cutoffStr = cutoff.toISOString().slice(0, 10);
|
|
@@ -6124,7 +6559,7 @@ function cleanOldLogs() {
|
|
|
6124
6559
|
const dateMatch = file.match(/daemon-(\d{4}-\d{2}-\d{2})/);
|
|
6125
6560
|
if (dateMatch && dateMatch[1] < cutoffStr) {
|
|
6126
6561
|
try {
|
|
6127
|
-
|
|
6562
|
+
fs6.unlinkSync(path7.join(LOG_DIR, file));
|
|
6128
6563
|
} catch {
|
|
6129
6564
|
}
|
|
6130
6565
|
}
|
|
@@ -6134,14 +6569,14 @@ function cleanOldLogs() {
|
|
|
6134
6569
|
}
|
|
6135
6570
|
function rotateSizeIfNeeded() {
|
|
6136
6571
|
try {
|
|
6137
|
-
const stat =
|
|
6572
|
+
const stat = fs6.statSync(currentLogFile);
|
|
6138
6573
|
if (stat.size > MAX_LOG_SIZE) {
|
|
6139
6574
|
const backup = currentLogFile.replace(".log", ".1.log");
|
|
6140
6575
|
try {
|
|
6141
|
-
|
|
6576
|
+
fs6.unlinkSync(backup);
|
|
6142
6577
|
} catch {
|
|
6143
6578
|
}
|
|
6144
|
-
|
|
6579
|
+
fs6.renameSync(currentLogFile, backup);
|
|
6145
6580
|
}
|
|
6146
6581
|
} catch {
|
|
6147
6582
|
}
|
|
@@ -6152,7 +6587,7 @@ function writeToFile(line) {
|
|
|
6152
6587
|
checkDateRotation();
|
|
6153
6588
|
rotateSizeIfNeeded();
|
|
6154
6589
|
}
|
|
6155
|
-
|
|
6590
|
+
fs6.appendFileSync(currentLogFile, line + "\n");
|
|
6156
6591
|
} catch {
|
|
6157
6592
|
}
|
|
6158
6593
|
}
|
|
@@ -6253,36 +6688,36 @@ function installGlobalInterceptor() {
|
|
|
6253
6688
|
function getLogPath() {
|
|
6254
6689
|
return currentLogFile;
|
|
6255
6690
|
}
|
|
6256
|
-
var
|
|
6691
|
+
var fs6, path7, os7, LEVEL_NUM, LEVEL_LABEL, currentLevel, LOG_DIR, MAX_LOG_SIZE, MAX_LOG_DAYS, currentDate, currentLogFile, writeCount, RING_BUFFER_SIZE, ringBuffer, origConsoleLog, origConsoleError, origConsoleWarn, LOG, interceptorInstalled, LOG_PATH, LOG_DIR_PATH;
|
|
6257
6692
|
var init_daemon_logger = __esm({
|
|
6258
6693
|
"src/daemon-logger.ts"() {
|
|
6259
6694
|
"use strict";
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6695
|
+
fs6 = __toESM(require("fs"));
|
|
6696
|
+
path7 = __toESM(require("path"));
|
|
6697
|
+
os7 = __toESM(require("os"));
|
|
6263
6698
|
LEVEL_NUM = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
6264
6699
|
LEVEL_LABEL = { debug: "DBG", info: "INF", warn: "WRN", error: "ERR" };
|
|
6265
6700
|
currentLevel = "info";
|
|
6266
|
-
LOG_DIR = process.platform === "darwin" ?
|
|
6701
|
+
LOG_DIR = process.platform === "darwin" ? path7.join(os7.homedir(), "Library", "Logs", "adhdev") : path7.join(os7.homedir(), ".local", "share", "adhdev", "logs");
|
|
6267
6702
|
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
6268
6703
|
MAX_LOG_DAYS = 7;
|
|
6269
6704
|
try {
|
|
6270
|
-
|
|
6705
|
+
fs6.mkdirSync(LOG_DIR, { recursive: true });
|
|
6271
6706
|
} catch {
|
|
6272
6707
|
}
|
|
6273
6708
|
currentDate = getDateStr();
|
|
6274
|
-
currentLogFile =
|
|
6709
|
+
currentLogFile = path7.join(LOG_DIR, `daemon-${currentDate}.log`);
|
|
6275
6710
|
cleanOldLogs();
|
|
6276
6711
|
try {
|
|
6277
|
-
const oldLog =
|
|
6278
|
-
if (
|
|
6279
|
-
const stat =
|
|
6712
|
+
const oldLog = path7.join(LOG_DIR, "daemon.log");
|
|
6713
|
+
if (fs6.existsSync(oldLog)) {
|
|
6714
|
+
const stat = fs6.statSync(oldLog);
|
|
6280
6715
|
const oldDate = stat.mtime.toISOString().slice(0, 10);
|
|
6281
|
-
|
|
6716
|
+
fs6.renameSync(oldLog, path7.join(LOG_DIR, `daemon-${oldDate}.log`));
|
|
6282
6717
|
}
|
|
6283
|
-
const oldLogBackup =
|
|
6284
|
-
if (
|
|
6285
|
-
|
|
6718
|
+
const oldLogBackup = path7.join(LOG_DIR, "daemon.log.old");
|
|
6719
|
+
if (fs6.existsSync(oldLogBackup)) {
|
|
6720
|
+
fs6.unlinkSync(oldLogBackup);
|
|
6286
6721
|
}
|
|
6287
6722
|
} catch {
|
|
6288
6723
|
}
|
|
@@ -6299,18 +6734,18 @@ var init_daemon_logger = __esm({
|
|
|
6299
6734
|
error: (category, msg) => daemonLog(category, msg, "error")
|
|
6300
6735
|
};
|
|
6301
6736
|
interceptorInstalled = false;
|
|
6302
|
-
LOG_PATH =
|
|
6737
|
+
LOG_PATH = path7.join(LOG_DIR, `daemon-${getDateStr()}.log`);
|
|
6303
6738
|
LOG_DIR_PATH = LOG_DIR;
|
|
6304
6739
|
}
|
|
6305
6740
|
});
|
|
6306
6741
|
|
|
6307
6742
|
// src/daemon-status.ts
|
|
6308
|
-
var
|
|
6743
|
+
var os8, path8, DaemonStatusReporter;
|
|
6309
6744
|
var init_daemon_status = __esm({
|
|
6310
6745
|
"src/daemon-status.ts"() {
|
|
6311
6746
|
"use strict";
|
|
6312
|
-
|
|
6313
|
-
|
|
6747
|
+
os8 = __toESM(require("os"));
|
|
6748
|
+
path8 = __toESM(require("path"));
|
|
6314
6749
|
init_config();
|
|
6315
6750
|
init_daemon_logger();
|
|
6316
6751
|
DaemonStatusReporter = class {
|
|
@@ -6371,7 +6806,7 @@ var init_daemon_status = __esm({
|
|
|
6371
6806
|
}
|
|
6372
6807
|
emitStatusEvent(event) {
|
|
6373
6808
|
LOG.info("StatusEvent", `${event.event} (${event.providerType || event.ideType || ""})`);
|
|
6374
|
-
this.deps.
|
|
6809
|
+
this.deps.serverConn?.sendMessage("status_event", event);
|
|
6375
6810
|
}
|
|
6376
6811
|
removeAgentTracking(_key) {
|
|
6377
6812
|
}
|
|
@@ -6383,8 +6818,8 @@ var init_daemon_status = __esm({
|
|
|
6383
6818
|
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
6384
6819
|
}
|
|
6385
6820
|
async sendUnifiedStatusReport(opts) {
|
|
6386
|
-
const {
|
|
6387
|
-
if (!
|
|
6821
|
+
const { serverConn, cdpManagers, p2p, providerLoader, localServer, adapters } = this.deps;
|
|
6822
|
+
if (!serverConn?.isConnected()) return;
|
|
6388
6823
|
this.lastStatusSentAt = Date.now();
|
|
6389
6824
|
const now = this.lastStatusSentAt;
|
|
6390
6825
|
const target = opts?.p2pOnly ? "P2P" : "P2P+Server";
|
|
@@ -6468,6 +6903,7 @@ var init_daemon_status = __esm({
|
|
|
6468
6903
|
}
|
|
6469
6904
|
const managedClis = cliStates.map((s) => ({
|
|
6470
6905
|
id: s.instanceId,
|
|
6906
|
+
instanceId: s.instanceId,
|
|
6471
6907
|
cliType: s.type,
|
|
6472
6908
|
cliName: s.name,
|
|
6473
6909
|
status: s.status,
|
|
@@ -6483,7 +6919,10 @@ var init_daemon_status = __esm({
|
|
|
6483
6919
|
mode: "chat",
|
|
6484
6920
|
workingDir: s.workingDir || "",
|
|
6485
6921
|
activeChat: s.activeChat,
|
|
6486
|
-
currentModel: s.currentModel
|
|
6922
|
+
currentModel: s.currentModel,
|
|
6923
|
+
currentPlan: s.currentPlan,
|
|
6924
|
+
acpConfigOptions: s.acpConfigOptions,
|
|
6925
|
+
acpModes: s.acpModes
|
|
6487
6926
|
}));
|
|
6488
6927
|
const extSummary = localServer?.getLatestExtensionData() || {
|
|
6489
6928
|
activeFile: null,
|
|
@@ -6496,15 +6935,15 @@ var init_daemon_status = __esm({
|
|
|
6496
6935
|
daemonMode: true,
|
|
6497
6936
|
machineNickname: loadConfig().machineNickname || null,
|
|
6498
6937
|
machine: {
|
|
6499
|
-
hostname:
|
|
6500
|
-
platform:
|
|
6501
|
-
release:
|
|
6502
|
-
arch:
|
|
6503
|
-
cpus:
|
|
6504
|
-
totalMem:
|
|
6505
|
-
freeMem:
|
|
6506
|
-
loadavg:
|
|
6507
|
-
uptime:
|
|
6938
|
+
hostname: os8.hostname(),
|
|
6939
|
+
platform: os8.platform(),
|
|
6940
|
+
release: os8.release(),
|
|
6941
|
+
arch: os8.arch(),
|
|
6942
|
+
cpus: os8.cpus().length,
|
|
6943
|
+
totalMem: os8.totalmem(),
|
|
6944
|
+
freeMem: os8.freemem(),
|
|
6945
|
+
loadavg: os8.loadavg(),
|
|
6946
|
+
uptime: os8.uptime()
|
|
6508
6947
|
},
|
|
6509
6948
|
managedIdes,
|
|
6510
6949
|
managedClis,
|
|
@@ -6527,7 +6966,7 @@ var init_daemon_status = __esm({
|
|
|
6527
6966
|
})),
|
|
6528
6967
|
timestamp: now,
|
|
6529
6968
|
activeFile: extSummary.activeFile,
|
|
6530
|
-
workspaceFolders: extSummary.workspaceFolders?.length > 0 ? extSummary.workspaceFolders : Array.from(adapters.values()).map((a) => ({ name:
|
|
6969
|
+
workspaceFolders: extSummary.workspaceFolders?.length > 0 ? extSummary.workspaceFolders : Array.from(adapters.values()).map((a) => ({ name: path8.basename(a.workingDir), path: a.workingDir })),
|
|
6531
6970
|
terminals: extSummary.terminals,
|
|
6532
6971
|
aiAgents: [
|
|
6533
6972
|
...managedIdes.flatMap((ide) => (ide.aiAgents || []).map((a) => ({
|
|
@@ -6541,24 +6980,24 @@ var init_daemon_status = __esm({
|
|
|
6541
6980
|
activeChat: managedClis[0]?.activeChat || managedIdes[0]?.activeChat || null,
|
|
6542
6981
|
agentStreams: managedIdes.flatMap((ide) => ide.agentStreams || []),
|
|
6543
6982
|
connectedExtensions: extSummary.connectedIdes,
|
|
6544
|
-
system: { platform:
|
|
6983
|
+
system: { platform: os8.platform(), hostname: os8.hostname() }
|
|
6545
6984
|
};
|
|
6546
6985
|
const p2pSent = this.sendP2PPayload(payload);
|
|
6547
6986
|
if (p2pSent) {
|
|
6548
6987
|
LOG.debug("P2P", `sent (${JSON.stringify(payload).length} bytes)`);
|
|
6549
6988
|
}
|
|
6550
6989
|
if (opts?.p2pOnly) return;
|
|
6551
|
-
const plan =
|
|
6990
|
+
const plan = serverConn.getUserPlan();
|
|
6552
6991
|
if (plan !== "free") {
|
|
6553
6992
|
const wsPayload = {
|
|
6554
6993
|
daemonMode: true,
|
|
6555
6994
|
machineNickname: payload.machineNickname,
|
|
6556
6995
|
machine: {
|
|
6557
|
-
hostname:
|
|
6558
|
-
platform:
|
|
6559
|
-
arch:
|
|
6560
|
-
cpus:
|
|
6561
|
-
totalMem:
|
|
6996
|
+
hostname: os8.hostname(),
|
|
6997
|
+
platform: os8.platform(),
|
|
6998
|
+
arch: os8.arch(),
|
|
6999
|
+
cpus: os8.cpus().length,
|
|
7000
|
+
totalMem: os8.totalmem()
|
|
6562
7001
|
},
|
|
6563
7002
|
managedIdes: managedIdes.map((ide) => ({
|
|
6564
7003
|
ideType: ide.ideType,
|
|
@@ -6581,7 +7020,7 @@ var init_daemon_status = __esm({
|
|
|
6581
7020
|
cdpConnected: payload.cdpConnected,
|
|
6582
7021
|
timestamp: now
|
|
6583
7022
|
};
|
|
6584
|
-
|
|
7023
|
+
serverConn.sendMessage("status_report", wsPayload);
|
|
6585
7024
|
LOG.debug("Server", `sent status_report (${JSON.stringify(wsPayload).length} bytes)`);
|
|
6586
7025
|
}
|
|
6587
7026
|
}
|
|
@@ -6617,7 +7056,7 @@ function stripAnsi(str) {
|
|
|
6617
7056
|
return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
|
|
6618
7057
|
}
|
|
6619
7058
|
function findBinary(name) {
|
|
6620
|
-
const isWin =
|
|
7059
|
+
const isWin = os9.platform() === "win32";
|
|
6621
7060
|
try {
|
|
6622
7061
|
const cmd = isWin ? `where ${name}` : `which ${name}`;
|
|
6623
7062
|
return (0, import_child_process4.execSync)(cmd, { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n")[0].trim();
|
|
@@ -6625,11 +7064,11 @@ function findBinary(name) {
|
|
|
6625
7064
|
return isWin ? `${name}.cmd` : name;
|
|
6626
7065
|
}
|
|
6627
7066
|
}
|
|
6628
|
-
var
|
|
7067
|
+
var os9, import_child_process4, pty, ProviderCliAdapter;
|
|
6629
7068
|
var init_provider_cli_adapter = __esm({
|
|
6630
7069
|
"src/cli-adapters/provider-cli-adapter.ts"() {
|
|
6631
7070
|
"use strict";
|
|
6632
|
-
|
|
7071
|
+
os9 = __toESM(require("os"));
|
|
6633
7072
|
import_child_process4 = require("child_process");
|
|
6634
7073
|
try {
|
|
6635
7074
|
pty = require("node-pty");
|
|
@@ -6642,7 +7081,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
6642
7081
|
this.provider = provider2;
|
|
6643
7082
|
this.cliType = provider2.type;
|
|
6644
7083
|
this.cliName = provider2.name;
|
|
6645
|
-
this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/,
|
|
7084
|
+
this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os9.homedir()) : workingDir;
|
|
6646
7085
|
const t = provider2.timeouts || {};
|
|
6647
7086
|
this.timeouts = {
|
|
6648
7087
|
ptyFlush: t.ptyFlush ?? 50,
|
|
@@ -6675,17 +7114,17 @@ var init_provider_cli_adapter = __esm({
|
|
|
6675
7114
|
ptyOutputBuffer = "";
|
|
6676
7115
|
ptyOutputFlushTimer = null;
|
|
6677
7116
|
// Bridge for log forwarding
|
|
6678
|
-
|
|
7117
|
+
serverConn = null;
|
|
6679
7118
|
logBuffer = [];
|
|
6680
7119
|
// Approval cooldown
|
|
6681
7120
|
lastApprovalResolvedAt = 0;
|
|
6682
7121
|
// Resolved timeouts (provider defaults + overrides)
|
|
6683
7122
|
timeouts;
|
|
6684
7123
|
// ─── Lifecycle ─────────────────────────────────
|
|
6685
|
-
|
|
6686
|
-
this.
|
|
6687
|
-
if (this.
|
|
6688
|
-
this.logBuffer.forEach((log2) => this.
|
|
7124
|
+
setServerConn(serverConn) {
|
|
7125
|
+
this.serverConn = serverConn;
|
|
7126
|
+
if (this.serverConn && this.logBuffer.length > 0) {
|
|
7127
|
+
this.logBuffer.forEach((log2) => this.serverConn.sendMessage("log", log2));
|
|
6689
7128
|
this.logBuffer = [];
|
|
6690
7129
|
}
|
|
6691
7130
|
}
|
|
@@ -6700,7 +7139,7 @@ var init_provider_cli_adapter = __esm({
|
|
|
6700
7139
|
if (!pty) throw new Error("node-pty is not installed");
|
|
6701
7140
|
const { spawn: spawnConfig } = this.provider;
|
|
6702
7141
|
const binaryPath = findBinary(spawnConfig.command);
|
|
6703
|
-
const isWin =
|
|
7142
|
+
const isWin = os9.platform() === "win32";
|
|
6704
7143
|
const allArgs = [...spawnConfig.args, ...this.extraArgs];
|
|
6705
7144
|
console.log(`[${this.cliType}] Spawning in ${this.workingDir}`);
|
|
6706
7145
|
let shellCmd;
|
|
@@ -6753,8 +7192,8 @@ var init_provider_cli_adapter = __esm({
|
|
|
6753
7192
|
const cleanData = stripAnsi(rawData);
|
|
6754
7193
|
const { patterns } = this.provider;
|
|
6755
7194
|
if (cleanData.trim()) {
|
|
6756
|
-
if (this.
|
|
6757
|
-
this.
|
|
7195
|
+
if (this.serverConn) {
|
|
7196
|
+
this.serverConn.sendMessage("log", { message: cleanData.trim(), level: "info" });
|
|
6758
7197
|
} else {
|
|
6759
7198
|
this.logBuffer.push({ message: cleanData.trim(), level: "info" });
|
|
6760
7199
|
}
|
|
@@ -6941,11 +7380,12 @@ var init_provider_cli_adapter = __esm({
|
|
|
6941
7380
|
});
|
|
6942
7381
|
|
|
6943
7382
|
// src/cli-detector.ts
|
|
6944
|
-
async function detectCLIs() {
|
|
6945
|
-
const platform7 =
|
|
7383
|
+
async function detectCLIs(providerLoader) {
|
|
7384
|
+
const platform7 = os10.platform();
|
|
6946
7385
|
const whichCmd = platform7 === "win32" ? "where" : "which";
|
|
7386
|
+
const cliList = providerLoader ? providerLoader.getCliDetectionList() : [];
|
|
6947
7387
|
const results = [];
|
|
6948
|
-
for (const cli of
|
|
7388
|
+
for (const cli of cliList) {
|
|
6949
7389
|
try {
|
|
6950
7390
|
const pathResult = (0, import_child_process5.execSync)(`${whichCmd} ${cli.command} 2>/dev/null`, {
|
|
6951
7391
|
encoding: "utf-8",
|
|
@@ -6971,22 +7411,17 @@ async function detectCLIs() {
|
|
|
6971
7411
|
}
|
|
6972
7412
|
return results;
|
|
6973
7413
|
}
|
|
6974
|
-
async function detectCLI(cliId) {
|
|
6975
|
-
const
|
|
6976
|
-
const all = await detectCLIs();
|
|
6977
|
-
return all.find((c) => c.id ===
|
|
7414
|
+
async function detectCLI(cliId, providerLoader) {
|
|
7415
|
+
const resolvedId = providerLoader ? providerLoader.resolveAlias(cliId) : cliId;
|
|
7416
|
+
const all = await detectCLIs(providerLoader);
|
|
7417
|
+
return all.find((c) => c.id === resolvedId && c.installed) || null;
|
|
6978
7418
|
}
|
|
6979
|
-
var import_child_process5,
|
|
7419
|
+
var import_child_process5, os10;
|
|
6980
7420
|
var init_cli_detector = __esm({
|
|
6981
7421
|
"src/cli-detector.ts"() {
|
|
6982
7422
|
"use strict";
|
|
6983
7423
|
import_child_process5 = require("child_process");
|
|
6984
|
-
|
|
6985
|
-
KNOWN_CLIS = [
|
|
6986
|
-
{ id: "gemini-cli", displayName: "Gemini CLI", icon: "\u264A", command: "gemini" },
|
|
6987
|
-
{ id: "claude-code", displayName: "Claude Code", icon: "\u{1F916}", command: "claude" },
|
|
6988
|
-
{ id: "codex-cli", displayName: "Codex CLI", icon: "\u{1F9E0}", command: "codex" }
|
|
6989
|
-
];
|
|
7424
|
+
os10 = __toESM(require("os"));
|
|
6990
7425
|
}
|
|
6991
7426
|
});
|
|
6992
7427
|
|
|
@@ -7084,21 +7519,24 @@ var init_status_monitor = __esm({
|
|
|
7084
7519
|
});
|
|
7085
7520
|
|
|
7086
7521
|
// src/providers/cli-provider-instance.ts
|
|
7087
|
-
var
|
|
7522
|
+
var crypto2, CliProviderInstance;
|
|
7088
7523
|
var init_cli_provider_instance = __esm({
|
|
7089
7524
|
"src/providers/cli-provider-instance.ts"() {
|
|
7090
7525
|
"use strict";
|
|
7091
|
-
|
|
7526
|
+
crypto2 = __toESM(require("crypto"));
|
|
7092
7527
|
init_provider_cli_adapter();
|
|
7093
7528
|
init_status_monitor();
|
|
7529
|
+
init_chat_history();
|
|
7094
7530
|
CliProviderInstance = class {
|
|
7095
|
-
constructor(provider2, workingDir, cliArgs = []) {
|
|
7531
|
+
constructor(provider2, workingDir, cliArgs = [], instanceId) {
|
|
7096
7532
|
this.provider = provider2;
|
|
7097
7533
|
this.workingDir = workingDir;
|
|
7098
7534
|
this.cliArgs = cliArgs;
|
|
7099
7535
|
this.type = provider2.type;
|
|
7536
|
+
this.instanceId = instanceId || crypto2.randomUUID();
|
|
7100
7537
|
this.adapter = new ProviderCliAdapter(provider2, workingDir, cliArgs);
|
|
7101
7538
|
this.monitor = new StatusMonitor();
|
|
7539
|
+
this.historyWriter = new ChatHistoryWriter();
|
|
7102
7540
|
}
|
|
7103
7541
|
type;
|
|
7104
7542
|
category = "cli";
|
|
@@ -7109,6 +7547,8 @@ var init_cli_provider_instance = __esm({
|
|
|
7109
7547
|
generatingStartedAt = 0;
|
|
7110
7548
|
settings = {};
|
|
7111
7549
|
monitor;
|
|
7550
|
+
historyWriter;
|
|
7551
|
+
instanceId;
|
|
7112
7552
|
// ─── Lifecycle ─────────────────────────────────
|
|
7113
7553
|
async init(context) {
|
|
7114
7554
|
this.context = context;
|
|
@@ -7118,8 +7558,8 @@ var init_cli_provider_instance = __esm({
|
|
|
7118
7558
|
longGeneratingAlert: this.settings.longGeneratingAlert !== false,
|
|
7119
7559
|
longGeneratingThresholdSec: this.settings.longGeneratingThresholdSec || 180
|
|
7120
7560
|
});
|
|
7121
|
-
if (context.
|
|
7122
|
-
this.adapter.
|
|
7561
|
+
if (context.serverConn) {
|
|
7562
|
+
this.adapter.setServerConn(context.serverConn);
|
|
7123
7563
|
}
|
|
7124
7564
|
if (context.onPtyData) {
|
|
7125
7565
|
this.adapter.setOnPtyData(context.onPtyData);
|
|
@@ -7150,6 +7590,15 @@ var init_cli_provider_instance = __esm({
|
|
|
7150
7590
|
});
|
|
7151
7591
|
}
|
|
7152
7592
|
}
|
|
7593
|
+
if (recentMessages.length > 0) {
|
|
7594
|
+
const dirName2 = this.workingDir.split("/").filter(Boolean).pop() || "session";
|
|
7595
|
+
this.historyWriter.appendNewMessages(
|
|
7596
|
+
this.type,
|
|
7597
|
+
recentMessages,
|
|
7598
|
+
`${this.provider.name} \xB7 ${dirName2}`,
|
|
7599
|
+
this.instanceId
|
|
7600
|
+
);
|
|
7601
|
+
}
|
|
7153
7602
|
return {
|
|
7154
7603
|
type: this.type,
|
|
7155
7604
|
name: this.provider.name,
|
|
@@ -7165,7 +7614,7 @@ var init_cli_provider_instance = __esm({
|
|
|
7165
7614
|
inputContent: ""
|
|
7166
7615
|
},
|
|
7167
7616
|
workingDir: this.workingDir,
|
|
7168
|
-
instanceId:
|
|
7617
|
+
instanceId: this.instanceId,
|
|
7169
7618
|
lastUpdated: Date.now(),
|
|
7170
7619
|
settings: this.settings,
|
|
7171
7620
|
pendingEvents: this.flushEvents()
|
|
@@ -7174,8 +7623,8 @@ var init_cli_provider_instance = __esm({
|
|
|
7174
7623
|
onEvent(event, data) {
|
|
7175
7624
|
if (event === "send_message" && data?.text) {
|
|
7176
7625
|
this.adapter.sendMessage(data.text);
|
|
7177
|
-
} else if (event === "
|
|
7178
|
-
this.adapter.
|
|
7626
|
+
} else if (event === "server_connected" && data?.bridge) {
|
|
7627
|
+
this.adapter.setServerConn(data.serverConn);
|
|
7179
7628
|
}
|
|
7180
7629
|
}
|
|
7181
7630
|
dispose() {
|
|
@@ -7199,7 +7648,8 @@ var init_cli_provider_instance = __esm({
|
|
|
7199
7648
|
event: "agent:waiting_approval",
|
|
7200
7649
|
chatTitle,
|
|
7201
7650
|
timestamp: now,
|
|
7202
|
-
modalMessage: adapterStatus.activeModal?.message
|
|
7651
|
+
modalMessage: adapterStatus.activeModal?.message,
|
|
7652
|
+
modalButtons: adapterStatus.activeModal?.buttons
|
|
7203
7653
|
});
|
|
7204
7654
|
} else if (newStatus === "idle" && (this.lastStatus === "generating" || this.lastStatus === "waiting_approval")) {
|
|
7205
7655
|
const duration = this.generatingStartedAt ? Math.round((now - this.generatingStartedAt) / 1e3) : 0;
|
|
@@ -7240,12 +7690,12 @@ var init_cli_provider_instance = __esm({
|
|
|
7240
7690
|
});
|
|
7241
7691
|
|
|
7242
7692
|
// src/providers/acp-provider-instance.ts
|
|
7243
|
-
var path9,
|
|
7693
|
+
var path9, crypto3, import_child_process6, import_readline, AcpProviderInstance;
|
|
7244
7694
|
var init_acp_provider_instance = __esm({
|
|
7245
7695
|
"src/providers/acp-provider-instance.ts"() {
|
|
7246
7696
|
"use strict";
|
|
7247
7697
|
path9 = __toESM(require("path"));
|
|
7248
|
-
|
|
7698
|
+
crypto3 = __toESM(require("crypto"));
|
|
7249
7699
|
import_child_process6 = require("child_process");
|
|
7250
7700
|
import_readline = require("readline");
|
|
7251
7701
|
init_status_monitor();
|
|
@@ -7255,7 +7705,7 @@ var init_acp_provider_instance = __esm({
|
|
|
7255
7705
|
this.type = provider2.type;
|
|
7256
7706
|
this.provider = provider2;
|
|
7257
7707
|
this.workingDir = workingDir;
|
|
7258
|
-
this.instanceId =
|
|
7708
|
+
this.instanceId = crypto3.randomUUID();
|
|
7259
7709
|
this.monitor = new StatusMonitor();
|
|
7260
7710
|
}
|
|
7261
7711
|
type;
|
|
@@ -7282,6 +7732,18 @@ var init_acp_provider_instance = __esm({
|
|
|
7282
7732
|
activeToolCalls = [];
|
|
7283
7733
|
stopReason = null;
|
|
7284
7734
|
partialContent = "";
|
|
7735
|
+
// Error tracking
|
|
7736
|
+
errorMessage = null;
|
|
7737
|
+
errorReason = null;
|
|
7738
|
+
stderrBuffer = [];
|
|
7739
|
+
spawnedAt = 0;
|
|
7740
|
+
// ACP ConfigOptions & Modes (from session/new response or static fallback)
|
|
7741
|
+
configOptions = [];
|
|
7742
|
+
availableModes = [];
|
|
7743
|
+
/** Static config mode — agent doesn't support config/* methods */
|
|
7744
|
+
useStaticConfig = false;
|
|
7745
|
+
/** Current config selections (for spawnArgBuilder) */
|
|
7746
|
+
selectedConfig = {};
|
|
7285
7747
|
// Config
|
|
7286
7748
|
workingDir;
|
|
7287
7749
|
instanceId;
|
|
@@ -7342,7 +7804,13 @@ var init_acp_provider_instance = __esm({
|
|
|
7342
7804
|
instanceId: this.instanceId,
|
|
7343
7805
|
lastUpdated: Date.now(),
|
|
7344
7806
|
settings: this.settings,
|
|
7345
|
-
pendingEvents: this.flushEvents()
|
|
7807
|
+
pendingEvents: this.flushEvents(),
|
|
7808
|
+
// ACP-specific: expose available models/modes for dashboard
|
|
7809
|
+
acpConfigOptions: this.configOptions,
|
|
7810
|
+
acpModes: this.availableModes,
|
|
7811
|
+
// Error details for dashboard display
|
|
7812
|
+
errorMessage: this.errorMessage,
|
|
7813
|
+
errorReason: this.errorReason
|
|
7346
7814
|
};
|
|
7347
7815
|
}
|
|
7348
7816
|
onEvent(event, data) {
|
|
@@ -7357,8 +7825,144 @@ var init_acp_provider_instance = __esm({
|
|
|
7357
7825
|
this.cancelSession().catch(
|
|
7358
7826
|
(e) => console.warn(`[ACP:${this.type}] cancel error:`, e?.message)
|
|
7359
7827
|
);
|
|
7828
|
+
} else if (event === "change_model" && data?.model) {
|
|
7829
|
+
this.setConfigOption("model", data.model).catch(
|
|
7830
|
+
(e) => console.warn(`[ACP:${this.type}] change_model error:`, e?.message)
|
|
7831
|
+
);
|
|
7832
|
+
} else if (event === "set_mode" && data?.mode) {
|
|
7833
|
+
this.setMode(data.mode).catch(
|
|
7834
|
+
(e) => console.warn(`[ACP:${this.type}] set_mode error:`, e?.message)
|
|
7835
|
+
);
|
|
7836
|
+
} else if (event === "set_thought_level" && data?.level) {
|
|
7837
|
+
this.setConfigOption("thought_level", data.level).catch(
|
|
7838
|
+
(e) => console.warn(`[ACP:${this.type}] set_thought_level error:`, e?.message)
|
|
7839
|
+
);
|
|
7840
|
+
}
|
|
7841
|
+
}
|
|
7842
|
+
// ─── ACP Config Options & Modes ─────────────────────
|
|
7843
|
+
parseConfigOptions(raw) {
|
|
7844
|
+
if (!Array.isArray(raw)) return;
|
|
7845
|
+
this.configOptions = [];
|
|
7846
|
+
for (const opt of raw) {
|
|
7847
|
+
const category = opt.category || "other";
|
|
7848
|
+
const configId = opt.configId || opt.id || "";
|
|
7849
|
+
const currentValue = opt.currentValue ?? opt.select?.currentValue;
|
|
7850
|
+
const flatOptions = [];
|
|
7851
|
+
const selectOpts = opt.select?.options || opt.options;
|
|
7852
|
+
if (selectOpts) {
|
|
7853
|
+
if (Array.isArray(selectOpts.ungrouped)) {
|
|
7854
|
+
for (const o of selectOpts.ungrouped) {
|
|
7855
|
+
flatOptions.push({ value: o.value, name: o.name || o.value, description: o.description });
|
|
7856
|
+
}
|
|
7857
|
+
}
|
|
7858
|
+
if (Array.isArray(selectOpts.grouped)) {
|
|
7859
|
+
for (const g of selectOpts.grouped) {
|
|
7860
|
+
const groupName = g.name || g.group || "";
|
|
7861
|
+
for (const o of Array.isArray(g.options?.ungrouped) ? g.options.ungrouped : g.options || []) {
|
|
7862
|
+
flatOptions.push({ value: o.value, name: o.name || o.value, description: o.description, group: groupName });
|
|
7863
|
+
}
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7866
|
+
if (Array.isArray(selectOpts)) {
|
|
7867
|
+
for (const o of selectOpts) {
|
|
7868
|
+
if (o.value) flatOptions.push({ value: o.value, name: o.name || o.value, description: o.description });
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
}
|
|
7872
|
+
this.configOptions.push({ category, configId, currentValue, options: flatOptions });
|
|
7873
|
+
if (category === "model" && currentValue) this.currentModel = currentValue;
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
parseModes(raw) {
|
|
7877
|
+
if (!raw) return;
|
|
7878
|
+
if (raw.currentModeId) this.currentMode = raw.currentModeId;
|
|
7879
|
+
if (Array.isArray(raw.availableModes)) {
|
|
7880
|
+
this.availableModes = raw.availableModes.map((m) => ({
|
|
7881
|
+
id: m.id,
|
|
7882
|
+
name: m.name || m.id,
|
|
7883
|
+
description: m.description
|
|
7884
|
+
}));
|
|
7360
7885
|
}
|
|
7361
7886
|
}
|
|
7887
|
+
async setConfigOption(category, value) {
|
|
7888
|
+
const opt = this.configOptions.find((c) => c.category === category);
|
|
7889
|
+
if (!opt) {
|
|
7890
|
+
console.warn(`[ACP:${this.type}] No config option for category: ${category}`);
|
|
7891
|
+
return;
|
|
7892
|
+
}
|
|
7893
|
+
if (this.useStaticConfig) {
|
|
7894
|
+
opt.currentValue = value;
|
|
7895
|
+
this.selectedConfig[opt.configId] = value;
|
|
7896
|
+
if (category === "model") this.currentModel = value;
|
|
7897
|
+
if (category === "mode") this.currentMode = value;
|
|
7898
|
+
console.log(`[ACP:${this.type}] Static config ${category} set to: ${value} \u2014 restarting agent`);
|
|
7899
|
+
await this.restartWithNewConfig();
|
|
7900
|
+
return;
|
|
7901
|
+
}
|
|
7902
|
+
try {
|
|
7903
|
+
console.log(`[ACP:${this.type}] Sending session/set_config_option: configId=${opt.configId} value=${value} sessionId=${this.sessionId}`);
|
|
7904
|
+
const result = await this.sendRequest("session/set_config_option", {
|
|
7905
|
+
sessionId: this.sessionId,
|
|
7906
|
+
configId: opt.configId,
|
|
7907
|
+
value
|
|
7908
|
+
});
|
|
7909
|
+
opt.currentValue = value;
|
|
7910
|
+
if (category === "model") this.currentModel = value;
|
|
7911
|
+
if (result?.configOptions) this.parseConfigOptions(result.configOptions);
|
|
7912
|
+
console.log(`[ACP:${this.type}] Config ${category} set to: ${value} | response: ${JSON.stringify(result)?.slice(0, 300)}`);
|
|
7913
|
+
} catch (e) {
|
|
7914
|
+
console.warn(`[ACP:${this.type}] set_config_option failed:`, e?.message);
|
|
7915
|
+
}
|
|
7916
|
+
}
|
|
7917
|
+
async setMode(modeId) {
|
|
7918
|
+
if (this.useStaticConfig) {
|
|
7919
|
+
const opt = this.configOptions.find((c) => c.category === "mode");
|
|
7920
|
+
if (opt) {
|
|
7921
|
+
opt.currentValue = modeId;
|
|
7922
|
+
this.selectedConfig[opt.configId] = modeId;
|
|
7923
|
+
}
|
|
7924
|
+
this.currentMode = modeId;
|
|
7925
|
+
console.log(`[ACP:${this.type}] Static mode set to: ${modeId} \u2014 restarting agent`);
|
|
7926
|
+
await this.restartWithNewConfig();
|
|
7927
|
+
return;
|
|
7928
|
+
}
|
|
7929
|
+
try {
|
|
7930
|
+
await this.sendRequest("session/set_mode", {
|
|
7931
|
+
sessionId: this.sessionId,
|
|
7932
|
+
modeId
|
|
7933
|
+
});
|
|
7934
|
+
this.currentMode = modeId;
|
|
7935
|
+
console.log(`[ACP:${this.type}] Mode set to: ${modeId}`);
|
|
7936
|
+
} catch (e) {
|
|
7937
|
+
console.warn(`[ACP:${this.type}] set_mode failed:`, e?.message);
|
|
7938
|
+
}
|
|
7939
|
+
}
|
|
7940
|
+
/** Static config: 프로세스를 죽이고 새 args로 재시작 */
|
|
7941
|
+
async restartWithNewConfig() {
|
|
7942
|
+
if (this.provider.spawnArgBuilder) {
|
|
7943
|
+
this.cliArgs = [];
|
|
7944
|
+
}
|
|
7945
|
+
if (this.process) {
|
|
7946
|
+
try {
|
|
7947
|
+
this.process.kill("SIGTERM");
|
|
7948
|
+
} catch {
|
|
7949
|
+
}
|
|
7950
|
+
this.process = null;
|
|
7951
|
+
}
|
|
7952
|
+
if (this.readline) {
|
|
7953
|
+
this.readline.close();
|
|
7954
|
+
this.readline = null;
|
|
7955
|
+
}
|
|
7956
|
+
for (const [, pending] of this.pendingRequests) {
|
|
7957
|
+
clearTimeout(pending.timer);
|
|
7958
|
+
pending.reject(new Error("Agent restarting"));
|
|
7959
|
+
}
|
|
7960
|
+
this.pendingRequests.clear();
|
|
7961
|
+
this.sessionId = null;
|
|
7962
|
+
this.currentStatus = "starting";
|
|
7963
|
+
this.detectStatusTransition();
|
|
7964
|
+
await this.spawnAgent();
|
|
7965
|
+
}
|
|
7362
7966
|
dispose() {
|
|
7363
7967
|
if (this.process) {
|
|
7364
7968
|
try {
|
|
@@ -7385,9 +7989,24 @@ var init_acp_provider_instance = __esm({
|
|
|
7385
7989
|
throw new Error(`[ACP:${this.type}] No spawn config defined`);
|
|
7386
7990
|
}
|
|
7387
7991
|
const command = spawnConfig.command;
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7992
|
+
let baseArgs = spawnConfig.args || [];
|
|
7993
|
+
if (this.provider.spawnArgBuilder && Object.keys(this.selectedConfig).length > 0) {
|
|
7994
|
+
baseArgs = this.provider.spawnArgBuilder(this.selectedConfig);
|
|
7995
|
+
}
|
|
7996
|
+
const args = [...baseArgs, ...this.cliArgs];
|
|
7997
|
+
let authEnv = {};
|
|
7998
|
+
try {
|
|
7999
|
+
const { loadConfig: loadConfig2 } = (init_config(), __toCommonJS(config_exports));
|
|
8000
|
+
const config = loadConfig2();
|
|
8001
|
+
authEnv = config.acpAuth?.[this.type] || {};
|
|
8002
|
+
} catch {
|
|
8003
|
+
}
|
|
8004
|
+
const env = { ...process.env, ...spawnConfig.env || {}, ...authEnv };
|
|
8005
|
+
console.log(`[ACP:${this.type}] Spawning: ${command} ${args.join(" ")} in ${this.workingDir}${Object.keys(authEnv).length > 0 ? ` (auth: ${Object.keys(authEnv).join(", ")})` : ""}`);
|
|
8006
|
+
this.spawnedAt = Date.now();
|
|
8007
|
+
this.errorMessage = null;
|
|
8008
|
+
this.errorReason = null;
|
|
8009
|
+
this.stderrBuffer = [];
|
|
7391
8010
|
this.process = (0, import_child_process6.spawn)(command, args, {
|
|
7392
8011
|
cwd: this.workingDir,
|
|
7393
8012
|
env,
|
|
@@ -7404,19 +8023,65 @@ var init_acp_provider_instance = __esm({
|
|
|
7404
8023
|
} catch (e) {
|
|
7405
8024
|
}
|
|
7406
8025
|
});
|
|
8026
|
+
const AUTH_ERROR_PATTERNS = [
|
|
8027
|
+
/unauthorized|unauthenticated/i,
|
|
8028
|
+
/invalid.*(?:api[_ ]?key|token|credential)/i,
|
|
8029
|
+
/auth(?:entication|orization).*(?:fail|error|denied|invalid|expired)/i,
|
|
8030
|
+
/(?:api[_ ]?key|token).*(?:missing|required|not set|not found|invalid|expired)/i,
|
|
8031
|
+
/ENOENT|command not found|not recognized/i,
|
|
8032
|
+
/permission denied/i,
|
|
8033
|
+
/rate.?limit|quota.?exceeded/i,
|
|
8034
|
+
/login.*required|please.*(?:login|authenticate|sign.?in)/i
|
|
8035
|
+
];
|
|
7407
8036
|
this.process.stderr?.on("data", (data) => {
|
|
7408
8037
|
const text = data.toString().trim();
|
|
7409
|
-
if (text)
|
|
7410
|
-
|
|
8038
|
+
if (!text) return;
|
|
8039
|
+
console.log(`[ACP:${this.type}:stderr] ${text.slice(0, 300)}`);
|
|
8040
|
+
this.stderrBuffer.push(text);
|
|
8041
|
+
if (this.stderrBuffer.length > 20) this.stderrBuffer.shift();
|
|
8042
|
+
for (const pattern of AUTH_ERROR_PATTERNS) {
|
|
8043
|
+
if (pattern.test(text)) {
|
|
8044
|
+
if (/ENOENT|command not found|not recognized/i.test(text)) {
|
|
8045
|
+
this.errorReason = "not_installed";
|
|
8046
|
+
this.errorMessage = `Command '${command}' not found. Install: ${this.provider.install || "check documentation"}`;
|
|
8047
|
+
} else {
|
|
8048
|
+
this.errorReason = "auth_failed";
|
|
8049
|
+
this.errorMessage = text.slice(0, 300);
|
|
8050
|
+
}
|
|
8051
|
+
console.warn(`[ACP:${this.type}] Error detected (${this.errorReason}): ${this.errorMessage?.slice(0, 100)}`);
|
|
8052
|
+
break;
|
|
8053
|
+
}
|
|
7411
8054
|
}
|
|
7412
8055
|
});
|
|
7413
8056
|
this.process.on("exit", (code, signal) => {
|
|
7414
|
-
|
|
7415
|
-
this.
|
|
8057
|
+
const elapsed = Date.now() - this.spawnedAt;
|
|
8058
|
+
console.log(`[ACP:${this.type}] Process exited: code=${code} signal=${signal} elapsed=${elapsed}ms`);
|
|
8059
|
+
if (code !== 0 && code !== null) {
|
|
8060
|
+
if (!this.errorReason) {
|
|
8061
|
+
if (code === 127) {
|
|
8062
|
+
this.errorReason = "not_installed";
|
|
8063
|
+
this.errorMessage = `Command '${command}' not found (exit code 127). Install: ${this.provider.install || "check documentation"}`;
|
|
8064
|
+
} else if (elapsed < 3e3) {
|
|
8065
|
+
this.errorReason = this.stderrBuffer.length > 0 ? "crash" : "spawn_error";
|
|
8066
|
+
this.errorMessage = this.stderrBuffer.length > 0 ? `Agent crashed immediately (exit code ${code}): ${this.stderrBuffer.slice(-3).join(" | ").slice(0, 300)}` : `Agent exited immediately with code ${code}. The agent may not be installed correctly.`;
|
|
8067
|
+
} else {
|
|
8068
|
+
this.errorReason = "crash";
|
|
8069
|
+
this.errorMessage = `Agent exited with code ${code}${this.stderrBuffer.length > 0 ? ": " + this.stderrBuffer.slice(-1)[0]?.slice(0, 200) : ""}`;
|
|
8070
|
+
}
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
this.currentStatus = this.errorReason ? "error" : "stopped";
|
|
7416
8074
|
this.detectStatusTransition();
|
|
7417
8075
|
});
|
|
7418
8076
|
this.process.on("error", (err) => {
|
|
7419
|
-
console.error(`[ACP:${this.type}] Process error:`, err.message);
|
|
8077
|
+
console.error(`[ACP:${this.type}] Process spawn error:`, err.message);
|
|
8078
|
+
if (err.message.includes("ENOENT")) {
|
|
8079
|
+
this.errorReason = "not_installed";
|
|
8080
|
+
this.errorMessage = `Command '${command}' not found. Install: ${this.provider.install || "check documentation"}`;
|
|
8081
|
+
} else {
|
|
8082
|
+
this.errorReason = "spawn_error";
|
|
8083
|
+
this.errorMessage = err.message;
|
|
8084
|
+
}
|
|
7420
8085
|
this.currentStatus = "error";
|
|
7421
8086
|
this.detectStatusTransition();
|
|
7422
8087
|
});
|
|
@@ -7426,7 +8091,8 @@ var init_acp_provider_instance = __esm({
|
|
|
7426
8091
|
async initialize() {
|
|
7427
8092
|
try {
|
|
7428
8093
|
const result = await this.sendRequest("initialize", {
|
|
7429
|
-
protocolVersion:
|
|
8094
|
+
protocolVersion: 1,
|
|
8095
|
+
// number — Gemini CLI 등 일부 에이전트는 숫자만 허용
|
|
7430
8096
|
clientInfo: {
|
|
7431
8097
|
name: "adhdev",
|
|
7432
8098
|
version: "0.1.0"
|
|
@@ -7445,6 +8111,10 @@ var init_acp_provider_instance = __esm({
|
|
|
7445
8111
|
await this.createSession();
|
|
7446
8112
|
} catch (e) {
|
|
7447
8113
|
console.error(`[ACP:${this.type}] Initialize failed:`, e?.message);
|
|
8114
|
+
if (!this.errorReason) {
|
|
8115
|
+
this.errorReason = "init_failed";
|
|
8116
|
+
this.errorMessage = `ACP handshake failed: ${e?.message}${this.stderrBuffer.length > 0 ? "\n" + this.stderrBuffer.slice(-2).join("\n").slice(0, 200) : ""}`;
|
|
8117
|
+
}
|
|
7448
8118
|
this.currentStatus = "error";
|
|
7449
8119
|
}
|
|
7450
8120
|
}
|
|
@@ -7457,10 +8127,36 @@ var init_acp_provider_instance = __esm({
|
|
|
7457
8127
|
this.sessionId = result?.sessionId || null;
|
|
7458
8128
|
this.currentStatus = "idle";
|
|
7459
8129
|
this.messages = [];
|
|
7460
|
-
|
|
8130
|
+
console.log(`[ACP:${this.type}] session/new result keys: ${result ? Object.keys(result).join(", ") : "null"}`);
|
|
8131
|
+
if (result?.configOptions) console.log(`[ACP:${this.type}] configOptions: ${JSON.stringify(result.configOptions).slice(0, 500)}`);
|
|
8132
|
+
if (result?.modes) console.log(`[ACP:${this.type}] modes: ${JSON.stringify(result.modes).slice(0, 300)}`);
|
|
8133
|
+
this.parseConfigOptions(result?.configOptions);
|
|
8134
|
+
this.parseModes(result?.modes);
|
|
8135
|
+
if (!this.currentModel && result?.models?.currentModelId) {
|
|
7461
8136
|
this.currentModel = result.models.currentModelId;
|
|
7462
8137
|
}
|
|
7463
|
-
|
|
8138
|
+
if (this.configOptions.length === 0 && this.provider.staticConfigOptions?.length) {
|
|
8139
|
+
this.useStaticConfig = true;
|
|
8140
|
+
for (const sc of this.provider.staticConfigOptions) {
|
|
8141
|
+
const defaultVal = this.selectedConfig[sc.configId] || sc.defaultValue || sc.options[0]?.value;
|
|
8142
|
+
this.configOptions.push({
|
|
8143
|
+
category: sc.category,
|
|
8144
|
+
configId: sc.configId,
|
|
8145
|
+
currentValue: defaultVal,
|
|
8146
|
+
options: sc.options.map((o) => ({ ...o }))
|
|
8147
|
+
});
|
|
8148
|
+
if (defaultVal) {
|
|
8149
|
+
this.selectedConfig[sc.configId] = defaultVal;
|
|
8150
|
+
if (sc.category === "model") this.currentModel = defaultVal;
|
|
8151
|
+
if (sc.category === "mode") this.currentMode = defaultVal;
|
|
8152
|
+
}
|
|
8153
|
+
}
|
|
8154
|
+
console.log(`[ACP:${this.type}] Using static configOptions (${this.configOptions.length} options)`);
|
|
8155
|
+
}
|
|
8156
|
+
console.log(`[ACP:${this.type}] Session created: ${this.sessionId}${this.currentModel ? ` (model: ${this.currentModel})` : ""}${this.currentMode ? ` (mode: ${this.currentMode})` : ""}`);
|
|
8157
|
+
if (this.configOptions.length > 0) {
|
|
8158
|
+
console.log(`[ACP:${this.type}] Config options: ${this.configOptions.map((c) => `${c.category}(${c.options.length})`).join(", ")}`);
|
|
8159
|
+
}
|
|
7464
8160
|
} catch (e) {
|
|
7465
8161
|
console.warn(`[ACP:${this.type}] session/new failed:`, e?.message);
|
|
7466
8162
|
this.currentStatus = "idle";
|
|
@@ -7483,6 +8179,27 @@ var init_acp_provider_instance = __esm({
|
|
|
7483
8179
|
if (result?.stopReason) {
|
|
7484
8180
|
this.stopReason = result.stopReason;
|
|
7485
8181
|
}
|
|
8182
|
+
if (!this.partialContent.trim() && result) {
|
|
8183
|
+
let responseText = "";
|
|
8184
|
+
if (typeof result.content === "string") {
|
|
8185
|
+
responseText = result.content;
|
|
8186
|
+
} else if (Array.isArray(result.content)) {
|
|
8187
|
+
responseText = result.content.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
|
|
8188
|
+
} else if (result.text) {
|
|
8189
|
+
responseText = result.text;
|
|
8190
|
+
} else if (result.message?.content) {
|
|
8191
|
+
const mc = result.message.content;
|
|
8192
|
+
if (typeof mc === "string") responseText = mc;
|
|
8193
|
+
else if (Array.isArray(mc)) responseText = mc.filter((p) => p.type === "text").map((p) => p.text || "").join("\n");
|
|
8194
|
+
}
|
|
8195
|
+
if (responseText.trim()) {
|
|
8196
|
+
this.messages.push({
|
|
8197
|
+
role: "assistant",
|
|
8198
|
+
content: responseText.trim(),
|
|
8199
|
+
timestamp: Date.now()
|
|
8200
|
+
});
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
7486
8203
|
if (this.partialContent.trim()) {
|
|
7487
8204
|
this.messages.push({
|
|
7488
8205
|
role: "assistant",
|
|
@@ -7527,7 +8244,7 @@ var init_acp_provider_instance = __esm({
|
|
|
7527
8244
|
}
|
|
7528
8245
|
// ─── JSON-RPC Transport ──────────────────────────
|
|
7529
8246
|
sendRequest(method, params, timeoutMs = 3e4) {
|
|
7530
|
-
return new Promise((
|
|
8247
|
+
return new Promise((resolve5, reject) => {
|
|
7531
8248
|
if (!this.process?.stdin?.writable) {
|
|
7532
8249
|
reject(new Error("Process stdin not writable"));
|
|
7533
8250
|
return;
|
|
@@ -7543,7 +8260,7 @@ var init_acp_provider_instance = __esm({
|
|
|
7543
8260
|
this.pendingRequests.delete(id);
|
|
7544
8261
|
reject(new Error(`Request ${method} timed out after ${timeoutMs}ms`));
|
|
7545
8262
|
}, timeoutMs);
|
|
7546
|
-
this.pendingRequests.set(id, { resolve:
|
|
8263
|
+
this.pendingRequests.set(id, { resolve: resolve5, reject, timer });
|
|
7547
8264
|
const line = JSON.stringify(msg) + "\n";
|
|
7548
8265
|
this.process.stdin.write(line);
|
|
7549
8266
|
});
|
|
@@ -7604,13 +8321,13 @@ var init_acp_provider_instance = __esm({
|
|
|
7604
8321
|
case "requestPermission": {
|
|
7605
8322
|
this.currentStatus = "waiting_approval";
|
|
7606
8323
|
this.detectStatusTransition();
|
|
7607
|
-
const promise = new Promise((
|
|
7608
|
-
this.permissionResolvers.push(
|
|
8324
|
+
const promise = new Promise((resolve5) => {
|
|
8325
|
+
this.permissionResolvers.push(resolve5);
|
|
7609
8326
|
setTimeout(() => {
|
|
7610
|
-
const idx = this.permissionResolvers.indexOf(
|
|
8327
|
+
const idx = this.permissionResolvers.indexOf(resolve5);
|
|
7611
8328
|
if (idx >= 0) {
|
|
7612
8329
|
this.permissionResolvers.splice(idx, 1);
|
|
7613
|
-
|
|
8330
|
+
resolve5(false);
|
|
7614
8331
|
}
|
|
7615
8332
|
}, 3e5);
|
|
7616
8333
|
});
|
|
@@ -7653,6 +8370,14 @@ var init_acp_provider_instance = __esm({
|
|
|
7653
8370
|
// ─── ACP session/update 처리 ─────────────────────
|
|
7654
8371
|
handleSessionUpdate(params) {
|
|
7655
8372
|
if (!params) return;
|
|
8373
|
+
const update = params.update;
|
|
8374
|
+
if (update?.sessionUpdate === "agent_message_chunk" && update.content) {
|
|
8375
|
+
const part = update.content;
|
|
8376
|
+
if (part.type === "text" && part.text) {
|
|
8377
|
+
this.partialContent += part.text;
|
|
8378
|
+
}
|
|
8379
|
+
this.currentStatus = "generating";
|
|
8380
|
+
}
|
|
7656
8381
|
if (params.messageDelta) {
|
|
7657
8382
|
const delta = params.messageDelta;
|
|
7658
8383
|
if (delta.content) {
|
|
@@ -7707,6 +8432,13 @@ var init_acp_provider_instance = __esm({
|
|
|
7707
8432
|
if (params.model) {
|
|
7708
8433
|
this.currentModel = params.model;
|
|
7709
8434
|
}
|
|
8435
|
+
const upd = params.update || params;
|
|
8436
|
+
if (upd?.sessionUpdate === "config_option_update" && upd.configOptions) {
|
|
8437
|
+
this.parseConfigOptions(upd.configOptions);
|
|
8438
|
+
}
|
|
8439
|
+
if (upd?.sessionUpdate === "current_mode_update" && upd.modeId) {
|
|
8440
|
+
this.currentMode = upd.modeId;
|
|
8441
|
+
}
|
|
7710
8442
|
}
|
|
7711
8443
|
// ─── 상태 전이 감지 ────────────────────────────
|
|
7712
8444
|
detectStatusTransition() {
|
|
@@ -7766,12 +8498,13 @@ var init_acp_provider_instance = __esm({
|
|
|
7766
8498
|
});
|
|
7767
8499
|
|
|
7768
8500
|
// src/daemon-cli.ts
|
|
7769
|
-
var
|
|
8501
|
+
var os11, path10, crypto4, import_chalk, DaemonCliManager;
|
|
7770
8502
|
var init_daemon_cli = __esm({
|
|
7771
8503
|
"src/daemon-cli.ts"() {
|
|
7772
8504
|
"use strict";
|
|
7773
|
-
|
|
8505
|
+
os11 = __toESM(require("os"));
|
|
7774
8506
|
path10 = __toESM(require("path"));
|
|
8507
|
+
crypto4 = __toESM(require("crypto"));
|
|
7775
8508
|
import_chalk = __toESM(require("chalk"));
|
|
7776
8509
|
init_provider_cli_adapter();
|
|
7777
8510
|
init_cli_detector();
|
|
@@ -7792,41 +8525,39 @@ var init_daemon_cli = __esm({
|
|
|
7792
8525
|
return `${cliType}_${hash}`;
|
|
7793
8526
|
}
|
|
7794
8527
|
createAdapter(cliType, workingDir, cliArgs) {
|
|
7795
|
-
const
|
|
7796
|
-
"claude-code": "claude-cli",
|
|
7797
|
-
"codex": "codex-cli"
|
|
7798
|
-
};
|
|
7799
|
-
const normalizedType = typeMap[cliType] || cliType;
|
|
8528
|
+
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
7800
8529
|
const provider2 = this.providerLoader.get(normalizedType);
|
|
7801
8530
|
if (provider2 && provider2.category === "cli" && provider2.patterns && provider2.spawn) {
|
|
7802
8531
|
console.log(import_chalk.default.cyan(` \u{1F4E6} Using provider: ${provider2.name} (${provider2.type})`));
|
|
7803
8532
|
return new ProviderCliAdapter(provider2, workingDir, cliArgs);
|
|
7804
8533
|
}
|
|
7805
|
-
console.log(import_chalk.default.yellow(` \u26A0 No CLI provider for '${cliType}', falling back to generic`));
|
|
7806
|
-
const fallbackProvider = this.providerLoader.get("gemini-cli");
|
|
7807
|
-
if (fallbackProvider) {
|
|
7808
|
-
return new ProviderCliAdapter(fallbackProvider, workingDir, cliArgs);
|
|
7809
|
-
}
|
|
7810
8534
|
throw new Error(`No CLI provider found for '${cliType}'. Create a provider.js in providers/cli/${cliType}/`);
|
|
7811
8535
|
}
|
|
7812
8536
|
// ─── 세션 시작/중지 ──────────────────────────────
|
|
7813
|
-
async startSession(cliType, workingDir, cliArgs) {
|
|
7814
|
-
const trimmed = (workingDir ||
|
|
7815
|
-
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/,
|
|
7816
|
-
const
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
};
|
|
7820
|
-
const normalizedType = typeMap[cliType] || cliType;
|
|
7821
|
-
const provider2 = this.providerLoader.get(normalizedType);
|
|
7822
|
-
const key = this.getCliKey(normalizedType, resolvedDir);
|
|
7823
|
-
if (this.adapters.has(key)) {
|
|
7824
|
-
console.log(import_chalk.default.yellow(` \u26A1 ${cliType} already running in ${resolvedDir}`));
|
|
7825
|
-
return;
|
|
7826
|
-
}
|
|
8537
|
+
async startSession(cliType, workingDir, cliArgs, initialModel) {
|
|
8538
|
+
const trimmed = (workingDir || os11.homedir()).trim();
|
|
8539
|
+
const resolvedDir = trimmed.startsWith("~") ? trimmed.replace(/^~/, os11.homedir()) : path10.resolve(trimmed);
|
|
8540
|
+
const normalizedType = this.providerLoader.resolveAlias(cliType);
|
|
8541
|
+
const provider2 = this.providerLoader.getByAlias(cliType);
|
|
8542
|
+
const key = crypto4.randomUUID();
|
|
7827
8543
|
if (provider2 && provider2.category === "acp") {
|
|
7828
8544
|
const instanceManager2 = this.deps.getInstanceManager();
|
|
7829
8545
|
if (!instanceManager2) throw new Error("InstanceManager not available");
|
|
8546
|
+
const spawnCmd = provider2.spawn?.command;
|
|
8547
|
+
if (spawnCmd) {
|
|
8548
|
+
try {
|
|
8549
|
+
const { execSync: execSync6 } = require("child_process");
|
|
8550
|
+
execSync6(`which ${spawnCmd}`, { stdio: "ignore" });
|
|
8551
|
+
} catch {
|
|
8552
|
+
const installInfo = provider2.install || `Install: check ${provider2.displayName || provider2.name} documentation`;
|
|
8553
|
+
throw new Error(
|
|
8554
|
+
`${provider2.displayName || provider2.name} is not installed.
|
|
8555
|
+
Command '${spawnCmd}' not found in PATH.
|
|
8556
|
+
|
|
8557
|
+
${installInfo}`
|
|
8558
|
+
);
|
|
8559
|
+
}
|
|
8560
|
+
}
|
|
7830
8561
|
console.log(import_chalk.default.cyan(` \u{1F50C} Starting ACP agent: ${provider2.name} (${provider2.type}) in ${resolvedDir}`));
|
|
7831
8562
|
const acpInstance = new AcpProviderInstance(provider2, resolvedDir, cliArgs);
|
|
7832
8563
|
await instanceManager2.addInstance(key, acpInstance, {
|
|
@@ -7835,6 +8566,7 @@ var init_daemon_cli = __esm({
|
|
|
7835
8566
|
this.adapters.set(key, {
|
|
7836
8567
|
cliType: normalizedType,
|
|
7837
8568
|
workingDir: resolvedDir,
|
|
8569
|
+
_acpInstance: acpInstance,
|
|
7838
8570
|
spawn: async () => {
|
|
7839
8571
|
},
|
|
7840
8572
|
shutdown: () => {
|
|
@@ -7857,6 +8589,14 @@ var init_daemon_cli = __esm({
|
|
|
7857
8589
|
}
|
|
7858
8590
|
});
|
|
7859
8591
|
console.log(import_chalk.default.green(` \u2713 ACP agent started: ${provider2.name} in ${resolvedDir}`));
|
|
8592
|
+
if (initialModel) {
|
|
8593
|
+
try {
|
|
8594
|
+
await acpInstance.setConfigOption("model", initialModel);
|
|
8595
|
+
console.log(import_chalk.default.green(` \u{1F916} Initial model set: ${initialModel}`));
|
|
8596
|
+
} catch (e) {
|
|
8597
|
+
console.warn(`[ACP] Initial model set failed: ${e?.message}`);
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
7860
8600
|
try {
|
|
7861
8601
|
addCliHistory({ cliType: normalizedType, dir: resolvedDir, cliArgs });
|
|
7862
8602
|
} catch (e) {
|
|
@@ -7865,7 +8605,7 @@ var init_daemon_cli = __esm({
|
|
|
7865
8605
|
this.deps.onStatusChange();
|
|
7866
8606
|
return;
|
|
7867
8607
|
}
|
|
7868
|
-
const cliInfo = await detectCLI(cliType);
|
|
8608
|
+
const cliInfo = await detectCLI(cliType, this.providerLoader);
|
|
7869
8609
|
if (!cliInfo) throw new Error(`${cliType} not found`);
|
|
7870
8610
|
console.log(import_chalk.default.yellow(` \u26A1 Starting CLI ${cliType} in ${resolvedDir}...`));
|
|
7871
8611
|
if (provider2) {
|
|
@@ -7873,9 +8613,9 @@ var init_daemon_cli = __esm({
|
|
|
7873
8613
|
}
|
|
7874
8614
|
const instanceManager = this.deps.getInstanceManager();
|
|
7875
8615
|
if (provider2 && instanceManager) {
|
|
7876
|
-
const cliInstance = new CliProviderInstance(provider2, resolvedDir, cliArgs);
|
|
8616
|
+
const cliInstance = new CliProviderInstance(provider2, resolvedDir, cliArgs, key);
|
|
7877
8617
|
await instanceManager.addInstance(key, cliInstance, {
|
|
7878
|
-
|
|
8618
|
+
serverConn: this.deps.getServerConn(),
|
|
7879
8619
|
settings: {},
|
|
7880
8620
|
onPtyData: (data) => {
|
|
7881
8621
|
this.deps.getP2p()?.broadcastPtyOutput(key, data);
|
|
@@ -7886,9 +8626,9 @@ var init_daemon_cli = __esm({
|
|
|
7886
8626
|
} else {
|
|
7887
8627
|
const adapter = this.createAdapter(cliType, resolvedDir, cliArgs);
|
|
7888
8628
|
await adapter.spawn();
|
|
7889
|
-
const
|
|
7890
|
-
if (
|
|
7891
|
-
adapter.
|
|
8629
|
+
const serverConn = this.deps.getServerConn();
|
|
8630
|
+
if (serverConn && typeof adapter.setServerConn === "function") {
|
|
8631
|
+
adapter.setServerConn(serverConn);
|
|
7892
8632
|
}
|
|
7893
8633
|
adapter.setOnStatusChange(() => {
|
|
7894
8634
|
this.deps.onStatusChange();
|
|
@@ -7935,14 +8675,29 @@ var init_daemon_cli = __esm({
|
|
|
7935
8675
|
this.adapters.clear();
|
|
7936
8676
|
}
|
|
7937
8677
|
// ─── Adapter 검색 ─────────────────────────────
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
8678
|
+
/**
|
|
8679
|
+
* CLI adapter를 검색합니다. 우선순위:
|
|
8680
|
+
* 0. instanceKey (UUID direct match) — _targetInstance / composite ID에서 추출
|
|
8681
|
+
* 1. agentType + dir (iteration match)
|
|
8682
|
+
* 2. agentType fuzzy match (⚠ 다중 세션 시 첫 번째 반환)
|
|
8683
|
+
*/
|
|
8684
|
+
findAdapter(agentType, opts) {
|
|
8685
|
+
if (opts?.instanceKey) {
|
|
8686
|
+
let ik = opts.instanceKey;
|
|
8687
|
+
const colonIdx = ik.lastIndexOf(":");
|
|
8688
|
+
if (colonIdx >= 0) ik = ik.substring(colonIdx + 1);
|
|
8689
|
+
const adapter = this.adapters.get(ik);
|
|
8690
|
+
if (adapter) return { adapter, key: ik };
|
|
8691
|
+
}
|
|
8692
|
+
if (opts?.dir) {
|
|
8693
|
+
for (const [k, a] of this.adapters) {
|
|
8694
|
+
if (a.cliType === agentType && a.workingDir === opts.dir) {
|
|
8695
|
+
return { adapter: a, key: k };
|
|
8696
|
+
}
|
|
8697
|
+
}
|
|
7943
8698
|
}
|
|
7944
8699
|
for (const [k, a] of this.adapters) {
|
|
7945
|
-
if (a.cliType === agentType
|
|
8700
|
+
if (a.cliType === agentType) {
|
|
7946
8701
|
return { adapter: a, key: k };
|
|
7947
8702
|
}
|
|
7948
8703
|
}
|
|
@@ -7954,43 +8709,38 @@ var init_daemon_cli = __esm({
|
|
|
7954
8709
|
case "launch_cli": {
|
|
7955
8710
|
const cliType = args?.cliType;
|
|
7956
8711
|
const defaultedToHome = !args?.dir;
|
|
7957
|
-
const dir = args?.dir ||
|
|
8712
|
+
const dir = args?.dir || os11.homedir();
|
|
7958
8713
|
if (!cliType) throw new Error("cliType required");
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
console.log(import_chalk.default.cyan(` \u{1F4C2} Saving recent workspace: ${dir}`));
|
|
7965
|
-
const recent = config.recentCliWorkspaces || [];
|
|
7966
|
-
if (!recent.includes(dir)) {
|
|
7967
|
-
const updated = [dir, ...recent].slice(0, 10);
|
|
7968
|
-
saveConfig({ ...config, recentCliWorkspaces: updated });
|
|
7969
|
-
console.log(import_chalk.default.green(` \u2713 Recent workspace saved: ${dir}`));
|
|
7970
|
-
}
|
|
7971
|
-
} catch (e) {
|
|
7972
|
-
console.error(import_chalk.default.red(` \u2717 Failed to save recent workspace: ${e}`));
|
|
8714
|
+
await this.startSession(cliType, dir, args?.cliArgs, args?.initialModel);
|
|
8715
|
+
let newKey = null;
|
|
8716
|
+
for (const [k, adapter] of this.adapters) {
|
|
8717
|
+
if (adapter.cliType === cliType && adapter.workingDir === dir) {
|
|
8718
|
+
newKey = k;
|
|
7973
8719
|
}
|
|
7974
8720
|
}
|
|
7975
|
-
|
|
8721
|
+
try {
|
|
8722
|
+
const config = loadConfig();
|
|
8723
|
+
console.log(import_chalk.default.cyan(` \u{1F4C2} Saving recent workspace: ${dir}`));
|
|
8724
|
+
const recent = config.recentCliWorkspaces || [];
|
|
8725
|
+
if (!recent.includes(dir)) {
|
|
8726
|
+
const updated = [dir, ...recent].slice(0, 10);
|
|
8727
|
+
saveConfig({ ...config, recentCliWorkspaces: updated });
|
|
8728
|
+
console.log(import_chalk.default.green(` \u2713 Recent workspace saved: ${dir}`));
|
|
8729
|
+
}
|
|
8730
|
+
} catch (e) {
|
|
8731
|
+
console.error(import_chalk.default.red(` \u2717 Failed to save recent workspace: ${e}`));
|
|
8732
|
+
}
|
|
8733
|
+
return { success: true, cliType, dir, id: newKey, defaultedToHome };
|
|
7976
8734
|
}
|
|
7977
8735
|
case "stop_cli": {
|
|
7978
8736
|
const cliType = args?.cliType;
|
|
7979
8737
|
const dir = args?.dir || "";
|
|
7980
8738
|
if (!cliType) throw new Error("cliType required");
|
|
7981
|
-
const
|
|
7982
|
-
if (
|
|
7983
|
-
await this.stopSession(key);
|
|
8739
|
+
const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
|
|
8740
|
+
if (found) {
|
|
8741
|
+
await this.stopSession(found.key);
|
|
7984
8742
|
} else {
|
|
7985
|
-
|
|
7986
|
-
for (const [k, adapter] of this.adapters) {
|
|
7987
|
-
if (adapter.cliType === cliType) {
|
|
7988
|
-
await this.stopSession(k);
|
|
7989
|
-
found = true;
|
|
7990
|
-
break;
|
|
7991
|
-
}
|
|
7992
|
-
}
|
|
7993
|
-
if (!found) console.log(import_chalk.default.yellow(` \u26A0 No adapter found for ${cliType} (key: ${key})`));
|
|
8743
|
+
console.log(import_chalk.default.yellow(` \u26A0 No adapter found for ${cliType}`));
|
|
7994
8744
|
}
|
|
7995
8745
|
return { success: true, cliType, dir, stopped: true };
|
|
7996
8746
|
}
|
|
@@ -7998,8 +8748,8 @@ var init_daemon_cli = __esm({
|
|
|
7998
8748
|
const cliType = args?.cliType || args?.agentType || args?.ideType;
|
|
7999
8749
|
const dir = args?.dir || process.cwd();
|
|
8000
8750
|
if (!cliType) throw new Error("cliType required");
|
|
8001
|
-
const
|
|
8002
|
-
await this.stopSession(key);
|
|
8751
|
+
const found = this.findAdapter(cliType, { instanceKey: args?._targetInstance, dir });
|
|
8752
|
+
if (found) await this.stopSession(found.key);
|
|
8003
8753
|
await this.startSession(cliType, dir);
|
|
8004
8754
|
return { success: true, restarted: true };
|
|
8005
8755
|
}
|
|
@@ -8007,7 +8757,10 @@ var init_daemon_cli = __esm({
|
|
|
8007
8757
|
const agentType = args?.agentType || args?.cliType;
|
|
8008
8758
|
const action = args?.action;
|
|
8009
8759
|
if (!agentType || !action) throw new Error("agentType and action required");
|
|
8010
|
-
const found = this.findAdapter(agentType,
|
|
8760
|
+
const found = this.findAdapter(agentType, {
|
|
8761
|
+
dir: args?.dir,
|
|
8762
|
+
instanceKey: args?._targetInstance
|
|
8763
|
+
});
|
|
8011
8764
|
if (!found) throw new Error(`CLI agent not running: ${agentType}`);
|
|
8012
8765
|
const { adapter, key } = found;
|
|
8013
8766
|
if (action === "send_chat") {
|
|
@@ -8203,7 +8956,7 @@ var init_extension_provider_instance = __esm({
|
|
|
8203
8956
|
constructor(provider2) {
|
|
8204
8957
|
this.type = provider2.type;
|
|
8205
8958
|
this.provider = provider2;
|
|
8206
|
-
this.instanceId =
|
|
8959
|
+
this.instanceId = crypto.randomUUID();
|
|
8207
8960
|
this.monitor = new StatusMonitor();
|
|
8208
8961
|
}
|
|
8209
8962
|
// ─── Lifecycle ──────────────────────────────────
|
|
@@ -8251,7 +9004,6 @@ var init_extension_provider_instance = __esm({
|
|
|
8251
9004
|
}
|
|
8252
9005
|
} else if (event === "extension_connected") {
|
|
8253
9006
|
this.ideType = data?.ideType || "";
|
|
8254
|
-
this.instanceId = `ext_${this.type}_${data?.instanceId || ""}`;
|
|
8255
9007
|
}
|
|
8256
9008
|
}
|
|
8257
9009
|
dispose() {
|
|
@@ -8259,6 +9011,10 @@ var init_extension_provider_instance = __esm({
|
|
|
8259
9011
|
this.messages = [];
|
|
8260
9012
|
this.monitor.reset();
|
|
8261
9013
|
}
|
|
9014
|
+
/** UUID instanceId 조회 */
|
|
9015
|
+
getInstanceId() {
|
|
9016
|
+
return this.instanceId;
|
|
9017
|
+
}
|
|
8262
9018
|
// ─── 상태 전이 감지 ──────────────────────────────
|
|
8263
9019
|
detectTransition(newStatus, data) {
|
|
8264
9020
|
const now = Date.now();
|
|
@@ -8298,14 +9054,14 @@ var init_extension_provider_instance = __esm({
|
|
|
8298
9054
|
});
|
|
8299
9055
|
|
|
8300
9056
|
// src/providers/ide-provider-instance.ts
|
|
8301
|
-
var
|
|
9057
|
+
var crypto5, IdeProviderInstance;
|
|
8302
9058
|
var init_ide_provider_instance = __esm({
|
|
8303
9059
|
"src/providers/ide-provider-instance.ts"() {
|
|
8304
9060
|
"use strict";
|
|
8305
|
-
|
|
8306
|
-
crypto2 = __toESM(require("crypto"));
|
|
9061
|
+
crypto5 = __toESM(require("crypto"));
|
|
8307
9062
|
init_extension_provider_instance();
|
|
8308
9063
|
init_status_monitor();
|
|
9064
|
+
init_chat_history();
|
|
8309
9065
|
IdeProviderInstance = class {
|
|
8310
9066
|
type;
|
|
8311
9067
|
category = "ide";
|
|
@@ -8321,6 +9077,7 @@ var init_ide_provider_instance = __esm({
|
|
|
8321
9077
|
generatingStartedAt = /* @__PURE__ */ new Map();
|
|
8322
9078
|
tickBusy = false;
|
|
8323
9079
|
monitor;
|
|
9080
|
+
historyWriter;
|
|
8324
9081
|
// IDE 메타
|
|
8325
9082
|
ideVersion = "";
|
|
8326
9083
|
instanceId;
|
|
@@ -8331,8 +9088,9 @@ var init_ide_provider_instance = __esm({
|
|
|
8331
9088
|
constructor(provider2, instanceKey) {
|
|
8332
9089
|
this.type = provider2.type;
|
|
8333
9090
|
this.provider = provider2;
|
|
8334
|
-
this.instanceId =
|
|
9091
|
+
this.instanceId = crypto5.randomUUID();
|
|
8335
9092
|
this.monitor = new StatusMonitor();
|
|
9093
|
+
this.historyWriter = new ChatHistoryWriter();
|
|
8336
9094
|
}
|
|
8337
9095
|
// ─── Lifecycle ─────────────────────────────────
|
|
8338
9096
|
async init(context) {
|
|
@@ -8432,7 +9190,7 @@ var init_ide_provider_instance = __esm({
|
|
|
8432
9190
|
const ext = new ExtensionProviderInstance(provider2);
|
|
8433
9191
|
await ext.init({
|
|
8434
9192
|
cdp: this.context?.cdp,
|
|
8435
|
-
|
|
9193
|
+
serverConn: this.context?.serverConn,
|
|
8436
9194
|
settings: settings || {}
|
|
8437
9195
|
});
|
|
8438
9196
|
ext.onEvent("extension_connected", { ideType: this.type });
|
|
@@ -8455,6 +9213,14 @@ var init_ide_provider_instance = __esm({
|
|
|
8455
9213
|
getExtensionTypes() {
|
|
8456
9214
|
return [...this.extensions.keys()];
|
|
8457
9215
|
}
|
|
9216
|
+
/** UUID instanceId 조회 */
|
|
9217
|
+
getInstanceId() {
|
|
9218
|
+
return this.instanceId;
|
|
9219
|
+
}
|
|
9220
|
+
/** 모든 Extension Instance 목록 */
|
|
9221
|
+
getExtensionInstances() {
|
|
9222
|
+
return [...this.extensions.values()];
|
|
9223
|
+
}
|
|
8458
9224
|
// ─── CDP readChat ───────────────────────────────
|
|
8459
9225
|
async readChat() {
|
|
8460
9226
|
const { cdp: cdp2 } = this.context;
|
|
@@ -8518,6 +9284,23 @@ var init_ide_provider_instance = __esm({
|
|
|
8518
9284
|
}
|
|
8519
9285
|
this.cachedChat = { ...raw, activeModal };
|
|
8520
9286
|
this.detectAgentTransitions(raw, now);
|
|
9287
|
+
if (raw.messages?.length > 0) {
|
|
9288
|
+
let toSave = raw.messages;
|
|
9289
|
+
if (raw.status === "generating" || raw.status === "long_generating") {
|
|
9290
|
+
const lastIdx = toSave.length - 1;
|
|
9291
|
+
if (lastIdx >= 0 && toSave[lastIdx].role === "assistant") {
|
|
9292
|
+
toSave = toSave.slice(0, lastIdx);
|
|
9293
|
+
}
|
|
9294
|
+
}
|
|
9295
|
+
if (toSave.length > 0) {
|
|
9296
|
+
this.historyWriter.appendNewMessages(
|
|
9297
|
+
this.type,
|
|
9298
|
+
toSave,
|
|
9299
|
+
raw.title,
|
|
9300
|
+
this.instanceId
|
|
9301
|
+
);
|
|
9302
|
+
}
|
|
9303
|
+
}
|
|
8521
9304
|
} catch (e) {
|
|
8522
9305
|
const msg = e?.message || String(e);
|
|
8523
9306
|
if (msg.includes("Timeout") || msg.includes("timeout") || msg.includes("Target closed")) {
|
|
@@ -8551,7 +9334,8 @@ var init_ide_provider_instance = __esm({
|
|
|
8551
9334
|
chatTitle,
|
|
8552
9335
|
timestamp: now,
|
|
8553
9336
|
ideType: this.type,
|
|
8554
|
-
modalMessage: chatData.activeModal?.message
|
|
9337
|
+
modalMessage: chatData.activeModal?.message,
|
|
9338
|
+
modalButtons: chatData.activeModal?.buttons
|
|
8555
9339
|
});
|
|
8556
9340
|
} else if (agentStatus === "idle" && (lastStatus === "generating" || lastStatus === "waiting_approval")) {
|
|
8557
9341
|
const startedAt = this.generatingStartedAt.get(agentKey);
|
|
@@ -8592,23 +9376,23 @@ __export(adhdev_daemon_exports, {
|
|
|
8592
9376
|
});
|
|
8593
9377
|
function getDaemonPidFile() {
|
|
8594
9378
|
const dir = path11.join(os12.homedir(), ".adhdev");
|
|
8595
|
-
if (!
|
|
9379
|
+
if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
|
|
8596
9380
|
return path11.join(dir, "daemon.pid");
|
|
8597
9381
|
}
|
|
8598
9382
|
function writeDaemonPid(pid) {
|
|
8599
|
-
|
|
9383
|
+
fs7.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
|
|
8600
9384
|
}
|
|
8601
9385
|
function removeDaemonPid() {
|
|
8602
9386
|
try {
|
|
8603
|
-
|
|
9387
|
+
fs7.unlinkSync(getDaemonPidFile());
|
|
8604
9388
|
} catch (e) {
|
|
8605
9389
|
}
|
|
8606
9390
|
}
|
|
8607
9391
|
function isDaemonRunning() {
|
|
8608
9392
|
const pidFile = getDaemonPidFile();
|
|
8609
9393
|
try {
|
|
8610
|
-
if (!
|
|
8611
|
-
const pid = parseInt(
|
|
9394
|
+
if (!fs7.existsSync(pidFile)) return false;
|
|
9395
|
+
const pid = parseInt(fs7.readFileSync(pidFile, "utf-8").trim());
|
|
8612
9396
|
process.kill(pid, 0);
|
|
8613
9397
|
return true;
|
|
8614
9398
|
} catch {
|
|
@@ -8619,8 +9403,8 @@ function isDaemonRunning() {
|
|
|
8619
9403
|
function stopDaemon() {
|
|
8620
9404
|
const pidFile = getDaemonPidFile();
|
|
8621
9405
|
try {
|
|
8622
|
-
if (!
|
|
8623
|
-
const pid = parseInt(
|
|
9406
|
+
if (!fs7.existsSync(pidFile)) return false;
|
|
9407
|
+
const pid = parseInt(fs7.readFileSync(pidFile, "utf-8").trim());
|
|
8624
9408
|
process.kill(pid, "SIGTERM");
|
|
8625
9409
|
removeDaemonPid();
|
|
8626
9410
|
return true;
|
|
@@ -8629,11 +9413,11 @@ function stopDaemon() {
|
|
|
8629
9413
|
return false;
|
|
8630
9414
|
}
|
|
8631
9415
|
}
|
|
8632
|
-
var os12,
|
|
9416
|
+
var os12, fs7, path11, crypto6, import_chalk2, DANGEROUS_PATTERNS, AdhdevDaemon;
|
|
8633
9417
|
var init_adhdev_daemon = __esm({
|
|
8634
9418
|
"src/adhdev-daemon.ts"() {
|
|
8635
9419
|
"use strict";
|
|
8636
|
-
|
|
9420
|
+
init_server_connection();
|
|
8637
9421
|
init_local_server();
|
|
8638
9422
|
init_config();
|
|
8639
9423
|
init_detector();
|
|
@@ -8651,9 +9435,9 @@ var init_adhdev_daemon = __esm({
|
|
|
8651
9435
|
init_ipc_protocol();
|
|
8652
9436
|
init_daemon_logger();
|
|
8653
9437
|
os12 = __toESM(require("os"));
|
|
8654
|
-
|
|
9438
|
+
fs7 = __toESM(require("fs"));
|
|
8655
9439
|
path11 = __toESM(require("path"));
|
|
8656
|
-
|
|
9440
|
+
crypto6 = __toESM(require("crypto"));
|
|
8657
9441
|
import_chalk2 = __toESM(require("chalk"));
|
|
8658
9442
|
DANGEROUS_PATTERNS = [
|
|
8659
9443
|
/\brm\s+(-[a-z]*f|-[a-z]*r|--force|--recursive)/i,
|
|
@@ -8669,7 +9453,7 @@ var init_adhdev_daemon = __esm({
|
|
|
8669
9453
|
];
|
|
8670
9454
|
AdhdevDaemon = class {
|
|
8671
9455
|
localServer = null;
|
|
8672
|
-
|
|
9456
|
+
serverConn = null;
|
|
8673
9457
|
cliManager;
|
|
8674
9458
|
cdpManagers = /* @__PURE__ */ new Map();
|
|
8675
9459
|
cdpDiscoveryTimer = null;
|
|
@@ -8685,13 +9469,23 @@ var init_adhdev_daemon = __esm({
|
|
|
8685
9469
|
detectedIdes = [];
|
|
8686
9470
|
localPort;
|
|
8687
9471
|
ideType = "unknown";
|
|
9472
|
+
/** UUID instanceId → CDP manager key (ideType) 매핑 */
|
|
9473
|
+
instanceIdMap = /* @__PURE__ */ new Map();
|
|
8688
9474
|
constructor() {
|
|
8689
9475
|
this.localPort = 19222;
|
|
8690
9476
|
this.providerLoader = new ProviderLoader();
|
|
8691
9477
|
this.providerLoader.loadAll();
|
|
9478
|
+
this.providerLoader.fetchLatest().then(({ updated }) => {
|
|
9479
|
+
if (updated) {
|
|
9480
|
+
this.providerLoader.reload();
|
|
9481
|
+
this.providerLoader.registerToDetector();
|
|
9482
|
+
console.log("[Daemon] Providers auto-updated from upstream");
|
|
9483
|
+
}
|
|
9484
|
+
}).catch(() => {
|
|
9485
|
+
});
|
|
8692
9486
|
this.instanceManager = new ProviderInstanceManager();
|
|
8693
9487
|
this.cliManager = new DaemonCliManager({
|
|
8694
|
-
|
|
9488
|
+
getServerConn: () => this.serverConn,
|
|
8695
9489
|
getP2p: () => this.p2p,
|
|
8696
9490
|
onStatusChange: () => this.statusReporter?.onStatusChange(),
|
|
8697
9491
|
removeAgentTracking: (key) => this.statusReporter?.removeAgentTracking(key),
|
|
@@ -8724,7 +9518,7 @@ var init_adhdev_daemon = __esm({
|
|
|
8724
9518
|
onExtensionConnected: (ext) => {
|
|
8725
9519
|
console.log(import_chalk2.default.green(` \u{1F4CE} Extension connected: ${ext.ideType} v${ext.ideVersion}`));
|
|
8726
9520
|
this.localServer?.notifyServerState(
|
|
8727
|
-
this.
|
|
9521
|
+
this.serverConn?.isConnected() || false,
|
|
8728
9522
|
config.serverUrl
|
|
8729
9523
|
);
|
|
8730
9524
|
setTimeout(() => this.statusReporter?.throttledReport(), 500);
|
|
@@ -8742,10 +9536,10 @@ var init_adhdev_daemon = __esm({
|
|
|
8742
9536
|
const cfg = loadConfig();
|
|
8743
9537
|
cfg.connectionToken = data.token;
|
|
8744
9538
|
saveConfig(cfg);
|
|
8745
|
-
if (this.
|
|
8746
|
-
this.
|
|
8747
|
-
this.
|
|
8748
|
-
this.
|
|
9539
|
+
if (this.serverConn) {
|
|
9540
|
+
this.serverConn.disconnect();
|
|
9541
|
+
this.serverConn.token = data.token;
|
|
9542
|
+
this.serverConn.connect().catch((e) => {
|
|
8749
9543
|
console.error(import_chalk2.default.red(` \u2717 Reconnect failed: ${e.message}`));
|
|
8750
9544
|
});
|
|
8751
9545
|
}
|
|
@@ -8778,7 +9572,8 @@ var init_adhdev_daemon = __esm({
|
|
|
8778
9572
|
localServer: this.localServer,
|
|
8779
9573
|
ideType: this.ideType,
|
|
8780
9574
|
adapters: this.cliManager.adapters,
|
|
8781
|
-
providerLoader: this.providerLoader
|
|
9575
|
+
providerLoader: this.providerLoader,
|
|
9576
|
+
instanceIdMap: this.instanceIdMap
|
|
8782
9577
|
});
|
|
8783
9578
|
this.agentStreamManager = new DaemonAgentStreamManager(
|
|
8784
9579
|
console.log,
|
|
@@ -8788,9 +9583,9 @@ var init_adhdev_daemon = __esm({
|
|
|
8788
9583
|
this.commandHandler.setAgentStreamManager(this.agentStreamManager);
|
|
8789
9584
|
this.startAgentStreamPolling();
|
|
8790
9585
|
const machineId = os12.hostname().replace(/[^a-zA-Z0-9]/g, "_");
|
|
8791
|
-
const machineHash =
|
|
9586
|
+
const machineHash = crypto6.createHash("md5").update(os12.hostname() + os12.homedir()).digest("hex").slice(0, 8);
|
|
8792
9587
|
const instanceId = `daemon_${machineId}_${machineHash}`;
|
|
8793
|
-
this.
|
|
9588
|
+
this.serverConn = new ServerConnection({
|
|
8794
9589
|
serverUrl: options.serverUrl || config.serverUrl,
|
|
8795
9590
|
token: config.connectionToken,
|
|
8796
9591
|
cliInfo: {
|
|
@@ -8800,7 +9595,7 @@ var init_adhdev_daemon = __esm({
|
|
|
8800
9595
|
instanceId
|
|
8801
9596
|
}
|
|
8802
9597
|
});
|
|
8803
|
-
this.p2p = new DaemonP2PSender(this.
|
|
9598
|
+
this.p2p = new DaemonP2PSender(this.serverConn);
|
|
8804
9599
|
if (this.p2p.isAvailable) {
|
|
8805
9600
|
console.log(import_chalk2.default.green(" \u{1F517} P2P available (node-datachannel)"));
|
|
8806
9601
|
this.p2p.onInput(async (event) => {
|
|
@@ -8823,29 +9618,15 @@ var init_adhdev_daemon = __esm({
|
|
|
8823
9618
|
}
|
|
8824
9619
|
});
|
|
8825
9620
|
this.p2p.onPtyInput((cliId, data) => {
|
|
8826
|
-
const
|
|
8827
|
-
if (
|
|
8828
|
-
|
|
8829
|
-
return;
|
|
8830
|
-
}
|
|
8831
|
-
for (const [, adapter] of this.cliManager.adapters) {
|
|
8832
|
-
if (adapter.cliType === cliId && typeof adapter.writeRaw === "function") {
|
|
8833
|
-
adapter.writeRaw(data);
|
|
8834
|
-
return;
|
|
8835
|
-
}
|
|
9621
|
+
const found = this.cliManager.findAdapter(cliId, { instanceKey: cliId });
|
|
9622
|
+
if (found && typeof found.adapter.writeRaw === "function") {
|
|
9623
|
+
found.adapter.writeRaw(data);
|
|
8836
9624
|
}
|
|
8837
9625
|
});
|
|
8838
9626
|
this.p2p.onPtyResize((cliId, cols, rows) => {
|
|
8839
|
-
const
|
|
8840
|
-
if (
|
|
8841
|
-
|
|
8842
|
-
return;
|
|
8843
|
-
}
|
|
8844
|
-
for (const [, adapter] of this.cliManager.adapters) {
|
|
8845
|
-
if (adapter.cliType === cliId && typeof adapter.resize === "function") {
|
|
8846
|
-
adapter.resize(cols, rows);
|
|
8847
|
-
return;
|
|
8848
|
-
}
|
|
9627
|
+
const found = this.cliManager.findAdapter(cliId, { instanceKey: cliId });
|
|
9628
|
+
if (found && typeof found.adapter.resize === "function") {
|
|
9629
|
+
found.adapter.resize(cols, rows);
|
|
8849
9630
|
}
|
|
8850
9631
|
});
|
|
8851
9632
|
this.p2p.onStateChange((state) => {
|
|
@@ -8855,32 +9636,79 @@ var init_adhdev_daemon = __esm({
|
|
|
8855
9636
|
}
|
|
8856
9637
|
});
|
|
8857
9638
|
let ssDebugCount = 0;
|
|
8858
|
-
|
|
9639
|
+
let lastScreenshotSize = 0;
|
|
9640
|
+
let lastScreenshotHash = 0;
|
|
9641
|
+
let staticFrameCount = 0;
|
|
9642
|
+
let currentInterval = 2e3;
|
|
9643
|
+
const MIN_INTERVAL = 300;
|
|
9644
|
+
const MAX_INTERVAL = 2e3;
|
|
9645
|
+
const STATIC_THRESHOLD = 3;
|
|
9646
|
+
const fnvHash = (buf) => {
|
|
9647
|
+
let h = 2166136261;
|
|
9648
|
+
const sampleEnd = Math.min(1024, buf.length);
|
|
9649
|
+
for (let i = 0; i < sampleEnd; i++) {
|
|
9650
|
+
h ^= buf[i];
|
|
9651
|
+
h = h * 16777619 >>> 0;
|
|
9652
|
+
}
|
|
9653
|
+
if (buf.length > 1024) {
|
|
9654
|
+
const start = Math.max(0, buf.length - 1024);
|
|
9655
|
+
for (let i = start; i < buf.length; i++) {
|
|
9656
|
+
h ^= buf[i];
|
|
9657
|
+
h = h * 16777619 >>> 0;
|
|
9658
|
+
}
|
|
9659
|
+
}
|
|
9660
|
+
return h;
|
|
9661
|
+
};
|
|
9662
|
+
const screenshotTick = async () => {
|
|
9663
|
+
if (!this.running) return;
|
|
8859
9664
|
const active = this.p2p?.screenshotActive;
|
|
8860
9665
|
const ssIdeType = this.p2p?.screenshotIdeType;
|
|
8861
9666
|
const cdp2 = ssIdeType ? this.getCdpFor(ssIdeType) : this.getAnyCdp();
|
|
8862
|
-
if (!active || !cdp2)
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
9667
|
+
if (!active || !cdp2) {
|
|
9668
|
+
staticFrameCount = 0;
|
|
9669
|
+
currentInterval = MAX_INTERVAL;
|
|
9670
|
+
this.screenshotTimer = setTimeout(screenshotTick, currentInterval);
|
|
9671
|
+
return;
|
|
8866
9672
|
}
|
|
9673
|
+
ssDebugCount++;
|
|
8867
9674
|
try {
|
|
8868
9675
|
const buf = await cdp2.captureScreenshot();
|
|
8869
9676
|
if (buf) {
|
|
8870
|
-
const
|
|
8871
|
-
|
|
9677
|
+
const hash = fnvHash(buf);
|
|
9678
|
+
const sizeMatch = buf.length === lastScreenshotSize;
|
|
9679
|
+
const hashMatch = hash === lastScreenshotHash;
|
|
9680
|
+
if (sizeMatch && hashMatch) {
|
|
9681
|
+
staticFrameCount++;
|
|
9682
|
+
if (staticFrameCount >= STATIC_THRESHOLD) {
|
|
9683
|
+
currentInterval = Math.min(currentInterval + 200, MAX_INTERVAL);
|
|
9684
|
+
}
|
|
9685
|
+
if (ssDebugCount <= 5 || ssDebugCount % 50 === 0) {
|
|
9686
|
+
LOG.debug("Screenshot", `skip (unchanged, static=${staticFrameCount}, interval=${currentInterval}ms)`);
|
|
9687
|
+
}
|
|
9688
|
+
} else {
|
|
9689
|
+
lastScreenshotSize = buf.length;
|
|
9690
|
+
lastScreenshotHash = hash;
|
|
9691
|
+
staticFrameCount = 0;
|
|
9692
|
+
currentInterval = MIN_INTERVAL;
|
|
9693
|
+
const sent = this.p2p.sendScreenshotBuffer(buf);
|
|
9694
|
+
if (ssDebugCount <= 3) {
|
|
9695
|
+
LOG.debug("Screenshot", `sent: ${buf.length} bytes, delivered=${sent}, interval=${currentInterval}ms`);
|
|
9696
|
+
}
|
|
9697
|
+
}
|
|
8872
9698
|
} else {
|
|
8873
9699
|
if (ssDebugCount <= 5) LOG.debug("Screenshot", "captureScreenshot returned null");
|
|
8874
9700
|
}
|
|
8875
9701
|
} catch (e) {
|
|
8876
9702
|
if (ssDebugCount <= 5) LOG.warn("Screenshot", `error: ${e?.message}`);
|
|
8877
9703
|
}
|
|
8878
|
-
|
|
9704
|
+
this.screenshotTimer = setTimeout(screenshotTick, currentInterval);
|
|
9705
|
+
};
|
|
9706
|
+
this.screenshotTimer = setTimeout(screenshotTick, 1e3);
|
|
8879
9707
|
} else {
|
|
8880
9708
|
console.log(import_chalk2.default.gray(" \u26A0 P2P unavailable \u2014 using server relay"));
|
|
8881
9709
|
}
|
|
8882
9710
|
this.registerServerHandlers();
|
|
8883
|
-
this.
|
|
9711
|
+
this.serverConn.onStateChange((state) => {
|
|
8884
9712
|
if (state === "connected") {
|
|
8885
9713
|
console.log(import_chalk2.default.green(" \u{1F4E1} Connected to ADHDev server"));
|
|
8886
9714
|
this.localServer?.notifyServerState(true, config.serverUrl);
|
|
@@ -8890,9 +9718,9 @@ var init_adhdev_daemon = __esm({
|
|
|
8890
9718
|
this.localServer?.notifyServerState(false, config.serverUrl);
|
|
8891
9719
|
}
|
|
8892
9720
|
});
|
|
8893
|
-
await this.
|
|
9721
|
+
await this.serverConn.connect();
|
|
8894
9722
|
this.statusReporter = new DaemonStatusReporter({
|
|
8895
|
-
|
|
9723
|
+
serverConn: this.serverConn,
|
|
8896
9724
|
cdpManagers: this.cdpManagers,
|
|
8897
9725
|
p2p: this.p2p,
|
|
8898
9726
|
providerLoader: this.providerLoader,
|
|
@@ -8935,21 +9763,21 @@ var init_adhdev_daemon = __esm({
|
|
|
8935
9763
|
}
|
|
8936
9764
|
// ─── 서버 명령 핸들러 ───────────────────────────
|
|
8937
9765
|
registerServerHandlers() {
|
|
8938
|
-
if (!this.
|
|
8939
|
-
this.
|
|
9766
|
+
if (!this.serverConn) return;
|
|
9767
|
+
this.serverConn.on("command", async (msg) => {
|
|
8940
9768
|
const cmd = msg.payload.command;
|
|
8941
9769
|
const args = msg.payload.args;
|
|
8942
9770
|
await this.handleCommand(msg, cmd, args);
|
|
8943
9771
|
});
|
|
8944
|
-
this.
|
|
9772
|
+
this.serverConn.on("agent_command", async (msg) => {
|
|
8945
9773
|
const payload = msg.payload;
|
|
8946
9774
|
await this.handleCommand(msg, payload.action, payload);
|
|
8947
9775
|
});
|
|
8948
|
-
this.
|
|
9776
|
+
this.serverConn.on("resolve_action", async (msg) => {
|
|
8949
9777
|
await this.handleCommand(msg, "resolve_action", msg.payload);
|
|
8950
9778
|
});
|
|
8951
9779
|
for (const sigType of ["p2p_ready", "p2p_offer", "p2p_answer", "p2p_ice"]) {
|
|
8952
|
-
this.
|
|
9780
|
+
this.serverConn.on(sigType, (msg) => {
|
|
8953
9781
|
if (this.p2p) this.p2p.handleSignaling(sigType, msg.payload);
|
|
8954
9782
|
});
|
|
8955
9783
|
}
|
|
@@ -8961,6 +9789,7 @@ var init_adhdev_daemon = __esm({
|
|
|
8961
9789
|
"switch_chat",
|
|
8962
9790
|
"set_mode",
|
|
8963
9791
|
"change_model",
|
|
9792
|
+
"set_thought_level",
|
|
8964
9793
|
"screenshot",
|
|
8965
9794
|
"cdp_eval",
|
|
8966
9795
|
"cdp_screenshot",
|
|
@@ -8974,8 +9803,10 @@ var init_adhdev_daemon = __esm({
|
|
|
8974
9803
|
"file_list_browse",
|
|
8975
9804
|
"terminal_exec",
|
|
8976
9805
|
"refresh_scripts",
|
|
8977
|
-
// CLI/daemon-local commands (launch, restart, detect)
|
|
9806
|
+
// CLI/daemon-local commands (launch, restart, detect, stop)
|
|
8978
9807
|
"launch_ide",
|
|
9808
|
+
"stop_ide",
|
|
9809
|
+
"restart_ide",
|
|
8979
9810
|
"detect_ides",
|
|
8980
9811
|
"restart_session",
|
|
8981
9812
|
"exec_command",
|
|
@@ -9010,7 +9841,7 @@ var init_adhdev_daemon = __esm({
|
|
|
9010
9841
|
"pty_resize"
|
|
9011
9842
|
];
|
|
9012
9843
|
for (const cmdType of directCdpCommands) {
|
|
9013
|
-
this.
|
|
9844
|
+
this.serverConn.on(cmdType, async (msg) => {
|
|
9014
9845
|
await this.handleCommand(msg, cmdType, msg.payload);
|
|
9015
9846
|
});
|
|
9016
9847
|
}
|
|
@@ -9042,11 +9873,11 @@ var init_adhdev_daemon = __esm({
|
|
|
9042
9873
|
const cwd = args?.dir || process.cwd();
|
|
9043
9874
|
const { exec: exec2 } = require("child_process");
|
|
9044
9875
|
exec2(cmdStr, { cwd, timeout: 6e4 }, (err, stdout, stderr) => {
|
|
9045
|
-
if (!this.
|
|
9046
|
-
if (err) this.
|
|
9876
|
+
if (!this.serverConn) return;
|
|
9877
|
+
if (err) this.serverConn.sendMessage("log", { message: `\u274C ${err.message}`, level: "error" });
|
|
9047
9878
|
else {
|
|
9048
|
-
if (stdout) this.
|
|
9049
|
-
if (stderr) this.
|
|
9879
|
+
if (stdout) this.serverConn.sendMessage("log", { message: stdout.slice(0, 1e4), level: "info" });
|
|
9880
|
+
if (stderr) this.serverConn.sendMessage("log", { message: stderr.slice(0, 5e3), level: "warn" });
|
|
9050
9881
|
}
|
|
9051
9882
|
});
|
|
9052
9883
|
this.sendResult(msg, true, { started: true });
|
|
@@ -9087,6 +9918,66 @@ var init_adhdev_daemon = __esm({
|
|
|
9087
9918
|
saveConfig(config);
|
|
9088
9919
|
return { success: true, nickname };
|
|
9089
9920
|
}
|
|
9921
|
+
case "get_acp_auth": {
|
|
9922
|
+
const config = loadConfig();
|
|
9923
|
+
const allProviders = this.providerLoader.getAll().filter((p) => p.category === "acp");
|
|
9924
|
+
const result2 = [];
|
|
9925
|
+
const { execSync: execSync6 } = require("child_process");
|
|
9926
|
+
for (const provider2 of allProviders) {
|
|
9927
|
+
const authMethods = provider2.auth || [];
|
|
9928
|
+
const savedAuth = config.acpAuth?.[provider2.type] || {};
|
|
9929
|
+
const methods = authMethods.map((m) => {
|
|
9930
|
+
if (m.type === "env_var") {
|
|
9931
|
+
const vars = (m.vars || []).map((v) => ({
|
|
9932
|
+
...v,
|
|
9933
|
+
hasValue: !!savedAuth[v.name],
|
|
9934
|
+
maskedValue: savedAuth[v.name] ? "\u2022\u2022\u2022\u2022" + savedAuth[v.name].slice(-4) : null
|
|
9935
|
+
}));
|
|
9936
|
+
return { ...m, vars };
|
|
9937
|
+
}
|
|
9938
|
+
return m;
|
|
9939
|
+
});
|
|
9940
|
+
let installed = false;
|
|
9941
|
+
const spawnCmd = provider2.spawn?.command;
|
|
9942
|
+
if (spawnCmd) {
|
|
9943
|
+
try {
|
|
9944
|
+
execSync6(`which ${spawnCmd}`, { stdio: "ignore" });
|
|
9945
|
+
installed = true;
|
|
9946
|
+
} catch {
|
|
9947
|
+
}
|
|
9948
|
+
}
|
|
9949
|
+
result2.push({
|
|
9950
|
+
type: provider2.type,
|
|
9951
|
+
name: provider2.name,
|
|
9952
|
+
icon: provider2.icon || "",
|
|
9953
|
+
displayName: provider2.displayName || provider2.name,
|
|
9954
|
+
auth: methods,
|
|
9955
|
+
installed,
|
|
9956
|
+
installCommand: provider2.install || null
|
|
9957
|
+
});
|
|
9958
|
+
}
|
|
9959
|
+
return { success: true, providers: result2 };
|
|
9960
|
+
}
|
|
9961
|
+
case "set_acp_auth": {
|
|
9962
|
+
const providerType = data.providerType;
|
|
9963
|
+
const vars = data.vars;
|
|
9964
|
+
if (!providerType || !vars) {
|
|
9965
|
+
return { success: false, error: "providerType and vars required" };
|
|
9966
|
+
}
|
|
9967
|
+
const config = loadConfig();
|
|
9968
|
+
if (!config.acpAuth) config.acpAuth = {};
|
|
9969
|
+
const existing = config.acpAuth[providerType] || {};
|
|
9970
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
9971
|
+
if (value && value.trim()) {
|
|
9972
|
+
existing[key] = value.trim();
|
|
9973
|
+
} else {
|
|
9974
|
+
delete existing[key];
|
|
9975
|
+
}
|
|
9976
|
+
}
|
|
9977
|
+
config.acpAuth[providerType] = existing;
|
|
9978
|
+
saveConfig(config);
|
|
9979
|
+
return { success: true, providerType, saved: Object.keys(existing) };
|
|
9980
|
+
}
|
|
9090
9981
|
case "get_daemon_logs": {
|
|
9091
9982
|
const count = parseInt(data.lines) || 100;
|
|
9092
9983
|
const minLevel = data.minLevel || "info";
|
|
@@ -9121,18 +10012,44 @@ var init_adhdev_daemon = __esm({
|
|
|
9121
10012
|
}
|
|
9122
10013
|
// ─── 통합 Daemon 명령 코어 ────────────────────────
|
|
9123
10014
|
/**
|
|
9124
|
-
* Daemon-level 명령 실행 (
|
|
9125
|
-
*
|
|
10015
|
+
* Daemon-level 명령 실행 (IDE start/stop/restart, CLI, detect 등)
|
|
10016
|
+
* 서버 WS와 P2P에서 공통으로 호출.
|
|
9126
10017
|
* null 반환 시 = 이 레벨에서 처리 안 됨 → CommandHandler로 위임
|
|
9127
10018
|
*/
|
|
9128
10019
|
async executeDaemonCommand(cmd, args) {
|
|
9129
10020
|
switch (cmd) {
|
|
9130
10021
|
case "launch_cli":
|
|
9131
10022
|
case "stop_cli":
|
|
9132
|
-
case "restart_session":
|
|
9133
10023
|
case "agent_command": {
|
|
9134
10024
|
return this.cliManager.handleCliCommand(cmd, args);
|
|
9135
10025
|
}
|
|
10026
|
+
// ─── restart_session: IDE / CLI / ACP 통합 ───
|
|
10027
|
+
case "restart_session": {
|
|
10028
|
+
const targetType = args?.cliType || args?.agentType || args?.ideType;
|
|
10029
|
+
if (!targetType) throw new Error("cliType or ideType required");
|
|
10030
|
+
const isIde = this.cdpManagers.has(targetType) || this.providerLoader.get(targetType)?.category === "ide";
|
|
10031
|
+
if (isIde) {
|
|
10032
|
+
await this.stopIde(targetType);
|
|
10033
|
+
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType: targetType, enableCdp: true });
|
|
10034
|
+
return { success: true, restarted: true, ideType: targetType, launch: launchResult };
|
|
10035
|
+
}
|
|
10036
|
+
return this.cliManager.handleCliCommand(cmd, args);
|
|
10037
|
+
}
|
|
10038
|
+
// ─── IDE 종료 ───
|
|
10039
|
+
case "stop_ide": {
|
|
10040
|
+
const ideType = args?.ideType;
|
|
10041
|
+
if (!ideType) throw new Error("ideType required");
|
|
10042
|
+
await this.stopIde(ideType);
|
|
10043
|
+
return { success: true, ideType, stopped: true };
|
|
10044
|
+
}
|
|
10045
|
+
// ─── IDE 재시작 ───
|
|
10046
|
+
case "restart_ide": {
|
|
10047
|
+
const ideType = args?.ideType;
|
|
10048
|
+
if (!ideType) throw new Error("ideType required");
|
|
10049
|
+
await this.stopIde(ideType);
|
|
10050
|
+
const launchResult = await this.executeDaemonCommand("launch_ide", { ideType, enableCdp: true });
|
|
10051
|
+
return { success: true, ideType, restarted: true, launch: launchResult };
|
|
10052
|
+
}
|
|
9136
10053
|
case "launch_ide": {
|
|
9137
10054
|
const launchArgs = { ...args, ideId: args?.ideId || args?.ideType };
|
|
9138
10055
|
const ideKey = launchArgs.ideId;
|
|
@@ -9166,6 +10083,39 @@ var init_adhdev_daemon = __esm({
|
|
|
9166
10083
|
}
|
|
9167
10084
|
return null;
|
|
9168
10085
|
}
|
|
10086
|
+
/**
|
|
10087
|
+
* IDE 종료: CDP disconnect + InstanceManager에서 제거 + instanceIdMap 정리
|
|
10088
|
+
*/
|
|
10089
|
+
async stopIde(ideType) {
|
|
10090
|
+
const cdp2 = this.cdpManagers.get(ideType);
|
|
10091
|
+
if (cdp2) {
|
|
10092
|
+
try {
|
|
10093
|
+
cdp2.disconnect();
|
|
10094
|
+
} catch {
|
|
10095
|
+
}
|
|
10096
|
+
this.cdpManagers.delete(ideType);
|
|
10097
|
+
LOG.info("StopIDE", `CDP disconnected: ${ideType}`);
|
|
10098
|
+
}
|
|
10099
|
+
const instanceKey = `ide:${ideType}`;
|
|
10100
|
+
const ideInstance = this.instanceManager.getInstance(instanceKey);
|
|
10101
|
+
if (ideInstance) {
|
|
10102
|
+
if (ideInstance.getInstanceId) {
|
|
10103
|
+
this.instanceIdMap.delete(ideInstance.getInstanceId());
|
|
10104
|
+
}
|
|
10105
|
+
if (ideInstance.getExtensionInstances) {
|
|
10106
|
+
for (const ext of ideInstance.getExtensionInstances()) {
|
|
10107
|
+
if (ext.getInstanceId) this.instanceIdMap.delete(ext.getInstanceId());
|
|
10108
|
+
}
|
|
10109
|
+
}
|
|
10110
|
+
this.instanceManager.removeInstance(instanceKey);
|
|
10111
|
+
LOG.info("StopIDE", `Instance removed: ${instanceKey}`);
|
|
10112
|
+
}
|
|
10113
|
+
if (this._agentStreamCdpIdeType === ideType) {
|
|
10114
|
+
this._agentStreamCdpIdeType = null;
|
|
10115
|
+
}
|
|
10116
|
+
this.statusReporter?.onStatusChange();
|
|
10117
|
+
console.log(import_chalk2.default.yellow(` \u{1F6D1} IDE stopped: ${ideType}`));
|
|
10118
|
+
}
|
|
9169
10119
|
sendResult(msg, success, extra) {
|
|
9170
10120
|
if (!msg.id) return;
|
|
9171
10121
|
if (msg.ipcWs && this.localServer) {
|
|
@@ -9179,7 +10129,7 @@ var init_adhdev_daemon = __esm({
|
|
|
9179
10129
|
}
|
|
9180
10130
|
}));
|
|
9181
10131
|
}
|
|
9182
|
-
this.
|
|
10132
|
+
this.serverConn?.sendMessage("command_result", {
|
|
9183
10133
|
requestId: msg.id,
|
|
9184
10134
|
success,
|
|
9185
10135
|
source: msg.source,
|
|
@@ -9187,31 +10137,70 @@ var init_adhdev_daemon = __esm({
|
|
|
9187
10137
|
});
|
|
9188
10138
|
}
|
|
9189
10139
|
// ─── 라이프사이클 ───────────────────────────────
|
|
9190
|
-
|
|
10140
|
+
/**
|
|
10141
|
+
* 데몬 정리 (모든 리소스 해제)
|
|
10142
|
+
* @param exitProcess true면 정리 후 process.exit(0), false면 정리만
|
|
10143
|
+
*/
|
|
10144
|
+
async stop(exitProcess = true) {
|
|
9191
10145
|
if (!this.running) return;
|
|
9192
10146
|
this.running = false;
|
|
9193
10147
|
console.log(import_chalk2.default.yellow("\n Shutting down ADHDev Daemon..."));
|
|
9194
|
-
this.
|
|
9195
|
-
|
|
10148
|
+
if (this.cdpDiscoveryTimer) {
|
|
10149
|
+
clearInterval(this.cdpDiscoveryTimer);
|
|
10150
|
+
this.cdpDiscoveryTimer = null;
|
|
10151
|
+
}
|
|
10152
|
+
if (this.screenshotTimer) {
|
|
10153
|
+
clearTimeout(this.screenshotTimer);
|
|
10154
|
+
this.screenshotTimer = null;
|
|
10155
|
+
}
|
|
10156
|
+
if (this.agentStreamTimer) {
|
|
10157
|
+
clearInterval(this.agentStreamTimer);
|
|
10158
|
+
this.agentStreamTimer = null;
|
|
10159
|
+
}
|
|
9196
10160
|
if (this.statusReporter) {
|
|
9197
10161
|
this.statusReporter.stopReporting();
|
|
9198
10162
|
this.statusReporter = null;
|
|
9199
10163
|
}
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
10164
|
+
try {
|
|
10165
|
+
const anyCdpStop = this.getAnyCdp();
|
|
10166
|
+
if (this.agentStreamManager && anyCdpStop) {
|
|
10167
|
+
await this.agentStreamManager.dispose(anyCdpStop);
|
|
10168
|
+
}
|
|
10169
|
+
} catch (e) {
|
|
10170
|
+
console.warn("[AgentStream] Dispose failed:", e?.message);
|
|
10171
|
+
}
|
|
10172
|
+
try {
|
|
10173
|
+
await this.cliManager.shutdownAll();
|
|
10174
|
+
} catch {
|
|
10175
|
+
}
|
|
10176
|
+
try {
|
|
10177
|
+
this.instanceManager.disposeAll();
|
|
10178
|
+
} catch {
|
|
10179
|
+
}
|
|
10180
|
+
try {
|
|
10181
|
+
this.p2p?.disconnect();
|
|
10182
|
+
} catch {
|
|
10183
|
+
}
|
|
10184
|
+
for (const m of this.cdpManagers.values()) {
|
|
10185
|
+
try {
|
|
10186
|
+
m.disconnect();
|
|
10187
|
+
} catch {
|
|
10188
|
+
}
|
|
9206
10189
|
}
|
|
9207
|
-
this.p2p?.disconnect();
|
|
9208
|
-
for (const m of this.cdpManagers.values()) m.disconnect();
|
|
9209
10190
|
this.cdpManagers.clear();
|
|
9210
|
-
|
|
9211
|
-
|
|
10191
|
+
try {
|
|
10192
|
+
this.localServer?.stop();
|
|
10193
|
+
} catch {
|
|
10194
|
+
}
|
|
10195
|
+
try {
|
|
10196
|
+
this.serverConn?.disconnect();
|
|
10197
|
+
} catch {
|
|
10198
|
+
}
|
|
9212
10199
|
removeDaemonPid();
|
|
9213
10200
|
console.log(import_chalk2.default.green(" \u2713 ADHDev Daemon stopped.\n"));
|
|
9214
|
-
|
|
10201
|
+
if (exitProcess) {
|
|
10202
|
+
process.exit(0);
|
|
10203
|
+
}
|
|
9215
10204
|
}
|
|
9216
10205
|
// ─── CDP 관리 ───────────────────────────────────
|
|
9217
10206
|
/** 첫 번째 연결된 CDP 매니저 반환 */
|
|
@@ -9318,13 +10307,18 @@ var init_adhdev_daemon = __esm({
|
|
|
9318
10307
|
const resolvedSettings = this.providerLoader.getSettings(ideType);
|
|
9319
10308
|
this.instanceManager.addInstance(`ide:${managerKey}`, ideInstance, {
|
|
9320
10309
|
cdp: manager,
|
|
9321
|
-
|
|
10310
|
+
serverConn: this.serverConn || void 0,
|
|
9322
10311
|
settings: resolvedSettings
|
|
9323
10312
|
}).then(async () => {
|
|
10313
|
+
this.instanceIdMap.set(ideInstance.getInstanceId(), managerKey);
|
|
9324
10314
|
const extensionProviders = this.providerLoader.getEnabledByCategory("extension", ideType);
|
|
9325
10315
|
for (const extProvider of extensionProviders) {
|
|
9326
10316
|
const extSettings = this.providerLoader.getSettings(extProvider.type);
|
|
9327
10317
|
await ideInstance.addExtension(extProvider, extSettings);
|
|
10318
|
+
const extInstances = ideInstance.getExtensionInstances();
|
|
10319
|
+
for (const ext of extInstances) {
|
|
10320
|
+
this.instanceIdMap.set(ext.getInstanceId(), managerKey);
|
|
10321
|
+
}
|
|
9328
10322
|
}
|
|
9329
10323
|
}).catch((e) => console.warn(`[Instance] Failed to init IDE instance ${managerKey}:`, e?.message));
|
|
9330
10324
|
}
|
|
@@ -9450,19 +10444,6 @@ init_provider_loader();
|
|
|
9450
10444
|
// src/installer.ts
|
|
9451
10445
|
var import_child_process2 = require("child_process");
|
|
9452
10446
|
var EXTENSION_CATALOG = [
|
|
9453
|
-
// Bridge extension (always installed — from local VSIX)
|
|
9454
|
-
{
|
|
9455
|
-
id: "adhdev",
|
|
9456
|
-
name: "ADHDev",
|
|
9457
|
-
displayName: "ADHDev Bridge",
|
|
9458
|
-
marketplaceId: "adhdev.adhdev-bridge",
|
|
9459
|
-
description: "Connects your IDE to the ADHDev cloud for remote control",
|
|
9460
|
-
category: "bridge",
|
|
9461
|
-
icon: "\u{1F309}",
|
|
9462
|
-
recommended: true,
|
|
9463
|
-
vsixUrl: "https://adhf.dev/releases/bridge/latest.vsix"
|
|
9464
|
-
// 웹사이트에서 정적 서빙
|
|
9465
|
-
},
|
|
9466
10447
|
// AI Agent extensions
|
|
9467
10448
|
{
|
|
9468
10449
|
id: "roo-code",
|
|
@@ -9575,12 +10556,12 @@ async function installExtension(ide, extension) {
|
|
|
9575
10556
|
const res = await fetch(extension.vsixUrl);
|
|
9576
10557
|
if (res.ok) {
|
|
9577
10558
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
9578
|
-
const
|
|
9579
|
-
|
|
9580
|
-
return new Promise((
|
|
10559
|
+
const fs8 = await import("fs");
|
|
10560
|
+
fs8.writeFileSync(vsixPath, buffer);
|
|
10561
|
+
return new Promise((resolve5) => {
|
|
9581
10562
|
const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
|
|
9582
10563
|
(0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, _stdout, stderr) => {
|
|
9583
|
-
|
|
10564
|
+
resolve5({
|
|
9584
10565
|
extensionId: extension.id,
|
|
9585
10566
|
marketplaceId: extension.marketplaceId,
|
|
9586
10567
|
success: !error,
|
|
@@ -9590,32 +10571,14 @@ async function installExtension(ide, extension) {
|
|
|
9590
10571
|
});
|
|
9591
10572
|
});
|
|
9592
10573
|
}
|
|
9593
|
-
if (extension.category === "bridge") {
|
|
9594
|
-
return {
|
|
9595
|
-
extensionId: extension.id,
|
|
9596
|
-
marketplaceId: extension.marketplaceId,
|
|
9597
|
-
success: false,
|
|
9598
|
-
alreadyInstalled: false,
|
|
9599
|
-
error: "VSIX download failed. Check your internet connection and try again."
|
|
9600
|
-
};
|
|
9601
|
-
}
|
|
9602
10574
|
} catch (e) {
|
|
9603
|
-
if (extension.category === "bridge") {
|
|
9604
|
-
return {
|
|
9605
|
-
extensionId: extension.id,
|
|
9606
|
-
marketplaceId: extension.marketplaceId,
|
|
9607
|
-
success: false,
|
|
9608
|
-
alreadyInstalled: false,
|
|
9609
|
-
error: `VSIX download error: ${e?.message || "Unknown error"}`
|
|
9610
|
-
};
|
|
9611
|
-
}
|
|
9612
10575
|
}
|
|
9613
10576
|
}
|
|
9614
|
-
return new Promise((
|
|
10577
|
+
return new Promise((resolve5) => {
|
|
9615
10578
|
const cmd = `"${ide.cliCommand}" --install-extension ${extension.marketplaceId} --force`;
|
|
9616
10579
|
(0, import_child_process2.exec)(cmd, { timeout: 6e4 }, (error, stdout, stderr) => {
|
|
9617
10580
|
if (error) {
|
|
9618
|
-
|
|
10581
|
+
resolve5({
|
|
9619
10582
|
extensionId: extension.id,
|
|
9620
10583
|
marketplaceId: extension.marketplaceId,
|
|
9621
10584
|
success: false,
|
|
@@ -9623,7 +10586,7 @@ async function installExtension(ide, extension) {
|
|
|
9623
10586
|
error: stderr || error.message
|
|
9624
10587
|
});
|
|
9625
10588
|
} else {
|
|
9626
|
-
|
|
10589
|
+
resolve5({
|
|
9627
10590
|
extensionId: extension.id,
|
|
9628
10591
|
marketplaceId: extension.marketplaceId,
|
|
9629
10592
|
success: true,
|
|
@@ -9705,7 +10668,7 @@ async function runWizard(options = {}) {
|
|
|
9705
10668
|
name: "mode",
|
|
9706
10669
|
message: "Setup mode:",
|
|
9707
10670
|
choices: [
|
|
9708
|
-
{ name: `\u{1F680} ${import_chalk3.default.bold("Quick Setup")} \u2014 Auto-detect IDEs,
|
|
10671
|
+
{ name: `\u{1F680} ${import_chalk3.default.bold("Quick Setup")} \u2014 Auto-detect IDEs, login ${import_chalk3.default.gray("(recommended)")}`, value: "quick" },
|
|
9709
10672
|
{ name: `\u2699\uFE0F ${import_chalk3.default.bold("Custom Setup")} \u2014 Choose IDE, AI extensions, and more`, value: "custom" },
|
|
9710
10673
|
{ name: `\u{1F527} ${import_chalk3.default.bold("CLI Only")} \u2014 Install adhdev command globally`, value: "cli-only" }
|
|
9711
10674
|
]
|
|
@@ -9759,16 +10722,6 @@ async function quickSetup() {
|
|
|
9759
10722
|
]);
|
|
9760
10723
|
selectedIDEs = installedIDEs.filter((i) => selectedIdeIds.includes(i.id));
|
|
9761
10724
|
}
|
|
9762
|
-
const bridgeExt = EXTENSION_CATALOG.find((e) => e.category === "bridge");
|
|
9763
|
-
for (const ide of selectedIDEs) {
|
|
9764
|
-
const installSpinner = (0, import_ora.default)(`Installing ADHDev Bridge \u2192 ${ide.displayName}...`).start();
|
|
9765
|
-
const result = await installExtension(ide, bridgeExt);
|
|
9766
|
-
if (result.success) {
|
|
9767
|
-
installSpinner.succeed(result.alreadyInstalled ? `${ide.icon} ${ide.displayName} \u2014 Bridge already installed \u2713` : `${ide.icon} ${ide.displayName} \u2014 Bridge installed \u2713`);
|
|
9768
|
-
} else {
|
|
9769
|
-
installSpinner.fail(`${ide.icon} ${ide.displayName} \u2014 Failed: ${result.error}`);
|
|
9770
|
-
}
|
|
9771
|
-
}
|
|
9772
10725
|
console.log(DIVIDER);
|
|
9773
10726
|
const loginResult = await loginFlow();
|
|
9774
10727
|
if (!loginResult) {
|
|
@@ -9856,9 +10809,8 @@ async function customSetup() {
|
|
|
9856
10809
|
}))
|
|
9857
10810
|
}
|
|
9858
10811
|
]);
|
|
9859
|
-
const bridgeExt = EXTENSION_CATALOG.find((e) => e.category === "bridge");
|
|
9860
10812
|
const selectedAIExts = aiExtensions.filter((e) => selectedExtIds.includes(e.id));
|
|
9861
|
-
const allExtensions =
|
|
10813
|
+
const allExtensions = selectedAIExts;
|
|
9862
10814
|
console.log(import_chalk3.default.bold("\n\u{1F4CD} Step 4/4 \u2014 Installing extensions\n"));
|
|
9863
10815
|
if (selectedIDE.cliCommand) {
|
|
9864
10816
|
await installExtensions(
|
|
@@ -9975,7 +10927,7 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
9975
10927
|
if (!ide.cliCommand) return;
|
|
9976
10928
|
try {
|
|
9977
10929
|
const os13 = await import("os");
|
|
9978
|
-
const
|
|
10930
|
+
const fs8 = await import("fs");
|
|
9979
10931
|
const path12 = await import("path");
|
|
9980
10932
|
const platform7 = os13.platform();
|
|
9981
10933
|
const home = os13.homedir();
|
|
@@ -9996,18 +10948,18 @@ async function injectTokenToIDE(ide, connectionToken) {
|
|
|
9996
10948
|
if (!appName) return;
|
|
9997
10949
|
const settingsPath = getSettingsPath(appName);
|
|
9998
10950
|
let settings = {};
|
|
9999
|
-
if (
|
|
10951
|
+
if (fs8.existsSync(settingsPath)) {
|
|
10000
10952
|
try {
|
|
10001
|
-
settings = JSON.parse(
|
|
10953
|
+
settings = JSON.parse(fs8.readFileSync(settingsPath, "utf-8"));
|
|
10002
10954
|
} catch {
|
|
10003
10955
|
settings = {};
|
|
10004
10956
|
}
|
|
10005
10957
|
} else {
|
|
10006
|
-
|
|
10958
|
+
fs8.mkdirSync(path12.dirname(settingsPath), { recursive: true });
|
|
10007
10959
|
}
|
|
10008
10960
|
settings["adhdev.connectionToken"] = connectionToken;
|
|
10009
10961
|
settings["adhdev.autoConnect"] = true;
|
|
10010
|
-
|
|
10962
|
+
fs8.writeFileSync(settingsPath, JSON.stringify(settings, null, 4), "utf-8");
|
|
10011
10963
|
console.log(import_chalk3.default.green(" \u2713 Connection token saved to IDE settings"));
|
|
10012
10964
|
} catch (e) {
|
|
10013
10965
|
console.log(import_chalk3.default.yellow(` \u26A0 Could not inject token: ${e.message}`));
|
|
@@ -10182,20 +11134,13 @@ _cliProviderLoader.loadAll();
|
|
|
10182
11134
|
_cliProviderLoader.registerToDetector();
|
|
10183
11135
|
var program = new import_commander.Command();
|
|
10184
11136
|
program.name("adhdev").description("\u{1F309} ADHDev \u2014 Agent Dashboard Hub for your IDE").version(pkgVersion);
|
|
10185
|
-
program.command("setup").description("Run the interactive setup wizard (detect IDEs,
|
|
11137
|
+
program.command("setup").description("Run the interactive setup wizard (detect IDEs, login)").option("-f, --force", "Force re-run setup even if already configured").action(async (options) => {
|
|
10186
11138
|
await runWizard({ force: options.force });
|
|
10187
11139
|
});
|
|
10188
11140
|
program.command("launch [target]").description("Launch IDE with CDP or start CLI agent (e.g. cursor, gemini, claude)").option("-w, --workspace <path>", "Workspace/folder to open").option("-n, --new-window", "Open in a new window").option("-d, --dir <path>", "Working directory for CLI agent", process.cwd()).action(async (targetArg, options) => {
|
|
10189
|
-
const
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
"claude-cli": "claude-code",
|
|
10193
|
-
"gemini": "gemini-cli",
|
|
10194
|
-
"gemini-cli": "gemini-cli",
|
|
10195
|
-
"codex": "codex-cli",
|
|
10196
|
-
"codex-cli": "codex-cli"
|
|
10197
|
-
};
|
|
10198
|
-
const cliType = targetArg ? CLI_ALIASES[targetArg.toLowerCase()] : null;
|
|
11141
|
+
const resolvedType = targetArg ? _cliProviderLoader.resolveAlias(targetArg.toLowerCase()) : null;
|
|
11142
|
+
const resolvedProvider = resolvedType ? _cliProviderLoader.get(resolvedType) : null;
|
|
11143
|
+
const cliType = resolvedProvider && (resolvedProvider.category === "cli" || resolvedProvider.category === "acp") ? resolvedType : null;
|
|
10199
11144
|
if (cliType) {
|
|
10200
11145
|
const workingDir = options.dir || options.workspace || process.cwd();
|
|
10201
11146
|
const ora3 = await import("ora");
|
|
@@ -10304,7 +11249,7 @@ program.command("launch [target]").description("Launch IDE with CDP or start CLI
|
|
|
10304
11249
|
}
|
|
10305
11250
|
});
|
|
10306
11251
|
console.log(import_chalk4.default.gray("\n Available CLI Agents:"));
|
|
10307
|
-
const clis = await detectCLIs();
|
|
11252
|
+
const clis = await detectCLIs(_cliProviderLoader);
|
|
10308
11253
|
clis.forEach((cli) => {
|
|
10309
11254
|
if (cli.installed) {
|
|
10310
11255
|
console.log(` ${import_chalk4.default.green("\u2713")} ${cli.icon} ${import_chalk4.default.bold(cli.id)} \u2014 ${cli.displayName}`);
|
|
@@ -10365,7 +11310,7 @@ program.command("status").description("Show current ADHDev setup status").action
|
|
|
10365
11310
|
}
|
|
10366
11311
|
}
|
|
10367
11312
|
}
|
|
10368
|
-
const clis = await detectCLIs();
|
|
11313
|
+
const clis = await detectCLIs(_cliProviderLoader);
|
|
10369
11314
|
const installedClis = clis.filter((c) => c.installed);
|
|
10370
11315
|
if (installedClis.length > 0) {
|
|
10371
11316
|
console.log(` ${import_chalk4.default.bold("CLI Agents:")}`);
|
|
@@ -10402,7 +11347,7 @@ program.command("detect").description("Detect installed IDEs on your system").ac
|
|
|
10402
11347
|
}
|
|
10403
11348
|
});
|
|
10404
11349
|
console.log(import_chalk4.default.bold("\n\u{1F50D} Detecting installed CLI Agents...\n"));
|
|
10405
|
-
const clis = await detectCLIs();
|
|
11350
|
+
const clis = await detectCLIs(_cliProviderLoader);
|
|
10406
11351
|
clis.forEach((cli) => {
|
|
10407
11352
|
if (cli.installed) {
|
|
10408
11353
|
const version = cli.version ? import_chalk4.default.gray(` v${cli.version}`) : "";
|
|
@@ -10465,10 +11410,116 @@ program.command("daemon:stop").description("Stop ADHDev Daemon").action(async ()
|
|
|
10465
11410
|
`));
|
|
10466
11411
|
}
|
|
10467
11412
|
});
|
|
11413
|
+
program.command("daemon:restart").description("Restart ADHDev Daemon (stop \u2192 start)").option("-p, --port <port>", "Local WS server port", "19222").option("--server <url>", "Override server URL").option("--dev", "Enable Dev Mode").action(async (options) => {
|
|
11414
|
+
const { stopDaemon: stopDaemon2, isDaemonRunning: isDaemonRunning2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
|
|
11415
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
11416
|
+
if (isDaemonRunning2()) {
|
|
11417
|
+
console.log(import_chalk4.default.yellow("\n Stopping existing daemon..."));
|
|
11418
|
+
stopDaemon2();
|
|
11419
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
11420
|
+
}
|
|
11421
|
+
console.log(import_chalk4.default.cyan(" Starting new daemon..."));
|
|
11422
|
+
const args = ["daemon", "-p", options.port || "19222"];
|
|
11423
|
+
if (options.server) args.push("--server", options.server);
|
|
11424
|
+
if (options.dev) args.push("--dev");
|
|
11425
|
+
const child = spawn3(process.execPath, [process.argv[1], ...args], {
|
|
11426
|
+
detached: true,
|
|
11427
|
+
stdio: "ignore",
|
|
11428
|
+
env: { ...process.env }
|
|
11429
|
+
});
|
|
11430
|
+
child.unref();
|
|
11431
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
11432
|
+
if (isDaemonRunning2()) {
|
|
11433
|
+
console.log(import_chalk4.default.green(` \u2713 ADHDev Daemon restarted (PID: ${child.pid})
|
|
11434
|
+
`));
|
|
11435
|
+
} else {
|
|
11436
|
+
console.log(import_chalk4.default.red(` \u2717 Daemon failed to start. Check logs: ~/.adhdev/daemon.log
|
|
11437
|
+
`));
|
|
11438
|
+
process.exit(1);
|
|
11439
|
+
}
|
|
11440
|
+
});
|
|
11441
|
+
program.command("daemon:upgrade").description("Upgrade ADHDev to latest version and restart daemon").option("--no-restart", "Upgrade only, skip daemon restart").action(async (options) => {
|
|
11442
|
+
const { isDaemonRunning: isDaemonRunning2, stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
|
|
11443
|
+
const { execSync: execSync6, spawn: spawn3 } = await import("child_process");
|
|
11444
|
+
const fsMod = await import("fs");
|
|
11445
|
+
const pathMod = await import("path");
|
|
11446
|
+
console.log(import_chalk4.default.bold("\n \u{1F504} ADHDev Upgrade\n"));
|
|
11447
|
+
const adhdevPath = process.argv[1];
|
|
11448
|
+
const realPath = fsMod.realpathSync(adhdevPath);
|
|
11449
|
+
const isLinked = realPath.includes(".openclaw") || realPath.includes("/src/");
|
|
11450
|
+
const currentVersion = pkgVersion;
|
|
11451
|
+
console.log(` ${import_chalk4.default.bold("Current:")} v${currentVersion}`);
|
|
11452
|
+
console.log(` ${import_chalk4.default.bold("Install:")} ${isLinked ? "npm link (dev)" : "npm global"}`);
|
|
11453
|
+
if (isLinked) {
|
|
11454
|
+
const projectRoot = pathMod.resolve(pathMod.dirname(realPath), "..");
|
|
11455
|
+
const launcherDir = pathMod.join(projectRoot);
|
|
11456
|
+
console.log(` ${import_chalk4.default.bold("Path:")} ${launcherDir}`);
|
|
11457
|
+
console.log(import_chalk4.default.cyan("\n Pulling latest..."));
|
|
11458
|
+
try {
|
|
11459
|
+
let gitRoot = launcherDir;
|
|
11460
|
+
while (!fsMod.existsSync(pathMod.join(gitRoot, ".git")) && gitRoot !== "/") {
|
|
11461
|
+
gitRoot = pathMod.dirname(gitRoot);
|
|
11462
|
+
}
|
|
11463
|
+
execSync6("git pull --rebase", { cwd: gitRoot, stdio: "inherit" });
|
|
11464
|
+
console.log(import_chalk4.default.cyan("\n Building..."));
|
|
11465
|
+
execSync6("npm run build", { cwd: launcherDir, stdio: "inherit" });
|
|
11466
|
+
execSync6("npm link", { cwd: launcherDir, stdio: "inherit" });
|
|
11467
|
+
console.log(import_chalk4.default.green("\n \u2713 Build complete"));
|
|
11468
|
+
} catch (e) {
|
|
11469
|
+
console.log(import_chalk4.default.red(`
|
|
11470
|
+
\u2717 Build failed: ${e?.message}
|
|
11471
|
+
`));
|
|
11472
|
+
process.exit(1);
|
|
11473
|
+
}
|
|
11474
|
+
} else {
|
|
11475
|
+
console.log(import_chalk4.default.cyan("\n Checking for updates..."));
|
|
11476
|
+
try {
|
|
11477
|
+
const latest = execSync6("npm view adhdev version", { encoding: "utf-8" }).trim();
|
|
11478
|
+
console.log(` ${import_chalk4.default.bold("Latest:")} v${latest}`);
|
|
11479
|
+
if (latest === currentVersion) {
|
|
11480
|
+
console.log(import_chalk4.default.green("\n \u2713 Already on latest version.\n"));
|
|
11481
|
+
if (!options.restart) return;
|
|
11482
|
+
} else {
|
|
11483
|
+
console.log(import_chalk4.default.cyan(`
|
|
11484
|
+
Upgrading v${currentVersion} \u2192 v${latest}...`));
|
|
11485
|
+
execSync6("npm install -g adhdev@latest", { stdio: "inherit" });
|
|
11486
|
+
console.log(import_chalk4.default.green("\n \u2713 Upgrade complete"));
|
|
11487
|
+
}
|
|
11488
|
+
} catch (e) {
|
|
11489
|
+
console.log(import_chalk4.default.red(`
|
|
11490
|
+
\u2717 Upgrade failed: ${e?.message}
|
|
11491
|
+
`));
|
|
11492
|
+
process.exit(1);
|
|
11493
|
+
}
|
|
11494
|
+
}
|
|
11495
|
+
if (options.restart !== false && isDaemonRunning2()) {
|
|
11496
|
+
console.log(import_chalk4.default.yellow("\n Restarting daemon..."));
|
|
11497
|
+
stopDaemon2();
|
|
11498
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
11499
|
+
const child = spawn3(process.execPath, [process.argv[1], "daemon", "-p", "19222"], {
|
|
11500
|
+
detached: true,
|
|
11501
|
+
stdio: "ignore",
|
|
11502
|
+
env: { ...process.env }
|
|
11503
|
+
});
|
|
11504
|
+
child.unref();
|
|
11505
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
11506
|
+
if (isDaemonRunning2()) {
|
|
11507
|
+
console.log(import_chalk4.default.green(` \u2713 Daemon restarted with new version
|
|
11508
|
+
`));
|
|
11509
|
+
} else {
|
|
11510
|
+
console.log(import_chalk4.default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
|
|
11511
|
+
`));
|
|
11512
|
+
}
|
|
11513
|
+
} else if (options.restart !== false) {
|
|
11514
|
+
console.log(import_chalk4.default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
|
|
11515
|
+
} else {
|
|
11516
|
+
console.log(import_chalk4.default.green("\n \u2713 Upgrade complete (daemon not restarted)\n"));
|
|
11517
|
+
}
|
|
11518
|
+
});
|
|
10468
11519
|
async function sendDaemonCommand(cmd, args = {}, port = 19222) {
|
|
10469
11520
|
const WebSocket4 = (await import("ws")).default;
|
|
10470
11521
|
const { DAEMON_WS_PATH: DAEMON_WS_PATH2 } = await Promise.resolve().then(() => (init_ipc_protocol(), ipc_protocol_exports));
|
|
10471
|
-
return new Promise((
|
|
11522
|
+
return new Promise((resolve5, reject) => {
|
|
10472
11523
|
const wsUrl = `ws://127.0.0.1:${port}${DAEMON_WS_PATH2 || "/daemon"}`;
|
|
10473
11524
|
const ws = new WebSocket4(wsUrl);
|
|
10474
11525
|
const timeout = setTimeout(() => {
|
|
@@ -10499,7 +11550,7 @@ async function sendDaemonCommand(cmd, args = {}, port = 19222) {
|
|
|
10499
11550
|
if (msg.type === "daemon:command_result" || msg.type === "command_result") {
|
|
10500
11551
|
clearTimeout(timeout);
|
|
10501
11552
|
ws.close();
|
|
10502
|
-
|
|
11553
|
+
resolve5(msg.payload?.result || msg.payload || msg);
|
|
10503
11554
|
}
|
|
10504
11555
|
} catch {
|
|
10505
11556
|
}
|
|
@@ -10516,13 +11567,13 @@ Is 'adhdev daemon' running?`));
|
|
|
10516
11567
|
}
|
|
10517
11568
|
async function directCdpEval(expression, port = 9222) {
|
|
10518
11569
|
const http3 = await import("http");
|
|
10519
|
-
const targets = await new Promise((
|
|
11570
|
+
const targets = await new Promise((resolve5, reject) => {
|
|
10520
11571
|
http3.get(`http://127.0.0.1:${port}/json`, (res) => {
|
|
10521
11572
|
let data = "";
|
|
10522
11573
|
res.on("data", (c) => data += c);
|
|
10523
11574
|
res.on("end", () => {
|
|
10524
11575
|
try {
|
|
10525
|
-
|
|
11576
|
+
resolve5(JSON.parse(data));
|
|
10526
11577
|
} catch {
|
|
10527
11578
|
reject(new Error("Invalid JSON"));
|
|
10528
11579
|
}
|
|
@@ -10535,7 +11586,7 @@ async function directCdpEval(expression, port = 9222) {
|
|
|
10535
11586
|
const target = (mainPages.length > 0 ? mainPages[0] : pages[0]) || targets[0];
|
|
10536
11587
|
if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target found");
|
|
10537
11588
|
const WebSocket4 = (await import("ws")).default;
|
|
10538
|
-
return new Promise((
|
|
11589
|
+
return new Promise((resolve5, reject) => {
|
|
10539
11590
|
const ws = new WebSocket4(target.webSocketDebuggerUrl);
|
|
10540
11591
|
const timeout = setTimeout(() => {
|
|
10541
11592
|
ws.close();
|
|
@@ -10557,11 +11608,11 @@ async function directCdpEval(expression, port = 9222) {
|
|
|
10557
11608
|
clearTimeout(timeout);
|
|
10558
11609
|
ws.close();
|
|
10559
11610
|
if (msg.result?.result?.value !== void 0) {
|
|
10560
|
-
|
|
11611
|
+
resolve5(msg.result.result.value);
|
|
10561
11612
|
} else if (msg.result?.exceptionDetails) {
|
|
10562
11613
|
reject(new Error(msg.result.exceptionDetails.text));
|
|
10563
11614
|
} else {
|
|
10564
|
-
|
|
11615
|
+
resolve5(msg.result);
|
|
10565
11616
|
}
|
|
10566
11617
|
}
|
|
10567
11618
|
});
|
|
@@ -10615,7 +11666,7 @@ provider.command("list").description("List all loaded providers").option("-j, --
|
|
|
10615
11666
|
provider.command("reload").description("Hot-reload all providers (requires daemon --dev)").action(async () => {
|
|
10616
11667
|
try {
|
|
10617
11668
|
const http3 = await import("http");
|
|
10618
|
-
const result = await new Promise((
|
|
11669
|
+
const result = await new Promise((resolve5, reject) => {
|
|
10619
11670
|
const req = http3.request({
|
|
10620
11671
|
hostname: "127.0.0.1",
|
|
10621
11672
|
port: 19280,
|
|
@@ -10627,9 +11678,9 @@ provider.command("reload").description("Hot-reload all providers (requires daemo
|
|
|
10627
11678
|
res.on("data", (c) => data += c);
|
|
10628
11679
|
res.on("end", () => {
|
|
10629
11680
|
try {
|
|
10630
|
-
|
|
11681
|
+
resolve5(JSON.parse(data));
|
|
10631
11682
|
} catch {
|
|
10632
|
-
|
|
11683
|
+
resolve5({ raw: data });
|
|
10633
11684
|
}
|
|
10634
11685
|
});
|
|
10635
11686
|
});
|
|
@@ -10663,7 +11714,7 @@ provider.command("create <type>").description("Scaffold a new provider.js from t
|
|
|
10663
11714
|
const category = options.category;
|
|
10664
11715
|
const location = options.builtin ? "builtin" : "user";
|
|
10665
11716
|
const http3 = await import("http");
|
|
10666
|
-
const result = await new Promise((
|
|
11717
|
+
const result = await new Promise((resolve5, reject) => {
|
|
10667
11718
|
const postData = JSON.stringify({ type, name, category, location });
|
|
10668
11719
|
const req = http3.request({
|
|
10669
11720
|
hostname: "127.0.0.1",
|
|
@@ -10676,9 +11727,9 @@ provider.command("create <type>").description("Scaffold a new provider.js from t
|
|
|
10676
11727
|
res.on("data", (c) => data += c);
|
|
10677
11728
|
res.on("end", () => {
|
|
10678
11729
|
try {
|
|
10679
|
-
|
|
11730
|
+
resolve5(JSON.parse(data));
|
|
10680
11731
|
} catch {
|
|
10681
|
-
|
|
11732
|
+
resolve5({ raw: data });
|
|
10682
11733
|
}
|
|
10683
11734
|
});
|
|
10684
11735
|
});
|
|
@@ -10760,7 +11811,7 @@ provider.command("run <type> <script>").description("Run a specific provider scr
|
|
|
10760
11811
|
script,
|
|
10761
11812
|
params: options.param ? { text: options.param, sessionId: options.param, buttonText: options.param } : {}
|
|
10762
11813
|
});
|
|
10763
|
-
const result = await new Promise((
|
|
11814
|
+
const result = await new Promise((resolve5, reject) => {
|
|
10764
11815
|
const req = http3.request({
|
|
10765
11816
|
hostname: "127.0.0.1",
|
|
10766
11817
|
port: 19280,
|
|
@@ -10772,9 +11823,9 @@ provider.command("run <type> <script>").description("Run a specific provider scr
|
|
|
10772
11823
|
res.on("data", (c) => data += c);
|
|
10773
11824
|
res.on("end", () => {
|
|
10774
11825
|
try {
|
|
10775
|
-
|
|
11826
|
+
resolve5(JSON.parse(data));
|
|
10776
11827
|
} catch {
|
|
10777
|
-
|
|
11828
|
+
resolve5({ raw: data });
|
|
10778
11829
|
}
|
|
10779
11830
|
});
|
|
10780
11831
|
});
|
|
@@ -10810,15 +11861,15 @@ provider.command("run <type> <script>").description("Run a specific provider scr
|
|
|
10810
11861
|
provider.command("source <type>").description("View source code of a provider").action(async (type) => {
|
|
10811
11862
|
try {
|
|
10812
11863
|
const http3 = await import("http");
|
|
10813
|
-
const result = await new Promise((
|
|
11864
|
+
const result = await new Promise((resolve5, reject) => {
|
|
10814
11865
|
http3.get(`http://127.0.0.1:19280/api/providers/${type}/source`, (res) => {
|
|
10815
11866
|
let data = "";
|
|
10816
11867
|
res.on("data", (c) => data += c);
|
|
10817
11868
|
res.on("end", () => {
|
|
10818
11869
|
try {
|
|
10819
|
-
|
|
11870
|
+
resolve5(JSON.parse(data));
|
|
10820
11871
|
} catch {
|
|
10821
|
-
|
|
11872
|
+
resolve5({ raw: data });
|
|
10822
11873
|
}
|
|
10823
11874
|
});
|
|
10824
11875
|
}).on("error", () => {
|
|
@@ -10945,8 +11996,8 @@ cdp.command("dump [selector]").description("Dump DOM tree (default: body)").opti
|
|
|
10945
11996
|
}
|
|
10946
11997
|
const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
10947
11998
|
if (options.output) {
|
|
10948
|
-
const
|
|
10949
|
-
|
|
11999
|
+
const fs8 = await import("fs");
|
|
12000
|
+
fs8.writeFileSync(options.output, output, "utf-8");
|
|
10950
12001
|
console.log(import_chalk4.default.green(`
|
|
10951
12002
|
\u2713 Saved to ${options.output} (${output.length} chars)
|
|
10952
12003
|
`));
|
|
@@ -11022,13 +12073,13 @@ cdp.command("eval <expression>").description("Execute JavaScript expression via
|
|
|
11022
12073
|
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) => {
|
|
11023
12074
|
try {
|
|
11024
12075
|
const http3 = await import("http");
|
|
11025
|
-
const targets = await new Promise((
|
|
12076
|
+
const targets = await new Promise((resolve5, reject) => {
|
|
11026
12077
|
http3.get(`http://127.0.0.1:${options.port}/json`, (res) => {
|
|
11027
12078
|
let data = "";
|
|
11028
12079
|
res.on("data", (c) => data += c);
|
|
11029
12080
|
res.on("end", () => {
|
|
11030
12081
|
try {
|
|
11031
|
-
|
|
12082
|
+
resolve5(JSON.parse(data));
|
|
11032
12083
|
} catch {
|
|
11033
12084
|
reject(new Error("Invalid JSON"));
|
|
11034
12085
|
}
|
|
@@ -11042,20 +12093,20 @@ cdp.command("screenshot").description("Capture IDE screenshot").option("-p, --po
|
|
|
11042
12093
|
if (!target?.webSocketDebuggerUrl) throw new Error("No CDP target");
|
|
11043
12094
|
const WebSocket4 = (await import("ws")).default;
|
|
11044
12095
|
const ws = new WebSocket4(target.webSocketDebuggerUrl);
|
|
11045
|
-
await new Promise((
|
|
12096
|
+
await new Promise((resolve5, reject) => {
|
|
11046
12097
|
ws.on("open", () => {
|
|
11047
12098
|
ws.send(JSON.stringify({ id: 1, method: "Page.captureScreenshot", params: { format: "jpeg", quality: 50 } }));
|
|
11048
12099
|
});
|
|
11049
12100
|
ws.on("message", async (data) => {
|
|
11050
12101
|
const msg = JSON.parse(data.toString());
|
|
11051
12102
|
if (msg.id === 1 && msg.result?.data) {
|
|
11052
|
-
const
|
|
11053
|
-
|
|
12103
|
+
const fs8 = await import("fs");
|
|
12104
|
+
fs8.writeFileSync(options.output, Buffer.from(msg.result.data, "base64"));
|
|
11054
12105
|
console.log(import_chalk4.default.green(`
|
|
11055
12106
|
\u2713 Screenshot saved to ${options.output}
|
|
11056
12107
|
`));
|
|
11057
12108
|
ws.close();
|
|
11058
|
-
|
|
12109
|
+
resolve5();
|
|
11059
12110
|
}
|
|
11060
12111
|
});
|
|
11061
12112
|
ws.on("error", (e) => reject(e));
|