clishop 0.1.1 → 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 +446 -194
- 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 -724
- package/dist/mcp.d.ts +19 -0
- package/dist/mcp.js +14430 -0
- package/package.json +15 -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 -122
- 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,176 +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-auth";
|
|
101
|
-
var ACCOUNT_TOKEN = "auth-token";
|
|
102
|
-
var ACCOUNT_REFRESH = "refresh-token";
|
|
103
|
-
var ACCOUNT_USER = "user-info";
|
|
104
|
-
var authStore = new Conf2({
|
|
105
|
-
projectName: SERVICE_NAME,
|
|
106
|
-
encryptionKey: "clishop-local-encryption-key"
|
|
107
|
-
// Basic obfuscation
|
|
108
|
-
});
|
|
109
|
-
async function storeToken(token) {
|
|
110
|
-
authStore.set(ACCOUNT_TOKEN, token);
|
|
111
|
-
}
|
|
112
|
-
async function storeRefreshToken(token) {
|
|
113
|
-
authStore.set(ACCOUNT_REFRESH, token);
|
|
114
|
-
}
|
|
115
|
-
async function storeUserInfo(user) {
|
|
116
|
-
authStore.set(ACCOUNT_USER, JSON.stringify(user));
|
|
117
|
-
}
|
|
118
|
-
async function getToken() {
|
|
119
|
-
return authStore.get(ACCOUNT_TOKEN) || null;
|
|
35
|
+
return Buffer.concat(chunks).toString("utf8").trim();
|
|
120
36
|
}
|
|
121
|
-
async function getRefreshToken() {
|
|
122
|
-
return authStore.get(ACCOUNT_REFRESH) || null;
|
|
123
|
-
}
|
|
124
|
-
async function getUserInfo() {
|
|
125
|
-
const raw = authStore.get(ACCOUNT_USER);
|
|
126
|
-
if (!raw) return null;
|
|
127
|
-
try {
|
|
128
|
-
return JSON.parse(raw);
|
|
129
|
-
} catch {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
async function clearAuth() {
|
|
134
|
-
authStore.delete(ACCOUNT_TOKEN);
|
|
135
|
-
authStore.delete(ACCOUNT_REFRESH);
|
|
136
|
-
authStore.delete(ACCOUNT_USER);
|
|
137
|
-
}
|
|
138
|
-
async function isLoggedIn() {
|
|
139
|
-
const token = await getToken();
|
|
140
|
-
return !!token;
|
|
141
|
-
}
|
|
142
|
-
async function login(email, password) {
|
|
143
|
-
const config2 = getConfig();
|
|
144
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
145
|
-
const res = await axios.post(`${baseUrl}/auth/login`, { email, password });
|
|
146
|
-
const { token, refreshToken, user } = res.data;
|
|
147
|
-
await storeToken(token);
|
|
148
|
-
if (refreshToken) await storeRefreshToken(refreshToken);
|
|
149
|
-
await storeUserInfo(user);
|
|
150
|
-
return user;
|
|
151
|
-
}
|
|
152
|
-
async function register(email, password, name) {
|
|
153
|
-
const config2 = getConfig();
|
|
154
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
155
|
-
const res = await axios.post(`${baseUrl}/auth/register`, { email, password, name });
|
|
156
|
-
const { token, refreshToken, user } = res.data;
|
|
157
|
-
await storeToken(token);
|
|
158
|
-
if (refreshToken) await storeRefreshToken(refreshToken);
|
|
159
|
-
await storeUserInfo(user);
|
|
160
|
-
return user;
|
|
161
|
-
}
|
|
162
|
-
async function logout() {
|
|
163
|
-
const config2 = getConfig();
|
|
164
|
-
const baseUrl = config2.get("apiBaseUrl");
|
|
165
|
-
const token = await getToken();
|
|
166
|
-
if (token) {
|
|
167
|
-
try {
|
|
168
|
-
await axios.post(`${baseUrl}/auth/logout`, {}, {
|
|
169
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
170
|
-
});
|
|
171
|
-
} catch {
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
await clearAuth();
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// src/commands/auth.ts
|
|
178
37
|
function registerAuthCommands(program2) {
|
|
179
|
-
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) => {
|
|
180
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
|
+
}
|
|
181
45
|
if (await isLoggedIn()) {
|
|
182
46
|
const user2 = await getUserInfo();
|
|
183
47
|
const { confirm } = await inquirer.prompt([
|
|
@@ -192,6 +56,18 @@ function registerAuthCommands(program2) {
|
|
|
192
56
|
}
|
|
193
57
|
let email = opts.email;
|
|
194
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
|
+
}
|
|
195
71
|
if (!email || !password) {
|
|
196
72
|
const answers = await inquirer.prompt([
|
|
197
73
|
...!email ? [{ type: "input", name: "email", message: "Email:" }] : [],
|
|
@@ -397,83 +273,9 @@ function registerAgentCommands(program2) {
|
|
|
397
273
|
}
|
|
398
274
|
|
|
399
275
|
// src/commands/address.ts
|
|
400
|
-
import
|
|
276
|
+
import chalk3 from "chalk";
|
|
401
277
|
import ora2 from "ora";
|
|
402
278
|
import inquirer3 from "inquirer";
|
|
403
|
-
|
|
404
|
-
// src/api.ts
|
|
405
|
-
import axios2 from "axios";
|
|
406
|
-
import chalk3 from "chalk";
|
|
407
|
-
var client = null;
|
|
408
|
-
var API_BASE_URL = process.env.CLISHOP_API_URL || "https://clishop-backend.vercel.app/api";
|
|
409
|
-
function getApiClient() {
|
|
410
|
-
if (client) return client;
|
|
411
|
-
const baseUrl = API_BASE_URL;
|
|
412
|
-
client = axios2.create({
|
|
413
|
-
baseURL: baseUrl,
|
|
414
|
-
timeout: 3e4,
|
|
415
|
-
headers: {
|
|
416
|
-
"Content-Type": "application/json"
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
client.interceptors.request.use(async (reqConfig) => {
|
|
420
|
-
const token = await getToken();
|
|
421
|
-
if (token) {
|
|
422
|
-
reqConfig.headers.Authorization = `Bearer ${token}`;
|
|
423
|
-
}
|
|
424
|
-
return reqConfig;
|
|
425
|
-
});
|
|
426
|
-
client.interceptors.response.use(
|
|
427
|
-
(res) => res,
|
|
428
|
-
async (error) => {
|
|
429
|
-
if (error.response?.status === 401) {
|
|
430
|
-
const refreshToken = await getRefreshToken();
|
|
431
|
-
if (refreshToken) {
|
|
432
|
-
try {
|
|
433
|
-
const res = await axios2.post(`${baseUrl}/auth/refresh`, { refreshToken });
|
|
434
|
-
await storeToken(res.data.token);
|
|
435
|
-
if (error.config) {
|
|
436
|
-
error.config.headers.Authorization = `Bearer ${res.data.token}`;
|
|
437
|
-
return axios2(error.config);
|
|
438
|
-
}
|
|
439
|
-
} catch {
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
console.error(chalk3.red("\n\u2717 Session expired. Please login again: clishop login\n"));
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
throw error;
|
|
446
|
-
}
|
|
447
|
-
);
|
|
448
|
-
return client;
|
|
449
|
-
}
|
|
450
|
-
function handleApiError(error) {
|
|
451
|
-
if (axios2.isAxiosError(error)) {
|
|
452
|
-
const data = error.response?.data;
|
|
453
|
-
const message = data?.message || data?.error || error.message;
|
|
454
|
-
const status = error.response?.status;
|
|
455
|
-
if (status === 422 && data?.errors) {
|
|
456
|
-
console.error(chalk3.red("\n\u2717 Validation errors:"));
|
|
457
|
-
for (const [field, msgs] of Object.entries(data.errors)) {
|
|
458
|
-
console.error(chalk3.red(` ${field}: ${msgs.join(", ")}`));
|
|
459
|
-
}
|
|
460
|
-
} else if (status === 404) {
|
|
461
|
-
console.error(chalk3.red(`
|
|
462
|
-
\u2717 Not found: ${message}`));
|
|
463
|
-
} else {
|
|
464
|
-
console.error(chalk3.red(`
|
|
465
|
-
\u2717 API error (${status || "network"}): ${message}`));
|
|
466
|
-
}
|
|
467
|
-
} else if (error instanceof Error) {
|
|
468
|
-
console.error(chalk3.red(`
|
|
469
|
-
\u2717 ${error.message}`));
|
|
470
|
-
} else {
|
|
471
|
-
console.error(chalk3.red("\n\u2717 An unexpected error occurred."));
|
|
472
|
-
}
|
|
473
|
-
process.exit(1);
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/commands/address.ts
|
|
477
279
|
function registerAddressCommands(program2) {
|
|
478
280
|
const address = program2.command("address").description("Manage shipping addresses (scoped to the active agent)");
|
|
479
281
|
address.command("list").alias("ls").description("List all addresses for the active agent").action(async () => {
|
|
@@ -487,26 +289,26 @@ function registerAddressCommands(program2) {
|
|
|
487
289
|
spinner.stop();
|
|
488
290
|
const addresses = res.data.addresses;
|
|
489
291
|
if (addresses.length === 0) {
|
|
490
|
-
console.log(
|
|
292
|
+
console.log(chalk3.yellow("\nNo addresses found. Add one with: clishop address add\n"));
|
|
491
293
|
return;
|
|
492
294
|
}
|
|
493
|
-
console.log(
|
|
295
|
+
console.log(chalk3.bold(`
|
|
494
296
|
Addresses for agent "${agent.name}":
|
|
495
297
|
`));
|
|
496
298
|
for (const addr of addresses) {
|
|
497
299
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
498
|
-
const marker = isDefault ?
|
|
499
|
-
console.log(`${marker}${
|
|
300
|
+
const marker = isDefault ? chalk3.green("\u25CF ") : " ";
|
|
301
|
+
console.log(`${marker}${chalk3.bold(addr.label)} ${chalk3.dim(`(${addr.id})`)}`);
|
|
500
302
|
if (addr.recipientName) console.log(` ${addr.recipientName}`);
|
|
501
|
-
if (addr.companyName) console.log(` ${
|
|
303
|
+
if (addr.companyName) console.log(` ${chalk3.cyan(addr.companyName)}`);
|
|
502
304
|
console.log(` ${addr.line1}`);
|
|
503
305
|
if (addr.line2) console.log(` ${addr.line2}`);
|
|
504
306
|
console.log(` ${addr.city}${addr.region ? `, ${addr.region}` : ""} ${addr.postalCode}`);
|
|
505
307
|
console.log(` ${addr.country}`);
|
|
506
|
-
if (addr.recipientPhone) console.log(` ${
|
|
507
|
-
if (addr.vatNumber) console.log(` ${
|
|
508
|
-
if (addr.taxId) console.log(` ${
|
|
509
|
-
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}`);
|
|
510
312
|
console.log();
|
|
511
313
|
}
|
|
512
314
|
} catch (error) {
|
|
@@ -596,7 +398,7 @@ Addresses for agent "${agent.name}":
|
|
|
596
398
|
if (setDefault) {
|
|
597
399
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
598
400
|
}
|
|
599
|
-
spinner.succeed(
|
|
401
|
+
spinner.succeed(chalk3.green(`Address "${answers.label}" added.`));
|
|
600
402
|
} catch (error) {
|
|
601
403
|
handleApiError(error);
|
|
602
404
|
}
|
|
@@ -615,7 +417,7 @@ Addresses for agent "${agent.name}":
|
|
|
615
417
|
const spinner = ora2("Removing address...").start();
|
|
616
418
|
const api = getApiClient();
|
|
617
419
|
await api.delete(`/addresses/${id}`);
|
|
618
|
-
spinner.succeed(
|
|
420
|
+
spinner.succeed(chalk3.green("Address removed."));
|
|
619
421
|
const agent = getActiveAgent();
|
|
620
422
|
if (agent.defaultAddressId === id) {
|
|
621
423
|
updateAgent(agent.name, { defaultAddressId: void 0 });
|
|
@@ -627,13 +429,13 @@ Addresses for agent "${agent.name}":
|
|
|
627
429
|
address.command("set-default <id>").description("Set the default address for the active agent").action((id) => {
|
|
628
430
|
const agent = getActiveAgent();
|
|
629
431
|
updateAgent(agent.name, { defaultAddressId: id });
|
|
630
|
-
console.log(
|
|
432
|
+
console.log(chalk3.green(`
|
|
631
433
|
\u2713 Default address for agent "${agent.name}" set to ${id}.`));
|
|
632
434
|
});
|
|
633
435
|
}
|
|
634
436
|
|
|
635
437
|
// src/commands/payment.ts
|
|
636
|
-
import
|
|
438
|
+
import chalk4 from "chalk";
|
|
637
439
|
import ora3 from "ora";
|
|
638
440
|
function registerPaymentCommands(program2) {
|
|
639
441
|
const payment = program2.command("payment").description("Manage payment methods (scoped to the active agent)");
|
|
@@ -648,17 +450,17 @@ function registerPaymentCommands(program2) {
|
|
|
648
450
|
spinner.stop();
|
|
649
451
|
const methods = res.data.paymentMethods;
|
|
650
452
|
if (methods.length === 0) {
|
|
651
|
-
console.log(
|
|
453
|
+
console.log(chalk4.yellow("\nNo payment methods found. Add one with: clishop payment add\n"));
|
|
652
454
|
return;
|
|
653
455
|
}
|
|
654
|
-
console.log(
|
|
456
|
+
console.log(chalk4.bold(`
|
|
655
457
|
Payment methods for agent "${agent.name}":
|
|
656
458
|
`));
|
|
657
459
|
for (const pm of methods) {
|
|
658
460
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
659
|
-
const marker = isDefault ?
|
|
461
|
+
const marker = isDefault ? chalk4.green("\u25CF ") : " ";
|
|
660
462
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
661
|
-
console.log(`${marker}${
|
|
463
|
+
console.log(`${marker}${chalk4.bold(pm.label)}${last4} ${chalk4.dim(`[${pm.type}]`)} ${chalk4.dim(`(${pm.id})`)}`);
|
|
662
464
|
}
|
|
663
465
|
console.log();
|
|
664
466
|
} catch (error) {
|
|
@@ -675,11 +477,11 @@ Payment methods for agent "${agent.name}":
|
|
|
675
477
|
});
|
|
676
478
|
spinner.stop();
|
|
677
479
|
const { setupUrl } = res.data;
|
|
678
|
-
console.log(
|
|
679
|
-
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}
|
|
680
482
|
`));
|
|
681
|
-
console.log(
|
|
682
|
-
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"));
|
|
683
485
|
} catch (error) {
|
|
684
486
|
handleApiError(error);
|
|
685
487
|
}
|
|
@@ -689,7 +491,7 @@ Payment methods for agent "${agent.name}":
|
|
|
689
491
|
const spinner = ora3("Removing payment method...").start();
|
|
690
492
|
const api = getApiClient();
|
|
691
493
|
await api.delete(`/payment-methods/${id}`);
|
|
692
|
-
spinner.succeed(
|
|
494
|
+
spinner.succeed(chalk4.green("Payment method removed."));
|
|
693
495
|
const agent = getActiveAgent();
|
|
694
496
|
if (agent.defaultPaymentMethodId === id) {
|
|
695
497
|
updateAgent(agent.name, { defaultPaymentMethodId: void 0 });
|
|
@@ -701,13 +503,13 @@ Payment methods for agent "${agent.name}":
|
|
|
701
503
|
payment.command("set-default <id>").description("Set the default payment method for the active agent").action((id) => {
|
|
702
504
|
const agent = getActiveAgent();
|
|
703
505
|
updateAgent(agent.name, { defaultPaymentMethodId: id });
|
|
704
|
-
console.log(
|
|
506
|
+
console.log(chalk4.green(`
|
|
705
507
|
\u2713 Default payment for agent "${agent.name}" set to ${id}.`));
|
|
706
508
|
});
|
|
707
509
|
}
|
|
708
510
|
|
|
709
511
|
// src/commands/search.ts
|
|
710
|
-
import
|
|
512
|
+
import chalk5 from "chalk";
|
|
711
513
|
import ora4 from "ora";
|
|
712
514
|
import inquirer4 from "inquirer";
|
|
713
515
|
function formatPrice(cents, currency) {
|
|
@@ -741,7 +543,7 @@ function convertPrice(cents, fromCurrency, toCurrency, rates) {
|
|
|
741
543
|
function formatConverted(cents, fromCurrency, toCurrency, rates) {
|
|
742
544
|
const converted = convertPrice(cents, fromCurrency, toCurrency, rates);
|
|
743
545
|
if (converted == null) return "";
|
|
744
|
-
return
|
|
546
|
+
return chalk5.dim(` (~${formatPrice(converted, toCurrency)})`);
|
|
745
547
|
}
|
|
746
548
|
function renderStars(rating) {
|
|
747
549
|
const full = Math.floor(rating);
|
|
@@ -772,13 +574,13 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
772
574
|
let line = "";
|
|
773
575
|
for (const word of words) {
|
|
774
576
|
if (line.length + word.length + 1 > 90) {
|
|
775
|
-
console.log(`${pad}${
|
|
577
|
+
console.log(`${pad}${chalk5.dim(line)}`);
|
|
776
578
|
line = word;
|
|
777
579
|
} else {
|
|
778
580
|
line = line ? `${line} ${word}` : word;
|
|
779
581
|
}
|
|
780
582
|
}
|
|
781
|
-
if (line) console.log(`${pad}${
|
|
583
|
+
if (line) console.log(`${pad}${chalk5.dim(line)}`);
|
|
782
584
|
} else {
|
|
783
585
|
console.log(`${pad}${data}`);
|
|
784
586
|
}
|
|
@@ -791,12 +593,12 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
791
593
|
if (Array.isArray(data)) {
|
|
792
594
|
for (const item of data) {
|
|
793
595
|
if (typeof item === "string") {
|
|
794
|
-
console.log(`${pad}${
|
|
596
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${item}`);
|
|
795
597
|
} else if (typeof item === "object" && item !== null) {
|
|
796
598
|
renderFreeFormInfo(item, indent + 2);
|
|
797
599
|
console.log();
|
|
798
600
|
} else {
|
|
799
|
-
console.log(`${pad}${
|
|
601
|
+
console.log(`${pad}${chalk5.dim("\u2022")} ${String(item)}`);
|
|
800
602
|
}
|
|
801
603
|
}
|
|
802
604
|
return;
|
|
@@ -808,20 +610,20 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
808
610
|
if (value == null) continue;
|
|
809
611
|
if (typeof value === "string") {
|
|
810
612
|
if (value.length > 80) {
|
|
811
|
-
console.log(`${pad}${
|
|
613
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
812
614
|
renderFreeFormInfo(value, indent + 4);
|
|
813
615
|
} else {
|
|
814
|
-
console.log(`${pad}${
|
|
616
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
815
617
|
}
|
|
816
618
|
} else if (typeof value === "number") {
|
|
817
|
-
console.log(`${pad}${
|
|
619
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value}`);
|
|
818
620
|
} else if (typeof value === "boolean") {
|
|
819
|
-
console.log(`${pad}${
|
|
621
|
+
console.log(`${pad}${chalk5.bold(label + ":")} ${value ? chalk5.green("Yes") : chalk5.red("No")}`);
|
|
820
622
|
} else if (Array.isArray(value)) {
|
|
821
|
-
console.log(`${pad}${
|
|
623
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
822
624
|
renderFreeFormInfo(value, indent + 4);
|
|
823
625
|
} else if (typeof value === "object") {
|
|
824
|
-
console.log(`${pad}${
|
|
626
|
+
console.log(`${pad}${chalk5.bold(label + ":")}`);
|
|
825
627
|
renderFreeFormInfo(value, indent + 4);
|
|
826
628
|
}
|
|
827
629
|
}
|
|
@@ -829,95 +631,95 @@ function renderFreeFormInfo(data, indent = 0) {
|
|
|
829
631
|
}
|
|
830
632
|
function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
831
633
|
const num = index + 1;
|
|
832
|
-
const storeBadge = result.storeName ?
|
|
634
|
+
const storeBadge = result.storeName ? chalk5.dim(` from ${result.storeName}`) : "";
|
|
833
635
|
console.log(
|
|
834
|
-
` ${
|
|
636
|
+
` ${chalk5.dim(`[${num}]`)} ${chalk5.bold.cyan(result.info?.title || result.info?.product_id || result.productId)}${storeBadge}`
|
|
835
637
|
);
|
|
836
|
-
console.log(` ${
|
|
638
|
+
console.log(` ${chalk5.dim(`ID: ${result.productId}`)}`);
|
|
837
639
|
if (result.error) {
|
|
838
|
-
console.log(` ${
|
|
640
|
+
console.log(` ${chalk5.red(`Error: ${result.error}`)}`);
|
|
839
641
|
console.log();
|
|
840
642
|
return;
|
|
841
643
|
}
|
|
842
644
|
if (result.info?.product_url) {
|
|
843
|
-
console.log(` ${
|
|
645
|
+
console.log(` ${chalk5.blue.underline(result.info.product_url)}`);
|
|
844
646
|
}
|
|
845
647
|
console.log();
|
|
846
648
|
const info = result.info || {};
|
|
847
649
|
if (info.price) {
|
|
848
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"}`;
|
|
849
|
-
let priceLine = ` ${
|
|
651
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
850
652
|
if (info.list_price?.amount) {
|
|
851
653
|
const listStr = formatPriceFn(
|
|
852
654
|
Math.round(parseFloat(info.list_price.amount) * 100),
|
|
853
655
|
info.list_price.currency || info.price.currency
|
|
854
656
|
);
|
|
855
|
-
priceLine +=
|
|
657
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
856
658
|
}
|
|
857
659
|
console.log(priceLine);
|
|
858
660
|
}
|
|
859
661
|
if (info.pricing && !info.price) {
|
|
860
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"}`;
|
|
861
|
-
let priceLine = ` ${
|
|
663
|
+
let priceLine = ` ${chalk5.bold("Price:")} ${chalk5.bold.white(priceStr)}`;
|
|
862
664
|
if (info.pricing.compare_at) {
|
|
863
665
|
const listStr = formatPriceFn(
|
|
864
666
|
Math.round(parseFloat(info.pricing.compare_at) * 100),
|
|
865
667
|
info.pricing.currency
|
|
866
668
|
);
|
|
867
|
-
priceLine +=
|
|
669
|
+
priceLine += chalk5.dim.strikethrough(` ${listStr}`);
|
|
868
670
|
}
|
|
869
671
|
console.log(priceLine);
|
|
870
672
|
}
|
|
871
673
|
if (info.rating) {
|
|
872
674
|
const ratingScore = typeof info.rating === "object" ? `${info.rating.score}/${info.rating.max}` : String(info.rating);
|
|
873
|
-
let ratingLine = ` ${
|
|
675
|
+
let ratingLine = ` ${chalk5.bold("Rating:")} ${chalk5.yellow(ratingScore)}`;
|
|
874
676
|
if (info.review_count) {
|
|
875
|
-
ratingLine +=
|
|
677
|
+
ratingLine += chalk5.dim(` (${info.review_count.toLocaleString()} reviews)`);
|
|
876
678
|
}
|
|
877
679
|
console.log(ratingLine);
|
|
878
680
|
}
|
|
879
681
|
if (info.brand) {
|
|
880
|
-
console.log(` ${
|
|
682
|
+
console.log(` ${chalk5.bold("Brand:")} ${info.brand}`);
|
|
881
683
|
}
|
|
882
684
|
if (info.marketplace) {
|
|
883
|
-
console.log(` ${
|
|
685
|
+
console.log(` ${chalk5.bold("Marketplace:")} ${info.marketplace.name || info.marketplace.domain || ""}`);
|
|
884
686
|
}
|
|
885
687
|
if (info.availability) {
|
|
886
688
|
if (typeof info.availability === "string") {
|
|
887
689
|
const isInStock = info.availability.toLowerCase().includes("in stock");
|
|
888
|
-
console.log(` ${
|
|
690
|
+
console.log(` ${chalk5.bold("Availability:")} ${isInStock ? chalk5.green(info.availability) : chalk5.yellow(info.availability)}`);
|
|
889
691
|
} else if (typeof info.availability === "object") {
|
|
890
|
-
const status = info.availability.in_stock ?
|
|
891
|
-
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}`;
|
|
892
694
|
if (info.availability.quantity != null) {
|
|
893
|
-
availLine +=
|
|
695
|
+
availLine += chalk5.dim(` (${info.availability.quantity} available)`);
|
|
894
696
|
}
|
|
895
697
|
console.log(availLine);
|
|
896
698
|
}
|
|
897
699
|
}
|
|
898
700
|
if (info.prime) {
|
|
899
|
-
console.log(` ${
|
|
701
|
+
console.log(` ${chalk5.bold("Prime:")} ${chalk5.blue("\u2713 Prime eligible")}`);
|
|
900
702
|
}
|
|
901
703
|
if (info.shipping && typeof info.shipping === "object") {
|
|
902
704
|
const parts = [];
|
|
903
|
-
if (info.shipping.free) parts.push(
|
|
705
|
+
if (info.shipping.free) parts.push(chalk5.green("Free Shipping"));
|
|
904
706
|
if (info.shipping.estimated_days) parts.push(`${info.shipping.estimated_days}-day delivery`);
|
|
905
707
|
if (info.shipping.price?.amount) parts.push(`${info.shipping.price.amount} ${info.shipping.price.currency || ""}`);
|
|
906
708
|
if (info.shipping.weight_kg) parts.push(`${info.shipping.weight_kg}kg`);
|
|
907
709
|
if (parts.length > 0) {
|
|
908
|
-
console.log(` ${
|
|
710
|
+
console.log(` ${chalk5.bold("Shipping:")} ${parts.join(" \xB7 ")}`);
|
|
909
711
|
}
|
|
910
712
|
}
|
|
911
713
|
if (info.delivery_info) {
|
|
912
|
-
console.log(` ${
|
|
714
|
+
console.log(` ${chalk5.bold("Delivery:")} ${info.delivery_info}`);
|
|
913
715
|
}
|
|
914
716
|
if (info.returns && typeof info.returns === "object") {
|
|
915
717
|
const parts = [];
|
|
916
|
-
if (info.returns.free) parts.push(
|
|
718
|
+
if (info.returns.free) parts.push(chalk5.green("Free Returns"));
|
|
917
719
|
if (info.returns.window_days) parts.push(`${info.returns.window_days}-day window`);
|
|
918
720
|
if (info.returns.note) parts.push(info.returns.note);
|
|
919
721
|
if (parts.length > 0) {
|
|
920
|
-
console.log(` ${
|
|
722
|
+
console.log(` ${chalk5.bold("Returns:")} ${parts.join(" \xB7 ")}`);
|
|
921
723
|
}
|
|
922
724
|
}
|
|
923
725
|
if (info.checkout && typeof info.checkout === "object") {
|
|
@@ -925,18 +727,18 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
925
727
|
if (info.checkout.mode) parts.push(info.checkout.mode);
|
|
926
728
|
if (info.checkout.note) parts.push(info.checkout.note);
|
|
927
729
|
if (parts.length > 0) {
|
|
928
|
-
console.log(` ${
|
|
730
|
+
console.log(` ${chalk5.bold("Checkout:")} ${parts.join(" \u2014 ")}`);
|
|
929
731
|
}
|
|
930
732
|
}
|
|
931
733
|
if (info.sold_by) {
|
|
932
|
-
console.log(` ${
|
|
734
|
+
console.log(` ${chalk5.bold("Sold by:")} ${info.sold_by}`);
|
|
933
735
|
}
|
|
934
736
|
if (info.categories && Array.isArray(info.categories)) {
|
|
935
|
-
console.log(` ${
|
|
737
|
+
console.log(` ${chalk5.bold("Category:")} ${info.categories.join(" > ")}`);
|
|
936
738
|
}
|
|
937
739
|
console.log();
|
|
938
740
|
if (info.features && Array.isArray(info.features) && info.features.length > 0) {
|
|
939
|
-
console.log(` ${
|
|
741
|
+
console.log(` ${chalk5.bold("Key Features:")}`);
|
|
940
742
|
for (const feature of info.features) {
|
|
941
743
|
if (feature.length > 80) {
|
|
942
744
|
const words = feature.split(/\s+/);
|
|
@@ -945,7 +747,7 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
945
747
|
for (const word of words) {
|
|
946
748
|
if (line.length + word.length + 1 > 76) {
|
|
947
749
|
if (first) {
|
|
948
|
-
console.log(` ${
|
|
750
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
949
751
|
first = false;
|
|
950
752
|
} else {
|
|
951
753
|
console.log(` ${line}`);
|
|
@@ -957,75 +759,75 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
957
759
|
}
|
|
958
760
|
if (line) {
|
|
959
761
|
if (first) {
|
|
960
|
-
console.log(` ${
|
|
762
|
+
console.log(` ${chalk5.dim("\u2022")} ${line}`);
|
|
961
763
|
} else {
|
|
962
764
|
console.log(` ${line}`);
|
|
963
765
|
}
|
|
964
766
|
}
|
|
965
767
|
} else {
|
|
966
|
-
console.log(` ${
|
|
768
|
+
console.log(` ${chalk5.dim("\u2022")} ${feature}`);
|
|
967
769
|
}
|
|
968
770
|
}
|
|
969
771
|
console.log();
|
|
970
772
|
}
|
|
971
773
|
if (info.description) {
|
|
972
|
-
console.log(` ${
|
|
774
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
973
775
|
const words = info.description.split(/\s+/);
|
|
974
776
|
let line = "";
|
|
975
777
|
for (const word of words) {
|
|
976
778
|
if (line.length + word.length + 1 > 76) {
|
|
977
|
-
console.log(` ${
|
|
779
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
978
780
|
line = word;
|
|
979
781
|
} else {
|
|
980
782
|
line = line ? `${line} ${word}` : word;
|
|
981
783
|
}
|
|
982
784
|
}
|
|
983
|
-
if (line) console.log(` ${
|
|
785
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
984
786
|
console.log();
|
|
985
787
|
}
|
|
986
788
|
if (info.specifications && typeof info.specifications === "object") {
|
|
987
789
|
const specs = info.specifications;
|
|
988
790
|
const keys = Object.keys(specs);
|
|
989
791
|
if (keys.length > 0) {
|
|
990
|
-
console.log(` ${
|
|
792
|
+
console.log(` ${chalk5.bold("Specifications:")}`);
|
|
991
793
|
const maxKeyLen = Math.min(30, Math.max(...keys.map((k) => k.length)));
|
|
992
794
|
for (const [key, value] of Object.entries(specs)) {
|
|
993
795
|
const paddedKey = key.padEnd(maxKeyLen);
|
|
994
|
-
console.log(` ${
|
|
796
|
+
console.log(` ${chalk5.dim(paddedKey)} ${value}`);
|
|
995
797
|
}
|
|
996
798
|
console.log();
|
|
997
799
|
}
|
|
998
800
|
}
|
|
999
801
|
if (info.images && Array.isArray(info.images) && info.images.length > 0) {
|
|
1000
|
-
console.log(` ${
|
|
802
|
+
console.log(` ${chalk5.bold("Images:")} ${chalk5.dim(`${info.images.length} available`)}`);
|
|
1001
803
|
for (let j = 0; j < Math.min(3, info.images.length); j++) {
|
|
1002
|
-
console.log(` ${
|
|
804
|
+
console.log(` ${chalk5.dim(`[${j + 1}]`)} ${chalk5.blue.underline(info.images[j])}`);
|
|
1003
805
|
}
|
|
1004
806
|
if (info.images.length > 3) {
|
|
1005
|
-
console.log(` ${
|
|
807
|
+
console.log(` ${chalk5.dim(`... and ${info.images.length - 3} more`)}`);
|
|
1006
808
|
}
|
|
1007
809
|
console.log();
|
|
1008
810
|
}
|
|
1009
811
|
if (info.about && Array.isArray(info.about) && info.about.length > 0) {
|
|
1010
|
-
console.log(` ${
|
|
812
|
+
console.log(` ${chalk5.bold("About This Item:")}`);
|
|
1011
813
|
for (const section of info.about) {
|
|
1012
814
|
const words = section.split(/\s+/);
|
|
1013
815
|
let line = "";
|
|
1014
816
|
for (const word of words) {
|
|
1015
817
|
if (line.length + word.length + 1 > 76) {
|
|
1016
|
-
console.log(` ${
|
|
818
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1017
819
|
line = word;
|
|
1018
820
|
} else {
|
|
1019
821
|
line = line ? `${line} ${word}` : word;
|
|
1020
822
|
}
|
|
1021
823
|
}
|
|
1022
|
-
if (line) console.log(` ${
|
|
824
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1023
825
|
console.log();
|
|
1024
826
|
}
|
|
1025
827
|
}
|
|
1026
828
|
if (info.seo && typeof info.seo === "object" && Object.keys(info.seo).length > 0) {
|
|
1027
829
|
if (info.seo.title || info.seo.description) {
|
|
1028
|
-
console.log(` ${
|
|
830
|
+
console.log(` ${chalk5.bold("SEO:")}`);
|
|
1029
831
|
if (info.seo.title) console.log(` Title: ${info.seo.title}`);
|
|
1030
832
|
if (info.seo.description) console.log(` Description: ${info.seo.description}`);
|
|
1031
833
|
console.log();
|
|
@@ -1069,33 +871,33 @@ function renderProductInfo(result, index, totalResults, formatPriceFn) {
|
|
|
1069
871
|
]);
|
|
1070
872
|
const extraKeys = Object.keys(info).filter((k) => !handledKeys.has(k));
|
|
1071
873
|
if (extraKeys.length > 0) {
|
|
1072
|
-
console.log(` ${
|
|
874
|
+
console.log(` ${chalk5.bold("Additional Information:")}`);
|
|
1073
875
|
for (const key of extraKeys) {
|
|
1074
876
|
const value = info[key];
|
|
1075
877
|
if (value == null) continue;
|
|
1076
878
|
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
1077
879
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1078
|
-
console.log(` ${
|
|
880
|
+
console.log(` ${chalk5.dim(label + ":")} ${value}`);
|
|
1079
881
|
} else if (Array.isArray(value)) {
|
|
1080
|
-
console.log(` ${
|
|
882
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1081
883
|
renderFreeFormInfo(value, 10);
|
|
1082
884
|
} else if (typeof value === "object") {
|
|
1083
|
-
console.log(` ${
|
|
885
|
+
console.log(` ${chalk5.dim(label + ":")}`);
|
|
1084
886
|
renderFreeFormInfo(value, 10);
|
|
1085
887
|
}
|
|
1086
888
|
}
|
|
1087
889
|
console.log();
|
|
1088
890
|
}
|
|
1089
891
|
if (info.tags && Array.isArray(info.tags) && info.tags.length > 0) {
|
|
1090
|
-
console.log(` ${
|
|
892
|
+
console.log(` ${chalk5.bold("Tags:")} ${info.tags.map((t) => chalk5.dim(`#${t}`)).join(" ")}`);
|
|
1091
893
|
console.log();
|
|
1092
894
|
}
|
|
1093
895
|
if (info.note) {
|
|
1094
|
-
console.log(` ${
|
|
896
|
+
console.log(` ${chalk5.dim(`\u2139 ${info.note}`)}`);
|
|
1095
897
|
console.log();
|
|
1096
898
|
}
|
|
1097
899
|
if (index < totalResults - 1) {
|
|
1098
|
-
console.log(
|
|
900
|
+
console.log(chalk5.dim(" " + "\u2500".repeat(60)));
|
|
1099
901
|
console.log();
|
|
1100
902
|
}
|
|
1101
903
|
}
|
|
@@ -1250,21 +1052,21 @@ function registerSearchCommands(program2) {
|
|
|
1250
1052
|
const didExtendedSearch = forceExtended || res.data.extended != null;
|
|
1251
1053
|
if (result.products.length === 0 && (!extended || extended.total === 0)) {
|
|
1252
1054
|
if (didExtendedSearch) {
|
|
1253
|
-
console.log(
|
|
1055
|
+
console.log(chalk5.yellow(`
|
|
1254
1056
|
No results found for "${query}" (searched local catalog + all vendor stores).`));
|
|
1255
1057
|
} else {
|
|
1256
|
-
console.log(
|
|
1058
|
+
console.log(chalk5.yellow(`
|
|
1257
1059
|
No results found for "${query}".`));
|
|
1258
1060
|
}
|
|
1259
1061
|
if (!didExtendedSearch && disableExtended) {
|
|
1260
1062
|
console.log(
|
|
1261
|
-
|
|
1262
|
-
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")
|
|
1263
1065
|
);
|
|
1264
1066
|
}
|
|
1265
1067
|
console.log(
|
|
1266
|
-
|
|
1267
|
-
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")
|
|
1268
1070
|
);
|
|
1269
1071
|
return;
|
|
1270
1072
|
}
|
|
@@ -1364,15 +1166,15 @@ No results found for "${query}".`));
|
|
|
1364
1166
|
} else {
|
|
1365
1167
|
const totalCount = result.total + (extended?.total || 0);
|
|
1366
1168
|
if (result.products.length > 0 && extended?.total > 0) {
|
|
1367
|
-
console.log(
|
|
1169
|
+
console.log(chalk5.bold(`
|
|
1368
1170
|
Results for "${query}" \u2014 ${result.total} local + ${extended.total} from stores
|
|
1369
1171
|
`));
|
|
1370
1172
|
} else if (extended?.total > 0) {
|
|
1371
|
-
console.log(
|
|
1173
|
+
console.log(chalk5.bold(`
|
|
1372
1174
|
Extended search for "${query}" \u2014 ${extended.total} result(s) from ${extended.storesResponded} store(s)
|
|
1373
1175
|
`));
|
|
1374
1176
|
} else {
|
|
1375
|
-
console.log(
|
|
1177
|
+
console.log(chalk5.bold(`
|
|
1376
1178
|
Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
1377
1179
|
`));
|
|
1378
1180
|
}
|
|
@@ -1385,20 +1187,20 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1385
1187
|
const fastest = allProducts.filter((p) => p.shippingDays != null).sort((a, b) => (a.shippingDays ?? 99) - (b.shippingDays ?? 99))[0];
|
|
1386
1188
|
const bestRated = allProducts.filter((p) => (p.rating ?? 0) > 0).sort((a, b) => (b.rating ?? 0) - (a.rating ?? 0))[0];
|
|
1387
1189
|
const parts = [];
|
|
1388
|
-
parts.push(`${
|
|
1190
|
+
parts.push(`${chalk5.green("Best price:")} ${formatPrice(cheapest.totalCost, cheapest.currency)} at ${cheapest.vendor}`);
|
|
1389
1191
|
if (fastest?.shippingDays != null) {
|
|
1390
|
-
parts.push(`${
|
|
1192
|
+
parts.push(`${chalk5.blue("Fastest:")} ${deliveryLabel(fastest.shippingDays)} at ${fastest.vendor}`);
|
|
1391
1193
|
}
|
|
1392
1194
|
if (bestRated?.rating) {
|
|
1393
|
-
parts.push(`${
|
|
1195
|
+
parts.push(`${chalk5.yellow("Top rated:")} ${scoreOutOf10(bestRated.rating)}/10 at ${bestRated.vendor}`);
|
|
1394
1196
|
}
|
|
1395
|
-
console.log(` ${
|
|
1197
|
+
console.log(` ${chalk5.dim("\u250C")} ${parts.join(chalk5.dim(" \u2502 "))}`);
|
|
1396
1198
|
const prices = withTotal.map((p) => p.totalCost);
|
|
1397
1199
|
const minP = Math.min(...prices);
|
|
1398
1200
|
const maxP = Math.max(...prices);
|
|
1399
1201
|
const avgP = Math.round(prices.reduce((a, b) => a + b, 0) / prices.length);
|
|
1400
1202
|
const curr = allProducts[0].currency;
|
|
1401
|
-
console.log(` ${
|
|
1203
|
+
console.log(` ${chalk5.dim("\u2514")} ${chalk5.dim(`Price range: ${formatPrice(minP, curr)} \u2013 ${formatPrice(maxP, curr)} \xB7 Average: ${formatPrice(avgP, curr)}`)}`);
|
|
1402
1204
|
console.log();
|
|
1403
1205
|
}
|
|
1404
1206
|
for (let i = 0; i < allProducts.length; i++) {
|
|
@@ -1408,53 +1210,53 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1408
1210
|
const shippingPrice = p.freeShipping ? 0 : p.shippingPriceInCents ?? 0;
|
|
1409
1211
|
const totalCost = itemPrice + shippingPrice;
|
|
1410
1212
|
const badges = [];
|
|
1411
|
-
if (i === 0) badges.push(
|
|
1213
|
+
if (i === 0) badges.push(chalk5.bgGreen.black(" BEST MATCH "));
|
|
1412
1214
|
const allCosts = allProducts.map((x) => x.priceInCents + (x.freeShipping ? 0 : x.shippingPriceInCents ?? 0));
|
|
1413
|
-
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(
|
|
1215
|
+
if (totalCost === Math.min(...allCosts) && i !== 0) badges.push(chalk5.bgYellow.black(" BEST VALUE "));
|
|
1414
1216
|
const converted = formatConverted(totalCost, p.currency, userCurrency, exchangeRates);
|
|
1415
1217
|
if (opts.compact) {
|
|
1416
1218
|
const priceStr = formatPrice(totalCost, p.currency) + converted;
|
|
1417
1219
|
const store = p.vendor;
|
|
1418
1220
|
const delivery = p.shippingDays != null ? `Arrives ${estimatedArrival(p.shippingDays)}` : "";
|
|
1419
1221
|
console.log(
|
|
1420
|
-
` ${
|
|
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(" ") : "")
|
|
1421
1223
|
);
|
|
1422
1224
|
continue;
|
|
1423
1225
|
}
|
|
1424
|
-
console.log(` ${
|
|
1425
|
-
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))}`;
|
|
1426
1228
|
if (p.freeShipping) {
|
|
1427
|
-
priceLine +=
|
|
1229
|
+
priceLine += chalk5.green(" Free Shipping");
|
|
1428
1230
|
} else if (p.shippingPriceInCents != null && (p.shippingPriceInCents ?? 0) > 0) {
|
|
1429
|
-
priceLine +=
|
|
1430
|
-
priceLine +=
|
|
1231
|
+
priceLine += chalk5.dim(` + ${formatPrice(shippingPrice, p.currency)} shipping`);
|
|
1232
|
+
priceLine += chalk5.bold(` = ${formatPrice(totalCost, p.currency)}`);
|
|
1431
1233
|
}
|
|
1432
1234
|
priceLine += converted;
|
|
1433
1235
|
if (p.shippingDays != null) {
|
|
1434
|
-
priceLine +=
|
|
1236
|
+
priceLine += chalk5.blue(` Arrives ${estimatedArrival(p.shippingDays)}`);
|
|
1435
1237
|
}
|
|
1436
1238
|
console.log(priceLine);
|
|
1437
1239
|
const meta = [];
|
|
1438
|
-
const storeBadge = p.storeVerified ?
|
|
1439
|
-
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)");
|
|
1440
1242
|
meta.push(`${p.vendor}${storeBadge}${storeScore}`);
|
|
1441
1243
|
if (p.brand) meta.push(p.brand);
|
|
1442
1244
|
if (p.rating && p.rating > 0) {
|
|
1443
|
-
meta.push(
|
|
1245
|
+
meta.push(chalk5.yellow(`${scoreOutOf10(p.rating)}/10`) + (p.reviewCount ? chalk5.dim(` (${p.reviewCount})`) : ""));
|
|
1444
1246
|
}
|
|
1445
|
-
if (p.isExtended) meta.push(
|
|
1446
|
-
console.log(` ${
|
|
1447
|
-
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}`)}`);
|
|
1448
1250
|
;
|
|
1449
1251
|
if (opts.detailed) {
|
|
1450
|
-
if (p.category) console.log(` ${
|
|
1451
|
-
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}`)}`);
|
|
1452
1254
|
if (p.description) {
|
|
1453
|
-
console.log(` ${
|
|
1255
|
+
console.log(` ${chalk5.dim(p.description.length > 200 ? p.description.slice(0, 200) + "..." : p.description)}`);
|
|
1454
1256
|
}
|
|
1455
1257
|
} else {
|
|
1456
1258
|
if (p.description) {
|
|
1457
|
-
console.log(` ${
|
|
1259
|
+
console.log(` ${chalk5.dim(p.description.length > 80 ? p.description.slice(0, 80) + "..." : p.description)}`);
|
|
1458
1260
|
}
|
|
1459
1261
|
}
|
|
1460
1262
|
console.log();
|
|
@@ -1464,15 +1266,15 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1464
1266
|
if (opts.interactive && allProducts.length > 0) {
|
|
1465
1267
|
const selectableProducts = allProducts.map((p, idx) => ({ product: p, index: idx })).filter((item) => item.product.id);
|
|
1466
1268
|
if (selectableProducts.length > 0) {
|
|
1467
|
-
console.log(
|
|
1269
|
+
console.log(chalk5.dim(" \u2500".repeat(30)));
|
|
1468
1270
|
console.log();
|
|
1469
1271
|
const choices = selectableProducts.map((item) => {
|
|
1470
1272
|
const p = item.product;
|
|
1471
1273
|
const priceStr = formatPrice(p.priceInCents, p.currency);
|
|
1472
1274
|
const storeStr = p.vendor || "Unknown";
|
|
1473
|
-
const extLabel = p.isExtended ?
|
|
1275
|
+
const extLabel = p.isExtended ? chalk5.magenta(" [store]") : chalk5.dim(" [local]");
|
|
1474
1276
|
return {
|
|
1475
|
-
name: `${
|
|
1277
|
+
name: `${chalk5.cyan(p.name.length > 50 ? p.name.slice(0, 47) + "..." : p.name)} ${chalk5.bold(priceStr)} ${chalk5.dim(storeStr)}${extLabel}`,
|
|
1476
1278
|
value: item.product.id,
|
|
1477
1279
|
short: p.name.length > 40 ? p.name.slice(0, 37) + "..." : p.name
|
|
1478
1280
|
};
|
|
@@ -1504,7 +1306,7 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1504
1306
|
infoSpinner.stop();
|
|
1505
1307
|
const { results: infoResults } = infoRes.data;
|
|
1506
1308
|
if (infoResults && infoResults.length > 0) {
|
|
1507
|
-
console.log(
|
|
1309
|
+
console.log(chalk5.bold(`
|
|
1508
1310
|
Store Information \u2014 ${infoResults.length} result(s)
|
|
1509
1311
|
`));
|
|
1510
1312
|
for (let i = 0; i < infoResults.length; i++) {
|
|
@@ -1512,11 +1314,11 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1512
1314
|
renderProductInfo(infoResult, i, infoResults.length, formatPrice);
|
|
1513
1315
|
}
|
|
1514
1316
|
} else {
|
|
1515
|
-
console.log(
|
|
1317
|
+
console.log(chalk5.yellow("\n No additional information returned from the stores."));
|
|
1516
1318
|
}
|
|
1517
1319
|
} catch (infoErr) {
|
|
1518
1320
|
infoSpinner.stop();
|
|
1519
|
-
console.error(
|
|
1321
|
+
console.error(chalk5.red("\n Failed to fetch product info from stores."));
|
|
1520
1322
|
}
|
|
1521
1323
|
}
|
|
1522
1324
|
if (localIds.length > 0) {
|
|
@@ -1528,31 +1330,31 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1528
1330
|
const p = detailRes.data.product;
|
|
1529
1331
|
if (p) {
|
|
1530
1332
|
console.log();
|
|
1531
|
-
console.log(` ${
|
|
1532
|
-
console.log(` ${
|
|
1533
|
-
if (p.brand) console.log(` ${
|
|
1534
|
-
if (p.model) console.log(` ${
|
|
1535
|
-
if (p.sku) console.log(` ${
|
|
1536
|
-
console.log(` ${
|
|
1537
|
-
const status = p.inStock ?
|
|
1538
|
-
console.log(` ${
|
|
1539
|
-
if (p.freeShipping) console.log(` ${
|
|
1540
|
-
else if (p.shippingPriceInCents != null) console.log(` ${
|
|
1541
|
-
if (p.shippingDays != null) console.log(` ${
|
|
1542
|
-
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` : ""}`);
|
|
1543
1345
|
if (p.description) {
|
|
1544
|
-
console.log(` ${
|
|
1346
|
+
console.log(` ${chalk5.bold("Description:")}`);
|
|
1545
1347
|
const words = p.description.split(/\s+/);
|
|
1546
1348
|
let line = "";
|
|
1547
1349
|
for (const word of words) {
|
|
1548
1350
|
if (line.length + word.length + 1 > 76) {
|
|
1549
|
-
console.log(` ${
|
|
1351
|
+
console.log(` ${chalk5.dim(line)}`);
|
|
1550
1352
|
line = word;
|
|
1551
1353
|
} else {
|
|
1552
1354
|
line = line ? `${line} ${word}` : word;
|
|
1553
1355
|
}
|
|
1554
1356
|
}
|
|
1555
|
-
if (line) console.log(` ${
|
|
1357
|
+
if (line) console.log(` ${chalk5.dim(line)}`);
|
|
1556
1358
|
}
|
|
1557
1359
|
console.log();
|
|
1558
1360
|
}
|
|
@@ -1567,26 +1369,26 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1567
1369
|
} else if (hasExtendedProducts && !opts.interactive) {
|
|
1568
1370
|
const sampleIds = extended.products.slice(0, 2).map((ep) => ep.id).join(" ");
|
|
1569
1371
|
console.log(
|
|
1570
|
-
|
|
1571
|
-
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")
|
|
1572
1374
|
);
|
|
1573
1375
|
}
|
|
1574
1376
|
const totalPages = Math.ceil(result.total / result.pageSize);
|
|
1575
1377
|
if (totalPages > 1) {
|
|
1576
|
-
console.log(
|
|
1378
|
+
console.log(chalk5.dim(` Page ${result.page} of ${totalPages}. Use --page to navigate.
|
|
1577
1379
|
`));
|
|
1578
1380
|
}
|
|
1579
1381
|
const showAdvertiseTip = result.page >= totalPages || suggestAdvertise;
|
|
1580
1382
|
if (showAdvertiseTip) {
|
|
1581
1383
|
if (!didExtendedSearch && disableExtended && result.products.length > 0) {
|
|
1582
1384
|
console.log(
|
|
1583
|
-
|
|
1584
|
-
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")
|
|
1585
1387
|
);
|
|
1586
1388
|
}
|
|
1587
1389
|
console.log(
|
|
1588
|
-
|
|
1589
|
-
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")
|
|
1590
1392
|
);
|
|
1591
1393
|
}
|
|
1592
1394
|
} catch (error) {
|
|
@@ -1604,38 +1406,38 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1604
1406
|
console.log(JSON.stringify(p, null, 2));
|
|
1605
1407
|
return;
|
|
1606
1408
|
}
|
|
1607
|
-
const storeBadge = p.storeVerified ?
|
|
1409
|
+
const storeBadge = p.storeVerified ? chalk5.green(" \u2713 Verified") : "";
|
|
1608
1410
|
console.log();
|
|
1609
|
-
console.log(
|
|
1610
|
-
console.log(
|
|
1611
|
-
if (p.brand) console.log(
|
|
1612
|
-
if (p.model) console.log(
|
|
1613
|
-
if (p.variant) console.log(
|
|
1614
|
-
if (p.sku) console.log(
|
|
1615
|
-
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}`));
|
|
1616
1418
|
console.log();
|
|
1617
|
-
console.log(` Price: ${
|
|
1419
|
+
console.log(` Price: ${chalk5.bold(formatPrice(p.priceInCents, p.currency))}`);
|
|
1618
1420
|
if (p.freeShipping) {
|
|
1619
|
-
console.log(` Shipping: ${
|
|
1421
|
+
console.log(` Shipping: ${chalk5.green("Free")}`);
|
|
1620
1422
|
} else if (p.shippingPriceInCents != null) {
|
|
1621
1423
|
console.log(` Shipping: ${formatPrice(p.shippingPriceInCents, p.currency)}`);
|
|
1622
1424
|
}
|
|
1623
1425
|
if (p.shippingPriceInCents != null || p.freeShipping) {
|
|
1624
1426
|
const total = p.priceInCents + (p.freeShipping ? 0 : p.shippingPriceInCents ?? 0);
|
|
1625
|
-
console.log(` Total: ${
|
|
1427
|
+
console.log(` Total: ${chalk5.bold(formatPrice(total, p.currency))}`);
|
|
1626
1428
|
}
|
|
1627
|
-
const status = p.inStock ?
|
|
1628
|
-
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)`) : ""}`);
|
|
1629
1431
|
if (p.shippingDays != null) console.log(` Delivery: ${deliveryLabel(p.shippingDays)}`);
|
|
1630
|
-
console.log(` Rating: ${
|
|
1432
|
+
console.log(` Rating: ${chalk5.yellow(renderStars(p.rating))} ${chalk5.dim(`(${p.reviewCount} reviews)`)}`);
|
|
1631
1433
|
console.log(` Category: ${p.category}`);
|
|
1632
|
-
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)`) : ""}`);
|
|
1633
1435
|
const returnParts = [];
|
|
1634
|
-
if (p.freeReturns) returnParts.push(
|
|
1436
|
+
if (p.freeReturns) returnParts.push(chalk5.green("Free Returns"));
|
|
1635
1437
|
if (p.returnWindowDays) returnParts.push(`${p.returnWindowDays}-day return window`);
|
|
1636
1438
|
if (returnParts.length) console.log(` Returns: ${returnParts.join(" \xB7 ")}`);
|
|
1637
1439
|
if (p.checkoutMode && p.checkoutMode !== "instant") {
|
|
1638
|
-
console.log(` Checkout: ${
|
|
1440
|
+
console.log(` Checkout: ${chalk5.yellow(p.checkoutMode)}`);
|
|
1639
1441
|
}
|
|
1640
1442
|
console.log();
|
|
1641
1443
|
console.log(` ${p.description}`);
|
|
@@ -1647,13 +1449,13 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1647
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) => {
|
|
1648
1450
|
try {
|
|
1649
1451
|
if (ids.length === 0) {
|
|
1650
|
-
console.error(
|
|
1651
|
-
console.log(
|
|
1652
|
-
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"));
|
|
1653
1455
|
process.exit(1);
|
|
1654
1456
|
}
|
|
1655
1457
|
if (ids.length > 20) {
|
|
1656
|
-
console.error(
|
|
1458
|
+
console.error(chalk5.red("\n\u2717 Maximum 20 products per request."));
|
|
1657
1459
|
process.exit(1);
|
|
1658
1460
|
}
|
|
1659
1461
|
const spinner = ora4(`Requesting detailed info for ${ids.length} product(s)...`).start();
|
|
@@ -1670,12 +1472,12 @@ Results for "${query}" \u2014 ${result.total} found (page ${result.page})
|
|
|
1670
1472
|
return;
|
|
1671
1473
|
}
|
|
1672
1474
|
if (!results || results.length === 0) {
|
|
1673
|
-
console.log(
|
|
1674
|
-
console.log(
|
|
1675
|
-
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"));
|
|
1676
1478
|
return;
|
|
1677
1479
|
}
|
|
1678
|
-
console.log(
|
|
1480
|
+
console.log(chalk5.bold(`
|
|
1679
1481
|
Product Information \u2014 ${total} result(s)
|
|
1680
1482
|
`));
|
|
1681
1483
|
for (let i = 0; i < results.length; i++) {
|
|
@@ -1688,19 +1490,19 @@ Product Information \u2014 ${total} result(s)
|
|
|
1688
1490
|
}
|
|
1689
1491
|
|
|
1690
1492
|
// src/commands/order.ts
|
|
1691
|
-
import
|
|
1493
|
+
import chalk6 from "chalk";
|
|
1692
1494
|
import ora5 from "ora";
|
|
1693
1495
|
import inquirer5 from "inquirer";
|
|
1694
1496
|
function formatPrice2(cents, currency) {
|
|
1695
1497
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
1696
1498
|
}
|
|
1697
1499
|
var STATUS_COLORS = {
|
|
1698
|
-
pending:
|
|
1699
|
-
confirmed:
|
|
1700
|
-
processing:
|
|
1701
|
-
shipped:
|
|
1702
|
-
delivered:
|
|
1703
|
-
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
|
|
1704
1506
|
};
|
|
1705
1507
|
function registerOrderCommands(program2) {
|
|
1706
1508
|
const order = program2.command("order").description("Place and manage orders");
|
|
@@ -1710,12 +1512,12 @@ function registerOrderCommands(program2) {
|
|
|
1710
1512
|
const addressId = opts.address || agent.defaultAddressId;
|
|
1711
1513
|
const paymentId = opts.payment || agent.defaultPaymentMethodId;
|
|
1712
1514
|
if (!addressId) {
|
|
1713
|
-
console.error(
|
|
1515
|
+
console.error(chalk6.red("\n\u2717 No address set. Add one with: clishop address add"));
|
|
1714
1516
|
process.exitCode = 1;
|
|
1715
1517
|
return;
|
|
1716
1518
|
}
|
|
1717
1519
|
if (!paymentId) {
|
|
1718
|
-
console.error(
|
|
1520
|
+
console.error(chalk6.red("\n\u2717 No payment method set. Add one with: clishop payment add"));
|
|
1719
1521
|
process.exitCode = 1;
|
|
1720
1522
|
return;
|
|
1721
1523
|
}
|
|
@@ -1734,7 +1536,7 @@ function registerOrderCommands(program2) {
|
|
|
1734
1536
|
isExtended = true;
|
|
1735
1537
|
} catch {
|
|
1736
1538
|
prodSpinner.stop();
|
|
1737
|
-
console.error(
|
|
1539
|
+
console.error(chalk6.red(`
|
|
1738
1540
|
\u2717 Product ${productId} not found.`));
|
|
1739
1541
|
process.exitCode = 1;
|
|
1740
1542
|
return;
|
|
@@ -1748,7 +1550,7 @@ function registerOrderCommands(program2) {
|
|
|
1748
1550
|
const totalCents = product.priceInCents * opts.quantity;
|
|
1749
1551
|
if (agent.maxOrderAmount && totalCents / 100 > agent.maxOrderAmount) {
|
|
1750
1552
|
console.error(
|
|
1751
|
-
|
|
1553
|
+
chalk6.red(
|
|
1752
1554
|
`
|
|
1753
1555
|
\u2717 Order total (${formatPrice2(totalCents, product.currency)}) exceeds agent "${agent.name}" limit of $${agent.maxOrderAmount}.`
|
|
1754
1556
|
)
|
|
@@ -1757,23 +1559,23 @@ function registerOrderCommands(program2) {
|
|
|
1757
1559
|
return;
|
|
1758
1560
|
}
|
|
1759
1561
|
if (agent.blockedCategories?.includes(product.category)) {
|
|
1760
|
-
console.error(
|
|
1562
|
+
console.error(chalk6.red(`
|
|
1761
1563
|
\u2717 Category "${product.category}" is blocked for agent "${agent.name}".`));
|
|
1762
1564
|
process.exitCode = 1;
|
|
1763
1565
|
return;
|
|
1764
1566
|
}
|
|
1765
1567
|
if (agent.allowedCategories?.length && !agent.allowedCategories.includes(product.category)) {
|
|
1766
|
-
console.error(
|
|
1568
|
+
console.error(chalk6.red(`
|
|
1767
1569
|
\u2717 Category "${product.category}" is not in the allowed list for agent "${agent.name}".`));
|
|
1768
1570
|
process.exitCode = 1;
|
|
1769
1571
|
return;
|
|
1770
1572
|
}
|
|
1771
1573
|
if (agent.requireConfirmation && !opts.yes) {
|
|
1772
|
-
console.log(
|
|
1574
|
+
console.log(chalk6.bold("\n Order Summary:"));
|
|
1773
1575
|
console.log(` Product: ${product.name}`);
|
|
1774
1576
|
console.log(` Store: ${product.vendor || product.storeName || "\u2014"}`);
|
|
1775
1577
|
console.log(` Quantity: ${opts.quantity}`);
|
|
1776
|
-
console.log(` Total: ${
|
|
1578
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(totalCents, product.currency))}`);
|
|
1777
1579
|
console.log(` Agent: ${agent.name}`);
|
|
1778
1580
|
console.log();
|
|
1779
1581
|
const { confirm } = await inquirer5.prompt([
|
|
@@ -1785,7 +1587,7 @@ function registerOrderCommands(program2) {
|
|
|
1785
1587
|
}
|
|
1786
1588
|
]);
|
|
1787
1589
|
if (!confirm) {
|
|
1788
|
-
console.log(
|
|
1590
|
+
console.log(chalk6.yellow("Order cancelled."));
|
|
1789
1591
|
return;
|
|
1790
1592
|
}
|
|
1791
1593
|
}
|
|
@@ -1796,7 +1598,7 @@ function registerOrderCommands(program2) {
|
|
|
1796
1598
|
shippingAddressId: addressId,
|
|
1797
1599
|
paymentMethodId: paymentId
|
|
1798
1600
|
});
|
|
1799
|
-
spinner.succeed(
|
|
1601
|
+
spinner.succeed(chalk6.green(`Order placed! Order ID: ${chalk6.bold(res.data.order.id)}`));
|
|
1800
1602
|
} catch (error) {
|
|
1801
1603
|
handleApiError(error);
|
|
1802
1604
|
}
|
|
@@ -1818,18 +1620,18 @@ function registerOrderCommands(program2) {
|
|
|
1818
1620
|
return;
|
|
1819
1621
|
}
|
|
1820
1622
|
if (orders.length === 0) {
|
|
1821
|
-
console.log(
|
|
1623
|
+
console.log(chalk6.yellow("\nNo orders found.\n"));
|
|
1822
1624
|
return;
|
|
1823
1625
|
}
|
|
1824
|
-
console.log(
|
|
1626
|
+
console.log(chalk6.bold("\nYour Orders:\n"));
|
|
1825
1627
|
for (const o of orders) {
|
|
1826
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1628
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1827
1629
|
const date = new Date(o.createdAt).toLocaleDateString();
|
|
1828
1630
|
console.log(
|
|
1829
|
-
` ${
|
|
1631
|
+
` ${chalk6.bold(o.id)} ${statusColor(o.status.toUpperCase().padEnd(12))} ${formatPrice2(o.totalAmountInCents, o.currency)} ${chalk6.dim(date)} ${chalk6.dim(`agent: ${o.agent}`)}`
|
|
1830
1632
|
);
|
|
1831
1633
|
for (const item of o.items) {
|
|
1832
|
-
console.log(
|
|
1634
|
+
console.log(chalk6.dim(` \xB7 ${item.productName} \xD7 ${item.quantity}`));
|
|
1833
1635
|
}
|
|
1834
1636
|
console.log();
|
|
1835
1637
|
}
|
|
@@ -1848,26 +1650,26 @@ function registerOrderCommands(program2) {
|
|
|
1848
1650
|
console.log(JSON.stringify(o, null, 2));
|
|
1849
1651
|
return;
|
|
1850
1652
|
}
|
|
1851
|
-
const statusColor = STATUS_COLORS[o.status] ||
|
|
1653
|
+
const statusColor = STATUS_COLORS[o.status] || chalk6.white;
|
|
1852
1654
|
console.log();
|
|
1853
|
-
console.log(
|
|
1655
|
+
console.log(chalk6.bold(` Order ${o.id}`));
|
|
1854
1656
|
console.log(` Status: ${statusColor(o.status.toUpperCase())}`);
|
|
1855
|
-
console.log(` Total: ${
|
|
1657
|
+
console.log(` Total: ${chalk6.bold(formatPrice2(o.totalAmountInCents, o.currency))}`);
|
|
1856
1658
|
if (o.storeName) console.log(` Store: ${o.storeName}`);
|
|
1857
1659
|
console.log(` Agent: ${o.agent}`);
|
|
1858
1660
|
if (o.paymentLabel) console.log(` Payment: ${o.paymentLabel}`);
|
|
1859
1661
|
console.log(` Placed: ${new Date(o.createdAt).toLocaleString()}`);
|
|
1860
1662
|
console.log(` Updated: ${new Date(o.updatedAt).toLocaleString()}`);
|
|
1861
1663
|
if (o.externalOrderId) {
|
|
1862
|
-
console.log(` eBay Ref: ${
|
|
1664
|
+
console.log(` eBay Ref: ${chalk6.dim(o.externalOrderId)}`);
|
|
1863
1665
|
}
|
|
1864
1666
|
if (o.shipments?.length) {
|
|
1865
1667
|
for (const s of o.shipments) {
|
|
1866
1668
|
console.log(` Tracking: ${s.trackingNumber || "pending"} ${s.carrier ? `(${s.carrier})` : ""}`);
|
|
1867
|
-
if (s.trackingUrl) console.log(` Track: ${
|
|
1669
|
+
if (s.trackingUrl) console.log(` Track: ${chalk6.cyan.underline(s.trackingUrl)}`);
|
|
1868
1670
|
}
|
|
1869
1671
|
}
|
|
1870
|
-
console.log(
|
|
1672
|
+
console.log(chalk6.bold("\n Items:"));
|
|
1871
1673
|
for (const item of o.items) {
|
|
1872
1674
|
console.log(` \xB7 ${item.productName} \xD7 ${item.quantity} ${formatPrice2(item.totalPriceInCents, o.currency)}`);
|
|
1873
1675
|
}
|
|
@@ -1885,14 +1687,14 @@ function registerOrderCommands(program2) {
|
|
|
1885
1687
|
FULFILLED: "Fulfilled",
|
|
1886
1688
|
CANCELLED: "Cancelled"
|
|
1887
1689
|
};
|
|
1888
|
-
console.log(
|
|
1889
|
-
console.log(` ${
|
|
1690
|
+
console.log(chalk6.bold("\n eBay Status:"));
|
|
1691
|
+
console.log(` ${chalk6.cyan(STATUS_MAP[ebayStatus] || ebayStatus)}`);
|
|
1890
1692
|
for (const li of tracking.line_items || []) {
|
|
1891
1693
|
if (li.tracking_number) {
|
|
1892
|
-
console.log(` Tracking: ${
|
|
1694
|
+
console.log(` Tracking: ${chalk6.bold(li.tracking_number)}${li.carrier ? ` (${li.carrier})` : ""}`);
|
|
1893
1695
|
}
|
|
1894
1696
|
if (li.tracking_url) {
|
|
1895
|
-
console.log(` Track: ${
|
|
1697
|
+
console.log(` Track: ${chalk6.cyan.underline(li.tracking_url)}`);
|
|
1896
1698
|
}
|
|
1897
1699
|
if (li.estimated_delivery) {
|
|
1898
1700
|
const eta = new Date(li.estimated_delivery).toLocaleDateString();
|
|
@@ -1900,7 +1702,7 @@ function registerOrderCommands(program2) {
|
|
|
1900
1702
|
}
|
|
1901
1703
|
}
|
|
1902
1704
|
} else if (trackRes.data.message) {
|
|
1903
|
-
console.log(
|
|
1705
|
+
console.log(chalk6.dim(`
|
|
1904
1706
|
Vendor: ${trackRes.data.message}`));
|
|
1905
1707
|
}
|
|
1906
1708
|
} catch {
|
|
@@ -1925,7 +1727,7 @@ function registerOrderCommands(program2) {
|
|
|
1925
1727
|
const spinner = ora5("Cancelling order...").start();
|
|
1926
1728
|
const api = getApiClient();
|
|
1927
1729
|
await api.post(`/orders/${id}/cancel`);
|
|
1928
|
-
spinner.succeed(
|
|
1730
|
+
spinner.succeed(chalk6.green("Order cancelled."));
|
|
1929
1731
|
} catch (error) {
|
|
1930
1732
|
handleApiError(error);
|
|
1931
1733
|
}
|
|
@@ -1933,17 +1735,17 @@ function registerOrderCommands(program2) {
|
|
|
1933
1735
|
}
|
|
1934
1736
|
|
|
1935
1737
|
// src/commands/review.ts
|
|
1936
|
-
import
|
|
1738
|
+
import chalk7 from "chalk";
|
|
1937
1739
|
import ora6 from "ora";
|
|
1938
1740
|
import inquirer6 from "inquirer";
|
|
1939
1741
|
function renderStars2(rating) {
|
|
1940
1742
|
const filled = Math.round(rating);
|
|
1941
1743
|
const empty = 10 - filled;
|
|
1942
|
-
return
|
|
1744
|
+
return chalk7.yellow("\u2605".repeat(filled) + "\u2606".repeat(empty));
|
|
1943
1745
|
}
|
|
1944
1746
|
function renderRating(rating) {
|
|
1945
|
-
if (rating === 0) return
|
|
1946
|
-
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;
|
|
1947
1749
|
return `${renderStars2(rating)} ${color(`${rating.toFixed(1)}/10`)}`;
|
|
1948
1750
|
}
|
|
1949
1751
|
var RATING_CHOICES = [
|
|
@@ -1977,17 +1779,17 @@ function registerReviewCommands(program2) {
|
|
|
1977
1779
|
const unreviewedItems = reviewable.items.filter((i) => !i.alreadyReviewed);
|
|
1978
1780
|
const storeAlreadyReviewed = reviewable.store.alreadyReviewed;
|
|
1979
1781
|
if (unreviewedItems.length === 0 && storeAlreadyReviewed) {
|
|
1980
|
-
console.log(
|
|
1782
|
+
console.log(chalk7.yellow("\nYou've already reviewed everything in this order.\n"));
|
|
1981
1783
|
return;
|
|
1982
1784
|
}
|
|
1983
|
-
console.log(
|
|
1984
|
-
Review Order ${
|
|
1985
|
-
console.log(
|
|
1785
|
+
console.log(chalk7.bold(`
|
|
1786
|
+
Review Order ${chalk7.cyan(orderId)}`));
|
|
1787
|
+
console.log(chalk7.dim(` Store: ${reviewable.store.name}
|
|
1986
1788
|
`));
|
|
1987
1789
|
const itemReviews = [];
|
|
1988
1790
|
let storeReview = void 0;
|
|
1989
1791
|
for (const item of unreviewedItems) {
|
|
1990
|
-
console.log(
|
|
1792
|
+
console.log(chalk7.bold(` Product: ${item.productName}`));
|
|
1991
1793
|
const { wantReview } = await inquirer6.prompt([
|
|
1992
1794
|
{
|
|
1993
1795
|
type: "confirm",
|
|
@@ -2023,11 +1825,11 @@ function registerReviewCommands(program2) {
|
|
|
2023
1825
|
title: answers.title.trim(),
|
|
2024
1826
|
body: answers.body.trim()
|
|
2025
1827
|
});
|
|
2026
|
-
console.log(
|
|
1828
|
+
console.log(chalk7.dim(` \u2713 ${item.productName}: ${answers.rating}/10
|
|
2027
1829
|
`));
|
|
2028
1830
|
}
|
|
2029
1831
|
if (!storeAlreadyReviewed) {
|
|
2030
|
-
console.log(
|
|
1832
|
+
console.log(chalk7.bold(` Store: ${reviewable.store.name}`));
|
|
2031
1833
|
const { wantStoreReview } = await inquirer6.prompt([
|
|
2032
1834
|
{
|
|
2033
1835
|
type: "confirm",
|
|
@@ -2065,7 +1867,7 @@ function registerReviewCommands(program2) {
|
|
|
2065
1867
|
}
|
|
2066
1868
|
}
|
|
2067
1869
|
if (itemReviews.length === 0 && !storeReview) {
|
|
2068
|
-
console.log(
|
|
1870
|
+
console.log(chalk7.yellow("\nNo reviews submitted.\n"));
|
|
2069
1871
|
return;
|
|
2070
1872
|
}
|
|
2071
1873
|
const submitSpinner = ora6("Submitting reviews...").start();
|
|
@@ -2074,17 +1876,17 @@ function registerReviewCommands(program2) {
|
|
|
2074
1876
|
itemReviews,
|
|
2075
1877
|
storeReview
|
|
2076
1878
|
});
|
|
2077
|
-
submitSpinner.succeed(
|
|
1879
|
+
submitSpinner.succeed(chalk7.green("Reviews submitted!"));
|
|
2078
1880
|
const data = res.data;
|
|
2079
1881
|
if (data.productReviews?.length > 0) {
|
|
2080
|
-
console.log(
|
|
1882
|
+
console.log(chalk7.bold("\n Product Reviews:"));
|
|
2081
1883
|
for (const r of data.productReviews) {
|
|
2082
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
1884
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
2083
1885
|
}
|
|
2084
1886
|
}
|
|
2085
1887
|
if (data.storeReview && !data.storeReview.skipped) {
|
|
2086
|
-
console.log(
|
|
2087
|
-
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)}`);
|
|
2088
1890
|
}
|
|
2089
1891
|
console.log();
|
|
2090
1892
|
} catch (err) {
|
|
@@ -2119,10 +1921,10 @@ function registerReviewCommands(program2) {
|
|
|
2119
1921
|
body: answers.body.trim(),
|
|
2120
1922
|
orderId: opts.order || void 0
|
|
2121
1923
|
});
|
|
2122
|
-
spinner.succeed(
|
|
1924
|
+
spinner.succeed(chalk7.green("Review submitted!"));
|
|
2123
1925
|
console.log(`
|
|
2124
|
-
${renderRating(answers.rating)} ${
|
|
2125
|
-
console.log(
|
|
1926
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
1927
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2126
1928
|
`));
|
|
2127
1929
|
} catch (error) {
|
|
2128
1930
|
handleApiError(error);
|
|
@@ -2152,10 +1954,10 @@ function registerReviewCommands(program2) {
|
|
|
2152
1954
|
body: answers.body.trim(),
|
|
2153
1955
|
orderId: opts.order || void 0
|
|
2154
1956
|
});
|
|
2155
|
-
spinner.succeed(
|
|
1957
|
+
spinner.succeed(chalk7.green("Store review submitted!"));
|
|
2156
1958
|
console.log(`
|
|
2157
|
-
${renderRating(answers.rating)} ${
|
|
2158
|
-
console.log(
|
|
1959
|
+
${renderRating(answers.rating)} ${chalk7.bold(answers.title)}`);
|
|
1960
|
+
console.log(chalk7.dim(` Review ID: ${res.data.review.id}
|
|
2159
1961
|
`));
|
|
2160
1962
|
} catch (error) {
|
|
2161
1963
|
handleApiError(error);
|
|
@@ -2173,25 +1975,25 @@ function registerReviewCommands(program2) {
|
|
|
2173
1975
|
return;
|
|
2174
1976
|
}
|
|
2175
1977
|
if (productReviews.length === 0 && storeReviews.length === 0) {
|
|
2176
|
-
console.log(
|
|
1978
|
+
console.log(chalk7.yellow("\nYou haven't written any reviews yet.\n"));
|
|
2177
1979
|
return;
|
|
2178
1980
|
}
|
|
2179
1981
|
if (productReviews.length > 0) {
|
|
2180
|
-
console.log(
|
|
1982
|
+
console.log(chalk7.bold("\nProduct Reviews:\n"));
|
|
2181
1983
|
for (const r of productReviews) {
|
|
2182
1984
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2183
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2184
|
-
console.log(` ${
|
|
1985
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
1986
|
+
console.log(` ${chalk7.dim(`on ${r.productName}`)} ${chalk7.dim(date)}`);
|
|
2185
1987
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2186
1988
|
console.log();
|
|
2187
1989
|
}
|
|
2188
1990
|
}
|
|
2189
1991
|
if (storeReviews.length > 0) {
|
|
2190
|
-
console.log(
|
|
1992
|
+
console.log(chalk7.bold("Store Reviews:\n"));
|
|
2191
1993
|
for (const r of storeReviews) {
|
|
2192
1994
|
const date = new Date(r.createdAt).toLocaleDateString();
|
|
2193
|
-
console.log(` ${renderStars2(r.rating)} ${
|
|
2194
|
-
console.log(` ${
|
|
1995
|
+
console.log(` ${renderStars2(r.rating)} ${chalk7.bold(r.title)}`);
|
|
1996
|
+
console.log(` ${chalk7.dim(`store: ${r.storeName}`)} ${chalk7.dim(date)}`);
|
|
2195
1997
|
console.log(` ${r.body.length > 150 ? r.body.slice(0, 150) + "..." : r.body}`);
|
|
2196
1998
|
console.log();
|
|
2197
1999
|
}
|
|
@@ -2210,7 +2012,7 @@ function registerReviewCommands(program2) {
|
|
|
2210
2012
|
const { rating } = res.data;
|
|
2211
2013
|
const entity = opts.store ? res.data.store : res.data.product;
|
|
2212
2014
|
console.log();
|
|
2213
|
-
console.log(
|
|
2015
|
+
console.log(chalk7.bold(` ${entity.name}`));
|
|
2214
2016
|
console.log(` ${renderRating(rating.displayRating)}`);
|
|
2215
2017
|
console.log();
|
|
2216
2018
|
console.log(` Reviews: ${rating.reviewCount}`);
|
|
@@ -2218,11 +2020,11 @@ function registerReviewCommands(program2) {
|
|
|
2218
2020
|
console.log(` Bayesian Avg: ${rating.bayesianAverage.toFixed(2)}`);
|
|
2219
2021
|
console.log(` Effective Cap: ${rating.effectiveCeiling.toFixed(1)}`);
|
|
2220
2022
|
if (rating.isCapped) {
|
|
2221
|
-
console.log(
|
|
2023
|
+
console.log(chalk7.yellow(` \u26A0 Rating is capped \u2014 needs more orders to unlock higher rating`));
|
|
2222
2024
|
if (rating.totalOrders < 100) {
|
|
2223
|
-
console.log(
|
|
2025
|
+
console.log(chalk7.dim(` ${100 - rating.totalOrders} more orders needed to unlock 8.0+ rating`));
|
|
2224
2026
|
} else if (rating.totalOrders < 1e3) {
|
|
2225
|
-
console.log(
|
|
2027
|
+
console.log(chalk7.dim(` ${1e3 - rating.totalOrders} more orders needed to unlock 9.0+ rating`));
|
|
2226
2028
|
}
|
|
2227
2029
|
}
|
|
2228
2030
|
console.log();
|
|
@@ -2245,7 +2047,7 @@ function registerReviewCommands(program2) {
|
|
|
2245
2047
|
const api = getApiClient();
|
|
2246
2048
|
const endpoint = opts.store ? `/store-reviews/${reviewId}` : `/reviews/${reviewId}`;
|
|
2247
2049
|
await api.delete(endpoint);
|
|
2248
|
-
spinner.succeed(
|
|
2050
|
+
spinner.succeed(chalk7.green("Review deleted."));
|
|
2249
2051
|
} catch (error) {
|
|
2250
2052
|
handleApiError(error);
|
|
2251
2053
|
}
|
|
@@ -2253,41 +2055,41 @@ function registerReviewCommands(program2) {
|
|
|
2253
2055
|
}
|
|
2254
2056
|
|
|
2255
2057
|
// src/commands/config.ts
|
|
2256
|
-
import
|
|
2058
|
+
import chalk8 from "chalk";
|
|
2257
2059
|
function registerConfigCommands(program2) {
|
|
2258
|
-
const
|
|
2259
|
-
|
|
2060
|
+
const config = program2.command("config").description("View and manage CLI configuration");
|
|
2061
|
+
config.command("show").description("Show current configuration").action(() => {
|
|
2260
2062
|
const cfg = getConfig();
|
|
2261
|
-
console.log(
|
|
2262
|
-
console.log(` Active agent: ${
|
|
2263
|
-
console.log(` Output format: ${
|
|
2264
|
-
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)}`);
|
|
2265
2067
|
console.log();
|
|
2266
2068
|
});
|
|
2267
|
-
|
|
2069
|
+
config.command("set-output <format>").description("Set output format: human or json").action((format) => {
|
|
2268
2070
|
if (format !== "human" && format !== "json") {
|
|
2269
|
-
console.error(
|
|
2071
|
+
console.error(chalk8.red('\u2717 Format must be "human" or "json".'));
|
|
2270
2072
|
process.exitCode = 1;
|
|
2271
2073
|
return;
|
|
2272
2074
|
}
|
|
2273
2075
|
const cfg = getConfig();
|
|
2274
2076
|
cfg.set("outputFormat", format);
|
|
2275
|
-
console.log(
|
|
2077
|
+
console.log(chalk8.green(`
|
|
2276
2078
|
\u2713 Output format set to "${format}".`));
|
|
2277
2079
|
});
|
|
2278
|
-
|
|
2080
|
+
config.command("reset").description("Reset all configuration to defaults").action(() => {
|
|
2279
2081
|
const cfg = getConfig();
|
|
2280
2082
|
cfg.clear();
|
|
2281
|
-
console.log(
|
|
2083
|
+
console.log(chalk8.green("\n\u2713 Configuration reset to defaults."));
|
|
2282
2084
|
});
|
|
2283
|
-
|
|
2085
|
+
config.command("path").description("Show the config file path").action(() => {
|
|
2284
2086
|
const cfg = getConfig();
|
|
2285
2087
|
console.log(cfg.path);
|
|
2286
2088
|
});
|
|
2287
2089
|
}
|
|
2288
2090
|
|
|
2289
2091
|
// src/commands/store.ts
|
|
2290
|
-
import
|
|
2092
|
+
import chalk9 from "chalk";
|
|
2291
2093
|
import ora7 from "ora";
|
|
2292
2094
|
function formatPrice3(cents, currency) {
|
|
2293
2095
|
return new Intl.NumberFormat("en-US", {
|
|
@@ -2332,28 +2134,28 @@ function registerStoreCommands(program2) {
|
|
|
2332
2134
|
return;
|
|
2333
2135
|
}
|
|
2334
2136
|
if (stores.length === 0) {
|
|
2335
|
-
console.log(
|
|
2137
|
+
console.log(chalk9.yellow("\nNo stores found.\n"));
|
|
2336
2138
|
return;
|
|
2337
2139
|
}
|
|
2338
|
-
console.log(
|
|
2140
|
+
console.log(chalk9.bold(`
|
|
2339
2141
|
Stores \u2014 ${total} found (page ${page})
|
|
2340
2142
|
`));
|
|
2341
2143
|
for (const s of stores) {
|
|
2342
|
-
const badge = s.verified ?
|
|
2343
|
-
const rating = s.rating != null ?
|
|
2344
|
-
const products = s.productCount != null ?
|
|
2345
|
-
const country = s.country ?
|
|
2346
|
-
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})`)}`);
|
|
2347
2149
|
console.log(` ${rating} ${products}${country}`);
|
|
2348
2150
|
if (s.description) {
|
|
2349
2151
|
const desc = s.description.length > 100 ? s.description.slice(0, 100) + "..." : s.description;
|
|
2350
|
-
console.log(` ${
|
|
2152
|
+
console.log(` ${chalk9.dim(desc)}`);
|
|
2351
2153
|
}
|
|
2352
2154
|
console.log();
|
|
2353
2155
|
}
|
|
2354
2156
|
const totalPages = Math.ceil(total / pageSize);
|
|
2355
2157
|
if (totalPages > 1) {
|
|
2356
|
-
console.log(
|
|
2158
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2357
2159
|
`));
|
|
2358
2160
|
}
|
|
2359
2161
|
} catch (error) {
|
|
@@ -2371,24 +2173,24 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2371
2173
|
console.log(JSON.stringify(s, null, 2));
|
|
2372
2174
|
return;
|
|
2373
2175
|
}
|
|
2374
|
-
const badge = s.verified ?
|
|
2176
|
+
const badge = s.verified ? chalk9.green(" \u2713 Verified") : chalk9.dim(" Unverified");
|
|
2375
2177
|
console.log();
|
|
2376
|
-
console.log(
|
|
2377
|
-
console.log(
|
|
2178
|
+
console.log(chalk9.bold.cyan(` ${s.name}`) + badge);
|
|
2179
|
+
console.log(chalk9.dim(` ID: ${s.id} | Slug: ${s.slug}`));
|
|
2378
2180
|
console.log();
|
|
2379
2181
|
if (s.description) console.log(` ${s.description}`);
|
|
2380
2182
|
console.log();
|
|
2381
2183
|
if (s.rating != null) {
|
|
2382
|
-
console.log(` Rating: ${
|
|
2184
|
+
console.log(` Rating: ${chalk9.yellow(renderStars3(s.rating))} ${chalk9.dim(`(${s.rating.toFixed(1)})`)}`);
|
|
2383
2185
|
}
|
|
2384
2186
|
if (s.productCount != null) console.log(` Products: ${s.productCount}`);
|
|
2385
2187
|
if (s.country) console.log(` Country: ${s.country}`);
|
|
2386
2188
|
console.log(` Currency: ${s.currency}`);
|
|
2387
|
-
if (s.domain) console.log(` Website: ${
|
|
2189
|
+
if (s.domain) console.log(` Website: ${chalk9.cyan.underline(s.domain)}`);
|
|
2388
2190
|
if (s.contactEmail) console.log(` Contact: ${s.contactEmail}`);
|
|
2389
2191
|
console.log();
|
|
2390
|
-
console.log(
|
|
2391
|
-
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}`));
|
|
2392
2194
|
console.log();
|
|
2393
2195
|
} catch (error) {
|
|
2394
2196
|
handleApiError(error);
|
|
@@ -2419,43 +2221,43 @@ Stores \u2014 ${total} found (page ${page})
|
|
|
2419
2221
|
console.log(JSON.stringify(res.data, null, 2));
|
|
2420
2222
|
return;
|
|
2421
2223
|
}
|
|
2422
|
-
const badge = storeInfo.verified ?
|
|
2423
|
-
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)`) : "";
|
|
2424
2226
|
console.log(
|
|
2425
|
-
|
|
2227
|
+
chalk9.bold(`
|
|
2426
2228
|
${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
2427
2229
|
`)
|
|
2428
2230
|
);
|
|
2429
2231
|
if (products.length === 0) {
|
|
2430
|
-
console.log(
|
|
2232
|
+
console.log(chalk9.yellow(" No products found matching your filters.\n"));
|
|
2431
2233
|
return;
|
|
2432
2234
|
}
|
|
2433
2235
|
for (const p of products) {
|
|
2434
|
-
const stock = p.inStock ?
|
|
2435
|
-
const price =
|
|
2436
|
-
const stars =
|
|
2437
|
-
const shippingInfo = p.freeShipping ?
|
|
2438
|
-
const deliveryInfo = p.shippingDays != null ?
|
|
2439
|
-
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})`)}`);
|
|
2440
2242
|
console.log(
|
|
2441
|
-
` ${price} ${stock} ${stars} ${
|
|
2243
|
+
` ${price} ${stock} ${stars} ${chalk9.dim(`(${p.reviewCount} reviews)`)}` + (shippingInfo ? ` ${shippingInfo}` : "") + (deliveryInfo ? ` ${deliveryInfo}` : "")
|
|
2442
2244
|
);
|
|
2443
2245
|
const meta = [];
|
|
2444
2246
|
if (p.category) meta.push(p.category);
|
|
2445
2247
|
if (p.brand) meta.push(p.brand);
|
|
2446
2248
|
if (p.variant) meta.push(p.variant);
|
|
2447
|
-
if (meta.length) console.log(` ${
|
|
2249
|
+
if (meta.length) console.log(` ${chalk9.dim(meta.join(" \xB7 "))}`);
|
|
2448
2250
|
const returnInfo = [];
|
|
2449
2251
|
if (p.freeReturns) returnInfo.push("Free Returns");
|
|
2450
2252
|
if (p.returnWindowDays) returnInfo.push(`${p.returnWindowDays}d return window`);
|
|
2451
|
-
if (returnInfo.length) console.log(` ${
|
|
2253
|
+
if (returnInfo.length) console.log(` ${chalk9.dim(returnInfo.join(" \xB7 "))}`);
|
|
2452
2254
|
const desc = p.description.length > 120 ? p.description.slice(0, 120) + "..." : p.description;
|
|
2453
2255
|
console.log(` ${desc}`);
|
|
2454
2256
|
console.log();
|
|
2455
2257
|
}
|
|
2456
2258
|
const totalPages = Math.ceil(total / pageSize);
|
|
2457
2259
|
if (totalPages > 1) {
|
|
2458
|
-
console.log(
|
|
2260
|
+
console.log(chalk9.dim(` Page ${page} of ${totalPages}. Use --page to navigate.
|
|
2459
2261
|
`));
|
|
2460
2262
|
}
|
|
2461
2263
|
} catch (error) {
|
|
@@ -2465,13 +2267,13 @@ ${storeInfo.name}${badge}${storeRating} \u2014 ${total} products (page ${page})
|
|
|
2465
2267
|
}
|
|
2466
2268
|
|
|
2467
2269
|
// src/commands/status.ts
|
|
2468
|
-
import
|
|
2270
|
+
import chalk10 from "chalk";
|
|
2469
2271
|
import ora8 from "ora";
|
|
2470
2272
|
function registerStatusCommand(program2) {
|
|
2471
2273
|
program2.command("status").description("Show full account overview \u2014 user, agents, addresses, payment methods").option("--json", "Output raw JSON").action(async (opts) => {
|
|
2472
2274
|
try {
|
|
2473
2275
|
if (!await isLoggedIn()) {
|
|
2474
|
-
console.log(
|
|
2276
|
+
console.log(chalk10.yellow("\nNot logged in. Run: clishop login\n"));
|
|
2475
2277
|
return;
|
|
2476
2278
|
}
|
|
2477
2279
|
const spinner = ora8("Fetching account overview...").start();
|
|
@@ -2505,59 +2307,59 @@ function registerStatusCommand(program2) {
|
|
|
2505
2307
|
}, null, 2));
|
|
2506
2308
|
return;
|
|
2507
2309
|
}
|
|
2508
|
-
console.log(
|
|
2509
|
-
console.log(
|
|
2510
|
-
console.log(
|
|
2511
|
-
console.log(
|
|
2512
|
-
console.log(` Name: ${
|
|
2513
|
-
console.log(` Email: ${
|
|
2514
|
-
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")}`);
|
|
2515
2317
|
console.log();
|
|
2516
|
-
console.log(
|
|
2318
|
+
console.log(chalk10.bold(` \u{1F916} Agents (${agents.length})`));
|
|
2517
2319
|
if (agentDetails.length === 0) {
|
|
2518
|
-
console.log(
|
|
2320
|
+
console.log(chalk10.yellow(" No agents found."));
|
|
2519
2321
|
}
|
|
2520
2322
|
for (const agent of agentDetails) {
|
|
2521
2323
|
const isActive = agent.name === activeAgentName;
|
|
2522
|
-
const marker = isActive ?
|
|
2523
|
-
const activeLabel = isActive ?
|
|
2324
|
+
const marker = isActive ? chalk10.green("\u25CF ") : " ";
|
|
2325
|
+
const activeLabel = isActive ? chalk10.green(" (active)") : "";
|
|
2524
2326
|
const limit = agent.maxOrderAmountInCents ? `$${(agent.maxOrderAmountInCents / 100).toFixed(2)}` : "No limit";
|
|
2525
2327
|
const confirm = agent.requireConfirmation ? "Yes" : "No";
|
|
2526
2328
|
console.log();
|
|
2527
|
-
console.log(` ${marker}${
|
|
2528
|
-
console.log(` ID: ${
|
|
2529
|
-
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)}`);
|
|
2530
2332
|
console.log(` Require confirm: ${confirm}`);
|
|
2531
2333
|
console.log();
|
|
2532
|
-
console.log(
|
|
2334
|
+
console.log(chalk10.bold(` \u{1F4CD} Addresses (${agent.addresses.length})`));
|
|
2533
2335
|
if (agent.addresses.length === 0) {
|
|
2534
|
-
console.log(
|
|
2336
|
+
console.log(chalk10.dim(" None"));
|
|
2535
2337
|
} else {
|
|
2536
2338
|
for (const addr of agent.addresses) {
|
|
2537
2339
|
const isDefault = addr.id === agent.defaultAddressId;
|
|
2538
|
-
const defaultBadge = isDefault ?
|
|
2539
|
-
console.log(` \u2022 ${
|
|
2540
|
-
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}`));
|
|
2541
2343
|
}
|
|
2542
2344
|
}
|
|
2543
2345
|
console.log();
|
|
2544
|
-
console.log(
|
|
2346
|
+
console.log(chalk10.bold(` \u{1F4B3} Payment Methods (${agent.paymentMethods.length})`));
|
|
2545
2347
|
if (agent.paymentMethods.length === 0) {
|
|
2546
|
-
console.log(
|
|
2348
|
+
console.log(chalk10.dim(" None \u2014 run: clishop payment add"));
|
|
2547
2349
|
} else {
|
|
2548
2350
|
for (const pm of agent.paymentMethods) {
|
|
2549
2351
|
const isDefault = pm.id === agent.defaultPaymentMethodId;
|
|
2550
|
-
const defaultBadge = isDefault ?
|
|
2352
|
+
const defaultBadge = isDefault ? chalk10.green(" \u2605 default") : "";
|
|
2551
2353
|
const last4 = pm.last4 ? ` \u2022\u2022\u2022\u2022 ${pm.last4}` : "";
|
|
2552
|
-
console.log(` \u2022 ${
|
|
2553
|
-
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})`));
|
|
2554
2356
|
}
|
|
2555
2357
|
}
|
|
2556
2358
|
}
|
|
2557
2359
|
console.log();
|
|
2558
|
-
console.log(
|
|
2559
|
-
console.log(
|
|
2560
|
-
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"));
|
|
2561
2363
|
console.log();
|
|
2562
2364
|
} catch (error) {
|
|
2563
2365
|
handleApiError(error);
|
|
@@ -2566,10 +2368,10 @@ function registerStatusCommand(program2) {
|
|
|
2566
2368
|
}
|
|
2567
2369
|
|
|
2568
2370
|
// src/commands/setup.ts
|
|
2569
|
-
import
|
|
2371
|
+
import chalk11 from "chalk";
|
|
2570
2372
|
import ora9 from "ora";
|
|
2571
2373
|
import inquirer7 from "inquirer";
|
|
2572
|
-
function divider(color =
|
|
2374
|
+
function divider(color = chalk11.cyan) {
|
|
2573
2375
|
console.log(" " + color("\u2500".repeat(48)));
|
|
2574
2376
|
}
|
|
2575
2377
|
function stepHeader(step, total, title) {
|
|
@@ -2577,7 +2379,7 @@ function stepHeader(step, total, title) {
|
|
|
2577
2379
|
divider();
|
|
2578
2380
|
console.log();
|
|
2579
2381
|
console.log(
|
|
2580
|
-
|
|
2382
|
+
chalk11.bold.white(` STEP ${step} of ${total}`) + chalk11.dim(" \xB7 ") + chalk11.bold(title)
|
|
2581
2383
|
);
|
|
2582
2384
|
console.log();
|
|
2583
2385
|
}
|
|
@@ -2595,29 +2397,29 @@ function registerSetupCommand(program2) {
|
|
|
2595
2397
|
});
|
|
2596
2398
|
}
|
|
2597
2399
|
async function runSetupWizard() {
|
|
2598
|
-
const
|
|
2400
|
+
const config = getConfig();
|
|
2599
2401
|
console.log();
|
|
2600
|
-
divider(
|
|
2402
|
+
divider(chalk11.cyan);
|
|
2601
2403
|
console.log();
|
|
2602
|
-
console.log(
|
|
2603
|
-
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."));
|
|
2604
2406
|
console.log();
|
|
2605
|
-
divider(
|
|
2407
|
+
divider(chalk11.cyan);
|
|
2606
2408
|
console.log();
|
|
2607
2409
|
console.log(
|
|
2608
|
-
|
|
2410
|
+
chalk11.dim(" This wizard will guide you through the initial setup.")
|
|
2609
2411
|
);
|
|
2610
2412
|
console.log(
|
|
2611
|
-
|
|
2413
|
+
chalk11.dim(" It only takes a minute. You can re-run it anytime with:")
|
|
2612
2414
|
);
|
|
2613
|
-
console.log(
|
|
2415
|
+
console.log(chalk11.dim(" ") + chalk11.white("clishop setup"));
|
|
2614
2416
|
stepHeader(1, 5, "Account");
|
|
2615
2417
|
let loggedIn = await isLoggedIn();
|
|
2616
2418
|
if (loggedIn) {
|
|
2617
2419
|
const user = await getUserInfo();
|
|
2618
2420
|
console.log(
|
|
2619
|
-
|
|
2620
|
-
` \u2713 Already logged in as ${
|
|
2421
|
+
chalk11.green(
|
|
2422
|
+
` \u2713 Already logged in as ${chalk11.bold(user?.name || user?.email || "unknown")}`
|
|
2621
2423
|
)
|
|
2622
2424
|
);
|
|
2623
2425
|
console.log();
|
|
@@ -2663,9 +2465,9 @@ async function runSetupWizard() {
|
|
|
2663
2465
|
}
|
|
2664
2466
|
]);
|
|
2665
2467
|
if (answers.password !== answers.confirmPassword) {
|
|
2666
|
-
console.error(
|
|
2468
|
+
console.error(chalk11.red("\n \u2717 Passwords do not match."));
|
|
2667
2469
|
console.log(
|
|
2668
|
-
|
|
2470
|
+
chalk11.dim(" Run ") + chalk11.white("clishop setup") + chalk11.dim(" to try again.\n")
|
|
2669
2471
|
);
|
|
2670
2472
|
process.exitCode = 1;
|
|
2671
2473
|
return;
|
|
@@ -2678,24 +2480,24 @@ async function runSetupWizard() {
|
|
|
2678
2480
|
answers.name
|
|
2679
2481
|
);
|
|
2680
2482
|
spinner.succeed(
|
|
2681
|
-
|
|
2682
|
-
`Account created! Welcome, ${
|
|
2483
|
+
chalk11.green(
|
|
2484
|
+
`Account created! Welcome, ${chalk11.bold(user.name)}.`
|
|
2683
2485
|
)
|
|
2684
2486
|
);
|
|
2685
2487
|
} catch (error) {
|
|
2686
2488
|
spinner.fail(
|
|
2687
|
-
|
|
2489
|
+
chalk11.red(
|
|
2688
2490
|
`Registration failed: ${error?.response?.data?.message || error.message}`
|
|
2689
2491
|
)
|
|
2690
2492
|
);
|
|
2691
2493
|
console.log();
|
|
2692
2494
|
console.log(
|
|
2693
|
-
|
|
2694
|
-
" Make sure the backend is running at: " +
|
|
2495
|
+
chalk11.dim(
|
|
2496
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2695
2497
|
)
|
|
2696
2498
|
);
|
|
2697
2499
|
console.log(
|
|
2698
|
-
|
|
2500
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2699
2501
|
);
|
|
2700
2502
|
process.exitCode = 1;
|
|
2701
2503
|
return;
|
|
@@ -2714,22 +2516,22 @@ async function runSetupWizard() {
|
|
|
2714
2516
|
try {
|
|
2715
2517
|
const user = await login(answers.email, answers.password);
|
|
2716
2518
|
spinner.succeed(
|
|
2717
|
-
|
|
2519
|
+
chalk11.green(`Logged in as ${chalk11.bold(user.name)}.`)
|
|
2718
2520
|
);
|
|
2719
2521
|
} catch (error) {
|
|
2720
2522
|
spinner.fail(
|
|
2721
|
-
|
|
2523
|
+
chalk11.red(
|
|
2722
2524
|
`Login failed: ${error?.response?.data?.message || error.message}`
|
|
2723
2525
|
)
|
|
2724
2526
|
);
|
|
2725
2527
|
console.log();
|
|
2726
2528
|
console.log(
|
|
2727
|
-
|
|
2728
|
-
" Make sure the backend is running at: " +
|
|
2529
|
+
chalk11.dim(
|
|
2530
|
+
" Make sure the backend is running at: " + chalk11.white(config.get("apiBaseUrl"))
|
|
2729
2531
|
)
|
|
2730
2532
|
);
|
|
2731
2533
|
console.log(
|
|
2732
|
-
|
|
2534
|
+
chalk11.dim(" Then run ") + chalk11.white("clishop setup") + chalk11.dim(" again.\n")
|
|
2733
2535
|
);
|
|
2734
2536
|
process.exitCode = 1;
|
|
2735
2537
|
return;
|
|
@@ -2739,12 +2541,12 @@ async function runSetupWizard() {
|
|
|
2739
2541
|
stepHeader(2, 5, "Agent (optional)");
|
|
2740
2542
|
const activeAgent = getActiveAgent();
|
|
2741
2543
|
console.log(
|
|
2742
|
-
|
|
2544
|
+
chalk11.dim(
|
|
2743
2545
|
` A default agent is ready ($${activeAgent.maxOrderAmount} limit, confirmation on).`
|
|
2744
2546
|
)
|
|
2745
2547
|
);
|
|
2746
2548
|
console.log(
|
|
2747
|
-
|
|
2549
|
+
chalk11.dim(" Agents control spending limits and category restrictions.")
|
|
2748
2550
|
);
|
|
2749
2551
|
console.log();
|
|
2750
2552
|
const { agentChoice } = await inquirer7.prompt([
|
|
@@ -2783,22 +2585,22 @@ async function runSetupWizard() {
|
|
|
2783
2585
|
});
|
|
2784
2586
|
setActiveAgent(agent.name);
|
|
2785
2587
|
console.log(
|
|
2786
|
-
|
|
2588
|
+
chalk11.green(
|
|
2787
2589
|
`
|
|
2788
|
-
\u2713 Agent "${
|
|
2590
|
+
\u2713 Agent "${chalk11.bold(agent.name)}" created and set as active.`
|
|
2789
2591
|
)
|
|
2790
2592
|
);
|
|
2791
2593
|
} catch (error) {
|
|
2792
|
-
console.error(
|
|
2594
|
+
console.error(chalk11.red(`
|
|
2793
2595
|
\u2717 ${error.message}`));
|
|
2794
|
-
console.log(
|
|
2596
|
+
console.log(chalk11.dim(" Continuing with the default agent."));
|
|
2795
2597
|
}
|
|
2796
2598
|
} else {
|
|
2797
|
-
console.log(
|
|
2599
|
+
console.log(chalk11.green(" \u2713 Using default agent."));
|
|
2798
2600
|
}
|
|
2799
2601
|
stepHeader(3, 5, "Shipping Address");
|
|
2800
2602
|
console.log(
|
|
2801
|
-
|
|
2603
|
+
chalk11.dim(
|
|
2802
2604
|
" Add an address so products can be delivered to you."
|
|
2803
2605
|
)
|
|
2804
2606
|
);
|
|
@@ -2920,37 +2722,37 @@ async function runSetupWizard() {
|
|
|
2920
2722
|
});
|
|
2921
2723
|
updateAgent(agent.name, { defaultAddressId: res.data.address.id });
|
|
2922
2724
|
spinner.succeed(
|
|
2923
|
-
|
|
2725
|
+
chalk11.green(
|
|
2924
2726
|
`Address "${addr.label}" saved and set as default.`
|
|
2925
2727
|
)
|
|
2926
2728
|
);
|
|
2927
2729
|
} catch (error) {
|
|
2928
2730
|
spinner.fail(
|
|
2929
|
-
|
|
2731
|
+
chalk11.red(
|
|
2930
2732
|
`Failed to save address: ${error?.response?.data?.message || error.message}`
|
|
2931
2733
|
)
|
|
2932
2734
|
);
|
|
2933
2735
|
console.log(
|
|
2934
|
-
|
|
2935
|
-
" You can add an address later with: " +
|
|
2736
|
+
chalk11.dim(
|
|
2737
|
+
" You can add an address later with: " + chalk11.white("clishop address add")
|
|
2936
2738
|
)
|
|
2937
2739
|
);
|
|
2938
2740
|
}
|
|
2939
2741
|
} else {
|
|
2940
2742
|
console.log(
|
|
2941
|
-
|
|
2942
|
-
"\n You can add one later with: " +
|
|
2743
|
+
chalk11.dim(
|
|
2744
|
+
"\n You can add one later with: " + chalk11.white("clishop address add")
|
|
2943
2745
|
)
|
|
2944
2746
|
);
|
|
2945
2747
|
}
|
|
2946
2748
|
stepHeader(4, 5, "Payment Method");
|
|
2947
2749
|
console.log(
|
|
2948
|
-
|
|
2750
|
+
chalk11.dim(
|
|
2949
2751
|
" For security, payment details are entered through a secure web"
|
|
2950
2752
|
)
|
|
2951
2753
|
);
|
|
2952
2754
|
console.log(
|
|
2953
|
-
|
|
2755
|
+
chalk11.dim(" page. The CLI never sees your card number.")
|
|
2954
2756
|
);
|
|
2955
2757
|
console.log();
|
|
2956
2758
|
const { addPayment } = await inquirer7.prompt([
|
|
@@ -2973,46 +2775,46 @@ async function runSetupWizard() {
|
|
|
2973
2775
|
const { setupUrl } = res.data;
|
|
2974
2776
|
console.log();
|
|
2975
2777
|
console.log(
|
|
2976
|
-
|
|
2778
|
+
chalk11.bold(
|
|
2977
2779
|
" Open this link in your browser to add a payment method:"
|
|
2978
2780
|
)
|
|
2979
2781
|
);
|
|
2980
2782
|
console.log();
|
|
2981
|
-
console.log(" " +
|
|
2783
|
+
console.log(" " + chalk11.cyan.underline(setupUrl));
|
|
2982
2784
|
console.log();
|
|
2983
2785
|
console.log(
|
|
2984
|
-
|
|
2985
|
-
" Once done, verify with: " +
|
|
2786
|
+
chalk11.dim(
|
|
2787
|
+
" Once done, verify with: " + chalk11.white("clishop payment list")
|
|
2986
2788
|
)
|
|
2987
2789
|
);
|
|
2988
2790
|
} catch (error) {
|
|
2989
2791
|
spinner.fail(
|
|
2990
|
-
|
|
2792
|
+
chalk11.red(
|
|
2991
2793
|
`Could not get setup link: ${error?.response?.data?.message || error.message}`
|
|
2992
2794
|
)
|
|
2993
2795
|
);
|
|
2994
2796
|
console.log(
|
|
2995
|
-
|
|
2996
|
-
" You can set up payment later with: " +
|
|
2797
|
+
chalk11.dim(
|
|
2798
|
+
" You can set up payment later with: " + chalk11.white("clishop payment add")
|
|
2997
2799
|
)
|
|
2998
2800
|
);
|
|
2999
2801
|
}
|
|
3000
2802
|
} else {
|
|
3001
2803
|
console.log(
|
|
3002
|
-
|
|
3003
|
-
"\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")
|
|
3004
2806
|
)
|
|
3005
2807
|
);
|
|
3006
2808
|
}
|
|
3007
2809
|
stepHeader(5, 5, "Your First Search");
|
|
3008
2810
|
if (addressCity) {
|
|
3009
2811
|
console.log(
|
|
3010
|
-
|
|
3011
|
-
` 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)}.`
|
|
3012
2814
|
)
|
|
3013
2815
|
);
|
|
3014
2816
|
} else {
|
|
3015
|
-
console.log(
|
|
2817
|
+
console.log(chalk11.dim(" Let's find something to order!"));
|
|
3016
2818
|
}
|
|
3017
2819
|
console.log();
|
|
3018
2820
|
const { searchQuery } = await inquirer7.prompt([
|
|
@@ -3034,14 +2836,14 @@ async function runSetupWizard() {
|
|
|
3034
2836
|
const result = res.data;
|
|
3035
2837
|
if (result.products.length === 0) {
|
|
3036
2838
|
console.log(
|
|
3037
|
-
|
|
2839
|
+
chalk11.yellow(
|
|
3038
2840
|
`
|
|
3039
2841
|
No results for "${searchQuery}". Try other terms later!`
|
|
3040
2842
|
)
|
|
3041
2843
|
);
|
|
3042
2844
|
} else {
|
|
3043
2845
|
console.log(
|
|
3044
|
-
|
|
2846
|
+
chalk11.bold(
|
|
3045
2847
|
`
|
|
3046
2848
|
Found ${result.total} result${result.total !== 1 ? "s" : ""} for "${searchQuery}":
|
|
3047
2849
|
`
|
|
@@ -3049,85 +2851,85 @@ async function runSetupWizard() {
|
|
|
3049
2851
|
);
|
|
3050
2852
|
for (const p of result.products) {
|
|
3051
2853
|
const price = formatPrice4(p.priceInCents, p.currency || "USD");
|
|
3052
|
-
const stock = p.inStock ?
|
|
2854
|
+
const stock = p.inStock ? chalk11.green("In Stock") : chalk11.red("Out of Stock");
|
|
3053
2855
|
console.log(
|
|
3054
|
-
` ${
|
|
2856
|
+
` ${chalk11.bold.cyan(p.name)} ${chalk11.bold.white(price)} ${stock}`
|
|
3055
2857
|
);
|
|
3056
|
-
console.log(
|
|
2858
|
+
console.log(chalk11.dim(` ID: ${p.id}`));
|
|
3057
2859
|
console.log(
|
|
3058
|
-
|
|
2860
|
+
chalk11.dim(
|
|
3059
2861
|
` ${p.description.length > 100 ? p.description.slice(0, 100) + "..." : p.description}`
|
|
3060
2862
|
)
|
|
3061
2863
|
);
|
|
3062
2864
|
console.log();
|
|
3063
2865
|
}
|
|
3064
2866
|
console.log(
|
|
3065
|
-
|
|
2867
|
+
chalk11.dim(" Buy a product with: ") + chalk11.white("clishop buy <product-id>")
|
|
3066
2868
|
);
|
|
3067
2869
|
}
|
|
3068
2870
|
} catch (error) {
|
|
3069
2871
|
spinner.fail(
|
|
3070
|
-
|
|
2872
|
+
chalk11.red(
|
|
3071
2873
|
`Search failed: ${error?.response?.data?.message || error.message}`
|
|
3072
2874
|
)
|
|
3073
2875
|
);
|
|
3074
2876
|
}
|
|
3075
2877
|
}
|
|
3076
|
-
|
|
2878
|
+
config.set("setupCompleted", true);
|
|
3077
2879
|
console.log();
|
|
3078
|
-
divider(
|
|
2880
|
+
divider(chalk11.green);
|
|
3079
2881
|
console.log();
|
|
3080
|
-
console.log(
|
|
2882
|
+
console.log(chalk11.bold.green(" \u2713 You're all set!"));
|
|
3081
2883
|
console.log();
|
|
3082
|
-
console.log(
|
|
2884
|
+
console.log(chalk11.dim(" Here are some commands to get you started:"));
|
|
3083
2885
|
console.log();
|
|
3084
2886
|
console.log(
|
|
3085
|
-
|
|
2887
|
+
chalk11.white(" clishop search <query> ") + chalk11.dim("Search for products")
|
|
3086
2888
|
);
|
|
3087
2889
|
console.log(
|
|
3088
|
-
|
|
2890
|
+
chalk11.white(" clishop buy <id> ") + chalk11.dim("Quick-buy a product")
|
|
3089
2891
|
);
|
|
3090
2892
|
console.log(
|
|
3091
|
-
|
|
2893
|
+
chalk11.white(" clishop order list ") + chalk11.dim("View your orders")
|
|
3092
2894
|
);
|
|
3093
2895
|
console.log(
|
|
3094
|
-
|
|
2896
|
+
chalk11.white(" clishop agent list ") + chalk11.dim("Manage your agents")
|
|
3095
2897
|
);
|
|
3096
2898
|
console.log(
|
|
3097
|
-
|
|
2899
|
+
chalk11.white(" clishop --help ") + chalk11.dim("See all commands")
|
|
3098
2900
|
);
|
|
3099
2901
|
console.log();
|
|
3100
|
-
divider(
|
|
2902
|
+
divider(chalk11.green);
|
|
3101
2903
|
console.log();
|
|
3102
2904
|
}
|
|
3103
2905
|
|
|
3104
2906
|
// src/commands/advertise.ts
|
|
3105
|
-
import
|
|
2907
|
+
import chalk12 from "chalk";
|
|
3106
2908
|
import ora10 from "ora";
|
|
3107
2909
|
import inquirer8 from "inquirer";
|
|
3108
2910
|
function formatPrice5(cents, currency) {
|
|
3109
2911
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
|
|
3110
2912
|
}
|
|
3111
2913
|
var STATUS_COLORS2 = {
|
|
3112
|
-
open:
|
|
3113
|
-
closed:
|
|
3114
|
-
accepted:
|
|
3115
|
-
cancelled:
|
|
3116
|
-
expired:
|
|
2914
|
+
open: chalk12.green,
|
|
2915
|
+
closed: chalk12.dim,
|
|
2916
|
+
accepted: chalk12.blue,
|
|
2917
|
+
cancelled: chalk12.red,
|
|
2918
|
+
expired: chalk12.yellow
|
|
3117
2919
|
};
|
|
3118
2920
|
var BID_STATUS_COLORS = {
|
|
3119
|
-
pending:
|
|
3120
|
-
accepted:
|
|
3121
|
-
rejected:
|
|
3122
|
-
withdrawn:
|
|
2921
|
+
pending: chalk12.yellow,
|
|
2922
|
+
accepted: chalk12.green,
|
|
2923
|
+
rejected: chalk12.red,
|
|
2924
|
+
withdrawn: chalk12.dim
|
|
3123
2925
|
};
|
|
3124
2926
|
function registerAdvertiseCommands(program2) {
|
|
3125
2927
|
const advertise = program2.command("advertise").description("Advertise a request for vendors to bid on (when you can't find what you need)");
|
|
3126
2928
|
advertise.command("create").alias("new").description("Create a new advertised request").action(async () => {
|
|
3127
2929
|
try {
|
|
3128
2930
|
const agent = getActiveAgent();
|
|
3129
|
-
console.log(
|
|
3130
|
-
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"));
|
|
3131
2933
|
const answers = await inquirer8.prompt([
|
|
3132
2934
|
{
|
|
3133
2935
|
type: "input",
|
|
@@ -3263,7 +3065,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3263
3065
|
addrChoices.push({ name: displayName, value: displayName });
|
|
3264
3066
|
}
|
|
3265
3067
|
const skipOption = "Skip \u2014 don't set a delivery address";
|
|
3266
|
-
addrChoices.push({ name:
|
|
3068
|
+
addrChoices.push({ name: chalk12.dim(skipOption), value: skipOption });
|
|
3267
3069
|
const defaultAddr = addresses.find((a) => a.id === agent.defaultAddressId);
|
|
3268
3070
|
const defaultDisplay = defaultAddr ? `${defaultAddr.label} \u2014 ${defaultAddr.line1}` : "";
|
|
3269
3071
|
const { selectedAddress } = await inquirer8.prompt([
|
|
@@ -3278,11 +3080,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3278
3080
|
addressId = addrMap.get(selectedAddress) || void 0;
|
|
3279
3081
|
} else {
|
|
3280
3082
|
addrSpinner.stop();
|
|
3281
|
-
console.log(
|
|
3083
|
+
console.log(chalk12.dim(" No addresses found. You can add one later with: clishop address add"));
|
|
3282
3084
|
}
|
|
3283
3085
|
} catch {
|
|
3284
3086
|
addrSpinner.stop();
|
|
3285
|
-
console.log(
|
|
3087
|
+
console.log(chalk12.dim(" Could not fetch addresses. Skipping delivery location."));
|
|
3286
3088
|
}
|
|
3287
3089
|
let paymentMethods;
|
|
3288
3090
|
const paySpinner = ora10("Fetching your payment methods...").start();
|
|
@@ -3292,7 +3094,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3292
3094
|
const payments = payRes.data.paymentMethods;
|
|
3293
3095
|
if (payments.length > 0) {
|
|
3294
3096
|
const payChoices = [
|
|
3295
|
-
{ name:
|
|
3097
|
+
{ name: chalk12.green("Accept all payment methods"), value: "__ALL__" },
|
|
3296
3098
|
...payments.map((p) => ({
|
|
3297
3099
|
name: `${p.label}${p.brand ? ` (${p.brand})` : ""}`,
|
|
3298
3100
|
value: p.id,
|
|
@@ -3314,11 +3116,11 @@ function registerAdvertiseCommands(program2) {
|
|
|
3314
3116
|
paymentMethods = JSON.stringify(selectedPayments);
|
|
3315
3117
|
}
|
|
3316
3118
|
} else {
|
|
3317
|
-
console.log(
|
|
3119
|
+
console.log(chalk12.dim(" No payment methods found. You can add one later with: clishop payment add"));
|
|
3318
3120
|
}
|
|
3319
3121
|
} catch {
|
|
3320
3122
|
paySpinner.stop();
|
|
3321
|
-
console.log(
|
|
3123
|
+
console.log(chalk12.dim(" Could not fetch payment methods. Skipping."));
|
|
3322
3124
|
}
|
|
3323
3125
|
const body = {
|
|
3324
3126
|
title: answers.title,
|
|
@@ -3357,9 +3159,9 @@ function registerAdvertiseCommands(program2) {
|
|
|
3357
3159
|
}
|
|
3358
3160
|
const spinner = ora10("Publishing your request...").start();
|
|
3359
3161
|
const res = await api.post("/advertise", body);
|
|
3360
|
-
spinner.succeed(
|
|
3361
|
-
console.log(
|
|
3362
|
-
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}
|
|
3363
3165
|
`));
|
|
3364
3166
|
} catch (error) {
|
|
3365
3167
|
handleApiError(error);
|
|
@@ -3407,7 +3209,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3407
3209
|
const spinner = ora10("Publishing your request...").start();
|
|
3408
3210
|
const api = getApiClient();
|
|
3409
3211
|
const res = await api.post("/advertise", body);
|
|
3410
|
-
spinner.succeed(
|
|
3212
|
+
spinner.succeed(chalk12.green(`Request published! ID: ${chalk12.bold(res.data.advertise.id)}`));
|
|
3411
3213
|
} catch (error) {
|
|
3412
3214
|
handleApiError(error);
|
|
3413
3215
|
}
|
|
@@ -3426,18 +3228,18 @@ function registerAdvertiseCommands(program2) {
|
|
|
3426
3228
|
return;
|
|
3427
3229
|
}
|
|
3428
3230
|
if (ads.length === 0) {
|
|
3429
|
-
console.log(
|
|
3430
|
-
console.log(
|
|
3231
|
+
console.log(chalk12.yellow("\nNo advertised requests found.\n"));
|
|
3232
|
+
console.log(chalk12.dim(" Create one with: clishop advertise create\n"));
|
|
3431
3233
|
return;
|
|
3432
3234
|
}
|
|
3433
|
-
console.log(
|
|
3235
|
+
console.log(chalk12.bold("\n\u{1F4E2} Your Advertised Requests:\n"));
|
|
3434
3236
|
for (const ad of ads) {
|
|
3435
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3237
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3436
3238
|
const date = new Date(ad.createdAt).toLocaleDateString();
|
|
3437
3239
|
const bidCount = ad.bids?.length || 0;
|
|
3438
|
-
const bidInfo = bidCount > 0 ?
|
|
3240
|
+
const bidInfo = bidCount > 0 ? chalk12.cyan(` (${bidCount} bid${bidCount > 1 ? "s" : ""})`) : chalk12.dim(" (no bids yet)");
|
|
3439
3241
|
console.log(
|
|
3440
|
-
` ${
|
|
3242
|
+
` ${chalk12.bold(ad.id)} ${statusColor(ad.status.toUpperCase().padEnd(10))} ${chalk12.bold(ad.title)}${bidInfo} ${chalk12.dim(date)}`
|
|
3441
3243
|
);
|
|
3442
3244
|
const meta = [];
|
|
3443
3245
|
if (ad.quantity > 1) meta.push(`qty: ${ad.quantity}`);
|
|
@@ -3449,13 +3251,13 @@ function registerAdvertiseCommands(program2) {
|
|
|
3449
3251
|
if (ad.recurring) meta.push("recurring");
|
|
3450
3252
|
if (ad.address) meta.push(`\u2192 ${ad.address.label}`);
|
|
3451
3253
|
if (meta.length) {
|
|
3452
|
-
console.log(` ${
|
|
3254
|
+
console.log(` ${chalk12.dim(meta.join(" \xB7 "))}`);
|
|
3453
3255
|
}
|
|
3454
3256
|
console.log();
|
|
3455
3257
|
}
|
|
3456
3258
|
const totalPages = Math.ceil(res.data.total / res.data.pageSize);
|
|
3457
3259
|
if (totalPages > 1) {
|
|
3458
|
-
console.log(
|
|
3260
|
+
console.log(chalk12.dim(` Page ${res.data.page} of ${totalPages}. Use --page to navigate.
|
|
3459
3261
|
`));
|
|
3460
3262
|
}
|
|
3461
3263
|
} catch (error) {
|
|
@@ -3473,10 +3275,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3473
3275
|
console.log(JSON.stringify(ad, null, 2));
|
|
3474
3276
|
return;
|
|
3475
3277
|
}
|
|
3476
|
-
const statusColor = STATUS_COLORS2[ad.status] ||
|
|
3278
|
+
const statusColor = STATUS_COLORS2[ad.status] || chalk12.white;
|
|
3477
3279
|
console.log();
|
|
3478
|
-
console.log(
|
|
3479
|
-
console.log(` ID: ${
|
|
3280
|
+
console.log(chalk12.bold.cyan(` \u{1F4E2} ${ad.title}`));
|
|
3281
|
+
console.log(` ID: ${chalk12.dim(ad.id)}`);
|
|
3480
3282
|
console.log(` Status: ${statusColor(ad.status.toUpperCase())}`);
|
|
3481
3283
|
if (ad.description) console.log(` Details: ${ad.description}`);
|
|
3482
3284
|
if (ad.sku) console.log(` SKU: ${ad.sku}`);
|
|
@@ -3484,10 +3286,10 @@ function registerAdvertiseCommands(program2) {
|
|
|
3484
3286
|
if (ad.company) console.log(` Company: ${ad.company}`);
|
|
3485
3287
|
if (ad.features) console.log(` Features: ${ad.features}`);
|
|
3486
3288
|
console.log(` Quantity: ${ad.quantity}`);
|
|
3487
|
-
if (ad.bidPriceInCents) console.log(` Max Bid: ${
|
|
3289
|
+
if (ad.bidPriceInCents) console.log(` Max Bid: ${chalk12.bold(formatPrice5(ad.bidPriceInCents, ad.currency))}`);
|
|
3488
3290
|
if (ad.speedDays) console.log(` Speed: ${ad.speedDays}-day delivery`);
|
|
3489
3291
|
const returnReqs = [];
|
|
3490
|
-
if (ad.freeReturns) returnReqs.push(
|
|
3292
|
+
if (ad.freeReturns) returnReqs.push(chalk12.green("Free Returns required"));
|
|
3491
3293
|
if (ad.minReturnDays) returnReqs.push(`${ad.minReturnDays}-day return window min`);
|
|
3492
3294
|
if (returnReqs.length) console.log(` Returns: ${returnReqs.join(" \xB7 ")}`);
|
|
3493
3295
|
if (ad.recurring) console.log(` Recurring: Yes${ad.recurringNote ? ` (${ad.recurringNote})` : ""}`);
|
|
@@ -3497,7 +3299,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3497
3299
|
}
|
|
3498
3300
|
if (ad.paymentMethods) {
|
|
3499
3301
|
if (ad.paymentMethods === "all") {
|
|
3500
|
-
console.log(` Payment: ${
|
|
3302
|
+
console.log(` Payment: ${chalk12.green("All methods accepted")}`);
|
|
3501
3303
|
} else {
|
|
3502
3304
|
try {
|
|
3503
3305
|
const ids = JSON.parse(ad.paymentMethods);
|
|
@@ -3511,20 +3313,20 @@ function registerAdvertiseCommands(program2) {
|
|
|
3511
3313
|
console.log(` Created: ${new Date(ad.createdAt).toLocaleString()}`);
|
|
3512
3314
|
const bids = ad.bids || [];
|
|
3513
3315
|
if (bids.length === 0) {
|
|
3514
|
-
console.log(
|
|
3316
|
+
console.log(chalk12.dim("\n No bids yet. Vendors will be able to see your request and submit bids."));
|
|
3515
3317
|
} else {
|
|
3516
|
-
console.log(
|
|
3318
|
+
console.log(chalk12.bold(`
|
|
3517
3319
|
Bids (${bids.length}):
|
|
3518
3320
|
`));
|
|
3519
3321
|
for (const bid of bids) {
|
|
3520
|
-
const bidStatusColor = BID_STATUS_COLORS[bid.status] ||
|
|
3521
|
-
const storeBadge = bid.store?.verified ?
|
|
3522
|
-
const storeRating = bid.store?.rating != null ?
|
|
3523
|
-
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))}`);
|
|
3524
3326
|
console.log(` Store: ${bid.store?.name || bid.storeId}${storeBadge}${storeRating}`);
|
|
3525
3327
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3526
3328
|
const bidReturns = [];
|
|
3527
|
-
if (bid.freeReturns) bidReturns.push(
|
|
3329
|
+
if (bid.freeReturns) bidReturns.push(chalk12.green("Free Returns"));
|
|
3528
3330
|
if (bid.returnWindowDays) bidReturns.push(`${bid.returnWindowDays}-day return window`);
|
|
3529
3331
|
if (bidReturns.length) console.log(` Returns: ${bidReturns.join(" \xB7 ")}`);
|
|
3530
3332
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
@@ -3535,8 +3337,8 @@ function registerAdvertiseCommands(program2) {
|
|
|
3535
3337
|
if (ad.status === "open") {
|
|
3536
3338
|
const pendingBids = bids.filter((b) => b.status === "pending");
|
|
3537
3339
|
if (pendingBids.length > 0) {
|
|
3538
|
-
console.log(
|
|
3539
|
-
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>
|
|
3540
3342
|
`));
|
|
3541
3343
|
}
|
|
3542
3344
|
}
|
|
@@ -3555,15 +3357,15 @@ function registerAdvertiseCommands(program2) {
|
|
|
3555
3357
|
const ad = detailRes.data.advertise;
|
|
3556
3358
|
const bid = ad.bids?.find((b) => b.id === bidId);
|
|
3557
3359
|
if (!bid) {
|
|
3558
|
-
console.error(
|
|
3360
|
+
console.error(chalk12.red(`
|
|
3559
3361
|
\u2717 Bid ${bidId} not found on request ${advertiseId}.`));
|
|
3560
3362
|
process.exitCode = 1;
|
|
3561
3363
|
return;
|
|
3562
3364
|
}
|
|
3563
|
-
console.log(
|
|
3365
|
+
console.log(chalk12.bold("\n Accept this bid?\n"));
|
|
3564
3366
|
console.log(` Request: ${ad.title}`);
|
|
3565
|
-
console.log(` Store: ${bid.store?.name || bid.storeId}${bid.store?.verified ?
|
|
3566
|
-
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))}`);
|
|
3567
3369
|
if (bid.shippingDays != null) console.log(` Delivery: ${bid.shippingDays}-day`);
|
|
3568
3370
|
if (bid.note) console.log(` Note: ${bid.note}`);
|
|
3569
3371
|
console.log();
|
|
@@ -3576,12 +3378,12 @@ function registerAdvertiseCommands(program2) {
|
|
|
3576
3378
|
}
|
|
3577
3379
|
]);
|
|
3578
3380
|
if (!confirm) {
|
|
3579
|
-
console.log(
|
|
3381
|
+
console.log(chalk12.yellow("Cancelled."));
|
|
3580
3382
|
return;
|
|
3581
3383
|
}
|
|
3582
3384
|
const spinner = ora10("Accepting bid...").start();
|
|
3583
3385
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/accept`);
|
|
3584
|
-
spinner.succeed(
|
|
3386
|
+
spinner.succeed(chalk12.green("Bid accepted! The vendor will now fulfill your request."));
|
|
3585
3387
|
} catch (error) {
|
|
3586
3388
|
handleApiError(error);
|
|
3587
3389
|
}
|
|
@@ -3600,7 +3402,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3600
3402
|
const spinner = ora10("Rejecting bid...").start();
|
|
3601
3403
|
const api = getApiClient();
|
|
3602
3404
|
await api.post(`/advertise/${advertiseId}/bids/${bidId}/reject`);
|
|
3603
|
-
spinner.succeed(
|
|
3405
|
+
spinner.succeed(chalk12.green("Bid rejected."));
|
|
3604
3406
|
} catch (error) {
|
|
3605
3407
|
handleApiError(error);
|
|
3606
3408
|
}
|
|
@@ -3619,7 +3421,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3619
3421
|
const spinner = ora10("Cancelling request...").start();
|
|
3620
3422
|
const api = getApiClient();
|
|
3621
3423
|
await api.post(`/advertise/${id}/cancel`);
|
|
3622
|
-
spinner.succeed(
|
|
3424
|
+
spinner.succeed(chalk12.green("Request cancelled."));
|
|
3623
3425
|
} catch (error) {
|
|
3624
3426
|
handleApiError(error);
|
|
3625
3427
|
}
|
|
@@ -3627,7 +3429,7 @@ function registerAdvertiseCommands(program2) {
|
|
|
3627
3429
|
}
|
|
3628
3430
|
|
|
3629
3431
|
// src/commands/support.ts
|
|
3630
|
-
import
|
|
3432
|
+
import chalk13 from "chalk";
|
|
3631
3433
|
import ora11 from "ora";
|
|
3632
3434
|
import inquirer9 from "inquirer";
|
|
3633
3435
|
var CATEGORY_CHOICES = [
|
|
@@ -3646,18 +3448,18 @@ var PRIORITY_CHOICES = [
|
|
|
3646
3448
|
{ name: "Urgent", value: "urgent" }
|
|
3647
3449
|
];
|
|
3648
3450
|
var STATUS_COLORS3 = {
|
|
3649
|
-
open:
|
|
3650
|
-
in_progress:
|
|
3651
|
-
awaiting_customer:
|
|
3652
|
-
awaiting_store:
|
|
3653
|
-
resolved:
|
|
3654
|
-
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
|
|
3655
3457
|
};
|
|
3656
3458
|
var PRIORITY_COLORS = {
|
|
3657
|
-
low:
|
|
3658
|
-
normal:
|
|
3659
|
-
high:
|
|
3660
|
-
urgent:
|
|
3459
|
+
low: chalk13.dim,
|
|
3460
|
+
normal: chalk13.white,
|
|
3461
|
+
high: chalk13.yellow,
|
|
3462
|
+
urgent: chalk13.red
|
|
3661
3463
|
};
|
|
3662
3464
|
function registerSupportCommands(program2) {
|
|
3663
3465
|
const support = program2.command("support").description("Manage support tickets for orders");
|
|
@@ -3690,7 +3492,7 @@ function registerSupportCommands(program2) {
|
|
|
3690
3492
|
}
|
|
3691
3493
|
]);
|
|
3692
3494
|
if (!answers.message || !answers.message.trim()) {
|
|
3693
|
-
console.error(
|
|
3495
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3694
3496
|
return;
|
|
3695
3497
|
}
|
|
3696
3498
|
const spinner = ora11("Creating support ticket...").start();
|
|
@@ -3702,17 +3504,17 @@ function registerSupportCommands(program2) {
|
|
|
3702
3504
|
priority: answers.priority,
|
|
3703
3505
|
message: answers.message.trim()
|
|
3704
3506
|
});
|
|
3705
|
-
spinner.succeed(
|
|
3507
|
+
spinner.succeed(chalk13.green("Support ticket created!"));
|
|
3706
3508
|
const t = res.data.ticket;
|
|
3707
3509
|
console.log();
|
|
3708
|
-
console.log(` ${
|
|
3709
|
-
console.log(` ${
|
|
3710
|
-
console.log(` ${
|
|
3711
|
-
console.log(` ${
|
|
3712
|
-
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)}`);
|
|
3713
3515
|
console.log();
|
|
3714
|
-
console.log(
|
|
3715
|
-
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}`));
|
|
3716
3518
|
console.log();
|
|
3717
3519
|
} catch (error) {
|
|
3718
3520
|
handleApiError(error);
|
|
@@ -3732,22 +3534,22 @@ function registerSupportCommands(program2) {
|
|
|
3732
3534
|
return;
|
|
3733
3535
|
}
|
|
3734
3536
|
if (tickets.length === 0) {
|
|
3735
|
-
console.log(
|
|
3537
|
+
console.log(chalk13.yellow("\nNo support tickets found.\n"));
|
|
3736
3538
|
return;
|
|
3737
3539
|
}
|
|
3738
|
-
console.log(
|
|
3540
|
+
console.log(chalk13.bold("\nYour Support Tickets:\n"));
|
|
3739
3541
|
for (const t of tickets) {
|
|
3740
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3741
|
-
const priorityColor = PRIORITY_COLORS[t.priority] ||
|
|
3542
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3543
|
+
const priorityColor = PRIORITY_COLORS[t.priority] || chalk13.white;
|
|
3742
3544
|
const date = new Date(t.createdAt).toLocaleDateString();
|
|
3743
3545
|
console.log(
|
|
3744
|
-
` ${
|
|
3546
|
+
` ${chalk13.bold(t.id)} ${statusColor(t.status.toUpperCase().padEnd(20))} ${priorityColor(t.priority.padEnd(8))} ${chalk13.dim(date)}`
|
|
3745
3547
|
);
|
|
3746
|
-
console.log(` ${
|
|
3747
|
-
console.log(` ${
|
|
3548
|
+
console.log(` ${chalk13.bold(t.subject)}`);
|
|
3549
|
+
console.log(` ${chalk13.dim(`Store: ${t.storeName} \xB7 Order: ${t.orderId} \xB7 ${t.category}`)}`);
|
|
3748
3550
|
if (t.lastMessage) {
|
|
3749
|
-
const sender = t.lastMessage.senderType === "store" ?
|
|
3750
|
-
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)}`);
|
|
3751
3553
|
}
|
|
3752
3554
|
console.log();
|
|
3753
3555
|
}
|
|
@@ -3766,25 +3568,25 @@ function registerSupportCommands(program2) {
|
|
|
3766
3568
|
console.log(JSON.stringify(t, null, 2));
|
|
3767
3569
|
return;
|
|
3768
3570
|
}
|
|
3769
|
-
const statusColor = STATUS_COLORS3[t.status] ||
|
|
3571
|
+
const statusColor = STATUS_COLORS3[t.status] || chalk13.white;
|
|
3770
3572
|
console.log();
|
|
3771
|
-
console.log(
|
|
3772
|
-
console.log(` Subject: ${
|
|
3573
|
+
console.log(chalk13.bold(` Ticket ${chalk13.cyan(t.id)}`));
|
|
3574
|
+
console.log(` Subject: ${chalk13.bold(t.subject)}`);
|
|
3773
3575
|
console.log(` Status: ${statusColor(t.status.toUpperCase())}`);
|
|
3774
3576
|
console.log(` Category: ${t.category}`);
|
|
3775
|
-
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] ||
|
|
3577
|
+
console.log(` Priority: ${(PRIORITY_COLORS[t.priority] || chalk13.white)(t.priority)}`);
|
|
3776
3578
|
console.log(` Store: ${t.storeName}`);
|
|
3777
3579
|
console.log(` Order: ${t.orderId}`);
|
|
3778
3580
|
console.log(` Created: ${new Date(t.createdAt).toLocaleString()}`);
|
|
3779
3581
|
if (t.resolvedAt) console.log(` Resolved: ${new Date(t.resolvedAt).toLocaleString()}`);
|
|
3780
|
-
console.log(
|
|
3582
|
+
console.log(chalk13.bold("\n Messages:\n"));
|
|
3781
3583
|
for (const m of t.messages) {
|
|
3782
3584
|
const time = new Date(m.createdAt).toLocaleString();
|
|
3783
3585
|
let sender;
|
|
3784
|
-
if (m.senderType === "customer") sender =
|
|
3785
|
-
else if (m.senderType === "store") sender =
|
|
3786
|
-
else sender =
|
|
3787
|
-
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)}`);
|
|
3788
3590
|
console.log(` ${m.body}`);
|
|
3789
3591
|
console.log();
|
|
3790
3592
|
}
|
|
@@ -3802,7 +3604,7 @@ function registerSupportCommands(program2) {
|
|
|
3802
3604
|
}
|
|
3803
3605
|
]);
|
|
3804
3606
|
if (!message || !message.trim()) {
|
|
3805
|
-
console.error(
|
|
3607
|
+
console.error(chalk13.red("\n\u2717 Message is required.\n"));
|
|
3806
3608
|
return;
|
|
3807
3609
|
}
|
|
3808
3610
|
const spinner = ora11("Sending reply...").start();
|
|
@@ -3810,8 +3612,8 @@ function registerSupportCommands(program2) {
|
|
|
3810
3612
|
const res = await api.post(`/support/${ticketId}/reply`, {
|
|
3811
3613
|
message: message.trim()
|
|
3812
3614
|
});
|
|
3813
|
-
spinner.succeed(
|
|
3814
|
-
console.log(
|
|
3615
|
+
spinner.succeed(chalk13.green("Reply sent!"));
|
|
3616
|
+
console.log(chalk13.dim(` Ticket status: ${res.data.ticketStatus}
|
|
3815
3617
|
`));
|
|
3816
3618
|
} catch (error) {
|
|
3817
3619
|
handleApiError(error);
|
|
@@ -3831,7 +3633,235 @@ function registerSupportCommands(program2) {
|
|
|
3831
3633
|
const spinner = ora11("Closing ticket...").start();
|
|
3832
3634
|
const api = getApiClient();
|
|
3833
3635
|
await api.patch(`/support/${ticketId}/status`, { status: "closed" });
|
|
3834
|
-
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();
|
|
3835
3865
|
} catch (error) {
|
|
3836
3866
|
handleApiError(error);
|
|
3837
3867
|
}
|
|
@@ -3845,8 +3875,8 @@ program.name("clishop").version("0.1.0").description(
|
|
|
3845
3875
|
).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
|
|
3846
3876
|
const agentOpt = thisCommand.opts().agent;
|
|
3847
3877
|
if (agentOpt) {
|
|
3848
|
-
const
|
|
3849
|
-
if (!
|
|
3878
|
+
const config = getConfig();
|
|
3879
|
+
if (!config.store.agents[agentOpt]) {
|
|
3850
3880
|
console.error(chalk15.red(`\u2717 Agent "${agentOpt}" does not exist.`));
|
|
3851
3881
|
process.exit(1);
|
|
3852
3882
|
}
|
|
@@ -3866,11 +3896,17 @@ registerStatusCommand(program);
|
|
|
3866
3896
|
registerSetupCommand(program);
|
|
3867
3897
|
registerAdvertiseCommands(program);
|
|
3868
3898
|
registerSupportCommands(program);
|
|
3899
|
+
registerFeedbackCommands(program);
|
|
3869
3900
|
async function main() {
|
|
3901
|
+
if (process.argv.includes("--mcp")) {
|
|
3902
|
+
const { startMcpServer } = await import("./mcp.js");
|
|
3903
|
+
await startMcpServer();
|
|
3904
|
+
return;
|
|
3905
|
+
}
|
|
3870
3906
|
const hasSubcommand = process.argv.length > 2;
|
|
3871
3907
|
if (!hasSubcommand) {
|
|
3872
|
-
const
|
|
3873
|
-
if (!
|
|
3908
|
+
const config = getConfig();
|
|
3909
|
+
if (!config.get("setupCompleted")) {
|
|
3874
3910
|
await runSetupWizard();
|
|
3875
3911
|
return;
|
|
3876
3912
|
}
|