shopstack 0.2.3 → 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 +8 -2
- package/SKILL.md +6 -1
- package/package.json +3 -3
- package/src/cli.js +82 -2
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.
|
|
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
|
|
|
@@ -125,6 +128,9 @@ Treat the printed URL as opaque and open it exactly as returned, including its
|
|
|
125
128
|
`#token=...` fragment. If that fragment is lost while the checkout is active,
|
|
126
129
|
run `shopstack checkout view CHECKOUT_ID` to receive a replacement URL. The
|
|
127
130
|
replacement disconnects an older viewer session but does not restart checkout.
|
|
131
|
+
Omit `model_handle` to use the production GLM route. Set it to `qwen` only when
|
|
132
|
+
you explicitly need the fixed Qwen comparison route.
|
|
133
|
+
|
|
128
134
|
When no provider is selected, the checkout runs normally until the payment
|
|
129
135
|
form, then asks for card details through a no-echo terminal prompt. Card data is
|
|
130
136
|
sent only to the protected payment-details endpoint. The CLI separately shows
|
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.
|
|
@@ -141,7 +146,7 @@ replacement verbatim.
|
|
|
141
146
|
|
|
142
147
|
- Use `GET/POST /v1/checkout/{id}/messages` only for ordinary missing information.
|
|
143
148
|
- Use protected SDK/CLI payment input when `required_input.type` is `payment_card`.
|
|
144
|
-
- Use only the dedicated payment-approval endpoint
|
|
149
|
+
- Use only the dedicated payment-approval endpoint with the owning user's credential.
|
|
145
150
|
- MCP and messages cannot approve payment and expose no approval tool.
|
|
146
151
|
- Never infer approval from a user's conversational message.
|
|
147
152
|
- Cancel with the typed cancellation operation if the user withdraws the request.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shopstack",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Production Shopstack SDK and CLI for agentic checkout.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"check": "npm run format:check && npm run test:coverage",
|
|
27
27
|
"format:check": "prettier --check .",
|
|
28
28
|
"shopstack": "node ./bin/shopstack",
|
|
29
|
-
"test": "node --test test",
|
|
30
|
-
"test:coverage": "node --test --experimental-test-coverage test",
|
|
29
|
+
"test": "node --test test/*.test.js",
|
|
30
|
+
"test:coverage": "node --test --experimental-test-coverage test/*.test.js",
|
|
31
31
|
"prepublishOnly": "npm run check"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
package/src/cli.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
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
|
|
|
4
5
|
import { ShopstackClient } from "./client.js";
|
|
5
6
|
import { ConfigStore } from "./config.js";
|
|
6
7
|
|
|
7
|
-
const VERSION =
|
|
8
|
+
const VERSION = JSON.parse(
|
|
9
|
+
await readFile(new URL("../package.json", import.meta.url), "utf8"),
|
|
10
|
+
).version;
|
|
11
|
+
const LINK_POLL_ATTEMPTS = 60;
|
|
12
|
+
const LINK_POLL_INTERVAL_MS = 5_000;
|
|
8
13
|
|
|
9
14
|
const HELP = `Shopstack CLI ${VERSION}
|
|
10
15
|
|
|
@@ -90,6 +95,77 @@ function writeJson(stream, value) {
|
|
|
90
95
|
stream.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
91
96
|
}
|
|
92
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
|
+
|
|
93
169
|
async function readJsonFile(path) {
|
|
94
170
|
return JSON.parse(await readFile(path, "utf8"));
|
|
95
171
|
}
|
|
@@ -264,6 +340,10 @@ export async function runCli(args, supplied = {}) {
|
|
|
264
340
|
clientFactory: (options) => new ShopstackClient(options),
|
|
265
341
|
configStore: new ConfigStore(),
|
|
266
342
|
confirm: undefined,
|
|
343
|
+
delay,
|
|
344
|
+
linkPollAttempts: LINK_POLL_ATTEMPTS,
|
|
345
|
+
linkPollIntervalMs: LINK_POLL_INTERVAL_MS,
|
|
346
|
+
openExternal,
|
|
267
347
|
prompt: undefined,
|
|
268
348
|
readJsonFile,
|
|
269
349
|
secretPrompt: undefined,
|
|
@@ -472,7 +552,7 @@ export async function runCli(args, supplied = {}) {
|
|
|
472
552
|
const result =
|
|
473
553
|
action === "list"
|
|
474
554
|
? await client.listConnections()
|
|
475
|
-
: await client
|
|
555
|
+
: await connectLink(client, dependencies);
|
|
476
556
|
writeJson(dependencies.stdout, result);
|
|
477
557
|
return;
|
|
478
558
|
}
|