ccsini 0.1.41 → 0.1.43

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 +161 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -27882,6 +27882,7 @@ var init_constants = __esm(() => {
27882
27882
  session: ["projects/**/*.jsonl"],
27883
27883
  memory: ["projects/*/memory/**/*"],
27884
27884
  skill: ["skills/**/*"],
27885
+ plugin: ["plugins/**/*"],
27885
27886
  command: ["commands/**/*"],
27886
27887
  plan: ["plans/**/*"],
27887
27888
  config: ["settings.json"],
@@ -27895,6 +27896,7 @@ var init_constants = __esm(() => {
27895
27896
  session: "append-dedup",
27896
27897
  memory: "append-sections",
27897
27898
  skill: "content-hash",
27899
+ plugin: "content-hash",
27898
27900
  command: "content-hash",
27899
27901
  plan: "merge-by-id",
27900
27902
  config: "last-write-wins",
@@ -27920,7 +27922,6 @@ var init_constants = __esm(() => {
27920
27922
  "**/node_modules/**",
27921
27923
  "**/cache/**",
27922
27924
  "**/tool-results/**",
27923
- "plugins/**",
27924
27925
  "plans/**",
27925
27926
  "todos/**",
27926
27927
  "tasks/**",
@@ -28018,14 +28019,14 @@ var {
28018
28019
  } = import__.default;
28019
28020
 
28020
28021
  // src/version.ts
28021
- var VERSION = "0.1.41";
28022
+ var VERSION = "0.1.43";
28022
28023
 
28023
28024
  // src/commands/init.ts
28024
28025
  init_source();
28025
28026
  init_ora();
28026
28027
  init_dist16();
28027
28028
  init_auth();
28028
- import { platform } from "os";
28029
+ import { platform as platform2 } from "os";
28029
28030
 
28030
28031
  // ../../node_modules/.bun/hash-wasm@4.12.0/node_modules/hash-wasm/dist/index.esm.js
28031
28032
  /*!
@@ -29078,6 +29079,12 @@ class CcsiniClient {
29078
29079
  throw new Error("Failed to reset account data");
29079
29080
  return res.json();
29080
29081
  }
29082
+ async heartbeat() {
29083
+ await fetch(`${this.apiUrl}/api/sync/heartbeat`, {
29084
+ method: "POST",
29085
+ headers: this.getHeaders()
29086
+ });
29087
+ }
29081
29088
  async logSyncEvent(event) {
29082
29089
  await fetch(`${this.apiUrl}/api/sync/log`, {
29083
29090
  method: "POST",
@@ -29541,8 +29548,8 @@ function chunkArray(arr, size) {
29541
29548
  }
29542
29549
 
29543
29550
  // src/commands/init.ts
29544
- import { writeFile as writeFile6 } from "fs/promises";
29545
- import { join as join7 } from "path";
29551
+ import { writeFile as writeFile7 } from "fs/promises";
29552
+ import { join as join8 } from "path";
29546
29553
 
29547
29554
  // src/branding.ts
29548
29555
  init_source();
@@ -29558,6 +29565,120 @@ var TAGLINE = ` ${source_default.bold(`ccsini v${VERSION}`)} \u2014 Claude Code
29558
29565
  ${source_default.dim("Encrypted sync for your Claude Code settings across devices")}
29559
29566
  `;
29560
29567
 
29568
+ // src/heartbeat/scheduler.ts
29569
+ import { execSync } from "child_process";
29570
+ import { platform } from "os";
29571
+ import { writeFile as writeFile6, unlink } from "fs/promises";
29572
+ import { join as join7 } from "path";
29573
+ import { homedir as homedir3 } from "os";
29574
+ var TASK_NAME = "ccsini-heartbeat";
29575
+ var INTERVAL_MINUTES = 3;
29576
+ async function installHeartbeatScheduler() {
29577
+ const os3 = platform();
29578
+ if (os3 === "win32") {
29579
+ await installWindows();
29580
+ } else if (os3 === "darwin") {
29581
+ await installMacOS();
29582
+ } else {
29583
+ await installLinux();
29584
+ }
29585
+ }
29586
+ async function uninstallHeartbeatScheduler() {
29587
+ const os3 = platform();
29588
+ if (os3 === "win32") {
29589
+ await uninstallWindows();
29590
+ } else if (os3 === "darwin") {
29591
+ await uninstallMacOS();
29592
+ } else {
29593
+ await uninstallLinux();
29594
+ }
29595
+ }
29596
+ async function installWindows() {
29597
+ try {
29598
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
29599
+ } catch {}
29600
+ execSync(`schtasks /Create /TN "${TASK_NAME}" /TR "ccsini heartbeat" /SC MINUTE /MO ${INTERVAL_MINUTES} /F`, { stdio: "ignore" });
29601
+ }
29602
+ async function uninstallWindows() {
29603
+ try {
29604
+ execSync(`schtasks /Delete /TN "${TASK_NAME}" /F`, { stdio: "ignore" });
29605
+ } catch {}
29606
+ }
29607
+ function getLaunchAgentPath() {
29608
+ return join7(homedir3(), "Library", "LaunchAgents", `com.ccsini.heartbeat.plist`);
29609
+ }
29610
+ async function installMacOS() {
29611
+ const plistPath = getLaunchAgentPath();
29612
+ let ccsiniPath = "ccsini";
29613
+ try {
29614
+ ccsiniPath = execSync("which ccsini", { encoding: "utf-8" }).trim();
29615
+ } catch {}
29616
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
29617
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
29618
+ <plist version="1.0">
29619
+ <dict>
29620
+ <key>Label</key>
29621
+ <string>com.ccsini.heartbeat</string>
29622
+ <key>ProgramArguments</key>
29623
+ <array>
29624
+ <string>${ccsiniPath}</string>
29625
+ <string>heartbeat</string>
29626
+ </array>
29627
+ <key>StartInterval</key>
29628
+ <integer>${INTERVAL_MINUTES * 60}</integer>
29629
+ <key>RunAtLoad</key>
29630
+ <true/>
29631
+ <key>StandardOutPath</key>
29632
+ <string>/dev/null</string>
29633
+ <key>StandardErrorPath</key>
29634
+ <string>/dev/null</string>
29635
+ </dict>
29636
+ </plist>`;
29637
+ await writeFile6(plistPath, plist);
29638
+ try {
29639
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
29640
+ } catch {}
29641
+ execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
29642
+ }
29643
+ async function uninstallMacOS() {
29644
+ const plistPath = getLaunchAgentPath();
29645
+ try {
29646
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
29647
+ } catch {}
29648
+ try {
29649
+ await unlink(plistPath);
29650
+ } catch {}
29651
+ }
29652
+ async function installLinux() {
29653
+ let existing = "";
29654
+ try {
29655
+ existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
29656
+ } catch {}
29657
+ const lines = existing.split(`
29658
+ `).filter((l) => !l.includes(TASK_NAME));
29659
+ lines.push(`*/${INTERVAL_MINUTES} * * * * ccsini heartbeat # ${TASK_NAME}`);
29660
+ const newCrontab = lines.filter((l) => l.trim() !== "").join(`
29661
+ `) + `
29662
+ `;
29663
+ execSync(`echo "${newCrontab.replace(/"/g, "\\\"")}" | crontab -`, { stdio: "ignore" });
29664
+ }
29665
+ async function uninstallLinux() {
29666
+ let existing = "";
29667
+ try {
29668
+ existing = execSync("crontab -l 2>/dev/null", { encoding: "utf-8" });
29669
+ } catch {
29670
+ return;
29671
+ }
29672
+ const lines = existing.split(`
29673
+ `).filter((l) => !l.includes(TASK_NAME));
29674
+ const newCrontab = lines.filter((l) => l.trim() !== "").join(`
29675
+ `) + `
29676
+ `;
29677
+ try {
29678
+ execSync(`echo "${newCrontab.replace(/"/g, "\\\"")}" | crontab -`, { stdio: "ignore" });
29679
+ } catch {}
29680
+ }
29681
+
29561
29682
  // src/commands/init.ts
29562
29683
  function registerInitCommand(program2) {
29563
29684
  program2.command("init").description("Connect this device to your ccsini account").option("--token <token>", "Setup token from dashboard").action(async (opts) => {
@@ -29727,17 +29848,22 @@ function registerInitCommand(program2) {
29727
29848
  deviceName,
29728
29849
  apiUrl: "https://ccsini-api.anis-maisara190.workers.dev"
29729
29850
  });
29730
- await writeFile6(join7(configDir, ".cached-key"), masterKey, {
29851
+ await writeFile7(join8(configDir, ".cached-key"), masterKey, {
29731
29852
  mode: 384
29732
29853
  });
29733
29854
  await saveDefaultSchema(configDir);
29734
29855
  saveSpinner.text = "Installing Claude Code hooks...";
29735
29856
  await installHooks(getClaudeDir());
29857
+ saveSpinner.text = "Setting up heartbeat scheduler...";
29858
+ try {
29859
+ await installHeartbeatScheduler();
29860
+ } catch {}
29736
29861
  saveSpinner.succeed("Setup complete!");
29737
29862
  console.log(source_default.green(`
29738
29863
  \u2713 Encryption keys generated`));
29739
29864
  console.log(source_default.green(" \u2713 Device registered"));
29740
29865
  console.log(source_default.green(" \u2713 Claude Code hooks installed"));
29866
+ console.log(source_default.green(" \u2713 Heartbeat scheduler installed"));
29741
29867
  console.log(source_default.green(" \u2713 Auto-sync is active"));
29742
29868
  console.log(source_default.dim(`
29743
29869
  Just use 'claude' as normal. Sync happens automatically.
@@ -29749,7 +29875,7 @@ function registerInitCommand(program2) {
29749
29875
  });
29750
29876
  }
29751
29877
  function detectPlatform() {
29752
- const p = platform();
29878
+ const p = platform2();
29753
29879
  if (p === "win32")
29754
29880
  return "windows";
29755
29881
  if (p === "darwin")
@@ -29761,10 +29887,10 @@ function detectPlatform() {
29761
29887
  init_auth();
29762
29888
  init_auth();
29763
29889
  init_src();
29764
- import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
29765
- import { join as join8 } from "path";
29890
+ import { readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
29891
+ import { join as join9 } from "path";
29766
29892
  async function getMasterKey(configDir) {
29767
- const cachedKeyPath = join8(configDir, ".cached-key");
29893
+ const cachedKeyPath = join9(configDir, ".cached-key");
29768
29894
  try {
29769
29895
  const cached = await readFile7(cachedKeyPath);
29770
29896
  return new Uint8Array(cached);
@@ -29836,16 +29962,25 @@ function registerAutoCommands(program2) {
29836
29962
  ]);
29837
29963
  const config = await loadKeys(configDir);
29838
29964
  const masterKey = await deriveKeyFromPassphrase(password, config.salt);
29839
- await writeFile7(join8(configDir, ".cached-key"), masterKey, {
29965
+ await writeFile8(join9(configDir, ".cached-key"), masterKey, {
29840
29966
  mode: 384
29841
29967
  });
29842
29968
  console.log("Unlocked. Auto-sync is now active.");
29843
29969
  });
29970
+ program2.command("heartbeat").description("Send heartbeat to keep device online (runs via scheduled task)").action(async () => {
29971
+ const configDir = getConfigDir();
29972
+ if (!await configExists(configDir))
29973
+ return;
29974
+ try {
29975
+ const client = await getAuthenticatedClient(configDir);
29976
+ await client.heartbeat();
29977
+ } catch {}
29978
+ });
29844
29979
  program2.command("lock").description("Remove cached encryption key").action(async () => {
29845
29980
  const configDir = getConfigDir();
29846
29981
  const { rm } = await import("fs/promises");
29847
29982
  try {
29848
- await rm(join8(configDir, ".cached-key"));
29983
+ await rm(join9(configDir, ".cached-key"));
29849
29984
  console.log("Locked. Auto-sync paused until next unlock.");
29850
29985
  } catch {
29851
29986
  console.log("Already locked.");
@@ -29856,7 +29991,7 @@ function registerAutoCommands(program2) {
29856
29991
  // src/commands/doctor.ts
29857
29992
  init_source();
29858
29993
  import { access as access2 } from "fs/promises";
29859
- import { join as join9 } from "path";
29994
+ import { join as join10 } from "path";
29860
29995
  function registerDoctorCommand(program2) {
29861
29996
  program2.command("doctor").description("Diagnose ccsini setup and connectivity").action(async () => {
29862
29997
  const configDir = getConfigDir();
@@ -29898,7 +30033,7 @@ function registerDoctorCommand(program2) {
29898
30033
  allGood = false;
29899
30034
  }
29900
30035
  try {
29901
- await access2(join9(configDir, ".cached-key"));
30036
+ await access2(join10(configDir, ".cached-key"));
29902
30037
  console.log(source_default.green(" \u2713 Auto-sync unlocked"));
29903
30038
  } catch {
29904
30039
  console.log(source_default.yellow(" \u26A0 Auto-sync locked (run 'ccsini unlock')"));
@@ -29926,10 +30061,10 @@ function registerDoctorCommand(program2) {
29926
30061
  }
29927
30062
 
29928
30063
  // src/commands/self.ts
29929
- import { execSync } from "child_process";
30064
+ import { execSync as execSync2 } from "child_process";
29930
30065
  function getLatestVersion() {
29931
30066
  try {
29932
- const result = execSync("npm view ccsini version", { encoding: "utf-8" }).trim();
30067
+ const result = execSync2("npm view ccsini version", { encoding: "utf-8" }).trim();
29933
30068
  return result;
29934
30069
  } catch {
29935
30070
  return null;
@@ -29952,7 +30087,7 @@ function registerSelfCommands(program2) {
29952
30087
  `);
29953
30088
  const cmd = `bun add -g ccsini@${latest}`;
29954
30089
  try {
29955
- execSync(cmd, { stdio: "inherit" });
30090
+ execSync2(cmd, { stdio: "inherit" });
29956
30091
  console.log(`
29957
30092
  Updated to v${latest}`);
29958
30093
  try {
@@ -29972,13 +30107,16 @@ Update failed. Try manually:`);
29972
30107
  program2.command("uninstall").description("Uninstall ccsini from this machine").action(async () => {
29973
30108
  console.log(`Uninstalling ccsini...
29974
30109
  `);
30110
+ try {
30111
+ await uninstallHeartbeatScheduler();
30112
+ } catch {}
29975
30113
  let removed = false;
29976
30114
  try {
29977
- execSync("bun remove -g ccsini", { stdio: "inherit" });
30115
+ execSync2("bun remove -g ccsini", { stdio: "inherit" });
29978
30116
  removed = true;
29979
30117
  } catch {}
29980
30118
  try {
29981
- execSync("npm uninstall -g ccsini", { stdio: "inherit" });
30119
+ execSync2("npm uninstall -g ccsini", { stdio: "inherit" });
29982
30120
  removed = true;
29983
30121
  } catch {}
29984
30122
  if (removed) {
@@ -29997,9 +30135,9 @@ Uninstall failed. Try manually:`);
29997
30135
  init_auth();
29998
30136
  init_src();
29999
30137
  import { readFile as readFile8 } from "fs/promises";
30000
- import { join as join10 } from "path";
30138
+ import { join as join11 } from "path";
30001
30139
  async function getMasterKey2(configDir) {
30002
- const cachedKeyPath = join10(configDir, ".cached-key");
30140
+ const cachedKeyPath = join11(configDir, ".cached-key");
30003
30141
  try {
30004
30142
  const cached = await readFile8(cachedKeyPath);
30005
30143
  return new Uint8Array(cached);
@@ -30015,8 +30153,8 @@ async function getMasterKey2(configDir) {
30015
30153
  ]);
30016
30154
  const config = await loadKeys(configDir);
30017
30155
  const masterKey = await deriveKeyFromPassphrase(password, config.salt);
30018
- const { writeFile: writeFile8 } = await import("fs/promises");
30019
- await writeFile8(cachedKeyPath, masterKey, { mode: 384 });
30156
+ const { writeFile: writeFile9 } = await import("fs/promises");
30157
+ await writeFile9(cachedKeyPath, masterKey, { mode: 384 });
30020
30158
  return masterKey;
30021
30159
  }
30022
30160
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccsini",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Claude Code seamless sync across devices",
5
5
  "type": "module",
6
6
  "bin": {