@wbern/cc-ping 1.9.0 → 1.10.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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.js +45 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -5,6 +5,10 @@
5
5
  [![CI](https://github.com/wbern/cc-ping/actions/workflows/ci.yml/badge.svg)](https://github.com/wbern/cc-ping/actions/workflows/ci.yml)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
+ <p align="center">
9
+ <img src="docs/daemon-status.png" alt="cc-ping daemon status" width="520" />
10
+ </p>
11
+
8
12
  **Ping Claude Code sessions to trigger quota windows early across multiple accounts.**
9
13
 
10
14
  Claude Code has a 5-hour quota window that starts on your first message. If you rotate between accounts, your idle accounts sit there with full quota doing nothing. cc-ping pings them so their windows start ticking — when you need them, they've already reset.
package/dist/cli.js CHANGED
@@ -1772,12 +1772,31 @@ function colorizeStatus(windowStatus) {
1772
1772
  return yellow(windowStatus);
1773
1773
  }
1774
1774
  }
1775
- function formatStatusLine(status) {
1775
+ function censorHandle(handle) {
1776
+ const atIdx = handle.indexOf("@");
1777
+ if (atIdx !== -1) {
1778
+ const local = handle.slice(0, atIdx);
1779
+ const domain = handle.slice(atIdx + 1);
1780
+ return censorPart(local) + "@" + censorDomain(domain);
1781
+ }
1782
+ return censorDomain(handle);
1783
+ }
1784
+ function censorPart(part) {
1785
+ if (part.length <= 1) return part;
1786
+ return part[0] + "*".repeat(part.length - 1);
1787
+ }
1788
+ function censorDomain(domain) {
1789
+ const lastDot = domain.lastIndexOf(".");
1790
+ if (lastDot === -1) return censorPart(domain);
1791
+ const name = domain.slice(0, lastDot);
1792
+ const tld = domain.slice(lastDot);
1793
+ return censorPart(name) + tld;
1794
+ }
1795
+ function formatStatusLine(status, options) {
1776
1796
  const lines = [];
1777
- const dup = status.duplicateOf ? ` [duplicate of ${status.duplicateOf}]` : "";
1778
- lines.push(
1779
- ` ${status.handle}: ${colorizeStatus(status.windowStatus)}${dup}`
1780
- );
1797
+ const handle = options?.censor ? censorHandle(status.handle) : status.handle;
1798
+ const dup = status.duplicateOf ? ` [duplicate of ${options?.censor ? censorHandle(status.duplicateOf) : status.duplicateOf}]` : "";
1799
+ lines.push(` ${handle}: ${colorizeStatus(status.windowStatus)}${dup}`);
1781
1800
  const ping = status.lastPing === null ? "never" : status.lastPing.replace("T", " ").replace(/\.\d+Z$/, "Z");
1782
1801
  lines.push(` - last ping: ${ping}`);
1783
1802
  if (status.timeUntilReset !== null) {
@@ -1832,7 +1851,7 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
1832
1851
  };
1833
1852
  });
1834
1853
  }
