@paylobster/cli 4.0.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/BUILD_SUMMARY.md +429 -0
- package/CHANGELOG.md +78 -0
- package/CONTRIBUTING.md +368 -0
- package/EXAMPLES.md +432 -0
- package/LICENSE +21 -0
- package/QUICKSTART.md +189 -0
- package/README.md +377 -0
- package/TEST_REPORT.md +191 -0
- package/bin/plob.js +9 -0
- package/bin/plob.ts +9 -0
- package/demo.sh +154 -0
- package/dist/bin/plob.d.ts +7 -0
- package/dist/bin/plob.d.ts.map +1 -0
- package/dist/bin/plob.js +10 -0
- package/dist/bin/plob.js.map +1 -0
- package/dist/src/commands/auth.d.ts +3 -0
- package/dist/src/commands/auth.d.ts.map +1 -0
- package/dist/src/commands/auth.js +75 -0
- package/dist/src/commands/auth.js.map +1 -0
- package/dist/src/commands/config.d.ts +3 -0
- package/dist/src/commands/config.d.ts.map +1 -0
- package/dist/src/commands/config.js +79 -0
- package/dist/src/commands/config.js.map +1 -0
- package/dist/src/commands/escrow.d.ts +3 -0
- package/dist/src/commands/escrow.d.ts.map +1 -0
- package/dist/src/commands/escrow.js +193 -0
- package/dist/src/commands/escrow.js.map +1 -0
- package/dist/src/commands/mandate.d.ts +8 -0
- package/dist/src/commands/mandate.d.ts.map +1 -0
- package/dist/src/commands/mandate.js +54 -0
- package/dist/src/commands/mandate.js.map +1 -0
- package/dist/src/commands/pay.d.ts +6 -0
- package/dist/src/commands/pay.d.ts.map +1 -0
- package/dist/src/commands/pay.js +77 -0
- package/dist/src/commands/pay.js.map +1 -0
- package/dist/src/commands/register.d.ts +3 -0
- package/dist/src/commands/register.d.ts.map +1 -0
- package/dist/src/commands/register.js +51 -0
- package/dist/src/commands/register.js.map +1 -0
- package/dist/src/commands/reputation.d.ts +3 -0
- package/dist/src/commands/reputation.d.ts.map +1 -0
- package/dist/src/commands/reputation.js +116 -0
- package/dist/src/commands/reputation.js.map +1 -0
- package/dist/src/commands/status.d.ts +3 -0
- package/dist/src/commands/status.d.ts.map +1 -0
- package/dist/src/commands/status.js +82 -0
- package/dist/src/commands/status.js.map +1 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +59 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/lib/config.d.ts +26 -0
- package/dist/src/lib/config.d.ts.map +1 -0
- package/dist/src/lib/config.js +91 -0
- package/dist/src/lib/config.js.map +1 -0
- package/dist/src/lib/contracts.d.ts +18798 -0
- package/dist/src/lib/contracts.d.ts.map +1 -0
- package/dist/src/lib/contracts.js +361 -0
- package/dist/src/lib/contracts.js.map +1 -0
- package/dist/src/lib/display.d.ts +83 -0
- package/dist/src/lib/display.d.ts.map +1 -0
- package/dist/src/lib/display.js +293 -0
- package/dist/src/lib/display.js.map +1 -0
- package/dist/src/lib/types.d.ts +49 -0
- package/dist/src/lib/types.d.ts.map +1 -0
- package/dist/src/lib/types.js +3 -0
- package/dist/src/lib/types.js.map +1 -0
- package/dist/src/lib/wallet.d.ts +30 -0
- package/dist/src/lib/wallet.d.ts.map +1 -0
- package/dist/src/lib/wallet.js +143 -0
- package/dist/src/lib/wallet.js.map +1 -0
- package/jest.config.js +15 -0
- package/package.json +55 -0
- package/src/__tests__/cli.test.ts +38 -0
- package/src/commands/auth.ts +75 -0
- package/src/commands/config.ts +84 -0
- package/src/commands/escrow.ts +222 -0
- package/src/commands/mandate.ts +56 -0
- package/src/commands/pay.ts +96 -0
- package/src/commands/register.ts +57 -0
- package/src/commands/reputation.ts +84 -0
- package/src/commands/status.ts +91 -0
- package/src/index.ts +63 -0
- package/src/lib/config.ts +90 -0
- package/src/lib/contracts.ts +392 -0
- package/src/lib/display.ts +265 -0
- package/src/lib/types.ts +57 -0
- package/src/lib/wallet.ts +146 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { saveToKeystore, loadWallet, getWalletAddress } from '../lib/wallet';
|
|
3
|
+
import { saveConfig } from '../lib/config';
|
|
4
|
+
import { success, error, warning, info } from '../lib/display';
|
|
5
|
+
|
|
6
|
+
export function createAuthCommand(): Command {
|
|
7
|
+
const cmd = new Command('auth')
|
|
8
|
+
.description('Set up wallet authentication');
|
|
9
|
+
|
|
10
|
+
cmd
|
|
11
|
+
.option('--private-key <key>', 'Set private key directly')
|
|
12
|
+
.option('--env <var>', 'Use environment variable (PRIVATE_KEY or custom)')
|
|
13
|
+
.option('--keystore <file>', 'Save to keystore file')
|
|
14
|
+
.action(async (options) => {
|
|
15
|
+
try {
|
|
16
|
+
if (options.privateKey) {
|
|
17
|
+
// Validate private key format
|
|
18
|
+
if (!options.privateKey.startsWith('0x') || options.privateKey.length !== 66) {
|
|
19
|
+
error('Invalid private key format. Must be 0x-prefixed 64-character hex string');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Save to keystore
|
|
24
|
+
const keystorePath = saveToKeystore(options.privateKey, options.keystore || 'default.key');
|
|
25
|
+
|
|
26
|
+
// Update config
|
|
27
|
+
saveConfig({
|
|
28
|
+
wallet: {
|
|
29
|
+
type: 'keystore',
|
|
30
|
+
path: options.keystore || 'default.key',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
success('Wallet configured successfully');
|
|
35
|
+
info(`Private key saved to keystore: ${keystorePath}`);
|
|
36
|
+
warning('Keep your keystore file secure!');
|
|
37
|
+
|
|
38
|
+
// Show address
|
|
39
|
+
const address = getWalletAddress();
|
|
40
|
+
info(`Wallet address: ${address}`);
|
|
41
|
+
} else if (options.env) {
|
|
42
|
+
// Use environment variable
|
|
43
|
+
saveConfig({
|
|
44
|
+
wallet: {
|
|
45
|
+
type: 'env',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
success(`Configured to use environment variable: ${options.env}`);
|
|
50
|
+
info('Set the environment variable before running commands:');
|
|
51
|
+
console.log(` export ${options.env}="0x..."`);
|
|
52
|
+
} else {
|
|
53
|
+
// Default: check if wallet is already configured
|
|
54
|
+
try {
|
|
55
|
+
const address = getWalletAddress();
|
|
56
|
+
success('Wallet is already configured');
|
|
57
|
+
info(`Address: ${address}`);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
error('No wallet configured');
|
|
60
|
+
console.log();
|
|
61
|
+
console.log('Usage:');
|
|
62
|
+
console.log(' plob auth --private-key 0x...');
|
|
63
|
+
console.log(' plob auth --env PRIVATE_KEY');
|
|
64
|
+
console.log();
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
error(`Failed to configure wallet: ${err}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return cmd;
|
|
75
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { loadConfig, saveConfig, clearConfig } from '../lib/config';
|
|
3
|
+
import { success, error, outputJSON, printConfig, formatNetwork } from '../lib/display';
|
|
4
|
+
import type { OutputOptions } from '../lib/types';
|
|
5
|
+
|
|
6
|
+
export function createConfigCommand(): Command {
|
|
7
|
+
const cmd = new Command('config')
|
|
8
|
+
.description('Manage CLI configuration');
|
|
9
|
+
|
|
10
|
+
// plob config show
|
|
11
|
+
cmd
|
|
12
|
+
.command('show')
|
|
13
|
+
.description('Show current configuration')
|
|
14
|
+
.option('--json', 'Output as JSON')
|
|
15
|
+
.action(async (options: OutputOptions) => {
|
|
16
|
+
try {
|
|
17
|
+
const config = loadConfig();
|
|
18
|
+
|
|
19
|
+
if (outputJSON(config, options)) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log();
|
|
24
|
+
printConfig({
|
|
25
|
+
network: formatNetwork(config.network),
|
|
26
|
+
rpcUrl: config.rpcUrl || 'default',
|
|
27
|
+
indexerUrl: config.indexerUrl || 'default',
|
|
28
|
+
wallet: config.wallet?.type || 'env',
|
|
29
|
+
});
|
|
30
|
+
console.log();
|
|
31
|
+
} catch (err) {
|
|
32
|
+
error(`Failed to load config: ${err}`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// plob config set <key> <value>
|
|
38
|
+
cmd
|
|
39
|
+
.command('set')
|
|
40
|
+
.description('Set a configuration value')
|
|
41
|
+
.argument('<key>', 'Configuration key (network, rpcUrl, indexerUrl)')
|
|
42
|
+
.argument('<value>', 'Configuration value')
|
|
43
|
+
.action(async (key: string, value: string) => {
|
|
44
|
+
try {
|
|
45
|
+
const validKeys = ['network', 'rpcUrl', 'indexerUrl'];
|
|
46
|
+
|
|
47
|
+
if (!validKeys.includes(key)) {
|
|
48
|
+
error(`Invalid config key: ${key}`);
|
|
49
|
+
console.log(`Valid keys: ${validKeys.join(', ')}`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (key === 'network' && !['mainnet', 'sepolia'].includes(value)) {
|
|
54
|
+
error('Network must be "mainnet" or "sepolia"');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const config = loadConfig();
|
|
59
|
+
(config as any)[key] = value;
|
|
60
|
+
saveConfig(config);
|
|
61
|
+
|
|
62
|
+
success(`Set ${key} = ${value}`);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
error(`Failed to set config: ${err}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// plob config reset
|
|
70
|
+
cmd
|
|
71
|
+
.command('reset')
|
|
72
|
+
.description('Reset configuration to defaults')
|
|
73
|
+
.action(async () => {
|
|
74
|
+
try {
|
|
75
|
+
clearConfig();
|
|
76
|
+
success('Configuration reset to defaults');
|
|
77
|
+
} catch (err) {
|
|
78
|
+
error(`Failed to reset config: ${err}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return cmd;
|
|
84
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createEscrow, releaseEscrow, getEscrow, formatUSDC, getUserEscrowCount } from '../lib/contracts';
|
|
3
|
+
import { getWalletAddress } from '../lib/wallet';
|
|
4
|
+
import { success, error, info, withSpinner, outputJSON, formatAddress, formatStatus, printEscrowTable, confirm } from '../lib/display';
|
|
5
|
+
import type { OutputOptions } from '../lib/types';
|
|
6
|
+
|
|
7
|
+
export function createEscrowCommand(): Command {
|
|
8
|
+
const cmd = new Command('escrow')
|
|
9
|
+
.description('Manage escrows');
|
|
10
|
+
|
|
11
|
+
// plob escrow create
|
|
12
|
+
cmd
|
|
13
|
+
.command('create')
|
|
14
|
+
.description('Create a new escrow')
|
|
15
|
+
.requiredOption('--to <address>', 'Recipient address')
|
|
16
|
+
.requiredOption('--amount <amount>', 'Amount in USDC')
|
|
17
|
+
.option('--description <desc>', 'Escrow description', 'Payment via PayLobster CLI')
|
|
18
|
+
.option('--json', 'Output as JSON')
|
|
19
|
+
.action(async (options: { to: string; amount: string; description: string } & OutputOptions) => {
|
|
20
|
+
try {
|
|
21
|
+
// Validate address format
|
|
22
|
+
if (!options.to.startsWith('0x') || options.to.length !== 42) {
|
|
23
|
+
error('Invalid recipient address format');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Validate amount
|
|
28
|
+
const amount = parseFloat(options.amount);
|
|
29
|
+
if (isNaN(amount) || amount <= 0) {
|
|
30
|
+
error('Invalid amount. Must be a positive number');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
info(`Creating escrow for ${options.amount} USDC to ${formatAddress(options.to)}`);
|
|
35
|
+
|
|
36
|
+
const txHash = await withSpinner(
|
|
37
|
+
'Creating escrow (approving USDC + creating escrow)...',
|
|
38
|
+
async () => {
|
|
39
|
+
return await createEscrow(
|
|
40
|
+
options.to as `0x${string}`,
|
|
41
|
+
options.amount,
|
|
42
|
+
options.description
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
if (outputJSON({
|
|
48
|
+
txHash: txHash.toString(),
|
|
49
|
+
to: options.to,
|
|
50
|
+
amount: options.amount,
|
|
51
|
+
description: options.description,
|
|
52
|
+
}, options)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
success('Escrow created successfully!');
|
|
57
|
+
info(`Transaction: ${txHash.toString()}`);
|
|
58
|
+
info(`Recipient: ${options.to}`);
|
|
59
|
+
info(`Amount: ${options.amount} USDC`);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
error(`Failed to create escrow: ${err}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// plob escrow list
|
|
67
|
+
cmd
|
|
68
|
+
.command('list')
|
|
69
|
+
.description('List your escrows')
|
|
70
|
+
.option('--json', 'Output as JSON')
|
|
71
|
+
.action(async (options: OutputOptions) => {
|
|
72
|
+
try {
|
|
73
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
74
|
+
const count = await getUserEscrowCount(address);
|
|
75
|
+
|
|
76
|
+
if (count === 0) {
|
|
77
|
+
info('No escrows found');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
info(`Total escrows: ${count}`);
|
|
82
|
+
|
|
83
|
+
// Fetch details for each escrow
|
|
84
|
+
// Note: This is a simplified implementation. In production, you'd want pagination
|
|
85
|
+
// or fetch from an indexer
|
|
86
|
+
const escrows = [];
|
|
87
|
+
for (let i = 0; i < Math.min(count, 10); i++) {
|
|
88
|
+
try {
|
|
89
|
+
const escrowData = await getEscrow(BigInt(i));
|
|
90
|
+
escrows.push({
|
|
91
|
+
id: i,
|
|
92
|
+
from: escrowData.sender,
|
|
93
|
+
to: escrowData.recipient,
|
|
94
|
+
amount: formatUSDC(escrowData.amount) + ' USDC',
|
|
95
|
+
status: escrowData.status,
|
|
96
|
+
});
|
|
97
|
+
} catch (err) {
|
|
98
|
+
// Skip if can't fetch
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (outputJSON(escrows, options)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
printEscrowTable(escrows);
|
|
107
|
+
|
|
108
|
+
if (count > 10) {
|
|
109
|
+
info(`Showing first 10 of ${count} escrows`);
|
|
110
|
+
}
|
|
111
|
+
} catch (err) {
|
|
112
|
+
error(`Failed to list escrows: ${err}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// plob escrow get
|
|
118
|
+
cmd
|
|
119
|
+
.command('get')
|
|
120
|
+
.description('Get escrow details')
|
|
121
|
+
.argument('<id>', 'Escrow ID')
|
|
122
|
+
.option('--json', 'Output as JSON')
|
|
123
|
+
.action(async (id: string, options: OutputOptions) => {
|
|
124
|
+
try {
|
|
125
|
+
const escrowId = BigInt(id);
|
|
126
|
+
const escrow = await getEscrow(escrowId);
|
|
127
|
+
|
|
128
|
+
if (outputJSON({
|
|
129
|
+
id: id,
|
|
130
|
+
sender: escrow.sender,
|
|
131
|
+
recipient: escrow.recipient,
|
|
132
|
+
amount: formatUSDC(escrow.amount),
|
|
133
|
+
token: escrow.token,
|
|
134
|
+
status: escrow.status,
|
|
135
|
+
}, options)) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log();
|
|
140
|
+
console.log('Escrow ID: ', id);
|
|
141
|
+
console.log('From: ', escrow.sender);
|
|
142
|
+
console.log('To: ', escrow.recipient);
|
|
143
|
+
console.log('Amount: ', formatUSDC(escrow.amount), 'USDC');
|
|
144
|
+
console.log('Token: ', escrow.token);
|
|
145
|
+
console.log('Status: ', formatStatus(escrow.status));
|
|
146
|
+
console.log();
|
|
147
|
+
} catch (err) {
|
|
148
|
+
error(`Failed to get escrow: ${err}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// plob escrow release
|
|
154
|
+
cmd
|
|
155
|
+
.command('release')
|
|
156
|
+
.description('Release an escrow')
|
|
157
|
+
.argument('<id>', 'Escrow ID')
|
|
158
|
+
.option('--yes', 'Skip confirmation')
|
|
159
|
+
.option('--json', 'Output as JSON')
|
|
160
|
+
.action(async (id: string, options: { yes?: boolean } & OutputOptions) => {
|
|
161
|
+
try {
|
|
162
|
+
const escrowId = BigInt(id);
|
|
163
|
+
|
|
164
|
+
// Get escrow details
|
|
165
|
+
const escrow = await getEscrow(escrowId);
|
|
166
|
+
|
|
167
|
+
// Verify sender
|
|
168
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
169
|
+
if (escrow.sender.toLowerCase() !== address.toLowerCase()) {
|
|
170
|
+
error('You are not the sender of this escrow');
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check status
|
|
175
|
+
if (escrow.status !== 1) { // 1 = active
|
|
176
|
+
error(`Escrow is not active (status: ${formatStatus(escrow.status)})`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Confirm
|
|
181
|
+
if (!options.yes) {
|
|
182
|
+
console.log();
|
|
183
|
+
console.log('Escrow details:');
|
|
184
|
+
console.log(' ID: ', id);
|
|
185
|
+
console.log(' To: ', escrow.recipient);
|
|
186
|
+
console.log(' Amount: ', formatUSDC(escrow.amount), 'USDC');
|
|
187
|
+
console.log();
|
|
188
|
+
|
|
189
|
+
const confirmed = await confirm('Release this escrow?');
|
|
190
|
+
if (!confirmed) {
|
|
191
|
+
info('Cancelled');
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const txHash = await withSpinner(
|
|
197
|
+
'Releasing escrow...',
|
|
198
|
+
async () => {
|
|
199
|
+
return await releaseEscrow(escrowId);
|
|
200
|
+
}
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
if (outputJSON({
|
|
204
|
+
txHash,
|
|
205
|
+
escrowId: id,
|
|
206
|
+
released: formatUSDC(escrow.amount),
|
|
207
|
+
}, options)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
success('Escrow released successfully!');
|
|
212
|
+
info(`Transaction: ${txHash}`);
|
|
213
|
+
info(`Released: ${formatUSDC(escrow.amount)} USDC`);
|
|
214
|
+
info(`Recipient: ${escrow.recipient}`);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
error(`Failed to release escrow: ${err}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return cmd;
|
|
222
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { error, info, warning } from '../lib/display';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Mandate commands
|
|
6
|
+
* Note: Full mandate implementation requires additional contract functions
|
|
7
|
+
* This is a placeholder for future implementation
|
|
8
|
+
*/
|
|
9
|
+
export function createMandateCommand(): Command {
|
|
10
|
+
const cmd = new Command('mandate')
|
|
11
|
+
.description('Manage payment mandates (coming soon)');
|
|
12
|
+
|
|
13
|
+
cmd
|
|
14
|
+
.command('create')
|
|
15
|
+
.description('Create a new mandate')
|
|
16
|
+
.option('--agent <address>', 'Agent address')
|
|
17
|
+
.option('--limit <amount>', 'Spending limit in USDC')
|
|
18
|
+
.option('--duration <days>', 'Duration in days')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
warning('Mandate creation is not yet implemented');
|
|
21
|
+
info('This feature will be available in a future update');
|
|
22
|
+
console.log();
|
|
23
|
+
console.log('Mandates allow you to delegate spending permissions to agents');
|
|
24
|
+
console.log('with configurable limits and time restrictions.');
|
|
25
|
+
console.log();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
cmd
|
|
29
|
+
.command('list')
|
|
30
|
+
.description('List your mandates')
|
|
31
|
+
.action(async () => {
|
|
32
|
+
warning('Mandate listing is not yet implemented');
|
|
33
|
+
info('This feature will be available in a future update');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
cmd
|
|
37
|
+
.command('revoke')
|
|
38
|
+
.description('Revoke a mandate')
|
|
39
|
+
.argument('<id>', 'Mandate ID')
|
|
40
|
+
.action(async () => {
|
|
41
|
+
warning('Mandate revocation is not yet implemented');
|
|
42
|
+
info('This feature will be available in a future update');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
cmd
|
|
46
|
+
.command('adjust')
|
|
47
|
+
.description('Adjust mandate limits')
|
|
48
|
+
.argument('<id>', 'Mandate ID')
|
|
49
|
+
.option('--limit <amount>', 'New spending limit')
|
|
50
|
+
.action(async () => {
|
|
51
|
+
warning('Mandate adjustment is not yet implemented');
|
|
52
|
+
info('This feature will be available in a future update');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return cmd;
|
|
56
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createEscrow } from '../lib/contracts';
|
|
3
|
+
import { success, error, info, withSpinner, outputJSON, formatAddress, confirm } from '../lib/display';
|
|
4
|
+
import type { OutputOptions } from '../lib/types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Simple pay command (wrapper around escrow create)
|
|
8
|
+
*/
|
|
9
|
+
export function createPayCommand(): Command {
|
|
10
|
+
const cmd = new Command('pay')
|
|
11
|
+
.description('Execute payment (creates escrow)')
|
|
12
|
+
.requiredOption('--to <address>', 'Recipient address')
|
|
13
|
+
.requiredOption('--amount <amount>', 'Amount in USDC')
|
|
14
|
+
.option('--description <desc>', 'Payment description', 'Payment via PayLobster CLI')
|
|
15
|
+
.option('--mandate <id>', 'Mandate ID (not yet implemented)')
|
|
16
|
+
.option('--yes', 'Skip confirmation')
|
|
17
|
+
.option('--json', 'Output as JSON')
|
|
18
|
+
.action(async (options: {
|
|
19
|
+
to: string;
|
|
20
|
+
amount: string;
|
|
21
|
+
description: string;
|
|
22
|
+
mandate?: string;
|
|
23
|
+
yes?: boolean;
|
|
24
|
+
} & OutputOptions) => {
|
|
25
|
+
try {
|
|
26
|
+
// Validate address format
|
|
27
|
+
if (!options.to.startsWith('0x') || options.to.length !== 42) {
|
|
28
|
+
error('Invalid recipient address format');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Validate amount
|
|
33
|
+
const amount = parseFloat(options.amount);
|
|
34
|
+
if (isNaN(amount) || amount <= 0) {
|
|
35
|
+
error('Invalid amount. Must be a positive number');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (options.mandate) {
|
|
40
|
+
error('Mandate payments are not yet implemented');
|
|
41
|
+
info('Use plob escrow create instead');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Confirm payment
|
|
46
|
+
if (!options.yes) {
|
|
47
|
+
console.log();
|
|
48
|
+
console.log('Payment details:');
|
|
49
|
+
console.log(' To: ', options.to);
|
|
50
|
+
console.log(' Amount: ', options.amount, 'USDC');
|
|
51
|
+
console.log(' Description: ', options.description);
|
|
52
|
+
console.log();
|
|
53
|
+
|
|
54
|
+
const confirmed = await confirm('Send this payment?');
|
|
55
|
+
if (!confirmed) {
|
|
56
|
+
info('Cancelled');
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
info(`Sending ${options.amount} USDC to ${formatAddress(options.to)}`);
|
|
62
|
+
|
|
63
|
+
const txHash = await withSpinner(
|
|
64
|
+
'Processing payment...',
|
|
65
|
+
async () => {
|
|
66
|
+
return await createEscrow(
|
|
67
|
+
options.to as `0x${string}`,
|
|
68
|
+
options.amount,
|
|
69
|
+
options.description
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (outputJSON({
|
|
75
|
+
txHash: txHash.toString(),
|
|
76
|
+
to: options.to,
|
|
77
|
+
amount: options.amount,
|
|
78
|
+
description: options.description,
|
|
79
|
+
}, options)) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
success('Payment sent successfully!');
|
|
84
|
+
info(`Transaction: ${txHash.toString()}`);
|
|
85
|
+
info(`Recipient: ${options.to}`);
|
|
86
|
+
info(`Amount: ${options.amount} USDC`);
|
|
87
|
+
console.log();
|
|
88
|
+
info('Note: Payment is held in escrow. Use `plob escrow release <id>` to release funds.');
|
|
89
|
+
} catch (err) {
|
|
90
|
+
error(`Failed to send payment: ${err}`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return cmd;
|
|
96
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { registerAgent, getAgentInfo } from '../lib/contracts';
|
|
3
|
+
import { getWalletAddress } from '../lib/wallet';
|
|
4
|
+
import { success, error, info, withSpinner, outputJSON } from '../lib/display';
|
|
5
|
+
import type { OutputOptions } from '../lib/types';
|
|
6
|
+
|
|
7
|
+
export function createRegisterCommand(): Command {
|
|
8
|
+
const cmd = new Command('register')
|
|
9
|
+
.description('Register agent identity on-chain')
|
|
10
|
+
.requiredOption('--name <name>', 'Agent name')
|
|
11
|
+
.requiredOption('--capabilities <capabilities>', 'Comma-separated capabilities (e.g., "code-review,bug-fix")')
|
|
12
|
+
.option('--json', 'Output as JSON')
|
|
13
|
+
.action(async (options: { name: string; capabilities: string } & OutputOptions) => {
|
|
14
|
+
try {
|
|
15
|
+
// Check if already registered
|
|
16
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
17
|
+
const agentInfo = await getAgentInfo(address);
|
|
18
|
+
|
|
19
|
+
if (agentInfo.registered) {
|
|
20
|
+
error('Agent is already registered');
|
|
21
|
+
info(`Current name: ${agentInfo.name}`);
|
|
22
|
+
info(`Token ID: ${agentInfo.tokenId.toString()}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Register
|
|
27
|
+
const txHash = await withSpinner(
|
|
28
|
+
'Registering agent on-chain...',
|
|
29
|
+
async () => {
|
|
30
|
+
return await registerAgent(options.name, options.capabilities);
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
// Get updated info
|
|
35
|
+
const updatedInfo = await getAgentInfo(address);
|
|
36
|
+
|
|
37
|
+
if (outputJSON({
|
|
38
|
+
txHash: txHash.toString(),
|
|
39
|
+
agentId: updatedInfo.tokenId.toString(),
|
|
40
|
+
name: updatedInfo.name,
|
|
41
|
+
address,
|
|
42
|
+
}, options)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
success('Agent registered successfully!');
|
|
47
|
+
info(`Name: ${updatedInfo.name}`);
|
|
48
|
+
info(`Agent ID: ${updatedInfo.tokenId.toString()}`);
|
|
49
|
+
info(`Transaction: ${txHash.toString()}`);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
error(`Failed to register agent: ${err}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return cmd;
|
|
57
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { getReputation, getAgentInfo } from '../lib/contracts';
|
|
3
|
+
import { error, outputJSON, printReputation, header } from '../lib/display';
|
|
4
|
+
import type { OutputOptions } from '../lib/types';
|
|
5
|
+
|
|
6
|
+
export function createReputationCommand(): Command {
|
|
7
|
+
const cmd = new Command('reputation')
|
|
8
|
+
.description('Check reputation score')
|
|
9
|
+
.argument('[address]', 'Address to check (default: your wallet)')
|
|
10
|
+
.option('--json', 'Output as JSON')
|
|
11
|
+
.action(async (addressArg: string | undefined, options: OutputOptions) => {
|
|
12
|
+
try {
|
|
13
|
+
let address: `0x${string}`;
|
|
14
|
+
|
|
15
|
+
if (addressArg) {
|
|
16
|
+
// Validate address format
|
|
17
|
+
if (!addressArg.startsWith('0x') || addressArg.length !== 42) {
|
|
18
|
+
error('Invalid address format');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
address = addressArg as `0x${string}`;
|
|
22
|
+
} else {
|
|
23
|
+
// Use wallet address
|
|
24
|
+
const { getWalletAddress } = await import('../lib/wallet');
|
|
25
|
+
address = getWalletAddress() as `0x${string}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Fetch reputation and agent info
|
|
29
|
+
const [reputation, agentInfo] = await Promise.all([
|
|
30
|
+
getReputation(address),
|
|
31
|
+
getAgentInfo(address),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const reputationScore = Number(reputation.score);
|
|
35
|
+
const trustVector = Number(reputation.trustVector);
|
|
36
|
+
|
|
37
|
+
if (outputJSON({
|
|
38
|
+
address,
|
|
39
|
+
name: agentInfo.name,
|
|
40
|
+
registered: agentInfo.registered,
|
|
41
|
+
reputation: {
|
|
42
|
+
score: reputationScore,
|
|
43
|
+
trustVector,
|
|
44
|
+
},
|
|
45
|
+
}, options)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Pretty output
|
|
50
|
+
header('Reputation Report');
|
|
51
|
+
console.log();
|
|
52
|
+
console.log('Address: ', address);
|
|
53
|
+
|
|
54
|
+
if (agentInfo.registered) {
|
|
55
|
+
console.log('Name: ', agentInfo.name);
|
|
56
|
+
console.log('Agent ID: ', agentInfo.tokenId.toString());
|
|
57
|
+
} else {
|
|
58
|
+
console.log('Status: ', 'Not registered');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log();
|
|
62
|
+
console.log('Reputation: ', printReputation(reputationScore));
|
|
63
|
+
console.log('Trust: ', trustVector);
|
|
64
|
+
console.log();
|
|
65
|
+
|
|
66
|
+
// Add interpretation
|
|
67
|
+
if (reputationScore >= 80) {
|
|
68
|
+
console.log('✅ Excellent reputation - highly trusted');
|
|
69
|
+
} else if (reputationScore >= 60) {
|
|
70
|
+
console.log('⚠️ Good reputation - generally trusted');
|
|
71
|
+
} else if (reputationScore >= 40) {
|
|
72
|
+
console.log('⚠️ Fair reputation - proceed with caution');
|
|
73
|
+
} else {
|
|
74
|
+
console.log('❌ Low reputation - high risk');
|
|
75
|
+
}
|
|
76
|
+
console.log();
|
|
77
|
+
} catch (err) {
|
|
78
|
+
error(`Failed to fetch reputation: ${err}`);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return cmd;
|
|
84
|
+
}
|