clishop 0.2.0 → 0.2.1
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-EGTXN73P.js +188 -0
- package/dist/chunk-ZF3JIQRK.js +108 -0
- package/dist/config-63FY6NWX.js +24 -0
- package/dist/index.js +760 -719
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.js +14430 -0
- package/package.json +13 -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-EGTXN73P.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.');
|
|
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)));
|
|
84
34
|
}
|
|
85
|
-
|
|
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);
|
|
35
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
118
36
|
}
|
|
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;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
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:" }] : [],
|
|
@@ -392,83 +273,9 @@ function registerAgentCommands(program2) {
|
|
|
392
273
|
}
|
|
393
274
|
|
|
394
275
|
// src/commands/address.ts
|
|
395
|
-
import
|
|
276
|
+
import chalk3 from "chalk";
|
|
396
277
|
import ora2 from "ora";
|
|
397
278
|
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
279
|
function registerAddressCommands(program2) {
|
|
473
280
|
const address = program2.command("address").description("Manage shipping addresses (scoped to the active agent)");
|
|
474
281
|
address.command("list").alias("ls").description("List all addresses for the active agent").action(async () => {
|
|
@@ -482,26 +289,26 @@ function registerAddressCommands(program2) {
|
|
|
482
289
|
spinner.stop();
|
|
483
290
|
const addresses = res.data.addresses;
|
|
484
291
|
if (addresses.length === 0) {
|
|
485
|
-
console.log(
|
|
292
|
+
console.log(chalk3.yellow("\nNo addresses found. Add one with: clishop address add\n"));
|
|
486
293
|
return;
|
|
487
294
|
}
|
|
488
|
-
console.log(
|
|
295
|
+
console.log(chalk3.bold(`
|
|
489
296
|
Addresses for agent "${agent.name}":
|
|
490
297
|
`));
|
|
491
298
|
for (const addr of addresses) {
|
|
492
299
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
493
|
-
const marker = isDefault ?
|
|
494
|
-
console.log(`${marker}${
|
|
300
|
+
const marker = isDefault ? chalk3.green("\u25CF ") : " ";
|
|
301
|
+
console.log(`${marker}${chalk3.bold(addr.label)} ${chalk3.dim(`(${addr.id})`)}`);
|
|
495
302
|
if (addr.recipientName) console.log(` ${addr.recipientName}`);
|
|
496
|
-
if (addr.companyName) console.log(` ${
|
|
303
|
+
if (addr.companyName) console.log(` ${chalk3.cyan(addr.companyName)}`);
|
|
497
304
|
console.log(` ${addr.line1}`);
|
|
498
305
|
if (addr.line2) console.log(` ${addr.line2}`);
|
|
499
306
|
console.log(` ${addr.city}${addr.region ? `, ${addr.region}` : ""} ${addr.postalCode}`);
|
|
500
307
|
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(` ${
|
|
308
|
+
if (addr.recipientPhone) console.log(` ${chalk3.dim("Phone:")} ${addr.recipientPhone}`);
|
|
309
|
+
if (addr.vatNumber) console.log(` ${chalk3.dim("VAT:")} ${addr.vatNumber}`);
|
|
310
|
+
if (addr.taxId) console.log(` ${chalk3.dim("Tax ID:")} ${addr.taxId}`);
|
|
311
|
+
if (addr.instructions) console.log(` ${chalk3.dim("Instructions:")} ${addr.instructions}`);
|
|
505
312
|
console.log();
|
|
506
313
|
}
|
|
507
314
|
} catch (error) {
|
|
@@ -591,7 +398,7 @@ Addresses for agent "${agent.name}":
|
|
|
591
398
|
if (setDefault) {
|
|
592
399
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
593
400
|
}
|
|
594
|
-
spinner.succeed(
|
|
401
|
+
spinner.succeed(chalk3.green(`Address "${answers.label}" added.`));
|
|
595
402
|
} catch (error) {
|
|
596
403
|
handleApiError(error);
|
|
597
404
|
}
|
|
@@ -610,7 +417,7 @@ Addresses for agent "${agent.name}":
|
|
|
610
417
|
const spinner = ora2("Removing address...").start();
|
|
611
418
|
const api = getApiClient();
|
|
612
419
|
await api.delete(`/addresses/${id}`);
|
|
613
|
-
spinner.succeed(
|
|
420
|
+
spinner.succeed(chalk3.green("Address removed."));
|
|
614
421
|
const agent = getActiveAgent();
|
|
615
422
|
if (agent.defaultAddressId === id) {
|
|
616
423
|
updateAgent(agent.name, { defaultAddressId: void 0 });
|
|
@@ -622,13 +429,13 @@ Addresses for agent "${agent.name}":
|
|
|
622
429
|
address.command("set-default <id>").description("Set the default address for the active agent").action((id) => {
|
|
623
430
|
const agent = getActiveAgent();
|
|
624
431
|
updateAgent(agent.name, { defaultAddressId: id });
|
|
625
|
-
console.log(
|
|
432
|
+
console.log(chalk3.green(`
|
|
626
433
|
\u2713 Default address for agent "${agent.name}" set to ${id}.`));
|
|
627
434
|
});
|
|
628
435
|
}
|
|
629
436
|
|
|
630
437
|
// src/commands/payment.ts
|
|
631
|
-
import
|
|
438
|
+
import chalk4 from "chalk";
|
|
632
439
|
import ora3 from "ora";
|
|
633
440
|
function registerPaymentCommands(program2) {
|
|
634
441
|
const payment = program2.command("payment").description("Manage payment methods (scoped to the active agent)");
|
|
@@ -643,17 +450,17 @@ function registerPaymentCommands(program2) {
|
|
|
643
450
|
spinner.stop();
|
|
644
451
|
const methods = res.data.paymentMethods;
|
|
645
452
|
if (methods.length === 0) {
|
|
646
|
-
console.log(
|
|
453
|
+
console.log(chalk4.yellow("\nNo payment methods found. Add one with: clishop payment add\n"));
|
|
647
454
|
return;
|
|
648
455
|
}
|
|
649
|
-
console.log(
|
|
456
|
+
console.log(chalk4.bold(`
|
|
650
457
|
Payment methods for agent "${agent.name}":
|
|
651
458
|
`));
|
|
652
459
|
for (const pm of methods) {
|
|
653
460
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
654
|
-
const marker = isDefault ?
|
|
461
|
+
const marker = isDefault ? chalk4.green("\u25CF ") : " ";
|
|
655
462
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
656
|
-
console.log(`${marker}${
|
|
463
|
+
console.log(`${marker}${chalk4.bold(pm.label)}${last4} ${chalk4.dim(`[${pm.type}]`)} ${chalk4.dim(`(${pm.id})`)}`);
|
|
657
464
|
}
|
|
658
465
|
console.log();
|
|
659
466
|
} catch (error) {
|
|
@@ -670,11 +477,11 @@ Payment methods for agent "${agent.name}":
|
|
|
670
477
|
});
|
|
671
478
|
spinner.stop();
|
|
672
479
|
const { setupUrl } = res.data;
|
|
673
|
-
console.log(
|
|
674
|
-
console.log(
|
|
480
|
+
console.log(chalk4.bold("\nTo add a payment method securely, open this link in your browser:\n"));
|
|
481
|
+
console.log(chalk4.cyan.underline(` ${setupUrl}
|
|
675
482
|
`));
|
|
676
|
-
console.log(
|
|
677
|
-
console.log(
|
|
483
|
+
console.log(chalk4.dim("The CLI never collects raw card details. Payment is set up via the secure web portal."));
|
|
484
|
+
console.log(chalk4.dim("Once completed, run 'clishop payment list' to see your new method.\n"));
|
|
678
485
|
} catch (error) {
|
|
679
486
|
handleApiError(error);
|
|
680
487
|
}
|
|
@@ -684,7 +491,7 @@ Payment methods for agent "${agent.name}":
|
|
|
684
491
|
const spinner = ora3("Removing payment method...").start();
|
|
685
492
|
const api = getApiClient();
|
|
686
493
|
await api.delete(`/payment-methods/${id}`);
|
|
687
|
-
spinner.succeed(
|
|
494
|
+
spinner.succeed(chalk4.green("Payment method removed."));
|
|
688
495
|
const agent = getActiveAgent();
|
|
689
496
|
if (agent.defaultPaymentMethodId === id) {
|
|
690
497
|
updateAgent(agent.name, { defaultPaymentMethodId: void 0 });
|
|
@@ -696,13 +503,13 @@ Payment methods for agent "${agent.name}":
|
|
|
696
503
|
payment.command("set-default <id>").description("Set the default payment method for the active agent").action((id) => {
|
|
697
504
|
const agent = getActiveAgent();
|
|
698
505
|
updateAgent(agent.name, { defaultPaymentMethodId: id });
|
|
699
|
-
console.log(
|
|
506
|
+
console.log(chalk4.green(`
|
|
700
507
|
\u2713 Default payment for agent "${agent.name}" set to ${id}.`));
|
|
701
508
|
});
|
|
702
509
|
}
|
|
703
510
|
|
|
704
511
|
// src/commands/search.ts
|
|
705
|
-
import
|
|
512
|
+
import chalk5 from "chalk";
|
|
706
513
|
import ora4 from "ora";
|
|
707
514
|
import inquirer4 from "inquirer";
|
|
708
515
|
function formatPrice(cents, currency) {
|
|
@@ -736,7 +543,7 @@ function convertPrice(cents, fromCurrency, toCurrency, rates) {
|
|
|
736
543
|
function formatConverted(cents, fromCurrency, toCurrency, rates) {
|
|
737
544
|
const converted = convertPrice(cents, fromCurrency, toCurrency, rates);
|
|
738
545
|
if (converted == null) return "";
|
|
739
|
-
return
|
|
546
|
+
return chalk5.dim(` (~${formatPrice(converted, toCurrency)})`);
|
|
740
547
|
}
|
|
741
548
|
function renderStars(rating) {
|
|
742
549
|
const full = Math.floor(rating);
|
|
@@ -767,13 +574,13 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
767
574
|
let line = "";
|
|
768
575
|
for (const word of words) {
|
|
769
576
|
if (line.length + word.length + 1 > 90) {
|
|
770
|
-
console.log(`${pad}${
|
|
577
|
+
console.log(`${pad}${chalk5.dim(line)}`);
|
|
771
578
|
line = word;
|
|
772
579
|
} else {
|
|
773
580
|
line = line ? `${line} ${word}` : word;
|
|
774
581
|
}
|
|
775
582
|
}
|
|
776
|
-
if (line) console.log(`${pad}${
|
|
583
|
+
if (line) console.log(`${pad}${chalk5.dim(line)}`);
|
|
777
584
|
} else {
|
|
778
585
|
console.log(`${pad}${data}`);
|
|
779
586
|
}
|
|
@@ -786,12 +593,12 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
786
593
|
if (Array.isArray(data)) {
|
|
787
594
|
for (const item of data) {
|
|
788
595
|
if (typeof item === "string") {
|
|
789
|
-
console.log(`${pad}${
|
|
596
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${item}`);
|
|
790
597
|
} else if (typeof item === "object" && item !== null) {
|
|
791
598
|
renderFreeFormInfo(item, indent + 2);
|
|
792
599
|
console.log();
|
|
793
600
|
} else {
|
|
794
|
-
console.log(`${pad}${
|
|
601
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${String(item)}`);
|
|
795
602
|
}
|
|
796
603
|
}
|
|
797
604
|
return;
|
|
@@ -803,20 +610,20 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
803
610
|
if (value == null) continue;
|
|
804
611
|
if (typeof value === "string") {
|
|
805
612
|
if (value.length > 80) {
|
|
806
|
-
console.log(`${pad}${
|
|
613
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
807
614
|
renderFreeFormInfo(value, indent + 4);
|
|
808
615
|
} else {
|
|
809
|
-
console.log(`${pad}${
|
|
616
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
810
617
|
}
|
|
811
618
|
} else if (typeof value === "number") {
|
|
812
|
-
console.log(`${pad}${
|
|
619
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
813
620
|
} else if (typeof value === "boolean") {
|
|
814
|
-
console.log(`${pad}${
|
|
621
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value ? chalk5.green("Yes") : chalk5.red("No")}`);
|
|
815
622
|
} else if (Array.isArray(value)) {
|
|
816
|
-
console.log(`${pad}${
|
|
623
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
817
624
|
renderFreeFormInfo(value, indent + 4);
|
|
818
625
|
} else if (typeof value === "object") {
|
|
819
|
-
console.log(`${pad}${
|
|
626
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
820
627
|
renderFreeFormInfo(value, indent + 4);
|
|
821
628
|
}
|
|
822
629
|
}
|
|
@@ -824,95 +631,95 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
824
631
|
}
|
|
825
632
|
function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
826
633
|
const num = index + 1;
|
|
827
|
-
const storeBadge = result.storeName ?
|
|
634
|
+
const storeBadge = result.storeName ? chalk5.dim(` from ${result.storeName}`) : "";
|
|
828
635
|
console.log(
|
|
829
|
-
` ${
|
|
636
|
+
` ${chalk5.dim(`[${num}]`)} ${chalk5.bold.cyan(result.info?.title || result.info?.product_id || result.productId)}${storeBadge}`
|
|
830
637
|
);
|
|
831
|
-
console.log(` ${
|
|
638
|
+
console.log(` ${chalk5.dim(`ID: ${result.productId}`)}`);
|
|
832
639
|
if (result.error) {
|
|
833
|
-
console.log(` ${
|
|
640
|
+
console.log(` ${chalk5.red(`Error: ${result.error}`)}`);
|
|
834
641
|
console.log();
|
|
835
642
|
return;
|
|
836
643
|
}
|
|
837
644
|
if (result.info?.product_url) {
|
|
838
|
-
console.log(` ${
|
|
645
|
+
console.log(` ${chalk5.blue.underline(result.info.product_url)}`);
|
|
839
646
|
}
|
|
840
647
|
console.log();
|
|
841
648
|
const info = result.info || {};
|
|
842
649
|
if (info.price) {
|
|
843
650
|
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 = ` ${
|
|
651
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
845
652
|
if (info.list_price?.amount) {
|
|
846
653
|
const listStr = formatPriceFn(
|
|
847
654
|
Math.round(parseFloat(info.list_price.amount) * 100),
|
|
848
655
|
info.list_price.currency || info.price.currency
|
|
849
656
|
);
|
|
850
|
-
priceLine +=
|
|
657
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
851
658
|
}
|
|
852
659
|
console.log(priceLine);
|
|
853
660
|
}
|
|
854
661
|
if (info.pricing && !info.price) {
|
|
855
662
|
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 = ` ${
|
|
663
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
857
664
|
if (info.pricing.compare_at) {
|
|
858
665
|
const listStr = formatPriceFn(
|
|
859
666
|
Math.round(parseFloat(info.pricing.compare_at) * 100),
|
|
860
667
|
info.pricing.currency
|
|
861
668
|
);
|
|
862
|
-
priceLine +=
|
|
669
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
863
670
|
}
|
|
864
671
|
console.log(priceLine);
|
|
865
672
|
}
|
|
866
673
|
if (info.rating) {
|
|
867
674
|
const ratingScore = typeof info.rating === "object" ? `${info.rating.score}/${info.rating.max}` : String(info.rating);
|
|
868
|
-
let ratingLine = ` ${
|
|
675
|
+
let ratingLine = ` ${chalk5.bold("Rating:")} ${chalk5.yellow(ratingScore)}`;
|
|
869
676
|
if (info.review_count) {
|
|
870
|
-
ratingLine +=
|
|
677
|
+
ratingLine += chalk5.dim(` (${info.review_count.toLocaleString()} reviews)`);
|
|
871
678
|
}
|
|
872
679
|
console.log(ratingLine);
|
|
873
680
|
}
|
|
874
681
|
if (info.brand) {
|
|
875
|
-
console.log(` ${
|
|
682
|
+
console.log(` ${chalk5.bold("Brand:")} ${info.brand}`);
|
|
876
683
|
}
|
|
877
684
|
if (info.marketplace) {
|
|
878
|
-
console.log(` ${
|
|
685
|
+
console.log(` ${chalk5.bold("Marketplace:")} ${info.marketplace.name || info.marketplace.domain || ""}`);
|
|
879
686
|
}
|
|
880
687
|
if (info.availability) {
|
|
881
688
|
if (typeof info.availability === "string") {
|
|
882
689
|
const isInStock = info.availability.toLowerCase().includes("in stock");
|
|
883
|
-
console.log(` ${
|
|
690
|
+
console.log(` ${chalk5.bold("Availability:")} ${isInStock ? chalk5.green(info.availability) : chalk5.yellow(info.availability)}`);
|
|
884
691
|
} else if (typeof info.availability === "object") {
|
|
885
|
-
const status = info.availability.in_stock ?
|
|
886
|
-
let availLine = ` ${
|
|
692
|
+
const status = info.availability.in_stock ? chalk5.green("In Stock") : chalk5.red("Out of Stock");
|
|
693
|
+
let availLine = ` ${chalk5.bold("Availability:")} ${status}`;
|
|
887
694
|
if (info.availability.quantity != null) {
|
|
888
|
-
availLine +=
|
|
695
|
+
availLine += chalk5.dim(` (${info.availability.quantity} available)`);
|
|
889
696
|
}
|
|
890
697
|
console.log(availLine);
|
|
891
698
|
}
|
|
892
699
|
}
|
|
893
700
|
if (info.prime) {
|
|
894
|
-
console.log(` ${
|
|
701
|
+
console.log(` ${chalk5.bold("Prime:")} ${chalk5.blue("\u2713 Prime eligible")}`);
|
|
895
702
|
}
|
|
896
703
|
if (info.shipping && typeof info.shipping === "object") {
|
|
897
704
|
const parts = [];
|
|
898
|
-
if (info.shipping.free) parts.push(
|
|
705
|
+
if (info.shipping.free) parts.push(chalk5.green("Free Shipping"));
|
|
899
706
|
if (info.shipping.estimated_days) parts.push(`${info.shipping.estimated_days}-day delivery`);
|
|
900
707
|
if (info.shipping.price?.amount) parts.push(`${info.shipping.price.amount} ${info.shipping.price.currency || ""}`);
|
|
901
708
|
if (info.shipping.weight_kg) parts.push(`${info.shipping.weight_kg}kg`);
|
|
902
709
|
if (parts.length > 0) {
|
|
903
|
-
console.log(` ${
|
|
710
|
+
console.log(` ${chalk5.bold("Shipping:")} ${parts.join(" \xB7 ")}`);
|
|
904
711
|
}
|
|
905
712
|
}
|
|
906
713
|
if (info.delivery_info) {
|
|
907
|
-
console.log(` ${
|
|
714
|
+
console.log(` ${chalk5.bold("Delivery:")} ${info.delivery_info}`);
|
|
908
715
|
}
|
|
909
716
|
if (info.returns && typeof info.returns === "object") {
|
|
910
717
|
const parts = [];
|
|
911
|
-
if (info.returns.free) parts.push(
|
|
718
|
+
if (info.returns.free) parts.push(chalk5.green("Free Returns"));
|
|
912
719
|
if (info.returns.window_days) parts.push(`${info.returns.window_days}-day window`);
|
|
913
720
|
if (info.returns.note) parts.push(info.returns.note);
|
|
914
721
|
if (parts.length > 0) {
|
|
915
|
-
console.log(` ${
|
|
722
|
+
console.log(` ${chalk5.bold("Returns:")} ${parts.join(" \xB7 ")}`);
|
|
916
723
|
}
|
|
917
724
|
}
|
|
918
725
|
if (info.checkout && typeof info.checkout === "object") {
|
|
@@ -920,18 +727,18 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
920
727
|
if (info.checkout.mode) parts.push(info.checkout.mode);
|
|
921
728
|
if (info.checkout.note) parts.push(info.checkout.note);
|
|
922
729
|
if (parts.length > 0) {
|
|
923
|
-
console.log(` ${
|
|
730
|
+
console.log(` ${chalk5.bold("Checkout:")} ${parts.join(" \u2014 ")}`);
|
|
924
731
|
}
|
|
925
732
|
}
|
|
926
733
|
if (info.sold_by) {
|
|
927
|
-
console.log(` ${
|
|
734
|
+
console.log(` ${chalk5.bold("Sold by:")} ${info.sold_by}`);
|
|
928
735
|
}
|
|
929
736
|
if (info.categories && Array.isArray(info.categories)) {
|
|
930
|
-
console.log(` ${
|
|
737
|
+
console.log(` ${chalk5.bold("Category:")} ${info.categories.join(" > ")}`);
|
|
931
738
|
}
|
|
932
739
|
console.log();
|
|
933
740
|
if (info.features && Array.isArray(info.features) && info.features.length > 0) {
|
|
934
|
-
console.log(` ${
|
|
741
|
+
console.log(` ${chalk5.bold("Key Features:")}`);
|
|
935
742
|
for (const feature of info.features) {
|
|
936
743
|
if (feature.length > 80) {
|
|
937
744
|
const words = feature.split(/\s+/);
|
|
@@ -940,7 +747,7 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
940
747
|
for (const word of words) {
|
|
941
748
|
if (line.length + word.length + 1 > 76) {
|
|
942
749
|
if (first) {
|
|
943
|
-
console.log(` ${
|
|
750
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
944
751
|
first = false;
|
|
945
752
|
} else {
|
|
946
753
|
console.log(` ${line}`);
|
|
@@ -952,75 +759,75 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
952
759
|
}
|
|
953
760
|
if (line) {
|
|
954
761
|
if (first) {
|
|
955
|
-
console.log(` ${
|
|
762
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
956
763
|
} else {
|
|
957
764
|
console.log(` ${line}`);
|
|
958
765
|
}
|
|
959
766
|
}
|
|
960
767
|
} else {
|
|
961
|
-
console.log(` ${
|
|
768
|
+
console.log(` ${chalk5.dim("\u2022")} ${feature}`);
|
|
962
769
|
}
|
|
963
770
|
}
|
|
964
771
|
console.log();
|
|
965
772
|
}
|
|
966
773
|
if (info.description) {
|
|
967
|
-
console.log(` ${
|
|
774
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
968
775
|
const words = info.description.split(/\s+/);
|
|
969
776
|
let line = "";
|
|
970
777
|
for (const word of words) {
|
|
971
778
|
if (line.length + word.length + 1 > 76) {
|
|
972
|
-
console.log(` ${
|
|
779
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
973
780
|
line = word;
|
|
974
781
|
} else {
|
|
975
782
|
line = line ? `${line} ${word}` : word;
|
|
976
783
|
}
|
|
977
784
|
}
|
|
978
|
-
if (line) console.log(` ${
|
|
785
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
979
786
|
console.log();
|
|
980
787
|
}
|
|
981
788
|
if (info.specifications && typeof info.specifications === "object") {
|
|
982
789
|
const specs = info.specifications;
|
|
983
790
|
const keys = Object.keys(specs);
|
|
984
791
|
if (keys.length > 0) {
|
|
985
|
-
console.log(` ${
|
|
792
|
+
console.log(` ${chalk5.bold("Specifications:")}`);
|
|
986
793
|
const maxKeyLen = Math.min(30, Math.max(...keys.map((k) => k.length)));
|
|
987
794
|
for (const [key, value] of Object.entries(specs)) {
|
|
988
795
|
const paddedKey = key.padEnd(maxKeyLen);
|
|
989
|
-
console.log(` ${
|
|
796
|
+
console.log(` ${chalk5.dim(paddedKey)} ${value}`);
|
|
990
797
|
}
|
|
991
798
|
console.log();
|
|
992
799
|
}
|
|
993
800
|
}
|
|
994
801
|
if (info.images && Array.isArray(info.images) && info.images.length > 0) {
|
|
995
|
-
console.log(` ${
|
|
802
|
+
console.log(` ${chalk5.bold("Images:")} ${chalk5.dim(`${info.images.length} available`)}`);
|
|
996
803
|
for (let j = 0; j < Math.min(3, info.images.length); j++) {
|
|
997
|
-
console.log(` ${
|
|
804
|
+
console.log(` ${chalk5.dim(`[${j + 1}]`)} ${chalk5.blue.underline(info.images[j])}`);
|
|
998
805
|
}
|
|
999
806
|
if (info.images.length > 3) {
|
|
1000
|
-
console.log(` ${
|
|
807
|
+
console.log(` ${chalk5.dim(`... and ${info.images.length - 3} more`)}`);
|
|
1001
808
|
}
|
|
1002
809
|
console.log();
|
|
1003
810
|
}
|
|
1004
811
|
if (info.about && Array.isArray(info.about) && info.about.length > 0) {
|
|
1005
|
-
console.log(` ${
|
|
812
|
+
console.log(` ${chalk5.bold("About This Item:")}`);
|
|
1006
813
|
for (const section of info.about) {
|
|
1007
814
|
const words = section.split(/\s+/);
|
|
1008
815
|
let line = "";
|
|
1009
816
|
for (const word of words) {
|
|
1010
817
|
if (line.length + word.length + 1 > 76) {
|
|
1011
|
-
console.log(` ${
|
|
818
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1012
819
|
line = word;
|
|
1013
820
|
} else {
|
|
1014
821
|
line = line ? `${line} ${word}` : word;
|
|
1015
822
|
}
|
|
1016
823
|
}
|
|
1017
|
-
if (line) console.log(` ${
|
|
824
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1018
825
|
console.log();
|
|
1019
826
|
}
|
|
1020
827
|
}
|
|
1021
828
|
if (info.seo && typeof info.seo === "object" && Object.keys(info.seo).length > 0) {
|
|
1022
829
|
if (info.seo.title || info.seo.description) {
|
|
1023
|
-
console.log(` ${
|
|
830
|
+
console.log(` ${chalk5.bold("SEO:")}`);
|
|
1024
831
|
if (info.seo.title) console.log(` Title: ${info.seo.title}`);
|
|
1025
832
|
if (info.seo.description) console.log(` Description: ${info.seo.description}`);
|
|
1026
833
|
console.log();
|
|
@@ -1064,33 +871,33 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
1064
871
|
]);
|
|
1065
872
|
const extraKeys = Object.keys(info).filter((k) => !handledKeys.has(k));
|
|
1066
873
|
if (extraKeys.length > 0) {
|
|
1067
|
-
console.log(` ${
|
|
874
|
+
console.log(` ${chalk5.bold("Additional Information:")}`);
|
|
1068
875
|
for (const key of extraKeys) {
|
|
1069
876
|
const value = info[key];
|
|
1070
877
|
if (value == null) continue;
|
|
1071
878
|
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1072
879
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1073
|
-
console.log(` ${
|
|
880
|
+
console.log(` ${chalk5.dim(label + ":")} ${value}`);
|
|
1074
881
|
} else if (Array.isArray(value)) {
|
|
1075
|
-
console.log(` ${
|
|
882
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1076
883
|
renderFreeFormInfo(value, 10);
|
|
1077
884
|
} else if (typeof value === "object") {
|
|
1078
|
-
console.log(` ${
|
|
885
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1079
886
|
renderFreeFormInfo(value, 10);
|
|
1080
887
|
}
|
|
1081
888
|
}
|
|
1082
889
|
console.log();
|
|
1083
890
|
}
|
|
1084
891
|
if (info.tags && Array.isArray(info.tags) && info.tags.length > 0) {
|
|
1085
|
-
console.log(` ${
|
|
892
|
+
console.log(` ${chalk5.bold("Tags:")} ${info.tags.map((t) => chalk5.dim(`#${t}`)).join(" ")}`);
|
|
1086
893
|
console.log();
|
|
1087
894
|
}
|
|
1088
895
|
if (info.note) {
|
|
1089
|
-
console.log(` ${
|
|
896
|
+
console.log(` ${chalk5.dim(`\u2139 ${info.note}`)}`);
|
|
1090
897
|
console.log();
|
|
1091
898
|
}
|
|
1092
899
|
if (index < totalResults - 1) {
|
|
1093
|
-
console.log(
|
|
900
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1094
901
|
console.log();
|
|
1095
902
|
}
|
|
1096
903
|
}
|
|
@@ -1245,21 +1052,21 @@ function registerSearchCommands(program2) {
|
|
|
1245
1052
|
const didExtendedSearch = forceExtended || res.data.extended != null;
|
|
1246
1053
|
if (result.products.length === 0 && (!extended || extended.total === 0)) {
|
|
1247
1054
|
if (didExtendedSearch) {
|
|
1248
|
-
console.log(
|
|
1055
|
+
console.log(chalk5.yellow(`
|
|
1249
1056
|
No results found for "${query}" (searched local catalog + all vendor stores).`));
|
|
1250
1057
|
} else {
|
|
1251
|
-
console.log(
|
|
1058
|
+
console.log(chalk5.yellow(`
|
|
1252
1059
|
No results found for "${query}".`));
|
|
1253
1060
|
}
|
|
1254
1061
|
if (!didExtendedSearch && disableExtended) {
|
|
1255
1062
|
console.log(
|
|
1256
|
-
|
|
1257
|
-
Run: `) +
|
|
1063
|
+
chalk5.dim("\n \u{1F50D} Tip: ") + chalk5.white("Extended search was disabled. Enable it to query vendor stores in real-time:") + chalk5.dim(`
|
|
1064
|
+
Run: `) + chalk5.cyan(`clishop search "${query}" --extended-search`) + chalk5.dim("\n")
|
|
1258
1065
|
);
|
|
1259
1066
|
}
|
|
1260
1067
|
console.log(
|
|
1261
|
-
|
|
1262
|
-
Run: `) +
|
|
1068
|
+
chalk5.dim(" \u{1F4A1} Tip: ") + chalk5.white("Can't find what you need? Advertise your request and let vendors come to you!") + chalk5.dim(`
|
|
1069
|
+
Run: `) + chalk5.cyan(`clishop advertise create`) + chalk5.dim(` or `) + chalk5.cyan(`clishop advertise quick "${query}"`) + chalk5.dim("\n")
|
|
1263
1070
|
);
|
|
1264
1071
|
return;
|
|
1265
1072
|
}
|
|
@@ -1359,15 +1166,15 @@ No results found for "${query}".`));
|
|
|
1359
1166
|
} else {
|
|
1360
1167
|
const totalCount = result.total + (extended?.total || 0);
|
|
1361
1168
|
if (result.products.length > 0 && extended?.total > 0) {
|
|
1362
|
-
console.log(
|
|
1169
|
+
console.log(chalk5.bold(`
|
|
1363
1170
|
Results for "${query}" \u2014 ${result.total} local + ${extended.total} from stores
|
|
1364
1171
|
`));
|
|
1365
1172
|
} else if (extended?.total > 0) {
|
|
1366
|
-
console.log(
|
|
1173
|
+
console.log(chalk5.bold(`
|
|
1367
1174
|
Extended search for "${query}" \u2014 ${extended.total} result(s) from ${extended.storesResponded} store(s)
|
|
1368
1175
|
`));
|
|
1369
1176
|
} else {
|
|
1370
|
-
console.log(
|
|
1177
|
+
console.log(chalk5.bold(`
|
|
1371
1178
|
Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
1372
1179
|
`));
|
|
1373
1180
|
}
|
|
@@ -1380,20 +1187,20 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1380
1187
|
const fastest = allProducts.filter((p) => p.shippingDays != null).sort((a, b) => (a.shippingDays ?? 99) - (b.shippingDays ?? 99))[0];
|
|
1381
1188
|
const bestRated = allProducts.filter((p) => (p.rating ?? 0) > 0).sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0))[0];
|
|
1382
1189
|
const parts = [];
|
|
1383
|
-
parts.push(`${
|
|
1190
|
+
parts.push(`${chalk5.green("Best price:")} ${formatPrice(cheapest.totalCost, cheapest.currency)} at ${cheapest.vendor}`);
|
|
1384
1191
|
if (fastest?.shippingDays != null) {
|
|
1385
|
-
parts.push(`${
|
|
1192
|
+
parts.push(`${chalk5.blue("Fastest:")} ${deliveryLabel(fastest.shippingDays)} at ${fastest.vendor}`);
|
|
1386
1193
|
}
|
|
1387
1194
|
if (bestRated?.rating) {
|
|
1388
|
-
parts.push(`${
|
|
1195
|
+
parts.push(`${chalk5.yellow("Top rated:")} ${scoreOutOf10(bestRated.rating)}/10 at ${bestRated.vendor}`);
|
|
1389
1196
|
}
|
|
1390
|
-
console.log(` ${
|
|
1197
|
+
console.log(` ${chalk5.dim("\u250C")} ${parts.join(chalk5.dim(" \u2502 "))}`);
|
|
1391
1198
|
const prices = withTotal.map((p) => p.totalCost);
|
|
1392
1199
|
const minP = Math.min(...prices);
|
|
1393
1200
|
const maxP = Math.max(...prices);
|
|
1394
1201
|
const avgP = Math.round(prices.reduce((a, b) => a + b, 0) / prices.length);
|
|
1395
1202
|
const curr = allProducts[0].currency;
|
|
1396
|
-
console.log(` ${
|
|
1203
|
+
console.log(` ${chalk5.dim("\u2514")} ${chalk5.dim(`Price range: ${formatPrice(minP, curr)} \u2013 ${formatPrice(maxP, curr)} \xB7 Average: ${formatPrice(avgP, curr)}`)}`);
|
|
1397
1204
|
console.log();
|
|
1398
1205
|
}
|
|
1399
1206
|
for (let i = 0; i < allProducts.length; i++) {
|
|
@@ -1403,53 +1210,53 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1403
1210
|
const shippingPrice = p.freeShipping ? 0 : p.shippingPriceInCents ?? 0;
|
|
1404
1211
|
const totalCost = itemPrice + shippingPrice;
|
|
1405
1212
|
const badges = [];
|
|
1406
|
-
if (i === 0) badges.push(
|
|
1213
|
+
if (i === 0) badges.push(chalk5.bgGreen.black(" BEST MATCH "));
|
|
1407
1214
|
const allCosts = allProducts.map((x) => x.priceInCents + (x.freeShipping ? 0 : x.shippingPriceInCents ?? 0));
|
|
1408
|
-
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(
|
|
1215
|
+
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(chalk5.bgYellow.black(" BEST VALUE "));
|
|
1409
1216
|
const converted = formatConverted(totalCost, p.currency, userCurrency, exchangeRates);
|
|
1410
1217
|
if (opts.compact) {
|
|
1411
1218
|
const priceStr = formatPrice(totalCost, p.currency) + converted;
|
|
1412
1219
|
const store = p.vendor;
|
|
1413
1220
|
const delivery = p.shippingDays != null ? `Arrives ${estimatedArrival(p.shippingDays)}` : "";
|
|
1414
1221
|
console.log(
|
|
1415
|
-
` ${
|
|
1222
|
+
` ${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
1223
|
);
|
|
1417
1224
|
continue;
|
|
1418
1225
|
}
|
|
1419
|
-
console.log(` ${
|
|
1420
|
-
let priceLine = ` ${
|
|
1226
|
+
console.log(` ${chalk5.dim(`[${num}]`)} ${chalk5.bold.cyan(p.name)}${badges.length ? " " + badges.join(" ") : ""}`);
|
|
1227
|
+
let priceLine = ` ${chalk5.bold.white(formatPrice(itemPrice, p.currency))}`;
|
|
1421
1228
|
if (p.freeShipping) {
|
|
1422
|
-
priceLine +=
|
|
1229
|
+
priceLine += chalk5.green(" Free Shipping");
|
|
1423
1230
|
} else if (p.shippingPriceInCents != null && (p.shippingPriceInCents ?? 0) > 0) {
|
|
1424
|
-
priceLine +=
|
|
1425
|
-
priceLine +=
|
|
1231
|
+
priceLine += chalk5.dim(` + ${formatPrice(shippingPrice, p.currency)} shipping`);
|
|
1232
|
+
priceLine += chalk5.bold(` = ${formatPrice(totalCost, p.currency)}`);
|
|
1426
1233
|
}
|
|
1427
1234
|
priceLine += converted;
|
|
1428
1235
|
if (p.shippingDays != null) {
|
|
1429
|
-
priceLine +=
|
|
1236
|
+
priceLine += chalk5.blue(` Arrives ${estimatedArrival(p.shippingDays)}`);
|
|
1430
1237
|
}
|
|
1431
1238
|
console.log(priceLine);
|
|
1432
1239
|
const meta = [];
|
|
1433
|
-
const storeBadge = p.storeVerified ?
|
|
1434
|
-
const storeScore = p.storeRating != null ?
|
|
1240
|
+
const storeBadge = p.storeVerified ? chalk5.green(" \u2713") : "";
|
|
1241
|
+
const storeScore = p.storeRating != null ? chalk5.dim(` ${p.storeRating.toFixed(1)}/10`) : chalk5.dim(" (no store rating)");
|
|
1435
1242
|
meta.push(`${p.vendor}${storeBadge}${storeScore}`);
|
|
1436
1243
|
if (p.brand) meta.push(p.brand);
|
|
1437
1244
|
if (p.rating && p.rating > 0) {
|
|
1438
|
-
meta.push(
|
|
1245
|
+
meta.push(chalk5.yellow(`${scoreOutOf10(p.rating)}/10`) + (p.reviewCount ? chalk5.dim(` (${p.reviewCount})`) : ""));
|
|
1439
1246
|
}
|
|
1440
|
-
if (p.isExtended) meta.push(
|
|
1441
|
-
console.log(` ${
|
|
1442
|
-
if (p.id) console.log(` ${
|
|
1247
|
+
if (p.isExtended) meta.push(chalk5.magenta("via extended search"));
|
|
1248
|
+
console.log(` ${chalk5.dim(meta.join(" \xB7 "))}`);
|
|
1249
|
+
if (p.id) console.log(` ${chalk5.dim(`ID: ${p.id}`)}`);
|
|
1443
1250
|
;
|
|
1444
1251
|
if (opts.detailed) {
|
|
1445
|
-
if (p.category) console.log(` ${
|
|
1446
|
-
if (p.variant || p.variantLabel) console.log(` ${
|
|
1252
|
+
if (p.category) console.log(` ${chalk5.dim(`Category: ${p.category}`)}`);
|
|
1253
|
+
if (p.variant || p.variantLabel) console.log(` ${chalk5.dim(`Variant: ${p.variant || p.variantLabel}`)}`);
|
|
1447
1254
|
if (p.description) {
|
|
1448
|
-
console.log(` ${
|
|
1255
|
+
console.log(` ${chalk5.dim(p.description.length > 200 ? p.description.slice(0, 200) + "..." : p.description)}`);
|
|
1449
1256
|
}
|
|
1450
1257
|
} else {
|
|
1451
1258
|
if (p.description) {
|
|
1452
|
-
console.log(` ${
|
|
1259
|
+
console.log(` ${chalk5.dim(p.description.length > 80 ? p.description.slice(0, 80) + "..." : p.description)}`);
|
|
1453
1260
|
}
|
|
1454
1261
|
}
|
|
1455
1262
|
console.log();
|
|
@@ -1459,15 +1266,15 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1459
1266
|
if (opts.interactive && allProducts.length > 0) {
|
|
1460
1267
|
const selectableProducts = allProducts.map((p, idx) => ({ product: p, index: idx })).filter((item) => item.product.id);
|
|
1461
1268
|
if (selectableProducts.length > 0) {
|
|
1462
|
-
console.log(
|
|
1269
|
+
console.log(chalk5.dim(" \u2500".repeat(30)));
|
|
1463
1270
|
console.log();
|
|
1464
1271
|
const choices = selectableProducts.map((item) => {
|
|
1465
1272
|
const p = item.product;
|
|
1466
1273
|
const priceStr = formatPrice(p.priceInCents, p.currency);
|
|
1467
1274
|
const storeStr = p.vendor || "Unknown";
|
|
1468
|
-
const extLabel = p.isExtended ?
|
|
1275
|
+
const extLabel = p.isExtended ? chalk5.magenta(" [store]") : chalk5.dim(" [local]");
|
|
1469
1276
|
return {
|
|
1470
|
-
name: `${
|
|
1277
|
+
name: `${chalk5.cyan(p.name.length > 50 ? p.name.slice(0, 47) + "..." : p.name)} ${chalk5.bold(priceStr)} ${chalk5.dim(storeStr)}${extLabel}`,
|
|
1471
1278
|
value: item.product.id,
|
|
1472
1279
|
short: p.name.length > 40 ? p.name.slice(0, 37) + "..." : p.name
|
|
1473
1280
|
};
|
|
@@ -1499,7 +1306,7 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1499
1306
|
infoSpinner.stop();
|
|
1500
1307
|
const { results: infoResults } = infoRes.data;
|
|
1501
1308
|
if (infoResults && infoResults.length > 0) {
|
|
1502
|
-
console.log(
|
|
1309
|
+
console.log(chalk5.bold(`
|
|
1503
1310
|
Store Information \u2014 ${infoResults.length} result(s)
|
|
1504
1311
|
`));
|
|
1505
1312
|
for (let i = 0; i < infoResults.length; i++) {
|
|
@@ -1507,11 +1314,11 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1507
1314
|
renderProductInfo(infoResult, i, infoResults.length, formatPrice);
|
|
1508
1315
|
}
|
|
1509
1316
|
} else {
|
|
1510
|
-
console.log(
|
|
1317
|
+
console.log(chalk5.yellow("\n No additional information returned from the stores."));
|
|
1511
1318
|
}
|
|
1512
1319
|
} catch (infoErr) {
|
|
1513
1320
|
infoSpinner.stop();
|
|
1514
|
-
console.error(
|
|
1321
|
+
console.error(chalk5.red("\n Failed to fetch product info from stores."));
|
|
1515
1322
|
}
|
|
1516
1323
|
}
|
|
1517
1324
|
if (localIds.length > 0) {
|
|
@@ -1523,31 +1330,31 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1523
1330
|
const p = detailRes.data.product;
|
|
1524
1331
|
if (p) {
|
|
1525
1332
|
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(` ${
|
|
1333
|
+
console.log(` ${chalk5.bold.cyan(p.name)}`);
|
|
1334
|
+
console.log(` ${chalk5.dim(`ID: ${p.id}`)}`);
|
|
1335
|
+
if (p.brand) console.log(` ${chalk5.bold("Brand:")} ${p.brand}`);
|
|
1336
|
+
if (p.model) console.log(` ${chalk5.bold("Model:")} ${p.model}`);
|
|
1337
|
+
if (p.sku) console.log(` ${chalk5.bold("SKU:")} ${p.sku}`);
|
|
1338
|
+
console.log(` ${chalk5.bold("Price:")} ${chalk5.bold.white(formatPrice(p.priceInCents, p.currency))}`);
|
|
1339
|
+
const status = p.inStock ? chalk5.green("In Stock") : chalk5.red("Out of Stock");
|
|
1340
|
+
console.log(` ${chalk5.bold("Availability:")} ${status}${p.stockQuantity != null ? chalk5.dim(` (${p.stockQuantity} available)`) : ""}`);
|
|
1341
|
+
if (p.freeShipping) console.log(` ${chalk5.bold("Shipping:")} ${chalk5.green("Free")}`);
|
|
1342
|
+
else if (p.shippingPriceInCents != null) console.log(` ${chalk5.bold("Shipping:")} ${formatPrice(p.shippingPriceInCents, p.currency)}`);
|
|
1343
|
+
if (p.shippingDays != null) console.log(` ${chalk5.bold("Delivery:")} ${deliveryLabel(p.shippingDays)}`);
|
|
1344
|
+
if (p.freeReturns) console.log(` ${chalk5.bold("Returns:")} ${chalk5.green("Free Returns")}${p.returnWindowDays ? ` \xB7 ${p.returnWindowDays}-day window` : ""}`);
|
|
1538
1345
|
if (p.description) {
|
|
1539
|
-
console.log(` ${
|
|
1346
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
1540
1347
|
const words = p.description.split(/\s+/);
|
|
1541
1348
|
let line = "";
|
|
1542
1349
|
for (const word of words) {
|
|
1543
1350
|
if (line.length + word.length + 1 > 76) {
|
|
1544
|
-
console.log(` ${
|
|
1351
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1545
1352
|
line = word;
|
|
1546
1353
|
} else {
|
|
1547
1354
|
line = line ? `${line} ${word}` : word;
|
|
1548
1355
|
}
|
|
1549
1356
|
}
|
|
1550
|
-
if (line) console.log(` ${
|
|
1357
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1551
1358
|
}
|
|
1552
1359
|
console.log();
|
|
1553
1360
|
}
|
|
@@ -1562,26 +1369,26 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1562
1369
|
} else if (hasExtendedProducts && !opts.interactive) {
|
|
1563
1370
|
const sampleIds = extended.products.slice(0, 2).map((ep) => ep.id).join(" ");
|
|
1564
1371
|
console.log(
|
|
1565
|
-
|
|
1566
|
-
Run: `) +
|
|
1372
|
+
chalk5.dim(" \u2139\uFE0F Tip: ") + chalk5.white("Want more details? Request info from the store:") + chalk5.dim(`
|
|
1373
|
+
Run: `) + chalk5.cyan(`clishop info ${sampleIds}`) + chalk5.dim(` or use `) + chalk5.cyan(`--interactive`) + chalk5.dim(` to select interactively`) + chalk5.dim("\n")
|
|
1567
1374
|
);
|
|
1568
1375
|
}
|
|
1569
1376
|
const totalPages = Math.ceil(result.total / result.pageSize);
|
|
1570
1377
|
if (totalPages > 1) {
|
|
1571
|
-
console.log(
|
|
1378
|
+
console.log(chalk5.dim(` Page ${result.page} of ${totalPages}. Use --page to navigate.
|
|
1572
1379
|
`));
|
|
1573
1380
|
}
|
|
1574
1381
|
const showAdvertiseTip = result.page >= totalPages || suggestAdvertise;
|
|
1575
1382
|
if (showAdvertiseTip) {
|
|
1576
1383
|
if (!didExtendedSearch && disableExtended && result.products.length > 0) {
|
|
1577
1384
|
console.log(
|
|
1578
|
-
|
|
1579
|
-
Run: `) +
|
|
1385
|
+
chalk5.dim(" \u{1F50D} Tip: ") + chalk5.white("Want more results? Extended search was disabled. Enable it:") + chalk5.dim(`
|
|
1386
|
+
Run: `) + chalk5.cyan(`clishop search "${query}" --extended-search`) + chalk5.dim("\n")
|
|
1580
1387
|
);
|
|
1581
1388
|
}
|
|
1582
1389
|
console.log(
|
|
1583
|
-
|
|
1584
|
-
Run: `) +
|
|
1390
|
+
chalk5.dim(" \u{1F4A1} Tip: ") + chalk5.white("Didn't find the right match? Advertise your request for vendors to bid on.") + chalk5.dim(`
|
|
1391
|
+
Run: `) + chalk5.cyan(`clishop advertise create`) + chalk5.dim(` or `) + chalk5.cyan(`clishop advertise quick "${query}"`) + chalk5.dim("\n")
|
|
1585
1392
|
);
|
|
1586
1393
|
}
|
|
1587
1394
|
} catch (error) {
|
|
@@ -1599,38 +1406,38 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1599
1406
|
console.log(JSON.stringify(p, null, 2));
|
|
1600
1407
|
return;
|
|
1601
1408
|
}
|
|
1602
|
-
const storeBadge = p.storeVerified ?
|
|
1409
|
+
const storeBadge = p.storeVerified ? chalk5.green(" \u2713 Verified") : "";
|
|
1603
1410
|
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(
|
|
1411
|
+
console.log(chalk5.bold.cyan(` ${p.name}`));
|
|
1412
|
+
console.log(chalk5.dim(` ID: ${p.id}`));
|
|
1413
|
+
if (p.brand) console.log(chalk5.dim(` Brand: ${p.brand}`));
|
|
1414
|
+
if (p.model) console.log(chalk5.dim(` Model: ${p.model}`));
|
|
1415
|
+
if (p.variant) console.log(chalk5.dim(` Variant: ${p.variant}`));
|
|
1416
|
+
if (p.sku) console.log(chalk5.dim(` SKU: ${p.sku}`));
|
|
1417
|
+
if (p.gtin) console.log(chalk5.dim(` GTIN: ${p.gtin}`));
|
|
1611
1418
|
console.log();
|
|
1612
|
-
console.log(` Price: ${
|
|
1419
|
+
console.log(` Price: ${chalk5.bold(formatPrice(p.priceInCents, p.currency))}`);
|
|
1613
1420
|
if (p.freeShipping) {
|
|
1614
|
-
console.log(` Shipping: ${
|
|
1421
|
+
console.log(` Shipping: ${chalk5.green("Free")}`);
|
|
1615
1422
|
} else if (p.shippingPriceInCents != null) {
|
|
1616
1423
|
console.log(` Shipping: ${formatPrice(p.shippingPriceInCents, p.currency)}`);
|
|
1617
1424
|
}
|
|
1618
1425
|
if (p.shippingPriceInCents != null || p.freeShipping) {
|
|
1619
1426
|
const total = p.priceInCents + (p.freeShipping ? 0 : p.shippingPriceInCents ?? 0);
|
|
1620
|
-
console.log(` Total: ${
|
|
1427
|
+
console.log(` Total: ${chalk5.bold(formatPrice(total, p.currency))}`);
|
|
1621
1428
|
}
|
|
1622
|
-
const status = p.inStock ?
|
|
1623
|
-
console.log(` Status: ${status}${p.stockQuantity != null ?
|
|
1429
|
+
const status = p.inStock ? chalk5.green("In Stock") : p.backorder ? chalk5.yellow("Backorder") : chalk5.red("Out of Stock");
|
|
1430
|
+
console.log(` Status: ${status}${p.stockQuantity != null ? chalk5.dim(` (${p.stockQuantity} available)`) : ""}`);
|
|
1624
1431
|
if (p.shippingDays != null) console.log(` Delivery: ${deliveryLabel(p.shippingDays)}`);
|
|
1625
|
-
console.log(` Rating: ${
|
|
1432
|
+
console.log(` Rating: ${chalk5.yellow(renderStars(p.rating))} ${chalk5.dim(`(${p.reviewCount} reviews)`)}`);
|
|
1626
1433
|
console.log(` Category: ${p.category}`);
|
|
1627
|
-
console.log(` Store: ${p.vendor}${storeBadge}${p.storeRating != null ?
|
|
1434
|
+
console.log(` Store: ${p.vendor}${storeBadge}${p.storeRating != null ? chalk5.dim(` (${p.storeRating.toFixed(1)} store rating)`) : ""}`);
|
|
1628
1435
|
const returnParts = [];
|
|
1629
|
-
if (p.freeReturns) returnParts.push(
|
|
1436
|
+
if (p.freeReturns) returnParts.push(chalk5.green("Free Returns"));
|
|
1630
1437
|
if (p.returnWindowDays) returnParts.push(`${p.returnWindowDays}-day return window`);
|
|
1631
1438
|
if (returnParts.length) console.log(` Returns: ${returnParts.join(" \xB7 ")}`);
|
|
1632
1439
|
if (p.checkoutMode && p.checkoutMode !== "instant") {
|
|
1633
|
-
console.log(` Checkout: ${
|
|
1440
|
+
console.log(` Checkout: ${chalk5.yellow(p.checkoutMode)}`);
|
|
1634
1441
|
}
|
|
1635
1442
|
console.log();
|
|
1636
1443
|
console.log(` ${p.description}`);
|
|
@@ -1642,13 +1449,13 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1642
1449
|
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
1450
|
try {
|
|
1644
1451
|
if (ids.length === 0) {
|
|
1645
|
-
console.error(
|
|
1646
|
-
console.log(
|
|
1647
|
-
console.log(
|
|
1452
|
+
console.error(chalk5.red("\n\u2717 Please provide one or more product IDs."));
|
|
1453
|
+
console.log(chalk5.dim(" Usage: clishop info <product-id> [product-id...]"));
|
|
1454
|
+
console.log(chalk5.dim(" Example: clishop info ep_abc123 ep_def456\n"));
|
|
1648
1455
|
process.exit(1);
|
|
1649
1456
|
}
|
|
1650
1457
|
if (ids.length > 20) {
|
|
1651
|
-
console.error(
|
|
1458
|
+
console.error(chalk5.red("\n\u2717 Maximum 20 products per request."));
|
|
1652
1459
|
process.exit(1);
|
|
1653
1460
|
}
|
|
1654
1461
|
const spinner = ora4(`Requesting detailed info for ${ids.length} product(s)...`).start();
|
|
@@ -1665,12 +1472,12 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1665
1472
|
return;
|
|
1666
1473
|
}
|
|
1667
1474
|
if (!results || results.length === 0) {
|
|
1668
|
-
console.log(
|
|
1669
|
-
console.log(
|
|
1670
|
-
console.log(
|
|
1475
|
+
console.log(chalk5.yellow("\nNo information returned for the requested products."));
|
|
1476
|
+
console.log(chalk5.dim(" Make sure you're using product IDs from extended search results (ep_...)."));
|
|
1477
|
+
console.log(chalk5.dim(" Product IDs are shown after each search result.\n"));
|
|
1671
1478
|
return;
|
|
1672
1479
|
}
|
|
1673
|
-
console.log(
|
|
1480
|
+
console.log(chalk5.bold(`
|
|
1674
1481
|
Product Information \u2014 ${total} result(s)
|
|
1675
1482
|
`));
|
|
1676
1483
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -1683,19 +1490,19 @@ Product Information \u2014 ${total} result(s)
|
|
|
1683
1490
|
}
|
|
1684
1491
|
|
|
1685
1492
|
// src/commands/order.ts
|
|
1686
|
-
import
|
|
1493
|
+
import chalk6 from "chalk";
|
|
1687
1494
|
import ora5 from "ora";
|
|
1688
1495
|
import inquirer5 from "inquirer";
|
|
1689
1496
|
function formatPrice2(cents, currency) {
|
|
1690
1497
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
1691
1498
|
}
|
|
1692
1499
|
var STATUS_COLORS = {
|
|
1693
|
-
pending:
|
|
1694
|
-
confirmed:
|
|
1695
|
-
processing:
|
|
1696
|
-
shipped:
|
|
1697
|
-
delivered:
|
|
1698
|
-
cancelled:
|
|
1500
|
+
pending: chalk6.yellow,
|
|
1501
|
+
confirmed: chalk6.blue,
|
|
1502
|
+
processing: chalk6.cyan,
|
|
1503
|
+
shipped: chalk6.magenta,
|
|
1504
|
+
delivered: chalk6.green,
|
|
1505
|
+
cancelled: chalk6.red
|
|
1699
1506
|
};
|
|
1700
1507
|
function registerOrderCommands(program2) {
|
|
1701
1508
|
const order = program2.command("order").description("Place and manage orders");
|
|
@@ -1705,12 +1512,12 @@ function registerOrderCommands(program2) {
|
|
|
1705
1512
|
const addressId = opts.address || agent.defaultAddressId;
|
|
1706
1513
|
const paymentId = opts.payment || agent.defaultPaymentMethodId;
|
|
1707
1514
|
if (!addressId) {
|
|
1708
|
-
console.error(
|
|
1515
|
+
console.error(chalk6.red("\n\u2717 No address set. Add one with: clishop address add"));
|
|
1709
1516
|
process.exitCode = 1;
|
|
1710
1517
|
return;
|
|
1711
1518
|
}
|
|
1712
1519
|
if (!paymentId) {
|
|
1713
|
-
console.error(
|
|
1520
|
+
console.error(chalk6.red("\n\u2717 No payment method set. Add one with: clishop payment add"));
|
|
1714
1521
|
process.exitCode = 1;
|
|
1715
1522
|
return;
|
|
1716
1523
|
}
|
|
@@ -1729,7 +1536,7 @@ function registerOrderCommands(program2) {
|
|
|
1729
1536
|
isExtended = true;
|
|
1730
1537
|
} catch {
|
|
1731
1538
|
prodSpinner.stop();
|
|
1732
|
-
console.error(
|
|
1539
|
+
console.error(chalk6.red(`
|
|
1733
1540
|
\u2717 Product ${productId} not found.`));
|
|
1734
1541
|
process.exitCode = 1;
|
|
1735
1542
|
return;
|
|
@@ -1743,7 +1550,7 @@ function registerOrderCommands(program2) {
|
|
|
1743
1550
|
const totalCents = product.priceInCents * opts.quantity;
|
|
1744
1551
|
if (agent.maxOrderAmount && totalCents / 100 > agent.maxOrderAmount) {
|
|
1745
1552
|
console.error(
|
|
1746
|
-
|
|
1553
|
+
chalk6.red(
|
|
1747
1554
|
`
|
|
1748
1555
|
\u2717 Order total (${formatPrice2(totalCents, product.currency)}) exceeds agent "${agent.name}" limit of $${agent.maxOrderAmount}.`
|
|
1749
1556
|
)
|
|
@@ -1752,23 +1559,23 @@ function registerOrderCommands(program2) {
|
|
|
1752
1559
|
return;
|
|
1753
1560
|
}
|
|
1754
1561
|
if (agent.blockedCategories?.includes(product.category)) {
|
|
1755
|
-
console.error(
|
|
1562
|
+
console.error(chalk6.red(`
|
|
1756
1563
|
\u2717 Category "${product.category}" is blocked for agent "${agent.name}".`));
|
|
1757
1564
|
process.exitCode = 1;
|
|
1758
1565
|
return;
|
|
1759
1566
|
}
|
|
1760
1567
|
if (agent.allowedCategories?.length && !agent.allowedCategories.includes(product.category)) {
|
|
1761
|
-
console.error(
|
|
1568
|
+
console.error(chalk6.red(`
|
|
1762
1569
|
\u2717 Category "${product.category}" is not in the allowed list for agent "${agent.name}".`));
|
|
1763
1570
|
process.exitCode = 1;
|
|
1764
1571
|
return;
|
|
1765
1572
|
}
|
|
1766
1573
|
if (agent.requireConfirmation && !opts.yes) {
|
|
1767
|
-
console.log(
|
|
1574
|
+
console.log(chalk6.bold("\n Order Summary:"));
|
|
1768
1575
|
console.log(` Product: ${product.name}`);
|
|
1769
1576
|
console.log(` Store: ${product.vendor || product.storeName || "\u2014"}`);
|
|
1770
1577
|
console.log(` Quantity: ${opts.quantity}`);
|
|
1771
|
-
console.log(` Total: ${
|
|
1578
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(totalCents, product.currency))}`);
|
|
1772
1579
|
console.log(` Agent: ${agent.name}`);
|
|
1773
1580
|
console.log();
|
|
1774
1581
|
const { confirm } = await inquirer5.prompt([
|
|
@@ -1780,7 +1587,7 @@ function registerOrderCommands(program2) {
|
|
|
1780
1587
|
}
|
|
1781
1588
|
]);
|
|
1782
1589
|
if (!confirm) {
|
|
1783
|
-
console.log(
|
|
1590
|
+
console.log(chalk6.yellow("Order cancelled."));
|
|
1784
1591
|
return;
|
|
1785
1592
|
}
|
|
1786
1593
|
}
|
|
@@ -1791,7 +1598,7 @@ function registerOrderCommands(program2) {
|
|
|
1791
1598
|
shippingAddressId: addressId,
|
|
1792
1599
|
paymentMethodId: paymentId
|
|
1793
1600
|
});
|
|
1794
|
-
spinner.succeed(
|
|
1601
|
+
spinner.succeed(chalk6.green(`Order placed! Order ID: ${chalk6.bold(res.data.order.id)}`));
|
|
1795
1602
|
} catch (error) {
|
|
1796
1603
|
handleApiError(error);
|
|
1797
1604
|
}
|
|
@@ -1813,18 +1620,18 @@ function registerOrderCommands(program2) {
|
|
|
1813
1620
|
return;
|
|
1814
1621
|
}
|
|
1815
1622
|
if (orders.length === 0) {
|
|
1816
|
-
console.log(
|
|
1623
|
+
console.log(chalk6.yellow("\nNo orders found.\n"));
|
|
1817
1624
|
return;
|
|
1818
1625
|
}
|
|
1819
|
-
console.log(
|
|
1626
|
+
console.log(chalk6.bold("\nYour Orders:\n"));
|
|
1820
1627
|
for (const o of orders) {
|
|
1821
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1628
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1822
1629
|
const date = new Date(o.createdAt).toLocaleDateString();
|
|
1823
1630
|
console.log(
|
|
1824
|
-
` ${
|
|
1631
|
+
` ${chalk6.bold(o.id)} ${statusColor(o.status.toUpperCase().padEnd(12))} ${formatPrice2(o.totalAmountInCents, o.currency)} ${chalk6.dim(date)} ${chalk6.dim(`agent: ${o.agent}`)}`
|
|
1825
1632
|
);
|
|
1826
1633
|
for (const item of o.items) {
|
|
1827
|
-
console.log(
|
|
1634
|
+
console.log(chalk6.dim(` \xB7 ${item.productName} \xD7 ${item.quantity}`));
|
|
1828
1635
|
}
|
|
1829
1636
|
console.log();
|
|
1830
1637
|
}
|
|
@@ -1843,26 +1650,26 @@ function registerOrderCommands(program2) {
|
|
|
1843
1650
|
console.log(JSON.stringify(o, null, 2));
|
|
1844
1651
|
return;
|
|
1845
1652
|
}
|
|
1846
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1653
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1847
1654
|
console.log();
|
|
1848
|
-
console.log(
|
|
1655
|
+
console.log(chalk6.bold(` Order ${o.id}`));
|
|
1849
1656
|
console.log(` Status: ${statusColor(o.status.toUpperCase())}`);
|
|
1850
|
-
console.log(` Total: ${
|
|
1657
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(o.totalAmountInCents, o.currency))}`);
|
|
1851
1658
|
if (o.storeName) console.log(` Store: ${o.storeName}`);
|
|
1852
1659
|
console.log(` Agent: ${o.agent}`);
|
|
1853
1660
|
if (o.paymentLabel) console.log(` Payment: ${o.paymentLabel}`);
|
|
1854
1661
|
console.log(` Placed: ${new Date(o.createdAt).toLocaleString()}`);
|
|
1855
1662
|
console.log(` Updated: ${new Date(o.updatedAt).toLocaleString()}`);
|
|
1856
1663
|
if (o.externalOrderId) {
|
|
1857
|
-
console.log(` eBay Ref: ${
|
|
1664
|
+
console.log(` eBay Ref: ${chalk6.dim(o.externalOrderId)}`);
|
|
1858
1665
|
}
|
|
1859
1666
|
if (o.shipments?.length) {
|
|
1860
1667
|
for (const s of o.shipments) {
|
|
1861
1668
|
console.log(` Tracking: ${s.trackingNumber || "pending"} ${s.carrier ? `(${s.carrier})` : ""}`);
|
|
1862
|
-
if (s.trackingUrl) console.log(` Track: ${
|
|
1669
|
+
if (s.trackingUrl) console.log(` Track: ${chalk6.cyan.underline(s.trackingUrl)}`);
|
|
1863
1670
|
}
|
|
1864
1671
|
}
|
|
1865
|
-
console.log(
|
|
1672
|
+
console.log(chalk6.bold("\n Items:"));
|
|
1866
1673
|
for (const item of o.items) {
|
|
1867
1674
|
console.log(` \xB7 ${item.productName} \xD7 ${item.quantity} ${formatPrice2(item.totalPriceInCents, o.currency)}`);
|
|
1868
1675
|
}
|
|
@@ -1880,14 +1687,14 @@ function registerOrderCommands(program2) {
|
|
|
1880
1687
|
FULFILLED: "Fulfilled",
|
|
1881
1688
|
CANCELLED: "Cancelled"
|
|
1882
1689
|
};
|
|
1883
|
-
console.log(
|
|
1884
|
-
console.log(` ${
|
|
1690
|
+
console.log(chalk6.bold("\n eBay Status:"));
|
|
1691
|
+
console.log(` ${chalk6.cyan(STATUS_MAP[ebayStatus] || ebayStatus)}`);
|
|
1885
1692
|
for (const li of tracking.line_items || []) {
|
|
1886
1693
|
if (li.tracking_number) {
|
|
1887
|
-
console.log(` Tracking: ${
|
|
1694
|
+
console.log(` Tracking: ${chalk6.bold(li.tracking_number)}${li.carrier ? ` (${li.carrier})` : ""}`);
|
|
1888
1695
|
}
|
|
1889
1696
|
if (li.tracking_url) {
|
|
1890
|
-
console.log(` Track: ${
|
|
1697
|
+
console.log(` Track: ${chalk6.cyan.underline(li.tracking_url)}`);
|
|
1891
1698
|
}
|
|
1892
1699
|
if (li.estimated_delivery) {
|
|
1893
1700
|
const eta = new Date(li.estimated_delivery).toLocaleDateString();
|
|
@@ -1895,7 +1702,7 @@ function registerOrderCommands(program2) {
|
|
|
1895
1702
|
}
|
|
1896
1703
|
}
|
|
1897
1704
|
} else if (trackRes.data.message) {
|
|
1898
|
-
console.log(
|
|
1705
|
+
console.log(chalk6.dim(`
|
|
1899
1706
|
Vendor: ${trackRes.data.message}`));
|
|
1900
1707
|
}
|
|
1901
1708
|
} catch {
|
|
@@ -1920,7 +1727,7 @@ function registerOrderCommands(program2) {
|
|
|
1920
1727
|
const spinner = ora5("Cancelling order...").start();
|
|
1921
1728
|
const api = getApiClient();
|
|
1922
1729
|
await api.post(`/orders/${id}/cancel`);
|
|
1923
|
-
spinner.succeed(
|
|
1730
|
+
spinner.succeed(chalk6.green("Order cancelled."));
|
|
1924
1731
|
} catch (error) {
|
|
1925
1732
|
handleApiError(error);
|
|
1926
1733
|
}
|
|
@@ -1928,17 +1735,17 @@ function registerOrderCommands(program2) {
|
|
|
1928
1735
|
}
|
|
1929
1736
|
|
|
1930
1737
|
// src/commands/review.ts
|
|
1931
|
-
import
|
|
1738
|
+
import chalk7 from "chalk";
|
|
1932
1739
|
import ora6 from "ora";
|
|
1933
1740
|
import inquirer6 from "inquirer";
|
|
1934
1741
|
function renderStars2(rating) {
|
|
1935
1742
|
const filled = Math.round(rating);
|
|
1936
1743
|
const empty = 10 - filled;
|
|
1937
|
-
return
|
|
1744
|
+
return chalk7.yellow("\u2605".repeat(filled) + "\u2606".repeat(empty));
|
|
1938
1745
|
}
|
|
1939
1746
|
function renderRating(rating) {
|
|
1940
|
-
if (rating === 0) return
|
|
1941
|
-
const color = rating >= 8 ?
|
|
1747
|
+
if (rating === 0) return chalk7.dim("No ratings yet");
|
|
1748
|
+
const color = rating >= 8 ? chalk7.green : rating >= 5 ? chalk7.yellow : chalk7.red;
|
|
1942
1749
|
return `${renderStars2(rating)} ${color(`${rating.toFixed(1)}/10`)}`;
|
|
1943
1750
|
}
|
|
1944
1751
|
var RATING_CHOICES = [
|
|
@@ -1972,17 +1779,17 @@ function registerReviewCommands(program2) {
|
|
|
1972
1779
|
const unreviewedItems = reviewable.items.filter((i) => !i.alreadyReviewed);
|
|
1973
1780
|
const storeAlreadyReviewed = reviewable.store.alreadyReviewed;
|
|
1974
1781
|
if (unreviewedItems.length === 0 && storeAlreadyReviewed) {
|
|
1975
|
-
console.log(
|
|
1782
|
+
console.log(chalk7.yellow("\nYou've already reviewed everything in this order.\n"));
|
|
1976
1783
|
return;
|
|
1977
1784
|
}
|
|
1978
|
-
console.log(
|
|
1979
|
-
Review Order ${
|
|
1980
|
-
console.log(
|
|
1785
|
+
console.log(chalk7.bold(`
|
|
1786
|
+
Review Order ${chalk7.cyan(orderId)}`));
|
|
1787
|
+
console.log(chalk7.dim(` Store: ${reviewable.store.name}
|
|
1981
1788
|
`));
|
|
1982
1789
|
const itemReviews = [];
|
|
1983
1790
|
let storeReview = void 0;
|
|
1984
1791
|
for (const item of unreviewedItems) {
|
|
1985
|
-
console.log(
|
|
1792
|
+
console.log(chalk7.bold(` Product: ${item.productName}`));
|
|
1986
1793
|
const { wantReview } = await inquirer6.prompt([
|
|
1987
1794
|
{
|
|
1988
1795
|
type: "confirm",
|
|
@@ -2018,11 +1825,11 @@ function registerReviewCommands(program2) {
|
|
|
2018
1825
|
title: answers.title.trim(),
|
|
2019
1826
|
body: answers.body.trim()
|
|
2020
1827
|
});
|
|
2021
|
-
console.log(
|
|
1828
|
+
console.log(chalk7.dim(` \u2713 ${item.productName}: ${answers.rating}/10
|
|
2022
1829
|
`));
|
|
2023
1830
|
}
|
|
2024
1831
|
if (!storeAlreadyReviewed) {
|
|
2025
|
-
console.log(
|
|
1832
|
+
console.log(chalk7.bold(` Store: ${reviewable.store.name}`));
|
|
2026
1833
|
const { wantStoreReview } = await inquirer6.prompt([
|
|
2027
1834
|
{
|
|
2028
1835
|
type: "confirm",
|
|
@@ -2060,7 +1867,7 @@ function registerReviewCommands(program2) {
|
|
|
2060
1867
|
}
|
|
2061
1868
|
}
|
|
2062
1869
|
if (itemReviews.length === 0 && !storeReview) {
|
|
2063
|
-
console.log(
|
|
1870
|
+
console.log(chalk7.yellow("\nNo reviews submitted.\n"));
|
|
2064
1871
|
return;
|
|
2065
1872
|
}
|
|
2066
1873
|
const submitSpinner = ora6("Submitting reviews...").start();
|
|
@@ -2069,17 +1876,17 @@ function registerReviewCommands(program2) {
|
|
|
2069
1876
|
itemReviews,
|
|
2070
1877
|
storeReview
|
|
2071
1878
|
});
|
|
2072
|
-
submitSpinner.succeed(
|
|
1879
|
+
submitSpinner.succeed(chalk7.green("Reviews submitted!"));
|
|
2073
1880
|
const data = res.data;
|
|
2074
1881
|
if (data.productReviews?.length > 0) {
|
|
2075
|
-
console.log(
|
|
1882
|
+
console.log(chalk7.bold("\n Product Reviews:"));
|
|
2076
1883
|
for (const r of data.productReviews) {
|
|
2077
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
1884
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
2078
1885
|
}
|
|
2079
1886
|
}
|
|
2080
1887
|
if (data.storeReview && !data.storeReview.skipped) {
|
|
2081
|
-
console.log(
|
|
2082
|
-
console.log(` ${renderStars2(data.storeReview.rating)} ${
|
|
1888
|
+
console.log(chalk7.bold("\n Store Review:"));
|
|
1889
|
+
console.log(` ${renderStars2(data.storeReview.rating)} ${chalk7.bold(data.storeReview.title)}`);
|
|
2083
1890
|
}
|
|
2084
1891
|
console.log();
|
|
2085
1892
|
} catch (err) {
|
|
@@ -2114,10 +1921,10 @@ function registerReviewCommands(program2) {
|
|
|
2114
1921
|
body: answers.body.trim(),
|
|
2115
1922
|
orderId: opts.order || void 0
|
|
2116
1923
|
});
|
|
2117
|
-
spinner.succeed(
|
|
1924
|
+
spinner.succeed(chalk7.green("Review submitted!"));
|
|
2118
1925
|
console.log(`
|
|
2119
|
-
${renderRating(answers.rating)} ${
|
|
2120
|
-
console.log(
|
|
1926
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
1927
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2121
1928
|
`));
|
|
2122
1929
|
} catch (error) {
|
|
2123
1930
|
handleApiError(error);
|
|
@@ -2147,10 +1954,10 @@ function registerReviewCommands(program2) {
|
|
|
2147
1954
|
body: answers.body.trim(),
|
|
2148
1955
|
orderId: opts.order || void 0
|
|
2149
1956
|
});
|
|
2150
|
-
spinner.succeed(
|
|
1957
|
+
spinner.succeed(chalk7.green("Store review submitted!"));
|
|
2151
1958
|
console.log(`
|
|
2152
|
-
${renderRating(answers.rating)} ${
|
|
2153
|
-
console.log(
|
|
1959
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
1960
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2154
1961
|
`));
|
|
2155
1962
|
} catch (error) {
|
|
2156
1963
|
handleApiError(error);
|
|
@@ -2168,25 +1975,25 @@ function registerReviewCommands(program2) {
|
|
|
2168
1975
|
return;
|
|
2169
1976
|
}
|
|
2170
1977
|
if (productReviews.length === 0 && storeReviews.length === 0) {
|
|
2171
|
-
console.log(
|
|
1978
|
+
console.log(chalk7.yellow("\nYou haven't written any reviews yet.\n"));
|
|
2172
1979
|
return;
|
|
2173
1980
|
}
|
|
2174
1981
|
if (productReviews.length > 0) {
|
|
2175
|
-
console.log(
|
|
1982
|
+
console.log(chalk7.bold("\nProduct Reviews:\n"));
|
|
2176
1983
|
for (const r of productReviews) {
|
|
2177
1984
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2178
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2179
|
-
console.log(` ${
|
|
1985
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
1986
|
+
console.log(` ${chalk7.dim(`on ${r.productName}`)} ${chalk7.dim(date)}`);
|
|
2180
1987
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2181
1988
|
console.log();
|
|
2182
1989
|
}
|
|
2183
1990
|
}
|
|
2184
1991
|
if (storeReviews.length > 0) {
|
|
2185
|
-
console.log(
|
|
1992
|
+
console.log(chalk7.bold("Store Reviews:\n"));
|
|
2186
1993
|
for (const r of storeReviews) {
|
|
2187
1994
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2188
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2189
|
-
console.log(` ${
|
|
1995
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
1996
|
+
console.log(` ${chalk7.dim(`store: ${r.storeName}`)} ${chalk7.dim(date)}`);
|
|
2190
1997
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2191
1998
|
console.log();
|
|
2192
1999
|
}
|
|
@@ -2205,7 +2012,7 @@ function registerReviewCommands(program2) {
|
|
|
2205
2012
|
const { rating } = res.data;
|
|
2206
2013
|
const entity = opts.store ? res.data.store : res.data.product;
|
|
2207
2014
|
console.log();
|
|
2208
|
-
console.log(
|
|
2015
|
+
console.log(chalk7.bold(` ${entity.name}`));
|
|
2209
2016
|
console.log(` ${renderRating(rating.displayRating)}`);
|
|
2210
2017
|
console.log();
|
|
2211
2018
|
console.log(` Reviews: ${rating.reviewCount}`);
|
|
@@ -2213,11 +2020,11 @@ function registerReviewCommands(program2) {
|
|
|
2213
2020
|
console.log(` Bayesian Avg: ${rating.bayesianAverage.toFixed(2)}`);
|
|
2214
2021
|
console.log(` Effective Cap: ${rating.effectiveCeiling.toFixed(1)}`);
|
|
2215
2022
|
if (rating.isCapped) {
|
|
2216
|
-
console.log(
|
|
2023
|
+
console.log(chalk7.yellow(` \u26A0 Rating is capped \u2014 needs more orders to unlock higher rating`));
|
|
2217
2024
|
if (rating.totalOrders < 100) {
|
|
2218
|
-
console.log(
|
|
2025
|
+
console.log(chalk7.dim(` ${100 - rating.totalOrders} more orders needed to unlock 8.0+ rating`));
|
|
2219
2026
|
} else if (rating.totalOrders < 1e3) {
|
|
2220
|
-
console.log(
|
|
2027
|
+
console.log(chalk7.dim(` ${1e3 - rating.totalOrders} more orders needed to unlock 9.0+ rating`));
|
|
2221
2028
|
}
|
|
2222
2029
|
}
|
|
2223
2030
|
console.log();
|
|
@@ -2240,7 +2047,7 @@ function registerReviewCommands(program2) {
|
|
|
2240
2047
|
const api = getApiClient();
|
|
2241
2048
|
const endpoint = opts.store ? `/store-reviews/${reviewId}` : `/reviews/${reviewId}`;
|
|
2242
2049
|
await api.delete(endpoint);
|
|
2243
|
-
spinner.succeed(
|
|
2050
|
+
spinner.succeed(chalk7.green("Review deleted."));
|
|
2244
2051
|
} catch (error) {
|
|
2245
2052
|
handleApiError(error);
|
|
2246
2053
|
}
|
|
@@ -2248,41 +2055,41 @@ function registerReviewCommands(program2) {
|
|
|
2248
2055
|
}
|
|
2249
2056
|
|
|
2250
2057
|
// src/commands/config.ts
|
|
2251
|
-
import
|
|
2058
|
+
import chalk8 from "chalk";
|
|
2252
2059
|
function registerConfigCommands(program2) {
|
|
2253
|
-
const
|
|
2254
|
-
|
|
2060
|
+
const config = program2.command("config").description("View and manage CLI configuration");
|
|
2061
|
+
config.command("show").description("Show current configuration").action(() => {
|
|
2255
2062
|
const cfg = getConfig();
|
|
2256
|
-
console.log(
|
|
2257
|
-
console.log(` Active agent: ${
|
|
2258
|
-
console.log(` Output format: ${
|
|
2259
|
-
console.log(` Config path: ${
|
|
2063
|
+
console.log(chalk8.bold("\nCLISHOP Configuration:\n"));
|
|
2064
|
+
console.log(` Active agent: ${chalk8.cyan(cfg.get("activeAgent"))}`);
|
|
2065
|
+
console.log(` Output format: ${chalk8.cyan(cfg.get("outputFormat"))}`);
|
|
2066
|
+
console.log(` Config path: ${chalk8.dim(cfg.path)}`);
|
|
2260
2067
|
console.log();
|
|
2261
2068
|
});
|
|
2262
|
-
|
|
2069
|
+
config.command("set-output <format>").description("Set output format: human or json").action((format) => {
|
|
2263
2070
|
if (format !== "human" && format !== "json") {
|
|
2264
|
-
console.error(
|
|
2071
|
+
console.error(chalk8.red('\u2717 Format must be "human" or "json".'));
|
|
2265
2072
|
process.exitCode = 1;
|
|
2266
2073
|
return;
|
|
2267
2074
|
}
|
|
2268
2075
|
const cfg = getConfig();
|
|
2269
2076
|
cfg.set("outputFormat", format);
|
|
2270
|
-
console.log(
|
|
2077
|
+
console.log(chalk8.green(`
|
|
2271
2078
|
\u2713 Output format set to "${format}".`));
|
|
2272
2079
|
});
|
|
2273
|
-
|
|
2080
|
+
config.command("reset").description("Reset all configuration to defaults").action(() => {
|
|
2274
2081
|
const cfg = getConfig();
|
|
2275
2082
|
cfg.clear();
|
|
2276
|
-
console.log(
|
|
2083
|
+
console.log(chalk8.green("\n\u2713 Configuration reset to defaults."));
|
|
2277
2084
|
});
|
|
2278
|
-
|
|
2085
|
+
config.command("path").description("Show the config file path").action(() => {
|
|
2279
2086
|
const cfg = getConfig();
|
|
2280
2087
|
console.log(cfg.path);
|
|
2281
2088
|
});
|
|
2282
2089
|
}
|
|
2283
2090
|
|
|
2284
2091
|
// src/commands/store.ts
|
|
2285
|
-
import
|
|
2092
|
+
import chalk9 from "chalk";
|
|
2286
2093
|
import ora7 from "ora";
|
|
2287
2094
|
function formatPrice3(cents, currency) {
|
|
2288
2095
|
return new Intl.NumberFormat("en-US", {
|
|
@@ -2327,28 +2134,28 @@ function registerStoreCommands(program2) {
|
|
|
2327
2134
|
return;
|
|
2328
2135
|
}
|
|
2329
2136
|
if (stores.length === 0) {
|
|
2330
|
-
console.log(
|
|
2137
|
+
console.log(chalk9.yellow("\nNo stores found.\n"));
|
|
2331
2138
|
return;
|
|
2332
2139
|
}
|
|
2333
|
-
console.log(
|
|
2140
|
+
console.log(chalk9.bold(`
|
|
2334
2141
|
Stores \u2014 ${total} found (page ${page})
|
|
2335
2142
|
`));
|
|
2336
2143
|
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(` ${
|
|
2144
|
+
const badge = s.verified ? chalk9.green(" \u2713") : "";
|
|
2145
|
+
const rating = s.rating != null ? chalk9.yellow(renderStars3(s.rating)) + chalk9.dim(` (${s.rating.toFixed(1)})`) : chalk9.dim("No rating");
|
|
2146
|
+
const products = s.productCount != null ? chalk9.dim(`${s.productCount} products`) : "";
|
|
2147
|
+
const country = s.country ? chalk9.dim(` ${s.country}`) : "";
|
|
2148
|
+
console.log(` ${chalk9.bold.cyan(s.name)}${badge} ${chalk9.dim(`(${s.slug})`)}`);
|
|
2342
2149
|
console.log(` ${rating} ${products}${country}`);
|
|
2343
2150
|
if (s.description) {
|
|
2344
2151
|
const desc = s.description.length > 100 ? s.description.slice(0, 100) + "..." : s.description;
|
|
2345
|
-
console.log(` ${
|
|
2152
|
+
console.log(` ${chalk9.dim(desc)}`);
|
|
2346
2153
|
}
|
|
2347
2154
|
console.log();
|
|
2348
2155
|
}
|
|
2349
2156
|
const totalPages = Math.ceil(total / pageSize);
|
|
2350
2157
|
if (totalPages > 1) {
|
|
2351
|
-
console.log(
|
|
2158
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2352
2159
|
`));
|
|
2353
2160
|
}
|
|
2354
2161
|
} catch (error) {
|
|
@@ -2366,24 +2173,24 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2366
2173
|
console.log(JSON.stringify(s, null, 2));
|
|
2367
2174
|
return;
|
|
2368
2175
|
}
|
|
2369
|
-
const badge = s.verified ?
|
|
2176
|
+
const badge = s.verified ? chalk9.green(" \u2713 Verified") : chalk9.dim(" Unverified");
|
|
2370
2177
|
console.log();
|
|
2371
|
-
console.log(
|
|
2372
|
-
console.log(
|
|
2178
|
+
console.log(chalk9.bold.cyan(` ${s.name}`) + badge);
|
|
2179
|
+
console.log(chalk9.dim(` ID: ${s.id} | Slug: ${s.slug}`));
|
|
2373
2180
|
console.log();
|
|
2374
2181
|
if (s.description) console.log(` ${s.description}`);
|
|
2375
2182
|
console.log();
|
|
2376
2183
|
if (s.rating != null) {
|
|
2377
|
-
console.log(` Rating: ${
|
|
2184
|
+
console.log(` Rating: ${chalk9.yellow(renderStars3(s.rating))} ${chalk9.dim(`(${s.rating.toFixed(1)})`)}`);
|
|
2378
2185
|
}
|
|
2379
2186
|
if (s.productCount != null) console.log(` Products: ${s.productCount}`);
|
|
2380
2187
|
if (s.country) console.log(` Country: ${s.country}`);
|
|
2381
2188
|
console.log(` Currency: ${s.currency}`);
|
|
2382
|
-
if (s.domain) console.log(` Website: ${
|
|
2189
|
+
if (s.domain) console.log(` Website: ${chalk9.cyan.underline(s.domain)}`);
|
|
2383
2190
|
if (s.contactEmail) console.log(` Contact: ${s.contactEmail}`);
|
|
2384
2191
|
console.log();
|
|
2385
|
-
console.log(
|
|
2386
|
-
console.log(
|
|
2192
|
+
console.log(chalk9.dim(` Browse catalog: clishop store catalog ${s.slug}`));
|
|
2193
|
+
console.log(chalk9.dim(` Search products: clishop search "<query>" --store ${s.slug}`));
|
|
2387
2194
|
console.log();
|
|
2388
2195
|
} catch (error) {
|
|
2389
2196
|
handleApiError(error);
|
|
@@ -2414,43 +2221,43 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2414
2221
|
console.log(JSON.stringify(res.data, null, 2));
|
|
2415
2222
|
return;
|
|
2416
2223
|
}
|
|
2417
|
-
const badge = storeInfo.verified ?
|
|
2418
|
-
const storeRating = storeInfo.rating != null ?
|
|
2224
|
+
const badge = storeInfo.verified ? chalk9.green(" \u2713") : "";
|
|
2225
|
+
const storeRating = storeInfo.rating != null ? chalk9.dim(` (${storeInfo.rating.toFixed(1)} \u2605)`) : "";
|
|
2419
2226
|
console.log(
|
|
2420
|
-
|
|
2227
|
+
chalk9.bold(`
|
|
2421
2228
|
${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
2422
2229
|
`)
|
|
2423
2230
|
);
|
|
2424
2231
|
if (products.length === 0) {
|
|
2425
|
-
console.log(
|
|
2232
|
+
console.log(chalk9.yellow(" No products found matching your filters.\n"));
|
|
2426
2233
|
return;
|
|
2427
2234
|
}
|
|
2428
2235
|
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(` ${
|
|
2236
|
+
const stock = p.inStock ? chalk9.green("In Stock") : p.backorder ? chalk9.yellow("Backorder") : chalk9.red("Out of Stock");
|
|
2237
|
+
const price = chalk9.bold.white(formatPrice3(p.priceInCents, p.currency));
|
|
2238
|
+
const stars = chalk9.yellow(renderStars3(p.rating));
|
|
2239
|
+
const shippingInfo = p.freeShipping ? chalk9.green("Free Shipping") : p.shippingPriceInCents != null ? chalk9.dim(`+${formatPrice3(p.shippingPriceInCents, p.currency)} shipping`) : "";
|
|
2240
|
+
const deliveryInfo = p.shippingDays != null ? chalk9.dim(`(${deliveryLabel2(p.shippingDays)})`) : "";
|
|
2241
|
+
console.log(` ${chalk9.bold.cyan(p.name)} ${chalk9.dim(`(${p.id})`)}`);
|
|
2435
2242
|
console.log(
|
|
2436
|
-
` ${price} ${stock} ${stars} ${
|
|
2243
|
+
` ${price} ${stock} ${stars} ${chalk9.dim(`(${p.reviewCount} reviews)`)}` + (shippingInfo ? ` ${shippingInfo}` : "") + (deliveryInfo ? ` ${deliveryInfo}` : "")
|
|
2437
2244
|
);
|
|
2438
2245
|
const meta = [];
|
|
2439
2246
|
if (p.category) meta.push(p.category);
|
|
2440
2247
|
if (p.brand) meta.push(p.brand);
|
|
2441
2248
|
if (p.variant) meta.push(p.variant);
|
|
2442
|
-
if (meta.length) console.log(` ${
|
|
2249
|
+
if (meta.length) console.log(` ${chalk9.dim(meta.join(" \xB7 "))}`);
|
|
2443
2250
|
const returnInfo = [];
|
|
2444
2251
|
if (p.freeReturns) returnInfo.push("Free Returns");
|
|
2445
2252
|
if (p.returnWindowDays) returnInfo.push(`${p.returnWindowDays}d return window`);
|
|
2446
|
-
if (returnInfo.length) console.log(` ${
|
|
2253
|
+
if (returnInfo.length) console.log(` ${chalk9.dim(returnInfo.join(" \xB7 "))}`);
|
|
2447
2254
|
const desc = p.description.length > 120 ? p.description.slice(0, 120) + "..." : p.description;
|
|
2448
2255
|
console.log(` ${desc}`);
|
|
2449
2256
|
console.log();
|
|
2450
2257
|
}
|
|
2451
2258
|
const totalPages = Math.ceil(total / pageSize);
|
|
2452
2259
|
if (totalPages > 1) {
|
|
2453
|
-
console.log(
|
|
2260
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2454
2261
|
`));
|
|
2455
2262
|
}
|
|
2456
2263
|
} catch (error) {
|
|
@@ -2460,13 +2267,13 @@ ${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
|
2460
2267
|
}
|
|
2461
2268
|
|
|
2462
2269
|
// src/commands/status.ts
|
|
2463
|
-
import
|
|
2270
|
+
import chalk10 from "chalk";
|
|
2464
2271
|
import ora8 from "ora";
|
|
2465
2272
|
function registerStatusCommand(program2) {
|
|
2466
2273
|
program2.command("status").description("Show full account overview \u2014 user, agents, addresses, payment methods").option("--json", "Output raw JSON").action(async (opts) => {
|
|
2467
2274
|
try {
|
|
2468
2275
|
if (!await isLoggedIn()) {
|
|
2469
|
-
console.log(
|
|
2276
|
+
console.log(chalk10.yellow("\nNot logged in. Run: clishop login\n"));
|
|
2470
2277
|
return;
|
|
2471
2278
|
}
|
|
2472
2279
|
const spinner = ora8("Fetching account overview...").start();
|
|
@@ -2500,59 +2307,59 @@ function registerStatusCommand(program2) {
|
|
|
2500
2307
|
}, null, 2));
|
|
2501
2308
|
return;
|
|
2502
2309
|
}
|
|
2503
|
-
console.log(
|
|
2504
|
-
console.log(
|
|
2505
|
-
console.log(
|
|
2506
|
-
console.log(
|
|
2507
|
-
console.log(` Name: ${
|
|
2508
|
-
console.log(` Email: ${
|
|
2509
|
-
console.log(` ID: ${
|
|
2310
|
+
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"));
|
|
2311
|
+
console.log(chalk10.bold.cyan(" CLISHOP Account Overview"));
|
|
2312
|
+
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"));
|
|
2313
|
+
console.log(chalk10.bold(" \u{1F464} User"));
|
|
2314
|
+
console.log(` Name: ${chalk10.white(userInfo?.name || "\u2014")}`);
|
|
2315
|
+
console.log(` Email: ${chalk10.white(userInfo?.email || "\u2014")}`);
|
|
2316
|
+
console.log(` ID: ${chalk10.dim(userInfo?.id || "\u2014")}`);
|
|
2510
2317
|
console.log();
|
|
2511
|
-
console.log(
|
|
2318
|
+
console.log(chalk10.bold(` \u{1F916} Agents (${agents.length})`));
|
|
2512
2319
|
if (agentDetails.length === 0) {
|
|
2513
|
-
console.log(
|
|
2320
|
+
console.log(chalk10.yellow(" No agents found."));
|
|
2514
2321
|
}
|
|
2515
2322
|
for (const agent of agentDetails) {
|
|
2516
2323
|
const isActive = agent.name === activeAgentName;
|
|
2517
|
-
const marker = isActive ?
|
|
2518
|
-
const activeLabel = isActive ?
|
|
2324
|
+
const marker = isActive ? chalk10.green("\u25CF ") : " ";
|
|
2325
|
+
const activeLabel = isActive ? chalk10.green(" (active)") : "";
|
|
2519
2326
|
const limit = agent.maxOrderAmountInCents ? `$${(agent.maxOrderAmountInCents / 100).toFixed(2)}` : "No limit";
|
|
2520
2327
|
const confirm = agent.requireConfirmation ? "Yes" : "No";
|
|
2521
2328
|
console.log();
|
|
2522
|
-
console.log(` ${marker}${
|
|
2523
|
-
console.log(` ID: ${
|
|
2524
|
-
console.log(` Max order: ${
|
|
2329
|
+
console.log(` ${marker}${chalk10.bold.white(agent.name)}${activeLabel}`);
|
|
2330
|
+
console.log(` ID: ${chalk10.dim(agent.id)}`);
|
|
2331
|
+
console.log(` Max order: ${chalk10.yellow(limit)}`);
|
|
2525
2332
|
console.log(` Require confirm: ${confirm}`);
|
|
2526
2333
|
console.log();
|
|
2527
|
-
console.log(
|
|
2334
|
+
console.log(chalk10.bold(` \u{1F4CD} Addresses (${agent.addresses.length})`));
|
|
2528
2335
|
if (agent.addresses.length === 0) {
|
|
2529
|
-
console.log(
|
|
2336
|
+
console.log(chalk10.dim(" None"));
|
|
2530
2337
|
} else {
|
|
2531
2338
|
for (const addr of agent.addresses) {
|
|
2532
2339
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
2533
|
-
const defaultBadge = isDefault ?
|
|
2534
|
-
console.log(` \u2022 ${
|
|
2535
|
-
console.log(
|
|
2340
|
+
const defaultBadge = isDefault ? chalk10.green(" \u2605 default") : "";
|
|
2341
|
+
console.log(` \u2022 ${chalk10.white(addr.label)}${defaultBadge}`);
|
|
2342
|
+
console.log(chalk10.dim(` ${addr.line1}, ${addr.city}, ${addr.postalCode} ${addr.country}`));
|
|
2536
2343
|
}
|
|
2537
2344
|
}
|
|
2538
2345
|
console.log();
|
|
2539
|
-
console.log(
|
|
2346
|
+
console.log(chalk10.bold(` \u{1F4B3} Payment Methods (${agent.paymentMethods.length})`));
|
|
2540
2347
|
if (agent.paymentMethods.length === 0) {
|
|
2541
|
-
console.log(
|
|
2348
|
+
console.log(chalk10.dim(" None \u2014 run: clishop payment add"));
|
|
2542
2349
|
} else {
|
|
2543
2350
|
for (const pm of agent.paymentMethods) {
|
|
2544
2351
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
2545
|
-
const defaultBadge = isDefault ?
|
|
2352
|
+
const defaultBadge = isDefault ? chalk10.green(" \u2605 default") : "";
|
|
2546
2353
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
2547
|
-
console.log(` \u2022 ${
|
|
2548
|
-
console.log(
|
|
2354
|
+
console.log(` \u2022 ${chalk10.white(pm.label)}${last4}${defaultBadge}`);
|
|
2355
|
+
console.log(chalk10.dim(` ${pm.type} via ${pm.provider || "unknown"} (${pm.id})`));
|
|
2549
2356
|
}
|
|
2550
2357
|
}
|
|
2551
2358
|
}
|
|
2552
2359
|
console.log();
|
|
2553
|
-
console.log(
|
|
2554
|
-
console.log(
|
|
2555
|
-
console.log(
|
|
2360
|
+
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"));
|
|
2361
|
+
console.log(chalk10.dim(" Tip: Use --agent <name> to run commands as a specific agent"));
|
|
2362
|
+
console.log(chalk10.dim(" Tip: Use 'clishop agent switch <name>' to change active agent"));
|
|
2556
2363
|
console.log();
|
|
2557
2364
|
} catch (error) {
|
|
2558
2365
|
handleApiError(error);
|
|
@@ -2561,10 +2368,10 @@ function registerStatusCommand(program2) {
|
|
|
2561
2368
|
}
|
|
2562
2369
|
|
|
2563
2370
|
// src/commands/setup.ts
|
|
2564
|
-
import
|
|
2371
|
+
import chalk11 from "chalk";
|
|
2565
2372
|
import ora9 from "ora";
|
|
2566
2373
|
import inquirer7 from "inquirer";
|
|
2567
|
-
function divider(color =
|
|
2374
|
+
function divider(color = chalk11.cyan) {
|
|
2568
2375
|
console.log(" " + color("\u2500".repeat(48)));
|
|
2569
2376
|
}
|
|
2570
2377
|
function stepHeader(step, total, title) {
|
|
@@ -2572,7 +2379,7 @@ function stepHeader(step, total, title) {
|
|
|
2572
2379
|
divider();
|
|
2573
2380
|
console.log();
|
|
2574
2381
|
console.log(
|
|
2575
|
-
|
|
2382
|
+
chalk11.bold.white(` STEP ${step} of ${total}`) + chalk11.dim(" \xB7 ") + chalk11.bold(title)
|
|
2576
2383
|
);
|
|
2577
2384
|
console.log();
|
|
2578
2385
|
}
|
|
@@ -2590,29 +2397,29 @@ function registerSetupCommand(program2) {
|
|
|
2590
2397
|
});
|
|
2591
2398
|
}
|
|
2592
2399
|
async function runSetupWizard() {
|
|
2593
|
-
const
|
|
2400
|
+
const config = getConfig();
|
|
2594
2401
|
console.log();
|
|
2595
|
-
divider(
|
|
2402
|
+
divider(chalk11.cyan);
|
|
2596
2403
|
console.log();
|
|
2597
|
-
console.log(
|
|
2598
|
-
console.log(
|
|
2404
|
+
console.log(chalk11.bold.cyan(" W E L C O M E T O C L I S H O P"));
|
|
2405
|
+
console.log(chalk11.dim(" Order anything from your terminal."));
|
|
2599
2406
|
console.log();
|
|
2600
|
-
divider(
|
|
2407
|
+
divider(chalk11.cyan);
|
|
2601
2408
|
console.log();
|
|
2602
2409
|
console.log(
|
|
2603
|
-
|
|
2410
|
+
chalk11.dim(" This wizard will guide you through the initial setup.")
|
|
2604
2411
|
);
|
|
2605
2412
|
console.log(
|
|
2606
|
-
|
|
2413
|
+
chalk11.dim(" It only takes a minute. You can re-run it anytime with:")
|
|
2607
2414
|
);
|
|
2608
|
-
console.log(
|
|
2415
|
+
console.log(chalk11.dim(" ") + chalk11.white("clishop setup"));
|
|
2609
2416
|
stepHeader(1, 5, "Account");
|
|
2610
2417
|
let loggedIn = await isLoggedIn();
|
|
2611
2418
|
if (loggedIn) {
|
|
2612
2419
|
const user = await getUserInfo();
|
|
2613
2420
|
console.log(
|
|
2614
|
-
|
|
2615
|
-
` \u2713 Already logged in as ${
|
|
2421
|
+
chalk11.green(
|
|
2422
|
+
` \u2713 Already logged in as ${chalk11.bold(user?.name || user?.email || "unknown")}`
|
|
2616
2423
|
)
|
|
2617
2424
|
);
|
|
2618
2425
|
console.log();
|
|
@@ -2658,9 +2465,9 @@ async function runSetupWizard() {
|
|
|
2658
2465
|
}
|
|
2659
2466
|
]);
|
|
2660
2467
|
if (answers.password !== answers.confirmPassword) {
|
|
2661
|
-
console.error(
|
|
2468
|
+
console.error(chalk11.red("\n \u2717 Passwords do not match."));
|
|
2662
2469
|
console.log(
|
|
2663
|
-
|
|
2470
|
+
chalk11.dim(" Run ") + chalk11.white("clishop setup") + chalk11.dim(" to try again.\n")
|
|
2664
2471
|
);
|
|
2665
2472
|
process.exitCode = 1;
|
|
2666
2473
|
return;
|
|
@@ -2673,24 +2480,24 @@ async function runSetupWizard() {
|
|
|
2673
2480
|
answers.name
|
|
2674
2481
|
);
|
|
2675
2482
|
spinner.succeed(
|
|
2676
|
-
|
|
2677
|
-
`Account created! Welcome, ${
|
|
2483
|
+
chalk11.green(
|
|
2484
|
+
`Account created! Welcome, ${chalk11.bold(user.name)}.`
|
|
2678
2485
|
)
|
|
2679
2486
|
);
|
|
2680
2487
|
} catch (error) {
|
|
2681
2488
|
spinner.fail(
|
|
2682
|
-
|
|
2489
|
+
chalk11.red(
|
|
2683
2490
|
`Registration failed: ${error?.response?.data?.message || error.message}`
|
|
2684
2491
|
)
|
|
2685
2492
|
);
|
|
2686
2493
|
console.log();
|
|
2687
2494
|
console.log(
|
|
2688
|
-
|
|
2689
|
-
" Make sure the backend is running at: " +
|
|
2495
|
+
chalk11.dim(
|
|
2496
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2690
2497
|
)
|
|
2691
2498
|
);
|
|
2692
2499
|
console.log(
|
|
2693
|
-
|
|
2500
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2694
2501
|
);
|
|
2695
2502
|
process.exitCode = 1;
|
|
2696
2503
|
return;
|
|
@@ -2709,22 +2516,22 @@ async function runSetupWizard() {
|
|
|
2709
2516
|
try {
|
|
2710
2517
|
const user = await login(answers.email, answers.password);
|
|
2711
2518
|
spinner.succeed(
|
|
2712
|
-
|
|
2519
|
+
chalk11.green(`Logged in as ${chalk11.bold(user.name)}.`)
|
|
2713
2520
|
);
|
|
2714
2521
|
} catch (error) {
|
|
2715
2522
|
spinner.fail(
|
|
2716
|
-
|
|
2523
|
+
chalk11.red(
|
|
2717
2524
|
`Login failed: ${error?.response?.data?.message || error.message}`
|
|
2718
2525
|
)
|
|
2719
2526
|
);
|
|
2720
2527
|
console.log();
|
|
2721
2528
|
console.log(
|
|
2722
|
-
|
|
2723
|
-
" Make sure the backend is running at: " +
|
|
2529
|
+
chalk11.dim(
|
|
2530
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2724
2531
|
)
|
|
2725
2532
|
);
|
|
2726
2533
|
console.log(
|
|
2727
|
-
|
|
2534
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2728
2535
|
);
|
|
2729
2536
|
process.exitCode = 1;
|
|
2730
2537
|
return;
|
|
@@ -2734,12 +2541,12 @@ async function runSetupWizard() {
|
|
|
2734
2541
|
stepHeader(2, 5, "Agent (optional)");
|
|
2735
2542
|
const activeAgent = getActiveAgent();
|
|
2736
2543
|
console.log(
|
|
2737
|
-
|
|
2544
|
+
chalk11.dim(
|
|
2738
2545
|
` A default agent is ready ($${activeAgent.maxOrderAmount} limit, confirmation on).`
|
|
2739
2546
|
)
|
|
2740
2547
|
);
|
|
2741
2548
|
console.log(
|
|
2742
|
-
|
|
2549
|
+
chalk11.dim(" Agents control spending limits and category restrictions.")
|
|
2743
2550
|
);
|
|
2744
2551
|
console.log();
|
|
2745
2552
|
const { agentChoice } = await inquirer7.prompt([
|
|
@@ -2778,22 +2585,22 @@ async function runSetupWizard() {
|
|
|
2778
2585
|
});
|
|
2779
2586
|
setActiveAgent(agent.name);
|
|
2780
2587
|
console.log(
|
|
2781
|
-
|
|
2588
|
+
chalk11.green(
|
|
2782
2589
|
`
|
|
2783
|
-
\u2713 Agent "${
|
|
2590
|
+
\u2713 Agent "${chalk11.bold(agent.name)}" created and set as active.`
|
|
2784
2591
|
)
|
|
2785
2592
|
);
|
|
2786
2593
|
} catch (error) {
|
|
2787
|
-
console.error(
|
|
2594
|
+
console.error(chalk11.red(`
|
|
2788
2595
|
\u2717 ${error.message}`));
|
|
2789
|
-
console.log(
|
|
2596
|
+
console.log(chalk11.dim(" Continuing with the default agent."));
|
|
2790
2597
|
}
|
|
2791
2598
|
} else {
|
|
2792
|
-
console.log(
|
|
2599
|
+
console.log(chalk11.green(" \u2713 Using default agent."));
|
|
2793
2600
|
}
|
|
2794
2601
|
stepHeader(3, 5, "Shipping Address");
|
|
2795
2602
|
console.log(
|
|
2796
|
-
|
|
2603
|
+
chalk11.dim(
|
|
2797
2604
|
" Add an address so products can be delivered to you."
|
|
2798
2605
|
)
|
|
2799
2606
|
);
|
|
@@ -2915,37 +2722,37 @@ async function runSetupWizard() {
|
|
|
2915
2722
|
});
|
|
2916
2723
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
2917
2724
|
spinner.succeed(
|
|
2918
|
-
|
|
2725
|
+
chalk11.green(
|
|
2919
2726
|
`Address "${addr.label}" saved and set as default.`
|
|
2920
2727
|
)
|
|
2921
2728
|
);
|
|
2922
2729
|
} catch (error) {
|
|
2923
2730
|
spinner.fail(
|
|
2924
|
-
|
|
2731
|
+
chalk11.red(
|
|
2925
2732
|
`Failed to save address: ${error?.response?.data?.message || error.message}`
|
|
2926
2733
|
)
|
|
2927
2734
|
);
|
|
2928
2735
|
console.log(
|
|
2929
|
-
|
|
2930
|
-
" You can add an address later with: " +
|
|
2736
|
+
chalk11.dim(
|
|
2737
|
+
" You can add an address later with: " + chalk11.white("clishop address add")
|
|
2931
2738
|
)
|
|
2932
2739
|
);
|
|
2933
2740
|
}
|
|
2934
2741
|
} else {
|
|
2935
2742
|
console.log(
|
|
2936
|
-
|
|
2937
|
-
"\n You can add one later with: " +
|
|
2743
|
+
chalk11.dim(
|
|
2744
|
+
"\n You can add one later with: " + chalk11.white("clishop address add")
|
|
2938
2745
|
)
|
|
2939
2746
|
);
|
|
2940
2747
|
}
|
|
2941
2748
|
stepHeader(4, 5, "Payment Method");
|
|
2942
2749
|
console.log(
|
|
2943
|
-
|
|
2750
|
+
chalk11.dim(
|
|
2944
2751
|
" For security, payment details are entered through a secure web"
|
|
2945
2752
|
)
|
|
2946
2753
|
);
|
|
2947
2754
|
console.log(
|
|
2948
|
-
|
|
2755
|
+
chalk11.dim(" page. The CLI never sees your card number.")
|
|
2949
2756
|
);
|
|
2950
2757
|
console.log();
|
|
2951
2758
|
const { addPayment } = await inquirer7.prompt([
|
|
@@ -2968,46 +2775,46 @@ async function runSetupWizard() {
|
|
|
2968
2775
|
const { setupUrl } = res.data;
|
|
2969
2776
|
console.log();
|
|
2970
2777
|
console.log(
|
|
2971
|
-
|
|
2778
|
+
chalk11.bold(
|
|
2972
2779
|
" Open this link in your browser to add a payment method:"
|
|
2973
2780
|
)
|
|
2974
2781
|
);
|
|
2975
2782
|
console.log();
|
|
2976
|
-
console.log(" " +
|
|
2783
|
+
console.log(" " + chalk11.cyan.underline(setupUrl));
|
|
2977
2784
|
console.log();
|
|
2978
2785
|
console.log(
|
|
2979
|
-
|
|
2980
|
-
" Once done, verify with: " +
|
|
2786
|
+
chalk11.dim(
|
|
2787
|
+
" Once done, verify with: " + chalk11.white("clishop payment list")
|
|
2981
2788
|
)
|
|
2982
2789
|
);
|
|
2983
2790
|
} catch (error) {
|
|
2984
2791
|
spinner.fail(
|
|
2985
|
-
|
|
2792
|
+
chalk11.red(
|
|
2986
2793
|
`Could not get setup link: ${error?.response?.data?.message || error.message}`
|
|
2987
2794
|
)
|
|
2988
2795
|
);
|
|
2989
2796
|
console.log(
|
|
2990
|
-
|
|
2991
|
-
" You can set up payment later with: " +
|
|
2797
|
+
chalk11.dim(
|
|
2798
|
+
" You can set up payment later with: " + chalk11.white("clishop payment add")
|
|
2992
2799
|
)
|
|
2993
2800
|
);
|
|
2994
2801
|
}
|
|
2995
2802
|
} else {
|
|
2996
2803
|
console.log(
|
|
2997
|
-
|
|
2998
|
-
"\n You can set one up later with: " +
|
|
2804
|
+
chalk11.dim(
|
|
2805
|
+
"\n You can set one up later with: " + chalk11.white("clishop payment add")
|
|
2999
2806
|
)
|
|
3000
2807
|
);
|
|
3001
2808
|
}
|
|
3002
2809
|
stepHeader(5, 5, "Your First Search");
|
|
3003
2810
|
if (addressCity) {
|
|
3004
2811
|
console.log(
|
|
3005
|
-
|
|
3006
|
-
` Let's find something! Products can be shipped to ${
|
|
2812
|
+
chalk11.dim(
|
|
2813
|
+
` Let's find something! Products can be shipped to ${chalk11.white(addressCity)}.`
|
|
3007
2814
|
)
|
|
3008
2815
|
);
|
|
3009
2816
|
} else {
|
|
3010
|
-
console.log(
|
|
2817
|
+
console.log(chalk11.dim(" Let's find something to order!"));
|
|
3011
2818
|
}
|
|
3012
2819
|
console.log();
|
|
3013
2820
|
const { searchQuery } = await inquirer7.prompt([
|
|
@@ -3029,14 +2836,14 @@ async function runSetupWizard() {
|
|
|
3029
2836
|
const result = res.data;
|
|
3030
2837
|
if (result.products.length === 0) {
|
|
3031
2838
|
console.log(
|
|
3032
|
-
|
|
2839
|
+
chalk11.yellow(
|
|
3033
2840
|
`
|
|
3034
2841
|
No results for "${searchQuery}". Try other terms later!`
|
|
3035
2842
|
)
|
|
3036
2843
|
);
|
|
3037
2844
|
} else {
|
|
3038
2845
|
console.log(
|
|
3039
|
-
|
|
2846
|
+
chalk11.bold(
|
|
3040
2847
|
`
|
|
3041
2848
|
Found ${result.total} result${result.total !== 1 ? "s" : ""} for "${searchQuery}":
|
|
3042
2849
|
`
|
|
@@ -3044,85 +2851,85 @@ async function runSetupWizard() {
|
|
|
3044
2851
|
);
|
|
3045
2852
|
for (const p of result.products) {
|
|
3046
2853
|
const price = formatPrice4(p.priceInCents, p.currency || "USD");
|
|
3047
|
-
const stock = p.inStock ?
|
|
2854
|
+
const stock = p.inStock ? chalk11.green("In Stock") : chalk11.red("Out of Stock");
|
|
3048
2855
|
console.log(
|
|
3049
|
-
` ${
|
|
2856
|
+
` ${chalk11.bold.cyan(p.name)} ${chalk11.bold.white(price)} ${stock}`
|
|
3050
2857
|
);
|
|
3051
|
-
console.log(
|
|
2858
|
+
console.log(chalk11.dim(` ID: ${p.id}`));
|
|
3052
2859
|
console.log(
|
|
3053
|
-
|
|
2860
|
+
chalk11.dim(
|
|
3054
2861
|
` ${p.description.length > 100 ? p.description.slice(0, 100) + "..." : p.description}`
|
|
3055
2862
|
)
|
|
3056
2863
|
);
|
|
3057
2864
|
console.log();
|
|
3058
2865
|
}
|
|
3059
2866
|
console.log(
|
|
3060
|
-
|
|
2867
|
+
chalk11.dim(" Buy a product with: ") + chalk11.white("clishop buy <product-id>")
|
|
3061
2868
|
);
|
|
3062
2869
|
}
|
|
3063
2870
|
} catch (error) {
|
|
3064
2871
|
spinner.fail(
|
|
3065
|
-
|
|
2872
|
+
chalk11.red(
|
|
3066
2873
|
`Search failed: ${error?.response?.data?.message || error.message}`
|
|
3067
2874
|
)
|
|
3068
2875
|
);
|
|
3069
2876
|
}
|
|
3070
2877
|
}
|
|
3071
|
-
|
|
2878
|
+
config.set("setupCompleted", true);
|
|
3072
2879
|
console.log();
|
|
3073
|
-
divider(
|
|
2880
|
+
divider(chalk11.green);
|
|
3074
2881
|
console.log();
|
|
3075
|
-
console.log(
|
|
2882
|
+
console.log(chalk11.bold.green(" \u2713 You're all set!"));
|
|
3076
2883
|
console.log();
|
|
3077
|
-
console.log(
|
|
2884
|
+
console.log(chalk11.dim(" Here are some commands to get you started:"));
|
|
3078
2885
|
console.log();
|
|
3079
2886
|
console.log(
|
|
3080
|
-
|
|
2887
|
+
chalk11.white(" clishop search <query> ") + chalk11.dim("Search for products")
|
|
3081
2888
|
);
|
|
3082
2889
|
console.log(
|
|
3083
|
-
|
|
2890
|
+
chalk11.white(" clishop buy <id> ") + chalk11.dim("Quick-buy a product")
|
|
3084
2891
|
);
|
|
3085
2892
|
console.log(
|
|
3086
|
-
|
|
2893
|
+
chalk11.white(" clishop order list ") + chalk11.dim("View your orders")
|
|
3087
2894
|
);
|
|
3088
2895
|
console.log(
|
|
3089
|
-
|
|
2896
|
+
chalk11.white(" clishop agent list ") + chalk11.dim("Manage your agents")
|
|
3090
2897
|
);
|
|
3091
2898
|
console.log(
|
|
3092
|
-
|
|
2899
|
+
chalk11.white(" clishop --help ") + chalk11.dim("See all commands")
|
|
3093
2900
|
);
|
|
3094
2901
|
console.log();
|
|
3095
|
-
divider(
|
|
2902
|
+
divider(chalk11.green);
|
|
3096
2903
|
console.log();
|
|
3097
2904
|
}
|
|
3098
2905
|
|
|
3099
2906
|
// src/commands/advertise.ts
|
|
3100
|
-
import
|
|
2907
|
+
import chalk12 from "chalk";
|
|
3101
2908
|
import ora10 from "ora";
|
|
3102
2909
|
import inquirer8 from "inquirer";
|
|
3103
2910
|
function formatPrice5(cents, currency) {
|
|
3104
2911
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
3105
2912
|
}
|
|
3106
2913
|
var STATUS_COLORS2 = {
|
|
3107
|
-
open:
|
|
3108
|
-
closed:
|
|
3109
|
-
accepted:
|
|
3110
|
-
cancelled:
|
|
3111
|
-
expired:
|
|
2914
|
+
open: chalk12.green,
|
|
2915
|
+
closed: chalk12.dim,
|
|
2916
|
+
accepted: chalk12.blue,
|
|
2917
|
+
cancelled: chalk12.red,
|
|
2918
|
+
expired: chalk12.yellow
|
|
3112
2919
|
};
|
|
3113
2920
|
var BID_STATUS_COLORS = {
|
|
3114
|
-
pending:
|
|
3115
|
-
accepted:
|
|
3116
|
-
rejected:
|
|
3117
|
-
withdrawn:
|
|
2921
|
+
pending: chalk12.yellow,
|
|
2922
|
+
accepted: chalk12.green,
|
|
2923
|
+
rejected: chalk12.red,
|
|
2924
|
+
withdrawn: chalk12.dim
|
|
3118
2925
|
};
|
|
3119
2926
|
function registerAdvertiseCommands(program2) {
|
|
3120
2927
|
const advertise = program2.command("advertise").description("Advertise a request for vendors to bid on (when you can't find what you need)");
|
|
3121
2928
|
advertise.command("create").alias("new").description("Create a new advertised request").action(async () => {
|
|
3122
2929
|
try {
|
|
3123
2930
|
const agent = getActiveAgent();
|
|
3124
|
-
console.log(
|
|
3125
|
-
console.log(
|
|
2931
|
+
console.log(chalk12.bold("\n \u{1F4E2} Advertise a Request\n"));
|
|
2932
|
+
console.log(chalk12.dim(" Can't find what you need? Describe it and vendors will bid to fulfill it.\n"));
|
|
3126
2933
|
const answers = await inquirer8.prompt([
|
|
3127
2934
|
{
|
|
3128
2935
|
type: "input",
|
|
@@ -3258,7 +3065,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3258
3065
|
addrChoices.push({ name: displayName, value: displayName });
|
|
3259
3066
|
}
|
|
3260
3067
|
const skipOption = "Skip \u2014 don't set a delivery address";
|
|
3261
|
-
addrChoices.push({ name:
|
|
3068
|
+
addrChoices.push({ name: chalk12.dim(skipOption), value: skipOption });
|
|
3262
3069
|
const defaultAddr = addresses.find((a) => a.id === agent.defaultAddressId);
|
|
3263
3070
|
const defaultDisplay = defaultAddr ? `${defaultAddr.label} \u2014 ${defaultAddr.line1}` : "";
|
|
3264
3071
|
const { selectedAddress } = await inquirer8.prompt([
|
|
@@ -3273,11 +3080,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3273
3080
|
addressId = addrMap.get(selectedAddress) || void 0;
|
|
3274
3081
|
} else {
|
|
3275
3082
|
addrSpinner.stop();
|
|
3276
|
-
console.log(
|
|
3083
|
+
console.log(chalk12.dim(" No addresses found. You can add one later with: clishop address add"));
|
|
3277
3084
|
}
|
|
3278
3085
|
} catch {
|
|
3279
3086
|
addrSpinner.stop();
|
|
3280
|
-
console.log(
|
|
3087
|
+
console.log(chalk12.dim(" Could not fetch addresses. Skipping delivery location."));
|
|
3281
3088
|
}
|
|
3282
3089
|
let paymentMethods;
|
|
3283
3090
|
const paySpinner = ora10("Fetching your payment methods...").start();
|
|
@@ -3287,7 +3094,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3287
3094
|
const payments = payRes.data.paymentMethods;
|
|
3288
3095
|
if (payments.length > 0) {
|
|
3289
3096
|
const payChoices = [
|
|
3290
|
-
{ name:
|
|
3097
|
+
{ name: chalk12.green("Accept all payment methods"), value: "__ALL__" },
|
|
3291
3098
|
...payments.map((p) => ({
|
|
3292
3099
|
name: `${p.label}${p.brand ? ` (${p.brand})` : ""}`,
|
|
3293
3100
|
value: p.id,
|
|
@@ -3309,11 +3116,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3309
3116
|
paymentMethods = JSON.stringify(selectedPayments);
|
|
3310
3117
|
}
|
|
3311
3118
|
} else {
|
|
3312
|
-
console.log(
|
|
3119
|
+
console.log(chalk12.dim(" No payment methods found. You can add one later with: clishop payment add"));
|
|
3313
3120
|
}
|
|
3314
3121
|
} catch {
|
|
3315
3122
|
paySpinner.stop();
|
|
3316
|
-
console.log(
|
|
3123
|
+
console.log(chalk12.dim(" Could not fetch payment methods. Skipping."));
|
|
3317
3124
|
}
|
|
3318
3125
|
const body = {
|
|
3319
3126
|
title: answers.title,
|
|
@@ -3352,9 +3159,9 @@ function registerAdvertiseCommands(program2) {
|
|
|
3352
3159
|
}
|
|
3353
3160
|
const spinner = ora10("Publishing your request...").start();
|
|
3354
3161
|
const res = await api.post("/advertise", body);
|
|
3355
|
-
spinner.succeed(
|
|
3356
|
-
console.log(
|
|
3357
|
-
console.log(
|
|
3162
|
+
spinner.succeed(chalk12.green(`Request published! ID: ${chalk12.bold(res.data.advertise.id)}`));
|
|
3163
|
+
console.log(chalk12.dim("\n Vendors can now see your request and submit bids."));
|
|
3164
|
+
console.log(chalk12.dim(` Check bids with: clishop advertise show ${res.data.advertise.id}
|
|
3358
3165
|
`));
|
|
3359
3166
|
} catch (error) {
|
|
3360
3167
|
handleApiError(error);
|
|
@@ -3402,7 +3209,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3402
3209
|
const spinner = ora10("Publishing your request...").start();
|
|
3403
3210
|
const api = getApiClient();
|
|
3404
3211
|
const res = await api.post("/advertise", body);
|
|
3405
|
-
spinner.succeed(
|
|
3212
|
+
spinner.succeed(chalk12.green(`Request published! ID: ${chalk12.bold(res.data.advertise.id)}`));
|
|
3406
3213
|
} catch (error) {
|
|
3407
3214
|
handleApiError(error);
|
|
3408
3215
|
}
|
|
@@ -3421,18 +3228,18 @@ function registerAdvertiseCommands(program2) {
|
|
|
3421
3228
|
return;
|
|
3422
3229
|
}
|
|
3423
3230
|
if (ads.length === 0) {
|
|
3424
|
-
console.log(
|
|
3425
|
-
console.log(
|
|
3231
|
+
console.log(chalk12.yellow("\nNo advertised requests found.\n"));
|
|
3232
|
+
console.log(chalk12.dim(" Create one with: clishop advertise create\n"));
|
|
3426
3233
|
return;
|
|
3427
3234
|
}
|
|
3428
|
-
console.log(
|
|
3235
|
+
console.log(chalk12.bold("\n\u{1F4E2} Your Advertised Requests:\n"));
|
|
3429
3236
|
for (const ad of ads) {
|
|
3430
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3237
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3431
3238
|
const date = new Date(ad.createdAt).toLocaleDateString();
|
|
3432
3239
|
const bidCount = ad.bids?.length || 0;
|
|
3433
|
-
const bidInfo = bidCount > 0 ?
|
|
3240
|
+
const bidInfo = bidCount > 0 ? chalk12.cyan(` (${bidCount} bid${bidCount > 1 ? "s" : ""})`) : chalk12.dim(" (no bids yet)");
|
|
3434
3241
|
console.log(
|
|
3435
|
-
` ${
|
|
3242
|
+
` ${chalk12.bold(ad.id)} ${statusColor(ad.status.toUpperCase().padEnd(10))} ${chalk12.bold(ad.title)}${bidInfo} ${chalk12.dim(date)}`
|
|
3436
3243
|
);
|
|
3437
3244
|
const meta = [];
|
|
3438
3245
|
if (ad.quantity > 1) meta.push(`qty: ${ad.quantity}`);
|
|
@@ -3444,13 +3251,13 @@ function registerAdvertiseCommands(program2) {
|
|
|
3444
3251
|
if (ad.recurring) meta.push("recurring");
|
|
3445
3252
|
if (ad.address) meta.push(`\u2192 ${ad.address.label}`);
|
|
3446
3253
|
if (meta.length) {
|
|
3447
|
-
console.log(` ${
|
|
3254
|
+
console.log(` ${chalk12.dim(meta.join(" \xB7 "))}`);
|
|
3448
3255
|
}
|
|
3449
3256
|
console.log();
|
|
3450
3257
|
}
|
|
3451
3258
|
const totalPages = Math.ceil(res.data.total / res.data.pageSize);
|
|
3452
3259
|
if (totalPages > 1) {
|
|
3453
|
-
console.log(
|
|
3260
|
+
console.log(chalk12.dim(` Page ${res.data.page} of ${totalPages}. Use --page to navigate.
|
|
3454
3261
|
`));
|
|
3455
3262
|
}
|
|
3456
3263
|
} catch (error) {
|
|
@@ -3468,10 +3275,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3468
3275
|
console.log(JSON.stringify(ad, null, 2));
|
|
3469
3276
|
return;
|
|
3470
3277
|
}
|
|
3471
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3278
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3472
3279
|
console.log();
|
|
3473
|
-
console.log(
|
|
3474
|
-
console.log(` ID: ${
|
|
3280
|
+
console.log(chalk12.bold.cyan(` \u{1F4E2} ${ad.title}`));
|
|
3281
|
+
console.log(` ID: ${chalk12.dim(ad.id)}`);
|
|
3475
3282
|
console.log(` Status: ${statusColor(ad.status.toUpperCase())}`);
|
|
3476
3283
|
if (ad.description) console.log(` Details: ${ad.description}`);
|
|
3477
3284
|
if (ad.sku) console.log(` SKU: ${ad.sku}`);
|
|
@@ -3479,10 +3286,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3479
3286
|
if (ad.company) console.log(` Company: ${ad.company}`);
|
|
3480
3287
|
if (ad.features) console.log(` Features: ${ad.features}`);
|
|
3481
3288
|
console.log(` Quantity: ${ad.quantity}`);
|
|
3482
|
-
if (ad.bidPriceInCents) console.log(` Max Bid: ${
|
|
3289
|
+
if (ad.bidPriceInCents) console.log(` Max Bid: ${chalk12.bold(formatPrice5(ad.bidPriceInCents, ad.currency))}`);
|
|
3483
3290
|
if (ad.speedDays) console.log(` Speed: ${ad.speedDays}-day delivery`);
|
|
3484
3291
|
const returnReqs = [];
|
|
3485
|
-
if (ad.freeReturns) returnReqs.push(
|
|
3292
|
+
if (ad.freeReturns) returnReqs.push(chalk12.green("Free Returns required"));
|
|
3486
3293
|
if (ad.minReturnDays) returnReqs.push(`${ad.minReturnDays}-day return window min`);
|
|
3487
3294
|
if (returnReqs.length) console.log(` Returns: ${returnReqs.join(" \xB7 ")}`);
|
|
3488
3295
|
if (ad.recurring) console.log(` Recurring: Yes${ad.recurringNote ? ` (${ad.recurringNote})` : ""}`);
|
|
@@ -3492,7 +3299,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3492
3299
|
}
|
|
3493
3300
|
if (ad.paymentMethods) {
|
|
3494
3301
|
if (ad.paymentMethods === "all") {
|
|
3495
|
-
console.log(` Payment: ${
|
|
3302
|
+
console.log(` Payment: ${chalk12.green("All methods accepted")}`);
|
|
3496
3303
|
} else {
|
|
3497
3304
|
try {
|
|
3498
3305
|
const ids = JSON.parse(ad.paymentMethods);
|
|
@@ -3506,20 +3313,20 @@ function registerAdvertiseCommands(program2) {
|
|
|
3506
3313
|
console.log(` Created: ${new Date(ad.createdAt).toLocaleString()}`);
|
|
3507
3314
|
const bids = ad.bids || [];
|
|
3508
3315
|
if (bids.length === 0) {
|
|
3509
|
-
console.log(
|
|
3316
|
+
console.log(chalk12.dim("\n No bids yet. Vendors will be able to see your request and submit bids."));
|
|
3510
3317
|
} else {
|
|
3511
|
-
console.log(
|
|
3318
|
+
console.log(chalk12.bold(`
|
|
3512
3319
|
Bids (${bids.length}):
|
|
3513
3320
|
`));
|
|
3514
3321
|
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(` ${
|
|
3322
|
+
const bidStatusColor = BID_STATUS_COLORS[bid.status] || chalk12.white;
|
|
3323
|
+
const storeBadge = bid.store?.verified ? chalk12.green(" \u2713") : "";
|
|
3324
|
+
const storeRating = bid.store?.rating != null ? chalk12.dim(` (${bid.store.rating.toFixed(1)}\u2605)`) : "";
|
|
3325
|
+
console.log(` ${chalk12.bold(bid.id)} ${bidStatusColor(bid.status.toUpperCase().padEnd(10))} ${chalk12.bold(formatPrice5(bid.priceInCents, bid.currency))}`);
|
|
3519
3326
|
console.log(` Store: ${bid.store?.name || bid.storeId}${storeBadge}${storeRating}`);
|
|
3520
3327
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3521
3328
|
const bidReturns = [];
|
|
3522
|
-
if (bid.freeReturns) bidReturns.push(
|
|
3329
|
+
if (bid.freeReturns) bidReturns.push(chalk12.green("Free Returns"));
|
|
3523
3330
|
if (bid.returnWindowDays) bidReturns.push(`${bid.returnWindowDays}-day return window`);
|
|
3524
3331
|
if (bidReturns.length) console.log(` Returns: ${bidReturns.join(" \xB7 ")}`);
|
|
3525
3332
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
@@ -3530,8 +3337,8 @@ function registerAdvertiseCommands(program2) {
|
|
|
3530
3337
|
if (ad.status === "open") {
|
|
3531
3338
|
const pendingBids = bids.filter((b) => b.status === "pending");
|
|
3532
3339
|
if (pendingBids.length > 0) {
|
|
3533
|
-
console.log(
|
|
3534
|
-
console.log(
|
|
3340
|
+
console.log(chalk12.dim(` Accept a bid: clishop advertise accept ${ad.id} <bidId>`));
|
|
3341
|
+
console.log(chalk12.dim(` Reject a bid: clishop advertise reject ${ad.id} <bidId>
|
|
3535
3342
|
`));
|
|
3536
3343
|
}
|
|
3537
3344
|
}
|
|
@@ -3550,15 +3357,15 @@ function registerAdvertiseCommands(program2) {
|
|
|
3550
3357
|
const ad = detailRes.data.advertise;
|
|
3551
3358
|
const bid = ad.bids?.find((b) => b.id === bidId);
|
|
3552
3359
|
if (!bid) {
|
|
3553
|
-
console.error(
|
|
3360
|
+
console.error(chalk12.red(`
|
|
3554
3361
|
\u2717 Bid ${bidId} not found on request ${advertiseId}.`));
|
|
3555
3362
|
process.exitCode = 1;
|
|
3556
3363
|
return;
|
|
3557
3364
|
}
|
|
3558
|
-
console.log(
|
|
3365
|
+
console.log(chalk12.bold("\n Accept this bid?\n"));
|
|
3559
3366
|
console.log(` Request: ${ad.title}`);
|
|
3560
|
-
console.log(` Store: ${bid.store?.name || bid.storeId}${bid.store?.verified ?
|
|
3561
|
-
console.log(` Price: ${
|
|
3367
|
+
console.log(` Store: ${bid.store?.name || bid.storeId}${bid.store?.verified ? chalk12.green(" \u2713") : ""}`);
|
|
3368
|
+
console.log(` Price: ${chalk12.bold(formatPrice5(bid.priceInCents, bid.currency))}`);
|
|
3562
3369
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3563
3370
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
3564
3371
|
console.log();
|
|
@@ -3571,12 +3378,12 @@ function registerAdvertiseCommands(program2) {
|
|
|
3571
3378
|
}
|
|
3572
3379
|
]);
|
|
3573
3380
|
if (!confirm) {
|
|
3574
|
-
console.log(
|
|
3381
|
+
console.log(chalk12.yellow("Cancelled."));
|
|
3575
3382
|
return;
|
|
3576
3383
|
}
|
|
3577
3384
|
const spinner = ora10("Accepting bid...").start();
|
|
3578
3385
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/accept`);
|
|
3579
|
-
spinner.succeed(
|
|
3386
|
+
spinner.succeed(chalk12.green("Bid accepted! The vendor will now fulfill your request."));
|
|
3580
3387
|
} catch (error) {
|
|
3581
3388
|
handleApiError(error);
|
|
3582
3389
|
}
|
|
@@ -3595,7 +3402,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3595
3402
|
const spinner = ora10("Rejecting bid...").start();
|
|
3596
3403
|
const api = getApiClient();
|
|
3597
3404
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/reject`);
|
|
3598
|
-
spinner.succeed(
|
|
3405
|
+
spinner.succeed(chalk12.green("Bid rejected."));
|
|
3599
3406
|
} catch (error) {
|
|
3600
3407
|
handleApiError(error);
|
|
3601
3408
|
}
|
|
@@ -3614,7 +3421,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3614
3421
|
const spinner = ora10("Cancelling request...").start();
|
|
3615
3422
|
const api = getApiClient();
|
|
3616
3423
|
await api.post(`/advertise/${id}/cancel`);
|
|
3617
|
-
spinner.succeed(
|
|
3424
|
+
spinner.succeed(chalk12.green("Request cancelled."));
|
|
3618
3425
|
} catch (error) {
|
|
3619
3426
|
handleApiError(error);
|
|
3620
3427
|
}
|
|
@@ -3622,7 +3429,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3622
3429
|
}
|
|
3623
3430
|
|
|
3624
3431
|
// src/commands/support.ts
|
|
3625
|
-
import
|
|
3432
|
+
import chalk13 from "chalk";
|
|
3626
3433
|
import ora11 from "ora";
|
|
3627
3434
|
import inquirer9 from "inquirer";
|
|
3628
3435
|
var CATEGORY_CHOICES = [
|
|
@@ -3641,18 +3448,18 @@ var PRIORITY_CHOICES = [
|
|
|
3641
3448
|
{ name: "Urgent", value: "urgent" }
|
|
3642
3449
|
];
|
|
3643
3450
|
var STATUS_COLORS3 = {
|
|
3644
|
-
open:
|
|
3645
|
-
in_progress:
|
|
3646
|
-
awaiting_customer:
|
|
3647
|
-
awaiting_store:
|
|
3648
|
-
resolved:
|
|
3649
|
-
closed:
|
|
3451
|
+
open: chalk13.green,
|
|
3452
|
+
in_progress: chalk13.cyan,
|
|
3453
|
+
awaiting_customer: chalk13.yellow,
|
|
3454
|
+
awaiting_store: chalk13.blue,
|
|
3455
|
+
resolved: chalk13.gray,
|
|
3456
|
+
closed: chalk13.dim
|
|
3650
3457
|
};
|
|
3651
3458
|
var PRIORITY_COLORS = {
|
|
3652
|
-
low:
|
|
3653
|
-
normal:
|
|
3654
|
-
high:
|
|
3655
|
-
urgent:
|
|
3459
|
+
low: chalk13.dim,
|
|
3460
|
+
normal: chalk13.white,
|
|
3461
|
+
high: chalk13.yellow,
|
|
3462
|
+
urgent: chalk13.red
|
|
3656
3463
|
};
|
|
3657
3464
|
function registerSupportCommands(program2) {
|
|
3658
3465
|
const support = program2.command("support").description("Manage support tickets for orders");
|
|
@@ -3685,7 +3492,7 @@ function registerSupportCommands(program2) {
|
|
|
3685
3492
|
}
|
|
3686
3493
|
]);
|
|
3687
3494
|
if (!answers.message || !answers.message.trim()) {
|
|
3688
|
-
console.error(
|
|
3495
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3689
3496
|
return;
|
|
3690
3497
|
}
|
|
3691
3498
|
const spinner = ora11("Creating support ticket...").start();
|
|
@@ -3697,17 +3504,17 @@ function registerSupportCommands(program2) {
|
|
|
3697
3504
|
priority: answers.priority,
|
|
3698
3505
|
message: answers.message.trim()
|
|
3699
3506
|
});
|
|
3700
|
-
spinner.succeed(
|
|
3507
|
+
spinner.succeed(chalk13.green("Support ticket created!"));
|
|
3701
3508
|
const t = res.data.ticket;
|
|
3702
3509
|
console.log();
|
|
3703
|
-
console.log(` ${
|
|
3704
|
-
console.log(` ${
|
|
3705
|
-
console.log(` ${
|
|
3706
|
-
console.log(` ${
|
|
3707
|
-
console.log(` ${
|
|
3510
|
+
console.log(` ${chalk13.bold("Ticket ID:")} ${chalk13.cyan(t.id)}`);
|
|
3511
|
+
console.log(` ${chalk13.bold("Subject:")} ${t.subject}`);
|
|
3512
|
+
console.log(` ${chalk13.bold("Store:")} ${t.storeName}`);
|
|
3513
|
+
console.log(` ${chalk13.bold("Category:")} ${t.category}`);
|
|
3514
|
+
console.log(` ${chalk13.bold("Status:")} ${(STATUS_COLORS3[t.status] || chalk13.white)(t.status)}`);
|
|
3708
3515
|
console.log();
|
|
3709
|
-
console.log(
|
|
3710
|
-
console.log(
|
|
3516
|
+
console.log(chalk13.dim(" The store will be notified. You can follow up with:"));
|
|
3517
|
+
console.log(chalk13.dim(` clishop support show ${t.id}`));
|
|
3711
3518
|
console.log();
|
|
3712
3519
|
} catch (error) {
|
|
3713
3520
|
handleApiError(error);
|
|
@@ -3727,22 +3534,22 @@ function registerSupportCommands(program2) {
|
|
|
3727
3534
|
return;
|
|
3728
3535
|
}
|
|
3729
3536
|
if (tickets.length === 0) {
|
|
3730
|
-
console.log(
|
|
3537
|
+
console.log(chalk13.yellow("\nNo support tickets found.\n"));
|
|
3731
3538
|
return;
|
|
3732
3539
|
}
|
|
3733
|
-
console.log(
|
|
3540
|
+
console.log(chalk13.bold("\nYour Support Tickets:\n"));
|
|
3734
3541
|
for (const t of tickets) {
|
|
3735
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3736
|
-
const priorityColor = PRIORITY_COLORS[t.priority] ||
|
|
3542
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3543
|
+
const priorityColor = PRIORITY_COLORS[t.priority] || chalk13.white;
|
|
3737
3544
|
const date = new Date(t.createdAt).toLocaleDateString();
|
|
3738
3545
|
console.log(
|
|
3739
|
-
` ${
|
|
3546
|
+
` ${chalk13.bold(t.id)} ${statusColor(t.status.toUpperCase().padEnd(20))} ${priorityColor(t.priority.padEnd(8))} ${chalk13.dim(date)}`
|
|
3740
3547
|
);
|
|
3741
|
-
console.log(` ${
|
|
3742
|
-
console.log(` ${
|
|
3548
|
+
console.log(` ${chalk13.bold(t.subject)}`);
|
|
3549
|
+
console.log(` ${chalk13.dim(`Store: ${t.storeName} \xB7 Order: ${t.orderId} \xB7 ${t.category}`)}`);
|
|
3743
3550
|
if (t.lastMessage) {
|
|
3744
|
-
const sender = t.lastMessage.senderType === "store" ?
|
|
3745
|
-
console.log(` ${
|
|
3551
|
+
const sender = t.lastMessage.senderType === "store" ? chalk13.blue("Store") : t.lastMessage.senderType === "system" ? chalk13.gray("System") : chalk13.green("You");
|
|
3552
|
+
console.log(` ${chalk13.dim("Last:")} ${sender}: ${chalk13.dim(t.lastMessage.body)}`);
|
|
3746
3553
|
}
|
|
3747
3554
|
console.log();
|
|
3748
3555
|
}
|
|
@@ -3761,25 +3568,25 @@ function registerSupportCommands(program2) {
|
|
|
3761
3568
|
console.log(JSON.stringify(t, null, 2));
|
|
3762
3569
|
return;
|
|
3763
3570
|
}
|
|
3764
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3571
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3765
3572
|
console.log();
|
|
3766
|
-
console.log(
|
|
3767
|
-
console.log(` Subject: ${
|
|
3573
|
+
console.log(chalk13.bold(` Ticket ${chalk13.cyan(t.id)}`));
|
|
3574
|
+
console.log(` Subject: ${chalk13.bold(t.subject)}`);
|
|
3768
3575
|
console.log(` Status: ${statusColor(t.status.toUpperCase())}`);
|
|
3769
3576
|
console.log(` Category: ${t.category}`);
|
|
3770
|
-
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] ||
|
|
3577
|
+
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] || chalk13.white)(t.priority)}`);
|
|
3771
3578
|
console.log(` Store: ${t.storeName}`);
|
|
3772
3579
|
console.log(` Order: ${t.orderId}`);
|
|
3773
3580
|
console.log(` Created: ${new Date(t.createdAt).toLocaleString()}`);
|
|
3774
3581
|
if (t.resolvedAt) console.log(` Resolved: ${new Date(t.resolvedAt).toLocaleString()}`);
|
|
3775
|
-
console.log(
|
|
3582
|
+
console.log(chalk13.bold("\n Messages:\n"));
|
|
3776
3583
|
for (const m of t.messages) {
|
|
3777
3584
|
const time = new Date(m.createdAt).toLocaleString();
|
|
3778
3585
|
let sender;
|
|
3779
|
-
if (m.senderType === "customer") sender =
|
|
3780
|
-
else if (m.senderType === "store") sender =
|
|
3781
|
-
else sender =
|
|
3782
|
-
console.log(` ${sender} ${
|
|
3586
|
+
if (m.senderType === "customer") sender = chalk13.green.bold("You");
|
|
3587
|
+
else if (m.senderType === "store") sender = chalk13.blue.bold("Store");
|
|
3588
|
+
else sender = chalk13.gray.bold("System");
|
|
3589
|
+
console.log(` ${sender} ${chalk13.dim(time)}`);
|
|
3783
3590
|
console.log(` ${m.body}`);
|
|
3784
3591
|
console.log();
|
|
3785
3592
|
}
|
|
@@ -3797,7 +3604,7 @@ function registerSupportCommands(program2) {
|
|
|
3797
3604
|
}
|
|
3798
3605
|
]);
|
|
3799
3606
|
if (!message || !message.trim()) {
|
|
3800
|
-
console.error(
|
|
3607
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3801
3608
|
return;
|
|
3802
3609
|
}
|
|
3803
3610
|
const spinner = ora11("Sending reply...").start();
|
|
@@ -3805,8 +3612,8 @@ function registerSupportCommands(program2) {
|
|
|
3805
3612
|
const res = await api.post(`/support/${ticketId}/reply`, {
|
|
3806
3613
|
message: message.trim()
|
|
3807
3614
|
});
|
|
3808
|
-
spinner.succeed(
|
|
3809
|
-
console.log(
|
|
3615
|
+
spinner.succeed(chalk13.green("Reply sent!"));
|
|
3616
|
+
console.log(chalk13.dim(` Ticket status: ${res.data.ticketStatus}
|
|
3810
3617
|
`));
|
|
3811
3618
|
} catch (error) {
|
|
3812
3619
|
handleApiError(error);
|
|
@@ -3826,7 +3633,235 @@ function registerSupportCommands(program2) {
|
|
|
3826
3633
|
const spinner = ora11("Closing ticket...").start();
|
|
3827
3634
|
const api = getApiClient();
|
|
3828
3635
|
await api.patch(`/support/${ticketId}/status`, { status: "closed" });
|
|
3829
|
-
spinner.succeed(
|
|
3636
|
+
spinner.succeed(chalk13.green("Ticket closed."));
|
|
3637
|
+
} catch (error) {
|
|
3638
|
+
handleApiError(error);
|
|
3639
|
+
}
|
|
3640
|
+
});
|
|
3641
|
+
}
|
|
3642
|
+
|
|
3643
|
+
// src/commands/feedback.ts
|
|
3644
|
+
import chalk14 from "chalk";
|
|
3645
|
+
import ora12 from "ora";
|
|
3646
|
+
import inquirer10 from "inquirer";
|
|
3647
|
+
var STATUS_COLORS4 = {
|
|
3648
|
+
open: chalk14.green,
|
|
3649
|
+
acknowledged: chalk14.cyan,
|
|
3650
|
+
in_progress: chalk14.yellow,
|
|
3651
|
+
fixed: chalk14.blue,
|
|
3652
|
+
wont_fix: chalk14.red,
|
|
3653
|
+
closed: chalk14.dim
|
|
3654
|
+
};
|
|
3655
|
+
var STATUS_LABELS = {
|
|
3656
|
+
open: "Open",
|
|
3657
|
+
acknowledged: "Acknowledged",
|
|
3658
|
+
in_progress: "In Progress",
|
|
3659
|
+
fixed: "Fixed",
|
|
3660
|
+
wont_fix: "Won't Fix",
|
|
3661
|
+
closed: "Closed"
|
|
3662
|
+
};
|
|
3663
|
+
var TYPE_COLORS = {
|
|
3664
|
+
bug: chalk14.red,
|
|
3665
|
+
suggestion: chalk14.magenta
|
|
3666
|
+
};
|
|
3667
|
+
function registerFeedbackCommands(program2) {
|
|
3668
|
+
const feedback = program2.command("feedback").description("Report bugs and suggest improvements");
|
|
3669
|
+
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) => {
|
|
3670
|
+
try {
|
|
3671
|
+
let title = opts.title;
|
|
3672
|
+
let description = opts.description;
|
|
3673
|
+
let stepsToReproduce = opts.steps;
|
|
3674
|
+
let actualBehavior = opts.actual;
|
|
3675
|
+
let expectedBehavior = opts.expected;
|
|
3676
|
+
if (!title || !description || !stepsToReproduce || !actualBehavior || !expectedBehavior) {
|
|
3677
|
+
console.log(chalk14.bold("\n\u{1F41B} Report a Bug\n"));
|
|
3678
|
+
console.log(chalk14.dim("Help us fix issues by describing what went wrong.\n"));
|
|
3679
|
+
const answers = await inquirer10.prompt([
|
|
3680
|
+
...!title ? [{
|
|
3681
|
+
type: "input",
|
|
3682
|
+
name: "title",
|
|
3683
|
+
message: "Bug title (short summary):",
|
|
3684
|
+
validate: (v) => v.trim().length > 0 || "Title is required"
|
|
3685
|
+
}] : [],
|
|
3686
|
+
...!description ? [{
|
|
3687
|
+
type: "input",
|
|
3688
|
+
name: "description",
|
|
3689
|
+
message: "General description of the bug:",
|
|
3690
|
+
validate: (v) => v.trim().length > 0 || "Description is required"
|
|
3691
|
+
}] : [],
|
|
3692
|
+
...!stepsToReproduce ? [{
|
|
3693
|
+
type: "editor",
|
|
3694
|
+
name: "stepsToReproduce",
|
|
3695
|
+
message: "Steps to reproduce (how do you trigger this bug?):"
|
|
3696
|
+
}] : [],
|
|
3697
|
+
...!actualBehavior ? [{
|
|
3698
|
+
type: "input",
|
|
3699
|
+
name: "actualBehavior",
|
|
3700
|
+
message: "What happens (actual behavior)?",
|
|
3701
|
+
validate: (v) => v.trim().length > 0 || "Actual behavior is required"
|
|
3702
|
+
}] : [],
|
|
3703
|
+
...!expectedBehavior ? [{
|
|
3704
|
+
type: "input",
|
|
3705
|
+
name: "expectedBehavior",
|
|
3706
|
+
message: "What did you expect to happen?",
|
|
3707
|
+
validate: (v) => v.trim().length > 0 || "Expected behavior is required"
|
|
3708
|
+
}] : []
|
|
3709
|
+
]);
|
|
3710
|
+
title = title || answers.title;
|
|
3711
|
+
description = description || answers.description;
|
|
3712
|
+
stepsToReproduce = stepsToReproduce || answers.stepsToReproduce;
|
|
3713
|
+
actualBehavior = actualBehavior || answers.actualBehavior;
|
|
3714
|
+
expectedBehavior = expectedBehavior || answers.expectedBehavior;
|
|
3715
|
+
}
|
|
3716
|
+
if (!stepsToReproduce?.trim()) {
|
|
3717
|
+
console.error(chalk14.red("\n\u2717 Steps to reproduce are required.\n"));
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3720
|
+
const spinner = ora12("Submitting bug report...").start();
|
|
3721
|
+
const api = getApiClient();
|
|
3722
|
+
const res = await api.post("/feedback", {
|
|
3723
|
+
type: "bug",
|
|
3724
|
+
title: title.trim(),
|
|
3725
|
+
description: description.trim(),
|
|
3726
|
+
stepsToReproduce: stepsToReproduce.trim(),
|
|
3727
|
+
actualBehavior: actualBehavior.trim(),
|
|
3728
|
+
expectedBehavior: expectedBehavior.trim()
|
|
3729
|
+
});
|
|
3730
|
+
spinner.succeed(chalk14.green("Bug report submitted!"));
|
|
3731
|
+
const f = res.data.feedback;
|
|
3732
|
+
console.log();
|
|
3733
|
+
console.log(` ${chalk14.bold("ID:")} ${chalk14.cyan(f.id)}`);
|
|
3734
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
3735
|
+
console.log(` ${chalk14.bold("Status:")} ${(STATUS_COLORS4[f.status] || chalk14.white)(STATUS_LABELS[f.status] || f.status)}`);
|
|
3736
|
+
console.log();
|
|
3737
|
+
console.log(chalk14.dim(" We'll investigate and update the status. Check back with:"));
|
|
3738
|
+
console.log(chalk14.dim(` clishop feedback show ${f.id}`));
|
|
3739
|
+
console.log();
|
|
3740
|
+
} catch (error) {
|
|
3741
|
+
handleApiError(error);
|
|
3742
|
+
}
|
|
3743
|
+
});
|
|
3744
|
+
feedback.command("suggest").alias("suggestion").description("Suggest an improvement or feature").option("--title <title>", "Short summary").option("--description <desc>", "Detailed suggestion").action(async (opts) => {
|
|
3745
|
+
try {
|
|
3746
|
+
let title = opts.title;
|
|
3747
|
+
let description = opts.description;
|
|
3748
|
+
if (!title || !description) {
|
|
3749
|
+
console.log(chalk14.bold("\n\u{1F4A1} Suggest an Improvement\n"));
|
|
3750
|
+
console.log(chalk14.dim("Tell us how we can make CLISHOP better.\n"));
|
|
3751
|
+
const answers = await inquirer10.prompt([
|
|
3752
|
+
...!title ? [{
|
|
3753
|
+
type: "input",
|
|
3754
|
+
name: "title",
|
|
3755
|
+
message: "Suggestion title (short summary):",
|
|
3756
|
+
validate: (v) => v.trim().length > 0 || "Title is required"
|
|
3757
|
+
}] : [],
|
|
3758
|
+
...!description ? [{
|
|
3759
|
+
type: "editor",
|
|
3760
|
+
name: "description",
|
|
3761
|
+
message: "Describe your suggestion (opens editor):"
|
|
3762
|
+
}] : []
|
|
3763
|
+
]);
|
|
3764
|
+
title = title || answers.title;
|
|
3765
|
+
description = description || answers.description;
|
|
3766
|
+
}
|
|
3767
|
+
if (!description?.trim()) {
|
|
3768
|
+
console.error(chalk14.red("\n\u2717 Description is required.\n"));
|
|
3769
|
+
return;
|
|
3770
|
+
}
|
|
3771
|
+
const spinner = ora12("Submitting suggestion...").start();
|
|
3772
|
+
const api = getApiClient();
|
|
3773
|
+
const res = await api.post("/feedback", {
|
|
3774
|
+
type: "suggestion",
|
|
3775
|
+
title: title.trim(),
|
|
3776
|
+
description: description.trim()
|
|
3777
|
+
});
|
|
3778
|
+
spinner.succeed(chalk14.green("Suggestion submitted!"));
|
|
3779
|
+
const f = res.data.feedback;
|
|
3780
|
+
console.log();
|
|
3781
|
+
console.log(` ${chalk14.bold("ID:")} ${chalk14.cyan(f.id)}`);
|
|
3782
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
3783
|
+
console.log(` ${chalk14.bold("Status:")} ${(STATUS_COLORS4[f.status] || chalk14.white)(STATUS_LABELS[f.status] || f.status)}`);
|
|
3784
|
+
console.log();
|
|
3785
|
+
console.log(chalk14.dim(" Thanks for the suggestion! Check status with:"));
|
|
3786
|
+
console.log(chalk14.dim(` clishop feedback show ${f.id}`));
|
|
3787
|
+
console.log();
|
|
3788
|
+
} catch (error) {
|
|
3789
|
+
handleApiError(error);
|
|
3790
|
+
}
|
|
3791
|
+
});
|
|
3792
|
+
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) => {
|
|
3793
|
+
try {
|
|
3794
|
+
const spinner = ora12("Fetching feedback...").start();
|
|
3795
|
+
const api = getApiClient();
|
|
3796
|
+
const res = await api.get("/feedback", {
|
|
3797
|
+
params: {
|
|
3798
|
+
...opts.type ? { type: opts.type } : {},
|
|
3799
|
+
...opts.status ? { status: opts.status } : {}
|
|
3800
|
+
}
|
|
3801
|
+
});
|
|
3802
|
+
spinner.stop();
|
|
3803
|
+
const items = res.data.feedback;
|
|
3804
|
+
if (opts.json) {
|
|
3805
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
3806
|
+
return;
|
|
3807
|
+
}
|
|
3808
|
+
if (items.length === 0) {
|
|
3809
|
+
console.log(chalk14.yellow("\nNo feedback found.\n"));
|
|
3810
|
+
return;
|
|
3811
|
+
}
|
|
3812
|
+
console.log(chalk14.bold("\nYour Feedback:\n"));
|
|
3813
|
+
for (const f of items) {
|
|
3814
|
+
const typeColor = TYPE_COLORS[f.type] || chalk14.white;
|
|
3815
|
+
const statusColor = STATUS_COLORS4[f.status] || chalk14.white;
|
|
3816
|
+
const date = new Date(f.createdAt).toLocaleDateString();
|
|
3817
|
+
const typeLabel = f.type === "bug" ? "\u{1F41B} BUG" : "\u{1F4A1} SUGGESTION";
|
|
3818
|
+
console.log(
|
|
3819
|
+
` ${chalk14.bold(f.id)} ${typeColor(typeLabel.padEnd(14))} ${statusColor((STATUS_LABELS[f.status] || f.status).padEnd(14))} ${chalk14.dim(date)}`
|
|
3820
|
+
);
|
|
3821
|
+
console.log(` ${chalk14.bold(f.title)}`);
|
|
3822
|
+
if (f.adminNote) {
|
|
3823
|
+
console.log(` ${chalk14.cyan("Admin:")} ${f.adminNote}`);
|
|
3824
|
+
}
|
|
3825
|
+
console.log();
|
|
3826
|
+
}
|
|
3827
|
+
} catch (error) {
|
|
3828
|
+
handleApiError(error);
|
|
3829
|
+
}
|
|
3830
|
+
});
|
|
3831
|
+
feedback.command("show <id>").description("View a bug report or suggestion").option("--json", "Output raw JSON").action(async (id, opts) => {
|
|
3832
|
+
try {
|
|
3833
|
+
const spinner = ora12("Fetching feedback...").start();
|
|
3834
|
+
const api = getApiClient();
|
|
3835
|
+
const res = await api.get(`/feedback/${id}`);
|
|
3836
|
+
spinner.stop();
|
|
3837
|
+
const f = res.data.feedback;
|
|
3838
|
+
if (opts.json) {
|
|
3839
|
+
console.log(JSON.stringify(f, null, 2));
|
|
3840
|
+
return;
|
|
3841
|
+
}
|
|
3842
|
+
const statusColor = STATUS_COLORS4[f.status] || chalk14.white;
|
|
3843
|
+
const typeLabel = f.type === "bug" ? "\u{1F41B} Bug Report" : "\u{1F4A1} Suggestion";
|
|
3844
|
+
console.log();
|
|
3845
|
+
console.log(chalk14.bold(` ${typeLabel} \u2014 ${chalk14.cyan(f.id)}`));
|
|
3846
|
+
console.log(` ${chalk14.bold("Title:")} ${f.title}`);
|
|
3847
|
+
console.log(` ${chalk14.bold("Status:")} ${statusColor(STATUS_LABELS[f.status] || f.status)}`);
|
|
3848
|
+
console.log(` ${chalk14.bold("Created:")} ${new Date(f.createdAt).toLocaleString()}`);
|
|
3849
|
+
console.log(` ${chalk14.bold("Updated:")} ${new Date(f.updatedAt).toLocaleString()}`);
|
|
3850
|
+
console.log(chalk14.bold("\n Description:"));
|
|
3851
|
+
console.log(` ${f.description}`);
|
|
3852
|
+
if (f.type === "bug") {
|
|
3853
|
+
console.log(chalk14.bold("\n Steps to Reproduce:"));
|
|
3854
|
+
console.log(` ${f.stepsToReproduce}`);
|
|
3855
|
+
console.log(chalk14.bold("\n Actual Behavior:"));
|
|
3856
|
+
console.log(` ${chalk14.red(f.actualBehavior)}`);
|
|
3857
|
+
console.log(chalk14.bold("\n Expected Behavior:"));
|
|
3858
|
+
console.log(` ${chalk14.green(f.expectedBehavior)}`);
|
|
3859
|
+
}
|
|
3860
|
+
if (f.adminNote) {
|
|
3861
|
+
console.log(chalk14.bold("\n Admin Response:"));
|
|
3862
|
+
console.log(` ${chalk14.cyan(f.adminNote)}`);
|
|
3863
|
+
}
|
|
3864
|
+
console.log();
|
|
3830
3865
|
} catch (error) {
|
|
3831
3866
|
handleApiError(error);
|
|
3832
3867
|
}
|
|
@@ -3840,8 +3875,8 @@ program.name("clishop").version("0.1.0").description(
|
|
|
3840
3875
|
).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
|
|
3841
3876
|
const agentOpt = thisCommand.opts().agent;
|
|
3842
3877
|
if (agentOpt) {
|
|
3843
|
-
const
|
|
3844
|
-
if (!
|
|
3878
|
+
const config = getConfig();
|
|
3879
|
+
if (!config.store.agents[agentOpt]) {
|
|
3845
3880
|
console.error(chalk15.red(`\u2717 Agent "${agentOpt}" does not exist.`));
|
|
3846
3881
|
process.exit(1);
|
|
3847
3882
|
}
|
|
@@ -3861,11 +3896,17 @@ registerStatusCommand(program);
|
|
|
3861
3896
|
registerSetupCommand(program);
|
|
3862
3897
|
registerAdvertiseCommands(program);
|
|
3863
3898
|
registerSupportCommands(program);
|
|
3899
|
+
registerFeedbackCommands(program);
|
|
3864
3900
|
async function main() {
|
|
3901
|
+
if (process.argv.includes("--mcp")) {
|
|
3902
|
+
const { startMcpServer } = await import("./mcp.js");
|
|
3903
|
+
await startMcpServer();
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3865
3906
|
const hasSubcommand = process.argv.length > 2;
|
|
3866
3907
|
if (!hasSubcommand) {
|
|
3867
|
-
const
|
|
3868
|
-
if (!
|
|
3908
|
+
const config = getConfig();
|
|
3909
|
+
if (!config.get("setupCompleted")) {
|
|
3869
3910
|
await runSetupWizard();
|
|
3870
3911
|
return;
|
|
3871
3912
|
}
|