@wbern/cc-ping 1.5.1 → 1.7.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 +113 -42
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,6 +9,27 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/color.ts
|
|
13
|
+
function isColorEnabled() {
|
|
14
|
+
if (process.env.NO_COLOR) return false;
|
|
15
|
+
if (process.env.FORCE_COLOR === "1") return true;
|
|
16
|
+
if (process.env.FORCE_COLOR === "0") return false;
|
|
17
|
+
return process.stdout.isTTY ?? false;
|
|
18
|
+
}
|
|
19
|
+
function wrap(code, text) {
|
|
20
|
+
return isColorEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
21
|
+
}
|
|
22
|
+
var green, red, yellow, blue;
|
|
23
|
+
var init_color = __esm({
|
|
24
|
+
"src/color.ts"() {
|
|
25
|
+
"use strict";
|
|
26
|
+
green = (text) => wrap("32", text);
|
|
27
|
+
red = (text) => wrap("31", text);
|
|
28
|
+
yellow = (text) => wrap("33", text);
|
|
29
|
+
blue = (text) => wrap("34", text);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
12
33
|
// src/paths.ts
|
|
13
34
|
var paths_exports = {};
|
|
14
35
|
__export(paths_exports, {
|
|
@@ -186,26 +207,6 @@ var init_bell = __esm({
|
|
|
186
207
|
}
|
|
187
208
|
});
|
|
188
209
|
|
|
189
|
-
// src/color.ts
|
|
190
|
-
function isColorEnabled() {
|
|
191
|
-
if (process.env.NO_COLOR) return false;
|
|
192
|
-
if (process.env.FORCE_COLOR === "1") return true;
|
|
193
|
-
if (process.env.FORCE_COLOR === "0") return false;
|
|
194
|
-
return process.stdout.isTTY ?? false;
|
|
195
|
-
}
|
|
196
|
-
function wrap(code, text) {
|
|
197
|
-
return isColorEnabled() ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
198
|
-
}
|
|
199
|
-
var green, red, yellow;
|
|
200
|
-
var init_color = __esm({
|
|
201
|
-
"src/color.ts"() {
|
|
202
|
-
"use strict";
|
|
203
|
-
green = (text) => wrap("32", text);
|
|
204
|
-
red = (text) => wrap("31", text);
|
|
205
|
-
yellow = (text) => wrap("33", text);
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
210
|
// src/history.ts
|
|
210
211
|
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "fs";
|
|
211
212
|
import { join as join5 } from "path";
|
|
@@ -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) {
|
|
@@ -1440,6 +1466,9 @@ function checkAccounts(accounts) {
|
|
|
1440
1466
|
return accounts.map((a) => checkAccount(a));
|
|
1441
1467
|
}
|
|
1442
1468
|
|
|
1469
|
+
// src/cli.ts
|
|
1470
|
+
init_color();
|
|
1471
|
+
|
|
1443
1472
|
// src/completions.ts
|
|
1444
1473
|
var COMMANDS = "ping scan add remove list status next-reset history suggest check completions moo daemon";
|
|
1445
1474
|
function bashCompletion() {
|
|
@@ -1707,17 +1736,29 @@ function colorizeStatus(windowStatus) {
|
|
|
1707
1736
|
return green(windowStatus);
|
|
1708
1737
|
case "needs ping":
|
|
1709
1738
|
return red(windowStatus);
|
|
1739
|
+
case "deferred":
|
|
1740
|
+
return blue(windowStatus);
|
|
1710
1741
|
default:
|
|
1711
1742
|
return yellow(windowStatus);
|
|
1712
1743
|
}
|
|
1713
1744
|
}
|
|
1714
1745
|
function formatStatusLine(status) {
|
|
1715
|
-
const
|
|
1716
|
-
const reset = status.timeUntilReset !== null ? ` (resets in ${status.timeUntilReset})` : "";
|
|
1746
|
+
const lines = [];
|
|
1717
1747
|
const dup = status.duplicateOf ? ` [duplicate of ${status.duplicateOf}]` : "";
|
|
1718
|
-
|
|
1748
|
+
lines.push(
|
|
1749
|
+
` ${status.handle}: ${colorizeStatus(status.windowStatus)}${dup}`
|
|
1750
|
+
);
|
|
1751
|
+
const ping = status.lastPing === null ? "never" : status.lastPing.replace("T", " ").replace(/\.\d+Z$/, "Z");
|
|
1752
|
+
lines.push(` - last ping: ${ping}`);
|
|
1753
|
+
if (status.timeUntilReset !== null) {
|
|
1754
|
+
lines.push(` - resets in ${status.timeUntilReset}`);
|
|
1755
|
+
}
|
|
1756
|
+
if (status.deferUntilUtcHour !== void 0) {
|
|
1757
|
+
lines.push(` - scheduled ping at ${status.deferUntilUtcHour}:00 UTC`);
|
|
1758
|
+
}
|
|
1759
|
+
return lines.join("\n");
|
|
1719
1760
|
}
|
|
1720
|
-
function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicates) {
|
|
1761
|
+
function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicates, deferredHandles) {
|
|
1721
1762
|
const dupLookup = /* @__PURE__ */ new Map();
|
|
1722
1763
|
if (duplicates) {
|
|
1723
1764
|
for (const group of duplicates.values()) {
|
|
@@ -1746,33 +1787,36 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
|
|
|
1746
1787
|
};
|
|
1747
1788
|
}
|
|
1748
1789
|
const window = getWindowReset(account.handle, now);
|
|
1790
|
+
const isDeferred = !window && deferredHandles?.has(account.handle);
|
|
1791
|
+
const deferUntilUtcHour = isDeferred ? deferredHandles?.get(account.handle) : void 0;
|
|
1749
1792
|
return {
|
|
1750
1793
|
handle: account.handle,
|
|
1751
1794
|
configDir: account.configDir,
|
|
1752
1795
|
lastPing: lastPing.toISOString(),
|
|
1753
|
-
windowStatus: window ? "active" : "needs ping",
|
|
1796
|
+
windowStatus: window ? "active" : isDeferred ? "deferred" : "needs ping",
|
|
1754
1797
|
timeUntilReset: window ? formatTimeRemaining(window.remainingMs) : null,
|
|
1755
1798
|
lastCostUsd,
|
|
1756
1799
|
lastTokens,
|
|
1757
|
-
duplicateOf
|
|
1800
|
+
duplicateOf,
|
|
1801
|
+
deferUntilUtcHour
|
|
1758
1802
|
};
|
|
1759
1803
|
});
|
|
1760
1804
|
}
|
|
1761
|
-
function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
1805
|
+
function printAccountTable(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
|
|
1762
1806
|
const accounts = listAccounts();
|
|
1763
1807
|
if (accounts.length === 0) {
|
|
1764
1808
|
log("No accounts configured");
|
|
1765
1809
|
return;
|
|
1766
1810
|
}
|
|
1767
1811
|
const dupes = findDuplicates(accounts);
|
|
1768
|
-
const statuses = getAccountStatuses(accounts, now, dupes);
|
|
1812
|
+
const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
|
|
1769
1813
|
for (const s of statuses) {
|
|
1770
1814
|
log(formatStatusLine(s));
|
|
1771
1815
|
}
|
|
1772
1816
|
}
|
|
1773
1817
|
|
|
1774
1818
|
// src/default-command.ts
|
|
1775
|
-
function showDefault(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
1819
|
+
function showDefault(log = console.log, now = /* @__PURE__ */ new Date(), deferredHandles) {
|
|
1776
1820
|
const accounts = listAccounts();
|
|
1777
1821
|
if (accounts.length === 0) {
|
|
1778
1822
|
log("No accounts configured.");
|
|
@@ -1782,7 +1826,7 @@ function showDefault(log = console.log, now = /* @__PURE__ */ new Date()) {
|
|
|
1782
1826
|
return;
|
|
1783
1827
|
}
|
|
1784
1828
|
const dupes = findDuplicates(accounts);
|
|
1785
|
-
const statuses = getAccountStatuses(accounts, now, dupes);
|
|
1829
|
+
const statuses = getAccountStatuses(accounts, now, dupes, deferredHandles);
|
|
1786
1830
|
for (const s of statuses) {
|
|
1787
1831
|
log(formatStatusLine(s));
|
|
1788
1832
|
}
|
|
@@ -1916,7 +1960,18 @@ function suggestAccount(accounts, now = /* @__PURE__ */ new Date()) {
|
|
|
1916
1960
|
}
|
|
1917
1961
|
|
|
1918
1962
|
// src/cli.ts
|
|
1919
|
-
|
|
1963
|
+
function getDeferredHandles() {
|
|
1964
|
+
const deferred = /* @__PURE__ */ new Map();
|
|
1965
|
+
const now = /* @__PURE__ */ new Date();
|
|
1966
|
+
for (const account of listAccounts()) {
|
|
1967
|
+
const schedule = readAccountSchedule(account.configDir);
|
|
1968
|
+
if (schedule && shouldDefer(now, schedule.optimalPingHour).defer) {
|
|
1969
|
+
deferred.set(account.handle, schedule.optimalPingHour);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
return deferred;
|
|
1973
|
+
}
|
|
1974
|
+
var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.7.0").option(
|
|
1920
1975
|
"--config <path>",
|
|
1921
1976
|
"Path to config directory (default: ~/.config/cc-ping, env: CC_PING_CONFIG)"
|
|
1922
1977
|
).hook("preAction", (thisCommand) => {
|
|
@@ -1925,7 +1980,7 @@ var program = new Command().name("cc-ping").description("Ping Claude Code sessio
|
|
|
1925
1980
|
setConfigDir(opts.config);
|
|
1926
1981
|
}
|
|
1927
1982
|
}).action(() => {
|
|
1928
|
-
showDefault();
|
|
1983
|
+
showDefault(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
1929
1984
|
});
|
|
1930
1985
|
program.command("ping").description("Ping configured accounts to start quota windows").argument(
|
|
1931
1986
|
"[handles...]",
|
|
@@ -2039,14 +2094,20 @@ program.command("list").description("List configured accounts").option("--json",
|
|
|
2039
2094
|
}
|
|
2040
2095
|
});
|
|
2041
2096
|
program.command("status").description("Show status of all accounts with window information").option("--json", "Output as JSON", false).action((opts) => {
|
|
2097
|
+
const deferred = getDeferredHandles();
|
|
2042
2098
|
if (opts.json) {
|
|
2043
2099
|
const accounts = listAccounts();
|
|
2044
2100
|
const dupes = findDuplicates(accounts);
|
|
2045
|
-
const statuses = getAccountStatuses(
|
|
2101
|
+
const statuses = getAccountStatuses(
|
|
2102
|
+
accounts,
|
|
2103
|
+
/* @__PURE__ */ new Date(),
|
|
2104
|
+
dupes,
|
|
2105
|
+
deferred
|
|
2106
|
+
);
|
|
2046
2107
|
console.log(JSON.stringify(statuses, null, 2));
|
|
2047
2108
|
return;
|
|
2048
2109
|
}
|
|
2049
|
-
printAccountTable();
|
|
2110
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), deferred);
|
|
2050
2111
|
});
|
|
2051
2112
|
program.command("next-reset").description("Show which account has its quota window resetting soonest").option("--json", "Output as JSON", false).action((opts) => {
|
|
2052
2113
|
const accounts = listAccounts();
|
|
@@ -2137,7 +2198,7 @@ daemon.command("start").description("Start the daemon process").option(
|
|
|
2137
2198
|
bell: opts.bell,
|
|
2138
2199
|
notify: opts.notify,
|
|
2139
2200
|
smartSchedule,
|
|
2140
|
-
version: "1.
|
|
2201
|
+
version: "1.7.0"
|
|
2141
2202
|
});
|
|
2142
2203
|
if (!result.success) {
|
|
2143
2204
|
console.error(result.error);
|
|
@@ -2151,7 +2212,7 @@ daemon.command("start").description("Start the daemon process").option(
|
|
|
2151
2212
|
"Hint: won't survive a reboot. Use `cc-ping daemon install` for a persistent service."
|
|
2152
2213
|
);
|
|
2153
2214
|
}
|
|
2154
|
-
printAccountTable();
|
|
2215
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
2155
2216
|
});
|
|
2156
2217
|
daemon.command("stop").description("Stop the daemon process").action(async () => {
|
|
2157
2218
|
const result = await stopDaemon();
|
|
@@ -2171,7 +2232,7 @@ daemon.command("stop").description("Stop the daemon process").action(async () =>
|
|
|
2171
2232
|
daemon.command("status").description("Show daemon status").option("--json", "Output as JSON", false).action(async (opts) => {
|
|
2172
2233
|
const { getServiceStatus: getServiceStatus2 } = await Promise.resolve().then(() => (init_service(), service_exports));
|
|
2173
2234
|
const svc = getServiceStatus2();
|
|
2174
|
-
const status = getDaemonStatus({ currentVersion: "1.
|
|
2235
|
+
const status = getDaemonStatus({ currentVersion: "1.7.0" });
|
|
2175
2236
|
if (opts.json) {
|
|
2176
2237
|
const serviceInfo = svc.installed ? {
|
|
2177
2238
|
service: {
|
|
@@ -2186,7 +2247,13 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
|
|
|
2186
2247
|
}
|
|
2187
2248
|
const accounts = listAccounts();
|
|
2188
2249
|
const dupes = findDuplicates(accounts);
|
|
2189
|
-
const
|
|
2250
|
+
const deferred = getDeferredHandles();
|
|
2251
|
+
const accountStatuses = getAccountStatuses(
|
|
2252
|
+
accounts,
|
|
2253
|
+
/* @__PURE__ */ new Date(),
|
|
2254
|
+
dupes,
|
|
2255
|
+
deferred
|
|
2256
|
+
);
|
|
2190
2257
|
console.log(
|
|
2191
2258
|
JSON.stringify(
|
|
2192
2259
|
{ ...status, ...serviceInfo, accounts: accountStatuses },
|
|
@@ -2225,14 +2292,18 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
|
|
|
2225
2292
|
}
|
|
2226
2293
|
if (status.versionMismatch) {
|
|
2227
2294
|
console.log(
|
|
2228
|
-
|
|
2295
|
+
yellow(
|
|
2296
|
+
` Warning: daemon is running v${status.daemonVersion} but v${"1.7.0"} is installed.`
|
|
2297
|
+
)
|
|
2229
2298
|
);
|
|
2230
2299
|
console.log(
|
|
2231
|
-
|
|
2300
|
+
yellow(
|
|
2301
|
+
" Restart to pick up the new version: cc-ping daemon stop && cc-ping daemon start"
|
|
2302
|
+
)
|
|
2232
2303
|
);
|
|
2233
2304
|
}
|
|
2234
2305
|
console.log("");
|
|
2235
|
-
printAccountTable();
|
|
2306
|
+
printAccountTable(console.log, /* @__PURE__ */ new Date(), getDeferredHandles());
|
|
2236
2307
|
});
|
|
2237
2308
|
daemon.command("install").description("Install daemon as a system service (launchd/systemd)").option(
|
|
2238
2309
|
"--interval <minutes>",
|
|
@@ -2288,7 +2359,7 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
|
|
|
2288
2359
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2289
2360
|
intervalMs,
|
|
2290
2361
|
configDir: resolveConfigDir2(),
|
|
2291
|
-
version: "1.
|
|
2362
|
+
version: "1.7.0"
|
|
2292
2363
|
});
|
|
2293
2364
|
}
|
|
2294
2365
|
await runDaemonWithDefaults(intervalMs, {
|