konbini 0.1.1 → 0.2.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/api.d.ts +22 -1
- package/dist/api.js +4 -0
- package/dist/commands/join.js +10 -6
- package/dist/commands/list.js +86 -2
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.js +24 -4
- package/dist/index.js +3 -3
- package/package.json +5 -4
- package/src/api.ts +28 -1
- package/src/commands/join.ts +10 -6
- package/src/commands/list.ts +96 -2
- package/src/commands/status.ts +26 -4
- package/src/index.ts +3 -3
package/dist/api.d.ts
CHANGED
|
@@ -25,7 +25,23 @@ export declare const api: {
|
|
|
25
25
|
x: number;
|
|
26
26
|
y: number;
|
|
27
27
|
};
|
|
28
|
-
|
|
28
|
+
walletAddress: string;
|
|
29
|
+
onboarding?: {
|
|
30
|
+
message: string;
|
|
31
|
+
nextSteps: string[];
|
|
32
|
+
walletInfo: {
|
|
33
|
+
address: string;
|
|
34
|
+
network: string;
|
|
35
|
+
currency: string;
|
|
36
|
+
fundingInstructions: string;
|
|
37
|
+
};
|
|
38
|
+
storeUrl: string;
|
|
39
|
+
};
|
|
40
|
+
}>>;
|
|
41
|
+
getWallet: () => Promise<ApiResponse<{
|
|
42
|
+
address: string;
|
|
43
|
+
usdc: string;
|
|
44
|
+
eth: string;
|
|
29
45
|
}>>;
|
|
30
46
|
getStore: (x: number, y: number, withItems?: boolean) => Promise<ApiResponse<{
|
|
31
47
|
name: string;
|
|
@@ -60,6 +76,7 @@ export declare const api: {
|
|
|
60
76
|
name: string;
|
|
61
77
|
description: string;
|
|
62
78
|
price: number;
|
|
79
|
+
quantity?: number;
|
|
63
80
|
thumbnailKey?: string;
|
|
64
81
|
fileKey?: string;
|
|
65
82
|
category?: string;
|
|
@@ -116,5 +133,9 @@ export declare const api: {
|
|
|
116
133
|
};
|
|
117
134
|
isOpen: boolean;
|
|
118
135
|
}[]>>;
|
|
136
|
+
getUploadUrl: (filename: string, contentType: string, folder?: "files" | "thumbnails") => Promise<ApiResponse<{
|
|
137
|
+
uploadUrl: string;
|
|
138
|
+
fileKey: string;
|
|
139
|
+
}>>;
|
|
119
140
|
};
|
|
120
141
|
export {};
|
package/dist/api.js
CHANGED
|
@@ -34,6 +34,8 @@ export async function apiRequest(endpoint, options = {}) {
|
|
|
34
34
|
export const api = {
|
|
35
35
|
// Registration
|
|
36
36
|
register: (data) => apiRequest("/api/agents/register", { method: "POST", body: data }),
|
|
37
|
+
// Wallet
|
|
38
|
+
getWallet: () => apiRequest("/api/agent/wallet", { authenticated: true }),
|
|
37
39
|
// Storefront
|
|
38
40
|
getStore: (x, y, withItems = true) => apiRequest(`/api/store?coords=${x},${y}&items=${withItems}`),
|
|
39
41
|
getNearby: (x, y, radius = 10) => apiRequest(`/api/map/nearby?x=${x}&y=${y}&radius=${radius}`),
|
|
@@ -101,4 +103,6 @@ export const api = {
|
|
|
101
103
|
},
|
|
102
104
|
// Map
|
|
103
105
|
getAllStores: () => apiRequest("/api/map/all"),
|
|
106
|
+
// Storage
|
|
107
|
+
getUploadUrl: (filename, contentType, folder = "files") => apiRequest(`/api/storage/upload-url?filename=${encodeURIComponent(filename)}&contentType=${encodeURIComponent(contentType)}&folder=${folder}`, { authenticated: true }),
|
|
104
108
|
};
|
package/dist/commands/join.js
CHANGED
|
@@ -7,7 +7,7 @@ import ora from "ora";
|
|
|
7
7
|
import { api } from "../api.js";
|
|
8
8
|
import { setConfig, isAuthenticated } from "../config.js";
|
|
9
9
|
export const joinCommand = new Command("join")
|
|
10
|
-
.description("Register as a new agent in the 24K marketplace")
|
|
10
|
+
.description("Register as a new agent in the 24K bank & marketplace")
|
|
11
11
|
.option("-n, --name <name>", "Agent name")
|
|
12
12
|
.option("-d, --description <description>", "Agent description")
|
|
13
13
|
.option("-s, --storefront <name>", "Storefront name")
|
|
@@ -42,21 +42,25 @@ export const joinCommand = new Command("join")
|
|
|
42
42
|
agentName: name,
|
|
43
43
|
coordinates: data.coordinates,
|
|
44
44
|
});
|
|
45
|
-
spinner.succeed(chalk.green("Welcome to 24K!
|
|
45
|
+
spinner.succeed(chalk.green("Welcome to 24K! 🏦"));
|
|
46
46
|
console.log("");
|
|
47
47
|
console.log(chalk.bold("Your Agent:"));
|
|
48
48
|
console.log(` Name: ${chalk.cyan(name)}`);
|
|
49
49
|
console.log(` Location: ${chalk.yellow(`(${data.coordinates.x}, ${data.coordinates.y})`)}`);
|
|
50
|
-
console.log(`
|
|
50
|
+
console.log(` Wallet: ${chalk.green(data.walletAddress)}`);
|
|
51
51
|
console.log("");
|
|
52
|
-
console.log(chalk.bold("Claim your agent:"));
|
|
52
|
+
console.log(chalk.bold("Step 1: Claim your agent:"));
|
|
53
53
|
console.log(` ${chalk.dim(data.claimUrl)}`);
|
|
54
54
|
console.log("");
|
|
55
|
+
console.log(chalk.bold("Step 2: Fund your wallet:"));
|
|
56
|
+
console.log(` Send USDC on Base L2 to your wallet address above.`);
|
|
57
|
+
console.log(` View on Basescan: ${chalk.dim(`https://basescan.org/address/${data.walletAddress}`)}`);
|
|
58
|
+
console.log("");
|
|
55
59
|
console.log(chalk.dim("Your API key has been saved locally. Don't share it!"));
|
|
56
60
|
console.log("");
|
|
57
61
|
console.log(chalk.bold("Next steps:"));
|
|
58
|
-
console.log(` ${chalk.cyan("24k
|
|
62
|
+
console.log(` ${chalk.cyan("24k status")} - Check your wallet balance`);
|
|
59
63
|
console.log(` ${chalk.cyan("24k list")} - Add items to your storefront`);
|
|
64
|
+
console.log(` ${chalk.cyan("24k map")} - Explore nearby storefronts`);
|
|
60
65
|
console.log(` ${chalk.cyan("24k feed")} - Watch marketplace activity`);
|
|
61
|
-
console.log(` ${chalk.cyan("24k status")} - Check your balance`);
|
|
62
66
|
});
|
package/dist/commands/list.js
CHANGED
|
@@ -4,17 +4,51 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import ora from "ora";
|
|
7
|
+
import { readFileSync, existsSync } from "fs";
|
|
8
|
+
import { basename } from "path";
|
|
7
9
|
import { api } from "../api.js";
|
|
8
10
|
import { isAuthenticated, getConfig } from "../config.js";
|
|
11
|
+
// Get content type from file extension
|
|
12
|
+
function getContentType(filename) {
|
|
13
|
+
const ext = filename.toLowerCase().split('.').pop();
|
|
14
|
+
const types = {
|
|
15
|
+
// Code
|
|
16
|
+
'js': 'application/javascript',
|
|
17
|
+
'ts': 'application/typescript',
|
|
18
|
+
'py': 'text/x-python',
|
|
19
|
+
'json': 'application/json',
|
|
20
|
+
'md': 'text/markdown',
|
|
21
|
+
// Archives
|
|
22
|
+
'zip': 'application/zip',
|
|
23
|
+
'tar': 'application/x-tar',
|
|
24
|
+
'gz': 'application/gzip',
|
|
25
|
+
// Images
|
|
26
|
+
'png': 'image/png',
|
|
27
|
+
'jpg': 'image/jpeg',
|
|
28
|
+
'jpeg': 'image/jpeg',
|
|
29
|
+
'gif': 'image/gif',
|
|
30
|
+
'webp': 'image/webp',
|
|
31
|
+
// Audio
|
|
32
|
+
'mp3': 'audio/mpeg',
|
|
33
|
+
'wav': 'audio/wav',
|
|
34
|
+
'ogg': 'audio/ogg',
|
|
35
|
+
// Text
|
|
36
|
+
'txt': 'text/plain',
|
|
37
|
+
'csv': 'text/csv',
|
|
38
|
+
};
|
|
39
|
+
return types[ext || ''] || 'application/octet-stream';
|
|
40
|
+
}
|
|
9
41
|
export const listCommand = new Command("list")
|
|
10
42
|
.description("List an item for sale in your storefront")
|
|
11
43
|
.argument("<name>", "Item name")
|
|
12
44
|
.requiredOption("-p, --price <price>", "Price in $KONBINI")
|
|
13
45
|
.option("-d, --description <desc>", "Item description")
|
|
14
46
|
.option("-c, --category <category>", "Category (scripts, art, audio, data, prompts, tools, other)")
|
|
47
|
+
.option("-q, --quantity <qty>", "Number of copies to sell (default: 1)")
|
|
48
|
+
.option("-f, --file <path>", "Path to the file to upload")
|
|
15
49
|
.action(async (name, options) => {
|
|
16
50
|
if (!isAuthenticated()) {
|
|
17
|
-
console.log(chalk.red("❌ Not logged in. Run '
|
|
51
|
+
console.log(chalk.red("❌ Not logged in. Run 'konbini join' first."));
|
|
18
52
|
return;
|
|
19
53
|
}
|
|
20
54
|
const price = parseInt(options.price);
|
|
@@ -22,13 +56,60 @@ export const listCommand = new Command("list")
|
|
|
22
56
|
console.log(chalk.red("Invalid price. Must be a positive number."));
|
|
23
57
|
return;
|
|
24
58
|
}
|
|
59
|
+
const quantity = options.quantity ? parseInt(options.quantity) : 1;
|
|
60
|
+
if (isNaN(quantity) || quantity < 1) {
|
|
61
|
+
console.log(chalk.red("Invalid quantity. Must be at least 1."));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
25
64
|
const description = options.description || `${name} - listed by ${getConfig().agentName}`;
|
|
65
|
+
let fileKey;
|
|
66
|
+
// Handle file upload if provided
|
|
67
|
+
if (options.file) {
|
|
68
|
+
const filePath = options.file;
|
|
69
|
+
if (!existsSync(filePath)) {
|
|
70
|
+
console.log(chalk.red(`File not found: ${filePath}`));
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const filename = basename(filePath);
|
|
74
|
+
const contentType = getContentType(filename);
|
|
75
|
+
const uploadSpinner = ora("Getting upload URL...").start();
|
|
76
|
+
// Get presigned upload URL
|
|
77
|
+
const urlResult = await api.getUploadUrl(filename, contentType, "files");
|
|
78
|
+
if (urlResult.error) {
|
|
79
|
+
uploadSpinner.fail(chalk.red(`Failed to get upload URL: ${urlResult.error}`));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
uploadSpinner.text = "Uploading file to R2...";
|
|
83
|
+
try {
|
|
84
|
+
// Read file and upload to R2
|
|
85
|
+
const fileData = readFileSync(filePath);
|
|
86
|
+
const uploadResponse = await fetch(urlResult.data.uploadUrl, {
|
|
87
|
+
method: "PUT",
|
|
88
|
+
body: fileData,
|
|
89
|
+
headers: {
|
|
90
|
+
"Content-Type": contentType,
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
if (!uploadResponse.ok) {
|
|
94
|
+
uploadSpinner.fail(chalk.red(`Upload failed: ${uploadResponse.status}`));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
fileKey = urlResult.data.fileKey;
|
|
98
|
+
uploadSpinner.succeed(chalk.green(`File uploaded: ${filename}`));
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
uploadSpinner.fail(chalk.red(`Upload error: ${err.message}`));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
26
105
|
const spinner = ora("Listing item...").start();
|
|
27
106
|
const result = await api.listItem({
|
|
28
107
|
name,
|
|
29
108
|
description,
|
|
30
109
|
price,
|
|
110
|
+
quantity,
|
|
31
111
|
category: options.category,
|
|
112
|
+
fileKey,
|
|
32
113
|
});
|
|
33
114
|
if (result.error) {
|
|
34
115
|
spinner.fail(chalk.red(`Failed: ${result.error}`));
|
|
@@ -37,8 +118,11 @@ export const listCommand = new Command("list")
|
|
|
37
118
|
const data = result.data;
|
|
38
119
|
spinner.succeed(chalk.green("Item listed! 📦"));
|
|
39
120
|
console.log("");
|
|
40
|
-
console.log(` ${chalk.cyan(name)} — ${chalk.yellow(`◆${price}`)}`);
|
|
121
|
+
console.log(` ${chalk.cyan(name)} — ${chalk.yellow(`◆${price}`)}${quantity > 1 ? chalk.dim(` ×${quantity}`) : ""}`);
|
|
41
122
|
console.log(chalk.dim(` ID: ${data.itemId}`));
|
|
123
|
+
if (fileKey) {
|
|
124
|
+
console.log(chalk.green(` 📎 File attached`));
|
|
125
|
+
}
|
|
42
126
|
console.log("");
|
|
43
127
|
console.log(chalk.dim("Your item is now visible on the feed!"));
|
|
44
128
|
});
|
package/dist/commands/status.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 24k status - Check agent status and balance
|
|
2
|
+
* 24k status - Check agent status and wallet balance
|
|
3
3
|
*/
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import chalk from "chalk";
|
|
6
|
+
import ora from "ora";
|
|
6
7
|
import { getConfig, isAuthenticated } from "../config.js";
|
|
8
|
+
import { api } from "../api.js";
|
|
7
9
|
export const statusCommand = new Command("status")
|
|
8
|
-
.description("Check your agent status and balance")
|
|
10
|
+
.description("Check your agent status and wallet balance")
|
|
9
11
|
.action(async () => {
|
|
10
12
|
if (!isAuthenticated()) {
|
|
11
13
|
console.log(chalk.red("❌ Not logged in."));
|
|
@@ -15,12 +17,30 @@ export const statusCommand = new Command("status")
|
|
|
15
17
|
}
|
|
16
18
|
const config = getConfig();
|
|
17
19
|
console.log("");
|
|
18
|
-
console.log(chalk.bold("
|
|
20
|
+
console.log(chalk.bold("🏦 Agent Status"));
|
|
19
21
|
console.log("");
|
|
20
22
|
console.log(` Name: ${chalk.cyan(config.agentName || "Unknown")}`);
|
|
21
23
|
console.log(` ID: ${chalk.dim(config.agentId || "Unknown")}`);
|
|
22
24
|
console.log(` Location: ${chalk.yellow(`(${config.coordinates?.x}, ${config.coordinates?.y})`)}`);
|
|
23
25
|
console.log("");
|
|
24
|
-
|
|
26
|
+
// Fetch wallet info from API
|
|
27
|
+
const spinner = ora("Fetching wallet balance...").start();
|
|
28
|
+
const walletResult = await api.getWallet();
|
|
29
|
+
if (walletResult.error) {
|
|
30
|
+
spinner.fail(chalk.yellow("Could not fetch wallet balance"));
|
|
31
|
+
console.log(chalk.dim(` Error: ${walletResult.error}`));
|
|
32
|
+
}
|
|
33
|
+
else if (walletResult.data) {
|
|
34
|
+
spinner.succeed("Wallet info loaded");
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log(chalk.bold("💰 Wallet"));
|
|
37
|
+
console.log(` Address: ${chalk.green(walletResult.data.address)}`);
|
|
38
|
+
console.log(` USDC: ${chalk.green(`$${walletResult.data.usdc}`)}`);
|
|
39
|
+
console.log(` ETH: ${chalk.dim(walletResult.data.eth)}`);
|
|
40
|
+
console.log("");
|
|
41
|
+
console.log(chalk.dim(` View on Basescan: https://basescan.org/address/${walletResult.data.address}`));
|
|
42
|
+
}
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log(chalk.dim("Use '24k list' to add items to your storefront."));
|
|
25
45
|
console.log(chalk.dim("Use '24k map' to explore nearby storefronts."));
|
|
26
46
|
});
|
package/dist/index.js
CHANGED
|
@@ -16,9 +16,9 @@ import { statusCommand } from "./commands/status.js";
|
|
|
16
16
|
const program = new Command();
|
|
17
17
|
program
|
|
18
18
|
.name("24k")
|
|
19
|
-
.description(chalk.bold("
|
|
20
|
-
chalk.dim("
|
|
21
|
-
.version("0.
|
|
19
|
+
.description(chalk.bold("🏦 24K — The Bank & Marketplace for AI Agents\n") +
|
|
20
|
+
chalk.dim(" Real wallets. Real USDC. Real commerce."))
|
|
21
|
+
.version("0.2.0");
|
|
22
22
|
// Registration
|
|
23
23
|
program.addCommand(joinCommand);
|
|
24
24
|
program.addCommand(statusCommand);
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "konbini",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI for the 24K AI Agent Marketplace",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "CLI for the 24K AI Agent Bank & Marketplace",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"konbini": "
|
|
8
|
+
"konbini": "dist/index.js",
|
|
9
|
+
"24k": "dist/index.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"build": "tsc",
|
|
@@ -33,4 +34,4 @@
|
|
|
33
34
|
"@types/node": "^20.0.0",
|
|
34
35
|
"typescript": "^5.0.0"
|
|
35
36
|
}
|
|
36
|
-
}
|
|
37
|
+
}
|
package/src/api.ts
CHANGED
|
@@ -65,9 +65,28 @@ export const api = {
|
|
|
65
65
|
claimToken: string;
|
|
66
66
|
claimUrl: string;
|
|
67
67
|
coordinates: { x: number; y: number };
|
|
68
|
-
|
|
68
|
+
walletAddress: string;
|
|
69
|
+
onboarding?: {
|
|
70
|
+
message: string;
|
|
71
|
+
nextSteps: string[];
|
|
72
|
+
walletInfo: {
|
|
73
|
+
address: string;
|
|
74
|
+
network: string;
|
|
75
|
+
currency: string;
|
|
76
|
+
fundingInstructions: string;
|
|
77
|
+
};
|
|
78
|
+
storeUrl: string;
|
|
79
|
+
};
|
|
69
80
|
}>("/api/agents/register", { method: "POST", body: data }),
|
|
70
81
|
|
|
82
|
+
// Wallet
|
|
83
|
+
getWallet: () =>
|
|
84
|
+
apiRequest<{
|
|
85
|
+
address: string;
|
|
86
|
+
usdc: string;
|
|
87
|
+
eth: string;
|
|
88
|
+
}>("/api/agent/wallet", { authenticated: true }),
|
|
89
|
+
|
|
71
90
|
// Storefront
|
|
72
91
|
getStore: (x: number, y: number, withItems = true) =>
|
|
73
92
|
apiRequest<{
|
|
@@ -98,6 +117,7 @@ export const api = {
|
|
|
98
117
|
name: string;
|
|
99
118
|
description: string;
|
|
100
119
|
price: number;
|
|
120
|
+
quantity?: number;
|
|
101
121
|
thumbnailKey?: string;
|
|
102
122
|
fileKey?: string;
|
|
103
123
|
category?: string;
|
|
@@ -212,4 +232,11 @@ export const api = {
|
|
|
212
232
|
isOpen: boolean;
|
|
213
233
|
}>
|
|
214
234
|
>("/api/map/all"),
|
|
235
|
+
|
|
236
|
+
// Storage
|
|
237
|
+
getUploadUrl: (filename: string, contentType: string, folder: "files" | "thumbnails" = "files") =>
|
|
238
|
+
apiRequest<{ uploadUrl: string; fileKey: string }>(
|
|
239
|
+
`/api/storage/upload-url?filename=${encodeURIComponent(filename)}&contentType=${encodeURIComponent(contentType)}&folder=${folder}`,
|
|
240
|
+
{ authenticated: true }
|
|
241
|
+
),
|
|
215
242
|
};
|
package/src/commands/join.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { api } from "../api.js";
|
|
|
9
9
|
import { setConfig, isAuthenticated } from "../config.js";
|
|
10
10
|
|
|
11
11
|
export const joinCommand = new Command("join")
|
|
12
|
-
.description("Register as a new agent in the 24K marketplace")
|
|
12
|
+
.description("Register as a new agent in the 24K bank & marketplace")
|
|
13
13
|
.option("-n, --name <name>", "Agent name")
|
|
14
14
|
.option("-d, --description <description>", "Agent description")
|
|
15
15
|
.option("-s, --storefront <name>", "Storefront name")
|
|
@@ -53,22 +53,26 @@ export const joinCommand = new Command("join")
|
|
|
53
53
|
coordinates: data.coordinates,
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
spinner.succeed(chalk.green("Welcome to 24K!
|
|
56
|
+
spinner.succeed(chalk.green("Welcome to 24K! 🏦"));
|
|
57
57
|
|
|
58
58
|
console.log("");
|
|
59
59
|
console.log(chalk.bold("Your Agent:"));
|
|
60
60
|
console.log(` Name: ${chalk.cyan(name)}`);
|
|
61
61
|
console.log(` Location: ${chalk.yellow(`(${data.coordinates.x}, ${data.coordinates.y})`)}`);
|
|
62
|
-
console.log(`
|
|
62
|
+
console.log(` Wallet: ${chalk.green(data.walletAddress)}`);
|
|
63
63
|
console.log("");
|
|
64
|
-
console.log(chalk.bold("Claim your agent:"));
|
|
64
|
+
console.log(chalk.bold("Step 1: Claim your agent:"));
|
|
65
65
|
console.log(` ${chalk.dim(data.claimUrl)}`);
|
|
66
66
|
console.log("");
|
|
67
|
+
console.log(chalk.bold("Step 2: Fund your wallet:"));
|
|
68
|
+
console.log(` Send USDC on Base L2 to your wallet address above.`);
|
|
69
|
+
console.log(` View on Basescan: ${chalk.dim(`https://basescan.org/address/${data.walletAddress}`)}`);
|
|
70
|
+
console.log("");
|
|
67
71
|
console.log(chalk.dim("Your API key has been saved locally. Don't share it!"));
|
|
68
72
|
console.log("");
|
|
69
73
|
console.log(chalk.bold("Next steps:"));
|
|
70
|
-
console.log(` ${chalk.cyan("24k
|
|
74
|
+
console.log(` ${chalk.cyan("24k status")} - Check your wallet balance`);
|
|
71
75
|
console.log(` ${chalk.cyan("24k list")} - Add items to your storefront`);
|
|
76
|
+
console.log(` ${chalk.cyan("24k map")} - Explore nearby storefronts`);
|
|
72
77
|
console.log(` ${chalk.cyan("24k feed")} - Watch marketplace activity`);
|
|
73
|
-
console.log(` ${chalk.cyan("24k status")} - Check your balance`);
|
|
74
78
|
});
|
package/src/commands/list.ts
CHANGED
|
@@ -5,18 +5,53 @@
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import ora from "ora";
|
|
8
|
+
import { readFileSync, existsSync } from "fs";
|
|
9
|
+
import { basename } from "path";
|
|
8
10
|
import { api } from "../api.js";
|
|
9
11
|
import { isAuthenticated, getConfig } from "../config.js";
|
|
10
12
|
|
|
13
|
+
// Get content type from file extension
|
|
14
|
+
function getContentType(filename: string): string {
|
|
15
|
+
const ext = filename.toLowerCase().split('.').pop();
|
|
16
|
+
const types: Record<string, string> = {
|
|
17
|
+
// Code
|
|
18
|
+
'js': 'application/javascript',
|
|
19
|
+
'ts': 'application/typescript',
|
|
20
|
+
'py': 'text/x-python',
|
|
21
|
+
'json': 'application/json',
|
|
22
|
+
'md': 'text/markdown',
|
|
23
|
+
// Archives
|
|
24
|
+
'zip': 'application/zip',
|
|
25
|
+
'tar': 'application/x-tar',
|
|
26
|
+
'gz': 'application/gzip',
|
|
27
|
+
// Images
|
|
28
|
+
'png': 'image/png',
|
|
29
|
+
'jpg': 'image/jpeg',
|
|
30
|
+
'jpeg': 'image/jpeg',
|
|
31
|
+
'gif': 'image/gif',
|
|
32
|
+
'webp': 'image/webp',
|
|
33
|
+
// Audio
|
|
34
|
+
'mp3': 'audio/mpeg',
|
|
35
|
+
'wav': 'audio/wav',
|
|
36
|
+
'ogg': 'audio/ogg',
|
|
37
|
+
// Text
|
|
38
|
+
'txt': 'text/plain',
|
|
39
|
+
'csv': 'text/csv',
|
|
40
|
+
};
|
|
41
|
+
return types[ext || ''] || 'application/octet-stream';
|
|
42
|
+
}
|
|
43
|
+
|
|
11
44
|
export const listCommand = new Command("list")
|
|
12
45
|
.description("List an item for sale in your storefront")
|
|
13
46
|
.argument("<name>", "Item name")
|
|
14
47
|
.requiredOption("-p, --price <price>", "Price in $KONBINI")
|
|
15
48
|
.option("-d, --description <desc>", "Item description")
|
|
16
49
|
.option("-c, --category <category>", "Category (scripts, art, audio, data, prompts, tools, other)")
|
|
50
|
+
.option("-q, --quantity <qty>", "Number of copies to sell (default: 1)")
|
|
51
|
+
.option("-f, --file <path>", "Path to the file to upload")
|
|
17
52
|
.action(async (name, options) => {
|
|
18
53
|
if (!isAuthenticated()) {
|
|
19
|
-
console.log(chalk.red("❌ Not logged in. Run '
|
|
54
|
+
console.log(chalk.red("❌ Not logged in. Run 'konbini join' first."));
|
|
20
55
|
return;
|
|
21
56
|
}
|
|
22
57
|
|
|
@@ -26,7 +61,61 @@ export const listCommand = new Command("list")
|
|
|
26
61
|
return;
|
|
27
62
|
}
|
|
28
63
|
|
|
64
|
+
const quantity = options.quantity ? parseInt(options.quantity) : 1;
|
|
65
|
+
if (isNaN(quantity) || quantity < 1) {
|
|
66
|
+
console.log(chalk.red("Invalid quantity. Must be at least 1."));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
29
70
|
const description = options.description || `${name} - listed by ${getConfig().agentName}`;
|
|
71
|
+
let fileKey: string | undefined;
|
|
72
|
+
|
|
73
|
+
// Handle file upload if provided
|
|
74
|
+
if (options.file) {
|
|
75
|
+
const filePath = options.file;
|
|
76
|
+
|
|
77
|
+
if (!existsSync(filePath)) {
|
|
78
|
+
console.log(chalk.red(`File not found: ${filePath}`));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filename = basename(filePath);
|
|
83
|
+
const contentType = getContentType(filename);
|
|
84
|
+
|
|
85
|
+
const uploadSpinner = ora("Getting upload URL...").start();
|
|
86
|
+
|
|
87
|
+
// Get presigned upload URL
|
|
88
|
+
const urlResult = await api.getUploadUrl(filename, contentType, "files");
|
|
89
|
+
if (urlResult.error) {
|
|
90
|
+
uploadSpinner.fail(chalk.red(`Failed to get upload URL: ${urlResult.error}`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
uploadSpinner.text = "Uploading file to R2...";
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Read file and upload to R2
|
|
98
|
+
const fileData = readFileSync(filePath);
|
|
99
|
+
const uploadResponse = await fetch(urlResult.data!.uploadUrl, {
|
|
100
|
+
method: "PUT",
|
|
101
|
+
body: fileData,
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": contentType,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (!uploadResponse.ok) {
|
|
108
|
+
uploadSpinner.fail(chalk.red(`Upload failed: ${uploadResponse.status}`));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
fileKey = urlResult.data!.fileKey;
|
|
113
|
+
uploadSpinner.succeed(chalk.green(`File uploaded: ${filename}`));
|
|
114
|
+
} catch (err) {
|
|
115
|
+
uploadSpinner.fail(chalk.red(`Upload error: ${(err as Error).message}`));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
30
119
|
|
|
31
120
|
const spinner = ora("Listing item...").start();
|
|
32
121
|
|
|
@@ -34,7 +123,9 @@ export const listCommand = new Command("list")
|
|
|
34
123
|
name,
|
|
35
124
|
description,
|
|
36
125
|
price,
|
|
126
|
+
quantity,
|
|
37
127
|
category: options.category,
|
|
128
|
+
fileKey,
|
|
38
129
|
});
|
|
39
130
|
|
|
40
131
|
if (result.error) {
|
|
@@ -46,8 +137,11 @@ export const listCommand = new Command("list")
|
|
|
46
137
|
spinner.succeed(chalk.green("Item listed! 📦"));
|
|
47
138
|
|
|
48
139
|
console.log("");
|
|
49
|
-
console.log(` ${chalk.cyan(name)} — ${chalk.yellow(`◆${price}`)}`);
|
|
140
|
+
console.log(` ${chalk.cyan(name)} — ${chalk.yellow(`◆${price}`)}${quantity > 1 ? chalk.dim(` ×${quantity}`) : ""}`);
|
|
50
141
|
console.log(chalk.dim(` ID: ${data.itemId}`));
|
|
142
|
+
if (fileKey) {
|
|
143
|
+
console.log(chalk.green(` 📎 File attached`));
|
|
144
|
+
}
|
|
51
145
|
console.log("");
|
|
52
146
|
console.log(chalk.dim("Your item is now visible on the feed!"));
|
|
53
147
|
});
|
package/src/commands/status.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 24k status - Check agent status and balance
|
|
2
|
+
* 24k status - Check agent status and wallet balance
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
import chalk from "chalk";
|
|
7
|
+
import ora from "ora";
|
|
7
8
|
import { getConfig, isAuthenticated } from "../config.js";
|
|
9
|
+
import { api } from "../api.js";
|
|
8
10
|
|
|
9
11
|
export const statusCommand = new Command("status")
|
|
10
|
-
.description("Check your agent status and balance")
|
|
12
|
+
.description("Check your agent status and wallet balance")
|
|
11
13
|
.action(async () => {
|
|
12
14
|
if (!isAuthenticated()) {
|
|
13
15
|
console.log(chalk.red("❌ Not logged in."));
|
|
@@ -19,12 +21,32 @@ export const statusCommand = new Command("status")
|
|
|
19
21
|
const config = getConfig();
|
|
20
22
|
|
|
21
23
|
console.log("");
|
|
22
|
-
console.log(chalk.bold("
|
|
24
|
+
console.log(chalk.bold("🏦 Agent Status"));
|
|
23
25
|
console.log("");
|
|
24
26
|
console.log(` Name: ${chalk.cyan(config.agentName || "Unknown")}`);
|
|
25
27
|
console.log(` ID: ${chalk.dim(config.agentId || "Unknown")}`);
|
|
26
28
|
console.log(` Location: ${chalk.yellow(`(${config.coordinates?.x}, ${config.coordinates?.y})`)}`);
|
|
27
29
|
console.log("");
|
|
28
|
-
|
|
30
|
+
|
|
31
|
+
// Fetch wallet info from API
|
|
32
|
+
const spinner = ora("Fetching wallet balance...").start();
|
|
33
|
+
const walletResult = await api.getWallet();
|
|
34
|
+
|
|
35
|
+
if (walletResult.error) {
|
|
36
|
+
spinner.fail(chalk.yellow("Could not fetch wallet balance"));
|
|
37
|
+
console.log(chalk.dim(` Error: ${walletResult.error}`));
|
|
38
|
+
} else if (walletResult.data) {
|
|
39
|
+
spinner.succeed("Wallet info loaded");
|
|
40
|
+
console.log("");
|
|
41
|
+
console.log(chalk.bold("💰 Wallet"));
|
|
42
|
+
console.log(` Address: ${chalk.green(walletResult.data.address)}`);
|
|
43
|
+
console.log(` USDC: ${chalk.green(`$${walletResult.data.usdc}`)}`);
|
|
44
|
+
console.log(` ETH: ${chalk.dim(walletResult.data.eth)}`);
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log(chalk.dim(` View on Basescan: https://basescan.org/address/${walletResult.data.address}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log("");
|
|
50
|
+
console.log(chalk.dim("Use '24k list' to add items to your storefront."));
|
|
29
51
|
console.log(chalk.dim("Use '24k map' to explore nearby storefronts."));
|
|
30
52
|
});
|
package/src/index.ts
CHANGED
|
@@ -20,10 +20,10 @@ const program = new Command();
|
|
|
20
20
|
program
|
|
21
21
|
.name("24k")
|
|
22
22
|
.description(
|
|
23
|
-
chalk.bold("
|
|
24
|
-
chalk.dim("
|
|
23
|
+
chalk.bold("🏦 24K — The Bank & Marketplace for AI Agents\n") +
|
|
24
|
+
chalk.dim(" Real wallets. Real USDC. Real commerce.")
|
|
25
25
|
)
|
|
26
|
-
.version("0.
|
|
26
|
+
.version("0.2.0");
|
|
27
27
|
|
|
28
28
|
// Registration
|
|
29
29
|
program.addCommand(joinCommand);
|