@claudecash/cli 0.1.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.
@@ -0,0 +1,2 @@
1
+ import type { Ad } from './api.js';
2
+ export declare function displayAd(ad: Ad, earned: number): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import chalk from 'chalk';
2
+ const DISPLAY_SECONDS = 5;
3
+ export async function displayAd(ad, earned) {
4
+ const width = Math.min(process.stdout.columns || 80, 72);
5
+ const line = '─'.repeat(width);
6
+ process.stdout.write('\n');
7
+ process.stdout.write(chalk.yellow(line) + '\n');
8
+ // Brand + sponsored label
9
+ const brand = ad.brand ? chalk.bold(ad.brand) : '';
10
+ const sponsored = chalk.dim('sponsored');
11
+ const brandLine = brand
12
+ ? ` ${brand} ${sponsored}`
13
+ : ` ${sponsored}`;
14
+ process.stdout.write(brandLine + '\n');
15
+ // Ad text
16
+ process.stdout.write(' ' + chalk.white.bold(ad.text) + '\n');
17
+ // URL
18
+ process.stdout.write(' ' + chalk.cyan.underline(ad.url) + '\n');
19
+ // Earnings line
20
+ if (earned > 0) {
21
+ process.stdout.write(' ' + chalk.green(`+$${earned.toFixed(4)} earned`) + chalk.dim(' · claudecash.netlify.app') + '\n');
22
+ }
23
+ process.stdout.write(chalk.yellow(line) + '\n\n');
24
+ // Countdown
25
+ for (let i = DISPLAY_SECONDS; i > 0; i--) {
26
+ process.stdout.write(chalk.dim(`\r Ad closes in ${i}s... `));
27
+ await sleep(1000);
28
+ }
29
+ process.stdout.write('\r' + ' '.repeat(30) + '\r');
30
+ }
31
+ function sleep(ms) {
32
+ return new Promise(resolve => setTimeout(resolve, ms));
33
+ }
package/dist/api.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export interface Ad {
2
+ id: string;
3
+ text: string;
4
+ url: string;
5
+ brand: string | null;
6
+ icon: string | null;
7
+ bid: number;
8
+ }
9
+ export declare function fetchAd(): Promise<Ad | null>;
10
+ export declare function reportImpression(bidId: string, apiKey: string): Promise<number>;
package/dist/api.js ADDED
@@ -0,0 +1,26 @@
1
+ import { getApiUrl } from './config.js';
2
+ export async function fetchAd() {
3
+ try {
4
+ const res = await fetch(`${getApiUrl()}/api/ad`, { signal: AbortSignal.timeout(5000) });
5
+ const data = await res.json();
6
+ return data.ad ?? null;
7
+ }
8
+ catch {
9
+ return null;
10
+ }
11
+ }
12
+ export async function reportImpression(bidId, apiKey) {
13
+ try {
14
+ const res = await fetch(`${getApiUrl()}/api/impression`, {
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json' },
17
+ body: JSON.stringify({ bid_id: bidId, api_key: apiKey }),
18
+ signal: AbortSignal.timeout(5000),
19
+ });
20
+ const data = await res.json();
21
+ return data.earned ?? 0;
22
+ }
23
+ catch {
24
+ return 0;
25
+ }
26
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk';
3
+ import { getApiKey, setApiKey, getApiUrl } from './config.js';
4
+ import { fetchAd, reportImpression } from './api.js';
5
+ import { displayAd } from './ad-display.js';
6
+ import { spawn } from 'child_process';
7
+ const args = process.argv.slice(2);
8
+ const command = args[0];
9
+ async function main() {
10
+ // claudecash login --key <KEY>
11
+ if (command === 'login') {
12
+ const keyFlag = args.indexOf('--key');
13
+ if (keyFlag === -1 || !args[keyFlag + 1]) {
14
+ console.log(chalk.yellow('\nGet your API key from: ') + chalk.cyan.underline(`${getApiUrl()}/dashboard`));
15
+ console.log(chalk.dim('\nUsage: claudecash login --key <YOUR_API_KEY>\n'));
16
+ process.exit(0);
17
+ }
18
+ const key = args[keyFlag + 1];
19
+ setApiKey(key);
20
+ console.log(chalk.green('\n✓ API key saved. You\'ll now earn on every Claude impression.\n'));
21
+ process.exit(0);
22
+ }
23
+ // claudecash status
24
+ if (command === 'status') {
25
+ const key = getApiKey();
26
+ if (!key) {
27
+ console.log(chalk.yellow('\nNot logged in. Run: claudecash login --key <YOUR_KEY>\n'));
28
+ }
29
+ else {
30
+ console.log(chalk.green('\n✓ Logged in'));
31
+ console.log(chalk.dim(`API key: ${key.slice(0, 8)}…`));
32
+ console.log(chalk.dim(`Dashboard: ${getApiUrl()}/dashboard\n`));
33
+ }
34
+ process.exit(0);
35
+ }
36
+ // claudecash run <...claude args>
37
+ // OR: claudecash <prompt> (shorthand)
38
+ const claudeArgs = command === 'run' ? args.slice(1) : args;
39
+ if (claudeArgs.length === 0) {
40
+ printHelp();
41
+ process.exit(0);
42
+ }
43
+ const apiKey = getApiKey();
44
+ // Fetch ad in background while spawning claude
45
+ let adPromise = null;
46
+ if (apiKey) {
47
+ adPromise = fetchAd();
48
+ }
49
+ // Spawn claude with remaining args
50
+ const claude = spawn('claude', claudeArgs, {
51
+ stdio: 'inherit',
52
+ shell: true,
53
+ });
54
+ // When claude exits, show ad + report impression
55
+ claude.on('close', async () => {
56
+ if (!apiKey || !adPromise)
57
+ return;
58
+ const ad = await adPromise;
59
+ if (!ad)
60
+ return;
61
+ const earned = await reportImpression(ad.id, apiKey);
62
+ await displayAd(ad, earned);
63
+ });
64
+ claude.on('error', () => {
65
+ // claude not installed — show help
66
+ console.log(chalk.red('\nCould not find `claude` CLI. Install it first:'));
67
+ console.log(chalk.dim(' npm install -g @anthropic-ai/claude-code\n'));
68
+ process.exit(1);
69
+ });
70
+ }
71
+ function printHelp() {
72
+ console.log(`
73
+ ${chalk.bold.yellow('C$')} ${chalk.bold('ClaudeCash')} — earn money while Claude thinks
74
+
75
+ ${chalk.bold('Usage:')}
76
+ claudecash login --key <KEY> Link your account
77
+ claudecash status Show login status
78
+ claudecash <prompt> Run Claude + earn on the wait
79
+ claudecash run <claude args> Pass custom Claude args
80
+
81
+ ${chalk.bold('Setup:')}
82
+ 1. Sign up at ${chalk.cyan.underline('https://claudecash.netlify.app')}
83
+ 2. Copy your API key from Dashboard
84
+ 3. Run: claudecash login --key <YOUR_KEY>
85
+ 4. Use claudecash instead of claude
86
+
87
+ ${chalk.dim('Every 5-second ad impression = earnings credited to your balance.')}
88
+ `);
89
+ }
90
+ main().catch(console.error);
@@ -0,0 +1,10 @@
1
+ import Conf from 'conf';
2
+ interface ConfigSchema {
3
+ apiKey: string;
4
+ apiUrl: string;
5
+ }
6
+ export declare const config: Conf<ConfigSchema>;
7
+ export declare function getApiKey(): string | null;
8
+ export declare function setApiKey(key: string): void;
9
+ export declare function getApiUrl(): string;
10
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,18 @@
1
+ import Conf from 'conf';
2
+ export const config = new Conf({
3
+ projectName: 'claudecash',
4
+ defaults: {
5
+ apiKey: '',
6
+ apiUrl: 'https://claudecash.netlify.app',
7
+ },
8
+ });
9
+ export function getApiKey() {
10
+ const key = config.get('apiKey');
11
+ return key || null;
12
+ }
13
+ export function setApiKey(key) {
14
+ config.set('apiKey', key);
15
+ }
16
+ export function getApiUrl() {
17
+ return config.get('apiUrl');
18
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@claudecash/cli",
3
+ "version": "0.1.0",
4
+ "description": "ClaudeCash CLI — earn money while Claude thinks",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "claudecash": "dist/cli.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": ["claude", "ai", "monetize", "ads", "developer-tools"],
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "chalk": "^5.3.0",
18
+ "conf": "^12.0.0",
19
+ "ora": "^8.0.1",
20
+ "node-fetch": "^3.3.2"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.0.0",
24
+ "typescript": "^5.5.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "type": "module"
30
+ }
@@ -0,0 +1,46 @@
1
+ import chalk from 'chalk'
2
+ import type { Ad } from './api.js'
3
+
4
+ const DISPLAY_SECONDS = 5
5
+
6
+ export async function displayAd(ad: Ad, earned: number): Promise<void> {
7
+ const width = Math.min(process.stdout.columns || 80, 72)
8
+ const line = '─'.repeat(width)
9
+
10
+ process.stdout.write('\n')
11
+ process.stdout.write(chalk.yellow(line) + '\n')
12
+
13
+ // Brand + sponsored label
14
+ const brand = ad.brand ? chalk.bold(ad.brand) : ''
15
+ const sponsored = chalk.dim('sponsored')
16
+ const brandLine = brand
17
+ ? ` ${brand} ${sponsored}`
18
+ : ` ${sponsored}`
19
+ process.stdout.write(brandLine + '\n')
20
+
21
+ // Ad text
22
+ process.stdout.write(' ' + chalk.white.bold(ad.text) + '\n')
23
+
24
+ // URL
25
+ process.stdout.write(' ' + chalk.cyan.underline(ad.url) + '\n')
26
+
27
+ // Earnings line
28
+ if (earned > 0) {
29
+ process.stdout.write(
30
+ ' ' + chalk.green(`+$${earned.toFixed(4)} earned`) + chalk.dim(' · claudecash.netlify.app') + '\n'
31
+ )
32
+ }
33
+
34
+ process.stdout.write(chalk.yellow(line) + '\n\n')
35
+
36
+ // Countdown
37
+ for (let i = DISPLAY_SECONDS; i > 0; i--) {
38
+ process.stdout.write(chalk.dim(`\r Ad closes in ${i}s... `))
39
+ await sleep(1000)
40
+ }
41
+ process.stdout.write('\r' + ' '.repeat(30) + '\r')
42
+ }
43
+
44
+ function sleep(ms: number): Promise<void> {
45
+ return new Promise(resolve => setTimeout(resolve, ms))
46
+ }
package/src/api.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { getApiUrl } from './config.js'
2
+
3
+ export interface Ad {
4
+ id: string
5
+ text: string
6
+ url: string
7
+ brand: string | null
8
+ icon: string | null
9
+ bid: number
10
+ }
11
+
12
+ export async function fetchAd(): Promise<Ad | null> {
13
+ try {
14
+ const res = await fetch(`${getApiUrl()}/api/ad`, { signal: AbortSignal.timeout(5000) })
15
+ const data = await res.json() as { ad: Ad | null }
16
+ return data.ad ?? null
17
+ } catch {
18
+ return null
19
+ }
20
+ }
21
+
22
+ export async function reportImpression(bidId: string, apiKey: string): Promise<number> {
23
+ try {
24
+ const res = await fetch(`${getApiUrl()}/api/impression`, {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({ bid_id: bidId, api_key: apiKey }),
28
+ signal: AbortSignal.timeout(5000),
29
+ })
30
+ const data = await res.json() as { earned?: number }
31
+ return data.earned ?? 0
32
+ } catch {
33
+ return 0
34
+ }
35
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk'
3
+ import { getApiKey, setApiKey, getApiUrl } from './config.js'
4
+ import { fetchAd, reportImpression } from './api.js'
5
+ import { displayAd } from './ad-display.js'
6
+ import { spawn } from 'child_process'
7
+
8
+ const args = process.argv.slice(2)
9
+ const command = args[0]
10
+
11
+ async function main() {
12
+ // claudecash login --key <KEY>
13
+ if (command === 'login') {
14
+ const keyFlag = args.indexOf('--key')
15
+ if (keyFlag === -1 || !args[keyFlag + 1]) {
16
+ console.log(chalk.yellow('\nGet your API key from: ') + chalk.cyan.underline(`${getApiUrl()}/dashboard`))
17
+ console.log(chalk.dim('\nUsage: claudecash login --key <YOUR_API_KEY>\n'))
18
+ process.exit(0)
19
+ }
20
+ const key = args[keyFlag + 1]
21
+ setApiKey(key)
22
+ console.log(chalk.green('\n✓ API key saved. You\'ll now earn on every Claude impression.\n'))
23
+ process.exit(0)
24
+ }
25
+
26
+ // claudecash status
27
+ if (command === 'status') {
28
+ const key = getApiKey()
29
+ if (!key) {
30
+ console.log(chalk.yellow('\nNot logged in. Run: claudecash login --key <YOUR_KEY>\n'))
31
+ } else {
32
+ console.log(chalk.green('\n✓ Logged in'))
33
+ console.log(chalk.dim(`API key: ${key.slice(0, 8)}…`))
34
+ console.log(chalk.dim(`Dashboard: ${getApiUrl()}/dashboard\n`))
35
+ }
36
+ process.exit(0)
37
+ }
38
+
39
+ // claudecash run <...claude args>
40
+ // OR: claudecash <prompt> (shorthand)
41
+ const claudeArgs = command === 'run' ? args.slice(1) : args
42
+
43
+ if (claudeArgs.length === 0) {
44
+ printHelp()
45
+ process.exit(0)
46
+ }
47
+
48
+ const apiKey = getApiKey()
49
+
50
+ // Fetch ad in background while spawning claude
51
+ let adPromise: Promise<Awaited<ReturnType<typeof fetchAd>>> | null = null
52
+ if (apiKey) {
53
+ adPromise = fetchAd()
54
+ }
55
+
56
+ // Spawn claude with remaining args
57
+ const claude = spawn('claude', claudeArgs, {
58
+ stdio: 'inherit',
59
+ shell: true,
60
+ })
61
+
62
+ // When claude exits, show ad + report impression
63
+ claude.on('close', async () => {
64
+ if (!apiKey || !adPromise) return
65
+
66
+ const ad = await adPromise
67
+ if (!ad) return
68
+
69
+ const earned = await reportImpression(ad.id, apiKey)
70
+ await displayAd(ad, earned)
71
+ })
72
+
73
+ claude.on('error', () => {
74
+ // claude not installed — show help
75
+ console.log(chalk.red('\nCould not find `claude` CLI. Install it first:'))
76
+ console.log(chalk.dim(' npm install -g @anthropic-ai/claude-code\n'))
77
+ process.exit(1)
78
+ })
79
+ }
80
+
81
+ function printHelp() {
82
+ console.log(`
83
+ ${chalk.bold.yellow('C$')} ${chalk.bold('ClaudeCash')} — earn money while Claude thinks
84
+
85
+ ${chalk.bold('Usage:')}
86
+ claudecash login --key <KEY> Link your account
87
+ claudecash status Show login status
88
+ claudecash <prompt> Run Claude + earn on the wait
89
+ claudecash run <claude args> Pass custom Claude args
90
+
91
+ ${chalk.bold('Setup:')}
92
+ 1. Sign up at ${chalk.cyan.underline('https://claudecash.netlify.app')}
93
+ 2. Copy your API key from Dashboard
94
+ 3. Run: claudecash login --key <YOUR_KEY>
95
+ 4. Use claudecash instead of claude
96
+
97
+ ${chalk.dim('Every 5-second ad impression = earnings credited to your balance.')}
98
+ `)
99
+ }
100
+
101
+ main().catch(console.error)
package/src/config.ts ADDED
@@ -0,0 +1,27 @@
1
+ import Conf from 'conf'
2
+
3
+ interface ConfigSchema {
4
+ apiKey: string
5
+ apiUrl: string
6
+ }
7
+
8
+ export const config = new Conf<ConfigSchema>({
9
+ projectName: 'claudecash',
10
+ defaults: {
11
+ apiKey: '',
12
+ apiUrl: 'https://claudecash.netlify.app',
13
+ },
14
+ })
15
+
16
+ export function getApiKey(): string | null {
17
+ const key = config.get('apiKey')
18
+ return key || null
19
+ }
20
+
21
+ export function setApiKey(key: string): void {
22
+ config.set('apiKey', key)
23
+ }
24
+
25
+ export function getApiUrl(): string {
26
+ return config.get('apiUrl')
27
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "declaration": true
12
+ },
13
+ "include": ["src"]
14
+ }