clishop 1.4.7 → 1.5.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 +31 -2
- package/dist/{chunk-EAXPWOMT.js → chunk-EM4ZGZOU.js} +75 -1
- package/dist/index.js +303 -94
- package/dist/mcp.js +17 -26
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,13 +74,37 @@ npm link
|
|
|
74
74
|
|
|
75
75
|
## Quick Start
|
|
76
76
|
|
|
77
|
-
You can create your account on [clishop.ai](https://clishop.ai) or do everything from the CLI
|
|
77
|
+
You can create your account on [clishop.ai](https://clishop.ai) or do everything from the CLI.
|
|
78
|
+
|
|
79
|
+
### Human-friendly setup
|
|
78
80
|
|
|
79
81
|
```bash
|
|
80
82
|
clishop setup
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
This starts the interactive setup flow, creates a resumable setup session, shows a secure payment link, and waits for completion.
|
|
86
|
+
|
|
87
|
+
### Agent-safe setup
|
|
88
|
+
|
|
89
|
+
For OpenClaw, MCP clients, Claude-style shells, and other tool runners, use the explicit setup session commands instead of scraping terminal output:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
clishop setup start --email user@example.com --json
|
|
93
|
+
clishop setup status --setup-id <setup_id> --json
|
|
94
|
+
clishop setup wait --setup-id <setup_id> --timeout 300 --json
|
|
95
|
+
clishop setup cancel --setup-id <setup_id> --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`setup start` returns immediately with:
|
|
99
|
+
|
|
100
|
+
- `setup_id`: the resumable setup session handle for the agent
|
|
101
|
+
- `setup_url`: the link the human must open in a browser
|
|
102
|
+
- `expires_at`: when the session expires
|
|
103
|
+
- `poll_after_seconds`: suggested polling interval
|
|
104
|
+
|
|
105
|
+
The agent sends `setup_url` to the human and keeps `setup_id` locally for `status`, `wait`, or `cancel`.
|
|
106
|
+
|
|
107
|
+
After setup is complete, add a shipping address and start ordering:
|
|
84
108
|
|
|
85
109
|
```
|
|
86
110
|
$ clishop search "wireless headphones"
|
|
@@ -179,6 +203,11 @@ clishop-mcp # If installed globally
|
|
|
179
203
|
npx -y clishop --mcp # Without installing
|
|
180
204
|
```
|
|
181
205
|
|
|
206
|
+
The MCP onboarding tools now follow the same resumable setup session model:
|
|
207
|
+
|
|
208
|
+
- `setup` starts the session and returns `setup_id` plus `setup_url`
|
|
209
|
+
- `setup_status` checks progress and finalizes auth when setup is complete
|
|
210
|
+
|
|
182
211
|
See the [MCP setup guides](https://clishop.ai/docs#mcp-overview) for VS Code, Claude Desktop, Cursor, and Windsurf configuration.
|
|
183
212
|
|
|
184
213
|
---
|
|
@@ -141,6 +141,76 @@ async function storeAuthFromSetup(data) {
|
|
|
141
141
|
await storeRefreshToken(data.refreshToken);
|
|
142
142
|
await storeUserInfo(data.user);
|
|
143
143
|
}
|
|
144
|
+
async function postSetupRequest(path, body) {
|
|
145
|
+
const baseUrl = getApiBaseUrl();
|
|
146
|
+
try {
|
|
147
|
+
const res = await axios.post(`${baseUrl}${path}`, body);
|
|
148
|
+
return res.data;
|
|
149
|
+
} catch (error) {
|
|
150
|
+
if (error?.response?.data) {
|
|
151
|
+
return error.response.data;
|
|
152
|
+
}
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function startSetupSession(email) {
|
|
157
|
+
const data = await postSetupRequest("/auth/setup-link", { email });
|
|
158
|
+
if (!data.setupUrl || !(data.setupId || data.deviceCode) || !data.expiresIn || !data.pollInterval) {
|
|
159
|
+
throw new Error(data?.message || "Failed to create setup session.");
|
|
160
|
+
}
|
|
161
|
+
const setupId = data.setupId || data.deviceCode;
|
|
162
|
+
const expiresAt = data.expiresAt || new Date(Date.now() + data.expiresIn * 1e3).toISOString();
|
|
163
|
+
return {
|
|
164
|
+
ok: true,
|
|
165
|
+
setup_id: setupId,
|
|
166
|
+
status: "pending_user_action",
|
|
167
|
+
next_action: "open_setup_url",
|
|
168
|
+
setup_url: data.setupUrl,
|
|
169
|
+
expires_at: expiresAt,
|
|
170
|
+
poll_after_seconds: data.pollInterval,
|
|
171
|
+
human_message: "Open this link to securely connect your payment method."
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async function getSetupStatus(setupId) {
|
|
175
|
+
return postSetupRequest("/auth/setup/status", { setupId });
|
|
176
|
+
}
|
|
177
|
+
async function cancelSetupSession(setupId) {
|
|
178
|
+
return postSetupRequest("/auth/setup/cancel", { setupId });
|
|
179
|
+
}
|
|
180
|
+
async function claimSetupSession(setupId, { storeAuth = true } = {}) {
|
|
181
|
+
const data = await postSetupRequest("/auth/setup/claim", { setupId });
|
|
182
|
+
if (storeAuth && data.ok && data.token && data.refreshToken && data.user) {
|
|
183
|
+
await storeAuthFromSetup({
|
|
184
|
+
token: data.token,
|
|
185
|
+
refreshToken: data.refreshToken,
|
|
186
|
+
user: data.user
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return data;
|
|
190
|
+
}
|
|
191
|
+
async function waitForSetupSession(setupId, { timeout = 3e5 } = {}) {
|
|
192
|
+
const deadline = Date.now() + timeout;
|
|
193
|
+
while (Date.now() < deadline) {
|
|
194
|
+
const status = await getSetupStatus(setupId);
|
|
195
|
+
if (status.status === "completed") {
|
|
196
|
+
return claimSetupSession(setupId);
|
|
197
|
+
}
|
|
198
|
+
if (status.status === "expired" || status.status === "cancelled" || status.status === "failed") {
|
|
199
|
+
return status;
|
|
200
|
+
}
|
|
201
|
+
const waitMs = Math.max(1, status.poll_after_seconds || 5) * 1e3;
|
|
202
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
ok: false,
|
|
206
|
+
setup_id: setupId,
|
|
207
|
+
status: "pending_user_action",
|
|
208
|
+
error: {
|
|
209
|
+
code: "human_action_required",
|
|
210
|
+
message: "Timed out waiting for payment setup to complete."
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
144
214
|
async function logout() {
|
|
145
215
|
const baseUrl = getApiBaseUrl();
|
|
146
216
|
const token = await getToken();
|
|
@@ -261,7 +331,11 @@ export {
|
|
|
261
331
|
getToken,
|
|
262
332
|
getUserInfo,
|
|
263
333
|
isLoggedIn,
|
|
264
|
-
|
|
334
|
+
startSetupSession,
|
|
335
|
+
getSetupStatus,
|
|
336
|
+
cancelSetupSession,
|
|
337
|
+
claimSetupSession,
|
|
338
|
+
waitForSetupSession,
|
|
265
339
|
logout,
|
|
266
340
|
getApiClient,
|
|
267
341
|
ensureAgentOnBackend,
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
cancelSetupSession,
|
|
4
|
+
claimSetupSession,
|
|
3
5
|
ensureAgentOnBackend,
|
|
4
6
|
getApiClient,
|
|
7
|
+
getSetupStatus,
|
|
5
8
|
getToken,
|
|
6
9
|
getUserInfo,
|
|
7
10
|
handleApiError,
|
|
@@ -9,8 +12,9 @@ import {
|
|
|
9
12
|
isLoggedIn,
|
|
10
13
|
logout,
|
|
11
14
|
resolveBackend,
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
startSetupSession,
|
|
16
|
+
waitForSetupSession
|
|
17
|
+
} from "./chunk-EM4ZGZOU.js";
|
|
14
18
|
import {
|
|
15
19
|
createAgent,
|
|
16
20
|
deleteAgent,
|
|
@@ -51,6 +55,7 @@ function registerAuthCommands(program2) {
|
|
|
51
55
|
program2.command("whoami").description("Show the currently logged-in user").action(async () => {
|
|
52
56
|
if (!await isLoggedIn()) {
|
|
53
57
|
console.log(chalk.yellow("Not set up yet. Run: clishop setup"));
|
|
58
|
+
console.log(chalk.dim("For agent runners, use: clishop setup start --email <email> --json"));
|
|
54
59
|
return;
|
|
55
60
|
}
|
|
56
61
|
const user = await getUserInfo();
|
|
@@ -783,19 +788,19 @@ import chalk4 from "chalk";
|
|
|
783
788
|
import inquirer3 from "inquirer";
|
|
784
789
|
import open from "open";
|
|
785
790
|
import { execFileSync } from "child_process";
|
|
786
|
-
|
|
791
|
+
var DEFAULT_SETUP_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
787
792
|
async function openBrowser(url) {
|
|
788
793
|
try {
|
|
789
794
|
if (process.platform === "win32") {
|
|
790
795
|
execFileSync("cmd", ["/c", "start", "", url], { stdio: "ignore" });
|
|
791
796
|
return true;
|
|
792
|
-
}
|
|
797
|
+
}
|
|
798
|
+
if (process.platform === "darwin") {
|
|
793
799
|
execFileSync("open", [url], { stdio: "ignore" });
|
|
794
800
|
return true;
|
|
795
|
-
} else {
|
|
796
|
-
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
797
|
-
return true;
|
|
798
801
|
}
|
|
802
|
+
execFileSync("xdg-open", [url], { stdio: "ignore" });
|
|
803
|
+
return true;
|
|
799
804
|
} catch {
|
|
800
805
|
}
|
|
801
806
|
try {
|
|
@@ -808,6 +813,29 @@ async function openBrowser(url) {
|
|
|
808
813
|
function divider(color = chalk4.cyan) {
|
|
809
814
|
console.log(" " + color("\u2500".repeat(48)));
|
|
810
815
|
}
|
|
816
|
+
function writeJson(payload) {
|
|
817
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
818
|
+
}
|
|
819
|
+
function isInteractiveSession() {
|
|
820
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
821
|
+
}
|
|
822
|
+
function isLikelyEmail(value) {
|
|
823
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
824
|
+
}
|
|
825
|
+
function buildCliError(code, message, extra = {}) {
|
|
826
|
+
return {
|
|
827
|
+
ok: false,
|
|
828
|
+
error: {
|
|
829
|
+
code,
|
|
830
|
+
message,
|
|
831
|
+
...extra
|
|
832
|
+
}
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
function sanitizeSetupPayload(payload) {
|
|
836
|
+
const { token, refreshToken, user, ...rest } = payload;
|
|
837
|
+
return rest;
|
|
838
|
+
}
|
|
811
839
|
function printSetupLink(message, setupUrl) {
|
|
812
840
|
console.log();
|
|
813
841
|
console.log(chalk4.bold(` ${message}`));
|
|
@@ -818,14 +846,225 @@ function printSetupLink(message, setupUrl) {
|
|
|
818
846
|
console.log(" " + setupUrl);
|
|
819
847
|
console.log();
|
|
820
848
|
}
|
|
849
|
+
function printSetupStartResult(result) {
|
|
850
|
+
console.log();
|
|
851
|
+
console.log(chalk4.bold(" Setup session created."));
|
|
852
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
853
|
+
console.log(chalk4.dim(` Expires: ${new Date(result.expires_at).toLocaleString()}`));
|
|
854
|
+
printSetupLink("Give this link to your human to configure the payment method:", result.setup_url);
|
|
855
|
+
console.log(chalk4.dim(" Check progress later with:"));
|
|
856
|
+
console.log(chalk4.white(` clishop setup status --setup-id ${result.setup_id}`));
|
|
857
|
+
console.log(chalk4.white(` clishop setup wait --setup-id ${result.setup_id}`));
|
|
858
|
+
console.log();
|
|
859
|
+
}
|
|
860
|
+
function printSetupStatusResult(result) {
|
|
861
|
+
console.log();
|
|
862
|
+
if (!result.ok) {
|
|
863
|
+
console.log(chalk4.red(` \u2717 ${result.error?.message || "Setup failed."}`));
|
|
864
|
+
if (result.setup_id) {
|
|
865
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
866
|
+
}
|
|
867
|
+
console.log();
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
switch (result.status) {
|
|
871
|
+
case "pending_user_action":
|
|
872
|
+
console.log(chalk4.yellow(" Waiting for the human to complete payment setup."));
|
|
873
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
874
|
+
if (result.expires_at) {
|
|
875
|
+
console.log(chalk4.dim(` Expires: ${new Date(result.expires_at).toLocaleString()}`));
|
|
876
|
+
}
|
|
877
|
+
console.log();
|
|
878
|
+
break;
|
|
879
|
+
case "completed":
|
|
880
|
+
console.log(chalk4.green(" \u2713 Payment linked and account activated!"));
|
|
881
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
882
|
+
if (result.account_id) {
|
|
883
|
+
console.log(chalk4.dim(` Account: ${result.account_id}`));
|
|
884
|
+
}
|
|
885
|
+
console.log();
|
|
886
|
+
break;
|
|
887
|
+
case "cancelled":
|
|
888
|
+
console.log(chalk4.yellow(" Setup session cancelled."));
|
|
889
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
890
|
+
console.log();
|
|
891
|
+
break;
|
|
892
|
+
case "expired":
|
|
893
|
+
console.log(chalk4.red(" Setup session expired."));
|
|
894
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
895
|
+
console.log();
|
|
896
|
+
break;
|
|
897
|
+
case "processing":
|
|
898
|
+
console.log(chalk4.dim(" Setup is being processed."));
|
|
899
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
900
|
+
console.log();
|
|
901
|
+
break;
|
|
902
|
+
default:
|
|
903
|
+
console.log(chalk4.red(" Setup failed."));
|
|
904
|
+
if (result.setup_id) {
|
|
905
|
+
console.log(chalk4.dim(` Setup ID: ${result.setup_id}`));
|
|
906
|
+
}
|
|
907
|
+
console.log();
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
async function activateCompletedSetup(result) {
|
|
912
|
+
if (!result.ok || result.status !== "completed" || !result.setup_id) {
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
const config = getConfig();
|
|
916
|
+
const currentUser = await getUserInfo();
|
|
917
|
+
if (currentUser && (!result.account_id || currentUser.id === result.account_id)) {
|
|
918
|
+
config.set("setupCompleted", true);
|
|
919
|
+
return result;
|
|
920
|
+
}
|
|
921
|
+
const claimed = await claimSetupSession(result.setup_id);
|
|
922
|
+
if (claimed.ok) {
|
|
923
|
+
config.set("setupCompleted", true);
|
|
924
|
+
}
|
|
925
|
+
return claimed;
|
|
926
|
+
}
|
|
927
|
+
async function runSetupStartCommand(email, json = false) {
|
|
928
|
+
const normalizedEmail = email.trim();
|
|
929
|
+
if (!isLikelyEmail(normalizedEmail)) {
|
|
930
|
+
const payload = buildCliError("invalid_email", "A valid email address is required.");
|
|
931
|
+
if (json) {
|
|
932
|
+
writeJson(payload);
|
|
933
|
+
} else {
|
|
934
|
+
console.error(chalk4.red(`
|
|
935
|
+
\u2717 ${payload.error.message}
|
|
936
|
+
`));
|
|
937
|
+
}
|
|
938
|
+
process.exitCode = 1;
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
try {
|
|
942
|
+
const result = await startSetupSession(normalizedEmail);
|
|
943
|
+
if (json) {
|
|
944
|
+
writeJson(sanitizeSetupPayload(result));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
printSetupStartResult(result);
|
|
948
|
+
} catch (error) {
|
|
949
|
+
const payload = buildCliError("internal_error", error?.message || "Failed to create setup session.");
|
|
950
|
+
if (json) {
|
|
951
|
+
writeJson(payload);
|
|
952
|
+
} else {
|
|
953
|
+
console.error(chalk4.red(`
|
|
954
|
+
\u2717 ${payload.error.message}
|
|
955
|
+
`));
|
|
956
|
+
}
|
|
957
|
+
process.exitCode = 1;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
async function runSetupStatusCommand(setupId, json = false) {
|
|
961
|
+
try {
|
|
962
|
+
let result = await getSetupStatus(setupId);
|
|
963
|
+
result = await activateCompletedSetup(result);
|
|
964
|
+
if (json) {
|
|
965
|
+
writeJson(sanitizeSetupPayload(result));
|
|
966
|
+
} else {
|
|
967
|
+
printSetupStatusResult(result);
|
|
968
|
+
}
|
|
969
|
+
if (!result.ok) {
|
|
970
|
+
process.exitCode = 1;
|
|
971
|
+
}
|
|
972
|
+
} catch (error) {
|
|
973
|
+
const payload = buildCliError("internal_error", error?.message || "Failed to fetch setup status.");
|
|
974
|
+
if (json) {
|
|
975
|
+
writeJson(payload);
|
|
976
|
+
} else {
|
|
977
|
+
console.error(chalk4.red(`
|
|
978
|
+
\u2717 ${payload.error.message}
|
|
979
|
+
`));
|
|
980
|
+
}
|
|
981
|
+
process.exitCode = 1;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
async function runSetupCancelCommand(setupId, json = false) {
|
|
985
|
+
try {
|
|
986
|
+
const result = await cancelSetupSession(setupId);
|
|
987
|
+
if (json) {
|
|
988
|
+
writeJson(sanitizeSetupPayload(result));
|
|
989
|
+
} else {
|
|
990
|
+
printSetupStatusResult(result);
|
|
991
|
+
}
|
|
992
|
+
if (!result.ok) {
|
|
993
|
+
process.exitCode = 1;
|
|
994
|
+
}
|
|
995
|
+
} catch (error) {
|
|
996
|
+
const payload = buildCliError("internal_error", error?.message || "Failed to cancel setup session.");
|
|
997
|
+
if (json) {
|
|
998
|
+
writeJson(payload);
|
|
999
|
+
} else {
|
|
1000
|
+
console.error(chalk4.red(`
|
|
1001
|
+
\u2717 ${payload.error.message}
|
|
1002
|
+
`));
|
|
1003
|
+
}
|
|
1004
|
+
process.exitCode = 1;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
async function runSetupWaitCommand(setupId, timeoutSeconds, json = false) {
|
|
1008
|
+
try {
|
|
1009
|
+
if (!json) {
|
|
1010
|
+
console.log();
|
|
1011
|
+
console.log(chalk4.dim(` Waiting up to ${timeoutSeconds}s for payment setup to complete...`));
|
|
1012
|
+
console.log(chalk4.dim(` Setup ID: ${setupId}`));
|
|
1013
|
+
console.log();
|
|
1014
|
+
}
|
|
1015
|
+
const result = await waitForSetupSession(setupId, {
|
|
1016
|
+
timeout: timeoutSeconds * 1e3
|
|
1017
|
+
});
|
|
1018
|
+
if (result.ok && result.status === "completed") {
|
|
1019
|
+
getConfig().set("setupCompleted", true);
|
|
1020
|
+
}
|
|
1021
|
+
if (json) {
|
|
1022
|
+
writeJson(sanitizeSetupPayload(result));
|
|
1023
|
+
} else {
|
|
1024
|
+
printSetupStatusResult(result);
|
|
1025
|
+
}
|
|
1026
|
+
if (!result.ok) {
|
|
1027
|
+
process.exitCode = 1;
|
|
1028
|
+
}
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
const payload = buildCliError("internal_error", error?.message || "Failed while waiting for setup.");
|
|
1031
|
+
if (json) {
|
|
1032
|
+
writeJson(payload);
|
|
1033
|
+
} else {
|
|
1034
|
+
console.error(chalk4.red(`
|
|
1035
|
+
\u2717 ${payload.error.message}
|
|
1036
|
+
`));
|
|
1037
|
+
}
|
|
1038
|
+
process.exitCode = 1;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
821
1041
|
function registerSetupCommand(program2) {
|
|
822
|
-
program2.command("setup").description(
|
|
823
|
-
|
|
824
|
-
)
|
|
825
|
-
|
|
1042
|
+
const setup = program2.command("setup").description("Set up your CLISHOP account or manage setup sessions").argument("[email]", "Email address for the human-friendly wrapper").action(async (email) => {
|
|
1043
|
+
await runSetupWizard(email);
|
|
1044
|
+
});
|
|
1045
|
+
setup.command("start").description("Create a setup session and return immediately").requiredOption("--email <email>", "Email address").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
1046
|
+
await runSetupStartCommand(opts.email, opts.json);
|
|
1047
|
+
});
|
|
1048
|
+
setup.command("status").description("Check the status of a setup session").requiredOption("--setup-id <setupId>", "Setup session ID").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
1049
|
+
await runSetupStatusCommand(opts.setupId, opts.json);
|
|
1050
|
+
});
|
|
1051
|
+
setup.command("cancel").description("Cancel a setup session").requiredOption("--setup-id <setupId>", "Setup session ID").option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
1052
|
+
await runSetupCancelCommand(opts.setupId, opts.json);
|
|
1053
|
+
});
|
|
1054
|
+
setup.command("wait").description("Wait for setup completion until timeout").requiredOption("--setup-id <setupId>", "Setup session ID").option("--timeout <seconds>", "Timeout in seconds", (value) => parseInt(value, 10), 300).option("--json", "Output machine-readable JSON").action(async (opts) => {
|
|
1055
|
+
await runSetupWaitCommand(opts.setupId, opts.timeout, opts.json);
|
|
826
1056
|
});
|
|
827
1057
|
}
|
|
828
|
-
async function runSetupWizard(emailArg) {
|
|
1058
|
+
async function runSetupWizard(emailArg, { json = false } = {}) {
|
|
1059
|
+
if (json) {
|
|
1060
|
+
if (!emailArg) {
|
|
1061
|
+
writeJson(buildCliError("invalid_email", "Use --email when running setup in JSON mode."));
|
|
1062
|
+
process.exitCode = 1;
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
await runSetupStartCommand(emailArg, true);
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
829
1068
|
const config = getConfig();
|
|
830
1069
|
const loggedIn = await isLoggedIn();
|
|
831
1070
|
if (loggedIn) {
|
|
@@ -856,94 +1095,61 @@ async function runSetupWizard(emailArg) {
|
|
|
856
1095
|
console.log();
|
|
857
1096
|
console.log(chalk4.bold.cyan(" W E L C O M E T O C L I S H O P"));
|
|
858
1097
|
console.log(chalk4.dim(" Order anything from your terminal."));
|
|
859
|
-
console.log(chalk4.dim(` npm: v${"1.
|
|
860
|
-
console.log(chalk4.dim(` Build: ${"2026-04-
|
|
1098
|
+
console.log(chalk4.dim(` npm: v${"1.5.1"}`));
|
|
1099
|
+
console.log(chalk4.dim(` Build: ${"2026-04-04T20:33:18.769Z"}`));
|
|
861
1100
|
console.log();
|
|
862
1101
|
divider(chalk4.cyan);
|
|
863
1102
|
console.log();
|
|
864
|
-
console.log(
|
|
865
|
-
|
|
866
|
-
);
|
|
867
|
-
console.log(
|
|
868
|
-
chalk4.dim(" securely link your payment method in the browser.")
|
|
869
|
-
);
|
|
870
|
-
console.log(
|
|
871
|
-
chalk4.dim(" Your AI agent can then add addresses and place orders for you.")
|
|
872
|
-
);
|
|
1103
|
+
console.log(chalk4.dim(" Set up your account in one step. You'll get a link to"));
|
|
1104
|
+
console.log(chalk4.dim(" securely link your payment method in the browser."));
|
|
1105
|
+
console.log(chalk4.dim(" Your AI agent can then add addresses and place orders for you."));
|
|
873
1106
|
console.log();
|
|
874
|
-
console.log(
|
|
875
|
-
|
|
876
|
-
);
|
|
877
|
-
console.log(
|
|
878
|
-
chalk4.dim(" Terms & Conditions: ") + chalk4.cyan.underline("https://clishop.ai/terms")
|
|
879
|
-
);
|
|
880
|
-
console.log(
|
|
881
|
-
chalk4.dim(" Privacy Policy: ") + chalk4.cyan.underline("https://clishop.ai/privacy")
|
|
882
|
-
);
|
|
1107
|
+
console.log(chalk4.dim(" By creating an account you agree to the CLISHOP"));
|
|
1108
|
+
console.log(chalk4.dim(" Terms & Conditions: ") + chalk4.cyan.underline("https://clishop.ai/terms"));
|
|
1109
|
+
console.log(chalk4.dim(" Privacy Policy: ") + chalk4.cyan.underline("https://clishop.ai/privacy"));
|
|
883
1110
|
console.log();
|
|
884
|
-
let email = emailArg;
|
|
1111
|
+
let email = emailArg?.trim();
|
|
885
1112
|
if (!email) {
|
|
1113
|
+
if (!isInteractiveSession()) {
|
|
1114
|
+
console.error(chalk4.red("\n\u2717 Email is required in non-interactive mode. Use: clishop setup start --email <email> --json\n"));
|
|
1115
|
+
process.exitCode = 1;
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
886
1118
|
const answers = await inquirer3.prompt([
|
|
887
1119
|
{ type: "input", name: "email", message: "Email:" }
|
|
888
1120
|
]);
|
|
889
|
-
email = answers.email;
|
|
1121
|
+
email = answers.email?.trim();
|
|
1122
|
+
}
|
|
1123
|
+
if (!email || !isLikelyEmail(email)) {
|
|
1124
|
+
console.error(chalk4.red("\n\u2717 A valid email address is required.\n"));
|
|
1125
|
+
process.exitCode = 1;
|
|
1126
|
+
return;
|
|
890
1127
|
}
|
|
891
|
-
|
|
892
|
-
let setupUrl;
|
|
893
|
-
let deviceCode;
|
|
1128
|
+
let startResult;
|
|
894
1129
|
try {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
setupUrl = res.data.setupUrl;
|
|
898
|
-
deviceCode = res.data.deviceCode;
|
|
1130
|
+
console.log(chalk4.dim(" Creating your account and payment link..."));
|
|
1131
|
+
startResult = await startSetupSession(email);
|
|
899
1132
|
} catch (error) {
|
|
900
|
-
|
|
901
|
-
console.log(chalk4.red(` \u2717 Setup failed: ${msg}`));
|
|
1133
|
+
console.log(chalk4.red(` \u2717 Setup failed: ${error?.message || "Unknown error"}`));
|
|
902
1134
|
console.log();
|
|
903
1135
|
console.log(chalk4.dim(" You can try again with: ") + chalk4.white("clishop setup"));
|
|
904
1136
|
console.log();
|
|
905
1137
|
process.exitCode = 1;
|
|
906
1138
|
return;
|
|
907
1139
|
}
|
|
908
|
-
|
|
909
|
-
"Give this link to your human to configure the payment method:",
|
|
910
|
-
setupUrl
|
|
911
|
-
);
|
|
1140
|
+
printSetupStartResult(startResult);
|
|
912
1141
|
console.log(chalk4.dim(" Waiting for you to complete payment setup..."));
|
|
913
|
-
console.log(chalk4.dim("
|
|
1142
|
+
console.log(chalk4.dim(" You can resume later with the setup ID shown above."));
|
|
914
1143
|
console.log();
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
try {
|
|
921
|
-
const res = await axios.post(`${baseUrl}/auth/device/poll`, { deviceCode });
|
|
922
|
-
const data = res.data;
|
|
923
|
-
if (data.status === "complete" && data.token && data.refreshToken && data.user) {
|
|
924
|
-
await storeAuthFromSetup({
|
|
925
|
-
token: data.token,
|
|
926
|
-
refreshToken: data.refreshToken,
|
|
927
|
-
user: data.user
|
|
928
|
-
});
|
|
929
|
-
config.set("setupCompleted", true);
|
|
930
|
-
console.log(chalk4.green(" \u2713 Payment linked and account activated!"));
|
|
931
|
-
completed = true;
|
|
932
|
-
break;
|
|
933
|
-
}
|
|
934
|
-
if (data.status === "expired") {
|
|
935
|
-
console.log(chalk4.red(" Setup link expired. Run ") + chalk4.white("clishop setup") + chalk4.red(" to try again."));
|
|
936
|
-
process.exitCode = 1;
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
} catch {
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
if (!completed) {
|
|
943
|
-
console.log(chalk4.red(" Timed out waiting for setup. Run ") + chalk4.white("clishop setup") + chalk4.red(" to try again."));
|
|
1144
|
+
const result = await waitForSetupSession(startResult.setup_id, {
|
|
1145
|
+
timeout: DEFAULT_SETUP_TIMEOUT_MS
|
|
1146
|
+
});
|
|
1147
|
+
if (!result.ok || result.status !== "completed") {
|
|
1148
|
+
printSetupStatusResult(result);
|
|
944
1149
|
process.exitCode = 1;
|
|
945
1150
|
return;
|
|
946
1151
|
}
|
|
1152
|
+
config.set("setupCompleted", true);
|
|
947
1153
|
console.log();
|
|
948
1154
|
divider(chalk4.green);
|
|
949
1155
|
console.log();
|
|
@@ -955,18 +1161,10 @@ async function runSetupWizard(emailArg) {
|
|
|
955
1161
|
console.log();
|
|
956
1162
|
console.log(chalk4.dim(" Here are some commands to get you started:"));
|
|
957
1163
|
console.log();
|
|
958
|
-
console.log(
|
|
959
|
-
|
|
960
|
-
);
|
|
961
|
-
console.log(
|
|
962
|
-
chalk4.white(" clishop buy <id> ") + chalk4.dim("Quick-buy a product")
|
|
963
|
-
);
|
|
964
|
-
console.log(
|
|
965
|
-
chalk4.white(" clishop order list ") + chalk4.dim("View your orders")
|
|
966
|
-
);
|
|
967
|
-
console.log(
|
|
968
|
-
chalk4.white(" clishop --help ") + chalk4.dim("See all commands")
|
|
969
|
-
);
|
|
1164
|
+
console.log(chalk4.white(" clishop search <query> ") + chalk4.dim("Search for products"));
|
|
1165
|
+
console.log(chalk4.white(" clishop buy <id> ") + chalk4.dim("Quick-buy a product"));
|
|
1166
|
+
console.log(chalk4.white(" clishop order list ") + chalk4.dim("View your orders"));
|
|
1167
|
+
console.log(chalk4.white(" clishop --help ") + chalk4.dim("See all commands"));
|
|
970
1168
|
console.log();
|
|
971
1169
|
divider(chalk4.green);
|
|
972
1170
|
console.log();
|
|
@@ -2974,6 +3172,7 @@ function registerStatusCommand(program2) {
|
|
|
2974
3172
|
try {
|
|
2975
3173
|
if (!await isLoggedIn()) {
|
|
2976
3174
|
console.log(chalk11.yellow("\nNot set up yet. Run: clishop setup\n"));
|
|
3175
|
+
console.log(chalk11.dim("For agent runners, use: clishop setup start --email <email> --json\n"));
|
|
2977
3176
|
return;
|
|
2978
3177
|
}
|
|
2979
3178
|
const spinner = ora8("Fetching account overview...").start();
|
|
@@ -4036,7 +4235,7 @@ import chalk15 from "chalk";
|
|
|
4036
4235
|
import { join } from "path";
|
|
4037
4236
|
import { homedir } from "os";
|
|
4038
4237
|
import { mkdirSync } from "fs";
|
|
4039
|
-
import
|
|
4238
|
+
import axios from "axios";
|
|
4040
4239
|
function registerDoctorCommand(program2) {
|
|
4041
4240
|
program2.command("doctor").description("Check system compatibility and auth status").action(async () => {
|
|
4042
4241
|
const checks = [];
|
|
@@ -4071,7 +4270,7 @@ function registerDoctorCommand(program2) {
|
|
|
4071
4270
|
});
|
|
4072
4271
|
const apiUrl = getApiBaseUrl();
|
|
4073
4272
|
try {
|
|
4074
|
-
await
|
|
4273
|
+
await axios.get(`${apiUrl}/health`, { timeout: 5e3 });
|
|
4075
4274
|
checks.push({ name: "API reachable", ok: true, detail: apiUrl });
|
|
4076
4275
|
} catch {
|
|
4077
4276
|
checks.push({
|
|
@@ -4091,10 +4290,11 @@ function registerDoctorCommand(program2) {
|
|
|
4091
4290
|
|
|
4092
4291
|
// src/index.ts
|
|
4093
4292
|
var program = new Command();
|
|
4094
|
-
program.name("clishop").version("1.
|
|
4293
|
+
program.name("clishop").version("1.5.1").description(
|
|
4095
4294
|
chalk16.bold("CLISHOP") + ` \u2014 Order anything from your terminal.
|
|
4096
4295
|
|
|
4097
|
-
Run 'clishop setup'
|
|
4296
|
+
Run 'clishop setup' for the human-friendly flow, or
|
|
4297
|
+
'clishop setup start --email <email> --json' for agent-safe setup.
|
|
4098
4298
|
Use agents to set safety limits, addresses, and payment methods.
|
|
4099
4299
|
The "default" agent is used when no agent is specified.`
|
|
4100
4300
|
).option("--agent <name>", "Use a specific agent for this command").hook("preAction", (thisCommand) => {
|
|
@@ -4133,7 +4333,16 @@ async function main() {
|
|
|
4133
4333
|
if (!hasSubcommand) {
|
|
4134
4334
|
const config = getConfig();
|
|
4135
4335
|
if (!config.get("setupCompleted")) {
|
|
4136
|
-
|
|
4336
|
+
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
4337
|
+
await runSetupWizard();
|
|
4338
|
+
return;
|
|
4339
|
+
}
|
|
4340
|
+
console.error(
|
|
4341
|
+
chalk16.yellow(
|
|
4342
|
+
"CLISHOP setup is incomplete. Run 'clishop setup start --email <email> --json' in non-interactive environments."
|
|
4343
|
+
)
|
|
4344
|
+
);
|
|
4345
|
+
process.exit(1);
|
|
4137
4346
|
return;
|
|
4138
4347
|
}
|
|
4139
4348
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
claimSetupSession,
|
|
3
4
|
getApiClient,
|
|
5
|
+
getSetupStatus,
|
|
4
6
|
getUserInfo,
|
|
5
7
|
isLoggedIn,
|
|
6
|
-
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
startSetupSession
|
|
9
|
+
} from "./chunk-EM4ZGZOU.js";
|
|
8
10
|
import {
|
|
9
11
|
__export,
|
|
10
12
|
getActiveAgent,
|
|
11
|
-
getApiBaseUrl,
|
|
12
13
|
getConfig
|
|
13
14
|
} from "./chunk-X3H7SYR4.js";
|
|
14
15
|
|
|
@@ -13785,7 +13786,6 @@ function date4(params) {
|
|
|
13785
13786
|
config(en_default());
|
|
13786
13787
|
|
|
13787
13788
|
// src/mcp.ts
|
|
13788
|
-
import axios from "axios";
|
|
13789
13789
|
function formatPrice(cents, currency) {
|
|
13790
13790
|
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(
|
|
13791
13791
|
cents / 100
|
|
@@ -13823,7 +13823,7 @@ var server = new McpServer(
|
|
|
13823
13823
|
);
|
|
13824
13824
|
server.registerTool("setup", {
|
|
13825
13825
|
title: "Setup",
|
|
13826
|
-
description: "
|
|
13826
|
+
description: "Start a resumable setup session. Returns a setup URL for the human and a setup_id for the agent to check later. After the human completes the link, call setup_status with the returned setup_id.",
|
|
13827
13827
|
inputSchema: {
|
|
13828
13828
|
email: external_exports.string().email().describe("User's email address")
|
|
13829
13829
|
},
|
|
@@ -13834,21 +13834,18 @@ server.registerTool("setup", {
|
|
|
13834
13834
|
}
|
|
13835
13835
|
}, async (args) => {
|
|
13836
13836
|
return safeCall(async () => {
|
|
13837
|
-
const
|
|
13838
|
-
const res = await axios.post(`${baseUrl}/auth/setup-link`, {
|
|
13839
|
-
email: args.email
|
|
13840
|
-
});
|
|
13837
|
+
const data = await startSetupSession(args.email);
|
|
13841
13838
|
return {
|
|
13842
|
-
...
|
|
13843
|
-
message: "Give this link to your human to configure their payment method in the browser. Then call setup_status with the
|
|
13839
|
+
...data,
|
|
13840
|
+
message: "Give this link to your human to configure their payment method in the browser. Then call setup_status with the setup_id to check when they're done."
|
|
13844
13841
|
};
|
|
13845
13842
|
});
|
|
13846
13843
|
});
|
|
13847
13844
|
server.registerTool("setup_status", {
|
|
13848
13845
|
title: "Setup Status",
|
|
13849
|
-
description: "
|
|
13846
|
+
description: "Check the current setup state using the setup_id returned by the setup tool. If setup is complete, auth is stored locally so later CLISHOP calls can proceed.",
|
|
13850
13847
|
inputSchema: {
|
|
13851
|
-
|
|
13848
|
+
setupId: external_exports.string().describe("The setup_id returned by the setup tool")
|
|
13852
13849
|
},
|
|
13853
13850
|
annotations: {
|
|
13854
13851
|
title: "Setup Status",
|
|
@@ -13856,19 +13853,13 @@ server.registerTool("setup_status", {
|
|
|
13856
13853
|
}
|
|
13857
13854
|
}, async (args) => {
|
|
13858
13855
|
return safeCall(async () => {
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
token: data.token,
|
|
13867
|
-
refreshToken: data.refreshToken,
|
|
13868
|
-
user: data.user
|
|
13869
|
-
});
|
|
13870
|
-
const config2 = getConfig();
|
|
13871
|
-
config2.set("setupCompleted", true);
|
|
13856
|
+
let data = await getSetupStatus(args.setupId);
|
|
13857
|
+
if (data.ok && data.status === "completed") {
|
|
13858
|
+
data = await claimSetupSession(args.setupId);
|
|
13859
|
+
if (data.ok) {
|
|
13860
|
+
const config2 = getConfig();
|
|
13861
|
+
config2.set("setupCompleted", true);
|
|
13862
|
+
}
|
|
13872
13863
|
}
|
|
13873
13864
|
return data;
|
|
13874
13865
|
});
|