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 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
- The setup wizard walks you through creating an account, adding an address, and linking a payment method. After that:
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
- storeAuthFromSetup,
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
- storeAuthFromSetup
13
- } from "./chunk-EAXPWOMT.js";
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
- import axios from "axios";
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
- } else if (process.platform === "darwin") {
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
- "Set up your CLISHOP account \u2014 links your payment method via a secure browser link"
824
- ).option("--email <email>", "Email address (skips prompt)").action(async (opts) => {
825
- await runSetupWizard(opts.email);
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.4.7"}`));
860
- console.log(chalk4.dim(` Build: ${"2026-04-04T18:17:20.412Z"}`));
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
- chalk4.dim(" Set up your account in one step. You'll get a link to")
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
- chalk4.dim(" By creating an account you agree to the CLISHOP")
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
- console.log(chalk4.dim(" Creating your account and payment link..."));
892
- let setupUrl;
893
- let deviceCode;
1128
+ let startResult;
894
1129
  try {
895
- const baseUrl2 = getApiBaseUrl();
896
- const res = await axios.post(`${baseUrl2}/auth/setup-link`, { email });
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
- const msg = error?.response?.data?.message || error.message;
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
- printSetupLink(
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(" If your terminal UI hides earlier output, use the plain-text URL above."));
1142
+ console.log(chalk4.dim(" You can resume later with the setup ID shown above."));
914
1143
  console.log();
915
- const baseUrl = getApiBaseUrl();
916
- const maxAttempts = 120;
917
- let completed = false;
918
- for (let i = 0; i < maxAttempts; i++) {
919
- await new Promise((r) => setTimeout(r, 5e3));
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
- chalk4.white(" clishop search <query> ") + chalk4.dim("Search for products")
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 axios2 from "axios";
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 axios2.get(`${apiUrl}/health`, { timeout: 5e3 });
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.4.7").description(
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' to get started with a single payment link.
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
- await runSetupWizard();
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
- storeAuthFromSetup
7
- } from "./chunk-EAXPWOMT.js";
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: "Onboard a new user by creating their account and generating a Stripe payment setup link. The user must open this link in their browser to link their payment method. This is the ONLY step requiring human interaction. After the user completes the link, call setup_status with the returned deviceCode to get auth tokens. The agent can then use add_address to set up shipping autonomously.",
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 baseUrl = getApiBaseUrl();
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
- ...res.data,
13843
- message: "Give this link to your human to configure their payment method in the browser. Then call setup_status with the deviceCode to check when they're done."
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: "Poll the setup status after the user was given a payment link via the setup tool. Returns 'pending' while waiting, 'complete' with auth tokens when done, or 'expired' if timed out.",
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
- deviceCode: external_exports.string().describe("The deviceCode returned by the setup tool")
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
- const baseUrl = getApiBaseUrl();
13860
- const res = await axios.post(`${baseUrl}/auth/device/poll`, {
13861
- deviceCode: args.deviceCode
13862
- });
13863
- const data = res.data;
13864
- if (data.status === "complete" && data.token) {
13865
- await storeAuthFromSetup({
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clishop",
3
- "version": "1.4.7",
3
+ "version": "1.5.1",
4
4
  "mcpName": "io.github.StefDCL/clishop",
5
5
  "description": "CLISHOP — Order anything from your terminal",
6
6
  "main": "dist/index.js",