basketeer 0.1.0 → 0.1.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 +3 -3
- package/dist/cli.js +2 -2
- package/dist/is-main.d.ts +8 -0
- package/dist/is-main.js +20 -0
- package/dist/mcp-server.js +3 -3
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Run your weekly shop from code, the terminal, or an AI agent. Everything but sig
|
|
|
23
23
|
<br>
|
|
24
24
|
|
|
25
25
|
[](https://github.com/tobyandrews1985/basketeer/actions/workflows/ci.yml)
|
|
26
|
-
[](tests/)
|
|
27
27
|
[](./LICENSE)
|
|
28
28
|
[](https://nodejs.org)
|
|
29
29
|
|
|
@@ -39,7 +39,7 @@ Tesco has no public API, and the usual approach (scraping the DOM) shatters on t
|
|
|
39
39
|
- **Agent-ready.** A stdio MCP server lets Claude or any MCP client run the shop. Read-only and destructive tools are annotated, and checkout never pays.
|
|
40
40
|
- **Typed and lean.** One fully-typed client you import; the CLI and MCP server are built on it. The data path imports no third-party packages (the three runtime deps — commander, the MCP SDK, and zod — are pulled only by the CLI and MCP server). It is pure `fetch` with no Node-only APIs, so it runs on Node and Node-compatible runtimes.
|
|
41
41
|
- **Safe.** `checkout()` stops at the payment URL. A human finishes 3-D Secure in a browser, by design.
|
|
42
|
-
- **Tested.**
|
|
42
|
+
- **Tested.** 75 tests across the data plane and its parsers.
|
|
43
43
|
|
|
44
44
|
## Nutrition, the part nothing else has
|
|
45
45
|
|
|
@@ -260,7 +260,7 @@ Personal-account interoperability automation: your account, your data. The clien
|
|
|
260
260
|
|
|
261
261
|
```bash
|
|
262
262
|
npm install
|
|
263
|
-
npm test #
|
|
263
|
+
npm test # 75 tests: vitest unit + regression + smoke
|
|
264
264
|
npm run build # clean build to dist/
|
|
265
265
|
npm run example:lookup
|
|
266
266
|
```
|
package/dist/cli.js
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
* to stderr with a non-zero exit. Per the SDK's ethics ceiling, `checkout` only
|
|
9
9
|
* hands back a payment URL — a human completes payment in a browser.
|
|
10
10
|
*/
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
11
|
import { Command, InvalidArgumentError } from "commander";
|
|
13
12
|
import { BrowserAuthBackend } from "./auth/browser/playwright.js";
|
|
14
13
|
import { Basketeer, BasketeerError, FileTokenStore } from "./index.js";
|
|
14
|
+
import { isMainModule } from "./is-main.js";
|
|
15
15
|
/** Pretty-print any JSON-serialisable value to stdout. */
|
|
16
16
|
function emit(value) {
|
|
17
17
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
@@ -201,7 +201,7 @@ program
|
|
|
201
201
|
});
|
|
202
202
|
// --- entrypoint -------------------------------------------------------------
|
|
203
203
|
// Only parse argv when run as a binary, so the module is importable (tests).
|
|
204
|
-
if (
|
|
204
|
+
if (isMainModule(import.meta.url)) {
|
|
205
205
|
program.parseAsync().catch((err) => {
|
|
206
206
|
const name = err instanceof BasketeerError ? err.name : "Error";
|
|
207
207
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when `moduleUrl` (pass `import.meta.url`) is the entrypoint the process
|
|
3
|
+
* was launched with. Resolves symlinks first, so it still holds when the module
|
|
4
|
+
* runs via an installed bin — npx, a global install, or `node_modules/.bin` —
|
|
5
|
+
* where `process.argv[1]` is a symlink to the real file and a raw `===` against
|
|
6
|
+
* `fileURLToPath(import.meta.url)` would wrongly be false.
|
|
7
|
+
*/
|
|
8
|
+
export declare function isMainModule(moduleUrl: string): boolean;
|
package/dist/is-main.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
/**
|
|
4
|
+
* True when `moduleUrl` (pass `import.meta.url`) is the entrypoint the process
|
|
5
|
+
* was launched with. Resolves symlinks first, so it still holds when the module
|
|
6
|
+
* runs via an installed bin — npx, a global install, or `node_modules/.bin` —
|
|
7
|
+
* where `process.argv[1]` is a symlink to the real file and a raw `===` against
|
|
8
|
+
* `fileURLToPath(import.meta.url)` would wrongly be false.
|
|
9
|
+
*/
|
|
10
|
+
export function isMainModule(moduleUrl) {
|
|
11
|
+
const entry = process.argv[1];
|
|
12
|
+
if (!entry)
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
return realpathSync(entry) === fileURLToPath(moduleUrl);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
* Run: `basketeer-mcp` (stdio). Configure it as an MCP server in your agent.
|
|
23
23
|
*/
|
|
24
24
|
import { createHash } from "node:crypto";
|
|
25
|
-
import { fileURLToPath } from "node:url";
|
|
26
25
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27
26
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
28
27
|
import { z } from "zod";
|
|
29
28
|
import { BrowserAuthBackend } from "./auth/browser/playwright.js";
|
|
30
29
|
import { Basketeer, BasketeerError, FileTokenStore } from "./index.js";
|
|
30
|
+
import { isMainModule } from "./is-main.js";
|
|
31
31
|
/** Wrap a value as MCP JSON text content. */
|
|
32
32
|
function json(value) {
|
|
33
33
|
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
|
|
@@ -55,7 +55,7 @@ export function confirmToken(parts) {
|
|
|
55
55
|
* Pure: no filesystem/auth/transport side effects — safe to call from a test.
|
|
56
56
|
*/
|
|
57
57
|
export function buildServer(client) {
|
|
58
|
-
const server = new McpServer({ name: "basketeer", version: "0.1.
|
|
58
|
+
const server = new McpServer({ name: "basketeer", version: "0.1.1" });
|
|
59
59
|
// --- reads (anonymous OK) -------------------------------------------------
|
|
60
60
|
server.tool("basketeer_search", "Search the Tesco grocery catalogue. Returns matching products with SKU, title, price, and any offers.", {
|
|
61
61
|
query: z.string().describe("Search terms, e.g. 'semi skimmed milk'."),
|
|
@@ -153,7 +153,7 @@ export function buildServer(client) {
|
|
|
153
153
|
// Only build the client (which reads ~/.basketeer/session.json) and attach the
|
|
154
154
|
// stdio transport when run as a binary, so the module can be imported (e.g. by a
|
|
155
155
|
// smoke test) without any filesystem/auth side effects or starting the server.
|
|
156
|
-
if (
|
|
156
|
+
if (isMainModule(import.meta.url)) {
|
|
157
157
|
const store = new FileTokenStore();
|
|
158
158
|
const client = new Basketeer({
|
|
159
159
|
session: await store.load(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basketeer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Typed, pure-HTTP TypeScript SDK for your own Tesco grocery account — search, basket, delivery slots, and orders over plain fetch. Browser only for login + payment.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,12 +56,16 @@
|
|
|
56
56
|
"keywords": [
|
|
57
57
|
"tesco",
|
|
58
58
|
"groceries",
|
|
59
|
+
"grocery",
|
|
60
|
+
"supermarket",
|
|
61
|
+
"nutrition",
|
|
59
62
|
"automation",
|
|
60
63
|
"graphql",
|
|
61
64
|
"client",
|
|
62
65
|
"mcp",
|
|
63
66
|
"cli",
|
|
64
|
-
"ai-agent"
|
|
67
|
+
"ai-agent",
|
|
68
|
+
"uk"
|
|
65
69
|
],
|
|
66
70
|
"dependencies": {
|
|
67
71
|
"@modelcontextprotocol/sdk": "^1.0.0",
|