adhdev 0.1.33 → 0.1.35
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 +305 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -364,8 +364,9 @@ async function detectCLIs() {
|
|
|
364
364
|
return results;
|
|
365
365
|
}
|
|
366
366
|
async function detectCLI(cliId) {
|
|
367
|
+
const normalizedId = cliId === "claude-cli" ? "claude-code" : cliId;
|
|
367
368
|
const all = await detectCLIs();
|
|
368
|
-
return all.find((c) => c.id ===
|
|
369
|
+
return all.find((c) => c.id === normalizedId && c.installed) || null;
|
|
369
370
|
}
|
|
370
371
|
var import_child_process3, os, KNOWN_CLIS;
|
|
371
372
|
var init_cli_detector = __esm({
|
|
@@ -933,6 +934,47 @@ var init_cli_bridge = __esm({
|
|
|
933
934
|
function stripAnsi(str) {
|
|
934
935
|
return str.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B\][^\x1B]*\x1B\\/g, "");
|
|
935
936
|
}
|
|
937
|
+
function cleanGeminiResponse(raw, lastUserInput) {
|
|
938
|
+
const lines = raw.split("\n");
|
|
939
|
+
const cleaned = [];
|
|
940
|
+
for (const line of lines) {
|
|
941
|
+
const trimmed = line.trim();
|
|
942
|
+
if (!trimmed) {
|
|
943
|
+
cleaned.push("");
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
if (/^[\u256d\u256e\u2570\u256f\u2502\u251c\u2524\u252c\u2534\u253c\u2500\u2501\u2550\u2554\u2557\u255a\u255d\u2551]+$/.test(trimmed)) continue;
|
|
947
|
+
if (/^[\u256d\u2570]\u2500\u2500/.test(trimmed) || /\u2500\u2500[\u256e\u256f]$/.test(trimmed)) continue;
|
|
948
|
+
if (/^\u2502.*\u2502$/.test(trimmed)) continue;
|
|
949
|
+
if (/[\u2500\u2501\u2550\u2580\u2584]{4,}/.test(trimmed)) continue;
|
|
950
|
+
if (/^YOLO\s+ctrl\+y/i.test(trimmed)) continue;
|
|
951
|
+
if (/^\? for shortcuts/.test(trimmed)) continue;
|
|
952
|
+
if (/^\/model\s/i.test(trimmed)) continue;
|
|
953
|
+
if (/^~\s.*no sandbox/i.test(trimmed)) continue;
|
|
954
|
+
if (/^Type your message/i.test(trimmed)) continue;
|
|
955
|
+
if (/ctrl\+[a-z]/i.test(trimmed) && trimmed.length < 50) continue;
|
|
956
|
+
if (/Shortcuts?\s*\(for more/i.test(trimmed)) continue;
|
|
957
|
+
if (/shell mode|cycle mode|paste images|select file or folder|reverse-search|clear prompt|rewind|raw markdown|open external editor/i.test(trimmed)) continue;
|
|
958
|
+
if (/Shift\+Tab|Option\+M|Ctrl\+X|Ctrl\+R|Ctrl\+Y/i.test(trimmed) && /mode|prompt|editor|search/i.test(trimmed)) continue;
|
|
959
|
+
if (/^[\u276f>]\s*$/.test(trimmed)) continue;
|
|
960
|
+
if (/^[*\u2022]\s*$/.test(trimmed)) continue;
|
|
961
|
+
if (lastUserInput && /^[*\u2022]\s/.test(trimmed)) {
|
|
962
|
+
const bulletContent = trimmed.replace(/^[*\u2022]\s*/, "").trim();
|
|
963
|
+
if (bulletContent === lastUserInput.trim() || lastUserInput.trim().startsWith(bulletContent.slice(0, 15))) {
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if (/Gemini CLI update available/i.test(trimmed)) continue;
|
|
968
|
+
if (/brew upgrade gemini-cli/i.test(trimmed)) continue;
|
|
969
|
+
if (/Installed via .+ Please update/i.test(trimmed)) continue;
|
|
970
|
+
if (/Waiting for auth/i.test(trimmed)) continue;
|
|
971
|
+
if (/ℹ Gemini CLI/i.test(trimmed)) continue;
|
|
972
|
+
if (/^[\u280b\u2819\u2839\u2838\u283c\u2834\u2826\u2827\u2807\u280f\s]+$/.test(trimmed)) continue;
|
|
973
|
+
if (trimmed.length <= 2 && /^[!@#$%^&*()_+=\-<>?/\\|~`]+$/.test(trimmed)) continue;
|
|
974
|
+
cleaned.push(line);
|
|
975
|
+
}
|
|
976
|
+
return cleaned.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
977
|
+
}
|
|
936
978
|
function findGeminiBinary() {
|
|
937
979
|
const isWin = os3.platform() === "win32";
|
|
938
980
|
const cmd = isWin ? "where gemini" : "which gemini";
|
|
@@ -983,12 +1025,20 @@ var init_gemini_cli = __esm({
|
|
|
983
1025
|
ready = false;
|
|
984
1026
|
startupBuffer = "";
|
|
985
1027
|
startupDialogDismissed = false;
|
|
1028
|
+
// Raw PTY I/O (터미널 뷰용)
|
|
1029
|
+
onPtyData = null;
|
|
1030
|
+
ptyOutputBuffer = "";
|
|
1031
|
+
ptyOutputFlushTimer = null;
|
|
986
1032
|
constructor(workingDir) {
|
|
987
1033
|
this.workingDir = workingDir.startsWith("~") ? workingDir.replace(/^~/, os3.homedir()) : workingDir;
|
|
988
1034
|
}
|
|
989
1035
|
setOnStatusChange(callback) {
|
|
990
1036
|
this.onStatusChange = callback;
|
|
991
1037
|
}
|
|
1038
|
+
/** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
|
|
1039
|
+
setOnPtyData(callback) {
|
|
1040
|
+
this.onPtyData = callback;
|
|
1041
|
+
}
|
|
992
1042
|
/**
|
|
993
1043
|
* PTY로 gemini 프로세스 시작 (인터랙티브 모드)
|
|
994
1044
|
*/
|
|
@@ -1000,7 +1050,7 @@ var init_gemini_cli = __esm({
|
|
|
1000
1050
|
console.log(`[GeminiAdapter] Spawning interactive session in ${this.workingDir}`);
|
|
1001
1051
|
console.log(`[GeminiAdapter] Binary: ${geminiBin}`);
|
|
1002
1052
|
const shell = isWin ? "cmd.exe" : process.env.SHELL || "/bin/zsh";
|
|
1003
|
-
const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c",
|
|
1053
|
+
const shellArgs = isWin ? ["/c", `${geminiBin} --yolo`] : ["-l", "-c", `${geminiBin} --yolo`];
|
|
1004
1054
|
console.log(`[GeminiAdapter] Shell: ${shell} ${shellArgs.join(" ")}`);
|
|
1005
1055
|
this.ptyProcess = pty.spawn(shell, shellArgs, {
|
|
1006
1056
|
name: "xterm-256color",
|
|
@@ -1013,6 +1063,18 @@ var init_gemini_cli = __esm({
|
|
|
1013
1063
|
});
|
|
1014
1064
|
this.ptyProcess.onData((data) => {
|
|
1015
1065
|
this.handleOutput(data);
|
|
1066
|
+
if (this.onPtyData) {
|
|
1067
|
+
this.ptyOutputBuffer += data;
|
|
1068
|
+
if (!this.ptyOutputFlushTimer) {
|
|
1069
|
+
this.ptyOutputFlushTimer = setTimeout(() => {
|
|
1070
|
+
if (this.ptyOutputBuffer && this.onPtyData) {
|
|
1071
|
+
this.onPtyData(this.ptyOutputBuffer);
|
|
1072
|
+
}
|
|
1073
|
+
this.ptyOutputBuffer = "";
|
|
1074
|
+
this.ptyOutputFlushTimer = null;
|
|
1075
|
+
}, 50);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1016
1078
|
});
|
|
1017
1079
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
1018
1080
|
console.log(`[GeminiAdapter] Process exited with code ${exitCode}`);
|
|
@@ -1091,6 +1153,8 @@ var init_gemini_cli = __esm({
|
|
|
1091
1153
|
}
|
|
1092
1154
|
}
|
|
1093
1155
|
response = lines.join("\n").trim();
|
|
1156
|
+
const lastUserText = this.messages.length > 0 && this.messages[this.messages.length - 1].role === "user" ? this.messages[this.messages.length - 1].content : void 0;
|
|
1157
|
+
response = cleanGeminiResponse(response, lastUserText);
|
|
1094
1158
|
if (response) {
|
|
1095
1159
|
this.messages.push({
|
|
1096
1160
|
role: "assistant",
|
|
@@ -1185,6 +1249,21 @@ var init_gemini_cli = __esm({
|
|
|
1185
1249
|
isReady() {
|
|
1186
1250
|
return this.ready;
|
|
1187
1251
|
}
|
|
1252
|
+
/** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
|
|
1253
|
+
writeRaw(data) {
|
|
1254
|
+
if (this.ptyProcess) {
|
|
1255
|
+
this.ptyProcess.write(data);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
/** PTY 터미널 사이즈 변경 */
|
|
1259
|
+
resize(cols, rows) {
|
|
1260
|
+
if (this.ptyProcess) {
|
|
1261
|
+
try {
|
|
1262
|
+
this.ptyProcess.resize(cols, rows);
|
|
1263
|
+
} catch {
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1188
1267
|
/** 작업 디렉토리 변경 (세션 재시작) */
|
|
1189
1268
|
async changeWorkingDir(newDir) {
|
|
1190
1269
|
this.cancel();
|
|
@@ -1239,6 +1318,7 @@ var init_claude_cli = __esm({
|
|
|
1239
1318
|
startupBuffer = "";
|
|
1240
1319
|
bridge = null;
|
|
1241
1320
|
logBuffer = [];
|
|
1321
|
+
onPtyDataCallback = null;
|
|
1242
1322
|
// Patterns for Claude Code
|
|
1243
1323
|
PROMPT_PATTERNS = [
|
|
1244
1324
|
/❯\s*$/,
|
|
@@ -1282,6 +1362,7 @@ var init_claude_cli = __esm({
|
|
|
1282
1362
|
env: { ...process.env }
|
|
1283
1363
|
});
|
|
1284
1364
|
this.ptyProcess.onData((data) => {
|
|
1365
|
+
this.onPtyDataCallback?.(data);
|
|
1285
1366
|
this.handleOutput(data);
|
|
1286
1367
|
});
|
|
1287
1368
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
@@ -1407,6 +1488,25 @@ var init_claude_cli = __esm({
|
|
|
1407
1488
|
await new Promise((r) => setTimeout(r, 1e3));
|
|
1408
1489
|
await this.spawn();
|
|
1409
1490
|
}
|
|
1491
|
+
/** PTY raw data 콜백 설정 (터미널 뷰 스트리밍용) */
|
|
1492
|
+
setOnPtyData(callback) {
|
|
1493
|
+
this.onPtyDataCallback = callback;
|
|
1494
|
+
}
|
|
1495
|
+
/** Raw 키 입력을 PTY에 직접 전달 (터미널 뷰용) */
|
|
1496
|
+
writeRaw(data) {
|
|
1497
|
+
if (this.ptyProcess) {
|
|
1498
|
+
this.ptyProcess.write(data);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
/** PTY 터미널 사이즈 변경 */
|
|
1502
|
+
resize(cols, rows) {
|
|
1503
|
+
if (this.ptyProcess) {
|
|
1504
|
+
try {
|
|
1505
|
+
this.ptyProcess.resize(cols, rows);
|
|
1506
|
+
} catch {
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1410
1510
|
};
|
|
1411
1511
|
}
|
|
1412
1512
|
});
|
|
@@ -2729,7 +2829,13 @@ var init_daemon_p2p = __esm({
|
|
|
2729
2829
|
fileRequestHandler = null;
|
|
2730
2830
|
inputHandler = null;
|
|
2731
2831
|
commandHandler = null;
|
|
2832
|
+
ptyInputHandler = null;
|
|
2833
|
+
ptyResizeHandler = null;
|
|
2732
2834
|
_ssDebugDone = false;
|
|
2835
|
+
// PTY scrollback buffer per cliType (재연결 시 최근 출력 전송)
|
|
2836
|
+
ptyScrollback = /* @__PURE__ */ new Map();
|
|
2837
|
+
PTY_SCROLLBACK_MAX = 64 * 1024;
|
|
2838
|
+
// 64KB per CLI
|
|
2733
2839
|
get screenshotActive() {
|
|
2734
2840
|
for (const peer of this.peers.values()) {
|
|
2735
2841
|
if (peer.screenshotActive && peer.state === "connected") return true;
|
|
@@ -2973,7 +3079,10 @@ var init_daemon_p2p = __esm({
|
|
|
2973
3079
|
screenshotCh.onError((err) => log(`Screenshots error: ${err}`));
|
|
2974
3080
|
const filesCh = pc.createDataChannel("files");
|
|
2975
3081
|
entry.filesChannel = filesCh;
|
|
2976
|
-
filesCh.onOpen(() =>
|
|
3082
|
+
filesCh.onOpen(() => {
|
|
3083
|
+
log(`Files channel OPEN for peer ${pid}`);
|
|
3084
|
+
setTimeout(() => this.sendPtyScrollback(pid), 100);
|
|
3085
|
+
});
|
|
2977
3086
|
filesCh.onClosed(() => {
|
|
2978
3087
|
const peer = this.peers.get(pid);
|
|
2979
3088
|
if (peer?.filesChannel === filesCh) peer.filesChannel = null;
|
|
@@ -3021,6 +3130,18 @@ var init_daemon_p2p = __esm({
|
|
|
3021
3130
|
this.handleP2PCommand(peerId, parsed);
|
|
3022
3131
|
return;
|
|
3023
3132
|
}
|
|
3133
|
+
if (parsed.type === "pty_input") {
|
|
3134
|
+
if (this.ptyInputHandler && parsed.data) {
|
|
3135
|
+
this.ptyInputHandler(parsed.cliType || "gemini-cli", parsed.data);
|
|
3136
|
+
}
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
3139
|
+
if (parsed.type === "pty_resize") {
|
|
3140
|
+
if (this.ptyResizeHandler && parsed.cols && parsed.rows) {
|
|
3141
|
+
this.ptyResizeHandler(parsed.cliType || "gemini-cli", parsed.cols, parsed.rows);
|
|
3142
|
+
}
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
3024
3145
|
this.handleFileRequest(peerId, parsed);
|
|
3025
3146
|
} catch (e) {
|
|
3026
3147
|
log(`Parse error from peer ${peerId}: ${e?.message}`);
|
|
@@ -3044,6 +3165,44 @@ var init_daemon_p2p = __esm({
|
|
|
3044
3165
|
}
|
|
3045
3166
|
return sentAny;
|
|
3046
3167
|
}
|
|
3168
|
+
/** PTY 출력을 모든 connected peer에게 브로드캐스트 */
|
|
3169
|
+
broadcastPtyOutput(cliType, data) {
|
|
3170
|
+
const prev = this.ptyScrollback.get(cliType) || "";
|
|
3171
|
+
const updated = prev + data;
|
|
3172
|
+
this.ptyScrollback.set(
|
|
3173
|
+
cliType,
|
|
3174
|
+
updated.length > this.PTY_SCROLLBACK_MAX ? updated.slice(-this.PTY_SCROLLBACK_MAX) : updated
|
|
3175
|
+
);
|
|
3176
|
+
const msg = JSON.stringify({ type: "pty_output", cliType, data });
|
|
3177
|
+
let sentAny = false;
|
|
3178
|
+
for (const peer of this.peers.values()) {
|
|
3179
|
+
if (peer.state !== "connected" || !peer.filesChannel) continue;
|
|
3180
|
+
try {
|
|
3181
|
+
peer.filesChannel.sendMessage(msg);
|
|
3182
|
+
sentAny = true;
|
|
3183
|
+
} catch {
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
return sentAny;
|
|
3187
|
+
}
|
|
3188
|
+
/** Peer 연결 시 scrollback 전송 */
|
|
3189
|
+
sendPtyScrollback(peerId) {
|
|
3190
|
+
const peer = this.peers.get(peerId);
|
|
3191
|
+
if (!peer?.filesChannel) return;
|
|
3192
|
+
for (const [cliType, buffer] of this.ptyScrollback) {
|
|
3193
|
+
if (!buffer) continue;
|
|
3194
|
+
try {
|
|
3195
|
+
peer.filesChannel.sendMessage(JSON.stringify({
|
|
3196
|
+
type: "pty_output",
|
|
3197
|
+
cliType,
|
|
3198
|
+
data: buffer,
|
|
3199
|
+
scrollback: true
|
|
3200
|
+
}));
|
|
3201
|
+
log(`Sent PTY scrollback to peer ${peerId}: ${cliType} (${buffer.length} bytes)`);
|
|
3202
|
+
} catch {
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3047
3206
|
sendScreenshot(base64Data) {
|
|
3048
3207
|
let sentAny = false;
|
|
3049
3208
|
const buffer = Buffer.from(base64Data, "base64");
|
|
@@ -3081,6 +3240,12 @@ var init_daemon_p2p = __esm({
|
|
|
3081
3240
|
onCommand(handler) {
|
|
3082
3241
|
this.commandHandler = handler;
|
|
3083
3242
|
}
|
|
3243
|
+
onPtyInput(handler) {
|
|
3244
|
+
this.ptyInputHandler = handler;
|
|
3245
|
+
}
|
|
3246
|
+
onPtyResize(handler) {
|
|
3247
|
+
this.ptyResizeHandler = handler;
|
|
3248
|
+
}
|
|
3084
3249
|
// ─── P2P 명령/입력/파일 처리 ────────────────
|
|
3085
3250
|
async handleP2PCommand(peerId, msg) {
|
|
3086
3251
|
const { id, commandType, data } = msg;
|
|
@@ -3479,6 +3644,11 @@ var init_daemon_commands = __esm({
|
|
|
3479
3644
|
return this.handleAgentStreamSwitchSession(args);
|
|
3480
3645
|
case "agent_stream_focus":
|
|
3481
3646
|
return this.handleAgentStreamFocus(args);
|
|
3647
|
+
// ─── PTY Raw I/O (터미널 뷰) ─────────
|
|
3648
|
+
case "pty_input":
|
|
3649
|
+
return this.handlePtyInput(args);
|
|
3650
|
+
case "pty_resize":
|
|
3651
|
+
return this.handlePtyResize(args);
|
|
3482
3652
|
default:
|
|
3483
3653
|
return { success: false, error: `Unknown command: ${cmd}` };
|
|
3484
3654
|
}
|
|
@@ -3938,6 +4108,35 @@ var init_daemon_commands = __esm({
|
|
|
3938
4108
|
const ok = await this.agentStream.focusAgentEditor(this.getCdp(), agentType);
|
|
3939
4109
|
return { success: ok };
|
|
3940
4110
|
}
|
|
4111
|
+
// ─── PTY Raw I/O (터미널 뷰) ──────────────────────
|
|
4112
|
+
handlePtyInput(args) {
|
|
4113
|
+
const { cliType, data } = args || {};
|
|
4114
|
+
if (!data) return { success: false, error: "data required" };
|
|
4115
|
+
if (this.ctx.adapters) {
|
|
4116
|
+
const targetCli = cliType || "gemini-cli";
|
|
4117
|
+
for (const [, adapter] of this.ctx.adapters) {
|
|
4118
|
+
if (adapter.cliType === targetCli && typeof adapter.writeRaw === "function") {
|
|
4119
|
+
adapter.writeRaw(data);
|
|
4120
|
+
return { success: true };
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
return { success: false, error: `CLI adapter not found: ${cliType}` };
|
|
4125
|
+
}
|
|
4126
|
+
handlePtyResize(args) {
|
|
4127
|
+
const { cliType, cols, rows } = args || {};
|
|
4128
|
+
if (!cols || !rows) return { success: false, error: "cols and rows required" };
|
|
4129
|
+
if (this.ctx.adapters) {
|
|
4130
|
+
const targetCli = cliType || "gemini-cli";
|
|
4131
|
+
for (const [, adapter] of this.ctx.adapters) {
|
|
4132
|
+
if (adapter.cliType === targetCli && typeof adapter.resize === "function") {
|
|
4133
|
+
adapter.resize(cols, rows);
|
|
4134
|
+
return { success: true };
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
}
|
|
4138
|
+
return { success: false, error: `CLI adapter not found: ${cliType}` };
|
|
4139
|
+
}
|
|
3941
4140
|
};
|
|
3942
4141
|
}
|
|
3943
4142
|
});
|
|
@@ -4542,6 +4741,11 @@ var init_adhdev_daemon = __esm({
|
|
|
4542
4741
|
scriptLoaders = /* @__PURE__ */ new Map();
|
|
4543
4742
|
commandHandler = null;
|
|
4544
4743
|
screenshotTimer = null;
|
|
4744
|
+
lastStatusSentFull = false;
|
|
4745
|
+
lastStatusSentAt = 0;
|
|
4746
|
+
statusPendingThrottle = false;
|
|
4747
|
+
lastP2PStatusHash = "";
|
|
4748
|
+
statusReportCount = 0;
|
|
4545
4749
|
agentStreamManager = null;
|
|
4546
4750
|
agentStreamTimer = null;
|
|
4547
4751
|
running = false;
|
|
@@ -4592,7 +4796,7 @@ var init_adhdev_daemon = __esm({
|
|
|
4592
4796
|
setTimeout(() => this.sendUnifiedStatusReport(), 500);
|
|
4593
4797
|
},
|
|
4594
4798
|
onExtensionStatus: () => {
|
|
4595
|
-
this.
|
|
4799
|
+
this.throttledStatusReport();
|
|
4596
4800
|
},
|
|
4597
4801
|
onExtensionEvent: (ext, event, data) => {
|
|
4598
4802
|
if (event === "token_updated" && data.token) {
|
|
@@ -4705,6 +4909,22 @@ var init_adhdev_daemon = __esm({
|
|
|
4705
4909
|
return { id: req.id, success: result.success, entries: result.files, error: result.error };
|
|
4706
4910
|
}
|
|
4707
4911
|
});
|
|
4912
|
+
this.p2p.onPtyInput((cliType, data) => {
|
|
4913
|
+
for (const [, adapter] of this.adapters) {
|
|
4914
|
+
if (adapter.cliType === cliType && typeof adapter.writeRaw === "function") {
|
|
4915
|
+
adapter.writeRaw(data);
|
|
4916
|
+
return;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
});
|
|
4920
|
+
this.p2p.onPtyResize((cliType, cols, rows) => {
|
|
4921
|
+
for (const [, adapter] of this.adapters) {
|
|
4922
|
+
if (adapter.cliType === cliType && typeof adapter.resize === "function") {
|
|
4923
|
+
adapter.resize(cols, rows);
|
|
4924
|
+
return;
|
|
4925
|
+
}
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4708
4928
|
let ssDebugCount = 0;
|
|
4709
4929
|
this.screenshotTimer = setInterval(async () => {
|
|
4710
4930
|
const active = this.p2p?.screenshotActive;
|
|
@@ -4842,7 +5062,10 @@ var init_adhdev_daemon = __esm({
|
|
|
4842
5062
|
"agent_stream_new",
|
|
4843
5063
|
"agent_stream_list_chats",
|
|
4844
5064
|
"agent_stream_switch_session",
|
|
4845
|
-
"agent_stream_focus"
|
|
5065
|
+
"agent_stream_focus",
|
|
5066
|
+
// PTY I/O commands (터미널 뷰)
|
|
5067
|
+
"pty_input",
|
|
5068
|
+
"pty_resize"
|
|
4846
5069
|
];
|
|
4847
5070
|
for (const cmdType of directCdpCommands) {
|
|
4848
5071
|
this.bridge.on(cmdType, async (msg) => {
|
|
@@ -4911,7 +5134,19 @@ var init_adhdev_daemon = __esm({
|
|
|
4911
5134
|
const dir = args?.dir || process.cwd();
|
|
4912
5135
|
if (!cliType) throw new Error("cliType required");
|
|
4913
5136
|
const key = this.getCliKey(cliType, dir);
|
|
4914
|
-
|
|
5137
|
+
if (this.adapters.has(key)) {
|
|
5138
|
+
await this.stopCliSession(key);
|
|
5139
|
+
} else {
|
|
5140
|
+
let found = false;
|
|
5141
|
+
for (const [k, adapter] of this.adapters) {
|
|
5142
|
+
if (adapter.cliType === cliType) {
|
|
5143
|
+
await this.stopCliSession(k);
|
|
5144
|
+
found = true;
|
|
5145
|
+
break;
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
if (!found) console.log(import_chalk2.default.yellow(` \u26A0 No adapter found for ${cliType} (key: ${key})`));
|
|
5149
|
+
}
|
|
4915
5150
|
this.sendResult(msg, true, { cliType, dir });
|
|
4916
5151
|
return;
|
|
4917
5152
|
}
|
|
@@ -5033,6 +5268,17 @@ var init_adhdev_daemon = __esm({
|
|
|
5033
5268
|
adapter.setBridge(this.bridge);
|
|
5034
5269
|
}
|
|
5035
5270
|
adapter.setOnStatusChange(() => this.sendUnifiedStatusReport());
|
|
5271
|
+
if (typeof adapter.setOnPtyData === "function") {
|
|
5272
|
+
adapter.setOnPtyData((data) => {
|
|
5273
|
+
const sentViaP2P = this.p2pSender?.broadcastPtyOutput(adapter.cliType, data);
|
|
5274
|
+
if (!sentViaP2P && this.bridge) {
|
|
5275
|
+
this.bridge.sendMessage("pty_output", {
|
|
5276
|
+
cliType: adapter.cliType,
|
|
5277
|
+
data
|
|
5278
|
+
});
|
|
5279
|
+
}
|
|
5280
|
+
});
|
|
5281
|
+
}
|
|
5036
5282
|
this.adapters.set(key, adapter);
|
|
5037
5283
|
this.lastAgentStatus.set(key, "idle");
|
|
5038
5284
|
console.log(import_chalk2.default.green(` \u2713 CLI started: ${cliInfo.displayName} v${cliInfo.version || "unknown"} in ${resolvedDir}`));
|
|
@@ -5052,18 +5298,33 @@ var init_adhdev_daemon = __esm({
|
|
|
5052
5298
|
// ─── 통합 상태 보고 ─────────────────────────────
|
|
5053
5299
|
startStatusReporting() {
|
|
5054
5300
|
const scheduleNext = () => {
|
|
5055
|
-
const isProcessing = Array.from(this.adapters.values()).some((a) => a.isProcessing());
|
|
5056
|
-
const interval = isProcessing ? 2e3 : 15e3;
|
|
5057
5301
|
this.statusTimer = setTimeout(() => {
|
|
5058
5302
|
this.sendUnifiedStatusReport().catch(() => {
|
|
5059
5303
|
});
|
|
5060
5304
|
scheduleNext();
|
|
5061
|
-
},
|
|
5305
|
+
}, 15e3);
|
|
5062
5306
|
};
|
|
5063
5307
|
scheduleNext();
|
|
5064
5308
|
}
|
|
5309
|
+
/** Throttled status report: 최소 3초 간격 보장 */
|
|
5310
|
+
throttledStatusReport() {
|
|
5311
|
+
const now = Date.now();
|
|
5312
|
+
const elapsed = now - this.lastStatusSentAt;
|
|
5313
|
+
if (elapsed >= 3e3) {
|
|
5314
|
+
this.sendUnifiedStatusReport().catch(() => {
|
|
5315
|
+
});
|
|
5316
|
+
} else if (!this.statusPendingThrottle) {
|
|
5317
|
+
this.statusPendingThrottle = true;
|
|
5318
|
+
setTimeout(() => {
|
|
5319
|
+
this.statusPendingThrottle = false;
|
|
5320
|
+
this.sendUnifiedStatusReport().catch(() => {
|
|
5321
|
+
});
|
|
5322
|
+
}, 3e3 - elapsed);
|
|
5323
|
+
}
|
|
5324
|
+
}
|
|
5065
5325
|
async sendUnifiedStatusReport() {
|
|
5066
5326
|
if (!this.bridge?.isConnected()) return;
|
|
5327
|
+
this.lastStatusSentAt = Date.now();
|
|
5067
5328
|
if (this.cdpManagers.size > 0 && this.scriptLoaders.size > 0 && !this._cdpChatBusy) {
|
|
5068
5329
|
this._cdpChatBusy = true;
|
|
5069
5330
|
for (const [ideType, cdp] of this.cdpManagers) {
|
|
@@ -5148,7 +5409,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5148
5409
|
this.generatingStartedAt.set(key, now);
|
|
5149
5410
|
this.bridge?.sendMessage("status_event", {
|
|
5150
5411
|
event: "agent:generating_started",
|
|
5151
|
-
chatTitle: `${adapter.cliName}
|
|
5412
|
+
chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5152
5413
|
timestamp: now
|
|
5153
5414
|
});
|
|
5154
5415
|
} else if (lastStatus === "generating" && cliStatus === "idle") {
|
|
@@ -5156,7 +5417,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5156
5417
|
const duration = startedAt ? Math.round((now - startedAt) / 1e3) : 0;
|
|
5157
5418
|
this.bridge?.sendMessage("status_event", {
|
|
5158
5419
|
event: "agent:generating_completed",
|
|
5159
|
-
chatTitle: `${adapter.cliName}
|
|
5420
|
+
chatTitle: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5160
5421
|
duration,
|
|
5161
5422
|
timestamp: now
|
|
5162
5423
|
});
|
|
@@ -5182,7 +5443,7 @@ var init_adhdev_daemon = __esm({
|
|
|
5182
5443
|
activeChat: {
|
|
5183
5444
|
id: key,
|
|
5184
5445
|
status: cliStatus,
|
|
5185
|
-
title: `${adapter.cliName}
|
|
5446
|
+
title: `${adapter.cliName} \xB7 ${adapter.workingDir.split("/").filter(Boolean).pop() || "session"}`,
|
|
5186
5447
|
messages: cliMessages,
|
|
5187
5448
|
inputContent: "",
|
|
5188
5449
|
activeModal: adapterStatus.activeModal
|
|
@@ -5257,8 +5518,38 @@ var init_adhdev_daemon = __esm({
|
|
|
5257
5518
|
hostname: os8.hostname()
|
|
5258
5519
|
}
|
|
5259
5520
|
};
|
|
5260
|
-
|
|
5261
|
-
|
|
5521
|
+
const { timestamp: _ts, system: _sys, ...hashTarget } = payload;
|
|
5522
|
+
if (hashTarget.machine) {
|
|
5523
|
+
const { freeMem: _f, loadavg: _l, uptime: _u, ...stableMachine } = hashTarget.machine;
|
|
5524
|
+
hashTarget.machine = stableMachine;
|
|
5525
|
+
}
|
|
5526
|
+
const statusHash = this.simpleHash(JSON.stringify(hashTarget));
|
|
5527
|
+
if (statusHash !== this.lastP2PStatusHash) {
|
|
5528
|
+
this.lastP2PStatusHash = statusHash;
|
|
5529
|
+
this.p2p?.sendStatus(payload);
|
|
5530
|
+
}
|
|
5531
|
+
const p2pConnected = (this.p2p?.connectedPeerCount || 0) > 0;
|
|
5532
|
+
this.statusReportCount = (this.statusReportCount || 0) + 1;
|
|
5533
|
+
const forceFullSync = this.statusReportCount % 4 === 0;
|
|
5534
|
+
if (p2pConnected && this.lastStatusSentFull && !forceFullSync) {
|
|
5535
|
+
this.bridge.sendMessage("status_heartbeat", {
|
|
5536
|
+
timestamp: now,
|
|
5537
|
+
p2pConnected: true,
|
|
5538
|
+
p2pPeers: this.p2p?.connectedPeerCount || 0
|
|
5539
|
+
});
|
|
5540
|
+
} else {
|
|
5541
|
+
this.bridge.sendMessage("status_report", payload);
|
|
5542
|
+
this.lastStatusSentFull = true;
|
|
5543
|
+
}
|
|
5544
|
+
}
|
|
5545
|
+
/** Fast string hash (FNV-1a 32-bit) */
|
|
5546
|
+
simpleHash(s) {
|
|
5547
|
+
let h = 2166136261;
|
|
5548
|
+
for (let i = 0; i < s.length; i++) {
|
|
5549
|
+
h ^= s.charCodeAt(i);
|
|
5550
|
+
h = h * 16777619 >>> 0;
|
|
5551
|
+
}
|
|
5552
|
+
return h.toString(36);
|
|
5262
5553
|
}
|
|
5263
5554
|
sendResult(msg, success, extra) {
|
|
5264
5555
|
if (!msg.id) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adhdev",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"description": "ADHDev CLI — Detect, install and configure your IDE + AI agent extensions",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
"tsx": "^4.19.0",
|
|
55
55
|
"typescript": "^5.5.0"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|