@instaclaw/dispatch 0.1.0 → 0.1.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/dist/index.js +29 -6
- package/dist/permissions.d.ts +11 -3
- package/dist/permissions.js +99 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,21 +7,44 @@
|
|
|
7
7
|
* npx @instaclaw/dispatch --token XXX --vm HOST # Direct connect
|
|
8
8
|
*/
|
|
9
9
|
import { loadConfig, saveConfig, getConfigPath } from "./config.js";
|
|
10
|
-
import { checkPermissions, printPermissionGuide } from "./permissions.js";
|
|
10
|
+
import { checkPermissions, printPermissionGuide, runPermissionTest } from "./permissions.js";
|
|
11
11
|
import { connect, disconnect } from "./connection.js";
|
|
12
12
|
import { closeSupervisor } from "./supervisor.js";
|
|
13
13
|
import readline from "readline";
|
|
14
|
+
import os from "os";
|
|
14
15
|
async function main() {
|
|
15
16
|
console.log(`
|
|
16
17
|
InstaClaw Dispatch — Remote Computer Control
|
|
17
18
|
─────────────────────────────────────────────
|
|
18
19
|
`);
|
|
19
|
-
// 1. Check
|
|
20
|
-
|
|
21
|
-
if (!perms.accessibility || !perms.screenRecording) {
|
|
20
|
+
// 1. Check permissions (macOS only)
|
|
21
|
+
if (os.platform() === "darwin") {
|
|
22
22
|
console.log(" Checking permissions...");
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const perms = checkPermissions();
|
|
24
|
+
printPermissionGuide(perms);
|
|
25
|
+
if (perms.screenRecording === false || perms.accessibility === false) {
|
|
26
|
+
await waitForEnter();
|
|
27
|
+
// Re-check after user grants
|
|
28
|
+
const recheck = checkPermissions();
|
|
29
|
+
if (recheck.screenRecording === false) {
|
|
30
|
+
console.log(" Warning: Screen Recording still not granted. Screenshots may fail.\n");
|
|
31
|
+
}
|
|
32
|
+
if (recheck.accessibility === false) {
|
|
33
|
+
console.log(" Warning: Accessibility still not granted. Click/type may fail.\n");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Run a functional test to be sure
|
|
38
|
+
console.log(" Running permission test...");
|
|
39
|
+
const test = await runPermissionTest();
|
|
40
|
+
if (test.ok) {
|
|
41
|
+
console.log(" Permissions: all OK\n");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
console.log(` Warning: ${test.error}`);
|
|
45
|
+
console.log(" Continuing anyway — some commands may fail.\n");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
25
48
|
}
|
|
26
49
|
// 2. Load or create config
|
|
27
50
|
let config = loadConfig();
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
interface PermissionStatus {
|
|
2
|
-
accessibility: boolean;
|
|
3
|
-
screenRecording: boolean;
|
|
2
|
+
accessibility: boolean | "unknown";
|
|
3
|
+
screenRecording: boolean | "unknown";
|
|
4
4
|
platform: string;
|
|
5
5
|
}
|
|
6
6
|
export declare function checkPermissions(): PermissionStatus;
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Run a quick functional test: take a screenshot via usecomputer.
|
|
9
|
+
* If it fails, permissions are likely missing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function runPermissionTest(): Promise<{
|
|
12
|
+
ok: boolean;
|
|
13
|
+
error?: string;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function printPermissionGuide(status: PermissionStatus): void;
|
|
8
16
|
export {};
|
package/dist/permissions.js
CHANGED
|
@@ -1,44 +1,118 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
1
2
|
import os from "os";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
2
5
|
export function checkPermissions() {
|
|
3
6
|
const platform = os.platform();
|
|
4
7
|
if (platform === "darwin") {
|
|
5
8
|
return checkMacPermissions();
|
|
6
9
|
}
|
|
7
|
-
// Linux/Windows — no special permissions needed
|
|
10
|
+
// Linux/Windows — no special permissions needed
|
|
8
11
|
return { accessibility: true, screenRecording: true, platform };
|
|
9
12
|
}
|
|
10
13
|
function checkMacPermissions() {
|
|
11
|
-
let accessibility =
|
|
12
|
-
let screenRecording =
|
|
13
|
-
// Check
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
let accessibility = "unknown";
|
|
15
|
+
let screenRecording = "unknown";
|
|
16
|
+
// Check Accessibility via TCC database
|
|
17
|
+
// macOS stores permission grants in ~/Library/Application Support/com.apple.TCC/TCC.db
|
|
18
|
+
// and /Library/Application Support/com.apple.TCC/TCC.db (system)
|
|
19
|
+
// We can try a quick test: usecomputer mouse position (fails without accessibility)
|
|
20
|
+
try {
|
|
21
|
+
const result = execSync('osascript -e \'tell application "System Events" to get name of first process\'', { timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
22
|
+
accessibility = true;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// AppleScript fails without Accessibility permission
|
|
26
|
+
accessibility = false;
|
|
27
|
+
}
|
|
28
|
+
// Check Screen Recording by attempting a tiny screenshot
|
|
29
|
+
// CGWindowListCreateImage returns a null/empty image without Screen Recording permission
|
|
30
|
+
try {
|
|
31
|
+
const tmpFile = path.join(os.tmpdir(), `dispatch-perm-check-${Date.now()}.png`);
|
|
32
|
+
execSync(`screencapture -x -t png "${tmpFile}" 2>/dev/null`, { timeout: 5000 });
|
|
33
|
+
if (fs.existsSync(tmpFile)) {
|
|
34
|
+
const size = fs.statSync(tmpFile).size;
|
|
35
|
+
fs.unlinkSync(tmpFile);
|
|
36
|
+
// A valid screenshot is at least a few KB. A blocked capture may produce a tiny or empty file.
|
|
37
|
+
screenRecording = size > 500;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
screenRecording = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
screenRecording = false;
|
|
45
|
+
}
|
|
19
46
|
return { accessibility, screenRecording, platform: "darwin" };
|
|
20
47
|
}
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Run a quick functional test: take a screenshot via usecomputer.
|
|
50
|
+
* If it fails, permissions are likely missing.
|
|
51
|
+
*/
|
|
52
|
+
export async function runPermissionTest() {
|
|
53
|
+
try {
|
|
54
|
+
const uc = await import("usecomputer");
|
|
55
|
+
const tmpPath = path.join(os.tmpdir(), `dispatch-test-${Date.now()}.png`);
|
|
56
|
+
await uc.screenshot({ path: tmpPath, display: null, window: null, region: null, annotate: null });
|
|
57
|
+
if (fs.existsSync(tmpPath)) {
|
|
58
|
+
const size = fs.statSync(tmpPath).size;
|
|
59
|
+
fs.unlinkSync(tmpPath);
|
|
60
|
+
if (size > 500)
|
|
61
|
+
return { ok: true };
|
|
62
|
+
return { ok: false, error: "Screenshot captured but empty — Screen Recording permission may be missing" };
|
|
63
|
+
}
|
|
64
|
+
return { ok: false, error: "Screenshot file not created" };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const msg = String(err);
|
|
68
|
+
if (msg.includes("EVENT_POST_FAILED") || msg.includes("CGEvent")) {
|
|
69
|
+
return { ok: false, error: "Accessibility permission not granted" };
|
|
70
|
+
}
|
|
71
|
+
if (msg.includes("CAPTURE_FAILED")) {
|
|
72
|
+
return { ok: false, error: "Screen Recording permission not granted" };
|
|
73
|
+
}
|
|
74
|
+
return { ok: false, error: msg.slice(0, 100) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export function printPermissionGuide(status) {
|
|
78
|
+
const platform = status.platform;
|
|
23
79
|
if (platform === "darwin") {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
80
|
+
const terminal = process.env.TERM_PROGRAM || "your terminal app";
|
|
81
|
+
const terminalName = {
|
|
82
|
+
"Apple_Terminal": "Terminal",
|
|
83
|
+
"iTerm.app": "iTerm2",
|
|
84
|
+
"vscode": "Visual Studio Code",
|
|
85
|
+
"WarpTerminal": "Warp",
|
|
86
|
+
}[terminal] || terminal;
|
|
87
|
+
console.log("");
|
|
88
|
+
if (status.screenRecording === false) {
|
|
89
|
+
console.log(` Screen Recording: NOT GRANTED`);
|
|
90
|
+
console.log(` → System Settings → Privacy & Security → Screen Recording`);
|
|
91
|
+
console.log(` → Enable "${terminalName}"`);
|
|
92
|
+
console.log(` → You may need to restart ${terminalName} after granting`);
|
|
93
|
+
console.log("");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
console.log(` Screen Recording: OK`);
|
|
97
|
+
}
|
|
98
|
+
if (status.accessibility === false) {
|
|
99
|
+
console.log(` Accessibility: NOT GRANTED`);
|
|
100
|
+
console.log(` → System Settings → Privacy & Security → Accessibility`);
|
|
101
|
+
console.log(` → Enable "${terminalName}"`);
|
|
102
|
+
console.log("");
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.log(` Accessibility: OK`);
|
|
106
|
+
}
|
|
107
|
+
if (status.screenRecording === false || status.accessibility === false) {
|
|
108
|
+
console.log(` After granting permissions, press Enter to continue...`);
|
|
109
|
+
}
|
|
32
110
|
}
|
|
33
111
|
else if (platform === "linux") {
|
|
34
|
-
console.log(`
|
|
35
|
-
Linux: No special permissions needed.
|
|
36
|
-
Ensure DISPLAY is set if using X11.`);
|
|
112
|
+
console.log(` Linux: No special permissions needed.`);
|
|
37
113
|
}
|
|
38
114
|
else {
|
|
39
|
-
console.log(`
|
|
40
|
-
Windows: No special permissions needed.
|
|
41
|
-
Run as administrator if controlling elevated apps.`);
|
|
115
|
+
console.log(` Windows: No special permissions needed.`);
|
|
42
116
|
}
|
|
43
117
|
}
|
|
44
118
|
//# sourceMappingURL=permissions.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instaclaw/dispatch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Let your InstaClaw AI agent control your computer — screenshots, clicks, typing, and more via a secure WebSocket relay",
|
|
5
5
|
"bin": {
|
|
6
6
|
"instaclaw-dispatch": "./dist/index.js"
|