screenhand 0.4.5 → 0.4.6

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.
@@ -164,6 +164,48 @@ Docs: https://github.com/manushi4/Screenhand
164
164
  `);
165
165
  process.exit(0);
166
166
  }
167
+ // ── Startup cleanup: remove corrupt/empty state files ──
168
+ {
169
+ const stateDir = path.join(os.homedir(), ".screenhand", "state");
170
+ if (fs.existsSync(stateDir)) {
171
+ try {
172
+ const files = fs.readdirSync(stateDir).filter(f => f.endsWith(".json"));
173
+ let removed = 0;
174
+ for (const file of files) {
175
+ const filePath = path.join(stateDir, file);
176
+ try {
177
+ const stat = fs.statSync(filePath);
178
+ // Remove empty files
179
+ if (stat.size === 0) {
180
+ fs.unlinkSync(filePath);
181
+ removed++;
182
+ continue;
183
+ }
184
+ // Remove files older than 7 days
185
+ if (Date.now() - stat.mtimeMs > 7 * 24 * 60 * 60 * 1000) {
186
+ fs.unlinkSync(filePath);
187
+ removed++;
188
+ continue;
189
+ }
190
+ // Remove corrupt JSON (can't parse)
191
+ const content = fs.readFileSync(filePath, "utf-8");
192
+ JSON.parse(content);
193
+ }
194
+ catch {
195
+ try {
196
+ fs.unlinkSync(filePath);
197
+ removed++;
198
+ }
199
+ catch { /* ignore */ }
200
+ }
201
+ }
202
+ if (removed > 0) {
203
+ console.error(`[screenhand] Cleaned up ${removed} stale/corrupt state files from ${stateDir}`);
204
+ }
205
+ }
206
+ catch { /* ignore if dir can't be read */ }
207
+ }
208
+ }
167
209
  // ── Audit logging for dangerous tools ──
168
210
  const AUDIT_LOG_PATH = path.resolve(__dirname, ".audit-log.jsonl");
169
211
  function auditLog(tool, params) {
@@ -251,13 +251,28 @@ export class BridgeClient extends EventEmitter {
251
251
  return [...this.recentStderr];
252
252
  }
253
253
  async spawn() {
254
+ // Check binary exists before spawning — give actionable error instead of cryptic ENOENT
255
+ if (!fs.existsSync(this.binaryPath)) {
256
+ const platform = process.platform;
257
+ const hint = platform === "darwin"
258
+ ? `Run: npm run build:native (requires Xcode + Swift)`
259
+ : platform === "win32"
260
+ ? `Run: npm run build:native:windows (requires .NET 8 SDK)`
261
+ : `Native bridge is only available on macOS and Windows.`;
262
+ throw new Error(`ScreenHand native bridge not found at: ${this.binaryPath}\n` +
263
+ `${hint}\n` +
264
+ `If installed via npm, try: npm rebuild screenhand`);
265
+ }
254
266
  const child = spawn(this.binaryPath, [], {
255
267
  stdio: ["pipe", "pipe", "pipe"],
256
268
  });
257
269
  // Track which process this is so stale event handlers don't trigger restarts
258
270
  const spawnedProcess = child;
259
271
  child.on("error", (err) => {
260
- this.emit("error", err);
272
+ const msg = err.code === "ENOENT"
273
+ ? new Error(`ScreenHand native bridge binary not found: ${this.binaryPath}. Run: npm run build:native`)
274
+ : err;
275
+ this.emit("error", msg);
261
276
  // Only auto-restart if this is still the active process
262
277
  if (this.started && this.process === spawnedProcess) {
263
278
  this.restart().catch(() => { });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screenhand",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "mcpName": "io.github.manushi4/screenhand",
5
5
  "description": "Give AI eyes and hands on your desktop. ScreenHand is an open-source MCP server that lets Claude and other AI agents see your screen, click buttons, type text, and control any app on macOS and Windows.",
6
6
  "homepage": "https://screenhand.com",
@@ -70,6 +70,9 @@
70
70
  "ocr",
71
71
  "computer-use"
72
72
  ],
73
+ "engines": {
74
+ "node": ">=18"
75
+ },
73
76
  "dependencies": {
74
77
  "@anthropic-ai/sdk": "^0.78.0",
75
78
  "@modelcontextprotocol/sdk": "^1.27.1",