@wbern/cc-ping 1.11.0 → 1.12.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 +82 -24
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -60,6 +60,7 @@ __export(config_exports, {
|
|
|
60
60
|
listAccounts: () => listAccounts,
|
|
61
61
|
loadConfig: () => loadConfig,
|
|
62
62
|
removeAccount: () => removeAccount,
|
|
63
|
+
resetSchedule: () => resetSchedule,
|
|
63
64
|
saveConfig: () => saveConfig
|
|
64
65
|
});
|
|
65
66
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
@@ -109,6 +110,21 @@ function removeAccount(handle) {
|
|
|
109
110
|
function listAccounts() {
|
|
110
111
|
return loadConfig().accounts;
|
|
111
112
|
}
|
|
113
|
+
function resetSchedule(handle, now = /* @__PURE__ */ new Date()) {
|
|
114
|
+
const config = loadConfig();
|
|
115
|
+
if (handle) {
|
|
116
|
+
const account = config.accounts.find((a) => a.handle === handle);
|
|
117
|
+
if (!account) return false;
|
|
118
|
+
account.scheduleResetAt = now.toISOString();
|
|
119
|
+
} else {
|
|
120
|
+
if (config.accounts.length === 0) return false;
|
|
121
|
+
for (const account of config.accounts) {
|
|
122
|
+
account.scheduleResetAt = now.toISOString();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
saveConfig(config);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
112
128
|
var init_config = __esm({
|
|
113
129
|
"src/config.ts"() {
|
|
114
130
|
"use strict";
|
|
@@ -644,8 +660,11 @@ function shouldDefer(now, optimalPingHour) {
|
|
|
644
660
|
}
|
|
645
661
|
return { defer: false };
|
|
646
662
|
}
|
|
647
|
-
function getAccountSchedule(historyLines, now = /* @__PURE__ */ new Date()) {
|
|
648
|
-
const cutoff =
|
|
663
|
+
function getAccountSchedule(historyLines, now = /* @__PURE__ */ new Date(), resetAt) {
|
|
664
|
+
const cutoff = Math.max(
|
|
665
|
+
now.getTime() - HISTORY_WINDOW_MS,
|
|
666
|
+
resetAt?.getTime() ?? 0
|
|
667
|
+
);
|
|
649
668
|
const timestamps = [];
|
|
650
669
|
const daysSeen = /* @__PURE__ */ new Set();
|
|
651
670
|
for (const line of historyLines) {
|
|
@@ -667,14 +686,16 @@ function getAccountSchedule(historyLines, now = /* @__PURE__ */ new Date()) {
|
|
|
667
686
|
if (max <= avg * 1.5) return null;
|
|
668
687
|
const optimalPingHour = findOptimalPingHour(histogram);
|
|
669
688
|
if (optimalPingHour === -1) return null;
|
|
670
|
-
|
|
689
|
+
const peakStart = (optimalPingHour + Math.floor(QUOTA_WINDOW_HOURS / 2) + 1) % 24;
|
|
690
|
+
const peakEnd = (peakStart + QUOTA_WINDOW_HOURS) % 24;
|
|
691
|
+
return { optimalPingHour, peakStart, peakEnd, histogram };
|
|
671
692
|
}
|
|
672
|
-
function readAccountSchedule(configDir, now = /* @__PURE__ */ new Date()) {
|
|
693
|
+
function readAccountSchedule(configDir, now = /* @__PURE__ */ new Date(), resetAt) {
|
|
673
694
|
const historyPath = join6(configDir, "history.jsonl");
|
|
674
695
|
if (!existsSync5(historyPath)) return null;
|
|
675
696
|
const content = readFileSync5(historyPath, "utf-8");
|
|
676
697
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
677
|
-
return getAccountSchedule(lines, now);
|
|
698
|
+
return getAccountSchedule(lines, now, resetAt);
|
|
678
699
|
}
|
|
679
700
|
function parseSmartSchedule(value) {
|
|
680
701
|
const lower = value.toLowerCase();
|
|
@@ -1093,10 +1114,15 @@ async function runDaemonWithDefaults(intervalMs, options) {
|
|
|
1093
1114
|
if (smartScheduleEnabled) {
|
|
1094
1115
|
const { readAccountSchedule: readAccountSchedule2, shouldDefer: shouldDefer2 } = await Promise.resolve().then(() => (init_schedule(), schedule_exports));
|
|
1095
1116
|
for (const account of listAccounts2()) {
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1117
|
+
const resetAt = account.scheduleResetAt ? new Date(account.scheduleResetAt) : void 0;
|
|
1118
|
+
const schedule2 = readAccountSchedule2(
|
|
1119
|
+
account.configDir,
|
|
1120
|
+
/* @__PURE__ */ new Date(),
|
|
1121
|
+
resetAt
|
|
1122
|
+
);
|
|
1123
|
+
if (schedule2) {
|
|
1098
1124
|
console.log(
|
|
1099
|
-
`Smart schedule: ${account.handle} \u2192 optimal ping at ${
|
|
1125
|
+
`Smart schedule: ${account.handle} \u2192 optimal ping at ${schedule2.optimalPingHour}:00 UTC`
|
|
1100
1126
|
);
|
|
1101
1127
|
} else {
|
|
1102
1128
|
console.log(
|
|
@@ -1104,10 +1130,13 @@ async function runDaemonWithDefaults(intervalMs, options) {
|
|
|
1104
1130
|
);
|
|
1105
1131
|
}
|
|
1106
1132
|
}
|
|
1107
|
-
shouldDeferPing = (
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1133
|
+
shouldDeferPing = (handle, configDir) => {
|
|
1134
|
+
const accounts = listAccounts2();
|
|
1135
|
+
const account = accounts.find((a) => a.handle === handle);
|
|
1136
|
+
const resetAt = account?.scheduleResetAt ? new Date(account.scheduleResetAt) : void 0;
|
|
1137
|
+
const schedule2 = readAccountSchedule2(configDir, /* @__PURE__ */ new Date(), resetAt);
|
|
1138
|
+
if (!schedule2) return { defer: false };
|
|
1139
|
+
return shouldDefer2(/* @__PURE__ */ new Date(), schedule2.optimalPingHour);
|
|
1111
1140
|
};
|
|
1112
1141
|
}
|
|
1113
1142
|
let hasUpgraded;
|
|
@@ -1806,7 +1835,10 @@ function formatStatusLine(status, options) {
|
|
|
1806
1835
|
lines.push(` - resets in ${status.timeUntilReset}`);
|
|
1807
1836
|
}
|
|
1808
1837
|
if (status.deferUntilUtcHour !== void 0) {
|
|
1809
|
-
|
|
1838
|
+
const peak = status.peakWindowUtc ? ` (peak: ${status.peakWindowUtc} UTC)` : "";
|
|
1839
|
+
lines.push(
|
|
1840
|
+
` - scheduled ping at ${status.deferUntilUtcHour}:00 UTC${peak}`
|
|
1841
|
+
);
|
|
1810
1842
|
}
|
|
1811
1843
|
return lines.join("\n");
|
|
1812
1844
|
}
|
|
@@ -1839,8 +1871,10 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
|
|
|
1839
1871
|
};
|
|
1840
1872
|
}
|
|
1841
1873
|
const window = getWindowReset(account.handle, now);
|
|
1842
|
-
const
|
|
1843
|
-
const
|
|
1874
|
+
const deferInfo = deferredHandles?.get(account.handle);
|
|
1875
|
+
const isDeferred = !window && deferInfo !== void 0;
|
|
1876
|
+
const deferUntilUtcHour = isDeferred ? deferInfo.optimalPingHour : void 0;
|
|
1877
|
+
const peakWindowUtc = isDeferred ? `${deferInfo.peakStart}-${deferInfo.peakEnd}` : void 0;
|
|
1844
1878
|
return {
|
|
1845
1879
|
handle: account.handle,
|
|
1846
1880
|
configDir: account.configDir,
|
|
@@ -1850,7 +1884,8 @@ function getAccountStatuses(accounts, now = /* @__PURE__ */ new Date(), duplicat
|
|
|
1850
1884
|
lastCostUsd,
|
|
1851
1885
|
lastTokens,
|
|
1852
1886
|
duplicateOf,
|
|
1853
|
-
deferUntilUtcHour
|
|
1887
|
+
deferUntilUtcHour,
|
|
1888
|
+
peakWindowUtc
|
|
1854
1889
|
};
|
|
1855
1890
|
});
|
|
1856
1891
|
}
|
|
@@ -2016,14 +2051,19 @@ function getDeferredHandles() {
|
|
|
2016
2051
|
const deferred = /* @__PURE__ */ new Map();
|
|
2017
2052
|
const now = /* @__PURE__ */ new Date();
|
|
2018
2053
|
for (const account of listAccounts()) {
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2021
|
-
|
|
2054
|
+
const resetAt = account.scheduleResetAt ? new Date(account.scheduleResetAt) : void 0;
|
|
2055
|
+
const schedule2 = readAccountSchedule(account.configDir, now, resetAt);
|
|
2056
|
+
if (schedule2 && shouldDefer(now, schedule2.optimalPingHour).defer) {
|
|
2057
|
+
deferred.set(account.handle, {
|
|
2058
|
+
optimalPingHour: schedule2.optimalPingHour,
|
|
2059
|
+
peakStart: schedule2.peakStart,
|
|
2060
|
+
peakEnd: schedule2.peakEnd
|
|
2061
|
+
});
|
|
2022
2062
|
}
|
|
2023
2063
|
}
|
|
2024
2064
|
return deferred;
|
|
2025
2065
|
}
|
|
2026
|
-
var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.
|
|
2066
|
+
var program = new Command().name("cc-ping").description("Ping Claude Code sessions to trigger quota windows early").version("1.12.0").option(
|
|
2027
2067
|
"--config <path>",
|
|
2028
2068
|
"Path to config directory (default: ~/.config/cc-ping, env: CC_PING_CONFIG)"
|
|
2029
2069
|
).hook("preAction", (thisCommand) => {
|
|
@@ -2252,7 +2292,7 @@ daemon.command("start").description("Start the daemon process").option(
|
|
|
2252
2292
|
bell: opts.bell,
|
|
2253
2293
|
notify: opts.notify,
|
|
2254
2294
|
smartSchedule,
|
|
2255
|
-
version: "1.
|
|
2295
|
+
version: "1.12.0"
|
|
2256
2296
|
});
|
|
2257
2297
|
if (!result.success) {
|
|
2258
2298
|
console.error(result.error);
|
|
@@ -2288,7 +2328,7 @@ daemon.command("stop").description("Stop the daemon process").action(async () =>
|
|
|
2288
2328
|
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) => {
|
|
2289
2329
|
const { getServiceStatus: getServiceStatus2 } = await Promise.resolve().then(() => (init_service(), service_exports));
|
|
2290
2330
|
const svc = getServiceStatus2();
|
|
2291
|
-
const status = getDaemonStatus({ currentVersion: "1.
|
|
2331
|
+
const status = getDaemonStatus({ currentVersion: "1.12.0" });
|
|
2292
2332
|
if (opts.json) {
|
|
2293
2333
|
const serviceInfo = svc.installed ? {
|
|
2294
2334
|
service: {
|
|
@@ -2349,7 +2389,7 @@ daemon.command("status").description("Show daemon status").option("--json", "Out
|
|
|
2349
2389
|
if (status.versionMismatch) {
|
|
2350
2390
|
console.log(
|
|
2351
2391
|
yellow(
|
|
2352
|
-
` Warning: daemon is running v${status.daemonVersion} but v${"1.
|
|
2392
|
+
` Warning: daemon is running v${status.daemonVersion} but v${"1.12.0"} is installed.`
|
|
2353
2393
|
)
|
|
2354
2394
|
);
|
|
2355
2395
|
console.log(
|
|
@@ -2417,7 +2457,7 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
|
|
|
2417
2457
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2418
2458
|
intervalMs,
|
|
2419
2459
|
configDir: resolveConfigDir2(),
|
|
2420
|
-
version: "1.
|
|
2460
|
+
version: "1.12.0"
|
|
2421
2461
|
});
|
|
2422
2462
|
}
|
|
2423
2463
|
await runDaemonWithDefaults(intervalMs, {
|
|
@@ -2428,4 +2468,22 @@ daemon.command("_run", { hidden: true }).option("--interval-ms <ms>", "Ping inte
|
|
|
2428
2468
|
autoUpdate: opts.autoUpdate
|
|
2429
2469
|
});
|
|
2430
2470
|
});
|
|
2471
|
+
var schedule = program.command("schedule").description("Manage smart scheduling");
|
|
2472
|
+
schedule.command("reset").description("Reset smart scheduling data to recompute optimal ping times").argument("[handle]", "Specific account handle (default: all accounts)").action((handle) => {
|
|
2473
|
+
if (resetSchedule(handle)) {
|
|
2474
|
+
if (handle) {
|
|
2475
|
+
console.log(`Schedule reset for: ${handle}`);
|
|
2476
|
+
} else {
|
|
2477
|
+
console.log("Schedule reset for all accounts");
|
|
2478
|
+
}
|
|
2479
|
+
} else {
|
|
2480
|
+
if (handle) {
|
|
2481
|
+
console.error(`Account not found: ${handle}`);
|
|
2482
|
+
process.exit(1);
|
|
2483
|
+
} else {
|
|
2484
|
+
console.error("No accounts configured");
|
|
2485
|
+
process.exit(1);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
});
|
|
2431
2489
|
program.parse();
|