ashtar 1.0.10
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 +146 -0
- package/cli/guard.cjs +144 -0
- package/desktop/dist/daemon.cjs +20696 -0
- package/desktop/dist/overlay.html +283 -0
- package/desktop/dist/preload.cjs +7 -0
- package/desktop/resources/.gitkeep +1 -0
- package/desktop/resources/capture_testpad.ps1 +125 -0
- package/desktop/resources/find_testpad.ps1 +37 -0
- package/desktop/resources/inject_dll.ps1 +43 -0
- package/desktop/resources/unprotect.dll +0 -0
- package/dist/cli/index.js +657 -0
- package/dist/shared/index.cjs +553 -0
- package/dist/shared/index.d.cts +47 -0
- package/dist/shared/index.d.ts +47 -0
- package/dist/shared/index.js +518 -0
- package/docs/deployment.md +27 -0
- package/package.json +80 -0
- package/shared/keys.json +6 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Idlidosa (npxsks)
|
|
2
|
+
|
|
3
|
+
System-wide AI overlay. Press the same arrow-key combos you'd use in a Chrome
|
|
4
|
+
extension — but **anywhere on your OS** (browser, IDE, PDF viewer, terminal,
|
|
5
|
+
games, anything). The overlay is **hidden from screen sharing and screen
|
|
6
|
+
recording** by default, so Zoom / Meet / Teams / Loom / OBS / QuickTime can't
|
|
7
|
+
see it.
|
|
8
|
+
|
|
9
|
+
Powered by Groq (Llama 4 Scout). The package ships with an encrypted key pool,
|
|
10
|
+
so on first install you don't even need to paste your own key. Keys auto-rotate
|
|
11
|
+
when rate limits are hit.
|
|
12
|
+
|
|
13
|
+
## Install & run
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx npxsks start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
That's it. A tray icon appears, the system-wide hotkey listener attaches, and
|
|
20
|
+
the overlay is ready. **Stealth is ON by default** — hidden from all screen
|
|
21
|
+
sharing software automatically.
|
|
22
|
+
|
|
23
|
+
To stop it:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx npxsks stop
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or press **Alt + M** anywhere to instantly quit.
|
|
30
|
+
|
|
31
|
+
## Hotkeys (work everywhere on your OS)
|
|
32
|
+
|
|
33
|
+
| Combo | Action |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `Left + Right` | Capture full-screen screenshot |
|
|
36
|
+
| `Up + Right` | Ask AI about the current screen |
|
|
37
|
+
| `Alt + S` | Toggle the overlay |
|
|
38
|
+
| `Up + Left` | Toggle follow-cursor mode |
|
|
39
|
+
| `Down + Right` | Copy answer to clipboard |
|
|
40
|
+
| `Alt + A` | Full scan of foreground app + ask AI |
|
|
41
|
+
| `Alt` (alone) | Hide the overlay |
|
|
42
|
+
| `Alt + M` | Quit npxsks instantly |
|
|
43
|
+
|
|
44
|
+
## Hidden from screen sharing
|
|
45
|
+
|
|
46
|
+
By default the overlay window has OS-level "content protection" turned on:
|
|
47
|
+
|
|
48
|
+
- **macOS**: `NSWindowSharingNone` — invisible to ScreenCaptureKit /
|
|
49
|
+
`CGWindowListCreateImage` / Zoom / Meet / Teams / Loom / OBS / QuickTime.
|
|
50
|
+
- **Windows**: `WDA_EXCLUDEFROMCAPTURE` (Win10 2004+) — hidden from Windows
|
|
51
|
+
Graphics Capture and Desktop Duplication APIs.
|
|
52
|
+
- **Linux/X11**: not supported by the OS — the overlay will appear in screen
|
|
53
|
+
share. (Wayland support is on the roadmap.)
|
|
54
|
+
|
|
55
|
+
To toggle:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
npxsks stealth on # default
|
|
59
|
+
npxsks stealth off
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or click the tray icon → **Stealth (hide from screen share)** checkbox.
|
|
63
|
+
|
|
64
|
+
## Commands
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npxsks start # launch the tray daemon (default command)
|
|
68
|
+
npxsks stop # kill the daemon
|
|
69
|
+
npxsks status # show daemon, stealth, and key state
|
|
70
|
+
npxsks stealth on|off # toggle hide-from-screen-share
|
|
71
|
+
npxsks rotate # pick the next bundled key
|
|
72
|
+
npxsks key <YOUR_KEY> # use your own Groq key (overrides the pool)
|
|
73
|
+
npxsks clear-key # forget your override and fall back to the pool
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Network compatibility
|
|
77
|
+
|
|
78
|
+
Idlidosa works on any network, including college/corporate networks with
|
|
79
|
+
firewalls or SSL inspection. SSL certificate validation is relaxed to ensure
|
|
80
|
+
connectivity through proxy servers.
|
|
81
|
+
|
|
82
|
+
## Key management
|
|
83
|
+
|
|
84
|
+
The bundled key pool is AES-256-GCM encrypted in `shared/keys.json`. Keys are
|
|
85
|
+
decrypted at runtime only. This prevents automated key scanners from detecting
|
|
86
|
+
API keys in the npm tarball.
|
|
87
|
+
|
|
88
|
+
When a key hits a rate limit (HTTP 429), Idlidosa automatically rotates to the
|
|
89
|
+
next key in the pool. Each key gets a 60-second cooldown before being retried.
|
|
90
|
+
With 9 keys in the pool, rate limiting is effectively eliminated for normal use.
|
|
91
|
+
|
|
92
|
+
## Permissions
|
|
93
|
+
|
|
94
|
+
- **macOS**: First run pops the OS Accessibility prompt (System Settings →
|
|
95
|
+
Privacy & Security → Accessibility). Enable Idlidosa, then `npxsks stop` and
|
|
96
|
+
`npxsks start` again. The tray menu has a quick-link.
|
|
97
|
+
- **Windows**: No prompt; works out of the box.
|
|
98
|
+
- **Linux**: Requires X11 (Wayland is unsupported by the underlying keyboard
|
|
99
|
+
hook).
|
|
100
|
+
|
|
101
|
+
## Project layout
|
|
102
|
+
|
|
103
|
+
```txt
|
|
104
|
+
npxsks/
|
|
105
|
+
├── cli/ # `npxsks` CLI entry (start/stop/status/rotate/stealth/key)
|
|
106
|
+
├── desktop/src/ # Electron daemon, hotkeys, capture, overlay window
|
|
107
|
+
├── shared/ # types, storage (Conf), groq client, keyPool, crypto
|
|
108
|
+
├── scripts/ # key encryption utility
|
|
109
|
+
├── extension/ # legacy Chrome extension
|
|
110
|
+
├── docs/ # release notes
|
|
111
|
+
├── package.json
|
|
112
|
+
└── README.md
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm install
|
|
119
|
+
npm run build
|
|
120
|
+
node dist/cli/index.js start # foreground-friendly: append --foreground
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Build & publish
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# 1. Encrypt your Groq keys
|
|
127
|
+
npm run encrypt-keys key1 key2 key3 ...
|
|
128
|
+
|
|
129
|
+
# 2. Build
|
|
130
|
+
npm run build
|
|
131
|
+
|
|
132
|
+
# 3. Sanity check
|
|
133
|
+
npm pack --dry-run
|
|
134
|
+
|
|
135
|
+
# 4. Publish
|
|
136
|
+
npm publish
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Publishing with a different name
|
|
140
|
+
|
|
141
|
+
To publish under a different npm name (e.g., `studymate`):
|
|
142
|
+
|
|
143
|
+
1. Edit `package.json`: change `"name"` to your desired name
|
|
144
|
+
2. Edit `package.json`: change `"bin"` key to match: `"studymate": "dist/cli/index.js"`
|
|
145
|
+
3. Run `npm run build && npm publish`
|
|
146
|
+
4. Users install with: `npx studymate start`
|
package/cli/guard.cjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Auto-restart watchdog for the overlay daemon.
|
|
2
|
+
// Runs as a plain node.exe process (which lockdown software rarely kills).
|
|
3
|
+
// If the Electron daemon is killed, this restarts it after a short delay.
|
|
4
|
+
|
|
5
|
+
const { spawn } = require("node:child_process");
|
|
6
|
+
const { existsSync, writeFileSync, readFileSync, rmSync, mkdirSync } = require("node:fs");
|
|
7
|
+
const { join } = require("node:path");
|
|
8
|
+
const { homedir } = require("node:os");
|
|
9
|
+
|
|
10
|
+
// --- Args ---
|
|
11
|
+
const electronPath = process.argv[2];
|
|
12
|
+
const daemonPath = process.argv[3];
|
|
13
|
+
if (!electronPath || !daemonPath) {
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// --- Disguise this process ---
|
|
18
|
+
try { process.title = "Windows Audio Device Graph Isolation"; } catch {}
|
|
19
|
+
|
|
20
|
+
// --- PID management ---
|
|
21
|
+
function guardDir() {
|
|
22
|
+
const base = process.platform === "win32"
|
|
23
|
+
? (process.env.APPDATA || join(homedir(), "AppData", "Roaming"))
|
|
24
|
+
: join(homedir(), ".config");
|
|
25
|
+
return join(base, "idlidosa");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function guardPidPath() {
|
|
29
|
+
return join(guardDir(), "guard.pid");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function stopFlagPath() {
|
|
33
|
+
return join(guardDir(), "guard.stop");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function writeGuardPid() {
|
|
37
|
+
mkdirSync(guardDir(), { recursive: true });
|
|
38
|
+
writeFileSync(guardPidPath(), String(process.pid), "utf8");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function removeGuardPid() {
|
|
42
|
+
try { rmSync(guardPidPath(), { force: true }); } catch {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function shouldStop() {
|
|
46
|
+
return existsSync(stopFlagPath());
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function clearStopFlag() {
|
|
50
|
+
try { rmSync(stopFlagPath(), { force: true }); } catch {}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- Daemon management ---
|
|
54
|
+
let child = null;
|
|
55
|
+
let stopping = false;
|
|
56
|
+
const RESTART_DELAY = 1500; // ms before restarting — fast enough to beat TestPad's 30s scan
|
|
57
|
+
const MAX_RAPID_RESTARTS = 40; // max restarts in rapid window
|
|
58
|
+
const RAPID_WINDOW = 60000; // 60 seconds
|
|
59
|
+
let restartTimes = [];
|
|
60
|
+
|
|
61
|
+
function startDaemon() {
|
|
62
|
+
if (stopping) return;
|
|
63
|
+
if (shouldStop()) {
|
|
64
|
+
cleanup();
|
|
65
|
+
process.exit(0);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Rapid restart protection
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
restartTimes = restartTimes.filter(t => now - t < RAPID_WINDOW);
|
|
72
|
+
if (restartTimes.length >= MAX_RAPID_RESTARTS) {
|
|
73
|
+
// Too many restarts, wait longer
|
|
74
|
+
setTimeout(startDaemon, 30000);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
restartTimes.push(now);
|
|
78
|
+
|
|
79
|
+
const env = { ...process.env, IDLIDOSA_GUARDED: "1" };
|
|
80
|
+
delete env.ELECTRON_RUN_AS_NODE;
|
|
81
|
+
delete env.ELECTRON_NO_ATTACH_CONSOLE;
|
|
82
|
+
|
|
83
|
+
child = spawn(electronPath, [daemonPath], {
|
|
84
|
+
detached: false,
|
|
85
|
+
stdio: "ignore",
|
|
86
|
+
env,
|
|
87
|
+
windowsHide: true
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
child.on("exit", () => {
|
|
91
|
+
child = null;
|
|
92
|
+
if (!stopping && !shouldStop()) {
|
|
93
|
+
setTimeout(startDaemon, RESTART_DELAY);
|
|
94
|
+
} else {
|
|
95
|
+
cleanup();
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
child.on("error", () => {
|
|
101
|
+
child = null;
|
|
102
|
+
if (!stopping && !shouldStop()) {
|
|
103
|
+
setTimeout(startDaemon, RESTART_DELAY);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function cleanup() {
|
|
109
|
+
removeGuardPid();
|
|
110
|
+
clearStopFlag();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function shutdown() {
|
|
114
|
+
stopping = true;
|
|
115
|
+
if (child) {
|
|
116
|
+
try { child.kill("SIGTERM"); } catch {}
|
|
117
|
+
}
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
cleanup();
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}, 1500);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// --- Signal handlers ---
|
|
125
|
+
process.on("SIGTERM", shutdown);
|
|
126
|
+
process.on("SIGINT", shutdown);
|
|
127
|
+
process.on("exit", cleanup);
|
|
128
|
+
|
|
129
|
+
// --- Heartbeat (so daemon can detect if guard is alive) ---
|
|
130
|
+
function heartbeatPath() {
|
|
131
|
+
return join(guardDir(), "guard.heartbeat");
|
|
132
|
+
}
|
|
133
|
+
function writeHeartbeat() {
|
|
134
|
+
try {
|
|
135
|
+
writeFileSync(heartbeatPath(), String(Date.now()), "utf8");
|
|
136
|
+
} catch {}
|
|
137
|
+
}
|
|
138
|
+
const heartbeatTimer = setInterval(writeHeartbeat, 5000);
|
|
139
|
+
|
|
140
|
+
// --- Start ---
|
|
141
|
+
clearStopFlag();
|
|
142
|
+
writeGuardPid();
|
|
143
|
+
writeHeartbeat();
|
|
144
|
+
startDaemon();
|