clishop 0.2.0 → 0.3.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/LICENSE +216 -0
- package/README.md +447 -195
- package/SECURITY.md +25 -0
- package/dist/chunk-RHBOI27D.js +192 -0
- package/dist/chunk-ZF3JIQRK.js +108 -0
- package/dist/config-63FY6NWX.js +24 -0
- package/dist/index.js +975 -727
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.js +14430 -0
- package/package.json +14 -4
- package/server.json +29 -0
- package/.cursor/rules/commit-workflow.mdc +0 -42
- package/src/api.ts +0 -89
- package/src/auth.ts +0 -117
- package/src/commands/address.ts +0 -213
- package/src/commands/advertise.ts +0 -702
- package/src/commands/agent.ts +0 -177
- package/src/commands/auth.ts +0 -122
- package/src/commands/config.ts +0 -56
- package/src/commands/order.ts +0 -334
- package/src/commands/payment.ts +0 -108
- package/src/commands/review.ts +0 -412
- package/src/commands/search.ts +0 -1319
- package/src/commands/setup.ts +0 -644
- package/src/commands/status.ts +0 -131
- package/src/commands/store.ts +0 -302
- package/src/commands/support.ts +0 -264
- package/src/config.ts +0 -127
- package/src/index.ts +0 -80
- package/tsconfig.json +0 -22
package/dist/index.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getApiClient,
|
|
4
|
+
getUserInfo,
|
|
5
|
+
handleApiError,
|
|
6
|
+
isLoggedIn,
|
|
7
|
+
login,
|
|
8
|
+
logout,
|
|
9
|
+
register
|
|
10
|
+
} from "./chunk-RHBOI27D.js";
|
|
11
|
+
import {
|
|
12
|
+
createAgent,
|
|
13
|
+
deleteAgent,
|
|
14
|
+
getActiveAgent,
|
|
15
|
+
getAgent,
|
|
16
|
+
getConfig,
|
|
17
|
+
listAgents,
|
|
18
|
+
setActiveAgent,
|
|
19
|
+
updateAgent
|
|
20
|
+
} from "./chunk-ZF3JIQRK.js";
|
|
2
21
|
|
|
3
22
|
// src/index.ts
|
|
4
23
|
import { Command } from "commander";
|
|
@@ -8,171 +27,21 @@ import chalk15 from "chalk";
|
|
|
8
27
|
import chalk from "chalk";
|
|
9
28
|
import ora from "ora";
|
|
10
29
|
import inquirer from "inquirer";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// src/config.ts
|
|
17
|
-
import Conf from "conf";
|
|
18
|
-
var DEFAULT_AGENT = {
|
|
19
|
-
name: "default",
|
|
20
|
-
requireConfirmation: true,
|
|
21
|
-
maxOrderAmount: 500,
|
|
22
|
-
allowedCategories: [],
|
|
23
|
-
blockedCategories: []
|
|
24
|
-
};
|
|
25
|
-
var config = new Conf({
|
|
26
|
-
projectName: "clishop",
|
|
27
|
-
defaults: {
|
|
28
|
-
activeAgent: "default",
|
|
29
|
-
agents: {
|
|
30
|
-
default: DEFAULT_AGENT
|
|
31
|
-
},
|
|
32
|
-
apiBaseUrl: "https://clishop-backend.vercel.app/api",
|
|
33
|
-
outputFormat: "human",
|
|
34
|
-
setupCompleted: false
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
function getConfig() {
|
|
38
|
-
return config;
|
|
39
|
-
}
|
|
40
|
-
function getActiveAgent() {
|
|
41
|
-
const cfg = config.store;
|
|
42
|
-
const override = process.env.__CLISHOP_AGENT_OVERRIDE;
|
|
43
|
-
if (override && cfg.agents[override]) {
|
|
44
|
-
return cfg.agents[override];
|
|
45
|
-
}
|
|
46
|
-
return cfg.agents[cfg.activeAgent] || cfg.agents["default"];
|
|
47
|
-
}
|
|
48
|
-
function getAgent(name) {
|
|
49
|
-
return config.store.agents[name];
|
|
50
|
-
}
|
|
51
|
-
function setActiveAgent(name) {
|
|
52
|
-
if (!config.store.agents[name]) {
|
|
53
|
-
throw new Error(`Agent "${name}" does not exist. Create it first with: clishop agent create ${name}`);
|
|
54
|
-
}
|
|
55
|
-
config.set("activeAgent", name);
|
|
56
|
-
}
|
|
57
|
-
function createAgent(name, opts = {}) {
|
|
58
|
-
if (config.store.agents[name]) {
|
|
59
|
-
throw new Error(`Agent "${name}" already exists.`);
|
|
60
|
-
}
|
|
61
|
-
const agent = {
|
|
62
|
-
name,
|
|
63
|
-
requireConfirmation: true,
|
|
64
|
-
maxOrderAmount: 500,
|
|
65
|
-
allowedCategories: [],
|
|
66
|
-
blockedCategories: [],
|
|
67
|
-
...opts
|
|
68
|
-
};
|
|
69
|
-
config.set(`agents.${name}`, agent);
|
|
70
|
-
return agent;
|
|
71
|
-
}
|
|
72
|
-
function updateAgent(name, opts) {
|
|
73
|
-
const existing = config.store.agents[name];
|
|
74
|
-
if (!existing) {
|
|
75
|
-
throw new Error(`Agent "${name}" does not exist.`);
|
|
76
|
-
}
|
|
77
|
-
const updated = { ...existing, ...opts, name };
|
|
78
|
-
config.set(`agents.${name}`, updated);
|
|
79
|
-
return updated;
|
|
80
|
-
}
|
|
81
|
-
function deleteAgent(name) {
|
|
82
|
-
if (name === "default") {
|
|
83
|
-
throw new Error('Cannot delete the "default" agent.');
|
|
84
|
-
}
|
|
85
|
-
if (!config.store.agents[name]) {
|
|
86
|
-
throw new Error(`Agent "${name}" does not exist.`);
|
|
87
|
-
}
|
|
88
|
-
const agents = { ...config.store.agents };
|
|
89
|
-
delete agents[name];
|
|
90
|
-
config.set("agents", agents);
|
|
91
|
-
if (config.store.activeAgent === name) {
|
|
92
|
-
config.set("activeAgent", "default");
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function listAgents() {
|
|
96
|
-
return Object.values(config.store.agents);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// src/auth.ts
|
|
100
|
-
var SERVICE_NAME = "clishop";
|
|
101
|
-
var ACCOUNT_TOKEN = "auth-token";
|
|
102
|
-
var ACCOUNT_REFRESH = "refresh-token";
|
|
103
|
-
var ACCOUNT_USER = "user-info";
|
|
104
|
-
async function storeToken(token) {
|
|
105
|
-
await keytar.setPassword(SERVICE_NAME, ACCOUNT_TOKEN, token);
|
|
106
|
-
}
|
|
107
|
-
async function storeRefreshToken(token) {
|
|
108
|
-
await keytar.setPassword(SERVICE_NAME, ACCOUNT_REFRESH, token);
|
|
109
|
-
}
|
|
110
|
-
async function storeUserInfo(user) {
|
|
111
|
-
await keytar.setPassword(SERVICE_NAME, ACCOUNT_USER, JSON.stringify(user));
|
|
112
|
-
}
|
|
113
|
-
async function getToken() {
|
|
114
|
-
return keytar.getPassword(SERVICE_NAME, ACCOUNT_TOKEN);
|
|
115
|
-
}
|
|
116
|
-
async function getRefreshToken() {
|
|
117
|
-
return keytar.getPassword(SERVICE_NAME, ACCOUNT_REFRESH);
|
|
118
|
-
}
|
|
119
|
-
async function getUserInfo() {
|
|
120
|
-
const raw = await keytar.getPassword(SERVICE_NAME, ACCOUNT_USER);
|
|
121
|
-
if (!raw) return null;
|
|
122
|
-
try {
|
|
123
|
-
return JSON.parse(raw);
|
|
124
|
-
} catch {
|
|
125
|
-
return null;
|
|
30
|
+
async function readPasswordFromStdin() {
|
|
31
|
+
const chunks = [];
|
|
32
|
+
for await (const chunk of process.stdin) {
|
|
33
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
126
34
|
}
|
|
35
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
127
36
|
}
|
|
128
|
-
async function clearAuth() {
|
|
129
|
-
await keytar.deletePassword(SERVICE_NAME, ACCOUNT_TOKEN);
|
|
130
|
-
await keytar.deletePassword(SERVICE_NAME, ACCOUNT_REFRESH);
|
|
131
|
-
await keytar.deletePassword(SERVICE_NAME, ACCOUNT_USER);
|
|
132
|
-
}
|
|
133
|
-
async function isLoggedIn() {
|
|
134
|
-
const token = await getToken();
|
|
135
|
-
return !!token;
|
|
136
|
-
}
|
|
137
|
-
async function login(email, password) {
|
|
138
|
-
const config2 = getConfig();
|
|
139
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
140
|
-
const res = await axios.post(`${baseUrl}/auth/login`, { email, password });
|
|
141
|
-
const { token, refreshToken, user } = res.data;
|
|
142
|
-
await storeToken(token);
|
|
143
|
-
if (refreshToken) await storeRefreshToken(refreshToken);
|
|
144
|
-
await storeUserInfo(user);
|
|
145
|
-
return user;
|
|
146
|
-
}
|
|
147
|
-
async function register(email, password, name) {
|
|
148
|
-
const config2 = getConfig();
|
|
149
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
150
|
-
const res = await axios.post(`${baseUrl}/auth/register`, { email, password, name });
|
|
151
|
-
const { token, refreshToken, user } = res.data;
|
|
152
|
-
await storeToken(token);
|
|
153
|
-
if (refreshToken) await storeRefreshToken(refreshToken);
|
|
154
|
-
await storeUserInfo(user);
|
|
155
|
-
return user;
|
|
156
|
-
}
|
|
157
|
-
async function logout() {
|
|
158
|
-
const config2 = getConfig();
|
|
159
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
160
|
-
const token = await getToken();
|
|
161
|
-
if (token) {
|
|
162
|
-
try {
|
|
163
|
-
await axios.post(`${baseUrl}/auth/logout`, {}, {
|
|
164
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
165
|
-
});
|
|
166
|
-
} catch {
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
await clearAuth();
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// src/commands/auth.ts
|
|
173
37
|
function registerAuthCommands(program2) {
|
|
174
|
-
program2.command("login").description("Log in to your CLISHOP account").option("-e, --email <email>", "Email address").option("-p, --password <password>", "Password (
|
|
38
|
+
program2.command("login").description("Log in to your CLISHOP account").option("-e, --email <email>", "Email address").option("-p, --password <password>", "Password (less secure: exposed to shell history)").option("--password-stdin", "Read password from stdin").action(async (opts) => {
|
|
175
39
|
try {
|
|
40
|
+
if (opts.password && opts.passwordStdin) {
|
|
41
|
+
console.error(chalk.red("\n\u2717 Use either --password or --password-stdin, not both."));
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
176
45
|
if (await isLoggedIn()) {
|
|
177
46
|
const user2 = await getUserInfo();
|
|
178
47
|
const { confirm } = await inquirer.prompt([
|
|
@@ -187,6 +56,18 @@ function registerAuthCommands(program2) {
|
|
|
187
56
|
}
|
|
188
57
|
let email = opts.email;
|
|
189
58
|
let password = opts.password;
|
|
59
|
+
if (opts.passwordStdin) {
|
|
60
|
+
password = await readPasswordFromStdin();
|
|
61
|
+
if (!password) {
|
|
62
|
+
console.error(chalk.red("\n\u2717 No password was provided on stdin."));
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (opts.password) {
|
|
68
|
+
console.log(chalk.yellow("\u26A0 Warning: --password can leak credentials via shell history/process list."));
|
|
69
|
+
console.log(chalk.yellow(" Prefer --password-stdin or the interactive masked prompt.\n"));
|
|
70
|
+
}
|
|
190
71
|
if (!email || !password) {
|
|
191
72
|
const answers = await inquirer.prompt([
|
|
192
73
|
...!email ? [{ type: "input", name: "email", message: "Email:" }] : [],
|
|
@@ -273,12 +154,25 @@ function printAgent(agent, isActive) {
|
|
|
273
154
|
console.log(` Default payment: ${agent.defaultPaymentMethodId || chalk2.dim("not set")}`);
|
|
274
155
|
console.log();
|
|
275
156
|
}
|
|
157
|
+
async function printMonthlyLimit() {
|
|
158
|
+
try {
|
|
159
|
+
const api = getApiClient();
|
|
160
|
+
const res = await api.get("/spending-limit");
|
|
161
|
+
const limit = res.data.monthlySpendingLimitInCents;
|
|
162
|
+
console.log(
|
|
163
|
+
chalk2.dim(" Monthly spending limit: ") + chalk2.bold.white(`$${(limit / 100).toFixed(0)}/month`) + chalk2.dim(" (change at https://clishop.ai/agents)")
|
|
164
|
+
);
|
|
165
|
+
console.log();
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
276
169
|
function registerAgentCommands(program2) {
|
|
277
170
|
const agent = program2.command("agent").description("Manage agents (safety profiles for ordering)");
|
|
278
|
-
agent.command("list").alias("ls").description("List all agents").action(() => {
|
|
171
|
+
agent.command("list").alias("ls").description("List all agents").action(async () => {
|
|
279
172
|
const agents = listAgents();
|
|
280
173
|
const active = getConfig().get("activeAgent");
|
|
281
174
|
console.log(chalk2.bold("\nAgents:\n"));
|
|
175
|
+
await printMonthlyLimit();
|
|
282
176
|
for (const a of agents) {
|
|
283
177
|
printAgent(a, a.name === active);
|
|
284
178
|
}
|
|
@@ -332,6 +226,16 @@ function registerAgentCommands(program2) {
|
|
|
332
226
|
process.exitCode = 1;
|
|
333
227
|
return;
|
|
334
228
|
}
|
|
229
|
+
console.log();
|
|
230
|
+
console.log(
|
|
231
|
+
chalk2.dim(
|
|
232
|
+
" Note: To change the monthly spending limit, run:"
|
|
233
|
+
)
|
|
234
|
+
);
|
|
235
|
+
console.log(
|
|
236
|
+
chalk2.dim(" ") + chalk2.white("clishop agent spending-limit <amount>")
|
|
237
|
+
);
|
|
238
|
+
console.log();
|
|
335
239
|
const answers = await inquirer2.prompt([
|
|
336
240
|
{
|
|
337
241
|
type: "number",
|
|
@@ -369,6 +273,79 @@ function registerAgentCommands(program2) {
|
|
|
369
273
|
`));
|
|
370
274
|
printAgent(updated, agentName === getConfig().get("activeAgent"));
|
|
371
275
|
});
|
|
276
|
+
agent.command("spending-limit [amount]").alias("limit").description("View or change your monthly spending limit").action(async (amount) => {
|
|
277
|
+
const api = getApiClient();
|
|
278
|
+
let current;
|
|
279
|
+
try {
|
|
280
|
+
const res = await api.get("/spending-limit");
|
|
281
|
+
current = res.data;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
console.error(chalk2.red(`
|
|
284
|
+
\u2717 ${error?.response?.data?.message || error.message}`));
|
|
285
|
+
process.exitCode = 1;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const currentDollars = (current.monthlySpendingLimitInCents / 100).toFixed(0);
|
|
289
|
+
console.log(
|
|
290
|
+
chalk2.bold(`
|
|
291
|
+
Monthly spending limit: `) + chalk2.bold.cyan(`$${currentDollars}/month`)
|
|
292
|
+
);
|
|
293
|
+
if (!amount) {
|
|
294
|
+
if (current.configured) {
|
|
295
|
+
console.log(
|
|
296
|
+
chalk2.dim(" To change it, run: ") + chalk2.white("clishop agent spending-limit <amount>")
|
|
297
|
+
);
|
|
298
|
+
console.log(
|
|
299
|
+
chalk2.dim(" A confirmation email will be sent to verify the change.\n")
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
console.log(
|
|
303
|
+
chalk2.dim(" Not yet configured. Run: ") + chalk2.white("clishop agent spending-limit <amount>") + chalk2.dim(" to set it.\n")
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const newAmount = parseFloat(amount);
|
|
309
|
+
if (isNaN(newAmount) || newAmount < 1) {
|
|
310
|
+
console.error(chalk2.red("\n\u2717 Please provide a valid amount (minimum $1).\n"));
|
|
311
|
+
process.exitCode = 1;
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const limitInCents = Math.round(newAmount * 100);
|
|
315
|
+
if (limitInCents === current.monthlySpendingLimitInCents) {
|
|
316
|
+
console.log(chalk2.yellow(`
|
|
317
|
+
Already set to $${newAmount}. No change needed.
|
|
318
|
+
`));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const res = await api.patch("/spending-limit", { limitInCents });
|
|
323
|
+
if (res.status === 202 || res.data.pendingLimitInCents) {
|
|
324
|
+
console.log();
|
|
325
|
+
console.log(chalk2.yellow(" \u{1F4E7} Confirmation email sent!"));
|
|
326
|
+
console.log(
|
|
327
|
+
chalk2.dim(" Check your inbox and click the link to confirm the change:")
|
|
328
|
+
);
|
|
329
|
+
console.log(
|
|
330
|
+
chalk2.dim(` $${currentDollars}/month \u2192 $${newAmount}/month`)
|
|
331
|
+
);
|
|
332
|
+
console.log(
|
|
333
|
+
chalk2.dim(" The link expires in 72 hours.\n")
|
|
334
|
+
);
|
|
335
|
+
} else {
|
|
336
|
+
console.log(
|
|
337
|
+
chalk2.green(`
|
|
338
|
+
\u2713 Monthly spending limit set to $${newAmount}/month.
|
|
339
|
+
`)
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
} catch (error) {
|
|
343
|
+
console.error(chalk2.red(`
|
|
344
|
+
\u2717 ${error?.response?.data?.message || error.message}
|
|
345
|
+
`));
|
|
346
|
+
process.exitCode = 1;
|
|
347
|
+
}
|
|
348
|
+
});
|
|
372
349
|
agent.command("delete <name>").alias("rm").description("Delete an agent").action(async (name) => {
|
|
373
350
|
try {
|
|
374
351
|
const { confirm } = await inquirer2.prompt([
|
|
@@ -392,83 +369,9 @@ function registerAgentCommands(program2) {
|
|
|
392
369
|
}
|
|
393
370
|
|
|
394
371
|
// src/commands/address.ts
|
|
395
|
-
import
|
|
372
|
+
import chalk3 from "chalk";
|
|
396
373
|
import ora2 from "ora";
|
|
397
374
|
import inquirer3 from "inquirer";
|
|
398
|
-
|
|
399
|
-
// src/api.ts
|
|
400
|
-
import axios2 from "axios";
|
|
401
|
-
import chalk3 from "chalk";
|
|
402
|
-
var client = null;
|
|
403
|
-
var API_BASE_URL = process.env.CLISHOP_API_URL || "https://clishop-backend.vercel.app/api";
|
|
404
|
-
function getApiClient() {
|
|
405
|
-
if (client) return client;
|
|
406
|
-
const baseUrl = API_BASE_URL;
|
|
407
|
-
client = axios2.create({
|
|
408
|
-
baseURL: baseUrl,
|
|
409
|
-
timeout: 3e4,
|
|
410
|
-
headers: {
|
|
411
|
-
"Content-Type": "application/json"
|
|
412
|
-
}
|
|
413
|
-
});
|
|
414
|
-
client.interceptors.request.use(async (reqConfig) => {
|
|
415
|
-
const token = await getToken();
|
|
416
|
-
if (token) {
|
|
417
|
-
reqConfig.headers.Authorization = `Bearer ${token}`;
|
|
418
|
-
}
|
|
419
|
-
return reqConfig;
|
|
420
|
-
});
|
|
421
|
-
client.interceptors.response.use(
|
|
422
|
-
(res) => res,
|
|
423
|
-
async (error) => {
|
|
424
|
-
if (error.response?.status === 401) {
|
|
425
|
-
const refreshToken = await getRefreshToken();
|
|
426
|
-
if (refreshToken) {
|
|
427
|
-
try {
|
|
428
|
-
const res = await axios2.post(`${baseUrl}/auth/refresh`, { refreshToken });
|
|
429
|
-
await storeToken(res.data.token);
|
|
430
|
-
if (error.config) {
|
|
431
|
-
error.config.headers.Authorization = `Bearer ${res.data.token}`;
|
|
432
|
-
return axios2(error.config);
|
|
433
|
-
}
|
|
434
|
-
} catch {
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
console.error(chalk3.red("\n\u2717 Session expired. Please login again: clishop login\n"));
|
|
438
|
-
process.exit(1);
|
|
439
|
-
}
|
|
440
|
-
throw error;
|
|
441
|
-
}
|
|
442
|
-
);
|
|
443
|
-
return client;
|
|
444
|
-
}
|
|
445
|
-
function handleApiError(error) {
|
|
446
|
-
if (axios2.isAxiosError(error)) {
|
|
447
|
-
const data = error.response?.data;
|
|
448
|
-
const message = data?.message || data?.error || error.message;
|
|
449
|
-
const status = error.response?.status;
|
|
450
|
-
if (status === 422 && data?.errors) {
|
|
451
|
-
console.error(chalk3.red("\n\u2717 Validation errors:"));
|
|
452
|
-
for (const [field, msgs] of Object.entries(data.errors)) {
|
|
453
|
-
console.error(chalk3.red(` ${field}: ${msgs.join(", ")}`));
|
|
454
|
-
}
|
|
455
|
-
} else if (status === 404) {
|
|
456
|
-
console.error(chalk3.red(`
|
|
457
|
-
\u2717 Not found: ${message}`));
|
|
458
|
-
} else {
|
|
459
|
-
console.error(chalk3.red(`
|
|
460
|
-
\u2717 API error (${status || "network"}): ${message}`));
|
|
461
|
-
}
|
|
462
|
-
} else if (error instanceof Error) {
|
|
463
|
-
console.error(chalk3.red(`
|
|
464
|
-
\u2717 ${error.message}`));
|
|
465
|
-
} else {
|
|
466
|
-
console.error(chalk3.red("\n\u2717 An unexpected error occurred."));
|
|
467
|
-
}
|
|
468
|
-
process.exit(1);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// src/commands/address.ts
|
|
472
375
|
function registerAddressCommands(program2) {
|
|
473
376
|
const address = program2.command("address").description("Manage shipping addresses (scoped to the active agent)");
|
|
474
377
|
address.command("list").alias("ls").description("List all addresses for the active agent").action(async () => {
|
|
@@ -482,26 +385,26 @@ function registerAddressCommands(program2) {
|
|
|
482
385
|
spinner.stop();
|
|
483
386
|
const addresses = res.data.addresses;
|
|
484
387
|
if (addresses.length === 0) {
|
|
485
|
-
console.log(
|
|
388
|
+
console.log(chalk3.yellow("\nNo addresses found. Add one with: clishop address add\n"));
|
|
486
389
|
return;
|
|
487
390
|
}
|
|
488
|
-
console.log(
|
|
391
|
+
console.log(chalk3.bold(`
|
|
489
392
|
Addresses for agent "${agent.name}":
|
|
490
393
|
`));
|
|
491
394
|
for (const addr of addresses) {
|
|
492
395
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
493
|
-
const marker = isDefault ?
|
|
494
|
-
console.log(`${marker}${
|
|
396
|
+
const marker = isDefault ? chalk3.green("\u25CF ") : " ";
|
|
397
|
+
console.log(`${marker}${chalk3.bold(addr.label)} ${chalk3.dim(`(${addr.id})`)}`);
|
|
495
398
|
if (addr.recipientName) console.log(` ${addr.recipientName}`);
|
|
496
|
-
if (addr.companyName) console.log(` ${
|
|
399
|
+
if (addr.companyName) console.log(` ${chalk3.cyan(addr.companyName)}`);
|
|
497
400
|
console.log(` ${addr.line1}`);
|
|
498
401
|
if (addr.line2) console.log(` ${addr.line2}`);
|
|
499
402
|
console.log(` ${addr.city}${addr.region ? `, ${addr.region}` : ""} ${addr.postalCode}`);
|
|
500
403
|
console.log(` ${addr.country}`);
|
|
501
|
-
if (addr.recipientPhone) console.log(` ${
|
|
502
|
-
if (addr.vatNumber) console.log(` ${
|
|
503
|
-
if (addr.taxId) console.log(` ${
|
|
504
|
-
if (addr.instructions) console.log(` ${
|
|
404
|
+
if (addr.recipientPhone) console.log(` ${chalk3.dim("Phone:")} ${addr.recipientPhone}`);
|
|
405
|
+
if (addr.vatNumber) console.log(` ${chalk3.dim("VAT:")} ${addr.vatNumber}`);
|
|
406
|
+
if (addr.taxId) console.log(` ${chalk3.dim("Tax ID:")} ${addr.taxId}`);
|
|
407
|
+
if (addr.instructions) console.log(` ${chalk3.dim("Instructions:")} ${addr.instructions}`);
|
|
505
408
|
console.log();
|
|
506
409
|
}
|
|
507
410
|
} catch (error) {
|
|
@@ -591,7 +494,7 @@ Addresses for agent "${agent.name}":
|
|
|
591
494
|
if (setDefault) {
|
|
592
495
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
593
496
|
}
|
|
594
|
-
spinner.succeed(
|
|
497
|
+
spinner.succeed(chalk3.green(`Address "${answers.label}" added.`));
|
|
595
498
|
} catch (error) {
|
|
596
499
|
handleApiError(error);
|
|
597
500
|
}
|
|
@@ -610,7 +513,7 @@ Addresses for agent "${agent.name}":
|
|
|
610
513
|
const spinner = ora2("Removing address...").start();
|
|
611
514
|
const api = getApiClient();
|
|
612
515
|
await api.delete(`/addresses/${id}`);
|
|
613
|
-
spinner.succeed(
|
|
516
|
+
spinner.succeed(chalk3.green("Address removed."));
|
|
614
517
|
const agent = getActiveAgent();
|
|
615
518
|
if (agent.defaultAddressId === id) {
|
|
616
519
|
updateAgent(agent.name, { defaultAddressId: void 0 });
|
|
@@ -622,14 +525,15 @@ Addresses for agent "${agent.name}":
|
|
|
622
525
|
address.command("set-default <id>").description("Set the default address for the active agent").action((id) => {
|
|
623
526
|
const agent = getActiveAgent();
|
|
624
527
|
updateAgent(agent.name, { defaultAddressId: id });
|
|
625
|
-
console.log(
|
|
528
|
+
console.log(chalk3.green(`
|
|
626
529
|
\u2713 Default address for agent "${agent.name}" set to ${id}.`));
|
|
627
530
|
});
|
|
628
531
|
}
|
|
629
532
|
|
|
630
533
|
// src/commands/payment.ts
|
|
631
|
-
import
|
|
534
|
+
import chalk4 from "chalk";
|
|
632
535
|
import ora3 from "ora";
|
|
536
|
+
import open from "open";
|
|
633
537
|
function registerPaymentCommands(program2) {
|
|
634
538
|
const payment = program2.command("payment").description("Manage payment methods (scoped to the active agent)");
|
|
635
539
|
payment.command("list").alias("ls").description("List payment methods for the active agent").action(async () => {
|
|
@@ -643,17 +547,17 @@ function registerPaymentCommands(program2) {
|
|
|
643
547
|
spinner.stop();
|
|
644
548
|
const methods = res.data.paymentMethods;
|
|
645
549
|
if (methods.length === 0) {
|
|
646
|
-
console.log(
|
|
550
|
+
console.log(chalk4.yellow("\nNo payment methods found. Add one with: clishop payment add\n"));
|
|
647
551
|
return;
|
|
648
552
|
}
|
|
649
|
-
console.log(
|
|
553
|
+
console.log(chalk4.bold(`
|
|
650
554
|
Payment methods for agent "${agent.name}":
|
|
651
555
|
`));
|
|
652
556
|
for (const pm of methods) {
|
|
653
557
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
654
|
-
const marker = isDefault ?
|
|
558
|
+
const marker = isDefault ? chalk4.green("\u25CF ") : " ";
|
|
655
559
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
656
|
-
console.log(`${marker}${
|
|
560
|
+
console.log(`${marker}${chalk4.bold(pm.label)}${last4} ${chalk4.dim(`[${pm.type}]`)} ${chalk4.dim(`(${pm.id})`)}`);
|
|
657
561
|
}
|
|
658
562
|
console.log();
|
|
659
563
|
} catch (error) {
|
|
@@ -670,11 +574,12 @@ Payment methods for agent "${agent.name}":
|
|
|
670
574
|
});
|
|
671
575
|
spinner.stop();
|
|
672
576
|
const { setupUrl } = res.data;
|
|
673
|
-
console.log(
|
|
674
|
-
|
|
577
|
+
console.log(chalk4.bold("\nOpening secure payment setup in your browser...\n"));
|
|
578
|
+
await open(setupUrl);
|
|
579
|
+
console.log(chalk4.cyan.underline(` ${setupUrl}
|
|
675
580
|
`));
|
|
676
|
-
console.log(
|
|
677
|
-
console.log(
|
|
581
|
+
console.log(chalk4.dim("The CLI never collects raw card details. Payment is set up via the secure web portal."));
|
|
582
|
+
console.log(chalk4.dim("Once completed, run 'clishop payment list' to see your new method.\n"));
|
|
678
583
|
} catch (error) {
|
|
679
584
|
handleApiError(error);
|
|
680
585
|
}
|
|
@@ -684,7 +589,7 @@ Payment methods for agent "${agent.name}":
|
|
|
684
589
|
const spinner = ora3("Removing payment method...").start();
|
|
685
590
|
const api = getApiClient();
|
|
686
591
|
await api.delete(`/payment-methods/${id}`);
|
|
687
|
-
spinner.succeed(
|
|
592
|
+
spinner.succeed(chalk4.green("Payment method removed."));
|
|
688
593
|
const agent = getActiveAgent();
|
|
689
594
|
if (agent.defaultPaymentMethodId === id) {
|
|
690
595
|
updateAgent(agent.name, { defaultPaymentMethodId: void 0 });
|
|
@@ -696,13 +601,13 @@ Payment methods for agent "${agent.name}":
|
|
|
696
601
|
payment.command("set-default <id>").description("Set the default payment method for the active agent").action((id) => {
|
|
697
602
|
const agent = getActiveAgent();
|
|
698
603
|
updateAgent(agent.name, { defaultPaymentMethodId: id });
|
|
699
|
-
console.log(
|
|
604
|
+
console.log(chalk4.green(`
|
|
700
605
|
\u2713 Default payment for agent "${agent.name}" set to ${id}.`));
|
|
701
606
|
});
|
|
702
607
|
}
|
|
703
608
|
|
|
704
609
|
// src/commands/search.ts
|
|
705
|
-
import
|
|
610
|
+
import chalk5 from "chalk";
|
|
706
611
|
import ora4 from "ora";
|
|
707
612
|
import inquirer4 from "inquirer";
|
|
708
613
|
function formatPrice(cents, currency) {
|
|
@@ -736,7 +641,7 @@ function convertPrice(cents, fromCurrency, toCurrency, rates) {
|
|
|
736
641
|
function formatConverted(cents, fromCurrency, toCurrency, rates) {
|
|
737
642
|
const converted = convertPrice(cents, fromCurrency, toCurrency, rates);
|
|
738
643
|
if (converted == null) return "";
|
|
739
|
-
return
|
|
644
|
+
return chalk5.dim(` (~${formatPrice(converted, toCurrency)})`);
|
|
740
645
|
}
|
|
741
646
|
function renderStars(rating) {
|
|
742
647
|
const full = Math.floor(rating);
|
|
@@ -767,13 +672,13 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
767
672
|
let line = "";
|
|
768
673
|
for (const word of words) {
|
|
769
674
|
if (line.length + word.length + 1 > 90) {
|
|
770
|
-
console.log(`${pad}${
|
|
675
|
+
console.log(`${pad}${chalk5.dim(line)}`);
|
|
771
676
|
line = word;
|
|
772
677
|
} else {
|
|
773
678
|
line = line ? `${line} ${word}` : word;
|
|
774
679
|
}
|
|
775
680
|
}
|
|
776
|
-
if (line) console.log(`${pad}${
|
|
681
|
+
if (line) console.log(`${pad}${chalk5.dim(line)}`);
|
|
777
682
|
} else {
|
|
778
683
|
console.log(`${pad}${data}`);
|
|
779
684
|
}
|
|
@@ -786,12 +691,12 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
786
691
|
if (Array.isArray(data)) {
|
|
787
692
|
for (const item of data) {
|
|
788
693
|
if (typeof item === "string") {
|
|
789
|
-
console.log(`${pad}${
|
|
694
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${item}`);
|
|
790
695
|
} else if (typeof item === "object" && item !== null) {
|
|
791
696
|
renderFreeFormInfo(item, indent + 2);
|
|
792
697
|
console.log();
|
|
793
698
|
} else {
|
|
794
|
-
console.log(`${pad}${
|
|
699
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${String(item)}`);
|
|
795
700
|
}
|
|
796
701
|
}
|
|
797
702
|
return;
|
|
@@ -803,20 +708,20 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
803
708
|
if (value == null) continue;
|
|
804
709
|
if (typeof value === "string") {
|
|
805
710
|
if (value.length > 80) {
|
|
806
|
-
console.log(`${pad}${
|
|
711
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
807
712
|
renderFreeFormInfo(value, indent + 4);
|
|
808
713
|
} else {
|
|
809
|
-
console.log(`${pad}${
|
|
714
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
810
715
|
}
|
|
811
716
|
} else if (typeof value === "number") {
|
|
812
|
-
console.log(`${pad}${
|
|
717
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
813
718
|
} else if (typeof value === "boolean") {
|
|
814
|
-
console.log(`${pad}${
|
|
719
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value ? chalk5.green("Yes") : chalk5.red("No")}`);
|
|
815
720
|
} else if (Array.isArray(value)) {
|
|
816
|
-
console.log(`${pad}${
|
|
721
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
817
722
|
renderFreeFormInfo(value, indent + 4);
|
|
818
723
|
} else if (typeof value === "object") {
|
|
819
|
-
console.log(`${pad}${
|
|
724
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
820
725
|
renderFreeFormInfo(value, indent + 4);
|
|
821
726
|
}
|
|
822
727
|
}
|
|
@@ -824,95 +729,95 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
824
729
|
}
|
|
825
730
|
function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
826
731
|
const num = index + 1;
|
|
827
|
-
const storeBadge = result.storeName ?
|
|
732
|
+
const storeBadge = result.storeName ? chalk5.dim(` from ${result.storeName}`) : "";
|
|
828
733
|
console.log(
|
|
829
|
-
` ${
|
|
734
|
+
` ${chalk5.dim(`[${num}]`)} ${chalk5.bold.cyan(result.info?.title || result.info?.product_id || result.productId)}${storeBadge}`
|
|
830
735
|
);
|
|
831
|
-
console.log(` ${
|
|
736
|
+
console.log(` ${chalk5.dim(`ID: ${result.productId}`)}`);
|
|
832
737
|
if (result.error) {
|
|
833
|
-
console.log(` ${
|
|
738
|
+
console.log(` ${chalk5.red(`Error: ${result.error}`)}`);
|
|
834
739
|
console.log();
|
|
835
740
|
return;
|
|
836
741
|
}
|
|
837
742
|
if (result.info?.product_url) {
|
|
838
|
-
console.log(` ${
|
|
743
|
+
console.log(` ${chalk5.blue.underline(result.info.product_url)}`);
|
|
839
744
|
}
|
|
840
745
|
console.log();
|
|
841
746
|
const info = result.info || {};
|
|
842
747
|
if (info.price) {
|
|
843
748
|
const priceStr = info.price.amount && info.price.currency ? formatPriceFn(Math.round(parseFloat(info.price.amount) * 100), info.price.currency) : `${info.price.amount || "N/A"}`;
|
|
844
|
-
let priceLine = ` ${
|
|
749
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
845
750
|
if (info.list_price?.amount) {
|
|
846
751
|
const listStr = formatPriceFn(
|
|
847
752
|
Math.round(parseFloat(info.list_price.amount) * 100),
|
|
848
753
|
info.list_price.currency || info.price.currency
|
|
849
754
|
);
|
|
850
|
-
priceLine +=
|
|
755
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
851
756
|
}
|
|
852
757
|
console.log(priceLine);
|
|
853
758
|
}
|
|
854
759
|
if (info.pricing && !info.price) {
|
|
855
760
|
const priceStr = info.pricing.amount && info.pricing.currency ? formatPriceFn(Math.round(parseFloat(info.pricing.amount) * 100), info.pricing.currency) : `${info.pricing.amount || "N/A"}`;
|
|
856
|
-
let priceLine = ` ${
|
|
761
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
857
762
|
if (info.pricing.compare_at) {
|
|
858
763
|
const listStr = formatPriceFn(
|
|
859
764
|
Math.round(parseFloat(info.pricing.compare_at) * 100),
|
|
860
765
|
info.pricing.currency
|
|
861
766
|
);
|
|
862
|
-
priceLine +=
|
|
767
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
863
768
|
}
|
|
864
769
|
console.log(priceLine);
|
|
865
770
|
}
|
|
866
771
|
if (info.rating) {
|
|
867
772
|
const ratingScore = typeof info.rating === "object" ? `${info.rating.score}/${info.rating.max}` : String(info.rating);
|
|
868
|
-
let ratingLine = ` ${
|
|
773
|
+
let ratingLine = ` ${chalk5.bold("Rating:")} ${chalk5.yellow(ratingScore)}`;
|
|
869
774
|
if (info.review_count) {
|
|
870
|
-
ratingLine +=
|
|
775
|
+
ratingLine += chalk5.dim(` (${info.review_count.toLocaleString()} reviews)`);
|
|
871
776
|
}
|
|
872
777
|
console.log(ratingLine);
|
|
873
778
|
}
|
|
874
779
|
if (info.brand) {
|
|
875
|
-
console.log(` ${
|
|
780
|
+
console.log(` ${chalk5.bold("Brand:")} ${info.brand}`);
|
|
876
781
|
}
|
|
877
782
|
if (info.marketplace) {
|
|
878
|
-
console.log(` ${
|
|
783
|
+
console.log(` ${chalk5.bold("Marketplace:")} ${info.marketplace.name || info.marketplace.domain || ""}`);
|
|
879
784
|
}
|
|
880
785
|
if (info.availability) {
|
|
881
786
|
if (typeof info.availability === "string") {
|
|
882
787
|
const isInStock = info.availability.toLowerCase().includes("in stock");
|
|
883
|
-
console.log(` ${
|
|
788
|
+
console.log(` ${chalk5.bold("Availability:")} ${isInStock ? chalk5.green(info.availability) : chalk5.yellow(info.availability)}`);
|
|
884
789
|
} else if (typeof info.availability === "object") {
|
|
885
|
-
const status = info.availability.in_stock ?
|
|
886
|
-
let availLine = ` ${
|
|
790
|
+
const status = info.availability.in_stock ? chalk5.green("In Stock") : chalk5.red("Out of Stock");
|
|
791
|
+
let availLine = ` ${chalk5.bold("Availability:")} ${status}`;
|
|
887
792
|
if (info.availability.quantity != null) {
|
|
888
|
-
availLine +=
|
|
793
|
+
availLine += chalk5.dim(` (${info.availability.quantity} available)`);
|
|
889
794
|
}
|
|
890
795
|
console.log(availLine);
|
|
891
796
|
}
|
|
892
797
|
}
|
|
893
798
|
if (info.prime) {
|
|
894
|
-
console.log(` ${
|
|
799
|
+
console.log(` ${chalk5.bold("Prime:")} ${chalk5.blue("\u2713 Prime eligible")}`);
|
|
895
800
|
}
|
|
896
801
|
if (info.shipping && typeof info.shipping === "object") {
|
|
897
802
|
const parts = [];
|
|
898
|
-
if (info.shipping.free) parts.push(
|
|
803
|
+
if (info.shipping.free) parts.push(chalk5.green("Free Shipping"));
|
|
899
804
|
if (info.shipping.estimated_days) parts.push(`${info.shipping.estimated_days}-day delivery`);
|
|
900
805
|
if (info.shipping.price?.amount) parts.push(`${info.shipping.price.amount} ${info.shipping.price.currency || ""}`);
|
|
901
806
|
if (info.shipping.weight_kg) parts.push(`${info.shipping.weight_kg}kg`);
|
|
902
807
|
if (parts.length > 0) {
|
|
903
|
-
console.log(` ${
|
|
808
|
+
console.log(` ${chalk5.bold("Shipping:")} ${parts.join(" \xB7 ")}`);
|
|
904
809
|
}
|
|
905
810
|
}
|
|
906
811
|
if (info.delivery_info) {
|
|
907
|
-
console.log(` ${
|
|
812
|
+
console.log(` ${chalk5.bold("Delivery:")} ${info.delivery_info}`);
|
|
908
813
|
}
|
|
909
814
|
if (info.returns && typeof info.returns === "object") {
|
|
910
815
|
const parts = [];
|
|
911
|
-
if (info.returns.free) parts.push(
|
|
816
|
+
if (info.returns.free) parts.push(chalk5.green("Free Returns"));
|
|
912
817
|
if (info.returns.window_days) parts.push(`${info.returns.window_days}-day window`);
|
|
913
818
|
if (info.returns.note) parts.push(info.returns.note);
|
|
914
819
|
if (parts.length > 0) {
|
|
915
|
-
console.log(` ${
|
|
820
|
+
console.log(` ${chalk5.bold("Returns:")} ${parts.join(" \xB7 ")}`);
|
|
916
821
|
}
|
|
917
822
|
}
|
|
918
823
|
if (info.checkout && typeof info.checkout === "object") {
|
|
@@ -920,18 +825,18 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
920
825
|
if (info.checkout.mode) parts.push(info.checkout.mode);
|
|
921
826
|
if (info.checkout.note) parts.push(info.checkout.note);
|
|
922
827
|
if (parts.length > 0) {
|
|
923
|
-
console.log(` ${
|
|
828
|
+
console.log(` ${chalk5.bold("Checkout:")} ${parts.join(" \u2014 ")}`);
|
|
924
829
|
}
|
|
925
830
|
}
|
|
926
831
|
if (info.sold_by) {
|
|
927
|
-
console.log(` ${
|
|
832
|
+
console.log(` ${chalk5.bold("Sold by:")} ${info.sold_by}`);
|
|
928
833
|
}
|
|
929
834
|
if (info.categories && Array.isArray(info.categories)) {
|
|
930
|
-
console.log(` ${
|
|
835
|
+
console.log(` ${chalk5.bold("Category:")} ${info.categories.join(" > ")}`);
|
|
931
836
|
}
|
|
932
837
|
console.log();
|
|
933
838
|
if (info.features && Array.isArray(info.features) && info.features.length > 0) {
|
|
934
|
-
console.log(` ${
|
|
839
|
+
console.log(` ${chalk5.bold("Key Features:")}`);
|
|
935
840
|
for (const feature of info.features) {
|
|
936
841
|
if (feature.length > 80) {
|
|
937
842
|
const words = feature.split(/\s+/);
|
|
@@ -940,7 +845,7 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
940
845
|
for (const word of words) {
|
|
941
846
|
if (line.length + word.length + 1 > 76) {
|
|
942
847
|
if (first) {
|
|
943
|
-
console.log(` ${
|
|
848
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
944
849
|
first = false;
|
|
945
850
|
} else {
|
|
946
851
|
console.log(` ${line}`);
|
|
@@ -952,75 +857,75 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
952
857
|
}
|
|
953
858
|
if (line) {
|
|
954
859
|
if (first) {
|
|
955
|
-
console.log(` ${
|
|
860
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
956
861
|
} else {
|
|
957
862
|
console.log(` ${line}`);
|
|
958
863
|
}
|
|
959
864
|
}
|
|
960
865
|
} else {
|
|
961
|
-
console.log(` ${
|
|
866
|
+
console.log(` ${chalk5.dim("\u2022")} ${feature}`);
|
|
962
867
|
}
|
|
963
868
|
}
|
|
964
869
|
console.log();
|
|
965
870
|
}
|
|
966
871
|
if (info.description) {
|
|
967
|
-
console.log(` ${
|
|
872
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
968
873
|
const words = info.description.split(/\s+/);
|
|
969
874
|
let line = "";
|
|
970
875
|
for (const word of words) {
|
|
971
876
|
if (line.length + word.length + 1 > 76) {
|
|
972
|
-
console.log(` ${
|
|
877
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
973
878
|
line = word;
|
|
974
879
|
} else {
|
|
975
880
|
line = line ? `${line} ${word}` : word;
|
|
976
881
|
}
|
|
977
882
|
}
|
|
978
|
-
if (line) console.log(` ${
|
|
883
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
979
884
|
console.log();
|
|
980
885
|
}
|
|
981
886
|
if (info.specifications && typeof info.specifications === "object") {
|
|
982
887
|
const specs = info.specifications;
|
|
983
888
|
const keys = Object.keys(specs);
|
|
984
889
|
if (keys.length > 0) {
|
|
985
|
-
console.log(` ${
|
|
890
|
+
console.log(` ${chalk5.bold("Specifications:")}`);
|
|
986
891
|
const maxKeyLen = Math.min(30, Math.max(...keys.map((k) => k.length)));
|
|
987
892
|
for (const [key, value] of Object.entries(specs)) {
|
|
988
893
|
const paddedKey = key.padEnd(maxKeyLen);
|
|
989
|
-
console.log(` ${
|
|
894
|
+
console.log(` ${chalk5.dim(paddedKey)} ${value}`);
|
|
990
895
|
}
|
|
991
896
|
console.log();
|
|
992
897
|
}
|
|
993
898
|
}
|
|
994
899
|
if (info.images && Array.isArray(info.images) && info.images.length > 0) {
|
|
995
|
-
console.log(` ${
|
|
900
|
+
console.log(` ${chalk5.bold("Images:")} ${chalk5.dim(`${info.images.length} available`)}`);
|
|
996
901
|
for (let j = 0; j < Math.min(3, info.images.length); j++) {
|
|
997
|
-
console.log(` ${
|
|
902
|
+
console.log(` ${chalk5.dim(`[${j + 1}]`)} ${chalk5.blue.underline(info.images[j])}`);
|
|
998
903
|
}
|
|
999
904
|
if (info.images.length > 3) {
|
|
1000
|
-
console.log(` ${
|
|
905
|
+
console.log(` ${chalk5.dim(`... and ${info.images.length - 3} more`)}`);
|
|
1001
906
|
}
|
|
1002
907
|
console.log();
|
|
1003
908
|
}
|
|
1004
909
|
if (info.about && Array.isArray(info.about) && info.about.length > 0) {
|
|
1005
|
-
console.log(` ${
|
|
910
|
+
console.log(` ${chalk5.bold("About This Item:")}`);
|
|
1006
911
|
for (const section of info.about) {
|
|
1007
912
|
const words = section.split(/\s+/);
|
|
1008
913
|
let line = "";
|
|
1009
914
|
for (const word of words) {
|
|
1010
915
|
if (line.length + word.length + 1 > 76) {
|
|
1011
|
-
console.log(` ${
|
|
916
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1012
917
|
line = word;
|
|
1013
918
|
} else {
|
|
1014
919
|
line = line ? `${line} ${word}` : word;
|
|
1015
920
|
}
|
|
1016
921
|
}
|
|
1017
|
-
if (line) console.log(` ${
|
|
922
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1018
923
|
console.log();
|
|
1019
924
|
}
|
|
1020
925
|
}
|
|
1021
926
|
if (info.seo && typeof info.seo === "object" && Object.keys(info.seo).length > 0) {
|
|
1022
927
|
if (info.seo.title || info.seo.description) {
|
|
1023
|
-
console.log(` ${
|
|
928
|
+
console.log(` ${chalk5.bold("SEO:")}`);
|
|
1024
929
|
if (info.seo.title) console.log(` Title: ${info.seo.title}`);
|
|
1025
930
|
if (info.seo.description) console.log(` Description: ${info.seo.description}`);
|
|
1026
931
|
console.log();
|
|
@@ -1064,33 +969,33 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
1064
969
|
]);
|
|
1065
970
|
const extraKeys = Object.keys(info).filter((k) => !handledKeys.has(k));
|
|
1066
971
|
if (extraKeys.length > 0) {
|
|
1067
|
-
console.log(` ${
|
|
972
|
+
console.log(` ${chalk5.bold("Additional Information:")}`);
|
|
1068
973
|
for (const key of extraKeys) {
|
|
1069
974
|
const value = info[key];
|
|
1070
975
|
if (value == null) continue;
|
|
1071
976
|
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1072
977
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1073
|
-
console.log(` ${
|
|
978
|
+
console.log(` ${chalk5.dim(label + ":")} ${value}`);
|
|
1074
979
|
} else if (Array.isArray(value)) {
|
|
1075
|
-
console.log(` ${
|
|
980
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1076
981
|
renderFreeFormInfo(value, 10);
|
|
1077
982
|
} else if (typeof value === "object") {
|
|
1078
|
-
console.log(` ${
|
|
983
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1079
984
|
renderFreeFormInfo(value, 10);
|
|
1080
985
|
}
|
|
1081
986
|
}
|
|
1082
987
|
console.log();
|
|
1083
988
|
}
|
|
1084
989
|
if (info.tags && Array.isArray(info.tags) && info.tags.length > 0) {
|
|
1085
|
-
console.log(` ${
|
|
990
|
+
console.log(` ${chalk5.bold("Tags:")} ${info.tags.map((t) => chalk5.dim(`#${t}`)).join(" ")}`);
|
|
1086
991
|
console.log();
|
|
1087
992
|
}
|
|
1088
993
|
if (info.note) {
|
|
1089
|
-
console.log(` ${
|
|
994
|
+
console.log(` ${chalk5.dim(`\u2139 ${info.note}`)}`);
|
|
1090
995
|
console.log();
|
|
1091
996
|
}
|
|
1092
997
|
if (index < totalResults - 1) {
|
|
1093
|
-
console.log(
|
|
998
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1094
999
|
console.log();
|
|
1095
1000
|
}
|
|
1096
1001
|
}
|
|
@@ -1245,21 +1150,21 @@ function registerSearchCommands(program2) {
|
|
|
1245
1150
|
const didExtendedSearch = forceExtended || res.data.extended != null;
|
|
1246
1151
|
if (result.products.length === 0 && (!extended || extended.total === 0)) {
|
|
1247
1152
|
if (didExtendedSearch) {
|
|
1248
|
-
console.log(
|
|
1153
|
+
console.log(chalk5.yellow(`
|
|
1249
1154
|
No results found for "${query}" (searched local catalog + all vendor stores).`));
|
|
1250
1155
|
} else {
|
|
1251
|
-
console.log(
|
|
1156
|
+
console.log(chalk5.yellow(`
|
|
1252
1157
|
No results found for "${query}".`));
|
|
1253
1158
|
}
|
|
1254
1159
|
if (!didExtendedSearch && disableExtended) {
|
|
1255
1160
|
console.log(
|
|
1256
|
-
|
|
1257
|
-
Run: `) +
|
|
1161
|
+
chalk5.dim("\n \u{1F50D} Tip: ") + chalk5.white("Extended search was disabled. Enable it to query vendor stores in real-time:") + chalk5.dim(`
|
|
1162
|
+
Run: `) + chalk5.cyan(`clishop search "${query}" --extended-search`) + chalk5.dim("\n")
|
|
1258
1163
|
);
|
|
1259
1164
|
}
|
|
1260
1165
|
console.log(
|
|
1261
|
-
|
|
1262
|
-
Run: `) +
|
|
1166
|
+
chalk5.dim(" \u{1F4A1} Tip: ") + chalk5.white("Can't find what you need? Advertise your request and let vendors come to you!") + chalk5.dim(`
|
|
1167
|
+
Run: `) + chalk5.cyan(`clishop advertise create`) + chalk5.dim(` or `) + chalk5.cyan(`clishop advertise quick "${query}"`) + chalk5.dim("\n")
|
|
1263
1168
|
);
|
|
1264
1169
|
return;
|
|
1265
1170
|
}
|
|
@@ -1359,15 +1264,15 @@ No results found for "${query}".`));
|
|
|
1359
1264
|
} else {
|
|
1360
1265
|
const totalCount = result.total + (extended?.total || 0);
|
|
1361
1266
|
if (result.products.length > 0 && extended?.total > 0) {
|
|
1362
|
-
console.log(
|
|
1267
|
+
console.log(chalk5.bold(`
|
|
1363
1268
|
Results for "${query}" \u2014 ${result.total} local + ${extended.total} from stores
|
|
1364
1269
|
`));
|
|
1365
1270
|
} else if (extended?.total > 0) {
|
|
1366
|
-
console.log(
|
|
1271
|
+
console.log(chalk5.bold(`
|
|
1367
1272
|
Extended search for "${query}" \u2014 ${extended.total} result(s) from ${extended.storesResponded} store(s)
|
|
1368
1273
|
`));
|
|
1369
1274
|
} else {
|
|
1370
|
-
console.log(
|
|
1275
|
+
console.log(chalk5.bold(`
|
|
1371
1276
|
Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
1372
1277
|
`));
|
|
1373
1278
|
}
|
|
@@ -1380,20 +1285,20 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1380
1285
|
const fastest = allProducts.filter((p) => p.shippingDays != null).sort((a, b) => (a.shippingDays ?? 99) - (b.shippingDays ?? 99))[0];
|
|
1381
1286
|
const bestRated = allProducts.filter((p) => (p.rating ?? 0) > 0).sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0))[0];
|
|
1382
1287
|
const parts = [];
|
|
1383
|
-
parts.push(`${
|
|
1288
|
+
parts.push(`${chalk5.green("Best price:")} ${formatPrice(cheapest.totalCost, cheapest.currency)} at ${cheapest.vendor}`);
|
|
1384
1289
|
if (fastest?.shippingDays != null) {
|
|
1385
|
-
parts.push(`${
|
|
1290
|
+
parts.push(`${chalk5.blue("Fastest:")} ${deliveryLabel(fastest.shippingDays)} at ${fastest.vendor}`);
|
|
1386
1291
|
}
|
|
1387
1292
|
if (bestRated?.rating) {
|
|
1388
|
-
parts.push(`${
|
|
1293
|
+
parts.push(`${chalk5.yellow("Top rated:")} ${scoreOutOf10(bestRated.rating)}/10 at ${bestRated.vendor}`);
|
|
1389
1294
|
}
|
|
1390
|
-
console.log(` ${
|
|
1295
|
+
console.log(` ${chalk5.dim("\u250C")} ${parts.join(chalk5.dim(" \u2502 "))}`);
|
|
1391
1296
|
const prices = withTotal.map((p) => p.totalCost);
|
|
1392
1297
|
const minP = Math.min(...prices);
|
|
1393
1298
|
const maxP = Math.max(...prices);
|
|
1394
1299
|
const avgP = Math.round(prices.reduce((a, b) => a + b, 0) / prices.length);
|
|
1395
1300
|
const curr = allProducts[0].currency;
|
|
1396
|
-
console.log(` ${
|
|
1301
|
+
console.log(` ${chalk5.dim("\u2514")} ${chalk5.dim(`Price range: ${formatPrice(minP, curr)} \u2013 ${formatPrice(maxP, curr)} \xB7 Average: ${formatPrice(avgP, curr)}`)}`);
|
|
1397
1302
|
console.log();
|
|
1398
1303
|
}
|
|
1399
1304
|
for (let i = 0; i < allProducts.length; i++) {
|
|
@@ -1403,53 +1308,53 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1403
1308
|
const shippingPrice = p.freeShipping ? 0 : p.shippingPriceInCents ?? 0;
|
|
1404
1309
|
const totalCost = itemPrice + shippingPrice;
|
|
1405
1310
|
const badges = [];
|
|
1406
|
-
if (i === 0) badges.push(
|
|
1311
|
+
if (i === 0) badges.push(chalk5.bgGreen.black(" BEST MATCH "));
|
|
1407
1312
|
const allCosts = allProducts.map((x) => x.priceInCents + (x.freeShipping ? 0 : x.shippingPriceInCents ?? 0));
|
|
1408
|
-
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(
|
|
1313
|
+
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(chalk5.bgYellow.black(" BEST VALUE "));
|
|
1409
1314
|
const converted = formatConverted(totalCost, p.currency, userCurrency, exchangeRates);
|
|
1410
1315
|
if (opts.compact) {
|
|
1411
1316
|
const priceStr = formatPrice(totalCost, p.currency) + converted;
|
|
1412
1317
|
const store = p.vendor;
|
|
1413
1318
|
const delivery = p.shippingDays != null ? `Arrives ${estimatedArrival(p.shippingDays)}` : "";
|
|
1414
1319
|
console.log(
|
|
1415
|
-
` ${
|
|
1320
|
+
` ${chalk5.dim(`[${num}]`)} ${chalk5.cyan(p.name.length > 60 ? p.name.slice(0, 57) + "..." : p.name)} ${chalk5.bold.white(priceStr)} ${chalk5.dim(store)}${delivery ? " " + chalk5.dim(delivery) : ""}` + (badges.length ? " " + badges.join(" ") : "")
|
|
1416
1321
|
);
|
|
1417
1322
|
continue;
|
|
1418
1323
|
}
|
|
1419
|
-
console.log(` ${
|
|
1420
|
-
let priceLine = ` ${
|
|
1324
|
+
console.log(` ${chalk5.dim(`[${num}]`)} ${chalk5.bold.cyan(p.name)}${badges.length ? " " + badges.join(" ") : ""}`);
|
|
1325
|
+
let priceLine = ` ${chalk5.bold.white(formatPrice(itemPrice, p.currency))}`;
|
|
1421
1326
|
if (p.freeShipping) {
|
|
1422
|
-
priceLine +=
|
|
1327
|
+
priceLine += chalk5.green(" Free Shipping");
|
|
1423
1328
|
} else if (p.shippingPriceInCents != null && (p.shippingPriceInCents ?? 0) > 0) {
|
|
1424
|
-
priceLine +=
|
|
1425
|
-
priceLine +=
|
|
1329
|
+
priceLine += chalk5.dim(` + ${formatPrice(shippingPrice, p.currency)} shipping`);
|
|
1330
|
+
priceLine += chalk5.bold(` = ${formatPrice(totalCost, p.currency)}`);
|
|
1426
1331
|
}
|
|
1427
1332
|
priceLine += converted;
|
|
1428
1333
|
if (p.shippingDays != null) {
|
|
1429
|
-
priceLine +=
|
|
1334
|
+
priceLine += chalk5.blue(` Arrives ${estimatedArrival(p.shippingDays)}`);
|
|
1430
1335
|
}
|
|
1431
1336
|
console.log(priceLine);
|
|
1432
1337
|
const meta = [];
|
|
1433
|
-
const storeBadge = p.storeVerified ?
|
|
1434
|
-
const storeScore = p.storeRating != null ?
|
|
1338
|
+
const storeBadge = p.storeVerified ? chalk5.green(" \u2713") : "";
|
|
1339
|
+
const storeScore = p.storeRating != null ? chalk5.dim(` ${p.storeRating.toFixed(1)}/10`) : chalk5.dim(" (no store rating)");
|
|
1435
1340
|
meta.push(`${p.vendor}${storeBadge}${storeScore}`);
|
|
1436
1341
|
if (p.brand) meta.push(p.brand);
|
|
1437
1342
|
if (p.rating && p.rating > 0) {
|
|
1438
|
-
meta.push(
|
|
1343
|
+
meta.push(chalk5.yellow(`${scoreOutOf10(p.rating)}/10`) + (p.reviewCount ? chalk5.dim(` (${p.reviewCount})`) : ""));
|
|
1439
1344
|
}
|
|
1440
|
-
if (p.isExtended) meta.push(
|
|
1441
|
-
console.log(` ${
|
|
1442
|
-
if (p.id) console.log(` ${
|
|
1345
|
+
if (p.isExtended) meta.push(chalk5.magenta("via extended search"));
|
|
1346
|
+
console.log(` ${chalk5.dim(meta.join(" \xB7 "))}`);
|
|
1347
|
+
if (p.id) console.log(` ${chalk5.dim(`ID: ${p.id}`)}`);
|
|
1443
1348
|
;
|
|
1444
1349
|
if (opts.detailed) {
|
|
1445
|
-
if (p.category) console.log(` ${
|
|
1446
|
-
if (p.variant || p.variantLabel) console.log(` ${
|
|
1350
|
+
if (p.category) console.log(` ${chalk5.dim(`Category: ${p.category}`)}`);
|
|
1351
|
+
if (p.variant || p.variantLabel) console.log(` ${chalk5.dim(`Variant: ${p.variant || p.variantLabel}`)}`);
|
|
1447
1352
|
if (p.description) {
|
|
1448
|
-
console.log(` ${
|
|
1353
|
+
console.log(` ${chalk5.dim(p.description.length > 200 ? p.description.slice(0, 200) + "..." : p.description)}`);
|
|
1449
1354
|
}
|
|
1450
1355
|
} else {
|
|
1451
1356
|
if (p.description) {
|
|
1452
|
-
console.log(` ${
|
|
1357
|
+
console.log(` ${chalk5.dim(p.description.length > 80 ? p.description.slice(0, 80) + "..." : p.description)}`);
|
|
1453
1358
|
}
|
|
1454
1359
|
}
|
|
1455
1360
|
console.log();
|
|
@@ -1459,15 +1364,15 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1459
1364
|
if (opts.interactive && allProducts.length > 0) {
|
|
1460
1365
|
const selectableProducts = allProducts.map((p, idx) => ({ product: p, index: idx })).filter((item) => item.product.id);
|
|
1461
1366
|
if (selectableProducts.length > 0) {
|
|
1462
|
-
console.log(
|
|
1367
|
+
console.log(chalk5.dim(" \u2500".repeat(30)));
|
|
1463
1368
|
console.log();
|
|
1464
1369
|
const choices = selectableProducts.map((item) => {
|
|
1465
1370
|
const p = item.product;
|
|
1466
1371
|
const priceStr = formatPrice(p.priceInCents, p.currency);
|
|
1467
1372
|
const storeStr = p.vendor || "Unknown";
|
|
1468
|
-
const extLabel = p.isExtended ?
|
|
1373
|
+
const extLabel = p.isExtended ? chalk5.magenta(" [store]") : chalk5.dim(" [local]");
|
|
1469
1374
|
return {
|
|
1470
|
-
name: `${
|
|
1375
|
+
name: `${chalk5.cyan(p.name.length > 50 ? p.name.slice(0, 47) + "..." : p.name)} ${chalk5.bold(priceStr)} ${chalk5.dim(storeStr)}${extLabel}`,
|
|
1471
1376
|
value: item.product.id,
|
|
1472
1377
|
short: p.name.length > 40 ? p.name.slice(0, 37) + "..." : p.name
|
|
1473
1378
|
};
|
|
@@ -1499,7 +1404,7 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1499
1404
|
infoSpinner.stop();
|
|
1500
1405
|
const { results: infoResults } = infoRes.data;
|
|
1501
1406
|
if (infoResults && infoResults.length > 0) {
|
|
1502
|
-
console.log(
|
|
1407
|
+
console.log(chalk5.bold(`
|
|
1503
1408
|
Store Information \u2014 ${infoResults.length} result(s)
|
|
1504
1409
|
`));
|
|
1505
1410
|
for (let i = 0; i < infoResults.length; i++) {
|
|
@@ -1507,11 +1412,11 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1507
1412
|
renderProductInfo(infoResult, i, infoResults.length, formatPrice);
|
|
1508
1413
|
}
|
|
1509
1414
|
} else {
|
|
1510
|
-
console.log(
|
|
1415
|
+
console.log(chalk5.yellow("\n No additional information returned from the stores."));
|
|
1511
1416
|
}
|
|
1512
1417
|
} catch (infoErr) {
|
|
1513
1418
|
infoSpinner.stop();
|
|
1514
|
-
console.error(
|
|
1419
|
+
console.error(chalk5.red("\n Failed to fetch product info from stores."));
|
|
1515
1420
|
}
|
|
1516
1421
|
}
|
|
1517
1422
|
if (localIds.length > 0) {
|
|
@@ -1523,31 +1428,31 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1523
1428
|
const p = detailRes.data.product;
|
|
1524
1429
|
if (p) {
|
|
1525
1430
|
console.log();
|
|
1526
|
-
console.log(` ${
|
|
1527
|
-
console.log(` ${
|
|
1528
|
-
if (p.brand) console.log(` ${
|
|
1529
|
-
if (p.model) console.log(` ${
|
|
1530
|
-
if (p.sku) console.log(` ${
|
|
1531
|
-
console.log(` ${
|
|
1532
|
-
const status = p.inStock ?
|
|
1533
|
-
console.log(` ${
|
|
1534
|
-
if (p.freeShipping) console.log(` ${
|
|
1535
|
-
else if (p.shippingPriceInCents != null) console.log(` ${
|
|
1536
|
-
if (p.shippingDays != null) console.log(` ${
|
|
1537
|
-
if (p.freeReturns) console.log(` ${
|
|
1431
|
+
console.log(` ${chalk5.bold.cyan(p.name)}`);
|
|
1432
|
+
console.log(` ${chalk5.dim(`ID: ${p.id}`)}`);
|
|
1433
|
+
if (p.brand) console.log(` ${chalk5.bold("Brand:")} ${p.brand}`);
|
|
1434
|
+
if (p.model) console.log(` ${chalk5.bold("Model:")} ${p.model}`);
|
|
1435
|
+
if (p.sku) console.log(` ${chalk5.bold("SKU:")} ${p.sku}`);
|
|
1436
|
+
console.log(` ${chalk5.bold("Price:")} ${chalk5.bold.white(formatPrice(p.priceInCents, p.currency))}`);
|
|
1437
|
+
const status = p.inStock ? chalk5.green("In Stock") : chalk5.red("Out of Stock");
|
|
1438
|
+
console.log(` ${chalk5.bold("Availability:")} ${status}${p.stockQuantity != null ? chalk5.dim(` (${p.stockQuantity} available)`) : ""}`);
|
|
1439
|
+
if (p.freeShipping) console.log(` ${chalk5.bold("Shipping:")} ${chalk5.green("Free")}`);
|
|
1440
|
+
else if (p.shippingPriceInCents != null) console.log(` ${chalk5.bold("Shipping:")} ${formatPrice(p.shippingPriceInCents, p.currency)}`);
|
|
1441
|
+
if (p.shippingDays != null) console.log(` ${chalk5.bold("Delivery:")} ${deliveryLabel(p.shippingDays)}`);
|
|
1442
|
+
if (p.freeReturns) console.log(` ${chalk5.bold("Returns:")} ${chalk5.green("Free Returns")}${p.returnWindowDays ? ` \xB7 ${p.returnWindowDays}-day window` : ""}`);
|
|
1538
1443
|
if (p.description) {
|
|
1539
|
-
console.log(` ${
|
|
1444
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
1540
1445
|
const words = p.description.split(/\s+/);
|
|
1541
1446
|
let line = "";
|
|
1542
1447
|
for (const word of words) {
|
|
1543
1448
|
if (line.length + word.length + 1 > 76) {
|
|
1544
|
-
console.log(` ${
|
|
1449
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1545
1450
|
line = word;
|
|
1546
1451
|
} else {
|
|
1547
1452
|
line = line ? `${line} ${word}` : word;
|
|
1548
1453
|
}
|
|
1549
1454
|
}
|
|
1550
|
-
if (line) console.log(` ${
|
|
1455
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1551
1456
|
}
|
|
1552
1457
|
console.log();
|
|
1553
1458
|
}
|
|
@@ -1562,26 +1467,26 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1562
1467
|
} else if (hasExtendedProducts && !opts.interactive) {
|
|
1563
1468
|
const sampleIds = extended.products.slice(0, 2).map((ep) => ep.id).join(" ");
|
|
1564
1469
|
console.log(
|
|
1565
|
-
|
|
1566
|
-
Run: `) +
|
|
1470
|
+
chalk5.dim(" \u2139\uFE0F Tip: ") + chalk5.white("Want more details? Request info from the store:") + chalk5.dim(`
|
|
1471
|
+
Run: `) + chalk5.cyan(`clishop info ${sampleIds}`) + chalk5.dim(` or use `) + chalk5.cyan(`--interactive`) + chalk5.dim(` to select interactively`) + chalk5.dim("\n")
|
|
1567
1472
|
);
|
|
1568
1473
|
}
|
|
1569
1474
|
const totalPages = Math.ceil(result.total / result.pageSize);
|
|
1570
1475
|
if (totalPages > 1) {
|
|
1571
|
-
console.log(
|
|
1476
|
+
console.log(chalk5.dim(` Page ${result.page} of ${totalPages}. Use --page to navigate.
|
|
1572
1477
|
`));
|
|
1573
1478
|
}
|
|
1574
1479
|
const showAdvertiseTip = result.page >= totalPages || suggestAdvertise;
|
|
1575
1480
|
if (showAdvertiseTip) {
|
|
1576
1481
|
if (!didExtendedSearch && disableExtended && result.products.length > 0) {
|
|
1577
1482
|
console.log(
|
|
1578
|
-
|
|
1579
|
-
Run: `) +
|
|
1483
|
+
chalk5.dim(" \u{1F50D} Tip: ") + chalk5.white("Want more results? Extended search was disabled. Enable it:") + chalk5.dim(`
|
|
1484
|
+
Run: `) + chalk5.cyan(`clishop search "${query}" --extended-search`) + chalk5.dim("\n")
|
|
1580
1485
|
);
|
|
1581
1486
|
}
|
|
1582
1487
|
console.log(
|
|
1583
|
-
|
|
1584
|
-
Run: `) +
|
|
1488
|
+
chalk5.dim(" \u{1F4A1} Tip: ") + chalk5.white("Didn't find the right match? Advertise your request for vendors to bid on.") + chalk5.dim(`
|
|
1489
|
+
Run: `) + chalk5.cyan(`clishop advertise create`) + chalk5.dim(` or `) + chalk5.cyan(`clishop advertise quick "${query}"`) + chalk5.dim("\n")
|
|
1585
1490
|
);
|
|
1586
1491
|
}
|
|
1587
1492
|
} catch (error) {
|
|
@@ -1599,38 +1504,38 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1599
1504
|
console.log(JSON.stringify(p, null, 2));
|
|
1600
1505
|
return;
|
|
1601
1506
|
}
|
|
1602
|
-
const storeBadge = p.storeVerified ?
|
|
1507
|
+
const storeBadge = p.storeVerified ? chalk5.green(" \u2713 Verified") : "";
|
|
1603
1508
|
console.log();
|
|
1604
|
-
console.log(
|
|
1605
|
-
console.log(
|
|
1606
|
-
if (p.brand) console.log(
|
|
1607
|
-
if (p.model) console.log(
|
|
1608
|
-
if (p.variant) console.log(
|
|
1609
|
-
if (p.sku) console.log(
|
|
1610
|
-
if (p.gtin) console.log(
|
|
1509
|
+
console.log(chalk5.bold.cyan(` ${p.name}`));
|
|
1510
|
+
console.log(chalk5.dim(` ID: ${p.id}`));
|
|
1511
|
+
if (p.brand) console.log(chalk5.dim(` Brand: ${p.brand}`));
|
|
1512
|
+
if (p.model) console.log(chalk5.dim(` Model: ${p.model}`));
|
|
1513
|
+
if (p.variant) console.log(chalk5.dim(` Variant: ${p.variant}`));
|
|
1514
|
+
if (p.sku) console.log(chalk5.dim(` SKU: ${p.sku}`));
|
|
1515
|
+
if (p.gtin) console.log(chalk5.dim(` GTIN: ${p.gtin}`));
|
|
1611
1516
|
console.log();
|
|
1612
|
-
console.log(` Price: ${
|
|
1517
|
+
console.log(` Price: ${chalk5.bold(formatPrice(p.priceInCents, p.currency))}`);
|
|
1613
1518
|
if (p.freeShipping) {
|
|
1614
|
-
console.log(` Shipping: ${
|
|
1519
|
+
console.log(` Shipping: ${chalk5.green("Free")}`);
|
|
1615
1520
|
} else if (p.shippingPriceInCents != null) {
|
|
1616
1521
|
console.log(` Shipping: ${formatPrice(p.shippingPriceInCents, p.currency)}`);
|
|
1617
1522
|
}
|
|
1618
1523
|
if (p.shippingPriceInCents != null || p.freeShipping) {
|
|
1619
1524
|
const total = p.priceInCents + (p.freeShipping ? 0 : p.shippingPriceInCents ?? 0);
|
|
1620
|
-
console.log(` Total: ${
|
|
1525
|
+
console.log(` Total: ${chalk5.bold(formatPrice(total, p.currency))}`);
|
|
1621
1526
|
}
|
|
1622
|
-
const status = p.inStock ?
|
|
1623
|
-
console.log(` Status: ${status}${p.stockQuantity != null ?
|
|
1527
|
+
const status = p.inStock ? chalk5.green("In Stock") : p.backorder ? chalk5.yellow("Backorder") : chalk5.red("Out of Stock");
|
|
1528
|
+
console.log(` Status: ${status}${p.stockQuantity != null ? chalk5.dim(` (${p.stockQuantity} available)`) : ""}`);
|
|
1624
1529
|
if (p.shippingDays != null) console.log(` Delivery: ${deliveryLabel(p.shippingDays)}`);
|
|
1625
|
-
console.log(` Rating: ${
|
|
1530
|
+
console.log(` Rating: ${chalk5.yellow(renderStars(p.rating))} ${chalk5.dim(`(${p.reviewCount} reviews)`)}`);
|
|
1626
1531
|
console.log(` Category: ${p.category}`);
|
|
1627
|
-
console.log(` Store: ${p.vendor}${storeBadge}${p.storeRating != null ?
|
|
1532
|
+
console.log(` Store: ${p.vendor}${storeBadge}${p.storeRating != null ? chalk5.dim(` (${p.storeRating.toFixed(1)} store rating)`) : ""}`);
|
|
1628
1533
|
const returnParts = [];
|
|
1629
|
-
if (p.freeReturns) returnParts.push(
|
|
1534
|
+
if (p.freeReturns) returnParts.push(chalk5.green("Free Returns"));
|
|
1630
1535
|
if (p.returnWindowDays) returnParts.push(`${p.returnWindowDays}-day return window`);
|
|
1631
1536
|
if (returnParts.length) console.log(` Returns: ${returnParts.join(" \xB7 ")}`);
|
|
1632
1537
|
if (p.checkoutMode && p.checkoutMode !== "instant") {
|
|
1633
|
-
console.log(` Checkout: ${
|
|
1538
|
+
console.log(` Checkout: ${chalk5.yellow(p.checkoutMode)}`);
|
|
1634
1539
|
}
|
|
1635
1540
|
console.log();
|
|
1636
1541
|
console.log(` ${p.description}`);
|
|
@@ -1642,13 +1547,13 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1642
1547
|
program2.command("info <ids...>").description("Request detailed information about search result products from their stores").option("--json", "Output raw JSON").action(async (ids, opts) => {
|
|
1643
1548
|
try {
|
|
1644
1549
|
if (ids.length === 0) {
|
|
1645
|
-
console.error(
|
|
1646
|
-
console.log(
|
|
1647
|
-
console.log(
|
|
1550
|
+
console.error(chalk5.red("\n\u2717 Please provide one or more product IDs."));
|
|
1551
|
+
console.log(chalk5.dim(" Usage: clishop info <product-id> [product-id...]"));
|
|
1552
|
+
console.log(chalk5.dim(" Example: clishop info ep_abc123 ep_def456\n"));
|
|
1648
1553
|
process.exit(1);
|
|
1649
1554
|
}
|
|
1650
1555
|
if (ids.length > 20) {
|
|
1651
|
-
console.error(
|
|
1556
|
+
console.error(chalk5.red("\n\u2717 Maximum 20 products per request."));
|
|
1652
1557
|
process.exit(1);
|
|
1653
1558
|
}
|
|
1654
1559
|
const spinner = ora4(`Requesting detailed info for ${ids.length} product(s)...`).start();
|
|
@@ -1665,12 +1570,12 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1665
1570
|
return;
|
|
1666
1571
|
}
|
|
1667
1572
|
if (!results || results.length === 0) {
|
|
1668
|
-
console.log(
|
|
1669
|
-
console.log(
|
|
1670
|
-
console.log(
|
|
1573
|
+
console.log(chalk5.yellow("\nNo information returned for the requested products."));
|
|
1574
|
+
console.log(chalk5.dim(" Make sure you're using product IDs from extended search results (ep_...)."));
|
|
1575
|
+
console.log(chalk5.dim(" Product IDs are shown after each search result.\n"));
|
|
1671
1576
|
return;
|
|
1672
1577
|
}
|
|
1673
|
-
console.log(
|
|
1578
|
+
console.log(chalk5.bold(`
|
|
1674
1579
|
Product Information \u2014 ${total} result(s)
|
|
1675
1580
|
`));
|
|
1676
1581
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -1683,19 +1588,19 @@ Product Information \u2014 ${total} result(s)
|
|
|
1683
1588
|
}
|
|
1684
1589
|
|
|
1685
1590
|
// src/commands/order.ts
|
|
1686
|
-
import
|
|
1591
|
+
import chalk6 from "chalk";
|
|
1687
1592
|
import ora5 from "ora";
|
|
1688
1593
|
import inquirer5 from "inquirer";
|
|
1689
1594
|
function formatPrice2(cents, currency) {
|
|
1690
1595
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
1691
1596
|
}
|
|
1692
1597
|
var STATUS_COLORS = {
|
|
1693
|
-
pending:
|
|
1694
|
-
confirmed:
|
|
1695
|
-
processing:
|
|
1696
|
-
shipped:
|
|
1697
|
-
delivered:
|
|
1698
|
-
cancelled:
|
|
1598
|
+
pending: chalk6.yellow,
|
|
1599
|
+
confirmed: chalk6.blue,
|
|
1600
|
+
processing: chalk6.cyan,
|
|
1601
|
+
shipped: chalk6.magenta,
|
|
1602
|
+
delivered: chalk6.green,
|
|
1603
|
+
cancelled: chalk6.red
|
|
1699
1604
|
};
|
|
1700
1605
|
function registerOrderCommands(program2) {
|
|
1701
1606
|
const order = program2.command("order").description("Place and manage orders");
|
|
@@ -1705,12 +1610,12 @@ function registerOrderCommands(program2) {
|
|
|
1705
1610
|
const addressId = opts.address || agent.defaultAddressId;
|
|
1706
1611
|
const paymentId = opts.payment || agent.defaultPaymentMethodId;
|
|
1707
1612
|
if (!addressId) {
|
|
1708
|
-
console.error(
|
|
1613
|
+
console.error(chalk6.red("\n\u2717 No address set. Add one with: clishop address add"));
|
|
1709
1614
|
process.exitCode = 1;
|
|
1710
1615
|
return;
|
|
1711
1616
|
}
|
|
1712
1617
|
if (!paymentId) {
|
|
1713
|
-
console.error(
|
|
1618
|
+
console.error(chalk6.red("\n\u2717 No payment method set. Add one with: clishop payment add"));
|
|
1714
1619
|
process.exitCode = 1;
|
|
1715
1620
|
return;
|
|
1716
1621
|
}
|
|
@@ -1729,7 +1634,7 @@ function registerOrderCommands(program2) {
|
|
|
1729
1634
|
isExtended = true;
|
|
1730
1635
|
} catch {
|
|
1731
1636
|
prodSpinner.stop();
|
|
1732
|
-
console.error(
|
|
1637
|
+
console.error(chalk6.red(`
|
|
1733
1638
|
\u2717 Product ${productId} not found.`));
|
|
1734
1639
|
process.exitCode = 1;
|
|
1735
1640
|
return;
|
|
@@ -1743,7 +1648,7 @@ function registerOrderCommands(program2) {
|
|
|
1743
1648
|
const totalCents = product.priceInCents * opts.quantity;
|
|
1744
1649
|
if (agent.maxOrderAmount && totalCents / 100 > agent.maxOrderAmount) {
|
|
1745
1650
|
console.error(
|
|
1746
|
-
|
|
1651
|
+
chalk6.red(
|
|
1747
1652
|
`
|
|
1748
1653
|
\u2717 Order total (${formatPrice2(totalCents, product.currency)}) exceeds agent "${agent.name}" limit of $${agent.maxOrderAmount}.`
|
|
1749
1654
|
)
|
|
@@ -1752,23 +1657,23 @@ function registerOrderCommands(program2) {
|
|
|
1752
1657
|
return;
|
|
1753
1658
|
}
|
|
1754
1659
|
if (agent.blockedCategories?.includes(product.category)) {
|
|
1755
|
-
console.error(
|
|
1660
|
+
console.error(chalk6.red(`
|
|
1756
1661
|
\u2717 Category "${product.category}" is blocked for agent "${agent.name}".`));
|
|
1757
1662
|
process.exitCode = 1;
|
|
1758
1663
|
return;
|
|
1759
1664
|
}
|
|
1760
1665
|
if (agent.allowedCategories?.length && !agent.allowedCategories.includes(product.category)) {
|
|
1761
|
-
console.error(
|
|
1666
|
+
console.error(chalk6.red(`
|
|
1762
1667
|
\u2717 Category "${product.category}" is not in the allowed list for agent "${agent.name}".`));
|
|
1763
1668
|
process.exitCode = 1;
|
|
1764
1669
|
return;
|
|
1765
1670
|
}
|
|
1766
1671
|
if (agent.requireConfirmation && !opts.yes) {
|
|
1767
|
-
console.log(
|
|
1672
|
+
console.log(chalk6.bold("\n Order Summary:"));
|
|
1768
1673
|
console.log(` Product: ${product.name}`);
|
|
1769
1674
|
console.log(` Store: ${product.vendor || product.storeName || "\u2014"}`);
|
|
1770
1675
|
console.log(` Quantity: ${opts.quantity}`);
|
|
1771
|
-
console.log(` Total: ${
|
|
1676
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(totalCents, product.currency))}`);
|
|
1772
1677
|
console.log(` Agent: ${agent.name}`);
|
|
1773
1678
|
console.log();
|
|
1774
1679
|
const { confirm } = await inquirer5.prompt([
|
|
@@ -1780,7 +1685,7 @@ function registerOrderCommands(program2) {
|
|
|
1780
1685
|
}
|
|
1781
1686
|
]);
|
|
1782
1687
|
if (!confirm) {
|
|
1783
|
-
console.log(
|
|
1688
|
+
console.log(chalk6.yellow("Order cancelled."));
|
|
1784
1689
|
return;
|
|
1785
1690
|
}
|
|
1786
1691
|
}
|
|
@@ -1791,7 +1696,7 @@ function registerOrderCommands(program2) {
|
|
|
1791
1696
|
shippingAddressId: addressId,
|
|
1792
1697
|
paymentMethodId: paymentId
|
|
1793
1698
|
});
|
|
1794
|
-
spinner.succeed(
|
|
1699
|
+
spinner.succeed(chalk6.green(`Order placed! Order ID: ${chalk6.bold(res.data.order.id)}`));
|
|
1795
1700
|
} catch (error) {
|
|
1796
1701
|
handleApiError(error);
|
|
1797
1702
|
}
|
|
@@ -1813,18 +1718,18 @@ function registerOrderCommands(program2) {
|
|
|
1813
1718
|
return;
|
|
1814
1719
|
}
|
|
1815
1720
|
if (orders.length === 0) {
|
|
1816
|
-
console.log(
|
|
1721
|
+
console.log(chalk6.yellow("\nNo orders found.\n"));
|
|
1817
1722
|
return;
|
|
1818
1723
|
}
|
|
1819
|
-
console.log(
|
|
1724
|
+
console.log(chalk6.bold("\nYour Orders:\n"));
|
|
1820
1725
|
for (const o of orders) {
|
|
1821
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1726
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1822
1727
|
const date = new Date(o.createdAt).toLocaleDateString();
|
|
1823
1728
|
console.log(
|
|
1824
|
-
` ${
|
|
1729
|
+
` ${chalk6.bold(o.id)} ${statusColor(o.status.toUpperCase().padEnd(12))} ${formatPrice2(o.totalAmountInCents, o.currency)} ${chalk6.dim(date)} ${chalk6.dim(`agent: ${o.agent}`)}`
|
|
1825
1730
|
);
|
|
1826
1731
|
for (const item of o.items) {
|
|
1827
|
-
console.log(
|
|
1732
|
+
console.log(chalk6.dim(` \xB7 ${item.productName} \xD7 ${item.quantity}`));
|
|
1828
1733
|
}
|
|
1829
1734
|
console.log();
|
|
1830
1735
|
}
|
|
@@ -1843,26 +1748,26 @@ function registerOrderCommands(program2) {
|
|
|
1843
1748
|
console.log(JSON.stringify(o, null, 2));
|
|
1844
1749
|
return;
|
|
1845
1750
|
}
|
|
1846
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1751
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1847
1752
|
console.log();
|
|
1848
|
-
console.log(
|
|
1753
|
+
console.log(chalk6.bold(` Order ${o.id}`));
|
|
1849
1754
|
console.log(` Status: ${statusColor(o.status.toUpperCase())}`);
|
|
1850
|
-
console.log(` Total: ${
|
|
1755
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(o.totalAmountInCents, o.currency))}`);
|
|
1851
1756
|
if (o.storeName) console.log(` Store: ${o.storeName}`);
|
|
1852
1757
|
console.log(` Agent: ${o.agent}`);
|
|
1853
1758
|
if (o.paymentLabel) console.log(` Payment: ${o.paymentLabel}`);
|
|
1854
1759
|
console.log(` Placed: ${new Date(o.createdAt).toLocaleString()}`);
|
|
1855
1760
|
console.log(` Updated: ${new Date(o.updatedAt).toLocaleString()}`);
|
|
1856
1761
|
if (o.externalOrderId) {
|
|
1857
|
-
console.log(` eBay Ref: ${
|
|
1762
|
+
console.log(` eBay Ref: ${chalk6.dim(o.externalOrderId)}`);
|
|
1858
1763
|
}
|
|
1859
1764
|
if (o.shipments?.length) {
|
|
1860
1765
|
for (const s of o.shipments) {
|
|
1861
1766
|
console.log(` Tracking: ${s.trackingNumber || "pending"} ${s.carrier ? `(${s.carrier})` : ""}`);
|
|
1862
|
-
if (s.trackingUrl) console.log(` Track: ${
|
|
1767
|
+
if (s.trackingUrl) console.log(` Track: ${chalk6.cyan.underline(s.trackingUrl)}`);
|
|
1863
1768
|
}
|
|
1864
1769
|
}
|
|
1865
|
-
console.log(
|
|
1770
|
+
console.log(chalk6.bold("\n Items:"));
|
|
1866
1771
|
for (const item of o.items) {
|
|
1867
1772
|
console.log(` \xB7 ${item.productName} \xD7 ${item.quantity} ${formatPrice2(item.totalPriceInCents, o.currency)}`);
|
|
1868
1773
|
}
|
|
@@ -1880,14 +1785,14 @@ function registerOrderCommands(program2) {
|
|
|
1880
1785
|
FULFILLED: "Fulfilled",
|
|
1881
1786
|
CANCELLED: "Cancelled"
|
|
1882
1787
|
};
|
|
1883
|
-
console.log(
|
|
1884
|
-
console.log(` ${
|
|
1788
|
+
console.log(chalk6.bold("\n eBay Status:"));
|
|
1789
|
+
console.log(` ${chalk6.cyan(STATUS_MAP[ebayStatus] || ebayStatus)}`);
|
|
1885
1790
|
for (const li of tracking.line_items || []) {
|
|
1886
1791
|
if (li.tracking_number) {
|
|
1887
|
-
console.log(` Tracking: ${
|
|
1792
|
+
console.log(` Tracking: ${chalk6.bold(li.tracking_number)}${li.carrier ? ` (${li.carrier})` : ""}`);
|
|
1888
1793
|
}
|
|
1889
1794
|
if (li.tracking_url) {
|
|
1890
|
-
console.log(` Track: ${
|
|
1795
|
+
console.log(` Track: ${chalk6.cyan.underline(li.tracking_url)}`);
|
|
1891
1796
|
}
|
|
1892
1797
|
if (li.estimated_delivery) {
|
|
1893
1798
|
const eta = new Date(li.estimated_delivery).toLocaleDateString();
|
|
@@ -1895,7 +1800,7 @@ function registerOrderCommands(program2) {
|
|
|
1895
1800
|
}
|
|
1896
1801
|
}
|
|
1897
1802
|
} else if (trackRes.data.message) {
|
|
1898
|
-
console.log(
|
|
1803
|
+
console.log(chalk6.dim(`
|
|
1899
1804
|
Vendor: ${trackRes.data.message}`));
|
|
1900
1805
|
}
|
|
1901
1806
|
} catch {
|
|
@@ -1920,7 +1825,7 @@ function registerOrderCommands(program2) {
|
|
|
1920
1825
|
const spinner = ora5("Cancelling order...").start();
|
|
1921
1826
|
const api = getApiClient();
|
|
1922
1827
|
await api.post(`/orders/${id}/cancel`);
|
|
1923
|
-
spinner.succeed(
|
|
1828
|
+
spinner.succeed(chalk6.green("Order cancelled."));
|
|
1924
1829
|
} catch (error) {
|
|
1925
1830
|
handleApiError(error);
|
|
1926
1831
|
}
|
|
@@ -1928,17 +1833,17 @@ function registerOrderCommands(program2) {
|
|
|
1928
1833
|
}
|
|
1929
1834
|
|
|
1930
1835
|
// src/commands/review.ts
|
|
1931
|
-
import
|
|
1836
|
+
import chalk7 from "chalk";
|
|
1932
1837
|
import ora6 from "ora";
|
|
1933
1838
|
import inquirer6 from "inquirer";
|
|
1934
1839
|
function renderStars2(rating) {
|
|
1935
1840
|
const filled = Math.round(rating);
|
|
1936
1841
|
const empty = 10 - filled;
|
|
1937
|
-
return
|
|
1842
|
+
return chalk7.yellow("\u2605".repeat(filled) + "\u2606".repeat(empty));
|
|
1938
1843
|
}
|
|
1939
1844
|
function renderRating(rating) {
|
|
1940
|
-
if (rating === 0) return
|
|
1941
|
-
const color = rating >= 8 ?
|
|
1845
|
+
if (rating === 0) return chalk7.dim("No ratings yet");
|
|
1846
|
+
const color = rating >= 8 ? chalk7.green : rating >= 5 ? chalk7.yellow : chalk7.red;
|
|
1942
1847
|
return `${renderStars2(rating)} ${color(`${rating.toFixed(1)}/10`)}`;
|
|
1943
1848
|
}
|
|
1944
1849
|
var RATING_CHOICES = [
|
|
@@ -1972,17 +1877,17 @@ function registerReviewCommands(program2) {
|
|
|
1972
1877
|
const unreviewedItems = reviewable.items.filter((i) => !i.alreadyReviewed);
|
|
1973
1878
|
const storeAlreadyReviewed = reviewable.store.alreadyReviewed;
|
|
1974
1879
|
if (unreviewedItems.length === 0 && storeAlreadyReviewed) {
|
|
1975
|
-
console.log(
|
|
1880
|
+
console.log(chalk7.yellow("\nYou've already reviewed everything in this order.\n"));
|
|
1976
1881
|
return;
|
|
1977
1882
|
}
|
|
1978
|
-
console.log(
|
|
1979
|
-
Review Order ${
|
|
1980
|
-
console.log(
|
|
1883
|
+
console.log(chalk7.bold(`
|
|
1884
|
+
Review Order ${chalk7.cyan(orderId)}`));
|
|
1885
|
+
console.log(chalk7.dim(` Store: ${reviewable.store.name}
|
|
1981
1886
|
`));
|
|
1982
1887
|
const itemReviews = [];
|
|
1983
1888
|
let storeReview = void 0;
|
|
1984
1889
|
for (const item of unreviewedItems) {
|
|
1985
|
-
console.log(
|
|
1890
|
+
console.log(chalk7.bold(` Product: ${item.productName}`));
|
|
1986
1891
|
const { wantReview } = await inquirer6.prompt([
|
|
1987
1892
|
{
|
|
1988
1893
|
type: "confirm",
|
|
@@ -2018,11 +1923,11 @@ function registerReviewCommands(program2) {
|
|
|
2018
1923
|
title: answers.title.trim(),
|
|
2019
1924
|
body: answers.body.trim()
|
|
2020
1925
|
});
|
|
2021
|
-
console.log(
|
|
1926
|
+
console.log(chalk7.dim(` \u2713 ${item.productName}: ${answers.rating}/10
|
|
2022
1927
|
`));
|
|
2023
1928
|
}
|
|
2024
1929
|
if (!storeAlreadyReviewed) {
|
|
2025
|
-
console.log(
|
|
1930
|
+
console.log(chalk7.bold(` Store: ${reviewable.store.name}`));
|
|
2026
1931
|
const { wantStoreReview } = await inquirer6.prompt([
|
|
2027
1932
|
{
|
|
2028
1933
|
type: "confirm",
|
|
@@ -2060,7 +1965,7 @@ function registerReviewCommands(program2) {
|
|
|
2060
1965
|
}
|
|
2061
1966
|
}
|
|
2062
1967
|
if (itemReviews.length === 0 && !storeReview) {
|
|
2063
|
-
console.log(
|
|
1968
|
+
console.log(chalk7.yellow("\nNo reviews submitted.\n"));
|
|
2064
1969
|
return;
|
|
2065
1970
|
}
|
|
2066
1971
|
const submitSpinner = ora6("Submitting reviews...").start();
|
|
@@ -2069,17 +1974,17 @@ function registerReviewCommands(program2) {
|
|
|
2069
1974
|
itemReviews,
|
|
2070
1975
|
storeReview
|
|
2071
1976
|
});
|
|
2072
|
-
submitSpinner.succeed(
|
|
1977
|
+
submitSpinner.succeed(chalk7.green("Reviews submitted!"));
|
|
2073
1978
|
const data = res.data;
|
|
2074
1979
|
if (data.productReviews?.length > 0) {
|
|
2075
|
-
console.log(
|
|
1980
|
+
console.log(chalk7.bold("\n Product Reviews:"));
|
|
2076
1981
|
for (const r of data.productReviews) {
|
|
2077
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
1982
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
2078
1983
|
}
|
|
2079
1984
|
}
|
|
2080
1985
|
if (data.storeReview && !data.storeReview.skipped) {
|
|
2081
|
-
console.log(
|
|
2082
|
-
console.log(` ${renderStars2(data.storeReview.rating)} ${
|
|
1986
|
+
console.log(chalk7.bold("\n Store Review:"));
|
|
1987
|
+
console.log(` ${renderStars2(data.storeReview.rating)} ${chalk7.bold(data.storeReview.title)}`);
|
|
2083
1988
|
}
|
|
2084
1989
|
console.log();
|
|
2085
1990
|
} catch (err) {
|
|
@@ -2114,10 +2019,10 @@ function registerReviewCommands(program2) {
|
|
|
2114
2019
|
body: answers.body.trim(),
|
|
2115
2020
|
orderId: opts.order || void 0
|
|
2116
2021
|
});
|
|
2117
|
-
spinner.succeed(
|
|
2022
|
+
spinner.succeed(chalk7.green("Review submitted!"));
|
|
2118
2023
|
console.log(`
|
|
2119
|
-
${renderRating(answers.rating)} ${
|
|
2120
|
-
console.log(
|
|
2024
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
2025
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2121
2026
|
`));
|
|
2122
2027
|
} catch (error) {
|
|
2123
2028
|
handleApiError(error);
|
|
@@ -2147,10 +2052,10 @@ function registerReviewCommands(program2) {
|
|
|
2147
2052
|
body: answers.body.trim(),
|
|
2148
2053
|
orderId: opts.order || void 0
|
|
2149
2054
|
});
|
|
2150
|
-
spinner.succeed(
|
|
2055
|
+
spinner.succeed(chalk7.green("Store review submitted!"));
|
|
2151
2056
|
console.log(`
|
|
2152
|
-
${renderRating(answers.rating)} ${
|
|
2153
|
-
console.log(
|
|
2057
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
2058
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2154
2059
|
`));
|
|
2155
2060
|
} catch (error) {
|
|
2156
2061
|
handleApiError(error);
|
|
@@ -2168,25 +2073,25 @@ function registerReviewCommands(program2) {
|
|
|
2168
2073
|
return;
|
|
2169
2074
|
}
|
|
2170
2075
|
if (productReviews.length === 0 && storeReviews.length === 0) {
|
|
2171
|
-
console.log(
|
|
2076
|
+
console.log(chalk7.yellow("\nYou haven't written any reviews yet.\n"));
|
|
2172
2077
|
return;
|
|
2173
2078
|
}
|
|
2174
2079
|
if (productReviews.length > 0) {
|
|
2175
|
-
console.log(
|
|
2080
|
+
console.log(chalk7.bold("\nProduct Reviews:\n"));
|
|
2176
2081
|
for (const r of productReviews) {
|
|
2177
2082
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2178
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2179
|
-
console.log(` ${
|
|
2083
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
2084
|
+
console.log(` ${chalk7.dim(`on ${r.productName}`)} ${chalk7.dim(date)}`);
|
|
2180
2085
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2181
2086
|
console.log();
|
|
2182
2087
|
}
|
|
2183
2088
|
}
|
|
2184
2089
|
if (storeReviews.length > 0) {
|
|
2185
|
-
console.log(
|
|
2090
|
+
console.log(chalk7.bold("Store Reviews:\n"));
|
|
2186
2091
|
for (const r of storeReviews) {
|
|
2187
2092
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2188
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2189
|
-
console.log(` ${
|
|
2093
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
2094
|
+
console.log(` ${chalk7.dim(`store: ${r.storeName}`)} ${chalk7.dim(date)}`);
|
|
2190
2095
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2191
2096
|
console.log();
|
|
2192
2097
|
}
|
|
@@ -2205,7 +2110,7 @@ function registerReviewCommands(program2) {
|
|
|
2205
2110
|
const { rating } = res.data;
|
|
2206
2111
|
const entity = opts.store ? res.data.store : res.data.product;
|
|
2207
2112
|
console.log();
|
|
2208
|
-
console.log(
|
|
2113
|
+
console.log(chalk7.bold(` ${entity.name}`));
|
|
2209
2114
|
console.log(` ${renderRating(rating.displayRating)}`);
|
|
2210
2115
|
console.log();
|
|
2211
2116
|
console.log(` Reviews: ${rating.reviewCount}`);
|
|
@@ -2213,11 +2118,11 @@ function registerReviewCommands(program2) {
|
|
|
2213
2118
|
console.log(` Bayesian Avg: ${rating.bayesianAverage.toFixed(2)}`);
|
|
2214
2119
|
console.log(` Effective Cap: ${rating.effectiveCeiling.toFixed(1)}`);
|
|
2215
2120
|
if (rating.isCapped) {
|
|
2216
|
-
console.log(
|
|
2121
|
+
console.log(chalk7.yellow(` \u26A0 Rating is capped \u2014 needs more orders to unlock higher rating`));
|
|
2217
2122
|
if (rating.totalOrders < 100) {
|
|
2218
|
-
console.log(
|
|
2123
|
+
console.log(chalk7.dim(` ${100 - rating.totalOrders} more orders needed to unlock 8.0+ rating`));
|
|
2219
2124
|
} else if (rating.totalOrders < 1e3) {
|
|
2220
|
-
console.log(
|
|
2125
|
+
console.log(chalk7.dim(` ${1e3 - rating.totalOrders} more orders needed to unlock 9.0+ rating`));
|
|
2221
2126
|
}
|
|
2222
2127
|
}
|
|
2223
2128
|
console.log();
|
|
@@ -2240,7 +2145,7 @@ function registerReviewCommands(program2) {
|
|
|
2240
2145
|
const api = getApiClient();
|
|
2241
2146
|
const endpoint = opts.store ? `/store-reviews/${reviewId}` : `/reviews/${reviewId}`;
|
|
2242
2147
|
await api.delete(endpoint);
|
|
2243
|
-
spinner.succeed(
|
|
2148
|
+
spinner.succeed(chalk7.green("Review deleted."));
|
|
2244
2149
|
} catch (error) {
|
|
2245
2150
|
handleApiError(error);
|
|
2246
2151
|
}
|
|
@@ -2248,41 +2153,41 @@ function registerReviewCommands(program2) {
|
|
|
2248
2153
|
}
|
|
2249
2154
|
|
|
2250
2155
|
// src/commands/config.ts
|
|
2251
|
-
import
|
|
2156
|
+
import chalk8 from "chalk";
|
|
2252
2157
|
function registerConfigCommands(program2) {
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2158
|
+
const config = program2.command("config").description("View and manage CLI configuration");
|
|
2159
|
+
config.command("show").description("Show current configuration").action(() => {
|
|
2255
2160
|
const cfg = getConfig();
|
|
2256
|
-
console.log(
|
|
2257
|
-
console.log(` Active agent: ${
|
|
2258
|
-
console.log(` Output format: ${
|
|
2259
|
-
console.log(` Config path: ${
|
|
2161
|
+
console.log(chalk8.bold("\nCLISHOP Configuration:\n"));
|
|
2162
|
+
console.log(` Active agent: ${chalk8.cyan(cfg.get("activeAgent"))}`);
|
|
2163
|
+
console.log(` Output format: ${chalk8.cyan(cfg.get("outputFormat"))}`);
|
|
2164
|
+
console.log(` Config path: ${chalk8.dim(cfg.path)}`);
|
|
2260
2165
|
console.log();
|
|
2261
2166
|
});
|
|
2262
|
-
|
|
2167
|
+
config.command("set-output <format>").description("Set output format: human or json").action((format) => {
|
|
2263
2168
|
if (format !== "human" && format !== "json") {
|
|
2264
|
-
console.error(
|
|
2169
|
+
console.error(chalk8.red('\u2717 Format must be "human" or "json".'));
|
|
2265
2170
|
process.exitCode = 1;
|
|
2266
2171
|
return;
|
|
2267
2172
|
}
|
|
2268
2173
|
const cfg = getConfig();
|
|
2269
2174
|
cfg.set("outputFormat", format);
|
|
2270
|
-
console.log(
|
|
2175
|
+
console.log(chalk8.green(`
|
|
2271
2176
|
\u2713 Output format set to "${format}".`));
|
|
2272
2177
|
});
|
|
2273
|
-
|
|
2178
|
+
config.command("reset").description("Reset all configuration to defaults").action(() => {
|
|
2274
2179
|
const cfg = getConfig();
|
|
2275
2180
|
cfg.clear();
|
|
2276
|
-
console.log(
|
|
2181
|
+
console.log(chalk8.green("\n\u2713 Configuration reset to defaults."));
|
|
2277
2182
|
});
|
|
2278
|
-
|
|
2183
|
+
config.command("path").description("Show the config file path").action(() => {
|
|
2279
2184
|
const cfg = getConfig();
|
|
2280
2185
|
console.log(cfg.path);
|
|
2281
2186
|
});
|
|
2282
2187
|
}
|
|
2283
2188
|
|
|
2284
2189
|
// src/commands/store.ts
|
|
2285
|
-
import
|
|
2190
|
+
import chalk9 from "chalk";
|
|
2286
2191
|
import ora7 from "ora";
|
|
2287
2192
|
function formatPrice3(cents, currency) {
|
|
2288
2193
|
return new Intl.NumberFormat("en-US", {
|
|
@@ -2327,28 +2232,28 @@ function registerStoreCommands(program2) {
|
|
|
2327
2232
|
return;
|
|
2328
2233
|
}
|
|
2329
2234
|
if (stores.length === 0) {
|
|
2330
|
-
console.log(
|
|
2235
|
+
console.log(chalk9.yellow("\nNo stores found.\n"));
|
|
2331
2236
|
return;
|
|
2332
2237
|
}
|
|
2333
|
-
console.log(
|
|
2238
|
+
console.log(chalk9.bold(`
|
|
2334
2239
|
Stores \u2014 ${total} found (page ${page})
|
|
2335
2240
|
`));
|
|
2336
2241
|
for (const s of stores) {
|
|
2337
|
-
const badge = s.verified ?
|
|
2338
|
-
const rating = s.rating != null ?
|
|
2339
|
-
const products = s.productCount != null ?
|
|
2340
|
-
const country = s.country ?
|
|
2341
|
-
console.log(` ${
|
|
2242
|
+
const badge = s.verified ? chalk9.green(" \u2713") : "";
|
|
2243
|
+
const rating = s.rating != null ? chalk9.yellow(renderStars3(s.rating)) + chalk9.dim(` (${s.rating.toFixed(1)})`) : chalk9.dim("No rating");
|
|
2244
|
+
const products = s.productCount != null ? chalk9.dim(`${s.productCount} products`) : "";
|
|
2245
|
+
const country = s.country ? chalk9.dim(` ${s.country}`) : "";
|
|
2246
|
+
console.log(` ${chalk9.bold.cyan(s.name)}${badge} ${chalk9.dim(`(${s.slug})`)}`);
|
|
2342
2247
|
console.log(` ${rating} ${products}${country}`);
|
|
2343
2248
|
if (s.description) {
|
|
2344
2249
|
const desc = s.description.length > 100 ? s.description.slice(0, 100) + "..." : s.description;
|
|
2345
|
-
console.log(` ${
|
|
2250
|
+
console.log(` ${chalk9.dim(desc)}`);
|
|
2346
2251
|
}
|
|
2347
2252
|
console.log();
|
|
2348
2253
|
}
|
|
2349
2254
|
const totalPages = Math.ceil(total / pageSize);
|
|
2350
2255
|
if (totalPages > 1) {
|
|
2351
|
-
console.log(
|
|
2256
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2352
2257
|
`));
|
|
2353
2258
|
}
|
|
2354
2259
|
} catch (error) {
|
|
@@ -2366,24 +2271,24 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2366
2271
|
console.log(JSON.stringify(s, null, 2));
|
|
2367
2272
|
return;
|
|
2368
2273
|
}
|
|
2369
|
-
const badge = s.verified ?
|
|
2274
|
+
const badge = s.verified ? chalk9.green(" \u2713 Verified") : chalk9.dim(" Unverified");
|
|
2370
2275
|
console.log();
|
|
2371
|
-
console.log(
|
|
2372
|
-
console.log(
|
|
2276
|
+
console.log(chalk9.bold.cyan(` ${s.name}`) + badge);
|
|
2277
|
+
console.log(chalk9.dim(` ID: ${s.id} | Slug: ${s.slug}`));
|
|
2373
2278
|
console.log();
|
|
2374
2279
|
if (s.description) console.log(` ${s.description}`);
|
|
2375
2280
|
console.log();
|
|
2376
2281
|
if (s.rating != null) {
|
|
2377
|
-
console.log(` Rating: ${
|
|
2282
|
+
console.log(` Rating: ${chalk9.yellow(renderStars3(s.rating))} ${chalk9.dim(`(${s.rating.toFixed(1)})`)}`);
|
|
2378
2283
|
}
|
|
2379
2284
|
if (s.productCount != null) console.log(` Products: ${s.productCount}`);
|
|
2380
2285
|
if (s.country) console.log(` Country: ${s.country}`);
|
|
2381
2286
|
console.log(` Currency: ${s.currency}`);
|
|
2382
|
-
if (s.domain) console.log(` Website: ${
|
|
2287
|
+
if (s.domain) console.log(` Website: ${chalk9.cyan.underline(s.domain)}`);
|
|
2383
2288
|
if (s.contactEmail) console.log(` Contact: ${s.contactEmail}`);
|
|
2384
2289
|
console.log();
|
|
2385
|
-
console.log(
|
|
2386
|
-
console.log(
|
|
2290
|
+
console.log(chalk9.dim(` Browse catalog: clishop store catalog ${s.slug}`));
|
|
2291
|
+
console.log(chalk9.dim(` Search products: clishop search "<query>" --store ${s.slug}`));
|
|
2387
2292
|
console.log();
|
|
2388
2293
|
} catch (error) {
|
|
2389
2294
|
handleApiError(error);
|
|
@@ -2414,43 +2319,43 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2414
2319
|
console.log(JSON.stringify(res.data, null, 2));
|
|
2415
2320
|
return;
|
|
2416
2321
|
}
|
|
2417
|
-
const badge = storeInfo.verified ?
|
|
2418
|
-
const storeRating = storeInfo.rating != null ?
|
|
2322
|
+
const badge = storeInfo.verified ? chalk9.green(" \u2713") : "";
|
|
2323
|
+
const storeRating = storeInfo.rating != null ? chalk9.dim(` (${storeInfo.rating.toFixed(1)} \u2605)`) : "";
|
|
2419
2324
|
console.log(
|
|
2420
|
-
|
|
2325
|
+
chalk9.bold(`
|
|
2421
2326
|
${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
2422
2327
|
`)
|
|
2423
2328
|
);
|
|
2424
2329
|
if (products.length === 0) {
|
|
2425
|
-
console.log(
|
|
2330
|
+
console.log(chalk9.yellow(" No products found matching your filters.\n"));
|
|
2426
2331
|
return;
|
|
2427
2332
|
}
|
|
2428
2333
|
for (const p of products) {
|
|
2429
|
-
const stock = p.inStock ?
|
|
2430
|
-
const price =
|
|
2431
|
-
const stars =
|
|
2432
|
-
const shippingInfo = p.freeShipping ?
|
|
2433
|
-
const deliveryInfo = p.shippingDays != null ?
|
|
2434
|
-
console.log(` ${
|
|
2334
|
+
const stock = p.inStock ? chalk9.green("In Stock") : p.backorder ? chalk9.yellow("Backorder") : chalk9.red("Out of Stock");
|
|
2335
|
+
const price = chalk9.bold.white(formatPrice3(p.priceInCents, p.currency));
|
|
2336
|
+
const stars = chalk9.yellow(renderStars3(p.rating));
|
|
2337
|
+
const shippingInfo = p.freeShipping ? chalk9.green("Free Shipping") : p.shippingPriceInCents != null ? chalk9.dim(`+${formatPrice3(p.shippingPriceInCents, p.currency)} shipping`) : "";
|
|
2338
|
+
const deliveryInfo = p.shippingDays != null ? chalk9.dim(`(${deliveryLabel2(p.shippingDays)})`) : "";
|
|
2339
|
+
console.log(` ${chalk9.bold.cyan(p.name)} ${chalk9.dim(`(${p.id})`)}`);
|
|
2435
2340
|
console.log(
|
|
2436
|
-
` ${price} ${stock} ${stars} ${
|
|
2341
|
+
` ${price} ${stock} ${stars} ${chalk9.dim(`(${p.reviewCount} reviews)`)}` + (shippingInfo ? ` ${shippingInfo}` : "") + (deliveryInfo ? ` ${deliveryInfo}` : "")
|
|
2437
2342
|
);
|
|
2438
2343
|
const meta = [];
|
|
2439
2344
|
if (p.category) meta.push(p.category);
|
|
2440
2345
|
if (p.brand) meta.push(p.brand);
|
|
2441
2346
|
if (p.variant) meta.push(p.variant);
|
|
2442
|
-
if (meta.length) console.log(` ${
|
|
2347
|
+
if (meta.length) console.log(` ${chalk9.dim(meta.join(" \xB7 "))}`);
|
|
2443
2348
|
const returnInfo = [];
|
|
2444
2349
|
if (p.freeReturns) returnInfo.push("Free Returns");
|
|
2445
2350
|
if (p.returnWindowDays) returnInfo.push(`${p.returnWindowDays}d return window`);
|
|
2446
|
-
if (returnInfo.length) console.log(` ${
|
|
2351
|
+
if (returnInfo.length) console.log(` ${chalk9.dim(returnInfo.join(" \xB7 "))}`);
|
|
2447
2352
|
const desc = p.description.length > 120 ? p.description.slice(0, 120) + "..." : p.description;
|
|
2448
2353
|
console.log(` ${desc}`);
|
|
2449
2354
|
console.log();
|
|
2450
2355
|
}
|
|
2451
2356
|
const totalPages = Math.ceil(total / pageSize);
|
|
2452
2357
|
if (totalPages > 1) {
|
|
2453
|
-
console.log(
|
|
2358
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2454
2359
|
`));
|
|
2455
2360
|
}
|
|
2456
2361
|
} catch (error) {
|
|
@@ -2460,13 +2365,13 @@ ${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
|
2460
2365
|
}
|
|
2461
2366
|
|
|
2462
2367
|
// src/commands/status.ts
|
|
2463
|
-
import
|
|
2368
|
+
import chalk10 from "chalk";
|
|
2464
2369
|
import ora8 from "ora";
|
|
2465
2370
|
function registerStatusCommand(program2) {
|
|
2466
2371
|
program2.command("status").description("Show full account overview \u2014 user, agents, addresses, payment methods").option("--json", "Output raw JSON").action(async (opts) => {
|
|
2467
2372
|
try {
|
|
2468
2373
|
if (!await isLoggedIn()) {
|
|
2469
|
-
console.log(
|
|
2374
|
+
console.log(chalk10.yellow("\nNot logged in. Run: clishop login\n"));
|
|
2470
2375
|
return;
|
|
2471
2376
|
}
|
|
2472
2377
|
const spinner = ora8("Fetching account overview...").start();
|
|
@@ -2500,59 +2405,59 @@ function registerStatusCommand(program2) {
|
|
|
2500
2405
|
}, null, 2));
|
|
2501
2406
|
return;
|
|
2502
2407
|
}
|
|
2503
|
-
console.log(
|
|
2504
|
-
console.log(
|
|
2505
|
-
console.log(
|
|
2506
|
-
console.log(
|
|
2507
|
-
console.log(` Name: ${
|
|
2508
|
-
console.log(` Email: ${
|
|
2509
|
-
console.log(` ID: ${
|
|
2408
|
+
console.log(chalk10.bold.cyan("\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
2409
|
+
console.log(chalk10.bold.cyan(" CLISHOP Account Overview"));
|
|
2410
|
+
console.log(chalk10.bold.cyan("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n"));
|
|
2411
|
+
console.log(chalk10.bold(" \u{1F464} User"));
|
|
2412
|
+
console.log(` Name: ${chalk10.white(userInfo?.name || "\u2014")}`);
|
|
2413
|
+
console.log(` Email: ${chalk10.white(userInfo?.email || "\u2014")}`);
|
|
2414
|
+
console.log(` ID: ${chalk10.dim(userInfo?.id || "\u2014")}`);
|
|
2510
2415
|
console.log();
|
|
2511
|
-
console.log(
|
|
2416
|
+
console.log(chalk10.bold(` \u{1F916} Agents (${agents.length})`));
|
|
2512
2417
|
if (agentDetails.length === 0) {
|
|
2513
|
-
console.log(
|
|
2418
|
+
console.log(chalk10.yellow(" No agents found."));
|
|
2514
2419
|
}
|
|
2515
2420
|
for (const agent of agentDetails) {
|
|
2516
2421
|
const isActive = agent.name === activeAgentName;
|
|
2517
|
-
const marker = isActive ?
|
|
2518
|
-
const activeLabel = isActive ?
|
|
2422
|
+
const marker = isActive ? chalk10.green("\u25CF ") : " ";
|
|
2423
|
+
const activeLabel = isActive ? chalk10.green(" (active)") : "";
|
|
2519
2424
|
const limit = agent.maxOrderAmountInCents ? `$${(agent.maxOrderAmountInCents / 100).toFixed(2)}` : "No limit";
|
|
2520
2425
|
const confirm = agent.requireConfirmation ? "Yes" : "No";
|
|
2521
2426
|
console.log();
|
|
2522
|
-
console.log(` ${marker}${
|
|
2523
|
-
console.log(` ID: ${
|
|
2524
|
-
console.log(` Max order: ${
|
|
2427
|
+
console.log(` ${marker}${chalk10.bold.white(agent.name)}${activeLabel}`);
|
|
2428
|
+
console.log(` ID: ${chalk10.dim(agent.id)}`);
|
|
2429
|
+
console.log(` Max order: ${chalk10.yellow(limit)}`);
|
|
2525
2430
|
console.log(` Require confirm: ${confirm}`);
|
|
2526
2431
|
console.log();
|
|
2527
|
-
console.log(
|
|
2432
|
+
console.log(chalk10.bold(` \u{1F4CD} Addresses (${agent.addresses.length})`));
|
|
2528
2433
|
if (agent.addresses.length === 0) {
|
|
2529
|
-
console.log(
|
|
2434
|
+
console.log(chalk10.dim(" None"));
|
|
2530
2435
|
} else {
|
|
2531
2436
|
for (const addr of agent.addresses) {
|
|
2532
2437
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
2533
|
-
const defaultBadge = isDefault ?
|
|
2534
|
-
console.log(` \u2022 ${
|
|
2535
|
-
console.log(
|
|
2438
|
+
const defaultBadge = isDefault ? chalk10.green(" \u2605 default") : "";
|
|
2439
|
+
console.log(` \u2022 ${chalk10.white(addr.label)}${defaultBadge}`);
|
|
2440
|
+
console.log(chalk10.dim(` ${addr.line1}, ${addr.city}, ${addr.postalCode} ${addr.country}`));
|
|
2536
2441
|
}
|
|
2537
2442
|
}
|
|
2538
2443
|
console.log();
|
|
2539
|
-
console.log(
|
|
2444
|
+
console.log(chalk10.bold(` \u{1F4B3} Payment Methods (${agent.paymentMethods.length})`));
|
|
2540
2445
|
if (agent.paymentMethods.length === 0) {
|
|
2541
|
-
console.log(
|
|
2446
|
+
console.log(chalk10.dim(" None \u2014 run: clishop payment add"));
|
|
2542
2447
|
} else {
|
|
2543
2448
|
for (const pm of agent.paymentMethods) {
|
|
2544
2449
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
2545
|
-
const defaultBadge = isDefault ?
|
|
2450
|
+
const defaultBadge = isDefault ? chalk10.green(" \u2605 default") : "";
|
|
2546
2451
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
2547
|
-
console.log(` \u2022 ${
|
|
2548
|
-
console.log(
|
|
2452
|
+
console.log(` \u2022 ${chalk10.white(pm.label)}${last4}${defaultBadge}`);
|
|
2453
|
+
console.log(chalk10.dim(` ${pm.type} via ${pm.provider || "unknown"} (${pm.id})`));
|
|
2549
2454
|
}
|
|
2550
2455
|
}
|
|
2551
2456
|
}
|
|
2552
2457
|
console.log();
|
|
2553
|
-
console.log(
|
|
2554
|
-
console.log(
|
|
2555
|
-
console.log(
|
|
2458
|
+
console.log(chalk10.dim("\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\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
2459
|
+
console.log(chalk10.dim(" Tip: Use --agent <name> to run commands as a specific agent"));
|
|
2460
|
+
console.log(chalk10.dim(" Tip: Use 'clishop agent switch <name>' to change active agent"));
|
|
2556
2461
|
console.log();
|
|
2557
2462
|
} catch (error) {
|
|
2558
2463
|
handleApiError(error);
|
|
@@ -2561,10 +2466,10 @@ function registerStatusCommand(program2) {
|
|
|
2561
2466
|
}
|
|
2562
2467
|
|
|
2563
2468
|
// src/commands/setup.ts
|
|
2564
|
-
import
|
|
2469
|
+
import chalk11 from "chalk";
|
|
2565
2470
|
import ora9 from "ora";
|
|
2566
2471
|
import inquirer7 from "inquirer";
|
|
2567
|
-
function divider(color =
|
|
2472
|
+
function divider(color = chalk11.cyan) {
|
|
2568
2473
|
console.log(" " + color("\u2500".repeat(48)));
|
|
2569
2474
|
}
|
|
2570
2475
|
function stepHeader(step, total, title) {
|
|
@@ -2572,7 +2477,7 @@ function stepHeader(step, total, title) {
|
|
|
2572
2477
|
divider();
|
|
2573
2478
|
console.log();
|
|
2574
2479
|
console.log(
|
|
2575
|
-
|
|
2480
|
+
chalk11.bold.white(` STEP ${step} of ${total}`) + chalk11.dim(" \xB7 ") + chalk11.bold(title)
|
|
2576
2481
|
);
|
|
2577
2482
|
console.log();
|
|
2578
2483
|
}
|
|
@@ -2590,29 +2495,30 @@ function registerSetupCommand(program2) {
|
|
|
2590
2495
|
});
|
|
2591
2496
|
}
|
|
2592
2497
|
async function runSetupWizard() {
|
|
2593
|
-
const
|
|
2498
|
+
const config = getConfig();
|
|
2594
2499
|
console.log();
|
|
2595
|
-
divider(
|
|
2500
|
+
divider(chalk11.cyan);
|
|
2596
2501
|
console.log();
|
|
2597
|
-
console.log(
|
|
2598
|
-
console.log(
|
|
2502
|
+
console.log(chalk11.bold.cyan(" W E L C O M E T O C L I S H O P"));
|
|
2503
|
+
console.log(chalk11.dim(" Order anything from your terminal."));
|
|
2599
2504
|
console.log();
|
|
2600
|
-
divider(
|
|
2505
|
+
divider(chalk11.cyan);
|
|
2601
2506
|
console.log();
|
|
2602
2507
|
console.log(
|
|
2603
|
-
|
|
2508
|
+
chalk11.dim(" This wizard will guide you through the initial setup.")
|
|
2604
2509
|
);
|
|
2605
2510
|
console.log(
|
|
2606
|
-
|
|
2511
|
+
chalk11.dim(" It only takes a minute. You can re-run it anytime with:")
|
|
2607
2512
|
);
|
|
2608
|
-
console.log(
|
|
2609
|
-
stepHeader(1,
|
|
2513
|
+
console.log(chalk11.dim(" ") + chalk11.white("clishop setup"));
|
|
2514
|
+
stepHeader(1, 6, "Account");
|
|
2610
2515
|
let loggedIn = await isLoggedIn();
|
|
2516
|
+
let isNewAccount = false;
|
|
2611
2517
|
if (loggedIn) {
|
|
2612
2518
|
const user = await getUserInfo();
|
|
2613
2519
|
console.log(
|
|
2614
|
-
|
|
2615
|
-
` \u2713 Already logged in as ${
|
|
2520
|
+
chalk11.green(
|
|
2521
|
+
` \u2713 Already logged in as ${chalk11.bold(user?.name || user?.email || "unknown")}`
|
|
2616
2522
|
)
|
|
2617
2523
|
);
|
|
2618
2524
|
console.log();
|
|
@@ -2658,39 +2564,67 @@ async function runSetupWizard() {
|
|
|
2658
2564
|
}
|
|
2659
2565
|
]);
|
|
2660
2566
|
if (answers.password !== answers.confirmPassword) {
|
|
2661
|
-
console.error(
|
|
2567
|
+
console.error(chalk11.red("\n \u2717 Passwords do not match."));
|
|
2662
2568
|
console.log(
|
|
2663
|
-
|
|
2569
|
+
chalk11.dim(" Run ") + chalk11.white("clishop setup") + chalk11.dim(" to try again.\n")
|
|
2664
2570
|
);
|
|
2665
2571
|
process.exitCode = 1;
|
|
2666
2572
|
return;
|
|
2667
2573
|
}
|
|
2574
|
+
console.log();
|
|
2575
|
+
console.log(
|
|
2576
|
+
chalk11.dim(
|
|
2577
|
+
" Your monthly spending limit controls the maximum your AI agent"
|
|
2578
|
+
)
|
|
2579
|
+
);
|
|
2580
|
+
console.log(
|
|
2581
|
+
chalk11.dim(
|
|
2582
|
+
" can spend per month. Default: $200. You can only change this"
|
|
2583
|
+
)
|
|
2584
|
+
);
|
|
2585
|
+
console.log(
|
|
2586
|
+
chalk11.dim(
|
|
2587
|
+
" later via the website or email confirmation."
|
|
2588
|
+
)
|
|
2589
|
+
);
|
|
2590
|
+
console.log();
|
|
2591
|
+
const { spendingLimit } = await inquirer7.prompt([
|
|
2592
|
+
{
|
|
2593
|
+
type: "number",
|
|
2594
|
+
name: "spendingLimit",
|
|
2595
|
+
message: "Monthly spending limit ($):",
|
|
2596
|
+
default: 200
|
|
2597
|
+
}
|
|
2598
|
+
]);
|
|
2599
|
+
const limitInCents = Math.round((spendingLimit || 200) * 100);
|
|
2668
2600
|
const spinner = ora9("Creating your account...").start();
|
|
2669
2601
|
try {
|
|
2670
2602
|
const user = await register(
|
|
2671
2603
|
answers.email,
|
|
2672
2604
|
answers.password,
|
|
2673
|
-
answers.name
|
|
2605
|
+
answers.name,
|
|
2606
|
+
limitInCents
|
|
2674
2607
|
);
|
|
2675
2608
|
spinner.succeed(
|
|
2676
|
-
|
|
2677
|
-
`Account created! Welcome, ${
|
|
2609
|
+
chalk11.green(
|
|
2610
|
+
`Account created! Welcome, ${chalk11.bold(user.name)}.`
|
|
2678
2611
|
)
|
|
2679
2612
|
);
|
|
2613
|
+
isNewAccount = true;
|
|
2680
2614
|
} catch (error) {
|
|
2681
2615
|
spinner.fail(
|
|
2682
|
-
|
|
2616
|
+
chalk11.red(
|
|
2683
2617
|
`Registration failed: ${error?.response?.data?.message || error.message}`
|
|
2684
2618
|
)
|
|
2685
2619
|
);
|
|
2686
2620
|
console.log();
|
|
2687
2621
|
console.log(
|
|
2688
|
-
|
|
2689
|
-
" Make sure the backend is running at: " +
|
|
2622
|
+
chalk11.dim(
|
|
2623
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2690
2624
|
)
|
|
2691
2625
|
);
|
|
2692
2626
|
console.log(
|
|
2693
|
-
|
|
2627
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2694
2628
|
);
|
|
2695
2629
|
process.exitCode = 1;
|
|
2696
2630
|
return;
|
|
@@ -2709,37 +2643,117 @@ async function runSetupWizard() {
|
|
|
2709
2643
|
try {
|
|
2710
2644
|
const user = await login(answers.email, answers.password);
|
|
2711
2645
|
spinner.succeed(
|
|
2712
|
-
|
|
2646
|
+
chalk11.green(`Logged in as ${chalk11.bold(user.name)}.`)
|
|
2713
2647
|
);
|
|
2714
2648
|
} catch (error) {
|
|
2715
2649
|
spinner.fail(
|
|
2716
|
-
|
|
2650
|
+
chalk11.red(
|
|
2717
2651
|
`Login failed: ${error?.response?.data?.message || error.message}`
|
|
2718
2652
|
)
|
|
2719
2653
|
);
|
|
2720
2654
|
console.log();
|
|
2721
2655
|
console.log(
|
|
2722
|
-
|
|
2723
|
-
" Make sure the backend is running at: " +
|
|
2656
|
+
chalk11.dim(
|
|
2657
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2724
2658
|
)
|
|
2725
2659
|
);
|
|
2726
2660
|
console.log(
|
|
2727
|
-
|
|
2661
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2728
2662
|
);
|
|
2729
2663
|
process.exitCode = 1;
|
|
2730
2664
|
return;
|
|
2731
2665
|
}
|
|
2732
2666
|
}
|
|
2733
2667
|
}
|
|
2734
|
-
stepHeader(2,
|
|
2668
|
+
stepHeader(2, 6, "Monthly Spending Limit");
|
|
2669
|
+
if (isNewAccount) {
|
|
2670
|
+
console.log(
|
|
2671
|
+
chalk11.green(" \u2713 Spending limit was set during registration.")
|
|
2672
|
+
);
|
|
2673
|
+
console.log(
|
|
2674
|
+
chalk11.dim(
|
|
2675
|
+
" To change it later, visit " + chalk11.white("https://clishop.ai/agents") + chalk11.dim(" or use email confirmation.")
|
|
2676
|
+
)
|
|
2677
|
+
);
|
|
2678
|
+
} else {
|
|
2679
|
+
try {
|
|
2680
|
+
const api = getApiClient();
|
|
2681
|
+
const res = await api.get("/spending-limit");
|
|
2682
|
+
const { monthlySpendingLimitInCents, configured } = res.data;
|
|
2683
|
+
const currentLimit = (monthlySpendingLimitInCents / 100).toFixed(0);
|
|
2684
|
+
if (!configured) {
|
|
2685
|
+
console.log(
|
|
2686
|
+
chalk11.dim(
|
|
2687
|
+
" Your monthly spending limit controls the maximum your AI agent"
|
|
2688
|
+
)
|
|
2689
|
+
);
|
|
2690
|
+
console.log(
|
|
2691
|
+
chalk11.dim(
|
|
2692
|
+
" can spend per month. Default: $200. You can only change this"
|
|
2693
|
+
)
|
|
2694
|
+
);
|
|
2695
|
+
console.log(
|
|
2696
|
+
chalk11.dim(
|
|
2697
|
+
" later via the website or email confirmation."
|
|
2698
|
+
)
|
|
2699
|
+
);
|
|
2700
|
+
console.log();
|
|
2701
|
+
const { spendingLimit } = await inquirer7.prompt([
|
|
2702
|
+
{
|
|
2703
|
+
type: "number",
|
|
2704
|
+
name: "spendingLimit",
|
|
2705
|
+
message: "Monthly spending limit ($):",
|
|
2706
|
+
default: parseInt(currentLimit) || 200
|
|
2707
|
+
}
|
|
2708
|
+
]);
|
|
2709
|
+
const limitInCents = Math.round((spendingLimit || 200) * 100);
|
|
2710
|
+
const spinner = ora9("Setting spending limit...").start();
|
|
2711
|
+
try {
|
|
2712
|
+
await api.patch("/spending-limit", { limitInCents });
|
|
2713
|
+
spinner.succeed(
|
|
2714
|
+
chalk11.green(
|
|
2715
|
+
`Monthly spending limit set to $${(limitInCents / 100).toFixed(0)}.`
|
|
2716
|
+
)
|
|
2717
|
+
);
|
|
2718
|
+
} catch (error) {
|
|
2719
|
+
spinner.fail(
|
|
2720
|
+
chalk11.red(
|
|
2721
|
+
`Failed: ${error?.response?.data?.message || error.message}`
|
|
2722
|
+
)
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2725
|
+
} else {
|
|
2726
|
+
console.log(
|
|
2727
|
+
chalk11.green(
|
|
2728
|
+
` \u2713 Monthly spending limit: $${currentLimit}/month (already configured).`
|
|
2729
|
+
)
|
|
2730
|
+
);
|
|
2731
|
+
console.log(
|
|
2732
|
+
chalk11.dim(
|
|
2733
|
+
" To change it, visit " + chalk11.white("https://clishop.ai/agents") + chalk11.dim(" or it will require email confirmation.")
|
|
2734
|
+
)
|
|
2735
|
+
);
|
|
2736
|
+
}
|
|
2737
|
+
} catch (error) {
|
|
2738
|
+
console.log(
|
|
2739
|
+
chalk11.yellow(
|
|
2740
|
+
` \u26A0 Could not fetch spending limit: ${error?.response?.data?.message || error.message}`
|
|
2741
|
+
)
|
|
2742
|
+
);
|
|
2743
|
+
console.log(
|
|
2744
|
+
chalk11.dim(" You can set it later on the website.")
|
|
2745
|
+
);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
stepHeader(3, 6, "Agent (optional)");
|
|
2735
2749
|
const activeAgent = getActiveAgent();
|
|
2736
2750
|
console.log(
|
|
2737
|
-
|
|
2751
|
+
chalk11.dim(
|
|
2738
2752
|
` A default agent is ready ($${activeAgent.maxOrderAmount} limit, confirmation on).`
|
|
2739
2753
|
)
|
|
2740
2754
|
);
|
|
2741
2755
|
console.log(
|
|
2742
|
-
|
|
2756
|
+
chalk11.dim(" Agents control spending limits and category restrictions.")
|
|
2743
2757
|
);
|
|
2744
2758
|
console.log();
|
|
2745
2759
|
const { agentChoice } = await inquirer7.prompt([
|
|
@@ -2778,22 +2792,22 @@ async function runSetupWizard() {
|
|
|
2778
2792
|
});
|
|
2779
2793
|
setActiveAgent(agent.name);
|
|
2780
2794
|
console.log(
|
|
2781
|
-
|
|
2795
|
+
chalk11.green(
|
|
2782
2796
|
`
|
|
2783
|
-
\u2713 Agent "${
|
|
2797
|
+
\u2713 Agent "${chalk11.bold(agent.name)}" created and set as active.`
|
|
2784
2798
|
)
|
|
2785
2799
|
);
|
|
2786
2800
|
} catch (error) {
|
|
2787
|
-
console.error(
|
|
2801
|
+
console.error(chalk11.red(`
|
|
2788
2802
|
\u2717 ${error.message}`));
|
|
2789
|
-
console.log(
|
|
2803
|
+
console.log(chalk11.dim(" Continuing with the default agent."));
|
|
2790
2804
|
}
|
|
2791
2805
|
} else {
|
|
2792
|
-
console.log(
|
|
2806
|
+
console.log(chalk11.green(" \u2713 Using default agent."));
|
|
2793
2807
|
}
|
|
2794
|
-
stepHeader(
|
|
2808
|
+
stepHeader(4, 6, "Shipping Address");
|
|
2795
2809
|
console.log(
|
|
2796
|
-
|
|
2810
|
+
chalk11.dim(
|
|
2797
2811
|
" Add an address so products can be delivered to you."
|
|
2798
2812
|
)
|
|
2799
2813
|
);
|
|
@@ -2915,37 +2929,37 @@ async function runSetupWizard() {
|
|
|
2915
2929
|
});
|
|
2916
2930
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
2917
2931
|
spinner.succeed(
|
|
2918
|
-
|
|
2932
|
+
chalk11.green(
|
|
2919
2933
|
`Address "${addr.label}" saved and set as default.`
|
|
2920
2934
|
)
|
|
2921
2935
|
);
|
|
2922
2936
|
} catch (error) {
|
|
2923
2937
|
spinner.fail(
|
|
2924
|
-
|
|
2938
|
+
chalk11.red(
|
|
2925
2939
|
`Failed to save address: ${error?.response?.data?.message || error.message}`
|
|
2926
2940
|
)
|
|
2927
2941
|
);
|
|
2928
2942
|
console.log(
|
|
2929
|
-
|
|
2930
|
-
" You can add an address later with: " +
|
|
2943
|
+
chalk11.dim(
|
|
2944
|
+
" You can add an address later with: " + chalk11.white("clishop address add")
|
|
2931
2945
|
)
|
|
2932
2946
|
);
|
|
2933
2947
|
}
|
|
2934
2948
|
} else {
|
|
2935
2949
|
console.log(
|
|
2936
|
-
|
|
2937
|
-
"\n You can add one later with: " +
|
|
2950
|
+
chalk11.dim(
|
|
2951
|
+
"\n You can add one later with: " + chalk11.white("clishop address add")
|
|
2938
2952
|
)
|
|
2939
2953
|
);
|
|
2940
2954
|
}
|
|
2941
|
-
stepHeader(
|
|
2955
|
+
stepHeader(5, 6, "Payment Method");
|
|
2942
2956
|
console.log(
|
|
2943
|
-
|
|
2957
|
+
chalk11.dim(
|
|
2944
2958
|
" For security, payment details are entered through a secure web"
|
|
2945
2959
|
)
|
|
2946
2960
|
);
|
|
2947
2961
|
console.log(
|
|
2948
|
-
|
|
2962
|
+
chalk11.dim(" page. The CLI never sees your card number.")
|
|
2949
2963
|
);
|
|
2950
2964
|
console.log();
|
|
2951
2965
|
const { addPayment } = await inquirer7.prompt([
|
|
@@ -2968,46 +2982,46 @@ async function runSetupWizard() {
|
|
|
2968
2982
|
const { setupUrl } = res.data;
|
|
2969
2983
|
console.log();
|
|
2970
2984
|
console.log(
|
|
2971
|
-
|
|
2985
|
+
chalk11.bold(
|
|
2972
2986
|
" Open this link in your browser to add a payment method:"
|
|
2973
2987
|
)
|
|
2974
2988
|
);
|
|
2975
2989
|
console.log();
|
|
2976
|
-
console.log(" " +
|
|
2990
|
+
console.log(" " + chalk11.cyan.underline(setupUrl));
|
|
2977
2991
|
console.log();
|
|
2978
2992
|
console.log(
|
|
2979
|
-
|
|
2980
|
-
" Once done, verify with: " +
|
|
2993
|
+
chalk11.dim(
|
|
2994
|
+
" Once done, verify with: " + chalk11.white("clishop payment list")
|
|
2981
2995
|
)
|
|
2982
2996
|
);
|
|
2983
2997
|
} catch (error) {
|
|
2984
2998
|
spinner.fail(
|
|
2985
|
-
|
|
2999
|
+
chalk11.red(
|
|
2986
3000
|
`Could not get setup link: ${error?.response?.data?.message || error.message}`
|
|
2987
3001
|
)
|
|
2988
3002
|
);
|
|
2989
3003
|
console.log(
|
|
2990
|
-
|
|
2991
|
-
" You can set up payment later with: " +
|
|
3004
|
+
chalk11.dim(
|
|
3005
|
+
" You can set up payment later with: " + chalk11.white("clishop payment add")
|
|
2992
3006
|
)
|
|
2993
3007
|
);
|
|
2994
3008
|
}
|
|
2995
3009
|
} else {
|
|
2996
3010
|
console.log(
|
|
2997
|
-
|
|
2998
|
-
"\n You can set one up later with: " +
|
|
3011
|
+
chalk11.dim(
|
|
3012
|
+
"\n You can set one up later with: " + chalk11.white("clishop payment add")
|
|
2999
3013
|
)
|
|
3000
3014
|
);
|
|
3001
3015
|
}
|
|
3002
|
-
stepHeader(
|
|
3016
|
+
stepHeader(6, 6, "Your First Search");
|
|
3003
3017
|
if (addressCity) {
|
|
3004
3018
|
console.log(
|
|
3005
|
-
|
|
3006
|
-
` Let's find something! Products can be shipped to ${
|
|
3019
|
+
chalk11.dim(
|
|
3020
|
+
` Let's find something! Products can be shipped to ${chalk11.white(addressCity)}.`
|
|
3007
3021
|
)
|
|
3008
3022
|
);
|
|
3009
3023
|
} else {
|
|
3010
|
-
console.log(
|
|
3024
|
+
console.log(chalk11.dim(" Let's find something to order!"));
|
|
3011
3025
|
}
|
|
3012
3026
|
console.log();
|
|
3013
3027
|
const { searchQuery } = await inquirer7.prompt([
|
|
@@ -3029,14 +3043,14 @@ async function runSetupWizard() {
|
|
|
3029
3043
|
const result = res.data;
|
|
3030
3044
|
if (result.products.length === 0) {
|
|
3031
3045
|
console.log(
|
|
3032
|
-
|
|
3046
|
+
chalk11.yellow(
|
|
3033
3047
|
`
|
|
3034
3048
|
No results for "${searchQuery}". Try other terms later!`
|
|
3035
3049
|
)
|
|
3036
3050
|
);
|
|
3037
3051
|
} else {
|
|
3038
3052
|
console.log(
|
|
3039
|
-
|
|
3053
|
+
chalk11.bold(
|
|
3040
3054
|
`
|
|
3041
3055
|
Found ${result.total} result${result.total !== 1 ? "s" : ""} for "${searchQuery}":
|
|
3042
3056
|
`
|
|
@@ -3044,85 +3058,85 @@ async function runSetupWizard() {
|
|
|
3044
3058
|
);
|
|
3045
3059
|
for (const p of result.products) {
|
|
3046
3060
|
const price = formatPrice4(p.priceInCents, p.currency || "USD");
|
|
3047
|
-
const stock = p.inStock ?
|
|
3061
|
+
const stock = p.inStock ? chalk11.green("In Stock") : chalk11.red("Out of Stock");
|
|
3048
3062
|
console.log(
|
|
3049
|
-
` ${
|
|
3063
|
+
` ${chalk11.bold.cyan(p.name)} ${chalk11.bold.white(price)} ${stock}`
|
|
3050
3064
|
);
|
|
3051
|
-
console.log(
|
|
3065
|
+
console.log(chalk11.dim(` ID: ${p.id}`));
|
|
3052
3066
|
console.log(
|
|
3053
|
-
|
|
3067
|
+
chalk11.dim(
|
|
3054
3068
|
` ${p.description.length > 100 ? p.description.slice(0, 100) + "..." : p.description}`
|
|
3055
3069
|
)
|
|
3056
3070
|
);
|
|
3057
3071
|
console.log();
|
|
3058
3072
|
}
|
|
3059
3073
|
console.log(
|
|
3060
|
-
|
|
3074
|
+
chalk11.dim(" Buy a product with: ") + chalk11.white("clishop buy <product-id>")
|
|
3061
3075
|
);
|
|
3062
3076
|
}
|
|
3063
3077
|
} catch (error) {
|
|
3064
3078
|
spinner.fail(
|
|
3065
|
-
|
|
3079
|
+
chalk11.red(
|
|
3066
3080
|
`Search failed: ${error?.response?.data?.message || error.message}`
|
|
3067
3081
|
)
|
|
3068
3082
|
);
|
|
3069
3083
|
}
|
|
3070
3084
|
}
|
|
3071
|
-
|
|
3085
|
+
config.set("setupCompleted", true);
|
|
3072
3086
|
console.log();
|
|
3073
|
-
divider(
|
|
3087
|
+
divider(chalk11.green);
|
|
3074
3088
|
console.log();
|
|
3075
|
-
console.log(
|
|
3089
|
+
console.log(chalk11.bold.green(" \u2713 You're all set!"));
|
|
3076
3090
|
console.log();
|
|
3077
|
-
console.log(
|
|
3091
|
+
console.log(chalk11.dim(" Here are some commands to get you started:"));
|
|
3078
3092
|
console.log();
|
|
3079
3093
|
console.log(
|
|
3080
|
-
|
|
3094
|
+
chalk11.white(" clishop search <query> ") + chalk11.dim("Search for products")
|
|
3081
3095
|
);
|
|
3082
3096
|
console.log(
|
|
3083
|
-
|
|
3097
|
+
chalk11.white(" clishop buy <id> ") + chalk11.dim("Quick-buy a product")
|
|
3084
3098
|
);
|
|
3085
3099
|
console.log(
|
|
3086
|
-
|
|
3100
|
+
chalk11.white(" clishop order list ") + chalk11.dim("View your orders")
|
|
3087
3101
|
);
|
|
3088
3102
|
console.log(
|
|
3089
|
-
|
|
3103
|
+
chalk11.white(" clishop agent list ") + chalk11.dim("Manage your agents")
|
|
3090
3104
|
);
|
|
3091
3105
|
console.log(
|
|
3092
|
-
|
|
3106
|
+
chalk11.white(" clishop --help ") + chalk11.dim("See all commands")
|
|
3093
3107
|
);
|
|
3094
3108
|
console.log();
|
|
3095
|
-
divider(
|
|
3109
|
+
divider(chalk11.green);
|
|
3096
3110
|
console.log();
|
|
3097
3111
|
}
|
|
3098
3112
|
|
|
3099
3113
|
// src/commands/advertise.ts
|
|
3100
|
-
import
|
|
3114
|
+
import chalk12 from "chalk";
|
|
3101
3115
|
import ora10 from "ora";
|
|
3102
3116
|
import inquirer8 from "inquirer";
|
|
3103
3117
|
function formatPrice5(cents, currency) {
|
|
3104
3118
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
3105
3119
|
}
|
|
3106
3120
|
var STATUS_COLORS2 = {
|
|
3107
|
-
open:
|
|
3108
|
-
closed:
|
|
3109
|
-
accepted:
|
|
3110
|
-
cancelled:
|
|
3111
|
-
expired:
|
|
3121
|
+
open: chalk12.green,
|
|
3122
|
+
closed: chalk12.dim,
|
|
3123
|
+
accepted: chalk12.blue,
|
|
3124
|
+
cancelled: chalk12.red,
|
|
3125
|
+
expired: chalk12.yellow
|
|
3112
3126
|
};
|
|
3113
3127
|
var BID_STATUS_COLORS = {
|
|
3114
|
-
pending:
|
|
3115
|
-
accepted:
|
|
3116
|
-
rejected:
|
|
3117
|
-
withdrawn:
|
|
3128
|
+
pending: chalk12.yellow,
|
|
3129
|
+
accepted: chalk12.green,
|
|
3130
|
+
rejected: chalk12.red,
|
|
3131
|
+
withdrawn: chalk12.dim
|
|
3118
3132
|
};
|
|
3119
3133
|
function registerAdvertiseCommands(program2) {
|
|
3120
3134
|
const advertise = program2.command("advertise").description("Advertise a request for vendors to bid on (when you can't find what you need)");
|
|
3121
3135
|
advertise.command("create").alias("new").description("Create a new advertised request").action(async () => {
|
|
3122
3136
|
try {
|
|
3123
3137
|
const agent = getActiveAgent();
|
|
3124
|
-
console.log(
|
|
3125
|
-
console.log(
|
|
3138
|
+
console.log(chalk12.bold("\n \u{1F4E2} Advertise a Request\n"));
|
|
3139
|
+
console.log(chalk12.dim(" Can't find what you need? Describe it and vendors will bid to fulfill it.\n"));
|
|
3126
3140
|
const answers = await inquirer8.prompt([
|
|
3127
3141
|
{
|
|
3128
3142
|
type: "input",
|
|
@@ -3258,7 +3272,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3258
3272
|
addrChoices.push({ name: displayName, value: displayName });
|
|
3259
3273
|
}
|
|
3260
3274
|
const skipOption = "Skip \u2014 don't set a delivery address";
|
|
3261
|
-
addrChoices.push({ name:
|
|
3275
|
+
addrChoices.push({ name: chalk12.dim(skipOption), value: skipOption });
|
|
3262
3276
|
const defaultAddr = addresses.find((a) => a.id === agent.defaultAddressId);
|
|
3263
3277
|
const defaultDisplay = defaultAddr ? `${defaultAddr.label} \u2014 ${defaultAddr.line1}` : "";
|
|
3264
3278
|
const { selectedAddress } = await inquirer8.prompt([
|
|
@@ -3273,11 +3287,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3273
3287
|
addressId = addrMap.get(selectedAddress) || void 0;
|
|
3274
3288
|
} else {
|
|
3275
3289
|
addrSpinner.stop();
|
|
3276
|
-
console.log(
|
|
3290
|
+
console.log(chalk12.dim(" No addresses found. You can add one later with: clishop address add"));
|
|
3277
3291
|
}
|
|
3278
3292
|
} catch {
|
|
3279
3293
|
addrSpinner.stop();
|
|
3280
|
-
console.log(
|
|
3294
|
+
console.log(chalk12.dim(" Could not fetch addresses. Skipping delivery location."));
|
|
3281
3295
|
}
|
|
3282
3296
|
let paymentMethods;
|
|
3283
3297
|
const paySpinner = ora10("Fetching your payment methods...").start();
|
|
@@ -3287,7 +3301,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3287
3301
|
const payments = payRes.data.paymentMethods;
|
|
3288
3302
|
if (payments.length > 0) {
|
|
3289
3303
|
const payChoices = [
|
|
3290
|
-
{ name:
|
|
3304
|
+
{ name: chalk12.green("Accept all payment methods"), value: "__ALL__" },
|
|
3291
3305
|
...payments.map((p) => ({
|
|
3292
3306
|
name: `${p.label}${p.brand ? ` (${p.brand})` : ""}`,
|
|
3293
3307
|
value: p.id,
|
|
@@ -3309,11 +3323,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3309
3323
|
paymentMethods = JSON.stringify(selectedPayments);
|
|
3310
3324
|
}
|
|
3311
3325
|
} else {
|
|
3312
|
-
console.log(
|
|
3326
|
+
console.log(chalk12.dim(" No payment methods found. You can add one later with: clishop payment add"));
|
|
3313
3327
|
}
|
|
3314
3328
|
} catch {
|
|
3315
3329
|
paySpinner.stop();
|
|
3316
|
-
console.log(
|
|
3330
|
+
console.log(chalk12.dim(" Could not fetch payment methods. Skipping."));
|
|
3317
3331
|
}
|
|
3318
3332
|
const body = {
|
|
3319
3333
|
title: answers.title,
|
|
@@ -3352,9 +3366,9 @@ function registerAdvertiseCommands(program2) {
|
|
|
3352
3366
|
}
|
|
3353
3367
|
const spinner = ora10("Publishing your request...").start();
|
|
3354
3368
|
const res = await api.post("/advertise", body);
|
|
3355
|
-
spinner.succeed(
|
|
3356
|
-
console.log(
|
|
3357
|
-
console.log(
|
|
3369
|
+
spinner.succeed(chalk12.green(`Request published! ID: ${chalk12.bold(res.data.advertise.id)}`));
|
|
3370
|
+
console.log(chalk12.dim("\n Vendors can now see your request and submit bids."));
|
|
3371
|
+
console.log(chalk12.dim(` Check bids with: clishop advertise show ${res.data.advertise.id}
|
|
3358
3372
|
`));
|
|
3359
3373
|
} catch (error) {
|
|
3360
3374
|
handleApiError(error);
|
|
@@ -3402,7 +3416,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3402
3416
|
const spinner = ora10("Publishing your request...").start();
|
|
3403
3417
|
const api = getApiClient();
|
|
3404
3418
|
const res = await api.post("/advertise", body);
|
|
3405
|
-
spinner.succeed(
|
|
3419
|
+
spinner.succeed(chalk12.green(`Request published! ID: ${chalk12.bold(res.data.advertise.id)}`));
|
|
3406
3420
|
} catch (error) {
|
|
3407
3421
|
handleApiError(error);
|
|
3408
3422
|
}
|
|
@@ -3421,18 +3435,18 @@ function registerAdvertiseCommands(program2) {
|
|
|
3421
3435
|
return;
|
|
3422
3436
|
}
|
|
3423
3437
|
if (ads.length === 0) {
|
|
3424
|
-
console.log(
|
|
3425
|
-
console.log(
|
|
3438
|
+
console.log(chalk12.yellow("\nNo advertised requests found.\n"));
|
|
3439
|
+
console.log(chalk12.dim(" Create one with: clishop advertise create\n"));
|
|
3426
3440
|
return;
|
|
3427
3441
|
}
|
|
3428
|
-
console.log(
|
|
3442
|
+
console.log(chalk12.bold("\n\u{1F4E2} Your Advertised Requests:\n"));
|
|
3429
3443
|
for (const ad of ads) {
|
|
3430
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3444
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3431
3445
|
const date = new Date(ad.createdAt).toLocaleDateString();
|
|
3432
3446
|
const bidCount = ad.bids?.length || 0;
|
|
3433
|
-
const bidInfo = bidCount > 0 ?
|
|
3447
|
+
const bidInfo = bidCount > 0 ? chalk12.cyan(` (${bidCount} bid${bidCount > 1 ? "s" : ""})`) : chalk12.dim(" (no bids yet)");
|
|
3434
3448
|
console.log(
|
|
3435
|
-
` ${
|
|
3449
|
+
` ${chalk12.bold(ad.id)} ${statusColor(ad.status.toUpperCase().padEnd(10))} ${chalk12.bold(ad.title)}${bidInfo} ${chalk12.dim(date)}`
|
|
3436
3450
|
);
|
|
3437
3451
|
const meta = [];
|
|
3438
3452
|
if (ad.quantity > 1) meta.push(`qty: ${ad.quantity}`);
|
|
@@ -3444,13 +3458,13 @@ function registerAdvertiseCommands(program2) {
|
|
|
3444
3458
|
if (ad.recurring) meta.push("recurring");
|
|
3445
3459
|
if (ad.address) meta.push(`\u2192 ${ad.address.label}`);
|
|
3446
3460
|
if (meta.length) {
|
|
3447
|
-
console.log(` ${
|
|
3461
|
+
console.log(` ${chalk12.dim(meta.join(" \xB7 "))}`);
|
|
3448
3462
|
}
|
|
3449
3463
|
console.log();
|
|
3450
3464
|
}
|
|
3451
3465
|
const totalPages = Math.ceil(res.data.total / res.data.pageSize);
|
|
3452
3466
|
if (totalPages > 1) {
|
|
3453
|
-
console.log(
|
|
3467
|
+
console.log(chalk12.dim(` Page ${res.data.page} of ${totalPages}. Use --page to navigate.
|
|
3454
3468
|
`));
|
|
3455
3469
|
}
|
|
3456
3470
|
} catch (error) {
|
|
@@ -3468,10 +3482,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3468
3482
|
console.log(JSON.stringify(ad, null, 2));
|
|
3469
3483
|
return;
|
|
3470
3484
|
}
|
|
3471
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3485
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3472
3486
|
console.log();
|
|
3473
|
-
console.log(
|
|
3474
|
-
console.log(` ID: ${
|
|
3487
|
+
console.log(chalk12.bold.cyan(` \u{1F4E2} ${ad.title}`));
|
|
3488
|
+
console.log(` ID: ${chalk12.dim(ad.id)}`);
|
|
3475
3489
|
console.log(` Status: ${statusColor(ad.status.toUpperCase())}`);
|
|
3476
3490
|
if (ad.description) console.log(` Details: ${ad.description}`);
|
|
3477
3491
|
if (ad.sku) console.log(` SKU: ${ad.sku}`);
|
|
@@ -3479,10 +3493,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3479
3493
|
if (ad.company) console.log(` Company: ${ad.company}`);
|
|
3480
3494
|
if (ad.features) console.log(` Features: ${ad.features}`);
|
|
3481
3495
|
console.log(` Quantity: ${ad.quantity}`);
|
|
3482
|
-
if (ad.bidPriceInCents) console.log(` Max Bid: ${
|
|
3496
|
+
if (ad.bidPriceInCents) console.log(` Max Bid: ${chalk12.bold(formatPrice5(ad.bidPriceInCents, ad.currency))}`);
|
|
3483
3497
|
if (ad.speedDays) console.log(` Speed: ${ad.speedDays}-day delivery`);
|
|
3484
3498
|
const returnReqs = [];
|
|
3485
|
-
if (ad.freeReturns) returnReqs.push(
|
|
3499
|
+
if (ad.freeReturns) returnReqs.push(chalk12.green("Free Returns required"));
|
|
3486
3500
|
if (ad.minReturnDays) returnReqs.push(`${ad.minReturnDays}-day return window min`);
|
|
3487
3501
|
if (returnReqs.length) console.log(` Returns: ${returnReqs.join(" \xB7 ")}`);
|
|
3488
3502
|
if (ad.recurring) console.log(` Recurring: Yes${ad.recurringNote ? ` (${ad.recurringNote})` : ""}`);
|
|
@@ -3492,7 +3506,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3492
3506
|
}
|
|
3493
3507
|
if (ad.paymentMethods) {
|
|
3494
3508
|
if (ad.paymentMethods === "all") {
|
|
3495
|
-
console.log(` Payment: ${
|
|
3509
|
+
console.log(` Payment: ${chalk12.green("All methods accepted")}`);
|
|
3496
3510
|
} else {
|
|
3497
3511
|
try {
|
|
3498
3512
|
const ids = JSON.parse(ad.paymentMethods);
|
|
@@ -3506,20 +3520,20 @@ function registerAdvertiseCommands(program2) {
|
|
|
3506
3520
|
console.log(` Created: ${new Date(ad.createdAt).toLocaleString()}`);
|
|
3507
3521
|
const bids = ad.bids || [];
|
|
3508
3522
|
if (bids.length === 0) {
|
|
3509
|
-
console.log(
|
|
3523
|
+
console.log(chalk12.dim("\n No bids yet. Vendors will be able to see your request and submit bids."));
|
|
3510
3524
|
} else {
|
|
3511
|
-
console.log(
|
|
3525
|
+
console.log(chalk12.bold(`
|
|
3512
3526
|
Bids (${bids.length}):
|
|
3513
3527
|
`));
|
|
3514
3528
|
for (const bid of bids) {
|
|
3515
|
-
const bidStatusColor = BID_STATUS_COLORS[bid.status] ||
|
|
3516
|
-
const storeBadge = bid.store?.verified ?
|
|
3517
|
-
const storeRating = bid.store?.rating != null ?
|
|
3518
|
-
console.log(` ${
|
|
3529
|
+
const bidStatusColor = BID_STATUS_COLORS[bid.status] || chalk12.white;
|
|
3530
|
+
const storeBadge = bid.store?.verified ? chalk12.green(" \u2713") : "";
|
|
3531
|
+
const storeRating = bid.store?.rating != null ? chalk12.dim(` (${bid.store.rating.toFixed(1)}\u2605)`) : "";
|
|
3532
|
+
console.log(` ${chalk12.bold(bid.id)} ${bidStatusColor(bid.status.toUpperCase().padEnd(10))} ${chalk12.bold(formatPrice5(bid.priceInCents, bid.currency))}`);
|
|
3519
3533
|
console.log(` Store: ${bid.store?.name || bid.storeId}${storeBadge}${storeRating}`);
|
|
3520
3534
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3521
3535
|
const bidReturns = [];
|
|
3522
|
-
if (bid.freeReturns) bidReturns.push(
|
|
3536
|
+
if (bid.freeReturns) bidReturns.push(chalk12.green("Free Returns"));
|
|
3523
3537
|
if (bid.returnWindowDays) bidReturns.push(`${bid.returnWindowDays}-day return window`);
|
|
3524
3538
|
if (bidReturns.length) console.log(` Returns: ${bidReturns.join(" \xB7 ")}`);
|
|
3525
3539
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
@@ -3530,8 +3544,8 @@ function registerAdvertiseCommands(program2) {
|
|
|
3530
3544
|
if (ad.status === "open") {
|
|
3531
3545
|
const pendingBids = bids.filter((b) => b.status === "pending");
|
|
3532
3546
|
if (pendingBids.length > 0) {
|
|
3533
|
-
console.log(
|
|
3534
|
-
console.log(
|
|
3547
|
+
console.log(chalk12.dim(` Accept a bid: clishop advertise accept ${ad.id} <bidId>`));
|
|
3548
|
+
console.log(chalk12.dim(` Reject a bid: clishop advertise reject ${ad.id} <bidId>
|
|
3535
3549
|
`));
|
|
3536
3550
|
}
|
|
3537
3551
|
}
|
|
@@ -3550,15 +3564,15 @@ function registerAdvertiseCommands(program2) {
|
|
|
3550
3564
|
const ad = detailRes.data.advertise;
|
|
3551
3565
|
const bid = ad.bids?.find((b) => b.id === bidId);
|
|
3552
3566
|
if (!bid) {
|
|
3553
|
-
console.error(
|
|
3567
|
+
console.error(chalk12.red(`
|
|
3554
3568
|
\u2717 Bid ${bidId} not found on request ${advertiseId}.`));
|
|
3555
3569
|
process.exitCode = 1;
|
|
3556
3570
|
return;
|
|
3557
3571
|
}
|
|
3558
|
-
console.log(
|
|
3572
|
+
console.log(chalk12.bold("\n Accept this bid?\n"));
|
|
3559
3573
|
console.log(` Request: ${ad.title}`);
|
|
3560
|
-
console.log(` Store: ${bid.store?.name || bid.storeId}${bid.store?.verified ?
|
|
3561
|
-
console.log(` Price: ${
|
|
3574
|
+
console.log(` Store: ${bid.store?.name || bid.storeId}${bid.store?.verified ? chalk12.green(" \u2713") : ""}`);
|
|
3575
|
+
console.log(` Price: ${chalk12.bold(formatPrice5(bid.priceInCents, bid.currency))}`);
|
|
3562
3576
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3563
3577
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
3564
3578
|
console.log();
|
|
@@ -3571,12 +3585,12 @@ function registerAdvertiseCommands(program2) {
|
|
|
3571
3585
|
}
|
|
3572
3586
|
]);
|
|
3573
3587
|
if (!confirm) {
|
|
3574
|
-
console.log(
|
|
3588
|
+
console.log(chalk12.yellow("Cancelled."));
|
|
3575
3589
|
return;
|
|
3576
3590
|
}
|
|
3577
3591
|
const spinner = ora10("Accepting bid...").start();
|
|
3578
3592
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/accept`);
|
|
3579
|
-
spinner.succeed(
|
|
3593
|
+
spinner.succeed(chalk12.green("Bid accepted! The vendor will now fulfill your request."));
|
|
3580
3594
|
} catch (error) {
|
|
3581
3595
|
handleApiError(error);
|
|
3582
3596
|
}
|
|
@@ -3595,7 +3609,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3595
3609
|
const spinner = ora10("Rejecting bid...").start();
|
|
3596
3610
|
const api = getApiClient();
|
|
3597
3611
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/reject`);
|
|
3598
|
-
spinner.succeed(
|
|
3612
|
+
spinner.succeed(chalk12.green("Bid rejected."));
|
|
3599
3613
|
} catch (error) {
|
|
3600
3614
|
handleApiError(error);
|
|
3601
3615
|
}
|
|
@@ -3614,7 +3628,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3614
3628
|
const spinner = ora10("Cancelling request...").start();
|
|
3615
3629
|
const api = getApiClient();
|
|
3616
3630
|
await api.post(`/advertise/${id}/cancel`);
|
|
3617
|
-
spinner.succeed(
|
|
3631
|
+
spinner.succeed(chalk12.green("Request cancelled."));
|
|
3618
3632
|
} catch (error) {
|
|
3619
3633
|
handleApiError(error);
|
|
3620
3634
|
}
|
|
@@ -3622,7 +3636,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3622
3636
|
}
|
|
3623
3637
|
|
|
3624
3638
|
// src/commands/support.ts
|
|
3625
|
-
import
|
|
3639
|
+
import chalk13 from "chalk";
|
|
3626
3640
|
import ora11 from "ora";
|
|
3627
3641
|
import inquirer9 from "inquirer";
|
|
3628
3642
|
var CATEGORY_CHOICES = [
|
|
@@ -3641,18 +3655,18 @@ var PRIORITY_CHOICES = [
|
|
|
3641
3655
|
{ name: "Urgent", value: "urgent" }
|
|
3642
3656
|
];
|
|
3643
3657
|
var STATUS_COLORS3 = {
|
|
3644
|
-
open:
|
|
3645
|
-
in_progress:
|
|
3646
|
-
awaiting_customer:
|
|
3647
|
-
awaiting_store:
|
|
3648
|
-
resolved:
|
|
3649
|
-
closed:
|
|
3658
|
+
open: chalk13.green,
|
|
3659
|
+
in_progress: chalk13.cyan,
|
|
3660
|
+
awaiting_customer: chalk13.yellow,
|
|
3661
|
+
awaiting_store: chalk13.blue,
|
|
3662
|
+
resolved: chalk13.gray,
|
|
3663
|
+
closed: chalk13.dim
|
|
3650
3664
|
};
|
|
3651
3665
|
var PRIORITY_COLORS = {
|
|
3652
|
-
low:
|
|
3653
|
-
normal:
|
|
3654
|
-
high:
|
|
3655
|
-
urgent:
|
|
3666
|
+
low: chalk13.dim,
|
|
3667
|
+
normal: chalk13.white,
|
|
3668
|
+
high: chalk13.yellow,
|
|
3669
|
+
urgent: chalk13.red
|
|
3656
3670
|
};
|
|
3657
3671
|
function registerSupportCommands(program2) {
|
|
3658
3672
|
const support = program2.command("support").description("Manage support tickets for orders");
|
|
@@ -3685,7 +3699,7 @@ function registerSupportCommands(program2) {
|
|
|
3685
3699
|
}
|
|
3686
3700
|
]);
|
|
3687
3701
|
if (!answers.message || !answers.message.trim()) {
|
|
3688
|
-
console.error(
|
|
3702
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3689
3703
|
return;
|
|
3690
3704
|
}
|
|
3691
3705
|
const spinner = ora11("Creating support ticket...").start();
|
|
@@ -3697,17 +3711,17 @@ function registerSupportCommands(program2) {
|
|
|
3697
3711
|
priority: answers.priority,
|
|
3698
3712
|
message: answers.message.trim()
|
|
3699
3713
|
});
|
|
3700
|
-
spinner.succeed(
|
|
3714
|
+
spinner.succeed(chalk13.green("Support ticket created!"));
|
|
3701
3715
|
const t = res.data.ticket;
|
|
3702
3716
|
console.log();
|
|
3703
|
-
console.log(` ${
|
|
3704
|
-
console.log(` ${
|
|
3705
|
-
console.log(` ${
|
|
3706
|
-
console.log(` ${
|
|
3707
|
-
console.log(` ${
|
|
3717
|
+
console.log(` ${chalk13.bold("Ticket ID:")} ${chalk13.cyan(t.id)}`);
|
|
3718
|
+
console.log(` ${chalk13.bold("Subject:")} ${t.subject}`);
|
|
3719
|
+
console.log(` ${chalk13.bold("Store:")} ${t.storeName}`);
|
|
3720
|
+
console.log(` ${chalk13.bold("Category:")} ${t.category}`);
|
|
3721
|
+
console.log(` ${chalk13.bold("Status:")} ${(STATUS_COLORS3[t.status] || chalk13.white)(t.status)}`);
|
|
3708
3722
|
console.log();
|
|
3709
|
-
console.log(
|
|
3710
|
-
console.log(
|
|
3723
|
+
console.log(chalk13.dim(" The store will be notified. You can follow up with:"));
|
|
3724
|
+
console.log(chalk13.dim(` clishop support show ${t.id}`));
|
|
3711
3725
|
console.log();
|
|
3712
3726
|
} catch (error) {
|
|
3713
3727
|
handleApiError(error);
|
|
@@ -3727,22 +3741,22 @@ function registerSupportCommands(program2) {
|
|
|
3727
3741
|
return;
|
|
3728
3742
|
}
|
|
3729
3743
|
if (tickets.length === 0) {
|
|
3730
|
-
console.log(
|
|
3744
|
+
console.log(chalk13.yellow("\nNo support tickets found.\n"));
|
|
3731
3745
|
return;
|
|
3732
3746
|
}
|
|
3733
|
-
console.log(
|
|
3747
|
+
console.log(chalk13.bold("\nYour Support Tickets:\n"));
|
|
3734
3748
|
for (const t of tickets) {
|
|
3735
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3736
|
-
const priorityColor = PRIORITY_COLORS[t.priority] ||
|
|
3749
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3750
|
+
const priorityColor = PRIORITY_COLORS[t.priority] || chalk13.white;
|
|
3737
3751
|
const date = new Date(t.createdAt).toLocaleDateString();
|
|
3738
3752
|
console.log(
|
|
3739
|
-
` ${
|
|
3753
|
+
` ${chalk13.bold(t.id)} ${statusColor(t.status.toUpperCase().padEnd(20))} ${priorityColor(t.priority.padEnd(8))} ${chalk13.dim(date)}`
|
|
3740
3754
|
);
|
|
3741
|
-
console.log(` ${
|
|
3742
|
-
console.log(` ${
|
|
3755
|
+
console.log(` ${chalk13.bold(t.subject)}`);
|
|
3756
|
+
console.log(` ${chalk13.dim(`Store: ${t.storeName} \xB7 Order: ${t.orderId} \xB7 ${t.category}`)}`);
|
|
3743
3757
|
if (t.lastMessage) {
|
|
3744
|
-
const sender = t.lastMessage.senderType === "store" ?
|
|
3745
|
-
console.log(` ${
|
|
3758
|
+
const sender = t.lastMessage.senderType === "store" ? chalk13.blue("Store") : t.lastMessage.senderType === "system" ? chalk13.gray("System") : chalk13.green("You");
|
|
3759
|
+
console.log(` ${chalk13.dim("Last:")} ${sender}: ${chalk13.dim(t.lastMessage.body)}`);
|
|
3746
3760
|
}
|
|
3747
3761
|
console.log();
|
|
3748
3762
|
}
|
|
@@ -3761,25 +3775,25 @@ function registerSupportCommands(program2) {
|
|
|
3761
3775
|
console.log(JSON.stringify(t, null, 2));
|
|
3762
3776
|
return;
|
|
3763
3777
|
}
|
|
3764
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3778
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3765
3779
|
console.log();
|
|
3766
|
-
console.log(
|
|
3767
|
-
console.log(` Subject: ${
|
|
3780
|
+
console.log(chalk13.bold(` Ticket ${chalk13.cyan(t.id)}`));
|
|
3781
|
+
console.log(` Subject: ${chalk13.bold(t.subject)}`);
|
|
3768
3782
|
console.log(` Status: ${statusColor(t.status.toUpperCase())}`);
|
|
3769
3783
|
console.log(` Category: ${t.category}`);
|
|
3770
|
-
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] ||
|
|
3784
|
+
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] || chalk13.white)(t.priority)}`);
|
|
3771
3785
|
console.log(` Store: ${t.storeName}`);
|
|
3772
3786
|
console.log(` Order: ${t.orderId}`);
|
|
3773
3787
|
console.log(` Created: ${new Date(t.createdAt).toLocaleString()}`);
|
|
3774
3788
|
if (t.resolvedAt) console.log(` Resolved: ${new Date(t.resolvedAt).toLocaleString()}`);
|
|
3775
|
-
console.log(
|
|
3789
|
+
console.log(chalk13.bold("\n Messages:\n"));
|
|
3776
3790
|
for (const m of t.messages) {
|
|
3777
3791
|
const time = new Date(m.createdAt).toLocaleString();
|
|
3778
3792
|
let sender;
|
|
3779
|
-
if (m.senderType === "customer") sender =
|
|
3780
|
-
else if (m.senderType === "store") sender =
|
|
3781
|
-
else sender =
|
|
3782
|
-
console.log(` ${sender} ${
|
|
3793
|
+
if (m.senderType === "customer") sender = chalk13.green.bold("You");
|
|
3794
|
+
else if (m.senderType === "store") sender = chalk13.blue.bold("Store");
|
|
3795
|
+
else sender = chalk13.gray.bold("System");
|
|
3796
|
+
console.log(` ${sender} ${chalk13.dim(time)}`);
|
|
3783
3797
|
console.log(` ${m.body}`);
|
|
3784
3798
|
console.log();
|
|
3785
3799
|
}
|
|
@@ -3797,7 +3811,7 @@ function registerSupportCommands(program2) {
|
|
|
3797
3811
|
}
|
|
3798
3812
|
]);
|
|
3799
3813
|
if (!message || !message.trim()) {
|
|
3800
|
-
console.error(
|
|
3814
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3801
3815
|
return;
|
|
3802
3816
|
}
|
|
3803
3817
|
const spinner = ora11("Sending reply...").start();
|
|
@@ -3805,8 +3819,8 @@ function registerSupportCommands(program2) {
|
|
|
3805
3819
|
const res = await api.post(`/support/${ticketId}/reply`, {
|
|
3806
3820
|
message: message.trim()
|
|
3807
3821
|
});
|
|
3808
|
-
spinner.succeed(
|
|
3809
|
-
console.log(
|
|
3822
|
+
spinner.succeed(chalk13.green("Reply sent!"));
|
|
3823
|
+
console.log(chalk13.dim(` Ticket status: ${res.data.ticketStatus}
|
|
3810
3824
|
`));
|
|
3811
3825
|
} catch (error) {
|
|
3812
3826
|
handleApiError(error);
|
|
@@ -3826,7 +3840,235 @@ function registerSupportCommands(program2) {
|
|
|
3826
3840
|
const spinner = ora11("Closing ticket...").start();
|
|
3827
3841
|
const api = getApiClient();
|
|
3828
3842
|
await api.patch(`/support/${ticketId}/status`, { status: "closed" });
|
|
3829
|
-
spinner.succeed(
|
|
3843
|
+
spinner.succeed(chalk13.green("Ticket closed."));
|
|
3844
|
+
} catch (error) {
|
|
3845
|
+
handleApiError(error);
|
|
3846
|
+
}
|
|
3847
|
+
});
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
// src/commands/feedback.ts
|
|
3851
|
+
import chalk14 from "chalk";
|
|
3852
|
+
import ora12 from "ora";
|
|
3853
|
+
import inquirer10 from "inquirer";
|
|
3854
|
+
var STATUS_COLORS4 = {
|
|
3855
|
+
open: chalk14.green,
|
|
3856
|
+
acknowledged: chalk14.cyan,
|
|
3857
|
+
in_progress: chalk14.yellow,
|
|
3858
|
+
fixed: chalk14.blue,
|
|
3859
|
+
wont_fix: chalk14.red,
|
|
3860
|
+
closed: chalk14.dim
|
|
3861
|
+
};
|
|
3862
|
+
var STATUS_LABELS = {
|
|
3863
|
+
open: "Open",
|
|
3864
|
+
acknowledged: "Acknowledged",
|
|
3865
|
+
in_progress: "In Progress",
|
|
3866
|
+
fixed: "Fixed",
|
|
3867
|
+
wont_fix: "Won't Fix",
|
|
3868
|
+
closed: "Closed"
|
|
3869
|
+
};
|
|
3870
|
+
var TYPE_COLORS = {
|
|
3871
|
+
bug: chalk14.red,
|
|
3872
|
+
suggestion: chalk14.magenta
|
|
3873
|
+
};
|
|
3874
|
+
function registerFeedbackCommands(program2) {
|
|
3875
|
+
const feedback = program2.command("feedback").description("Report bugs and suggest improvements");
|
|
3876
|
+
feedback.command("bug").alias("report-bug").description("Report a bug").option("--title <title>", "Short summary of the bug").option("--description <desc>", "General description").option("--steps <steps>", "Steps to reproduce the bug").option("--actual <behavior>", "What actually happens").option("--expected <behavior>", "What you expected to happen").action(async (opts) => {
|
|
3877
|
+
try {
|
|
3878
|
+
let title = opts.title;
|
|
3879
|
+
let description = opts.description;
|
|
3880
|
+
let stepsToReproduce = opts.steps;
|
|
3881
|
+
let actualBehavior = opts.actual;
|
|
3882
|
+
let expectedBehavior = opts.expected;
|
|
3883
|
+
if (!title || !description || !stepsToReproduce || !actualBehavior || !expectedBehavior) {
|
|
3884
|
+
console.log(chalk14.bold("\n\u{1F41B} Report a Bug\n"));
|
|
3885
|
+
console.log(chalk14.dim("Help us fix issues by describing what went wrong.\n"));
|
|
3886
|
+
const answers = await inquirer10.prompt([
|
|
3887
|
+
...!title ? [{
|
|
3888
|
+
type: "input",
|
|
3889
|
+
name: "title",
|
|
3890
|
+
message: "Bug title (short summary):",
|
|
3891
|
+
validate: (v) => v.trim().length > 0 || "Title is required"
|
|
3892
|
+
}] : [],
|
|
3893
|
+
...!description ? [{
|
|
3894
|
+
type: "input",
|
|
3895
|
+
name: "description",
|
|
3896
|
+
message: "General description of the bug:",
|
|
3897
|
+
validate: (v) => v.trim().length > 0 || "Description is required"
|
|
3898
|
+
}] : [],
|
|
3899
|
+
...!stepsToReproduce ? [{
|
|
3900
|
+
type: "editor",
|
|
3901
|
+
name: "stepsToReproduce",
|
|
3902
|
+
message: "Steps to reproduce (how do you trigger this bug?):"
|
|
3903
|
+
}] : [],
|
|
3904
|
+
...!actualBehavior ? [{
|
|
3905
|
+
type: "input",
|
|
3906
|
+
name: "actualBehavior",
|
|
3907
|
+
message: "What happens (actual behavior)?",
|
|
3908
|
+
validate: (v) => v.trim().length > 0 || "Actual behavior is required"
|
|
3909
|
+
}] : [],
|
|
3910
|
+
...!expectedBehavior ? [{
|
|
3911
|
+
type: "input",
|
|
3912
|
+
name: "expectedBehavior",
|
|
3913
|
+
message: "What did you expect to happen?",
|
|
3914
|
+
validate: (v) => v.trim().length > 0 || "Expected behavior is required"
|
|
3915
|
+
}] : []
|
|
3916
|
+
]);
|
|
3917
|
+
title = title || answers.title;
|
|
3918
|
+
description = description || answers.description;
|
|
3919
|
+
stepsToReproduce = stepsToReproduce || answers.stepsToReproduce;
|
|
3920
|
+
actualBehavior = actualBehavior || answers.actualBehavior;
|
|
3921
|
+
expectedBehavior = expectedBehavior || answers.expectedBehavior;
|
|
3922
|
+
}
|
|
3923
|
+
if (!stepsToReproduce?.trim()) {
|
|
3924
|
+
console.error(chalk14.red("\n\u2717 Steps to reproduce are required.\n"));
|
|
3925
|
+
return;
|
|
3926
|
+
}
|
|
3927
|
+
const spinner = ora12("Submitting bug report...").start();
|
|
3928
|
+
const api = getApiClient();
|
|
3929
|
+
const res = await api.post("/feedback", {
|
|
3930
|
+
type: "bug",
|
|
3931
|
+
title: title.trim(),
|
|
3932
|
+
description: description.trim(),
|
|
3933
|
+
stepsToReproduce: stepsToReproduce.trim(),
|
|
3934
|
+
actualBehavior: actualBehavior.trim(),
|
|
3935
|
+
expectedBehavior: expectedBehavior.trim()
|
|
3936
|
+
});
|
|
3937
|
+
spinner.succeed(chalk14.green("Bug report submitted!"));
|
|
3938
|
+
const f = res.data.feedback;
|
|
3939
|
+
console.log();
|
|
3940
|
+
console.log(` ${chalk14.bold("ID:")} ${chalk14.cyan(f.id)}`);
|
|
3941
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
3942
|
+
console.log(` ${chalk14.bold("Status:")} ${(STATUS_COLORS4[f.status] || chalk14.white)(STATUS_LABELS[f.status] || f.status)}`);
|
|
3943
|
+
console.log();
|
|
3944
|
+
console.log(chalk14.dim(" We'll investigate and update the status. Check back with:"));
|
|
3945
|
+
console.log(chalk14.dim(` clishop feedback show ${f.id}`));
|
|
3946
|
+
console.log();
|
|
3947
|
+
} catch (error) {
|
|
3948
|
+
handleApiError(error);
|
|
3949
|
+
}
|
|
3950
|
+
});
|
|
3951
|
+
feedback.command("suggest").alias("suggestion").description("Suggest an improvement or feature").option("--title <title>", "Short summary").option("--description <desc>", "Detailed suggestion").action(async (opts) => {
|
|
3952
|
+
try {
|
|
3953
|
+
let title = opts.title;
|
|
3954
|
+
let description = opts.description;
|
|
3955
|
+
if (!title || !description) {
|
|
3956
|
+
console.log(chalk14.bold("\n\u{1F4A1} Suggest an Improvement\n"));
|
|
3957
|
+
console.log(chalk14.dim("Tell us how we can make CLISHOP better.\n"));
|
|
3958
|
+
const answers = await inquirer10.prompt([
|
|
3959
|
+
...!title ? [{
|
|
3960
|
+
type: "input",
|
|
3961
|
+
name: "title",
|
|
3962
|
+
message: "Suggestion title (short summary):",
|
|
3963
|
+
validate: (v) => v.trim().length > 0 || "Title is required"
|
|
3964
|
+
}] : [],
|
|
3965
|
+
...!description ? [{
|
|
3966
|
+
type: "editor",
|
|
3967
|
+
name: "description",
|
|
3968
|
+
message: "Describe your suggestion (opens editor):"
|
|
3969
|
+
}] : []
|
|
3970
|
+
]);
|
|
3971
|
+
title = title || answers.title;
|
|
3972
|
+
description = description || answers.description;
|
|
3973
|
+
}
|
|
3974
|
+
if (!description?.trim()) {
|
|
3975
|
+
console.error(chalk14.red("\n\u2717 Description is required.\n"));
|
|
3976
|
+
return;
|
|
3977
|
+
}
|
|
3978
|
+
const spinner = ora12("Submitting suggestion...").start();
|
|
3979
|
+
const api = getApiClient();
|
|
3980
|
+
const res = await api.post("/feedback", {
|
|
3981
|
+
type: "suggestion",
|
|
3982
|
+
title: title.trim(),
|
|
3983
|
+
description: description.trim()
|
|
3984
|
+
});
|
|
3985
|
+
spinner.succeed(chalk14.green("Suggestion submitted!"));
|
|
3986
|
+
const f = res.data.feedback;
|
|
3987
|
+
console.log();
|
|
3988
|
+
console.log(` ${chalk14.bold("ID:")} ${chalk14.cyan(f.id)}`);
|
|
3989
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
3990
|
+
console.log(` ${chalk14.bold("Status:")} ${(STATUS_COLORS4[f.status] || chalk14.white)(STATUS_LABELS[f.status] || f.status)}`);
|
|
3991
|
+
console.log();
|
|
3992
|
+
console.log(chalk14.dim(" Thanks for the suggestion! Check status with:"));
|
|
3993
|
+
console.log(chalk14.dim(` clishop feedback show ${f.id}`));
|
|
3994
|
+
console.log();
|
|
3995
|
+
} catch (error) {
|
|
3996
|
+
handleApiError(error);
|
|
3997
|
+
}
|
|
3998
|
+
});
|
|
3999
|
+
feedback.command("list").alias("ls").description("List your bug reports and suggestions").option("--type <type>", "Filter by type (bug or suggestion)").option("--status <status>", "Filter by status").option("--json", "Output raw JSON").action(async (opts) => {
|
|
4000
|
+
try {
|
|
4001
|
+
const spinner = ora12("Fetching feedback...").start();
|
|
4002
|
+
const api = getApiClient();
|
|
4003
|
+
const res = await api.get("/feedback", {
|
|
4004
|
+
params: {
|
|
4005
|
+
...opts.type ? { type: opts.type } : {},
|
|
4006
|
+
...opts.status ? { status: opts.status } : {}
|
|
4007
|
+
}
|
|
4008
|
+
});
|
|
4009
|
+
spinner.stop();
|
|
4010
|
+
const items = res.data.feedback;
|
|
4011
|
+
if (opts.json) {
|
|
4012
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
4013
|
+
return;
|
|
4014
|
+
}
|
|
4015
|
+
if (items.length === 0) {
|
|
4016
|
+
console.log(chalk14.yellow("\nNo feedback found.\n"));
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
4019
|
+
console.log(chalk14.bold("\nYour Feedback:\n"));
|
|
4020
|
+
for (const f of items) {
|
|
4021
|
+
const typeColor = TYPE_COLORS[f.type] || chalk14.white;
|
|
4022
|
+
const statusColor = STATUS_COLORS4[f.status] || chalk14.white;
|
|
4023
|
+
const date = new Date(f.createdAt).toLocaleDateString();
|
|
4024
|
+
const typeLabel = f.type === "bug" ? "\u{1F41B} BUG" : "\u{1F4A1} SUGGESTION";
|
|
4025
|
+
console.log(
|
|
4026
|
+
` ${chalk14.bold(f.id)} ${typeColor(typeLabel.padEnd(14))} ${statusColor((STATUS_LABELS[f.status] || f.status).padEnd(14))} ${chalk14.dim(date)}`
|
|
4027
|
+
);
|
|
4028
|
+
console.log(` ${chalk14.bold(f.title)}`);
|
|
4029
|
+
if (f.adminNote) {
|
|
4030
|
+
console.log(` ${chalk14.cyan("Admin:")} ${f.adminNote}`);
|
|
4031
|
+
}
|
|
4032
|
+
console.log();
|
|
4033
|
+
}
|
|
4034
|
+
} catch (error) {
|
|
4035
|
+
handleApiError(error);
|
|
4036
|
+
}
|
|
4037
|
+
});
|
|
4038
|
+
feedback.command("show <id>").description("View a bug report or suggestion").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
4039
|
+
try {
|
|
4040
|
+
const spinner = ora12("Fetching feedback...").start();
|
|
4041
|
+
const api = getApiClient();
|
|
4042
|
+
const res = await api.get(`/feedback/${id}`);
|
|
4043
|
+
spinner.stop();
|
|
4044
|
+
const f = res.data.feedback;
|
|
4045
|
+
if (opts.json) {
|
|
4046
|
+
console.log(JSON.stringify(f, null, 2));
|
|
4047
|
+
return;
|
|
4048
|
+
}
|
|
4049
|
+
const statusColor = STATUS_COLORS4[f.status] || chalk14.white;
|
|
4050
|
+
const typeLabel = f.type === "bug" ? "\u{1F41B} Bug Report" : "\u{1F4A1} Suggestion";
|
|
4051
|
+
console.log();
|
|
4052
|
+
console.log(chalk14.bold(` ${typeLabel} \u2014 ${chalk14.cyan(f.id)}`));
|
|
4053
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
4054
|
+
console.log(` ${chalk14.bold("Status:")} ${statusColor(STATUS_LABELS[f.status] || f.status)}`);
|
|
4055
|
+
console.log(` ${chalk14.bold("Created:")} ${new Date(f.createdAt).toLocaleString()}`);
|
|
4056
|
+
console.log(` ${chalk14.bold("Updated:")} ${new Date(f.updatedAt).toLocaleString()}`);
|
|
4057
|
+
console.log(chalk14.bold("\n Description:"));
|
|
4058
|
+
console.log(` ${f.description}`);
|
|
4059
|
+
if (f.type === "bug") {
|
|
4060
|
+
console.log(chalk14.bold("\n Steps to Reproduce:"));
|
|
4061
|
+
console.log(` ${f.stepsToReproduce}`);
|
|
4062
|
+
console.log(chalk14.bold("\n Actual Behavior:"));
|
|
4063
|
+
console.log(` ${chalk14.red(f.actualBehavior)}`);
|
|
4064
|
+
console.log(chalk14.bold("\n Expected Behavior:"));
|
|
4065
|
+
console.log(` ${chalk14.green(f.expectedBehavior)}`);
|
|
4066
|
+
}
|
|
4067
|
+
if (f.adminNote) {
|
|
4068
|
+
console.log(chalk14.bold("\n Admin Response:"));
|
|
4069
|
+
console.log(` ${chalk14.cyan(f.adminNote)}`);
|
|
4070
|
+
}
|
|
4071
|
+
console.log();
|
|
3830
4072
|
} catch (error) {
|
|
3831
4073
|
handleApiError(error);
|
|
3832
4074
|
}
|
|
@@ -3835,13 +4077,13 @@ function registerSupportCommands(program2) {
|
|
|
3835
4077
|
|
|
3836
4078
|
// src/index.ts
|
|
3837
4079
|
var program = new Command();
|
|
3838
|
-
program.name("clishop").version("0.
|
|
4080
|
+
program.name("clishop").version("0.3.0").description(
|
|
3839
4081
|
chalk15.bold("CLISHOP") + ' \u2014 Order anything from your terminal.\n\n Use agents to set safety limits, addresses, and payment methods.\n The "default" agent is used when no agent is specified.'
|
|
3840
4082
|
).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
|
|
3841
4083
|
const agentOpt = thisCommand.opts().agent;
|
|
3842
4084
|
if (agentOpt) {
|
|
3843
|
-
const
|
|
3844
|
-
if (!
|
|
4085
|
+
const config = getConfig();
|
|
4086
|
+
if (!config.store.agents[agentOpt]) {
|
|
3845
4087
|
console.error(chalk15.red(`\u2717 Agent "${agentOpt}" does not exist.`));
|
|
3846
4088
|
process.exit(1);
|
|
3847
4089
|
}
|
|
@@ -3861,11 +4103,17 @@ registerStatusCommand(program);
|
|
|
3861
4103
|
registerSetupCommand(program);
|
|
3862
4104
|
registerAdvertiseCommands(program);
|
|
3863
4105
|
registerSupportCommands(program);
|
|
4106
|
+
registerFeedbackCommands(program);
|
|
3864
4107
|
async function main() {
|
|
4108
|
+
if (process.argv.includes("--mcp")) {
|
|
4109
|
+
const { startMcpServer } = await import("./mcp.js");
|
|
4110
|
+
await startMcpServer();
|
|
4111
|
+
return;
|
|
4112
|
+
}
|
|
3865
4113
|
const hasSubcommand = process.argv.length > 2;
|
|
3866
4114
|
if (!hasSubcommand) {
|
|
3867
|
-
const
|
|
3868
|
-
if (!
|
|
4115
|
+
const config = getConfig();
|
|
4116
|
+
if (!config.get("setupCompleted")) {
|
|
3869
4117
|
await runSetupWizard();
|
|
3870
4118
|
return;
|
|
3871
4119
|
}
|