@wbern/cc-ping 1.5.1 → 1.6.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.
- package/dist/cli.js +70 -18
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -196,13 +196,14 @@ function isColorEnabled() {
|
|
|
196
196
|
function wrap(code, text) {
|
|
197
197
|
return isColorEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
198
198
|
}
|
|
199
|
-
var green, red, yellow;
|
|
199
|
+
var green, red, yellow, blue;
|
|
200
200
|
var init_color = __esm({
|
|
201
201
|
"src/color.ts"() {
|
|
202
202
|
"use strict";
|
|
203
203
|
green = (text) => wrap("32", text);
|
|
204
204
|
red = (text) => wrap("31", text);
|
|
205
205
|
yellow = (text) => wrap("33", text);
|
|
206
|
+
blue = (text) => wrap("34", text);
|
|
206
207
|
}
|
|
207
208
|
});
|
|
208
209
|
|
|
@@ -991,12 +992,22 @@ async function stopDaemon(deps) {
|
|
|
991
992
|
process.kill(pid2, "SIGTERM");
|
|
992
993
|
}
|
|
993
994
|
});
|
|
995
|
+
const _forceKill = deps?.forceKill ?? /* c8 ignore next 7 -- production default */
|
|
996
|
+
((pid2) => {
|
|
997
|
+
if (process.platform === "win32") {
|
|
998
|
+
execSync(`taskkill /F /PID ${pid2}`);
|
|
999
|
+
} else {
|
|
1000
|
+
process.kill(pid2, "SIGKILL");
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
const _log = deps?.log ?? ((msg) => console.log(msg));
|
|
994
1004
|
const status = _getDaemonStatus();
|
|
995
1005
|
if (!status.running || !status.pid) {
|
|
996
1006
|
return { success: false, error: "Daemon is not running" };
|
|
997
1007
|
}
|
|
998
1008
|
const pid = status.pid;
|
|
999
1009
|
_writeStopFile();
|
|
1010
|
+
_log(`Waiting for daemon to stop (PID: ${pid})...`);
|
|
1000
1011
|
for (let i = 0; i < GRACEFUL_POLL_ATTEMPTS; i++) {
|
|
1001
1012
|
await _sleep(GRACEFUL_POLL_MS);
|
|
1002
1013
|
if (!_isProcessRunning(pid)) {
|
|
@@ -1005,13 +1016,28 @@ async function stopDaemon(deps) {
|
|
|
1005
1016
|
return { success: true, pid };
|
|
1006
1017
|
}
|
|
1007
1018
|
}
|
|
1019
|
+
_log("Force-killing daemon...");
|
|
1008
1020
|
try {
|
|
1009
1021
|
_kill(pid);
|
|
1010
1022
|
} catch {
|
|
1011
1023
|
}
|
|
1012
1024
|
await _sleep(POST_KILL_DELAY_MS);
|
|
1025
|
+
if (_isProcessRunning(pid)) {
|
|
1026
|
+
try {
|
|
1027
|
+
_forceKill(pid);
|
|
1028
|
+
} catch {
|
|
1029
|
+
}
|
|
1030
|
+
await _sleep(POST_KILL_DELAY_MS);
|
|
1031
|
+
}
|
|
1013
1032
|
_removeDaemonState();
|
|
1014
1033
|
_removeStopFile();
|
|
1034
|
+
if (_isProcessRunning(pid)) {
|
|
1035
|
+
return {
|
|
1036
|
+
success: false,
|
|
1037
|
+
pid,
|
|
1038
|
+
error: `Failed to stop daemon (PID: ${pid})`
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1015
1041
|
return { success: true, pid };
|
|
1016
1042
|
}
|
|
1017
1043
|
async function runDaemon(intervalMs, options, deps) {
|
|
@@ -1707,6 +1733,8 @@ function colorizeStatus(windowStatus) {
|
|
|
1707
1733
|
return green(windowStatus);
|
|
1708
1734
|
case "needs ping":
|
|
1709
1735
|
return red(windowStatus);
|
|
1736
|
+
case "deferred":
|
|
1737
|
+
return blue(windowStatus);
|
|
1710
1738
|
default:
|
|
1711
1739
|
return yellow(windowStatus);
|
|
1712
1740
|
}
|
|
@@ -1717,7 +1745,7 @@ function formatStatusLine(status) {
|
|
|
1717
1745
|
const dup = status.duplicateOf ? ` [duplicate of ${status.duplicateOf}]` : "";
|
|
1718
1746
|
return ` ${status.handle}: ${colorizeStatus(status.windowStatus)} last ping: ${ping}${reset}${dup}`;
|
|
1719
1747
|
}
|
|
1720
|
-
function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicates) {
|
|
1748
|
+
function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicates, deferredHandles) {
|
|
1721
1749
|
const dupLookup = /* @__PURE__ */ new Map();
|
|
1722
1750
|
if (duplicates) {
|
|
1723
1751
|
for (const group of duplicates.values()) {
|
|
@@ -1746,11 +1774,12 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
|
|
|
1746
1774
|
};
|
|
1747
1775
|
}
|
|
1748
1776
|
const window = getWindowReset(account.handle, now);
|
|
1777
|
+
const isDeferred = !window && deferredHandles?.has(account.handle);
|
|
1749
1778
|
return {
|
|
1750
1779
|
handle: account.handle,
|
|
1751
1780
|
configDir: account.configDir,
|
|
1752
1781
|
lastPing: lastPing.toISOString(),
|
|
1753
|
-
windowStatus: window ? "active" : "needs ping",
|
|
1782
|
+
windowStatus: window ? "active" : isDeferred ? "deferred" : "needs ping",
|
|
1754
1783
|
timeUntilReset: window ? formatTimeRemaining(window.remainingMs) : null,
|
|
1755
1784
|
lastCostUsd,
|
|
1756
1785
|
lastTokens,
|
|
@@ -1758,21 +1787,21 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
|
|
|
1758
1787
|
};
|
|
1759
1788
|
});
|
|
1760
1789
|
}
|
|
1761
|
-
function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
1790
|
+
function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
|
|
1762
1791
|
const accounts = listAccounts();
|
|
1763
1792
|
if (accounts.length === 0) {
|
|
1764
1793
|
log("No accounts configured");
|
|
1765
1794
|
return;
|
|
1766
1795
|
}
|
|
1767
1796
|
const dupes = findDuplicates(accounts);
|
|
1768
|
-
const statuses = getAccountStatuses(accounts, now, dupes);
|
|
1797
|
+
const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
|
|
1769
1798
|
for (const s of statuses) {
|
|
1770
1799
|
log(formatStatusLine(s));
|
|
1771
1800
|
}
|
|
1772
1801
|
}
|
|
1773
1802
|
|
|
1774
1803
|
// src/default-command.ts
|
|
1775
|
-
function showDefault(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
1804
|
+
function showDefault(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
|
|
1776
1805
|
const accounts = listAccounts();
|
|
1777
1806
|
if (accounts.length === 0) {
|
|
1778
1807
|
log("No accounts configured.");
|
|
@@ -1782,7 +1811,7 @@ function showDefault(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
|
1782
1811
|
return;
|
|
1783
1812
|
}
|
|
1784
1813
|
const dupes = findDuplicates(accounts);
|
|
1785
|
-
const statuses = getAccountStatuses(accounts, now, dupes);
|
|
1814
|
+
const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
|
|
1786
1815
|
for (const s of statuses) {
|
|
1787
1816
|
log(formatStatusLine(s));
|
|
1788
1817
|
}
|
|
@@ -1916,7 +1945,18 @@ function suggestAccount(accounts, now = /* @__PURE__ */ new Date()) {
|
|
|
1916
1945
|
}
|
|
1917
1946
|
|
|
1918
1947
|
// src/cli.ts
|
|
1919
|
-
|
|
1948
|
+
function getDeferredHandles() {
|
|
1949
|
+
const deferred = /* @__PURE__ */ new Set();
|
|
1950
|
+
const now = /* @__PURE__ */ new Date();
|
|
1951
|
+
for (const account of listAccounts()) {
|
|
1952
|
+
const schedule = readAccountSchedule(account.configDir);
|
|
1953
|
+
if (schedule && shouldDefer(now, schedule.optimalPingHour).defer) {
|
|
1954
|
+
deferred.add(account.handle);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return deferred;
|
|
1958
|
+
}
|
|
1959
|
+
var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.6.0").option(
|
|
1920
1960
|
"--config <path>",
|
|
1921
1961
|
"Path to config directory (default: ~/.config/cc-ping, env: CC_PING_CONFIG)"
|
|
1922
1962
|
).hook("preAction", (thisCommand) => {
|
|
@@ -1925,7 +1965,7 @@ var program = new Command().name("cc-ping").description("Ping Claude Code sessio
|
|
|
1925
1965
|
setConfigDir(opts.config);
|
|
1926
1966
|
}
|
|
1927
1967
|
}).action(() => {
|
|
1928
|
-
showDefault();
|
|
1968
|
+
showDefault(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
1929
1969
|
});
|
|
1930
1970
|
program.command("ping").description("Ping configured accounts to start quota windows").argument(
|
|
1931
1971
|
"[handles...]",
|
|
@@ -2039,14 +2079,20 @@ program.command("list").description("List configured accounts").option("--json",
|
|
|
2039
2079
|
}
|
|
2040
2080
|
});
|
|
2041
2081
|
program.command("status").description("Show status of all accounts with window information").option("--json", "Output as JSON", false).action((opts) => {
|
|
2082
|
+
const deferred = getDeferredHandles();
|
|
2042
2083
|
if (opts.json) {
|
|
2043
2084
|
const accounts = listAccounts();
|
|
2044
2085
|
const dupes = findDuplicates(accounts);
|
|
2045
|
-
const statuses = getAccountStatuses(
|
|
2086
|
+
const statuses = getAccountStatuses(
|
|
2087
|
+
accounts,
|
|
2088
|
+
/* @__PURE__ */ new Date(),
|
|
2089
|
+
dupes,
|
|
2090
|
+
deferred
|
|
2091
|
+
);
|
|
2046
2092
|
console.log(JSON.stringify(statuses, null, 2));
|
|
2047
2093
|
return;
|
|
2048
2094
|
}
|
|
2049
|
-
printAccountTable();
|
|
2095
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), deferred);
|
|
2050
2096
|
});
|
|
2051
2097
|
program.command("next-reset").description("Show which account has its quota window resetting soonest").option("--json", "Output as JSON", false).action((opts) => {
|
|
2052
2098
|
const accounts = listAccounts();
|
|
@@ -2137,7 +2183,7 @@ daemon.command("start").description("Start the daemon process").option(
|
|
|
2137
2183
|
bell: opts.bell,
|
|
2138
2184
|
notify: opts.notify,
|
|
2139
2185
|
smartSchedule,
|
|
2140
|
-
version: "1.
|
|
2186
|
+
version: "1.6.0"
|
|
2141
2187
|
});
|
|
2142
2188
|
if (!result.success) {
|
|
2143
2189
|
console.error(result.error);
|
|
@@ -2151,7 +2197,7 @@ daemon.command("start").description("Start the daemon process").option(
|
|
|
2151
2197
|
"Hint: won't survive a reboot. Use `cc-ping daemon install` for a persistent service."
|
|
2152
2198
|
);
|
|
2153
2199
|
}
|
|
2154
|
-
printAccountTable();
|
|
2200
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
2155
2201
|
});
|
|
2156
2202
|
daemon.command("stop").description("Stop the daemon process").action(async () => {
|
|
2157
2203
|
const result = await stopDaemon();
|
|
@@ -2171,7 +2217,7 @@ daemon.command("stop").description("Stop the daemon process").action(async () =>
|
|
|
2171
2217
|
daemon.command("status").description("Show daemon status").option("--json", "Output as JSON", false).action(async (opts) => {
|
|
2172
2218
|
const { getServiceStatus: getServiceStatus2 } = await Promise.resolve().then(() => (init_service(), service_exports));
|
|
2173
2219
|
const svc = getServiceStatus2();
|
|
2174
|
-
const status = getDaemonStatus({ currentVersion: "1.
|
|
2220
|
+
const status = getDaemonStatus({ currentVersion: "1.6.0" });
|
|
2175
2221
|
if (opts.json) {
|
|
2176
2222
|
const serviceInfo = svc.installed ? {
|
|
2177
2223
|
service: {
|
|
@@ -2186,7 +2232,13 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
|
|
|
2186
2232
|
}
|
|
2187
2233
|
const accounts = listAccounts();
|
|
2188
2234
|
const dupes = findDuplicates(accounts);
|
|
2189
|
-
const
|
|
2235
|
+
const deferred = getDeferredHandles();
|
|
2236
|
+
const accountStatuses = getAccountStatuses(
|
|
2237
|
+
accounts,
|
|
2238
|
+
/* @__PURE__ */ new Date(),
|
|
2239
|
+
dupes,
|
|
2240
|
+
deferred
|
|
2241
|
+
);
|
|
2190
2242
|
console.log(
|
|
2191
2243
|
JSON.stringify(
|
|
2192
2244
|
{ ...status, ...serviceInfo, accounts: accountStatuses },
|
|
@@ -2225,14 +2277,14 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
|
|
|
2225
2277
|
}
|
|
2226
2278
|
if (status.versionMismatch) {
|
|
2227
2279
|
console.log(
|
|
2228
|
-
` Warning: daemon is running v${status.daemonVersion} but v${"1.
|
|
2280
|
+
` Warning: daemon is running v${status.daemonVersion} but v${"1.6.0"} is installed.`
|
|
2229
2281
|
);
|
|
2230
2282
|
console.log(
|
|
2231
2283
|
" Restart to pick up the new version: cc-ping daemon stop && cc-ping daemon start"
|
|
2232
2284
|
);
|
|
2233
2285
|
}
|
|
2234
2286
|
console.log("");
|
|
2235
|
-
printAccountTable();
|
|
2287
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
2236
2288
|
});
|
|
2237
2289
|
daemon.command("install").description("Install daemon as a system service (launchd/systemd)").option(
|
|
2238
2290
|
"--interval <minutes>",
|
|
@@ -2288,7 +2340,7 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
|
|
|
2288
2340
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2289
2341
|
intervalMs,
|
|
2290
2342
|
configDir: resolveConfigDir2(),
|
|
2291
|
-
version: "1.
|
|
2343
|
+
version: "1.6.0"
|
|
2292
2344
|
});
|
|
2293
2345
|
}
|
|
2294
2346
|
await runDaemonWithDefaults(intervalMs, {
|