whoburnedmore 0.9.10 → 0.9.12
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/index.js +156 -80
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -96,15 +96,15 @@ async function readJson(res) {
|
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
async function
|
|
99
|
+
async function send(method, path, body, token) {
|
|
100
100
|
const headers = { "Content-Type": "application/json" };
|
|
101
101
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
102
102
|
let res;
|
|
103
103
|
try {
|
|
104
104
|
res = await fetch(`${apiBase()}${path}`, {
|
|
105
|
-
method
|
|
105
|
+
method,
|
|
106
106
|
headers,
|
|
107
|
-
body: JSON.stringify(body),
|
|
107
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
108
108
|
// Bound the request so a slow/black-holing/hostile server can't hang the CLI
|
|
109
109
|
// — or the unattended 15-minute background sync — indefinitely.
|
|
110
110
|
signal: AbortSignal.timeout(3e4)
|
|
@@ -116,6 +116,9 @@ async function post(path, body, token) {
|
|
|
116
116
|
}
|
|
117
117
|
return { status: res.status, body: await readJson(res) };
|
|
118
118
|
}
|
|
119
|
+
async function post(path, body, token) {
|
|
120
|
+
return send("POST", path, body, token);
|
|
121
|
+
}
|
|
119
122
|
async function verifyUsage(token, payload) {
|
|
120
123
|
const { status, body } = await post("/v1/verify", payload, token);
|
|
121
124
|
if (status === 401) throw new UnauthorizedError();
|
|
@@ -127,18 +130,6 @@ async function verifyUsage(token, payload) {
|
|
|
127
130
|
}
|
|
128
131
|
return body;
|
|
129
132
|
}
|
|
130
|
-
var ANON_RETIRED_MESSAGE = "Anonymous mode has been retired. Run `npx whoburnedmore` and sign in, or `npx whoburnedmore link --token=\u2026` for a server/CI, to put your usage on the leaderboard.";
|
|
131
|
-
async function anonSubmit(anonKey, payload) {
|
|
132
|
-
const { status, body } = await post("/v1/anon/submit", { ...payload, anonKey });
|
|
133
|
-
if (status === 410) throw new Error(ANON_RETIRED_MESSAGE);
|
|
134
|
-
if (status !== 200) {
|
|
135
|
-
const err = body;
|
|
136
|
-
const details = err.details?.length ? `
|
|
137
|
-
- ${err.details.join("\n - ")}` : "";
|
|
138
|
-
throw new Error(`${err.error ?? `submit failed (HTTP ${status})`}${details}`);
|
|
139
|
-
}
|
|
140
|
-
return body;
|
|
141
|
-
}
|
|
142
133
|
var UnauthorizedError = class extends Error {
|
|
143
134
|
constructor(message = "your sign-in has expired") {
|
|
144
135
|
super(message);
|
|
@@ -207,31 +198,21 @@ async function submit(token, payload) {
|
|
|
207
198
|
}
|
|
208
199
|
return body;
|
|
209
200
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
async function anonVisibility(anonKey, listed) {
|
|
214
|
-
const { status, body } = await post(
|
|
215
|
-
"/v1/anon/visibility",
|
|
216
|
-
{ anonKey, listed }
|
|
217
|
-
);
|
|
218
|
-
if (status === 410) throw new Error(ANON_RETIRED_MESSAGE);
|
|
201
|
+
async function setVisibility(token, listed) {
|
|
202
|
+
const { status, body } = await post("/v1/cli/visibility", { listed }, token);
|
|
203
|
+
if (status === 401) throw new UnauthorizedError();
|
|
219
204
|
if (status !== 200) {
|
|
220
205
|
throw new Error(body.error ?? `failed (HTTP ${status})`);
|
|
221
206
|
}
|
|
207
|
+
return { listed: body.listed === true, eligible: body.eligible === true };
|
|
222
208
|
}
|
|
223
|
-
async function
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
body
|
|
228
|
-
signal: AbortSignal.timeout(3e4)
|
|
229
|
-
});
|
|
230
|
-
if (res.status === 410) throw new Error(ANON_RETIRED_MESSAGE);
|
|
231
|
-
if (res.status !== 200) {
|
|
232
|
-
const b = await res.json().catch(() => ({}));
|
|
233
|
-
throw new Error(b.error ?? `failed (HTTP ${res.status})`);
|
|
209
|
+
async function removeUsage(token) {
|
|
210
|
+
const { status, body } = await send("DELETE", "/v1/cli/usage", void 0, token);
|
|
211
|
+
if (status === 401) throw new UnauthorizedError();
|
|
212
|
+
if (status !== 200) {
|
|
213
|
+
throw new Error(body.error ?? `failed (HTTP ${status})`);
|
|
234
214
|
}
|
|
215
|
+
return { deletedDays: body.deletedDays ?? 0 };
|
|
235
216
|
}
|
|
236
217
|
async function redeemServerInstall(token, anonKey) {
|
|
237
218
|
const { status, body } = await post("/v1/server-install/redeem", { token, anonKey });
|
|
@@ -8004,17 +7985,16 @@ function renderDashboardHtml(entries, generatedAt = /* @__PURE__ */ new Date(),
|
|
|
8004
7985
|
(p) => `<div class="tbcell"><div class="tblabel"><span class="dot" style="background:${p.color}"></span>${p.label}</div><div class="tbval">${esc(formatTokens(p.value))}</div><div class="tbpct">${Math.round(p.value / tbSum * 100)}%</div></div>`
|
|
8005
7986
|
).join("");
|
|
8006
7987
|
const connectCta = connect ? `
|
|
8007
|
-
<
|
|
8008
|
-
<input type="hidden" name="payload" value="${Buffer.from(JSON.stringify(connect.payload)).toString("base64")}">
|
|
7988
|
+
<div class="connect">
|
|
8009
7989
|
<div class="connect-row">
|
|
8010
7990
|
<div>
|
|
8011
|
-
<div class="connect-title">
|
|
8012
|
-
<div class="connect-sub">
|
|
7991
|
+
<div class="connect-title">Claim your spot on the public leaderboard</div>
|
|
7992
|
+
<div class="connect-sub">Run the command below in your terminal \u2014 it signs you in and puts these numbers on <a href="${esc(connect.webBaseUrl)}">whoburnedmore.com</a> under your handle. Nothing has left your machine yet.</div>
|
|
8013
7993
|
</div>
|
|
8014
|
-
<
|
|
7994
|
+
<code class="connect-cmd">npx whoburnedmore</code>
|
|
8015
7995
|
</div>
|
|
8016
|
-
<div class="connect-note">
|
|
8017
|
-
</
|
|
7996
|
+
<div class="connect-note">Only daily totals are submitted \u2014 never your prompts or code. Background sync keeps your page fresh afterwards.</div>
|
|
7997
|
+
</div>` : "";
|
|
8018
7998
|
return `<!doctype html>
|
|
8019
7999
|
<html lang="en">
|
|
8020
8000
|
<head>
|
|
@@ -8074,8 +8054,8 @@ function renderDashboardHtml(entries, generatedAt = /* @__PURE__ */ new Date(),
|
|
|
8074
8054
|
@media (min-width: 640px) { .connect-row { flex-direction: row; align-items: center; justify-content: space-between; } }
|
|
8075
8055
|
.connect-title { font-size: 16px; font-weight: 700; }
|
|
8076
8056
|
.connect-sub { color: #d6d3d1; font-size: 13px; margin-top: 4px; max-width: 60ch; }
|
|
8077
|
-
.connect
|
|
8078
|
-
.connect
|
|
8057
|
+
.connect-cmd { flex-shrink: 0; display: inline-block; border-radius: 10px; background: rgba(0,0,0,.35); border: 1px solid rgba(234,88,12,.5); color: #fed7aa; font-size: 14px; font-weight: 600; padding: 11px 18px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; user-select: all; }
|
|
8058
|
+
.connect-sub a { color: #fed7aa; }
|
|
8079
8059
|
.connect-note { color: #a8a29e; font-size: 12px; margin-top: 14px; padding-top: 12px; border-top: 1px solid rgba(234,88,12,.25); }
|
|
8080
8060
|
.connect-note code { color: #fed7aa; background: rgba(0,0,0,.25); padding: 1px 6px; border-radius: 5px; }
|
|
8081
8061
|
</style>
|
|
@@ -8145,20 +8125,24 @@ ${connectCta}
|
|
|
8145
8125
|
|
|
8146
8126
|
// src/publish.ts
|
|
8147
8127
|
async function publishLocal(payload, deps) {
|
|
8148
|
-
if (!await deps.confirm(" Publish this to the public leaderboard?")) {
|
|
8128
|
+
if (!await deps.confirm(" Publish this to the public leaderboard? (You'll sign in first.)")) {
|
|
8149
8129
|
deps.log(" Kept local. Nothing left your machine.");
|
|
8150
8130
|
return false;
|
|
8151
8131
|
}
|
|
8152
|
-
const
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
|
|
8132
|
+
const auth = await deps.signIn();
|
|
8133
|
+
if (!auth) {
|
|
8134
|
+
deps.log(" Sign-in didn't complete \u2014 kept local. Nothing left your machine.");
|
|
8135
|
+
return false;
|
|
8136
|
+
}
|
|
8137
|
+
const res = await deps.submit(auth.token, payload);
|
|
8138
|
+
deps.log(` Published \u2014 you're on the board: ${sanitizeServerText(res.profileUrl)}`);
|
|
8139
|
+
if (isTrustedWebUrl(res.profileUrl)) {
|
|
8140
|
+
deps.openBrowser(res.profileUrl);
|
|
8157
8141
|
} else {
|
|
8158
8142
|
deps.log(" (The server returned an unexpected address, so it was not auto-opened.)");
|
|
8159
8143
|
}
|
|
8160
8144
|
deps.log(
|
|
8161
|
-
"
|
|
8145
|
+
" Manage anytime: `npx whoburnedmore private` hides you, `remove` deletes your data."
|
|
8162
8146
|
);
|
|
8163
8147
|
return true;
|
|
8164
8148
|
}
|
|
@@ -8241,10 +8225,7 @@ function showLocalDashboard(payload) {
|
|
|
8241
8225
|
const file = join10(dir, "dashboard.html");
|
|
8242
8226
|
writeFileSync3(
|
|
8243
8227
|
file,
|
|
8244
|
-
renderDashboardHtml(payload.entries, /* @__PURE__ */ new Date(), {
|
|
8245
|
-
payload,
|
|
8246
|
-
webBaseUrl: webBase()
|
|
8247
|
-
})
|
|
8228
|
+
renderDashboardHtml(payload.entries, /* @__PURE__ */ new Date(), { webBaseUrl: webBase() })
|
|
8248
8229
|
);
|
|
8249
8230
|
console.log();
|
|
8250
8231
|
console.log(` Local dashboard: ${pc2.cyan(`file://${file}`)}`);
|
|
@@ -8300,8 +8281,23 @@ async function run(flags) {
|
|
|
8300
8281
|
if (!flags.quiet && process.stdin.isTTY) {
|
|
8301
8282
|
await publishLocal(payload, {
|
|
8302
8283
|
confirm,
|
|
8303
|
-
|
|
8304
|
-
|
|
8284
|
+
signIn: ensureSignedIn,
|
|
8285
|
+
submit: async (token, p) => {
|
|
8286
|
+
const result = await submit(token, p);
|
|
8287
|
+
try {
|
|
8288
|
+
recordSync();
|
|
8289
|
+
} catch {
|
|
8290
|
+
}
|
|
8291
|
+
try {
|
|
8292
|
+
const cfgNow = loadConfig();
|
|
8293
|
+
if (!cfgNow?.deviceBoundAt) {
|
|
8294
|
+
const key = ensureAnonKey();
|
|
8295
|
+
if (await bindDeviceKey(token, key)) recordDeviceBound();
|
|
8296
|
+
}
|
|
8297
|
+
} catch {
|
|
8298
|
+
}
|
|
8299
|
+
return result;
|
|
8300
|
+
},
|
|
8305
8301
|
openBrowser,
|
|
8306
8302
|
log: (line) => console.log(pc2.dim(line))
|
|
8307
8303
|
});
|
|
@@ -8667,6 +8663,102 @@ async function runVerify() {
|
|
|
8667
8663
|
);
|
|
8668
8664
|
}
|
|
8669
8665
|
}
|
|
8666
|
+
async function requireSignedIn(commandName) {
|
|
8667
|
+
const cfg = loadConfig();
|
|
8668
|
+
if (cfg?.cliToken) return { token: cfg.cliToken, handle: cfg.handle ?? "" };
|
|
8669
|
+
const healed = await refreshCliTokenFromConfig();
|
|
8670
|
+
if (healed) {
|
|
8671
|
+
saveAuth(void 0, { cliToken: healed.token, handle: healed.handle });
|
|
8672
|
+
return healed;
|
|
8673
|
+
}
|
|
8674
|
+
if (process.stdout.isTTY) return ensureSignedIn();
|
|
8675
|
+
console.log(
|
|
8676
|
+
pc2.yellow(
|
|
8677
|
+
` Sign in first \u2014 run \`npx whoburnedmore ${commandName}\` in an interactive terminal.`
|
|
8678
|
+
)
|
|
8679
|
+
);
|
|
8680
|
+
return null;
|
|
8681
|
+
}
|
|
8682
|
+
async function runVisibility(listed) {
|
|
8683
|
+
const auth = await requireSignedIn(listed ? "public" : "private");
|
|
8684
|
+
if (!auth) return;
|
|
8685
|
+
let res;
|
|
8686
|
+
try {
|
|
8687
|
+
res = await setVisibility(auth.token, listed);
|
|
8688
|
+
} catch (err) {
|
|
8689
|
+
if (err instanceof UnauthorizedError) {
|
|
8690
|
+
clearAuth();
|
|
8691
|
+
console.log(
|
|
8692
|
+
pc2.yellow(
|
|
8693
|
+
` Your sign-in expired \u2014 run \`npx whoburnedmore ${listed ? "public" : "private"}\` again to retry.`
|
|
8694
|
+
)
|
|
8695
|
+
);
|
|
8696
|
+
return;
|
|
8697
|
+
}
|
|
8698
|
+
throw err;
|
|
8699
|
+
}
|
|
8700
|
+
if (!listed) {
|
|
8701
|
+
console.log(" \u2713 You're off the public leaderboard. `npx whoburnedmore public` puts you back.");
|
|
8702
|
+
return;
|
|
8703
|
+
}
|
|
8704
|
+
if (res.listed) {
|
|
8705
|
+
console.log(" \u2713 You're back on the public leaderboard.");
|
|
8706
|
+
} else if (!res.eligible) {
|
|
8707
|
+
console.log(
|
|
8708
|
+
pc2.yellow(" You're signed in, but not on the leaderboard yet \u2014 it lists accounts with a social handle.")
|
|
8709
|
+
);
|
|
8710
|
+
console.log(
|
|
8711
|
+
pc2.dim(" Add your X, GitHub or Instagram on your profile at whoburnedmore.com, then you're on.")
|
|
8712
|
+
);
|
|
8713
|
+
} else {
|
|
8714
|
+
console.log(" \u2713 Leaderboard listing is on.");
|
|
8715
|
+
}
|
|
8716
|
+
}
|
|
8717
|
+
async function runRemove() {
|
|
8718
|
+
if (!process.stdin.isTTY) {
|
|
8719
|
+
console.log(
|
|
8720
|
+
pc2.yellow(" `remove` deletes data, so it needs an interactive terminal to confirm.")
|
|
8721
|
+
);
|
|
8722
|
+
console.log(pc2.dim(" Run `npx whoburnedmore remove` in a real terminal."));
|
|
8723
|
+
process.exitCode = 1;
|
|
8724
|
+
return;
|
|
8725
|
+
}
|
|
8726
|
+
const auth = await requireSignedIn("remove");
|
|
8727
|
+
if (!auth) return;
|
|
8728
|
+
const who = auth.handle ? `@${sanitizeServerText(auth.handle)}` : "your account";
|
|
8729
|
+
console.log(
|
|
8730
|
+
` This deletes ALL usage data stored for ${who} on whoburnedmore.com and turns off this machine's background sync.`
|
|
8731
|
+
);
|
|
8732
|
+
console.log(
|
|
8733
|
+
pc2.dim(" Your local logs are untouched, and the account itself survives (delete it from your dashboard settings on the web).")
|
|
8734
|
+
);
|
|
8735
|
+
if (!await confirm(" Delete your usage data?")) {
|
|
8736
|
+
console.log(pc2.dim(" Cancelled \u2014 nothing was deleted."));
|
|
8737
|
+
return;
|
|
8738
|
+
}
|
|
8739
|
+
let res;
|
|
8740
|
+
try {
|
|
8741
|
+
res = await removeUsage(auth.token);
|
|
8742
|
+
} catch (err) {
|
|
8743
|
+
if (err instanceof UnauthorizedError) {
|
|
8744
|
+
clearAuth();
|
|
8745
|
+
console.log(
|
|
8746
|
+
pc2.yellow(" Your sign-in expired \u2014 run `npx whoburnedmore remove` again to retry.")
|
|
8747
|
+
);
|
|
8748
|
+
return;
|
|
8749
|
+
}
|
|
8750
|
+
throw err;
|
|
8751
|
+
}
|
|
8752
|
+
try {
|
|
8753
|
+
uninstallAutoSync();
|
|
8754
|
+
} catch {
|
|
8755
|
+
}
|
|
8756
|
+
clearAuth();
|
|
8757
|
+
console.log(
|
|
8758
|
+
pc2.green(` \u2713 Deleted your usage data (${res.deletedDays} day${res.deletedDays === 1 ? "" : "s"}) and turned off background sync.`)
|
|
8759
|
+
);
|
|
8760
|
+
console.log(pc2.dim(" Run `npx whoburnedmore` anytime to start fresh."));
|
|
8761
|
+
}
|
|
8670
8762
|
async function main() {
|
|
8671
8763
|
const major = Number(process.versions.node.split(".")[0]);
|
|
8672
8764
|
if (major < 20) {
|
|
@@ -8711,28 +8803,12 @@ async function main() {
|
|
|
8711
8803
|
break;
|
|
8712
8804
|
}
|
|
8713
8805
|
case "private":
|
|
8714
|
-
case "public":
|
|
8715
|
-
|
|
8716
|
-
if (!cfg?.anonKey) {
|
|
8717
|
-
console.log(" No anonymous dashboard on this machine \u2014 run `npx whoburnedmore` first.");
|
|
8718
|
-
break;
|
|
8719
|
-
}
|
|
8720
|
-
await anonVisibility(cfg.anonKey, command === "public");
|
|
8721
|
-
console.log(
|
|
8722
|
-
command === "public" ? " Your dashboard is public on the leaderboard again." : " Your dashboard is now private \u2014 off the public leaderboard."
|
|
8723
|
-
);
|
|
8806
|
+
case "public":
|
|
8807
|
+
await runVisibility(command === "public");
|
|
8724
8808
|
break;
|
|
8725
|
-
|
|
8726
|
-
|
|
8727
|
-
const cfg = loadConfig();
|
|
8728
|
-
if (!cfg?.anonKey) {
|
|
8729
|
-
console.log(" No anonymous dashboard on this machine \u2014 nothing to remove.");
|
|
8730
|
-
break;
|
|
8731
|
-
}
|
|
8732
|
-
await anonRemove(cfg.anonKey);
|
|
8733
|
-
console.log(" Removed your anonymous dashboard and all its data.");
|
|
8809
|
+
case "remove":
|
|
8810
|
+
await runRemove();
|
|
8734
8811
|
break;
|
|
8735
|
-
}
|
|
8736
8812
|
case "install-sync":
|
|
8737
8813
|
console.log(` ${installAutoSync()}`);
|
|
8738
8814
|
break;
|
|
@@ -8764,9 +8840,9 @@ function printHelp() {
|
|
|
8764
8840
|
npx whoburnedmore --no-submit collect locally, send nothing (no dashboard)
|
|
8765
8841
|
npx whoburnedmore link --token=TOKEN link this server/VM to your signed-in account
|
|
8766
8842
|
npx whoburnedmore daemon keep syncing in the foreground (VMs/containers with no cron)
|
|
8767
|
-
npx whoburnedmore private
|
|
8768
|
-
npx whoburnedmore public put
|
|
8769
|
-
npx whoburnedmore remove delete your
|
|
8843
|
+
npx whoburnedmore private take yourself off the public leaderboard
|
|
8844
|
+
npx whoburnedmore public put yourself back on it
|
|
8845
|
+
npx whoburnedmore remove delete your usage data and stop background sync
|
|
8770
8846
|
npx whoburnedmore verify delisted? re-verify your usage to get back on (sends a detailed breakdown)
|
|
8771
8847
|
npx whoburnedmore status check background-sync health (last sync, staleness)
|
|
8772
8848
|
npx whoburnedmore uninstall-sync turn off the background sync
|
package/package.json
CHANGED