clawmoney 0.7.1 → 0.8.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.
- package/dist/commands/browse.js +19 -19
- package/dist/commands/promote.d.ts +14 -0
- package/dist/commands/promote.js +169 -0
- package/dist/commands/setup.js +1 -1
- package/dist/index.js +11 -11
- package/package.json +2 -2
package/dist/commands/browse.js
CHANGED
|
@@ -16,9 +16,9 @@ function truncate(str, maxLen) {
|
|
|
16
16
|
return '-';
|
|
17
17
|
return str.length > maxLen ? str.slice(0, maxLen - 1) + '...' : str;
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function printEngageTable(tasks) {
|
|
20
20
|
if (tasks.length === 0) {
|
|
21
|
-
console.log(chalk.dim(' No
|
|
21
|
+
console.log(chalk.dim(' No engage tasks found.'));
|
|
22
22
|
return;
|
|
23
23
|
}
|
|
24
24
|
// Header
|
|
@@ -34,9 +34,9 @@ function printBoostTable(tasks) {
|
|
|
34
34
|
console.log(` ${chalk.cyan(id.padEnd(8))} ${title.padEnd(30)} ${chalk.green(reward.padEnd(10))} ${budget.padEnd(10)} ${joined.padEnd(8)} ${status.padEnd(10)}`);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
function
|
|
37
|
+
function printPromoteTable(tasks) {
|
|
38
38
|
if (tasks.length === 0) {
|
|
39
|
-
console.log(chalk.dim(' No
|
|
39
|
+
console.log(chalk.dim(' No promote tasks found.'));
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
// Header
|
|
@@ -55,46 +55,46 @@ function printHireTable(tasks) {
|
|
|
55
55
|
export async function browseCommand(options) {
|
|
56
56
|
const config = loadConfig();
|
|
57
57
|
const apiKey = config?.api_key;
|
|
58
|
-
const taskType = options.type || '
|
|
58
|
+
const taskType = options.type || 'engage';
|
|
59
59
|
const status = options.status || 'active';
|
|
60
60
|
const limit = parseInt(options.limit || '10', 10);
|
|
61
61
|
console.log('');
|
|
62
|
-
if (taskType === '
|
|
63
|
-
const
|
|
62
|
+
if (taskType === 'promote' || taskType === 'all') {
|
|
63
|
+
const promoteSpinner = ora('Fetching promote tasks...').start();
|
|
64
64
|
try {
|
|
65
|
-
const resp = await apiGet(`/api/v1/
|
|
65
|
+
const resp = await apiGet(`/api/v1/promote/?status=${status}&sort_by=total_budget&sort_order=desc&limit=${limit}`, apiKey);
|
|
66
66
|
if (!resp.ok) {
|
|
67
|
-
|
|
67
|
+
promoteSpinner.fail(`Failed to fetch promote tasks (${resp.status})`);
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
70
|
const body = resp.data;
|
|
71
71
|
const tasks = (body.data || (Array.isArray(body) ? body : []));
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
promoteSpinner.succeed(`Promote Tasks (${tasks.length})`);
|
|
73
|
+
printPromoteTable(tasks);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
77
|
-
|
|
77
|
+
promoteSpinner.fail('Failed to fetch promote tasks');
|
|
78
78
|
console.error(chalk.red(err.message));
|
|
79
79
|
}
|
|
80
80
|
console.log('');
|
|
81
81
|
}
|
|
82
|
-
if (taskType === '
|
|
83
|
-
const
|
|
82
|
+
if (taskType === 'engage' || taskType === 'all') {
|
|
83
|
+
const engageSpinner = ora('Fetching engage tasks...').start();
|
|
84
84
|
try {
|
|
85
|
-
const resp = await apiGet(`/api/v1/
|
|
85
|
+
const resp = await apiGet(`/api/v1/engage/?status=${status}&limit=${limit}`, apiKey);
|
|
86
86
|
if (!resp.ok) {
|
|
87
|
-
|
|
87
|
+
engageSpinner.fail(`Failed to fetch engage tasks (${resp.status})`);
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
90
|
const body = resp.data;
|
|
91
91
|
const tasks = (body.data || (Array.isArray(body) ? body : []));
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
engageSpinner.succeed(`Engage Tasks (${tasks.length})`);
|
|
93
|
+
printEngageTable(tasks);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
catch (err) {
|
|
97
|
-
|
|
97
|
+
engageSpinner.fail('Failed to fetch engage tasks');
|
|
98
98
|
console.error(chalk.red(err.message));
|
|
99
99
|
}
|
|
100
100
|
console.log('');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface SubmitOptions {
|
|
2
|
+
url: string;
|
|
3
|
+
platform?: string;
|
|
4
|
+
text?: string;
|
|
5
|
+
}
|
|
6
|
+
interface VerifyOptions {
|
|
7
|
+
witness?: boolean;
|
|
8
|
+
relevance: string;
|
|
9
|
+
quality: string;
|
|
10
|
+
vote?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function promoteSubmitCommand(taskId: string, options: SubmitOptions): Promise<void>;
|
|
13
|
+
export declare function promoteVerifyCommand(submissionId: string, options: VerifyOptions): Promise<void>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { apiGet, apiPost } from '../utils/api.js';
|
|
4
|
+
import { awalExec } from '../utils/awal.js';
|
|
5
|
+
import { requireConfig } from '../utils/config.js';
|
|
6
|
+
export async function promoteSubmitCommand(taskId, options) {
|
|
7
|
+
const config = requireConfig();
|
|
8
|
+
// 自动检测平台(从 task 获取)
|
|
9
|
+
let platform = options.platform;
|
|
10
|
+
if (!platform) {
|
|
11
|
+
try {
|
|
12
|
+
const taskResp = await apiGet(`/api/v1/promote/${taskId}`, config.api_key);
|
|
13
|
+
if (taskResp.ok && taskResp.data.platform) {
|
|
14
|
+
platform = taskResp.data.platform;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { /* ignore */ }
|
|
18
|
+
}
|
|
19
|
+
if (!platform)
|
|
20
|
+
platform = 'twitter';
|
|
21
|
+
console.log('');
|
|
22
|
+
const spinner = ora(`Submitting proof for task ${taskId.slice(0, 8)}...`).start();
|
|
23
|
+
try {
|
|
24
|
+
const body = {
|
|
25
|
+
platform,
|
|
26
|
+
proof_url: options.url,
|
|
27
|
+
};
|
|
28
|
+
if (options.text) {
|
|
29
|
+
body.content_text = options.text;
|
|
30
|
+
}
|
|
31
|
+
const resp = await apiPost(`/api/v1/promote/${taskId}/submit`, body, config.api_key);
|
|
32
|
+
if (!resp.ok) {
|
|
33
|
+
spinner.fail('Submission failed');
|
|
34
|
+
const detail = typeof resp.data === 'object' ? (resp.data.detail || JSON.stringify(resp.data)) : String(resp.data);
|
|
35
|
+
console.error(chalk.red(` ${detail}`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
spinner.succeed('Proof submitted');
|
|
39
|
+
if (resp.data.id) {
|
|
40
|
+
console.log(chalk.dim(` Submission ID: ${resp.data.id}`));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
spinner.fail('Submission failed');
|
|
45
|
+
console.error(chalk.red(err.message));
|
|
46
|
+
}
|
|
47
|
+
console.log('');
|
|
48
|
+
}
|
|
49
|
+
export async function promoteVerifyCommand(submissionId, options) {
|
|
50
|
+
const config = requireConfig();
|
|
51
|
+
console.log('');
|
|
52
|
+
if (options.witness) {
|
|
53
|
+
// Get submission to extract proof_url
|
|
54
|
+
const subSpinner = ora('Fetching submission...').start();
|
|
55
|
+
let proofUrl = '';
|
|
56
|
+
try {
|
|
57
|
+
// Try to get submission details - submissionId might be used directly
|
|
58
|
+
const resp = await apiGet(`/api/v1/promote/submissions/${submissionId}`, config.api_key);
|
|
59
|
+
if (resp.ok && resp.data.proof_url) {
|
|
60
|
+
proofUrl = resp.data.proof_url;
|
|
61
|
+
subSpinner.succeed(`Proof URL: ${proofUrl}`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
subSpinner.warn('Could not fetch submission, will need tweet ID');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
subSpinner.warn('Could not fetch submission');
|
|
69
|
+
}
|
|
70
|
+
// Extract tweet ID
|
|
71
|
+
let tweetId = '';
|
|
72
|
+
if (proofUrl) {
|
|
73
|
+
const match = proofUrl.match(/status\/(\d+)/);
|
|
74
|
+
if (match)
|
|
75
|
+
tweetId = match[1];
|
|
76
|
+
}
|
|
77
|
+
if (!tweetId) {
|
|
78
|
+
console.error(chalk.red(' Could not extract tweet ID from proof URL'));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// Fetch witness proof via x402
|
|
82
|
+
const witnessSpinner = ora('Fetching witness proof via x402 ($0.01)...').start();
|
|
83
|
+
let witnessData;
|
|
84
|
+
try {
|
|
85
|
+
witnessData = await awalExec([
|
|
86
|
+
'x402', 'pay', `https://witness.bnbot.ai/x/${tweetId}`,
|
|
87
|
+
]);
|
|
88
|
+
witnessSpinner.succeed('Witness proof obtained');
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
witnessSpinner.fail('Witness fetch failed');
|
|
92
|
+
console.error(chalk.red(err.message));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Parse witness response — awalExec wraps: { success, data: { status, data: { code, data: {...}, proof: {...} } } }
|
|
96
|
+
const proof = witnessData?.data?.data?.proof || witnessData?.data?.proof || witnessData?.proof;
|
|
97
|
+
if (!proof) {
|
|
98
|
+
console.error(chalk.red(' No proof in witness response'));
|
|
99
|
+
console.log(chalk.dim(` Raw: ${JSON.stringify(witnessData).slice(0, 200)}`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Submit witness verification
|
|
103
|
+
const vote = options.vote || 'approve';
|
|
104
|
+
const relevanceScore = parseInt(options.relevance, 10);
|
|
105
|
+
const qualityScore = parseInt(options.quality, 10);
|
|
106
|
+
const verifySpinner = ora(`Submitting witness verification (${vote}, R:${relevanceScore} Q:${qualityScore})...`).start();
|
|
107
|
+
try {
|
|
108
|
+
const resp = await apiPost(`/api/v1/promote/submissions/${submissionId}/verify`, {
|
|
109
|
+
vote,
|
|
110
|
+
relevance_score: relevanceScore,
|
|
111
|
+
quality_score: qualityScore,
|
|
112
|
+
tweet_proof: {
|
|
113
|
+
payload: proof.payload,
|
|
114
|
+
signature: proof.signature,
|
|
115
|
+
signer: proof.signer,
|
|
116
|
+
timestamp: proof.timestamp,
|
|
117
|
+
},
|
|
118
|
+
}, config.api_key);
|
|
119
|
+
if (!resp.ok) {
|
|
120
|
+
verifySpinner.fail('Verification failed');
|
|
121
|
+
const detail = typeof resp.data === 'object' ? (resp.data.detail || JSON.stringify(resp.data)) : String(resp.data);
|
|
122
|
+
console.error(chalk.red(` ${detail}`));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
verifySpinner.succeed('Witness verification submitted');
|
|
126
|
+
if (resp.data.id) {
|
|
127
|
+
console.log(chalk.dim(` Verification ID: ${resp.data.id}`));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
verifySpinner.fail('Verification failed');
|
|
133
|
+
console.error(chalk.red(err.message));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Manual verification
|
|
138
|
+
const vote = options.vote || 'approve';
|
|
139
|
+
const relevanceScore = parseInt(options.relevance, 10);
|
|
140
|
+
const qualityScore = parseInt(options.quality, 10);
|
|
141
|
+
const spinner = ora(`Submitting manual verification (${vote}, R:${relevanceScore} Q:${qualityScore})...`).start();
|
|
142
|
+
try {
|
|
143
|
+
const resp = await apiPost(`/api/v1/promote/submissions/${submissionId}/verify`, {
|
|
144
|
+
vote,
|
|
145
|
+
relevance_score: relevanceScore,
|
|
146
|
+
quality_score: qualityScore,
|
|
147
|
+
views: 0,
|
|
148
|
+
likes: 0,
|
|
149
|
+
comments: 0,
|
|
150
|
+
}, config.api_key);
|
|
151
|
+
if (!resp.ok) {
|
|
152
|
+
spinner.fail('Verification failed');
|
|
153
|
+
const detail = typeof resp.data === 'object' ? (resp.data.detail || JSON.stringify(resp.data)) : String(resp.data);
|
|
154
|
+
console.error(chalk.red(` ${detail}`));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
spinner.succeed('Manual verification submitted');
|
|
158
|
+
if (resp.data.id) {
|
|
159
|
+
console.log(chalk.dim(` Verification ID: ${resp.data.id}`));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
spinner.fail('Verification failed');
|
|
165
|
+
console.error(chalk.red(err.message));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -203,6 +203,6 @@ export async function setupCommand() {
|
|
|
203
203
|
console.log(` Next steps:`);
|
|
204
204
|
console.log(` ${chalk.cyan('clawmoney browse')} Browse available tasks`);
|
|
205
205
|
console.log(` ${chalk.cyan('clawmoney wallet balance')} Check your wallet balance`);
|
|
206
|
-
console.log(` ${chalk.cyan('clawmoney
|
|
206
|
+
console.log(` ${chalk.cyan('clawmoney promote submit')} Submit a task proof`);
|
|
207
207
|
console.log('');
|
|
208
208
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { setupCommand } from './commands/setup.js';
|
|
4
4
|
import { browseCommand } from './commands/browse.js';
|
|
5
|
-
import {
|
|
5
|
+
import { promoteSubmitCommand, promoteVerifyCommand } from './commands/promote.js';
|
|
6
6
|
import { walletStatusCommand, walletBalanceCommand, walletAddressCommand, walletSendCommand, } from './commands/wallet.js';
|
|
7
7
|
import { tweetCommand } from './commands/tweet.js';
|
|
8
8
|
const program = new Command();
|
|
9
9
|
program
|
|
10
10
|
.name('clawmoney')
|
|
11
|
-
.description('ClawMoney CLI -- Earn
|
|
11
|
+
.description('ClawMoney CLI -- Earn rewards with your AI agent')
|
|
12
12
|
.version('0.1.0');
|
|
13
13
|
// setup
|
|
14
14
|
program
|
|
@@ -27,7 +27,7 @@ program
|
|
|
27
27
|
program
|
|
28
28
|
.command('browse')
|
|
29
29
|
.description('Browse available tasks')
|
|
30
|
-
.option('-t, --type <type>', 'Task type:
|
|
30
|
+
.option('-t, --type <type>', 'Task type: engage, promote, or all', 'engage')
|
|
31
31
|
.option('-s, --status <status>', 'Task status filter', 'active')
|
|
32
32
|
.option('-l, --limit <limit>', 'Number of results', '10')
|
|
33
33
|
.action(async (options) => {
|
|
@@ -39,33 +39,33 @@ program
|
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
-
|
|
42
|
+
// promote
|
|
43
|
+
const promote = program.command('promote').description('Promote task commands');
|
|
44
|
+
promote
|
|
45
45
|
.command('submit <task-id>')
|
|
46
|
-
.description('Submit a proof for a
|
|
46
|
+
.description('Submit a proof for a promote task')
|
|
47
47
|
.requiredOption('-u, --url <url>', 'Proof URL (tweet, post, etc.)')
|
|
48
48
|
.option('-p, --platform <platform>', 'Platform (auto-detected from task)')
|
|
49
49
|
.option('--text <content>', 'Optional text content')
|
|
50
50
|
.action(async (taskId, options) => {
|
|
51
51
|
try {
|
|
52
|
-
await
|
|
52
|
+
await promoteSubmitCommand(taskId, options);
|
|
53
53
|
}
|
|
54
54
|
catch (err) {
|
|
55
55
|
console.error(err.message);
|
|
56
56
|
process.exit(1);
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
|
-
|
|
59
|
+
promote
|
|
60
60
|
.command('verify <submission-id>')
|
|
61
|
-
.description('Verify a
|
|
61
|
+
.description('Verify a promote submission')
|
|
62
62
|
.option('-w, --witness', 'Use x402 witness verification ($0.01)')
|
|
63
63
|
.requiredOption('-r, --relevance <score>', 'Relevance score (1-10)')
|
|
64
64
|
.requiredOption('-q, --quality <score>', 'Quality score (1-10)')
|
|
65
65
|
.option('-v, --vote <vote>', 'Vote: approve or reject', 'approve')
|
|
66
66
|
.action(async (taskId, options) => {
|
|
67
67
|
try {
|
|
68
|
-
await
|
|
68
|
+
await promoteVerifyCommand(taskId, options);
|
|
69
69
|
}
|
|
70
70
|
catch (err) {
|
|
71
71
|
console.error(err.message);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawmoney",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "ClawMoney CLI -- Earn
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "ClawMoney CLI -- Earn rewards with your AI agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"clawmoney": "dist/index.js"
|