aibroker 0.2.6 → 0.6.1
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/README.md +164 -4
- package/dist/adapters/iterm/core.d.ts +2 -0
- package/dist/adapters/iterm/core.d.ts.map +1 -1
- package/dist/adapters/iterm/core.js +13 -5
- package/dist/adapters/iterm/core.js.map +1 -1
- package/dist/adapters/iterm/iterm2-api.d.ts +20 -0
- package/dist/adapters/iterm/iterm2-api.d.ts.map +1 -0
- package/dist/adapters/iterm/iterm2-api.js +244 -0
- package/dist/adapters/iterm/iterm2-api.js.map +1 -0
- package/dist/adapters/iterm/sessions.d.ts.map +1 -1
- package/dist/adapters/iterm/sessions.js +3 -2
- package/dist/adapters/iterm/sessions.js.map +1 -1
- package/dist/adapters/kokoro/media.d.ts +2 -1
- package/dist/adapters/kokoro/media.d.ts.map +1 -1
- package/dist/adapters/kokoro/media.js +53 -5
- package/dist/adapters/kokoro/media.js.map +1 -1
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).d.ts +49 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).js +632 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-13-59).js +632 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).d.ts +49 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).js +614 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/adapters/pailot/gateway (SFConflict mnott 2026-03-06-21-15-46).js +614 -0
- package/dist/adapters/pailot/gateway.d.ts +48 -0
- package/dist/adapters/pailot/gateway.d.ts (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/adapters/pailot/gateway.d.ts (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/adapters/pailot/gateway.d.ts.map +1 -0
- package/dist/adapters/pailot/gateway.js +828 -0
- package/dist/adapters/pailot/gateway.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/adapters/pailot/gateway.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/adapters/pailot/gateway.js.map +1 -0
- package/dist/backend/api.d.ts +5 -1
- package/dist/backend/api.d.ts.map +1 -1
- package/dist/backend/api.js +74 -3
- package/dist/backend/api.js.map +1 -1
- package/dist/core/hybrid.d.ts +7 -0
- package/dist/core/hybrid.d.ts.map +1 -1
- package/dist/core/hybrid.js +33 -0
- package/dist/core/hybrid.js.map +1 -1
- package/dist/core/state.d.ts +3 -0
- package/dist/core/state.d.ts.map +1 -1
- package/dist/core/state.js +4 -0
- package/dist/core/state.js.map +1 -1
- package/dist/core/status-cache.d.ts +51 -0
- package/dist/core/status-cache.d.ts.map +1 -0
- package/dist/core/status-cache.js +62 -0
- package/dist/core/status-cache.js.map +1 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).d.ts +63 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).js +229 -0
- package/dist/daemon/adapter-registry (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/daemon/adapter-registry.d.ts +63 -0
- package/dist/daemon/adapter-registry.d.ts.map +1 -0
- package/dist/daemon/adapter-registry.js +240 -0
- package/dist/daemon/adapter-registry.js.map +1 -0
- package/dist/daemon/cli.d.ts +14 -0
- package/dist/daemon/cli.d.ts.map +1 -0
- package/dist/daemon/cli.js +150 -0
- package/dist/daemon/cli.js.map +1 -0
- package/dist/daemon/command-context.d.ts +24 -0
- package/dist/daemon/command-context.d.ts.map +1 -0
- package/dist/daemon/command-context.js +13 -0
- package/dist/daemon/command-context.js.map +1 -0
- package/dist/daemon/commands.d.ts +22 -0
- package/dist/daemon/commands.d.ts.map +1 -0
- package/dist/daemon/commands.js +632 -0
- package/dist/daemon/commands.js.map +1 -0
- package/dist/daemon/core-handlers.d.ts +24 -0
- package/dist/daemon/core-handlers.d.ts.map +1 -0
- package/dist/daemon/core-handlers.js +640 -0
- package/dist/daemon/core-handlers.js.map +1 -0
- package/dist/daemon/create-adapter.d.ts +22 -0
- package/dist/daemon/create-adapter.d.ts.map +1 -0
- package/dist/daemon/create-adapter.js +153 -0
- package/dist/daemon/create-adapter.js.map +1 -0
- package/dist/daemon/image-gen.d.ts +28 -0
- package/dist/daemon/image-gen.d.ts.map +1 -0
- package/dist/daemon/image-gen.js +97 -0
- package/dist/daemon/image-gen.js.map +1 -0
- package/dist/daemon/index.d.ts +12 -0
- package/dist/daemon/index.d.ts.map +1 -0
- package/dist/daemon/index.js +184 -0
- package/dist/daemon/index.js.map +1 -0
- package/dist/daemon/pai-projects.d.ts +68 -0
- package/dist/daemon/pai-projects.d.ts.map +1 -0
- package/dist/daemon/pai-projects.js +174 -0
- package/dist/daemon/pai-projects.js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).d.ts +12 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).js +252 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-13-59).js +252 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).d.ts +12 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).js +240 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-46).js +240 -0
- package/dist/daemon/screenshot.d.ts +12 -0
- package/dist/daemon/screenshot.d.ts (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/daemon/screenshot.d.ts (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/daemon/screenshot.d.ts.map +1 -0
- package/dist/daemon/screenshot.js +252 -0
- package/dist/daemon/screenshot.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/daemon/screenshot.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/daemon/screenshot.js.map +1 -0
- package/dist/daemon/session-content.d.ts +27 -0
- package/dist/daemon/session-content.d.ts.map +1 -0
- package/dist/daemon/session-content.js +76 -0
- package/dist/daemon/session-content.js.map +1 -0
- package/dist/daemon/vision.d.ts +46 -0
- package/dist/daemon/vision.d.ts.map +1 -0
- package/dist/daemon/vision.js +176 -0
- package/dist/daemon/vision.js.map +1 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc/client.d.ts +4 -1
- package/dist/ipc/client.d.ts.map +1 -1
- package/dist/ipc/client.js +10 -1
- package/dist/ipc/client.js.map +1 -1
- package/dist/ipc/validate.d.ts +52 -0
- package/dist/ipc/validate.d.ts.map +1 -0
- package/dist/ipc/validate.js +129 -0
- package/dist/ipc/validate.js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).d.ts +23 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).d.ts.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).js +595 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-56).js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-13-59).js +595 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).d.ts +23 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).d.ts.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).js +592 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-36).js.map +1 -0
- package/dist/mcp/index (SFConflict mnott 2026-03-06-21-15-46).js +592 -0
- package/dist/mcp/index.d.ts +23 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +660 -0
- package/dist/mcp/index.js (SFConflict mnott 2026-03-06-21-13-59).map +1 -0
- package/dist/mcp/index.js (SFConflict mnott 2026-03-06-21-15-46).map +1 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/types/adapter.d.ts +41 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +2 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/backend.d.ts +29 -1
- package/dist/types/backend.d.ts.map +1 -1
- package/dist/types/broker.d.ts +47 -0
- package/dist/types/broker.d.ts.map +1 -0
- package/dist/types/broker.js +21 -0
- package/dist/types/broker.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +12 -2
- package/templates/adapter/ONBOARDING_PROMPT.md +309 -0
- package/templates/adapter/README.md.tmpl +81 -0
- package/templates/adapter/package.json.tmpl +23 -0
- package/templates/adapter/src/watcher/cli.ts.tmpl +12 -0
- package/templates/adapter/src/watcher/commands.ts.tmpl +44 -0
- package/templates/adapter/src/watcher/connection.ts.tmpl +59 -0
- package/templates/adapter/src/watcher/index.ts.tmpl +201 -0
- package/templates/adapter/src/watcher/ipc-server.ts.tmpl +250 -0
- package/templates/adapter/src/watcher/send.ts.tmpl +62 -0
- package/templates/adapter/src/watcher/state.ts.tmpl +39 -0
- package/templates/adapter/tsconfig.json.tmpl +14 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* daemon/screenshot.ts — Screenshot capture for the AIBroker hub.
|
|
3
|
+
*
|
|
4
|
+
* Captures the iTerm2 window containing the active session and sends
|
|
5
|
+
* it back through the CommandContext reply channel. Handles screen-lock
|
|
6
|
+
* detection with text fallback.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from Whazaa's screenshot.ts — now transport-agnostic.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
14
|
+
import { runAppleScript, stripItermPrefix, } from "../adapters/iterm/core.js";
|
|
15
|
+
import { listClaudeSessions } from "../adapters/iterm/sessions.js";
|
|
16
|
+
import { log } from "../core/log.js";
|
|
17
|
+
import { activeClientId, activeItermSessionId, setActiveItermSessionId, sessionRegistry, } from "../core/state.js";
|
|
18
|
+
import { broadcastImage, broadcastText } from "../adapters/pailot/gateway.js";
|
|
19
|
+
let lastScreenshotContent = null;
|
|
20
|
+
function getActiveSessionContent() {
|
|
21
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
22
|
+
const itermId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
23
|
+
if (!itermId)
|
|
24
|
+
return null;
|
|
25
|
+
const result = spawnSync("osascript", [], {
|
|
26
|
+
input: `tell application "iTerm2"
|
|
27
|
+
repeat with w in windows
|
|
28
|
+
repeat with t in tabs of w
|
|
29
|
+
repeat with s in sessions of t
|
|
30
|
+
if id of s is "${itermId}" then
|
|
31
|
+
return contents of s
|
|
32
|
+
end if
|
|
33
|
+
end repeat
|
|
34
|
+
end repeat
|
|
35
|
+
end repeat
|
|
36
|
+
return ""
|
|
37
|
+
end tell`,
|
|
38
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
39
|
+
timeout: 10_000,
|
|
40
|
+
});
|
|
41
|
+
if (result.status !== 0 || result.signal)
|
|
42
|
+
return null;
|
|
43
|
+
const stdout = result.stdout?.toString().trim() ?? "";
|
|
44
|
+
return stdout || null;
|
|
45
|
+
}
|
|
46
|
+
async function handleTextScreenshot(ctx) {
|
|
47
|
+
try {
|
|
48
|
+
const candidates = [];
|
|
49
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
50
|
+
const primaryId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
51
|
+
if (primaryId)
|
|
52
|
+
candidates.push({ id: primaryId, source: "active" });
|
|
53
|
+
const registryEntries = [...sessionRegistry.values()]
|
|
54
|
+
.sort((a, b) => b.registeredAt - a.registeredAt);
|
|
55
|
+
for (const entry of registryEntries) {
|
|
56
|
+
const rid = stripItermPrefix(entry.itermSessionId);
|
|
57
|
+
if (rid && !candidates.some((c) => c.id === rid)) {
|
|
58
|
+
candidates.push({ id: rid, source: `registry:${entry.name}` });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (candidates.length === 0) {
|
|
62
|
+
await ctx.reply("Screen is locked and no iTerm2 session found — cannot capture.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const script = `tell application "iTerm2"
|
|
67
|
+
repeat with w in windows
|
|
68
|
+
repeat with t in tabs of w
|
|
69
|
+
repeat with s in sessions of t
|
|
70
|
+
if id of s is "${candidate.id}" then
|
|
71
|
+
return contents of s
|
|
72
|
+
end if
|
|
73
|
+
end repeat
|
|
74
|
+
end repeat
|
|
75
|
+
end repeat
|
|
76
|
+
return "::NOT_FOUND::"
|
|
77
|
+
end tell`;
|
|
78
|
+
const result = spawnSync("osascript", [], {
|
|
79
|
+
input: script,
|
|
80
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
81
|
+
timeout: 10_000,
|
|
82
|
+
});
|
|
83
|
+
const stdout = result.stdout?.toString().trim() ?? "";
|
|
84
|
+
if (result.signal === "SIGTERM" || result.status !== 0 || stdout === "::NOT_FOUND::" || stdout === "")
|
|
85
|
+
continue;
|
|
86
|
+
lastScreenshotContent = stdout;
|
|
87
|
+
// Send to PAILot (skip if the request came from PAILot — ctx.reply handles it)
|
|
88
|
+
if (ctx.source !== "pailot") {
|
|
89
|
+
const cleaned = stdout
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter((line) => !/^[─━═┄┈╌╍┅┉]{3,}\s*$/.test(line.trim()))
|
|
92
|
+
.filter((line) => line.trim() !== "")
|
|
93
|
+
.slice(-50)
|
|
94
|
+
.join("\n");
|
|
95
|
+
broadcastText(`Terminal capture (screen locked):\n\n${cleaned}`);
|
|
96
|
+
}
|
|
97
|
+
// Send to originating adapter
|
|
98
|
+
const maxLen = 4000;
|
|
99
|
+
const trimmed = stdout.length > maxLen ? "...\n" + stdout.slice(-maxLen) : stdout;
|
|
100
|
+
await ctx.reply(`*Terminal capture (screen locked):*\n\n\`\`\`\n${trimmed}\n\`\`\``);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
await ctx.reply(`Screen is locked — tried ${candidates.length} session(s) but none returned buffer content.`);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
107
|
+
log(`/ss: text capture error — ${msg}`);
|
|
108
|
+
await ctx.reply(`Screen is locked — text capture failed: ${msg}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export async function handleScreenshot(ctx) {
|
|
112
|
+
// Check if content is unchanged
|
|
113
|
+
const currentContent = getActiveSessionContent();
|
|
114
|
+
if (currentContent && lastScreenshotContent) {
|
|
115
|
+
const tail = (s) => s.split("\n").slice(-100).join("\n").trim();
|
|
116
|
+
if (tail(currentContent) === tail(lastScreenshotContent)) {
|
|
117
|
+
const lines = currentContent
|
|
118
|
+
.split("\n")
|
|
119
|
+
.filter((l) => !/^[─━═┄┈╌╍┅┉]{3,}\s*$/.test(l.trim()))
|
|
120
|
+
.filter((l) => l.trim() !== "")
|
|
121
|
+
.slice(-30)
|
|
122
|
+
.join("\n");
|
|
123
|
+
if (lines) {
|
|
124
|
+
if (ctx.source !== "pailot")
|
|
125
|
+
broadcastText(lines);
|
|
126
|
+
await ctx.reply(`*Terminal (unchanged):*\n\n\`\`\`\n${lines}\n\`\`\``);
|
|
127
|
+
log("/ss: content unchanged, sent tail as text");
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
lastScreenshotContent = currentContent;
|
|
133
|
+
// Check screen lock
|
|
134
|
+
try {
|
|
135
|
+
const lockCheck = spawnSync("sh", ["-c", "ioreg -n Root -d1 -a | grep -c CGSSessionScreenIsLocked"], { timeout: 5_000, encoding: "utf8" });
|
|
136
|
+
if (parseInt((lockCheck.stdout ?? "0").trim(), 10) > 0) {
|
|
137
|
+
log("/ss: screen is locked — falling back to terminal text capture");
|
|
138
|
+
await handleTextScreenshot(ctx);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch { /* proceed */ }
|
|
143
|
+
await ctx.reply("Capturing screenshot...");
|
|
144
|
+
const filePath = join(tmpdir(), `aibroker-screenshot-${Date.now()}.png`);
|
|
145
|
+
try {
|
|
146
|
+
// Resolve the window
|
|
147
|
+
let windowId;
|
|
148
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
149
|
+
let itermSessionId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
150
|
+
if (!itermSessionId) {
|
|
151
|
+
const registryEntries = [...sessionRegistry.values()]
|
|
152
|
+
.sort((a, b) => b.registeredAt - a.registeredAt);
|
|
153
|
+
const newest = registryEntries.find(e => e.itermSessionId);
|
|
154
|
+
if (newest?.itermSessionId) {
|
|
155
|
+
itermSessionId = stripItermPrefix(newest.itermSessionId);
|
|
156
|
+
setActiveItermSessionId(itermSessionId);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!itermSessionId) {
|
|
160
|
+
const liveSessions = listClaudeSessions();
|
|
161
|
+
if (liveSessions.length > 0) {
|
|
162
|
+
itermSessionId = liveSessions[0].id;
|
|
163
|
+
setActiveItermSessionId(liveSessions[0].id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (itermSessionId) {
|
|
167
|
+
const findAndRaiseScript = `tell application "iTerm2"
|
|
168
|
+
repeat with w in windows
|
|
169
|
+
set tabCount to count of tabs of w
|
|
170
|
+
repeat with tabIdx from 1 to tabCount
|
|
171
|
+
set t to tab tabIdx of w
|
|
172
|
+
repeat with s in sessions of t
|
|
173
|
+
if id of s is "${itermSessionId}" then
|
|
174
|
+
select t
|
|
175
|
+
set index of w to 1
|
|
176
|
+
activate
|
|
177
|
+
return (id of w as text)
|
|
178
|
+
end if
|
|
179
|
+
end repeat
|
|
180
|
+
end repeat
|
|
181
|
+
end repeat
|
|
182
|
+
return ""
|
|
183
|
+
end tell`;
|
|
184
|
+
const findResult = runAppleScript(findAndRaiseScript);
|
|
185
|
+
if (findResult && findResult !== "") {
|
|
186
|
+
windowId = findResult.trim();
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
runAppleScript('tell application "iTerm2" to activate');
|
|
190
|
+
const fb = runAppleScript(`tell application "iTerm2"\n set w to window 1\n activate\n return (id of w as text)\nend tell`) ?? "";
|
|
191
|
+
windowId = fb.trim();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
runAppleScript('tell application "iTerm2" to activate');
|
|
196
|
+
const fb = runAppleScript(`tell application "iTerm2"\n set w to window 1\n activate\n return (id of w as text)\nend tell`) ?? "";
|
|
197
|
+
windowId = fb.trim();
|
|
198
|
+
}
|
|
199
|
+
if (!windowId) {
|
|
200
|
+
await ctx.reply("Error: Could not get iTerm2 window ID.");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
204
|
+
const boundsScript = `tell application "iTerm2"
|
|
205
|
+
repeat with w in windows
|
|
206
|
+
if (id of w as text) is "${windowId}" then
|
|
207
|
+
set wBounds to bounds of w
|
|
208
|
+
set wx to item 1 of wBounds
|
|
209
|
+
set wy to item 2 of wBounds
|
|
210
|
+
set wx2 to item 3 of wBounds
|
|
211
|
+
set wy2 to item 4 of wBounds
|
|
212
|
+
return (wx as text) & "," & (wy as text) & "," & ((wx2 - wx) as text) & "," & ((wy2 - wy) as text)
|
|
213
|
+
end if
|
|
214
|
+
end repeat
|
|
215
|
+
return ""
|
|
216
|
+
end tell`;
|
|
217
|
+
const boundsResult = runAppleScript(boundsScript) ?? "";
|
|
218
|
+
const bounds = boundsResult.trim();
|
|
219
|
+
if (!bounds || !bounds.includes(",")) {
|
|
220
|
+
throw new Error("Could not get window bounds from iTerm2");
|
|
221
|
+
}
|
|
222
|
+
log(`/ss: capturing screen region ${bounds} (iTerm2 window ${windowId})`);
|
|
223
|
+
execSync(`/usr/sbin/screencapture -x -R ${bounds} "${filePath}"`, { timeout: 15_000 });
|
|
224
|
+
const buffer = readFileSync(filePath);
|
|
225
|
+
// Send to PAILot WebSocket clients (skip if request came from PAILot)
|
|
226
|
+
if (ctx.source !== "pailot")
|
|
227
|
+
broadcastImage(buffer, "Screenshot");
|
|
228
|
+
// Send to originating adapter
|
|
229
|
+
await ctx.replyImage(buffer, "Screenshot");
|
|
230
|
+
log("/ss: screenshot sent");
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
234
|
+
log(`/ss: screencapture failed — ${msg}`);
|
|
235
|
+
// If screencapture failed (e.g. screen locked but ioreg didn't detect it,
|
|
236
|
+
// or "could not create image from rect"), fall back to text mode
|
|
237
|
+
log("/ss: falling back to terminal text capture");
|
|
238
|
+
try {
|
|
239
|
+
unlinkSync(filePath);
|
|
240
|
+
}
|
|
241
|
+
catch { /* ignore */ }
|
|
242
|
+
await handleTextScreenshot(ctx);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
try {
|
|
247
|
+
unlinkSync(filePath);
|
|
248
|
+
}
|
|
249
|
+
catch { /* ignore */ }
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=screenshot.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* daemon/screenshot.ts — Screenshot capture for the AIBroker hub.
|
|
3
|
+
*
|
|
4
|
+
* Captures the iTerm2 window containing the active session and sends
|
|
5
|
+
* it back through the CommandContext reply channel. Handles screen-lock
|
|
6
|
+
* detection with text fallback.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from Whazaa's screenshot.ts — now transport-agnostic.
|
|
9
|
+
*/
|
|
10
|
+
import type { CommandContext } from "./command-context.js";
|
|
11
|
+
export declare function handleScreenshot(ctx: CommandContext): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=screenshot%20(SFConflict%20mnott%202026-03-06-21-15-36).d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot (SFConflict mnott 2026-03-06-21-15-36).d.ts","sourceRoot":"","sources":["../../src/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqBH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAyG3D,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA6IzE"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* daemon/screenshot.ts — Screenshot capture for the AIBroker hub.
|
|
3
|
+
*
|
|
4
|
+
* Captures the iTerm2 window containing the active session and sends
|
|
5
|
+
* it back through the CommandContext reply channel. Handles screen-lock
|
|
6
|
+
* detection with text fallback.
|
|
7
|
+
*
|
|
8
|
+
* Extracted from Whazaa's screenshot.ts — now transport-agnostic.
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { spawnSync, execSync } from "node:child_process";
|
|
14
|
+
import { runAppleScript, stripItermPrefix, } from "../adapters/iterm/core.js";
|
|
15
|
+
import { listClaudeSessions } from "../adapters/iterm/sessions.js";
|
|
16
|
+
import { log } from "../core/log.js";
|
|
17
|
+
import { activeClientId, activeItermSessionId, setActiveItermSessionId, sessionRegistry, } from "../core/state.js";
|
|
18
|
+
import { broadcastImage, broadcastText } from "../adapters/pailot/gateway.js";
|
|
19
|
+
let lastScreenshotContent = null;
|
|
20
|
+
function getActiveSessionContent() {
|
|
21
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
22
|
+
const itermId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
23
|
+
if (!itermId)
|
|
24
|
+
return null;
|
|
25
|
+
const result = spawnSync("osascript", [], {
|
|
26
|
+
input: `tell application "iTerm2"
|
|
27
|
+
repeat with w in windows
|
|
28
|
+
repeat with t in tabs of w
|
|
29
|
+
repeat with s in sessions of t
|
|
30
|
+
if id of s is "${itermId}" then
|
|
31
|
+
return contents of s
|
|
32
|
+
end if
|
|
33
|
+
end repeat
|
|
34
|
+
end repeat
|
|
35
|
+
end repeat
|
|
36
|
+
return ""
|
|
37
|
+
end tell`,
|
|
38
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
39
|
+
timeout: 10_000,
|
|
40
|
+
});
|
|
41
|
+
if (result.status !== 0 || result.signal)
|
|
42
|
+
return null;
|
|
43
|
+
const stdout = result.stdout?.toString().trim() ?? "";
|
|
44
|
+
return stdout || null;
|
|
45
|
+
}
|
|
46
|
+
async function handleTextScreenshot(ctx) {
|
|
47
|
+
try {
|
|
48
|
+
const candidates = [];
|
|
49
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
50
|
+
const primaryId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
51
|
+
if (primaryId)
|
|
52
|
+
candidates.push({ id: primaryId, source: "active" });
|
|
53
|
+
const registryEntries = [...sessionRegistry.values()]
|
|
54
|
+
.sort((a, b) => b.registeredAt - a.registeredAt);
|
|
55
|
+
for (const entry of registryEntries) {
|
|
56
|
+
const rid = stripItermPrefix(entry.itermSessionId);
|
|
57
|
+
if (rid && !candidates.some((c) => c.id === rid)) {
|
|
58
|
+
candidates.push({ id: rid, source: `registry:${entry.name}` });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (candidates.length === 0) {
|
|
62
|
+
await ctx.reply("Screen is locked and no iTerm2 session found — cannot capture.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const candidate of candidates) {
|
|
66
|
+
const script = `tell application "iTerm2"
|
|
67
|
+
repeat with w in windows
|
|
68
|
+
repeat with t in tabs of w
|
|
69
|
+
repeat with s in sessions of t
|
|
70
|
+
if id of s is "${candidate.id}" then
|
|
71
|
+
return contents of s
|
|
72
|
+
end if
|
|
73
|
+
end repeat
|
|
74
|
+
end repeat
|
|
75
|
+
end repeat
|
|
76
|
+
return "::NOT_FOUND::"
|
|
77
|
+
end tell`;
|
|
78
|
+
const result = spawnSync("osascript", [], {
|
|
79
|
+
input: script,
|
|
80
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
81
|
+
timeout: 10_000,
|
|
82
|
+
});
|
|
83
|
+
const stdout = result.stdout?.toString().trim() ?? "";
|
|
84
|
+
if (result.signal === "SIGTERM" || result.status !== 0 || stdout === "::NOT_FOUND::" || stdout === "")
|
|
85
|
+
continue;
|
|
86
|
+
lastScreenshotContent = stdout;
|
|
87
|
+
// Send to PAILot
|
|
88
|
+
const cleaned = stdout
|
|
89
|
+
.split("\n")
|
|
90
|
+
.filter((line) => !/^[─━═┄┈╌╍┅┉]{3,}\s*$/.test(line.trim()))
|
|
91
|
+
.filter((line) => line.trim() !== "")
|
|
92
|
+
.slice(-50)
|
|
93
|
+
.join("\n");
|
|
94
|
+
broadcastText(`Terminal capture (screen locked):\n\n${cleaned}`);
|
|
95
|
+
// Send to originating adapter
|
|
96
|
+
const maxLen = 4000;
|
|
97
|
+
const trimmed = stdout.length > maxLen ? "...\n" + stdout.slice(-maxLen) : stdout;
|
|
98
|
+
await ctx.reply(`*Terminal capture (screen locked):*\n\n\`\`\`\n${trimmed}\n\`\`\``);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
await ctx.reply(`Screen is locked — tried ${candidates.length} session(s) but none returned buffer content.`);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
105
|
+
log(`/ss: text capture error — ${msg}`);
|
|
106
|
+
await ctx.reply(`Screen is locked — text capture failed: ${msg}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export async function handleScreenshot(ctx) {
|
|
110
|
+
// Check if content is unchanged
|
|
111
|
+
const currentContent = getActiveSessionContent();
|
|
112
|
+
if (currentContent && lastScreenshotContent) {
|
|
113
|
+
const tail = (s) => s.split("\n").slice(-100).join("\n").trim();
|
|
114
|
+
if (tail(currentContent) === tail(lastScreenshotContent)) {
|
|
115
|
+
const lines = currentContent
|
|
116
|
+
.split("\n")
|
|
117
|
+
.filter((l) => !/^[─━═┄┈╌╍┅┉]{3,}\s*$/.test(l.trim()))
|
|
118
|
+
.filter((l) => l.trim() !== "")
|
|
119
|
+
.slice(-30)
|
|
120
|
+
.join("\n");
|
|
121
|
+
if (lines) {
|
|
122
|
+
broadcastText(lines);
|
|
123
|
+
await ctx.reply(`*Terminal (unchanged):*\n\n\`\`\`\n${lines}\n\`\`\``);
|
|
124
|
+
log("/ss: content unchanged, sent tail as text");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
lastScreenshotContent = currentContent;
|
|
130
|
+
// Check screen lock
|
|
131
|
+
try {
|
|
132
|
+
const lockCheck = spawnSync("sh", ["-c", "ioreg -n Root -d1 -a | grep -c CGSSessionScreenIsLocked"], { timeout: 5_000, encoding: "utf8" });
|
|
133
|
+
if (parseInt((lockCheck.stdout ?? "0").trim(), 10) > 0) {
|
|
134
|
+
log("/ss: screen is locked — falling back to terminal text capture");
|
|
135
|
+
await handleTextScreenshot(ctx);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch { /* proceed */ }
|
|
140
|
+
await ctx.reply("Capturing screenshot...");
|
|
141
|
+
const filePath = join(tmpdir(), `aibroker-screenshot-${Date.now()}.png`);
|
|
142
|
+
try {
|
|
143
|
+
// Resolve the window
|
|
144
|
+
let windowId;
|
|
145
|
+
const activeEntry = activeClientId ? sessionRegistry.get(activeClientId) : undefined;
|
|
146
|
+
let itermSessionId = stripItermPrefix((activeItermSessionId || undefined) ?? activeEntry?.itermSessionId);
|
|
147
|
+
if (!itermSessionId) {
|
|
148
|
+
const registryEntries = [...sessionRegistry.values()]
|
|
149
|
+
.sort((a, b) => b.registeredAt - a.registeredAt);
|
|
150
|
+
const newest = registryEntries.find(e => e.itermSessionId);
|
|
151
|
+
if (newest?.itermSessionId) {
|
|
152
|
+
itermSessionId = stripItermPrefix(newest.itermSessionId);
|
|
153
|
+
setActiveItermSessionId(itermSessionId);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (!itermSessionId) {
|
|
157
|
+
const liveSessions = listClaudeSessions();
|
|
158
|
+
if (liveSessions.length > 0) {
|
|
159
|
+
itermSessionId = liveSessions[0].id;
|
|
160
|
+
setActiveItermSessionId(liveSessions[0].id);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (itermSessionId) {
|
|
164
|
+
const findAndRaiseScript = `tell application "iTerm2"
|
|
165
|
+
repeat with w in windows
|
|
166
|
+
set tabCount to count of tabs of w
|
|
167
|
+
repeat with tabIdx from 1 to tabCount
|
|
168
|
+
set t to tab tabIdx of w
|
|
169
|
+
repeat with s in sessions of t
|
|
170
|
+
if id of s is "${itermSessionId}" then
|
|
171
|
+
select t
|
|
172
|
+
set index of w to 1
|
|
173
|
+
activate
|
|
174
|
+
return (id of w as text)
|
|
175
|
+
end if
|
|
176
|
+
end repeat
|
|
177
|
+
end repeat
|
|
178
|
+
end repeat
|
|
179
|
+
return ""
|
|
180
|
+
end tell`;
|
|
181
|
+
const findResult = runAppleScript(findAndRaiseScript);
|
|
182
|
+
if (findResult && findResult !== "") {
|
|
183
|
+
windowId = findResult.trim();
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
runAppleScript('tell application "iTerm2" to activate');
|
|
187
|
+
const fb = runAppleScript(`tell application "iTerm2"\n set w to window 1\n activate\n return (id of w as text)\nend tell`) ?? "";
|
|
188
|
+
windowId = fb.trim();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
runAppleScript('tell application "iTerm2" to activate');
|
|
193
|
+
const fb = runAppleScript(`tell application "iTerm2"\n set w to window 1\n activate\n return (id of w as text)\nend tell`) ?? "";
|
|
194
|
+
windowId = fb.trim();
|
|
195
|
+
}
|
|
196
|
+
if (!windowId) {
|
|
197
|
+
await ctx.reply("Error: Could not get iTerm2 window ID.");
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
201
|
+
const boundsScript = `tell application "iTerm2"
|
|
202
|
+
repeat with w in windows
|
|
203
|
+
if (id of w as text) is "${windowId}" then
|
|
204
|
+
set wBounds to bounds of w
|
|
205
|
+
set wx to item 1 of wBounds
|
|
206
|
+
set wy to item 2 of wBounds
|
|
207
|
+
set wx2 to item 3 of wBounds
|
|
208
|
+
set wy2 to item 4 of wBounds
|
|
209
|
+
return (wx as text) & "," & (wy as text) & "," & ((wx2 - wx) as text) & "," & ((wy2 - wy) as text)
|
|
210
|
+
end if
|
|
211
|
+
end repeat
|
|
212
|
+
return ""
|
|
213
|
+
end tell`;
|
|
214
|
+
const boundsResult = runAppleScript(boundsScript) ?? "";
|
|
215
|
+
const bounds = boundsResult.trim();
|
|
216
|
+
if (!bounds || !bounds.includes(",")) {
|
|
217
|
+
throw new Error("Could not get window bounds from iTerm2");
|
|
218
|
+
}
|
|
219
|
+
log(`/ss: capturing screen region ${bounds} (iTerm2 window ${windowId})`);
|
|
220
|
+
execSync(`/usr/sbin/screencapture -x -R ${bounds} "${filePath}"`, { timeout: 15_000 });
|
|
221
|
+
const buffer = readFileSync(filePath);
|
|
222
|
+
// Send to PAILot WebSocket clients
|
|
223
|
+
broadcastImage(buffer, "Screenshot");
|
|
224
|
+
// Send to originating adapter
|
|
225
|
+
await ctx.replyImage(buffer, "Screenshot");
|
|
226
|
+
log("/ss: screenshot sent");
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
230
|
+
log(`/ss: error — ${msg}`);
|
|
231
|
+
await ctx.reply(`Error taking screenshot: ${msg}`);
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
try {
|
|
235
|
+
unlinkSync(filePath);
|
|
236
|
+
}
|
|
237
|
+
catch { /* ignore */ }
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=screenshot%20(SFConflict%20mnott%202026-03-06-21-15-36).js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot (SFConflict mnott 2026-03-06-21-15-36).js","sourceRoot":"","sources":["../../src/daemon/screenshot (SFConflict mnott 2026-03-06-21-15-36).ts"],"names":[],"mappings":"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"}
|