plexmint 0.1.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/README.md +73 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +720 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# PlexMint CLI
|
|
2
|
+
|
|
3
|
+
Browse, install, and manage AI prompts from the [PlexMint](https://plexmint.com) marketplace — right from your terminal.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Browse the marketplace
|
|
9
|
+
npx plexmint browse
|
|
10
|
+
|
|
11
|
+
# Search for prompts
|
|
12
|
+
npx plexmint search "email copywriting"
|
|
13
|
+
|
|
14
|
+
# Get details on a specific prompt
|
|
15
|
+
npx plexmint info NUE-SEO01
|
|
16
|
+
|
|
17
|
+
# Install a prompt locally
|
|
18
|
+
npx plexmint install NUE-SEO01
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Run directly with npx (no install needed)
|
|
25
|
+
npx plexmint browse
|
|
26
|
+
|
|
27
|
+
# Or install globally
|
|
28
|
+
npm install -g plexmint
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
| Command | Description |
|
|
34
|
+
|---------|-------------|
|
|
35
|
+
| `plexmint browse` | Browse the marketplace with filters |
|
|
36
|
+
| `plexmint search <query>` | Search prompts by keyword |
|
|
37
|
+
| `plexmint info <ticker>` | Get detailed prompt info |
|
|
38
|
+
| `plexmint install <ticker>` | Download prompt to `.plexmint/` |
|
|
39
|
+
| `plexmint login` | Authenticate with your account |
|
|
40
|
+
| `plexmint logout` | Remove stored credentials |
|
|
41
|
+
| `plexmint library` | View your purchased prompts |
|
|
42
|
+
|
|
43
|
+
## Options
|
|
44
|
+
|
|
45
|
+
### Search & Browse
|
|
46
|
+
- `--category` — Filter: business, writing, code, seo, design, education, data, social-media, personal
|
|
47
|
+
- `--model` — Filter: gpt-4o, gpt-5, claude-opus, claude-sonnet, gemini, midjourney, dall-e, etc.
|
|
48
|
+
- `--sort` — Sort: trending, top_rated, most_sales, newest, price_low, price_high
|
|
49
|
+
- `--limit` — Results per page (max 100)
|
|
50
|
+
|
|
51
|
+
### Install
|
|
52
|
+
- `--dir` — Custom install directory (default: `.plexmint/`)
|
|
53
|
+
- `--force` — Overwrite existing installation
|
|
54
|
+
|
|
55
|
+
### Login
|
|
56
|
+
- `--api-key` — Set API key directly without interactive prompt
|
|
57
|
+
|
|
58
|
+
## What is PlexMint?
|
|
59
|
+
|
|
60
|
+
PlexMint is the AI prompt marketplace where **quality drives price**. Living Prompts start at $1.99 — reviews, demand, and reputation push the price up. Every prompt gets a stock-style ticker symbol (like NUE-SEO01) and a dynamic price that reflects real quality.
|
|
61
|
+
|
|
62
|
+
- **For developers**: Install prompts into your agent workflows via CLI
|
|
63
|
+
- **For AI agents**: Use the API to browse, purchase, and retrieve prompts programmatically
|
|
64
|
+
- **For sellers**: List your prompts and let the market determine their value
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
The CLI uses the PlexMint Agent API v1. Full OpenAPI spec available at:
|
|
69
|
+
https://plexmint.com/api/v1/openapi.json
|
|
70
|
+
|
|
71
|
+
## License
|
|
72
|
+
|
|
73
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk7 from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/commands/search.ts
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import ora from "ora";
|
|
10
|
+
import Table from "cli-table3";
|
|
11
|
+
|
|
12
|
+
// src/lib/api.ts
|
|
13
|
+
var DEFAULT_BASE_URL = "https://plexmint.com/api/v1";
|
|
14
|
+
var PlexMintClient = class {
|
|
15
|
+
baseUrl;
|
|
16
|
+
apiKey;
|
|
17
|
+
constructor(apiKey = null, baseUrl) {
|
|
18
|
+
this.apiKey = apiKey;
|
|
19
|
+
this.baseUrl = baseUrl || DEFAULT_BASE_URL;
|
|
20
|
+
}
|
|
21
|
+
async request(path, options = {}) {
|
|
22
|
+
const headers = {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
"User-Agent": "plexmint-cli/0.1.0",
|
|
25
|
+
...options.headers
|
|
26
|
+
};
|
|
27
|
+
if (this.apiKey) {
|
|
28
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
29
|
+
}
|
|
30
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
31
|
+
...options,
|
|
32
|
+
headers
|
|
33
|
+
});
|
|
34
|
+
const json = await res.json();
|
|
35
|
+
if (!res.ok || !json.success) {
|
|
36
|
+
const error = json;
|
|
37
|
+
throw new PlexMintApiError(
|
|
38
|
+
error.error || `Request failed with status ${res.status}`,
|
|
39
|
+
res.status,
|
|
40
|
+
json
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return json;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Browse/search prompts
|
|
47
|
+
*/
|
|
48
|
+
async browse(options = {}) {
|
|
49
|
+
const params = new URLSearchParams();
|
|
50
|
+
if (options.query) params.set("q", options.query);
|
|
51
|
+
if (options.category) params.set("category", options.category);
|
|
52
|
+
if (options.model) params.set("model", options.model);
|
|
53
|
+
if (options.sort) params.set("sort", options.sort);
|
|
54
|
+
if (options.limit) params.set("limit", options.limit.toString());
|
|
55
|
+
const qs = params.toString();
|
|
56
|
+
return this.request(`/prompts${qs ? `?${qs}` : ""}`);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get a single prompt by ticker
|
|
60
|
+
*/
|
|
61
|
+
async getPrompt(ticker) {
|
|
62
|
+
return this.request(
|
|
63
|
+
`/prompts?ticker=${encodeURIComponent(ticker.toUpperCase())}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Initiate a purchase (requires API key)
|
|
68
|
+
*/
|
|
69
|
+
async purchase(ticker) {
|
|
70
|
+
if (!this.apiKey) {
|
|
71
|
+
throw new PlexMintApiError(
|
|
72
|
+
"Authentication required. Run: plexmint login",
|
|
73
|
+
401,
|
|
74
|
+
{}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return this.request("/purchase", {
|
|
78
|
+
method: "POST",
|
|
79
|
+
body: JSON.stringify({ ticker: ticker.toUpperCase() })
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get prompt content (requires purchase)
|
|
84
|
+
*/
|
|
85
|
+
async getContent(ticker) {
|
|
86
|
+
if (!this.apiKey) {
|
|
87
|
+
throw new PlexMintApiError(
|
|
88
|
+
"Authentication required. Run: plexmint login",
|
|
89
|
+
401,
|
|
90
|
+
{}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return this.request(
|
|
94
|
+
`/library/${encodeURIComponent(ticker.toUpperCase())}/content`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Authenticate with email/password
|
|
99
|
+
*/
|
|
100
|
+
async authenticate(email, password) {
|
|
101
|
+
return this.request("/auth", {
|
|
102
|
+
method: "POST",
|
|
103
|
+
body: JSON.stringify({ email, password })
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var PlexMintApiError = class extends Error {
|
|
108
|
+
status;
|
|
109
|
+
body;
|
|
110
|
+
constructor(message, status, body) {
|
|
111
|
+
super(message);
|
|
112
|
+
this.name = "PlexMintApiError";
|
|
113
|
+
this.status = status;
|
|
114
|
+
this.body = body;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// src/lib/config.ts
|
|
119
|
+
import Conf from "conf";
|
|
120
|
+
var config = new Conf({
|
|
121
|
+
projectName: "plexmint",
|
|
122
|
+
defaults: {
|
|
123
|
+
apiKey: null,
|
|
124
|
+
baseUrl: "https://plexmint.com/api/v1",
|
|
125
|
+
username: null,
|
|
126
|
+
email: null
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
function getApiKey() {
|
|
130
|
+
return config.get("apiKey");
|
|
131
|
+
}
|
|
132
|
+
function setApiKey(key) {
|
|
133
|
+
config.set("apiKey", key);
|
|
134
|
+
}
|
|
135
|
+
function getBaseUrl() {
|
|
136
|
+
return config.get("baseUrl");
|
|
137
|
+
}
|
|
138
|
+
function setUserInfo(username, email) {
|
|
139
|
+
config.set("username", username);
|
|
140
|
+
config.set("email", email);
|
|
141
|
+
}
|
|
142
|
+
function clearAuth() {
|
|
143
|
+
config.set("apiKey", null);
|
|
144
|
+
config.set("username", null);
|
|
145
|
+
config.set("email", null);
|
|
146
|
+
}
|
|
147
|
+
function isAuthenticated() {
|
|
148
|
+
return !!config.get("apiKey");
|
|
149
|
+
}
|
|
150
|
+
function getConfigPath() {
|
|
151
|
+
return config.path;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/commands/search.ts
|
|
155
|
+
async function searchCommand(query, options) {
|
|
156
|
+
const spinner = ora(`Searching for "${query}"...`).start();
|
|
157
|
+
try {
|
|
158
|
+
const client = new PlexMintClient(getApiKey(), getBaseUrl());
|
|
159
|
+
const result = await client.browse({
|
|
160
|
+
query,
|
|
161
|
+
category: options.category,
|
|
162
|
+
model: options.model,
|
|
163
|
+
sort: options.sort || "trending",
|
|
164
|
+
limit: options.limit ? parseInt(options.limit) : 12
|
|
165
|
+
});
|
|
166
|
+
spinner.stop();
|
|
167
|
+
const { items } = result.data;
|
|
168
|
+
if (items.length === 0) {
|
|
169
|
+
console.log(chalk.yellow(`
|
|
170
|
+
No prompts found for "${query}"
|
|
171
|
+
`));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
console.log(
|
|
175
|
+
chalk.bold(`
|
|
176
|
+
Found ${items.length} prompt${items.length > 1 ? "s" : ""}
|
|
177
|
+
`)
|
|
178
|
+
);
|
|
179
|
+
const table = new Table({
|
|
180
|
+
head: [
|
|
181
|
+
chalk.cyan("Ticker"),
|
|
182
|
+
chalk.cyan("Title"),
|
|
183
|
+
chalk.cyan("Price"),
|
|
184
|
+
chalk.cyan("Rating"),
|
|
185
|
+
chalk.cyan("Sales"),
|
|
186
|
+
chalk.cyan("Seller")
|
|
187
|
+
],
|
|
188
|
+
style: { head: [], border: ["gray"] },
|
|
189
|
+
colWidths: [14, 30, 10, 10, 8, 16]
|
|
190
|
+
});
|
|
191
|
+
for (const prompt2 of items) {
|
|
192
|
+
const price = parseFloat(prompt2.currentPrice);
|
|
193
|
+
const rating = parseFloat(prompt2.averageRating);
|
|
194
|
+
table.push([
|
|
195
|
+
chalk.bold(prompt2.ticker),
|
|
196
|
+
prompt2.title.length > 28 ? prompt2.title.slice(0, 25) + "..." : prompt2.title,
|
|
197
|
+
chalk.green(`$${price.toFixed(2)}`),
|
|
198
|
+
rating > 0 ? chalk.yellow(`\u2605 ${rating.toFixed(1)}`) : chalk.gray("\u2014"),
|
|
199
|
+
prompt2.salesCount.toString(),
|
|
200
|
+
prompt2.seller?.username ? chalk.gray(`@${prompt2.seller.username}`) : chalk.gray("\u2014")
|
|
201
|
+
]);
|
|
202
|
+
}
|
|
203
|
+
console.log(table.toString());
|
|
204
|
+
if (result.data.hasMore) {
|
|
205
|
+
console.log(chalk.gray("\n More results available. Use --limit to see more.\n"));
|
|
206
|
+
}
|
|
207
|
+
console.log(
|
|
208
|
+
chalk.gray(` Install a prompt: `) + chalk.white(`plexmint install <TICKER>
|
|
209
|
+
`)
|
|
210
|
+
);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
spinner.stop();
|
|
213
|
+
if (error instanceof PlexMintApiError) {
|
|
214
|
+
console.error(chalk.red(`
|
|
215
|
+
Error: ${error.message}
|
|
216
|
+
`));
|
|
217
|
+
} else {
|
|
218
|
+
console.error(chalk.red(`
|
|
219
|
+
Connection error. Is plexmint.com reachable?
|
|
220
|
+
`));
|
|
221
|
+
}
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/commands/browse.ts
|
|
227
|
+
import chalk2 from "chalk";
|
|
228
|
+
import ora2 from "ora";
|
|
229
|
+
import Table2 from "cli-table3";
|
|
230
|
+
async function browseCommand(options) {
|
|
231
|
+
const spinner = ora2("Browsing PlexMint marketplace...").start();
|
|
232
|
+
try {
|
|
233
|
+
const client = new PlexMintClient(getApiKey(), getBaseUrl());
|
|
234
|
+
const result = await client.browse({
|
|
235
|
+
category: options.category,
|
|
236
|
+
model: options.model,
|
|
237
|
+
sort: options.sort || "trending",
|
|
238
|
+
limit: options.limit ? parseInt(options.limit) : 20
|
|
239
|
+
});
|
|
240
|
+
spinner.stop();
|
|
241
|
+
const { items } = result.data;
|
|
242
|
+
if (items.length === 0) {
|
|
243
|
+
console.log(chalk2.yellow("\n No prompts found.\n"));
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const sortLabel = options.sort || "trending";
|
|
247
|
+
console.log(
|
|
248
|
+
chalk2.bold(`
|
|
249
|
+
PlexMint Marketplace`) + chalk2.gray(` \u2014 sorted by ${sortLabel}
|
|
250
|
+
`)
|
|
251
|
+
);
|
|
252
|
+
const table = new Table2({
|
|
253
|
+
head: [
|
|
254
|
+
chalk2.cyan("Ticker"),
|
|
255
|
+
chalk2.cyan("Title"),
|
|
256
|
+
chalk2.cyan("Category"),
|
|
257
|
+
chalk2.cyan("Price"),
|
|
258
|
+
chalk2.cyan("Rating"),
|
|
259
|
+
chalk2.cyan("Sales")
|
|
260
|
+
],
|
|
261
|
+
style: { head: [], border: ["gray"] },
|
|
262
|
+
colWidths: [14, 30, 14, 10, 10, 8]
|
|
263
|
+
});
|
|
264
|
+
for (const prompt2 of items) {
|
|
265
|
+
const price = parseFloat(prompt2.currentPrice);
|
|
266
|
+
const rating = parseFloat(prompt2.averageRating);
|
|
267
|
+
table.push([
|
|
268
|
+
chalk2.bold(prompt2.ticker),
|
|
269
|
+
prompt2.title.length > 28 ? prompt2.title.slice(0, 25) + "..." : prompt2.title,
|
|
270
|
+
prompt2.category,
|
|
271
|
+
chalk2.green(`$${price.toFixed(2)}`),
|
|
272
|
+
rating > 0 ? chalk2.yellow(`\u2605 ${rating.toFixed(1)}`) : chalk2.gray("\u2014"),
|
|
273
|
+
prompt2.salesCount.toString()
|
|
274
|
+
]);
|
|
275
|
+
}
|
|
276
|
+
console.log(table.toString());
|
|
277
|
+
console.log(
|
|
278
|
+
chalk2.gray("\n Get details: ") + chalk2.white("plexmint info <TICKER>") + chalk2.gray(" | Install: ") + chalk2.white("plexmint install <TICKER>\n")
|
|
279
|
+
);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
spinner.stop();
|
|
282
|
+
if (error instanceof PlexMintApiError) {
|
|
283
|
+
console.error(chalk2.red(`
|
|
284
|
+
Error: ${error.message}
|
|
285
|
+
`));
|
|
286
|
+
} else {
|
|
287
|
+
console.error(chalk2.red("\n Connection error. Is plexmint.com reachable?\n"));
|
|
288
|
+
}
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/commands/info.ts
|
|
294
|
+
import chalk3 from "chalk";
|
|
295
|
+
import ora3 from "ora";
|
|
296
|
+
async function infoCommand(ticker) {
|
|
297
|
+
const spinner = ora3(`Looking up ${ticker.toUpperCase()}...`).start();
|
|
298
|
+
try {
|
|
299
|
+
const client = new PlexMintClient(getApiKey(), getBaseUrl());
|
|
300
|
+
const result = await client.getPrompt(ticker);
|
|
301
|
+
spinner.stop();
|
|
302
|
+
const p = result.data;
|
|
303
|
+
const price = parseFloat(p.currentPrice);
|
|
304
|
+
const rating = parseFloat(p.averageRating);
|
|
305
|
+
console.log("");
|
|
306
|
+
console.log(chalk3.bold.white(` ${p.title}`));
|
|
307
|
+
console.log(chalk3.gray(` ${p.ticker}`) + chalk3.gray(` \xB7 ${p.category}`));
|
|
308
|
+
console.log("");
|
|
309
|
+
console.log(chalk3.white(` ${p.description}`));
|
|
310
|
+
console.log("");
|
|
311
|
+
console.log(
|
|
312
|
+
chalk3.green.bold(` $${price.toFixed(2)}`) + chalk3.gray(" \xB7 ") + (rating > 0 ? chalk3.yellow(`\u2605 ${rating.toFixed(1)}`) : chalk3.gray("No ratings")) + chalk3.gray(" \xB7 ") + chalk3.white(`${p.salesCount} sold`) + chalk3.gray(" \xB7 ") + chalk3.white(`${p.reviewCount} reviews`)
|
|
313
|
+
);
|
|
314
|
+
console.log("");
|
|
315
|
+
if (p.models.length > 0) {
|
|
316
|
+
const modelList = p.models.map((m) => m.isPrimary ? chalk3.cyan.bold(m.model) : chalk3.gray(m.model)).join(", ");
|
|
317
|
+
console.log(chalk3.gray(" Models: ") + modelList);
|
|
318
|
+
}
|
|
319
|
+
if (p.tags && p.tags.length > 0) {
|
|
320
|
+
console.log(
|
|
321
|
+
chalk3.gray(" Tags: ") + p.tags.map((t) => chalk3.gray(`#${t}`)).join(" ")
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
if (p.seller) {
|
|
325
|
+
console.log(
|
|
326
|
+
chalk3.gray(" Seller: ") + chalk3.white(p.seller.name || p.seller.username || "\u2014") + (p.seller.username ? chalk3.gray(` (@${p.seller.username})`) : "")
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (p.tokenEstimate) {
|
|
330
|
+
console.log(chalk3.gray(` Tokens: ~${p.tokenEstimate}`));
|
|
331
|
+
}
|
|
332
|
+
console.log("");
|
|
333
|
+
console.log(chalk3.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"));
|
|
334
|
+
console.log(
|
|
335
|
+
chalk3.gray(" \u2502 ") + chalk3.white("Install:") + chalk3.gray(" \u2502")
|
|
336
|
+
);
|
|
337
|
+
const installCmd = `npx plexmint install ${p.ticker}`;
|
|
338
|
+
const padding = 39 - installCmd.length;
|
|
339
|
+
console.log(
|
|
340
|
+
chalk3.gray(" \u2502 ") + chalk3.cyan.bold(installCmd) + " ".repeat(Math.max(0, padding)) + chalk3.gray("\u2502")
|
|
341
|
+
);
|
|
342
|
+
console.log(chalk3.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"));
|
|
343
|
+
console.log(
|
|
344
|
+
chalk3.gray("\n View on web: ") + chalk3.underline(`https://plexmint.com/prompt/${p.slug}
|
|
345
|
+
`)
|
|
346
|
+
);
|
|
347
|
+
} catch (error) {
|
|
348
|
+
spinner.stop();
|
|
349
|
+
if (error instanceof PlexMintApiError) {
|
|
350
|
+
if (error.status === 404) {
|
|
351
|
+
console.error(
|
|
352
|
+
chalk3.red(`
|
|
353
|
+
Prompt not found: ${ticker.toUpperCase()}
|
|
354
|
+
`)
|
|
355
|
+
);
|
|
356
|
+
} else {
|
|
357
|
+
console.error(chalk3.red(`
|
|
358
|
+
Error: ${error.message}
|
|
359
|
+
`));
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
console.error(chalk3.red("\n Connection error. Is plexmint.com reachable?\n"));
|
|
363
|
+
}
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/commands/install.ts
|
|
369
|
+
import chalk4 from "chalk";
|
|
370
|
+
import ora4 from "ora";
|
|
371
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
372
|
+
import { join, resolve } from "path";
|
|
373
|
+
async function installCommand(ticker, options) {
|
|
374
|
+
const upperTicker = ticker.toUpperCase();
|
|
375
|
+
if (!isAuthenticated()) {
|
|
376
|
+
console.log("");
|
|
377
|
+
console.log(
|
|
378
|
+
chalk4.yellow(" Authentication required to install prompts.")
|
|
379
|
+
);
|
|
380
|
+
console.log(
|
|
381
|
+
chalk4.gray(" Run: ") + chalk4.white("plexmint login") + chalk4.gray(" first\n")
|
|
382
|
+
);
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
const spinner = ora4(`Looking up ${upperTicker}...`).start();
|
|
386
|
+
try {
|
|
387
|
+
const client = new PlexMintClient(getApiKey(), getBaseUrl());
|
|
388
|
+
let content;
|
|
389
|
+
try {
|
|
390
|
+
content = await client.getContent(upperTicker);
|
|
391
|
+
spinner.text = `Downloading ${upperTicker}...`;
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (error instanceof PlexMintApiError && error.status === 403) {
|
|
394
|
+
spinner.stop();
|
|
395
|
+
const info = await client.getPrompt(upperTicker);
|
|
396
|
+
const p = info.data;
|
|
397
|
+
const price = parseFloat(p.currentPrice);
|
|
398
|
+
console.log("");
|
|
399
|
+
console.log(
|
|
400
|
+
chalk4.yellow(` You haven't purchased ${upperTicker} yet.`)
|
|
401
|
+
);
|
|
402
|
+
console.log("");
|
|
403
|
+
console.log(
|
|
404
|
+
chalk4.white(` ${p.title}`) + chalk4.gray(` by ${p.seller?.username || "unknown"}`)
|
|
405
|
+
);
|
|
406
|
+
console.log(chalk4.green.bold(` $${price.toFixed(2)}`));
|
|
407
|
+
console.log("");
|
|
408
|
+
const purchaseSpinner = ora4("Locking price...").start();
|
|
409
|
+
try {
|
|
410
|
+
const purchase = await client.purchase(upperTicker);
|
|
411
|
+
purchaseSpinner.stop();
|
|
412
|
+
console.log(
|
|
413
|
+
chalk4.green(` Price locked at $${purchase.data.lockedPrice} for ${purchase.data.lockDurationSeconds}s`)
|
|
414
|
+
);
|
|
415
|
+
console.log("");
|
|
416
|
+
console.log(
|
|
417
|
+
chalk4.white(" Complete your purchase here:")
|
|
418
|
+
);
|
|
419
|
+
console.log(
|
|
420
|
+
chalk4.cyan.underline(` ${purchase.data.checkoutUrl}`)
|
|
421
|
+
);
|
|
422
|
+
console.log("");
|
|
423
|
+
console.log(
|
|
424
|
+
chalk4.gray(
|
|
425
|
+
" After purchasing, run this command again to download.\n"
|
|
426
|
+
)
|
|
427
|
+
);
|
|
428
|
+
} catch (purchaseError) {
|
|
429
|
+
purchaseSpinner.stop();
|
|
430
|
+
if (purchaseError instanceof PlexMintApiError) {
|
|
431
|
+
if (purchaseError.status === 409) {
|
|
432
|
+
console.log(
|
|
433
|
+
chalk4.yellow(" You already own this prompt. Retrying download...\n")
|
|
434
|
+
);
|
|
435
|
+
content = await client.getContent(upperTicker);
|
|
436
|
+
} else {
|
|
437
|
+
console.error(chalk4.red(`
|
|
438
|
+
Purchase error: ${purchaseError.message}
|
|
439
|
+
`));
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
throw purchaseError;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (!content) return;
|
|
447
|
+
} else {
|
|
448
|
+
throw error;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
spinner.stop();
|
|
452
|
+
const data = content.data;
|
|
453
|
+
const installDir = resolve(options.dir || ".plexmint");
|
|
454
|
+
if (!existsSync(installDir)) {
|
|
455
|
+
mkdirSync(installDir, { recursive: true });
|
|
456
|
+
}
|
|
457
|
+
const promptDir = join(installDir, data.ticker.toLowerCase());
|
|
458
|
+
if (!existsSync(promptDir)) {
|
|
459
|
+
mkdirSync(promptDir, { recursive: true });
|
|
460
|
+
}
|
|
461
|
+
const promptFile = join(promptDir, "prompt.md");
|
|
462
|
+
if (existsSync(promptFile) && !options.force) {
|
|
463
|
+
console.log(
|
|
464
|
+
chalk4.yellow(
|
|
465
|
+
`
|
|
466
|
+
${upperTicker} is already installed. Use --force to overwrite.
|
|
467
|
+
`
|
|
468
|
+
)
|
|
469
|
+
);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
writeFileSync(promptFile, data.promptText, "utf-8");
|
|
473
|
+
const metaFile = join(promptDir, "plexmint.json");
|
|
474
|
+
writeFileSync(
|
|
475
|
+
metaFile,
|
|
476
|
+
JSON.stringify(
|
|
477
|
+
{
|
|
478
|
+
ticker: data.ticker,
|
|
479
|
+
title: data.title,
|
|
480
|
+
version: data.version,
|
|
481
|
+
variables: data.variables,
|
|
482
|
+
tips: data.tips,
|
|
483
|
+
models: data.models,
|
|
484
|
+
tokenEstimate: data.tokenEstimate,
|
|
485
|
+
costPerCall: data.costPerCall,
|
|
486
|
+
exampleInput: data.exampleInput,
|
|
487
|
+
exampleOutput: data.exampleOutput,
|
|
488
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
489
|
+
},
|
|
490
|
+
null,
|
|
491
|
+
2
|
|
492
|
+
),
|
|
493
|
+
"utf-8"
|
|
494
|
+
);
|
|
495
|
+
console.log("");
|
|
496
|
+
console.log(chalk4.green.bold(` \u2713 Installed ${data.ticker}`));
|
|
497
|
+
console.log(chalk4.gray(` ${data.title} v${data.version}`));
|
|
498
|
+
console.log("");
|
|
499
|
+
console.log(chalk4.gray(" Files:"));
|
|
500
|
+
console.log(chalk4.white(` ${promptFile}`) + chalk4.gray(" \u2014 prompt text"));
|
|
501
|
+
console.log(chalk4.white(` ${metaFile}`) + chalk4.gray(" \u2014 metadata"));
|
|
502
|
+
if (data.variables && data.variables.length > 0) {
|
|
503
|
+
console.log("");
|
|
504
|
+
console.log(chalk4.gray(" Variables:"));
|
|
505
|
+
for (const v of data.variables) {
|
|
506
|
+
console.log(chalk4.cyan(` {{${v}}}`));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (data.tips) {
|
|
510
|
+
console.log("");
|
|
511
|
+
console.log(chalk4.gray(" Tips: ") + chalk4.white(data.tips));
|
|
512
|
+
}
|
|
513
|
+
console.log("");
|
|
514
|
+
} catch (error) {
|
|
515
|
+
spinner.stop();
|
|
516
|
+
if (error instanceof PlexMintApiError) {
|
|
517
|
+
if (error.status === 404) {
|
|
518
|
+
console.error(chalk4.red(`
|
|
519
|
+
Prompt not found: ${upperTicker}
|
|
520
|
+
`));
|
|
521
|
+
} else {
|
|
522
|
+
console.error(chalk4.red(`
|
|
523
|
+
Error: ${error.message}
|
|
524
|
+
`));
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
console.error(chalk4.red("\n Connection error. Is plexmint.com reachable?\n"));
|
|
528
|
+
}
|
|
529
|
+
process.exit(1);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/commands/login.ts
|
|
534
|
+
import chalk5 from "chalk";
|
|
535
|
+
import ora5 from "ora";
|
|
536
|
+
import { createInterface } from "readline";
|
|
537
|
+
function prompt(question, hide = false) {
|
|
538
|
+
return new Promise((resolve2) => {
|
|
539
|
+
const rl = createInterface({
|
|
540
|
+
input: process.stdin,
|
|
541
|
+
output: process.stdout
|
|
542
|
+
});
|
|
543
|
+
if (hide) {
|
|
544
|
+
rl.question(question, (answer) => {
|
|
545
|
+
rl.close();
|
|
546
|
+
resolve2(answer);
|
|
547
|
+
});
|
|
548
|
+
} else {
|
|
549
|
+
rl.question(question, (answer) => {
|
|
550
|
+
rl.close();
|
|
551
|
+
resolve2(answer);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
async function loginCommand(options) {
|
|
557
|
+
console.log("");
|
|
558
|
+
console.log(chalk5.bold(" PlexMint Login"));
|
|
559
|
+
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"));
|
|
560
|
+
console.log("");
|
|
561
|
+
if (options.apiKey) {
|
|
562
|
+
setApiKey(options.apiKey);
|
|
563
|
+
console.log(chalk5.green(" \u2713 API key saved."));
|
|
564
|
+
console.log(chalk5.gray(` Config: ${getConfigPath()}
|
|
565
|
+
`));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
if (isAuthenticated()) {
|
|
569
|
+
const answer = await prompt(
|
|
570
|
+
chalk5.yellow(" Already logged in. Re-authenticate? (y/N) ")
|
|
571
|
+
);
|
|
572
|
+
if (answer.toLowerCase() !== "y") {
|
|
573
|
+
console.log(chalk5.gray(" Cancelled.\n"));
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
clearAuth();
|
|
577
|
+
}
|
|
578
|
+
console.log(
|
|
579
|
+
chalk5.gray(
|
|
580
|
+
" Enter your PlexMint credentials to authenticate.\n Your API key will be stored locally.\n"
|
|
581
|
+
)
|
|
582
|
+
);
|
|
583
|
+
const email = await prompt(chalk5.white(" Email: "));
|
|
584
|
+
const password = await prompt(chalk5.white(" Password: "));
|
|
585
|
+
if (!email || !password) {
|
|
586
|
+
console.log(chalk5.red("\n Email and password are required.\n"));
|
|
587
|
+
process.exit(1);
|
|
588
|
+
}
|
|
589
|
+
const spinner = ora5("Authenticating...").start();
|
|
590
|
+
try {
|
|
591
|
+
const client = new PlexMintClient(null, getBaseUrl());
|
|
592
|
+
const result = await client.authenticate(email, password);
|
|
593
|
+
spinner.stop();
|
|
594
|
+
const { user, hasApiKey } = result.data;
|
|
595
|
+
if (hasApiKey) {
|
|
596
|
+
console.log(chalk5.green(`
|
|
597
|
+
\u2713 Authenticated as ${user.username || user.email}`));
|
|
598
|
+
console.log("");
|
|
599
|
+
console.log(
|
|
600
|
+
chalk5.white(" Enter your API key") + chalk5.gray(" (from Settings > API Keys):")
|
|
601
|
+
);
|
|
602
|
+
const key = await prompt(chalk5.white(" API Key: "));
|
|
603
|
+
if (!key) {
|
|
604
|
+
console.log(chalk5.red("\n API key is required.\n"));
|
|
605
|
+
process.exit(1);
|
|
606
|
+
}
|
|
607
|
+
setApiKey(key);
|
|
608
|
+
setUserInfo(user.username || "", email);
|
|
609
|
+
console.log(chalk5.green("\n \u2713 Logged in successfully!"));
|
|
610
|
+
console.log(chalk5.gray(` User: ${user.username || user.email}`));
|
|
611
|
+
console.log(chalk5.gray(` Config: ${getConfigPath()}
|
|
612
|
+
`));
|
|
613
|
+
} else {
|
|
614
|
+
console.log(chalk5.yellow(`
|
|
615
|
+
\u2713 Credentials verified for ${user.email}`));
|
|
616
|
+
console.log(
|
|
617
|
+
chalk5.yellow(
|
|
618
|
+
" No API key found. Generate one at https://plexmint.com/settings"
|
|
619
|
+
)
|
|
620
|
+
);
|
|
621
|
+
console.log(
|
|
622
|
+
chalk5.gray(
|
|
623
|
+
"\n After generating a key, run: " + chalk5.white("plexmint login --api-key <YOUR_KEY>\n")
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
setUserInfo(user.username || "", email);
|
|
627
|
+
}
|
|
628
|
+
} catch (error) {
|
|
629
|
+
spinner.stop();
|
|
630
|
+
if (error instanceof PlexMintApiError) {
|
|
631
|
+
if (error.status === 401) {
|
|
632
|
+
console.error(chalk5.red("\n Invalid email or password.\n"));
|
|
633
|
+
} else {
|
|
634
|
+
console.error(chalk5.red(`
|
|
635
|
+
Error: ${error.message}
|
|
636
|
+
`));
|
|
637
|
+
}
|
|
638
|
+
} else {
|
|
639
|
+
console.error(
|
|
640
|
+
chalk5.red("\n Connection error. Is plexmint.com reachable?\n")
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
process.exit(1);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
async function logoutCommand() {
|
|
647
|
+
clearAuth();
|
|
648
|
+
console.log(chalk5.green("\n \u2713 Logged out. API key removed.\n"));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/commands/library.ts
|
|
652
|
+
import chalk6 from "chalk";
|
|
653
|
+
import ora6 from "ora";
|
|
654
|
+
async function libraryCommand() {
|
|
655
|
+
if (!isAuthenticated()) {
|
|
656
|
+
console.log("");
|
|
657
|
+
console.log(chalk6.yellow(" Authentication required to view your library."));
|
|
658
|
+
console.log(
|
|
659
|
+
chalk6.gray(" Run: ") + chalk6.white("plexmint login") + chalk6.gray(" first\n")
|
|
660
|
+
);
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
const spinner = ora6("Loading your library...").start();
|
|
664
|
+
try {
|
|
665
|
+
const client = new PlexMintClient(getApiKey(), getBaseUrl());
|
|
666
|
+
spinner.stop();
|
|
667
|
+
console.log("");
|
|
668
|
+
console.log(chalk6.bold(" Your Library"));
|
|
669
|
+
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"));
|
|
670
|
+
console.log("");
|
|
671
|
+
console.log(
|
|
672
|
+
chalk6.gray(
|
|
673
|
+
" View your purchased prompts at: "
|
|
674
|
+
) + chalk6.cyan.underline("https://plexmint.com/library")
|
|
675
|
+
);
|
|
676
|
+
console.log("");
|
|
677
|
+
console.log(
|
|
678
|
+
chalk6.gray(" To download a purchased prompt locally:")
|
|
679
|
+
);
|
|
680
|
+
console.log(chalk6.white(" plexmint install <TICKER>\n"));
|
|
681
|
+
} catch (error) {
|
|
682
|
+
spinner.stop();
|
|
683
|
+
if (error instanceof PlexMintApiError) {
|
|
684
|
+
console.error(chalk6.red(`
|
|
685
|
+
Error: ${error.message}
|
|
686
|
+
`));
|
|
687
|
+
} else {
|
|
688
|
+
console.error(chalk6.red("\n Connection error.\n"));
|
|
689
|
+
}
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// src/index.ts
|
|
695
|
+
var program = new Command();
|
|
696
|
+
var BANNER = `
|
|
697
|
+
${chalk7.bold.cyan("plex")}${chalk7.bold.green("mint")} ${chalk7.gray("\u2014 AI prompts that evolve")}
|
|
698
|
+
${chalk7.gray("https://plexmint.com")}
|
|
699
|
+
`;
|
|
700
|
+
program.name("plexmint").description("Browse, install, and manage AI prompts from the PlexMint marketplace").version("0.1.0").addHelpText("before", BANNER);
|
|
701
|
+
program.command("search <query>").description("Search for prompts by keyword").option("-c, --category <category>", "Filter by category (business, writing, code, seo, design, education, data, social-media, personal)").option("-m, --model <model>", "Filter by AI model (gpt-4o, claude-opus, claude-sonnet, gemini, midjourney, etc.)").option("-s, --sort <sort>", "Sort order (trending, top_rated, most_sales, newest, price_low, price_high)", "trending").option("-l, --limit <limit>", "Number of results (max 100)", "12").action(searchCommand);
|
|
702
|
+
program.command("browse").description("Browse the PlexMint marketplace").option("-c, --category <category>", "Filter by category").option("-m, --model <model>", "Filter by AI model").option("-s, --sort <sort>", "Sort order", "trending").option("-l, --limit <limit>", "Number of results", "20").action(browseCommand);
|
|
703
|
+
program.command("info <ticker>").description("Get detailed info about a prompt by its ticker symbol").action(infoCommand);
|
|
704
|
+
program.command("install <ticker>").description("Install a prompt locally (downloads to .plexmint/ directory)").option("-d, --dir <directory>", "Install directory (default: .plexmint)").option("-f, --force", "Overwrite if already installed").action(installCommand);
|
|
705
|
+
program.command("login").description("Authenticate with your PlexMint account").option("--api-key <key>", "Set API key directly (skip interactive login)").action(loginCommand);
|
|
706
|
+
program.command("logout").description("Remove stored API key").action(logoutCommand);
|
|
707
|
+
program.command("library").description("View your purchased prompts").action(libraryCommand);
|
|
708
|
+
program.action(() => {
|
|
709
|
+
console.log(BANNER);
|
|
710
|
+
console.log(chalk7.bold(" Quick Start:"));
|
|
711
|
+
console.log("");
|
|
712
|
+
console.log(chalk7.gray(" Browse marketplace ") + chalk7.white("plexmint browse"));
|
|
713
|
+
console.log(chalk7.gray(" Search prompts ") + chalk7.white('plexmint search "email copywriting"'));
|
|
714
|
+
console.log(chalk7.gray(" Get prompt details ") + chalk7.white("plexmint info NUE-SEO01"));
|
|
715
|
+
console.log(chalk7.gray(" Install a prompt ") + chalk7.white("plexmint install NUE-SEO01"));
|
|
716
|
+
console.log(chalk7.gray(" Login to your account ") + chalk7.white("plexmint login"));
|
|
717
|
+
console.log("");
|
|
718
|
+
console.log(chalk7.gray(" Run ") + chalk7.white("plexmint --help") + chalk7.gray(" for all commands.\n"));
|
|
719
|
+
});
|
|
720
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "plexmint",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "PlexMint CLI — Browse, install, and manage AI prompts from your terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"plexmint": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
14
|
+
"dev": "tsx src/index.ts",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"ai",
|
|
19
|
+
"prompts",
|
|
20
|
+
"marketplace",
|
|
21
|
+
"agent",
|
|
22
|
+
"cli",
|
|
23
|
+
"living-prompts",
|
|
24
|
+
"plexmint"
|
|
25
|
+
],
|
|
26
|
+
"author": "PlexMint <info@plexmint.com>",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/nuesion/plexmint.git",
|
|
31
|
+
"directory": "packages/cli"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://plexmint.com",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"chalk": "^5.4.1",
|
|
39
|
+
"commander": "^13.1.0",
|
|
40
|
+
"conf": "^13.1.0",
|
|
41
|
+
"ora": "^8.2.0",
|
|
42
|
+
"cli-table3": "^0.6.5"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"tsup": "^8.4.0",
|
|
46
|
+
"tsx": "^4.21.0",
|
|
47
|
+
"typescript": "^5.7.0",
|
|
48
|
+
"@types/node": "^20.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|