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.
@@ -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 printBoostTable(tasks) {
19
+ function printEngageTable(tasks) {
20
20
  if (tasks.length === 0) {
21
- console.log(chalk.dim(' No boost tasks found.'));
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 printHireTable(tasks) {
37
+ function printPromoteTable(tasks) {
38
38
  if (tasks.length === 0) {
39
- console.log(chalk.dim(' No hire tasks found.'));
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 || 'boost';
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 === 'hire' || taskType === 'all') {
63
- const hireSpinner = ora('Fetching hire tasks...').start();
62
+ if (taskType === 'promote' || taskType === 'all') {
63
+ const promoteSpinner = ora('Fetching promote tasks...').start();
64
64
  try {
65
- const resp = await apiGet(`/api/v1/hire/?status=${status}&sort_by=total_budget&sort_order=desc&limit=${limit}`, apiKey);
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
- hireSpinner.fail(`Failed to fetch hire tasks (${resp.status})`);
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
- hireSpinner.succeed(`Hire Tasks (${tasks.length})`);
73
- printHireTable(tasks);
72
+ promoteSpinner.succeed(`Promote Tasks (${tasks.length})`);
73
+ printPromoteTable(tasks);
74
74
  }
75
75
  }
76
76
  catch (err) {
77
- hireSpinner.fail('Failed to fetch hire tasks');
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 === 'boost' || taskType === 'all') {
83
- const boostSpinner = ora('Fetching boost tasks...').start();
82
+ if (taskType === 'engage' || taskType === 'all') {
83
+ const engageSpinner = ora('Fetching engage tasks...').start();
84
84
  try {
85
- const resp = await apiGet(`/api/v1/tasks/?status=${status}&sort=reward&limit=${limit}`, apiKey);
85
+ const resp = await apiGet(`/api/v1/engage/?status=${status}&limit=${limit}`, apiKey);
86
86
  if (!resp.ok) {
87
- boostSpinner.fail(`Failed to fetch boost tasks (${resp.status})`);
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
- boostSpinner.succeed(`Boost Tasks (${tasks.length})`);
93
- printBoostTable(tasks);
92
+ engageSpinner.succeed(`Engage Tasks (${tasks.length})`);
93
+ printEngageTable(tasks);
94
94
  }
95
95
  }
96
96
  catch (err) {
97
- boostSpinner.fail('Failed to fetch boost tasks');
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
+ }
@@ -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 hire submit')} Submit a task proof`);
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 { hireSubmitCommand, hireVerifyCommand } from './commands/hire.js';
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 crypto with your AI agent')
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: boost, hire, or all', 'boost')
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
- // hire
43
- const hire = program.command('hire').description('Hire task commands');
44
- hire
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 hire task')
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 hireSubmitCommand(taskId, options);
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
- hire
59
+ promote
60
60
  .command('verify <submission-id>')
61
- .description('Verify a hire submission')
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 hireVerifyCommand(taskId, options);
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.7.1",
4
- "description": "ClawMoney CLI -- Earn crypto with your AI agent",
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"