@claudeclock/run 1.0.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/components/ProgressBar.d.ts +6 -0
- package/dist/components/ProgressBar.js +10 -0
- package/dist/components/ProgressBar.js.map +1 -0
- package/dist/components/QuickStatus.d.ts +6 -0
- package/dist/components/QuickStatus.js +13 -0
- package/dist/components/QuickStatus.js.map +1 -0
- package/dist/components/WatchDashboard.d.ts +7 -0
- package/dist/components/WatchDashboard.js +41 -0
- package/dist/components/WatchDashboard.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/notify.d.ts +1 -0
- package/dist/notify.js +5 -0
- package/dist/notify.js.map +1 -0
- package/dist/promo.d.ts +49 -0
- package/dist/promo.js +258 -0
- package/dist/promo.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
export function ProgressBar({ progress, width = 20 }) {
|
|
4
|
+
const clamped = Math.max(0, Math.min(1, progress));
|
|
5
|
+
const filled = Math.round(clamped * width);
|
|
6
|
+
const empty = width - filled;
|
|
7
|
+
const percent = Math.round(clamped * 100);
|
|
8
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: "green", children: '▓'.repeat(filled) }), _jsx(Text, { dimColor: true, children: '░'.repeat(empty) }), _jsxs(Text, { children: [" ", percent, "%"] })] }));
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=ProgressBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProgressBar.js","sourceRoot":"","sources":["../../src/components/ProgressBar.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAO3B,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,KAAK,GAAG,EAAE,EAAoB;IACpE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;IAE1C,OAAO,CACL,MAAC,IAAI,eACH,KAAC,IAAI,IAAC,KAAK,EAAC,OAAO,YAAE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAQ,EAC/C,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAQ,EACzC,MAAC,IAAI,oBAAG,OAAO,SAAS,IACnB,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { formatDuration, formatLocalTime } from '../promo.js';
|
|
4
|
+
export function QuickStatus({ status }) {
|
|
5
|
+
if (!status.hasActivePromo) {
|
|
6
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ['\u23F8', " No active Claude promotion"] }), _jsx(Text, { dimColor: true, children: "Check claudeclock.com for updates" })] }));
|
|
7
|
+
}
|
|
8
|
+
if (status.bonusActive) {
|
|
9
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "green", bold: true, children: ['\u26A1', " ", status.multiplier, '\u00D7', " ACTIVE ", '\u2014', " ", formatDuration(status.minutesRemaining), " remaining"] }), status.windowEndLocal && (_jsxs(Text, { dimColor: true, children: ["Ends at ", formatLocalTime(status.windowEndLocal)] })), status.promoEndDate && (_jsxs(Text, { dimColor: true, children: ["Promo ends ", status.promoEndDate.toLocaleDateString()] }))] }));
|
|
10
|
+
}
|
|
11
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "yellow", children: ['\uD83D\uDE34', " Peak hours ", '\u2014', " ", status.multiplier, '\u00D7', " resumes in ", formatDuration(status.minutesUntilBonus)] }), status.nextBonusStartLocal && (_jsxs(Text, { dimColor: true, children: ["Next bonus at ", formatLocalTime(status.nextBonusStartLocal)] }))] }));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=QuickStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuickStatus.js","sourceRoot":"","sources":["../../src/components/QuickStatus.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAM9D,MAAM,UAAU,WAAW,CAAC,EAAE,MAAM,EAAoB;IACtD,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;QAC3B,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,IAAI,eAAE,QAAQ,mCAAmC,EAClD,KAAC,IAAI,IAAC,QAAQ,wDAAyC,IACnD,CACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,IAAI,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,mBACrB,QAAQ,OAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,cAAU,QAAQ,OAAG,cAAc,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAC9F,EACN,MAAM,CAAC,cAAc,IAAI,CACxB,MAAC,IAAI,IAAC,QAAQ,+BAAU,eAAe,CAAC,MAAM,CAAC,cAAc,CAAC,IAAQ,CACvE,EACA,MAAM,CAAC,YAAY,IAAI,CACtB,MAAC,IAAI,IAAC,QAAQ,kCAAa,MAAM,CAAC,YAAY,CAAC,kBAAkB,EAAE,IAAQ,CAC5E,IACG,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,MAAC,IAAI,IAAC,KAAK,EAAC,QAAQ,aACjB,cAAc,kBAAc,QAAQ,OAAG,MAAM,CAAC,UAAU,EAAE,QAAQ,kBAAc,cAAc,CAAC,MAAM,CAAC,iBAAiB,CAAC,IACpH,EACN,MAAM,CAAC,mBAAmB,IAAI,CAC7B,MAAC,IAAI,IAAC,QAAQ,qCAAgB,eAAe,CAAC,MAAM,CAAC,mBAAmB,CAAC,IAAQ,CAClF,IACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useEffect, useRef } from 'react';
|
|
3
|
+
import { Box, Text, useApp, useInput } from 'ink';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
import { getPromoStatus, formatDuration, formatLocalTime } from '../promo.js';
|
|
6
|
+
import { ProgressBar } from './ProgressBar.js';
|
|
7
|
+
import { sendNotification } from '../notify.js';
|
|
8
|
+
export function WatchDashboard({ config, notify }) {
|
|
9
|
+
const { exit } = useApp();
|
|
10
|
+
const [status, setStatus] = useState(() => getPromoStatus(config));
|
|
11
|
+
const prevBonusActive = useRef(status.bonusActive);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const timer = setInterval(() => {
|
|
14
|
+
setStatus(getPromoStatus(config));
|
|
15
|
+
}, 1000);
|
|
16
|
+
return () => clearInterval(timer);
|
|
17
|
+
}, [config]);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (!notify)
|
|
20
|
+
return;
|
|
21
|
+
if (prevBonusActive.current !== status.bonusActive) {
|
|
22
|
+
if (status.bonusActive) {
|
|
23
|
+
sendNotification('Claude Clock', '2\u00D7 bonus window is now active!');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
sendNotification('Claude Clock', 'Bonus window ended.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
prevBonusActive.current = status.bonusActive;
|
|
30
|
+
}, [status.bonusActive, notify]);
|
|
31
|
+
useInput((input) => {
|
|
32
|
+
if (input === 'q') {
|
|
33
|
+
exit();
|
|
34
|
+
}
|
|
35
|
+
}, { isActive: process.stdin.isTTY === true });
|
|
36
|
+
if (!status.hasActivePromo) {
|
|
37
|
+
return (_jsxs(Box, { borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ['\u26A1', " CLAUDE CLOCK"] }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "No active promotion" }), _jsxs(Text, { dimColor: true, children: ["q to quit ", '\u00B7', " claudeclock.com"] })] }));
|
|
38
|
+
}
|
|
39
|
+
return (_jsxs(Box, { borderStyle: "round", flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ['\u26A1', " CLAUDE CLOCK"] }), _jsx(Text, { children: " " }), status.bonusActive ? (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "green", bold: true, children: [status.multiplier, '\u00D7', " BONUS ACTIVE"] }), _jsx(Text, { children: " " }), _jsx(ProgressBar, { progress: status.bonusProgress }), _jsx(Text, { children: " " }), status.windowEndLocal && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["Ends: ", _jsx(Text, { bold: true, children: formatLocalTime(status.windowEndLocal) })] }), _jsxs(Text, { children: ["Remaining: ", _jsx(Text, { bold: true, children: formatDuration(status.minutesRemaining) })] })] }))] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "yellow", bold: true, children: ['\uD83D\uDE34', " PEAK HOURS (1", '\u00D7', ")"] }), _jsx(Text, { children: " " }), status.nextBonusStartLocal && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: [status.multiplier, '\u00D7', " in: ", _jsx(Text, { bold: true, children: formatDuration(status.minutesUntilBonus) })] }), _jsxs(Text, { children: ["Resumes: ", _jsx(Text, { bold: true, children: formatLocalTime(status.nextBonusStartLocal) })] })] }))] })), _jsx(Text, { children: " " }), status.promo && (_jsxs(Text, { dimColor: true, children: [status.promo.name, " ", '\u2014', " ends ", status.promoEndDate?.toLocaleDateString()] })), _jsxs(Text, { dimColor: true, children: ["q to quit ", '\u00B7', " claudeclock.com"] })] }));
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=WatchDashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WatchDashboard.js","sourceRoot":"","sources":["../../src/components/WatchDashboard.tsx"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
export const FALLBACK_CONFIG = require('../../../shared/promo-config.json');
|
|
4
|
+
const PROMO_URL = 'https://claudeclock.com/api/promo';
|
|
5
|
+
export async function loadConfig() {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(PROMO_URL, { signal: AbortSignal.timeout(5000) });
|
|
8
|
+
if (!res.ok)
|
|
9
|
+
throw new Error(`HTTP ${res.status}`);
|
|
10
|
+
return (await res.json());
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return FALLBACK_CONFIG;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAE/C,MAAM,CAAC,MAAM,eAAe,GAAgB,OAAO,CAAC,mCAAmC,CAAC,CAAC;AAEzF,MAAM,SAAS,GAAG,mCAAmC,CAAC;AAEtD,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1E,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgB,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,eAAe,CAAC;IACzB,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { render } from 'ink';
|
|
4
|
+
import meow from 'meow';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
import { getPromoStatus } from './promo.js';
|
|
7
|
+
import { QuickStatus } from './components/QuickStatus.js';
|
|
8
|
+
import { WatchDashboard } from './components/WatchDashboard.js';
|
|
9
|
+
const cli = meow(`
|
|
10
|
+
Usage
|
|
11
|
+
$ claudeclock
|
|
12
|
+
|
|
13
|
+
Options
|
|
14
|
+
--watch, -w Live dashboard mode
|
|
15
|
+
--json Machine-readable JSON output
|
|
16
|
+
--notify Enable system notifications (watch mode)
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
$ claudeclock
|
|
20
|
+
$ claudeclock --watch --notify
|
|
21
|
+
$ claudeclock --json
|
|
22
|
+
`, {
|
|
23
|
+
importMeta: import.meta,
|
|
24
|
+
flags: {
|
|
25
|
+
watch: {
|
|
26
|
+
type: 'boolean',
|
|
27
|
+
shortFlag: 'w',
|
|
28
|
+
default: false,
|
|
29
|
+
},
|
|
30
|
+
json: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
notify: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
default: false,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
async function main() {
|
|
41
|
+
const config = await loadConfig();
|
|
42
|
+
const status = getPromoStatus(config);
|
|
43
|
+
if (cli.flags.json) {
|
|
44
|
+
console.log(JSON.stringify(status, null, 2));
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
if (cli.flags.watch) {
|
|
48
|
+
render(_jsx(WatchDashboard, { config: config, notify: cli.flags.notify }));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
// Quick-check mode: render once and exit
|
|
52
|
+
const { unmount, waitUntilExit } = render(_jsx(QuickStatus, { status: status }));
|
|
53
|
+
unmount();
|
|
54
|
+
await waitUntilExit();
|
|
55
|
+
}
|
|
56
|
+
main().catch((err) => {
|
|
57
|
+
console.error(err);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAEhE,MAAM,GAAG,GAAG,IAAI,CACd;;;;;;;;;;;;;CAaD,EACC;IACE,UAAU,EAAE,MAAM,CAAC,IAAI;IACvB,KAAK,EAAE;QACL,KAAK,EAAE;YACL,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,GAAG;YACd,OAAO,EAAE,KAAK;SACf;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;SACf;QACD,MAAM,EAAE;YACN,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,KAAK;SACf;KACF;CACF,CACF,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAEtC,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACpB,MAAM,CAAC,KAAC,cAAc,IAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,GAAI,CAAC,CAAC;QACrE,OAAO;IACT,CAAC;IAED,yCAAyC;IACzC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,KAAC,WAAW,IAAC,MAAM,EAAE,MAAM,GAAI,CAAC,CAAC;IAC3E,OAAO,EAAE,CAAC;IACV,MAAM,aAAa,EAAE,CAAC;AACxB,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/notify.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sendNotification(title: string, message: string): void;
|
package/dist/notify.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../src/notify.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AAErC,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,OAAe;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
package/dist/promo.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface PeakHours {
|
|
2
|
+
timezone: string;
|
|
3
|
+
weekdaysOnly: boolean;
|
|
4
|
+
start: string;
|
|
5
|
+
end: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Promo {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
multiplier: number;
|
|
12
|
+
startDate: string;
|
|
13
|
+
endDate: string;
|
|
14
|
+
peakHours: PeakHours;
|
|
15
|
+
eligiblePlans: string[];
|
|
16
|
+
infoUrl: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PromoConfig {
|
|
19
|
+
version: number;
|
|
20
|
+
promos: Promo[];
|
|
21
|
+
}
|
|
22
|
+
export interface PromoStatus {
|
|
23
|
+
hasActivePromo: boolean;
|
|
24
|
+
promo: Promo | null;
|
|
25
|
+
bonusActive: boolean;
|
|
26
|
+
multiplier: number;
|
|
27
|
+
minutesRemaining: number;
|
|
28
|
+
minutesUntilBonus: number;
|
|
29
|
+
windowEndLocal: Date | null;
|
|
30
|
+
nextBonusStartLocal: Date | null;
|
|
31
|
+
promoEndDate: Date | null;
|
|
32
|
+
bonusProgress: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Find the first promo whose date range contains `now`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getActivePromo(config: PromoConfig, now?: Date): Promo | null;
|
|
38
|
+
/**
|
|
39
|
+
* Get full promo status for the given config and time.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getPromoStatus(config: PromoConfig, now?: Date): PromoStatus;
|
|
42
|
+
/**
|
|
43
|
+
* Format a duration in minutes as "Xh Ym".
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatDuration(totalMinutes: number): string;
|
|
46
|
+
/**
|
|
47
|
+
* Format a date as local time with timezone name.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatLocalTime(date: Date): string;
|
package/dist/promo.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// Core promo status logic with timezone handling.
|
|
2
|
+
// Shared brain used by CLI, web, and menu bar apps.
|
|
3
|
+
/**
|
|
4
|
+
* Find the first promo whose date range contains `now`.
|
|
5
|
+
*/
|
|
6
|
+
export function getActivePromo(config, now) {
|
|
7
|
+
const ts = (now ?? new Date()).getTime();
|
|
8
|
+
for (const promo of config.promos) {
|
|
9
|
+
const start = new Date(promo.startDate).getTime();
|
|
10
|
+
const end = new Date(promo.endDate).getTime();
|
|
11
|
+
if (ts >= start && ts <= end) {
|
|
12
|
+
return promo;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse "HH:MM" into { hours, minutes }.
|
|
19
|
+
*/
|
|
20
|
+
function parseTime(time) {
|
|
21
|
+
const [h, m] = time.split(':').map(Number);
|
|
22
|
+
return { hours: h, minutes: m };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the current time components in a specific timezone using Intl.
|
|
26
|
+
*/
|
|
27
|
+
function getTimeInTimezone(date, timezone) {
|
|
28
|
+
// Use Intl.DateTimeFormat to get components in the target timezone
|
|
29
|
+
const formatter = new Intl.DateTimeFormat('en-US', {
|
|
30
|
+
timeZone: timezone,
|
|
31
|
+
year: 'numeric',
|
|
32
|
+
month: 'numeric',
|
|
33
|
+
day: 'numeric',
|
|
34
|
+
hour: 'numeric',
|
|
35
|
+
minute: 'numeric',
|
|
36
|
+
weekday: 'short',
|
|
37
|
+
hour12: false,
|
|
38
|
+
});
|
|
39
|
+
const parts = formatter.formatToParts(date);
|
|
40
|
+
const get = (type) => parts.find(p => p.type === type)?.value ?? '';
|
|
41
|
+
const year = parseInt(get('year'), 10);
|
|
42
|
+
const month = parseInt(get('month'), 10);
|
|
43
|
+
const day = parseInt(get('day'), 10);
|
|
44
|
+
let hours = parseInt(get('hour'), 10);
|
|
45
|
+
// Intl with hour12:false can return 24 for midnight
|
|
46
|
+
if (hours === 24)
|
|
47
|
+
hours = 0;
|
|
48
|
+
const minutes = parseInt(get('minute'), 10);
|
|
49
|
+
const weekdayStr = get('weekday');
|
|
50
|
+
const dayMap = {
|
|
51
|
+
Sun: 0, Mon: 1, Tue: 2, Wed: 3, Thu: 4, Fri: 5, Sat: 6,
|
|
52
|
+
};
|
|
53
|
+
const dayOfWeek = dayMap[weekdayStr] ?? 0;
|
|
54
|
+
return { year, month, day, hours, minutes, dayOfWeek };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Convert hours and minutes in a given timezone on a given date to a UTC Date.
|
|
58
|
+
*
|
|
59
|
+
* Uses an iterative approach: make a UTC guess, measure how far off it is in
|
|
60
|
+
* the target timezone (including day differences), then correct. A second pass
|
|
61
|
+
* handles any remaining DST-boundary edge cases.
|
|
62
|
+
*/
|
|
63
|
+
function timezoneTimeToDate(year, month, day, hours, minutes, timezone) {
|
|
64
|
+
// Start with a naive UTC guess: treat the target local time as if it were UTC
|
|
65
|
+
let guess = new Date(Date.UTC(year, month - 1, day, hours, minutes, 0));
|
|
66
|
+
// Iteratively correct (two passes is enough to converge)
|
|
67
|
+
for (let i = 0; i < 2; i++) {
|
|
68
|
+
const inTz = getTimeInTimezone(guess, timezone);
|
|
69
|
+
// Build a Date representing what the guess looks like in the target timezone,
|
|
70
|
+
// interpreted as UTC, so we can diff full timestamps (not just hour+minute).
|
|
71
|
+
const tzAsUtc = Date.UTC(inTz.year, inTz.month - 1, inTz.day, inTz.hours, inTz.minutes, 0);
|
|
72
|
+
const targetAsUtc = Date.UTC(year, month - 1, day, hours, minutes, 0);
|
|
73
|
+
const diffMs = targetAsUtc - tzAsUtc;
|
|
74
|
+
if (diffMs === 0)
|
|
75
|
+
break;
|
|
76
|
+
guess = new Date(guess.getTime() + diffMs);
|
|
77
|
+
}
|
|
78
|
+
return guess;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if a day-of-week (0=Sun, 6=Sat) is a weekday.
|
|
82
|
+
*/
|
|
83
|
+
function isWeekday(dayOfWeek) {
|
|
84
|
+
return dayOfWeek >= 1 && dayOfWeek <= 5;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get full promo status for the given config and time.
|
|
88
|
+
*/
|
|
89
|
+
export function getPromoStatus(config, now) {
|
|
90
|
+
const currentTime = now ?? new Date();
|
|
91
|
+
const promo = getActivePromo(config, currentTime);
|
|
92
|
+
const noPromo = {
|
|
93
|
+
hasActivePromo: false,
|
|
94
|
+
promo: null,
|
|
95
|
+
bonusActive: false,
|
|
96
|
+
multiplier: 1,
|
|
97
|
+
minutesRemaining: 0,
|
|
98
|
+
minutesUntilBonus: 0,
|
|
99
|
+
windowEndLocal: null,
|
|
100
|
+
nextBonusStartLocal: null,
|
|
101
|
+
promoEndDate: null,
|
|
102
|
+
bonusProgress: 0,
|
|
103
|
+
};
|
|
104
|
+
if (!promo)
|
|
105
|
+
return noPromo;
|
|
106
|
+
const { peakHours } = promo;
|
|
107
|
+
const tz = peakHours.timezone;
|
|
108
|
+
// Get current time in the peak timezone
|
|
109
|
+
const tzTime = getTimeInTimezone(currentTime, tz);
|
|
110
|
+
const currentMinutesInDay = tzTime.hours * 60 + tzTime.minutes;
|
|
111
|
+
const peakStart = parseTime(peakHours.start);
|
|
112
|
+
const peakEnd = parseTime(peakHours.end);
|
|
113
|
+
const peakStartMinutes = peakStart.hours * 60 + peakStart.minutes;
|
|
114
|
+
const peakEndMinutes = peakEnd.hours * 60 + peakEnd.minutes;
|
|
115
|
+
const promoEndDate = new Date(promo.endDate);
|
|
116
|
+
// Check if we're in a peak window
|
|
117
|
+
const isDuringWeekday = isWeekday(tzTime.dayOfWeek);
|
|
118
|
+
const isInPeakTimeRange = currentMinutesInDay >= peakStartMinutes && currentMinutesInDay < peakEndMinutes;
|
|
119
|
+
// If weekdaysOnly, peak only applies on weekdays
|
|
120
|
+
const peakActive = peakHours.weekdaysOnly
|
|
121
|
+
? (isDuringWeekday && isInPeakTimeRange)
|
|
122
|
+
: isInPeakTimeRange;
|
|
123
|
+
const bonusActive = !peakActive;
|
|
124
|
+
const multiplier = bonusActive ? promo.multiplier : 1;
|
|
125
|
+
// Calculate timing info
|
|
126
|
+
let minutesRemaining = 0;
|
|
127
|
+
let minutesUntilBonus = 0;
|
|
128
|
+
let windowEndLocal = null;
|
|
129
|
+
let nextBonusStartLocal = null;
|
|
130
|
+
if (bonusActive) {
|
|
131
|
+
// Bonus is active. How long until peak starts?
|
|
132
|
+
// Find the next peak start time
|
|
133
|
+
if (peakHours.weekdaysOnly) {
|
|
134
|
+
// Find next weekday peak start
|
|
135
|
+
let daysUntilNextPeak = 0;
|
|
136
|
+
if (isDuringWeekday && currentMinutesInDay < peakStartMinutes) {
|
|
137
|
+
// Today is a weekday and peak hasn't started yet — peak starts today
|
|
138
|
+
daysUntilNextPeak = 0;
|
|
139
|
+
}
|
|
140
|
+
else if (isDuringWeekday && currentMinutesInDay >= peakEndMinutes) {
|
|
141
|
+
// After peak today. Next peak is tomorrow if weekday, else next Monday
|
|
142
|
+
daysUntilNextPeak = 1;
|
|
143
|
+
let nextDay = (tzTime.dayOfWeek + 1) % 7;
|
|
144
|
+
while (!isWeekday(nextDay)) {
|
|
145
|
+
daysUntilNextPeak++;
|
|
146
|
+
nextDay = (nextDay + 1) % 7;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
// Weekend — find next Monday
|
|
151
|
+
daysUntilNextPeak = 1;
|
|
152
|
+
let nextDay = (tzTime.dayOfWeek + 1) % 7;
|
|
153
|
+
while (!isWeekday(nextDay)) {
|
|
154
|
+
daysUntilNextPeak++;
|
|
155
|
+
nextDay = (nextDay + 1) % 7;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Window end = next peak start (the bonus window ends when peak begins)
|
|
159
|
+
const nextPeakDate = timezoneTimeToDate(tzTime.year, tzTime.month, tzTime.day + daysUntilNextPeak, peakStart.hours, peakStart.minutes, tz);
|
|
160
|
+
// Cap at promo end
|
|
161
|
+
const effectiveEnd = nextPeakDate.getTime() < promoEndDate.getTime()
|
|
162
|
+
? nextPeakDate : promoEndDate;
|
|
163
|
+
minutesRemaining = Math.max(0, Math.round((effectiveEnd.getTime() - currentTime.getTime()) / 60_000));
|
|
164
|
+
windowEndLocal = effectiveEnd;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Peak applies every day — next peak start is today or tomorrow
|
|
168
|
+
let daysUntilPeak = 0;
|
|
169
|
+
if (currentMinutesInDay >= peakEndMinutes) {
|
|
170
|
+
daysUntilPeak = 1;
|
|
171
|
+
}
|
|
172
|
+
const nextPeakDate = timezoneTimeToDate(tzTime.year, tzTime.month, tzTime.day + daysUntilPeak, peakStart.hours, peakStart.minutes, tz);
|
|
173
|
+
const effectiveEnd = nextPeakDate.getTime() < promoEndDate.getTime()
|
|
174
|
+
? nextPeakDate : promoEndDate;
|
|
175
|
+
minutesRemaining = Math.max(0, Math.round((effectiveEnd.getTime() - currentTime.getTime()) / 60_000));
|
|
176
|
+
windowEndLocal = effectiveEnd;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Peak is active (standard rate). How long until bonus starts?
|
|
181
|
+
// Bonus starts when peak ends
|
|
182
|
+
const peakEndDate = timezoneTimeToDate(tzTime.year, tzTime.month, tzTime.day, peakEnd.hours, peakEnd.minutes, tz);
|
|
183
|
+
minutesUntilBonus = Math.max(0, Math.round((peakEndDate.getTime() - currentTime.getTime()) / 60_000));
|
|
184
|
+
nextBonusStartLocal = peakEndDate;
|
|
185
|
+
}
|
|
186
|
+
// Calculate bonus progress (0-1) within the current bonus window
|
|
187
|
+
let bonusProgress = 0;
|
|
188
|
+
if (bonusActive && minutesRemaining > 0) {
|
|
189
|
+
// Total bonus window duration depends on context
|
|
190
|
+
// For simplicity: progress = time elapsed in window / total window duration
|
|
191
|
+
// We know the window ends at windowEndLocal. The window started at the last peak end.
|
|
192
|
+
// For a more accurate calculation, find window start
|
|
193
|
+
if (peakHours.weekdaysOnly && !isDuringWeekday) {
|
|
194
|
+
// Weekend: bonus started Friday at peak end (or Saturday midnight)
|
|
195
|
+
// Simplified: just use a ratio based on time until end
|
|
196
|
+
const totalWindowMinutes = calculateBonusWindowDuration(tzTime, peakStartMinutes, peakEndMinutes, peakHours.weekdaysOnly);
|
|
197
|
+
if (totalWindowMinutes > 0) {
|
|
198
|
+
bonusProgress = Math.min(1, 1 - (minutesRemaining / totalWindowMinutes));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// Weekday off-peak: window is from peak end to next peak start (next day)
|
|
203
|
+
const totalWindowMinutes = (24 * 60) - (peakEndMinutes - peakStartMinutes);
|
|
204
|
+
if (totalWindowMinutes > 0) {
|
|
205
|
+
bonusProgress = Math.min(1, 1 - (minutesRemaining / totalWindowMinutes));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
hasActivePromo: true,
|
|
211
|
+
promo,
|
|
212
|
+
bonusActive,
|
|
213
|
+
multiplier,
|
|
214
|
+
minutesRemaining,
|
|
215
|
+
minutesUntilBonus,
|
|
216
|
+
windowEndLocal,
|
|
217
|
+
nextBonusStartLocal,
|
|
218
|
+
promoEndDate,
|
|
219
|
+
bonusProgress,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Estimate total bonus window duration in minutes for progress calculation.
|
|
224
|
+
*/
|
|
225
|
+
function calculateBonusWindowDuration(tzTime, peakStartMinutes, peakEndMinutes, weekdaysOnly) {
|
|
226
|
+
if (weekdaysOnly && !isWeekday(tzTime.dayOfWeek)) {
|
|
227
|
+
// Weekend: bonus runs from Friday peak-end through Sunday midnight to Monday peak-start
|
|
228
|
+
// Friday: (24*60 - peakEndMinutes) + Saturday: 24*60 + Sunday: 24*60 + Monday: peakStartMinutes
|
|
229
|
+
// But depends on which day we're on — simplify
|
|
230
|
+
// Fri after peak + Sat + Sun + Mon before peak
|
|
231
|
+
return (24 * 60 - peakEndMinutes) + (2 * 24 * 60) + peakStartMinutes;
|
|
232
|
+
}
|
|
233
|
+
// Weekday: bonus = 24h - peak duration
|
|
234
|
+
return (24 * 60) - (peakEndMinutes - peakStartMinutes);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Format a duration in minutes as "Xh Ym".
|
|
238
|
+
*/
|
|
239
|
+
export function formatDuration(totalMinutes) {
|
|
240
|
+
if (totalMinutes <= 0)
|
|
241
|
+
return '0m';
|
|
242
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
243
|
+
const minutes = Math.round(totalMinutes % 60);
|
|
244
|
+
if (hours === 0)
|
|
245
|
+
return `${minutes}m`;
|
|
246
|
+
return `${hours}h ${minutes}m`;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Format a date as local time with timezone name.
|
|
250
|
+
*/
|
|
251
|
+
export function formatLocalTime(date) {
|
|
252
|
+
return date.toLocaleTimeString('en-US', {
|
|
253
|
+
hour: 'numeric',
|
|
254
|
+
minute: '2-digit',
|
|
255
|
+
timeZoneName: 'short',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=promo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promo.js","sourceRoot":"","sources":["../src/promo.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,oDAAoD;AAuCpD;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB,EAAE,GAAU;IAC5D,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;QAClD,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9C,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAU,EAAE,QAAgB;IAQrD,mEAAmE;IACnE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QACjD,QAAQ,EAAE,QAAQ;QAClB,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;IAE5E,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACtC,oDAAoD;IACpD,IAAI,KAAK,KAAK,EAAE;QAAE,KAAK,GAAG,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAE5C,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;IAClC,MAAM,MAAM,GAA2B;QACrC,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;KACvD,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAE1C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AACzD,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CACzB,IAAY,EACZ,KAAa,EACb,GAAW,EACX,KAAa,EACb,OAAe,EACf,QAAgB;IAEhB,8EAA8E;IAC9E,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IAExE,yDAAyD;IACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAEhD,8EAA8E;QAC9E,6EAA6E;QAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3F,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;QAEtE,MAAM,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;QACrC,IAAI,MAAM,KAAK,CAAC;YAAE,MAAM;QAExB,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,SAAS,CAAC,SAAiB;IAClC,OAAO,SAAS,IAAI,CAAC,IAAI,SAAS,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB,EAAE,GAAU;IAC5D,MAAM,WAAW,GAAG,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAElD,MAAM,OAAO,GAAgB;QAC3B,cAAc,EAAE,KAAK;QACrB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,KAAK;QAClB,UAAU,EAAE,CAAC;QACb,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,CAAC;QACpB,cAAc,EAAE,IAAI;QACpB,mBAAmB,EAAE,IAAI;QACzB,YAAY,EAAE,IAAI;QAClB,aAAa,EAAE,CAAC;KACjB,CAAC;IAEF,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC;IAE3B,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IAC5B,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,CAAC;IAE9B,wCAAwC;IACxC,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;IAE/D,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,GAAG,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC;IAClE,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAE5D,MAAM,YAAY,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAE7C,kCAAkC;IAClC,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,iBAAiB,GAAG,mBAAmB,IAAI,gBAAgB,IAAI,mBAAmB,GAAG,cAAc,CAAC;IAE1G,iDAAiD;IACjD,MAAM,UAAU,GAAG,SAAS,CAAC,YAAY;QACvC,CAAC,CAAC,CAAC,eAAe,IAAI,iBAAiB,CAAC;QACxC,CAAC,CAAC,iBAAiB,CAAC;IAEtB,MAAM,WAAW,GAAG,CAAC,UAAU,CAAC;IAChC,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtD,wBAAwB;IACxB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,iBAAiB,GAAG,CAAC,CAAC;IAC1B,IAAI,cAAc,GAAgB,IAAI,CAAC;IACvC,IAAI,mBAAmB,GAAgB,IAAI,CAAC;IAE5C,IAAI,WAAW,EAAE,CAAC;QAChB,+CAA+C;QAC/C,gCAAgC;QAChC,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC;YAC3B,+BAA+B;YAC/B,IAAI,iBAAiB,GAAG,CAAC,CAAC;YAC1B,IAAI,eAAe,IAAI,mBAAmB,GAAG,gBAAgB,EAAE,CAAC;gBAC9D,qEAAqE;gBACrE,iBAAiB,GAAG,CAAC,CAAC;YACxB,CAAC;iBAAM,IAAI,eAAe,IAAI,mBAAmB,IAAI,cAAc,EAAE,CAAC;gBACpE,uEAAuE;gBACvE,iBAAiB,GAAG,CAAC,CAAC;gBACtB,IAAI,OAAO,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBACzC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,iBAAiB,EAAE,CAAC;oBACpB,OAAO,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,6BAA6B;gBAC7B,iBAAiB,GAAG,CAAC,CAAC;gBACtB,IAAI,OAAO,GAAG,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBACzC,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,iBAAiB,EAAE,CAAC;oBACpB,OAAO,GAAG,CAAC,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;YAED,wEAAwE;YACxE,MAAM,YAAY,GAAG,kBAAkB,CACrC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,GAAG,iBAAiB,EACzD,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,CACvC,CAAC;YAEF,mBAAmB;YACnB,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE;gBAClE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;YAEhC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CACvC,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAC1D,CAAC,CAAC;YACH,cAAc,GAAG,YAAY,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,gEAAgE;YAChE,IAAI,aAAa,GAAG,CAAC,CAAC;YACtB,IAAI,mBAAmB,IAAI,cAAc,EAAE,CAAC;gBAC1C,aAAa,GAAG,CAAC,CAAC;YACpB,CAAC;YACD,MAAM,YAAY,GAAG,kBAAkB,CACrC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,GAAG,aAAa,EACrD,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,EAAE,EAAE,CACvC,CAAC;YACF,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,OAAO,EAAE;gBAClE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;YAChC,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CACvC,CAAC,YAAY,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAC1D,CAAC,CAAC;YACH,cAAc,GAAG,YAAY,CAAC;QAChC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,+DAA+D;QAC/D,8BAA8B;QAC9B,MAAM,WAAW,GAAG,kBAAkB,CACpC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,EACrC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CACnC,CAAC;QACF,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CACxC,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,WAAW,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CACzD,CAAC,CAAC;QACH,mBAAmB,GAAG,WAAW,CAAC;IACpC,CAAC;IAED,iEAAiE;IACjE,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,WAAW,IAAI,gBAAgB,GAAG,CAAC,EAAE,CAAC;QACxC,iDAAiD;QACjD,4EAA4E;QAC5E,sFAAsF;QACtF,qDAAqD;QACrD,IAAI,SAAS,CAAC,YAAY,IAAI,CAAC,eAAe,EAAE,CAAC;YAC/C,mEAAmE;YACnE,uDAAuD;YACvD,MAAM,kBAAkB,GAAG,4BAA4B,CACrD,MAAM,EAAE,gBAAgB,EAAE,cAAc,EAAE,SAAS,CAAC,YAAY,CACjE,CAAC;YACF,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;gBAC3B,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;aAAM,CAAC;YACN,0EAA0E;YAC1E,MAAM,kBAAkB,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,GAAG,gBAAgB,CAAC,CAAC;YAC3E,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;gBAC3B,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,gBAAgB,GAAG,kBAAkB,CAAC,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,cAAc,EAAE,IAAI;QACpB,KAAK;QACL,WAAW;QACX,UAAU;QACV,gBAAgB;QAChB,iBAAiB;QACjB,cAAc;QACd,mBAAmB;QACnB,YAAY;QACZ,aAAa;KACd,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,4BAA4B,CACnC,MAA6B,EAC7B,gBAAwB,EACxB,cAAsB,EACtB,YAAqB;IAErB,IAAI,YAAY,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACjD,wFAAwF;QACxF,gGAAgG;QAChG,+CAA+C;QAC/C,+CAA+C;QAC/C,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC;IACvE,CAAC;IACD,uCAAuC;IACvC,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,cAAc,GAAG,gBAAgB,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,YAAoB;IACjD,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,OAAO,GAAG,CAAC;IACtC,OAAO,GAAG,KAAK,KAAK,OAAO,GAAG,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU;IACxC,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE;QACtC,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,OAAO;KACtB,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@claudeclock/run",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "See when you're in the Claude 2x bonus window",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"claudeclock": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rm -rf dist && tsc",
|
|
17
|
+
"dev": "tsx src/index.tsx",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"claude",
|
|
23
|
+
"anthropic",
|
|
24
|
+
"tokens",
|
|
25
|
+
"bonus",
|
|
26
|
+
"cli"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"ink": "^5.1.0",
|
|
31
|
+
"meow": "^13.0.0",
|
|
32
|
+
"node-notifier": "^10.0.1",
|
|
33
|
+
"react": "^18.3.1"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"@types/node-notifier": "^8.0.5",
|
|
38
|
+
"@types/react": "^19.0.0",
|
|
39
|
+
"ink-testing-library": "^4.0.0",
|
|
40
|
+
"tsx": "^4.19.0",
|
|
41
|
+
"typescript": "^5.7.0",
|
|
42
|
+
"vitest": "^3.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|