ccsini 0.1.40 → 0.1.42

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 (2) hide show
  1. package/dist/index.js +176 -35
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -28018,14 +28018,14 @@ var {
28018
28018
  } = import__.default;
28019
28019
 
28020
28020
  // src/version.ts
28021
- var VERSION = "0.1.40";
28021
+ var VERSION = "0.1.42";
28022
28022
 
28023
28023
  // src/commands/init.ts
28024
28024
  init_source();
28025
28025
  init_ora();
28026
28026
  init_dist16();
28027
28027
  init_auth();
28028
- import { platform } from "os";
28028
+ import { platform as platform2 } from "os";
28029
28029
 
28030
28030
  // ../../node_modules/.bun/hash-wasm@4.12.0/node_modules/hash-wasm/dist/index.esm.js
28031
28031
  /*!
@@ -29078,6 +29078,12 @@ class CcsiniClient {
29078
29078
  throw new Error("Failed to reset account data");
29079
29079
  return res.json();
29080
29080
  }
29081
+ async heartbeat() {
29082
+ await fetch(`${this.apiUrl}/api/sync/heartbeat`, {
29083
+ method: "POST",
29084
+ headers: this.getHeaders()
29085
+ });
29086
+ }
29081
29087
  async logSyncEvent(event) {
29082
29088
  await fetch(`${this.apiUrl}/api/sync/log`, {
29083
29089
  method: "POST",
@@ -29541,8 +29547,138 @@ function chunkArray(arr, size) {
29541
29547
  }
29542
29548
 
29543
29549
  // src/commands/init.ts
29544
- import { writeFile as writeFile6 } from "fs/promises";
29550
+ import { writeFile as writeFile7 } from "fs/promises";
29551
+ import { join as join8 } from "path";
29552
+
29553
+ // src/branding.ts
29554
+ init_source();
29555
+ var LOGO = source_default.hex("#FFA500").bold(`
29556
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557
29557
+ \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551
29558
+ \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551
29559
+ \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551
29560
+ \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551
29561
+ \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D
29562
+ `);
29563
+ var TAGLINE = ` ${source_default.bold(`ccsini v${VERSION}`)} \u2014 Claude Code seamless sync
29564
+ ${source_default.dim("Encrypted sync for your Claude Code settings across devices")}
29565
+ `;
29566
+
29567
+ // src/heartbeat/scheduler.ts
29568
+ import { execSync } from "child_process";
29569
+ import { platform } from "os";
29570
+ import { writeFile as writeFile6, unlink } from "fs/promises";
29545
29571
  import { join as join7 } from "path";
29572
+ import { homedir as homedir3 } from "os";
29573
+ var TASK_NAME = "ccsini-heartbeat";
29574
+ var INTERVAL_MINUTES = 3;
29575
+ async function installHeartbeatScheduler() {
29576
+ const os3 = platform();
29577
+ if (os3 === "win32") {
29578
+ await installWindows();
29579
+ } else if (os3 === "darwin") {
29580
+ await installMacOS();
29581
+ } else {
29582
+ await installLinux();
29583
+ }
29584
+ }
29585
+ async function uninstallHeartbeatScheduler() {
29586
+ const os3 = platform();
29587
+ if (os3 === "win32") {
29588
+ await uninstallWindows();
29589
+ } else if (os3 === "darwin") {
29590
+ await uninstallMacOS();
29591
+ } else {
29592
+ await uninstallLinux();
29593
+ }
29594
+ }
29595
+ async function installWindows() {
29596
+ try {
29597
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
29598
+ } catch {}
29599
+ execSync(`schtasks /Create /TN "${TASK_NAME}" /TR "ccsini heartbeat" /SC MINUTE /MO ${INTERVAL_MINUTES} /F`, { stdio: "ignore" });
29600
+ }
29601
+ async function uninstallWindows() {
29602
+ try {
29603
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
29604
+ } catch {}
29605
+ }
29606
+ function getLaunchAgentPath() {
29607
+ return join7(homedir3(), "Library", "LaunchAgents", `com.ccsini.heartbeat.plist`);
29608
+ }
29609
+ async function installMacOS() {
29610
+ const plistPath = getLaunchAgentPath();
29611
+ let ccsiniPath = "ccsini";
29612
+ try {
29613
+ ccsiniPath = execSync("which ccsini", { encoding: "utf-8" }).trim();
29614
+ } catch {}
29615
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
29616
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
29617
+ <plist version="1.0">
29618
+ <dict>
29619
+ <key>Label</key>
29620
+ <string>com.ccsini.heartbeat</string>
29621
+ <key>ProgramArguments</key>
29622
+ <array>
29623
+ <string>${ccsiniPath}</string>
29624
+ <string>heartbeat</string>
29625
+ </array>
29626
+ <key>StartInterval</key>
29627
+ <integer>${INTERVAL_MINUTES * 60}</integer>
29628
+ <key>RunAtLoad</key>
29629
+ <true/>
29630
+ <key>StandardOutPath</key>
29631
+ <string>/dev/null</string>
29632
+ <key>StandardErrorPath</key>
29633
+ <string>/dev/null</string>
29634
+ </dict>
29635
+ </plist>`;
29636
+ await writeFile6(plistPath, plist);
29637
+ try {
29638
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
29639
+ } catch {}
29640
+ execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
29641
+ }
29642
+ async function uninstallMacOS() {
29643
+ const plistPath = getLaunchAgentPath();
29644
+ try {
29645
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
29646
+ } catch {}
29647
+ try {
29648
+ await unlink(plistPath);
29649
+ } catch {}
29650
+ }
29651
+ async function installLinux() {
29652
+ let existing = "";
29653
+ try {
29654
+ existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
29655
+ } catch {}
29656
+ const lines = existing.split(`
29657
+ `).filter((l) => !l.includes(TASK_NAME));
29658
+ lines.push(`*/${INTERVAL_MINUTES} * * * * ccsini heartbeat # ${TASK_NAME}`);
29659
+ const newCrontab = lines.filter((l) => l.trim() !== "").join(`
29660
+ `) + `
29661
+ `;
29662
+ execSync(`echo "${newCrontab.replace(/"/g, "\\\"")}" | crontab -`, { stdio: "ignore" });
29663
+ }
29664
+ async function uninstallLinux() {
29665
+ let existing = "";
29666
+ try {
29667
+ existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
29668
+ } catch {
29669
+ return;
29670
+ }
29671
+ const lines = existing.split(`
29672
+ `).filter((l) => !l.includes(TASK_NAME));
29673
+ const newCrontab = lines.filter((l) => l.trim() !== "").join(`
29674
+ `) + `
29675
+ `;
29676
+ try {
29677
+ execSync(`echo "${newCrontab.replace(/"/g, "\\\"")}" | crontab -`, { stdio: "ignore" });
29678
+ } catch {}
29679
+ }
29680
+
29681
+ // src/commands/init.ts
29546
29682
  function registerInitCommand(program2) {
29547
29683
  program2.command("init").description("Connect this device to your ccsini account").option("--token <token>", "Setup token from dashboard").action(async (opts) => {
29548
29684
  const configDir = getConfigDir();
@@ -29558,9 +29694,8 @@ function registerInitCommand(program2) {
29558
29694
  if (!overwrite)
29559
29695
  return;
29560
29696
  }
29561
- console.log(source_default.bold(`
29562
- Welcome to ccsini!
29563
- `));
29697
+ console.log(LOGO);
29698
+ console.log(TAGLINE);
29564
29699
  let token = opts.token;
29565
29700
  if (!token) {
29566
29701
  const answer = await dist_default12.prompt([
@@ -29712,17 +29847,22 @@ function registerInitCommand(program2) {
29712
29847
  deviceName,
29713
29848
  apiUrl: "https://ccsini-api.anis-maisara190.workers.dev"
29714
29849
  });
29715
- await writeFile6(join7(configDir, ".cached-key"), masterKey, {
29850
+ await writeFile7(join8(configDir, ".cached-key"), masterKey, {
29716
29851
  mode: 384
29717
29852
  });
29718
29853
  await saveDefaultSchema(configDir);
29719
29854
  saveSpinner.text = "Installing Claude Code hooks...";
29720
29855
  await installHooks(getClaudeDir());
29856
+ saveSpinner.text = "Setting up heartbeat scheduler...";
29857
+ try {
29858
+ await installHeartbeatScheduler();
29859
+ } catch {}
29721
29860
  saveSpinner.succeed("Setup complete!");
29722
29861
  console.log(source_default.green(`
29723
29862
  \u2713 Encryption keys generated`));
29724
29863
  console.log(source_default.green(" \u2713 Device registered"));
29725
29864
  console.log(source_default.green(" \u2713 Claude Code hooks installed"));
29865
+ console.log(source_default.green(" \u2713 Heartbeat scheduler installed"));
29726
29866
  console.log(source_default.green(" \u2713 Auto-sync is active"));
29727
29867
  console.log(source_default.dim(`
29728
29868
  Just use 'claude' as normal. Sync happens automatically.
@@ -29734,7 +29874,7 @@ function registerInitCommand(program2) {
29734
29874
  });
29735
29875
  }
29736
29876
  function detectPlatform() {
29737
- const p = platform();
29877
+ const p = platform2();
29738
29878
  if (p === "win32")
29739
29879
  return "windows";
29740
29880
  if (p === "darwin")
@@ -29746,10 +29886,10 @@ function detectPlatform() {
29746
29886
  init_auth();
29747
29887
  init_auth();
29748
29888
  init_src();
29749
- import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
29750
- import { join as join8 } from "path";
29889
+ import { readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
29890
+ import { join as join9 } from "path";
29751
29891
  async function getMasterKey(configDir) {
29752
- const cachedKeyPath = join8(configDir, ".cached-key");
29892
+ const cachedKeyPath = join9(configDir, ".cached-key");
29753
29893
  try {
29754
29894
  const cached = await readFile7(cachedKeyPath);
29755
29895
  return new Uint8Array(cached);
@@ -29821,16 +29961,25 @@ function registerAutoCommands(program2) {
29821
29961
  ]);
29822
29962
  const config = await loadKeys(configDir);
29823
29963
  const masterKey = await deriveKeyFromPassphrase(password, config.salt);
29824
- await writeFile7(join8(configDir, ".cached-key"), masterKey, {
29964
+ await writeFile8(join9(configDir, ".cached-key"), masterKey, {
29825
29965
  mode: 384
29826
29966
  });
29827
29967
  console.log("Unlocked. Auto-sync is now active.");
29828
29968
  });
29969
+ program2.command("heartbeat").description("Send heartbeat to keep device online (runs via scheduled task)").action(async () => {
29970
+ const configDir = getConfigDir();
29971
+ if (!await configExists(configDir))
29972
+ return;
29973
+ try {
29974
+ const client = await getAuthenticatedClient(configDir);
29975
+ await client.heartbeat();
29976
+ } catch {}
29977
+ });
29829
29978
  program2.command("lock").description("Remove cached encryption key").action(async () => {
29830
29979
  const configDir = getConfigDir();
29831
29980
  const { rm } = await import("fs/promises");
29832
29981
  try {
29833
- await rm(join8(configDir, ".cached-key"));
29982
+ await rm(join9(configDir, ".cached-key"));
29834
29983
  console.log("Locked. Auto-sync paused until next unlock.");
29835
29984
  } catch {
29836
29985
  console.log("Already locked.");
@@ -29841,7 +29990,7 @@ function registerAutoCommands(program2) {
29841
29990
  // src/commands/doctor.ts
29842
29991
  init_source();
29843
29992
  import { access as access2 } from "fs/promises";
29844
- import { join as join9 } from "path";
29993
+ import { join as join10 } from "path";
29845
29994
  function registerDoctorCommand(program2) {
29846
29995
  program2.command("doctor").description("Diagnose ccsini setup and connectivity").action(async () => {
29847
29996
  const configDir = getConfigDir();
@@ -29883,7 +30032,7 @@ function registerDoctorCommand(program2) {
29883
30032
  allGood = false;
29884
30033
  }
29885
30034
  try {
29886
- await access2(join9(configDir, ".cached-key"));
30035
+ await access2(join10(configDir, ".cached-key"));
29887
30036
  console.log(source_default.green(" \u2713 Auto-sync unlocked"));
29888
30037
  } catch {
29889
30038
  console.log(source_default.yellow(" \u26A0 Auto-sync locked (run 'ccsini unlock')"));
@@ -29911,10 +30060,10 @@ function registerDoctorCommand(program2) {
29911
30060
  }
29912
30061
 
29913
30062
  // src/commands/self.ts
29914
- import { execSync } from "child_process";
30063
+ import { execSync as execSync2 } from "child_process";
29915
30064
  function getLatestVersion() {
29916
30065
  try {
29917
- const result = execSync("npm view ccsini version", { encoding: "utf-8" }).trim();
30066
+ const result = execSync2("npm view ccsini version", { encoding: "utf-8" }).trim();
29918
30067
  return result;
29919
30068
  } catch {
29920
30069
  return null;
@@ -29937,7 +30086,7 @@ function registerSelfCommands(program2) {
29937
30086
  `);
29938
30087
  const cmd = `bun add -g ccsini@${latest}`;
29939
30088
  try {
29940
- execSync(cmd, { stdio: "inherit" });
30089
+ execSync2(cmd, { stdio: "inherit" });
29941
30090
  console.log(`
29942
30091
  Updated to v${latest}`);
29943
30092
  try {
@@ -29957,13 +30106,16 @@ Update failed. Try manually:`);
29957
30106
  program2.command("uninstall").description("Uninstall ccsini from this machine").action(async () => {
29958
30107
  console.log(`Uninstalling ccsini...
29959
30108
  `);
30109
+ try {
30110
+ await uninstallHeartbeatScheduler();
30111
+ } catch {}
29960
30112
  let removed = false;
29961
30113
  try {
29962
- execSync("bun remove -g ccsini", { stdio: "inherit" });
30114
+ execSync2("bun remove -g ccsini", { stdio: "inherit" });
29963
30115
  removed = true;
29964
30116
  } catch {}
29965
30117
  try {
29966
- execSync("npm uninstall -g ccsini", { stdio: "inherit" });
30118
+ execSync2("npm uninstall -g ccsini", { stdio: "inherit" });
29967
30119
  removed = true;
29968
30120
  } catch {}
29969
30121
  if (removed) {
@@ -29982,9 +30134,9 @@ Uninstall failed. Try manually:`);
29982
30134
  init_auth();
29983
30135
  init_src();
29984
30136
  import { readFile as readFile8 } from "fs/promises";
29985
- import { join as join10 } from "path";
30137
+ import { join as join11 } from "path";
29986
30138
  async function getMasterKey2(configDir) {
29987
- const cachedKeyPath = join10(configDir, ".cached-key");
30139
+ const cachedKeyPath = join11(configDir, ".cached-key");
29988
30140
  try {
29989
30141
  const cached = await readFile8(cachedKeyPath);
29990
30142
  return new Uint8Array(cached);
@@ -30000,8 +30152,8 @@ async function getMasterKey2(configDir) {
30000
30152
  ]);
30001
30153
  const config = await loadKeys(configDir);
30002
30154
  const masterKey = await deriveKeyFromPassphrase(password, config.salt);
30003
- const { writeFile: writeFile8 } = await import("fs/promises");
30004
- await writeFile8(cachedKeyPath, masterKey, { mode: 384 });
30155
+ const { writeFile: writeFile9 } = await import("fs/promises");
30156
+ await writeFile9(cachedKeyPath, masterKey, { mode: 384 });
30005
30157
  return masterKey;
30006
30158
  }
30007
30159
  }
@@ -30402,17 +30554,6 @@ function registerSessionsCommand(program2) {
30402
30554
  // src/commands/menu.ts
30403
30555
  init_source();
30404
30556
  init_dist16();
30405
- var LOGO = source_default.hex("#FFA500").bold(`
30406
- \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557
30407
- \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551
30408
- \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551
30409
- \u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551
30410
- \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551
30411
- \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u255D
30412
- `);
30413
- var TAGLINE = ` ${source_default.bold(`ccsini v${VERSION}`)} \u2014 Claude Code seamless sync
30414
- ${source_default.dim("Encrypted sync for your Claude Code settings across devices")}
30415
- `;
30416
30557
  async function showInteractiveMenu(program2) {
30417
30558
  if (!process.stdin.isTTY) {
30418
30559
  program2.help();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {