@brozarti/spincome 0.1.4 → 0.1.6
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/animate.js +5 -5
- package/dist/display.js +11 -5
- package/dist/hook.js +8 -2
- package/package.json +1 -1
- package/src/animate.ts +5 -5
- package/src/display.ts +28 -22
- package/src/hook.ts +10 -4
package/dist/animate.js
CHANGED
|
@@ -14,7 +14,7 @@ const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇",
|
|
|
14
14
|
const COIN = ["◐", "◓", "◑", "◒"];
|
|
15
15
|
const LOGO = `${BOLD}${GREEN}$${R}${BOLD} spincome${R}`;
|
|
16
16
|
async function animateWhileLoading(work) {
|
|
17
|
-
process.
|
|
17
|
+
process.stderr.write(HIDE_CURSOR);
|
|
18
18
|
let frame = 0;
|
|
19
19
|
let dots = 0;
|
|
20
20
|
let done = false;
|
|
@@ -26,7 +26,7 @@ async function animateWhileLoading(work) {
|
|
|
26
26
|
const coin = GREEN + COIN[frame % COIN.length] + R;
|
|
27
27
|
dots = (dots + 1) % 4;
|
|
28
28
|
const dotStr = DIM + ".".repeat(dots).padEnd(3) + R;
|
|
29
|
-
process.
|
|
29
|
+
process.stderr.write(CLEAR_LINE +
|
|
30
30
|
` ${spinner} ${LOGO} ${coin} ${DIM}earning${R}${dotStr}`);
|
|
31
31
|
frame++;
|
|
32
32
|
}, 80);
|
|
@@ -36,9 +36,9 @@ async function animateWhileLoading(work) {
|
|
|
36
36
|
finally {
|
|
37
37
|
done = true;
|
|
38
38
|
clearInterval(tick);
|
|
39
|
-
process.
|
|
40
|
-
process.
|
|
41
|
-
process.
|
|
39
|
+
process.stderr.write(CLEAR_LINE);
|
|
40
|
+
process.stderr.write(UP_ONE + CLEAR_LINE);
|
|
41
|
+
process.stderr.write(SHOW_CURSOR);
|
|
42
42
|
}
|
|
43
43
|
return result;
|
|
44
44
|
}
|
package/dist/display.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderEarnings = renderEarnings;
|
|
3
4
|
exports.renderAd = renderAd;
|
|
4
5
|
const R = "\x1b[0m";
|
|
5
6
|
const B = "\x1b[1m";
|
|
@@ -7,7 +8,6 @@ const DIM = "\x1b[2m";
|
|
|
7
8
|
const GREEN = "\x1b[32m";
|
|
8
9
|
const BRIGHT_GREEN = "\x1b[92m";
|
|
9
10
|
const CYAN = "\x1b[36m";
|
|
10
|
-
const YELLOW = "\x1b[33m";
|
|
11
11
|
const WHITE = "\x1b[97m";
|
|
12
12
|
const BG = "\x1b[48;5;234m";
|
|
13
13
|
const W = 62;
|
|
@@ -21,9 +21,15 @@ function box(content, visibleLen) {
|
|
|
21
21
|
function ruler() {
|
|
22
22
|
return `${DIM}${"─".repeat(W)}${R}`;
|
|
23
23
|
}
|
|
24
|
+
// Compact earnings ticker -- shown every tool call
|
|
25
|
+
function renderEarnings(sessionCents) {
|
|
26
|
+
const sessionDollars = (sessionCents / 100000).toFixed(4);
|
|
27
|
+
return `\n ${B}${GREEN}$${R} ${B}spincome${R} ${DIM}→${R} ${BRIGHT_GREEN}$${sessionDollars}${R} ${DIM}this session${R}\n`;
|
|
28
|
+
}
|
|
29
|
+
// Full ad box -- shown every Nth call
|
|
24
30
|
function renderAd(ad, earnedCents, sessionCents, context) {
|
|
25
|
-
const earnedDollars = (earnedCents /
|
|
26
|
-
const sessionDollars = (sessionCents /
|
|
31
|
+
const earnedDollars = (earnedCents / 100000).toFixed(4);
|
|
32
|
+
const sessionDollars = (sessionCents / 100000).toFixed(4);
|
|
27
33
|
const contextTag = context?.fileExt
|
|
28
34
|
? ` · ${context.fileExt.toUpperCase()} dev`
|
|
29
35
|
: context?.toolName
|
|
@@ -33,13 +39,13 @@ function renderAd(ad, earnedCents, sessionCents, context) {
|
|
|
33
39
|
const headlineLine = `${B}${WHITE}${ad.headline}${R}`;
|
|
34
40
|
const bodyLine = `${DIM}${ad.body}${R}`;
|
|
35
41
|
const ctaLine = `${CYAN}${ad.cta}${R} ${DIM}${ad.clickUrl}${R}`;
|
|
36
|
-
const earnLine = `${BRIGHT_GREEN}+$${earnedDollars} earned${R} ${DIM}session
|
|
42
|
+
const earnLine = `${BRIGHT_GREEN}+$${earnedDollars} earned${R} ${DIM}session: ${GREEN}$${sessionDollars}${R}`;
|
|
37
43
|
const footerLine = `${DIM}spincome · /disable to opt out${R}`;
|
|
38
44
|
const sponsorLen = `Sponsored · ${ad.advertiser}${contextTag}`.length;
|
|
39
45
|
const headlineLen = ad.headline.length;
|
|
40
46
|
const bodyLen = ad.body.length;
|
|
41
47
|
const ctaLen = `${ad.cta} ${ad.clickUrl}`.length;
|
|
42
|
-
const earnLen = `+$${earnedDollars} earned session
|
|
48
|
+
const earnLen = `+$${earnedDollars} earned session: $${sessionDollars}`.length;
|
|
43
49
|
const footerLen = `spincome · /disable to opt out`.length;
|
|
44
50
|
return [
|
|
45
51
|
"",
|
package/dist/hook.js
CHANGED
|
@@ -10,6 +10,7 @@ const config_js_1 = require("./config.js");
|
|
|
10
10
|
const session_js_1 = require("./session.js");
|
|
11
11
|
const animate_js_1 = require("./animate.js");
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const AD_EVERY_N = 10; // show full ad every 10th impression
|
|
13
14
|
function extractContext(payload) {
|
|
14
15
|
const toolName = payload.tool_name ?? undefined;
|
|
15
16
|
const filePath = payload.tool_input?.file_path ??
|
|
@@ -35,12 +36,17 @@ async function main() {
|
|
|
35
36
|
// empty or malformed stdin
|
|
36
37
|
}
|
|
37
38
|
const context = extractContext(payload);
|
|
38
|
-
// Fetch ad and record impression concurrently, animate while waiting
|
|
39
39
|
const ad = await (0, ad_js_1.fetchAd)(context);
|
|
40
40
|
if (!ad)
|
|
41
41
|
process.exit(0);
|
|
42
42
|
const earnedCents = await (0, animate_js_1.animateWhileLoading)((0, ad_js_1.recordImpression)(ad.id, config.developerKey, ad.actualCpmCents, context));
|
|
43
43
|
const session = (0, session_js_1.addToSession)(earnedCents);
|
|
44
|
-
|
|
44
|
+
// Every 10th impression show the full ad, otherwise just the earnings ticker
|
|
45
|
+
if (session.impressions % AD_EVERY_N === 0) {
|
|
46
|
+
process.stdout.write((0, display_js_1.renderAd)(ad, earnedCents, session.totalCents, context));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.stdout.write((0, display_js_1.renderEarnings)(session.totalCents));
|
|
50
|
+
}
|
|
45
51
|
}
|
|
46
52
|
main().catch(() => process.exit(0));
|
package/package.json
CHANGED
package/src/animate.ts
CHANGED
|
@@ -15,7 +15,7 @@ const COIN = ["◐","◓","◑","◒"];
|
|
|
15
15
|
const LOGO = `${BOLD}${GREEN}$${R}${BOLD} spincome${R}`;
|
|
16
16
|
|
|
17
17
|
export async function animateWhileLoading<T>(work: Promise<T>): Promise<T> {
|
|
18
|
-
process.
|
|
18
|
+
process.stderr.write(HIDE_CURSOR);
|
|
19
19
|
|
|
20
20
|
let frame = 0;
|
|
21
21
|
let dots = 0;
|
|
@@ -28,7 +28,7 @@ export async function animateWhileLoading<T>(work: Promise<T>): Promise<T> {
|
|
|
28
28
|
const coin = GREEN + COIN[frame % COIN.length] + R;
|
|
29
29
|
dots = (dots + 1) % 4;
|
|
30
30
|
const dotStr = DIM + ".".repeat(dots).padEnd(3) + R;
|
|
31
|
-
process.
|
|
31
|
+
process.stderr.write(
|
|
32
32
|
CLEAR_LINE +
|
|
33
33
|
` ${spinner} ${LOGO} ${coin} ${DIM}earning${R}${dotStr}`
|
|
34
34
|
);
|
|
@@ -40,9 +40,9 @@ export async function animateWhileLoading<T>(work: Promise<T>): Promise<T> {
|
|
|
40
40
|
} finally {
|
|
41
41
|
done = true;
|
|
42
42
|
clearInterval(tick);
|
|
43
|
-
process.
|
|
44
|
-
process.
|
|
45
|
-
process.
|
|
43
|
+
process.stderr.write(CLEAR_LINE);
|
|
44
|
+
process.stderr.write(UP_ONE + CLEAR_LINE);
|
|
45
|
+
process.stderr.write(SHOW_CURSOR);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
return result!;
|
package/src/display.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import type { Ad } from "./ad.js";
|
|
2
2
|
|
|
3
|
-
const R
|
|
4
|
-
const B
|
|
5
|
-
const DIM
|
|
6
|
-
const GREEN
|
|
3
|
+
const R = "\x1b[0m";
|
|
4
|
+
const B = "\x1b[1m";
|
|
5
|
+
const DIM = "\x1b[2m";
|
|
6
|
+
const GREEN = "\x1b[32m";
|
|
7
7
|
const BRIGHT_GREEN = "\x1b[92m";
|
|
8
|
-
const CYAN
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const BG = "\x1b[48;5;234m";
|
|
8
|
+
const CYAN = "\x1b[36m";
|
|
9
|
+
const WHITE = "\x1b[97m";
|
|
10
|
+
const BG = "\x1b[48;5;234m";
|
|
12
11
|
|
|
13
12
|
const W = 62;
|
|
14
13
|
|
|
@@ -25,9 +24,16 @@ function ruler(): string {
|
|
|
25
24
|
return `${DIM}${"─".repeat(W)}${R}`;
|
|
26
25
|
}
|
|
27
26
|
|
|
27
|
+
// Compact earnings ticker -- shown every tool call
|
|
28
|
+
export function renderEarnings(sessionCents: number): string {
|
|
29
|
+
const sessionDollars = (sessionCents / 100000).toFixed(4);
|
|
30
|
+
return `\n ${B}${GREEN}$${R} ${B}spincome${R} ${DIM}→${R} ${BRIGHT_GREEN}$${sessionDollars}${R} ${DIM}this session${R}\n`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Full ad box -- shown every Nth call
|
|
28
34
|
export function renderAd(ad: Ad, earnedCents: number, sessionCents: number, context?: { toolName?: string; fileExt?: string }): string {
|
|
29
|
-
const earnedDollars = (earnedCents /
|
|
30
|
-
const sessionDollars = (sessionCents /
|
|
35
|
+
const earnedDollars = (earnedCents / 100000).toFixed(4);
|
|
36
|
+
const sessionDollars = (sessionCents / 100000).toFixed(4);
|
|
31
37
|
|
|
32
38
|
const contextTag = context?.fileExt
|
|
33
39
|
? ` · ${context.fileExt.toUpperCase()} dev`
|
|
@@ -35,19 +41,19 @@ export function renderAd(ad: Ad, earnedCents: number, sessionCents: number, cont
|
|
|
35
41
|
? ` · ${context.toolName}`
|
|
36
42
|
: "";
|
|
37
43
|
|
|
38
|
-
const sponsorLine
|
|
39
|
-
const headlineLine
|
|
40
|
-
const bodyLine
|
|
41
|
-
const ctaLine
|
|
42
|
-
const earnLine
|
|
43
|
-
const footerLine
|
|
44
|
+
const sponsorLine = `${DIM}Sponsored · ${ad.advertiser}${contextTag}${R}`;
|
|
45
|
+
const headlineLine = `${B}${WHITE}${ad.headline}${R}`;
|
|
46
|
+
const bodyLine = `${DIM}${ad.body}${R}`;
|
|
47
|
+
const ctaLine = `${CYAN}${ad.cta}${R} ${DIM}${ad.clickUrl}${R}`;
|
|
48
|
+
const earnLine = `${BRIGHT_GREEN}+$${earnedDollars} earned${R} ${DIM}session: ${GREEN}$${sessionDollars}${R}`;
|
|
49
|
+
const footerLine = `${DIM}spincome · /disable to opt out${R}`;
|
|
44
50
|
|
|
45
|
-
const sponsorLen
|
|
46
|
-
const headlineLen
|
|
47
|
-
const bodyLen
|
|
48
|
-
const ctaLen
|
|
49
|
-
const earnLen
|
|
50
|
-
const footerLen
|
|
51
|
+
const sponsorLen = `Sponsored · ${ad.advertiser}${contextTag}`.length;
|
|
52
|
+
const headlineLen = ad.headline.length;
|
|
53
|
+
const bodyLen = ad.body.length;
|
|
54
|
+
const ctaLen = `${ad.cta} ${ad.clickUrl}`.length;
|
|
55
|
+
const earnLen = `+$${earnedDollars} earned session: $${sessionDollars}`.length;
|
|
56
|
+
const footerLen = `spincome · /disable to opt out`.length;
|
|
51
57
|
|
|
52
58
|
return [
|
|
53
59
|
"",
|
package/src/hook.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { fetchAd, recordImpression, type AdContext } from "./ad.js";
|
|
4
|
-
import { renderAd } from "./display.js";
|
|
4
|
+
import { renderEarnings, renderAd } from "./display.js";
|
|
5
5
|
import { readConfig } from "./config.js";
|
|
6
6
|
import { addToSession } from "./session.js";
|
|
7
7
|
import { animateWhileLoading } from "./animate.js";
|
|
8
8
|
import path from "path";
|
|
9
9
|
|
|
10
|
+
const AD_EVERY_N = 10; // show full ad every 10th impression
|
|
11
|
+
|
|
10
12
|
interface HookPayload {
|
|
11
13
|
tool_name?: string;
|
|
12
14
|
tool_input?: {
|
|
@@ -43,8 +45,6 @@ async function main() {
|
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
const context = extractContext(payload);
|
|
46
|
-
|
|
47
|
-
// Fetch ad and record impression concurrently, animate while waiting
|
|
48
48
|
const ad = await fetchAd(context);
|
|
49
49
|
if (!ad) process.exit(0);
|
|
50
50
|
|
|
@@ -53,7 +53,13 @@ async function main() {
|
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
const session = addToSession(earnedCents);
|
|
56
|
-
|
|
56
|
+
|
|
57
|
+
// Every 10th impression show the full ad, otherwise just the earnings ticker
|
|
58
|
+
if (session.impressions % AD_EVERY_N === 0) {
|
|
59
|
+
process.stdout.write(renderAd(ad, earnedCents, session.totalCents, context));
|
|
60
|
+
} else {
|
|
61
|
+
process.stdout.write(renderEarnings(session.totalCents));
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
main().catch(() => process.exit(0));
|