screenpipe-sync 0.1.0 → 0.2.0
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 +19 -8
- package/dist/index.js +103 -10
- package/package.json +1 -1
- package/src/index.ts +125 -9
package/README.md
CHANGED
|
@@ -7,17 +7,17 @@ Turn hours of screen recordings into actionable context: todos, goals, decisions
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
# One-liner -
|
|
11
|
-
bunx
|
|
10
|
+
# One-liner - AI summary to stdout
|
|
11
|
+
bunx screenpipe-sync
|
|
12
12
|
|
|
13
|
-
# Save
|
|
14
|
-
bunx
|
|
13
|
+
# Save daily summaries locally
|
|
14
|
+
bunx screenpipe-sync --output ~/Documents/brain/context --git
|
|
15
15
|
|
|
16
|
-
#
|
|
17
|
-
bunx
|
|
16
|
+
# Sync raw SQLite database to remote (full history!)
|
|
17
|
+
bunx screenpipe-sync --db --remote user@host:~/.screenpipe/
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
bunx
|
|
19
|
+
# Full sync: DB + daily summary
|
|
20
|
+
bunx screenpipe-sync --db -r clawdbot:~/.screenpipe && bunx screenpipe-sync -o ~/context -g
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## What It Extracts
|
|
@@ -114,12 +114,23 @@ bunx @screenpipe/sync --hours 168 --json > week.json
|
|
|
114
114
|
|
|
115
115
|
## How It Works
|
|
116
116
|
|
|
117
|
+
### Summary Mode (default)
|
|
117
118
|
1. **Query** - Fetches OCR data from local Screenpipe API
|
|
118
119
|
2. **Dedupe** - Removes duplicate/similar screen captures
|
|
119
120
|
3. **Extract** - Claude analyzes content for structured data
|
|
120
121
|
4. **Format** - Outputs markdown or JSON
|
|
121
122
|
5. **Sync** - Optionally git pushes or SCPs to remote
|
|
122
123
|
|
|
124
|
+
### DB Sync Mode (`--db`)
|
|
125
|
+
1. **Copy** - Copies `~/.screenpipe/db.sqlite` (your full history)
|
|
126
|
+
2. **Sync** - Uses rsync/scp to transfer to remote
|
|
127
|
+
3. **Query** - Remote can query SQLite directly
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# On remote, query your full history:
|
|
131
|
+
sqlite3 ~/.screenpipe/db.sqlite "SELECT text FROM ocr_text WHERE text LIKE '%meeting%' LIMIT 10;"
|
|
132
|
+
```
|
|
133
|
+
|
|
123
134
|
## Requirements
|
|
124
135
|
|
|
125
136
|
- [Screenpipe](https://github.com/mediar-ai/screenpipe) running locally
|
package/dist/index.js
CHANGED
|
@@ -9603,6 +9603,7 @@ var sdk_default = Anthropic;
|
|
|
9603
9603
|
// src/index.ts
|
|
9604
9604
|
function parseArgs() {
|
|
9605
9605
|
const args = process.argv.slice(2);
|
|
9606
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
9606
9607
|
const config = {
|
|
9607
9608
|
screenpipeUrl: process.env.SCREENPIPE_URL || "http://localhost:3030",
|
|
9608
9609
|
outputDir: null,
|
|
@@ -9614,7 +9615,9 @@ function parseArgs() {
|
|
|
9614
9615
|
ollamaUrl: process.env.OLLAMA_URL || "http://localhost:11434",
|
|
9615
9616
|
ollamaModel: process.env.OLLAMA_MODEL || "llama3.2",
|
|
9616
9617
|
format: "markdown",
|
|
9617
|
-
verbose: false
|
|
9618
|
+
verbose: false,
|
|
9619
|
+
dbSync: false,
|
|
9620
|
+
dbPath: process.env.SCREENPIPE_DB || `${home}/.screenpipe/db.sqlite`
|
|
9618
9621
|
};
|
|
9619
9622
|
for (let i2 = 0;i2 < args.length; i2++) {
|
|
9620
9623
|
const arg = args[i2];
|
|
@@ -9642,6 +9645,13 @@ function parseArgs() {
|
|
|
9642
9645
|
case "-v":
|
|
9643
9646
|
config.verbose = true;
|
|
9644
9647
|
break;
|
|
9648
|
+
case "--db":
|
|
9649
|
+
case "--db-sync":
|
|
9650
|
+
config.dbSync = true;
|
|
9651
|
+
break;
|
|
9652
|
+
case "--db-path":
|
|
9653
|
+
config.dbPath = args[++i2];
|
|
9654
|
+
break;
|
|
9645
9655
|
case "--help":
|
|
9646
9656
|
printHelp();
|
|
9647
9657
|
process.exit(0);
|
|
@@ -9651,10 +9661,14 @@ function parseArgs() {
|
|
|
9651
9661
|
}
|
|
9652
9662
|
function printHelp() {
|
|
9653
9663
|
console.log(`
|
|
9654
|
-
|
|
9664
|
+
screenpipe-sync - Extract daily context from Screenpipe
|
|
9655
9665
|
|
|
9656
9666
|
USAGE:
|
|
9657
|
-
bunx
|
|
9667
|
+
bunx screenpipe-sync [options]
|
|
9668
|
+
|
|
9669
|
+
MODES:
|
|
9670
|
+
Summary mode (default): AI-powered daily summary extraction
|
|
9671
|
+
DB sync mode (--db): Copy raw SQLite database to remote
|
|
9658
9672
|
|
|
9659
9673
|
OPTIONS:
|
|
9660
9674
|
-o, --output <dir> Save summary to directory (default: stdout)
|
|
@@ -9664,18 +9678,28 @@ OPTIONS:
|
|
|
9664
9678
|
--json Output as JSON instead of markdown
|
|
9665
9679
|
-v, --verbose Show debug output
|
|
9666
9680
|
|
|
9681
|
+
--db, --db-sync Sync raw SQLite database instead of summary
|
|
9682
|
+
--db-path <path> Path to Screenpipe DB (default: ~/.screenpipe/db.sqlite)
|
|
9683
|
+
|
|
9667
9684
|
ENVIRONMENT:
|
|
9668
9685
|
SCREENPIPE_URL Screenpipe API URL (default: http://localhost:3030)
|
|
9669
|
-
|
|
9686
|
+
SCREENPIPE_DB Path to Screenpipe database
|
|
9687
|
+
ANTHROPIC_API_KEY For AI summarization (or OPENAI_API_KEY)
|
|
9670
9688
|
|
|
9671
9689
|
EXAMPLES:
|
|
9672
|
-
|
|
9673
|
-
bunx
|
|
9674
|
-
|
|
9675
|
-
|
|
9690
|
+
# AI summary to stdout
|
|
9691
|
+
bunx screenpipe-sync
|
|
9692
|
+
|
|
9693
|
+
# Save daily summaries locally
|
|
9694
|
+
bunx screenpipe-sync --output ~/Documents/brain/context --git
|
|
9676
9695
|
|
|
9677
|
-
|
|
9678
|
-
|
|
9696
|
+
# Sync raw database to remote (e.g., Clawdbot)
|
|
9697
|
+
bunx screenpipe-sync --db --remote user@clawdbot:~/.screenpipe/
|
|
9698
|
+
|
|
9699
|
+
# Full sync: DB + daily summary
|
|
9700
|
+
bunx screenpipe-sync --db -r clawdbot:~/.screenpipe && bunx screenpipe-sync -o ~/context -g
|
|
9701
|
+
|
|
9702
|
+
OUTPUT (summary mode):
|
|
9679
9703
|
- Todo items extracted from screen content
|
|
9680
9704
|
- Goals and intentions mentioned
|
|
9681
9705
|
- Decisions made
|
|
@@ -9683,6 +9707,10 @@ OUTPUT:
|
|
|
9683
9707
|
- Meetings and conversations
|
|
9684
9708
|
- Blockers and problems
|
|
9685
9709
|
- AI-generated insights
|
|
9710
|
+
|
|
9711
|
+
OUTPUT (db mode):
|
|
9712
|
+
- Copies ~/.screenpipe/db.sqlite to remote
|
|
9713
|
+
- Remote can query SQLite directly for full history
|
|
9686
9714
|
`);
|
|
9687
9715
|
}
|
|
9688
9716
|
async function queryScreenpipe(config) {
|
|
@@ -9921,9 +9949,74 @@ async function writeOutput(content, config, filename) {
|
|
|
9921
9949
|
}
|
|
9922
9950
|
}
|
|
9923
9951
|
}
|
|
9952
|
+
async function syncDatabase(config) {
|
|
9953
|
+
const fs2 = await import("fs/promises");
|
|
9954
|
+
const { execSync } = await import("child_process");
|
|
9955
|
+
try {
|
|
9956
|
+
await fs2.access(config.dbPath);
|
|
9957
|
+
} catch {
|
|
9958
|
+
console.error(`[error] Database not found at ${config.dbPath}`);
|
|
9959
|
+
console.error(` Set --db-path or SCREENPIPE_DB environment variable`);
|
|
9960
|
+
process.exit(1);
|
|
9961
|
+
}
|
|
9962
|
+
const stats = await fs2.stat(config.dbPath);
|
|
9963
|
+
const sizeMB = (stats.size / 1024 / 1024).toFixed(1);
|
|
9964
|
+
console.error(`[db] Found database: ${config.dbPath} (${sizeMB} MB)`);
|
|
9965
|
+
if (!config.remote && !config.outputDir) {
|
|
9966
|
+
console.error(`[error] --db requires --remote or --output to specify destination`);
|
|
9967
|
+
process.exit(1);
|
|
9968
|
+
}
|
|
9969
|
+
if (config.outputDir) {
|
|
9970
|
+
const path = await import("path");
|
|
9971
|
+
const destDir = path.resolve(config.outputDir);
|
|
9972
|
+
await fs2.mkdir(destDir, { recursive: true });
|
|
9973
|
+
const destPath = path.join(destDir, "db.sqlite");
|
|
9974
|
+
console.error(`[db] Copying to ${destPath}...`);
|
|
9975
|
+
await fs2.copyFile(config.dbPath, destPath);
|
|
9976
|
+
try {
|
|
9977
|
+
await fs2.copyFile(`${config.dbPath}-wal`, `${destPath}-wal`);
|
|
9978
|
+
await fs2.copyFile(`${config.dbPath}-shm`, `${destPath}-shm`);
|
|
9979
|
+
} catch {}
|
|
9980
|
+
console.error(`[ok] Database copied to ${destPath}`);
|
|
9981
|
+
if (config.gitPush) {
|
|
9982
|
+
try {
|
|
9983
|
+
execSync(`cd "${destDir}" && git add -A && git commit -m "db sync $(date +%Y-%m-%d)" && git push`, {
|
|
9984
|
+
stdio: config.verbose ? "inherit" : "pipe"
|
|
9985
|
+
});
|
|
9986
|
+
console.error(`[ok] Git pushed`);
|
|
9987
|
+
} catch {
|
|
9988
|
+
console.error(`[warn] Git push failed - maybe no changes?`);
|
|
9989
|
+
}
|
|
9990
|
+
}
|
|
9991
|
+
}
|
|
9992
|
+
if (config.remote) {
|
|
9993
|
+
console.error(`[db] Syncing to ${config.remote}...`);
|
|
9994
|
+
try {
|
|
9995
|
+
execSync(`rsync -avz --progress "${config.dbPath}" "${config.remote}/db.sqlite"`, {
|
|
9996
|
+
stdio: config.verbose ? "inherit" : "pipe"
|
|
9997
|
+
});
|
|
9998
|
+
console.error(`[ok] Database synced to ${config.remote}`);
|
|
9999
|
+
} catch {
|
|
10000
|
+
try {
|
|
10001
|
+
execSync(`scp "${config.dbPath}" "${config.remote}/db.sqlite"`, {
|
|
10002
|
+
stdio: config.verbose ? "inherit" : "pipe"
|
|
10003
|
+
});
|
|
10004
|
+
console.error(`[ok] Database copied to ${config.remote}`);
|
|
10005
|
+
} catch (e2) {
|
|
10006
|
+
console.error(`[error] Failed to sync database: ${e2}`);
|
|
10007
|
+
process.exit(1);
|
|
10008
|
+
}
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
console.error(`[done] Database sync complete`);
|
|
10012
|
+
}
|
|
9924
10013
|
async function main() {
|
|
9925
10014
|
const config = parseArgs();
|
|
9926
10015
|
const today = new Date().toISOString().split("T")[0];
|
|
10016
|
+
if (config.dbSync) {
|
|
10017
|
+
await syncDatabase(config);
|
|
10018
|
+
return;
|
|
10019
|
+
}
|
|
9927
10020
|
console.error(`[screenpipe-sync] Analyzing last ${config.hours} hours...`);
|
|
9928
10021
|
const results = await queryScreenpipe(config);
|
|
9929
10022
|
console.error(`[ok] Retrieved ${results.length} screen captures`);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -51,6 +51,8 @@ interface Config {
|
|
|
51
51
|
ollamaModel: string;
|
|
52
52
|
format: "markdown" | "json";
|
|
53
53
|
verbose: boolean;
|
|
54
|
+
dbSync: boolean;
|
|
55
|
+
dbPath: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
// ============================================================================
|
|
@@ -59,6 +61,7 @@ interface Config {
|
|
|
59
61
|
|
|
60
62
|
function parseArgs(): Config {
|
|
61
63
|
const args = process.argv.slice(2);
|
|
64
|
+
const home = process.env.HOME || process.env.USERPROFILE || "~";
|
|
62
65
|
const config: Config = {
|
|
63
66
|
screenpipeUrl: process.env.SCREENPIPE_URL || "http://localhost:3030",
|
|
64
67
|
outputDir: null,
|
|
@@ -71,6 +74,8 @@ function parseArgs(): Config {
|
|
|
71
74
|
ollamaModel: process.env.OLLAMA_MODEL || "llama3.2",
|
|
72
75
|
format: "markdown",
|
|
73
76
|
verbose: false,
|
|
77
|
+
dbSync: false,
|
|
78
|
+
dbPath: process.env.SCREENPIPE_DB || `${home}/.screenpipe/db.sqlite`,
|
|
74
79
|
};
|
|
75
80
|
|
|
76
81
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -99,6 +104,13 @@ function parseArgs(): Config {
|
|
|
99
104
|
case "-v":
|
|
100
105
|
config.verbose = true;
|
|
101
106
|
break;
|
|
107
|
+
case "--db":
|
|
108
|
+
case "--db-sync":
|
|
109
|
+
config.dbSync = true;
|
|
110
|
+
break;
|
|
111
|
+
case "--db-path":
|
|
112
|
+
config.dbPath = args[++i];
|
|
113
|
+
break;
|
|
102
114
|
case "--help":
|
|
103
115
|
printHelp();
|
|
104
116
|
process.exit(0);
|
|
@@ -110,10 +122,14 @@ function parseArgs(): Config {
|
|
|
110
122
|
|
|
111
123
|
function printHelp() {
|
|
112
124
|
console.log(`
|
|
113
|
-
|
|
125
|
+
screenpipe-sync - Extract daily context from Screenpipe
|
|
114
126
|
|
|
115
127
|
USAGE:
|
|
116
|
-
bunx
|
|
128
|
+
bunx screenpipe-sync [options]
|
|
129
|
+
|
|
130
|
+
MODES:
|
|
131
|
+
Summary mode (default): AI-powered daily summary extraction
|
|
132
|
+
DB sync mode (--db): Copy raw SQLite database to remote
|
|
117
133
|
|
|
118
134
|
OPTIONS:
|
|
119
135
|
-o, --output <dir> Save summary to directory (default: stdout)
|
|
@@ -123,18 +139,28 @@ OPTIONS:
|
|
|
123
139
|
--json Output as JSON instead of markdown
|
|
124
140
|
-v, --verbose Show debug output
|
|
125
141
|
|
|
142
|
+
--db, --db-sync Sync raw SQLite database instead of summary
|
|
143
|
+
--db-path <path> Path to Screenpipe DB (default: ~/.screenpipe/db.sqlite)
|
|
144
|
+
|
|
126
145
|
ENVIRONMENT:
|
|
127
146
|
SCREENPIPE_URL Screenpipe API URL (default: http://localhost:3030)
|
|
128
|
-
|
|
147
|
+
SCREENPIPE_DB Path to Screenpipe database
|
|
148
|
+
ANTHROPIC_API_KEY For AI summarization (or OPENAI_API_KEY)
|
|
129
149
|
|
|
130
150
|
EXAMPLES:
|
|
131
|
-
|
|
132
|
-
bunx
|
|
133
|
-
|
|
134
|
-
|
|
151
|
+
# AI summary to stdout
|
|
152
|
+
bunx screenpipe-sync
|
|
153
|
+
|
|
154
|
+
# Save daily summaries locally
|
|
155
|
+
bunx screenpipe-sync --output ~/Documents/brain/context --git
|
|
135
156
|
|
|
136
|
-
|
|
137
|
-
|
|
157
|
+
# Sync raw database to remote (e.g., Clawdbot)
|
|
158
|
+
bunx screenpipe-sync --db --remote user@clawdbot:~/.screenpipe/
|
|
159
|
+
|
|
160
|
+
# Full sync: DB + daily summary
|
|
161
|
+
bunx screenpipe-sync --db -r clawdbot:~/.screenpipe && bunx screenpipe-sync -o ~/context -g
|
|
162
|
+
|
|
163
|
+
OUTPUT (summary mode):
|
|
138
164
|
- Todo items extracted from screen content
|
|
139
165
|
- Goals and intentions mentioned
|
|
140
166
|
- Decisions made
|
|
@@ -142,6 +168,10 @@ OUTPUT:
|
|
|
142
168
|
- Meetings and conversations
|
|
143
169
|
- Blockers and problems
|
|
144
170
|
- AI-generated insights
|
|
171
|
+
|
|
172
|
+
OUTPUT (db mode):
|
|
173
|
+
- Copies ~/.screenpipe/db.sqlite to remote
|
|
174
|
+
- Remote can query SQLite directly for full history
|
|
145
175
|
`);
|
|
146
176
|
}
|
|
147
177
|
|
|
@@ -435,10 +465,96 @@ async function writeOutput(content: string, config: Config, filename: string) {
|
|
|
435
465
|
// Main
|
|
436
466
|
// ============================================================================
|
|
437
467
|
|
|
468
|
+
async function syncDatabase(config: Config) {
|
|
469
|
+
const fs = await import("fs/promises");
|
|
470
|
+
const { execSync } = await import("child_process");
|
|
471
|
+
|
|
472
|
+
// Check if DB exists
|
|
473
|
+
try {
|
|
474
|
+
await fs.access(config.dbPath);
|
|
475
|
+
} catch {
|
|
476
|
+
console.error(`[error] Database not found at ${config.dbPath}`);
|
|
477
|
+
console.error(` Set --db-path or SCREENPIPE_DB environment variable`);
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const stats = await fs.stat(config.dbPath);
|
|
482
|
+
const sizeMB = (stats.size / 1024 / 1024).toFixed(1);
|
|
483
|
+
console.error(`[db] Found database: ${config.dbPath} (${sizeMB} MB)`);
|
|
484
|
+
|
|
485
|
+
if (!config.remote && !config.outputDir) {
|
|
486
|
+
console.error(`[error] --db requires --remote or --output to specify destination`);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Copy to local output dir
|
|
491
|
+
if (config.outputDir) {
|
|
492
|
+
const path = await import("path");
|
|
493
|
+
const destDir = path.resolve(config.outputDir);
|
|
494
|
+
await fs.mkdir(destDir, { recursive: true });
|
|
495
|
+
const destPath = path.join(destDir, "db.sqlite");
|
|
496
|
+
|
|
497
|
+
console.error(`[db] Copying to ${destPath}...`);
|
|
498
|
+
await fs.copyFile(config.dbPath, destPath);
|
|
499
|
+
|
|
500
|
+
// Also copy WAL files if they exist (for consistency)
|
|
501
|
+
try {
|
|
502
|
+
await fs.copyFile(`${config.dbPath}-wal`, `${destPath}-wal`);
|
|
503
|
+
await fs.copyFile(`${config.dbPath}-shm`, `${destPath}-shm`);
|
|
504
|
+
} catch {
|
|
505
|
+
// WAL files may not exist, that's ok
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
console.error(`[ok] Database copied to ${destPath}`);
|
|
509
|
+
|
|
510
|
+
if (config.gitPush) {
|
|
511
|
+
try {
|
|
512
|
+
execSync(`cd "${destDir}" && git add -A && git commit -m "db sync $(date +%Y-%m-%d)" && git push`, {
|
|
513
|
+
stdio: config.verbose ? "inherit" : "pipe",
|
|
514
|
+
});
|
|
515
|
+
console.error(`[ok] Git pushed`);
|
|
516
|
+
} catch {
|
|
517
|
+
console.error(`[warn] Git push failed - maybe no changes?`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Sync to remote
|
|
523
|
+
if (config.remote) {
|
|
524
|
+
console.error(`[db] Syncing to ${config.remote}...`);
|
|
525
|
+
try {
|
|
526
|
+
// Use rsync for efficiency (only transfers changes)
|
|
527
|
+
execSync(`rsync -avz --progress "${config.dbPath}" "${config.remote}/db.sqlite"`, {
|
|
528
|
+
stdio: config.verbose ? "inherit" : "pipe",
|
|
529
|
+
});
|
|
530
|
+
console.error(`[ok] Database synced to ${config.remote}`);
|
|
531
|
+
} catch {
|
|
532
|
+
// Fallback to scp if rsync not available
|
|
533
|
+
try {
|
|
534
|
+
execSync(`scp "${config.dbPath}" "${config.remote}/db.sqlite"`, {
|
|
535
|
+
stdio: config.verbose ? "inherit" : "pipe",
|
|
536
|
+
});
|
|
537
|
+
console.error(`[ok] Database copied to ${config.remote}`);
|
|
538
|
+
} catch (e) {
|
|
539
|
+
console.error(`[error] Failed to sync database: ${e}`);
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
console.error(`[done] Database sync complete`);
|
|
546
|
+
}
|
|
547
|
+
|
|
438
548
|
async function main() {
|
|
439
549
|
const config = parseArgs();
|
|
440
550
|
const today = new Date().toISOString().split("T")[0];
|
|
441
551
|
|
|
552
|
+
// DB sync mode
|
|
553
|
+
if (config.dbSync) {
|
|
554
|
+
await syncDatabase(config);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
|
|
442
558
|
console.error(`[screenpipe-sync] Analyzing last ${config.hours} hours...`);
|
|
443
559
|
|
|
444
560
|
// 1. Query Screenpipe
|