screenpipe-sync 0.2.0 → 0.3.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/README.md +5 -3
- package/dist/index.js +159 -1
- package/package.json +1 -1
- package/src/index.ts +185 -0
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
#
|
|
1
|
+
# screenpipe-sync
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Sync your Screenpipe data to remote AI agents.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
One-liner to permanently sync your screen memory to a remote server (e.g., [Clawdbot](https://github.com/moltinginstar/moltbot), any SSH server). Your AI agent can then query your full history via SQLite.
|
|
6
|
+
|
|
7
|
+
Also extracts structured daily summaries: todos, goals, decisions, and AI insights.
|
|
6
8
|
|
|
7
9
|
## Quick Start
|
|
8
10
|
|
package/dist/index.js
CHANGED
|
@@ -9617,7 +9617,10 @@ function parseArgs() {
|
|
|
9617
9617
|
format: "markdown",
|
|
9618
9618
|
verbose: false,
|
|
9619
9619
|
dbSync: false,
|
|
9620
|
-
dbPath: process.env.SCREENPIPE_DB || `${home}/.screenpipe/db.sqlite
|
|
9620
|
+
dbPath: process.env.SCREENPIPE_DB || `${home}/.screenpipe/db.sqlite`,
|
|
9621
|
+
daemon: false,
|
|
9622
|
+
daemonInterval: 3600,
|
|
9623
|
+
daemonStop: false
|
|
9621
9624
|
};
|
|
9622
9625
|
for (let i2 = 0;i2 < args.length; i2++) {
|
|
9623
9626
|
const arg = args[i2];
|
|
@@ -9652,6 +9655,17 @@ function parseArgs() {
|
|
|
9652
9655
|
case "--db-path":
|
|
9653
9656
|
config.dbPath = args[++i2];
|
|
9654
9657
|
break;
|
|
9658
|
+
case "--daemon":
|
|
9659
|
+
case "-d":
|
|
9660
|
+
config.daemon = true;
|
|
9661
|
+
config.dbSync = true;
|
|
9662
|
+
break;
|
|
9663
|
+
case "--interval":
|
|
9664
|
+
config.daemonInterval = parseInt(args[++i2]) || 3600;
|
|
9665
|
+
break;
|
|
9666
|
+
case "--stop":
|
|
9667
|
+
config.daemonStop = true;
|
|
9668
|
+
break;
|
|
9655
9669
|
case "--help":
|
|
9656
9670
|
printHelp();
|
|
9657
9671
|
process.exit(0);
|
|
@@ -9681,6 +9695,10 @@ OPTIONS:
|
|
|
9681
9695
|
--db, --db-sync Sync raw SQLite database instead of summary
|
|
9682
9696
|
--db-path <path> Path to Screenpipe DB (default: ~/.screenpipe/db.sqlite)
|
|
9683
9697
|
|
|
9698
|
+
-d, --daemon Install persistent background sync (survives reboot)
|
|
9699
|
+
--interval <secs> Sync interval in seconds (default: 3600 = 1 hour)
|
|
9700
|
+
--stop Stop and remove the daemon
|
|
9701
|
+
|
|
9684
9702
|
ENVIRONMENT:
|
|
9685
9703
|
SCREENPIPE_URL Screenpipe API URL (default: http://localhost:3030)
|
|
9686
9704
|
SCREENPIPE_DB Path to Screenpipe database
|
|
@@ -9699,6 +9717,12 @@ EXAMPLES:
|
|
|
9699
9717
|
# Full sync: DB + daily summary
|
|
9700
9718
|
bunx screenpipe-sync --db -r clawdbot:~/.screenpipe && bunx screenpipe-sync -o ~/context -g
|
|
9701
9719
|
|
|
9720
|
+
# ONE-LINER: Permanent background sync (survives reboot)
|
|
9721
|
+
bunx screenpipe-sync --daemon --remote user@server:~/.screenpipe/
|
|
9722
|
+
|
|
9723
|
+
# Stop the daemon
|
|
9724
|
+
bunx screenpipe-sync --stop
|
|
9725
|
+
|
|
9702
9726
|
OUTPUT (summary mode):
|
|
9703
9727
|
- Todo items extracted from screen content
|
|
9704
9728
|
- Goals and intentions mentioned
|
|
@@ -9949,6 +9973,132 @@ async function writeOutput(content, config, filename) {
|
|
|
9949
9973
|
}
|
|
9950
9974
|
}
|
|
9951
9975
|
}
|
|
9976
|
+
async function setupDaemon(config) {
|
|
9977
|
+
const fs2 = await import("fs/promises");
|
|
9978
|
+
const { execSync } = await import("child_process");
|
|
9979
|
+
const os = await import("os");
|
|
9980
|
+
const path = await import("path");
|
|
9981
|
+
const home = os.homedir();
|
|
9982
|
+
const platform = os.platform();
|
|
9983
|
+
if (!config.remote && !config.outputDir) {
|
|
9984
|
+
console.error(`[error] --daemon requires --remote or --output`);
|
|
9985
|
+
console.error(` Example: bunx screenpipe-sync --daemon -r user@host:~/.screenpipe/`);
|
|
9986
|
+
process.exit(1);
|
|
9987
|
+
}
|
|
9988
|
+
const remotePart = config.remote ? `--remote ${config.remote}` : "";
|
|
9989
|
+
const outputPart = config.outputDir ? `--output ${config.outputDir}` : "";
|
|
9990
|
+
const gitPart = config.gitPush ? "--git" : "";
|
|
9991
|
+
if (platform === "darwin") {
|
|
9992
|
+
const plistPath = path.join(home, "Library/LaunchAgents/com.screenpipe.sync.plist");
|
|
9993
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
9994
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
9995
|
+
<plist version="1.0">
|
|
9996
|
+
<dict>
|
|
9997
|
+
<key>Label</key>
|
|
9998
|
+
<string>com.screenpipe.sync</string>
|
|
9999
|
+
<key>ProgramArguments</key>
|
|
10000
|
+
<array>
|
|
10001
|
+
<string>/bin/bash</string>
|
|
10002
|
+
<string>-c</string>
|
|
10003
|
+
<string>export PATH="$HOME/.bun/bin:/usr/local/bin:/opt/homebrew/bin:$PATH" && bunx screenpipe-sync --db ${remotePart} ${outputPart} ${gitPart}</string>
|
|
10004
|
+
</array>
|
|
10005
|
+
<key>StartInterval</key>
|
|
10006
|
+
<integer>${config.daemonInterval}</integer>
|
|
10007
|
+
<key>RunAtLoad</key>
|
|
10008
|
+
<true/>
|
|
10009
|
+
<key>StandardOutPath</key>
|
|
10010
|
+
<string>/tmp/screenpipe-sync.log</string>
|
|
10011
|
+
<key>StandardErrorPath</key>
|
|
10012
|
+
<string>/tmp/screenpipe-sync.err</string>
|
|
10013
|
+
<key>EnvironmentVariables</key>
|
|
10014
|
+
<dict>
|
|
10015
|
+
<key>HOME</key>
|
|
10016
|
+
<string>${home}</string>
|
|
10017
|
+
</dict>
|
|
10018
|
+
</dict>
|
|
10019
|
+
</plist>`;
|
|
10020
|
+
await fs2.mkdir(path.dirname(plistPath), { recursive: true });
|
|
10021
|
+
await fs2.writeFile(plistPath, plist);
|
|
10022
|
+
try {
|
|
10023
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`);
|
|
10024
|
+
execSync(`launchctl load "${plistPath}"`);
|
|
10025
|
+
} catch (e2) {
|
|
10026
|
+
console.error(`[error] Failed to load LaunchAgent: ${e2}`);
|
|
10027
|
+
process.exit(1);
|
|
10028
|
+
}
|
|
10029
|
+
console.log(`[ok] Daemon installed (macOS LaunchAgent)`);
|
|
10030
|
+
console.log(` Syncs every ${config.daemonInterval}s to: ${config.remote || config.outputDir}`);
|
|
10031
|
+
console.log(` Logs: /tmp/screenpipe-sync.log`);
|
|
10032
|
+
console.log(` Stop: bunx screenpipe-sync --stop`);
|
|
10033
|
+
} else if (platform === "linux") {
|
|
10034
|
+
const serviceDir = path.join(home, ".config/systemd/user");
|
|
10035
|
+
const servicePath = path.join(serviceDir, "screenpipe-sync.service");
|
|
10036
|
+
const timerPath = path.join(serviceDir, "screenpipe-sync.timer");
|
|
10037
|
+
const service = `[Unit]
|
|
10038
|
+
Description=Screenpipe Sync
|
|
10039
|
+
|
|
10040
|
+
[Service]
|
|
10041
|
+
Type=oneshot
|
|
10042
|
+
ExecStart=/bin/bash -c 'export PATH="$HOME/.bun/bin:$PATH" && bunx screenpipe-sync --db ${remotePart} ${outputPart} ${gitPart}'
|
|
10043
|
+
Environment=HOME=${home}
|
|
10044
|
+
|
|
10045
|
+
[Install]
|
|
10046
|
+
WantedBy=default.target`;
|
|
10047
|
+
const timer = `[Unit]
|
|
10048
|
+
Description=Screenpipe Sync Timer
|
|
10049
|
+
|
|
10050
|
+
[Timer]
|
|
10051
|
+
OnBootSec=60
|
|
10052
|
+
OnUnitActiveSec=${config.daemonInterval}s
|
|
10053
|
+
Persistent=true
|
|
10054
|
+
|
|
10055
|
+
[Install]
|
|
10056
|
+
WantedBy=timers.target`;
|
|
10057
|
+
await fs2.mkdir(serviceDir, { recursive: true });
|
|
10058
|
+
await fs2.writeFile(servicePath, service);
|
|
10059
|
+
await fs2.writeFile(timerPath, timer);
|
|
10060
|
+
try {
|
|
10061
|
+
execSync("systemctl --user daemon-reload");
|
|
10062
|
+
execSync("systemctl --user enable --now screenpipe-sync.timer");
|
|
10063
|
+
} catch (e2) {
|
|
10064
|
+
console.error(`[error] Failed to enable systemd timer: ${e2}`);
|
|
10065
|
+
process.exit(1);
|
|
10066
|
+
}
|
|
10067
|
+
console.log(`[ok] Daemon installed (systemd user timer)`);
|
|
10068
|
+
console.log(` Syncs every ${config.daemonInterval}s to: ${config.remote || config.outputDir}`);
|
|
10069
|
+
console.log(` Status: systemctl --user status screenpipe-sync.timer`);
|
|
10070
|
+
console.log(` Stop: bunx screenpipe-sync --stop`);
|
|
10071
|
+
} else {
|
|
10072
|
+
console.error(`[error] Daemon not supported on ${platform}`);
|
|
10073
|
+
console.error(` Use cron instead: */60 * * * * bunx screenpipe-sync --db ${remotePart}`);
|
|
10074
|
+
process.exit(1);
|
|
10075
|
+
}
|
|
10076
|
+
}
|
|
10077
|
+
async function stopDaemon() {
|
|
10078
|
+
const { execSync } = await import("child_process");
|
|
10079
|
+
const os = await import("os");
|
|
10080
|
+
const fs2 = await import("fs/promises");
|
|
10081
|
+
const path = await import("path");
|
|
10082
|
+
const home = os.homedir();
|
|
10083
|
+
const platform = os.platform();
|
|
10084
|
+
if (platform === "darwin") {
|
|
10085
|
+
const plistPath = path.join(home, "Library/LaunchAgents/com.screenpipe.sync.plist");
|
|
10086
|
+
try {
|
|
10087
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null`);
|
|
10088
|
+
await fs2.unlink(plistPath);
|
|
10089
|
+
console.log(`[ok] Daemon stopped and removed`);
|
|
10090
|
+
} catch {
|
|
10091
|
+
console.log(`[ok] Daemon was not running`);
|
|
10092
|
+
}
|
|
10093
|
+
} else if (platform === "linux") {
|
|
10094
|
+
try {
|
|
10095
|
+
execSync("systemctl --user disable --now screenpipe-sync.timer 2>/dev/null");
|
|
10096
|
+
console.log(`[ok] Daemon stopped and disabled`);
|
|
10097
|
+
} catch {
|
|
10098
|
+
console.log(`[ok] Daemon was not running`);
|
|
10099
|
+
}
|
|
10100
|
+
}
|
|
10101
|
+
}
|
|
9952
10102
|
async function syncDatabase(config) {
|
|
9953
10103
|
const fs2 = await import("fs/promises");
|
|
9954
10104
|
const { execSync } = await import("child_process");
|
|
@@ -10013,6 +10163,14 @@ async function syncDatabase(config) {
|
|
|
10013
10163
|
async function main() {
|
|
10014
10164
|
const config = parseArgs();
|
|
10015
10165
|
const today = new Date().toISOString().split("T")[0];
|
|
10166
|
+
if (config.daemonStop) {
|
|
10167
|
+
await stopDaemon();
|
|
10168
|
+
return;
|
|
10169
|
+
}
|
|
10170
|
+
if (config.daemon) {
|
|
10171
|
+
await setupDaemon(config);
|
|
10172
|
+
return;
|
|
10173
|
+
}
|
|
10016
10174
|
if (config.dbSync) {
|
|
10017
10175
|
await syncDatabase(config);
|
|
10018
10176
|
return;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -53,6 +53,9 @@ interface Config {
|
|
|
53
53
|
verbose: boolean;
|
|
54
54
|
dbSync: boolean;
|
|
55
55
|
dbPath: string;
|
|
56
|
+
daemon: boolean;
|
|
57
|
+
daemonInterval: number;
|
|
58
|
+
daemonStop: boolean;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
// ============================================================================
|
|
@@ -76,6 +79,9 @@ function parseArgs(): Config {
|
|
|
76
79
|
verbose: false,
|
|
77
80
|
dbSync: false,
|
|
78
81
|
dbPath: process.env.SCREENPIPE_DB || `${home}/.screenpipe/db.sqlite`,
|
|
82
|
+
daemon: false,
|
|
83
|
+
daemonInterval: 3600,
|
|
84
|
+
daemonStop: false,
|
|
79
85
|
};
|
|
80
86
|
|
|
81
87
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -111,6 +117,17 @@ function parseArgs(): Config {
|
|
|
111
117
|
case "--db-path":
|
|
112
118
|
config.dbPath = args[++i];
|
|
113
119
|
break;
|
|
120
|
+
case "--daemon":
|
|
121
|
+
case "-d":
|
|
122
|
+
config.daemon = true;
|
|
123
|
+
config.dbSync = true; // daemon mode always syncs DB
|
|
124
|
+
break;
|
|
125
|
+
case "--interval":
|
|
126
|
+
config.daemonInterval = parseInt(args[++i]) || 3600;
|
|
127
|
+
break;
|
|
128
|
+
case "--stop":
|
|
129
|
+
config.daemonStop = true;
|
|
130
|
+
break;
|
|
114
131
|
case "--help":
|
|
115
132
|
printHelp();
|
|
116
133
|
process.exit(0);
|
|
@@ -142,6 +159,10 @@ OPTIONS:
|
|
|
142
159
|
--db, --db-sync Sync raw SQLite database instead of summary
|
|
143
160
|
--db-path <path> Path to Screenpipe DB (default: ~/.screenpipe/db.sqlite)
|
|
144
161
|
|
|
162
|
+
-d, --daemon Install persistent background sync (survives reboot)
|
|
163
|
+
--interval <secs> Sync interval in seconds (default: 3600 = 1 hour)
|
|
164
|
+
--stop Stop and remove the daemon
|
|
165
|
+
|
|
145
166
|
ENVIRONMENT:
|
|
146
167
|
SCREENPIPE_URL Screenpipe API URL (default: http://localhost:3030)
|
|
147
168
|
SCREENPIPE_DB Path to Screenpipe database
|
|
@@ -160,6 +181,12 @@ EXAMPLES:
|
|
|
160
181
|
# Full sync: DB + daily summary
|
|
161
182
|
bunx screenpipe-sync --db -r clawdbot:~/.screenpipe && bunx screenpipe-sync -o ~/context -g
|
|
162
183
|
|
|
184
|
+
# ONE-LINER: Permanent background sync (survives reboot)
|
|
185
|
+
bunx screenpipe-sync --daemon --remote user@server:~/.screenpipe/
|
|
186
|
+
|
|
187
|
+
# Stop the daemon
|
|
188
|
+
bunx screenpipe-sync --stop
|
|
189
|
+
|
|
163
190
|
OUTPUT (summary mode):
|
|
164
191
|
- Todo items extracted from screen content
|
|
165
192
|
- Goals and intentions mentioned
|
|
@@ -465,6 +492,152 @@ async function writeOutput(content: string, config: Config, filename: string) {
|
|
|
465
492
|
// Main
|
|
466
493
|
// ============================================================================
|
|
467
494
|
|
|
495
|
+
async function setupDaemon(config: Config) {
|
|
496
|
+
const fs = await import("fs/promises");
|
|
497
|
+
const { execSync } = await import("child_process");
|
|
498
|
+
const os = await import("os");
|
|
499
|
+
const path = await import("path");
|
|
500
|
+
|
|
501
|
+
const home = os.homedir();
|
|
502
|
+
const platform = os.platform();
|
|
503
|
+
|
|
504
|
+
if (!config.remote && !config.outputDir) {
|
|
505
|
+
console.error(`[error] --daemon requires --remote or --output`);
|
|
506
|
+
console.error(` Example: bunx screenpipe-sync --daemon -r user@host:~/.screenpipe/`);
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const remotePart = config.remote ? `--remote ${config.remote}` : "";
|
|
511
|
+
const outputPart = config.outputDir ? `--output ${config.outputDir}` : "";
|
|
512
|
+
const gitPart = config.gitPush ? "--git" : "";
|
|
513
|
+
|
|
514
|
+
if (platform === "darwin") {
|
|
515
|
+
// macOS: LaunchAgent
|
|
516
|
+
const plistPath = path.join(home, "Library/LaunchAgents/com.screenpipe.sync.plist");
|
|
517
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
518
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
519
|
+
<plist version="1.0">
|
|
520
|
+
<dict>
|
|
521
|
+
<key>Label</key>
|
|
522
|
+
<string>com.screenpipe.sync</string>
|
|
523
|
+
<key>ProgramArguments</key>
|
|
524
|
+
<array>
|
|
525
|
+
<string>/bin/bash</string>
|
|
526
|
+
<string>-c</string>
|
|
527
|
+
<string>export PATH="$HOME/.bun/bin:/usr/local/bin:/opt/homebrew/bin:$PATH" && bunx screenpipe-sync --db ${remotePart} ${outputPart} ${gitPart}</string>
|
|
528
|
+
</array>
|
|
529
|
+
<key>StartInterval</key>
|
|
530
|
+
<integer>${config.daemonInterval}</integer>
|
|
531
|
+
<key>RunAtLoad</key>
|
|
532
|
+
<true/>
|
|
533
|
+
<key>StandardOutPath</key>
|
|
534
|
+
<string>/tmp/screenpipe-sync.log</string>
|
|
535
|
+
<key>StandardErrorPath</key>
|
|
536
|
+
<string>/tmp/screenpipe-sync.err</string>
|
|
537
|
+
<key>EnvironmentVariables</key>
|
|
538
|
+
<dict>
|
|
539
|
+
<key>HOME</key>
|
|
540
|
+
<string>${home}</string>
|
|
541
|
+
</dict>
|
|
542
|
+
</dict>
|
|
543
|
+
</plist>`;
|
|
544
|
+
|
|
545
|
+
await fs.mkdir(path.dirname(plistPath), { recursive: true });
|
|
546
|
+
await fs.writeFile(plistPath, plist);
|
|
547
|
+
|
|
548
|
+
try {
|
|
549
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null || true`);
|
|
550
|
+
execSync(`launchctl load "${plistPath}"`);
|
|
551
|
+
} catch (e) {
|
|
552
|
+
console.error(`[error] Failed to load LaunchAgent: ${e}`);
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
console.log(`[ok] Daemon installed (macOS LaunchAgent)`);
|
|
557
|
+
console.log(` Syncs every ${config.daemonInterval}s to: ${config.remote || config.outputDir}`);
|
|
558
|
+
console.log(` Logs: /tmp/screenpipe-sync.log`);
|
|
559
|
+
console.log(` Stop: bunx screenpipe-sync --stop`);
|
|
560
|
+
|
|
561
|
+
} else if (platform === "linux") {
|
|
562
|
+
// Linux: systemd user service
|
|
563
|
+
const serviceDir = path.join(home, ".config/systemd/user");
|
|
564
|
+
const servicePath = path.join(serviceDir, "screenpipe-sync.service");
|
|
565
|
+
const timerPath = path.join(serviceDir, "screenpipe-sync.timer");
|
|
566
|
+
|
|
567
|
+
const service = `[Unit]
|
|
568
|
+
Description=Screenpipe Sync
|
|
569
|
+
|
|
570
|
+
[Service]
|
|
571
|
+
Type=oneshot
|
|
572
|
+
ExecStart=/bin/bash -c 'export PATH="$HOME/.bun/bin:$PATH" && bunx screenpipe-sync --db ${remotePart} ${outputPart} ${gitPart}'
|
|
573
|
+
Environment=HOME=${home}
|
|
574
|
+
|
|
575
|
+
[Install]
|
|
576
|
+
WantedBy=default.target`;
|
|
577
|
+
|
|
578
|
+
const timer = `[Unit]
|
|
579
|
+
Description=Screenpipe Sync Timer
|
|
580
|
+
|
|
581
|
+
[Timer]
|
|
582
|
+
OnBootSec=60
|
|
583
|
+
OnUnitActiveSec=${config.daemonInterval}s
|
|
584
|
+
Persistent=true
|
|
585
|
+
|
|
586
|
+
[Install]
|
|
587
|
+
WantedBy=timers.target`;
|
|
588
|
+
|
|
589
|
+
await fs.mkdir(serviceDir, { recursive: true });
|
|
590
|
+
await fs.writeFile(servicePath, service);
|
|
591
|
+
await fs.writeFile(timerPath, timer);
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
execSync("systemctl --user daemon-reload");
|
|
595
|
+
execSync("systemctl --user enable --now screenpipe-sync.timer");
|
|
596
|
+
} catch (e) {
|
|
597
|
+
console.error(`[error] Failed to enable systemd timer: ${e}`);
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
console.log(`[ok] Daemon installed (systemd user timer)`);
|
|
602
|
+
console.log(` Syncs every ${config.daemonInterval}s to: ${config.remote || config.outputDir}`);
|
|
603
|
+
console.log(` Status: systemctl --user status screenpipe-sync.timer`);
|
|
604
|
+
console.log(` Stop: bunx screenpipe-sync --stop`);
|
|
605
|
+
|
|
606
|
+
} else {
|
|
607
|
+
console.error(`[error] Daemon not supported on ${platform}`);
|
|
608
|
+
console.error(` Use cron instead: */60 * * * * bunx screenpipe-sync --db ${remotePart}`);
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async function stopDaemon() {
|
|
614
|
+
const { execSync } = await import("child_process");
|
|
615
|
+
const os = await import("os");
|
|
616
|
+
const fs = await import("fs/promises");
|
|
617
|
+
const path = await import("path");
|
|
618
|
+
|
|
619
|
+
const home = os.homedir();
|
|
620
|
+
const platform = os.platform();
|
|
621
|
+
|
|
622
|
+
if (platform === "darwin") {
|
|
623
|
+
const plistPath = path.join(home, "Library/LaunchAgents/com.screenpipe.sync.plist");
|
|
624
|
+
try {
|
|
625
|
+
execSync(`launchctl unload "${plistPath}" 2>/dev/null`);
|
|
626
|
+
await fs.unlink(plistPath);
|
|
627
|
+
console.log(`[ok] Daemon stopped and removed`);
|
|
628
|
+
} catch {
|
|
629
|
+
console.log(`[ok] Daemon was not running`);
|
|
630
|
+
}
|
|
631
|
+
} else if (platform === "linux") {
|
|
632
|
+
try {
|
|
633
|
+
execSync("systemctl --user disable --now screenpipe-sync.timer 2>/dev/null");
|
|
634
|
+
console.log(`[ok] Daemon stopped and disabled`);
|
|
635
|
+
} catch {
|
|
636
|
+
console.log(`[ok] Daemon was not running`);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
468
641
|
async function syncDatabase(config: Config) {
|
|
469
642
|
const fs = await import("fs/promises");
|
|
470
643
|
const { execSync } = await import("child_process");
|
|
@@ -549,6 +722,18 @@ async function main() {
|
|
|
549
722
|
const config = parseArgs();
|
|
550
723
|
const today = new Date().toISOString().split("T")[0];
|
|
551
724
|
|
|
725
|
+
// Stop daemon
|
|
726
|
+
if (config.daemonStop) {
|
|
727
|
+
await stopDaemon();
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Daemon mode - install persistent sync
|
|
732
|
+
if (config.daemon) {
|
|
733
|
+
await setupDaemon(config);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
|
|
552
737
|
// DB sync mode
|
|
553
738
|
if (config.dbSync) {
|
|
554
739
|
await syncDatabase(config);
|