@wbern/cc-ping 1.8.0 → 1.9.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.
Files changed (2) hide show
  1. package/dist/cli.js +62 -55
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -701,6 +701,7 @@ __export(daemon_exports, {
701
701
  daemonPidPath: () => daemonPidPath,
702
702
  daemonStopPath: () => daemonStopPath,
703
703
  getDaemonStatus: () => getDaemonStatus,
704
+ hasVersionChanged: () => hasVersionChanged,
704
705
  isProcessRunning: () => isProcessRunning,
705
706
  msUntilUtcHour: () => msUntilUtcHour,
706
707
  parseInterval: () => parseInterval,
@@ -712,15 +713,13 @@ __export(daemon_exports, {
712
713
  stopDaemon: () => stopDaemon,
713
714
  writeDaemonState: () => writeDaemonState
714
715
  });
715
- import { execSync, spawn } from "child_process";
716
+ import { execFileSync, execSync, spawn } from "child_process";
716
717
  import {
717
718
  existsSync as existsSync6,
718
719
  closeSync as fsCloseSync,
719
720
  openSync as fsOpenSync,
720
721
  mkdirSync as mkdirSync4,
721
722
  readFileSync as readFileSync6,
722
- realpathSync,
723
- statSync as statSync2,
724
723
  unlinkSync,
725
724
  writeFileSync as writeFileSync3
726
725
  } from "fs";
@@ -832,6 +831,14 @@ function msUntilUtcHour(targetHour, now) {
832
831
  const diff = targetMs - currentMs;
833
832
  return diff > 0 ? diff : diff + 24 * 36e5;
834
833
  }
834
+ function hasVersionChanged(runningVersion, getInstalledVersion) {
835
+ if (!runningVersion) return false;
836
+ try {
837
+ return getInstalledVersion() !== runningVersion;
838
+ } catch {
839
+ return false;
840
+ }
841
+ }
835
842
  async function daemonLoop(intervalMs, options, deps) {
836
843
  let wakeDelayMs;
837
844
  while (!deps.shouldStop()) {
@@ -1070,15 +1077,7 @@ async function runDaemon(intervalMs, options, deps) {
1070
1077
  cleanup();
1071
1078
  }
1072
1079
  if (exitReason === "upgrade") {
1073
- if (deps.restart) {
1074
- try {
1075
- deps.restart();
1076
- } catch {
1077
- deps.exit(75);
1078
- }
1079
- } else {
1080
- deps.exit(75);
1081
- }
1080
+ deps.exit(75);
1082
1081
  }
1083
1082
  }
1084
1083
  async function runDaemonWithDefaults(intervalMs, options) {
@@ -1108,8 +1107,28 @@ async function runDaemonWithDefaults(intervalMs, options) {
1108
1107
  return shouldDefer2(/* @__PURE__ */ new Date(), schedule.optimalPingHour);
1109
1108
  };
1110
1109
  }
1111
- const binaryPath = realpathSync(process.argv[1]);
1112
- const startMtimeMs = statSync2(binaryPath).mtimeMs;
1110
+ let hasUpgraded;
1111
+ if (options.autoUpdate) {
1112
+ let ccPingBin = process.env.CC_PING_BIN;
1113
+ if (!ccPingBin) {
1114
+ try {
1115
+ ccPingBin = execSync("which cc-ping", {
1116
+ encoding: "utf-8",
1117
+ timeout: 5e3
1118
+ }).trim();
1119
+ } catch {
1120
+ console.log(
1121
+ "Auto-update: cc-ping not found in PATH, version checks disabled"
1122
+ );
1123
+ }
1124
+ }
1125
+ if (ccPingBin) {
1126
+ hasUpgraded = () => hasVersionChanged(
1127
+ readDaemonState()?.version,
1128
+ () => execFileSync(ccPingBin, ["--version"], { timeout: 5e3 }).toString().trim()
1129
+ );
1130
+ }
1131
+ }
1113
1132
  await runDaemon(intervalMs, options, {
1114
1133
  runPing: runPing2,
1115
1134
  listAccounts: listAccounts2,
@@ -1118,36 +1137,14 @@ async function runDaemonWithDefaults(intervalMs, options) {
1118
1137
  log: (msg) => console.log(msg),
1119
1138
  isWindowActive: (handle) => getWindowReset2(handle) !== null,
1120
1139
  shouldDeferPing,
1121
- hasUpgraded: () => {
1122
- try {
1123
- return statSync2(binaryPath).mtimeMs !== startMtimeMs;
1124
- } catch {
1125
- return false;
1126
- }
1127
- },
1140
+ hasUpgraded,
1128
1141
  updateState: (patch) => {
1129
1142
  const current = readDaemonState();
1130
1143
  if (current) writeDaemonState({ ...current, ...patch });
1131
1144
  },
1132
1145
  onSignal: (signal, handler) => process.on(signal, handler),
1133
1146
  removeSignal: (signal, handler) => process.removeListener(signal, handler),
1134
- exit: (code) => process.exit(code),
1135
- restart: () => {
1136
- console.log("Restarting daemon with updated binary...");
1137
- const result = startDaemon({
1138
- interval: `${intervalMs / 6e4}m`,
1139
- quiet: options.quiet,
1140
- bell: options.bell,
1141
- notify: options.notify,
1142
- smartSchedule: options.smartSchedule
1143
- });
1144
- if (result.success) {
1145
- console.log(`Daemon restarted (PID: ${result.pid})`);
1146
- } else {
1147
- console.log(`Failed to restart: ${result.error}`);
1148
- process.exit(75);
1149
- }
1150
- }
1147
+ exit: (code) => process.exit(code)
1151
1148
  });
1152
1149
  }
1153
1150
  var GRACEFUL_POLL_MS, GRACEFUL_POLL_ATTEMPTS, POST_KILL_DELAY_MS;
@@ -1212,19 +1209,26 @@ function generateLaunchdPlist(options, execInfo, configDir) {
1212
1209
  if (options.notify) programArgs.push("--notify");
1213
1210
  if (options.smartSchedule === false)
1214
1211
  programArgs.push("--smart-schedule", "off");
1212
+ programArgs.push("--auto-update");
1215
1213
  const allArgs = [execInfo.executable, ...programArgs];
1216
1214
  const argsXml = allArgs.map((a) => ` <string>${escapeXml(a)}</string>`).join("\n");
1217
1215
  const logPath = join10(
1218
1216
  configDir || join10(nodeHomedir(), ".config", "cc-ping"),
1219
1217
  "daemon.log"
1220
1218
  );
1219
+ const envVars = {};
1220
+ if (configDir) envVars.CC_PING_CONFIG = configDir;
1221
+ if (execInfo.args.length === 0) envVars.CC_PING_BIN = execInfo.executable;
1221
1222
  let envSection = "";
1222
- if (configDir) {
1223
+ if (Object.keys(envVars).length > 0) {
1224
+ const entries = Object.entries(envVars).map(
1225
+ ([k, v]) => ` <key>${escapeXml(k)}</key>
1226
+ <string>${escapeXml(v)}</string>`
1227
+ ).join("\n");
1223
1228
  envSection = `
1224
1229
  <key>EnvironmentVariables</key>
1225
1230
  <dict>
1226
- <key>CC_PING_CONFIG</key>
1227
- <string>${escapeXml(configDir)}</string>
1231
+ ${entries}
1228
1232
  </dict>`;
1229
1233
  }
1230
1234
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -1266,12 +1270,14 @@ function generateSystemdUnit(options, execInfo, configDir) {
1266
1270
  if (options.notify) programArgs.push("--notify");
1267
1271
  if (options.smartSchedule === false)
1268
1272
  programArgs.push("--smart-schedule", "off");
1273
+ programArgs.push("--auto-update");
1269
1274
  const execStart = [execInfo.executable, ...programArgs].map((a) => a.includes(" ") ? `"${a}"` : a).join(" ");
1270
- let envLine = "";
1271
- if (configDir) {
1272
- envLine = `
1273
- Environment=CC_PING_CONFIG=${configDir}`;
1274
- }
1275
+ const envPairs = [];
1276
+ if (configDir) envPairs.push(`CC_PING_CONFIG=${configDir}`);
1277
+ if (execInfo.args.length === 0)
1278
+ envPairs.push(`CC_PING_BIN=${execInfo.executable}`);
1279
+ const envLine = envPairs.map((p) => `
1280
+ Environment=${p}`).join("");
1275
1281
  return `[Unit]
1276
1282
  Description=cc-ping daemon - auto-ping Claude Code sessions
1277
1283
 
@@ -1915,7 +1921,7 @@ init_paths();
1915
1921
  init_run_ping();
1916
1922
 
1917
1923
  // src/scan.ts
1918
- import { existsSync as existsSync7, readdirSync, statSync as statSync3 } from "fs";
1924
+ import { existsSync as existsSync7, readdirSync, statSync as statSync2 } from "fs";
1919
1925
  import { homedir as homedir2 } from "os";
1920
1926
  import { join as join9 } from "path";
1921
1927
  function scanAccounts(dir) {
@@ -1923,7 +1929,7 @@ function scanAccounts(dir) {
1923
1929
  if (!existsSync7(accountsDir)) return [];
1924
1930
  return readdirSync(accountsDir).filter((name) => {
1925
1931
  const full = join9(accountsDir, name);
1926
- return statSync3(full).isDirectory() && !name.startsWith(".") && existsSync7(join9(full, ".claude.json"));
1932
+ return statSync2(full).isDirectory() && !name.startsWith(".") && existsSync7(join9(full, ".claude.json"));
1927
1933
  }).map((name) => ({
1928
1934
  handle: name,
1929
1935
  configDir: join9(accountsDir, name)
@@ -1995,7 +2001,7 @@ function getDeferredHandles() {
1995
2001
  }
1996
2002
  return deferred;
1997
2003
  }
1998
- var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.8.0").option(
2004
+ var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.9.0").option(
1999
2005
  "--config <path>",
2000
2006
  "Path to config directory (default: ~/.config/cc-ping, env: CC_PING_CONFIG)"
2001
2007
  ).hook("preAction", (thisCommand) => {
@@ -2222,7 +2228,7 @@ daemon.command("start").description("Start the daemon process").option(
2222
2228
  bell: opts.bell,
2223
2229
  notify: opts.notify,
2224
2230
  smartSchedule,
2225
- version: "1.8.0"
2231
+ version: "1.9.0"
2226
2232
  });
2227
2233
  if (!result.success) {
2228
2234
  console.error(result.error);
@@ -2256,7 +2262,7 @@ daemon.command("stop").description("Stop the daemon process").action(async () =>
2256
2262
  daemon.command("status").description("Show daemon status").option("--json", "Output as JSON", false).action(async (opts) => {
2257
2263
  const { getServiceStatus: getServiceStatus2 } = await Promise.resolve().then(() => (init_service(), service_exports));
2258
2264
  const svc = getServiceStatus2();
2259
- const status = getDaemonStatus({ currentVersion: "1.8.0" });
2265
+ const status = getDaemonStatus({ currentVersion: "1.9.0" });
2260
2266
  if (opts.json) {
2261
2267
  const serviceInfo = svc.installed ? {
2262
2268
  service: {
@@ -2317,7 +2323,7 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
2317
2323
  if (status.versionMismatch) {
2318
2324
  console.log(
2319
2325
  yellow(
2320
- ` Warning: daemon is running v${status.daemonVersion} but v${"1.8.0"} is installed.`
2326
+ ` Warning: daemon is running v${status.daemonVersion} but v${"1.9.0"} is installed.`
2321
2327
  )
2322
2328
  );
2323
2329
  console.log(
@@ -2366,7 +2372,7 @@ daemon.command("uninstall").description("Remove daemon system service").action(a
2366
2372
  }
2367
2373
  console.log(`Service removed: ${result.servicePath}`);
2368
2374
  });
2369
- daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping interval in milliseconds").option("-q, --quiet", "Suppress ping output", false).option("--bell", "Ring terminal bell on ping failure", false).option("--notify", "Send desktop notification on ping failure", false).option("--smart-schedule <on|off>", "Smart scheduling (default: on)").action(async (opts) => {
2375
+ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping interval in milliseconds").option("-q, --quiet", "Suppress ping output", false).option("--bell", "Ring terminal bell on ping failure", false).option("--notify", "Send desktop notification on ping failure", false).option("--smart-schedule <on|off>", "Smart scheduling (default: on)").option("--auto-update", "Auto-restart on upgrade (for service installs)").action(async (opts) => {
2370
2376
  const intervalMs = Number(opts.intervalMs);
2371
2377
  if (!intervalMs || intervalMs <= 0) {
2372
2378
  console.error("Invalid --interval-ms");
@@ -2383,14 +2389,15 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
2383
2389
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2384
2390
  intervalMs,
2385
2391
  configDir: resolveConfigDir2(),
2386
- version: "1.8.0"
2392
+ version: "1.9.0"
2387
2393
  });
2388
2394
  }
2389
2395
  await runDaemonWithDefaults(intervalMs, {
2390
2396
  quiet: opts.quiet,
2391
2397
  bell: opts.bell,
2392
2398
  notify: opts.notify,
2393
- smartSchedule
2399
+ smartSchedule,
2400
+ autoUpdate: opts.autoUpdate
2394
2401
  });
2395
2402
  });
2396
2403
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbern/cc-ping",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Ping Claude Code sessions to trigger quota windows early across multiple accounts",
5
5
  "type": "module",
6
6
  "bin": {