screenpipe-mcp 0.16.1 → 0.16.3

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.
Files changed (3) hide show
  1. package/dist/index.js +36 -11
  2. package/package.json +1 -1
  3. package/src/index.ts +34 -10
package/dist/index.js CHANGED
@@ -53,25 +53,50 @@ for (let i = 0; i < args.length; i++) {
53
53
  }
54
54
  }
55
55
  const SCREENPIPE_API = `http://localhost:${port}`;
56
- // Discover API key: env var > screenpipe JS lib > npx via process.execPath > npx on PATH
56
+ // Discover API key: env var > db.sqlite direct read > npx fallbacks
57
57
  function discoverApiKey() {
58
58
  const envKey = process.env.SCREENPIPE_LOCAL_API_KEY || process.env.SCREENPIPE_API_KEY;
59
59
  if (envKey)
60
60
  return envKey;
61
- const { execFileSync, execSync } = require("child_process");
61
+ const os = require("os");
62
62
  const path = require("path");
63
- // Use the screenpipe npm package's JS API — resolves the bundled native
64
- // binary directly, no PATH dependency, no subprocess spawning of npx.
63
+ const fs = require("fs");
64
+ const { execFileSync, execSync } = require("child_process");
65
+ // Read api_auth_key directly from ~/.screenpipe/db.sqlite.
66
+ // The key may be stored as plaintext base64 (nonce=zeros, keychain unavailable)
67
+ // or encrypted (non-zero nonce, keychain was available at write time).
68
+ // If plaintext: decode and return. If encrypted: skip, fall through to CLI.
65
69
  try {
66
- const { getApiKey } = require("screenpipe");
67
- const token = getApiKey();
68
- if (token)
69
- return token;
70
+ const dbPath = path.join(os.homedir(), ".screenpipe", "db.sqlite");
71
+ if (fs.existsSync(dbPath)) {
72
+ const sqliteBin = process.platform === "win32" ? "sqlite3.exe" : "sqlite3";
73
+ // Check nonce — all zeros means plaintext base64, non-zero means encrypted
74
+ const row = execFileSync(sqliteBin, [
75
+ dbPath,
76
+ "SELECT hex(nonce), value FROM secrets WHERE key = 'api_auth_key';",
77
+ ], {
78
+ timeout: 5000,
79
+ encoding: "utf-8",
80
+ stdio: ["pipe", "pipe", "pipe"],
81
+ }).trim();
82
+ if (row) {
83
+ const sepIdx = row.indexOf("|");
84
+ const nonceHex = sepIdx >= 0 ? row.substring(0, sepIdx) : "";
85
+ const value = sepIdx >= 0 ? row.substring(sepIdx + 1) : row;
86
+ const isPlaintext = !nonceHex || /^0+$/.test(nonceHex);
87
+ if (isPlaintext && value) {
88
+ const decoded = Buffer.from(value, "base64").toString("utf-8");
89
+ if (decoded && decoded.startsWith("sp-"))
90
+ return decoded;
91
+ if (value.startsWith("sp-"))
92
+ return value;
93
+ }
94
+ // Non-zero nonce = encrypted — fall through to CLI which can decrypt via keychain
95
+ }
96
+ }
70
97
  }
71
98
  catch { }
72
- // Fallback: use the current Node binary to run npx (no PATH dependency).
73
- // Claude Code and other MCP hosts may strip PATH, making bare `npx` unfindable.
74
- // process.execPath is the absolute path to the Node binary running this MCP.
99
+ // Fallback: use the current Node binary to find npx (no PATH dependency)
75
100
  try {
76
101
  const npxPath = path.join(path.dirname(process.execPath), "npx");
77
102
  const token = execFileSync(npxPath, ["screenpipe@latest", "auth", "token"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screenpipe-mcp",
3
- "version": "0.16.1",
3
+ "version": "0.16.3",
4
4
  "description": "MCP server for screenpipe - search your screen recordings and audio transcriptions",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.ts CHANGED
@@ -28,25 +28,49 @@ for (let i = 0; i < args.length; i++) {
28
28
 
29
29
  const SCREENPIPE_API = `http://localhost:${port}`;
30
30
 
31
- // Discover API key: env var > screenpipe JS lib > npx via process.execPath > npx on PATH
31
+ // Discover API key: env var > db.sqlite direct read > npx fallbacks
32
32
  function discoverApiKey(): string {
33
33
  const envKey = process.env.SCREENPIPE_LOCAL_API_KEY || process.env.SCREENPIPE_API_KEY;
34
34
  if (envKey) return envKey;
35
35
 
36
- const { execFileSync, execSync } = require("child_process");
36
+ const os = require("os");
37
37
  const path = require("path");
38
+ const fs = require("fs");
39
+ const { execFileSync, execSync } = require("child_process");
38
40
 
39
- // Use the screenpipe npm package's JS API — resolves the bundled native
40
- // binary directly, no PATH dependency, no subprocess spawning of npx.
41
+ // Read api_auth_key directly from ~/.screenpipe/db.sqlite.
42
+ // The key may be stored as plaintext base64 (nonce=zeros, keychain unavailable)
43
+ // or encrypted (non-zero nonce, keychain was available at write time).
44
+ // If plaintext: decode and return. If encrypted: skip, fall through to CLI.
41
45
  try {
42
- const { getApiKey } = require("screenpipe");
43
- const token = getApiKey();
44
- if (token) return token;
46
+ const dbPath = path.join(os.homedir(), ".screenpipe", "db.sqlite");
47
+ if (fs.existsSync(dbPath)) {
48
+ const sqliteBin = process.platform === "win32" ? "sqlite3.exe" : "sqlite3";
49
+ // Check nonce — all zeros means plaintext base64, non-zero means encrypted
50
+ const row = execFileSync(sqliteBin, [
51
+ dbPath,
52
+ "SELECT hex(nonce), value FROM secrets WHERE key = 'api_auth_key';",
53
+ ], {
54
+ timeout: 5000,
55
+ encoding: "utf-8",
56
+ stdio: ["pipe", "pipe", "pipe"],
57
+ }).trim();
58
+ if (row) {
59
+ const sepIdx = row.indexOf("|");
60
+ const nonceHex = sepIdx >= 0 ? row.substring(0, sepIdx) : "";
61
+ const value = sepIdx >= 0 ? row.substring(sepIdx + 1) : row;
62
+ const isPlaintext = !nonceHex || /^0+$/.test(nonceHex);
63
+ if (isPlaintext && value) {
64
+ const decoded = Buffer.from(value, "base64").toString("utf-8");
65
+ if (decoded && decoded.startsWith("sp-")) return decoded;
66
+ if (value.startsWith("sp-")) return value;
67
+ }
68
+ // Non-zero nonce = encrypted — fall through to CLI which can decrypt via keychain
69
+ }
70
+ }
45
71
  } catch {}
46
72
 
47
- // Fallback: use the current Node binary to run npx (no PATH dependency).
48
- // Claude Code and other MCP hosts may strip PATH, making bare `npx` unfindable.
49
- // process.execPath is the absolute path to the Node binary running this MCP.
73
+ // Fallback: use the current Node binary to find npx (no PATH dependency)
50
74
  try {
51
75
  const npxPath = path.join(path.dirname(process.execPath), "npx");
52
76
  const token = execFileSync(npxPath, ["screenpipe@latest", "auth", "token"], {