shopstack 0.2.4 → 0.2.5

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
@@ -70,7 +70,7 @@ deliberately has no card-input or payment-approval tool.
70
70
 
71
71
  The canonical agent skill ships as `SKILL.md` in this package. After
72
72
  publication it is available at
73
- `https://unpkg.com/shopstack@0.2.4/SKILL.md` with the release-pinned package.
73
+ `https://unpkg.com/shopstack@0.2.5/SKILL.md` with the release-pinned package.
74
74
 
75
75
  ## Connections
76
76
 
@@ -80,7 +80,10 @@ shopstack connect link
80
80
  ```
81
81
 
82
82
  Link is currently the only persistent payment provider. Connecting it is
83
- optional.
83
+ optional. `shopstack connect link` opens the HTTPS Link setup page, displays the
84
+ confirmation phrase, and checks the connection every five seconds for up to
85
+ five minutes. It prints `Link connected` when the connection is ready. If a
86
+ browser cannot open automatically, use the exact URL printed in the terminal.
84
87
 
85
88
  ## Run a checkout
86
89
 
package/SKILL.md CHANGED
@@ -69,6 +69,11 @@ shopstack connect list
69
69
  shopstack connect link
70
70
  ```
71
71
 
72
+ The Link command opens the HTTPS setup page, displays the confirmation phrase,
73
+ and checks every five seconds for up to five minutes. Continue only after it
74
+ prints `Link connected` and reports `checkout_ready: true`. If the browser does
75
+ not open automatically, open the exact URL printed in the terminal.
76
+
72
77
  Include `payment_provider: "link"` only when the active user's Link connection reports checkout-ready. Otherwise omit `payment_provider`. Shopstack will run until card entry and request one protected checkout-scoped card through the SDK or no-echo CLI prompt.
73
78
 
74
79
  Never put card data in MCP arguments, natural-language messages, model output, logs, or ordinary CLI flags.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shopstack",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Production Shopstack SDK and CLI for agentic checkout.",
5
5
  "type": "module",
6
6
  "exports": {
package/src/cli.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { readFile } from "node:fs/promises";
2
3
  import { createInterface } from "node:readline/promises";
3
4
 
@@ -7,6 +8,8 @@ import { ConfigStore } from "./config.js";
7
8
  const VERSION = JSON.parse(
8
9
  await readFile(new URL("../package.json", import.meta.url), "utf8"),
9
10
  ).version;
11
+ const LINK_POLL_ATTEMPTS = 60;
12
+ const LINK_POLL_INTERVAL_MS = 5_000;
10
13
 
11
14
  const HELP = `Shopstack CLI ${VERSION}
12
15
 
@@ -92,6 +95,77 @@ function writeJson(stream, value) {
92
95
  stream.write(`${JSON.stringify(value, null, 2)}\n`);
93
96
  }
94
97
 
98
+ function delay(milliseconds) {
99
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
100
+ }
101
+
102
+ async function openExternal(url) {
103
+ const parsed = new URL(url);
104
+ if (parsed.protocol !== "https:") {
105
+ throw new Error("Only HTTPS connection URLs can be opened.");
106
+ }
107
+ const [command, args] =
108
+ process.platform === "darwin"
109
+ ? ["open", [url]]
110
+ : process.platform === "win32"
111
+ ? ["cmd.exe", ["/d", "/s", "/c", "start", "", url]]
112
+ : ["xdg-open", [url]];
113
+ await new Promise((resolve, reject) => {
114
+ const child = spawn(command, args, { detached: true, stdio: "ignore" });
115
+ child.once("error", reject);
116
+ child.once("spawn", () => {
117
+ child.unref();
118
+ resolve();
119
+ });
120
+ });
121
+ }
122
+
123
+ async function connectLink(client, dependencies) {
124
+ let result = await client.connect("link");
125
+ if (
126
+ result.connection_status !== "action_required" ||
127
+ typeof result.connect_url !== "string"
128
+ ) {
129
+ return result;
130
+ }
131
+
132
+ dependencies.stderr.write(`Open Link: ${result.connect_url}\n`);
133
+ if (typeof result.phrase === "string") {
134
+ dependencies.stderr.write(`Confirmation phrase: ${result.phrase}\n`);
135
+ }
136
+ try {
137
+ await dependencies.openExternal(result.connect_url);
138
+ } catch {
139
+ dependencies.stderr.write(
140
+ "The browser could not open automatically. Open the URL shown above.\n",
141
+ );
142
+ }
143
+ dependencies.stderr.write("Waiting for Link confirmation...\n");
144
+
145
+ for (let attempt = 0; attempt < dependencies.linkPollAttempts; attempt += 1) {
146
+ await dependencies.delay(dependencies.linkPollIntervalMs);
147
+ result = await client.connect("link");
148
+ if (
149
+ result.connection_status === "active" &&
150
+ result.checkout_ready === true
151
+ ) {
152
+ dependencies.stderr.write("✓ Link connected\n");
153
+ return result;
154
+ }
155
+ if (
156
+ result.connection_status !== "action_required" &&
157
+ result.connection_status !== "connecting"
158
+ ) {
159
+ return result;
160
+ }
161
+ }
162
+
163
+ dependencies.stderr.write(
164
+ "Link setup is still pending. Run `shopstack connect link` to resume.\n",
165
+ );
166
+ return result;
167
+ }
168
+
95
169
  async function readJsonFile(path) {
96
170
  return JSON.parse(await readFile(path, "utf8"));
97
171
  }
@@ -266,6 +340,10 @@ export async function runCli(args, supplied = {}) {
266
340
  clientFactory: (options) => new ShopstackClient(options),
267
341
  configStore: new ConfigStore(),
268
342
  confirm: undefined,
343
+ delay,
344
+ linkPollAttempts: LINK_POLL_ATTEMPTS,
345
+ linkPollIntervalMs: LINK_POLL_INTERVAL_MS,
346
+ openExternal,
269
347
  prompt: undefined,
270
348
  readJsonFile,
271
349
  secretPrompt: undefined,
@@ -474,7 +552,7 @@ export async function runCli(args, supplied = {}) {
474
552
  const result =
475
553
  action === "list"
476
554
  ? await client.listConnections()
477
- : await client.connect("link");
555
+ : await connectLink(client, dependencies);
478
556
  writeJson(dependencies.stdout, result);
479
557
  return;
480
558
  }