whoburnedmore 0.9.8 → 0.9.9

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.
Files changed (2) hide show
  1. package/dist/index.js +84 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -167,11 +167,40 @@ async function devicePoll(deviceCode) {
167
167
  }
168
168
  return body;
169
169
  }
170
+ async function refreshCliToken(anonKey) {
171
+ try {
172
+ const { status, body } = await post("/v1/auth/cli/refresh", { anonKey });
173
+ if (status === 200 && typeof body.token === "string" && body.token) {
174
+ return { token: body.token, handle: body.handle ?? "" };
175
+ }
176
+ } catch {
177
+ }
178
+ return null;
179
+ }
180
+ async function bindDeviceKey(token, anonKey) {
181
+ try {
182
+ const { status } = await post(
183
+ "/v1/me/devices/bind",
184
+ { anonKey },
185
+ token
186
+ );
187
+ return status === 200 || status === 409;
188
+ } catch {
189
+ return false;
190
+ }
191
+ }
170
192
  async function submit(token, payload) {
171
193
  const { status, body } = await post("/v1/submit", payload, token);
172
194
  if (status === 401) throw new UnauthorizedError();
173
195
  if (status !== 200) {
174
196
  const err = body;
197
+ if (status === 403 && err.error === "account blocked") {
198
+ const reason = err.reason ? `
199
+ Reason: ${err.reason}` : "";
200
+ const appeal = err.appealUrl ? `
201
+ If you think this is a mistake, appeal at: ${err.appealUrl}` : "";
202
+ throw new Error(`Your account is blocked.${reason}${appeal}`);
203
+ }
175
204
  const details = err.details?.length ? `
176
205
  - ${err.details.join("\n - ")}` : "";
177
206
  throw new Error(`${err.error ?? `submit failed (HTTP ${status})`}${details}`);
@@ -260,6 +289,9 @@ function loadConfig(dir = defaultConfigDir()) {
260
289
  if (typeof parsed.launchNotificationDeliveredAt === "number" && Number.isFinite(parsed.launchNotificationDeliveredAt)) {
261
290
  config.launchNotificationDeliveredAt = parsed.launchNotificationDeliveredAt;
262
291
  }
292
+ if (typeof parsed.deviceBoundAt === "number" && Number.isFinite(parsed.deviceBoundAt)) {
293
+ config.deviceBoundAt = parsed.deviceBoundAt;
294
+ }
263
295
  return Object.keys(config).length > 0 ? config : null;
264
296
  } catch {
265
297
  return null;
@@ -295,6 +327,10 @@ function recordSync(dir = defaultConfigDir(), when = Date.now()) {
295
327
  const config = loadConfig(dir) ?? {};
296
328
  saveConfig(dir, { ...config, lastSyncAt: when });
297
329
  }
330
+ function recordDeviceBound(dir = defaultConfigDir(), when = Date.now()) {
331
+ const config = loadConfig(dir) ?? {};
332
+ saveConfig(dir, { ...config, deviceBoundAt: when });
333
+ }
298
334
  function saveAuth(dir = defaultConfigDir(), auth = { cliToken: "" }) {
299
335
  const config = loadConfig(dir) ?? {};
300
336
  saveConfig(dir, { ...config, cliToken: auth.cliToken, handle: auth.handle });
@@ -7036,7 +7072,6 @@ var OrgSettingsInput = external_exports.object({
7036
7072
  name: external_exports.string().min(1).max(120).optional(),
7037
7073
  description: external_exports.string().max(2e3).nullable().optional(),
7038
7074
  accentColor: HexColor.optional(),
7039
- logoUrl: external_exports.string().url().max(500).nullable().optional(),
7040
7075
  boardVisibility: OrgBoardVisibility.optional(),
7041
7076
  window: OrgWindow.optional(),
7042
7077
  joinPolicy: OrgJoinPolicy.partial().optional()
@@ -8281,6 +8316,12 @@ async function run(flags) {
8281
8316
  const canSignIn = !flags.quiet && Boolean(process.stdout.isTTY);
8282
8317
  if (cfg?.cliToken) {
8283
8318
  await submitSignedIn(cfg.cliToken, payload, flags, canSignIn);
8319
+ return;
8320
+ }
8321
+ const healed = cfg?.anonKey ? await refreshCliToken(cfg.anonKey) : null;
8322
+ if (healed) {
8323
+ saveAuth(void 0, { cliToken: healed.token, handle: healed.handle });
8324
+ await submitSignedIn(healed.token, payload, flags, canSignIn);
8284
8325
  } else if (canSignIn) {
8285
8326
  const auth = await ensureSignedIn();
8286
8327
  if (!auth) return;
@@ -8299,6 +8340,11 @@ async function run(flags) {
8299
8340
  function sleep(ms) {
8300
8341
  return new Promise((resolve) => setTimeout(resolve, ms));
8301
8342
  }
8343
+ async function refreshCliTokenFromConfig() {
8344
+ const cfg = loadConfig();
8345
+ if (!cfg?.anonKey) return null;
8346
+ return refreshCliToken(cfg.anonKey);
8347
+ }
8302
8348
  async function ensureSignedIn() {
8303
8349
  const cfg = loadConfig();
8304
8350
  if (cfg?.cliToken) return { token: cfg.cliToken, handle: cfg.handle ?? "" };
@@ -8338,16 +8384,25 @@ async function ensureSignedIn() {
8338
8384
  return null;
8339
8385
  }
8340
8386
  async function submitSignedIn(token, payload, flags, interactive) {
8387
+ let activeToken = token;
8341
8388
  let result;
8342
8389
  try {
8343
8390
  result = await submit(token, payload);
8344
8391
  } catch (err) {
8345
8392
  if (err instanceof UnauthorizedError) {
8346
- clearAuth();
8347
- if (!interactive) return;
8348
- const auth = await ensureSignedIn();
8349
- if (!auth) return;
8350
- result = await submit(auth.token, payload);
8393
+ const healed = await refreshCliTokenFromConfig();
8394
+ if (healed) {
8395
+ saveAuth(void 0, { cliToken: healed.token, handle: healed.handle });
8396
+ activeToken = healed.token;
8397
+ result = await submit(healed.token, payload);
8398
+ } else {
8399
+ clearAuth();
8400
+ if (!interactive) return;
8401
+ const auth = await ensureSignedIn();
8402
+ if (!auth) return;
8403
+ activeToken = auth.token;
8404
+ result = await submit(auth.token, payload);
8405
+ }
8351
8406
  } else {
8352
8407
  throw err;
8353
8408
  }
@@ -8356,6 +8411,14 @@ async function submitSignedIn(token, payload, flags, interactive) {
8356
8411
  recordSync();
8357
8412
  } catch {
8358
8413
  }
8414
+ try {
8415
+ const cfgNow = loadConfig();
8416
+ if (!cfgNow?.deviceBoundAt) {
8417
+ const key = ensureAnonKey();
8418
+ if (await bindDeviceKey(activeToken, key)) recordDeviceBound();
8419
+ }
8420
+ } catch {
8421
+ }
8359
8422
  const baseUrl = result.orgBoardUrl ?? result.boardUrl ?? result.profileUrl;
8360
8423
  if (!flags.quiet) {
8361
8424
  console.log(
@@ -8384,6 +8447,20 @@ async function submitSignedIn(token, payload, flags, interactive) {
8384
8447
  )
8385
8448
  );
8386
8449
  }
8450
+ if (result.quarantinedDates && result.quarantinedDates.length > 0) {
8451
+ const days = result.quarantinedDates.map((d) => sanitizeServerText(d)).join(", ");
8452
+ console.log();
8453
+ console.log(
8454
+ pc2.yellow(
8455
+ ` \u26A0 These day(s) were held off the leaderboard as an anomaly: ${days}`
8456
+ )
8457
+ );
8458
+ console.log(
8459
+ pc2.dim(
8460
+ " Your data is kept, not deleted. Run `npx whoburnedmore verify` to get back on the board, or appeal at whoburnedmore.com/appeal to have these days reviewed and restored."
8461
+ )
8462
+ );
8463
+ }
8387
8464
  if (isTrustedWebUrl(baseUrl)) {
8388
8465
  console.log(pc2.dim(" Opening your dashboard in your browser\u2026"));
8389
8466
  openBrowser(baseUrl);
@@ -8681,7 +8758,7 @@ function printHelp() {
8681
8758
  ${pc2.bold("usage")}
8682
8759
  npx whoburnedmore sign in, burn + land on the public leaderboard, open your dashboard
8683
8760
  npx whoburnedmore --board=CODE compare with friends \u2014 sign in and join their board
8684
- npx whoburnedmore --org=SLUG submit to your organization's board (companies/hackathons)
8761
+ npx whoburnedmore --org=SLUG --pass=CODE join your organization's board (companies/hackathons)
8685
8762
  npx whoburnedmore --local build the dashboard on your machine and open it (offline)
8686
8763
  npx whoburnedmore --dry-run print exactly what would be sent, send nothing
8687
8764
  npx whoburnedmore --no-submit collect locally, send nothing (no dashboard)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whoburnedmore",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "description": "Find out who burned more — submit your AI coding-agent token usage to the public leaderboard at whoburnedmore.com",
5
5
  "type": "module",
6
6
  "bin": {