1835
- function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
1854
+ function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles, options) {
1836
1855
  const accounts = listAccounts();
1837
1856
  if (accounts.length === 0) {
1838
1857
  log("No accounts configured");
@@ -1841,12 +1860,12 @@ function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date(),
1841
1860
  const dupes = findDuplicates(accounts);
1842
1861
  const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
1843
1862
  for (const s of statuses) {
1844
- log(formatStatusLine(s));
1863
+ log(formatStatusLine(s, options));
1845
1864
  }
1846
1865
  }
1847
1866
 
1848
1867
  // src/default-command.ts
1849
- function showDefault(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
1868
+ function showDefault(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles, options) {
1850
1869
  const accounts = listAccounts();
1851
1870
  if (accounts.length === 0) {
1852
1871
  log("No accounts configured.");
@@ -1858,7 +1877,7 @@ function showDefault(log = console.log, now = /* @__PURE__ */ new Date(), deferr
1858
1877
  const dupes = findDuplicates(accounts);
1859
1878
  const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
1860
1879
  for (const s of statuses) {
1861
- log(formatStatusLine(s));
1880
+ log(formatStatusLine(s, options));
1862
1881
  }
1863
1882
  const needsPing = statuses.filter((s) => s.windowStatus !== "active");
1864
1883
  if (needsPing.length > 0) {
@@ -2001,7 +2020,7 @@ function getDeferredHandles() {
2001
2020
  }
2002
2021
  return deferred;
2003
2022
  }
2004
- var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.9.0").option(
2023
+ var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.10.1").option(
2005
2024
  "--config <path>",
2006
2025
  "Path to config directory (default: ~/.config/cc-ping, env: CC_PING_CONFIG)"
2007
2026
  ).hook("preAction", (thisCommand) => {
@@ -2123,7 +2142,7 @@ program.command("list").description("List configured accounts").option("--json",
2123
2142
  console.log(` ${a.handle} -> ${a.configDir}`);
2124
2143
  }
2125
2144
  });
2126
- program.command("status").description("Show status of all accounts with window information").option("--json", "Output as JSON", false).action((opts) => {
2145
+ program.command("status").description("Show status of all accounts with window information").option("--json", "Output as JSON", false).option("--censor", "Mask account handles in output (for screenshots)").action((opts) => {
2127
2146
  const deferred = getDeferredHandles();
2128
2147
  if (opts.json) {
2129
2148
  const accounts = listAccounts();
@@ -2137,7 +2156,9 @@ program.command("status").description("Show status of all accounts with window i
2137
2156
  console.log(JSON.stringify(statuses, null, 2));
2138
2157
  return;
2139
2158
  }
2140
- printAccountTable(console.log, /* @__PURE__ */ new Date(), deferred);
2159
+ printAccountTable(console.log, /* @__PURE__ */ new Date(), deferred, {
2160
+ censor: opts.censor
2161
+ });
2141
2162
  });
2142
2163
  program.command("next-reset").description("Show which account has its quota window resetting soonest").option("--json", "Output as JSON", false).action((opts) => {
2143
2164
  const accounts = listAccounts();
@@ -2217,7 +2238,7 @@ daemon.command("start").description("Start the daemon process").option(
2217
2238
  ).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(
2218
2239
  "--smart-schedule <on|off>",
2219
2240
  "Time pings based on usage patterns (default: on)"
2220
- ).action(async (opts) => {
2241
+ ).option("--censor", "Mask account handles in output (for screenshots)").action(async (opts) => {
2221
2242
  let smartSchedule;
2222
2243
  if (opts.smartSchedule !== void 0) {
2223
2244
  smartSchedule = parseSmartSchedule(opts.smartSchedule);
@@ -2228,7 +2249,7 @@ daemon.command("start").description("Start the daemon process").option(
2228
2249
  bell: opts.bell,
2229
2250
  notify: opts.notify,
2230
2251
  smartSchedule,
2231
- version: "1.9.0"
2252
+ version: "1.10.1"
2232
2253
  });
2233
2254
  if (!result.success) {
2234
2255
  console.error(result.error);
@@ -2242,7 +2263,9 @@ daemon.command("start").description("Start the daemon process").option(
2242
2263
  "Hint: won't survive a reboot. Use `cc-ping daemon install` for a persistent service."
2243
2264
  );
2244
2265
  }
2245
- printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
2266
+ printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles(), {
2267
+ censor: opts.censor
2268
+ });
2246
2269
  });
2247
2270
  daemon.command("stop").description("Stop the daemon process").action(async () => {
2248
2271
  const result = await stopDaemon();
@@ -2259,10 +2282,10 @@ daemon.command("stop").description("Stop the daemon process").action(async () =>
2259
2282
  );
2260
2283
  }
2261
2284
  });
2262
- daemon.command("status").description("Show daemon status").option("--json", "Output as JSON", false).action(async (opts) => {
2285
+ daemon.command("status").description("Show daemon status").option("--json", "Output as JSON", false).option("--censor", "Mask account handles in output (for screenshots)").action(async (opts) => {
2263
2286
  const { getServiceStatus: getServiceStatus2 } = await Promise.resolve().then(() => (init_service(), service_exports));
2264
2287
  const svc = getServiceStatus2();
2265
- const status = getDaemonStatus({ currentVersion: "1.9.0" });
2288
+ const status = getDaemonStatus({ currentVersion: "1.10.1" });
2266
2289
  if (opts.json) {
2267
2290
  const serviceInfo = svc.installed ? {
2268
2291
  service: {
@@ -2323,7 +2346,7 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
2323
2346
  if (status.versionMismatch) {
2324
2347
  console.log(
2325
2348
  yellow(
2326
- ` Warning: daemon is running v${status.daemonVersion} but v${"1.9.0"} is installed.`
2349
+ ` Warning: daemon is running v${status.daemonVersion} but v${"1.10.1"} is installed.`
2327
2350
  )
2328
2351
  );
2329
2352
  console.log(
@@ -2333,7 +2356,9 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
2333
2356
  );
2334
2357
  }
2335
2358
  console.log("");
2336
- printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
2359
+ printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles(), {
2360
+ censor: opts.censor
2361
+ });
2337
2362
  });
2338
2363
  daemon.command("install").description("Install daemon as a system service (launchd/systemd)").option(
2339
2364
  "--interval <minutes>",
@@ -2389,7 +2414,7 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
2389
2414
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
2390
2415
  intervalMs,
2391
2416
  configDir: resolveConfigDir2(),
2392
- version: "1.9.0"
2417
+ version: "1.10.1"
2393
2418
  });
2394
2419
  }
2395
2420
  await runDaemonWithDefaults(intervalMs, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbern/cc-ping",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Ping Claude Code sessions to trigger quota windows early across multiple accounts",
5
5
  "type": "module",
6
6
  "bin": {