plexmint 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/index.js +522 -494
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -2,32 +2,95 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import
|
|
5
|
+
import chalk10 from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/lib/config.ts
|
|
8
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
var CONFIG_DIR = join(homedir(), ".plexmint");
|
|
12
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
13
|
+
function ensureConfigDir() {
|
|
14
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
15
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function readConfig() {
|
|
19
|
+
try {
|
|
20
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
const raw = readFileSync(CONFIG_FILE, "utf-8");
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
} catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function writeConfig(config) {
|
|
30
|
+
ensureConfigDir();
|
|
31
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
32
|
+
}
|
|
33
|
+
function getApiKey() {
|
|
34
|
+
return readConfig().apiKey;
|
|
35
|
+
}
|
|
36
|
+
function setAuth(apiKey, email, name) {
|
|
37
|
+
writeConfig({ apiKey, email, name });
|
|
38
|
+
}
|
|
39
|
+
function clearAuth() {
|
|
40
|
+
writeConfig({});
|
|
41
|
+
}
|
|
42
|
+
function isAuthenticated() {
|
|
43
|
+
return !!readConfig().apiKey;
|
|
44
|
+
}
|
|
45
|
+
function getConfigPath() {
|
|
46
|
+
return CONFIG_FILE;
|
|
47
|
+
}
|
|
6
48
|
|
|
7
|
-
// src/
|
|
49
|
+
// src/lib/ui.ts
|
|
8
50
|
import chalk from "chalk";
|
|
51
|
+
var mint = chalk.hex("#00D68F");
|
|
52
|
+
var mintBold = chalk.hex("#00D68F").bold;
|
|
53
|
+
var BANNER = `
|
|
54
|
+
${chalk.bold.white("plex")}${mintBold("mint")} ${chalk.gray("\u2014 AI prompts that evolve")}
|
|
55
|
+
${chalk.gray("https://plexmint.com")}
|
|
56
|
+
`;
|
|
57
|
+
function requireAuth() {
|
|
58
|
+
if (!isAuthenticated()) {
|
|
59
|
+
console.log("");
|
|
60
|
+
console.log(chalk.yellow(" Not logged in."));
|
|
61
|
+
console.log(
|
|
62
|
+
chalk.gray(" Run ") + chalk.white("plexmint setup") + chalk.gray(" to create an account, or ") + chalk.white("plexmint login") + chalk.gray(" to sign in.\n")
|
|
63
|
+
);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function formatPrice(price) {
|
|
68
|
+
const n = typeof price === "string" ? parseFloat(price) : price;
|
|
69
|
+
return `$${n.toFixed(2)}`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/commands/setup.ts
|
|
73
|
+
import chalk2 from "chalk";
|
|
9
74
|
import ora from "ora";
|
|
10
|
-
import
|
|
75
|
+
import inquirer from "inquirer";
|
|
11
76
|
|
|
12
77
|
// src/lib/api.ts
|
|
13
|
-
var
|
|
78
|
+
var BASE_URL = "https://plexmint.com/api/v1";
|
|
14
79
|
var PlexMintClient = class {
|
|
15
|
-
baseUrl;
|
|
16
80
|
apiKey;
|
|
17
|
-
constructor(apiKey
|
|
81
|
+
constructor(apiKey) {
|
|
18
82
|
this.apiKey = apiKey;
|
|
19
|
-
this.baseUrl = baseUrl || DEFAULT_BASE_URL;
|
|
20
83
|
}
|
|
21
84
|
async request(path, options = {}) {
|
|
22
85
|
const headers = {
|
|
23
86
|
"Content-Type": "application/json",
|
|
24
|
-
"User-Agent": "plexmint-cli/0.
|
|
87
|
+
"User-Agent": "plexmint-cli/0.2.0",
|
|
25
88
|
...options.headers
|
|
26
89
|
};
|
|
27
90
|
if (this.apiKey) {
|
|
28
91
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
29
92
|
}
|
|
30
|
-
const res = await fetch(`${
|
|
93
|
+
const res = await fetch(`${BASE_URL}${path}`, {
|
|
31
94
|
...options,
|
|
32
95
|
headers
|
|
33
96
|
});
|
|
@@ -42,31 +105,42 @@ var PlexMintClient = class {
|
|
|
42
105
|
}
|
|
43
106
|
return json;
|
|
44
107
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
108
|
+
// ─── Auth ───
|
|
109
|
+
async register(data) {
|
|
110
|
+
return this.request("/register", {
|
|
111
|
+
method: "POST",
|
|
112
|
+
body: JSON.stringify(data)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async verify(email, code) {
|
|
116
|
+
return this.request("/verify", {
|
|
117
|
+
method: "POST",
|
|
118
|
+
body: JSON.stringify({ email, code })
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async login(email, password) {
|
|
122
|
+
return this.request("/login", {
|
|
123
|
+
method: "POST",
|
|
124
|
+
body: JSON.stringify({ email, password })
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
// ─── Prompts ───
|
|
48
128
|
async browse(options = {}) {
|
|
49
129
|
const params = new URLSearchParams();
|
|
50
|
-
if (options.query) params.set("
|
|
130
|
+
if (options.query) params.set("search", options.query);
|
|
51
131
|
if (options.category) params.set("category", options.category);
|
|
52
|
-
if (options.model) params.set("model", options.model);
|
|
53
132
|
if (options.sort) params.set("sort", options.sort);
|
|
54
133
|
if (options.limit) params.set("limit", options.limit.toString());
|
|
55
134
|
if (options.offset) params.set("offset", options.offset.toString());
|
|
56
135
|
const qs = params.toString();
|
|
57
136
|
return this.request(`/prompts${qs ? `?${qs}` : ""}`);
|
|
58
137
|
}
|
|
59
|
-
/**
|
|
60
|
-
* Get a single prompt by ticker
|
|
61
|
-
*/
|
|
62
138
|
async getPrompt(ticker) {
|
|
63
139
|
return this.request(
|
|
64
|
-
`/prompts
|
|
140
|
+
`/prompts/${encodeURIComponent(ticker.toUpperCase())}`
|
|
65
141
|
);
|
|
66
142
|
}
|
|
67
|
-
|
|
68
|
-
* Initiate a purchase (requires API key)
|
|
69
|
-
*/
|
|
143
|
+
// ─── Commerce ───
|
|
70
144
|
async purchase(ticker) {
|
|
71
145
|
if (!this.apiKey) {
|
|
72
146
|
throw new PlexMintApiError(
|
|
@@ -77,13 +151,13 @@ var PlexMintClient = class {
|
|
|
77
151
|
}
|
|
78
152
|
return this.request("/purchase", {
|
|
79
153
|
method: "POST",
|
|
80
|
-
body: JSON.stringify({
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
ticker: ticker.toUpperCase(),
|
|
156
|
+
payment_method: "wallet"
|
|
157
|
+
})
|
|
81
158
|
});
|
|
82
159
|
}
|
|
83
|
-
|
|
84
|
-
* Get prompt content (requires purchase)
|
|
85
|
-
*/
|
|
86
|
-
async getContent(ticker) {
|
|
160
|
+
async getWallet() {
|
|
87
161
|
if (!this.apiKey) {
|
|
88
162
|
throw new PlexMintApiError(
|
|
89
163
|
"Authentication required. Run: plexmint login",
|
|
@@ -91,18 +165,17 @@ var PlexMintClient = class {
|
|
|
91
165
|
{}
|
|
92
166
|
);
|
|
93
167
|
}
|
|
94
|
-
return this.request(
|
|
95
|
-
`/library/${encodeURIComponent(ticker.toUpperCase())}/content`
|
|
96
|
-
);
|
|
168
|
+
return this.request("/wallet");
|
|
97
169
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
170
|
+
async getLibrary() {
|
|
171
|
+
if (!this.apiKey) {
|
|
172
|
+
throw new PlexMintApiError(
|
|
173
|
+
"Authentication required. Run: plexmint login",
|
|
174
|
+
401,
|
|
175
|
+
{}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return this.request("/library");
|
|
106
179
|
}
|
|
107
180
|
};
|
|
108
181
|
var PlexMintApiError = class extends Error {
|
|
@@ -116,252 +189,307 @@ var PlexMintApiError = class extends Error {
|
|
|
116
189
|
}
|
|
117
190
|
};
|
|
118
191
|
|
|
119
|
-
// src/
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
192
|
+
// src/commands/setup.ts
|
|
193
|
+
async function setupCommand() {
|
|
194
|
+
console.log("");
|
|
195
|
+
console.log(mintBold(" PlexMint Setup"));
|
|
196
|
+
console.log(chalk2.gray(" Create your account and start browsing AI prompts."));
|
|
197
|
+
console.log("");
|
|
198
|
+
const answers = await inquirer.prompt([
|
|
199
|
+
{
|
|
200
|
+
type: "input",
|
|
201
|
+
name: "name",
|
|
202
|
+
message: "Your name:",
|
|
203
|
+
validate: (v) => v.trim().length > 0 || "Name is required."
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: "input",
|
|
207
|
+
name: "email",
|
|
208
|
+
message: "Email:",
|
|
209
|
+
validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email address."
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: "password",
|
|
213
|
+
name: "password",
|
|
214
|
+
message: "Password:",
|
|
215
|
+
mask: "*",
|
|
216
|
+
validate: (v) => v.length >= 8 || "Password must be at least 8 characters."
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: "password",
|
|
220
|
+
name: "confirmPassword",
|
|
221
|
+
message: "Confirm password:",
|
|
222
|
+
mask: "*",
|
|
223
|
+
validate: (v, a) => {
|
|
224
|
+
if (!a) return "Confirm your password.";
|
|
225
|
+
return v === a.password || "Passwords do not match.";
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: "input",
|
|
230
|
+
name: "tickerPrefix",
|
|
231
|
+
message: "Ticker prefix (2-5 uppercase letters, e.g. NUE):",
|
|
232
|
+
transformer: (v) => v.toUpperCase(),
|
|
233
|
+
validate: (v) => /^[A-Z]{2,5}$/i.test(v.trim()) || "Enter 2-5 letters (e.g. NUE, ACME)."
|
|
234
|
+
}
|
|
235
|
+
]);
|
|
236
|
+
const spinner = ora("Creating your account...").start();
|
|
158
237
|
try {
|
|
159
|
-
const client = new PlexMintClient(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
limit: options.limit ? parseInt(options.limit) : 12
|
|
238
|
+
const client = new PlexMintClient();
|
|
239
|
+
await client.register({
|
|
240
|
+
name: answers.name.trim(),
|
|
241
|
+
email: answers.email.trim().toLowerCase(),
|
|
242
|
+
password: answers.password,
|
|
243
|
+
tickerPrefix: answers.tickerPrefix.trim().toUpperCase()
|
|
166
244
|
});
|
|
245
|
+
spinner.succeed("Account created! Check your email for a verification code.");
|
|
246
|
+
console.log("");
|
|
247
|
+
const { code } = await inquirer.prompt([
|
|
248
|
+
{
|
|
249
|
+
type: "input",
|
|
250
|
+
name: "code",
|
|
251
|
+
message: "Enter 6-digit verification code:",
|
|
252
|
+
validate: (v) => /^\d{6}$/.test(v.trim()) || "Enter the 6-digit code from your email."
|
|
253
|
+
}
|
|
254
|
+
]);
|
|
255
|
+
const verifySpinner = ora("Verifying...").start();
|
|
256
|
+
const result = await client.verify(
|
|
257
|
+
answers.email.trim().toLowerCase(),
|
|
258
|
+
code.trim()
|
|
259
|
+
);
|
|
260
|
+
verifySpinner.stop();
|
|
261
|
+
const { apiKey, user } = result.data;
|
|
262
|
+
setAuth(apiKey, user.email, user.name);
|
|
263
|
+
console.log("");
|
|
264
|
+
console.log(mint(" \u2713 ") + chalk2.bold("Account verified!"));
|
|
265
|
+
console.log("");
|
|
266
|
+
console.log(chalk2.gray(" Name: ") + chalk2.white(user.name));
|
|
267
|
+
console.log(chalk2.gray(" Email: ") + chalk2.white(user.email));
|
|
268
|
+
console.log(chalk2.gray(" Ticker Prefix: ") + chalk2.white(user.tickerPrefix));
|
|
269
|
+
console.log(chalk2.gray(" API Key: ") + chalk2.white(apiKey));
|
|
270
|
+
console.log(chalk2.gray(" Config: ") + chalk2.white(getConfigPath()));
|
|
271
|
+
console.log("");
|
|
272
|
+
console.log(
|
|
273
|
+
chalk2.gray(" Get started: ") + chalk2.white("plexmint browse") + chalk2.gray(" or ") + chalk2.white("plexmint --help")
|
|
274
|
+
);
|
|
275
|
+
console.log("");
|
|
276
|
+
} catch (error) {
|
|
167
277
|
spinner.stop();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
No prompts found for "${query}"
|
|
278
|
+
if (error instanceof PlexMintApiError) {
|
|
279
|
+
console.error(chalk2.red(`
|
|
280
|
+
Error: ${error.message}
|
|
172
281
|
`));
|
|
173
|
-
|
|
282
|
+
} else {
|
|
283
|
+
console.error(
|
|
284
|
+
chalk2.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
285
|
+
);
|
|
174
286
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
prompt2.salesCount.toString(),
|
|
201
|
-
prompt2.seller?.username ? chalk.gray(`@${prompt2.seller.username}`) : chalk.gray("\u2014")
|
|
202
|
-
]);
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/commands/login.ts
|
|
292
|
+
import chalk3 from "chalk";
|
|
293
|
+
import ora2 from "ora";
|
|
294
|
+
import inquirer2 from "inquirer";
|
|
295
|
+
async function loginCommand() {
|
|
296
|
+
console.log("");
|
|
297
|
+
console.log(chalk3.bold(" PlexMint Login"));
|
|
298
|
+
console.log(chalk3.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
299
|
+
console.log("");
|
|
300
|
+
if (isAuthenticated()) {
|
|
301
|
+
const { proceed } = await inquirer2.prompt([
|
|
302
|
+
{
|
|
303
|
+
type: "confirm",
|
|
304
|
+
name: "proceed",
|
|
305
|
+
message: "Already logged in. Re-authenticate?",
|
|
306
|
+
default: false
|
|
307
|
+
}
|
|
308
|
+
]);
|
|
309
|
+
if (!proceed) {
|
|
310
|
+
console.log(chalk3.gray(" Cancelled.\n"));
|
|
311
|
+
return;
|
|
203
312
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
313
|
+
clearAuth();
|
|
314
|
+
}
|
|
315
|
+
const answers = await inquirer2.prompt([
|
|
316
|
+
{
|
|
317
|
+
type: "input",
|
|
318
|
+
name: "email",
|
|
319
|
+
message: "Email:",
|
|
320
|
+
validate: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) || "Enter a valid email."
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
type: "password",
|
|
324
|
+
name: "password",
|
|
325
|
+
message: "Password:",
|
|
326
|
+
mask: "*",
|
|
327
|
+
validate: (v) => v.length > 0 || "Password is required."
|
|
207
328
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
329
|
+
]);
|
|
330
|
+
const spinner = ora2("Logging in...").start();
|
|
331
|
+
try {
|
|
332
|
+
const client = new PlexMintClient();
|
|
333
|
+
const result = await client.login(
|
|
334
|
+
answers.email.trim().toLowerCase(),
|
|
335
|
+
answers.password
|
|
211
336
|
);
|
|
337
|
+
spinner.stop();
|
|
338
|
+
const { apiKey, user } = result.data;
|
|
339
|
+
setAuth(apiKey, user.email, user.name);
|
|
340
|
+
console.log("");
|
|
341
|
+
console.log(mint(" \u2713 ") + chalk3.bold("Logged in!"));
|
|
342
|
+
console.log(chalk3.gray(" Name: ") + chalk3.white(user.name));
|
|
343
|
+
console.log(chalk3.gray(" Email: ") + chalk3.white(user.email));
|
|
344
|
+
console.log(chalk3.gray(" Config: ") + chalk3.white(getConfigPath()));
|
|
345
|
+
console.log("");
|
|
212
346
|
} catch (error) {
|
|
213
347
|
spinner.stop();
|
|
214
348
|
if (error instanceof PlexMintApiError) {
|
|
215
|
-
|
|
349
|
+
if (error.status === 401) {
|
|
350
|
+
console.error(chalk3.red("\n Invalid email or password.\n"));
|
|
351
|
+
} else {
|
|
352
|
+
console.error(chalk3.red(`
|
|
216
353
|
Error: ${error.message}
|
|
217
354
|
`));
|
|
355
|
+
}
|
|
218
356
|
} else {
|
|
219
|
-
console.error(
|
|
220
|
-
Connection error. Is plexmint.com reachable
|
|
221
|
-
|
|
357
|
+
console.error(
|
|
358
|
+
chalk3.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
359
|
+
);
|
|
222
360
|
}
|
|
223
361
|
process.exit(1);
|
|
224
362
|
}
|
|
225
363
|
}
|
|
226
364
|
|
|
227
365
|
// src/commands/browse.ts
|
|
228
|
-
import
|
|
229
|
-
import
|
|
230
|
-
import
|
|
366
|
+
import chalk4 from "chalk";
|
|
367
|
+
import ora3 from "ora";
|
|
368
|
+
import Table from "cli-table3";
|
|
231
369
|
async function browseCommand(options) {
|
|
232
|
-
const spinner =
|
|
370
|
+
const spinner = ora3("Browsing PlexMint marketplace...").start();
|
|
233
371
|
try {
|
|
234
|
-
const client = new PlexMintClient(getApiKey()
|
|
235
|
-
const limit = options.limit ? parseInt(options.limit) : 20;
|
|
236
|
-
const page = options.page ? Math.max(1, parseInt(options.page)) : 1;
|
|
237
|
-
const offset = (page - 1) * limit;
|
|
372
|
+
const client = new PlexMintClient(getApiKey());
|
|
238
373
|
const result = await client.browse({
|
|
374
|
+
query: options.search,
|
|
239
375
|
category: options.category,
|
|
240
|
-
model: options.model,
|
|
241
376
|
sort: options.sort || "trending",
|
|
242
|
-
limit
|
|
243
|
-
offset
|
|
377
|
+
limit: 20
|
|
244
378
|
});
|
|
245
379
|
spinner.stop();
|
|
246
|
-
const { items,
|
|
380
|
+
const { items, total } = result.data;
|
|
247
381
|
if (items.length === 0) {
|
|
248
|
-
|
|
249
|
-
console.log(chalk2.yellow("\n No more prompts. You've reached the end.\n"));
|
|
250
|
-
} else {
|
|
251
|
-
console.log(chalk2.yellow("\n No prompts found.\n"));
|
|
252
|
-
}
|
|
382
|
+
console.log(chalk4.yellow("\n No prompts found.\n"));
|
|
253
383
|
return;
|
|
254
384
|
}
|
|
255
|
-
const
|
|
385
|
+
const filters = [];
|
|
386
|
+
if (options.category) filters.push(`category: ${options.category}`);
|
|
387
|
+
if (options.search) filters.push(`search: "${options.search}"`);
|
|
388
|
+
if (options.sort) filters.push(`sort: ${options.sort}`);
|
|
256
389
|
console.log(
|
|
257
|
-
|
|
258
|
-
PlexMint Marketplace`) +
|
|
259
|
-
`)
|
|
390
|
+
chalk4.bold(`
|
|
391
|
+
PlexMint Marketplace`) + chalk4.gray(` \u2014 ${total} prompt${total !== 1 ? "s" : ""}`) + (filters.length > 0 ? chalk4.gray(` (${filters.join(", ")})`) : "") + "\n"
|
|
260
392
|
);
|
|
261
|
-
const table = new
|
|
393
|
+
const table = new Table({
|
|
262
394
|
head: [
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
395
|
+
mint("Ticker"),
|
|
396
|
+
mint("Title"),
|
|
397
|
+
mint("Price"),
|
|
398
|
+
mint("Rating"),
|
|
399
|
+
mint("Sales"),
|
|
400
|
+
mint("Seller")
|
|
269
401
|
],
|
|
270
402
|
style: { head: [], border: ["gray"] },
|
|
271
|
-
colWidths: [14, 30,
|
|
403
|
+
colWidths: [14, 30, 10, 10, 8, 16]
|
|
272
404
|
});
|
|
273
|
-
for (const
|
|
274
|
-
const price = parseFloat(
|
|
275
|
-
const rating = parseFloat(
|
|
405
|
+
for (const prompt of items) {
|
|
406
|
+
const price = parseFloat(prompt.currentPrice);
|
|
407
|
+
const rating = parseFloat(prompt.averageRating);
|
|
276
408
|
table.push([
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
409
|
+
chalk4.bold(prompt.ticker),
|
|
410
|
+
prompt.title.length > 28 ? prompt.title.slice(0, 25) + "..." : prompt.title,
|
|
411
|
+
mint(formatPrice(price)),
|
|
412
|
+
rating > 0 ? chalk4.yellow(`\u2605 ${rating.toFixed(1)}`) : chalk4.gray("\u2014"),
|
|
413
|
+
prompt.salesCount.toString(),
|
|
414
|
+
prompt.seller?.username ? chalk4.gray(`@${prompt.seller.username}`) : chalk4.gray("\u2014")
|
|
283
415
|
]);
|
|
284
416
|
}
|
|
285
417
|
console.log(table.toString());
|
|
286
|
-
const footer = [];
|
|
287
|
-
if (page > 1) {
|
|
288
|
-
footer.push(chalk2.gray(" \u25C0 Previous: ") + chalk2.white(`plexmint browse --page ${page - 1}`));
|
|
289
|
-
}
|
|
290
|
-
if (hasMore) {
|
|
291
|
-
footer.push(chalk2.gray(" \u25B6 Next: ") + chalk2.white(`plexmint browse --page ${page + 1}`));
|
|
292
|
-
}
|
|
293
|
-
if (footer.length > 0) {
|
|
294
|
-
console.log("");
|
|
295
|
-
footer.forEach((f) => console.log(f));
|
|
296
|
-
}
|
|
297
418
|
console.log(
|
|
298
|
-
|
|
419
|
+
chalk4.gray("\n View details: ") + chalk4.white("plexmint view <TICKER>") + chalk4.gray(" | Buy: ") + chalk4.white("plexmint buy <TICKER>\n")
|
|
299
420
|
);
|
|
300
421
|
} catch (error) {
|
|
301
422
|
spinner.stop();
|
|
302
423
|
if (error instanceof PlexMintApiError) {
|
|
303
|
-
console.error(
|
|
424
|
+
console.error(chalk4.red(`
|
|
304
425
|
Error: ${error.message}
|
|
305
426
|
`));
|
|
306
427
|
} else {
|
|
307
|
-
console.error(
|
|
428
|
+
console.error(
|
|
429
|
+
chalk4.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
430
|
+
);
|
|
308
431
|
}
|
|
309
432
|
process.exit(1);
|
|
310
433
|
}
|
|
311
434
|
}
|
|
312
435
|
|
|
313
|
-
// src/commands/
|
|
314
|
-
import
|
|
315
|
-
import
|
|
316
|
-
async function
|
|
317
|
-
const
|
|
436
|
+
// src/commands/view.ts
|
|
437
|
+
import chalk5 from "chalk";
|
|
438
|
+
import ora4 from "ora";
|
|
439
|
+
async function viewCommand(ticker) {
|
|
440
|
+
const upperTicker = ticker.toUpperCase();
|
|
441
|
+
const spinner = ora4(`Looking up ${upperTicker}...`).start();
|
|
318
442
|
try {
|
|
319
|
-
const client = new PlexMintClient(getApiKey()
|
|
320
|
-
const result = await client.getPrompt(
|
|
443
|
+
const client = new PlexMintClient(getApiKey());
|
|
444
|
+
const result = await client.getPrompt(upperTicker);
|
|
321
445
|
spinner.stop();
|
|
322
446
|
const p = result.data;
|
|
323
447
|
const price = parseFloat(p.currentPrice);
|
|
324
448
|
const rating = parseFloat(p.averageRating);
|
|
325
449
|
console.log("");
|
|
326
|
-
console.log(
|
|
327
|
-
console.log(
|
|
450
|
+
console.log(chalk5.bold.white(` ${p.title}`));
|
|
451
|
+
console.log(
|
|
452
|
+
mint(p.ticker) + chalk5.gray(` \xB7 ${p.category}`)
|
|
453
|
+
);
|
|
328
454
|
console.log("");
|
|
329
|
-
console.log(
|
|
455
|
+
console.log(chalk5.white(` ${p.description}`));
|
|
330
456
|
console.log("");
|
|
331
457
|
console.log(
|
|
332
|
-
|
|
458
|
+
mint.bold(` ${formatPrice(price)}`) + chalk5.gray(" \xB7 ") + (rating > 0 ? chalk5.yellow(`\u2605 ${rating.toFixed(1)}`) : chalk5.gray("No ratings")) + chalk5.gray(" \xB7 ") + chalk5.white(`${p.salesCount} sold`) + chalk5.gray(" \xB7 ") + chalk5.white(`${p.reviewCount} reviews`)
|
|
333
459
|
);
|
|
334
460
|
console.log("");
|
|
335
461
|
if (p.models.length > 0) {
|
|
336
|
-
const modelList = p.models.map(
|
|
337
|
-
|
|
462
|
+
const modelList = p.models.map(
|
|
463
|
+
(m) => m.isPrimary ? chalk5.cyan.bold(m.model) : chalk5.gray(m.model)
|
|
464
|
+
).join(", ");
|
|
465
|
+
console.log(chalk5.gray(" Models: ") + modelList);
|
|
338
466
|
}
|
|
339
467
|
if (p.tags && p.tags.length > 0) {
|
|
340
468
|
console.log(
|
|
341
|
-
|
|
469
|
+
chalk5.gray(" Tags: ") + p.tags.map((t) => chalk5.gray(`#${t}`)).join(" ")
|
|
342
470
|
);
|
|
343
471
|
}
|
|
344
472
|
if (p.seller) {
|
|
345
473
|
console.log(
|
|
346
|
-
|
|
474
|
+
chalk5.gray(" Seller: ") + chalk5.white(p.seller.name || p.seller.username || "\u2014") + (p.seller.username ? chalk5.gray(` (@${p.seller.username})`) : "")
|
|
347
475
|
);
|
|
348
476
|
}
|
|
349
477
|
if (p.tokenEstimate) {
|
|
350
|
-
console.log(
|
|
478
|
+
console.log(chalk5.gray(` Tokens: ~${p.tokenEstimate}`));
|
|
351
479
|
}
|
|
352
480
|
console.log("");
|
|
353
|
-
console.log(
|
|
481
|
+
console.log(chalk5.gray(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
|
|
354
482
|
console.log(
|
|
355
|
-
|
|
483
|
+
chalk5.gray(" \u2502 ") + chalk5.white("Buy this prompt:") + " ".repeat(25) + chalk5.gray("\u2502")
|
|
356
484
|
);
|
|
357
|
-
const
|
|
358
|
-
const padding = 39 -
|
|
485
|
+
const buyCmd = `plexmint buy ${p.ticker}`;
|
|
486
|
+
const padding = 39 - buyCmd.length;
|
|
359
487
|
console.log(
|
|
360
|
-
|
|
488
|
+
chalk5.gray(" \u2502 ") + mint.bold(buyCmd) + " ".repeat(Math.max(0, padding)) + chalk5.gray("\u2502")
|
|
361
489
|
);
|
|
362
|
-
console.log(
|
|
490
|
+
console.log(chalk5.gray(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
|
|
363
491
|
console.log(
|
|
364
|
-
|
|
492
|
+
chalk5.gray("\n View on web: ") + chalk5.underline(`https://plexmint.com/prompt/${p.slug}
|
|
365
493
|
`)
|
|
366
494
|
);
|
|
367
495
|
} catch (error) {
|
|
@@ -369,372 +497,272 @@ async function infoCommand(ticker) {
|
|
|
369
497
|
if (error instanceof PlexMintApiError) {
|
|
370
498
|
if (error.status === 404) {
|
|
371
499
|
console.error(
|
|
372
|
-
|
|
373
|
-
Prompt not found: ${
|
|
500
|
+
chalk5.red(`
|
|
501
|
+
Prompt not found: ${upperTicker}
|
|
374
502
|
`)
|
|
375
503
|
);
|
|
376
504
|
} else {
|
|
377
|
-
console.error(
|
|
505
|
+
console.error(chalk5.red(`
|
|
378
506
|
Error: ${error.message}
|
|
379
507
|
`));
|
|
380
508
|
}
|
|
381
509
|
} else {
|
|
382
|
-
console.error(
|
|
510
|
+
console.error(
|
|
511
|
+
chalk5.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
512
|
+
);
|
|
383
513
|
}
|
|
384
514
|
process.exit(1);
|
|
385
515
|
}
|
|
386
516
|
}
|
|
387
517
|
|
|
388
|
-
// src/commands/
|
|
389
|
-
import
|
|
390
|
-
import
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
async function installCommand(ticker, options) {
|
|
518
|
+
// src/commands/buy.ts
|
|
519
|
+
import chalk6 from "chalk";
|
|
520
|
+
import ora5 from "ora";
|
|
521
|
+
async function buyCommand(ticker) {
|
|
522
|
+
requireAuth();
|
|
394
523
|
const upperTicker = ticker.toUpperCase();
|
|
395
|
-
|
|
396
|
-
console.log("");
|
|
397
|
-
console.log(
|
|
398
|
-
chalk4.yellow(" Authentication required to install prompts.")
|
|
399
|
-
);
|
|
400
|
-
console.log(
|
|
401
|
-
chalk4.gray(" Run: ") + chalk4.white("plexmint login") + chalk4.gray(" first\n")
|
|
402
|
-
);
|
|
403
|
-
process.exit(1);
|
|
404
|
-
}
|
|
405
|
-
const spinner = ora4(`Looking up ${upperTicker}...`).start();
|
|
524
|
+
const spinner = ora5(`Purchasing ${upperTicker}...`).start();
|
|
406
525
|
try {
|
|
407
|
-
const client = new PlexMintClient(getApiKey()
|
|
408
|
-
|
|
409
|
-
try {
|
|
410
|
-
content = await client.getContent(upperTicker);
|
|
411
|
-
spinner.text = `Downloading ${upperTicker}...`;
|
|
412
|
-
} catch (error) {
|
|
413
|
-
if (error instanceof PlexMintApiError && error.status === 403) {
|
|
414
|
-
spinner.stop();
|
|
415
|
-
const info = await client.getPrompt(upperTicker);
|
|
416
|
-
const p = info.data;
|
|
417
|
-
const price = parseFloat(p.currentPrice);
|
|
418
|
-
console.log("");
|
|
419
|
-
console.log(
|
|
420
|
-
chalk4.yellow(` You haven't purchased ${upperTicker} yet.`)
|
|
421
|
-
);
|
|
422
|
-
console.log("");
|
|
423
|
-
console.log(
|
|
424
|
-
chalk4.white(` ${p.title}`) + chalk4.gray(` by ${p.seller?.username || "unknown"}`)
|
|
425
|
-
);
|
|
426
|
-
console.log(chalk4.green.bold(` $${price.toFixed(2)}`));
|
|
427
|
-
console.log("");
|
|
428
|
-
const purchaseSpinner = ora4("Locking price...").start();
|
|
429
|
-
try {
|
|
430
|
-
const purchase = await client.purchase(upperTicker);
|
|
431
|
-
purchaseSpinner.stop();
|
|
432
|
-
console.log(
|
|
433
|
-
chalk4.green(` Price locked at $${purchase.data.lockedPrice} for ${purchase.data.lockDurationSeconds}s`)
|
|
434
|
-
);
|
|
435
|
-
console.log("");
|
|
436
|
-
console.log(
|
|
437
|
-
chalk4.white(" Complete your purchase here:")
|
|
438
|
-
);
|
|
439
|
-
console.log(
|
|
440
|
-
chalk4.cyan.underline(` ${purchase.data.checkoutUrl}`)
|
|
441
|
-
);
|
|
442
|
-
console.log("");
|
|
443
|
-
console.log(
|
|
444
|
-
chalk4.gray(
|
|
445
|
-
" After purchasing, run this command again to download.\n"
|
|
446
|
-
)
|
|
447
|
-
);
|
|
448
|
-
} catch (purchaseError) {
|
|
449
|
-
purchaseSpinner.stop();
|
|
450
|
-
if (purchaseError instanceof PlexMintApiError) {
|
|
451
|
-
if (purchaseError.status === 409) {
|
|
452
|
-
console.log(
|
|
453
|
-
chalk4.yellow(" You already own this prompt. Retrying download...\n")
|
|
454
|
-
);
|
|
455
|
-
content = await client.getContent(upperTicker);
|
|
456
|
-
} else {
|
|
457
|
-
console.error(chalk4.red(`
|
|
458
|
-
Purchase error: ${purchaseError.message}
|
|
459
|
-
`));
|
|
460
|
-
process.exit(1);
|
|
461
|
-
}
|
|
462
|
-
} else {
|
|
463
|
-
throw purchaseError;
|
|
464
|
-
}
|
|
465
|
-
}
|
|
466
|
-
if (!content) return;
|
|
467
|
-
} else {
|
|
468
|
-
throw error;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
526
|
+
const client = new PlexMintClient(getApiKey());
|
|
527
|
+
const result = await client.purchase(upperTicker);
|
|
471
528
|
spinner.stop();
|
|
472
|
-
const
|
|
473
|
-
const installDir = resolve(options.dir || ".plexmint");
|
|
474
|
-
if (!existsSync(installDir)) {
|
|
475
|
-
mkdirSync(installDir, { recursive: true });
|
|
476
|
-
}
|
|
477
|
-
const promptDir = join(installDir, data.ticker.toLowerCase());
|
|
478
|
-
if (!existsSync(promptDir)) {
|
|
479
|
-
mkdirSync(promptDir, { recursive: true });
|
|
480
|
-
}
|
|
481
|
-
const promptFile = join(promptDir, "prompt.md");
|
|
482
|
-
if (existsSync(promptFile) && !options.force) {
|
|
483
|
-
console.log(
|
|
484
|
-
chalk4.yellow(
|
|
485
|
-
`
|
|
486
|
-
${upperTicker} is already installed. Use --force to overwrite.
|
|
487
|
-
`
|
|
488
|
-
)
|
|
489
|
-
);
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
writeFileSync(promptFile, data.promptText, "utf-8");
|
|
493
|
-
const metaFile = join(promptDir, "plexmint.json");
|
|
494
|
-
writeFileSync(
|
|
495
|
-
metaFile,
|
|
496
|
-
JSON.stringify(
|
|
497
|
-
{
|
|
498
|
-
ticker: data.ticker,
|
|
499
|
-
title: data.title,
|
|
500
|
-
version: data.version,
|
|
501
|
-
variables: data.variables,
|
|
502
|
-
tips: data.tips,
|
|
503
|
-
models: data.models,
|
|
504
|
-
tokenEstimate: data.tokenEstimate,
|
|
505
|
-
costPerCall: data.costPerCall,
|
|
506
|
-
exampleInput: data.exampleInput,
|
|
507
|
-
exampleOutput: data.exampleOutput,
|
|
508
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
509
|
-
},
|
|
510
|
-
null,
|
|
511
|
-
2
|
|
512
|
-
),
|
|
513
|
-
"utf-8"
|
|
514
|
-
);
|
|
529
|
+
const { ticker: t, title, pricePaid, walletBalance } = result.data;
|
|
515
530
|
console.log("");
|
|
516
|
-
console.log(
|
|
517
|
-
console.log(chalk4.gray(` ${data.title} v${data.version}`));
|
|
531
|
+
console.log(mint(" \u2713 ") + chalk6.bold("Purchase complete!"));
|
|
518
532
|
console.log("");
|
|
519
|
-
console.log(
|
|
520
|
-
console.log(
|
|
521
|
-
console.log(
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
console.log(chalk4.cyan(` {{${v}}}`));
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
if (data.tips) {
|
|
530
|
-
console.log("");
|
|
531
|
-
console.log(chalk4.gray(" Tips: ") + chalk4.white(data.tips));
|
|
532
|
-
}
|
|
533
|
+
console.log(chalk6.gray(" Prompt: ") + chalk6.white(`${title} (${t})`));
|
|
534
|
+
console.log(chalk6.gray(" Price paid: ") + mint(formatPrice(pricePaid)));
|
|
535
|
+
console.log(chalk6.gray(" Wallet balance: ") + chalk6.white(formatPrice(walletBalance)));
|
|
536
|
+
console.log("");
|
|
537
|
+
console.log(
|
|
538
|
+
chalk6.gray(" View in your library: ") + chalk6.white("plexmint library")
|
|
539
|
+
);
|
|
533
540
|
console.log("");
|
|
534
541
|
} catch (error) {
|
|
535
542
|
spinner.stop();
|
|
536
543
|
if (error instanceof PlexMintApiError) {
|
|
537
|
-
if (error.status ===
|
|
538
|
-
console.
|
|
544
|
+
if (error.status === 409) {
|
|
545
|
+
console.log(
|
|
546
|
+
chalk6.yellow(`
|
|
547
|
+
You already own ${upperTicker}.
|
|
548
|
+
`)
|
|
549
|
+
);
|
|
550
|
+
console.log(
|
|
551
|
+
chalk6.gray(" View your library: ") + chalk6.white("plexmint library\n")
|
|
552
|
+
);
|
|
553
|
+
} else if (error.status === 402) {
|
|
554
|
+
console.error(
|
|
555
|
+
chalk6.red(`
|
|
556
|
+
Insufficient wallet balance.`) + chalk6.gray(` Add funds at https://plexmint.com/wallet
|
|
557
|
+
`)
|
|
558
|
+
);
|
|
559
|
+
} else if (error.status === 404) {
|
|
560
|
+
console.error(
|
|
561
|
+
chalk6.red(`
|
|
539
562
|
Prompt not found: ${upperTicker}
|
|
540
|
-
`)
|
|
563
|
+
`)
|
|
564
|
+
);
|
|
541
565
|
} else {
|
|
542
|
-
console.error(
|
|
566
|
+
console.error(chalk6.red(`
|
|
543
567
|
Error: ${error.message}
|
|
544
568
|
`));
|
|
545
569
|
}
|
|
546
570
|
} else {
|
|
547
|
-
console.error(
|
|
571
|
+
console.error(
|
|
572
|
+
chalk6.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
573
|
+
);
|
|
548
574
|
}
|
|
549
575
|
process.exit(1);
|
|
550
576
|
}
|
|
551
577
|
}
|
|
552
578
|
|
|
553
|
-
// src/commands/
|
|
554
|
-
import
|
|
555
|
-
import
|
|
556
|
-
import
|
|
557
|
-
function
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
resolve2(answer);
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
async function loginCommand(options) {
|
|
577
|
-
console.log("");
|
|
578
|
-
console.log(chalk5.bold(" PlexMint Login"));
|
|
579
|
-
console.log(chalk5.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
580
|
-
console.log("");
|
|
581
|
-
if (options.apiKey) {
|
|
582
|
-
setApiKey(options.apiKey);
|
|
583
|
-
console.log(chalk5.green(" \u2713 API key saved."));
|
|
584
|
-
console.log(chalk5.gray(` Config: ${getConfigPath()}
|
|
585
|
-
`));
|
|
586
|
-
return;
|
|
587
|
-
}
|
|
588
|
-
if (isAuthenticated()) {
|
|
589
|
-
const answer = await prompt(
|
|
590
|
-
chalk5.yellow(" Already logged in. Re-authenticate? (y/N) ")
|
|
579
|
+
// src/commands/wallet.ts
|
|
580
|
+
import chalk7 from "chalk";
|
|
581
|
+
import ora6 from "ora";
|
|
582
|
+
import Table2 from "cli-table3";
|
|
583
|
+
async function walletCommand() {
|
|
584
|
+
requireAuth();
|
|
585
|
+
const spinner = ora6("Loading wallet...").start();
|
|
586
|
+
try {
|
|
587
|
+
const client = new PlexMintClient(getApiKey());
|
|
588
|
+
const result = await client.getWallet();
|
|
589
|
+
spinner.stop();
|
|
590
|
+
const { balance, currency, transactions } = result.data;
|
|
591
|
+
console.log("");
|
|
592
|
+
console.log(chalk7.bold(" Wallet"));
|
|
593
|
+
console.log(chalk7.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
594
|
+
console.log("");
|
|
595
|
+
console.log(
|
|
596
|
+
chalk7.gray(" Balance: ") + mint.bold(formatPrice(balance)) + chalk7.gray(` ${currency}`)
|
|
591
597
|
);
|
|
592
|
-
|
|
593
|
-
|
|
598
|
+
console.log("");
|
|
599
|
+
if (transactions.length === 0) {
|
|
600
|
+
console.log(chalk7.gray(" No recent transactions.\n"));
|
|
594
601
|
return;
|
|
595
602
|
}
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
);
|
|
622
|
-
const key = await prompt(chalk5.white(" API Key: "));
|
|
623
|
-
if (!key) {
|
|
624
|
-
console.log(chalk5.red("\n API key is required.\n"));
|
|
625
|
-
process.exit(1);
|
|
626
|
-
}
|
|
627
|
-
setApiKey(key);
|
|
628
|
-
setUserInfo(user.username || "", email);
|
|
629
|
-
console.log(chalk5.green("\n \u2713 Logged in successfully!"));
|
|
630
|
-
console.log(chalk5.gray(` User: ${user.username || user.email}`));
|
|
631
|
-
console.log(chalk5.gray(` Config: ${getConfigPath()}
|
|
632
|
-
`));
|
|
633
|
-
} else {
|
|
634
|
-
console.log(chalk5.yellow(`
|
|
635
|
-
\u2713 Credentials verified for ${user.email}`));
|
|
636
|
-
console.log(
|
|
637
|
-
chalk5.yellow(
|
|
638
|
-
" No API key found. Generate one at https://plexmint.com/settings"
|
|
639
|
-
)
|
|
640
|
-
);
|
|
641
|
-
console.log(
|
|
642
|
-
chalk5.gray(
|
|
643
|
-
"\n After generating a key, run: " + chalk5.white("plexmint login --api-key <YOUR_KEY>\n")
|
|
644
|
-
)
|
|
645
|
-
);
|
|
646
|
-
setUserInfo(user.username || "", email);
|
|
603
|
+
console.log(chalk7.bold(" Recent Transactions"));
|
|
604
|
+
console.log("");
|
|
605
|
+
const table = new Table2({
|
|
606
|
+
head: [
|
|
607
|
+
mint("Date"),
|
|
608
|
+
mint("Type"),
|
|
609
|
+
mint("Amount"),
|
|
610
|
+
mint("Description")
|
|
611
|
+
],
|
|
612
|
+
style: { head: [], border: ["gray"] },
|
|
613
|
+
colWidths: [14, 12, 12, 40]
|
|
614
|
+
});
|
|
615
|
+
for (const tx of transactions) {
|
|
616
|
+
const amount = parseFloat(tx.amount);
|
|
617
|
+
const isCredit = amount >= 0;
|
|
618
|
+
const date = new Date(tx.createdAt).toLocaleDateString("en-US", {
|
|
619
|
+
month: "short",
|
|
620
|
+
day: "numeric"
|
|
621
|
+
});
|
|
622
|
+
table.push([
|
|
623
|
+
chalk7.gray(date),
|
|
624
|
+
tx.type,
|
|
625
|
+
isCredit ? mint(`+${formatPrice(amount)}`) : chalk7.red(`-${formatPrice(Math.abs(amount))}`),
|
|
626
|
+
tx.description.length > 38 ? tx.description.slice(0, 35) + "..." : tx.description
|
|
627
|
+
]);
|
|
647
628
|
}
|
|
629
|
+
console.log(table.toString());
|
|
630
|
+
console.log("");
|
|
648
631
|
} catch (error) {
|
|
649
632
|
spinner.stop();
|
|
650
633
|
if (error instanceof PlexMintApiError) {
|
|
651
|
-
|
|
652
|
-
console.error(chalk5.red("\n Invalid email or password.\n"));
|
|
653
|
-
} else {
|
|
654
|
-
console.error(chalk5.red(`
|
|
634
|
+
console.error(chalk7.red(`
|
|
655
635
|
Error: ${error.message}
|
|
656
636
|
`));
|
|
657
|
-
}
|
|
658
637
|
} else {
|
|
659
638
|
console.error(
|
|
660
|
-
|
|
639
|
+
chalk7.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
661
640
|
);
|
|
662
641
|
}
|
|
663
642
|
process.exit(1);
|
|
664
643
|
}
|
|
665
644
|
}
|
|
666
|
-
async function logoutCommand() {
|
|
667
|
-
clearAuth();
|
|
668
|
-
console.log(chalk5.green("\n \u2713 Logged out. API key removed.\n"));
|
|
669
|
-
}
|
|
670
645
|
|
|
671
646
|
// src/commands/library.ts
|
|
672
|
-
import
|
|
673
|
-
import
|
|
647
|
+
import chalk8 from "chalk";
|
|
648
|
+
import ora7 from "ora";
|
|
649
|
+
import Table3 from "cli-table3";
|
|
674
650
|
async function libraryCommand() {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
console.log(chalk6.yellow(" Authentication required to view your library."));
|
|
678
|
-
console.log(
|
|
679
|
-
chalk6.gray(" Run: ") + chalk6.white("plexmint login") + chalk6.gray(" first\n")
|
|
680
|
-
);
|
|
681
|
-
process.exit(1);
|
|
682
|
-
}
|
|
683
|
-
const spinner = ora6("Loading your library...").start();
|
|
651
|
+
requireAuth();
|
|
652
|
+
const spinner = ora7("Loading your library...").start();
|
|
684
653
|
try {
|
|
685
|
-
const client = new PlexMintClient(getApiKey()
|
|
654
|
+
const client = new PlexMintClient(getApiKey());
|
|
655
|
+
const result = await client.getLibrary();
|
|
686
656
|
spinner.stop();
|
|
687
|
-
|
|
688
|
-
console.log(chalk6.bold(" Your Library"));
|
|
689
|
-
console.log(chalk6.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
657
|
+
const { items, total } = result.data;
|
|
690
658
|
console.log("");
|
|
691
659
|
console.log(
|
|
692
|
-
|
|
693
|
-
" View your purchased prompts at: "
|
|
694
|
-
) + chalk6.cyan.underline("https://plexmint.com/library")
|
|
660
|
+
chalk8.bold(" Your Library") + chalk8.gray(` \u2014 ${total} prompt${total !== 1 ? "s" : ""}`)
|
|
695
661
|
);
|
|
696
662
|
console.log("");
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
663
|
+
if (items.length === 0) {
|
|
664
|
+
console.log(chalk8.gray(" No prompts yet. Browse the marketplace:"));
|
|
665
|
+
console.log(chalk8.white(" plexmint browse\n"));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const table = new Table3({
|
|
669
|
+
head: [
|
|
670
|
+
mint("Ticker"),
|
|
671
|
+
mint("Title"),
|
|
672
|
+
mint("Category"),
|
|
673
|
+
mint("Version"),
|
|
674
|
+
mint("Purchased")
|
|
675
|
+
],
|
|
676
|
+
style: { head: [], border: ["gray"] },
|
|
677
|
+
colWidths: [14, 32, 14, 10, 14]
|
|
678
|
+
});
|
|
679
|
+
for (const item of items) {
|
|
680
|
+
const date = new Date(item.purchasedAt).toLocaleDateString("en-US", {
|
|
681
|
+
month: "short",
|
|
682
|
+
day: "numeric",
|
|
683
|
+
year: "numeric"
|
|
684
|
+
});
|
|
685
|
+
table.push([
|
|
686
|
+
chalk8.bold(item.ticker),
|
|
687
|
+
item.title.length > 30 ? item.title.slice(0, 27) + "..." : item.title,
|
|
688
|
+
item.category,
|
|
689
|
+
`v${item.version}`,
|
|
690
|
+
chalk8.gray(date)
|
|
691
|
+
]);
|
|
692
|
+
}
|
|
693
|
+
console.log(table.toString());
|
|
694
|
+
console.log("");
|
|
701
695
|
} catch (error) {
|
|
702
696
|
spinner.stop();
|
|
703
697
|
if (error instanceof PlexMintApiError) {
|
|
704
|
-
console.error(
|
|
698
|
+
console.error(chalk8.red(`
|
|
705
699
|
Error: ${error.message}
|
|
706
700
|
`));
|
|
707
701
|
} else {
|
|
708
|
-
console.error(
|
|
702
|
+
console.error(
|
|
703
|
+
chalk8.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
704
|
+
);
|
|
709
705
|
}
|
|
710
706
|
process.exit(1);
|
|
711
707
|
}
|
|
712
708
|
}
|
|
713
709
|
|
|
710
|
+
// src/commands/whoami.ts
|
|
711
|
+
import chalk9 from "chalk";
|
|
712
|
+
function whoamiCommand() {
|
|
713
|
+
if (!isAuthenticated()) {
|
|
714
|
+
console.log(chalk9.yellow("\n Not logged in.\n"));
|
|
715
|
+
console.log(
|
|
716
|
+
chalk9.gray(" Run ") + chalk9.white("plexmint setup") + chalk9.gray(" or ") + chalk9.white("plexmint login") + chalk9.gray(" to authenticate.\n")
|
|
717
|
+
);
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const config = readConfig();
|
|
721
|
+
console.log("");
|
|
722
|
+
console.log(mint(" \u2713 ") + chalk9.bold("Authenticated"));
|
|
723
|
+
console.log("");
|
|
724
|
+
if (config.name) {
|
|
725
|
+
console.log(chalk9.gray(" Name: ") + chalk9.white(config.name));
|
|
726
|
+
}
|
|
727
|
+
if (config.email) {
|
|
728
|
+
console.log(chalk9.gray(" Email: ") + chalk9.white(config.email));
|
|
729
|
+
}
|
|
730
|
+
if (config.apiKey) {
|
|
731
|
+
const key = config.apiKey;
|
|
732
|
+
const masked = key.length > 16 ? key.slice(0, 8) + "..." + key.slice(-4) : key.slice(0, 4) + "...";
|
|
733
|
+
console.log(chalk9.gray(" API Key: ") + chalk9.white(masked));
|
|
734
|
+
}
|
|
735
|
+
console.log(chalk9.gray(" Config: ") + chalk9.white(getConfigPath()));
|
|
736
|
+
console.log("");
|
|
737
|
+
}
|
|
738
|
+
|
|
714
739
|
// src/index.ts
|
|
715
740
|
var program = new Command();
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
program.
|
|
721
|
-
program.command("
|
|
722
|
-
program.command("
|
|
723
|
-
program.command("
|
|
724
|
-
program.command("
|
|
725
|
-
program.command("login").description("Authenticate with your PlexMint account").option("--api-key <key>", "Set API key directly (skip interactive login)").action(loginCommand);
|
|
726
|
-
program.command("logout").description("Remove stored API key").action(logoutCommand);
|
|
727
|
-
program.command("library").description("View your purchased prompts").action(libraryCommand);
|
|
741
|
+
program.name("plexmint").description("Browse, buy, and manage AI prompts from the PlexMint marketplace").version("0.2.0").addHelpText("before", BANNER);
|
|
742
|
+
program.command("setup").description("Create a PlexMint account (interactive wizard)").action(setupCommand);
|
|
743
|
+
program.command("login").description("Sign in to your PlexMint account").action(loginCommand);
|
|
744
|
+
program.command("browse").description("Browse the PlexMint marketplace").option("-c, --category <category>", "Filter by category").option("-s, --sort <sort>", "Sort order (trending, top_rated, most_sales, newest, price_low, price_high)", "trending").option("--search <query>", "Search by keyword").action(browseCommand);
|
|
745
|
+
program.command("view <ticker>").description("View full details for a prompt by ticker").action(viewCommand);
|
|
746
|
+
program.command("buy <ticker>").description("Purchase a prompt using your wallet balance").action(buyCommand);
|
|
747
|
+
program.command("wallet").description("View your wallet balance and transactions").action(walletCommand);
|
|
748
|
+
program.command("library").description("List your purchased prompts").action(libraryCommand);
|
|
749
|
+
program.command("whoami").description("Show current authenticated user").action(whoamiCommand);
|
|
728
750
|
program.action(() => {
|
|
751
|
+
if (!isAuthenticated()) {
|
|
752
|
+
setupCommand();
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
729
755
|
console.log(BANNER);
|
|
730
|
-
console.log(
|
|
756
|
+
console.log(chalk10.bold(" Commands:"));
|
|
731
757
|
console.log("");
|
|
732
|
-
console.log(
|
|
733
|
-
console.log(
|
|
734
|
-
console.log(
|
|
735
|
-
console.log(
|
|
736
|
-
console.log(
|
|
758
|
+
console.log(chalk10.gray(" Browse marketplace ") + chalk10.white("plexmint browse"));
|
|
759
|
+
console.log(chalk10.gray(" Search prompts ") + chalk10.white('plexmint browse --search "email copywriting"'));
|
|
760
|
+
console.log(chalk10.gray(" View prompt details ") + chalk10.white("plexmint view <TICKER>"));
|
|
761
|
+
console.log(chalk10.gray(" Buy a prompt ") + chalk10.white("plexmint buy <TICKER>"));
|
|
762
|
+
console.log(chalk10.gray(" Your wallet ") + chalk10.white("plexmint wallet"));
|
|
763
|
+
console.log(chalk10.gray(" Your library ") + chalk10.white("plexmint library"));
|
|
764
|
+
console.log(chalk10.gray(" Current user ") + chalk10.white("plexmint whoami"));
|
|
737
765
|
console.log("");
|
|
738
|
-
console.log(
|
|
766
|
+
console.log(chalk10.gray(" Run ") + chalk10.white("plexmint --help") + chalk10.gray(" for all options.\n"));
|
|
739
767
|
});
|
|
740
768
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plexmint",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "PlexMint CLI — Browse,
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "PlexMint CLI — Browse, buy, and manage AI prompts from your terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"plexmint": "./dist/index.js"
|
|
@@ -37,11 +37,12 @@
|
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"chalk": "^5.4.1",
|
|
39
39
|
"commander": "^13.1.0",
|
|
40
|
-
"
|
|
40
|
+
"inquirer": "^12.3.0",
|
|
41
41
|
"ora": "^8.2.0",
|
|
42
42
|
"cli-table3": "^0.6.5"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
+
"@types/inquirer": "^9.0.7",
|
|
45
46
|
"tsup": "^8.4.0",
|
|
46
47
|
"tsx": "^4.21.0",
|
|
47
48
|
"typescript": "^5.7.0",
|