clishop 1.2.4 → 1.3.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/README.md +8 -8
- package/dist/{chunk-CVK6G342.js → chunk-ML7L6BAH.js} +109 -12
- package/dist/index.js +71 -8
- package/dist/mcp.js +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -31,12 +31,12 @@ CLISHOP is an open-source CLI that lets AI agents and humans search for products
|
|
|
31
31
|
|
|
32
32
|
## Highlights
|
|
33
33
|
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
34
|
+
- One query searches every store in the network. Results are filtered to what actually ships to your address.
|
|
35
|
+
- Set spending caps per order, require email confirmation before anything ships, or let it go through automatically — your call.
|
|
36
|
+
- Ships as a native [MCP server](https://modelcontextprotocol.io/) with 44 tools. Works with VS Code Copilot, Claude, Cursor, Windsurf, and anything else that speaks MCP.
|
|
37
|
+
- Can't find what you need? Post an advertise request and let vendors compete to fulfill it.
|
|
38
|
+
- Support tickets, product reviews, store reviews — all from the terminal.
|
|
39
|
+
- Anyone can sell on CLISHOP by deploying a [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE). No website needed.
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
@@ -132,7 +132,7 @@ clishop buy 1
|
|
|
132
132
|
|
|
133
133
|
## Sell on CLISHOP
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
You can run your own store with the [Dark Store](https://github.com/DavooxBv2/CLISHOP-DARKSTORE) template. Define your catalog, shipping rules, and pricing in YAML, deploy to Vercel, and you're live. No website needed.
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
@@ -150,7 +150,7 @@ npm run lint # Type-check
|
|
|
150
150
|
|
|
151
151
|
## MCP Server
|
|
152
152
|
|
|
153
|
-
CLISHOP ships as a native MCP server with
|
|
153
|
+
CLISHOP ships as a native MCP server with 44 tools. Any MCP-compatible client gets shopping capabilities out of the box.
|
|
154
154
|
|
|
155
155
|
```bash
|
|
156
156
|
clishop-mcp # If installed globally
|
|
@@ -3,12 +3,107 @@ import {
|
|
|
3
3
|
} from "./chunk-X3H7SYR4.js";
|
|
4
4
|
|
|
5
5
|
// src/auth.ts
|
|
6
|
-
import
|
|
6
|
+
import { createRequire } from "module";
|
|
7
7
|
import axios from "axios";
|
|
8
|
+
|
|
9
|
+
// src/auth-file-store.ts
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "fs";
|
|
11
|
+
import { join } from "path";
|
|
12
|
+
import { homedir } from "os";
|
|
13
|
+
var CONFIG_DIR = join(homedir(), ".config", "clishop");
|
|
14
|
+
var AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
15
|
+
function ensureDir() {
|
|
16
|
+
mkdirSync(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
17
|
+
}
|
|
18
|
+
function readStore() {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function writeStore(data) {
|
|
26
|
+
ensureDir();
|
|
27
|
+
writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
|
|
28
|
+
try {
|
|
29
|
+
chmodSync(AUTH_FILE, 384);
|
|
30
|
+
} catch {
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function fileSetPassword(service, account, password) {
|
|
34
|
+
const store = readStore();
|
|
35
|
+
store[`${service}:${account}`] = password;
|
|
36
|
+
writeStore(store);
|
|
37
|
+
}
|
|
38
|
+
function fileGetPassword(service, account) {
|
|
39
|
+
const store = readStore();
|
|
40
|
+
return store[`${service}:${account}`] ?? null;
|
|
41
|
+
}
|
|
42
|
+
function fileDeletePassword(service, account) {
|
|
43
|
+
const store = readStore();
|
|
44
|
+
delete store[`${service}:${account}`];
|
|
45
|
+
writeStore(store);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/auth.ts
|
|
49
|
+
var require2 = createRequire(import.meta.url);
|
|
50
|
+
var _keytar = null;
|
|
51
|
+
var _keytarChecked = false;
|
|
52
|
+
function getKeytar() {
|
|
53
|
+
if (_keytarChecked) return _keytar;
|
|
54
|
+
_keytarChecked = true;
|
|
55
|
+
try {
|
|
56
|
+
_keytar = require2("keytar");
|
|
57
|
+
} catch {
|
|
58
|
+
_keytar = null;
|
|
59
|
+
}
|
|
60
|
+
return _keytar;
|
|
61
|
+
}
|
|
8
62
|
var SERVICE_NAME = "clishop";
|
|
9
63
|
var ACCOUNT_TOKEN = "auth-token";
|
|
10
64
|
var ACCOUNT_REFRESH = "refresh-token";
|
|
11
65
|
var ACCOUNT_USER = "user-info";
|
|
66
|
+
var _activeBackend = null;
|
|
67
|
+
function resolveBackend() {
|
|
68
|
+
if (_activeBackend) return _activeBackend;
|
|
69
|
+
const kt = getKeytar();
|
|
70
|
+
if (kt) {
|
|
71
|
+
_activeBackend = "keytar";
|
|
72
|
+
return _activeBackend;
|
|
73
|
+
}
|
|
74
|
+
if (!process.env.CLISHOP_QUIET) {
|
|
75
|
+
console.warn(
|
|
76
|
+
"[clishop] Secure keychain unavailable \u2014 using file-based token storage (~/.config/clishop/auth.json).\n To enable keychain on Ubuntu/WSL: sudo apt install libsecret-1-0\n"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
_activeBackend = "file";
|
|
80
|
+
return _activeBackend;
|
|
81
|
+
}
|
|
82
|
+
function isKeytarAvailable() {
|
|
83
|
+
return getKeytar() !== null;
|
|
84
|
+
}
|
|
85
|
+
async function setPassword(account, value) {
|
|
86
|
+
const backend = resolveBackend();
|
|
87
|
+
if (backend === "keytar") {
|
|
88
|
+
return getKeytar().setPassword(SERVICE_NAME, account, value);
|
|
89
|
+
}
|
|
90
|
+
fileSetPassword(SERVICE_NAME, account, value);
|
|
91
|
+
}
|
|
92
|
+
async function getPassword(account) {
|
|
93
|
+
const backend = resolveBackend();
|
|
94
|
+
if (backend === "keytar") {
|
|
95
|
+
return getKeytar().getPassword(SERVICE_NAME, account);
|
|
96
|
+
}
|
|
97
|
+
return fileGetPassword(SERVICE_NAME, account);
|
|
98
|
+
}
|
|
99
|
+
async function deletePassword(account) {
|
|
100
|
+
const backend = resolveBackend();
|
|
101
|
+
if (backend === "keytar") {
|
|
102
|
+
await getKeytar().deletePassword(SERVICE_NAME, account);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
fileDeletePassword(SERVICE_NAME, account);
|
|
106
|
+
}
|
|
12
107
|
function assertAuthResponse(data) {
|
|
13
108
|
const payload = data;
|
|
14
109
|
if (!payload || typeof payload !== "object") {
|
|
@@ -29,22 +124,22 @@ function assertAuthResponse(data) {
|
|
|
29
124
|
return payload;
|
|
30
125
|
}
|
|
31
126
|
async function storeToken(token) {
|
|
32
|
-
await
|
|
127
|
+
await setPassword(ACCOUNT_TOKEN, token);
|
|
33
128
|
}
|
|
34
129
|
async function storeRefreshToken(token) {
|
|
35
|
-
await
|
|
130
|
+
await setPassword(ACCOUNT_REFRESH, token);
|
|
36
131
|
}
|
|
37
132
|
async function storeUserInfo(user) {
|
|
38
|
-
await
|
|
133
|
+
await setPassword(ACCOUNT_USER, JSON.stringify(user));
|
|
39
134
|
}
|
|
40
135
|
async function getToken() {
|
|
41
|
-
return
|
|
136
|
+
return getPassword(ACCOUNT_TOKEN);
|
|
42
137
|
}
|
|
43
138
|
async function getRefreshToken() {
|
|
44
|
-
return
|
|
139
|
+
return getPassword(ACCOUNT_REFRESH);
|
|
45
140
|
}
|
|
46
141
|
async function getUserInfo() {
|
|
47
|
-
const raw = await
|
|
142
|
+
const raw = await getPassword(ACCOUNT_USER);
|
|
48
143
|
if (!raw) return null;
|
|
49
144
|
try {
|
|
50
145
|
return JSON.parse(raw);
|
|
@@ -53,13 +148,12 @@ async function getUserInfo() {
|
|
|
53
148
|
}
|
|
54
149
|
}
|
|
55
150
|
async function clearAuth() {
|
|
56
|
-
await
|
|
57
|
-
await
|
|
58
|
-
await
|
|
151
|
+
await deletePassword(ACCOUNT_TOKEN);
|
|
152
|
+
await deletePassword(ACCOUNT_REFRESH);
|
|
153
|
+
await deletePassword(ACCOUNT_USER);
|
|
59
154
|
}
|
|
60
155
|
async function isLoggedIn() {
|
|
61
|
-
|
|
62
|
-
return !!token;
|
|
156
|
+
return !!await getToken();
|
|
63
157
|
}
|
|
64
158
|
async function login(email, password) {
|
|
65
159
|
const baseUrl = getApiBaseUrl();
|
|
@@ -198,6 +292,9 @@ function handleApiError(error) {
|
|
|
198
292
|
}
|
|
199
293
|
|
|
200
294
|
export {
|
|
295
|
+
resolveBackend,
|
|
296
|
+
isKeytarAvailable,
|
|
297
|
+
getToken,
|
|
201
298
|
getUserInfo,
|
|
202
299
|
isLoggedIn,
|
|
203
300
|
login,
|
package/dist/index.js
CHANGED
|
@@ -2,18 +2,22 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ensureAgentOnBackend,
|
|
4
4
|
getApiClient,
|
|
5
|
+
getToken,
|
|
5
6
|
getUserInfo,
|
|
6
7
|
handleApiError,
|
|
8
|
+
isKeytarAvailable,
|
|
7
9
|
isLoggedIn,
|
|
8
10
|
login,
|
|
9
11
|
logout,
|
|
10
|
-
register
|
|
11
|
-
|
|
12
|
+
register,
|
|
13
|
+
resolveBackend
|
|
14
|
+
} from "./chunk-ML7L6BAH.js";
|
|
12
15
|
import {
|
|
13
16
|
createAgent,
|
|
14
17
|
deleteAgent,
|
|
15
18
|
getActiveAgent,
|
|
16
19
|
getAgent,
|
|
20
|
+
getApiBaseUrl,
|
|
17
21
|
getConfig,
|
|
18
22
|
listAgents,
|
|
19
23
|
setActiveAgent,
|
|
@@ -22,7 +26,7 @@ import {
|
|
|
22
26
|
|
|
23
27
|
// src/index.ts
|
|
24
28
|
import { Command } from "commander";
|
|
25
|
-
import
|
|
29
|
+
import chalk16 from "chalk";
|
|
26
30
|
|
|
27
31
|
// src/commands/auth.ts
|
|
28
32
|
import chalk from "chalk";
|
|
@@ -932,7 +936,7 @@ async function runSetupWizard() {
|
|
|
932
936
|
console.log();
|
|
933
937
|
console.log(chalk4.bold.cyan(" W E L C O M E T O C L I S H O P"));
|
|
934
938
|
console.log(chalk4.dim(" Order anything from your terminal."));
|
|
935
|
-
console.log(chalk4.dim(` Build: ${"2026-03-
|
|
939
|
+
console.log(chalk4.dim(` Build: ${"2026-03-22T16:20:26.194Z"}`));
|
|
936
940
|
console.log();
|
|
937
941
|
divider(chalk4.cyan);
|
|
938
942
|
console.log();
|
|
@@ -4580,16 +4584,74 @@ function registerFeedbackCommands(program2) {
|
|
|
4580
4584
|
});
|
|
4581
4585
|
}
|
|
4582
4586
|
|
|
4587
|
+
// src/commands/doctor.ts
|
|
4588
|
+
import chalk15 from "chalk";
|
|
4589
|
+
import { join } from "path";
|
|
4590
|
+
import { homedir } from "os";
|
|
4591
|
+
import { mkdirSync } from "fs";
|
|
4592
|
+
import axios from "axios";
|
|
4593
|
+
function registerDoctorCommand(program2) {
|
|
4594
|
+
program2.command("doctor").description("Check system compatibility and auth status").action(async () => {
|
|
4595
|
+
const checks = [];
|
|
4596
|
+
const keytarOk = isKeytarAvailable();
|
|
4597
|
+
checks.push({
|
|
4598
|
+
name: "Secure keychain (keytar)",
|
|
4599
|
+
ok: keytarOk,
|
|
4600
|
+
detail: keytarOk ? "keytar loaded successfully" : "keytar unavailable \u2014 install libsecret:\n sudo apt install libsecret-1-0"
|
|
4601
|
+
});
|
|
4602
|
+
const backend = resolveBackend();
|
|
4603
|
+
checks.push({
|
|
4604
|
+
name: "Auth backend",
|
|
4605
|
+
ok: true,
|
|
4606
|
+
detail: backend === "keytar" ? "Using OS keychain" : "Using file store (~/.config/clishop/auth.json)"
|
|
4607
|
+
});
|
|
4608
|
+
try {
|
|
4609
|
+
const dir = join(homedir(), ".config", "clishop");
|
|
4610
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
4611
|
+
checks.push({ name: "File store writable", ok: true, detail: dir });
|
|
4612
|
+
} catch (e) {
|
|
4613
|
+
checks.push({
|
|
4614
|
+
name: "File store writable",
|
|
4615
|
+
ok: false,
|
|
4616
|
+
detail: e instanceof Error ? e.message : String(e)
|
|
4617
|
+
});
|
|
4618
|
+
}
|
|
4619
|
+
const token = await getToken();
|
|
4620
|
+
checks.push({
|
|
4621
|
+
name: "Authenticated",
|
|
4622
|
+
ok: !!token,
|
|
4623
|
+
detail: token ? "Token present" : "Not logged in \u2014 run: clishop setup"
|
|
4624
|
+
});
|
|
4625
|
+
const apiUrl = getApiBaseUrl();
|
|
4626
|
+
try {
|
|
4627
|
+
await axios.get(`${apiUrl}/health`, { timeout: 5e3 });
|
|
4628
|
+
checks.push({ name: "API reachable", ok: true, detail: apiUrl });
|
|
4629
|
+
} catch {
|
|
4630
|
+
checks.push({
|
|
4631
|
+
name: "API reachable",
|
|
4632
|
+
ok: false,
|
|
4633
|
+
detail: `Cannot reach ${apiUrl}`
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
console.log(chalk15.bold("\n CLISHOP Doctor\n"));
|
|
4637
|
+
for (const c of checks) {
|
|
4638
|
+
const icon = c.ok ? chalk15.green("\u2713") : chalk15.red("\u2717");
|
|
4639
|
+
console.log(` ${icon} ${chalk15.bold(c.name)}: ${c.detail}`);
|
|
4640
|
+
}
|
|
4641
|
+
console.log();
|
|
4642
|
+
});
|
|
4643
|
+
}
|
|
4644
|
+
|
|
4583
4645
|
// src/index.ts
|
|
4584
4646
|
var program = new Command();
|
|
4585
|
-
program.name("clishop").version("1.
|
|
4586
|
-
|
|
4647
|
+
program.name("clishop").version("1.3.0").description(
|
|
4648
|
+
chalk16.bold("CLISHOP") + ' \u2014 Order anything from your terminal.\n\n Use agents to set safety limits, addresses, and payment methods.\n The "default" agent is used when no agent is specified.'
|
|
4587
4649
|
).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
|
|
4588
4650
|
const agentOpt = thisCommand.opts().agent;
|
|
4589
4651
|
if (agentOpt) {
|
|
4590
4652
|
const config = getConfig();
|
|
4591
4653
|
if (!config.store.agents[agentOpt]) {
|
|
4592
|
-
console.error(
|
|
4654
|
+
console.error(chalk16.red(`\u2717 Agent "${agentOpt}" does not exist.`));
|
|
4593
4655
|
process.exit(1);
|
|
4594
4656
|
}
|
|
4595
4657
|
process.env.__CLISHOP_AGENT_OVERRIDE = agentOpt;
|
|
@@ -4609,6 +4671,7 @@ registerSetupCommand(program);
|
|
|
4609
4671
|
registerAdvertiseCommands(program);
|
|
4610
4672
|
registerSupportCommands(program);
|
|
4611
4673
|
registerFeedbackCommands(program);
|
|
4674
|
+
registerDoctorCommand(program);
|
|
4612
4675
|
async function main() {
|
|
4613
4676
|
if (process.argv.includes("--mcp")) {
|
|
4614
4677
|
const { startMcpServer } = await import("./mcp.js");
|
|
@@ -4626,6 +4689,6 @@ async function main() {
|
|
|
4626
4689
|
await program.parseAsync(process.argv);
|
|
4627
4690
|
}
|
|
4628
4691
|
main().catch((err) => {
|
|
4629
|
-
console.error(
|
|
4692
|
+
console.error(chalk16.red(err.message));
|
|
4630
4693
|
process.exit(1);
|
|
4631
4694
|
});
|
package/dist/mcp.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clishop",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"mcpName": "io.github.StefDCL/clishop",
|
|
5
5
|
"description": "CLISHOP — Order anything from your terminal",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"dev:mcp": "tsx src/mcp.ts",
|
|
18
18
|
"start": "node dist/index.js",
|
|
19
19
|
"start:mcp": "node dist/mcp.js",
|
|
20
|
-
"lint": "tsc --noEmit"
|
|
20
|
+
"lint": "tsc --noEmit",
|
|
21
|
+
"postinstall": "node -e \"try{require('keytar')}catch{console.warn('\\n[clishop] Optional: install libsecret for secure keychain storage:\\n sudo apt install libsecret-1-0\\nWithout it, clishop uses file-based token storage.\\n')}\""
|
|
21
22
|
},
|
|
22
23
|
"repository": {
|
|
23
24
|
"type": "git",
|
|
@@ -49,10 +50,12 @@
|
|
|
49
50
|
"commander": "^14.0.3",
|
|
50
51
|
"conf": "^15.1.0",
|
|
51
52
|
"inquirer": "^13.2.4",
|
|
52
|
-
"keytar": "^7.9.0",
|
|
53
53
|
"open": "^11.0.0",
|
|
54
54
|
"ora": "^9.3.0"
|
|
55
55
|
},
|
|
56
|
+
"optionalDependencies": {
|
|
57
|
+
"keytar": "^7.9.0"
|
|
58
|
+
},
|
|
56
59
|
"devDependencies": {
|
|
57
60
|
"@types/inquirer": "^9.0.9",
|
|
58
61
|
"@types/keytar": "^4.4.0",
|