@brozarti/spincome 0.1.3 → 0.1.5

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.
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.animateWhileLoading = animateWhileLoading;
4
+ const HIDE_CURSOR = "\x1b[?25l";
5
+ const SHOW_CURSOR = "\x1b[?25h";
6
+ const CLEAR_LINE = "\x1b[2K\r";
7
+ const UP_ONE = "\x1b[1A";
8
+ const GREEN = "\x1b[32m";
9
+ const BRIGHT_GREEN = "\x1b[92m";
10
+ const DIM = "\x1b[2m";
11
+ const BOLD = "\x1b[1m";
12
+ const R = "\x1b[0m";
13
+ const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
14
+ const COIN = ["◐", "◓", "◑", "◒"];
15
+ const LOGO = `${BOLD}${GREEN}$${R}${BOLD} spincome${R}`;
16
+ async function animateWhileLoading(work) {
17
+ process.stderr.write(HIDE_CURSOR);
18
+ let frame = 0;
19
+ let dots = 0;
20
+ let done = false;
21
+ let result;
22
+ const tick = setInterval(() => {
23
+ if (done)
24
+ return;
25
+ const spinner = BRIGHT_GREEN + SPINNER[frame % SPINNER.length] + R;
26
+ const coin = GREEN + COIN[frame % COIN.length] + R;
27
+ dots = (dots + 1) % 4;
28
+ const dotStr = DIM + ".".repeat(dots).padEnd(3) + R;
29
+ process.stderr.write(CLEAR_LINE +
30
+ ` ${spinner} ${LOGO} ${coin} ${DIM}earning${R}${dotStr}`);
31
+ frame++;
32
+ }, 80);
33
+ try {
34
+ result = await work;
35
+ }
36
+ finally {
37
+ done = true;
38
+ clearInterval(tick);
39
+ process.stderr.write(CLEAR_LINE);
40
+ process.stderr.write(UP_ONE + CLEAR_LINE);
41
+ process.stderr.write(SHOW_CURSOR);
42
+ }
43
+ return result;
44
+ }
package/dist/display.js CHANGED
@@ -22,8 +22,9 @@ function ruler() {
22
22
  return `${DIM}${"─".repeat(W)}${R}`;
23
23
  }
