openzoo 0.51.15 → 0.51.16
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/launcher/chat-visibility.cjs +37 -0
- package/launcher/package.json +3 -2
- package/launcher/savings.cjs +24 -8
- package/package.json +3 -2
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { execFile } = require('node:child_process');
|
|
2
|
+
const fs = require('node:fs/promises');
|
|
3
|
+
function run(file, args) {
|
|
4
|
+
return new Promise((resolve, reject) => execFile(file, args, { timeout: 1800, windowsHide: true, maxBuffer: 8192 }, (error, stdout) => error ? reject(error) : resolve(stdout.trim())));
|
|
5
|
+
}
|
|
6
|
+
async function foregroundApp(platform = process.platform) {
|
|
7
|
+
if (platform === 'darwin') {
|
|
8
|
+
return JSON.parse(await run('/usr/bin/osascript', ['-l', 'JavaScript', '-e', 'ObjC.import("AppKit"); var a=$.NSWorkspace.sharedWorkspace.frontmostApplication; JSON.stringify({pid:Number(a.processIdentifier),name:ObjC.unwrap(a.bundleIdentifier)||""});']));
|
|
9
|
+
}
|
|
10
|
+
if (platform === 'win32') {
|
|
11
|
+
const script = 'Add-Type -TypeDefinition \'using System; using System.Runtime.InteropServices; public class OZFocus { [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow(); [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint p); }\'; [uint32]$foregroundPid=0; [void][OZFocus]::GetWindowThreadProcessId([OZFocus]::GetForegroundWindow(),[ref]$foregroundPid); $p=Get-Process -Id $foregroundPid -ErrorAction Stop; @{pid=$foregroundPid;name=$p.ProcessName}|ConvertTo-Json -Compress';
|
|
12
|
+
return JSON.parse(await run('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script]));
|
|
13
|
+
}
|
|
14
|
+
const pid = Number(await run('xdotool', ['getactivewindow', 'getwindowpid']));
|
|
15
|
+
return { pid, name: (await fs.readFile(`/proc/${pid}/comm`, 'utf8')).trim() };
|
|
16
|
+
}
|
|
17
|
+
function isChatApp(app) {
|
|
18
|
+
return /^(com\.openai\.(chat|chatgpt|codex)|chatgpt|codex)$/i.test(app?.name || '');
|
|
19
|
+
}
|
|
20
|
+
function watchChatVisibility(onChange, { read = foregroundApp, ownPid = process.pid, intervalMs = 750 } = {}) {
|
|
21
|
+
let active = false, busy = false, stopped = false;
|
|
22
|
+
async function check() {
|
|
23
|
+
if (busy || stopped) return;
|
|
24
|
+
busy = true;
|
|
25
|
+
try {
|
|
26
|
+
const app = await read();
|
|
27
|
+
// Clicking our controls keeps the last ChatGPT visibility state.
|
|
28
|
+
const next = app.pid === ownPid ? active : isChatApp(app);
|
|
29
|
+
if (!stopped) { active = next; onChange(active); }
|
|
30
|
+
} catch { if (!stopped) { active = false; onChange(false); } }
|
|
31
|
+
finally { busy = false; }
|
|
32
|
+
}
|
|
33
|
+
check();
|
|
34
|
+
const timer = setInterval(check, intervalMs);
|
|
35
|
+
return () => { stopped = true; clearInterval(timer); };
|
|
36
|
+
}
|
|
37
|
+
module.exports = { foregroundApp, isChatApp, watchChatVisibility };
|
package/launcher/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prepare:bundle": "node prepare.mjs",
|
|
8
8
|
"dist": "electron-builder",
|
|
9
|
-
"test": "node --test test.cjs"
|
|
9
|
+
"test": "node --test test.cjs chat-visibility.test.cjs"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"electron": "44.3.0",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"savings.html",
|
|
26
26
|
"savings-renderer.js",
|
|
27
27
|
"savings-toggle.html",
|
|
28
|
-
"savings-toggle.js"
|
|
28
|
+
"savings-toggle.js",
|
|
29
|
+
"chat-visibility.cjs"
|
|
29
30
|
],
|
|
30
31
|
"extraResources": [
|
|
31
32
|
{
|
package/launcher/savings.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const { BrowserWindow, screen, ipcMain, clipboard, shell } = require('electron');
|
|
2
2
|
const path = require('node:path');
|
|
3
|
-
|
|
3
|
+
const { watchChatVisibility } = require('./chat-visibility.cjs');
|
|
4
|
+
let pill, revealPill;
|
|
4
5
|
function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
|
|
5
|
-
if (pill && !pill.isDestroyed()) {
|
|
6
|
+
if (pill && !pill.isDestroyed()) { revealPill?.(); return pill; }
|
|
6
7
|
const { x, y, width, height } = screen.getPrimaryDisplay().workArea;
|
|
7
8
|
pill = new BrowserWindow({ width: 420, height: 270, x: x + width - 440, y: y + height - 290,
|
|
8
|
-
frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false,
|
|
9
|
+
show: false, frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false,
|
|
9
10
|
webPreferences: { preload: path.join(__dirname, 'savings-preload.cjs'), contextIsolation: true, sandbox: true, nodeIntegration: false } });
|
|
10
11
|
pill.setAlwaysOnTop(true, 'floating');
|
|
11
12
|
pill.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
@@ -15,25 +16,40 @@ function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
|
|
|
15
16
|
const window = pill;
|
|
16
17
|
const visibilityControl = new BrowserWindow({ width: 136, height: 40,
|
|
17
18
|
x: x + 16, y: y + height - 100,
|
|
18
|
-
frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false,
|
|
19
|
+
show: false, frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false,
|
|
19
20
|
webPreferences: { preload: path.join(__dirname, 'savings-preload.cjs'), contextIsolation: true, sandbox: true, nodeIntegration: false } });
|
|
20
21
|
visibilityControl.setAlwaysOnTop(true, 'floating');
|
|
21
22
|
visibilityControl.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
|
|
22
23
|
visibilityControl.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
|
|
23
24
|
visibilityControl.webContents.on('will-navigate', e => e.preventDefault());
|
|
24
25
|
visibilityControl.loadFile(path.join(__dirname, 'savings-toggle.html'));
|
|
26
|
+
let chatActive = false, userVisible = true;
|
|
27
|
+
const applyVisibility = () => {
|
|
28
|
+
if (window.isDestroyed() || visibilityControl.isDestroyed()) return;
|
|
29
|
+
if (chatActive) {
|
|
30
|
+
if (!visibilityControl.isVisible()) visibilityControl.showInactive();
|
|
31
|
+
if (userVisible && !window.isVisible()) window.showInactive();
|
|
32
|
+
if (!userVisible && window.isVisible()) window.hide();
|
|
33
|
+
} else {
|
|
34
|
+
window.hide();
|
|
35
|
+
visibilityControl.hide();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
revealPill = () => { userVisible = true; applyVisibility(); syncVisibility(); };
|
|
25
39
|
const syncVisibility = () => {
|
|
26
|
-
if (!visibilityControl.isDestroyed()) visibilityControl.webContents.send('pill-visibility',
|
|
40
|
+
if (!visibilityControl.isDestroyed()) visibilityControl.webContents.send('pill-visibility', userVisible);
|
|
27
41
|
};
|
|
28
42
|
const toggleVisibility = event => {
|
|
29
43
|
if (event.sender !== visibilityControl.webContents || window.isDestroyed()) return;
|
|
30
|
-
|
|
44
|
+
userVisible = !userVisible;
|
|
45
|
+
applyVisibility();
|
|
31
46
|
syncVisibility();
|
|
32
47
|
};
|
|
33
48
|
ipcMain.on('toggle-savings-visibility', toggleVisibility);
|
|
34
49
|
window.on('show', syncVisibility);
|
|
35
50
|
window.on('hide', syncVisibility);
|
|
36
51
|
visibilityControl.webContents.on('did-finish-load', syncVisibility);
|
|
52
|
+
const stopVisibility = watchChatVisibility(active => { chatActive = active; applyVisibility(); });
|
|
37
53
|
let busy = false, last = null, wallet = null, expandedHeight = 270;
|
|
38
54
|
const resize = height => {
|
|
39
55
|
const b = window.getBounds(), area = screen.getDisplayMatching(b).workArea;
|
|
@@ -84,9 +100,9 @@ function showSavings(port = Number(process.env.OPENZOO_SAVINGS_PORT) || 8402) {
|
|
|
84
100
|
}
|
|
85
101
|
});
|
|
86
102
|
const timer = setInterval(poll, 3000);
|
|
87
|
-
const close = event => { if (event.sender === window.webContents)
|
|
103
|
+
const close = event => { if (event.sender === window.webContents) { userVisible = false; applyVisibility(); syncVisibility(); } };
|
|
88
104
|
ipcMain.on('close-savings', close);
|
|
89
|
-
window.on('closed', () => { ipcMain.removeListener('toggle-savings-visibility', toggleVisibility); if (!visibilityControl.isDestroyed()) visibilityControl.destroy(); clearInterval(timer); for (const [name, fn] of [["collapse-savings",collapse],["open-deposit",deposit],["copy-deposit",copy],["deposit-card",card],["close-deposit",back]]) ipcMain.removeListener(name,fn); ipcMain.removeListener('close-savings', close); if (pill === window) pill = null; });
|
|
105
|
+
window.on('closed', () => { stopVisibility(); revealPill = null; ipcMain.removeListener('toggle-savings-visibility', toggleVisibility); if (!visibilityControl.isDestroyed()) visibilityControl.destroy(); clearInterval(timer); for (const [name, fn] of [["collapse-savings",collapse],["open-deposit",deposit],["copy-deposit",copy],["deposit-card",card],["close-deposit",back]]) ipcMain.removeListener(name,fn); ipcMain.removeListener('close-savings', close); if (pill === window) pill = null; });
|
|
90
106
|
return pill;
|
|
91
107
|
}
|
|
92
108
|
module.exports = { showSavings };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openzoo",
|
|
3
|
-
"version": "0.51.
|
|
3
|
+
"version": "0.51.16",
|
|
4
4
|
"description": "Local x402-paying proxy + MCP server for openzoo.fun — point any OpenAI-compatible harness (Cursor, Claude Code, aider, SDKs) at localhost and it pays per call from a local burner wallet. Solana and Base rails live; Robinhood experimental.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"launcher/savings-preload.cjs",
|
|
20
20
|
"launcher/savings-renderer.js",
|
|
21
21
|
"launcher/savings-toggle.html",
|
|
22
|
-
"launcher/savings-toggle.js"
|
|
22
|
+
"launcher/savings-toggle.js",
|
|
23
|
+
"launcher/chat-visibility.cjs"
|
|
23
24
|
],
|
|
24
25
|
"engines": {
|
|
25
26
|
"node": ">=18"
|