24
24
  function renderAd(ad, earnedCents, sessionCents, context) {
25
- const earnedDollars = (earnedCents / 100).toFixed(4);
26
- const sessionDollars = (sessionCents / 100).toFixed(4);
25
+ // earnedCents and sessionCents are stored in milli-cents (1 unit = $0.00001)
26
+ const earnedDollars = (earnedCents / 100000).toFixed(4);
27
+ const sessionDollars = (sessionCents / 100000).toFixed(4);
27
28
  const contextTag = context?.fileExt
28
29
  ? ` · ${context.fileExt.toUpperCase()} dev`
29
30
  : context?.toolName
package/dist/hook.js CHANGED
@@ -8,6 +8,7 @@ const ad_js_1 = require("./ad.js");
8
8
  const display_js_1 = require("./display.js");
9
9
  const config_js_1 = require("./config.js");
10
10
  const session_js_1 = require("./session.js");
11
+ const animate_js_1 = require("./animate.js");
11
12
  const path_1 = __importDefault(require("path"));
12
13
  function extractContext(payload) {
13
14
  const toolName = payload.tool_name ?? undefined;
@@ -34,11 +35,11 @@ async function main() {
34
35
  // empty or malformed stdin
35
36
  }
36
37
  const context = extractContext(payload);
38
+ // Fetch ad and record impression concurrently, animate while waiting
37
39
  const ad = await (0, ad_js_1.fetchAd)(context);
38
40
  if (!ad)
39
41
  process.exit(0);
40
- // Record impression first to get earnedCents
41
- const earnedCents = await (0, ad_js_1.recordImpression)(ad.id, config.developerKey, ad.actualCpmCents, context);
42
+ const earnedCents = await (0, animate_js_1.animateWhileLoading)((0, ad_js_1.recordImpression)(ad.id, config.developerKey, ad.actualCpmCents, context));
42
43
  const session = (0, session_js_1.addToSession)(earnedCents);
43
44
  process.stdout.write((0, display_js_1.renderAd)(ad, earnedCents, session.totalCents, context));
44
45
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brozarti/spincome",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Earn passive income from your Claude Code sessions",
5
5
  "bin": {
6
6
  "spincome": "./dist/cli.js"
package/src/animate.ts ADDED
@@ -0,0 +1,49 @@
1
+ const HIDE_CURSOR = "\x1b[?25l";
2
+ const SHOW_CURSOR = "\x1b[?25h";
3
+ const CLEAR_LINE = "\x1b[2K\r";
4
+ const UP_ONE = "\x1b[1A";
5
+
6
+ const GREEN = "\x1b[32m";
7
+ const BRIGHT_GREEN = "\x1b[92m";
8
+ const DIM = "\x1b[2m";
9
+ const BOLD = "\x1b[1m";
10
+ const R = "\x1b[0m";
11
+
12
+ const SPINNER = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
13
+ const COIN = ["◐","◓","◑","◒"];
14
+
15
+ const LOGO = `${BOLD}${GREEN}$${R}${BOLD} spincome${R}`;
16
+
17
+ export async function animateWhileLoading<T>(work: Promise<T>): Promise<T> {
18
+ process.stderr.write(HIDE_CURSOR);
19
+
20
+ let frame = 0;
21
+ let dots = 0;
22
+ let done = false;
23
+ let result: T;
24
+
25
+ const tick = setInterval(() => {
26
+ if (done) return;
27
+ const spinner = BRIGHT_GREEN + SPINNER[frame % SPINNER.length] + R;
28
+ const coin = GREEN + COIN[frame % COIN.length] + R;
29
+ dots = (dots + 1) % 4;
30
+ const dotStr = DIM + ".".repeat(dots).padEnd(3) + R;
31
+ process.stderr.write(
32
+ CLEAR_LINE +
33
+ ` ${spinner} ${LOGO} ${coin} ${DIM}earning${R}${dotStr}`
34
+ );
35
+ frame++;
36
+ }, 80);
37
+
38
+ try {
39
+ result = await work;
40
+ } finally {
41
+ done = true;
42
+ clearInterval(tick);
43
+ process.stderr.write(CLEAR_LINE);
44
+ process.stderr.write(UP_ONE + CLEAR_LINE);
45
+ process.stderr.write(SHOW_CURSOR);
46
+ }
47
+
48
+ return result!;
49
+ }
package/src/display.ts CHANGED
@@ -26,8 +26,9 @@ function ruler(): string {
26
26
  }
27
27
 
28
28
  export function renderAd(ad: Ad, earnedCents: number, sessionCents: number, context?: { toolName?: string; fileExt?: string }): string {
29
- const earnedDollars = (earnedCents / 100).toFixed(4);
30
- const sessionDollars = (sessionCents / 100).toFixed(4);
29
+ // earnedCents and sessionCents are stored in milli-cents (1 unit = $0.00001)
30
+ const earnedDollars = (earnedCents / 100000).toFixed(4);
31
+ const sessionDollars = (sessionCents / 100000).toFixed(4);
31
32
 
32
33
  const contextTag = context?.fileExt
33
34
  ? ` · ${context.fileExt.toUpperCase()} dev`
package/src/hook.ts CHANGED
@@ -4,6 +4,7 @@ import { fetchAd, recordImpression, type AdContext } from "./ad.js";
4
4
  import { renderAd } from "./display.js";
5
5
  import { readConfig } from "./config.js";
6
6
  import { addToSession } from "./session.js";
7
+ import { animateWhileLoading } from "./animate.js";
7
8
  import path from "path";
8
9
 
9
10
  interface HookPayload {
@@ -42,13 +43,16 @@ async function main() {
42
43
  }
43
44
 
44
45
  const context = extractContext(payload);
46
+
47
+ // Fetch ad and record impression concurrently, animate while waiting
45
48
  const ad = await fetchAd(context);
46
49
  if (!ad) process.exit(0);
47
50
 
48
- // Record impression first to get earnedCents
49
- const earnedCents = await recordImpression(ad.id, config.developerKey, ad.actualCpmCents, context);
50
- const session = addToSession(earnedCents);
51
+ const earnedCents = await animateWhileLoading(
52
+ recordImpression(ad.id, config.developerKey, ad.actualCpmCents, context)
53
+ );
51
54
 
55
+ const session = addToSession(earnedCents);
52
56
  process.stdout.write(renderAd(ad, earnedCents, session.totalCents, context));
53
57
  }
54
58